js/src/jit/StupidAllocator.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/StupidAllocator.h"
michael@0 8
michael@0 9 #include "jstypes.h"
michael@0 10
michael@0 11 using namespace js;
michael@0 12 using namespace js::jit;
michael@0 13
michael@0 14 static inline uint32_t
michael@0 15 DefaultStackSlot(uint32_t vreg)
michael@0 16 {
michael@0 17 return vreg * sizeof(Value);
michael@0 18 }
michael@0 19
michael@0 20 LAllocation *
michael@0 21 StupidAllocator::stackLocation(uint32_t vreg)
michael@0 22 {
michael@0 23 LDefinition *def = virtualRegisters[vreg];
michael@0 24 if (def->policy() == LDefinition::PRESET && def->output()->isArgument())
michael@0 25 return def->output();
michael@0 26
michael@0 27 return new(alloc()) LStackSlot(DefaultStackSlot(vreg));
michael@0 28 }
michael@0 29
michael@0 30 StupidAllocator::RegisterIndex
michael@0 31 StupidAllocator::registerIndex(AnyRegister reg)
michael@0 32 {
michael@0 33 for (size_t i = 0; i < registerCount; i++) {
michael@0 34 if (reg == registers[i].reg)
michael@0 35 return i;
michael@0 36 }
michael@0 37 MOZ_ASSUME_UNREACHABLE("Bad register");
michael@0 38 }
michael@0 39
michael@0 40 bool
michael@0 41 StupidAllocator::init()
michael@0 42 {
michael@0 43 if (!RegisterAllocator::init())
michael@0 44 return false;
michael@0 45
michael@0 46 if (!virtualRegisters.appendN((LDefinition *)nullptr, graph.numVirtualRegisters()))
michael@0 47 return false;
michael@0 48
michael@0 49 for (size_t i = 0; i < graph.numBlocks(); i++) {
michael@0 50 LBlock *block = graph.getBlock(i);
michael@0 51 for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
michael@0 52 for (size_t j = 0; j < ins->numDefs(); j++) {
michael@0 53 LDefinition *def = ins->getDef(j);
michael@0 54 if (def->policy() != LDefinition::PASSTHROUGH)
michael@0 55 virtualRegisters[def->virtualRegister()] = def;
michael@0 56 }
michael@0 57
michael@0 58 for (size_t j = 0; j < ins->numTemps(); j++) {
michael@0 59 LDefinition *def = ins->getTemp(j);
michael@0 60 if (def->isBogusTemp())
michael@0 61 continue;
michael@0 62 virtualRegisters[def->virtualRegister()] = def;
michael@0 63 }
michael@0 64 }
michael@0 65 for (size_t j = 0; j < block->numPhis(); j++) {
michael@0 66 LPhi *phi = block->getPhi(j);
michael@0 67 LDefinition *def = phi->getDef(0);
michael@0 68 uint32_t vreg = def->virtualRegister();
michael@0 69
michael@0 70 virtualRegisters[vreg] = def;
michael@0 71 }
michael@0 72 }
michael@0 73
michael@0 74 // Assign physical registers to the tracked allocation.
michael@0 75 {
michael@0 76 registerCount = 0;
michael@0 77 RegisterSet remainingRegisters(allRegisters_);
michael@0 78 while (!remainingRegisters.empty(/* float = */ false))
michael@0 79 registers[registerCount++].reg = AnyRegister(remainingRegisters.takeGeneral());
michael@0 80 while (!remainingRegisters.empty(/* float = */ true))
michael@0 81 registers[registerCount++].reg = AnyRegister(remainingRegisters.takeFloat());
michael@0 82 JS_ASSERT(registerCount <= MAX_REGISTERS);
michael@0 83 }
michael@0 84
michael@0 85 return true;
michael@0 86 }
michael@0 87
michael@0 88 bool
michael@0 89 StupidAllocator::allocationRequiresRegister(const LAllocation *alloc, AnyRegister reg)
michael@0 90 {
michael@0 91 if (alloc->isRegister() && alloc->toRegister() == reg)
michael@0 92 return true;
michael@0 93 if (alloc->isUse()) {
michael@0 94 const LUse *use = alloc->toUse();
michael@0 95 if (use->policy() == LUse::FIXED) {
michael@0 96 AnyRegister usedReg = GetFixedRegister(virtualRegisters[use->virtualRegister()], use);
michael@0 97 if (usedReg == reg)
michael@0 98 return true;
michael@0 99 }
michael@0 100 }
michael@0 101 return false;
michael@0 102 }
michael@0 103
michael@0 104 bool
michael@0 105 StupidAllocator::registerIsReserved(LInstruction *ins, AnyRegister reg)
michael@0 106 {
michael@0 107 // Whether reg is already reserved for an input or output of ins.
michael@0 108 for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
michael@0 109 if (allocationRequiresRegister(*alloc, reg))
michael@0 110 return true;
michael@0 111 }
michael@0 112 for (size_t i = 0; i < ins->numTemps(); i++) {
michael@0 113 if (allocationRequiresRegister(ins->getTemp(i)->output(), reg))
michael@0 114 return true;
michael@0 115 }
michael@0 116 for (size_t i = 0; i < ins->numDefs(); i++) {
michael@0 117 if (allocationRequiresRegister(ins->getDef(i)->output(), reg))
michael@0 118 return true;
michael@0 119 }
michael@0 120 return false;
michael@0 121 }
michael@0 122
michael@0 123 AnyRegister
michael@0 124 StupidAllocator::ensureHasRegister(LInstruction *ins, uint32_t vreg)
michael@0 125 {
michael@0 126 // Ensure that vreg is held in a register before ins.
michael@0 127
michael@0 128 // Check if the virtual register is already held in a physical register.
michael@0 129 RegisterIndex existing = findExistingRegister(vreg);
michael@0 130 if (existing != UINT32_MAX) {
michael@0 131 if (registerIsReserved(ins, registers[existing].reg)) {
michael@0 132 evictRegister(ins, existing);
michael@0 133 } else {
michael@0 134 registers[existing].age = ins->id();
michael@0 135 return registers[existing].reg;
michael@0 136 }
michael@0 137 }
michael@0 138
michael@0 139 RegisterIndex best = allocateRegister(ins, vreg);
michael@0 140 loadRegister(ins, vreg, best, virtualRegisters[vreg]->type());
michael@0 141
michael@0 142 return registers[best].reg;
michael@0 143 }
michael@0 144
michael@0 145 StupidAllocator::RegisterIndex
michael@0 146 StupidAllocator::allocateRegister(LInstruction *ins, uint32_t vreg)
michael@0 147 {
michael@0 148 // Pick a register for vreg, evicting an existing register if necessary.
michael@0 149 // Spill code will be placed before ins, and no existing allocated input
michael@0 150 // for ins will be touched.
michael@0 151 JS_ASSERT(ins);
michael@0 152
michael@0 153 LDefinition *def = virtualRegisters[vreg];
michael@0 154 JS_ASSERT(def);
michael@0 155
michael@0 156 RegisterIndex best = UINT32_MAX;
michael@0 157
michael@0 158 for (size_t i = 0; i < registerCount; i++) {
michael@0 159 AnyRegister reg = registers[i].reg;
michael@0 160
michael@0 161 if (reg.isFloat() != def->isFloatReg())
michael@0 162 continue;
michael@0 163
michael@0 164 // Skip the register if it is in use for an allocated input or output.
michael@0 165 if (registerIsReserved(ins, reg))
michael@0 166 continue;
michael@0 167
michael@0 168 if (registers[i].vreg == MISSING_ALLOCATION ||
michael@0 169 best == UINT32_MAX ||
michael@0 170 registers[best].age > registers[i].age)
michael@0 171 {
michael@0 172 best = i;
michael@0 173 }
michael@0 174 }
michael@0 175
michael@0 176 evictRegister(ins, best);
michael@0 177 return best;
michael@0 178 }
michael@0 179
michael@0 180 void
michael@0 181 StupidAllocator::syncRegister(LInstruction *ins, RegisterIndex index)
michael@0 182 {
michael@0 183 if (registers[index].dirty) {
michael@0 184 LMoveGroup *input = getInputMoveGroup(ins->id());
michael@0 185 LAllocation *source = new(alloc()) LAllocation(registers[index].reg);
michael@0 186
michael@0 187 uint32_t existing = registers[index].vreg;
michael@0 188 LAllocation *dest = stackLocation(existing);
michael@0 189 input->addAfter(source, dest, registers[index].type);
michael@0 190
michael@0 191 registers[index].dirty = false;
michael@0 192 }
michael@0 193 }
michael@0 194
michael@0 195 void
michael@0 196 StupidAllocator::evictRegister(LInstruction *ins, RegisterIndex index)
michael@0 197 {
michael@0 198 syncRegister(ins, index);
michael@0 199 registers[index].set(MISSING_ALLOCATION);
michael@0 200 }
michael@0 201
michael@0 202 void
michael@0 203 StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type)
michael@0 204 {
michael@0 205 // Load a vreg from its stack location to a register.
michael@0 206 LMoveGroup *input = getInputMoveGroup(ins->id());
michael@0 207 LAllocation *source = stackLocation(vreg);
michael@0 208 LAllocation *dest = new(alloc()) LAllocation(registers[index].reg);
michael@0 209 input->addAfter(source, dest, type);
michael@0 210 registers[index].set(vreg, ins);
michael@0 211 registers[index].type = type;
michael@0 212 }
michael@0 213
michael@0 214 StupidAllocator::RegisterIndex
michael@0 215 StupidAllocator::findExistingRegister(uint32_t vreg)
michael@0 216 {
michael@0 217 for (size_t i = 0; i < registerCount; i++) {
michael@0 218 if (registers[i].vreg == vreg)
michael@0 219 return i;
michael@0 220 }
michael@0 221 return UINT32_MAX;
michael@0 222 }
michael@0 223
michael@0 224 bool
michael@0 225 StupidAllocator::go()
michael@0 226 {
michael@0 227 // This register allocator is intended to be as simple as possible, while
michael@0 228 // still being complicated enough to share properties with more complicated
michael@0 229 // allocators. Namely, physical registers may be used to carry virtual
michael@0 230 // registers across LIR instructions, but not across basic blocks.
michael@0 231 //
michael@0 232 // This algorithm does not pay any attention to liveness. It is performed
michael@0 233 // as a single forward pass through the basic blocks in the program. As
michael@0 234 // virtual registers and temporaries are defined they are assigned physical
michael@0 235 // registers, evicting existing allocations in an LRU fashion.
michael@0 236
michael@0 237 // For virtual registers not carried in a register, a canonical spill
michael@0 238 // location is used. Each vreg has a different spill location; since we do
michael@0 239 // not track liveness we cannot determine that two vregs have disjoint
michael@0 240 // lifetimes. Thus, the maximum stack height is the number of vregs (scaled
michael@0 241 // by two on 32 bit platforms to allow storing double values).
michael@0 242 graph.setLocalSlotCount(DefaultStackSlot(graph.numVirtualRegisters()));
michael@0 243
michael@0 244 if (!init())
michael@0 245 return false;
michael@0 246
michael@0 247 for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
michael@0 248 LBlock *block = graph.getBlock(blockIndex);
michael@0 249 JS_ASSERT(block->mir()->id() == blockIndex);
michael@0 250
michael@0 251 for (size_t i = 0; i < registerCount; i++)
michael@0 252 registers[i].set(MISSING_ALLOCATION);
michael@0 253
michael@0 254 for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
michael@0 255 LInstruction *ins = *iter;
michael@0 256
michael@0 257 if (ins == *block->rbegin())
michael@0 258 syncForBlockEnd(block, ins);
michael@0 259
michael@0 260 allocateForInstruction(ins);
michael@0 261 }
michael@0 262 }
michael@0 263
michael@0 264 return true;
michael@0 265 }
michael@0 266
michael@0 267 void
michael@0 268 StupidAllocator::syncForBlockEnd(LBlock *block, LInstruction *ins)
michael@0 269 {
michael@0 270 // Sync any dirty registers, and update the synced state for phi nodes at
michael@0 271 // each successor of a block. We cannot conflate the storage for phis with
michael@0 272 // that of their inputs, as we cannot prove the live ranges of the phi and
michael@0 273 // its input do not overlap. The values for the two may additionally be
michael@0 274 // different, as the phi could be for the value of the input in a previous
michael@0 275 // loop iteration.
michael@0 276
michael@0 277 for (size_t i = 0; i < registerCount; i++)
michael@0 278 syncRegister(ins, i);
michael@0 279
michael@0 280 LMoveGroup *group = nullptr;
michael@0 281
michael@0 282 MBasicBlock *successor = block->mir()->successorWithPhis();
michael@0 283 if (successor) {
michael@0 284 uint32_t position = block->mir()->positionInPhiSuccessor();
michael@0 285 LBlock *lirsuccessor = graph.getBlock(successor->id());
michael@0 286 for (size_t i = 0; i < lirsuccessor->numPhis(); i++) {
michael@0 287 LPhi *phi = lirsuccessor->getPhi(i);
michael@0 288
michael@0 289 uint32_t sourcevreg = phi->getOperand(position)->toUse()->virtualRegister();
michael@0 290 uint32_t destvreg = phi->getDef(0)->virtualRegister();
michael@0 291
michael@0 292 if (sourcevreg == destvreg)
michael@0 293 continue;
michael@0 294
michael@0 295 LAllocation *source = stackLocation(sourcevreg);
michael@0 296 LAllocation *dest = stackLocation(destvreg);
michael@0 297
michael@0 298 if (!group) {
michael@0 299 // The moves we insert here need to happen simultaneously with
michael@0 300 // each other, yet after any existing moves before the instruction.
michael@0 301 LMoveGroup *input = getInputMoveGroup(ins->id());
michael@0 302 if (input->numMoves() == 0) {
michael@0 303 group = input;
michael@0 304 } else {
michael@0 305 group = LMoveGroup::New(alloc());
michael@0 306 block->insertAfter(input, group);
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 group->add(source, dest, phi->getDef(0)->type());
michael@0 311 }
michael@0 312 }
michael@0 313 }
michael@0 314
michael@0 315 void
michael@0 316 StupidAllocator::allocateForInstruction(LInstruction *ins)
michael@0 317 {
michael@0 318 // Sync all registers before making a call.
michael@0 319 if (ins->isCall()) {
michael@0 320 for (size_t i = 0; i < registerCount; i++)
michael@0 321 syncRegister(ins, i);
michael@0 322 }
michael@0 323
michael@0 324 // Allocate for inputs which are required to be in registers.
michael@0 325 for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
michael@0 326 if (!alloc->isUse())
michael@0 327 continue;
michael@0 328 LUse *use = alloc->toUse();
michael@0 329 uint32_t vreg = use->virtualRegister();
michael@0 330 if (use->policy() == LUse::REGISTER) {
michael@0 331 AnyRegister reg = ensureHasRegister(ins, vreg);
michael@0 332 alloc.replace(LAllocation(reg));
michael@0 333 } else if (use->policy() == LUse::FIXED) {
michael@0 334 AnyRegister reg = GetFixedRegister(virtualRegisters[vreg], use);
michael@0 335 RegisterIndex index = registerIndex(reg);
michael@0 336 if (registers[index].vreg != vreg) {
michael@0 337 evictRegister(ins, index);
michael@0 338 RegisterIndex existing = findExistingRegister(vreg);
michael@0 339 if (existing != UINT32_MAX)
michael@0 340 evictRegister(ins, existing);
michael@0 341 loadRegister(ins, vreg, index, virtualRegisters[vreg]->type());
michael@0 342 }
michael@0 343 alloc.replace(LAllocation(reg));
michael@0 344 } else {
michael@0 345 // Inputs which are not required to be in a register are not
michael@0 346 // allocated until after temps/definitions, as the latter may need
michael@0 347 // to evict registers which hold these inputs.
michael@0 348 }
michael@0 349 }
michael@0 350
michael@0 351 // Find registers to hold all temporaries and outputs of the instruction.
michael@0 352 for (size_t i = 0; i < ins->numTemps(); i++) {
michael@0 353 LDefinition *def = ins->getTemp(i);
michael@0 354 if (!def->isBogusTemp())
michael@0 355 allocateForDefinition(ins, def);
michael@0 356 }
michael@0 357 for (size_t i = 0; i < ins->numDefs(); i++) {
michael@0 358 LDefinition *def = ins->getDef(i);
michael@0 359 if (def->policy() != LDefinition::PASSTHROUGH)
michael@0 360 allocateForDefinition(ins, def);
michael@0 361 }
michael@0 362
michael@0 363 // Allocate for remaining inputs which do not need to be in registers.
michael@0 364 for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
michael@0 365 if (!alloc->isUse())
michael@0 366 continue;
michael@0 367 LUse *use = alloc->toUse();
michael@0 368 uint32_t vreg = use->virtualRegister();
michael@0 369 JS_ASSERT(use->policy() != LUse::REGISTER && use->policy() != LUse::FIXED);
michael@0 370
michael@0 371 RegisterIndex index = findExistingRegister(vreg);
michael@0 372 if (index == UINT32_MAX) {
michael@0 373 LAllocation *stack = stackLocation(use->virtualRegister());
michael@0 374 alloc.replace(*stack);
michael@0 375 } else {
michael@0 376 registers[index].age = ins->id();
michael@0 377 alloc.replace(LAllocation(registers[index].reg));
michael@0 378 }
michael@0 379 }
michael@0 380
michael@0 381 // If this is a call, evict all registers except for those holding outputs.
michael@0 382 if (ins->isCall()) {
michael@0 383 for (size_t i = 0; i < registerCount; i++) {
michael@0 384 if (!registers[i].dirty)
michael@0 385 registers[i].set(MISSING_ALLOCATION);
michael@0 386 }
michael@0 387 }
michael@0 388 }
michael@0 389
michael@0 390 void
michael@0 391 StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def)
michael@0 392 {
michael@0 393 uint32_t vreg = def->virtualRegister();
michael@0 394
michael@0 395 CodePosition from;
michael@0 396 if ((def->output()->isRegister() && def->policy() == LDefinition::PRESET) ||
michael@0 397 def->policy() == LDefinition::MUST_REUSE_INPUT)
michael@0 398 {
michael@0 399 // Result will be in a specific register, spill any vreg held in
michael@0 400 // that register before the instruction.
michael@0 401 RegisterIndex index =
michael@0 402 registerIndex(def->policy() == LDefinition::PRESET
michael@0 403 ? def->output()->toRegister()
michael@0 404 : ins->getOperand(def->getReusedInput())->toRegister());
michael@0 405 evictRegister(ins, index);
michael@0 406 registers[index].set(vreg, ins, true);
michael@0 407 registers[index].type = virtualRegisters[vreg]->type();
michael@0 408 def->setOutput(LAllocation(registers[index].reg));
michael@0 409 } else if (def->policy() == LDefinition::PRESET) {
michael@0 410 // The result must be a stack location.
michael@0 411 def->setOutput(*stackLocation(vreg));
michael@0 412 } else {
michael@0 413 // Find a register to hold the result of the instruction.
michael@0 414 RegisterIndex best = allocateRegister(ins, vreg);
michael@0 415 registers[best].set(vreg, ins, true);
michael@0 416 registers[best].type = virtualRegisters[vreg]->type();
michael@0 417 def->setOutput(LAllocation(registers[best].reg));
michael@0 418 }
michael@0 419 }

mercurial