diff options
author | Derek Schuff <dschuff@google.com> | 2016-10-21 16:38:07 +0000 |
---|---|---|
committer | Derek Schuff <dschuff@google.com> | 2016-10-21 16:38:07 +0000 |
commit | 6f69783f1fa51cd748d0439b17da3e3ee23cc3f8 (patch) | |
tree | d9ad8bff62eca303a04816379f4e4e34b5464a33 /llvm/lib/Target/WebAssembly | |
parent | 9daf8110c82f5245e5e263e8a03ce745e6da5f40 (diff) | |
download | bcm5719-llvm-6f69783f1fa51cd748d0439b17da3e3ee23cc3f8.tar.gz bcm5719-llvm-6f69783f1fa51cd748d0439b17da3e3ee23cc3f8.zip |
[WebAssembly] Fix for 0xc call_indirect changes
Summary:
Need to reorder the operands to have the callee as the last argument.
Adds a pseudo-instruction, and a pass to lower it into a real
call_indirect.
This is the first of two options for how to fix the problem.
Reviewers: dschuff, sunfish
Subscribers: jfb, beanz, mgorny, llvm-commits
Differential Revision: https://reviews.llvm.org/D25708
llvm-svn: 284840
Diffstat (limited to 'llvm/lib/Target/WebAssembly')
6 files changed, 159 insertions, 20 deletions
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index 61c1d38f882..582269b8011 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -12,6 +12,7 @@ add_public_tablegen_target(WebAssemblyCommonTableGen) add_llvm_target(WebAssemblyCodeGen WebAssemblyArgumentMove.cpp WebAssemblyAsmPrinter.cpp + WebAssemblyCallIndirectFixup.cpp WebAssemblyCFGStackify.cpp WebAssemblyFastISel.cpp WebAssemblyFixIrreducibleControlFlow.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index c40e6c41e40..55e73ebd974 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -48,6 +48,7 @@ FunctionPass *createWebAssemblyCFGStackify(); FunctionPass *createWebAssemblyLowerBrUnless(); FunctionPass *createWebAssemblyRegNumbering(); FunctionPass *createWebAssemblyPeephole(); +FunctionPass *createWebAssemblyCallIndirectFixup(); } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp new file mode 100644 index 00000000000..31cef3c5457 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCallIndirectFixup.cpp @@ -0,0 +1,117 @@ +//===-- 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 converts pseudo call_indirect instructions into real +/// call_indirects. +/// +/// The order of arguments for a call_indirect is the arguments to the function +/// call, followed by the function pointer. There's no natural way to express +/// a machineinstr with varargs followed by one more arg, so we express it as +/// the function pointer followed by varargs, then rewrite it here. +/// +/// We need to rewrite the order of the arguments on the machineinstrs +/// themselves so that register stackification knows the order they'll be +/// executed in. +/// +//===----------------------------------------------------------------------===// + +#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 unsigned GetNonPseudoCallIndirectOpcode(const MachineInstr &MI) { + switch (MI.getOpcode()) { + using namespace WebAssembly; + case PCALL_INDIRECT_VOID: return CALL_INDIRECT_VOID; + case PCALL_INDIRECT_I32: return CALL_INDIRECT_I32; + case PCALL_INDIRECT_I64: return CALL_INDIRECT_I64; + case PCALL_INDIRECT_F32: return CALL_INDIRECT_F32; + case PCALL_INDIRECT_F64: return CALL_INDIRECT_F64; + case PCALL_INDIRECT_v16i8: return CALL_INDIRECT_v16i8; + case PCALL_INDIRECT_v8i16: return CALL_INDIRECT_v8i16; + case PCALL_INDIRECT_v4i32: return CALL_INDIRECT_v4i32; + case PCALL_INDIRECT_v4f32: return CALL_INDIRECT_v4f32; + default: return INSTRUCTION_LIST_END; + } +} + +static bool IsPseudoCallIndirect(const MachineInstr &MI) { + return GetNonPseudoCallIndirectOpcode(MI) != + WebAssembly::INSTRUCTION_LIST_END; +} + +bool WebAssemblyCallIndirectFixup::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "********** Fixing up CALL_INDIRECTs **********\n" + << MF.getName() << '\n'); + + bool Changed = false; + const WebAssemblyInstrInfo *TII = + MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); + + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (IsPseudoCallIndirect(MI)) { + DEBUG(dbgs() << "Found call_indirect: " << MI << '\n'); + + // Rewrite pseudo to non-pseudo + const MCInstrDesc &Desc = TII->get(GetNonPseudoCallIndirectOpcode(MI)); + MI.setDesc(Desc); + + // Rewrite argument order + auto Uses = MI.explicit_uses(); + MachineInstr::mop_iterator it = Uses.begin(); + const MachineOperand MO = *it; + unsigned num = MI.getOperandNo(it); + MI.RemoveOperand(num); + MI.addOperand(MF, MO); + + DEBUG(dbgs() << " After transform: " << MI); + Changed = true; + } + } + } + + DEBUG(dbgs() << "\nDone fixing up CALL_INDIRECTs\n\n"); + + return Changed; +} + diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp index 64d4dae9647..8575603075b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -668,7 +668,7 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) { 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 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) { 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: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td index 4bbe2d7443e..47459f85a16 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -29,9 +29,14 @@ multiclass CALL<WebAssemblyRegClass vt, string prefix> { 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), - [(set vt:$dst, (WebAssemblycall1 I32:$callee))], - !strconcat(prefix, "call_indirect\t$dst, $callee")>; + let isCodeGenOnly = 1 in { + def PCALL_INDIRECT_#vt : I<(outs vt:$dst), (ins I32:$callee, variable_ops), + [(set vt:$dst, (WebAssemblycall1 I32:$callee))], + "PSEUDO CALL INDIRECT\t$callee">; + } // isCodeGenOnly = 1 + def CALL_INDIRECT_#vt : I<(outs vt:$dst), (ins variable_ops), + [], + !strconcat(prefix, "call_indirect\t$dst")>; } multiclass SIMD_CALL<ValueType vt, string prefix> { @@ -39,12 +44,17 @@ multiclass SIMD_CALL<ValueType vt, string prefix> { [(set (vt V128:$dst), (WebAssemblycall1 (i32 imm:$callee)))], !strconcat(prefix, "call\t$dst, $callee")>; + let isCodeGenOnly = 1 in { + def PCALL_INDIRECT_#vt : SIMD_I<(outs V128:$dst), + (ins I32:$callee, variable_ops), + [(set (vt V128:$dst), + (WebAssemblycall1 I32:$callee))], + "PSEUDO CALL INDIRECT\t$callee">; + } // isCodeGenOnly = 1 def CALL_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")>; + (ins variable_ops), + [], + !strconcat(prefix, "call_indirect\t$dst")>; } let Uses = [SP32, SP64], isCall = 1 in { @@ -60,9 +70,14 @@ let Uses = [SP32, SP64], isCall = 1 in { 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">; + let isCodeGenOnly = 1 in { + def PCALL_INDIRECT_VOID : I<(outs), (ins I32:$callee, variable_ops), + [(WebAssemblycall0 I32:$callee)], + "PSEUDO CALL INDIRECT\t$callee">; + } // isCodeGenOnly = 1 + def CALL_INDIRECT_VOID : I<(outs), (ins variable_ops), + [], + "call_indirect\t">; } // Uses = [SP32,SP64], isCall = 1 } // Defs = [ARGUMENTS] diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index fe987f9e99d..e8fbe941345 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -229,6 +229,11 @@ void WebAssemblyPassConfig::addPreEmitPass() { // colored, and numbered with the rest of the registers. addPass(createWebAssemblyReplacePhysRegs()); + // Rewrite pseudo call_indirect instructions as real instructions. + // This needs to run before register stackification, because we change the + // order of the arguments. + addPass(createWebAssemblyCallIndirectFixup()); + if (getOptLevel() != CodeGenOpt::None) { // LiveIntervals isn't commonly run this late. Re-establish preconditions. addPass(createWebAssemblyPrepareForLiveIntervals()); |