https://github.com/adams381 created https://github.com/llvm/llvm-project/pull/191257
Replace errorNYI for isParamDestroyedInCallee with working implementation: create aggregate temp, mark externally destructed, emit expr. Unblocks [[trivial_abi]] types on Itanium ABI. Adds trivial-abi.cpp test covering 17 cases from CodeGenCXX/trivial_abi.cpp with CIR/LLVM/OGCG checks. Made with [Cursor](https://cursor.com) >From 0d65ceb769e5443a42544991533a34f3c072c67e Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 7 Apr 2026 15:30:13 -0700 Subject: [PATCH] [CIR] Handle callee-destructed params and add trivial_abi test Replace errorNYI for isParamDestroyedInCallee() with working implementation: create aggregate temp, mark externally destructed, emit expr. This unblocks [[trivial_abi]] types on Itanium ABI. Add clang/test/CIR/CodeGen/trivial-abi.cpp covering all 17 cases from CodeGenCXX/trivial_abi.cpp: param/return small+large, call-site copy, ignored return, trivial/non-trivial return, exception cases, GH93040 packed placement-new, and PR42961 lambda __invoke. CIR/LLVM/OGCG checks for every function. --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 23 +- clang/test/CIR/CodeGen/trivial-abi.cpp | 362 +++++++++++++++++++++++++ 2 files changed, 380 insertions(+), 5 deletions(-) create mode 100644 clang/test/CIR/CodeGen/trivial-abi.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 876fef687b477..ee89fdf0fd653 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1311,13 +1311,26 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e, bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType); - // In the Microsoft C++ ABI, aggregate arguments are destructed by the callee. - // However, we still have to push an EH-only cleanup in case we unwind before - // we make it to the call. + // For callee-destructed parameters (trivial_abi, MS ABI), create an + // aggregate temp and let the callee destroy it. if (argType->isRecordType() && argType->castAsRecordDecl()->isParamDestroyedInCallee()) { - assert(!cir::MissingFeatures::msabi()); - cgm.errorNYI(e->getSourceRange(), "emitCallArg: msabi is NYI"); + AggValueSlot slot = createAggTemp(argType, getLoc(e->getSourceRange()), + getCounterAggTmpAsString()); + + bool destroyedInCallee = true; + if (const auto *rd = argType->getAsCXXRecordDecl()) + destroyedInCallee = rd->hasNonTrivialDestructor(); + + if (destroyedInCallee) + slot.setExternallyDestructed(); + + emitAggExpr(e, slot); + RValue rv = slot.asRValue(); + args.add(rv, argType); + + assert(!cir::MissingFeatures::ehCleanupScope()); + return; } if (hasAggregateEvalKind && isa<ImplicitCastExpr>(e) && diff --git a/clang/test/CIR/CodeGen/trivial-abi.cpp b/clang/test/CIR/CodeGen/trivial-abi.cpp new file mode 100644 index 0000000000000..0d942f865bb3c --- /dev/null +++ b/clang/test/CIR/CodeGen/trivial-abi.cpp @@ -0,0 +1,362 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// All 17 cases from clang/test/CodeGenCXX/trivial_abi.cpp, adapted for +// x86_64-unknown-linux-gnu with CIR/LLVM/OGCG checks. + +struct __attribute__((trivial_abi)) Small { + int *p; + Small(); + ~Small(); + Small(const Small &) noexcept; + Small &operator=(const Small &); +}; + +struct __attribute__((trivial_abi)) Large { + int *p; + int a[128]; + Large(); + ~Large(); + Large(const Large &) noexcept; + Large &operator=(const Large &); +}; + +struct Trivial { int a; }; + +struct NonTrivial { + NonTrivial(); + ~NonTrivial(); + int a; +}; + +// --- Case 17: PR42961 lambda returning Small via __invoke --- +// CIR emits lambda internals before user functions, so this CIR check +// must come first to match CIR emission order. + +Small (*fp)() = []() -> Small { return Small(); }; + +// CIR-LABEL: cir.func {{.*}} @{{.*}}__invokeEv +// CIR: cir.call @{{.*}}clEv + +// --- Case 1: D0::m0 thunk returning trivial_abi Small --- + +struct B0 { virtual Small m0(); }; +struct B1 { virtual Small m0(); }; +struct D0 : B0, B1 { Small m0() override; }; +Small D0::m0() { return {}; } + +// CIR-LABEL: cir.func {{.*}} @_ZThn8_N2D02m0Ev +// CIR: cir.call @_ZN2D02m0Ev + +// --- Case 2: testParamSmall --- + +void testParamSmall(Small a) noexcept {} + +// CIR-LABEL: cir.func {{.*}} @_Z14testParamSmall5Small +// CIR: cir.return + +// --- Case 3: testReturnSmall --- + +Small testReturnSmall() { + Small t; + return t; +} + +// CIR-LABEL: cir.func {{.*}} @_Z15testReturnSmallv +// CIR: cir.call @_ZN5SmallC1Ev + +// --- Case 4: testCallSmall0 (local copy + callee-destructed param) --- + +void testCallSmall0() { + Small t; + testParamSmall(t); +} + +// CIR-LABEL: cir.func {{.*}} @_Z14testCallSmall0v +// CIR: cir.call @_ZN5SmallC1Ev +// CIR: cir.call @_ZN5SmallC1ERKS_ +// CIR: cir.call @_Z14testParamSmall5Small + +// --- Case 5: testCallSmall1 (pass returned value directly) --- + +void testCallSmall1() { + testParamSmall(testReturnSmall()); +} + +// CIR-LABEL: cir.func {{.*}} @_Z14testCallSmall1v +// CIR: cir.call @_Z15testReturnSmallv +// CIR: cir.call @_Z14testParamSmall5Small + +// --- Case 6: testIgnoredSmall (discard return, must destruct) --- + +void testIgnoredSmall() { + testReturnSmall(); +} + +// CIR-LABEL: cir.func {{.*}} @_Z16testIgnoredSmallv +// CIR: cir.call @_Z15testReturnSmallv +// CIR: cir.call @_ZN5SmallD1Ev + +// --- Case 7: testParamLarge --- + +void testParamLarge(Large a) noexcept {} + +// CIR-LABEL: cir.func {{.*}} @_Z14testParamLarge5Large +// CIR: cir.return + +// --- Case 8: testReturnLarge --- + +Large testReturnLarge() { + Large t; + return t; +} + +// CIR-LABEL: cir.func {{.*}} @_Z15testReturnLargev +// CIR: cir.call @_ZN5LargeC1Ev + +// --- Case 9: testCallLarge0 (local copy + callee-destructed param) --- + +void testCallLarge0() { + Large t; + testParamLarge(t); +} + +// CIR-LABEL: cir.func {{.*}} @_Z14testCallLarge0v +// CIR: cir.call @_ZN5LargeC1Ev +// CIR: cir.call @_ZN5LargeC1ERKS_ +// CIR: cir.call @_Z14testParamLarge5Large + +// --- Case 10: testCallLarge1 (pass returned value directly) --- + +void testCallLarge1() { + testParamLarge(testReturnLarge()); +} + +// CIR-LABEL: cir.func {{.*}} @_Z14testCallLarge1v +// CIR: cir.call @_Z15testReturnLargev +// CIR: cir.call @_Z14testParamLarge5Large + +// --- Case 11: testIgnoredLarge (discard return, must destruct) --- + +void testIgnoredLarge() { + testReturnLarge(); +} + +// CIR-LABEL: cir.func {{.*}} @_Z16testIgnoredLargev +// CIR: cir.call @_Z15testReturnLargev +// CIR: cir.call @_ZN5LargeD1Ev + +// --- Case 12: testReturnHasTrivial --- + +Trivial testReturnHasTrivial() { + Trivial t; + return t; +} + +// CIR-LABEL: cir.func {{.*}} @_Z20testReturnHasTrivialv +// CIR: cir.return + +// --- Case 13: testReturnHasNonTrivial --- + +NonTrivial testReturnHasNonTrivial() { + NonTrivial t; + return t; +} + +// CIR-LABEL: cir.func {{.*}} @_Z23testReturnHasNonTrivialv +// CIR: cir.call @_ZN10NonTrivialC1Ev + +// --- Case 14: testExceptionSmall --- +// CIR does not emit invoke/landingpad; the non-unwinding call sequence +// is tested here. OGCG verifies the full EH path. + +void calleeExceptionSmall(Small, Small); + +void testExceptionSmall() { + calleeExceptionSmall(Small(), Small()); +} + +// CIR-LABEL: cir.func {{.*}} @_Z18testExceptionSmallv +// CIR: cir.call @_ZN5SmallC1Ev +// CIR: cir.call @_ZN5SmallC1Ev +// CIR: cir.call @_Z20calleeExceptionSmall5SmallS_ + +// --- Case 15: testExceptionLarge --- + +void calleeExceptionLarge(Large, Large); + +void testExceptionLarge() { + calleeExceptionLarge(Large(), Large()); +} + +// CIR-LABEL: cir.func {{.*}} @_Z18testExceptionLargev +// CIR: cir.call @_ZN5LargeC1Ev +// CIR: cir.call @_ZN5LargeC1Ev +// CIR: cir.call @_Z20calleeExceptionLarge5LargeS_ + +// --- Case 16: GH93040 packed trivial_abi with placement new --- + +void* operator new(unsigned long, void*); +namespace GH93040 { +struct [[clang::trivial_abi]] S { + char a; + int x; + __attribute((aligned(2))) char y; + S(); +} __attribute((packed)); +S f(); +void g(S* s) { new(s) S(f()); } +} + +// CIR-LABEL: cir.func {{.*}} @_ZN7GH930401gEPNS_1SE +// CIR: cir.call @_ZN7GH930401fEv + +// ======================================================================= +// LLVM checks (CIR -> LLVM lowering output) +// ======================================================================= + +// LLVM emits lambda __invoke early (internal linkage). +// LLVM-LABEL: define {{.*}} @{{.*}}__invokeEv +// LLVM: call {{.*}} @{{.*}}clEv + +// LLVM-LABEL: define {{.*}} @_ZN2D02m0Ev( +// LLVM-LABEL: define {{.*}} @_ZThn8_N2D02m0Ev( +// LLVM: getelementptr i8, ptr {{.*}}, i64 -8 +// LLVM: call {{.*}} @_ZN2D02m0Ev( + +// LLVM-LABEL: define {{.*}} void @_Z14testParamSmall5Small( +// LLVM: ret void + +// LLVM-LABEL: define {{.*}} @_Z15testReturnSmallv( +// LLVM: call void @_ZN5SmallC1Ev( + +// LLVM-LABEL: define {{.*}} void @_Z14testCallSmall0v( +// LLVM: call void @_ZN5SmallC1Ev( +// LLVM: call void @_ZN5SmallC1ERKS_( +// LLVM: call void @_Z14testParamSmall5Small( + +// LLVM-LABEL: define {{.*}} void @_Z14testCallSmall1v( +// LLVM: call {{.*}} @_Z15testReturnSmallv( +// LLVM: call void @_Z14testParamSmall5Small( + +// LLVM-LABEL: define {{.*}} void @_Z16testIgnoredSmallv( +// LLVM: call {{.*}} @_Z15testReturnSmallv( +// LLVM: call void @_ZN5SmallD1Ev( + +// LLVM-LABEL: define {{.*}} void @_Z14testParamLarge5Large( +// LLVM: ret void + +// LLVM-LABEL: define {{.*}} @_Z15testReturnLargev( +// LLVM: call void @_ZN5LargeC1Ev( + +// LLVM-LABEL: define {{.*}} void @_Z14testCallLarge0v( +// LLVM: call void @_ZN5LargeC1Ev( +// LLVM: call void @_ZN5LargeC1ERKS_( +// LLVM: call void @_Z14testParamLarge5Large( + +// LLVM-LABEL: define {{.*}} void @_Z14testCallLarge1v( +// LLVM: call {{.*}} @_Z15testReturnLargev( +// LLVM: call void @_Z14testParamLarge5Large( + +// LLVM-LABEL: define {{.*}} void @_Z16testIgnoredLargev( +// LLVM: call {{.*}} @_Z15testReturnLargev( +// LLVM: call void @_ZN5LargeD1Ev( + +// LLVM-LABEL: define {{.*}} @_Z20testReturnHasTrivialv( +// LLVM: ret + +// LLVM-LABEL: define {{.*}} @_Z23testReturnHasNonTrivialv( +// LLVM: call void @_ZN10NonTrivialC1Ev( + +// LLVM-LABEL: define {{.*}} void @_Z18testExceptionSmallv( +// LLVM: call void @_ZN5SmallC1Ev( +// LLVM: call void @_ZN5SmallC1Ev( +// LLVM: call void @_Z20calleeExceptionSmall5SmallS_( + +// LLVM-LABEL: define {{.*}} void @_Z18testExceptionLargev( +// LLVM: call void @_ZN5LargeC1Ev( +// LLVM: call void @_ZN5LargeC1Ev( +// LLVM: call void @_Z20calleeExceptionLarge5LargeS_( + +// LLVM-LABEL: define {{.*}} void @_ZN7GH930401gEPNS_1SE( +// LLVM: call {{.*}} @_ZN7GH930401fEv( + +// ======================================================================= +// OGCG checks (classic codegen) +// ======================================================================= + +// OGCG-LABEL: define {{.*}} @_ZN2D02m0Ev( +// OGCG-LABEL: define {{.*}} @_ZThn8_N2D02m0Ev( +// OGCG: getelementptr inbounds i8, ptr {{.*}}, i64 -8 +// OGCG: {{.*}}call {{.*}} @_ZN2D02m0Ev( + +// OGCG-LABEL: define {{.*}} void @_Z14testParamSmall5Small(ptr %a.coerce) +// OGCG: call {{.*}} @_ZN5SmallD1Ev( +// OGCG: ret void + +// OGCG-LABEL: define {{.*}} ptr @_Z15testReturnSmallv( +// OGCG: call {{.*}} @_ZN5SmallC1Ev( + +// OGCG-LABEL: define {{.*}} void @_Z14testCallSmall0v( +// OGCG: call {{.*}} @_ZN5SmallC1Ev( +// OGCG: call {{.*}} @_ZN5SmallC1ERKS_( +// OGCG: call void @_Z14testParamSmall5Small( + +// OGCG-LABEL: define {{.*}} void @_Z14testCallSmall1v( +// OGCG: call {{.*}} @_Z15testReturnSmallv() +// OGCG: call void @_Z14testParamSmall5Small( + +// OGCG-LABEL: define {{.*}} void @_Z16testIgnoredSmallv( +// OGCG: call {{.*}} @_Z15testReturnSmallv() +// OGCG: call {{.*}} @_ZN5SmallD1Ev( + +// OGCG-LABEL: define {{.*}} void @_Z14testParamLarge5Large(ptr noundef byval(%struct.Large) align 8 %a) +// OGCG: call {{.*}} @_ZN5LargeD1Ev( +// OGCG: ret void + +// OGCG-LABEL: define {{.*}} void @_Z15testReturnLargev(ptr {{.*}}sret(%struct.Large) +// OGCG: call {{.*}} @_ZN5LargeC1Ev( + +// OGCG-LABEL: define {{.*}} void @_Z14testCallLarge0v( +// OGCG: call {{.*}} @_ZN5LargeC1Ev( +// OGCG: call {{.*}} @_ZN5LargeC1ERKS_( +// OGCG: call void @_Z14testParamLarge5Large( + +// OGCG-LABEL: define {{.*}} void @_Z14testCallLarge1v( +// OGCG: call void @_Z15testReturnLargev( +// OGCG: call void @_Z14testParamLarge5Large( + +// OGCG-LABEL: define {{.*}} void @_Z16testIgnoredLargev( +// OGCG: call void @_Z15testReturnLargev( +// OGCG: call {{.*}} @_ZN5LargeD1Ev( + +// OGCG-LABEL: define {{.*}} i32 @_Z20testReturnHasTrivialv( +// OGCG: ret i32 + +// OGCG-LABEL: define {{.*}} void @_Z23testReturnHasNonTrivialv(ptr {{.*}}sret(%struct.NonTrivial) +// OGCG: call {{.*}} @_ZN10NonTrivialC1Ev( + +// OGCG-LABEL: define {{.*}} void @_Z18testExceptionSmallv() {{.*}} personality ptr @__gxx_personality_v0 +// OGCG: call {{.*}} @_ZN5SmallC1Ev( +// OGCG: invoke {{.*}} @_ZN5SmallC1Ev( +// OGCG: call void @_Z20calleeExceptionSmall5SmallS_( +// OGCG: landingpad +// OGCG: call {{.*}} @_ZN5SmallD1Ev( + +// OGCG-LABEL: define {{.*}} void @_Z18testExceptionLargev() {{.*}} personality ptr @__gxx_personality_v0 +// OGCG: call {{.*}} @_ZN5LargeC1Ev( +// OGCG: invoke {{.*}} @_ZN5LargeC1Ev( +// OGCG: call void @_Z20calleeExceptionLarge5LargeS_( +// OGCG: landingpad +// OGCG: call {{.*}} @_ZN5LargeD1Ev( + +// OGCG-LABEL: define {{.*}} void @_ZN7GH930401gEPNS_1SE( +// OGCG: call void @_ZN7GH930401fEv(ptr {{.*}}sret( + +// Lambda __invoke comes last in OGCG (internal linkage). +// OGCG-LABEL: define {{.*}} @{{.*}}__invokeEv +// OGCG: call {{.*}} @{{.*}}clEv _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
