Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -14,6 +14,7 @@ WebAssemblyAsmPrinter.cpp WebAssemblyCallIndirectFixup.cpp WebAssemblyCFGStackify.cpp + WebAssemblyExplicitLocals.cpp WebAssemblyFastISel.cpp WebAssemblyFixIrreducibleControlFlow.cpp WebAssemblyFrameLowering.cpp @@ -40,6 +41,7 @@ WebAssemblyTargetMachine.cpp WebAssemblyTargetObjectFile.cpp WebAssemblyTargetTransformInfo.cpp + WebAssemblyUtilities.cpp ) add_dependencies(LLVMWebAssemblyCodeGen intrinsics_gen) Index: lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp =================================================================== --- lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -93,6 +93,7 @@ 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()) Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -149,16 +149,27 @@ 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 @@ 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; } } Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ 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 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( const MCInst &MI, raw_ostream &OS, SmallVectorImpl &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(OS).write(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(OS).write( - 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(OS).write(MO.getReg()); + /* nothing to encode */ } else if (MO.isImm()) { - support::endian::Writer(OS).write(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(OS).write(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(OS).write(f); + } else { + assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); + double d = MO.getFPImm(); + support::endian::Writer(OS).write(d); + } } else if (MO.isExpr()) { - support::endian::Writer(OS).write(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"); } Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -44,6 +44,8 @@ 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 @@ // 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 StoreP2AlignOperandNo = 2; /// This is used to indicate block signatures. -enum ExprType { +enum class ExprType { Void = 0, I32 = 1, I64 = 2, @@ -158,6 +163,20 @@ 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 Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -43,6 +43,7 @@ FunctionPass *createWebAssemblyStoreResults(); FunctionPass *createWebAssemblyRegStackify(); FunctionPass *createWebAssemblyRegColoring(); +FunctionPass *createWebAssemblyExplicitLocals(); FunctionPass *createWebAssemblyFixIrreducibleControlFlow(); FunctionPass *createWebAssemblyCFGStackify(); FunctionPass *createWebAssemblyLowerBrUnless(); Index: lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyArgumentMove.cpp +++ 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 @@ 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 @@ // Look for the first NonArg instruction. for (MachineInstr &MI : EntryMBB) { - if (!IsArgument(MI)) { + if (!WebAssembly::isArgument(MI)) { InsertPt = MI; break; } @@ -101,7 +85,7 @@ // 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; } Index: lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ 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 @@ 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 @@ // 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 @@ // 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 @@ ++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 @@ 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"); } Index: lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ 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(); + 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 &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(); + const auto *TII = MF.getSubtarget().getInstrInfo(); + + // Map non-stackified virtual registers to their local ids. + DenseMap 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; +} Index: lib/Target/WebAssembly/WebAssemblyFastISel.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -542,8 +542,8 @@ &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; Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -587,8 +587,8 @@ unsigned Reg = cast(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 Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -60,19 +60,19 @@ ? 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); } Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -71,6 +71,9 @@ let OperandType = "OPERAND_BASIC_BLOCK" in def bb_op : Operand; +let OperandType = "OPERAND_LOCAL" in +def local_op : Operand; + let OperandType = "OPERAND_I32IMM" in def i32imm_op : Operand; @@ -133,20 +136,42 @@ // are implied by virtual register uses and defs. multiclass LOCAL { 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_locals/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; @@ -155,19 +180,26 @@ defm : LOCAL; defm : LOCAL, 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] Index: lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp +++ lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp @@ -107,9 +107,9 @@ // 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; } Index: lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -64,6 +64,7 @@ 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)); Index: lib/Target/WebAssembly/WebAssemblyPeephole.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ lib/Target/WebAssembly/WebAssemblyPeephole.cpp @@ -83,8 +83,8 @@ 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 @@ 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 && Index: lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyPrepareForLiveIntervals.cpp +++ 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 @@ 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 @@ // 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); } } Index: lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp +++ 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 @@ // 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 Index: lib/Target/WebAssembly/WebAssemblyRegStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ 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 @@ 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 @@ /// 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 @@ /// 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 @@ 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 @@ // 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 Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -41,6 +41,11 @@ cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), cl::init(false)); +static cl::opt ExplicitLocals( + "wasm-explicit-locals", + cl::desc("WebAssembly with explicit get_local/set_local"), + cl::init(false)); + extern "C" void LLVMInitializeWebAssemblyTarget() { // Register the target. RegisterTargetMachine X( @@ -256,6 +261,10 @@ addPass(createWebAssemblyRegColoring()); } + // Insert explicit get_local and set_local operators. + if (ExplicitLocals) + addPass(createWebAssemblyExplicitLocals()); + // Eliminate multiple-entry loops. addPass(createWebAssemblyFixIrreducibleControlFlow()); Index: lib/Target/WebAssembly/WebAssemblyUtilities.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyUtilities.h +++ 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 Index: lib/Target/WebAssembly/WebAssemblyUtilities.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ 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); +}