xref: /src/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1b915e9e0SDimitry Andric //===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===//
2b915e9e0SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b915e9e0SDimitry Andric //
7b915e9e0SDimitry Andric //===----------------------------------------------------------------------===//
8b915e9e0SDimitry Andric ///
9b915e9e0SDimitry Andric /// \file
10eb11fae6SDimitry Andric /// This file converts any remaining registers into WebAssembly locals.
11b915e9e0SDimitry Andric ///
12b915e9e0SDimitry Andric /// After register stackification and register coloring, convert non-stackified
13d8e91e46SDimitry Andric /// registers into locals, inserting explicit local.get and local.set
14b915e9e0SDimitry Andric /// instructions.
15b915e9e0SDimitry Andric ///
16b915e9e0SDimitry Andric //===----------------------------------------------------------------------===//
17b915e9e0SDimitry Andric 
18b915e9e0SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19b915e9e0SDimitry Andric #include "WebAssembly.h"
20706b4fc4SDimitry Andric #include "WebAssemblyDebugValueManager.h"
21b915e9e0SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h"
22b915e9e0SDimitry Andric #include "WebAssemblySubtarget.h"
23b1c73532SDimitry Andric #include "WebAssemblyUtilities.h"
24b915e9e0SDimitry Andric #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
25b915e9e0SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
26b915e9e0SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
27b915e9e0SDimitry Andric #include "llvm/CodeGen/Passes.h"
28b915e9e0SDimitry Andric #include "llvm/Support/Debug.h"
29b915e9e0SDimitry Andric #include "llvm/Support/raw_ostream.h"
30b915e9e0SDimitry Andric using namespace llvm;
31b915e9e0SDimitry Andric 
32b915e9e0SDimitry Andric #define DEBUG_TYPE "wasm-explicit-locals"
33b915e9e0SDimitry Andric 
34b915e9e0SDimitry Andric namespace {
35b915e9e0SDimitry Andric class WebAssemblyExplicitLocals final : public MachineFunctionPass {
getPassName() const36b915e9e0SDimitry Andric   StringRef getPassName() const override {
37b915e9e0SDimitry Andric     return "WebAssembly Explicit Locals";
38b915e9e0SDimitry Andric   }
39b915e9e0SDimitry Andric 
getAnalysisUsage(AnalysisUsage & AU) const40b915e9e0SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
41b915e9e0SDimitry Andric     AU.setPreservesCFG();
42ac9a064cSDimitry Andric     AU.addPreserved<MachineBlockFrequencyInfoWrapperPass>();
43b915e9e0SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
44b915e9e0SDimitry Andric   }
45b915e9e0SDimitry Andric 
46b915e9e0SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
47b915e9e0SDimitry Andric 
48b915e9e0SDimitry Andric public:
49b915e9e0SDimitry Andric   static char ID; // Pass identification, replacement for typeid
WebAssemblyExplicitLocals()50b915e9e0SDimitry Andric   WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {}
51b915e9e0SDimitry Andric };
52b915e9e0SDimitry Andric } // end anonymous namespace
53b915e9e0SDimitry Andric 
54b915e9e0SDimitry Andric char WebAssemblyExplicitLocals::ID = 0;
55eb11fae6SDimitry Andric INITIALIZE_PASS(WebAssemblyExplicitLocals, DEBUG_TYPE,
56eb11fae6SDimitry Andric                 "Convert registers to WebAssembly locals", false, false)
57eb11fae6SDimitry Andric 
createWebAssemblyExplicitLocals()58b915e9e0SDimitry Andric FunctionPass *llvm::createWebAssemblyExplicitLocals() {
59b915e9e0SDimitry Andric   return new WebAssemblyExplicitLocals();
60b915e9e0SDimitry Andric }
61b915e9e0SDimitry Andric 
checkFrameBase(WebAssemblyFunctionInfo & MFI,unsigned Local,unsigned Reg)62cfca06d7SDimitry Andric static void checkFrameBase(WebAssemblyFunctionInfo &MFI, unsigned Local,
63cfca06d7SDimitry Andric                            unsigned Reg) {
64cfca06d7SDimitry Andric   // Mark a local for the frame base vreg.
65cfca06d7SDimitry Andric   if (MFI.isFrameBaseVirtual() && Reg == MFI.getFrameBaseVreg()) {
66cfca06d7SDimitry Andric     LLVM_DEBUG({
67cfca06d7SDimitry Andric       dbgs() << "Allocating local " << Local << "for VReg "
68cfca06d7SDimitry Andric              << Register::virtReg2Index(Reg) << '\n';
69cfca06d7SDimitry Andric     });
70cfca06d7SDimitry Andric     MFI.setFrameBaseLocal(Local);
71cfca06d7SDimitry Andric   }
72cfca06d7SDimitry Andric }
73cfca06d7SDimitry Andric 
74b915e9e0SDimitry Andric /// Return a local id number for the given register, assigning it a new one
75b915e9e0SDimitry Andric /// if it doesn't yet have one.
getLocalId(DenseMap<unsigned,unsigned> & Reg2Local,WebAssemblyFunctionInfo & MFI,unsigned & CurLocal,unsigned Reg)76b915e9e0SDimitry Andric static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,
77cfca06d7SDimitry Andric                            WebAssemblyFunctionInfo &MFI, unsigned &CurLocal,
78cfca06d7SDimitry Andric                            unsigned Reg) {
7971d5a254SDimitry Andric   auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal));
80cfca06d7SDimitry Andric   if (P.second) {
81cfca06d7SDimitry Andric     checkFrameBase(MFI, CurLocal, Reg);
8271d5a254SDimitry Andric     ++CurLocal;
83cfca06d7SDimitry Andric   }
8471d5a254SDimitry Andric   return P.first->second;
8571d5a254SDimitry Andric }
8671d5a254SDimitry Andric 
8771d5a254SDimitry Andric /// Get the appropriate drop opcode for the given register class.
getDropOpcode(const TargetRegisterClass * RC)8871d5a254SDimitry Andric static unsigned getDropOpcode(const TargetRegisterClass *RC) {
8971d5a254SDimitry Andric   if (RC == &WebAssembly::I32RegClass)
9071d5a254SDimitry Andric     return WebAssembly::DROP_I32;
9171d5a254SDimitry Andric   if (RC == &WebAssembly::I64RegClass)
9271d5a254SDimitry Andric     return WebAssembly::DROP_I64;
9371d5a254SDimitry Andric   if (RC == &WebAssembly::F32RegClass)
9471d5a254SDimitry Andric     return WebAssembly::DROP_F32;
9571d5a254SDimitry Andric   if (RC == &WebAssembly::F64RegClass)
9671d5a254SDimitry Andric     return WebAssembly::DROP_F64;
9771d5a254SDimitry Andric   if (RC == &WebAssembly::V128RegClass)
9871d5a254SDimitry Andric     return WebAssembly::DROP_V128;
99b60736ecSDimitry Andric   if (RC == &WebAssembly::FUNCREFRegClass)
100b60736ecSDimitry Andric     return WebAssembly::DROP_FUNCREF;
101b60736ecSDimitry Andric   if (RC == &WebAssembly::EXTERNREFRegClass)
102b60736ecSDimitry Andric     return WebAssembly::DROP_EXTERNREF;
103ac9a064cSDimitry Andric   if (RC == &WebAssembly::EXNREFRegClass)
104ac9a064cSDimitry Andric     return WebAssembly::DROP_EXNREF;
10571d5a254SDimitry Andric   llvm_unreachable("Unexpected register class");
106b915e9e0SDimitry Andric }
107b915e9e0SDimitry Andric 
108d8e91e46SDimitry Andric /// Get the appropriate local.get opcode for the given register class.
getLocalGetOpcode(const TargetRegisterClass * RC)109e6d15924SDimitry Andric static unsigned getLocalGetOpcode(const TargetRegisterClass *RC) {
110b915e9e0SDimitry Andric   if (RC == &WebAssembly::I32RegClass)
111d8e91e46SDimitry Andric     return WebAssembly::LOCAL_GET_I32;
112b915e9e0SDimitry Andric   if (RC == &WebAssembly::I64RegClass)
113d8e91e46SDimitry Andric     return WebAssembly::LOCAL_GET_I64;
114b915e9e0SDimitry Andric   if (RC == &WebAssembly::F32RegClass)
115d8e91e46SDimitry Andric     return WebAssembly::LOCAL_GET_F32;
116b915e9e0SDimitry Andric   if (RC == &WebAssembly::F64RegClass)
117d8e91e46SDimitry Andric     return WebAssembly::LOCAL_GET_F64;
118b915e9e0SDimitry Andric   if (RC == &WebAssembly::V128RegClass)
119d8e91e46SDimitry Andric     return WebAssembly::LOCAL_GET_V128;
120b60736ecSDimitry Andric   if (RC == &WebAssembly::FUNCREFRegClass)
121b60736ecSDimitry Andric     return WebAssembly::LOCAL_GET_FUNCREF;
122b60736ecSDimitry Andric   if (RC == &WebAssembly::EXTERNREFRegClass)
123b60736ecSDimitry Andric     return WebAssembly::LOCAL_GET_EXTERNREF;
124ac9a064cSDimitry Andric   if (RC == &WebAssembly::EXNREFRegClass)
125ac9a064cSDimitry Andric     return WebAssembly::LOCAL_GET_EXNREF;
126b915e9e0SDimitry Andric   llvm_unreachable("Unexpected register class");
127b915e9e0SDimitry Andric }
128b915e9e0SDimitry Andric 
129d8e91e46SDimitry Andric /// Get the appropriate local.set opcode for the given register class.
getLocalSetOpcode(const TargetRegisterClass * RC)130e6d15924SDimitry Andric static unsigned getLocalSetOpcode(const TargetRegisterClass *RC) {
131b915e9e0SDimitry Andric   if (RC == &WebAssembly::I32RegClass)
132d8e91e46SDimitry Andric     return WebAssembly::LOCAL_SET_I32;
133b915e9e0SDimitry Andric   if (RC == &WebAssembly::I64RegClass)
134d8e91e46SDimitry Andric     return WebAssembly::LOCAL_SET_I64;
135b915e9e0SDimitry Andric   if (RC == &WebAssembly::F32RegClass)
136d8e91e46SDimitry Andric     return WebAssembly::LOCAL_SET_F32;
137b915e9e0SDimitry Andric   if (RC == &WebAssembly::F64RegClass)
138d8e91e46SDimitry Andric     return WebAssembly::LOCAL_SET_F64;
139b915e9e0SDimitry Andric   if (RC == &WebAssembly::V128RegClass)
140d8e91e46SDimitry Andric     return WebAssembly::LOCAL_SET_V128;
141b60736ecSDimitry Andric   if (RC == &WebAssembly::FUNCREFRegClass)
142b60736ecSDimitry Andric     return WebAssembly::LOCAL_SET_FUNCREF;
143b60736ecSDimitry Andric   if (RC == &WebAssembly::EXTERNREFRegClass)
144b60736ecSDimitry Andric     return WebAssembly::LOCAL_SET_EXTERNREF;
145ac9a064cSDimitry Andric   if (RC == &WebAssembly::EXNREFRegClass)
146ac9a064cSDimitry Andric     return WebAssembly::LOCAL_SET_EXNREF;
147b915e9e0SDimitry Andric   llvm_unreachable("Unexpected register class");
148b915e9e0SDimitry Andric }
149b915e9e0SDimitry Andric 
150d8e91e46SDimitry Andric /// Get the appropriate local.tee opcode for the given register class.
getLocalTeeOpcode(const TargetRegisterClass * RC)151e6d15924SDimitry Andric static unsigned getLocalTeeOpcode(const TargetRegisterClass *RC) {
152b915e9e0SDimitry Andric   if (RC == &WebAssembly::I32RegClass)
153d8e91e46SDimitry Andric     return WebAssembly::LOCAL_TEE_I32;
154b915e9e0SDimitry Andric   if (RC == &WebAssembly::I64RegClass)
155d8e91e46SDimitry Andric     return WebAssembly::LOCAL_TEE_I64;
156b915e9e0SDimitry Andric   if (RC == &WebAssembly::F32RegClass)
157d8e91e46SDimitry Andric     return WebAssembly::LOCAL_TEE_F32;
158b915e9e0SDimitry Andric   if (RC == &WebAssembly::F64RegClass)
159d8e91e46SDimitry Andric     return WebAssembly::LOCAL_TEE_F64;
160b915e9e0SDimitry Andric   if (RC == &WebAssembly::V128RegClass)
161d8e91e46SDimitry Andric     return WebAssembly::LOCAL_TEE_V128;
162b60736ecSDimitry Andric   if (RC == &WebAssembly::FUNCREFRegClass)
163b60736ecSDimitry Andric     return WebAssembly::LOCAL_TEE_FUNCREF;
164b60736ecSDimitry Andric   if (RC == &WebAssembly::EXTERNREFRegClass)
165b60736ecSDimitry Andric     return WebAssembly::LOCAL_TEE_EXTERNREF;
166ac9a064cSDimitry Andric   if (RC == &WebAssembly::EXNREFRegClass)
167ac9a064cSDimitry Andric     return WebAssembly::LOCAL_TEE_EXNREF;
168b915e9e0SDimitry Andric   llvm_unreachable("Unexpected register class");
169b915e9e0SDimitry Andric }
170b915e9e0SDimitry Andric 
171b915e9e0SDimitry Andric /// Get the type associated with the given register class.
typeForRegClass(const TargetRegisterClass * RC)172b915e9e0SDimitry Andric static MVT typeForRegClass(const TargetRegisterClass *RC) {
173b915e9e0SDimitry Andric   if (RC == &WebAssembly::I32RegClass)
174b915e9e0SDimitry Andric     return MVT::i32;
175b915e9e0SDimitry Andric   if (RC == &WebAssembly::I64RegClass)
176b915e9e0SDimitry Andric     return MVT::i64;
177b915e9e0SDimitry Andric   if (RC == &WebAssembly::F32RegClass)
178b915e9e0SDimitry Andric     return MVT::f32;
179b915e9e0SDimitry Andric   if (RC == &WebAssembly::F64RegClass)
180b915e9e0SDimitry Andric     return MVT::f64;
181d8e91e46SDimitry Andric   if (RC == &WebAssembly::V128RegClass)
182d8e91e46SDimitry Andric     return MVT::v16i8;
183b60736ecSDimitry Andric   if (RC == &WebAssembly::FUNCREFRegClass)
184b60736ecSDimitry Andric     return MVT::funcref;
185b60736ecSDimitry Andric   if (RC == &WebAssembly::EXTERNREFRegClass)
186b60736ecSDimitry Andric     return MVT::externref;
187ac9a064cSDimitry Andric   if (RC == &WebAssembly::EXNREFRegClass)
188ac9a064cSDimitry Andric     return MVT::exnref;
189b915e9e0SDimitry Andric   llvm_unreachable("unrecognized register class");
190b915e9e0SDimitry Andric }
191b915e9e0SDimitry Andric 
192b915e9e0SDimitry Andric /// Given a MachineOperand of a stackified vreg, return the instruction at the
193b915e9e0SDimitry Andric /// start of the expression tree.
findStartOfTree(MachineOperand & MO,MachineRegisterInfo & MRI,const WebAssemblyFunctionInfo & MFI)194d8e91e46SDimitry Andric static MachineInstr *findStartOfTree(MachineOperand &MO,
195b915e9e0SDimitry Andric                                      MachineRegisterInfo &MRI,
196cfca06d7SDimitry Andric                                      const WebAssemblyFunctionInfo &MFI) {
1971d5ae102SDimitry Andric   Register Reg = MO.getReg();
198b915e9e0SDimitry Andric   assert(MFI.isVRegStackified(Reg));
199b915e9e0SDimitry Andric   MachineInstr *Def = MRI.getVRegDef(Reg);
200b915e9e0SDimitry Andric 
201cfca06d7SDimitry Andric   // If this instruction has any non-stackified defs, it is the start
202cfca06d7SDimitry Andric   for (auto DefReg : Def->defs()) {
203cfca06d7SDimitry Andric     if (!MFI.isVRegStackified(DefReg.getReg())) {
204cfca06d7SDimitry Andric       return Def;
205cfca06d7SDimitry Andric     }
206cfca06d7SDimitry Andric   }
207cfca06d7SDimitry Andric 
208b915e9e0SDimitry Andric   // Find the first stackified use and proceed from there.
209b915e9e0SDimitry Andric   for (MachineOperand &DefMO : Def->explicit_uses()) {
210b915e9e0SDimitry Andric     if (!DefMO.isReg())
211b915e9e0SDimitry Andric       continue;
212d8e91e46SDimitry Andric     return findStartOfTree(DefMO, MRI, MFI);
213b915e9e0SDimitry Andric   }
214b915e9e0SDimitry Andric 
215b915e9e0SDimitry Andric   // If there were no stackified uses, we've reached the start.
216b915e9e0SDimitry Andric   return Def;
217b915e9e0SDimitry Andric }
218b915e9e0SDimitry Andric 
runOnMachineFunction(MachineFunction & MF)219b915e9e0SDimitry Andric bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
220eb11fae6SDimitry Andric   LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n"
221b915e9e0SDimitry Andric                        "********** Function: "
222b915e9e0SDimitry Andric                     << MF.getName() << '\n');
223b915e9e0SDimitry Andric 
224b915e9e0SDimitry Andric   bool Changed = false;
225b915e9e0SDimitry Andric   MachineRegisterInfo &MRI = MF.getRegInfo();
226b915e9e0SDimitry Andric   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
227b915e9e0SDimitry Andric   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
228b915e9e0SDimitry Andric 
229b915e9e0SDimitry Andric   // Map non-stackified virtual registers to their local ids.
230b915e9e0SDimitry Andric   DenseMap<unsigned, unsigned> Reg2Local;
231b915e9e0SDimitry Andric 
232b915e9e0SDimitry Andric   // Handle ARGUMENTS first to ensure that they get the designated numbers.
233b915e9e0SDimitry Andric   for (MachineBasicBlock::iterator I = MF.begin()->begin(),
234b915e9e0SDimitry Andric                                    E = MF.begin()->end();
235b915e9e0SDimitry Andric        I != E;) {
236b915e9e0SDimitry Andric     MachineInstr &MI = *I++;
237e6d15924SDimitry Andric     if (!WebAssembly::isArgument(MI.getOpcode()))
238b915e9e0SDimitry Andric       break;
2391d5ae102SDimitry Andric     Register Reg = MI.getOperand(0).getReg();
240b915e9e0SDimitry Andric     assert(!MFI.isVRegStackified(Reg));
241cfca06d7SDimitry Andric     auto Local = static_cast<unsigned>(MI.getOperand(1).getImm());
242cfca06d7SDimitry Andric     Reg2Local[Reg] = Local;
243cfca06d7SDimitry Andric     checkFrameBase(MFI, Local, Reg);
244b60736ecSDimitry Andric 
245b60736ecSDimitry Andric     // Update debug value to point to the local before removing.
246b60736ecSDimitry Andric     WebAssemblyDebugValueManager(&MI).replaceWithLocal(Local);
247b60736ecSDimitry Andric 
248b915e9e0SDimitry Andric     MI.eraseFromParent();
249b915e9e0SDimitry Andric     Changed = true;
250b915e9e0SDimitry Andric   }
251b915e9e0SDimitry Andric 
252344a3780SDimitry Andric   // Start assigning local numbers after the last parameter and after any
253344a3780SDimitry Andric   // already-assigned locals.
254d8e91e46SDimitry Andric   unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());
255344a3780SDimitry Andric   CurLocal += static_cast<unsigned>(MFI.getLocals().size());
256b915e9e0SDimitry Andric 
25771d5a254SDimitry Andric   // Precompute the set of registers that are unused, so that we can insert
25871d5a254SDimitry Andric   // drops to their defs.
25971d5a254SDimitry Andric   BitVector UseEmpty(MRI.getNumVirtRegs());
260d8e91e46SDimitry Andric   for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I)
2611d5ae102SDimitry Andric     UseEmpty[I] = MRI.use_empty(Register::index2VirtReg(I));
26271d5a254SDimitry Andric 
263b915e9e0SDimitry Andric   // Visit each instruction in the function.
264b915e9e0SDimitry Andric   for (MachineBasicBlock &MBB : MF) {
265c0981da4SDimitry Andric     for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) {
266e6d15924SDimitry Andric       assert(!WebAssembly::isArgument(MI.getOpcode()));
267b915e9e0SDimitry Andric 
268eb11fae6SDimitry Andric       if (MI.isDebugInstr() || MI.isLabel())
269b915e9e0SDimitry Andric         continue;
270b915e9e0SDimitry Andric 
271cfca06d7SDimitry Andric       if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {
272cfca06d7SDimitry Andric         MI.eraseFromParent();
273cfca06d7SDimitry Andric         Changed = true;
274cfca06d7SDimitry Andric         continue;
275cfca06d7SDimitry Andric       }
276cfca06d7SDimitry Andric 
277d8e91e46SDimitry Andric       // Replace tee instructions with local.tee. The difference is that tee
278d8e91e46SDimitry Andric       // instructions have two defs, while local.tee instructions have one def
279b915e9e0SDimitry Andric       // and an index of a local to write to.
2807fa27ce4SDimitry Andric       //
2817fa27ce4SDimitry Andric       // - Before:
2827fa27ce4SDimitry Andric       // TeeReg, Reg = TEE DefReg
2837fa27ce4SDimitry Andric       // INST ..., TeeReg, ...
2847fa27ce4SDimitry Andric       // INST ..., Reg, ...
2857fa27ce4SDimitry Andric       // INST ..., Reg, ...
2867fa27ce4SDimitry Andric       // * DefReg: may or may not be stackified
2877fa27ce4SDimitry Andric       // * Reg: not stackified
2887fa27ce4SDimitry Andric       // * TeeReg: stackified
2897fa27ce4SDimitry Andric       //
2907fa27ce4SDimitry Andric       // - After (when DefReg was already stackified):
2917fa27ce4SDimitry Andric       // TeeReg = LOCAL_TEE LocalId1, DefReg
2927fa27ce4SDimitry Andric       // INST ..., TeeReg, ...
2937fa27ce4SDimitry Andric       // INST ..., Reg, ...
2947fa27ce4SDimitry Andric       // INST ..., Reg, ...
2957fa27ce4SDimitry Andric       // * Reg: mapped to LocalId1
2967fa27ce4SDimitry Andric       // * TeeReg: stackified
2977fa27ce4SDimitry Andric       //
2987fa27ce4SDimitry Andric       // - After (when DefReg was not already stackified):
2997fa27ce4SDimitry Andric       // NewReg = LOCAL_GET LocalId1
3007fa27ce4SDimitry Andric       // TeeReg = LOCAL_TEE LocalId2, NewReg
3017fa27ce4SDimitry Andric       // INST ..., TeeReg, ...
3027fa27ce4SDimitry Andric       // INST ..., Reg, ...
3037fa27ce4SDimitry Andric       // INST ..., Reg, ...
3047fa27ce4SDimitry Andric       // * DefReg: mapped to LocalId1
3057fa27ce4SDimitry Andric       // * Reg: mapped to LocalId2
3067fa27ce4SDimitry Andric       // * TeeReg: stackified
307e6d15924SDimitry Andric       if (WebAssembly::isTee(MI.getOpcode())) {
308b915e9e0SDimitry Andric         assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
309b915e9e0SDimitry Andric         assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));
3107fa27ce4SDimitry Andric         Register DefReg = MI.getOperand(2).getReg();
3117fa27ce4SDimitry Andric         const TargetRegisterClass *RC = MRI.getRegClass(DefReg);
312b915e9e0SDimitry Andric 
313b915e9e0SDimitry Andric         // Stackify the input if it isn't stackified yet.
3147fa27ce4SDimitry Andric         if (!MFI.isVRegStackified(DefReg)) {
3157fa27ce4SDimitry Andric           unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, DefReg);
3161d5ae102SDimitry Andric           Register NewReg = MRI.createVirtualRegister(RC);
317e6d15924SDimitry Andric           unsigned Opc = getLocalGetOpcode(RC);
318b915e9e0SDimitry Andric           BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
319b915e9e0SDimitry Andric               .addImm(LocalId);
320b915e9e0SDimitry Andric           MI.getOperand(2).setReg(NewReg);
321cfca06d7SDimitry Andric           MFI.stackifyVReg(MRI, NewReg);
322b915e9e0SDimitry Andric         }
323b915e9e0SDimitry Andric 
324d8e91e46SDimitry Andric         // Replace the TEE with a LOCAL_TEE.
325b915e9e0SDimitry Andric         unsigned LocalId =
326cfca06d7SDimitry Andric             getLocalId(Reg2Local, MFI, CurLocal, MI.getOperand(1).getReg());
327e6d15924SDimitry Andric         unsigned Opc = getLocalTeeOpcode(RC);
328b915e9e0SDimitry Andric         BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
329b915e9e0SDimitry Andric                 MI.getOperand(0).getReg())
330b915e9e0SDimitry Andric             .addImm(LocalId)
331b915e9e0SDimitry Andric             .addReg(MI.getOperand(2).getReg());
332b915e9e0SDimitry Andric 
333706b4fc4SDimitry Andric         WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);
334706b4fc4SDimitry Andric 
335b915e9e0SDimitry Andric         MI.eraseFromParent();
336b915e9e0SDimitry Andric         Changed = true;
337b915e9e0SDimitry Andric         continue;
338b915e9e0SDimitry Andric       }
339b915e9e0SDimitry Andric 
340cfca06d7SDimitry Andric       // Insert local.sets for any defs that aren't stackified yet.
341cfca06d7SDimitry Andric       for (auto &Def : MI.defs()) {
342cfca06d7SDimitry Andric         Register OldReg = Def.getReg();
34371d5a254SDimitry Andric         if (!MFI.isVRegStackified(OldReg)) {
344b915e9e0SDimitry Andric           const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
3451d5ae102SDimitry Andric           Register NewReg = MRI.createVirtualRegister(RC);
346e6d15924SDimitry Andric           auto InsertPt = std::next(MI.getIterator());
3471d5ae102SDimitry Andric           if (UseEmpty[Register::virtReg2Index(OldReg)]) {
34871d5a254SDimitry Andric             unsigned Opc = getDropOpcode(RC);
349eb11fae6SDimitry Andric             MachineInstr *Drop =
35071d5a254SDimitry Andric                 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
35171d5a254SDimitry Andric                     .addReg(NewReg);
352eb11fae6SDimitry Andric             // After the drop instruction, this reg operand will not be used
353eb11fae6SDimitry Andric             Drop->getOperand(0).setIsKill();
354cfca06d7SDimitry Andric             if (MFI.isFrameBaseVirtual() && OldReg == MFI.getFrameBaseVreg())
355cfca06d7SDimitry Andric               MFI.clearFrameBaseVreg();
35671d5a254SDimitry Andric           } else {
357cfca06d7SDimitry Andric             unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
358e6d15924SDimitry Andric             unsigned Opc = getLocalSetOpcode(RC);
359706b4fc4SDimitry Andric 
360706b4fc4SDimitry Andric             WebAssemblyDebugValueManager(&MI).replaceWithLocal(LocalId);
361706b4fc4SDimitry Andric 
362b915e9e0SDimitry Andric             BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
363b915e9e0SDimitry Andric                 .addImm(LocalId)
364b915e9e0SDimitry Andric                 .addReg(NewReg);
36571d5a254SDimitry Andric           }
366d8e91e46SDimitry Andric           // This register operand of the original instruction is now being used
367d8e91e46SDimitry Andric           // by the inserted drop or local.set instruction, so make it not dead
368d8e91e46SDimitry Andric           // yet.
369cfca06d7SDimitry Andric           Def.setReg(NewReg);
370cfca06d7SDimitry Andric           Def.setIsDead(false);
371cfca06d7SDimitry Andric           MFI.stackifyVReg(MRI, NewReg);
372b915e9e0SDimitry Andric           Changed = true;
373b915e9e0SDimitry Andric         }
374b915e9e0SDimitry Andric       }
375b915e9e0SDimitry Andric 
376d8e91e46SDimitry Andric       // Insert local.gets for any uses that aren't stackified yet.
377b915e9e0SDimitry Andric       MachineInstr *InsertPt = &MI;
378b915e9e0SDimitry Andric       for (MachineOperand &MO : reverse(MI.explicit_uses())) {
379b915e9e0SDimitry Andric         if (!MO.isReg())
380b915e9e0SDimitry Andric           continue;
381b915e9e0SDimitry Andric 
3821d5ae102SDimitry Andric         Register OldReg = MO.getReg();
383b915e9e0SDimitry Andric 
384044eb2f6SDimitry Andric         // Inline asm may have a def in the middle of the operands. Our contract
385044eb2f6SDimitry Andric         // with inline asm register operands is to provide local indices as
386044eb2f6SDimitry Andric         // immediates.
387044eb2f6SDimitry Andric         if (MO.isDef()) {
388e6d15924SDimitry Andric           assert(MI.isInlineAsm());
389cfca06d7SDimitry Andric           unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
390d8e91e46SDimitry Andric           // If this register operand is tied to another operand, we can't
391d8e91e46SDimitry Andric           // change it to an immediate. Untie it first.
3927fa27ce4SDimitry Andric           MI.untieRegOperand(MO.getOperandNo());
393d8e91e46SDimitry Andric           MO.ChangeToImmediate(LocalId);
394044eb2f6SDimitry Andric           continue;
395044eb2f6SDimitry Andric         }
396044eb2f6SDimitry Andric 
397b915e9e0SDimitry Andric         // If we see a stackified register, prepare to insert subsequent
398d8e91e46SDimitry Andric         // local.gets before the start of its tree.
399b915e9e0SDimitry Andric         if (MFI.isVRegStackified(OldReg)) {
400d8e91e46SDimitry Andric           InsertPt = findStartOfTree(MO, MRI, MFI);
401b915e9e0SDimitry Andric           continue;
402b915e9e0SDimitry Andric         }
403b915e9e0SDimitry Andric 
404044eb2f6SDimitry Andric         // Our contract with inline asm register operands is to provide local
405044eb2f6SDimitry Andric         // indices as immediates.
406e6d15924SDimitry Andric         if (MI.isInlineAsm()) {
407cfca06d7SDimitry Andric           unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
408d8e91e46SDimitry Andric           // Untie it first if this reg operand is tied to another operand.
4097fa27ce4SDimitry Andric           MI.untieRegOperand(MO.getOperandNo());
410d8e91e46SDimitry Andric           MO.ChangeToImmediate(LocalId);
411044eb2f6SDimitry Andric           continue;
412044eb2f6SDimitry Andric         }
413044eb2f6SDimitry Andric 
414d8e91e46SDimitry Andric         // Insert a local.get.
415cfca06d7SDimitry Andric         unsigned LocalId = getLocalId(Reg2Local, MFI, CurLocal, OldReg);
416b915e9e0SDimitry Andric         const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
4171d5ae102SDimitry Andric         Register NewReg = MRI.createVirtualRegister(RC);
418e6d15924SDimitry Andric         unsigned Opc = getLocalGetOpcode(RC);
419c0981da4SDimitry Andric         // Use a InsertPt as our DebugLoc, since MI may be discontinuous from
420c0981da4SDimitry Andric         // the where this local is being inserted, causing non-linear stepping
421c0981da4SDimitry Andric         // in the debugger or function entry points where variables aren't live
422c0981da4SDimitry Andric         // yet. Alternative is previous instruction, but that is strictly worse
423c0981da4SDimitry Andric         // since it can point at the previous statement.
424c0981da4SDimitry Andric         // See crbug.com/1251909, crbug.com/1249745
425c0981da4SDimitry Andric         InsertPt = BuildMI(MBB, InsertPt, InsertPt->getDebugLoc(),
426c0981da4SDimitry Andric                            TII->get(Opc), NewReg).addImm(LocalId);
427b915e9e0SDimitry Andric         MO.setReg(NewReg);
428cfca06d7SDimitry Andric         MFI.stackifyVReg(MRI, NewReg);
429b915e9e0SDimitry Andric         Changed = true;
430b915e9e0SDimitry Andric       }
431b915e9e0SDimitry Andric 
432b915e9e0SDimitry Andric       // Coalesce and eliminate COPY instructions.
433e6d15924SDimitry Andric       if (WebAssembly::isCopy(MI.getOpcode())) {
434b915e9e0SDimitry Andric         MRI.replaceRegWith(MI.getOperand(1).getReg(),
435b915e9e0SDimitry Andric                            MI.getOperand(0).getReg());
436b915e9e0SDimitry Andric         MI.eraseFromParent();
437b915e9e0SDimitry Andric         Changed = true;
438b915e9e0SDimitry Andric       }
439b915e9e0SDimitry Andric     }
440b915e9e0SDimitry Andric   }
441b915e9e0SDimitry Andric 
442b915e9e0SDimitry Andric   // Define the locals.
44371d5a254SDimitry Andric   // TODO: Sort the locals for better compression.
44471d5a254SDimitry Andric   MFI.setNumLocals(CurLocal - MFI.getParams().size());
445d8e91e46SDimitry Andric   for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
4466f8fc217SDimitry Andric     Register Reg = Register::index2VirtReg(I);
447d8e91e46SDimitry Andric     auto RL = Reg2Local.find(Reg);
448d8e91e46SDimitry Andric     if (RL == Reg2Local.end() || RL->second < MFI.getParams().size())
449b915e9e0SDimitry Andric       continue;
450b915e9e0SDimitry Andric 
451d8e91e46SDimitry Andric     MFI.setLocal(RL->second - MFI.getParams().size(),
45271d5a254SDimitry Andric                  typeForRegClass(MRI.getRegClass(Reg)));
453b915e9e0SDimitry Andric     Changed = true;
454b915e9e0SDimitry Andric   }
455b915e9e0SDimitry Andric 
456b915e9e0SDimitry Andric #ifndef NDEBUG
457b915e9e0SDimitry Andric   // Assert that all registers have been stackified at this point.
458b915e9e0SDimitry Andric   for (const MachineBasicBlock &MBB : MF) {
459b915e9e0SDimitry Andric     for (const MachineInstr &MI : MBB) {
460eb11fae6SDimitry Andric       if (MI.isDebugInstr() || MI.isLabel())
461b915e9e0SDimitry Andric         continue;
462b915e9e0SDimitry Andric       for (const MachineOperand &MO : MI.explicit_operands()) {
463b915e9e0SDimitry Andric         assert(
464b915e9e0SDimitry Andric             (!MO.isReg() || MRI.use_empty(MO.getReg()) ||
465b915e9e0SDimitry Andric              MFI.isVRegStackified(MO.getReg())) &&
466b915e9e0SDimitry Andric             "WebAssemblyExplicitLocals failed to stackify a register operand");
467b915e9e0SDimitry Andric       }
468b915e9e0SDimitry Andric     }
469b915e9e0SDimitry Andric   }
470b915e9e0SDimitry Andric #endif
471b915e9e0SDimitry Andric 
472b915e9e0SDimitry Andric   return Changed;
473b915e9e0SDimitry Andric }
474