Skip to content

Commit 0a9ccee

Browse files
committed
Address review feedback
1 parent bb9039f commit 0a9ccee

File tree

4 files changed

+287
-169
lines changed

4 files changed

+287
-169
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2405,34 +2405,34 @@ namespace {
24052405
std::optional<LValue> handleConditionalOperatorLValueSimpleCase(
24062406
CIRGenFunction &cgf, const AbstractConditionalOperator *e) {
24072407
const Expr *condExpr = e->getCond();
2408-
llvm::APSInt condExprInt;
2409-
if (cgf.constantFoldsToSimpleInteger(condExpr, condExprInt)) {
2410-
bool condExprBool = condExprInt.getBoolValue();
2411-
const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr();
2412-
if (!condExprBool)
2413-
std::swap(live, dead);
2414-
2415-
if (!cgf.containsLabel(dead)) {
2416-
// If the true case is live, we need to track its region.
2417-
assert(!cir::MissingFeatures::incrementProfileCounter());
2418-
assert(!cir::MissingFeatures::pgoUse());
2419-
// If a throw expression we emit it and return an undefined lvalue
2420-
// because it can't be used.
2421-
if (auto *throwExpr = dyn_cast<CXXThrowExpr>(live->IgnoreParens())) {
2422-
cgf.emitCXXThrowExpr(throwExpr);
2423-
// Return an undefined lvalue - the throw terminates execution
2424-
// so this value will never actually be used
2425-
mlir::Type elemTy = cgf.convertType(dead->getType());
2426-
mlir::Type ptrTy = cir::PointerType::get(elemTy);
2427-
mlir::Value undefPtr = cgf.getBuilder().getNullValue(
2428-
ptrTy, cgf.getLoc(throwExpr->getSourceRange()));
2429-
return cgf.makeAddrLValue(Address(undefPtr, elemTy, CharUnits::One()),
2430-
dead->getType());
2431-
}
2432-
return cgf.emitLValue(live);
2433-
}
2408+
llvm::APSInt condExprVal;
2409+
if (!cgf.constantFoldsToSimpleInteger(condExpr, condExprVal))
2410+
return std::nullopt;
2411+
2412+
const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr();
2413+
if (!condExprVal.getBoolValue())
2414+
std::swap(live, dead);
2415+
2416+
if (cgf.containsLabel(dead))
2417+
return std::nullopt;
2418+
2419+
// If the true case is live, we need to track its region.
2420+
assert(!cir::MissingFeatures::incrementProfileCounter());
2421+
assert(!cir::MissingFeatures::pgoUse());
2422+
// If a throw expression we emit it and return an undefined lvalue
2423+
// because it can't be used.
2424+
if (auto *throwExpr = dyn_cast<CXXThrowExpr>(live->IgnoreParens())) {
2425+
cgf.emitCXXThrowExpr(throwExpr);
2426+
// Return an undefined lvalue - the throw terminates execution
2427+
// so this value will never actually be used
2428+
mlir::Type elemTy = cgf.convertType(dead->getType());
2429+
mlir::Value undefPtr =
2430+
cgf.getBuilder().getNullPtr(cgf.getBuilder().getPointerTo(elemTy),
2431+
cgf.getLoc(throwExpr->getSourceRange()));
2432+
return cgf.makeAddrLValue(Address(undefPtr, elemTy, CharUnits::One()),
2433+
dead->getType());
24342434
}
2435-
return std::nullopt;
2435+
return cgf.emitLValue(live);
24362436
}
24372437

24382438
/// Emit the operand of a glvalue conditional operator. This is either a glvalue
@@ -2464,28 +2464,6 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e,
24642464
SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{};
24652465
mlir::Type yieldTy{};
24662466

2467-
auto patchVoidOrThrowSites = [&] {
2468-
if (insertPoints.empty())
2469-
return;
2470-
// If both arms are void, so be it.
2471-
if (!yieldTy)
2472-
yieldTy = VoidTy;
2473-
2474-
// Insert required yields.
2475-
for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) {
2476-
mlir::OpBuilder::InsertionGuard guard(builder);
2477-
builder.restoreInsertionPoint(toInsert);
2478-
2479-
// Block does not return: build empty yield.
2480-
if (mlir::isa<cir::VoidType>(yieldTy)) {
2481-
cir::YieldOp::create(builder, loc);
2482-
} else { // Block returns: set null yield value.
2483-
mlir::Value op0 = builder.getNullValue(yieldTy, loc);
2484-
cir::YieldOp::create(builder, loc, op0);
2485-
}
2486-
}
2487-
};
2488-
24892467
auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc,
24902468
const Expr *expr, std::optional<LValue> &resultLV) {
24912469
CIRGenFunction::LexicalScope lexScope{*this, loc, b.getInsertionBlock()};
@@ -2523,10 +2501,27 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e,
25232501
/*falseBuilder=*/
25242502
[&](mlir::OpBuilder &b, mlir::Location loc) {
25252503
emitBranch(b, loc, e->getFalseExpr(), info.rhs);
2526-
patchVoidOrThrowSites();
25272504
})
25282505
.getResult();
25292506

2507+
// If both arms are void, so be it.
2508+
if (!yieldTy)
2509+
yieldTy = VoidTy;
2510+
2511+
// Insert required yields.
2512+
for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) {
2513+
mlir::OpBuilder::InsertionGuard guard(builder);
2514+
builder.restoreInsertionPoint(toInsert);
2515+
2516+
// Block does not return: build empty yield.
2517+
if (!yieldTy) {
2518+
cir::YieldOp::create(builder, loc);
2519+
} else { // Block returns: set null yield value.
2520+
mlir::Value op0 = builder.getNullValue(yieldTy, loc);
2521+
cir::YieldOp::create(builder, loc, op0);
2522+
}
2523+
}
2524+
25302525
return info;
25312526
}
25322527

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,10 +1978,10 @@ void cir::TernaryOp::build(
19781978
result.addOperands(cond);
19791979
OpBuilder::InsertionGuard guard(builder);
19801980
Region *trueRegion = result.addRegion();
1981-
Block *trueBlock = builder.createBlock(trueRegion);
1981+
builder.createBlock(trueRegion);
19821982
trueBuilder(builder, result.location);
19831983
Region *falseRegion = result.addRegion();
1984-
Block *falseBlock = builder.createBlock(falseRegion);
1984+
builder.createBlock(falseRegion);
19851985
falseBuilder(builder, result.location);
19861986

19871987
// Get result type from whichever branch has a yield (the other may have
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fexceptions -fcxx-exceptions -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fexceptions -fcxx-exceptions -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
const int& test_cond_throw_false(bool flag) {
9+
const int a = 10;
10+
return flag ? a : throw 0;
11+
}
12+
13+
// CIR-LABEL: cir.func{{.*}} @_Z21test_cond_throw_falseb(
14+
// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["flag", init]
15+
// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init, const]
16+
// CIR: %[[TEN:.*]] = cir.const #cir.int<10> : !s32i
17+
// CIR: cir.store{{.*}} %[[TEN]], %[[A]] : !s32i, !cir.ptr<!s32i>
18+
// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] : !cir.ptr<!cir.bool>, !cir.bool
19+
// CIR: %[[RESULT:.*]] = cir.ternary(%[[FLAG_VAL]], true {
20+
// CIR: cir.yield %[[A]] : !cir.ptr<!s32i>
21+
// CIR: }, false {
22+
// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
23+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
24+
// CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i>
25+
// CIR: cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi
26+
// CIR: cir.unreachable
27+
// CIR: }) : (!cir.bool) -> !cir.ptr<!s32i>
28+
29+
// LLVM-LABEL: define{{.*}} ptr @_Z21test_cond_throw_falseb(
30+
// LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8
31+
// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr
32+
// LLVM: %[[A_ALLOCA:.*]] = alloca i32
33+
// LLVM: %[[ZEXT:.*]] = zext i1 %{{.*}} to i8
34+
// LLVM: store i8 %[[ZEXT]], ptr %[[FLAG_ALLOCA]]
35+
// LLVM: store i32 10, ptr %[[A_ALLOCA]]
36+
// LLVM: %[[LOAD:.*]] = load i8, ptr %[[FLAG_ALLOCA]]
37+
// LLVM: %[[BOOL:.*]] = trunc i8 %[[LOAD]] to i1
38+
// LLVM: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
39+
// LLVM: [[TRUE_BB]]:
40+
// LLVM: br label %[[PHI_BB:.*]]
41+
// LLVM: [[FALSE_BB]]:
42+
// LLVM: %[[EXC:.*]] = call{{.*}} ptr @__cxa_allocate_exception
43+
// LLVM: store i32 0, ptr %[[EXC]]
44+
// LLVM: call void @__cxa_throw(ptr %[[EXC]], ptr @_ZTIi
45+
// LLVM: unreachable
46+
// LLVM: [[PHI_BB]]:
47+
// LLVM: %[[PHI:.*]] = phi ptr [ %[[A_ALLOCA]], %[[TRUE_BB]] ]
48+
// LLVM: br label %[[CONT_BB:.*]]
49+
// LLVM: [[CONT_BB]]:
50+
// LLVM: store ptr %[[A_ALLOCA]], ptr %[[RET_ALLOCA]]
51+
// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]]
52+
// LLVM: ret ptr %[[RET]]
53+
54+
// OGCG-LABEL: define{{.*}} ptr @_Z21test_cond_throw_falseb(
55+
// OGCG: %{{.*}} = alloca i8
56+
// OGCG: %[[A:.*]] = alloca i32
57+
// OGCG: store i32 10, ptr %[[A]]
58+
// OGCG: %{{.*}} = load i8, ptr %{{.*}}
59+
// OGCG: %[[BOOL:.*]] = trunc i8 %{{.*}} to i1
60+
// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
61+
// OGCG: [[TRUE_BB]]:
62+
// OGCG: br label %[[END:.*]]
63+
// OGCG: [[FALSE_BB]]:
64+
// OGCG: %{{.*}} = call{{.*}} ptr @__cxa_allocate_exception
65+
// OGCG: store i32 0, ptr %{{.*}}
66+
// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi
67+
// OGCG: unreachable
68+
// OGCG: [[END]]:
69+
// OGCG: ret ptr %[[A]]
70+
71+
const int& test_cond_throw_true(bool flag) {
72+
const int a = 10;
73+
return flag ? throw 0 : a;
74+
}
75+
76+
// CIR-LABEL: cir.func{{.*}} @_Z20test_cond_throw_trueb(
77+
// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["flag", init]
78+
// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init, const]
79+
// CIR: %[[TEN:.*]] = cir.const #cir.int<10> : !s32i
80+
// CIR: cir.store{{.*}} %[[TEN]], %[[A]] : !s32i, !cir.ptr<!s32i>
81+
// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] : !cir.ptr<!cir.bool>, !cir.bool
82+
// CIR: %[[RESULT:.*]] = cir.ternary(%[[FLAG_VAL]], true {
83+
// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
84+
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
85+
// CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i>
86+
// CIR: cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi
87+
// CIR: cir.unreachable
88+
// CIR: }, false {
89+
// CIR: cir.yield %[[A]] : !cir.ptr<!s32i>
90+
// CIR: }) : (!cir.bool) -> !cir.ptr<!s32i>
91+
92+
// LLVM-LABEL: define{{.*}} ptr @_Z20test_cond_throw_trueb(
93+
// LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8
94+
// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr
95+
// LLVM: %[[A_ALLOCA:.*]] = alloca i32
96+
// LLVM: %[[ZEXT:.*]] = zext i1 %{{.*}} to i8
97+
// LLVM: store i8 %[[ZEXT]], ptr %[[FLAG_ALLOCA]]
98+
// LLVM: store i32 10, ptr %[[A_ALLOCA]]
99+
// LLVM: %[[LOAD:.*]] = load i8, ptr %[[FLAG_ALLOCA]]
100+
// LLVM: %[[BOOL:.*]] = trunc i8 %[[LOAD]] to i1
101+
// LLVM: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
102+
// LLVM: [[TRUE_BB]]:
103+
// LLVM: %[[EXC:.*]] = call{{.*}} ptr @__cxa_allocate_exception
104+
// LLVM: store i32 0, ptr %[[EXC]]
105+
// LLVM: call void @__cxa_throw(ptr %[[EXC]], ptr @_ZTIi
106+
// LLVM: unreachable
107+
// LLVM: [[FALSE_BB]]:
108+
// LLVM: br label %[[PHI_BB:.*]]
109+
// LLVM: [[PHI_BB]]:
110+
// LLVM: %[[PHI:.*]] = phi ptr [ %[[A_ALLOCA]], %[[FALSE_BB]] ]
111+
// LLVM: br label %[[CONT_BB:.*]]
112+
// LLVM: [[CONT_BB]]:
113+
// LLVM: store ptr %[[A_ALLOCA]], ptr %[[RET_ALLOCA]]
114+
// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]]
115+
// LLVM: ret ptr %[[RET]]
116+
117+
// OGCG-LABEL: define{{.*}} ptr @_Z20test_cond_throw_trueb(
118+
// OGCG: %{{.*}} = alloca i8
119+
// OGCG: %[[A:.*]] = alloca i32
120+
// OGCG: store i32 10, ptr %[[A]]
121+
// OGCG: %{{.*}} = load i8, ptr %{{.*}}
122+
// OGCG: %[[BOOL:.*]] = trunc i8 %{{.*}} to i1
123+
// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
124+
// OGCG: [[TRUE_BB]]:
125+
// OGCG: %{{.*}} = call{{.*}} ptr @__cxa_allocate_exception
126+
// OGCG: store i32 0, ptr %{{.*}}
127+
// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi
128+
// OGCG: unreachable
129+
// OGCG: [[FALSE_BB]]:
130+
// OGCG: br label %[[END:.*]]
131+
// OGCG: [[END]]:
132+
// OGCG: ret ptr %[[A]]
133+
134+
// Test constant folding with throw - compile-time true condition, dead throw in false branch
135+
const int& test_cond_const_true_throw_false() {
136+
const int a = 20;
137+
return true ? a : throw 0;
138+
}
139+
140+
// CIR-LABEL: cir.func{{.*}} @_Z32test_cond_const_true_throw_falsev(
141+
// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init, const]
142+
// CIR: %[[TWENTY:.*]] = cir.const #cir.int<20> : !s32i
143+
// CIR: cir.store{{.*}} %[[TWENTY]], %[[A]] : !s32i, !cir.ptr<!s32i>
144+
// CIR-NOT: cir.ternary
145+
// CIR-NOT: cir.throw
146+
// CIR: cir.store %[[A]]
147+
// CIR: %[[RET:.*]] = cir.load
148+
// CIR: cir.return %[[RET]] : !cir.ptr<!s32i>
149+
150+
// LLVM-LABEL: define{{.*}} ptr @_Z32test_cond_const_true_throw_falsev(
151+
// LLVM: %[[A:.*]] = alloca i32
152+
// LLVM: store i32 20, ptr %[[A]]
153+
// LLVM-NOT: br i1
154+
// LLVM-NOT: __cxa_throw
155+
// LLVM: store ptr %[[A]]
156+
// LLVM: %[[RET:.*]] = load ptr
157+
// LLVM: ret ptr %[[RET]]
158+
159+
// OGCG-LABEL: define{{.*}} ptr @_Z32test_cond_const_true_throw_falsev(
160+
// OGCG: %[[A:.*]] = alloca i32
161+
// OGCG: store i32 20, ptr %[[A]]
162+
// OGCG-NOT: br i1
163+
// OGCG-NOT: __cxa_throw
164+
// OGCG: ret ptr %[[A]]
165+
166+
// Test constant folding with throw - compile-time false condition, dead throw in true branch
167+
const int& test_cond_const_false_throw_true() {
168+
const int a = 30;
169+
return false ? throw 0 : a;
170+
}
171+
172+
// CIR-LABEL: cir.func{{.*}} @_Z32test_cond_const_false_throw_truev(
173+
// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init, const]
174+
// CIR: %[[THIRTY:.*]] = cir.const #cir.int<30> : !s32i
175+
// CIR: cir.store{{.*}} %[[THIRTY]], %[[A]] : !s32i, !cir.ptr<!s32i>
176+
// CIR-NOT: cir.ternary
177+
// CIR-NOT: cir.throw
178+
// CIR: cir.store %[[A]]
179+
// CIR: %[[RET:.*]] = cir.load
180+
// CIR: cir.return %[[RET]] : !cir.ptr<!s32i>
181+
182+
// LLVM-LABEL: define{{.*}} ptr @_Z32test_cond_const_false_throw_truev(
183+
// LLVM: %[[A:.*]] = alloca i32
184+
// LLVM: store i32 30, ptr %[[A]]
185+
// LLVM-NOT: br i1
186+
// LLVM-NOT: __cxa_throw
187+
// LLVM: store ptr %[[A]]
188+
// LLVM: %[[RET:.*]] = load ptr
189+
// LLVM: ret ptr %[[RET]]
190+
191+
// OGCG-LABEL: define{{.*}} ptr @_Z32test_cond_const_false_throw_truev(
192+
// OGCG: %[[A:.*]] = alloca i32
193+
// OGCG: store i32 30, ptr %[[A]]
194+
// OGCG-NOT: br i1
195+
// OGCG-NOT: __cxa_throw
196+
// OGCG: ret ptr %[[A]]
197+

0 commit comments

Comments
 (0)