llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-selectiondag @llvm/pr-subscribers-backend-aarch64 Author: eleviant <details> <summary>Changes</summary> After https://reviews.llvm.org/D100591 we no longer extend return value for small composite types to 64 bits. However we still zero extend value, when such composite types are passed as parameters. Below is an example: ``` struct U4 { uint32_t v; }; U4 getU4(); void setU4(U4); int main() { U4 v = getU4(); // %call = tail call i32 @<!-- -->_Z5getU4v() // %coerce.val.ii = zext i32 %call to i64 setU4(v); // tail call void @<!-- -->_Z5setU42U4(i64 %coerce.val.ii) return 0; } ``` This is not really required as according to AArch64 ABI unused bits have unspecified value. Patch attempts to optimize zext out if bit width of exetended value is greater or equal to bit width of composite value. Below is an example: --- Full diff: https://github.com/llvm/llvm-project/pull/214788.diff 13 Files Affected: - (modified) clang/lib/CodeGen/CGCall.cpp (+8-3) - (modified) clang/lib/CodeGen/Targets/AArch64.cpp (+5) - (modified) clang/test/CodeGen/AArch64/args.cpp (+1-1) - (modified) clang/test/CodeGen/AArch64/soft-float-abi.c (+1-1) - (modified) clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp (+2-2) - (modified) clang/test/CodeGen/arm64-arguments.c (+4-2) - (modified) clang/test/CodeGen/arm64-microsoft-arguments.cpp (+2-2) - (modified) clang/test/CodeGen/attr-noundef.cpp (+2-2) - (modified) clang/test/CodeGenCXX/aarch64-arguments.cpp (+1-1) - (modified) clang/test/CodeGenCXX/arm64-darwinpcs.cpp (+1-1) - (modified) clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp (+3-3) - (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+32-1) - (added) llvm/test/CodeGen/AArch64/small-struct.ll (+27) ``````````diff diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 08cb9860f2f92..3f3e4cfff1259 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3112,12 +3112,17 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, // sense to do it here because parameters are so messed up. switch (AI.getKind()) { case ABIArgInfo::Extend: - if (AI.isSignExt()) + if (AI.isSignExt()) { Attrs.addAttribute(llvm::Attribute::SExt); - else if (AI.isZeroExt()) + } else if (AI.isZeroExt()) { Attrs.addAttribute(llvm::Attribute::ZExt); - else + } else { Attrs.addAttribute(llvm::Attribute::NoExt); + if (getTriple().isAArch64() && getTriple().isLittleEndian()) { + uint64_t Size = getContext().getTypeSize(ParamType); + Attrs.addAttribute("bitwidth", std::to_string(Size)); + } + } [[fallthrough]]; case ABIArgInfo::TargetSpecific: case ABIArgInfo::Direct: diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index 2d73dd8cc2916..84edbf126f188 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -504,6 +504,11 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, std::max(getContext().getTypeAlign(Ty), (unsigned)getTarget().getPointerWidth(LangAS::Default)); } + if (Size < getDataLayout().getPointerSizeInBits() && + getDataLayout().isLittleEndian()) + return ABIArgInfo::getNoExtend(llvm::IntegerType::get( + getVMContext(), llvm::alignTo(Size, Alignment))); + Size = llvm::alignTo(Size, Alignment); // If the Aggregate is made up of pointers, use an array of pointers for the diff --git a/clang/test/CodeGen/AArch64/args.cpp b/clang/test/CodeGen/AArch64/args.cpp index 3b5ea2d7f5526..073cd29ca4a04 100644 --- a/clang/test/CodeGen/AArch64/args.cpp +++ b/clang/test/CodeGen/AArch64/args.cpp @@ -17,7 +17,7 @@ struct Empty {}; // DARWIN: define{{.*}} i32 @empty_arg(i32 noundef %a) // C: define{{.*}} i32 @empty_arg(i32 noundef %a) -// CXX: define{{.*}} i32 @empty_arg(i64 %e.coerce, i32 noundef %a) +// CXX: define{{.*}} i32 @empty_arg(i64 noext "bitwidth"="8" %e.coerce, i32 noundef %a) EXTERNC int empty_arg(struct Empty e, int a) { return a; } diff --git a/clang/test/CodeGen/AArch64/soft-float-abi.c b/clang/test/CodeGen/AArch64/soft-float-abi.c index 0e1188117c21c..9efe99331ee9e 100644 --- a/clang/test/CodeGen/AArch64/soft-float-abi.c +++ b/clang/test/CodeGen/AArch64/soft-float-abi.c @@ -24,7 +24,7 @@ long double test4(long double a) { return a; } struct A { float x; }; -// SOFT: define dso_local i32 @test10(i64 %a.coerce) +// SOFT: define dso_local i32 @test10(i64 noext "bitwidth"="32" %a.coerce) // HARD: define dso_local %struct.A @test10([1 x float] alignstack(8) %a.coerce) struct A test10(struct A a) { return a; } diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp index d00d64c6e84d2..be5ed88ae56e6 100644 --- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -575,7 +575,7 @@ void TSpp_align16(SSpp_align16 s) { *s.a.x = 1; } struct Sempty { }; // CHECK-A64-LABEL: define dso_local void @_Z6Tempty6Sempty( -// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: i64 noext "bitwidth"="8" [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1 // CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0 @@ -584,7 +584,7 @@ struct Sempty { // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z6Tempty6Sempty( -// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 noext "bitwidth"="8" [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1 // CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0 diff --git a/clang/test/CodeGen/arm64-arguments.c b/clang/test/CodeGen/arm64-arguments.c index 4a94841759bf8..bf3f11608c829 100644 --- a/clang/test/CodeGen/arm64-arguments.c +++ b/clang/test/CodeGen/arm64-arguments.c @@ -149,7 +149,8 @@ struct s30 f30() {} struct s31 { char x; }; void f31(struct s31 s) { } -// CHECK: define{{.*}} void @f31(i64 %s.coerce) +// CHECK-LE: define{{.*}} void @f31(i64 noext "bitwidth"="8" %s.coerce) +// CHECK-BE: define{{.*}} void @f31(i64 %s.coerce) // CHECK: %s = alloca %struct.s31, align 1 // CHECK-BE: %coerce.highbits = lshr i64 %s.coerce, 56 // CHECK-BE: trunc i64 %coerce.highbits to i8 @@ -171,7 +172,8 @@ void g34(struct s34 *s) { f34(*s); } // CHECK: @g34(ptr noundef %s) // CHECK: %[[a:.*]] = load i8, ptr %{{.*}} // CHECK: zext i8 %[[a]] to i64 -// CHECK: call void @f34(i64 %{{.*}}) +// CHECK-LE: call void @f34(i64 noext "bitwidth"="8" %{{.*}}) +// CHECK-BE: call void @f34(i64 %{{.*}}) /* * Check that va_arg accesses stack according to ABI alignment diff --git a/clang/test/CodeGen/arm64-microsoft-arguments.cpp b/clang/test/CodeGen/arm64-microsoft-arguments.cpp index 14707d9814cbb..ab7fa9da1cf29 100644 --- a/clang/test/CodeGen/arm64-microsoft-arguments.cpp +++ b/clang/test/CodeGen/arm64-microsoft-arguments.cpp @@ -57,7 +57,7 @@ S4 f4() { // Pass and return from instance method called from instance method. // CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(ptr {{[^,]*}} %this, ptr dead_on_unwind inreg noalias writable sret(%class.P1) align 1 %agg.result) -// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i64 %coerce.val.ii) +// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i64 noext "bitwidth"="8" %coerce.val.ii) class P1 { public: @@ -76,7 +76,7 @@ P1 Q1::bar() { // Pass and return from instance method called from free function. // CHECK: define {{.*}} void {{.*}}bar{{.*}}() -// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i64 %coerce.val.ii) +// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i64 noext "bitwidth"="8" %coerce.val.ii) class P2 { public: P2 foo(P2 x); diff --git a/clang/test/CodeGen/attr-noundef.cpp b/clang/test/CodeGen/attr-noundef.cpp index f90d5a4355fb5..9f33ae19b85af 100644 --- a/clang/test/CodeGen/attr-noundef.cpp +++ b/clang/test/CodeGen/attr-noundef.cpp @@ -18,7 +18,7 @@ void pass_trivial(Trivial e) {} // CHECK-INTEL: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial // CHECK-AARCH: [[DEF:define( dso_local)?]] i32 @{{.*}}ret_trivial // CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 % -// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 % +// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 noext "bitwidth"="32" % struct NoCopy { int a; @@ -51,7 +51,7 @@ void pass_trivial(Trivial e) {} // CHECK-INTEL: [[DEF]] i32 @{{.*}}ret_trivial // CHECK-AARCH: [[DEF]] i32 @{{.*}}ret_trivial // CHECK-INTEL: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i32 % -// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 % +// CHECK-AARCH: [[DEF]] void @{{.*}}pass_trivial{{.*}}(i64 noext "bitwidth"="32" % union NoCopy { int a; diff --git a/clang/test/CodeGenCXX/aarch64-arguments.cpp b/clang/test/CodeGenCXX/aarch64-arguments.cpp index 3206e38ad0090..8fa6d1db668f5 100644 --- a/clang/test/CodeGenCXX/aarch64-arguments.cpp +++ b/clang/test/CodeGenCXX/aarch64-arguments.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -triple arm64-none-linux -emit-llvm -w -o - %s | FileCheck -check-prefix=PCS %s -// PCS: define{{.*}} void @{{.*}}(i64 %a.coerce) +// PCS: define{{.*}} void @{{.*}}(i64 noext "bitwidth"="8" %a.coerce) struct s0 {}; void f0(s0 a) {} diff --git a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp index ef0e2da3effac..b69c0b1494c3d 100644 --- a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp +++ b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp @@ -7,7 +7,7 @@ void test_extensions(bool a, char b, short c) {} struct Empty {}; void test_empty(Empty e) {} -// CHECK: define{{.*}} void @_Z10test_empty5Empty(i64 %e.coerce) +// CHECK: define{{.*}} void @_Z10test_empty5Empty(i64 noext "bitwidth"="8" %e.coerce) // CHECK-DARWIN: define{{.*}} void @_Z10test_empty5Empty() struct HFA { diff --git a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp index 9adf0c6b1c0d0..60bbeed4e8fed 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -185,7 +185,7 @@ void small_arg_with_dtor(SmallWithDtor s) {} // WIN64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %s.coerce) {{.*}} { // WIN64: call void @"??1SmallWithDtor@@QEAA@XZ" // WIN64: } -// WOA64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i64 %s.coerce) {{.*}} { +// WOA64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i64 noext "bitwidth"="32" %s.coerce) {{.*}} { // WOA64: call void @"??1SmallWithDtor@@QEAA@XZ"(ptr {{[^,]*}} %s) // WOA64: } @@ -197,10 +197,10 @@ void small_arg_with_dtor(SmallWithDtor s) {} // Test that the eligible non-aggregate is passed directly, but returned // indirectly on ARM64 Windows. -// WOA64: define dso_local void @"?small_arg_with_private_member@@YA?AUSmallWithPrivate@@U1@@Z"(ptr dead_on_unwind inreg noalias writable sret(%struct.SmallWithPrivate) align 4 %agg.result, i64 %s.coerce) {{.*}} { +// WOA64: define dso_local void @"?small_arg_with_private_member@@YA?AUSmallWithPrivate@@U1@@Z"(ptr dead_on_unwind inreg noalias writable sret(%struct.SmallWithPrivate) align 4 %agg.result, i64 noext "bitwidth"="32" %s.coerce) {{.*}} { SmallWithPrivate small_arg_with_private_member(SmallWithPrivate s) { return s; } -// WOA64: define dso_local i32 @"?small_arg_with_small_struct_with_private_member@@YA?AUSmallWithSmallWithPrivate@@U1@@Z"(i64 %s.coerce) {{.*}} { +// WOA64: define dso_local i32 @"?small_arg_with_small_struct_with_private_member@@YA?AUSmallWithSmallWithPrivate@@U1@@Z"(i64 noext "bitwidth"="32" %s.coerce) {{.*}} { // WIN64: define dso_local i32 @"?small_arg_with_small_struct_with_private_member@@YA?AUSmallWithSmallWithPrivate@@U1@@Z"(i32 %s.coerce) {{.*}} { SmallWithSmallWithPrivate small_arg_with_small_struct_with_private_member(SmallWithSmallWithPrivate s) { return s; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index effa4a8d5f1b9..22c3bfd435472 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4017,6 +4017,35 @@ void SelectionDAGBuilder::visitTrunc(const User &I) { setValue(&I, DAG.getNode(ISD::TRUNCATE, getCurSDLoc(), DestVT, N, Flags)); } +static bool mayUseAnyExtend(const Triple &T, const Value *V) { + if (!T.isAArch64() || !V->getType()->isIntegerTy()) + return false; + const unsigned VSize = V->getType()->getIntegerBitWidth(); + for (const Use &U : V->uses()) { + const auto *CB = dyn_cast<CallBase>(U.getUser()); + if (!CB || !CB->isArgOperand(&U)) + return false; + + const unsigned ArgNo = CB->getArgOperandNo(&U); + + if (!CB->paramHasAttr(ArgNo, Attribute::NoExt)) + return false; + + Attribute BWAttr = CB->getParamAttr(ArgNo, "bitwidth"); + if (!BWAttr.isValid()) + return false; + + unsigned BitWidth = 0; + if (BWAttr.getValueAsString().getAsInteger(/*Radix=*/10, BitWidth)) + return false; + + if (BitWidth > VSize) + return false; + } + + return true; +} + void SelectionDAGBuilder::visitZExt(const User &I) { // ZExt cannot be a no-op cast because sizeof(src) < sizeof(dest). // ZExt also can't be a cast to bool for same reason. So, nothing much to do @@ -4037,7 +4066,9 @@ void SelectionDAGBuilder::visitZExt(const User &I) { return; } - setValue(&I, DAG.getNode(ISD::ZERO_EXTEND, getCurSDLoc(), DestVT, N, Flags)); + bool UseAnyExt = mayUseAnyExtend(DAG.getTarget().getTargetTriple(), &I); + setValue(&I, DAG.getNode(UseAnyExt ? ISD::ANY_EXTEND : ISD::ZERO_EXTEND, + getCurSDLoc(), DestVT, N, Flags)); } void SelectionDAGBuilder::visitSExt(const User &I) { diff --git a/llvm/test/CodeGen/AArch64/small-struct.ll b/llvm/test/CodeGen/AArch64/small-struct.ll new file mode 100644 index 0000000000000..93fecd8598d45 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/small-struct.ll @@ -0,0 +1,27 @@ +; RUN: llc -filetype=asm -O3 -asm-verbose=false %s -o - | FileCheck %s +; CHECK: main: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: str x30, [sp, #-16]! +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: .cfi_offset w30, -16 +; CHECK-NEXT: bl _Z5getU4v +; CHECK-NEXT: ldr x30, [sp], #16 +; CHECK-NEXT: b _Z3bar2U4 + + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32" +target triple = "aarch64-linux-gnu" + +; Function Attrs: mustprogress norecurse uwtable(sync) +define dso_local noundef i32 @main() local_unnamed_addr { +entry: + %call = tail call i32 @_Z5getU4v() + %coerce.val.ii = zext i32 %call to i64 + %call2 = tail call noundef i32 @_Z3bar2U4(i64 noext "bitwidth"="32" %coerce.val.ii) + ret i32 %call2 +} + +declare dso_local noext i32 @_Z5getU4v() local_unnamed_addr + +declare dso_local noundef i32 @_Z3bar2U4(i64 noext) local_unnamed_addr + `````````` </details> https://github.com/llvm/llvm-project/pull/214788 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
