RegisterAllocator.h (10046B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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/MathAlgorithms.h" 11 12 #include <compare> // std::strong_ordering 13 14 #include "jit/LIR.h" 15 #include "jit/MIRGenerator.h" 16 #include "jit/MIRGraph.h" 17 18 // Generic structures and functions for use by register allocators. 19 20 namespace js { 21 namespace jit { 22 23 class LIRGenerator; 24 25 #ifdef DEBUG 26 // Structure for running a liveness analysis on a finished register allocation. 27 // This analysis can be used for two purposes: 28 // 29 // - Check the integrity of the allocation, i.e. that the reads and writes of 30 // physical values preserve the semantics of the original virtual registers. 31 // 32 // - Populate safepoints with live registers, GC thing and value data, to 33 // streamline the process of prototyping new allocators. 34 struct AllocationIntegrityState { 35 explicit AllocationIntegrityState(LIRGraph& graph) : graph(graph) {} 36 37 // Record all virtual registers in the graph. This must be called before 38 // register allocation, to pick up the original LUses. 39 [[nodiscard]] bool record(); 40 41 // Perform the liveness analysis on the graph, and assert on an invalid 42 // allocation. This must be called after register allocation, to pick up 43 // all assigned physical values. 44 [[nodiscard]] bool check(); 45 46 private: 47 LIRGraph& graph; 48 49 // For all instructions and phis in the graph, keep track of the virtual 50 // registers for all inputs and outputs of the nodes. These are overwritten 51 // in place during register allocation. This information is kept on the 52 // side rather than in the instructions and phis themselves to avoid 53 // debug-builds-only bloat in the size of the involved structures. 54 55 struct InstructionInfo { 56 Vector<LAllocation, 2, SystemAllocPolicy> inputs; 57 Vector<LDefinition, 0, SystemAllocPolicy> temps; 58 Vector<LDefinition, 1, SystemAllocPolicy> outputs; 59 60 InstructionInfo() = default; 61 62 InstructionInfo(const InstructionInfo& o) { 63 AutoEnterOOMUnsafeRegion oomUnsafe; 64 if (!inputs.appendAll(o.inputs) || !temps.appendAll(o.temps) || 65 !outputs.appendAll(o.outputs)) { 66 oomUnsafe.crash("InstructionInfo::InstructionInfo"); 67 } 68 } 69 }; 70 Vector<InstructionInfo, 0, SystemAllocPolicy> instructions; 71 72 struct BlockInfo { 73 Vector<InstructionInfo, 5, SystemAllocPolicy> phis; 74 BlockInfo() = default; 75 BlockInfo(const BlockInfo& o) { 76 AutoEnterOOMUnsafeRegion oomUnsafe; 77 if (!phis.appendAll(o.phis)) { 78 oomUnsafe.crash("BlockInfo::BlockInfo"); 79 } 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 LBlock* block; 91 uint32_t vreg; 92 LAllocation alloc; 93 94 // Order of insertion into seen, for sorting. 95 uint32_t index; 96 97 using Lookup = IntegrityItem; 98 static HashNumber hash(const IntegrityItem& item) { 99 HashNumber hash = item.alloc.hash(); 100 hash = mozilla::RotateLeft(hash, 4) ^ item.vreg; 101 hash = mozilla::RotateLeft(hash, 4) ^ HashNumber(item.block->mir()->id()); 102 return hash; 103 } 104 static bool match(const IntegrityItem& one, const IntegrityItem& two) { 105 return one.block == two.block && one.vreg == two.vreg && 106 one.alloc == two.alloc; 107 } 108 }; 109 110 // Items still to be processed. 111 Vector<IntegrityItem, 10, SystemAllocPolicy> worklist; 112 113 // Set of all items that have already been processed. 114 using IntegrityItemSet = 115 HashSet<IntegrityItem, IntegrityItem, SystemAllocPolicy>; 116 IntegrityItemSet seen; 117 118 [[nodiscard]] bool checkIntegrity(LBlock* block, LInstruction* ins, 119 uint32_t vreg, LAllocation alloc); 120 void checkSafepointAllocation(LInstruction* ins, uint32_t vreg, 121 LAllocation alloc); 122 [[nodiscard]] bool addPredecessor(LBlock* block, uint32_t vreg, 123 LAllocation alloc); 124 125 void dump(); 126 }; 127 #endif // DEBUG 128 129 // Represents with better-than-instruction precision a position in the 130 // instruction stream. 131 // 132 // An issue comes up when performing register allocation as to how to represent 133 // information such as "this register is only needed for the input of 134 // this instruction, it can be clobbered in the output". Just having ranges 135 // of instruction IDs is insufficiently expressive to denote all possibilities. 136 // This class solves this issue by associating an extra bit with the instruction 137 // ID which indicates whether the position is the input half or output half of 138 // an instruction. 139 class CodePosition { 140 private: 141 constexpr explicit CodePosition(uint32_t bits) : bits_(bits) {} 142 143 static const unsigned int INSTRUCTION_SHIFT = 1; 144 static const unsigned int SUBPOSITION_MASK = 1; 145 uint32_t bits_; 146 147 public: 148 static const CodePosition MAX; 149 static const CodePosition MIN; 150 151 // This is the half of the instruction this code position represents, as 152 // described in the huge comment above. 153 enum SubPosition { INPUT, OUTPUT }; 154 155 constexpr CodePosition() : bits_(0) {} 156 157 CodePosition(uint32_t instruction, SubPosition where) { 158 MOZ_ASSERT(instruction < 0x80000000u); 159 MOZ_ASSERT(((uint32_t)where & SUBPOSITION_MASK) == (uint32_t)where); 160 bits_ = (instruction << INSTRUCTION_SHIFT) | (uint32_t)where; 161 } 162 163 uint32_t ins() const { return bits_ >> INSTRUCTION_SHIFT; } 164 165 uint32_t bits() const { return bits_; } 166 167 SubPosition subpos() const { return (SubPosition)(bits_ & SUBPOSITION_MASK); } 168 169 constexpr auto operator<=>(const CodePosition& other) const = default; 170 171 uint32_t operator-(CodePosition other) const { 172 MOZ_ASSERT(bits_ >= other.bits_); 173 return bits_ - other.bits_; 174 } 175 176 CodePosition previous() const { 177 MOZ_ASSERT(*this != MIN); 178 return CodePosition(bits_ - 1); 179 } 180 CodePosition next() const { 181 MOZ_ASSERT(*this != MAX); 182 return CodePosition(bits_ + 1); 183 } 184 }; 185 186 // Structure to track all moves inserted next to instructions in a graph. 187 class InstructionDataMap { 188 FixedList<LNode*> insData_; 189 190 public: 191 InstructionDataMap() {} 192 193 [[nodiscard]] bool init(MIRGenerator* gen, uint32_t numInstructions) { 194 if (!insData_.init(gen->alloc(), numInstructions)) { 195 return false; 196 } 197 memset(&insData_[0], 0, sizeof(LNode*) * numInstructions); 198 return true; 199 } 200 201 LNode*& operator[](CodePosition pos) { return operator[](pos.ins()); } 202 LNode* const& operator[](CodePosition pos) const { 203 return operator[](pos.ins()); 204 } 205 LNode*& operator[](uint32_t ins) { return insData_[ins]; } 206 LNode* const& operator[](uint32_t ins) const { return insData_[ins]; } 207 }; 208 209 // Common superclass for register allocators. 210 class RegisterAllocator { 211 void operator=(const RegisterAllocator&) = delete; 212 RegisterAllocator(const RegisterAllocator&) = delete; 213 214 protected: 215 // Context 216 MIRGenerator* mir; 217 LIRGenerator* lir; 218 LIRGraph& graph; 219 220 // Pool of all registers that should be considered allocateable 221 AllocatableRegisterSet allRegisters_; 222 223 RegisterAllocator(MIRGenerator* mir, LIRGenerator* lir, LIRGraph& graph) 224 : mir(mir), lir(lir), graph(graph), allRegisters_(RegisterSet::All()) { 225 MOZ_ASSERT(!allRegisters_.has(FramePointer)); 226 if (mir->compilingWasm()) { 227 takeWasmRegisters(allRegisters_); 228 } 229 } 230 231 TempAllocator& alloc() const { return mir->alloc(); } 232 233 CodePosition outputOf(const LNode* ins) const { 234 return ins->isPhi() ? outputOf(ins->toPhi()) 235 : outputOf(ins->toInstruction()); 236 } 237 CodePosition outputOf(const LPhi* ins) const { 238 // All phis in a block write their outputs after all of them have 239 // read their inputs. Consequently, it doesn't make sense to talk 240 // about code positions in the middle of a series of phis. 241 LBlock* block = ins->block(); 242 return CodePosition(block->getPhi(block->numPhis() - 1)->id(), 243 CodePosition::OUTPUT); 244 } 245 CodePosition outputOf(const LInstruction* ins) const { 246 return CodePosition(ins->id(), CodePosition::OUTPUT); 247 } 248 CodePosition inputOf(const LNode* ins) const { 249 return ins->isPhi() ? inputOf(ins->toPhi()) : inputOf(ins->toInstruction()); 250 } 251 CodePosition inputOf(const LPhi* ins) const { 252 // All phis in a block read their inputs before any of them write their 253 // outputs. Consequently, it doesn't make sense to talk about code 254 // positions in the middle of a series of phis. 255 return CodePosition(ins->block()->getPhi(0)->id(), CodePosition::INPUT); 256 } 257 CodePosition inputOf(const LInstruction* ins) const { 258 return CodePosition(ins->id(), CodePosition::INPUT); 259 } 260 261 LMoveGroup* getInputMoveGroup(LInstruction* ins); 262 LMoveGroup* getFixReuseMoveGroup(LInstruction* ins); 263 LMoveGroup* getMoveGroupAfter(LInstruction* ins); 264 265 void dumpInstructions(const char* who); 266 267 public: 268 template <typename TakeableSet> 269 static void takeWasmRegisters(TakeableSet& regs) { 270 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ 271 defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ 272 defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) 273 regs.take(HeapReg); 274 #endif 275 MOZ_ASSERT(!regs.has(FramePointer)); 276 } 277 }; 278 279 static inline AnyRegister GetFixedRegister(const LDefinition* def, 280 const LUse* use) { 281 return def->isFloatReg() 282 ? AnyRegister(FloatRegister::FromCode(use->registerCode())) 283 : AnyRegister(Register::FromCode(use->registerCode())); 284 } 285 286 } // namespace jit 287 } // namespace js 288 289 #endif /* jit_RegisterAllocator_h */