From: Heejin Ahn Date: Mon, 7 Oct 2019 21:14:45 +0000 (+0000) Subject: [WebAssembly] Add memory intrinsics handling to mayThrow() X-Git-Tag: llvmorg-11-init~7291 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=58af5be28dafedafba3fb20ba35ac0ae4f2c570a;p=platform%2Fupstream%2Fllvm.git [WebAssembly] Add memory intrinsics handling to mayThrow() Summary: 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 callees 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. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, hiraditya, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D68553 llvm-svn: 373967 --- diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp index 81c16f0..a237da8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -50,7 +50,21 @@ bool WebAssembly::mayThrow(const MachineInstr &MI) { 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(MO.getGlobal()); if (!F) return true; diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll index 191c3d7..c79eb93 100644 --- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -664,11 +664,51 @@ if.end: ; preds = %cont, %catch.start, 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 i8* @__cxa_begin_catch(i8*) 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 }