diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -1211,8 +1211,17 @@ namespace AttributeFuncs { -/// Which attributes cannot be applied to a type. -AttributeMask typeIncompatible(Type *Ty); +enum AttributeSafetyKind : uint8_t { + ASK_SAFE_TO_DROP = 1, + ASK_UNSAFE_TO_DROP = 2, + ASK_ALL = ASK_SAFE_TO_DROP | ASK_UNSAFE_TO_DROP, +}; + +/// Which attributes cannot be applied to a type. The argument \p ASK indicates, +/// if only attributes that are known to be safely droppable are contained in +/// the mask; only attributes that might be unsafe to drop (e.g., ABI-related +/// attributes) are in the mask; or both. +AttributeMask typeIncompatible(Type *Ty, AttributeSafetyKind ASK = ASK_ALL); /// Get param/return attributes which imply immediate undefined behavior if an /// invalid value is passed. For example, this includes noundef (where undef diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1773,40 +1773,50 @@ //===----------------------------------------------------------------------===// /// Which attributes cannot be applied to a type. -AttributeMask AttributeFuncs::typeIncompatible(Type *Ty) { +AttributeMask AttributeFuncs::typeIncompatible(Type *Ty, + AttributeSafetyKind ASK) { AttributeMask Incompatible; - if (!Ty->isIntegerTy()) + if (!Ty->isIntegerTy()) { // Attributes that only apply to integers. - Incompatible.addAttribute(Attribute::SExt) - .addAttribute(Attribute::ZExt) - .addAttribute(Attribute::AllocAlign); + if (ASK & ASK_SAFE_TO_DROP) + Incompatible.addAttribute(Attribute::AllocAlign); + if (ASK & ASK_UNSAFE_TO_DROP) + Incompatible.addAttribute(Attribute::SExt).addAttribute(Attribute::ZExt); + } - if (!Ty->isPointerTy()) + if (!Ty->isPointerTy()) { // Attributes that only apply to pointers. - Incompatible.addAttribute(Attribute::Nest) - .addAttribute(Attribute::NoAlias) - .addAttribute(Attribute::NoCapture) - .addAttribute(Attribute::NonNull) - .addAttribute(Attribute::ReadNone) - .addAttribute(Attribute::ReadOnly) - .addAttribute(Attribute::SwiftError) - .addAttribute(Attribute::Dereferenceable) - .addAttribute(Attribute::DereferenceableOrNull) - .addAttribute(Attribute::Preallocated) - .addAttribute(Attribute::InAlloca) - .addAttribute(Attribute::ByVal) - .addAttribute(Attribute::StructRet) - .addAttribute(Attribute::ByRef) - .addAttribute(Attribute::ElementType); - - if (!Ty->isPtrOrPtrVectorTy()) + if (ASK & ASK_SAFE_TO_DROP) + Incompatible.addAttribute(Attribute::NoAlias) + .addAttribute(Attribute::NoCapture) + .addAttribute(Attribute::NonNull) + .addAttribute(Attribute::ReadNone) + .addAttribute(Attribute::ReadOnly) + .addAttribute(Attribute::Dereferenceable) + .addAttribute(Attribute::DereferenceableOrNull); + if (ASK & ASK_UNSAFE_TO_DROP) + Incompatible.addAttribute(Attribute::Nest) + .addAttribute(Attribute::SwiftError) + .addAttribute(Attribute::Preallocated) + .addAttribute(Attribute::InAlloca) + .addAttribute(Attribute::ByVal) + .addAttribute(Attribute::StructRet) + .addAttribute(Attribute::ByRef) + .addAttribute(Attribute::ElementType); + } + // Attributes that only apply to pointers or vectors of pointers. - Incompatible.addAttribute(Attribute::Alignment); + if (!Ty->isPtrOrPtrVectorTy()) { + if (ASK & ASK_SAFE_TO_DROP) + Incompatible.addAttribute(Attribute::Alignment); + } // Some attributes can apply to all "values" but there are no `void` values. - if (Ty->isVoidTy()) - Incompatible.addAttribute(Attribute::NoUndef); + if (Ty->isVoidTy()) { + if (ASK & ASK_SAFE_TO_DROP) + Incompatible.addAttribute(Attribute::NoUndef); + } return Incompatible; } diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3198,12 +3198,15 @@ if (!CastInst::isBitOrNoopPointerCastable(ActTy, ParamTy, DL)) return false; // Cannot transform this parameter value. + // Check if there are any incompatible attributes we cannot drop safely. if (AttrBuilder(FT->getContext(), CallerPAL.getParamAttrs(i)) - .overlaps(AttributeFuncs::typeIncompatible(ParamTy))) + .overlaps(AttributeFuncs::typeIncompatible( + ParamTy, AttributeFuncs::ASK_UNSAFE_TO_DROP))) return false; // Attribute not compatible with transformed value. - if (Call.isInAllocaArgument(i)) - return false; // Cannot transform to and from inalloca. + if (Call.isInAllocaArgument(i) || + CallerPAL.hasParamAttr(i, Attribute::Preallocated)) + return false; // Cannot transform to and from inalloca/preallocated. if (CallerPAL.hasParamAttr(i, Attribute::SwiftError)) return false; @@ -3281,14 +3284,20 @@ NewArg = Builder.CreateBitOrPointerCast(*AI, ParamTy); Args.push_back(NewArg); - // Add any parameter attributes. + // Add any parameter attributes except the ones incompatible with the new + // type. Note that we made sure all incompatible ones are safe to drop. + AttributeMask IncompatibleAttrs = AttributeFuncs::typeIncompatible( + ParamTy, AttributeFuncs::ASK_SAFE_TO_DROP); if (CallerPAL.hasParamAttr(i, Attribute::ByVal) && !ParamTy->isOpaquePointerTy()) { - AttrBuilder AB(FT->getContext(), CallerPAL.getParamAttrs(i)); - AB.addByValAttr(ParamTy->getNonOpaquePointerElementType()); + AttrBuilder AB(Ctx, CallerPAL.getParamAttrs(i).removeAttributes( + Ctx, IncompatibleAttrs)); + AB.addByValAttr(NewArg->getType()->getPointerElementType()); ArgAttrs.push_back(AttributeSet::get(Ctx, AB)); - } else - ArgAttrs.push_back(CallerPAL.getParamAttrs(i)); + } else { + ArgAttrs.push_back( + CallerPAL.getParamAttrs(i).removeAttributes(Ctx, IncompatibleAttrs)); + } } // If the function takes more arguments than the call was taking, add them diff --git a/llvm/test/Transforms/InstCombine/call-cast-attrs.ll b/llvm/test/Transforms/InstCombine/call-cast-attrs.ll --- a/llvm/test/Transforms/InstCombine/call-cast-attrs.ll +++ b/llvm/test/Transforms/InstCombine/call-cast-attrs.ll @@ -1,4 +1,5 @@ -; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; RUN: opt < %s -passes=instcombine -data-layout="p:32:32" -S | FileCheck %s --check-prefixes=CHECK,CHECK32 +; RUN: opt < %s -passes=instcombine -data-layout="p:64:64" -S | FileCheck %s --check-prefixes=CHECK,CHECK64 define signext i32 @b(i32* inreg %x) { ret i32 0 @@ -20,10 +21,16 @@ call void bitcast (void (...)* @c to void (i32*)*)(i32* %y) call void bitcast (void (...)* @c to void (i32*)*)(i32* sret(i32) %y) call void bitcast (void (i32, ...)* @d to void (i32, i32*)*)(i32 0, i32* sret(i32) %y) + call void bitcast (void (i32, ...)* @d to void (i32, i32*)*)(i32 0, i32* nocapture %y) + call void bitcast (void (i32, ...)* @d to void (i32*)*)(i32* nocapture noundef %y) ret void } ; CHECK-LABEL: define void @g(i32* %y) -; CHECK: call i32 bitcast (i32 (i32*)* @b to i32 (i32)*)(i32 zeroext 0) -; CHECK: call void (...) @c(i32* %y) -; CHECK: call void bitcast (void (...)* @c to void (i32*)*)(i32* sret(i32) %y) -; CHECK: call void bitcast (void (i32, ...)* @d to void (i32, i32*)*)(i32 0, i32* sret(i32) %y) +; CHECK: call i32 bitcast (i32 (i32*)* @b to i32 (i32)*)(i32 zeroext 0) +; CHECK: call void (...) @c(i32* %y) +; CHECK: call void bitcast (void (...)* @c to void (i32*)*)(i32* sret(i32) %y) +; CHECK: call void bitcast (void (i32, ...)* @d to void (i32, i32*)*)(i32 0, i32* sret(i32) %y) +; CHECK: call void (i32, ...) @d(i32 0, i32* nocapture %y) +; CHECK32: %2 = ptrtoint i32* %y to i32 +; CHECK32: call void (i32, ...) @d(i32 noundef %2) +; CHECK64: call void bitcast (void (i32, ...)* @d to void (i32*)*)(i32* nocapture noundef %y)