Skip to content

Commit fc90685

Browse files
authored
[WebAssemblyLowerEmscriptenEHSjLj] Avoid lifetime of phi (#150932)
After #149310 lifetime intrinsics require an alloca argument, an invariant that this pass can break. I've fixed this in two ways: * First, move static allocas into the entry block. Currently, the way the pass splits the entry block makes all allocas dynamic, which I assume was not actually intended. This will avoid unnecessary SSA reconstruction for allocas as well, and thus avoid the problem. * If this fails (for dynamic allocas) drop all lifetime intrinsics if any one of them would require a rewrite during SSA reconstruction. Fixes #150498.
1 parent ab1f6ce commit fc90685

File tree

6 files changed

+165
-10
lines changed

6 files changed

+165
-10
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,24 @@ void WebAssemblyLowerEmscriptenEHSjLj::rebuildSSA(Function &F) {
782782
for (Instruction &I : BB) {
783783
if (I.getType()->isVoidTy())
784784
continue;
785+
786+
if (isa<AllocaInst>(&I)) {
787+
// If the alloca has any lifetime marker that is no longer dominated
788+
// by the alloca, remove all lifetime markers. Lifetime markers must
789+
// always work directly on the alloca, and this is no longer possible.
790+
bool HasNonDominatedLifetimeMarker = any_of(I.users(), [&](User *U) {
791+
auto *UserI = cast<Instruction>(U);
792+
return UserI->isLifetimeStartOrEnd() && !DT.dominates(&I, UserI);
793+
});
794+
if (HasNonDominatedLifetimeMarker) {
795+
for (User *U : make_early_inc_range(I.users())) {
796+
auto *UserI = cast<Instruction>(U);
797+
if (UserI->isLifetimeStartOrEnd())
798+
UserI->eraseFromParent();
799+
}
800+
}
801+
}
802+
785803
unsigned VarID = SSA.AddVariable(I.getName(), I.getType());
786804
// If a value is defined by an invoke instruction, it is only available in
787805
// its normal destination and not in its unwind destination.
@@ -1269,10 +1287,20 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runSjLjOnFunction(Function &F) {
12691287

12701288
// Setjmp preparation
12711289

1290+
SmallVector<AllocaInst *> StaticAllocas;
1291+
for (Instruction &I : F.getEntryBlock())
1292+
if (auto *AI = dyn_cast<AllocaInst>(&I))
1293+
if (AI->isStaticAlloca())
1294+
StaticAllocas.push_back(AI);
1295+
12721296
BasicBlock *Entry = &F.getEntryBlock();
12731297
DebugLoc FirstDL = getOrCreateDebugLoc(&*Entry->begin(), F.getSubprogram());
12741298
SplitBlock(Entry, &*Entry->getFirstInsertionPt());
12751299

1300+
// Move static allocas back into the entry block, so they stay static.
1301+
for (AllocaInst *AI : StaticAllocas)
1302+
AI->moveBefore(Entry->getTerminator()->getIterator());
1303+
12761304
IRB.SetInsertPoint(Entry->getTerminator()->getIterator());
12771305
// This alloca'ed pointer is used by the runtime to identify function
12781306
// invocations. It's just for pointer comparisons. It will never be
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -S -wasm-lower-em-ehsjlj -wasm-enable-sjlj -mtriple=wasm32-unknown-emscripten < %s | FileCheck %s
3+
4+
@buf = external global i8
5+
declare i32 @setjmp(ptr) returns_twice
6+
declare void @dummy()
7+
8+
define void @test_static() {
9+
; CHECK-LABEL: define void @test_static() personality ptr @__gxx_wasm_personality_v0 {
10+
; CHECK-NEXT: [[ENTRY:.*]]:
11+
; CHECK-NEXT: [[X:%.*]] = alloca i32, align 4
12+
; CHECK-NEXT: [[FUNCTIONINVOCATIONID:%.*]] = alloca i32, align 4
13+
; CHECK-NEXT: br label %[[SETJMP_DISPATCH:.*]]
14+
; CHECK: [[SETJMP_DISPATCH]]:
15+
; CHECK-NEXT: [[VAL1:%.*]] = phi i32 [ [[VAL:%.*]], %[[IF_END:.*]] ], [ undef, %[[ENTRY]] ]
16+
; CHECK-NEXT: [[LABEL_PHI:%.*]] = phi i32 [ [[LABEL:%.*]], %[[IF_END]] ], [ -1, %[[ENTRY]] ]
17+
; CHECK-NEXT: switch i32 [[LABEL_PHI]], label %[[ENTRY_SPLIT:.*]] [
18+
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_SPLIT:.*]]
19+
; CHECK-NEXT: ]
20+
; CHECK: [[ENTRY_SPLIT]]:
21+
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[X]])
22+
; CHECK-NEXT: call void @__wasm_setjmp(ptr @buf, i32 1, ptr [[FUNCTIONINVOCATIONID]])
23+
; CHECK-NEXT: br label %[[ENTRY_SPLIT_SPLIT]]
24+
; CHECK: [[ENTRY_SPLIT_SPLIT]]:
25+
; CHECK-NEXT: [[SETJMP_RET:%.*]] = phi i32 [ 0, %[[ENTRY_SPLIT]] ], [ [[VAL1]], %[[SETJMP_DISPATCH]] ]
26+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SETJMP_RET]], 0
27+
; CHECK-NEXT: br i1 [[CMP]], label %[[IF:.*]], label %[[ELSE:.*]]
28+
; CHECK: [[IF]]:
29+
; CHECK-NEXT: invoke void @dummy()
30+
; CHECK-NEXT: to [[DOTNOEXC:label %.*]] unwind label %[[CATCH_DISPATCH_LONGJMP:.*]]
31+
; CHECK: [[_NOEXC:.*:]]
32+
; CHECK-NEXT: ret void
33+
; CHECK: [[ELSE]]:
34+
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr [[X]])
35+
; CHECK-NEXT: ret void
36+
; CHECK: [[CATCH_DISPATCH_LONGJMP]]:
37+
; CHECK-NEXT: [[TMP0:%.*]] = catchswitch within none [label %catch.longjmp] unwind to caller
38+
; CHECK: [[CATCH_LONGJMP:.*:]]
39+
; CHECK-NEXT: [[TMP1:%.*]] = catchpad within [[TMP0]] []
40+
; CHECK-NEXT: [[THROWN:%.*]] = call ptr @llvm.wasm.catch(i32 1)
41+
; CHECK-NEXT: [[ENV_GEP:%.*]] = getelementptr { ptr, i32 }, ptr [[THROWN]], i32 0, i32 0
42+
; CHECK-NEXT: [[VAL_GEP:%.*]] = getelementptr { ptr, i32 }, ptr [[THROWN]], i32 0, i32 1
43+
; CHECK-NEXT: [[ENV:%.*]] = load ptr, ptr [[ENV_GEP]], align 4
44+
; CHECK-NEXT: [[VAL]] = load i32, ptr [[VAL_GEP]], align 4
45+
; CHECK-NEXT: [[LABEL]] = call i32 @__wasm_setjmp_test(ptr [[ENV]], ptr [[FUNCTIONINVOCATIONID]]) [ "funclet"(token [[TMP1]]) ]
46+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[LABEL]], 0
47+
; CHECK-NEXT: br i1 [[TMP2]], label %[[IF_THEN:.*]], label %[[IF_END]]
48+
; CHECK: [[IF_THEN]]:
49+
; CHECK-NEXT: call void @__wasm_longjmp(ptr [[ENV]], i32 [[VAL]]) [ "funclet"(token [[TMP1]]) ]
50+
; CHECK-NEXT: unreachable
51+
; CHECK: [[IF_END]]:
52+
; CHECK-NEXT: catchret from [[TMP1]] to label %[[SETJMP_DISPATCH]]
53+
;
54+
entry:
55+
%x = alloca i32, align 4
56+
call void @llvm.lifetime.start.p0(i64 4, ptr %x)
57+
%call = call i32 @setjmp(ptr @buf) returns_twice
58+
%cmp = icmp eq i32 %call, 0
59+
br i1 %cmp, label %if, label %else
60+
61+
if:
62+
call void @dummy()
63+
ret void
64+
65+
else:
66+
call void @llvm.lifetime.end.p0(i64 4, ptr %x)
67+
ret void
68+
}
69+
70+
define void @test_dynamic(i32 %size) {
71+
; CHECK-LABEL: define void @test_dynamic(
72+
; CHECK-SAME: i32 [[SIZE:%.*]]) personality ptr @__gxx_wasm_personality_v0 {
73+
; CHECK-NEXT: [[ENTRY:.*]]:
74+
; CHECK-NEXT: [[FUNCTIONINVOCATIONID:%.*]] = alloca i32, align 4
75+
; CHECK-NEXT: br label %[[SETJMP_DISPATCH:.*]]
76+
; CHECK: [[SETJMP_DISPATCH]]:
77+
; CHECK-NEXT: [[VAL1:%.*]] = phi i32 [ [[VAL:%.*]], %[[IF_END:.*]] ], [ undef, %[[ENTRY]] ]
78+
; CHECK-NEXT: [[LABEL_PHI:%.*]] = phi i32 [ [[LABEL:%.*]], %[[IF_END]] ], [ -1, %[[ENTRY]] ]
79+
; CHECK-NEXT: switch i32 [[LABEL_PHI]], label %[[ENTRY_SPLIT:.*]] [
80+
; CHECK-NEXT: i32 1, label %[[ENTRY_SPLIT_SPLIT:.*]]
81+
; CHECK-NEXT: ]
82+
; CHECK: [[ENTRY_SPLIT]]:
83+
; CHECK-NEXT: [[X:%.*]] = alloca i32, i32 [[SIZE]], align 4
84+
; CHECK-NEXT: call void @__wasm_setjmp(ptr @buf, i32 1, ptr [[FUNCTIONINVOCATIONID]])
85+
; CHECK-NEXT: br label %[[ENTRY_SPLIT_SPLIT]]
86+
; CHECK: [[ENTRY_SPLIT_SPLIT]]:
87+
; CHECK-NEXT: [[SETJMP_RET:%.*]] = phi i32 [ 0, %[[ENTRY_SPLIT]] ], [ [[VAL1]], %[[SETJMP_DISPATCH]] ]
88+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SETJMP_RET]], 0
89+
; CHECK-NEXT: br i1 [[CMP]], label %[[IF:.*]], label %[[ELSE:.*]]
90+
; CHECK: [[IF]]:
91+
; CHECK-NEXT: invoke void @dummy()
92+
; CHECK-NEXT: to [[DOTNOEXC:label %.*]] unwind label %[[CATCH_DISPATCH_LONGJMP:.*]]
93+
; CHECK: [[_NOEXC:.*:]]
94+
; CHECK-NEXT: ret void
95+
; CHECK: [[ELSE]]:
96+
; CHECK-NEXT: ret void
97+
; CHECK: [[CATCH_DISPATCH_LONGJMP]]:
98+
; CHECK-NEXT: [[TMP0:%.*]] = catchswitch within none [label %catch.longjmp] unwind to caller
99+
; CHECK: [[CATCH_LONGJMP:.*:]]
100+
; CHECK-NEXT: [[TMP1:%.*]] = catchpad within [[TMP0]] []
101+
; CHECK-NEXT: [[THROWN:%.*]] = call ptr @llvm.wasm.catch(i32 1)
102+
; CHECK-NEXT: [[ENV_GEP:%.*]] = getelementptr { ptr, i32 }, ptr [[THROWN]], i32 0, i32 0
103+
; CHECK-NEXT: [[VAL_GEP:%.*]] = getelementptr { ptr, i32 }, ptr [[THROWN]], i32 0, i32 1
104+
; CHECK-NEXT: [[ENV:%.*]] = load ptr, ptr [[ENV_GEP]], align 4
105+
; CHECK-NEXT: [[VAL]] = load i32, ptr [[VAL_GEP]], align 4
106+
; CHECK-NEXT: [[LABEL]] = call i32 @__wasm_setjmp_test(ptr [[ENV]], ptr [[FUNCTIONINVOCATIONID]]) [ "funclet"(token [[TMP1]]) ]
107+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[LABEL]], 0
108+
; CHECK-NEXT: br i1 [[TMP2]], label %[[IF_THEN:.*]], label %[[IF_END]]
109+
; CHECK: [[IF_THEN]]:
110+
; CHECK-NEXT: call void @__wasm_longjmp(ptr [[ENV]], i32 [[VAL]]) [ "funclet"(token [[TMP1]]) ]
111+
; CHECK-NEXT: unreachable
112+
; CHECK: [[IF_END]]:
113+
; CHECK-NEXT: catchret from [[TMP1]] to label %[[SETJMP_DISPATCH]]
114+
;
115+
entry:
116+
%x = alloca i32, i32 %size, align 4
117+
call void @llvm.lifetime.start.p0(i64 -1, ptr %x)
118+
%call = call i32 @setjmp(ptr @buf) returns_twice
119+
%cmp = icmp eq i32 %call, 0
120+
br i1 %cmp, label %if, label %else
121+
122+
if:
123+
call void @dummy()
124+
ret void
125+
126+
else:
127+
call void @llvm.lifetime.end.p0(i64 -1, ptr %x)
128+
ret void
129+
}

llvm/test/CodeGen/WebAssembly/lower-em-sjlj-debuginfo.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ entry:
1616
call void @foo(), !dbg !7
1717
ret void, !dbg !8
1818
; CHECK: entry:
19-
; CHECK-NEXT: %functionInvocationId = alloca i32, align 4, !dbg ![[DL0:.*]]
19+
; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16, !dbg ![[DL0:.*]]
20+
; CHECK-NEXT: %functionInvocationId = alloca i32, align 4, !dbg ![[DL0]]
2021

2122
; CHECK: entry.split:
22-
; CHECK: alloca {{.*}}, !dbg ![[DL0]]
2323
; CHECK: call void @__wasm_setjmp{{.*}}, !dbg ![[DL1:.*]]
2424
; CHECK-NEXT: br {{.*}}, !dbg ![[DL2:.*]]
2525

llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ entry:
2222
call void @longjmp(ptr %buf, i32 1) #1
2323
unreachable
2424
; CHECK: entry:
25+
; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
2526
; CHECK-NEXT: %functionInvocationId = alloca i32, align 4
2627
; CHECK-NEXT: br label %entry.split
2728

2829
; CHECK: entry.split
29-
; CHECK-NEXT: %[[BUF:.*]] = alloca [1 x %struct.__jmp_buf_tag]
30-
; CHECK-NEXT: call void @__wasm_setjmp(ptr %[[BUF]], i32 1, ptr %functionInvocationId)
30+
; CHECK-NEXT: call void @__wasm_setjmp(ptr %buf, i32 1, ptr %functionInvocationId)
3131
; CHECK-NEXT: br label %entry.split.split
3232

3333
; CHECK: entry.split.split:
3434
; CHECK-NEXT: phi i32 [ 0, %entry.split ], [ %[[LONGJMP_RESULT:.*]], %if.end ]
35-
; CHECK-NEXT: %[[JMPBUF:.*]] = ptrtoint ptr %[[BUF]] to [[PTR]]
35+
; CHECK-NEXT: %[[JMPBUF:.*]] = ptrtoint ptr %buf to [[PTR]]
3636
; CHECK-NEXT: store [[PTR]] 0, ptr @__THREW__
3737
; CHECK-NEXT: call cc{{.*}} void @__invoke_void_[[PTR]]_i32(ptr @emscripten_longjmp, [[PTR]] %[[JMPBUF]], i32 1)
3838
; CHECK-NEXT: %[[__THREW__VAL:.*]] = load [[PTR]], ptr @__THREW__

llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ catch: ; preds = %catch.start
108108
call void @__cxa_end_catch() [ "funclet"(token %2) ]
109109
catchret from %2 to label %catchret.dest
110110
; CHECK: catch: ; preds = %catch.start
111-
; CHECK-NEXT: %exn = load ptr, ptr %exn.slot6, align 4
111+
; CHECK-NEXT: %exn = load ptr, ptr %exn.slot, align 4
112112
; CHECK-NEXT: %5 = call ptr @__cxa_begin_catch(ptr %exn) #3 [ "funclet"(token %2) ]
113113
; CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %2) ]
114114
; CHECK-NEXT: to label %.noexc unwind label %catch.dispatch.longjmp

llvm/test/CodeGen/WebAssembly/lower-wasm-sjlj.ll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,24 @@ entry:
2525
unreachable
2626

2727
; CHECK: entry:
28+
; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
2829
; CHECK-NEXT: %functionInvocationId = alloca i32, align 4
2930
; CHECK-NEXT: br label %setjmp.dispatch
3031

3132
; CHECK: setjmp.dispatch:
3233
; CHECK-NEXT: %[[VAL2:.*]] = phi i32 [ %val, %if.end ], [ undef, %entry ]
33-
; CHECK-NEXT: %[[BUF:.*]] = phi ptr [ %[[BUF2:.*]], %if.end ], [ undef, %entry ]
3434
; CHECK-NEXT: %label.phi = phi i32 [ %label, %if.end ], [ -1, %entry ]
3535
; CHECK-NEXT: switch i32 %label.phi, label %entry.split [
3636
; CHECK-NEXT: i32 1, label %entry.split.split
3737
; CHECK-NEXT: ]
3838

3939
; CHECK: entry.split:
40-
; CHECK-NEXT: %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
4140
; CHECK-NEXT: call void @__wasm_setjmp(ptr %buf, i32 1, ptr %functionInvocationId)
4241
; CHECK-NEXT: br label %entry.split.split
4342

4443
; CHECK: entry.split.split:
45-
; CHECK-NEXT: %[[BUF2]] = phi ptr [ %[[BUF]], %setjmp.dispatch ], [ %buf, %entry.split ]
4644
; CHECK-NEXT: %setjmp.ret = phi i32 [ 0, %entry.split ], [ %[[VAL2]], %setjmp.dispatch ]
47-
; CHECK-NEXT: invoke void @__wasm_longjmp(ptr %[[BUF2]], i32 1)
45+
; CHECK-NEXT: invoke void @__wasm_longjmp(ptr %buf, i32 1)
4846
; CHECK-NEXT: to label %.noexc unwind label %catch.dispatch.longjmp
4947

5048
; CHECK: .noexc:

0 commit comments

Comments
 (0)