Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -12,6 +12,7 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyArgumentMove.cpp WebAssemblyAsmPrinter.cpp + WebAssemblyCallIndirectFixup.cpp WebAssemblyCFGStackify.cpp WebAssemblyFastISel.cpp WebAssemblyFixIrreducibleControlFlow.cpp Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -48,6 +48,7 @@ FunctionPass *createWebAssemblyLowerBrUnless(); FunctionPass *createWebAssemblyRegNumbering(); FunctionPass *createWebAssemblyPeephole(); +FunctionPass *createWebAssemblyCallIndirectFixup(); } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp =================================================================== --- /dev/null +++ lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp @@ -0,0 +1,111 @@ +//===-- WebAssemblyCallIndirectFixup.cpp - Fix call_indirects -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements a thing. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssembly.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_* +#include "WebAssemblyMachineFunctionInfo.h" +#include "WebAssemblySubtarget.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "wasm-call-indirect-fixup" + +namespace { +class WebAssemblyCallIndirectFixup final : public MachineFunctionPass { + StringRef getPassName() const override { + return "WebAssembly CallIndirect Fixup"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyCallIndirectFixup() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char WebAssemblyCallIndirectFixup::ID = 0; +FunctionPass *llvm::createWebAssemblyCallIndirectFixup() { + return new WebAssemblyCallIndirectFixup(); +} + +static const unsigned kSignal = WebAssembly::INSTRUCTION_LIST_END; + +static unsigned GetNonPseudoCallIndirectOpcode(const MachineInstr &MI) { + switch (MI.getOpcode()) { + case WebAssembly::PCALL_INDIRECT_VOID: + return WebAssembly::CALL_INDIRECT_VOID; + case WebAssembly::PCALL_INDIRECT_I32: + return WebAssembly::CALL_INDIRECT_I32; + case WebAssembly::PCALL_INDIRECT_I64: + return WebAssembly::CALL_INDIRECT_I64; + case WebAssembly::PCALL_INDIRECT_F32: + return WebAssembly::CALL_INDIRECT_F32; + case WebAssembly::PCALL_INDIRECT_F64: + return WebAssembly::CALL_INDIRECT_F64; + case WebAssembly::PCALL_INDIRECT_v16i8: + return WebAssembly::CALL_INDIRECT_v16i8; + case WebAssembly::PCALL_INDIRECT_v8i16: + return WebAssembly::CALL_INDIRECT_v8i16; + case WebAssembly::PCALL_INDIRECT_v4i32: + return WebAssembly::CALL_INDIRECT_v4i32; + case WebAssembly::PCALL_INDIRECT_v4f32: + return WebAssembly::CALL_INDIRECT_v4f32; + default: + return kSignal; + } +} + +static bool IsPseudoCallIndirect(const MachineInstr &MI) { + return GetNonPseudoCallIndirectOpcode(MI) != kSignal; +} + +bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "********** Fixing up CALL_INDIRECTs **********\n" + << MF.getName() << '\n'); + + bool Changed = false; + const auto *TII = MF.getSubtarget().getInstrInfo(); + + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (IsPseudoCallIndirect(MI)) { + DEBUG(dbgs() << "Found call_indirect: " << MI << '\n'); + const MCInstrDesc &Desc = TII->get(GetNonPseudoCallIndirectOpcode(MI)); + MI.setDesc(Desc); + auto Uses = MI.explicit_uses(); + auto it = Uses.begin(); + auto MO = *it; + unsigned num = MI.getOperandNo(it); + MI.RemoveOperand(num); + MI.addOperand(MF, MO); + DEBUG(dbgs() << " After transform: " << MI); + Changed = true; + } + } + } + + DEBUG(dbgs() << "\nDone with fixing CALL_INDIRECTs\n\n"); + + return Changed; +} + Index: lib/Target/WebAssembly/WebAssemblyFastISel.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -668,7 +668,7 @@ bool IsVoid = FuncTy->getReturnType()->isVoidTy(); unsigned ResultReg; if (IsVoid) { - Opc = IsDirect ? WebAssembly::CALL_VOID : WebAssembly::CALL_INDIRECT_VOID; + Opc = IsDirect ? WebAssembly::CALL_VOID : WebAssembly::PCALL_INDIRECT_VOID; } else { if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy()) return false; @@ -679,39 +679,39 @@ case MVT::i8: case MVT::i16: case MVT::i32: - Opc = IsDirect ? WebAssembly::CALL_I32 : WebAssembly::CALL_INDIRECT_I32; + Opc = IsDirect ? WebAssembly::CALL_I32 : WebAssembly::PCALL_INDIRECT_I32; ResultReg = createResultReg(&WebAssembly::I32RegClass); break; case MVT::i64: - Opc = IsDirect ? WebAssembly::CALL_I64 : WebAssembly::CALL_INDIRECT_I64; + Opc = IsDirect ? WebAssembly::CALL_I64 : WebAssembly::PCALL_INDIRECT_I64; ResultReg = createResultReg(&WebAssembly::I64RegClass); break; case MVT::f32: - Opc = IsDirect ? WebAssembly::CALL_F32 : WebAssembly::CALL_INDIRECT_F32; + Opc = IsDirect ? WebAssembly::CALL_F32 : WebAssembly::PCALL_INDIRECT_F32; ResultReg = createResultReg(&WebAssembly::F32RegClass); break; case MVT::f64: - Opc = IsDirect ? WebAssembly::CALL_F64 : WebAssembly::CALL_INDIRECT_F64; + Opc = IsDirect ? WebAssembly::CALL_F64 : WebAssembly::PCALL_INDIRECT_F64; ResultReg = createResultReg(&WebAssembly::F64RegClass); break; case MVT::v16i8: Opc = - IsDirect ? WebAssembly::CALL_v16i8 : WebAssembly::CALL_INDIRECT_v16i8; + IsDirect ? WebAssembly::CALL_v16i8 : WebAssembly::PCALL_INDIRECT_v16i8; ResultReg = createResultReg(&WebAssembly::V128RegClass); break; case MVT::v8i16: Opc = - IsDirect ? WebAssembly::CALL_v8i16 : WebAssembly::CALL_INDIRECT_v8i16; + IsDirect ? WebAssembly::CALL_v8i16 : WebAssembly::PCALL_INDIRECT_v8i16; ResultReg = createResultReg(&WebAssembly::V128RegClass); break; case MVT::v4i32: Opc = - IsDirect ? WebAssembly::CALL_v4i32 : WebAssembly::CALL_INDIRECT_v4i32; + IsDirect ? WebAssembly::CALL_v4i32 : WebAssembly::PCALL_INDIRECT_v4i32; ResultReg = createResultReg(&WebAssembly::V128RegClass); break; case MVT::v4f32: Opc = - IsDirect ? WebAssembly::CALL_v4f32 : WebAssembly::CALL_INDIRECT_v4f32; + IsDirect ? WebAssembly::CALL_v4f32 : WebAssembly::PCALL_INDIRECT_v4f32; ResultReg = createResultReg(&WebAssembly::V128RegClass); break; default: Index: lib/Target/WebAssembly/WebAssemblyInstrCall.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -29,9 +29,12 @@ def CALL_#vt : I<(outs vt:$dst), (ins i32imm:$callee, variable_ops), [(set vt:$dst, (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee")>; - def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops), + def PCALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops), [(set vt:$dst, (WebAssemblycall1 I32:$callee))], - !strconcat(prefix, "call_indirect\t$dst, $callee")>; + "PSEUDO ONLY">; + def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins variable_ops), + [], + !strconcat(prefix, "call_indirect\t$dst")>; } multiclass SIMD_CALL { @@ -39,12 +42,15 @@ [(set (vt V128:$dst), (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee")>; - def CALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), + def PCALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), (ins I32:$callee, variable_ops), [(set (vt V128:$dst), (WebAssemblycall1 I32:$callee))], - !strconcat(prefix, - "call_indirect\t$dst, $callee")>; + "PSEUDO ONLY">; + def CALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), + (ins variable_ops), + [], + !strconcat(prefix, "call_indirect\t$dst")>; } let Uses = [SP32, SP64], isCall = 1 in { @@ -60,9 +66,12 @@ def CALL_VOID : I<(outs), (ins i32imm:$callee, variable_ops), [(WebAssemblycall0 (i32 imm:$callee))], "call \t$callee">; - def CALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops), - [(WebAssemblycall0 I32:$callee)], - "call_indirect\t$callee">; + def PCALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops), + [(WebAssemblycall0 I32:$callee)], + "PSEUDO ONLY">; + def CALL_INDIRECT_VOID : I<(outs), (ins variable_ops), + [], + "call_indirect\t">; } // Uses = [SP32,SP64], isCall = 1 } // Defs = [ARGUMENTS] Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -239,6 +239,8 @@ // Prepare store instructions for register stackifying. addPass(createWebAssemblyStoreResults()); + addPass(createWebAssemblyCallIndirectFixup()); + // Mark registers as representing wasm's value stack. This is a key // code-compression technique in WebAssembly. We run this pass (and // StoreResults above) very late, so that it sees as much code as possible, Index: test/CodeGen/WebAssembly/call.ll =================================================================== --- test/CodeGen/WebAssembly/call.ll +++ test/CodeGen/WebAssembly/call.ll @@ -97,6 +97,24 @@ ret i32 %t } +; CHECK-LABEL: call_indirect_arg: +; CHECK-NEXT: .param i32, i32{{$}} +; CHECK-NEXT: {{^}} call_indirect $1, $0{{$}} +; CHECK-NEXT: return{{$}} +define void @call_indirect_arg(void (i32)* %callee, i32 %arg) { + call void %callee(i32 %arg) + ret void +} + +; CHECK-LABEL: call_indirect_arg_2: +; CHECK-NEXT: .param i32, i32, i32{{$}} +; CHECK-NEXT: {{^}} i32.call_indirect $drop=, $1, $2, $0{{$}} +; CHECK-NEXT: return{{$}} +define void @call_indirect_arg_2(i32 (i32, i32)* %callee, i32 %arg, i32 %arg2) { + call i32 %callee(i32 %arg, i32 %arg2) + ret void +} + ; CHECK-LABEL: tail_call_void_nullary: ; CHECK-NEXT: {{^}} call void_nullary@FUNCTION{{$}} ; CHECK-NEXT: return{{$}}