[WebAssembly] Fix LowerEmscriptenEHSjLj when there's only longjmp

Summary:
The pass incorrectly assumed if there's a longjmp declaration in the
module, there is also a setjmp function declaration. Fixed it, and now
the pass only converts longjmp and does not do any other transformation
when there's no setjmp declaration in the module.

Fixes PR39562.

Reviewers: jgravelle-google, sbc100

Subscribers: dschuff, sunfish, llvm-commits

Differential Revision: https://reviews.llvm.org/D54273

llvm-svn: 346445
This commit is contained in:
Heejin Ahn 2018-11-08 22:56:26 +00:00
parent ca591dea10
commit 0c68a875fa
2 changed files with 83 additions and 51 deletions

View File

@ -140,7 +140,17 @@
///
/// * Setjmp / Longjmp handling
///
/// 7) In the function entry that calls setjmp, initialize setjmpTable and
/// In case calls to longjmp() exists
///
/// 1) Lower
/// longjmp(buf, value)
/// into
/// emscripten_longjmp_jmpbuf(buf, value)
/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
///
/// In case calls to setjmp() exists
///
/// 2) In the function entry that calls setjmp, initialize setjmpTable and
/// sejmpTableSize as follows:
/// setjmpTableSize = 4;
/// setjmpTable = (int *) malloc(40);
@ -148,7 +158,7 @@
/// setjmpTable and setjmpTableSize are used in saveSetjmp() function in JS
/// code.
///
/// 8) Lower
/// 3) Lower
/// setjmp(buf)
/// into
/// setjmpTable = saveSetjmp(buf, label, setjmpTable, setjmpTableSize);
@ -162,13 +172,8 @@
/// A BB with setjmp is split into two after setjmp call in order to make the
/// post-setjmp BB the possible destination of longjmp BB.
///
/// 9) Lower
/// longjmp(buf, value)
/// into
/// emscripten_longjmp_jmpbuf(buf, value)
/// emscripten_longjmp_jmpbuf will be lowered to emscripten_longjmp later.
///
/// 10) Lower every call that might longjmp into
/// 4) Lower every call that might longjmp into
/// __THREW__ = 0;
/// call @__invoke_SIG(func, arg1, arg2)
/// %__THREW__.val = __THREW__;
@ -189,21 +194,21 @@
/// ...
/// default: goto splitted next BB
/// }
/// testSetjmp examines setjmpTable to see if there is a matching setjmp
/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
/// will be the address of matching jmp_buf buffer and __threwValue be the
/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
/// each setjmp callsite. Label 0 means this longjmp buffer does not
/// correspond to one of the setjmp callsites in this function, so in this
/// case we just chain the longjmp to the caller. (Here we call
/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
/// emscripten_longjmp takes an int. Both of them will eventually be lowered
/// to emscripten_longjmp in s2wasm, but here we need two signatures - we
/// can't translate an int value to a jmp_buf.)
/// Label -1 means no longjmp occurred. Otherwise we jump to the right
/// post-setjmp BB based on the label.
/// testSetjmp examines setjmpTable to see if there is a matching setjmp
/// call. After calling an invoke wrapper, if a longjmp occurred, __THREW__
/// will be the address of matching jmp_buf buffer and __threwValue be the
/// second argument to longjmp. mem[__THREW__.val] is a setjmp ID that is
/// stored in saveSetjmp. testSetjmp returns a setjmp label, a unique ID to
/// each setjmp callsite. Label 0 means this longjmp buffer does not
/// correspond to one of the setjmp callsites in this function, so in this
/// case we just chain the longjmp to the caller. (Here we call
/// emscripten_longjmp, which is different from emscripten_longjmp_jmpbuf.
/// emscripten_longjmp_jmpbuf takes jmp_buf as its first argument, while
/// emscripten_longjmp takes an int. Both of them will eventually be lowered
/// to emscripten_longjmp in s2wasm, but here we need two signatures - we
/// can't translate an int value to a jmp_buf.)
/// Label -1 means no longjmp occurred. Otherwise we jump to the right
/// post-setjmp BB based on the label.
///
///===----------------------------------------------------------------------===//
@ -662,22 +667,6 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
if (DoSjLj) {
Changed = true; // We have setjmp or longjmp somewhere
// Register saveSetjmp function
FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
IRB.getInt32Ty(), Type::getInt32PtrTy(C),
IRB.getInt32Ty()};
FunctionType *FTy =
FunctionType::get(Type::getInt32PtrTy(C), Params, false);
SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
SaveSetjmpFName, &M);
// Register testSetjmp function
Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()};
FTy = FunctionType::get(IRB.getInt32Ty(), Params, false);
TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
TestSetjmpFName, &M);
if (LongjmpF) {
// Replace all uses of longjmp with emscripten_longjmp_jmpbuf, which is
// defined in JS code
@ -687,20 +676,39 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
LongjmpF->replaceAllUsesWith(EmLongjmpJmpbufF);
}
FTy = FunctionType::get(IRB.getVoidTy(),
{IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
EmLongjmpF =
Function::Create(FTy, GlobalValue::ExternalLinkage, EmLongjmpFName, &M);
// Only traverse functions that uses setjmp in order not to insert
// unnecessary prep / cleanup code in every function
SmallPtrSet<Function *, 8> SetjmpUsers;
for (User *U : SetjmpF->users()) {
auto *UI = cast<Instruction>(U);
SetjmpUsers.insert(UI->getFunction());
if (SetjmpF) {
// Register saveSetjmp function
FunctionType *SetjmpFTy = SetjmpF->getFunctionType();
SmallVector<Type *, 4> Params = {SetjmpFTy->getParamType(0),
IRB.getInt32Ty(), Type::getInt32PtrTy(C),
IRB.getInt32Ty()};
FunctionType *FTy =
FunctionType::get(Type::getInt32PtrTy(C), Params, false);
SaveSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
SaveSetjmpFName, &M);
// Register testSetjmp function
Params = {IRB.getInt32Ty(), Type::getInt32PtrTy(C), IRB.getInt32Ty()};
FTy = FunctionType::get(IRB.getInt32Ty(), Params, false);
TestSetjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
TestSetjmpFName, &M);
FTy = FunctionType::get(IRB.getVoidTy(),
{IRB.getInt32Ty(), IRB.getInt32Ty()}, false);
EmLongjmpF = Function::Create(FTy, GlobalValue::ExternalLinkage,
EmLongjmpFName, &M);
// Only traverse functions that uses setjmp in order not to insert
// unnecessary prep / cleanup code in every function
SmallPtrSet<Function *, 8> SetjmpUsers;
for (User *U : SetjmpF->users()) {
auto *UI = cast<Instruction>(U);
SetjmpUsers.insert(UI->getFunction());
}
for (Function *F : SetjmpUsers)
runSjLjOnFunction(*F);
}
for (Function *F : SetjmpUsers)
runSjLjOnFunction(*F);
}
if (!Changed) {

View File

@ -0,0 +1,24 @@
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
%struct.__jmp_buf_tag = type { [6 x i32], i32, %struct.__sigset_t }
%struct.__sigset_t = type { [32 x i32] }
@buffer = global [1 x %struct.__jmp_buf_tag] zeroinitializer, align 16
; Tests if program does not crash when there's no setjmp function calls in the
; module.
; CHECK: call void @emscripten_longjmp_jmpbuf
define void @longjmp_only() {
entry:
call void @longjmp(%struct.__jmp_buf_tag* getelementptr inbounds ([1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* @buffer, i32 0, i32 0), i32 1) #1
unreachable
}
; Function Attrs: noreturn nounwind
declare void @longjmp(%struct.__jmp_buf_tag*, i32) #1
attributes #1 = { noreturn nounwind }