blob: cb7bcf3f69f2ee94c300c92d8e96c67d208a8244 [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
Adrian Prantl5f8f34e42018-05-01 15:54:18 +000011/// This file converts any remaining registers into WebAssembly locals.
Dan Gohman4fc4e422016-10-24 19:49:43 +000012///
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
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +000034// A command-line option to disable this pass, and keep implicit locals
35// for the purpose of testing with lit/llc ONLY.
36// This produces output which is not valid WebAssembly, and is not supported
37// by assemblers/disassemblers and other MC based tools.
38static cl::opt<bool> WasmDisableExplicitLocals(
39 "wasm-disable-explicit-locals", cl::Hidden,
40 cl::desc("WebAssembly: output implicit locals in"
41 " instruction output for test purposes only."),
Dan Gohman7d7409e2017-02-28 23:37:04 +000042 cl::init(false));
43
Dan Gohman4fc4e422016-10-24 19:49:43 +000044namespace {
45class WebAssemblyExplicitLocals final : public MachineFunctionPass {
46 StringRef getPassName() const override {
47 return "WebAssembly Explicit Locals";
48 }
49
50 void getAnalysisUsage(AnalysisUsage &AU) const override {
51 AU.setPreservesCFG();
52 AU.addPreserved<MachineBlockFrequencyInfo>();
53 MachineFunctionPass::getAnalysisUsage(AU);
54 }
55
56 bool runOnMachineFunction(MachineFunction &MF) override;
57
58public:
59 static char ID; // Pass identification, replacement for typeid
60 WebAssemblyExplicitLocals() : MachineFunctionPass(ID) {}
61};
62} // end anonymous namespace
63
64char WebAssemblyExplicitLocals::ID = 0;
Jacob Gravelle40926452018-03-30 20:36:58 +000065INITIALIZE_PASS(WebAssemblyExplicitLocals, DEBUG_TYPE,
66 "Convert registers to WebAssembly locals", false, false)
67
Dan Gohman4fc4e422016-10-24 19:49:43 +000068FunctionPass *llvm::createWebAssemblyExplicitLocals() {
69 return new WebAssemblyExplicitLocals();
70}
71
72/// Return a local id number for the given register, assigning it a new one
73/// if it doesn't yet have one.
74static unsigned getLocalId(DenseMap<unsigned, unsigned> &Reg2Local,
75 unsigned &CurLocal, unsigned Reg) {
Dan Gohmand934cb82017-02-24 23:18:00 +000076 auto P = Reg2Local.insert(std::make_pair(Reg, CurLocal));
77 if (P.second)
78 ++CurLocal;
79 return P.first->second;
80}
81
82/// Get the appropriate drop opcode for the given register class.
83static unsigned getDropOpcode(const TargetRegisterClass *RC) {
84 if (RC == &WebAssembly::I32RegClass)
85 return WebAssembly::DROP_I32;
86 if (RC == &WebAssembly::I64RegClass)
87 return WebAssembly::DROP_I64;
88 if (RC == &WebAssembly::F32RegClass)
89 return WebAssembly::DROP_F32;
90 if (RC == &WebAssembly::F64RegClass)
91 return WebAssembly::DROP_F64;
92 if (RC == &WebAssembly::V128RegClass)
93 return WebAssembly::DROP_V128;
Heejin Ahn0de58722018-03-08 04:05:37 +000094 if (RC == &WebAssembly::EXCEPT_REFRegClass)
95 return WebAssembly::DROP_EXCEPT_REF;
Dan Gohmand934cb82017-02-24 23:18:00 +000096 llvm_unreachable("Unexpected register class");
Dan Gohman4fc4e422016-10-24 19:49:43 +000097}
98
99/// Get the appropriate get_local opcode for the given register class.
100static unsigned getGetLocalOpcode(const TargetRegisterClass *RC) {
101 if (RC == &WebAssembly::I32RegClass)
102 return WebAssembly::GET_LOCAL_I32;
103 if (RC == &WebAssembly::I64RegClass)
104 return WebAssembly::GET_LOCAL_I64;
105 if (RC == &WebAssembly::F32RegClass)
106 return WebAssembly::GET_LOCAL_F32;
107 if (RC == &WebAssembly::F64RegClass)
108 return WebAssembly::GET_LOCAL_F64;
109 if (RC == &WebAssembly::V128RegClass)
110 return WebAssembly::GET_LOCAL_V128;
Heejin Ahn0de58722018-03-08 04:05:37 +0000111 if (RC == &WebAssembly::EXCEPT_REFRegClass)
112 return WebAssembly::GET_LOCAL_EXCEPT_REF;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000113 llvm_unreachable("Unexpected register class");
114}
115
116/// Get the appropriate set_local opcode for the given register class.
117static unsigned getSetLocalOpcode(const TargetRegisterClass *RC) {
118 if (RC == &WebAssembly::I32RegClass)
119 return WebAssembly::SET_LOCAL_I32;
120 if (RC == &WebAssembly::I64RegClass)
121 return WebAssembly::SET_LOCAL_I64;
122 if (RC == &WebAssembly::F32RegClass)
123 return WebAssembly::SET_LOCAL_F32;
124 if (RC == &WebAssembly::F64RegClass)
125 return WebAssembly::SET_LOCAL_F64;
126 if (RC == &WebAssembly::V128RegClass)
127 return WebAssembly::SET_LOCAL_V128;
Heejin Ahn0de58722018-03-08 04:05:37 +0000128 if (RC == &WebAssembly::EXCEPT_REFRegClass)
129 return WebAssembly::SET_LOCAL_EXCEPT_REF;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000130 llvm_unreachable("Unexpected register class");
131}
132
133/// Get the appropriate tee_local opcode for the given register class.
134static unsigned getTeeLocalOpcode(const TargetRegisterClass *RC) {
135 if (RC == &WebAssembly::I32RegClass)
136 return WebAssembly::TEE_LOCAL_I32;
137 if (RC == &WebAssembly::I64RegClass)
138 return WebAssembly::TEE_LOCAL_I64;
139 if (RC == &WebAssembly::F32RegClass)
140 return WebAssembly::TEE_LOCAL_F32;
141 if (RC == &WebAssembly::F64RegClass)
142 return WebAssembly::TEE_LOCAL_F64;
143 if (RC == &WebAssembly::V128RegClass)
144 return WebAssembly::TEE_LOCAL_V128;
Heejin Ahn0de58722018-03-08 04:05:37 +0000145 if (RC == &WebAssembly::EXCEPT_REFRegClass)
146 return WebAssembly::TEE_LOCAL_EXCEPT_REF;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000147 llvm_unreachable("Unexpected register class");
148}
149
150/// Get the type associated with the given register class.
Dan Gohman3acb1872016-10-24 23:27:49 +0000151static MVT typeForRegClass(const TargetRegisterClass *RC) {
Dan Gohman4fc4e422016-10-24 19:49:43 +0000152 if (RC == &WebAssembly::I32RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000153 return MVT::i32;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000154 if (RC == &WebAssembly::I64RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000155 return MVT::i64;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000156 if (RC == &WebAssembly::F32RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000157 return MVT::f32;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000158 if (RC == &WebAssembly::F64RegClass)
Dan Gohman3acb1872016-10-24 23:27:49 +0000159 return MVT::f64;
Heejin Ahn0de58722018-03-08 04:05:37 +0000160 if (RC == &WebAssembly::EXCEPT_REFRegClass)
161 return MVT::ExceptRef;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000162 llvm_unreachable("unrecognized register class");
163}
164
165/// Given a MachineOperand of a stackified vreg, return the instruction at the
166/// start of the expression tree.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000167static MachineInstr *findStartOfTree(MachineOperand &MO,
Dan Gohman4fc4e422016-10-24 19:49:43 +0000168 MachineRegisterInfo &MRI,
169 WebAssemblyFunctionInfo &MFI) {
170 unsigned Reg = MO.getReg();
171 assert(MFI.isVRegStackified(Reg));
172 MachineInstr *Def = MRI.getVRegDef(Reg);
173
174 // Find the first stackified use and proceed from there.
175 for (MachineOperand &DefMO : Def->explicit_uses()) {
176 if (!DefMO.isReg())
177 continue;
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000178 return findStartOfTree(DefMO, MRI, MFI);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000179 }
180
181 // If there were no stackified uses, we've reached the start.
182 return Def;
183}
184
185bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000186 LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n"
187 "********** Function: "
188 << MF.getName() << '\n');
Dan Gohman4fc4e422016-10-24 19:49:43 +0000189
Dan Gohman7d7409e2017-02-28 23:37:04 +0000190 // Disable this pass if directed to do so.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000191 if (WasmDisableExplicitLocals)
Dan Gohman7d7409e2017-02-28 23:37:04 +0000192 return false;
193
Dan Gohman4fc4e422016-10-24 19:49:43 +0000194 bool Changed = false;
195 MachineRegisterInfo &MRI = MF.getRegInfo();
196 WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
197 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
198
199 // Map non-stackified virtual registers to their local ids.
200 DenseMap<unsigned, unsigned> Reg2Local;
201
202 // Handle ARGUMENTS first to ensure that they get the designated numbers.
203 for (MachineBasicBlock::iterator I = MF.begin()->begin(),
204 E = MF.begin()->end();
205 I != E;) {
206 MachineInstr &MI = *I++;
207 if (!WebAssembly::isArgument(MI))
208 break;
209 unsigned Reg = MI.getOperand(0).getReg();
210 assert(!MFI.isVRegStackified(Reg));
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000211 Reg2Local[Reg] = static_cast<unsigned>(MI.getOperand(1).getImm());
Dan Gohman4fc4e422016-10-24 19:49:43 +0000212 MI.eraseFromParent();
213 Changed = true;
214 }
215
216 // Start assigning local numbers after the last parameter.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000217 unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());
Dan Gohman4fc4e422016-10-24 19:49:43 +0000218
Dan Gohmand934cb82017-02-24 23:18:00 +0000219 // Precompute the set of registers that are unused, so that we can insert
220 // drops to their defs.
221 BitVector UseEmpty(MRI.getNumVirtRegs());
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000222 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I)
223 UseEmpty[I] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(I));
Dan Gohmand934cb82017-02-24 23:18:00 +0000224
Dan Gohman4fc4e422016-10-24 19:49:43 +0000225 // Visit each instruction in the function.
226 for (MachineBasicBlock &MBB : MF) {
227 for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) {
228 MachineInstr &MI = *I++;
229 assert(!WebAssembly::isArgument(MI));
230
Shiva Chen801bf7e2018-05-09 02:42:00 +0000231 if (MI.isDebugInstr() || MI.isLabel())
Dan Gohman4fc4e422016-10-24 19:49:43 +0000232 continue;
233
234 // Replace tee instructions with tee_local. The difference is that tee
235 // instructins have two defs, while tee_local instructions have one def
236 // and an index of a local to write to.
237 if (WebAssembly::isTee(MI)) {
238 assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
239 assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));
240 unsigned OldReg = MI.getOperand(2).getReg();
241 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
242
243 // Stackify the input if it isn't stackified yet.
244 if (!MFI.isVRegStackified(OldReg)) {
245 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
246 unsigned NewReg = MRI.createVirtualRegister(RC);
247 unsigned Opc = getGetLocalOpcode(RC);
248 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
249 .addImm(LocalId);
250 MI.getOperand(2).setReg(NewReg);
251 MFI.stackifyVReg(NewReg);
252 }
253
254 // Replace the TEE with a TEE_LOCAL.
255 unsigned LocalId =
256 getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
257 unsigned Opc = getTeeLocalOpcode(RC);
258 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
259 MI.getOperand(0).getReg())
260 .addImm(LocalId)
261 .addReg(MI.getOperand(2).getReg());
262
263 MI.eraseFromParent();
264 Changed = true;
265 continue;
266 }
267
268 // Insert set_locals for any defs that aren't stackified yet. Currently
269 // we handle at most one def.
270 assert(MI.getDesc().getNumDefs() <= 1);
271 if (MI.getDesc().getNumDefs() == 1) {
272 unsigned OldReg = MI.getOperand(0).getReg();
Dan Gohmand934cb82017-02-24 23:18:00 +0000273 if (!MFI.isVRegStackified(OldReg)) {
Dan Gohman4fc4e422016-10-24 19:49:43 +0000274 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
275 unsigned NewReg = MRI.createVirtualRegister(RC);
276 auto InsertPt = std::next(MachineBasicBlock::iterator(&MI));
Dan Gohmand934cb82017-02-24 23:18:00 +0000277 if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {
278 MI.eraseFromParent();
279 Changed = true;
280 continue;
281 }
282 if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) {
283 unsigned Opc = getDropOpcode(RC);
Heejin Ahn891a7472018-06-19 20:30:42 +0000284 MachineInstr *Drop =
285 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
286 .addReg(NewReg);
287 // After the drop instruction, this reg operand will not be used
288 Drop->getOperand(0).setIsKill();
Dan Gohmand934cb82017-02-24 23:18:00 +0000289 } else {
290 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
291 unsigned Opc = getSetLocalOpcode(RC);
292 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
293 .addImm(LocalId)
294 .addReg(NewReg);
295 }
Dan Gohman4fc4e422016-10-24 19:49:43 +0000296 MI.getOperand(0).setReg(NewReg);
Heejin Ahn891a7472018-06-19 20:30:42 +0000297 // This register operand is now being used by the inserted drop
298 // instruction, so make it undead.
299 MI.getOperand(0).setIsDead(false);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000300 MFI.stackifyVReg(NewReg);
301 Changed = true;
302 }
303 }
304
305 // Insert get_locals for any uses that aren't stackified yet.
306 MachineInstr *InsertPt = &MI;
307 for (MachineOperand &MO : reverse(MI.explicit_uses())) {
308 if (!MO.isReg())
309 continue;
310
311 unsigned OldReg = MO.getReg();
312
Dan Gohmanb465aa02017-11-08 19:18:08 +0000313 // Inline asm may have a def in the middle of the operands. Our contract
314 // with inline asm register operands is to provide local indices as
315 // immediates.
316 if (MO.isDef()) {
317 assert(MI.getOpcode() == TargetOpcode::INLINEASM);
318 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
Heejin Ahn300f42f2018-09-12 21:34:39 +0000319 // If this register operand is tied to another operand, we can't
320 // change it to an immediate. Untie it first.
321 MI.untieRegOperand(MI.getOperandNo(&MO));
Dan Gohman045a2172018-09-04 17:46:12 +0000322 MO.ChangeToImmediate(LocalId);
Dan Gohmanb465aa02017-11-08 19:18:08 +0000323 continue;
324 }
325
Dan Gohman4fc4e422016-10-24 19:49:43 +0000326 // If we see a stackified register, prepare to insert subsequent
327 // get_locals before the start of its tree.
328 if (MFI.isVRegStackified(OldReg)) {
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000329 InsertPt = findStartOfTree(MO, MRI, MFI);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000330 continue;
331 }
332
Dan Gohmanb465aa02017-11-08 19:18:08 +0000333 // Our contract with inline asm register operands is to provide local
334 // indices as immediates.
335 if (MI.getOpcode() == TargetOpcode::INLINEASM) {
336 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
Heejin Ahn300f42f2018-09-12 21:34:39 +0000337 // Untie it first if this reg operand is tied to another operand.
338 MI.untieRegOperand(MI.getOperandNo(&MO));
Dan Gohman045a2172018-09-04 17:46:12 +0000339 MO.ChangeToImmediate(LocalId);
Dan Gohmanb465aa02017-11-08 19:18:08 +0000340 continue;
341 }
342
Dan Gohman4fc4e422016-10-24 19:49:43 +0000343 // Insert a get_local.
344 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
345 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
346 unsigned NewReg = MRI.createVirtualRegister(RC);
347 unsigned Opc = getGetLocalOpcode(RC);
348 InsertPt =
349 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg)
350 .addImm(LocalId);
351 MO.setReg(NewReg);
352 MFI.stackifyVReg(NewReg);
353 Changed = true;
354 }
355
356 // Coalesce and eliminate COPY instructions.
357 if (WebAssembly::isCopy(MI)) {
358 MRI.replaceRegWith(MI.getOperand(1).getReg(),
359 MI.getOperand(0).getReg());
360 MI.eraseFromParent();
361 Changed = true;
362 }
363 }
364 }
365
Dan Gohman3acb1872016-10-24 23:27:49 +0000366 // Define the locals.
Dan Gohmand934cb82017-02-24 23:18:00 +0000367 // TODO: Sort the locals for better compression.
368 MFI.setNumLocals(CurLocal - MFI.getParams().size());
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000369 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
370 unsigned Reg = TargetRegisterInfo::index2VirtReg(I);
371 auto RL = Reg2Local.find(Reg);
372 if (RL == Reg2Local.end() || RL->second < MFI.getParams().size())
Dan Gohman4fc4e422016-10-24 19:49:43 +0000373 continue;
374
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000375 MFI.setLocal(RL->second - MFI.getParams().size(),
Dan Gohmand934cb82017-02-24 23:18:00 +0000376 typeForRegClass(MRI.getRegClass(Reg)));
Dan Gohman3acb1872016-10-24 23:27:49 +0000377 Changed = true;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000378 }
379
Wouter van Oortmerssena7be3752018-08-13 23:12:49 +0000380#ifndef NDEBUG
381 // Assert that all registers have been stackified at this point.
382 for (const MachineBasicBlock &MBB : MF) {
383 for (const MachineInstr &MI : MBB) {
384 if (MI.isDebugInstr() || MI.isLabel())
385 continue;
386 for (const MachineOperand &MO : MI.explicit_operands()) {
387 assert(
388 (!MO.isReg() || MRI.use_empty(MO.getReg()) ||
389 MFI.isVRegStackified(MO.getReg())) &&
390 "WebAssemblyExplicitLocals failed to stackify a register operand");
391 }
392 }
Wouter van Oortmerssenab26bd02018-08-10 21:32:47 +0000393 }
Wouter van Oortmerssena7be3752018-08-13 23:12:49 +0000394#endif
395
396 return Changed;
Wouter van Oortmerssenab26bd02018-08-10 21:32:47 +0000397}