diff --git a/lib/Target/WebAssembly/CMakeLists.txt b/lib/Target/WebAssembly/CMakeLists.txt index 5d1a27a6f09..b5177265704 100644 --- a/lib/Target/WebAssembly/CMakeLists.txt +++ b/lib/Target/WebAssembly/CMakeLists.txt @@ -11,6 +11,7 @@ add_public_tablegen_target(WebAssemblyCommonTableGen) add_llvm_target(WebAssemblyCodeGen Relooper.cpp + WebAssemblyArgumentMove.cpp WebAssemblyAsmPrinter.cpp WebAssemblyCFGStackify.cpp WebAssemblyFastISel.cpp diff --git a/lib/Target/WebAssembly/WebAssembly.h b/lib/Target/WebAssembly/WebAssembly.h index 6705b22a376..f8d0a1ccdc2 100644 --- a/lib/Target/WebAssembly/WebAssembly.h +++ b/lib/Target/WebAssembly/WebAssembly.h @@ -27,6 +27,7 @@ FunctionPass *createWebAssemblyOptimizeReturned(); FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, CodeGenOpt::Level OptLevel); +FunctionPass *createWebAssemblyArgumentMove(); FunctionPass *createWebAssemblyStoreResults(); FunctionPass *createWebAssemblyRegStackify(); diff --git a/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp b/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp new file mode 100644 index 00000000000..3893c408cf6 --- /dev/null +++ b/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp @@ -0,0 +1,110 @@ +//===-- WebAssemblyArgumentMove.cpp - Argument instruction moving ---------===// +// +// 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 moves ARGUMENT instructions after ScheduleDAG scheduling. +/// +/// Arguments are really live-in registers, however, since we use virtual +/// registers and LLVM doesn't support live-in virtual registers, we're +/// currently making do with ARGUMENT instructions which are placed at the top +/// of the entry block. The trick is to get them to *stay* at the top of the +/// entry block. +/// +/// The ARGUMENTS physical register keeps these instructions pinned in place +/// during liveness-aware CodeGen passes, however one thing which does not +/// respect this is the ScheduleDAG scheduler. This pass is therefore run +/// immediately after that. +/// +/// This is all hopefully a temporary solution until we find a better solution +/// for describing the live-in nature of arguments. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssembly.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssemblyMachineFunctionInfo.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.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-argument-move" + +namespace { +class WebAssemblyArgumentMove final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyArgumentMove() : MachineFunctionPass(ID) {} + + const char *getPassName() const override { + return "WebAssembly Argument Move"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addPreserved(); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char WebAssemblyArgumentMove::ID = 0; +FunctionPass *llvm::createWebAssemblyArgumentMove() { + return new WebAssemblyArgumentMove(); +} + +/// Test whether the given instruction is an ARGUMENT. +static bool IsArgument(const MachineInstr *MI) { + switch (MI->getOpcode()) { + case WebAssembly::ARGUMENT_I32: + case WebAssembly::ARGUMENT_I64: + case WebAssembly::ARGUMENT_F32: + case WebAssembly::ARGUMENT_F64: + return true; + default: + return false; + } +} + +bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) { + DEBUG({ + dbgs() << "********** Argument Move **********\n" + << "********** Function: " << MF.getName() << '\n'; + }); + + bool Changed = false; + MachineBasicBlock &EntryMBB = MF.front(); + MachineBasicBlock::iterator InsertPt = EntryMBB.end(); + + // Look for the first NonArg instruction. + for (auto MII = EntryMBB.begin(), MIE = EntryMBB.end(); MII != MIE; ++MII) { + MachineInstr *MI = MII; + if (!IsArgument(MI)) { + InsertPt = MII; + break; + } + } + + // Now move any argument instructions later in the block + // to before our first NonArg instruction. + for (auto I = InsertPt, E = EntryMBB.end(); I != E; ++I) { + MachineInstr *MI = I; + if (IsArgument(MI)) { + EntryMBB.insert(InsertPt, MI->removeFromParent()); + Changed = true; + } + } + + return Changed; +} diff --git a/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index 917dfacfe9d..a333fb3055e 100644 --- a/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -149,6 +149,10 @@ bool WebAssemblyPassConfig::addInstSelector() { (void)TargetPassConfig::addInstSelector(); addPass( createWebAssemblyISelDag(getWebAssemblyTargetMachine(), getOptLevel())); + // Run the argument-move pass immediately after the ScheduleDAG scheduler + // so that we can fix up the ARGUMENT instructions before anything else + // sees them in the wrong place. + addPass(createWebAssemblyArgumentMove()); return false; } diff --git a/test/CodeGen/WebAssembly/dead-vreg.ll b/test/CodeGen/WebAssembly/dead-vreg.ll index cf1415c1982..b03e1569fde 100644 --- a/test/CodeGen/WebAssembly/dead-vreg.ll +++ b/test/CodeGen/WebAssembly/dead-vreg.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s ; Check that unused vregs aren't assigned registers. diff --git a/test/CodeGen/WebAssembly/phi.ll b/test/CodeGen/WebAssembly/phi.ll index abbc1c59af3..bae8a7c9e3b 100644 --- a/test/CodeGen/WebAssembly/phi.ll +++ b/test/CodeGen/WebAssembly/phi.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s ; Test that phis are lowered. diff --git a/test/CodeGen/WebAssembly/reg-stackify.ll b/test/CodeGen/WebAssembly/reg-stackify.ll index af4a3501531..3c343434836 100644 --- a/test/CodeGen/WebAssembly/reg-stackify.ll +++ b/test/CodeGen/WebAssembly/reg-stackify.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s ; Test the register stackifier pass. diff --git a/test/CodeGen/WebAssembly/switch.ll b/test/CodeGen/WebAssembly/switch.ll index b146a239b41..7f6f6efff7d 100644 --- a/test/CodeGen/WebAssembly/switch.ll +++ b/test/CodeGen/WebAssembly/switch.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -disable-block-placement | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-block-placement -verify-machineinstrs | FileCheck %s ; Test switch instructions. Block placement is disabled because it reorders ; the blocks in a way that isn't interesting here. diff --git a/test/CodeGen/WebAssembly/unreachable.ll b/test/CodeGen/WebAssembly/unreachable.ll index e14c58876ed..414767e5c35 100644 --- a/test/CodeGen/WebAssembly/unreachable.ll +++ b/test/CodeGen/WebAssembly/unreachable.ll @@ -1,5 +1,5 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s -; RUN: llc < %s -asm-verbose=false -fast-isel | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -asm-verbose=false -fast-isel -verify-machineinstrs | FileCheck %s ; Test that LLVM unreachable instruction and trap intrinsic are lowered to ; wasm unreachable diff --git a/test/CodeGen/WebAssembly/varargs.ll b/test/CodeGen/WebAssembly/varargs.ll index bda0dd779e6..10846f2a989 100644 --- a/test/CodeGen/WebAssembly/varargs.ll +++ b/test/CodeGen/WebAssembly/varargs.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false | FileCheck %s +; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s ; Test varargs constructs.