This is an archive of the discontinued LLVM Phabricator instance.

[WebAssembly] Fix call unwind mismatches
ClosedPublic

Authored by aheejin on Jan 4 2021, 3:53 PM.

Details

Summary

This adds delegate instruction and use it to fix unwind destination
mismatches created by marker placement in CFGStackify.

There are two kinds of unwind destination mismatches:

  • Mismatches caused by throwing instructions (here we call it "call unwind mismatches", even though throw and rethrow can also cause mismatches)
  • Mismatches caused by catches, in case a foreign exception is not caught by the nearest catch and the next outer catch is not the catch it should unwind to. This kind of mismatches didn't exist in the previous version of the spec, because in the previous spec catch was effectively catch_all, catching all exceptions.

This implements routines to fix the first kind of unwind mismatches,
which we call "call unwind mismatches". The second mismatch (catch
unwind mismatches) will be fixed in a later CL.

This also reenables all previously disabled tests in cfg-stackify-eh.ll
and updates FileCheck lines to match the new spec. Two tests were
deleted because they specifically tested the way we fixed unwind
mismatches before using exnrefs and branches, which we don't do
anymore.

Diff Detail

Event Timeline

aheejin created this revision.Jan 4 2021, 3:53 PM
aheejin requested review of this revision.Jan 4 2021, 3:53 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 4 2021, 3:53 PM

Looks good overall! Most of these comments are just me checking my understanding.

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
865–866

Is this to ensure that the arguments to the call are inside the try so that the try itself does not need to take any stack arguments?

888
911–912

The delegate needs to be in its own new BB because it is both a terminator and a destination, right?

921

It seems like it might be simpler to do these CFG transformations before register stackification. Is there a reason we do them after register stackification?

940
1076

Can this !MBB.hasEHPadSuccessor() be checked outside this inner loop over the instructions? I guess not because the EHPadStack still needs to be updated, right?

1098

Are the EH_LABELS a kind of metadata node? They can only be located immediately before or after the relevant MachineInstr?

1147–1149

Can there be a BB with a call that throws to the caller followed by an invoke that unwinds to an EH pad? In that case wouldn't the BB have an EH pad successor, but still contain an unwind to the caller?

1212

Would it be worth being more precise here? IIUC, this could return false if there were no mismatches.

aheejin marked 3 inline comments as done.Jan 15 2021, 9:05 AM

Thanks for the review, and sorry for delayed reply.

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
865–866

Yes. We do this for the normal block or try placement too.

911–912

Yes. The same reason with that catch starts a new BB.

921

These transformations can happen only after CFGStackify, which has to follow CFGSort. And RegColoring has to be after RegStackify. So if we want to do this fixing unwind mismatches before RegStackify, the pass order would be like:
CFGSort -> CFGStackify -> RegStackify -> RegColoring -> ExplicitLocals

This might be possible, but I haven't experimented with this. One thing that comes to mind is if we place block/try marker first and do RegStackify afterwards we may hamper some register stackification opportunities due to those markers, but this also might be solvable with care.

So the short answer is we might be able to, but we have stayed with this order for a long time and I'm not very sure about all the consequences if we change this. I think the original intention of the pipeline is we don't make any CFG-level changes after FixIrreducibleControlFlow. Unfortunately this fixing unwind mismatch transformation broke that assumption, which has to be after CFGStackify marker placement.

1076

Yes.

1098

AFAIK they are placed in two cases:

  • Before and after a call that was previously an invoke
EH_LABEL ...
CALL @foo ...
EH_LABEL
  • In the beginning of an EH pad
EH_LABEL ...
CATCH ...
...

We don't use EH labels for anything for now, but try to preserve the relationship between EH label instruction(s) and their relevant instruction if possible to follow the convention. In this case, calls (previously invokes) are wrapped with EH_LABEL and we don't want to place try or delegate between these EH label instructions and the invoke.

1147–1149

Good catch; I think you are right, there can be a BB like

call $foo ;; unwind to the caller
call $bar ;; unwind to an EH pad (previously invoke)

I changed the code so that we only skip when MBB.isEHPadSuccessor() and the instruction is the last throwing instruction. PTAL.

I think we eventually need to devise a way to carry IR's nounwind attribute to our backend in order to prevent treating too many non-throwing calls to possibly-throwing-to-caller ones.

1212

We do early exit on that case. See line 1178:

// We don't have any unwind destination mismatches to resolve.
if (UnwindDestToTryRanges.empty())
  return false;
aheejin updated this revision to Diff 316979.Jan 15 2021, 9:05 AM
aheejin marked an inline comment as done.
  • Address comments + change delegate's opcode
tlively accepted this revision.Jan 15 2021, 2:57 PM
tlively added inline comments.
llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
921

Got it, thanks for the detail!

1147–1149

Would it be worthwhile to add a test for this case?

This revision is now accepted and ready to land.Jan 15 2021, 2:57 PM
aheejin updated this revision to Diff 317507.Jan 19 2021, 3:26 AM

Add test + Merge two RUN lines into one

aheejin updated this revision to Diff 317512.Jan 19 2021, 3:36 AM

Add a missing LABEL line

aheejin marked an inline comment as done.Jan 19 2021, 5:22 AM
This revision was automatically updated to reflect the committed changes.
sbc100 added a comment.Feb 6 2021, 1:24 PM

I bisected a breaking in emscripten to this change.

Looks like it fails when building libc++-except:
https://logs.chromium.org/logs/emscripten-releases/buildbucket/cr-buildbucket.appspot.com/8856015771238931600/+/steps/Build_Emscripten__upstream_/0/stdout

I can repro locally with ./embuilder build libc++-except --force:

em++: error: '/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang++ -DEMSCRIPTEN -fno-inline-functions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -Xclang -iwithsysroot/include/SDL -target wasm32-unknown-emscripten -D__EMSCRIPTEN_major__=2 -D__EMSCRIPTEN_minor__=0 -D__EMSCRIPTEN_tiny__=13 -D_LIBCPP_ABI_VERSION=2 -Dunix -D__unix -D__unix__ -flegacy-pass-manager -Werror=implicit-function-declaration --sysroot=/usr/local/google/home/sbc/dev/wasm/emscripten/cache/sysroot -Xclang -iwithsysroot/include/compat -Werror -DLIBCXX_BUILDING_LIBCXXABI=1 -D_LIBCPP_BUILDING_LIBRARY -Oz -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -fwasm-exceptions -c /usr/local/google/home/sbc/dev/wasm/emscripten/system/lib/libcxx/src/filesystem/operations.cpp -o /usr/local/google/home/sbc/dev/wasm/emscripten/cache/build/libc++-except/operations.o' failed (134)
clang++: /usr/local/google/home/sbc/dev/wasm/llvm-project/llvm/include/llvm/ADT/SmallVector.h:295: llvm::SmallVectorTemplateCommon::reference llvm::SmallVectorTemplateCommon<const llvm::MachineBasicBlock *>::back() [T = const llvm::MachineBasicBlock *]: Assertion `!empty()' failed.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang++ -DEMSCRIPTEN -fno-inline-functions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -Xclang -iwithsysroot/include/SDL -target wasm32-unknown-emscripten -D__EMSCRIPTEN_major__=2 -D__EMSCRIPTEN_minor__=0 -D__EMSCRIPTEN_tiny__=13 -D_LIBCPP_ABI_VERSION=2 -Dunix -D__unix -D__unix__ -flegacy-pass-manager -Werror=implicit-function-declaration --sysroot=/usr/local/google/home/sbc/dev/wasm/emscripten/cache/sysroot -Xclang -iwithsysroot/include/compat -Werror -DLIBCXX_BUILDING_LIBCXXABI=1 -D_LIBCPP_BUILDING_LIBRARY -Oz -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -fwasm-exceptions -c /usr/local/google/home/sbc/dev/wasm/emscripten/system/lib/libcxx/src/locale.cpp -o /usr/local/google/home/sbc/dev/wasm/emscripten/cache/build/libc++-except/locale.o
1.	<eof> parser at end of file
2.	Code generation
3.	Running pass 'Function Pass Manager' on module '/usr/local/google/home/sbc/dev/wasm/emscripten/system/lib/libcxx/src/locale.cpp'.
4.	Running pass 'WebAssembly CFG Stackify' on function '@_ZNSt3__216__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_'
 #0 0x00007fb9af51eb63 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0x190b63)
 #1 0x00007fb9af51c8ae llvm::sys::RunSignalHandlers() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0x18e8ae)
 #2 0x00007fb9af51dedd llvm::sys::CleanupOnSignal(unsigned long) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0x18fedd)
 #3 0x00007fb9af44f603 (anonymous namespace)::CrashRecoveryContextImpl::HandleCrash(int, unsigned long) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0xc1603)
 #4 0x00007fb9af44f76e CrashRecoverySignalHandler(int) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0xc176e)
 #5 0x00007fb9b323c140 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x14140)
 #6 0x00007fb9af0a6ce1 raise ./signal/../sysdeps/unix/sysv/linux/raise.c:51:1
 #7 0x00007fb9af090537 abort ./stdlib/abort.c:81:7
 #8 0x00007fb9af09040f get_sysdep_segment_value ./intl/loadmsgcat.c:509:8
 #9 0x00007fb9af09040f _nl_load_domain ./intl/loadmsgcat.c:970:34
#10 0x00007fb9af09f662 (/lib/x86_64-linux-gnu/libc.so.6+0x34662)
#11 0x00007fb9b32e4343 (anonymous namespace)::WebAssemblyCFGStackify::placeMarkers(llvm::MachineFunction&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMWebAssemblyCodeGen.so.13git+0x3f343)
#12 0x00007fb9b32de823 (anonymous namespace)::WebAssemblyCFGStackify::runOnMachineFunction(llvm::MachineFunction&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMWebAssemblyCodeGen.so.13git+0x39823)
#13 0x00007fb9b2509dbd llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMCodeGen.so.13git+0x28adbd)
#14 0x00007fb9af8c7658 llvm::FPPassManager::runOnFunction(llvm::Function&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMCore.so.13git+0x21e658)
#15 0x00007fb9af8ce151 llvm::FPPassManager::runOnModule(llvm::Module&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMCore.so.13git+0x225151)
#16 0x00007fb9af8c7cf7 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMCore.so.13git+0x21ecf7)
#17 0x00007fb9b2a97878 clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::DataLayout const&, llvm::Module*, clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream> >) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangCodeGen.so.13git+0xcb878)
#18 0x00007fb9b2dcaaec clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangCodeGen.so.13git+0x3feaec)
#19 0x00007fb9acb4a5e4 clang::ParseAST(clang::Sema&, bool, bool) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/../lib/libclangParse.so.13git+0x365e4)
#20 0x00007fb9b15b5610 clang::FrontendAction::Execute() (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangFrontend.so.13git+0x127610)
#21 0x00007fb9b152fb4a clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangFrontend.so.13git+0xa1b4a)
#22 0x00007fb9b3223914 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangFrontendTool.so.13git+0x4914)
#23 0x0000000000413339 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang+++0x413339)
#24 0x0000000000411202 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang+++0x411202)
#25 0x00007fb9b12e48a2 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, bool*) const::$_1>(long) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangDriver.so.13git+0xa38a2)
#26 0x00007fb9af44f517 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libLLVMSupport.so.13git+0xc1517)
#27 0x00007fb9b12e43f7 clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, bool*) const (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangDriver.so.13git+0xa33f7)
#28 0x00007fb9b12b27bf clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&) const (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangDriver.so.13git+0x717bf)
#29 0x00007fb9b12b2ab7 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*> >&) const (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangDriver.so.13git+0x71ab7)
#30 0x00007fb9b12cabcc clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*> >&) (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/../lib/libclangDriver.so.13git+0x89bcc)
#31 0x0000000000410b5d main (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang+++0x410b5d)
#32 0x00007fb9af091d0a __libc_start_main ./csu/../csu/libc-start.c:308:16
#33 0x000000000040e00a _start (/usr/local/google/home/sbc/dev/wasm/llvm-build/bin/clang+++0x40e00a)
clang-13: error: clang frontend command failed with exit code 134 (use -v to see invocation)
clang version 13.0.0 (https://github.com/llvm/llvm-project.git ed41945faadab27036d368cda9223dc3cb6eb840)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /usr/local/google/home/sbc/dev/wasm/llvm-build/bin
clang-13: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang-13: note: diagnostic msg: /tmp/locale-aa9420.cpp
clang-13: note: diagnostic msg: /tmp/locale-aa9420.sh
clang-13: note: diagnostic msg:

@sbc100 Thanks for letting me know! I thought we disabled all wasm EH builds in Emscripten tests now but didn't know we were still compiling libraries with the EH enabled. This is a bug I should fix in LLVM, but I think there might be more bugs until we can fully compile Emscripten libraries, so I temporarily disabled wasm EH in Emscripten: https://github.com/emscripten-core/emscripten/pull/13439