[PATCH] D89032: [WebAssembly] Handle indirect uses of longjmp
Heejin Ahn via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 8 03:25:34 PDT 2020
aheejin created this revision.
aheejin added a reviewer: tlively.
Herald added subscribers: llvm-commits, ecnelises, sunfish, hiraditya, jgravelle-google, sbc100, dschuff.
Herald added a project: LLVM.
aheejin requested review of this revision.
In LowerEmscriptenEHSjLj, `longjmp` used to be replaced with
`emscripten_longjmp_jmpbuf(jmp_buf*, i32)`, which will eventually be
lowered to `emscripten_longjmp(i32, i32)`. The reason we used two
different names was because they had different signatures in the IR
pass.
D88697 <https://reviews.llvm.org/D88697> fixed this by only using `emscripten_longjmp(i32, i32)` and
adding a `ptrtoint` cast to its first argument, so
longjmp(buf, 0)
becomes
emscripten_longjmp((i32)buf, 0)
But this assumed all uses of `longjmp` was a direct call to it, which
was not the case. This patch handles indirect uses of `longjmp` by
replacing
longjmp(buf, 0)
with
(int(*)(jmp_buf*, int))emscripten_longjmp
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D89032
Files:
llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
Index: llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
===================================================================
--- llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
+++ llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll
@@ -11,6 +11,8 @@
; NO-TLS-DAG: __threwValue = external global i32
; TLS-DAG: __THREW__ = external thread_local(localexec) global i32
; TLS-DAG: __threwValue = external thread_local(localexec) global i32
+ at global_longjmp_ptr = global void (%struct.__jmp_buf_tag*, i32)* @longjmp, align 4
+; CHECK-DAG: @global_longjmp_ptr = global void (%struct.__jmp_buf_tag*, i32)* bitcast (void (i32, i32)* @emscripten_longjmp to void (%struct.__jmp_buf_tag*, i32)*)
; Test a simple setjmp - longjmp sequence
define void @setjmp_longjmp() {
@@ -250,6 +252,36 @@
; CHECK: %var[[VARNO]] = phi i32 [ %var, %for.inc ]
}
+; Tests cases where longjmp function pointer is used in other ways than direct
+; calls. longjmps should be replaced with
+; (int(*)(jmp_buf*, int))emscripten_longjmp.
+declare void @take_longjmp(void (%struct.__jmp_buf_tag*, i32)* %arg_ptr)
+define void @indirect_longjmp() {
+; CHECK-LABEL: @indirect_longjmp
+entry:
+ %local_longjmp_ptr = alloca void (%struct.__jmp_buf_tag*, i32)*, align 4
+ %buf0 = alloca [1 x %struct.__jmp_buf_tag], align 16
+ %buf1 = alloca [1 x %struct.__jmp_buf_tag], align 16
+
+ ; Store longjmp in a local variable, load it, and call it
+ store void (%struct.__jmp_buf_tag*, i32)* @longjmp, void (%struct.__jmp_buf_tag*, i32)** %local_longjmp_ptr, align 4
+ ; CHECK: store void (%struct.__jmp_buf_tag*, i32)* bitcast (void (i32, i32)* @emscripten_longjmp to void (%struct.__jmp_buf_tag*, i32)*), void (%struct.__jmp_buf_tag*, i32)** %local_longjmp_ptr, align 4
+ %longjmp_from_local_ptr = load void (%struct.__jmp_buf_tag*, i32)*, void (%struct.__jmp_buf_tag*, i32)** %local_longjmp_ptr, align 4
+ %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf0, i32 0, i32 0
+ call void %longjmp_from_local_ptr(%struct.__jmp_buf_tag* %arraydecay, i32 0)
+
+ ; Load longjmp from a global variable and call it
+ %longjmp_from_global_ptr = load void (%struct.__jmp_buf_tag*, i32)*, void (%struct.__jmp_buf_tag*, i32)** @global_longjmp_ptr, align 4
+ %arraydecay1 = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf1, i32 0, i32 0
+ call void %longjmp_from_global_ptr(%struct.__jmp_buf_tag* %arraydecay1, i32 0)
+
+ ; Pass longjmp as a function argument. This is a call but longjmp is not a
+ ; callee but an argument.
+ call void @take_longjmp(void (%struct.__jmp_buf_tag*, i32)* @longjmp)
+ ; CHECK: call void @take_longjmp(void (%struct.__jmp_buf_tag*, i32)* bitcast (void (i32, i32)* @emscripten_longjmp to void (%struct.__jmp_buf_tag*, i32)*))
+ ret void
+}
+
declare void @foo()
; Function Attrs: returns_twice
declare i32 @setjmp(%struct.__jmp_buf_tag*) #0
Index: llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -644,19 +644,29 @@
SmallVector<CallInst *, 8> ToErase;
LLVMContext &C = LongjmpF->getParent()->getContext();
IRBuilder<> IRB(C);
+
+ // For calls to longjmp, replace it with emscripten_longjmp and cast its first
+ // argument (jmp_buf*) to int
for (User *U : LongjmpF->users()) {
auto *CI = dyn_cast<CallInst>(U);
- if (!CI)
- report_fatal_error("Does not support indirect calls to longjmp");
- IRB.SetInsertPoint(CI);
- Value *Jmpbuf =
- IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf");
- IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
- ToErase.push_back(CI);
+ if (CI && CI->getCalledFunction() == LongjmpF) {
+ IRB.SetInsertPoint(CI);
+ Value *Jmpbuf =
+ IRB.CreatePtrToInt(CI->getArgOperand(0), IRB.getInt32Ty(), "jmpbuf");
+ IRB.CreateCall(EmLongjmpF, {Jmpbuf, CI->getArgOperand(1)});
+ ToErase.push_back(CI);
+ }
}
-
for (auto *I : ToErase)
I->eraseFromParent();
+
+ // If we have any remaining uses of longjmp's function pointer, replace it
+ // with (int(*)(jmp_buf*, int))emscripten_longjmp.
+ if (!LongjmpF->uses().empty()) {
+ Value *EmLongjmp =
+ IRB.CreateBitCast(EmLongjmpF, LongjmpF->getType(), "em_longjmp");
+ LongjmpF->replaceAllUsesWith(EmLongjmp);
+ }
}
bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D89032.296913.patch
Type: text/x-patch
Size: 4591 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20201008/878c66d6/attachment.bin>
More information about the llvm-commits
mailing list