blob: 50ee79b7079b7fd9095e4bda5a08fa66b9746b0c [file] [log] [blame]
Dan Gohman4fc4e422016-10-24 19:49:43 +00001//===-- WebAssemblyExplicitLocals.cpp - Make Locals Explicit --------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This file converts any remaining registers into WebAssembly locals.
12///
13/// After register stackification and register coloring, convert non-stackified
14/// registers into locals, inserting explicit get_local and set_local
15/// instructions.
16///
17//===----------------------------------------------------------------------===//
18
19#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
20#include "WebAssembly.h"
21#include "WebAssemblyMachineFunctionInfo.h"
22#include "WebAssemblySubtarget.h"
23#include "WebAssemblyUtilities.h"
24#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
25#include "llvm/CodeGen/MachineInstrBuilder.h"
26#include "llvm/CodeGen/MachineRegisterInfo.h"
27#include "llvm/CodeGen/Passes.h"
28#include "llvm/Support/Debug.h"
29#include "llvm/Support/raw_ostream.h"
30using namespace llvm;
31
32#define DEBUG_TYPE "wasm-explicit-locals"
33
34namespace {
35class WebAssemblyExplicitLocals final : public MachineFunctionPass {
36 StringRef getPassName() const override {
37 return "WebAssembly Explicit Locals";
38 }
39
40 void getAnalysisUsage(AnalysisUsage &AU) const override {
41 AU.setPreservesCFG();
42 AU.addPreserved<MachineBlockFrequencyInfo>();
43 MachineFunctionPass::getAnalysisUsage(AU);
44 }
45
46 bool runOnMachineFunction(MachineFunction &MF) override;
47
48public:
49 static char ID; // Pass identification, replacement for typeid
50 WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {}
51};
52} // end anonymous namespace
53
54char WebAssemblyExplicitLocals::ID = 0;
55FunctionPass *llvm::createWebAssemblyExplicitLocals() {
56 return new WebAssemblyExplicitLocals();
57}
58
59/// Return a local id number for the given register, assigning it a new one
60/// if it doesn't yet have one.
61static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,
62 unsigned &CurLocal, unsigned Reg) {
Dan Gohmand934cb82017-02-24 23:18:00 +000063 auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal));
64 if (P.second)
65 ++CurLocal;
66 return P.first->second;
67}
68
69/// Get the appropriate drop opcode for the given register class.
70static unsigned getDropOpcode(const TargetRegisterClass *RC) {
71 if (RC == &WebAssembly::I32RegClass)
72 return WebAssembly::DROP_I32;
73 if (RC == &WebAssembly::I64RegClass)
74 return WebAssembly::DROP_I64;
75 if (RC == &WebAssembly::F32RegClass)
76 return WebAssembly::DROP_F32;
77 if (RC == &WebAssembly::F64RegClass)
78 return WebAssembly::DROP_F64;
79 if (RC == &WebAssembly::V128RegClass)
80 return WebAssembly::DROP_V128;
81 llvm_unreachable("Unexpected register class");
Dan Gohman4fc4e422016-10-24 19:49:43 +000082}
83
84/// Get the appropriate get_local opcode for the given register class.
85static unsigned getGetLocalOpcode(const TargetRegisterClass *RC) {
86 if (RC == &WebAssembly::I32RegClass)
87 return WebAssembly::GET_LOCAL_I32;
88 if (RC == &WebAssembly::I64RegClass)
89 return WebAssembly::GET_LOCAL_I64;
90 if (RC == &WebAssembly::F32RegClass)
91 return WebAssembly::GET_LOCAL_F32;
92 if (RC == &WebAssembly::F64RegClass)
93 return WebAssembly::GET_LOCAL_F64;
94 if (RC == &WebAssembly::V128RegClass)
95 return WebAssembly::GET_LOCAL_V128;
96 llvm_unreachable("Unexpected register class");
97}
98
99/// Get the appropriate set_local opcode for the given register class.
100static unsigned getSetLocalOpcode(const TargetRegisterClass *RC) {
101 if (RC == &WebAssembly::I32RegClass)
102 return WebAssembly::SET_LOCAL_I32;
103 if (RC == &WebAssembly::I64RegClass)
104 return WebAssembly::SET_LOCAL_I64;
105 if (RC == &WebAssembly::F32RegClass)
106 return WebAssembly::SET_LOCAL_F32;
107 if (RC == &WebAssembly::F64RegClass)
108 return WebAssembly::SET_LOCAL_F64;
109 if (RC == &WebAssembly::V128RegClass)
110 return WebAssembly::SET_LOCAL_V128;
111 llvm_unreachable("Unexpected register class");
112}
113
114/// Get the appropriate tee_local opcode for the given register class.
115static unsigned getTeeLocalOpcode(const TargetRegisterClass *RC) {
116 if (RC == &WebAssembly::I32RegClass)
117 return WebAssembly::TEE_LOCAL_I32;
118 if (RC == &WebAssembly::I64RegClass)
119 return WebAssembly::TEE_LOCAL_I64;
120 if (RC == &WebAssembly::F32RegClass)
121 return WebAssembly::TEE_LOCAL_F32;
122 if (RC == &WebAssembly::F64RegClass)
123 return WebAssembly::TEE_LOCAL_F64;
124 if (RC == &WebAssembly::V128RegClass)
125 return WebAssembly::TEE_LOCAL_V128;
126 llvm_unreachable("Unexpected register class");
127}
128
129/// Get the type associated with the given register class.
Dan Gohman3acb1872016-10-24 23:27:49 +0000130static MVT typeForRegClass(const TargetRegisterClass *RC) {
Dan Gohman4fc4e422016-10-24 19:49:43 +0000131 if (RC == &WebAssembly::I32RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000132 return MVT::i32;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000133 if (RC == &WebAssembly::I64RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000134 return MVT::i64;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000135 if (RC == &WebAssembly::F32RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000136 return MVT::f32;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000137 if (RC == &WebAssembly::F64RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000138 return MVT::f64;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000139 llvm_unreachable("unrecognized register class");
140}
141
142/// Given a MachineOperand of a stackified vreg, return the instruction at the
143/// start of the expression tree.
144static MachineInstr *FindStartOfTree(MachineOperand &MO,
145 MachineRegisterInfo &MRI,
146 WebAssemblyFunctionInfo &MFI) {
147 unsigned Reg = MO.getReg();
148 assert(MFI.isVRegStackified(Reg));
149 MachineInstr *Def = MRI.getVRegDef(Reg);
150
151 // Find the first stackified use and proceed from there.
152 for (MachineOperand &DefMO : Def->explicit_uses()) {
153 if (!DefMO.isReg())
154 continue;
155 return FindStartOfTree(DefMO, MRI, MFI);
156 }
157
158 // If there were no stackified uses, we've reached the start.
159 return Def;
160}
161
162bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
163 DEBUG(dbgs() << "********** Make Locals Explicit **********\n"
164 "********** Function: "
165 << MF.getName() << '\n');
166
Dan Gohman66caac52016-12-03 23:00:12 +0000167 // Disable this pass if we aren't doing direct wasm object emission.
168 if (MF.getSubtarget<WebAssemblySubtarget>()
169 .getTargetTriple().isOSBinFormatELF())
170 return false;
171
Dan Gohman4fc4e422016-10-24 19:49:43 +0000172 bool Changed = false;
173 MachineRegisterInfo &MRI = MF.getRegInfo();
174 WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
175 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
176
177 // Map non-stackified virtual registers to their local ids.
178 DenseMap<unsigned, unsigned> Reg2Local;
179
180 // Handle ARGUMENTS first to ensure that they get the designated numbers.
181 for (MachineBasicBlock::iterator I = MF.begin()->begin(),
182 E = MF.begin()->end();
183 I != E;) {
184 MachineInstr &MI = *I++;
185 if (!WebAssembly::isArgument(MI))
186 break;
187 unsigned Reg = MI.getOperand(0).getReg();
188 assert(!MFI.isVRegStackified(Reg));
189 Reg2Local[Reg] = MI.getOperand(1).getImm();
190 MI.eraseFromParent();
191 Changed = true;
192 }
193
194 // Start assigning local numbers after the last parameter.
195 unsigned CurLocal = MFI.getParams().size();
196
Dan Gohmand934cb82017-02-24 23:18:00 +0000197 // Precompute the set of registers that are unused, so that we can insert
198 // drops to their defs.
199 BitVector UseEmpty(MRI.getNumVirtRegs());
200 for (unsigned i = 0, e = MRI.getNumVirtRegs(); i < e; ++i)
201 UseEmpty[i] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(i));
202
Dan Gohman4fc4e422016-10-24 19:49:43 +0000203 // Visit each instruction in the function.
204 for (MachineBasicBlock &MBB : MF) {
205 for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) {
206 MachineInstr &MI = *I++;
207 assert(!WebAssembly::isArgument(MI));
208
209 if (MI.isDebugValue() || MI.isLabel())
210 continue;
211
212 // Replace tee instructions with tee_local. The difference is that tee
213 // instructins have two defs, while tee_local instructions have one def
214 // and an index of a local to write to.
215 if (WebAssembly::isTee(MI)) {
216 assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
217 assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));
218 unsigned OldReg = MI.getOperand(2).getReg();
219 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
220
221 // Stackify the input if it isn't stackified yet.
222 if (!MFI.isVRegStackified(OldReg)) {
223 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
224 unsigned NewReg = MRI.createVirtualRegister(RC);
225 unsigned Opc = getGetLocalOpcode(RC);
226 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
227 .addImm(LocalId);
228 MI.getOperand(2).setReg(NewReg);
229 MFI.stackifyVReg(NewReg);
230 }
231
232 // Replace the TEE with a TEE_LOCAL.
233 unsigned LocalId =
234 getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
235 unsigned Opc = getTeeLocalOpcode(RC);
236 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
237 MI.getOperand(0).getReg())
238 .addImm(LocalId)
239 .addReg(MI.getOperand(2).getReg());
240
241 MI.eraseFromParent();
242 Changed = true;
243 continue;
244 }
245
246 // Insert set_locals for any defs that aren't stackified yet. Currently
247 // we handle at most one def.
248 assert(MI.getDesc().getNumDefs() <= 1);
249 if (MI.getDesc().getNumDefs() == 1) {
250 unsigned OldReg = MI.getOperand(0).getReg();
Dan Gohmand934cb82017-02-24 23:18:00 +0000251 if (!MFI.isVRegStackified(OldReg)) {
Dan Gohman4fc4e422016-10-24 19:49:43 +0000252 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
253 unsigned NewReg = MRI.createVirtualRegister(RC);
254 auto InsertPt = std::next(MachineBasicBlock::iterator(&MI));
Dan Gohmand934cb82017-02-24 23:18:00 +0000255 if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {
256 MI.eraseFromParent();
257 Changed = true;
258 continue;
259 }
260 if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) {
261 unsigned Opc = getDropOpcode(RC);
262 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
263 .addReg(NewReg);
264 } else {
265 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
266 unsigned Opc = getSetLocalOpcode(RC);
267 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
268 .addImm(LocalId)
269 .addReg(NewReg);
270 }
Dan Gohman4fc4e422016-10-24 19:49:43 +0000271 MI.getOperand(0).setReg(NewReg);
272 MFI.stackifyVReg(NewReg);
273 Changed = true;
274 }
275 }
276
277 // Insert get_locals for any uses that aren't stackified yet.
278 MachineInstr *InsertPt = &MI;
279 for (MachineOperand &MO : reverse(MI.explicit_uses())) {
280 if (!MO.isReg())
281 continue;
282
283 unsigned OldReg = MO.getReg();
284
285 // If we see a stackified register, prepare to insert subsequent
286 // get_locals before the start of its tree.
287 if (MFI.isVRegStackified(OldReg)) {
288 InsertPt = FindStartOfTree(MO, MRI, MFI);
289 continue;
290 }
291
292 // Insert a get_local.
293 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
294 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
295 unsigned NewReg = MRI.createVirtualRegister(RC);
296 unsigned Opc = getGetLocalOpcode(RC);
297 InsertPt =
298 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg)
299 .addImm(LocalId);
300 MO.setReg(NewReg);
301 MFI.stackifyVReg(NewReg);
302 Changed = true;
303 }
304
305 // Coalesce and eliminate COPY instructions.
306 if (WebAssembly::isCopy(MI)) {
307 MRI.replaceRegWith(MI.getOperand(1).getReg(),
308 MI.getOperand(0).getReg());
309 MI.eraseFromParent();
310 Changed = true;
311 }
312 }
313 }
314
Dan Gohman3acb1872016-10-24 23:27:49 +0000315 // Define the locals.
Dan Gohmand934cb82017-02-24 23:18:00 +0000316 // TODO: Sort the locals for better compression.
317 MFI.setNumLocals(CurLocal - MFI.getParams().size());
Dan Gohman4fc4e422016-10-24 19:49:43 +0000318 for (size_t i = 0, e = MRI.getNumVirtRegs(); i < e; ++i) {
319 unsigned Reg = TargetRegisterInfo::index2VirtReg(i);
320 auto I = Reg2Local.find(Reg);
321 if (I == Reg2Local.end() || I->second < MFI.getParams().size())
322 continue;
323
Dan Gohmand934cb82017-02-24 23:18:00 +0000324 MFI.setLocal(I->second - MFI.getParams().size(),
325 typeForRegClass(MRI.getRegClass(Reg)));
Dan Gohman3acb1872016-10-24 23:27:49 +0000326 Changed = true;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000327 }
328
329#ifndef NDEBUG
330 // Assert that all registers have been stackified at this point.
331 for (const MachineBasicBlock &MBB : MF) {
332 for (const MachineInstr &MI : MBB) {
333 if (MI.isDebugValue() || MI.isLabel())
334 continue;
335 for (const MachineOperand &MO : MI.explicit_operands()) {
336 assert(
337 (!MO.isReg() || MRI.use_empty(MO.getReg()) ||
338 MFI.isVRegStackified(MO.getReg())) &&
339 "WebAssemblyExplicitLocals failed to stackify a register operand");
340 }
341 }
342 }
343#endif
344
345 return Changed;
346}