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;
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)
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 }