[clang] [CIR] Implement abstract conditional operator handling for aggregates (PR #186284)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 17:04:09 PDT 2026
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/186284
This implements AggExprEmitter::VisitAbstractConditionalOperator for CIR.
>From f0a39a19c24eff7622a2be1e61ea99128ce2b972 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 12 Mar 2026 16:07:02 -0700
Subject: [PATCH] [CIR] Implement abstract conditional operator handling for
aggregate types
This implements AggExprEmitter::VisitAbstractConditionalOperator
for CIR.
---
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 56 ++++++++++++++++++-
clang/test/CIR/CodeGen/abstract-cond.c | 53 ++++++++++++++++++
2 files changed, 107 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/abstract-cond.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 9f390fec97613..ea5a2422f4717 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -348,8 +348,60 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
VisitInitListExpr(e->getUpdater());
}
void VisitAbstractConditionalOperator(const AbstractConditionalOperator *e) {
- cgf.cgm.errorNYI(e->getSourceRange(),
- "AggExprEmitter: VisitAbstractConditionalOperator");
+ mlir::Location loc = cgf.getLoc(e->getSourceRange());
+
+ CIRGenFunction::OpaqueValueMapping binding(cgf, e);
+ CIRGenFunction::ConditionalEvaluation eval(cgf);
+
+ // Save whether the destination's lifetime is externally managed.
+ bool isExternallyDestructed = dest.isExternallyDestructed();
+ bool destructNonTrivialCStruct =
+ !isExternallyDestructed &&
+ e->getType().isDestructedType() == QualType::DK_nontrivial_c_struct;
+ isExternallyDestructed |= destructNonTrivialCStruct;
+
+ cgf.emitIfOnBoolExpr(
+ e->getCond(),
+ /*thenBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ eval.beginEvaluation();
+ {
+ CIRGenFunction::LexicalScope lexScope{cgf, loc,
+ b.getInsertionBlock()};
+ cgf.curLexScope->setAsTernary();
+ dest.setExternallyDestructed(isExternallyDestructed);
+ assert(!cir::MissingFeatures::incrementProfileCounter());
+ Visit(e->getTrueExpr());
+ cir::YieldOp::create(b, loc);
+ }
+ eval.endEvaluation();
+ },
+ loc,
+ /*elseBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ eval.beginEvaluation();
+ {
+ CIRGenFunction::LexicalScope lexScope{cgf, loc,
+ b.getInsertionBlock()};
+ cgf.curLexScope->setAsTernary();
+
+ // If the result of an agg expression is unused, then the emission
+ // of the LHS might need to create a destination slot. That's fine
+ // with us, and we can safely emit the RHS into the same slot, but
+ // we shouldn't claim that it's already being destructed.
+ dest.setExternallyDestructed(isExternallyDestructed);
+ assert(!cir::MissingFeatures::incrementProfileCounter());
+ Visit(e->getFalseExpr());
+ cir::YieldOp::create(b, loc);
+ }
+ eval.endEvaluation();
+ },
+ loc);
+
+ if (destructNonTrivialCStruct)
+ cgf.cgm.errorNYI(
+ e->getSourceRange(),
+ "Abstract conditional aggregate: destructNonTrivialCStruct");
}
void VisitChooseExpr(const ChooseExpr *e) { Visit(e->getChosenSubExpr()); }
void VisitCXXParenListInitExpr(CXXParenListInitExpr *e) {
diff --git a/clang/test/CIR/CodeGen/abstract-cond.c b/clang/test/CIR/CodeGen/abstract-cond.c
new file mode 100644
index 0000000000000..c3f4c10f25fd9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/abstract-cond.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 -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 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// ?: in "lvalue"
+struct s6 { int f0; };
+int f6(int a0, struct s6 a1, struct s6 a2) {
+ return (a0 ? a1 : a2).f0;
+}
+
+// CIR: cir.func {{.*}} @f6
+// CIR: %[[A0:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a0"
+// CIR: %[[A1:.*]] = cir.alloca !rec_s6, !cir.ptr<!rec_s6>, ["a1"
+// CIR: %[[A2:.*]] = cir.alloca !rec_s6, !cir.ptr<!rec_s6>, ["a2"
+// CIR: cir.scope {
+// CIR: %[[TMP:.*]] = cir.alloca !rec_s6, !cir.ptr<!rec_s6>, ["ref.tmp0"]
+// CIR: %[[LOAD_A0:.*]] = cir.load{{.*}} %[[A0]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[COND:.*]] = cir.cast int_to_bool %[[LOAD_A0]] : !s32i -> !cir.bool
+// CIR: cir.if %[[COND]] {
+// CIR: cir.copy %[[A1]] to %[[TMP]] : !cir.ptr<!rec_s6>
+// CIR: } else {
+// CIR: cir.copy %[[A2]] to %[[TMP]] : !cir.ptr<!rec_s6>
+// CIR: }
+// CIR: cir.get_member %[[TMP]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i>
+
+// LLVM: define {{.*}} i32 @f6
+// LLVM: %[[LOAD_A0:.*]] = load i32, ptr {{.*}}
+// LLVM: %[[COND:.*]] = icmp ne i32 %[[LOAD_A0]], 0
+// LLVM: br i1 %[[COND]], label %[[A1_PATH:.*]], label %[[A2_PATH:.*]]
+// LLVM: [[A1_PATH]]:
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[TMP:.*]], ptr {{.*}}, i64 4, i1 false)
+// LLVM: br label %[[EXIT:.*]]
+// LLVM: [[A2_PATH]]:
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[TMP]], ptr {{.*}}, i64 4, i1 false)
+// LLVM: br label %[[EXIT]]
+// LLVM: [[EXIT]]:
+// LLVM: getelementptr {{.*}}, ptr %[[TMP]], i32 0, i32 0
+
+// OGCG: define {{.*}} i32 @f6
+// OGCG: %[[LOAD_A0:.*]] = load i32, ptr {{.*}}
+// OGCG: %[[COND:.*]] = icmp ne i32 %[[LOAD_A0]], 0
+// OGCG: br i1 %[[COND]], label %[[A1_PATH:.*]], label %[[A2_PATH:.*]]
+// OGCG: [[A1_PATH]]:
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[TMP:.*]], ptr {{.*}}, i64 4, i1 false)
+// OGCG: br label %[[EXIT:.*]]
+// OGCG: [[A2_PATH]]:
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[TMP]], ptr {{.*}}, i64 4, i1 false)
+// OGCG: br label %[[EXIT]]
+// OGCG: [[EXIT]]:
+// OGCG: getelementptr {{.*}}, ptr %[[TMP]], i32 0, i32 0
More information about the cfe-commits
mailing list