Index: llvm/lib/Transforms/IPO/MergeFunctions.cpp =================================================================== --- llvm/lib/Transforms/IPO/MergeFunctions.cpp +++ llvm/lib/Transforms/IPO/MergeFunctions.cpp @@ -450,28 +450,10 @@ ++UI; CallSite CS(U->getUser()); if (CS && CS.isCallee(U)) { - // Transfer the called function's attributes to the call site. Due to the - // bitcast we will 'lose' ABI changing attributes because the 'called - // function' is no longer a Function* but the bitcast. Code that looks up - // the attributes from the called function will fail. - - // FIXME: This is not actually true, at least not anymore. The callsite - // will always have the same ABI affecting attributes as the callee, - // because otherwise the original input has UB. Note that Old and New - // always have matching ABI, so no attributes need to be changed. - // Transferring other attributes may help other optimizations, but that - // should be done uniformly and not in this ad-hoc way. - auto &Context = New->getContext(); - auto NewPAL = New->getAttributes(); - SmallVector NewArgAttrs; - for (unsigned argIdx = 0; argIdx < CS.arg_size(); argIdx++) - NewArgAttrs.push_back(NewPAL.getParamAttributes(argIdx)); - // Don't transfer attributes from the function to the callee. Function - // attributes typically aren't relevant to the calling convention or ABI. - CS.setAttributes(AttributeList::get(Context, /*FnAttrs=*/AttributeSet(), - NewPAL.getRetAttributes(), - NewArgAttrs)); - + // Do not copy attributes from the called function to the call-site. + // Function comparison ensures that the attributes are the same up to + // type congruences in byval(), in which case we need to keep the byval + // type of the call-site, not the callee function. remove(CS.getInstruction()->getFunction()); U->set(BitcastNew); } Index: llvm/test/Transforms/MergeFunc/byval-attr-congruent-type.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/MergeFunc/byval-attr-congruent-type.ll @@ -0,0 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -mergefunc %s | FileCheck %s + +%struct.c = type { i32 } +%struct.a = type { i32 } + +@d = external dso_local global %struct.c + +define void @e(%struct.a* byval(%struct.a) %f) { +; CHECK-LABEL: @e( +; CHECK-NEXT: ret void +; + ret void +} + +define void @g(%struct.c* byval(%struct.c) %f) { +; CHECK-LABEL: @g( +; CHECK-NEXT: ret void +; + ret void +} + +define void @h() { +; CHECK-LABEL: @h( +; CHECK-NEXT: call void bitcast (void (%struct.a*)* @e to void (%struct.c*)*)(%struct.c* byval(%struct.c) @d) +; CHECK-NEXT: ret void +; + call void @g(%struct.c* byval(%struct.c) @d) + ret void +}