blob: 384c0d60db535b4db22d3aaba96c993e77ead238 [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;
Thomas Lively409f5842018-10-09 23:33:16 +0000160 if (RC == &WebAssembly::V128RegClass)
161 return MVT::v16i8;
Heejin Ahn0de58722018-03-08 04:05:37 +0000162 if (RC == &WebAssembly::EXCEPT_REFRegClass)
163 return MVT::ExceptRef;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000164 llvm_unreachable("unrecognized register class");
165}
166
167/// Given a MachineOperand of a stackified vreg, return the instruction at the
168/// start of the expression tree.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000169static MachineInstr *findStartOfTree(MachineOperand &MO,
Dan Gohman4fc4e422016-10-24 19:49:43 +0000170 MachineRegisterInfo &MRI,
171 WebAssemblyFunctionInfo &MFI) {
172 unsigned Reg = MO.getReg();
173 assert(MFI.isVRegStackified(Reg));
174 MachineInstr *Def = MRI.getVRegDef(Reg);
175
176 // Find the first stackified use and proceed from there.
177 for (MachineOperand &DefMO : Def->explicit_uses()) {
178 if (!DefMO.isReg())
179 continue;
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000180 return findStartOfTree(DefMO, MRI, MFI);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000181 }
182
183 // If there were no stackified uses, we've reached the start.
184 return Def;
185}
186
187bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) {
Nicola Zaghend34e60c2018-05-14 12:53:11 +0000188 LLVM_DEBUG(dbgs() << "********** Make Locals Explicit **********\n"
189 "********** Function: "
190 << MF.getName() << '\n');
Dan Gohman4fc4e422016-10-24 19:49:43 +0000191
Dan Gohman7d7409e2017-02-28 23:37:04 +0000192 // Disable this pass if directed to do so.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000193 if (WasmDisableExplicitLocals)
Dan Gohman7d7409e2017-02-28 23:37:04 +0000194 return false;
195
Dan Gohman4fc4e422016-10-24 19:49:43 +0000196 bool Changed = false;
197 MachineRegisterInfo &MRI = MF.getRegInfo();
198 WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
199 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
200
201 // Map non-stackified virtual registers to their local ids.
202 DenseMap<unsigned, unsigned> Reg2Local;
203
204 // Handle ARGUMENTS first to ensure that they get the designated numbers.
205 for (MachineBasicBlock::iterator I = MF.begin()->begin(),
206 E = MF.begin()->end();
207 I != E;) {
208 MachineInstr &MI = *I++;
209 if (!WebAssembly::isArgument(MI))
210 break;
211 unsigned Reg = MI.getOperand(0).getReg();
212 assert(!MFI.isVRegStackified(Reg));
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000213 Reg2Local[Reg] = static_cast<unsigned>(MI.getOperand(1).getImm());
Dan Gohman4fc4e422016-10-24 19:49:43 +0000214 MI.eraseFromParent();
215 Changed = true;
216 }
217
218 // Start assigning local numbers after the last parameter.
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000219 unsigned CurLocal = static_cast<unsigned>(MFI.getParams().size());
Dan Gohman4fc4e422016-10-24 19:49:43 +0000220
Dan Gohmand934cb82017-02-24 23:18:00 +0000221 // Precompute the set of registers that are unused, so that we can insert
222 // drops to their defs.
223 BitVector UseEmpty(MRI.getNumVirtRegs());
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000224 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I)
225 UseEmpty[I] = MRI.use_empty(TargetRegisterInfo::index2VirtReg(I));
Dan Gohmand934cb82017-02-24 23:18:00 +0000226
Dan Gohman4fc4e422016-10-24 19:49:43 +0000227 // Visit each instruction in the function.
228 for (MachineBasicBlock &MBB : MF) {
229 for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) {
230 MachineInstr &MI = *I++;
231 assert(!WebAssembly::isArgument(MI));
232
Shiva Chen801bf7e2018-05-09 02:42:00 +0000233 if (MI.isDebugInstr() || MI.isLabel())
Dan Gohman4fc4e422016-10-24 19:49:43 +0000234 continue;
235
236 // Replace tee instructions with tee_local. The difference is that tee
237 // instructins have two defs, while tee_local instructions have one def
238 // and an index of a local to write to.
239 if (WebAssembly::isTee(MI)) {
240 assert(MFI.isVRegStackified(MI.getOperand(0).getReg()));
241 assert(!MFI.isVRegStackified(MI.getOperand(1).getReg()));
242 unsigned OldReg = MI.getOperand(2).getReg();
243 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
244
245 // Stackify the input if it isn't stackified yet.
246 if (!MFI.isVRegStackified(OldReg)) {
247 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
248 unsigned NewReg = MRI.createVirtualRegister(RC);
249 unsigned Opc = getGetLocalOpcode(RC);
250 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc), NewReg)
251 .addImm(LocalId);
252 MI.getOperand(2).setReg(NewReg);
253 MFI.stackifyVReg(NewReg);
254 }
255
256 // Replace the TEE with a TEE_LOCAL.
257 unsigned LocalId =
258 getLocalId(Reg2Local, CurLocal, MI.getOperand(1).getReg());
259 unsigned Opc = getTeeLocalOpcode(RC);
260 BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(Opc),
261 MI.getOperand(0).getReg())
262 .addImm(LocalId)
263 .addReg(MI.getOperand(2).getReg());
264
265 MI.eraseFromParent();
266 Changed = true;
267 continue;
268 }
269
270 // Insert set_locals for any defs that aren't stackified yet. Currently
271 // we handle at most one def.
272 assert(MI.getDesc().getNumDefs() <= 1);
273 if (MI.getDesc().getNumDefs() == 1) {
274 unsigned OldReg = MI.getOperand(0).getReg();
Dan Gohmand934cb82017-02-24 23:18:00 +0000275 if (!MFI.isVRegStackified(OldReg)) {
Dan Gohman4fc4e422016-10-24 19:49:43 +0000276 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
277 unsigned NewReg = MRI.createVirtualRegister(RC);
278 auto InsertPt = std::next(MachineBasicBlock::iterator(&MI));
Dan Gohmand934cb82017-02-24 23:18:00 +0000279 if (MI.getOpcode() == WebAssembly::IMPLICIT_DEF) {
280 MI.eraseFromParent();
281 Changed = true;
282 continue;
283 }
284 if (UseEmpty[TargetRegisterInfo::virtReg2Index(OldReg)]) {
285 unsigned Opc = getDropOpcode(RC);
Heejin Ahn891a7472018-06-19 20:30:42 +0000286 MachineInstr *Drop =
287 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
288 .addReg(NewReg);
289 // After the drop instruction, this reg operand will not be used
290 Drop->getOperand(0).setIsKill();
Dan Gohmand934cb82017-02-24 23:18:00 +0000291 } else {
292 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
293 unsigned Opc = getSetLocalOpcode(RC);
294 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc))
295 .addImm(LocalId)
296 .addReg(NewReg);
297 }
Dan Gohman4fc4e422016-10-24 19:49:43 +0000298 MI.getOperand(0).setReg(NewReg);
Heejin Ahn891a7472018-06-19 20:30:42 +0000299 // This register operand is now being used by the inserted drop
300 // instruction, so make it undead.
301 MI.getOperand(0).setIsDead(false);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000302 MFI.stackifyVReg(NewReg);
303 Changed = true;
304 }
305 }
306
307 // Insert get_locals for any uses that aren't stackified yet.
308 MachineInstr *InsertPt = &MI;
309 for (MachineOperand &MO : reverse(MI.explicit_uses())) {
310 if (!MO.isReg())
311 continue;
312
313 unsigned OldReg = MO.getReg();
314
Dan Gohmanb465aa02017-11-08 19:18:08 +0000315 // Inline asm may have a def in the middle of the operands. Our contract
316 // with inline asm register operands is to provide local indices as
317 // immediates.
318 if (MO.isDef()) {
319 assert(MI.getOpcode() == TargetOpcode::INLINEASM);
320 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
Heejin Ahn300f42f2018-09-12 21:34:39 +0000321 // If this register operand is tied to another operand, we can't
322 // change it to an immediate. Untie it first.
323 MI.untieRegOperand(MI.getOperandNo(&MO));
Dan Gohman045a2172018-09-04 17:46:12 +0000324 MO.ChangeToImmediate(LocalId);
Dan Gohmanb465aa02017-11-08 19:18:08 +0000325 continue;
326 }
327
Dan Gohman4fc4e422016-10-24 19:49:43 +0000328 // If we see a stackified register, prepare to insert subsequent
329 // get_locals before the start of its tree.
330 if (MFI.isVRegStackified(OldReg)) {
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000331 InsertPt = findStartOfTree(MO, MRI, MFI);
Dan Gohman4fc4e422016-10-24 19:49:43 +0000332 continue;
333 }
334
Dan Gohmanb465aa02017-11-08 19:18:08 +0000335 // Our contract with inline asm register operands is to provide local
336 // indices as immediates.
337 if (MI.getOpcode() == TargetOpcode::INLINEASM) {
338 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
Heejin Ahn300f42f2018-09-12 21:34:39 +0000339 // Untie it first if this reg operand is tied to another operand.
340 MI.untieRegOperand(MI.getOperandNo(&MO));
Dan Gohman045a2172018-09-04 17:46:12 +0000341 MO.ChangeToImmediate(LocalId);
Dan Gohmanb465aa02017-11-08 19:18:08 +0000342 continue;
343 }
344
Dan Gohman4fc4e422016-10-24 19:49:43 +0000345 // Insert a get_local.
346 unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg);
347 const TargetRegisterClass *RC = MRI.getRegClass(OldReg);
348 unsigned NewReg = MRI.createVirtualRegister(RC);
349 unsigned Opc = getGetLocalOpcode(RC);
350 InsertPt =
351 BuildMI(MBB, InsertPt, MI.getDebugLoc(), TII->get(Opc), NewReg)
352 .addImm(LocalId);
353 MO.setReg(NewReg);
354 MFI.stackifyVReg(NewReg);
355 Changed = true;
356 }
357
358 // Coalesce and eliminate COPY instructions.
359 if (WebAssembly::isCopy(MI)) {
360 MRI.replaceRegWith(MI.getOperand(1).getReg(),
361 MI.getOperand(0).getReg());
362 MI.eraseFromParent();
363 Changed = true;
364 }
365 }
366 }
367
Dan Gohman3acb1872016-10-24 23:27:49 +0000368 // Define the locals.
Dan Gohmand934cb82017-02-24 23:18:00 +0000369 // TODO: Sort the locals for better compression.
370 MFI.setNumLocals(CurLocal - MFI.getParams().size());
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000371 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I < E; ++I) {
372 unsigned Reg = TargetRegisterInfo::index2VirtReg(I);
373 auto RL = Reg2Local.find(Reg);
374 if (RL == Reg2Local.end() || RL->second < MFI.getParams().size())
Dan Gohman4fc4e422016-10-24 19:49:43 +0000375 continue;
376
Wouter van Oortmerssen8a9cb242018-08-27 15:45:51 +0000377 MFI.setLocal(RL->second - MFI.getParams().size(),
Dan Gohmand934cb82017-02-24 23:18:00 +0000378 typeForRegClass(MRI.getRegClass(Reg)));
Dan Gohman3acb1872016-10-24 23:27:49 +0000379 Changed = true;
Dan Gohman4fc4e422016-10-24 19:49:43 +0000380 }
381
Wouter van Oortmerssena7be3752018-08-13 23:12:49 +0000382#ifndef NDEBUG
383 // Assert that all registers have been stackified at this point.
384 for (const MachineBasicBlock &MBB : MF) {
385 for (const MachineInstr &MI : MBB) {
386 if (MI.isDebugInstr() || MI.isLabel())
387 continue;
388 for (const MachineOperand &MO : MI.explicit_operands()) {
389 assert(
390 (!MO.isReg() || MRI.use_empty(MO.getReg()) ||
391 MFI.isVRegStackified(MO.getReg())) &&
392 "WebAssemblyExplicitLocals failed to stackify a register operand");
393 }
394 }
Wouter van Oortmerssenab26bd02018-08-10 21:32:47 +0000395 }
Wouter van Oortmerssena7be3752018-08-13 23:12:49 +0000396#endif
397
398 return Changed;
Wouter van Oortmerssenab26bd02018-08-10 21:32:47 +0000399}