Skip to content

Commit cef3459

Browse files
committed
[CIR][CIRGen] Properly link multiple level of cleanups
Generalize approach and be able to tie together cleanups with their matching throwing calls. Before this the dtors were not really emitted in the proper order. LLVM support for this still hits a NYI, so nothing special here on the LLVM lowering side.
1 parent fad1fd1 commit cef3459

File tree

10 files changed

+116
-75
lines changed

10 files changed

+116
-75
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3515,27 +3515,26 @@ def TryOp : CIR_Op<"try",
35153515
`synthetic`: use `cir.try` to represent try/catches not originally
35163516
present in the source code (e.g. `g = new Class` under `-fexceptions`).
35173517

3518+
`cleanup`: signal to targets (LLVM for now) that this try/catch, needs
3519+
to specially tag their landing pads as needing "cleanup".
3520+
35183521
Example: TBD
35193522
```
35203523
}];
35213524

3522-
let arguments = (ins UnitAttr:$synthetic,
3525+
let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup,
35233526
OptionalAttr<ArrayAttr>:$catch_types);
35243527
let regions = (region AnyRegion:$try_region,
3525-
AnyRegion:$cleanup_region,
35263528
VariadicRegion<AnyRegion>:$catch_regions);
35273529

35283530
let assemblyFormat = [{
3529-
(`synthetic` $synthetic^)? $try_region
3530-
`cleanup` $cleanup_region
3531+
(`synthetic` $synthetic^)?
3532+
(`cleanup` $cleanup^)?
3533+
$try_region
35313534
custom<CatchRegions>($catch_regions, $catch_types)
35323535
attr-dict
35333536
}];
35343537

3535-
let extraClassDeclaration = [{
3536-
bool isCleanupActive() { return !getCleanupRegion().empty(); }
3537-
}];
3538-
35393538
// Everything already covered elsewhere.
35403539
let hasVerifier = 0;
35413540
let builders = [

clang/lib/CIR/CodeGen/CIRGenCleanup.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ void CIRGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
158158

159159
// Check whether we need an EH cleanup. This is only true if we've
160160
// generated a lazy EH cleanup block.
161-
auto *EHEntry = Scope.getCachedEHDispatchBlock();
162-
assert(Scope.hasEHBranches() == (EHEntry != nullptr));
163-
bool RequiresEHCleanup = (EHEntry != nullptr);
161+
auto *ehEntry = Scope.getCachedEHDispatchBlock();
162+
assert(Scope.hasEHBranches() == (ehEntry != nullptr));
163+
bool RequiresEHCleanup = (ehEntry != nullptr);
164164
EHScopeStack::stable_iterator EHParent = Scope.getEnclosingEHScope();
165165

166166
// Check the three conditions which might require a normal cleanup:
@@ -300,8 +300,8 @@ void CIRGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
300300
}
301301

302302
assert(tryOp && "expected available cir.try");
303-
auto *NextAction = getEHDispatchBlock(EHParent, tryOp);
304-
(void)NextAction;
303+
auto *nextAction = getEHDispatchBlock(EHParent, tryOp);
304+
(void)nextAction;
305305

306306
// Push a terminate scope or cleanupendpad scope around the potentially
307307
// throwing cleanups. For funclet EH personalities, the cleanupendpad models
@@ -328,23 +328,34 @@ void CIRGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
328328
if (EHActiveFlag.isValid() || IsActive) {
329329
cleanupFlags.setIsForEHCleanup();
330330
mlir::OpBuilder::InsertionGuard guard(builder);
331-
if (!tryOp.isCleanupActive())
332-
builder.createBlock(&tryOp.getCleanupRegion());
333-
mlir::Block *cleanup = &tryOp.getCleanupRegion().back();
334-
if (cleanup->empty()) {
335-
builder.setInsertionPointToEnd(cleanup);
336-
builder.createYield(tryOp.getLoc());
337-
}
338331

339-
auto yield = cast<YieldOp>(cleanup->getTerminator());
332+
auto yield = cast<YieldOp>(ehEntry->getTerminator());
340333
builder.setInsertionPoint(yield);
341334
buildCleanup(*this, Fn, cleanupFlags, EHActiveFlag);
342335
}
343336

344-
// In LLVM traditional codegen, here's where it branches off to
345-
// NextAction.
346337
if (CPI)
347338
llvm_unreachable("NYI");
339+
else {
340+
// In LLVM traditional codegen, here's where it branches off to
341+
// nextAction. CIR does not have a flat layout at this point, so
342+
// instead patch all the landing pads that need to run this cleanup
343+
// as well.
344+
mlir::Block *currBlock = ehEntry;
345+
while (currBlock && cleanupsToPatch.contains(currBlock)) {
346+
mlir::OpBuilder::InsertionGuard guard(builder);
347+
mlir::Block *blockToPatch = cleanupsToPatch[currBlock];
348+
auto currYield = cast<YieldOp>(blockToPatch->getTerminator());
349+
builder.setInsertionPoint(currYield);
350+
buildCleanup(*this, Fn, cleanupFlags, EHActiveFlag);
351+
currBlock = blockToPatch;
352+
}
353+
354+
// The nextAction is yet to be populated, register that this
355+
// cleanup should also incorporate any cleanup from nextAction
356+
// when available.
357+
cleanupsToPatch[nextAction] = ehEntry;
358+
}
348359

349360
// Leave the terminate scope.
350361
if (PushedTerminate)

clang/lib/CIR/CodeGen/CIRGenException.cpp

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ static void buildCatchDispatchBlock(CIRGenFunction &CGF,
417417
// that catch-all as the dispatch block.
418418
if (catchScope.getNumHandlers() == 1 &&
419419
catchScope.getHandler(0).isCatchAll()) {
420-
assert(dispatchBlock == catchScope.getHandler(0).Block);
420+
// assert(dispatchBlock == catchScope.getHandler(0).Block);
421421
return;
422422
}
423423

@@ -721,8 +721,7 @@ mlir::Operation *CIRGenFunction::buildLandingPad(mlir::cir::TryOp tryOp) {
721721

722722
// Otherwise, signal that we at least have cleanups.
723723
} else if (hasCleanup) {
724-
if (!tryOp.isCleanupActive())
725-
builder.createBlock(&tryOp.getCleanupRegion());
724+
tryOp.setCleanup(true);
726725
}
727726

728727
assert((clauses.size() > 0 || hasCleanup) && "no catch clauses!");
@@ -739,9 +738,7 @@ mlir::Operation *CIRGenFunction::buildLandingPad(mlir::cir::TryOp tryOp) {
739738
mlir::ArrayAttr::get(builder.getContext(), clauses));
740739

741740
// In traditional LLVM codegen. this tells the backend how to generate the
742-
// landing pad by generating a branch to the dispatch block. In CIR the same
743-
// function is called to gather some state, but this block info it's not
744-
// useful per-se.
741+
// landing pad by generating a branch to the dispatch block.
745742
mlir::Block *dispatch =
746743
getEHDispatchBlock(EHStack.getInnermostEHScope(), tryOp);
747744
(void)dispatch;
@@ -772,28 +769,25 @@ CIRGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si,
772769
if (!dispatchBlock) {
773770
switch (scope.getKind()) {
774771
case EHScope::Catch: {
775-
// Apply a special case to a single catch-all.
776-
EHCatchScope &catchScope = cast<EHCatchScope>(scope);
777-
if (catchScope.getNumHandlers() == 1 &&
778-
catchScope.getHandler(0).isCatchAll()) {
779-
dispatchBlock = catchScope.getHandler(0).Block;
780-
781-
// Otherwise, make a dispatch block.
782-
} else {
783-
// As said in the function comment, just signal back we
784-
// have something - even though the block value doesn't
785-
// have any real meaning.
786-
dispatchBlock = catchScope.getHandler(0).Block;
787-
assert(dispatchBlock && "find another approach to signal");
772+
// LLVM does some optimization with branches here, CIR just keep track of
773+
// the corresponding calls.
774+
assert(callWithExceptionCtx && "expected call information");
775+
{
776+
mlir::OpBuilder::InsertionGuard guard(getBuilder());
777+
assert(callWithExceptionCtx.getCleanup().empty() &&
778+
"one per call: expected empty region at this point");
779+
dispatchBlock = builder.createBlock(&callWithExceptionCtx.getCleanup());
780+
builder.createYield(callWithExceptionCtx.getLoc());
788781
}
789782
break;
790783
}
791784

792785
case EHScope::Cleanup: {
793-
assert(tryOp && "expected cir.try available");
794786
assert(callWithExceptionCtx && "expected call information");
795787
{
796788
mlir::OpBuilder::InsertionGuard guard(getBuilder());
789+
assert(callWithExceptionCtx.getCleanup().empty() &&
790+
"one per call: expected empty region at this point");
797791
dispatchBlock = builder.createBlock(&callWithExceptionCtx.getCleanup());
798792
builder.createYield(callWithExceptionCtx.getLoc());
799793
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,7 @@ class CIRGenFunction : public CIRGenTypeCache {
17551755
mlir::cir::TryOp tryOp);
17561756
/// Unified block containing a call to cir.resume
17571757
mlir::Block *ehResumeBlock = nullptr;
1758+
llvm::DenseMap<mlir::Block *, mlir::Block *> cleanupsToPatch;
17581759

17591760
/// The cleanup depth enclosing all the cleanups associated with the
17601761
/// parameters.

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,9 +1237,6 @@ void TryOp::build(
12371237
// Try body region
12381238
Region *tryBodyRegion = result.addRegion();
12391239

1240-
// Try cleanup region
1241-
result.addRegion();
1242-
12431240
// Create try body region and set insertion point
12441241
builder.createBlock(tryBodyRegion);
12451242
tryBodyBuilder(builder, result.location);
@@ -1257,7 +1254,7 @@ void TryOp::getSuccessorRegions(mlir::RegionBranchPoint point,
12571254

12581255
// If the condition isn't constant, both regions may be executed.
12591256
regions.push_back(RegionSuccessor(&getTryRegion()));
1260-
regions.push_back(RegionSuccessor(&getCleanupRegion()));
1257+
12611258
// FIXME: optimize, ideas include:
12621259
// - If we know a target function never throws a specific type, we can
12631260
// remove the catch handler.
@@ -3004,7 +3001,7 @@ void printCallCommon(Operation *op, mlir::Value indirectCallee,
30043001
auto call = dyn_cast<mlir::cir::CallOp>(op);
30053002
assert(call && "expected regular call");
30063003
if (!call.getCleanup().empty()) {
3007-
state << "cleanup ";
3004+
state << " cleanup ";
30083005
state.printRegion(call.getCleanup());
30093006
}
30103007
}

clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,10 @@ class CIRTryOpFlattening : public mlir::OpRewritePattern<mlir::cir::TryOp> {
294294
return mlir::ArrayAttr::get(caseAttrList.getContext(), symbolList);
295295
}
296296

297-
mlir::Block *buildCatchers(mlir::cir::TryOp tryOp,
298-
mlir::PatternRewriter &rewriter,
299-
mlir::Block *afterBody,
300-
mlir::Block *afterTry) const {
297+
mlir::Block *
298+
buildCatchers(mlir::cir::TryOp tryOp, mlir::PatternRewriter &rewriter,
299+
mlir::Block *afterBody, mlir::Block *afterTry,
300+
SmallVectorImpl<mlir::cir::CallOp> &callsToRewrite) const {
301301
auto loc = tryOp.getLoc();
302302
// Replace the tryOp return with a branch that jumps out of the body.
303303
rewriter.setInsertionPointToEnd(afterBody);
@@ -317,22 +317,22 @@ class CIRTryOpFlattening : public mlir::OpRewritePattern<mlir::cir::TryOp> {
317317
mlir::ArrayAttr symlist = collectTypeSymbols(tryOp);
318318
auto inflightEh = rewriter.create<mlir::cir::EhInflightOp>(
319319
loc, exceptionPtrType, typeIdType,
320-
tryOp.isCleanupActive() ? mlir::UnitAttr::get(tryOp.getContext())
321-
: nullptr,
320+
tryOp.getCleanup() ? mlir::UnitAttr::get(tryOp.getContext()) : nullptr,
322321
symlist);
323322
auto selector = inflightEh.getTypeId();
324323
auto exceptionPtr = inflightEh.getExceptionPtr();
325324

326325
// Time to emit cleanup's.
327-
if (tryOp.isCleanupActive()) {
328-
assert(tryOp.getCleanupRegion().getBlocks().size() == 1 &&
326+
if (tryOp.getCleanup()) {
327+
assert(callsToRewrite.size() == 1 &&
329328
"NYI: if this isn't enough, move region instead");
330329
// TODO(cir): this might need to be duplicated instead of consumed since
331330
// for user-written try/catch we want these cleanups to also run when the
332331
// regular try scope adjurns (in case no exception is triggered).
333332
assert(tryOp.getSynthetic() &&
334333
"not implemented for user written try/catch");
335-
mlir::Block *cleanupBlock = &tryOp.getCleanupRegion().getBlocks().back();
334+
mlir::Block *cleanupBlock =
335+
&callsToRewrite[0].getCleanup().getBlocks().back();
336336
auto cleanupYield =
337337
cast<mlir::cir::YieldOp>(cleanupBlock->getTerminator());
338338
cleanupYield->erase();
@@ -465,7 +465,7 @@ class CIRTryOpFlattening : public mlir::OpRewritePattern<mlir::cir::TryOp> {
465465

466466
// Build catchers.
467467
mlir::Block *landingPad =
468-
buildCatchers(tryOp, rewriter, afterBody, afterTry);
468+
buildCatchers(tryOp, rewriter, afterBody, afterTry, callsToRewrite);
469469
rewriter.eraseOp(tryOp);
470470

471471
// Rewrite calls.

clang/test/CIR/CodeGen/global-new.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ e *g = new e(0);
2727
// CIR_AFTER: {{%.*}} = cir.const #cir.int<1> : !u64i
2828
// CIR_AFTER: {{%.*}} = cir.call @_Znwm(%1) : (!u64i) -> !cir.ptr<!void>
2929

30-
// CIR_EH: cir.try synthetic {
31-
// CIR_EH: cir.call exception @_ZN1eC1Ei
32-
// CIR_EH: cir.yield
33-
// CIR_EH: } cleanup {
34-
// CIR_EH: cir.call @_ZdlPvm
30+
// CIR_EH: cir.try synthetic cleanup {
31+
// CIR_EH: cir.call exception @_ZN1eC1Ei{{.*}} cleanup {
32+
// CIR_EH: cir.call @_ZdlPvm
33+
// CIR_EH: cir.yield
34+
// CIR_EH: }
3535
// CIR_EH: cir.yield
3636
// CIR_EH: } catch [#cir.unwind {
3737
// CIR_EH: cir.resume

clang/test/CIR/CodeGen/paren-list-init.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ void make1() {
4343
// CIR_EH: cir.scope {
4444
// CIR_EH: %1 = cir.alloca ![[S1]], !cir.ptr<![[S1]]>, ["agg.tmp.ensured"]
4545
// CIR_EH: %2 = cir.get_member %1[0] {name = "v"} : !cir.ptr<![[S1]]> -> !cir.ptr<![[VecType]]>
46-
// CIR_EH: cir.try synthetic {
46+
// CIR_EH: cir.try synthetic cleanup {
4747

4848
// Call v move ctor
49-
// CIR_EH: cir.call exception @_ZN3VecC1EOS_(%2, %[[VEC]]) : (!cir.ptr<![[VecType]]>, !cir.ptr<![[VecType]]>) -> ()
50-
// CIR_EH: cir.yield
51-
// CIR_EH: } cleanup {
49+
// CIR_EH: cir.call exception @_ZN3VecC1EOS_{{.*}} cleanup {
5250

5351
// Destroy v after v move ctor throws
54-
// CIR_EH: cir.call @_ZN3VecD1Ev(%[[VEC]]) : (!cir.ptr<![[VecType]]>) -> ()
52+
// CIR_EH: cir.call @_ZN3VecD1Ev(%[[VEC]])
53+
// CIR_EH: cir.yield
54+
// CIR_EH: }
5555
// CIR_EH: cir.yield
5656
// CIR_EH: } catch [#cir.unwind {
5757
// CIR_EH: cir.resume

clang/test/CIR/CodeGen/try-catch-dtors.cpp

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ void yo() {
2727
// CIR: cir.call exception @_ZN3VecC1Ev(%[[VADDR]]) : (!cir.ptr<![[VecTy]]>) -> ()
2828
// CIR: cir.call @_ZN3VecD1Ev(%[[VADDR]]) : (!cir.ptr<![[VecTy]]>) -> ()
2929
// CIR: cir.yield
30-
// CIR: } cleanup {
3130
// CIR: } catch [type #cir.all {
3231
// CIR: cir.catch_param -> !cir.ptr<!void>
3332
// CIR: }]
@@ -75,6 +74,16 @@ void yo2() {
7574
r++;
7675
}
7776
}
77+
78+
void yo3(bool x) {
79+
int r = 1;
80+
try {
81+
Vec v1, v2, v3, v4;
82+
} catch (...) {
83+
r++;
84+
}
85+
}
86+
7887
#endif
7988

8089
// CIR: cir.func @_Z3yo2v()
@@ -84,18 +93,49 @@ void yo2() {
8493
// CIR: cir.call exception @_ZN3VecC1Ev
8594
// CIR: cir.scope {
8695
// CIR: cir.alloca ![[S1:.*]], !cir.ptr<![[S1:.*]]>, ["agg.tmp.ensured"]
87-
// CIR: cir.call exception @_ZN3VecC1EOS_
96+
// CIR: cir.call exception @_ZN3VecC1EOS_{{.*}} cleanup {
97+
// CIR: cir.call @_ZN3VecD1Ev
98+
// CIR: cir.yield
8899
// CIR: cir.call @_ZN2S1D2Ev
89100
// CIR: }
90101
// CIR: cir.call @_ZN3VecD1Ev
91102
// CIR: cir.yield
92-
// CIR: } cleanup {
93-
// CIR: cir.call @_ZN3VecD1Ev
94-
// CIR: cir.yield
95103
// CIR: } catch [type #cir.all {
96104
// CIR: cir.catch_param -> !cir.ptr<!void>
97105
// CIR: cir.yield
98106
// CIR: }]
99107
// CIR: }
100108
// CIR: cir.return
101-
// CIR: }
109+
// CIR: }
110+
111+
// CIR: cir.scope {
112+
// CIR: %[[V1:.*]] = cir.alloca ![[VecTy]], !cir.ptr<![[VecTy]]>, ["v1"
113+
// CIR: %[[V2:.*]] = cir.alloca ![[VecTy]], !cir.ptr<![[VecTy]]>, ["v2"
114+
// CIR: %[[V3:.*]] = cir.alloca ![[VecTy]], !cir.ptr<![[VecTy]]>, ["v3"
115+
// CIR: %[[V4:.*]] = cir.alloca ![[VecTy]], !cir.ptr<![[VecTy]]>, ["v4"
116+
// CIR: cir.try {
117+
// CIR: cir.call exception @_ZN3VecC1Ev(%[[V1]]) : (!cir.ptr<![[VecTy]]>) -> ()
118+
// CIR: cir.call exception @_ZN3VecC1Ev(%[[V2]]) : (!cir.ptr<![[VecTy]]>) -> () cleanup {
119+
// CIR: cir.call @_ZN3VecD1Ev(%[[V1]]) : (!cir.ptr<![[VecTy]]>) -> ()
120+
// CIR: cir.yield
121+
// CIR: }
122+
// CIR: cir.call exception @_ZN3VecC1Ev(%[[V3]]) : (!cir.ptr<![[VecTy]]>) -> () cleanup {
123+
// CIR: cir.call @_ZN3VecD1Ev(%[[V2]]) : (!cir.ptr<![[VecTy]]>) -> ()
124+
// CIR: cir.call @_ZN3VecD1Ev(%[[V1]]) : (!cir.ptr<![[VecTy]]>) -> ()
125+
// CIR: cir.yield
126+
// CIR: }
127+
// CIR: cir.call exception @_ZN3VecC1Ev(%[[V4]]) : (!cir.ptr<![[VecTy]]>) -> () cleanup {
128+
// CIR: cir.call @_ZN3VecD1Ev(%[[V3]]) : (!cir.ptr<![[VecTy]]>) -> ()
129+
// CIR: cir.call @_ZN3VecD1Ev(%[[V2]]) : (!cir.ptr<![[VecTy]]>) -> ()
130+
// CIR: cir.call @_ZN3VecD1Ev(%[[V1]]) : (!cir.ptr<![[VecTy]]>) -> ()
131+
// CIR: cir.yield
132+
// CIR: }
133+
// CIR: cir.call @_ZN3VecD1Ev(%[[V4]]) : (!cir.ptr<![[VecTy]]>) -> ()
134+
// CIR: cir.call @_ZN3VecD1Ev(%[[V3]]) : (!cir.ptr<![[VecTy]]>) -> ()
135+
// CIR: cir.call @_ZN3VecD1Ev(%[[V2]]) : (!cir.ptr<![[VecTy]]>) -> ()
136+
// CIR: cir.call @_ZN3VecD1Ev(%[[V1]]) : (!cir.ptr<![[VecTy]]>) -> ()
137+
// CIR: cir.yield
138+
// CIR: } catch [type #cir.all {
139+
// CIR: }]
140+
// CIR: }
141+
// CIR: cir.return

clang/test/CIR/CodeGen/try-catch.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ unsigned long long tc() {
2121
a++;
2222

2323
} catch (int idx) {
24-
// CHECK: } cleanup {
2524
// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i> {
2625
// CHECK: %[[catch_idx_addr:.*]] = cir.catch_param -> !cir.ptr<!s32i>
2726
// CHECK: %[[idx_load:.*]] = cir.load %[[catch_idx_addr]] : !cir.ptr<!s32i>, !s32i

0 commit comments

Comments
 (0)