1*0b57cec5SDimitry Andric //===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric /// 9*0b57cec5SDimitry Andric /// \file 10*0b57cec5SDimitry Andric /// This file converts any remaining registers into WebAssembly locals. 11*0b57cec5SDimitry Andric /// 12*0b57cec5SDimitry Andric /// After register stackification and register coloring, convert non-stackified 13*0b57cec5SDimitry Andric /// registers into locals, inserting explicit local.get and local.set 14*0b57cec5SDimitry Andric /// instructions. 15*0b57cec5SDimitry Andric /// 16*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 17*0b57cec5SDimitry Andric 18*0b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 19*0b57cec5SDimitry Andric #include "WebAssembly.h" 20*0b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 21*0b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 22*0b57cec5SDimitry Andric #include "WebAssemblyUtilities.h" 23*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" 24*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 25*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 26*0b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 27*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 28*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 29*0b57cec5SDimitry Andric using namespace llvm; 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-explicit-locals" 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric // A command-line option to disable this pass, and keep implicit locals 34*0b57cec5SDimitry Andric // for the purpose of testing with lit/llc ONLY. 35*0b57cec5SDimitry Andric // This produces output which is not valid WebAssembly, and is not supported 36*0b57cec5SDimitry Andric // by assemblers/disassemblers and other MC based tools. 37*0b57cec5SDimitry Andric static cl::opt<bool> WasmDisableExplicitLocals( 38*0b57cec5SDimitry Andric "wasm-disable-explicit-locals", cl::Hidden, 39*0b57cec5SDimitry Andric cl::desc("WebAssembly: output implicit locals in" 40*0b57cec5SDimitry Andric " instruction output for test purposes only."), 41*0b57cec5SDimitry Andric cl::init(false)); 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric namespace { 44*0b57cec5SDimitry Andric class WebAssemblyExplicitLocals final : public MachineFunctionPass { 45*0b57cec5SDimitry Andric StringRef getPassName() const override { 46*0b57cec5SDimitry Andric return "WebAssembly Explicit Locals"; 47*0b57cec5SDimitry Andric } 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 50*0b57cec5SDimitry Andric AU.setPreservesCFG(); 51*0b57cec5SDimitry Andric AU.addPreserved<MachineBlockFrequencyInfo>(); 52*0b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 53*0b57cec5SDimitry Andric } 54*0b57cec5SDimitry Andric 55*0b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 56*0b57cec5SDimitry Andric 57*0b57cec5SDimitry Andric public: 58*0b57cec5SDimitry Andric static char ID; // Pass identification, replacement for typeid 59*0b57cec5SDimitry Andric WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {} 60*0b57cec5SDimitry Andric }; 61*0b57cec5SDimitry Andric } // end anonymous namespace 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric char WebAssemblyExplicitLocals::ID = 0; 64*0b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyExplicitLocals, DEBUG_TYPE, 65*0b57cec5SDimitry Andric "Convert registers to WebAssembly locals", false, false) 66*0b57cec5SDimitry Andric 67*0b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyExplicitLocals() { 68*0b57cec5SDimitry Andric return new WebAssemblyExplicitLocals(); 69*0b57cec5SDimitry Andric } 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric /// Return a local id number for the given register, assigning it a new one 72*0b57cec5SDimitry Andric /// if it doesn't yet have one. 73*0b57cec5SDimitry Andric static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local, 74*0b57cec5SDimitry Andric unsigned &CurLocal, unsigned Reg) { 75*0b57cec5SDimitry Andric auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal)); 76*0b57cec5SDimitry Andric if (P.second) 77*0b57cec5SDimitry Andric ++CurLocal; 78*0b57cec5SDimitry Andric return P.first->second; 79*0b57cec5SDimitry Andric } 80*0b57cec5SDimitry Andric 81*0b57cec5SDimitry Andric /// Get the appropriate drop opcode for the given register class. 82*0b57cec5SDimitry Andric static unsigned getDropOpcode(const TargetRegisterClass *RC) { 83*0b57cec5SDimitry Andric if (RC == &WebAssembly::I32RegClass) 84*0b57cec5SDimitry Andric return WebAssembly::DROP_I32; 85*0b57cec5SDimitry Andric if (RC == &WebAssembly::I64RegClass) 86*0b57cec5SDimitry Andric return WebAssembly::DROP_I64; 87*0b57cec5SDimitry Andric if (RC == &WebAssembly::F32RegClass) 88*0b57cec5SDimitry Andric return WebAssembly::DROP_F32; 89*0b57cec5SDimitry Andric if (RC == &WebAssembly::F64RegClass) 90*0b57cec5SDimitry Andric return WebAssembly::DROP_F64; 91*0b57cec5SDimitry Andric if (RC == &WebAssembly::V128RegClass) 92*0b57cec5SDimitry Andric return WebAssembly::DROP_V128; 93*0b57cec5SDimitry Andric if (RC == &WebAssembly::EXNREFRegClass) 94*0b57cec5SDimitry Andric return WebAssembly::DROP_EXNREF; 95*0b57cec5SDimitry Andric llvm_unreachable("Unexpected register class"); 96*0b57cec5SDimitry Andric } 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric /// Get the appropriate local.get opcode for the given register class. 99*0b57cec5SDimitry Andric static unsigned getLocalGetOpcode(const TargetRegisterClass *RC) { 100*0b57cec5SDimitry Andric if (RC == &WebAssembly::I32RegClass) 101*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_I32; 102*0b57cec5SDimitry Andric if (RC == &WebAssembly::I64RegClass) 103*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_I64; 104*0b57cec5SDimitry Andric if (RC == &WebAssembly::F32RegClass) 105*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_F32; 106*0b57cec5SDimitry Andric if (RC == &WebAssembly::F64RegClass) 107*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_F64; 108*0b57cec5SDimitry Andric if (RC == &WebAssembly::V128RegClass) 109*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_V128; 110*0b57cec5SDimitry Andric if (RC == &WebAssembly::EXNREFRegClass) 111*0b57cec5SDimitry Andric return WebAssembly::LOCAL_GET_EXNREF; 112*0b57cec5SDimitry Andric llvm_unreachable("Unexpected register class"); 113*0b57cec5SDimitry Andric } 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric /// Get the appropriate local.set opcode for the given register class. 116*0b57cec5SDimitry Andric static unsigned getLocalSetOpcode(const TargetRegisterClass *RC) { 117*0b57cec5SDimitry Andric if (RC == &WebAssembly::I32RegClass) 118*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_I32; 119*0b57cec5SDimitry Andric if (RC == &WebAssembly::I64RegClass) 120*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_I64; 121*0b57cec5SDimitry Andric if (RC == &WebAssembly::F32RegClass) 122*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_F32; 123*0b57cec5SDimitry Andric if (RC == &WebAssembly::F64RegClass) 124*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_F64; 125*0b57cec5SDimitry Andric if (RC == &WebAssembly::V128RegClass) 126*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_V128; 127*0b57cec5SDimitry Andric if (RC == &WebAssembly::EXNREFRegClass) 128*0b57cec5SDimitry Andric return WebAssembly::LOCAL_SET_EXNREF; 129*0b57cec5SDimitry Andric llvm_unreachable("Unexpected register class"); 130*0b57cec5SDimitry Andric } 131*0b57cec5SDimitry Andric 132*0b57cec5SDimitry Andric /// Get the appropriate local.tee opcode for the given register class. 133*0b57cec5SDimitry Andric static unsigned getLocalTeeOpcode(const TargetRegisterClass *RC) { 134*0b57cec5SDimitry Andric if (RC == &WebAssembly::I32RegClass) 135*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_I32; 136*0b57cec5SDimitry Andric if (RC == &WebAssembly::I64RegClass) 137*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_I64; 138*0b57cec5SDimitry Andric if (RC == &WebAssembly::F32RegClass) 139*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_F32; 140*0b57cec5SDimitry Andric if (RC == &WebAssembly::F64RegClass) 141*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_F64; 142*0b57cec5SDimitry Andric if (RC == &WebAssembly::V128RegClass) 143*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_V128; 144*0b57cec5SDimitry Andric if (RC == &WebAssembly::EXNREFRegClass) 145*0b57cec5SDimitry Andric return WebAssembly::LOCAL_TEE_EXNREF; 146*0b57cec5SDimitry Andric llvm_unreachable("Unexpected register class"); 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric /// Get the type associated with the given register class. 150*0b57cec5SDimitry Andric static MVT typeForRegClass(const TargetRegisterClass *RC) { 151*0b57cec5SDimitry Andric if (RC == &WebAssembly::I32RegClass) 152*0b57cec5SDimitry Andric return MVT::i32; 153*0b57cec5SDimitry Andric if (RC == &WebAssembly::I64RegClass) 154*0b57cec5SDimitry Andric return MVT::i64; 155*0b57cec5SDimitry Andric if (RC == &WebAssembly::F32RegClass) 156*0b57cec5SDimitry Andric return MVT::f32; 157*0b57cec5SDimitry Andric if (RC == &WebAssembly::F64RegClass) 158*0b57cec5SDimitry Andric return MVT::f64; 159*0b57cec5SDimitry Andric if (RC == &WebAssembly::V128RegClass) 160*0b57cec5SDimitry Andric return MVT::v16i8; 161*0b57cec5SDimitry Andric if (RC == &WebAssembly::EXNREFRegClass) 162*0b57cec5SDimitry Andric return MVT::exnref; 163*0b57cec5SDimitry Andric llvm_unreachable("unrecognized register class"); 164*0b57cec5SDimitry Andric } 165*0b57cec5SDimitry Andric 166*0b57cec5SDimitry Andric /// Given a MachineOperand of a stackified vreg, return the instruction at the 167*0b57cec5SDimitry Andric /// start of the expression tree. 168*0b57cec5SDimitry Andric static MachineInstr *findStartOfTree(MachineOperand &MO, 169*0b57cec5SDimitry Andric MachineRegisterInfo &MRI, 170*0b57cec5SDimitry Andric WebAssemblyFunctionInfo &MFI) { 171*0b57cec5SDimitry Andric unsigned Reg = MO.getReg(); 172*0b57cec5SDimitry Andric assert(MFI.isVRegStackified(Reg)); 173*0b57cec5SDimitry Andric MachineInstr *Def = MRI.getVRegDef(Reg); 174*0b57cec5SDimitry Andric 175*0b57cec5SDimitry Andric // Find the first stackified use and proceed from there. 176*0b57cec5SDimitry Andric for (MachineOperand &DefMO : Def->explicit_uses()) { 177*0b57cec5SDimitry Andric if (!DefMO.isReg()) 178*0b57cec5SDimitry Andric continue; 179*0b57cec5SDimitry Andric return findStartOfTree(DefMO, MRI, MFI); 180*0b57cec5SDimitry Andric } 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric // If there were no stackified uses, we've reached the start. 183*0b57cec5SDimitry Andric return Def; 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric 186*0b57cec5SDimitry Andric bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { 187*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n" 188*0b57cec5SDimitry Andric "********** Function: " 189*0b57cec5SDimitry Andric << MF.getName() << '\n'); 190*0b57cec5SDimitry Andric 191*0b57cec5SDimitry Andric // Disable this pass if directed to do so. 192*0b57cec5SDimitry Andric if (WasmDisableExplicitLocals) 193*0b57cec5SDimitry Andric return false; 194*0b57cec5SDimitry Andric 195*0b57cec5SDimitry Andric bool Changed = false; 196*0b57cec5SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 197*0b57cec5SDimitry Andric WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 198*0b57cec5SDimitry Andric const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric // Map non-stackified virtual registers to their local ids. 201*0b57cec5SDimitry Andric DenseMap<unsigned, unsigned> Reg2Local; 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric // Handle ARGUMENTS first to ensure that they get the designated numbers. 204*0b57cec5SDimitry Andric for (MachineBasicBlock::iterator I = MF.begin()->begin(), 205*0b57cec5SDimitry Andric E = MF.begin()->end(); 206*0b57cec5SDimitry Andric I != E;) { 207*0b57cec5SDimitry Andric MachineInstr &MI = *I++; 208*0b57cec5SDimitry Andric if (!WebAssembly::isArgument(MI.getOpcode())) 209*0b57cec5SDimitry Andric break; 210*0b57cec5SDimitry Andric unsigned Reg = MI.getOperand(0).getReg(); 211*0b57cec5SDimitry Andric assert(!MFI.isVRegStackified(Reg)); 212*0b57cec5SDimitry Andric Reg2Local[Reg] = static_cast<unsigned>(MI.getOperand(1).getImm()); 213*0b57cec5SDimitry Andric MI.eraseFromParent(); 214*0b57cec5SDimitry Andric Changed = true; 215*0b57cec5SDimitry Andric } 216*0b57cec5SDimitry Andric 217*0b57cec5SDimitry Andric // Start assigning local numbers after the last parameter. 218*0b57cec5SDimitry Andric unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size()); 219*0b57cec5SDimitry Andric 220*0b57cec5SDimitry Andric // Precompute the set of registers that are unused, so that we can insert 221*0b57cec5SDimitry Andric // drops to their defs. 222*0b57cec5SDimitry Andric BitVector UseEmpty(MRI.getNumVirtRegs()); 223*0b57cec5SDimitry Andric for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) 224*0b57cec5SDimitry Andric UseEmpty[I] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(I)); 225*0b57cec5SDimitry Andric 226*0b57cec5SDimitry Andric // Visit each instruction in the function. 227*0b57cec5SDimitry Andric for (MachineBasicBlock &MBB : MF) { 228*0b57cec5SDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { 229*0b57cec5SDimitry Andric MachineInstr &MI = *I++; 230*0b57cec5SDimitry Andric assert(!WebAssembly::isArgument(MI.getOpcode())); 231*0b57cec5SDimitry Andric 232*0b57cec5SDimitry Andric if (MI.isDebugInstr() || MI.isLabel()) 233*0b57cec5SDimitry Andric continue; 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric // Replace tee instructions with local.tee. The difference is that tee 236*0b57cec5SDimitry Andric // instructions have two defs, while local.tee instructions have one def 237*0b57cec5SDimitry Andric // and an index of a local to write to. 238*0b57cec5SDimitry Andric if (WebAssembly::isTee(MI.getOpcode())) { 239*0b57cec5SDimitry Andric assert(MFI.isVRegStackified(MI.getOperand(0).getReg())); 240*0b57cec5SDimitry Andric assert(!MFI.isVRegStackified(MI.getOperand(1).getReg())); 241*0b57cec5SDimitry Andric unsigned OldReg = MI.getOperand(2).getReg(); 242*0b57cec5SDimitry Andric const TargetRegisterClass *RC = MRI.getRegClass(OldReg); 243*0b57cec5SDimitry Andric 244*0b57cec5SDimitry Andric // Stackify the input if it isn't stackified yet. 245*0b57cec5SDimitry Andric if (!MFI.isVRegStackified(OldReg)) { 246*0b57cec5SDimitry Andric unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); 247*0b57cec5SDimitry Andric unsigned NewReg = MRI.createVirtualRegister(RC); 248*0b57cec5SDimitry Andric unsigned Opc = getLocalGetOpcode(RC); 249*0b57cec5SDimitry Andric BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg) 250*0b57cec5SDimitry Andric .addImm(LocalId); 251*0b57cec5SDimitry Andric MI.getOperand(2).setReg(NewReg); 252*0b57cec5SDimitry Andric MFI.stackifyVReg(NewReg); 253*0b57cec5SDimitry Andric } 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric // Replace the TEE with a LOCAL_TEE. 256*0b57cec5SDimitry Andric unsigned LocalId = 257*0b57cec5SDimitry Andric getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg()); 258*0b57cec5SDimitry Andric unsigned Opc = getLocalTeeOpcode(RC); 259*0b57cec5SDimitry Andric BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), 260*0b57cec5SDimitry Andric MI.getOperand(0).getReg()) 261*0b57cec5SDimitry Andric .addImm(LocalId) 262*0b57cec5SDimitry Andric .addReg(MI.getOperand(2).getReg()); 263*0b57cec5SDimitry Andric 264*0b57cec5SDimitry Andric MI.eraseFromParent(); 265*0b57cec5SDimitry Andric Changed = true; 266*0b57cec5SDimitry Andric continue; 267*0b57cec5SDimitry Andric } 268*0b57cec5SDimitry Andric 269*0b57cec5SDimitry Andric // Insert local.sets for any defs that aren't stackified yet. Currently 270*0b57cec5SDimitry Andric // we handle at most one def. 271*0b57cec5SDimitry Andric assert(MI.getDesc().getNumDefs() <= 1); 272*0b57cec5SDimitry Andric if (MI.getDesc().getNumDefs() == 1) { 273*0b57cec5SDimitry Andric unsigned OldReg = MI.getOperand(0).getReg(); 274*0b57cec5SDimitry Andric if (!MFI.isVRegStackified(OldReg)) { 275*0b57cec5SDimitry Andric const TargetRegisterClass *RC = MRI.getRegClass(OldReg); 276*0b57cec5SDimitry Andric unsigned NewReg = MRI.createVirtualRegister(RC); 277*0b57cec5SDimitry Andric auto InsertPt = std::next(MI.getIterator()); 278*0b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) { 279*0b57cec5SDimitry Andric MI.eraseFromParent(); 280*0b57cec5SDimitry Andric Changed = true; 281*0b57cec5SDimitry Andric continue; 282*0b57cec5SDimitry Andric } 283*0b57cec5SDimitry Andric if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) { 284*0b57cec5SDimitry Andric unsigned Opc = getDropOpcode(RC); 285*0b57cec5SDimitry Andric MachineInstr *Drop = 286*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) 287*0b57cec5SDimitry Andric .addReg(NewReg); 288*0b57cec5SDimitry Andric // After the drop instruction, this reg operand will not be used 289*0b57cec5SDimitry Andric Drop->getOperand(0).setIsKill(); 290*0b57cec5SDimitry Andric } else { 291*0b57cec5SDimitry Andric unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); 292*0b57cec5SDimitry Andric unsigned Opc = getLocalSetOpcode(RC); 293*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc)) 294*0b57cec5SDimitry Andric .addImm(LocalId) 295*0b57cec5SDimitry Andric .addReg(NewReg); 296*0b57cec5SDimitry Andric } 297*0b57cec5SDimitry Andric MI.getOperand(0).setReg(NewReg); 298*0b57cec5SDimitry Andric // This register operand of the original instruction is now being used 299*0b57cec5SDimitry Andric // by the inserted drop or local.set instruction, so make it not dead 300*0b57cec5SDimitry Andric // yet. 301*0b57cec5SDimitry Andric MI.getOperand(0).setIsDead(false); 302*0b57cec5SDimitry Andric MFI.stackifyVReg(NewReg); 303*0b57cec5SDimitry Andric Changed = true; 304*0b57cec5SDimitry Andric } 305*0b57cec5SDimitry Andric } 306*0b57cec5SDimitry Andric 307*0b57cec5SDimitry Andric // Insert local.gets for any uses that aren't stackified yet. 308*0b57cec5SDimitry Andric MachineInstr *InsertPt = &MI; 309*0b57cec5SDimitry Andric for (MachineOperand &MO : reverse(MI.explicit_uses())) { 310*0b57cec5SDimitry Andric if (!MO.isReg()) 311*0b57cec5SDimitry Andric continue; 312*0b57cec5SDimitry Andric 313*0b57cec5SDimitry Andric unsigned OldReg = MO.getReg(); 314*0b57cec5SDimitry Andric 315*0b57cec5SDimitry Andric // Inline asm may have a def in the middle of the operands. Our contract 316*0b57cec5SDimitry Andric // with inline asm register operands is to provide local indices as 317*0b57cec5SDimitry Andric // immediates. 318*0b57cec5SDimitry Andric if (MO.isDef()) { 319*0b57cec5SDimitry Andric assert(MI.isInlineAsm()); 320*0b57cec5SDimitry Andric unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); 321*0b57cec5SDimitry Andric // If this register operand is tied to another operand, we can't 322*0b57cec5SDimitry Andric // change it to an immediate. Untie it first. 323*0b57cec5SDimitry Andric MI.untieRegOperand(MI.getOperandNo(&MO)); 324*0b57cec5SDimitry Andric MO.ChangeToImmediate(LocalId); 325*0b57cec5SDimitry Andric continue; 326*0b57cec5SDimitry Andric } 327*0b57cec5SDimitry Andric 328*0b57cec5SDimitry Andric // If we see a stackified register, prepare to insert subsequent 329*0b57cec5SDimitry Andric // local.gets before the start of its tree. 330*0b57cec5SDimitry Andric if (MFI.isVRegStackified(OldReg)) { 331*0b57cec5SDimitry Andric InsertPt = findStartOfTree(MO, MRI, MFI); 332*0b57cec5SDimitry Andric continue; 333*0b57cec5SDimitry Andric } 334*0b57cec5SDimitry Andric 335*0b57cec5SDimitry Andric // Our contract with inline asm register operands is to provide local 336*0b57cec5SDimitry Andric // indices as immediates. 337*0b57cec5SDimitry Andric if (MI.isInlineAsm()) { 338*0b57cec5SDimitry Andric unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); 339*0b57cec5SDimitry Andric // Untie it first if this reg operand is tied to another operand. 340*0b57cec5SDimitry Andric MI.untieRegOperand(MI.getOperandNo(&MO)); 341*0b57cec5SDimitry Andric MO.ChangeToImmediate(LocalId); 342*0b57cec5SDimitry Andric continue; 343*0b57cec5SDimitry Andric } 344*0b57cec5SDimitry Andric 345*0b57cec5SDimitry Andric // Insert a local.get. 346*0b57cec5SDimitry Andric unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); 347*0b57cec5SDimitry Andric const TargetRegisterClass *RC = MRI.getRegClass(OldReg); 348*0b57cec5SDimitry Andric unsigned NewReg = MRI.createVirtualRegister(RC); 349*0b57cec5SDimitry Andric unsigned Opc = getLocalGetOpcode(RC); 350*0b57cec5SDimitry Andric InsertPt = 351*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg) 352*0b57cec5SDimitry Andric .addImm(LocalId); 353*0b57cec5SDimitry Andric MO.setReg(NewReg); 354*0b57cec5SDimitry Andric MFI.stackifyVReg(NewReg); 355*0b57cec5SDimitry Andric Changed = true; 356*0b57cec5SDimitry Andric } 357*0b57cec5SDimitry Andric 358*0b57cec5SDimitry Andric // Coalesce and eliminate COPY instructions. 359*0b57cec5SDimitry Andric if (WebAssembly::isCopy(MI.getOpcode())) { 360*0b57cec5SDimitry Andric MRI.replaceRegWith(MI.getOperand(1).getReg(), 361*0b57cec5SDimitry Andric MI.getOperand(0).getReg()); 362*0b57cec5SDimitry Andric MI.eraseFromParent(); 363*0b57cec5SDimitry Andric Changed = true; 364*0b57cec5SDimitry Andric } 365*0b57cec5SDimitry Andric } 366*0b57cec5SDimitry Andric } 367*0b57cec5SDimitry Andric 368*0b57cec5SDimitry Andric // Define the locals. 369*0b57cec5SDimitry Andric // TODO: Sort the locals for better compression. 370*0b57cec5SDimitry Andric MFI.setNumLocals(CurLocal - MFI.getParams().size()); 371*0b57cec5SDimitry Andric for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) { 372*0b57cec5SDimitry Andric unsigned Reg = TargetRegisterInfo::index2VirtReg(I); 373*0b57cec5SDimitry Andric auto RL = Reg2Local.find(Reg); 374*0b57cec5SDimitry Andric if (RL == Reg2Local.end() || RL->second < MFI.getParams().size()) 375*0b57cec5SDimitry Andric continue; 376*0b57cec5SDimitry Andric 377*0b57cec5SDimitry Andric MFI.setLocal(RL->second - MFI.getParams().size(), 378*0b57cec5SDimitry Andric typeForRegClass(MRI.getRegClass(Reg))); 379*0b57cec5SDimitry Andric Changed = true; 380*0b57cec5SDimitry Andric } 381*0b57cec5SDimitry Andric 382*0b57cec5SDimitry Andric #ifndef NDEBUG 383*0b57cec5SDimitry Andric // Assert that all registers have been stackified at this point. 384*0b57cec5SDimitry Andric for (const MachineBasicBlock &MBB : MF) { 385*0b57cec5SDimitry Andric for (const MachineInstr &MI : MBB) { 386*0b57cec5SDimitry Andric if (MI.isDebugInstr() || MI.isLabel()) 387*0b57cec5SDimitry Andric continue; 388*0b57cec5SDimitry Andric for (const MachineOperand &MO : MI.explicit_operands()) { 389*0b57cec5SDimitry Andric assert( 390*0b57cec5SDimitry Andric (!MO.isReg() || MRI.use_empty(MO.getReg()) || 391*0b57cec5SDimitry Andric MFI.isVRegStackified(MO.getReg())) && 392*0b57cec5SDimitry Andric "WebAssemblyExplicitLocals failed to stackify a register operand"); 393*0b57cec5SDimitry Andric } 394*0b57cec5SDimitry Andric } 395*0b57cec5SDimitry Andric } 396*0b57cec5SDimitry Andric #endif 397*0b57cec5SDimitry Andric 398*0b57cec5SDimitry Andric return Changed; 399*0b57cec5SDimitry Andric } 400