[clang] [CIR][ABI] Handle callee-destructed params for trivial_abi (PR #191257)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 9 10:56:02 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: adams381
<details>
<summary>Changes</summary>
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)
---
Full diff: https://github.com/llvm/llvm-project/pull/191257.diff
2 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+18-5)
- (added) clang/test/CIR/CodeGen/trivial-abi.cpp (+362)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/191257
More information about the cfe-commits
mailing list