ldc-developers / ldc

The LLVM-based D Compiler.
http://wiki.dlang.org/LDC
Other
1.2k stars 261 forks source link

WebAssembly Exceptions #4546

Open etcimon opened 9 months ago

etcimon commented 9 months ago

I tried compiling a simple try/catch wasm32-unknown-wasi .d file with --wasm-enable-eh and I get a crash while writing the object file. Is it supported currently?

Thanks

JohanEngelen commented 9 months ago

testcase: https://d.godbolt.org/z/fbMborTez

JohanEngelen commented 9 months ago

I've found the issue. Reproducer with LLC: https://llvm.godbolt.org/z/3bj1rT794

target datalayout = "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm64-unknown-unknown"

define void @foo() personality ptr @_d_eh_personality {
  unreachable
}

declare i32 @_d_eh_personality(i32, i32, i64, ptr, ptr)
declare i32 @__gxx_wasm_personality_v0(...)

_d_eh_personality is not recognized by LLVM as a "scoped EH personality function" https://github.com/llvm/llvm-project/blob/9423e459875b0dcdf24975976838d651a92f1bdb/llvm/lib/CodeGen/MachineFunction.cpp#L240-L243 https://github.com/llvm/llvm-project/blob/9423e459875b0dcdf24975976838d651a92f1bdb/llvm/lib/IR/EHPersonalities.cpp#L23-L46

If we'd use __gxx_wasm_personality_v0 as personality function, then there'd be no crash. But I can't find any implementation for that function, or other documentation.

etcimon commented 9 months ago

Here is the stack trace for a simple try/catch

Assertion failed: R == 0 && "Already initialized this value register!", file F:\Development\llvm-project\llvm\include\llvm/CodeGen/FunctionLoweringInfo.h, line 222


Exception Code: 0x80000003
 #0 0x00007ff7b7e688dc HandleAbort F:\Development\llvm-project\llvm\lib\Support\Windows\Signals.inc:419:0
 #1 0x00007ff80f958e05 (C:\Windows\SYSTEM32\ucrtbased.dll+0xa8e05)
 #2 0x00007ff80f95ab29 (C:\Windows\SYSTEM32\ucrtbased.dll+0xaab29)
 #3 0x00007ff80f961025 (C:\Windows\SYSTEM32\ucrtbased.dll+0xb1025)
 #4 0x00007ff80f9608b7 (C:\Windows\SYSTEM32\ucrtbased.dll+0xb08b7)
 #5 0x00007ff80f95e881 (C:\Windows\SYSTEM32\ucrtbased.dll+0xae881)
 #6 0x00007ff80f96158f (C:\Windows\SYSTEM32\ucrtbased.dll+0xb158f)
 #7 0x00007ff7b53dc03d llvm::FunctionLoweringInfo::InitializeRegForValue(class llvm::Value const *) F:\Development\llvm-project\llvm\include\llvm\CodeGen\FunctionLoweringInfo.h:222:0
 #8 0x00007ff7b53499a2 llvm::SelectionDAGBuilder::getValueImpl(class llvm::Value const *) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGBuilder.cpp:1784:0
 #9 0x00007ff7b53479d9 llvm::SelectionDAGBuilder::getNonRegisterValue(class llvm::Value const *) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGBuilder.cpp:1637:0
#10 0x00007ff7b5343ea2 llvm::SelectionDAGBuilder::CopyValueToVirtualRegister(class llvm::Value const *, unsigned int, enum llvm::ISD::NodeType) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGBuilder.cpp:10485:0
#11 0x00007ff7b534b07f llvm::SelectionDAGBuilder::CopyToExportRegsIfNeeded(class llvm::Value const *) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGBuilder.cpp:2183:0
#12 0x00007ff7b534477c llvm::SelectionDAGBuilder::visit(class llvm::Instruction const &) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGBuilder.cpp:1198:0
#13 0x00007ff7b53f9650 llvm::SelectionDAGISel::SelectBasicBlock(class llvm::ilist_iterator<struct llvm::ilist_detail::node_options<class llvm::Instruction, 1, 0, void>, 0, 1>, class llvm::ilist_iterator<struct llvm::ilist_detail::node_options<class llvm::Instruction, 1, 0, void>, 0, 1>, bool &) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGISel.cpp:692:0
#14 0x00007ff7b53f9209 llvm::SelectionDAGISel::SelectAllBasicBlocks(class llvm::Function const &) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGISel.cpp:1705:0
#15 0x00007ff7b53eb348 llvm::SelectionDAGISel::runOnMachineFunction(class llvm::MachineFunction &) F:\Development\llvm-project\llvm\lib\CodeGen\SelectionDAG\SelectionDAGISel.cpp:483:0
#16 0x00007ff7b34a6c08 `anonymous namespace'::WebAssemblyDAGToDAGISel::runOnMachineFunction F:\Development\llvm-project\llvm\lib\Target\WebAssembly\WebAssemblyISelDAGToDAG.cpp:61:0
#17 0x00007ff7b5b4f2e4 llvm::MachineFunctionPass::runOnFunction(class llvm::Function &) F:\Development\llvm-project\llvm\lib\CodeGen\MachineFunctionPass.cpp:91:0
#18 0x00007ff7b793f68e llvm::FPPassManager::runOnFunction(class llvm::Function &) F:\Development\llvm-project\llvm\lib\IR\LegacyPassManager.cpp:1435:0
#19 0x00007ff7b793fa67 llvm::FPPassManager::runOnModule(class llvm::Module &) F:\Development\llvm-project\llvm\lib\IR\LegacyPassManager.cpp:1481:0
#20 0x00007ff7b7942885 `anonymous namespace'::MPPassManager::runOnModule F:\Development\llvm-project\llvm\lib\IR\LegacyPassManager.cpp:1550:0
#21 0x00007ff7b7943737 llvm::legacy::PassManagerImpl::run(class llvm::Module &) F:\Development\llvm-project\llvm\lib\IR\LegacyPassManager.cpp:535:0
#22 0x00007ff7b793880c llvm::legacy::PassManager::run(class llvm::Module &) F:\Development\llvm-project\llvm\lib\IR\LegacyPassManager.cpp:1678:0
#23 0x00007ff7b2966741 `anonymous namespace'::codegenModule F:\Development\ldc\driver\toobj.cpp:138:0
#24 0x00007ff7b29674e8 `anonymous namespace'::writeObjectFile F:\Development\ldc\driver\toobj.cpp:293:0
#25 0x00007ff7b296602b writeModule(class llvm::Module *, char const *) F:\Development\ldc\driver\toobj.cpp:488:0
#26 0x00007ff7b2852892 ldc::CodeGenerator::writeAndFreeLLModule(char const *) F:\Development\ldc\driver\codegenerator.cpp:294:0
#27 0x00007ff7b28520d4 ldc::CodeGenerator::~CodeGenerator(void) F:\Development\ldc\driver\codegenerator.cpp:223:0
#28 0x00007ff7b2744c9c codegenModules(struct Array<class Module *> &) F:\Development\ldc\driver\main.cpp:1331:0
#29 0x00007ff7b2472e54 mars_mainBody(struct Param &, struct Array<char const *> &, struct Array<char const *> &) F:\Development\ldc\dmd\main.d:753:0
#30 0x00007ff7b27444eb cppmain(void) F:\Development\ldc\driver\main.cpp:1236:0
#31 0x00007ff7b236874c D main F:\Development\ldc\driver\main.d:27:0
#32 0x00007ff7b265884c rt.dmain2._d_run_main2.runAll.__lambda2 D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:506:0
#33 0x00007ff7b265866e _D2rt6dmain212_d_run_main2UAAamPUQgZiZ7tryExecMFMDFZvZv D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:467:0
#34 0x00007ff7b2658766 _D2rt6dmain212_d_run_main2UAAamPUQgZiZ6runAllMFZv D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:506:0
#35 0x00007ff7b265866e _D2rt6dmain212_d_run_main2UAAamPUQgZiZ7tryExecMFMDFZvZv D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:467:0
#36 0x00007ff7b2658064 _d_run_main2 D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:529:0
#37 0x00007ff7b265859b _d_wrun_main D:\a\ldc\ldc\runtime\druntime\src\rt\dmain2.d:375:0
#38 0x00007ff7b2838f93 args::forwardToDruntime(int, wchar_t const **) F:\Development\ldc\driver\args.cpp:82:0
#39 0x00007ff7b2743c0e wmain F:\Development\ldc\driver\main.cpp:1116:0
#40 0x00007ff7b7ff65a4 invoke_main D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:90:0
#41 0x00007ff7b7ff65a4 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288:0
#42 0x00007ff8733c257d (C:\Windows\System32\KERNEL32.DLL+0x1257d)
#43 0x00007ff873acaa58 (C:\Windows\SYSTEM32\ntdll.dll+0x5aa58)```
etcimon commented 9 months ago

I tried compiling with this and it was still giving the same error. Also, changing the function name to the Wasm_CXX __gxx_wasm_personality_v0 name caused an infinite loop.

[..]
      .Case("rust_eh_personality", EHPersonality::Rust)
      .Case("__gxx_wasm_personality_v0", EHPersonality::Wasm_CXX)
      .Case("_d_eh_personality", EHPersonality::DLang)
      .Case("__xlcxx_personality_v1", EHPersonality::XL_CXX)
      .Default(EHPersonality::Unknown);
[..]
  case EHPersonality::Wasm_CXX:
    return "__gxx_wasm_personality_v0";
  case EHPersonality::DLang:
    return "_d_eh_personality";
  case EHPersonality::XL_CXX:
    return "__xlcxx_personality_v1";
  case EHPersonality::Unknown:
[..]
/// Returns true if this personality uses scope-style EH IR instructions:
/// catchswitch, catchpad/ret, and cleanuppad/ret.
inline bool isScopedEHPersonality(EHPersonality Pers) {
  switch (Pers) {
  case EHPersonality::MSVC_CXX:
  case EHPersonality::MSVC_X86SEH:
  case EHPersonality::MSVC_TableSEH:
  case EHPersonality::CoreCLR:
  case EHPersonality::Wasm_CXX:
  case EHPersonality::DLang:
    return true;
  default:
    return false;
  }
  llvm_unreachable("invalid enum");
}

/// Return true if this personality may be safely removed if there
/// are no invoke instructions remaining in the current function.
inline bool isNoOpWithoutInvoke(EHPersonality Pers) {
  switch (Pers) {
  case EHPersonality::Unknown:
  case EHPersonality::DLang:
    return false;
  // All known personalities currently have this behavior
  default:
    return true;
  }
  llvm_unreachable("invalid enum");
}
etcimon commented 9 months ago

More debug info:

Type-legalized selection DAG: %bb.3 '_start:'
SelectionDAG has 4 nodes:
    t0: ch,glue = EntryToken
  t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
Legalizing: t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
Trying custom legalization
Could not custom legalize node
Trying to expand node
Cannot expand node
Trying to convert node to libcall
Could not convert node to libcall
Legalizing: t2: i32 = Register %6
Legalizing: t1: i32 = Constant<1>
Legal node: nothing to do
Legalizing: t0: ch,glue = EntryToken
Legal node: nothing to do
Legalized selection DAG: %bb.3 '_start:'
SelectionDAG has 4 nodes:
    t0: ch,glue = EntryToken
  t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
Legalizing: t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
Trying custom legalization
Could not custom legalize node
Trying to expand node
Cannot expand node
Trying to convert node to libcall
Could not convert node to libcall
Combining: t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
Legalizing: t2: i32 = Register %6
Combining: t2: i32 = Register %6
Legalizing: t1: i32 = Constant<1>
Legal node: nothing to do
Combining: t1: i32 = Constant<1>
Legalizing: t0: ch,glue = EntryToken
Legal node: nothing to do
Combining: t0: ch,glue = EntryToken
Optimized legalized selection DAG: %bb.3 '_start:'
SelectionDAG has 4 nodes:
    t0: ch,glue = EntryToken
  t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
===== Instruction selection begins: %bb.3 ''
ISEL: Starting selection on root node: t3: ch = CopyToReg t0, Register:i32 %6, Constant:i32<1>
ISEL: Starting selection on root node: t2: i32 = Register %6
ISEL: Starting selection on root node: t1: i32 = Constant<1>
ISEL: Starting pattern match
  Initial Opcode index to 14527
  TypeSwitch[i32] from 14528 to 14531
Creating constant: t4: i32 = TargetConstant<1>
  Morphed node: t1: i32 = CONST_I32 TargetConstant:i32<1>
ISEL: Match complete!
ISEL: Starting selection on root node: t0: ch,glue = EntryToken
===== Instruction selection ends:
Selected selection DAG: %bb.3 '_start:'
SelectionDAG has 5 nodes:
    t0: ch,glue = EntryToken
    t1: i32 = CONST_I32 TargetConstant:i32<1>
  t3: ch = CopyToReg t0, Register:i32 %6, t1
********** List Scheduling %bb.3 '' **********
SU(0): t3: ch = CopyToReg t0, Register:i32 %6, t1
  # preds left       : 1
  # succs left       : 0
  # rdefs left       : 0
  Latency            : 1
  Depth              : 1
  Height             : 0
  Predecessors:
    SU(1): Data Latency=1
SU(1): t1: i32 = CONST_I32 TargetConstant:i32<1>
  # preds left       : 0
  # succs left       : 1
  # rdefs left       : 1
  Latency            : 1
  Depth              : 0
  Height             : 1
  Successors:
    SU(0): Data Latency=1
Examining Available:
Height 0: SU(0): t3: ch = CopyToReg t0, Register:i32 %6, t1
*** Scheduling [0]: SU(0): t3: ch = CopyToReg t0, Register:i32 %6, t1
Examining Available:
Height 1: SU(1): t1: i32 = CONST_I32 TargetConstant:i32<1>
*** Scheduling [1]: SU(1): t1: i32 = CONST_I32 TargetConstant:i32<1>
*** Final schedule ***
SU(1): t1: i32 = CONST_I32 TargetConstant:i32<1>
SU(0): t3: ch = CopyToReg t0, Register:i32 %6, t1
FastISel missed (in function: _start)
Assertion failed: R == 0 && "Already initialized this value register!", file F:\Development\llvm-project\llvm\include\llvm/CodeGen/FunctionLoweringInfo.h,
line 222
etcimon commented 9 months ago

When changing to the __gxx_wasm_personality_v0 function, the infinite loop is on the EHPadBB while:

static void findWasmUnwindDestinations(
    FunctionLoweringInfo &FuncInfo, const BasicBlock *EHPadBB,
    BranchProbability Prob,
    SmallVectorImpl<std::pair<MachineBasicBlock *, BranchProbability>>
        &UnwindDests) {
  while (EHPadBB) {
    const Instruction *Pad = EHPadBB->getFirstNonPHI();
    if (isa<CleanupPadInst>(Pad)) {
      // Stop on cleanup pads.
      UnwindDests.emplace_back(FuncInfo.MBBMap[EHPadBB], Prob);
      UnwindDests.back().first->setIsEHScopeEntry();
      break;
    } else if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(Pad)) {
      // Add the catchpad handlers to the possible destinations. We don't
      // continue to the unwind destination of the catchswitch for wasm.
      for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) {
        UnwindDests.emplace_back(FuncInfo.MBBMap[CatchPadBB], Prob);
        UnwindDests.back().first->setIsEHScopeEntry();
      }
      break;
    } else {
      continue;
    }
  }
}
etcimon commented 9 months ago

Seems to be fixed in 1.36.0

etcimon commented 9 months ago

nevermind I didn't have the proper compile flag when testing (has to be in lflags too for dub now)