[PATCH] D68553: [WebAssembly] Add memory intrinsics handling to mayThrow()

Heejin Ahn via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 6 07:19:21 PDT 2019


aheejin created this revision.
aheejin added a reviewer: dschuff.
Herald added subscribers: llvm-commits, sunfish, hiraditya, jgravelle-google, sbc100.
Herald added a project: LLVM.

Previously, `WebAssembly::mayThrow()` assumed all inputs are global
addresses. But when intrinsics, such as `memcpy`, `memmove`, or `memset`
are lowered to external symbols in instruction selection and later
emitted as library calls. And these functions don't throw.

This patch adds handling to those memory intrinsics to `mayThrow`
function. But while most of libcalls don't throw, we can't guarantee all
of them don't throw, so currently we conservatively return true for all
other external symbols.

I think a better way to solve this problem is to embed 'nounwind' info
in `TargetLowering::CallLoweringInfo`, so that we can access the info
from the backend. This will also enable transferring 'nounwind'
properties of LLVM IR instructions. Currently we don't transfer that
info and we can only access properties of callee functions, if the
calles are within the module. Other targets don't need this info in the
backend because they do all the processing before isel, but it will help
us because that info will reduce code size increase in fixing unwind
destination mismatches in CFGStackify.

But for now we return false for these memory intrinsics and true for all
other libcalls conservatively.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D68553

Files:
  llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
  llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll


Index: llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
===================================================================
--- llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -664,11 +664,51 @@
   ret void
 }
 
+%class.Object = type { i8 }
+
+; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
+; calls to external symbols (not global addresses) in instruction selection,
+; which will be eventually lowered to library function calls.
+; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
+; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
+; unwinding destination mismatches in CFGStackify.
+
+; NOSORT-LABEL: test10
+; NOSORT: try
+; NOSORT:   call  foo
+; NOSORT:   i32.call {{.*}} memcpy
+; NOSORT:   i32.call {{.*}} memmove
+; NOSORT:   i32.call {{.*}} memset
+; NOSORT:   return
+; NOSORT: catch
+; NOSORT:   rethrow
+; NOSORT: end_try
+define void @test10(i8* %a, i8* %b) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
+entry:
+  %o = alloca %class.Object, align 1
+  invoke void @foo()
+          to label %invoke.cont unwind label %ehcleanup
+
+invoke.cont:                                      ; preds = %entry
+  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false)
+  call void @llvm.memmove.p0i8.p0i8.i32(i8* %a, i8* %b, i32 100, i1 false)
+  call void @llvm.memset.p0i8.i32(i8* %a, i8 0, i32 100, i1 false)
+  %call = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1
+  ret void
+
+ehcleanup:                                        ; preds = %entry
+  %0 = cleanuppad within none []
+  %call2 = call %class.Object* @_ZN6ObjectD2Ev(%class.Object* %o) #1 [ "funclet"(token %0) ]
+  cleanupret from %0 unwind to caller
+}
+
 declare void @foo()
 declare void @bar()
 declare i32 @baz()
 ; Function Attrs: nounwind
 declare void @nothrow(i32) #0
+; Function Attrs: nounwind
+declare %class.Object* @_ZN6ObjectD2Ev(%class.Object* returned) #0
 declare i32 @__gxx_wasm_personality_v0(...)
 declare i8* @llvm.wasm.get.exception(token)
 declare i32 @llvm.wasm.get.ehselector(token)
@@ -678,5 +718,11 @@
 declare void @__cxa_end_catch()
 declare void @__clang_call_terminate(i8*)
 declare void @_ZSt9terminatev()
+; Function Attrs: nounwind
+declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0
+; Function Attrs: nounwind
+declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1 immarg) #0
+; Function Attrs: nounwind
+declare void @llvm.memset.p0i8.i32(i8* nocapture writeonly, i8, i32, i1 immarg) #0
 
 attributes #0 = { nounwind }
Index: llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -50,7 +50,21 @@
     return false;
 
   const MachineOperand &MO = MI.getOperand(getCalleeOpNo(MI.getOpcode()));
-  assert(MO.isGlobal());
+  assert(MO.isGlobal() || MO.isSymbol());
+
+  if (MO.isSymbol()) {
+    // Some intrinsics are lowered to calls to external symbols, which are then
+    // lowered to calls to library functions. Most of libcalls don't throw, but
+    // we only list some of them here now.
+    // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo
+    // instead for more accurate info.
+    const char *Name = MO.getSymbolName();
+    if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||
+        strcmp(Name, "memset") == 0)
+      return false;
+    return true;
+  }
+
   const auto *F = dyn_cast<Function>(MO.getGlobal());
   if (!F)
     return true;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D68553.223417.patch
Type: text/x-patch
Size: 3801 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20191006/7f1a7636/attachment.bin>


More information about the llvm-commits mailing list