Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyUtilities.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ llvm/trunk/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(MO.getGlobal()); if (!F) return true; Index: llvm/trunk/test/CodeGen/WebAssembly/cfg-stackify-eh.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ llvm/trunk/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 }