Function *getInvokeWrapper(CallBase *CI);
bool areAllExceptionsAllowed() const { return EHAllowlistSet.empty(); }
- bool canLongjmp(const Value *Callee) const;
- bool isEmAsmCall(const Value *Callee) const;
bool supportsException(const Function *F) const {
return EnableEmEH && (areAllExceptionsAllowed() ||
EHAllowlistSet.count(std::string(F->getName())));
return F;
}
-bool WebAssemblyLowerEmscriptenEHSjLj::canLongjmp(const Value *Callee) const {
+static bool canLongjmp(const Value *Callee) {
if (auto *CalleeF = dyn_cast<Function>(Callee))
if (CalleeF->isIntrinsic())
return false;
return true;
}
-bool WebAssemblyLowerEmscriptenEHSjLj::isEmAsmCall(const Value *Callee) const {
+static bool isEmAsmCall(const Value *Callee) {
StringRef CalleeName = Callee->getName();
// This is an exhaustive list from Emscripten's <emscripten/em_asm.h>.
return CalleeName == "emscripten_asm_const_int" ||
}
}
+static bool containsLongjmpableCalls(const Function *F) {
+ for (const auto &BB : *F)
+ for (const auto &I : BB)
+ if (const auto *CB = dyn_cast<CallBase>(&I))
+ if (canLongjmp(CB->getCalledOperand()))
+ return true;
+ return false;
+}
+
bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
LLVM_DEBUG(dbgs() << "********** Lower Emscripten EH & SjLj **********\n");
Function *SetjmpF = M.getFunction("setjmp");
Function *LongjmpF = M.getFunction("longjmp");
- bool SetjmpUsed = SetjmpF && !SetjmpF->use_empty();
- bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
- DoSjLj = EnableEmSjLj && (SetjmpUsed || LongjmpUsed);
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
assert(TPC && "Expected a TargetPassConfig");
EHTypeIDF = getEmscriptenFunction(EHTypeIDTy, "llvm_eh_typeid_for", &M);
}
+ if (EnableEmSjLj && SetjmpF) {
+ // Precompute setjmp users
+ for (User *U : SetjmpF->users()) {
+ Function *UserF = cast<Instruction>(U)->getFunction();
+ // If a function that calls setjmp does not contain any other calls that
+ // can longjmp, we don't need to do any transformation on that function,
+ // so can ignore it
+ if (containsLongjmpableCalls(UserF))
+ SetjmpUsers.insert(UserF);
+ }
+ }
+
+ bool SetjmpUsed = SetjmpF && !SetjmpUsers.empty();
+ bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
+ DoSjLj = EnableEmSjLj && (SetjmpUsed || LongjmpUsed);
+
// Function registration and data pre-gathering for setjmp/longjmp handling
if (DoSjLj) {
// Register emscripten_longjmp function
{getAddrIntType(&M), Type::getInt32PtrTy(C), IRB.getInt32Ty()},
false);
TestSetjmpF = getEmscriptenFunction(FTy, "testSetjmp", &M);
-
- // Precompute setjmp users
- for (User *U : SetjmpF->users()) {
- auto *UI = cast<Instruction>(U);
- SetjmpUsers.insert(UI->getFunction());
- }
}
}
; CHECK-NEXT: ret void
}
+; Test a case where a function has a setjmp call but no other calls that can
+; longjmp. We don't need to do any transformation in this case.
+define void @setjmp_only(i8* %ptr) {
+; CHECK-LABEL: @setjmp_only
+entry:
+ %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
+ %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0
+ %call = call i32 @setjmp(%struct.__jmp_buf_tag* %arraydecay) #0
+ ; free cannot longjmp
+ call void @free(i8* %ptr)
+ ret void
+; CHECK-NOT: @malloc
+; CHECK-NOT: %setjmpTable
+; CHECK-NOT: @saveSetjmp
+; CHECK-NOT: @testSetjmp
+}
+
; Test SSA validity
define void @ssa(i32 %n) {
; CHECK-LABEL: @ssa