[PATCH] D109375: [WebAssembly] Error out on indirect uses of setjmp

Heejin Ahn via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 7 10:15:04 PDT 2021


aheejin created this revision.
aheejin added reviewers: dschuff, tlively.
Herald added subscribers: wingo, ecnelises, sunfish, hiraditya, jgravelle-google, sbc100.
aheejin requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Both Wasm & Emscripten SjLj handling has a restriction that `setjmp`
cannot be called indirectly. I thought we have been erroring out on
indirect uses of `setjmp`, but some recent CL disrupted the logic and
we are not erroring out anymore.

We currently

1. Collect functions that contain `setjmp` calls in `SetjmpUsers`. This only counts direct calls: https://github.com/llvm/llvm-project/blob/8f77dc459e31aad6daab89a124fa92067916274c/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp#L869-L878
2. Run `runSjLjOnFunction` only on those `SetjmpUsers`. Within `runSjLjOnFunction`, if we see an indirect use of `setjmp`, we error out: https://github.com/llvm/llvm-project/blob/8f77dc459e31aad6daab89a124fa92067916274c/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp#L1218-L1221

So if there are only indirect setjmp calls within the module,
`SetjmpUsers` will be empty, and `runSjLjOnFunction` is not even entered
once. And the indirect `setjmp` call will error out at link time. So in
this CL we check for the indirect uses of `setjmp` upfront before we
enter `runSjLjOnFunction`.

Also this currently errors out on `invoke @setjmp`, which can only occur
when using Wasm EH + Wasm SjLj within a function. We recently added Wasm
SjLj support but we don't support using Wasm EH + Wasm SjLj in the same
function yet. We plan to add this support very soon, so I don't think
it's worth creating another test file just for this. (This is an error
test so it needs its own file)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D109375

Files:
  llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
  llvm/test/CodeGen/WebAssembly/lower-em-sjlj-indirect-setjmp.ll


Index: llvm/test/CodeGen/WebAssembly/lower-em-sjlj-indirect-setjmp.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/WebAssembly/lower-em-sjlj-indirect-setjmp.ll
@@ -0,0 +1,27 @@
+; RUN: not --crash llc < %s -enable-emscripten-sjlj 2>&1 | FileCheck %s
+; RUN: not --crash llc < %s -wasm-enable-sjlj -mattr=+exception-handling -exception-model=wasm 2>&1 | 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, [32 x i32] }
+
+; CHECK: LLVM ERROR: Does not support indirect uses of setjmp
+ at setjmp_fp = global i32 (%struct.__jmp_buf_tag*)* @setjmp, align 4
+
+define void @indirect_setjmp_call() {
+entry:
+  %buf = alloca [1 x %struct.__jmp_buf_tag], align 16
+  %0 = load i32 (%struct.__jmp_buf_tag*)*, i32 (%struct.__jmp_buf_tag*)** @setjmp_fp, align 4
+  %arraydecay = getelementptr inbounds [1 x %struct.__jmp_buf_tag], [1 x %struct.__jmp_buf_tag]* %buf, i32 0, i32 0
+  %call = call i32 %0(%struct.__jmp_buf_tag* %arraydecay)
+  call void @foo()
+  ret void
+}
+
+declare void @foo()
+; Function Attrs: returns_twice
+declare i32 @setjmp(%struct.__jmp_buf_tag*) #0
+
+attributes #0 = { returns_twice }
+
Index: llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -867,13 +867,19 @@
   if ((EnableEmSjLj || EnableWasmSjLj) && SetjmpF) {
     // Precompute setjmp users
     for (User *U : SetjmpF->users()) {
-      if (auto *UI = dyn_cast<Instruction>(U)) {
-        auto *UserF = UI->getFunction();
+      if (auto *CB = dyn_cast<CallBase>(U)) {
+        auto *UserF = CB->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);
+      } else {
+        std::string S;
+        raw_string_ostream SS(S);
+        SS << *U;
+        report_fatal_error(Twine("Does not support indirect uses of setjmp: ") +
+                           SS.str());
       }
     }
   }
@@ -1217,9 +1223,10 @@
   Function *SetjmpF = M.getFunction("setjmp");
   for (User *U : SetjmpF->users()) {
     auto *CI = dyn_cast<CallInst>(U);
+    // FIXME 'invoke' to setjmp can happen when we use Wasm EH + Wasm SjLj, but
+    // we don't support two being used together yet.
     if (!CI)
-      report_fatal_error("Does not support indirect calls to setjmp");
-
+      report_fatal_error("Wasm EH + Wasm SjLj is not fully supported yet");
     BasicBlock *BB = CI->getParent();
     if (BB->getParent() != &F) // in other function
       continue;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D109375.371115.patch
Type: text/x-patch
Size: 2951 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20210907/ca77d990/attachment.bin>


More information about the llvm-commits mailing list