Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6043,6 +6043,12 @@ case Intrinsic::experimental_vector_reduce_fmin: visitVectorReduce(I, Intrinsic); return nullptr; + + case Intrinsic::wasm_eh_landingpad_index: { + // TODO + return nullptr; + } + } } Index: lib/Target/WebAssembly/WebAssemblyCFGSort.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGSort.cpp +++ lib/Target/WebAssembly/WebAssemblyCFGSort.cpp @@ -66,6 +66,9 @@ #endif bool AllAnalyzable = true; for (const MachineInstr &Term : MBB->terminators()) { + // We shouldn't update if_except terminator. + if (Term.getOpcode() == WebAssembly::IF_EXCEPT) + return; #ifndef NDEBUG AnyBarrier |= Term.isBarrier(); #endif Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -90,6 +90,7 @@ SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -151,6 +151,9 @@ // Trap lowers to wasm unreachable setOperationAction(ISD::TRAP, MVT::Other, Legal); + // Exception handling intrinsics + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + setMaxAtomicSizeInBitsSupported(64); } @@ -300,6 +303,98 @@ return DoneMBB; } +// Lower +// %val = wasm.catch.extract(); +// into +// %exn = catch +// if_except %exn 0 +// then +// %val1 = get_except_stack +// else +// %val2 = _Unwind_GetExceptionProxy(); +// end_if_except +// %val = phi %val1, %val2 +// +// - if_except's immediate operand 0 is the tag for C++ exception. +// - if_except queries if the except_ref object returned by catch instruction is +// a C++ exception (i.e., matches tag 0) or not. If the exception is a C++ +// exception, if_except extracts the original value thrown from the except_ref +// object and pushes it onto the stack. Nothing is pushed if it is not a C++ +// exception. +// - In LLVM instructions, We cannot express the behavior that an instruction +// pushes a value onto the stack only under a certain condition. +// get_except_stack is a fake pseudo instruction assumed to get the extracted +// value from the stack when if_except is satisfied. This pseudo instruction +// will be deleted later. +// - _Unwind_GetExceptionProxy() is a function in Wasm unwind library that +// returns a fake proxy exception object in case we catch a foreign exception. +static MachineBasicBlock *LowerCatchExtract(MachineInstr &MI, + MachineBasicBlock *MBB, + const TargetInstrInfo &TII, + bool Int64) { + MachineRegisterInfo &MRI = MBB->getParent()->getRegInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + unsigned ExnValReg = MI.getOperand(0).getReg(); + + // Create BBs + MachineFunction *MF = MBB->getParent(); + const BasicBlock *BB = MBB->getBasicBlock(); + MachineBasicBlock *TrueMBB = MF->CreateMachineBasicBlock(BB); + MachineBasicBlock *FalseMBB = MF->CreateMachineBasicBlock(BB); + MachineBasicBlock *DoneMBB = MF->CreateMachineBasicBlock(BB); + MachineFunction::iterator It = ++MBB->getIterator(); + MF->insert(It, TrueMBB); + MF->insert(It, FalseMBB); + MF->insert(It, DoneMBB); + // Transfer the remainder of BB and its successor edges to DoneMBB. + DoneMBB->splice(DoneMBB->begin(), MBB, + std::next(MachineBasicBlock::iterator(MI)), MBB->end()); + DoneMBB->transferSuccessorsAndUpdatePHIs(MBB); + MBB->addSuccessor(TrueMBB); + MBB->addSuccessor(FalseMBB); + TrueMBB->addSuccessor(DoneMBB); + FalseMBB->addSuccessor(DoneMBB); + + // catch / if_except + unsigned ExnReg = MRI.createVirtualRegister(&WebAssembly::EXCEPT_REFRegClass); + BuildMI(*MBB, MI, DL, TII.get(WebAssembly::CATCH), ExnReg); + BuildMI(*MBB, MI, DL, TII.get(WebAssembly::IF_EXCEPT)) + .addImm(0) // Tag for C++ exceptions + .addReg(ExnReg) + .addImm(int64_t(WebAssembly::ExprType::Void)); + MI.eraseFromParent(); + + // if_except then part + BuildMI(TrueMBB, DL, TII.get(WebAssembly::THEN)); + unsigned GetExceptStackOpcode = Int64 ? WebAssembly::GET_EXCEPT_STACK_I64 + : WebAssembly::GET_EXCEPT_STACK_I32; + + unsigned TrueReg = MRI.createVirtualRegister(MRI.getRegClass(ExnValReg)); + BuildMI(TrueMBB, DL, TII.get(GetExceptStackOpcode), TrueReg); + BuildMI(TrueMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); + + // if_except else part + BuildMI(FalseMBB, DL, TII.get(WebAssembly::ELSE)); + unsigned CallOpcode = Int64 ? WebAssembly::CALL_I64 : WebAssembly::CALL_I32; + unsigned FalseReg = MRI.createVirtualRegister(MRI.getRegClass(ExnValReg)); + const Module *M = MF->getFunction().getParent(); + BuildMI(FalseMBB, DL, TII.get(CallOpcode), FalseReg) + .addGlobalAddress(M->getNamedValue("_Unwind_GetExceptionProxy")); + BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); + + // phi / end_if_except + BuildMI(*DoneMBB, DoneMBB->getFirstNonPHI(), DL, TII.get(WebAssembly::PHI), + ExnValReg) + .addReg(TrueReg) + .addMBB(TrueMBB) + .addReg(FalseReg) + .addMBB(FalseMBB); + BuildMI(*DoneMBB, DoneMBB->getFirstNonPHI(), DL, + TII.get(WebAssembly::END_IF_EXCEPT)); + + return DoneMBB; +} + MachineBasicBlock * WebAssemblyTargetLowering::EmitInstrWithCustomInserter( MachineInstr &MI, @@ -334,7 +429,10 @@ case WebAssembly::FP_TO_UINT_I64_F64: return LowerFPToInt(MI, DL, BB, TII, true, true, true, WebAssembly::I64_TRUNC_U_F64); - llvm_unreachable("Unexpected instruction to emit with custom inserter"); + case WebAssembly::CATCH_EXTRACT_I32: + return LowerCatchExtract(MI, BB, TII, false); + case WebAssembly::CATCH_EXTRACT_I64: + return LowerCatchExtract(MI, BB, TII, true); } } @@ -737,6 +835,8 @@ return LowerFRAMEADDR(Op, DAG); case ISD::CopyToReg: return LowerCopyToReg(Op, DAG); + case ISD::INTRINSIC_WO_CHAIN: + return LowerINTRINSIC_WO_CHAIN(Op, DAG); } } @@ -869,6 +969,21 @@ MachinePointerInfo(SV), 0); } +SDValue +WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = cast(Op.getOperand(0))->getZExtValue(); + SDLoc DL(Op); + switch (IntNo) { + default: + return SDValue(); // Don't custom lower most intrinsics. + + case Intrinsic::wasm_eh_lsda: + // TODO For now, just return 0 not to crash + return DAG.getConstant(0, DL, Op.getValueType()); + } +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -130,12 +130,49 @@ 0x09>; } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 +// Quering an exception tag: if_except / then / else / end_if_except +// Retrieving an extracted exception value within if_except block: +// get_except_black (pseudo instruction) +let hasCtrlDep = 1 in { +let isBranch = 1, isTerminator = 1, hasSideEffects = 1 in +def IF_EXCEPT : I<(outs), + (ins i32imm:$tag, EXCEPT_REF:$obj, Signature:$sig), [], + "if_except\t$tag, $obj", 0x0a>; +def ELSE : I<(outs), (ins), [], "else", 0x05>; +def END_IF_EXCEPT : I<(outs), (ins), [], "end_if_except", 0x0b>; + +let isCodeGenOnly = 1 in { +def THEN : I<(outs), (ins), [], "then", 0>; +def GET_EXCEPT_STACK_I32 : I<(outs I32:$dst), (ins), [], + "get_except_stack $dst", 0>; +def GET_EXCEPT_STACK_I64 : I<(outs I64:$dst), (ins), [], + "get_except_stack $dst", 0>; +} +} + // Region within which an exception is caught: try / end_try let Uses = [VALUE_STACK], Defs = [VALUE_STACK] in { -def TRY : I<(outs), (ins Signature:$sig), [], "try \t$sig", 0x06>; +def TRY : I<(outs), (ins Signature:$sig), [], "try \t$sig", 0x06>; def END_TRY : I<(outs), (ins), [], "end_try", 0x0b>; } // Uses = [VALUE_STACK], Defs = [VALUE_STACK] +// Catching an exception: catch / catch_extract +// catch returns except_ref type object. +// catch_extract is a pseudo instruction to which calls to wasm.catch.extract +// intrinsic can be lowered into. This is a pseudo instruction that will be +// further lowered into a sequence of catch / if_except / else / end_if_except. +let hasCtrlDep = 1 in { +def CATCH : I<(outs EXCEPT_REF:$dst), (ins), [], "catch \t$dst", 0x07>; +let isPseudo = 1, usesCustomInserter = 1 in { +def CATCH_EXTRACT_I32 : I<(outs I32:$dst), (ins), + [(set I32:$dst, (int_wasm_catch_extract))], + "", 0>; +def CATCH_EXTRACT_I64 : I<(outs I64:$dst), (ins), + [(set I64:$dst, (int_wasm_catch_extract))], + "", 0>; +} // isPseudo = 1, usesCustomInserter = 1 +} // hasCtrlDep = 1 + } // Defs = [ARGUMENTS] // rethrow takes a relative depth as an argument, for which currently only 0 is Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -134,6 +134,10 @@ else FBB = MI.getOperand(0).getMBB(); break; + case WebAssembly::IF_EXCEPT: + // if_except shouldn't be converted or optimized into another branch + // instructions. + return true; } if (MI.isBarrier()) break; Index: test/CodeGen/WebAssembly/exception.ll =================================================================== --- test/CodeGen/WebAssembly/exception.ll +++ test/CodeGen/WebAssembly/exception.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -exception-model=wasm | FileCheck --match-full-lines %s target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" @@ -20,3 +20,39 @@ call void @llvm.wasm.rethrow() ret void } + +; CHECK-LABEL: catch: +; CHECK: call maythrow@FUNCTION +; CHECK: catch $push[[NUM0:[0-9]+]]= +; CHECK-NEXT: if_except 0, $pop[[NUM0]] +; CHECK: then +; CHECK-NEXT: get_except_stack $push[[NUM1:[0-9]+]]= +; CHECK-NEXT: set_local [[LOCAL0:[0-9]+]], $pop[[NUM1]] +; CHECK: else +; CHECK-NEXT: i32.call $push[[NUM2:[0-9]+]]=, _Unwind_GetExceptionProxy@FUNCTION +; CHECK-NEXT: set_local [[LOCAL0:[0-9]+]], $pop[[NUM2]] +; CHECK: end_if_except +; CHECK: get_local $push[[NUM3:[0-9]+]]=, [[LOCAL0]] +; CHECK-NEXT: i32.call $push{{[0-9]+}}=, _Unwind_CallPersonality@FUNCTION, $pop[[NUM3]] +define void @catch() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: + invoke void @maythrow() + to label %try.cont unwind label %lpad + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } + catch i8* null + %1 = extractvalue { i8*, i32 } %0, 0 + %2 = extractvalue { i8*, i32 } %0, 1 + %3 = call i8* @__cxa_begin_catch(i8* %1) #3 + call void @__cxa_end_catch() + br label %try.cont + +try.cont: ; preds = %entry, %lpad + ret void +} + +declare i32 @__gxx_personality_v0(...) +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() +declare void @maythrow()