diff options
author | Dan Gohman <dan433584@gmail.com> | 2016-10-24 19:49:43 +0000 |
---|---|---|
committer | Dan Gohman <dan433584@gmail.com> | 2016-10-24 19:49:43 +0000 |
commit | 4fc4e42dea2547f40d3672ccf473fc2fbbdfc903 (patch) | |
tree | ebcc8d0e10c97409e41a89e296db5aca2f0ce7fc /llvm/lib | |
parent | c3e0ce8f8527d340c168d7361c1c59d8df544ecb (diff) | |
download | bcm5719-llvm-4fc4e42dea2547f40d3672ccf473fc2fbbdfc903.tar.gz bcm5719-llvm-4fc4e42dea2547f40d3672ccf473fc2fbbdfc903.zip |
[WebAssembly] Add an option to make get_local/set_local explicit.
This patch adds a pass, controlled by an option and off by default for
now, for making implicit get_local/set_local explicit. This simplifies
emitting wasm with MC.
Differential Revision: https://reviews.llvm.org/D25836
llvm-svn: 285009
Diffstat (limited to 'llvm/lib')
22 files changed, 632 insertions, 177 deletions
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt index 582269b8011..70c95b94cab 100644 --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -14,6 +14,7 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyAsmPrinter.cpp WebAssemblyCallIndirectFixup.cpp WebAssemblyCFGStackify.cpp + WebAssemblyExplicitLocals.cpp WebAssemblyFastISel.cpp WebAssemblyFixIrreducibleControlFlow.cpp WebAssemblyFrameLowering.cpp @@ -40,6 +41,7 @@ add_llvm_target(WebAssemblyCodeGen WebAssemblyTargetMachine.cpp WebAssemblyTargetObjectFile.cpp WebAssemblyTargetTransformInfo.cpp + WebAssemblyUtilities.cpp ) add_dependencies(LLVMWebAssemblyCodeGen intrinsics_gen) diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp index 715d57d1531..b4763ca60ab 100644 --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -93,6 +93,7 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction( const MCOperandInfo &Info = Desc.OpInfo[i]; switch (Info.OperandType) { case MCOI::OPERAND_IMMEDIATE: + case WebAssembly::OPERAND_LOCAL: case WebAssembly::OPERAND_P2ALIGN: case WebAssembly::OPERAND_BASIC_BLOCK: { if (Pos + sizeof(uint64_t) > Bytes.size()) diff --git a/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp index d7e504f5d10..9b59c7554bd 100644 --- a/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -149,16 +149,27 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, if (OpNo < MII.get(MI->getOpcode()).getNumDefs()) O << '='; } else if (Op.isImm()) { - assert((OpNo < MII.get(MI->getOpcode()).getNumOperands() || - (MII.get(MI->getOpcode()).TSFlags & - WebAssemblyII::VariableOpIsImmediate)) && + const MCInstrDesc &Desc = MII.get(MI->getOpcode()); + assert((OpNo < Desc.getNumOperands() || + (Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate)) && "WebAssemblyII::VariableOpIsImmediate should be set for " "variable_ops immediate ops"); - // TODO: (MII.get(MI->getOpcode()).TSFlags & - // WebAssemblyII::VariableOpImmediateIsLabel) - // can tell us whether this is an immediate referencing a label in the - // control flow stack, and it may be nice to pretty-print. - O << Op.getImm(); + + if (Desc.TSFlags & WebAssemblyII::VariableOpImmediateIsType) { + switch (Op.getImm()) { + case int64_t(WebAssembly::ValType::I32): O << "i32"; break; + case int64_t(WebAssembly::ValType::I64): O << "i64"; break; + case int64_t(WebAssembly::ValType::F32): O << "f32"; break; + case int64_t(WebAssembly::ValType::F64): O << "f64"; break; + default: llvm_unreachable("unknown local type"); + } + } else { + // TODO: (MII.get(MI->getOpcode()).TSFlags & + // WebAssemblyII::VariableOpImmediateIsLabel) + // can tell us whether this is an immediate referencing a label in the + // control flow stack, and it may be nice to pretty-print. + O << Op.getImm(); + } } else if (Op.isFPImm()) { const MCInstrDesc &Desc = MII.get(MI->getOpcode()); assert(OpNo < Desc.getNumOperands() && @@ -200,18 +211,18 @@ WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) { int64_t Imm = MI->getOperand(OpNo).getImm(); - switch (Imm) { - case WebAssembly::Void: break; - case WebAssembly::I32: O << "i32"; break; - case WebAssembly::I64: O << "i64"; break; - case WebAssembly::F32: O << "f32"; break; - case WebAssembly::F64: O << "f64"; break; - case WebAssembly::I8x16: O << "i8x16"; break; - case WebAssembly::I16x8: O << "i16x8"; break; - case WebAssembly::I32x4: O << "i32x4"; break; - case WebAssembly::I64x2: O << "i32x4"; break; - case WebAssembly::F32x4: O << "f32x4"; break; - case WebAssembly::F64x2: O << "f64x2"; break; + switch (WebAssembly::ExprType(Imm)) { + case WebAssembly::ExprType::Void: break; + case WebAssembly::ExprType::I32: O << "i32"; break; + case WebAssembly::ExprType::I64: O << "i64"; break; + case WebAssembly::ExprType::F32: O << "f32"; break; + case WebAssembly::ExprType::F64: O << "f64"; break; + case WebAssembly::ExprType::I8x16: O << "i8x16"; break; + case WebAssembly::ExprType::I16x8: O << "i16x8"; break; + case WebAssembly::ExprType::I32x4: O << "i32x4"; break; + case WebAssembly::ExprType::I64x2: O << "i32x4"; break; + case WebAssembly::ExprType::F32x4: O << "f32x4"; break; + case WebAssembly::ExprType::F64x2: O << "f64x2"; break; } } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index 23f8b3d0e82..d740b0adc7a 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -23,6 +23,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -56,30 +57,54 @@ MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) { void WebAssemblyMCCodeEmitter::encodeInstruction( const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { - // FIXME: This is not the real binary encoding. This is an extremely - // over-simplified encoding where we just use uint64_t for everything. This - // is a temporary measure. - support::endian::Writer<support::little>(OS).write<uint64_t>(MI.getOpcode()); + uint64_t Start = OS.tell(); + + uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI); + encodeULEB128(Binary, OS); + const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); - if (Desc.isVariadic()) - support::endian::Writer<support::little>(OS).write<uint64_t>( - MI.getNumOperands() - Desc.NumOperands); for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) { const MCOperand &MO = MI.getOperand(i); if (MO.isReg()) { - support::endian::Writer<support::little>(OS).write<uint64_t>(MO.getReg()); + /* nothing to encode */ } else if (MO.isImm()) { - support::endian::Writer<support::little>(OS).write<uint64_t>(MO.getImm()); + assert(i < Desc.getNumOperands() && + "Unexpected integer immediate as a non-fixed operand"); + assert(Desc.TSFlags == 0 && + "WebAssembly variable_ops integer ops don't use TSFlags"); + const MCOperandInfo &Info = Desc.OpInfo[i]; + if (Info.OperandType == WebAssembly::OPERAND_I32IMM) { + encodeSLEB128(int32_t(MO.getImm()), OS); + } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) { + encodeSLEB128(int64_t(MO.getImm()), OS); + } else { + encodeULEB128(uint64_t(MO.getImm()), OS); + } } else if (MO.isFPImm()) { - support::endian::Writer<support::little>(OS).write<double>(MO.getFPImm()); + assert(i < Desc.getNumOperands() && + "Unexpected floating-point immediate as a non-fixed operand"); + assert(Desc.TSFlags == 0 && + "WebAssembly variable_ops floating point ops don't use TSFlags"); + const MCOperandInfo &Info = Desc.OpInfo[i]; + if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { + // TODO: MC converts all floating point immediate operands to double. + // This is fine for numeric values, but may cause NaNs to change bits. + float f = float(MO.getFPImm()); + support::endian::Writer<support::little>(OS).write<float>(f); + } else { + assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); + double d = MO.getFPImm(); + support::endian::Writer<support::little>(OS).write<double>(d); + } } else if (MO.isExpr()) { - support::endian::Writer<support::little>(OS).write<uint64_t>(0); Fixups.push_back(MCFixup::create( - (1 + MCII.get(MI.getOpcode()).isVariadic() + i) * sizeof(uint64_t), - MO.getExpr(), + OS.tell() - Start, MO.getExpr(), STI.getTargetTriple().isArch64Bit() ? FK_Data_8 : FK_Data_4, MI.getLoc())); ++MCNumFixups; + encodeULEB128(STI.getTargetTriple().isArch64Bit() ? UINT64_MAX + : uint64_t(UINT32_MAX), + OS); } else { llvm_unreachable("unexpected operand kind"); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index 8f3adc28487..5fa277324d6 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -44,6 +44,8 @@ namespace WebAssembly { enum OperandType { /// Basic block label in a branch construct. OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET, + /// Local index. + OPERAND_LOCAL, /// 32-bit integer immediates. OPERAND_I32IMM, /// 64-bit integer immediates. @@ -78,6 +80,9 @@ enum { // For immediate values in the variable_ops range, this flag indicates // whether the value represents a control-flow label. VariableOpImmediateIsLabel = (1 << 1), + // For immediate values in the variable_ops range, this flag indicates + // whether the value represents a ValType. + VariableOpImmediateIsType = (1 << 2), }; } // end namespace WebAssemblyII @@ -144,7 +149,7 @@ static const unsigned LoadP2AlignOperandNo = 3; static const unsigned StoreP2AlignOperandNo = 2; /// This is used to indicate block signatures. -enum ExprType { +enum class ExprType { Void = 0, I32 = 1, I64 = 2, @@ -158,6 +163,20 @@ enum ExprType { F64x2 = 10 }; +/// This is used to indicate local types. +enum class ValType { + I32 = 1, + I64 = 2, + F32 = 3, + F64 = 4, + I8x16 = 5, + I16x8 = 6, + I32x4 = 7, + I64x2 = 8, + F32x4 = 9, + F64x2 = 10 +}; + } // end namespace WebAssembly } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h index 55e73ebd974..09c35b4825f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -43,6 +43,7 @@ FunctionPass *createWebAssemblyOptimizeLiveIntervals(); FunctionPass *createWebAssemblyStoreResults(); FunctionPass *createWebAssemblyRegStackify(); FunctionPass *createWebAssemblyRegColoring(); +FunctionPass *createWebAssemblyExplicitLocals(); FunctionPass *createWebAssemblyFixIrreducibleControlFlow(); FunctionPass *createWebAssemblyCFGStackify(); FunctionPass *createWebAssemblyLowerBrUnless(); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp index 04951485a71..5fadca38b82 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp @@ -30,6 +30,7 @@ #include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/Passes.h" @@ -63,23 +64,6 @@ 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: - case WebAssembly::ARGUMENT_v16i8: - case WebAssembly::ARGUMENT_v8i16: - case WebAssembly::ARGUMENT_v4i32: - case WebAssembly::ARGUMENT_v4f32: - return true; - default: - return false; - } -} - bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) { DEBUG({ dbgs() << "********** Argument Move **********\n" @@ -92,7 +76,7 @@ bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) { // Look for the first NonArg instruction. for (MachineInstr &MI : EntryMBB) { - if (!IsArgument(MI)) { + if (!WebAssembly::isArgument(MI)) { InsertPt = MI; break; } @@ -101,7 +85,7 @@ bool WebAssemblyArgumentMove::runOnMachineFunction(MachineFunction &MF) { // Now move any argument instructions later in the block // to before our first NonArg instruction. for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) { - if (IsArgument(MI)) { + if (WebAssembly::isArgument(MI)) { EntryMBB.insert(InsertPt, MI.removeFromParent()); Changed = true; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp index 774c41d8909..18b6f932081 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -27,6 +27,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" #include "llvm/ADT/PriorityQueue.h" #include "llvm/ADT/SetVector.h" #include "llvm/CodeGen/MachineDominators.h" @@ -292,19 +293,6 @@ static bool ExplicitlyBranchesTo(MachineBasicBlock *Pred, return false; } -/// Test whether MI is a child of some other node in an expression tree. -static bool IsChild(const MachineInstr &MI, - const WebAssemblyFunctionInfo &MFI) { - if (MI.getNumOperands() == 0) - return false; - const MachineOperand &MO = MI.getOperand(0); - if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) - return false; - unsigned Reg = MO.getReg(); - return TargetRegisterInfo::isVirtualRegister(Reg) && - MFI.isVRegStackified(Reg); -} - /// Insert a BLOCK marker for branches to MBB (if needed). static void PlaceBlockMarker( MachineBasicBlock &MBB, MachineFunction &MF, @@ -365,7 +353,7 @@ static void PlaceBlockMarker( // beginning of the local expression tree and any nested BLOCKs. InsertPos = Header->getFirstTerminator(); while (InsertPos != Header->begin() && - IsChild(*std::prev(InsertPos), MFI) && + WebAssembly::isChild(*std::prev(InsertPos), MFI) && std::prev(InsertPos)->getOpcode() != WebAssembly::LOOP && std::prev(InsertPos)->getOpcode() != WebAssembly::END_BLOCK && std::prev(InsertPos)->getOpcode() != WebAssembly::END_LOOP) @@ -375,7 +363,7 @@ static void PlaceBlockMarker( // Add the BLOCK. MachineInstr *Begin = BuildMI(*Header, InsertPos, DebugLoc(), TII.get(WebAssembly::BLOCK)) - .addImm(WebAssembly::Void); + .addImm(int64_t(WebAssembly::ExprType::Void)); // Mark the end of the block. InsertPos = MBB.begin(); @@ -425,7 +413,7 @@ static void PlaceLoopMarker( ++InsertPos; MachineInstr *Begin = BuildMI(MBB, InsertPos, DebugLoc(), TII.get(WebAssembly::LOOP)) - .addImm(WebAssembly::Void); + .addImm(int64_t(WebAssembly::ExprType::Void)); // Mark the end of the loop. MachineInstr *End = BuildMI(*AfterLoop, AfterLoop->begin(), DebugLoc(), @@ -471,16 +459,16 @@ static void FixEndsAtEndOfFunction( WebAssembly::ExprType retType; switch (MFI.getResults().front().SimpleTy) { - case MVT::i32: retType = WebAssembly::I32; break; - case MVT::i64: retType = WebAssembly::I64; break; - case MVT::f32: retType = WebAssembly::F32; break; - case MVT::f64: retType = WebAssembly::F64; break; - case MVT::v16i8: retType = WebAssembly::I8x16; break; - case MVT::v8i16: retType = WebAssembly::I16x8; break; - case MVT::v4i32: retType = WebAssembly::I32x4; break; - case MVT::v2i64: retType = WebAssembly::I64x2; break; - case MVT::v4f32: retType = WebAssembly::F32x4; break; - case MVT::v2f64: retType = WebAssembly::F64x2; break; + case MVT::i32: retType = WebAssembly::ExprType::I32; break; + case MVT::i64: retType = WebAssembly::ExprType::I64; break; + case MVT::f32: retType = WebAssembly::ExprType::F32; break; + case MVT::f64: retType = WebAssembly::ExprType::F64; break; + case MVT::v16i8: retType = WebAssembly::ExprType::I8x16; break; + case MVT::v8i16: retType = WebAssembly::ExprType::I16x8; break; + case MVT::v4i32: retType = WebAssembly::ExprType::I32x4; break; + case MVT::v2i64: retType = WebAssembly::ExprType::I64x2; break; + case MVT::v4f32: retType = WebAssembly::ExprType::F32x4; break; + case MVT::v2f64: retType = WebAssembly::ExprType::F64x2; break; default: llvm_unreachable("unexpected return type"); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp new file mode 100644 index 00000000000..3b527922419 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -0,0 +1,309 @@ +//===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===// +// +// 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 any remaining registers into WebAssembly locals. +/// +/// After register stackification and register coloring, convert non-stackified +/// registers into locals, inserting explicit get_local and set_local +/// instructions. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssembly.h" +#include "WebAssemblyMachineFunctionInfo.h" +#include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.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-explicit-locals" + +namespace { +class WebAssemblyExplicitLocals final : public MachineFunctionPass { + StringRef getPassName() const override { + return "WebAssembly Explicit Locals"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addPreserved<MachineBlockFrequencyInfo>(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {} +}; +} // end anonymous namespace + +char WebAssemblyExplicitLocals::ID = 0; +FunctionPass *llvm::createWebAssemblyExplicitLocals() { + return new WebAssemblyExplicitLocals(); +} + +/// Return a local id number for the given register, assigning it a new one +/// if it doesn't yet have one. +static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local, + unsigned &CurLocal, unsigned Reg) { + return Reg2Local.insert(std::make_pair(Reg, CurLocal++)).first->second; +} + +/// Get the appropriate get_local opcode for the given register class. +static unsigned getGetLocalOpcode(const TargetRegisterClass *RC) { + if (RC == &WebAssembly::I32RegClass) + return WebAssembly::GET_LOCAL_I32; + if (RC == &WebAssembly::I64RegClass) + return WebAssembly::GET_LOCAL_I64; + if (RC == &WebAssembly::F32RegClass) + return WebAssembly::GET_LOCAL_F32; + if (RC == &WebAssembly::F64RegClass) + return WebAssembly::GET_LOCAL_F64; + if (RC == &WebAssembly::V128RegClass) + return WebAssembly::GET_LOCAL_V128; + llvm_unreachable("Unexpected register class"); +} + +/// Get the appropriate set_local opcode for the given register class. +static unsigned getSetLocalOpcode(const TargetRegisterClass *RC) { + if (RC == &WebAssembly::I32RegClass) + return WebAssembly::SET_LOCAL_I32; + if (RC == &WebAssembly::I64RegClass) + return WebAssembly::SET_LOCAL_I64; + if (RC == &WebAssembly::F32RegClass) + return WebAssembly::SET_LOCAL_F32; + if (RC == &WebAssembly::F64RegClass) + return WebAssembly::SET_LOCAL_F64; + if (RC == &WebAssembly::V128RegClass) + return WebAssembly::SET_LOCAL_V128; + llvm_unreachable("Unexpected register class"); +} + +/// Get the appropriate tee_local opcode for the given register class. +static unsigned getTeeLocalOpcode(const TargetRegisterClass *RC) { + if (RC == &WebAssembly::I32RegClass) + return WebAssembly::TEE_LOCAL_I32; + if (RC == &WebAssembly::I64RegClass) + return WebAssembly::TEE_LOCAL_I64; + if (RC == &WebAssembly::F32RegClass) + return WebAssembly::TEE_LOCAL_F32; + if (RC == &WebAssembly::F64RegClass) + return WebAssembly::TEE_LOCAL_F64; + if (RC == &WebAssembly::V128RegClass) + return WebAssembly::TEE_LOCAL_V128; + llvm_unreachable("Unexpected register class"); +} + +/// Get the type associated with the given register class. +static WebAssembly::ValType typeForRegClass(const TargetRegisterClass *RC) { + if (RC == &WebAssembly::I32RegClass) + return WebAssembly::ValType::I32; + if (RC == &WebAssembly::I64RegClass) + return WebAssembly::ValType::I64; + if (RC == &WebAssembly::F32RegClass) + return WebAssembly::ValType::F32; + if (RC == &WebAssembly::F64RegClass) + return WebAssembly::ValType::F64; + llvm_unreachable("unrecognized register class"); +} + +/// Given a MachineOperand of a stackified vreg, return the instruction at the +/// start of the expression tree. +static MachineInstr *FindStartOfTree(MachineOperand &MO, + MachineRegisterInfo &MRI, + WebAssemblyFunctionInfo &MFI) { + unsigned Reg = MO.getReg(); + assert(MFI.isVRegStackified(Reg)); + MachineInstr *Def = MRI.getVRegDef(Reg); + + // Find the first stackified use and proceed from there. + for (MachineOperand &DefMO : Def->explicit_uses()) { + if (!DefMO.isReg()) + continue; + return FindStartOfTree(DefMO, MRI, MFI); + } + + // If there were no stackified uses, we've reached the start. + return Def; +} + +bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "********** Make Locals Explicit **********\n" + "********** Function: " + << MF.getName() << '\n'); + + bool Changed = false; + MachineRegisterInfo &MRI = MF.getRegInfo(); + WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); + const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); + + // Map non-stackified virtual registers to their local ids. + DenseMap<unsigned, unsigned> Reg2Local; + + // Handle ARGUMENTS first to ensure that they get the designated numbers. + for (MachineBasicBlock::iterator I = MF.begin()->begin(), + E = MF.begin()->end(); + I != E;) { + MachineInstr &MI = *I++; + if (!WebAssembly::isArgument(MI)) + break; + unsigned Reg = MI.getOperand(0).getReg(); + assert(!MFI.isVRegStackified(Reg)); + Reg2Local[Reg] = MI.getOperand(1).getImm(); + MI.eraseFromParent(); + Changed = true; + } + + // Start assigning local numbers after the last parameter. + unsigned CurLocal = MFI.getParams().size(); + + // Visit each instruction in the function. + for (MachineBasicBlock &MBB : MF) { + for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { + MachineInstr &MI = *I++; + assert(!WebAssembly::isArgument(MI)); + + if (MI.isDebugValue() || MI.isLabel()) + continue; + + // Replace tee instructions with tee_local. The difference is that tee + // instructins have two defs, while tee_local instructions have one def + // and an index of a local to write to. + if (WebAssembly::isTee(MI)) { + assert(MFI.isVRegStackified(MI.getOperand(0).getReg())); + assert(!MFI.isVRegStackified(MI.getOperand(1).getReg())); + unsigned OldReg = MI.getOperand(2).getReg(); + const TargetRegisterClass *RC = MRI.getRegClass(OldReg); + + // Stackify the input if it isn't stackified yet. + if (!MFI.isVRegStackified(OldReg)) { + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + unsigned NewReg = MRI.createVirtualRegister(RC); + unsigned Opc = getGetLocalOpcode(RC); + BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg) + .addImm(LocalId); + MI.getOperand(2).setReg(NewReg); + MFI.stackifyVReg(NewReg); + } + + // Replace the TEE with a TEE_LOCAL. + unsigned LocalId = + getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg()); + unsigned Opc = getTeeLocalOpcode(RC); + BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), + MI.getOperand(0).getReg()) + .addImm(LocalId) + .addReg(MI.getOperand(2).getReg()); + + MI.eraseFromParent(); + Changed = true; + continue; + } + + // Insert set_locals for any defs that aren't stackified yet. Currently + // we handle at most one def. + assert(MI.getDesc().getNumDefs() <= 1); + if (MI.getDesc().getNumDefs() == 1) { + unsigned OldReg = MI.getOperand(0).getReg(); + if (!MFI.isVRegStackified(OldReg) && !MRI.use_empty(OldReg)) { + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + const TargetRegisterClass *RC = MRI.getRegClass(OldReg); + unsigned NewReg = MRI.createVirtualRegister(RC); + auto InsertPt = std::next(MachineBasicBlock::iterator(&MI)); + unsigned Opc = getSetLocalOpcode(RC); + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) + .addImm(LocalId) + .addReg(NewReg); + MI.getOperand(0).setReg(NewReg); + MFI.stackifyVReg(NewReg); + Changed = true; + } + } + + // Insert get_locals for any uses that aren't stackified yet. + MachineInstr *InsertPt = &MI; + for (MachineOperand &MO : reverse(MI.explicit_uses())) { + if (!MO.isReg()) + continue; + + unsigned OldReg = MO.getReg(); + + // If we see a stackified register, prepare to insert subsequent + // get_locals before the start of its tree. + if (MFI.isVRegStackified(OldReg)) { + InsertPt = FindStartOfTree(MO, MRI, MFI); + continue; + } + + // Insert a get_local. + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + const TargetRegisterClass *RC = MRI.getRegClass(OldReg); + unsigned NewReg = MRI.createVirtualRegister(RC); + unsigned Opc = getGetLocalOpcode(RC); + InsertPt = + BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg) + .addImm(LocalId); + MO.setReg(NewReg); + MFI.stackifyVReg(NewReg); + Changed = true; + } + + // Coalesce and eliminate COPY instructions. + if (WebAssembly::isCopy(MI)) { + MRI.replaceRegWith(MI.getOperand(1).getReg(), + MI.getOperand(0).getReg()); + MI.eraseFromParent(); + Changed = true; + } + } + } + + // Insert a .locals directive to declare the locals. + MachineInstrBuilder DeclareLocals; + for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) { + unsigned Reg = TargetRegisterInfo::index2VirtReg(i); + auto I = Reg2Local.find(Reg); + if (I == Reg2Local.end() || I->second < MFI.getParams().size()) + continue; + + if (!DeclareLocals) { + DeclareLocals = BuildMI(*MF.begin(), MF.begin()->begin(), DebugLoc(), + TII->get(WebAssembly::DECLARE_LOCALS)); + Changed = true; + } + + DeclareLocals.addImm(int64_t(typeForRegClass(MRI.getRegClass(Reg)))); + } + +#ifndef NDEBUG + // Assert that all registers have been stackified at this point. + for (const MachineBasicBlock &MBB : MF) { + for (const MachineInstr &MI : MBB) { + if (MI.isDebugValue() || MI.isLabel()) + continue; + for (const MachineOperand &MO : MI.explicit_operands()) { + assert( + (!MO.isReg() || MRI.use_empty(MO.getReg()) || + MFI.isVRegStackified(MO.getReg())) && + "WebAssemblyExplicitLocals failed to stackify a register operand"); + } + } + } +#endif + + return Changed; +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp index 8575603075b..5a1839459aa 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -542,8 +542,8 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { &WebAssembly::I64RegClass : &WebAssembly::I32RegClass); unsigned Opc = Subtarget->hasAddr64() ? - WebAssembly::COPY_LOCAL_I64 : - WebAssembly::COPY_LOCAL_I32; + WebAssembly::COPY_I64 : + WebAssembly::COPY_I32; BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) .addFrameIndex(SI->second); return ResultReg; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index fb84aa94350..6a7f75a6b3a 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -587,8 +587,8 @@ SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, unsigned Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg(); EVT VT = Src.getValueType(); SDValue Copy( - DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_LOCAL_I32 - : WebAssembly::COPY_LOCAL_I64, + DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32 + : WebAssembly::COPY_I64, DL, VT, Src), 0); return Op.getNode()->getNumValues() == 1 diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp index 91f53630701..0e2d8bbaf64 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -60,19 +60,19 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, ? MRI.getRegClass(DestReg) : MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(DestReg); - unsigned CopyLocalOpcode; + unsigned CopyOpcode; if (RC == &WebAssembly::I32RegClass) - CopyLocalOpcode = WebAssembly::COPY_LOCAL_I32; + CopyOpcode = WebAssembly::COPY_I32; else if (RC == &WebAssembly::I64RegClass) - CopyLocalOpcode = WebAssembly::COPY_LOCAL_I64; + CopyOpcode = WebAssembly::COPY_I64; else if (RC == &WebAssembly::F32RegClass) - CopyLocalOpcode = WebAssembly::COPY_LOCAL_F32; + CopyOpcode = WebAssembly::COPY_F32; else if (RC == &WebAssembly::F64RegClass) - CopyLocalOpcode = WebAssembly::COPY_LOCAL_F64; + CopyOpcode = WebAssembly::COPY_F64; else llvm_unreachable("Unexpected register class"); - BuildMI(MBB, I, DL, get(CopyLocalOpcode), DestReg) + BuildMI(MBB, I, DL, get(CopyOpcode), DestReg) .addReg(SrcReg, KillSrc ? RegState::Kill : 0); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index a316a74fdd2..f774e90d22f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -71,6 +71,9 @@ let OperandNamespace = "WebAssembly" in { let OperandType = "OPERAND_BASIC_BLOCK" in def bb_op : Operand<OtherVT>; +let OperandType = "OPERAND_LOCAL" in +def local_op : Operand<i32>; + let OperandType = "OPERAND_I32IMM" in def i32imm_op : Operand<i32>; @@ -133,20 +136,42 @@ let Defs = [ARGUMENTS] in { // are implied by virtual register uses and defs. multiclass LOCAL<WebAssemblyRegClass vt> { let hasSideEffects = 0 in { - // COPY_LOCAL is not an actual instruction in wasm, but since we allow - // get_local and set_local to be implicit, we can have a COPY_LOCAL which - // is actually a no-op because all the work is done in the implied - // get_local and set_local. - let isAsCheapAsAMove = 1 in - def COPY_LOCAL_#vt : I<(outs vt:$res), (ins vt:$src), [], - "copy_local\t$res, $src">; - - // TEE_LOCAL is similar to COPY_LOCAL, but writes two copies of its result. - // Typically this would be used to stackify one result and write the other - // result to a local. - let isAsCheapAsAMove = 1 in - def TEE_LOCAL_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), [], - "tee_local\t$res, $also, $src">; + // COPY is not an actual instruction in wasm, but since we allow get_local and + // set_local to be implicit during most of codegen, we can have a COPY which + // is actually a no-op because all the work is done in the implied get_local + // and set_local. COPYs are eliminated (and replaced with + // get_local/set_local) in the ExplicitLocals pass. + let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in + def COPY_#vt : I<(outs vt:$res), (ins vt:$src), [], "copy_local\t$res, $src">; + + // TEE is similar to COPY, but writes two copies of its result. Typically + // this would be used to stackify one result and write the other result to a + // local. + let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in + def TEE_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), [], + "tee_local\t$res, $also, $src">; + + // This is the actual get_local instruction in wasm. These are made explicit + // by the ExplicitLocals pass. It has mayLoad because it reads from a wasm + // local, which is a side effect not otherwise modeled in LLVM. + let mayLoad = 1, isAsCheapAsAMove = 1 in + def GET_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local), [], + "get_local\t$res, $local", 0x14>; + + // This is the actual set_local instruction in wasm. These are made explicit + // by the ExplicitLocals pass. It has mayStore because it writes to a wasm + // local, which is a side effect not otherwise modeled in LLVM. + let mayStore = 1, isAsCheapAsAMove = 1 in + def SET_LOCAL_#vt : I<(outs), (ins local_op:$local, vt:$src), [], + "set_local\t$local, $src", 0x15>; + + // This is the actual tee_local instruction in wasm. TEEs are turned into + // TEE_LOCALs by the ExplicitLocals pass. It has mayStore for the same reason + // as SET_LOCAL. + let mayStore = 1, isAsCheapAsAMove = 1 in + def TEE_LOCAL_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src), [], + "tee_local\t$res, $local, $src", 0x19>; + } // hasSideEffects = 0 } defm : LOCAL<I32>; @@ -155,19 +180,26 @@ defm : LOCAL<F32>; defm : LOCAL<F64>; defm : LOCAL<V128>, Requires<[HasSIMD128]>; +// Set TSFlags{0} to 1 to indicate that the variable_ops are immediates. +// Set TSFlags{2} to 1 to indicate that the immediates are ValTypes. +def DECLARE_LOCALS : I<(outs), (ins variable_ops), [], ".local \t"> { + let TSFlags{0} = 1; + let TSFlags{2} = 1; +} + let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in { def CONST_I32 : I<(outs I32:$res), (ins i32imm_op:$imm), [(set I32:$res, imm:$imm)], - "i32.const\t$res, $imm">; + "i32.const\t$res, $imm", 0x10>; def CONST_I64 : I<(outs I64:$res), (ins i64imm_op:$imm), [(set I64:$res, imm:$imm)], - "i64.const\t$res, $imm">; + "i64.const\t$res, $imm", 0x11>; def CONST_F32 : I<(outs F32:$res), (ins f32imm_op:$imm), [(set F32:$res, fpimm:$imm)], - "f32.const\t$res, $imm">; + "f32.const\t$res, $imm", 0x13>; def CONST_F64 : I<(outs F64:$res), (ins f64imm_op:$imm), [(set F64:$res, fpimm:$imm)], - "f64.const\t$res, $imm">; + "f64.const\t$res, $imm", 0x12>; } // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 } // Defs = [ARGUMENTS] diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp index 150ec5d65b3..7ea5d05a1b2 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp @@ -107,9 +107,9 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) { // instruction to invert it. if (!Inverted) { unsigned Tmp = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - MFI.stackifyVReg(Tmp); BuildMI(MBB, MI, MI->getDebugLoc(), TII.get(WebAssembly::EQZ_I32), Tmp) .addReg(Cond); + MFI.stackifyVReg(Tmp); Cond = Tmp; Inverted = true; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h index 86693a933ec..61382cd0640 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -64,6 +64,7 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { static const unsigned UnusedReg = -1u; void stackifyVReg(unsigned VReg) { + assert(MF.getRegInfo().getUniqueVRegDef(VReg)); if (TargetRegisterInfo::virtReg2Index(VReg) >= VRegStackified.size()) VRegStackified.resize(TargetRegisterInfo::virtReg2Index(VReg) + 1); VRegStackified.set(TargetRegisterInfo::virtReg2Index(VReg)); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp index a9e970c399d..32dde88c223 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp @@ -83,8 +83,8 @@ static bool MaybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, if (&MI != &MBB.back()) return false; - // If the operand isn't stackified, insert a COPY_LOCAL to read the operand - // and stackify it. + // If the operand isn't stackified, insert a COPY to read the operand and + // stackify it. MachineOperand &MO = MI.getOperand(0); unsigned Reg = MO.getReg(); if (!MFI.isVRegStackified(Reg)) { @@ -150,42 +150,42 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) { case WebAssembly::RETURN_I32: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32, - WebAssembly::COPY_LOCAL_I32); + WebAssembly::COPY_I32); break; case WebAssembly::RETURN_I64: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64, - WebAssembly::COPY_LOCAL_I64); + WebAssembly::COPY_I64); break; case WebAssembly::RETURN_F32: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32, - WebAssembly::COPY_LOCAL_F32); + WebAssembly::COPY_F32); break; case WebAssembly::RETURN_F64: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64, - WebAssembly::COPY_LOCAL_F64); + WebAssembly::COPY_F64); break; case WebAssembly::RETURN_v16i8: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8, - WebAssembly::COPY_LOCAL_V128); + WebAssembly::COPY_V128); break; case WebAssembly::RETURN_v8i16: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16, - WebAssembly::COPY_LOCAL_V128); + WebAssembly::COPY_V128); break; case WebAssembly::RETURN_v4i32: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32, - WebAssembly::COPY_LOCAL_V128); + WebAssembly::COPY_V128); break; case WebAssembly::RETURN_v4f32: Changed |= MaybeRewriteToFallthrough( MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32, - WebAssembly::COPY_LOCAL_V128); + WebAssembly::COPY_V128); break; case WebAssembly::RETURN_VOID: if (!DisableWebAssemblyFallthroughReturnOpt && diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp index 52fdaf0417a..473dcb7a33f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp @@ -23,6 +23,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -58,27 +59,10 @@ FunctionPass *llvm::createWebAssemblyPrepareForLiveIntervals() { return new WebAssemblyPrepareForLiveIntervals(); } -/// 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: - case WebAssembly::ARGUMENT_v16i8: - case WebAssembly::ARGUMENT_v8i16: - case WebAssembly::ARGUMENT_v4i32: - case WebAssembly::ARGUMENT_v4f32: - return true; - default: - return false; - } -} - // Test whether the given register has an ARGUMENT def. static bool HasArgumentDef(unsigned Reg, const MachineRegisterInfo &MRI) { for (const auto &Def : MRI.def_instructions(Reg)) - if (IsArgument(&Def)) + if (WebAssembly::isArgument(Def)) return true; return false; } @@ -126,10 +110,10 @@ bool WebAssemblyPrepareForLiveIntervals::runOnMachineFunction(MachineFunction &M // Move ARGUMENT_* instructions to the top of the entry block, so that their // liveness reflects the fact that these really are live-in values. for (auto MII = Entry.begin(), MIE = Entry.end(); MII != MIE; ) { - MachineInstr *MI = &*MII++; - if (IsArgument(MI)) { - MI->removeFromParent(); - Entry.insert(Entry.begin(), MI); + MachineInstr &MI = *MII++; + if (WebAssembly::isArgument(MI)) { + MI.removeFromParent(); + Entry.insert(Entry.begin(), &MI); } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp index 41e1a6b3129..e3470825940 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp @@ -17,6 +17,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -68,24 +69,13 @@ bool WebAssemblyRegNumbering::runOnMachineFunction(MachineFunction &MF) { // variables. Assign the numbers for them first. MachineBasicBlock &EntryMBB = MF.front(); for (MachineInstr &MI : EntryMBB) { - switch (MI.getOpcode()) { - case WebAssembly::ARGUMENT_I32: - case WebAssembly::ARGUMENT_I64: - case WebAssembly::ARGUMENT_F32: - case WebAssembly::ARGUMENT_F64: - case WebAssembly::ARGUMENT_v16i8: - case WebAssembly::ARGUMENT_v8i16: - case WebAssembly::ARGUMENT_v4i32: - case WebAssembly::ARGUMENT_v4f32: { - int64_t Imm = MI.getOperand(1).getImm(); - DEBUG(dbgs() << "Arg VReg " << MI.getOperand(0).getReg() << " -> WAReg " - << Imm << "\n"); - MFI.setWAReg(MI.getOperand(0).getReg(), Imm); + if (!WebAssembly::isArgument(MI)) break; - } - default: - break; - } + + int64_t Imm = MI.getOperand(1).getImm(); + DEBUG(dbgs() << "Arg VReg " << MI.getOperand(0).getReg() << " -> WAReg " + << Imm << "\n"); + MFI.setWAReg(MI.getOperand(0).getReg(), Imm); } // Then assign regular WebAssembly registers for all remaining used diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 6de724cfbdf..894f2dc5412 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -24,6 +24,7 @@ #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" // for WebAssembly::ARGUMENT_* #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/CodeGen/LiveIntervalAnalysis.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" @@ -404,18 +405,18 @@ static bool OneUseDominatesOtherUses(unsigned Reg, const MachineOperand &OneUse, return true; } -/// Get the appropriate tee_local opcode for the given register class. -static unsigned GetTeeLocalOpcode(const TargetRegisterClass *RC) { +/// Get the appropriate tee opcode for the given register class. +static unsigned GetTeeOpcode(const TargetRegisterClass *RC) { if (RC == &WebAssembly::I32RegClass) - return WebAssembly::TEE_LOCAL_I32; + return WebAssembly::TEE_I32; if (RC == &WebAssembly::I64RegClass) - return WebAssembly::TEE_LOCAL_I64; + return WebAssembly::TEE_I64; if (RC == &WebAssembly::F32RegClass) - return WebAssembly::TEE_LOCAL_F32; + return WebAssembly::TEE_F32; if (RC == &WebAssembly::F64RegClass) - return WebAssembly::TEE_LOCAL_F64; + return WebAssembly::TEE_F64; if (RC == &WebAssembly::V128RegClass) - return WebAssembly::TEE_LOCAL_V128; + return WebAssembly::TEE_V128; llvm_unreachable("Unexpected register class"); } @@ -513,8 +514,8 @@ static MachineInstr *RematerializeCheapDef( /// A multiple-use def in the same block with no intervening memory or register /// dependencies; move the def down, nest it with the current instruction, and -/// insert a tee_local to satisfy the rest of the uses. As an illustration, -/// rewrite this: +/// insert a tee to satisfy the rest of the uses. As an illustration, rewrite +/// this: /// /// Reg = INST ... // Def /// INST ..., Reg, ... // Insert @@ -524,7 +525,7 @@ static MachineInstr *RematerializeCheapDef( /// to this: /// /// DefReg = INST ... // Def (to become the new Insert) -/// TeeReg, Reg = TEE_LOCAL_... DefReg +/// TeeReg, Reg = TEE_... DefReg /// INST ..., TeeReg, ... // Insert /// INST ..., Reg, ... /// INST ..., Reg, ... @@ -547,7 +548,7 @@ static MachineInstr *MoveAndTeeForMultiUse( unsigned DefReg = MRI.createVirtualRegister(RegClass); MachineOperand &DefMO = Def->getOperand(0); MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(), - TII->get(GetTeeLocalOpcode(RegClass)), TeeReg) + TII->get(GetTeeOpcode(RegClass)), TeeReg) .addReg(Reg, RegState::Define) .addReg(DefReg, getUndefRegState(DefMO.isDead())); Op.setReg(TeeReg); @@ -759,18 +760,11 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { // Argument instructions represent live-in registers and not real // instructions. - if (Def->getOpcode() == WebAssembly::ARGUMENT_I32 || - Def->getOpcode() == WebAssembly::ARGUMENT_I64 || - Def->getOpcode() == WebAssembly::ARGUMENT_F32 || - Def->getOpcode() == WebAssembly::ARGUMENT_F64 || - Def->getOpcode() == WebAssembly::ARGUMENT_v16i8 || - Def->getOpcode() == WebAssembly::ARGUMENT_v8i16 || - Def->getOpcode() == WebAssembly::ARGUMENT_v4i32 || - Def->getOpcode() == WebAssembly::ARGUMENT_v4f32) + if (WebAssembly::isArgument(*Def)) continue; // Decide which strategy to take. Prefer to move a single-use value - // over cloning it, and prefer cloning over introducing a tee_local. + // over cloning it, and prefer cloning over introducing a tee. // For moving, we require the def to be in the same block as the use; // this makes things simpler (LiveIntervals' handleMove function only // supports intra-block moves) and it's MachineSink's job to catch all diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp index e8fbe941345..f0f74e7b5e8 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -41,6 +41,11 @@ static cl::opt<bool> EnableEmSjLj( cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), cl::init(false)); +static cl::opt<bool> ExplicitLocals( + "wasm-explicit-locals", + cl::desc("WebAssembly with explicit get_local/set_local"), + cl::init(false)); + extern "C" void LLVMInitializeWebAssemblyTarget() { // Register the target. RegisterTargetMachine<WebAssemblyTargetMachine> X( @@ -256,6 +261,10 @@ void WebAssemblyPassConfig::addPreEmitPass() { addPass(createWebAssemblyRegColoring()); } + // Insert explicit get_local and set_local operators. + if (ExplicitLocals) + addPass(createWebAssemblyExplicitLocals()); + // Eliminate multiple-entry loops. addPass(createWebAssemblyFixIrreducibleControlFlow()); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp new file mode 100644 index 00000000000..a0049c147d2 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -0,0 +1,71 @@ +//===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===// +// +// 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 several utility functions for WebAssembly. +/// +//===----------------------------------------------------------------------===// + +#include "WebAssemblyUtilities.h" +#include "WebAssemblyMachineFunctionInfo.h" +#include "llvm/CodeGen/MachineInstr.h" +using namespace llvm; + +bool WebAssembly::isArgument(const MachineInstr &MI) { + switch (MI.getOpcode()) { + case WebAssembly::ARGUMENT_I32: + case WebAssembly::ARGUMENT_I64: + case WebAssembly::ARGUMENT_F32: + case WebAssembly::ARGUMENT_F64: + case WebAssembly::ARGUMENT_v16i8: + case WebAssembly::ARGUMENT_v8i16: + case WebAssembly::ARGUMENT_v4i32: + case WebAssembly::ARGUMENT_v4f32: + return true; + default: + return false; + } +} + +bool WebAssembly::isCopy(const MachineInstr &MI) { + switch (MI.getOpcode()) { + case WebAssembly::COPY_I32: + case WebAssembly::COPY_I64: + case WebAssembly::COPY_F32: + case WebAssembly::COPY_F64: + return true; + default: + return false; + } +} + +bool WebAssembly::isTee(const MachineInstr &MI) { + switch (MI.getOpcode()) { + case WebAssembly::TEE_I32: + case WebAssembly::TEE_I64: + case WebAssembly::TEE_F32: + case WebAssembly::TEE_F64: + return true; + default: + return false; + } +} + +/// Test whether MI is a child of some other node in an expression tree. +bool WebAssembly::isChild(const MachineInstr &MI, + const WebAssemblyFunctionInfo &MFI) { + if (MI.getNumOperands() == 0) + return false; + const MachineOperand &MO = MI.getOperand(0); + if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) + return false; + unsigned Reg = MO.getReg(); + return TargetRegisterInfo::isVirtualRegister(Reg) && + MFI.isVRegStackified(Reg); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h new file mode 100644 index 00000000000..eb114403d14 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -0,0 +1,34 @@ +//===-- WebAssemblyUtilities - WebAssembly Utility Functions ---*- C++ -*-====// +// +// 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 contains the declaration of the WebAssembly-specific +/// utility functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H +#define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYUTILITIES_H + +namespace llvm { + +class MachineInstr; +class WebAssemblyFunctionInfo; + +namespace WebAssembly { + +bool isArgument(const MachineInstr &MI); +bool isCopy(const MachineInstr &MI); +bool isTee(const MachineInstr &MI); +bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI); + +} // end namespace WebAssembly +} // end namespace llvm + +#endif |