Skip to content

Commit d2c75bc

Browse files
bruteforceboylanza
authored andcommitted
[CIR][CodeGen] Insert new blocks after ThrowOp expressions (llvm#1654)
This PR fixes [Issue#1647](llvm/clangir#1647). It just takes the implementation from [`emitRethrow`](https://github.com/llvm/clangir/blob/105d898b9898d224f0baca4b161a84bdcf817617/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp#L2273C1-L2276C77) and extends the same logic to `emitThrow`. The only nitpick about the fix is same as before - we have this [redundant ScopeOp](https://github.com/llvm/clangir/blob/105d898b9898d224f0baca4b161a84bdcf817617/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp#L2298C1-L2303C37) which acts as a placeholder, so there are some redundant yield blocks in some cases. Aside that, I believe this fix is okay for now. I have added the tests from the issue to confirm everything works as intended. cc: @mmha, @bcardosolopes.
1 parent ad2159b commit d2c75bc

File tree

4 files changed

+175
-43
lines changed

4 files changed

+175
-43
lines changed

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,10 +2565,6 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator(
25652565
.create<cir::TernaryOp>(
25662566
loc, condV, /*trueBuilder=*/
25672567
[&](mlir::OpBuilder &b, mlir::Location loc) {
2568-
CIRGenFunction::LexicalScope lexScope{CGF, loc,
2569-
b.getInsertionBlock()};
2570-
CGF.currLexScope->setAsTernary();
2571-
25722568
assert(!cir::MissingFeatures::incrementProfileCounter());
25732569
eval.begin(CGF);
25742570
auto lhs = Visit(lhsExpr);
@@ -2585,10 +2581,6 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator(
25852581
},
25862582
/*falseBuilder=*/
25872583
[&](mlir::OpBuilder &b, mlir::Location loc) {
2588-
CIRGenFunction::LexicalScope lexScope{CGF, loc,
2589-
b.getInsertionBlock()};
2590-
CGF.currLexScope->setAsTernary();
2591-
25922584
assert(!cir::MissingFeatures::incrementProfileCounter());
25932585
eval.begin(CGF);
25942586
auto rhs = Visit(rhsExpr);

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,44 +2264,46 @@ mlir::Value CIRGenItaniumCXXABI::getCXXDestructorImplicitParam(
22642264
return CGF.GetVTTParameter(GD, ForVirtualBase, Delegating);
22652265
}
22662266

2267+
// The idea here is creating a separate block for the throw with an
2268+
// `UnreachableOp` as the terminator. So, we branch from the current block
2269+
// to the throw block and create a block for the remaining operations.
2270+
void insertThrowAndSplit(mlir::OpBuilder &builder, mlir::Location loc,
2271+
mlir::Value exceptionPtr,
2272+
mlir::FlatSymbolRefAttr typeInfo,
2273+
mlir::FlatSymbolRefAttr dtor) {
2274+
mlir::Block *currentBlock = builder.getInsertionBlock();
2275+
mlir::Region *region = currentBlock->getParent();
2276+
2277+
if (currentBlock->empty()) {
2278+
builder.create<cir::ThrowOp>(loc, exceptionPtr, typeInfo, dtor);
2279+
builder.create<cir::UnreachableOp>(loc);
2280+
} else {
2281+
mlir::Block *throwBlock = builder.createBlock(region);
2282+
builder.create<cir::ThrowOp>(loc, exceptionPtr, typeInfo, dtor);
2283+
builder.create<cir::UnreachableOp>(loc);
2284+
2285+
builder.setInsertionPointToEnd(currentBlock);
2286+
builder.create<cir::BrOp>(loc, throwBlock);
2287+
}
2288+
2289+
(void)builder.createBlock(region);
2290+
// This will be erased during codegen, it acts as a placeholder for the
2291+
// operations to be inserted (if any)
2292+
builder.create<cir::ScopeOp>(loc, /*scopeBuilder=*/
2293+
[&](mlir::OpBuilder &b, mlir::Location loc) {
2294+
b.create<cir::YieldOp>(loc);
2295+
});
2296+
}
2297+
22672298
void CIRGenItaniumCXXABI::emitRethrow(CIRGenFunction &CGF, bool isNoReturn) {
22682299
// void __cxa_rethrow();
22692300

22702301
if (isNoReturn) {
22712302
auto &builder = CGF.getBuilder();
22722303
assert(CGF.currSrcLoc && "expected source location");
22732304
auto loc = *CGF.currSrcLoc;
2274-
2275-
// The idea here is creating a separate block for the rethrow with an
2276-
// `UnreachableOp` as the terminator. So, we branch from the current block
2277-
// to the rethrow block and create a block for the remaining operations.
2278-
2279-
mlir::Block *currentBlock = builder.getInsertionBlock();
2280-
mlir::Region *region = currentBlock->getParent();
2281-
2282-
if (currentBlock->empty()) {
2283-
builder.create<cir::ThrowOp>(loc, mlir::Value{},
2284-
mlir::FlatSymbolRefAttr{},
2285-
mlir::FlatSymbolRefAttr{});
2286-
builder.create<cir::UnreachableOp>(loc);
2287-
} else {
2288-
mlir::Block *rethrowBlock = builder.createBlock(region);
2289-
builder.create<cir::ThrowOp>(loc, mlir::Value{},
2290-
mlir::FlatSymbolRefAttr{},
2291-
mlir::FlatSymbolRefAttr{});
2292-
builder.create<cir::UnreachableOp>(loc);
2293-
2294-
builder.setInsertionPointToEnd(currentBlock);
2295-
builder.create<cir::BrOp>(loc, rethrowBlock);
2296-
}
2297-
2298-
(void)builder.createBlock(region);
2299-
// This will be erased during codegen, it acts as a placeholder for the
2300-
// operations to be inserted (if any)
2301-
builder.create<cir::ScopeOp>(loc, /*scopeBuilder=*/
2302-
[&](mlir::OpBuilder &b, mlir::Location loc) {
2303-
b.create<cir::YieldOp>(loc);
2304-
});
2305+
insertThrowAndSplit(builder, loc, mlir::Value{}, mlir::FlatSymbolRefAttr{},
2306+
mlir::FlatSymbolRefAttr{});
23052307
} else {
23062308
llvm_unreachable("NYI");
23072309
}
@@ -2367,8 +2369,7 @@ void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &CGF,
23672369

23682370
// Now throw the exception.
23692371
mlir::Location loc = CGF.getLoc(E->getSourceRange());
2370-
builder.create<cir::ThrowOp>(loc, exceptionPtr, typeInfo.getSymbol(), dtor);
2371-
builder.create<cir::UnreachableOp>(loc);
2372+
insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo.getSymbol(), dtor);
23722373
}
23732374

23742375
mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,13 +1556,13 @@ void cir::TernaryOp::build(
15561556
result.addOperands(cond);
15571557
OpBuilder::InsertionGuard guard(builder);
15581558
Region *trueRegion = result.addRegion();
1559-
auto *block = builder.createBlock(trueRegion);
1559+
builder.createBlock(trueRegion);
15601560
trueBuilder(builder, result.location);
15611561
Region *falseRegion = result.addRegion();
15621562
builder.createBlock(falseRegion);
15631563
falseBuilder(builder, result.location);
15641564

1565-
auto yield = dyn_cast<YieldOp>(block->getTerminator());
1565+
auto yield = dyn_cast<YieldOp>(trueRegion->back().getTerminator());
15661566
assert((yield && yield.getNumOperands() <= 1) &&
15671567
"expected zero or one result type");
15681568
if (yield.getNumOperands() == 1)

clang/test/CIR/CodeGen/throw.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ double d(int a, int b) {
1616
// CIR-NEXT: cir.store{{.*}} %[[STR_ADD]], %[[ADDR]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>>
1717
// CIR-NEXT: cir.throw %[[ADDR]] : !cir.ptr<!cir.ptr<!s8i>>, @_ZTIPKc
1818
// CIR-NEXT: cir.unreachable
19+
// CIR-NEXT: ^bb1: // no predecessors
20+
// CIR-NEXT: cir.yield
1921
// CIR-NEXT: }
2022

2123
// LLVM: %[[ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 8)
@@ -293,3 +295,140 @@ void refoo4() {
293295
// LLVM: invoke void @__cxa_rethrow
294296
// LLVM: unreachable
295297
// LLVM: invoke void @_ZN1SC2Ev
298+
299+
void statements() {
300+
throw 0;
301+
123 + 456;
302+
}
303+
304+
// CIR: cir.func @_Z10statementsv()
305+
// CIR-NEXT: %[[V0:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
306+
// CIR-NEXT: %[[V1:.*]] = cir.const #cir.int<0> : !s32i
307+
// CIR-NEXT: cir.store align(16) %[[V1]], %[[V0]] : !s32i, !cir.ptr<!s32i>
308+
// CIR-NEXT: cir.throw %[[V0]] : !cir.ptr<!s32i>, @_ZTIi
309+
// CIR-NEXT: cir.unreachable
310+
// CIR-NEXT: ^bb1:
311+
// CIR-NEXT: %[[V2:.*]] = cir.const #cir.int<123> : !s32i
312+
// CIR-NEXT: %[[V3:.*]] = cir.const #cir.int<456> : !s32i
313+
// CIR-NEXT: %[[V4:.*]] = cir.binop(add, %[[V2]], %[[V3]]) nsw : !s32i
314+
// CIR-NEXT: cir.return
315+
// CIR-NEXT: }
316+
317+
// LLVM: call void @__cxa_throw
318+
// LLVM: unreachable
319+
320+
void paren_expr() { (throw 0, 123 + 456); }
321+
322+
// CIR: cir.func @_Z10paren_exprv()
323+
// CIR-NEXT: %[[V0:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
324+
// CIR-NEXT: %[[V1:.*]] = cir.const #cir.int<0> : !s32i
325+
// CIR-NEXT: cir.store align(16) %[[V1]], %[[V0]] : !s32i, !cir.ptr<!s32i>
326+
// CIR-NEXT: cir.throw %[[V0]] : !cir.ptr<!s32i>, @_ZTIi
327+
// CIR-NEXT: cir.unreachable
328+
// CIR-NEXT: ^bb1:
329+
// CIR-NEXT: %[[V2:.*]] = cir.const #cir.int<123> : !s32i
330+
// CIR-NEXT: %[[V3:.*]] = cir.const #cir.int<456> : !s32i
331+
// CIR-NEXT: %[[V4:.*]] = cir.binop(add, %[[V2]], %[[V3]]) nsw : !s32i
332+
// CIR-NEXT: cir.return
333+
// CIR-NEXT: }
334+
335+
// LLVM: call void @__cxa_throw
336+
// LLVM: unreachable
337+
338+
int ternary_throw1(bool condition, int x) {
339+
return condition ? throw x : x;
340+
}
341+
342+
// CIR: cir.func @_Z14ternary_throw1bi(%arg0: !cir.bool
343+
// CIR-NEXT: %[[V0:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["condition", init] {alignment = 1 : i64}
344+
// CIR-NEXT: %[[V1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
345+
// CIR-NEXT: %[[V2:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
346+
// CIR-NEXT: %[[V3:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"] {alignment = 1 : i64}
347+
// CIR-NEXT: %[[V4:.*]] = cir.const #false
348+
// CIR-NEXT: %[[V5:.*]] = cir.const #true
349+
// CIR-NEXT: cir.store %arg0, %[[V0]] : !cir.bool, !cir.ptr<!cir.bool>
350+
// CIR-NEXT: cir.store %arg1, %[[V1]] : !s32i, !cir.ptr<!s32i>
351+
// CIR-NEXT: %[[V6:.*]] = cir.load align(1) %[[V0]] : !cir.ptr<!cir.bool>, !cir.bool
352+
// CIR-NEXT: cir.store align(1) %[[V4]], %[[V3]] : !cir.bool, !cir.ptr<!cir.bool>
353+
// CIR-NEXT: %[[V7:.*]] = cir.ternary(%[[V6]], true {
354+
// CIR-NEXT: %[[V9:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
355+
// CIR-NEXT: cir.store align(1) %[[V5]], %[[V3]] : !cir.bool, !cir.ptr<!cir.bool>
356+
// CIR-NEXT: %[[V10:.*]] = cir.load align(4) %[[V1]] : !cir.ptr<!s32i>, !s32i
357+
// CIR-NEXT: cir.store align(16) %[[V10]], %[[V9]] : !s32i, !cir.ptr<!s32i>
358+
// CIR-NEXT: cir.throw %[[V9]] : !cir.ptr<!s32i>, @_ZTIi
359+
// CIR-NEXT: cir.unreachable
360+
// CIR-NEXT: ^bb1: // no predecessors
361+
// CIR-NEXT: %[[V11:.*]] = cir.const #cir.int<0> : !s32i loc(#loc173)
362+
// CIR-NEXT: cir.yield %[[V11]] : !s32i
363+
// CIR-NEXT: }, false {
364+
// CIR-NEXT: %[[V9:.*]] = cir.load align(4) %[[V1]] : !cir.ptr<!s32i>, !s32i
365+
// CIR-NEXT: cir.yield %[[V9]] : !s32i
366+
// CIR-NEXT: }) : (!cir.bool) -> !s32i
367+
// CIR-NEXT: cir.store %[[V7]], %[[V2]] : !s32i, !cir.ptr<!s32i>
368+
// CIR-NEXT: %[[V8:.*]] = cir.load %[[V2]] : !cir.ptr<!s32i>, !s32i
369+
// CIR-NEXT: cir.return %[[V8]] : !s32i
370+
// CIR-NEXT: }
371+
372+
// LLVM: @_Z14ternary_throw1bi
373+
// LLVM: %[[V3:.*]] = alloca i8, i64 1, align 1
374+
// LLVM: %[[V4:.*]] = alloca i32, i64 1, align 4
375+
// LLVM: %[[V5:.*]] = alloca i32, i64 1, align 4
376+
// LLVM: %[[V6:.*]] = alloca i8, i64 1, align 1
377+
// LLVM: %[[V7:.*]] = zext i1 %[[V0:.*]] to i8
378+
// LLVM: store i8 %[[V7]], ptr %[[V3]], align 1
379+
// LLVM: store i32 %[[V1:.*]], ptr %[[V4]], align 4
380+
// LLVM: %[[V8:.*]] = load i8, ptr %[[V3]], align 1
381+
// LLVM: %[[V9:.*]] = trunc i8 %[[V8]] to i1
382+
// LLVM: store i8 0, ptr %[[V6]], align 1
383+
// LLVM: br i1 %[[V9]], label %[[B10:.*]], label %[[B14:.*]]
384+
// LLVM: [[B10]]:
385+
// LLVM: %[[V11:.*]] = call ptr @__cxa_allocate_exception(i64 4)
386+
// LLVM: store i8 1, ptr %[[V6]], align 1
387+
// LLVM: %[[V12:.*]] = load i32, ptr %[[V4]], align 4
388+
// LLVM: store i32 %[[V12]], ptr %[[V11]], align 16
389+
// LLVM: call void @__cxa_throw(ptr %[[V11]], ptr @_ZTIi, ptr null)
390+
// LLVM: unreachable
391+
// LLVM: [[B13]]:
392+
// LLVM: br label %[[B16:.*]]
393+
// LLVM: [[B14]]:
394+
// LLVM: %[[V15:.*]] = load i32, ptr %[[V4]], align 4
395+
// LLVM: br label %[[B16]]
396+
// LLVM: [[B16]]:
397+
// LLVM: %[[V17:.*]] = phi i32 [ 0, %[[V13]] ], [ %[[V15]], %[[V14]] ]
398+
// LLVM: store i32 %[[V17]], ptr %[[V5]], align 4
399+
// LLVM: %[[V18:.*]] = load i32, ptr %[[V5]], align 4
400+
// LLVM: ret i32 %[[V18]]
401+
402+
int ternary_throw2(bool condition, int x) {
403+
return condition ? x : throw x;
404+
}
405+
406+
// LLVM: @_Z14ternary_throw2bi
407+
// LLVM: %[[V3:.*]] = alloca i8, i64 1, align 1
408+
// LLVM: %[[V4:.*]] = alloca i32, i64 1, align 4
409+
// LLVM: %[[V5:.*]] = alloca i32, i64 1, align 4
410+
// LLVM: %[[V6:.*]] = alloca i8, i64 1, align 1
411+
// LLVM: %[[V7:.*]] = zext i1 %[[V0:.*]] to i8
412+
// LLVM: store i8 %[[V7]], ptr %[[V3]], align 1
413+
// LLVM: store i32 %[[V1]], ptr %[[V4]], align 4
414+
// LLVM: %[[V8:.*]] = load i8, ptr %[[V3]], align 1
415+
// LLVM: %[[V9:.*]] = trunc i8 %[[V8]] to i1
416+
// LLVM: store i8 0, ptr %[[V6]], align 1
417+
// LLVM: br i1 %[[V9]], label %[[B10:.*]], label %[[B12:.*]]
418+
// LLVM: [[B10]]:
419+
// LLVM: %[[V11:.*]] = load i32, ptr %[[V4]], align 4
420+
// LLVM: br label %[[B16:.*]]
421+
// LLVM: [[B12]]:
422+
// LLVM: %[[V13:.*]] = call ptr @__cxa_allocate_exception(i64 4)
423+
// LLVM: store i8 1, ptr %[[V6]], align 1
424+
// LLVM: %[[V14:.*]] = load i32, ptr %[[V4]], align 4
425+
// LLVM: store i32 %[[V14]], ptr %[[V13]], align 16
426+
// LLVM: call void @__cxa_throw(ptr %[[V13]], ptr @_ZTIi, ptr null)
427+
// LLVM: unreachable
428+
// LLVM: [[B15:.*]]:
429+
// LLVM: br label %[[B16:.*]]
430+
// LLVM: [[B16]]:
431+
// LLVM: %[[V17:.*]] = phi i32 [ 0, %[[V15]] ], [ %[[V11]], %[[V10]] ]
432+
// LLVM: store i32 %[[V17]], ptr %[[V5]], align 4
433+
// LLVM: %[[V18:.*]] = load i32, ptr %[[V5]], align 4
434+
// LLVM: ret i32 %[[V18]]

0 commit comments

Comments
 (0)