js/src/jit/RegisterAllocator.h

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:3fd4e6c3fb8b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef jit_RegisterAllocator_h
8 #define jit_RegisterAllocator_h
9
10 #include "mozilla/Attributes.h"
11 #include "mozilla/MathAlgorithms.h"
12
13 #include "jit/LIR.h"
14 #include "jit/MIRGenerator.h"
15 #include "jit/MIRGraph.h"
16
17 // Generic structures and functions for use by register allocators.
18
19 namespace js {
20 namespace jit {
21
22 class LIRGenerator;
23
24 // Structure for running a liveness analysis on a finished register allocation.
25 // This analysis can be used for two purposes:
26 //
27 // - Check the integrity of the allocation, i.e. that the reads and writes of
28 // physical values preserve the semantics of the original virtual registers.
29 //
30 // - Populate safepoints with live registers, GC thing and value data, to
31 // streamline the process of prototyping new allocators.
32 struct AllocationIntegrityState
33 {
34 explicit AllocationIntegrityState(const LIRGraph &graph)
35 : graph(graph)
36 {}
37
38 // Record all virtual registers in the graph. This must be called before
39 // register allocation, to pick up the original LUses.
40 bool record();
41
42 // Perform the liveness analysis on the graph, and assert on an invalid
43 // allocation. This must be called after register allocation, to pick up
44 // all assigned physical values. If populateSafepoints is specified,
45 // safepoints will be filled in with liveness information.
46 bool check(bool populateSafepoints);
47
48 private:
49
50 const LIRGraph &graph;
51
52 // For all instructions and phis in the graph, keep track of the virtual
53 // registers for all inputs and outputs of the nodes. These are overwritten
54 // in place during register allocation. This information is kept on the
55 // side rather than in the instructions and phis themselves to avoid
56 // debug-builds-only bloat in the size of the involved structures.
57
58 struct InstructionInfo {
59 Vector<LAllocation, 2, SystemAllocPolicy> inputs;
60 Vector<LDefinition, 0, SystemAllocPolicy> temps;
61 Vector<LDefinition, 1, SystemAllocPolicy> outputs;
62
63 InstructionInfo()
64 { }
65
66 InstructionInfo(const InstructionInfo &o)
67 {
68 inputs.appendAll(o.inputs);
69 temps.appendAll(o.temps);
70 outputs.appendAll(o.outputs);
71 }
72 };
73 Vector<InstructionInfo, 0, SystemAllocPolicy> instructions;
74
75 struct BlockInfo {
76 Vector<InstructionInfo, 5, SystemAllocPolicy> phis;
77 BlockInfo() {}
78 BlockInfo(const BlockInfo &o) {
79 phis.appendAll(o.phis);
80 }
81 };
82 Vector<BlockInfo, 0, SystemAllocPolicy> blocks;
83
84 Vector<LDefinition*, 20, SystemAllocPolicy> virtualRegisters;
85
86 // Describes a correspondence that should hold at the end of a block.
87 // The value which was written to vreg in the original LIR should be
88 // physically stored in alloc after the register allocation.
89 struct IntegrityItem
90 {
91 LBlock *block;
92 uint32_t vreg;
93 LAllocation alloc;
94
95 // Order of insertion into seen, for sorting.
96 uint32_t index;
97
98 typedef IntegrityItem Lookup;
99 static HashNumber hash(const IntegrityItem &item) {
100 HashNumber hash = item.alloc.hash();
101 hash = mozilla::RotateLeft(hash, 4) ^ item.vreg;
102 hash = mozilla::RotateLeft(hash, 4) ^ HashNumber(item.block->mir()->id());
103 return hash;
104 }
105 static bool match(const IntegrityItem &one, const IntegrityItem &two) {
106 return one.block == two.block
107 && one.vreg == two.vreg
108 && one.alloc == two.alloc;
109 }
110 };
111
112 // Items still to be processed.
113 Vector<IntegrityItem, 10, SystemAllocPolicy> worklist;
114
115 // Set of all items that have already been processed.
116 typedef HashSet<IntegrityItem, IntegrityItem, SystemAllocPolicy> IntegrityItemSet;
117 IntegrityItemSet seen;
118
119 bool checkIntegrity(LBlock *block, LInstruction *ins, uint32_t vreg, LAllocation alloc,
120 bool populateSafepoints);
121 bool checkSafepointAllocation(LInstruction *ins, uint32_t vreg, LAllocation alloc,
122 bool populateSafepoints);
123 bool addPredecessor(LBlock *block, uint32_t vreg, LAllocation alloc);
124
125 void dump();
126 };
127
128 // Represents with better-than-instruction precision a position in the
129 // instruction stream.
130 //
131 // An issue comes up when performing register allocation as to how to represent
132 // information such as "this register is only needed for the input of
133 // this instruction, it can be clobbered in the output". Just having ranges
134 // of instruction IDs is insufficiently expressive to denote all possibilities.
135 // This class solves this issue by associating an extra bit with the instruction
136 // ID which indicates whether the position is the input half or output half of
137 // an instruction.
138 class CodePosition
139 {
140 private:
141 MOZ_CONSTEXPR CodePosition(const uint32_t &bits)
142 : bits_(bits)
143 { }
144
145 static const unsigned int INSTRUCTION_SHIFT = 1;
146 static const unsigned int SUBPOSITION_MASK = 1;
147 uint32_t bits_;
148
149 public:
150 static const CodePosition MAX;
151 static const CodePosition MIN;
152
153 // This is the half of the instruction this code position represents, as
154 // described in the huge comment above.
155 enum SubPosition {
156 INPUT,
157 OUTPUT
158 };
159
160 MOZ_CONSTEXPR CodePosition() : bits_(0)
161 { }
162
163 CodePosition(uint32_t instruction, SubPosition where) {
164 JS_ASSERT(instruction < 0x80000000u);
165 JS_ASSERT(((uint32_t)where & SUBPOSITION_MASK) == (uint32_t)where);
166 bits_ = (instruction << INSTRUCTION_SHIFT) | (uint32_t)where;
167 }
168
169 uint32_t ins() const {
170 return bits_ >> INSTRUCTION_SHIFT;
171 }
172
173 uint32_t pos() const {
174 return bits_;
175 }
176
177 SubPosition subpos() const {
178 return (SubPosition)(bits_ & SUBPOSITION_MASK);
179 }
180
181 bool operator <(const CodePosition &other) const {
182 return bits_ < other.bits_;
183 }
184
185 bool operator <=(const CodePosition &other) const {
186 return bits_ <= other.bits_;
187 }
188
189 bool operator !=(const CodePosition &other) const {
190 return bits_ != other.bits_;
191 }
192
193 bool operator ==(const CodePosition &other) const {
194 return bits_ == other.bits_;
195 }
196
197 bool operator >(const CodePosition &other) const {
198 return bits_ > other.bits_;
199 }
200
201 bool operator >=(const CodePosition &other) const {
202 return bits_ >= other.bits_;
203 }
204
205 CodePosition previous() const {
206 JS_ASSERT(*this != MIN);
207 return CodePosition(bits_ - 1);
208 }
209 CodePosition next() const {
210 JS_ASSERT(*this != MAX);
211 return CodePosition(bits_ + 1);
212 }
213 };
214
215 // Structure to track moves inserted before or after an instruction.
216 class InstructionData
217 {
218 LInstruction *ins_;
219 LBlock *block_;
220 LMoveGroup *inputMoves_;
221 LMoveGroup *movesAfter_;
222
223 public:
224 void init(LInstruction *ins, LBlock *block) {
225 JS_ASSERT(!ins_);
226 JS_ASSERT(!block_);
227 ins_ = ins;
228 block_ = block;
229 }
230 LInstruction *ins() const {
231 return ins_;
232 }
233 LBlock *block() const {
234 return block_;
235 }
236 void setInputMoves(LMoveGroup *moves) {
237 inputMoves_ = moves;
238 }
239 LMoveGroup *inputMoves() const {
240 return inputMoves_;
241 }
242 void setMovesAfter(LMoveGroup *moves) {
243 movesAfter_ = moves;
244 }
245 LMoveGroup *movesAfter() const {
246 return movesAfter_;
247 }
248 };
249
250 // Structure to track all moves inserted next to instructions in a graph.
251 class InstructionDataMap
252 {
253 InstructionData *insData_;
254 uint32_t numIns_;
255
256 public:
257 InstructionDataMap()
258 : insData_(nullptr),
259 numIns_(0)
260 { }
261
262 bool init(MIRGenerator *gen, uint32_t numInstructions) {
263 insData_ = gen->allocate<InstructionData>(numInstructions);
264 numIns_ = numInstructions;
265 if (!insData_)
266 return false;
267 memset(insData_, 0, sizeof(InstructionData) * numInstructions);
268 return true;
269 }
270
271 InstructionData &operator[](const CodePosition &pos) {
272 JS_ASSERT(pos.ins() < numIns_);
273 return insData_[pos.ins()];
274 }
275 InstructionData &operator[](LInstruction *ins) {
276 JS_ASSERT(ins->id() < numIns_);
277 return insData_[ins->id()];
278 }
279 InstructionData &operator[](uint32_t ins) {
280 JS_ASSERT(ins < numIns_);
281 return insData_[ins];
282 }
283 };
284
285 // Common superclass for register allocators.
286 class RegisterAllocator
287 {
288 void operator=(const RegisterAllocator &) MOZ_DELETE;
289 RegisterAllocator(const RegisterAllocator &) MOZ_DELETE;
290
291 protected:
292 // Context
293 MIRGenerator *mir;
294 LIRGenerator *lir;
295 LIRGraph &graph;
296
297 // Pool of all registers that should be considered allocateable
298 RegisterSet allRegisters_;
299
300 // Computed data
301 InstructionDataMap insData;
302
303 RegisterAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
304 : mir(mir),
305 lir(lir),
306 graph(graph),
307 allRegisters_(RegisterSet::All())
308 {
309 if (FramePointer != InvalidReg && mir->instrumentedProfiling())
310 allRegisters_.take(AnyRegister(FramePointer));
311 #if defined(JS_CODEGEN_X64)
312 if (mir->compilingAsmJS())
313 allRegisters_.take(AnyRegister(HeapReg));
314 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
315 if (mir->compilingAsmJS()) {
316 allRegisters_.take(AnyRegister(HeapReg));
317 allRegisters_.take(AnyRegister(GlobalReg));
318 allRegisters_.take(AnyRegister(NANReg));
319 }
320 #endif
321 }
322
323 bool init();
324
325 TempAllocator &alloc() const {
326 return mir->alloc();
327 }
328
329 static CodePosition outputOf(uint32_t pos) {
330 return CodePosition(pos, CodePosition::OUTPUT);
331 }
332 static CodePosition outputOf(const LInstruction *ins) {
333 return CodePosition(ins->id(), CodePosition::OUTPUT);
334 }
335 static CodePosition inputOf(uint32_t pos) {
336 return CodePosition(pos, CodePosition::INPUT);
337 }
338 static CodePosition inputOf(const LInstruction *ins) {
339 // Phi nodes "use" their inputs before the beginning of the block.
340 JS_ASSERT(!ins->isPhi());
341 return CodePosition(ins->id(), CodePosition::INPUT);
342 }
343
344 LMoveGroup *getInputMoveGroup(uint32_t ins);
345 LMoveGroup *getMoveGroupAfter(uint32_t ins);
346
347 LMoveGroup *getInputMoveGroup(CodePosition pos) {
348 return getInputMoveGroup(pos.ins());
349 }
350 LMoveGroup *getMoveGroupAfter(CodePosition pos) {
351 return getMoveGroupAfter(pos.ins());
352 }
353
354 CodePosition minimalDefEnd(LInstruction *ins) {
355 // Compute the shortest interval that captures vregs defined by ins.
356 // Watch for instructions that are followed by an OSI point and/or Nop.
357 // If moves are introduced between the instruction and the OSI point then
358 // safepoint information for the instruction may be incorrect.
359 while (true) {
360 LInstruction *next = insData[outputOf(ins).next()].ins();
361 if (!next->isNop() && !next->isOsiPoint())
362 break;
363 ins = next;
364 }
365
366 return outputOf(ins);
367 }
368 };
369
370 static inline AnyRegister
371 GetFixedRegister(const LDefinition *def, const LUse *use)
372 {
373 return def->isFloatReg()
374 ? AnyRegister(FloatRegister::FromCode(use->registerCode()))
375 : AnyRegister(Register::FromCode(use->registerCode()));
376 }
377
378 } // namespace jit
379 } // namespace js
380
381 #endif /* jit_RegisterAllocator_h */

mercurial