mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Factor common register allocation code and add integrity checker to RegisterAllocator.h, add baseline StupidAllocator, bug 812945. r=jandem
This commit is contained in:
parent
d91ef531e3
commit
fbc44adf92
@ -294,8 +294,10 @@ CPPSRCS += MIR.cpp \
|
||||
MIRGraph.cpp \
|
||||
MoveResolver.cpp \
|
||||
EdgeCaseAnalysis.cpp \
|
||||
RegisterAllocator.cpp \
|
||||
Snapshots.cpp \
|
||||
Safepoints.cpp \
|
||||
StupidAllocator.cpp \
|
||||
TypeOracle.cpp \
|
||||
TypePolicy.cpp \
|
||||
ValueNumbering.cpp \
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "jsworkers.h"
|
||||
#include "IonCompartment.h"
|
||||
#include "CodeGenerator.h"
|
||||
#include "StupidAllocator.h"
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
# include "x86/Lowering-x86.h"
|
||||
@ -949,16 +950,47 @@ CompileBackEnd(MIRGenerator *mir)
|
||||
if (mir->shouldCancel("Generate LIR"))
|
||||
return NULL;
|
||||
|
||||
if (js_IonOptions.lsra) {
|
||||
AllocationIntegrityState integrity(*lir);
|
||||
|
||||
switch (js_IonOptions.registerAllocator) {
|
||||
case RegisterAllocator_LSRA: {
|
||||
#ifdef DEBUG
|
||||
integrity.record();
|
||||
#endif
|
||||
|
||||
LinearScanAllocator regalloc(mir, &lirgen, *lir);
|
||||
if (!regalloc.go())
|
||||
return NULL;
|
||||
IonSpewPass("Allocate Registers", ®alloc);
|
||||
|
||||
if (mir->shouldCancel("Allocate Registers"))
|
||||
#ifdef DEBUG
|
||||
integrity.check(false);
|
||||
#endif
|
||||
|
||||
IonSpewPass("Allocate Registers [LSRA]", ®alloc);
|
||||
break;
|
||||
}
|
||||
|
||||
case RegisterAllocator_Stupid: {
|
||||
// Use the integrity checker to populate safepoint information, so
|
||||
// run it in all builds.
|
||||
integrity.record();
|
||||
|
||||
StupidAllocator regalloc(mir, &lirgen, *lir);
|
||||
if (!regalloc.go())
|
||||
return NULL;
|
||||
if (!integrity.check(true))
|
||||
return NULL;
|
||||
IonSpewPass("Allocate Registers [Stupid]");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
JS_NOT_REACHED("Bad regalloc");
|
||||
}
|
||||
|
||||
if (mir->shouldCancel("Allocate Registers"))
|
||||
return NULL;
|
||||
|
||||
CodeGenerator *codegen = js_new<CodeGenerator>(mir, lir);
|
||||
if (!codegen || !codegen->generate()) {
|
||||
js_delete(codegen);
|
||||
|
@ -19,6 +19,12 @@ namespace ion {
|
||||
|
||||
class TempAllocator;
|
||||
|
||||
// Possible register allocators which may be used.
|
||||
enum IonRegisterAllocator {
|
||||
RegisterAllocator_LSRA,
|
||||
RegisterAllocator_Stupid
|
||||
};
|
||||
|
||||
struct IonOptions
|
||||
{
|
||||
// Toggles whether global value numbering is used.
|
||||
@ -47,11 +53,10 @@ struct IonOptions
|
||||
// Default: true
|
||||
bool limitScriptSize;
|
||||
|
||||
// Toggles whether Linear Scan Register Allocation is used. If LSRA is not
|
||||
// used, then Greedy Register Allocation is used instead.
|
||||
// Describes which register allocator to use.
|
||||
//
|
||||
// Default: true
|
||||
bool lsra;
|
||||
// Default: LSRA
|
||||
IonRegisterAllocator registerAllocator;
|
||||
|
||||
// Toggles whether inlining is performed.
|
||||
//
|
||||
@ -162,7 +167,7 @@ struct IonOptions
|
||||
licm(true),
|
||||
osr(true),
|
||||
limitScriptSize(true),
|
||||
lsra(true),
|
||||
registerAllocator(RegisterAllocator_LSRA),
|
||||
inlining(true),
|
||||
edgeCaseAnalysis(true),
|
||||
rangeAnalysis(true),
|
||||
|
@ -32,7 +32,7 @@ namespace ion {
|
||||
_(Range) \
|
||||
/* Information during LICM */ \
|
||||
_(LICM) \
|
||||
/* Information during LSRA */ \
|
||||
/* Information during regalloc */ \
|
||||
_(RegAlloc) \
|
||||
/* Information during inlining */ \
|
||||
_(Inlining) \
|
||||
|
@ -105,10 +105,13 @@ class LMoveGroup : public LInstructionHelper<0, 0, 0>
|
||||
LIR_HEADER(MoveGroup);
|
||||
|
||||
void printOperands(FILE *fp);
|
||||
bool add(LAllocation *from, LAllocation *to) {
|
||||
JS_ASSERT(*from != *to);
|
||||
return moves_.append(LMove(from, to));
|
||||
}
|
||||
|
||||
// Add a move which takes place simultaneously with all others in the group.
|
||||
bool add(LAllocation *from, LAllocation *to);
|
||||
|
||||
// Add a move which takes place after existing moves in the group.
|
||||
bool addAfter(LAllocation *from, LAllocation *to);
|
||||
|
||||
size_t numMoves() const {
|
||||
return moves_.length();
|
||||
}
|
||||
|
@ -314,6 +314,44 @@ LInstruction::initSafepoint()
|
||||
JS_ASSERT(safepoint_);
|
||||
}
|
||||
|
||||
bool
|
||||
LMoveGroup::add(LAllocation *from, LAllocation *to)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(*from != *to);
|
||||
for (size_t i = 0; i < moves_.length(); i++)
|
||||
JS_ASSERT(*to != *moves_[i].to());
|
||||
#endif
|
||||
return moves_.append(LMove(from, to));
|
||||
}
|
||||
|
||||
bool
|
||||
LMoveGroup::addAfter(LAllocation *from, LAllocation *to)
|
||||
{
|
||||
// Transform the operands to this move so that performing the result
|
||||
// simultaneously with existing moves in the group will have the same
|
||||
// effect as if the original move took place after the existing moves.
|
||||
|
||||
for (size_t i = 0; i < moves_.length(); i++) {
|
||||
if (*moves_[i].to() == *from) {
|
||||
from = moves_[i].from();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*from == *to)
|
||||
return true;
|
||||
|
||||
for (size_t i = 0; i < moves_.length(); i++) {
|
||||
if (*to == *moves_[i].to()) {
|
||||
moves_[i] = LMove(from, to);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return add(from, to);
|
||||
}
|
||||
|
||||
void
|
||||
LMoveGroup::printOperands(FILE *fp)
|
||||
{
|
||||
|
148
js/src/ion/LIR.h
148
js/src/ion/LIR.h
@ -194,6 +194,10 @@ class LAllocation : public TempObject
|
||||
return bits_ != other.bits_;
|
||||
}
|
||||
|
||||
HashNumber hash() const {
|
||||
return bits_;
|
||||
}
|
||||
|
||||
static void PrintAllocation(FILE *fp, const LAllocation *a);
|
||||
};
|
||||
|
||||
@ -740,6 +744,9 @@ class LBlock : public TempObject
|
||||
LInstructionReverseIterator rbegin() {
|
||||
return instructions_.rbegin();
|
||||
}
|
||||
LInstructionReverseIterator rbegin(LInstruction *at) {
|
||||
return instructions_.rbegin(at);
|
||||
}
|
||||
LInstructionReverseIterator rend() {
|
||||
return instructions_.rend();
|
||||
}
|
||||
@ -935,6 +942,9 @@ class LSafepoint : public TempObject
|
||||
#ifdef JS_NUNBOX32
|
||||
// List of registers which contain pieces of values.
|
||||
NunboxList nunboxParts_;
|
||||
|
||||
// Number of nunboxParts which are not completely filled in.
|
||||
uint32 partialNunboxes_;
|
||||
#elif JS_PUNBOX64
|
||||
// List of registers which contain values.
|
||||
GeneralRegisterSet valueRegs_;
|
||||
@ -942,17 +952,20 @@ class LSafepoint : public TempObject
|
||||
|
||||
public:
|
||||
LSafepoint()
|
||||
: safepointOffset_(INVALID_SAFEPOINT_OFFSET),
|
||||
osiCallPointOffset_(0)
|
||||
: safepointOffset_(INVALID_SAFEPOINT_OFFSET)
|
||||
, osiCallPointOffset_(0)
|
||||
#ifdef JS_NUNBOX32
|
||||
, partialNunboxes_(0)
|
||||
#endif
|
||||
{ }
|
||||
void addLiveRegister(AnyRegister reg) {
|
||||
liveRegs_.add(reg);
|
||||
liveRegs_.addUnchecked(reg);
|
||||
}
|
||||
const RegisterSet &liveRegs() const {
|
||||
return liveRegs_;
|
||||
}
|
||||
void addGcRegister(Register reg) {
|
||||
gcRegs_.add(reg);
|
||||
gcRegs_.addUnchecked(reg);
|
||||
}
|
||||
GeneralRegisterSet gcRegs() const {
|
||||
return gcRegs_;
|
||||
@ -964,6 +977,27 @@ class LSafepoint : public TempObject
|
||||
return gcSlots_;
|
||||
}
|
||||
|
||||
void addGcPointer(LAllocation alloc) {
|
||||
if (alloc.isRegister())
|
||||
addGcRegister(alloc.toRegister().gpr());
|
||||
else if (alloc.isStackSlot())
|
||||
addGcSlot(alloc.toStackSlot()->slot());
|
||||
}
|
||||
|
||||
bool hasGcPointer(LAllocation alloc) {
|
||||
if (alloc.isRegister())
|
||||
return gcRegs().has(alloc.toRegister().gpr());
|
||||
if (alloc.isStackSlot()) {
|
||||
for (size_t i = 0; i < gcSlots_.length(); i++) {
|
||||
if (gcSlots_[i] == alloc.toStackSlot()->slot())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(alloc.isArgument());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addValueSlot(uint32 slot) {
|
||||
return valueSlots_.append(slot);
|
||||
}
|
||||
@ -971,21 +1005,125 @@ class LSafepoint : public TempObject
|
||||
return valueSlots_;
|
||||
}
|
||||
|
||||
bool hasValueSlot(uint32 slot) {
|
||||
for (size_t i = 0; i < valueSlots_.length(); i++) {
|
||||
if (valueSlots_[i] == slot)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JS_NUNBOX32
|
||||
|
||||
bool addNunboxParts(LAllocation type, LAllocation payload) {
|
||||
return nunboxParts_.append(NunboxEntry(type, payload));
|
||||
}
|
||||
|
||||
bool addNunboxType(uint32 typeVreg, LAllocation type) {
|
||||
for (size_t i = 0; i < nunboxParts_.length(); i++) {
|
||||
if (nunboxParts_[i].type == type)
|
||||
return true;
|
||||
if (nunboxParts_[i].type == LUse(LUse::ANY, typeVreg)) {
|
||||
nunboxParts_[i].type = type;
|
||||
partialNunboxes_--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
partialNunboxes_++;
|
||||
|
||||
// vregs for nunbox pairs are adjacent, with the type coming first.
|
||||
uint32 payloadVreg = typeVreg + 1;
|
||||
return nunboxParts_.append(NunboxEntry(type, LUse(payloadVreg, LUse::ANY)));
|
||||
}
|
||||
|
||||
bool hasNunboxType(LAllocation type) {
|
||||
if (type.isArgument())
|
||||
return true;
|
||||
if (type.isStackSlot() && hasValueSlot(type.toStackSlot()->slot() + 1))
|
||||
return true;
|
||||
for (size_t i = 0; i < nunboxParts_.length(); i++) {
|
||||
if (nunboxParts_[i].type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool addNunboxPayload(uint32 payloadVreg, LAllocation payload) {
|
||||
for (size_t i = 0; i < nunboxParts_.length(); i++) {
|
||||
if (nunboxParts_[i].payload == payload)
|
||||
return true;
|
||||
if (nunboxParts_[i].payload == LUse(LUse::ANY, payloadVreg)) {
|
||||
partialNunboxes_--;
|
||||
nunboxParts_[i].payload = payload;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
partialNunboxes_++;
|
||||
|
||||
// vregs for nunbox pairs are adjacent, with the type coming first.
|
||||
uint32 typeVreg = payloadVreg - 1;
|
||||
return nunboxParts_.append(NunboxEntry(LUse(typeVreg, LUse::ANY), payload));
|
||||
}
|
||||
|
||||
bool hasNunboxPayload(LAllocation payload) {
|
||||
if (payload.isArgument())
|
||||
return true;
|
||||
if (payload.isStackSlot() && hasValueSlot(payload.toStackSlot()->slot()))
|
||||
return true;
|
||||
for (size_t i = 0; i < nunboxParts_.length(); i++) {
|
||||
if (nunboxParts_[i].payload == payload)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NunboxList &nunboxParts() {
|
||||
return nunboxParts_;
|
||||
}
|
||||
|
||||
uint32 partialNunboxes() {
|
||||
return partialNunboxes_;
|
||||
}
|
||||
|
||||
#elif JS_PUNBOX64
|
||||
|
||||
void addValueRegister(Register reg) {
|
||||
valueRegs_.add(reg);
|
||||
}
|
||||
GeneralRegisterSet valueRegs() {
|
||||
return valueRegs_;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool addBoxedValue(LAllocation alloc) {
|
||||
if (alloc.isRegister()) {
|
||||
Register reg = alloc.toRegister().gpr();
|
||||
if (!valueRegs().has(reg))
|
||||
addValueRegister(reg);
|
||||
return true;
|
||||
}
|
||||
if (alloc.isStackSlot()) {
|
||||
uint32 slot = alloc.toStackSlot()->slot();
|
||||
for (size_t i = 0; i < valueSlots().length(); i++) {
|
||||
if (valueSlots()[i] == slot)
|
||||
return true;
|
||||
}
|
||||
return addValueSlot(slot);
|
||||
}
|
||||
JS_ASSERT(alloc.isArgument());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasBoxedValue(LAllocation alloc) {
|
||||
if (alloc.isRegister())
|
||||
return valueRegs().has(alloc.toRegister().gpr());
|
||||
if (alloc.isStackSlot())
|
||||
return hasValueSlot(alloc.toStackSlot()->slot());
|
||||
JS_ASSERT(alloc.isArgument());
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // JS_PUNBOX64
|
||||
|
||||
bool encoded() const {
|
||||
return safepointOffset_ != INVALID_SAFEPOINT_OFFSET;
|
||||
}
|
||||
|
@ -360,9 +360,6 @@ LiveInterval::firstIncompatibleUse(LAllocation alloc)
|
||||
return CodePosition::MAX;
|
||||
}
|
||||
|
||||
const CodePosition CodePosition::MAX(UINT_MAX);
|
||||
const CodePosition CodePosition::MIN(0);
|
||||
|
||||
/*
|
||||
* This function pre-allocates and initializes as much global state as possible
|
||||
* to avoid littering the algorithms with memory management cruft.
|
||||
@ -370,6 +367,9 @@ const CodePosition CodePosition::MIN(0);
|
||||
bool
|
||||
LinearScanAllocator::createDataStructures()
|
||||
{
|
||||
if (!RegisterAllocator::init())
|
||||
return false;
|
||||
|
||||
liveIn = lir->mir()->allocate<BitSet*>(graph.numBlockIds());
|
||||
if (!liveIn)
|
||||
return false;
|
||||
@ -387,9 +387,6 @@ LinearScanAllocator::createDataStructures()
|
||||
if (!vregs.init(lir->mir(), graph.numVirtualRegisters()))
|
||||
return false;
|
||||
|
||||
if (!insData.init(lir->mir(), graph.numInstructions()))
|
||||
return false;
|
||||
|
||||
// Build virtual register objects
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
if (mir->shouldCancel("LSRA create data structures (main loop)"))
|
||||
@ -405,7 +402,6 @@ LinearScanAllocator::createDataStructures()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
insData[*ins].init(*ins, block);
|
||||
|
||||
for (size_t j = 0; j < ins->numTemps(); j++) {
|
||||
LDefinition *def = ins->getTemp(j);
|
||||
@ -420,7 +416,6 @@ LinearScanAllocator::createDataStructures()
|
||||
LDefinition *def = phi->getDef(0);
|
||||
if (!vregs[def].init(phi->id(), block, phi, def, /* isTemp */ false))
|
||||
return false;
|
||||
insData[phi].init(phi, block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1870,44 +1865,6 @@ LinearScanAllocator::canCoexist(LiveInterval *a, LiveInterval *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
LMoveGroup *
|
||||
LinearScanAllocator::getInputMoveGroup(CodePosition pos)
|
||||
{
|
||||
InstructionData *data = &insData[pos];
|
||||
JS_ASSERT(!data->ins()->isPhi());
|
||||
JS_ASSERT(!data->ins()->isLabel());;
|
||||
JS_ASSERT(inputOf(data->ins()) == pos);
|
||||
|
||||
if (data->inputMoves())
|
||||
return data->inputMoves();
|
||||
|
||||
LMoveGroup *moves = new LMoveGroup;
|
||||
data->setInputMoves(moves);
|
||||
data->block()->insertBefore(data->ins(), moves);
|
||||
|
||||
return moves;
|
||||
}
|
||||
|
||||
LMoveGroup *
|
||||
LinearScanAllocator::getMoveGroupAfter(CodePosition pos)
|
||||
{
|
||||
InstructionData *data = &insData[pos];
|
||||
JS_ASSERT(!data->ins()->isPhi());
|
||||
JS_ASSERT(outputOf(data->ins()) == pos);
|
||||
|
||||
if (data->movesAfter())
|
||||
return data->movesAfter();
|
||||
|
||||
LMoveGroup *moves = new LMoveGroup;
|
||||
data->setMovesAfter(moves);
|
||||
|
||||
if (data->ins()->isLabel())
|
||||
data->block()->insertAfter(data->block()->getEntryMoveGroup(), moves);
|
||||
else
|
||||
data->block()->insertAfter(data->ins(), moves);
|
||||
return moves;
|
||||
}
|
||||
|
||||
bool
|
||||
LinearScanAllocator::addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to)
|
||||
{
|
||||
|
@ -5,15 +5,10 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef js_ion_registerallocator_h__
|
||||
#define js_ion_registerallocator_h__
|
||||
#ifndef js_ion_linearscan_h__
|
||||
#define js_ion_linearscan_h__
|
||||
|
||||
#include "Ion.h"
|
||||
#include "MIR.h"
|
||||
#include "MIRGraph.h"
|
||||
#include "InlineList.h"
|
||||
#include "LIR.h"
|
||||
#include "Lowering.h"
|
||||
#include "RegisterAllocator.h"
|
||||
#include "BitSet.h"
|
||||
#include "StackSlotAllocator.h"
|
||||
|
||||
@ -24,109 +19,6 @@ namespace ion {
|
||||
|
||||
class VirtualRegister;
|
||||
|
||||
/*
|
||||
* Represents with better-than-instruction precision a position in the
|
||||
* instruction stream.
|
||||
*
|
||||
* An issue comes up when dealing with live intervals as to how to represent
|
||||
* information such as "this register is only needed for the input of
|
||||
* this instruction, it can be clobbered in the output". Just having ranges
|
||||
* of instruction IDs is insufficiently expressive to denote all possibilities.
|
||||
* This class solves this issue by associating an extra bit with the instruction
|
||||
* ID which indicates whether the position is the input half or output half of
|
||||
* an instruction.
|
||||
*/
|
||||
class CodePosition
|
||||
{
|
||||
private:
|
||||
CodePosition(const uint32 &bits)
|
||||
: bits_(bits)
|
||||
{ }
|
||||
|
||||
static const unsigned int INSTRUCTION_SHIFT = 1;
|
||||
static const unsigned int SUBPOSITION_MASK = 1;
|
||||
uint32 bits_;
|
||||
|
||||
public:
|
||||
static const CodePosition MAX;
|
||||
static const CodePosition MIN;
|
||||
|
||||
/*
|
||||
* This is the half of the instruction this code position represents, as
|
||||
* described in the huge comment above.
|
||||
*/
|
||||
enum SubPosition {
|
||||
INPUT,
|
||||
OUTPUT
|
||||
};
|
||||
|
||||
CodePosition() : bits_(0)
|
||||
{ }
|
||||
|
||||
CodePosition(uint32 instruction, SubPosition where) {
|
||||
JS_ASSERT(instruction < 0x80000000u);
|
||||
JS_ASSERT(((uint32)where & SUBPOSITION_MASK) == (uint32)where);
|
||||
bits_ = (instruction << INSTRUCTION_SHIFT) | (uint32)where;
|
||||
}
|
||||
|
||||
uint32 ins() const {
|
||||
return bits_ >> INSTRUCTION_SHIFT;
|
||||
}
|
||||
|
||||
uint32 pos() const {
|
||||
return bits_;
|
||||
}
|
||||
|
||||
SubPosition subpos() const {
|
||||
return (SubPosition)(bits_ & SUBPOSITION_MASK);
|
||||
}
|
||||
|
||||
bool operator <(const CodePosition &other) const {
|
||||
return bits_ < other.bits_;
|
||||
}
|
||||
|
||||
bool operator <=(const CodePosition &other) const {
|
||||
return bits_ <= other.bits_;
|
||||
}
|
||||
|
||||
bool operator !=(const CodePosition &other) const {
|
||||
return bits_ != other.bits_;
|
||||
}
|
||||
|
||||
bool operator ==(const CodePosition &other) const {
|
||||
return bits_ == other.bits_;
|
||||
}
|
||||
|
||||
bool operator >(const CodePosition &other) const {
|
||||
return bits_ > other.bits_;
|
||||
}
|
||||
|
||||
bool operator >=(const CodePosition &other) const {
|
||||
return bits_ >= other.bits_;
|
||||
}
|
||||
|
||||
CodePosition previous() const {
|
||||
JS_ASSERT(*this != MIN);
|
||||
return CodePosition(bits_ - 1);
|
||||
}
|
||||
CodePosition next() const {
|
||||
JS_ASSERT(*this != MAX);
|
||||
return CodePosition(bits_ + 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct UsePosition : public TempObject,
|
||||
public InlineForwardListNode<UsePosition>
|
||||
{
|
||||
LUse *use;
|
||||
CodePosition pos;
|
||||
|
||||
UsePosition(LUse *use, CodePosition pos) :
|
||||
use(use),
|
||||
pos(pos)
|
||||
{ }
|
||||
};
|
||||
|
||||
class Requirement
|
||||
{
|
||||
public:
|
||||
@ -198,6 +90,18 @@ class Requirement
|
||||
CodePosition position_;
|
||||
};
|
||||
|
||||
struct UsePosition : public TempObject,
|
||||
public InlineForwardListNode<UsePosition>
|
||||
{
|
||||
LUse *use;
|
||||
CodePosition pos;
|
||||
|
||||
UsePosition(LUse *use, CodePosition pos) :
|
||||
use(use),
|
||||
pos(pos)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef InlineForwardListIterator<UsePosition> UsePositionIterator;
|
||||
|
||||
/*
|
||||
@ -494,70 +398,6 @@ class VirtualRegisterMap
|
||||
}
|
||||
};
|
||||
|
||||
class InstructionData
|
||||
{
|
||||
LInstruction *ins_;
|
||||
LBlock *block_;
|
||||
LMoveGroup *inputMoves_;
|
||||
LMoveGroup *movesAfter_;
|
||||
|
||||
public:
|
||||
void init(LInstruction *ins, LBlock *block) {
|
||||
JS_ASSERT(!ins_);
|
||||
JS_ASSERT(!block_);
|
||||
ins_ = ins;
|
||||
block_ = block;
|
||||
}
|
||||
LInstruction *ins() const {
|
||||
return ins_;
|
||||
}
|
||||
LBlock *block() const {
|
||||
return block_;
|
||||
}
|
||||
void setInputMoves(LMoveGroup *moves) {
|
||||
inputMoves_ = moves;
|
||||
}
|
||||
LMoveGroup *inputMoves() const {
|
||||
return inputMoves_;
|
||||
}
|
||||
void setMovesAfter(LMoveGroup *moves) {
|
||||
movesAfter_ = moves;
|
||||
}
|
||||
LMoveGroup *movesAfter() const {
|
||||
return movesAfter_;
|
||||
}
|
||||
};
|
||||
|
||||
class InstructionDataMap
|
||||
{
|
||||
InstructionData *insData_;
|
||||
uint32 numIns_;
|
||||
|
||||
public:
|
||||
InstructionDataMap()
|
||||
: insData_(NULL),
|
||||
numIns_(0)
|
||||
{ }
|
||||
|
||||
bool init(MIRGenerator *gen, uint32 numInstructions) {
|
||||
insData_ = gen->allocate<InstructionData>(numInstructions);
|
||||
numIns_ = numInstructions;
|
||||
if (!insData_)
|
||||
return false;
|
||||
memset(insData_, 0, sizeof(InstructionData) * numInstructions);
|
||||
return true;
|
||||
}
|
||||
|
||||
InstructionData &operator[](const CodePosition &pos) {
|
||||
JS_ASSERT(pos.ins() < numIns_);
|
||||
return insData_[pos.ins()];
|
||||
}
|
||||
InstructionData &operator[](LInstruction *ins) {
|
||||
JS_ASSERT(ins->id() < numIns_);
|
||||
return insData_[ins->id()];
|
||||
}
|
||||
};
|
||||
|
||||
typedef HashMap<uint32,
|
||||
LInstruction *,
|
||||
DefaultHasher<uint32>,
|
||||
@ -571,7 +411,7 @@ typedef HashMap<uint32,
|
||||
typedef InlineList<LiveInterval>::iterator IntervalIterator;
|
||||
typedef InlineList<LiveInterval>::reverse_iterator IntervalReverseIterator;
|
||||
|
||||
class LinearScanAllocator
|
||||
class LinearScanAllocator : public RegisterAllocator
|
||||
{
|
||||
friend class C1Spewer;
|
||||
friend class JSONSpewer;
|
||||
@ -590,15 +430,9 @@ class LinearScanAllocator
|
||||
LiveInterval *dequeue();
|
||||
};
|
||||
|
||||
// Context
|
||||
MIRGenerator *mir;
|
||||
LIRGenerator *lir;
|
||||
LIRGraph &graph;
|
||||
|
||||
// Computed inforamtion
|
||||
BitSet **liveIn;
|
||||
VirtualRegisterMap vregs;
|
||||
InstructionDataMap insData;
|
||||
FixedArityList<LiveInterval *, AnyRegister::Total> fixedIntervals;
|
||||
|
||||
// Union of all ranges in fixedIntervals, used to quickly determine
|
||||
@ -612,9 +446,6 @@ class LinearScanAllocator
|
||||
SlotList finishedSlots_;
|
||||
SlotList finishedDoubleSlots_;
|
||||
|
||||
// Pool of all registers that should be considered allocateable
|
||||
RegisterSet allRegisters_;
|
||||
|
||||
// Run-time state
|
||||
UnhandledQueue unhandled;
|
||||
InlineList<LiveInterval> active;
|
||||
@ -643,8 +474,6 @@ class LinearScanAllocator
|
||||
AnyRegister::Code findBestFreeRegister(CodePosition *freeUntil);
|
||||
AnyRegister::Code findBestBlockedRegister(CodePosition *nextUsed);
|
||||
bool canCoexist(LiveInterval *a, LiveInterval *b);
|
||||
LMoveGroup *getInputMoveGroup(CodePosition pos);
|
||||
LMoveGroup *getMoveGroupAfter(CodePosition pos);
|
||||
bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to);
|
||||
bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to);
|
||||
bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to);
|
||||
@ -669,31 +498,14 @@ class LinearScanAllocator
|
||||
inline void validateVirtualRegisters() { };
|
||||
#endif
|
||||
|
||||
CodePosition outputOf(uint32 pos) {
|
||||
return CodePosition(pos, CodePosition::OUTPUT);
|
||||
}
|
||||
CodePosition outputOf(LInstruction *ins) {
|
||||
return CodePosition(ins->id(), CodePosition::OUTPUT);
|
||||
}
|
||||
CodePosition inputOf(uint32 pos) {
|
||||
return CodePosition(pos, CodePosition::INPUT);
|
||||
}
|
||||
CodePosition inputOf(LInstruction *ins) {
|
||||
return CodePosition(ins->id(), CodePosition::INPUT);
|
||||
}
|
||||
#ifdef JS_NUNBOX32
|
||||
VirtualRegister *otherHalfOfNunbox(VirtualRegister *vreg);
|
||||
#endif
|
||||
|
||||
public:
|
||||
LinearScanAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
|
||||
: mir(mir),
|
||||
lir(lir),
|
||||
graph(graph),
|
||||
allRegisters_(RegisterSet::All())
|
||||
: RegisterAllocator(mir, lir, graph)
|
||||
{
|
||||
if (FramePointer != InvalidReg && lir->mir()->instrumentedProfiling())
|
||||
allRegisters_.take(AnyRegister(FramePointer));
|
||||
}
|
||||
|
||||
bool go();
|
||||
|
464
js/src/ion/RegisterAllocator.cpp
Normal file
464
js/src/ion/RegisterAllocator.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RegisterAllocator.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
bool
|
||||
AllocationIntegrityState::record()
|
||||
{
|
||||
// Ignore repeated record() calls.
|
||||
if (!instructions.empty())
|
||||
return true;
|
||||
|
||||
if (!instructions.reserve(graph.numInstructions()))
|
||||
return false;
|
||||
for (size_t i = 0; i < graph.numInstructions(); i++)
|
||||
instructions.infallibleAppend(InstructionInfo());
|
||||
|
||||
if (!virtualRegisters.reserve(graph.numVirtualRegisters()))
|
||||
return false;
|
||||
for (size_t i = 0; i < graph.numVirtualRegisters(); i++)
|
||||
virtualRegisters.infallibleAppend(NULL);
|
||||
|
||||
if (!blocks.reserve(graph.numBlocks()))
|
||||
return false;
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
blocks.infallibleAppend(BlockInfo());
|
||||
LBlock *block = graph.getBlock(i);
|
||||
JS_ASSERT(block->mir()->id() == i);
|
||||
|
||||
BlockInfo &blockInfo = blocks[i];
|
||||
if (!blockInfo.phis.reserve(block->numPhis()))
|
||||
return false;
|
||||
|
||||
for (size_t j = 0; j < block->numPhis(); j++) {
|
||||
blockInfo.phis.infallibleAppend(InstructionInfo());
|
||||
InstructionInfo &info = blockInfo.phis[j];
|
||||
LPhi *phi = block->getPhi(j);
|
||||
for (size_t k = 0; k < phi->numDefs(); k++) {
|
||||
uint32 vreg = phi->getDef(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = phi->getDef(k);
|
||||
if (!info.outputs.append(vreg))
|
||||
return false;
|
||||
}
|
||||
for (size_t k = 0; k < phi->numOperands(); k++) {
|
||||
if (!info.inputs.append(phi->getOperand(k)->toUse()->virtualRegister()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
InstructionInfo &info = instructions[ins->id()];
|
||||
|
||||
for (size_t k = 0; k < ins->numDefs(); k++) {
|
||||
uint32 vreg = ins->getDef(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = ins->getDef(k);
|
||||
if (!info.outputs.append(vreg))
|
||||
return false;
|
||||
}
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
if (!info.inputs.append(alloc->isUse() ? alloc->toUse()->virtualRegister() : UINT32_MAX))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return seen.init();
|
||||
}
|
||||
|
||||
bool
|
||||
AllocationIntegrityState::check(bool populateSafepoints)
|
||||
{
|
||||
JS_ASSERT(!instructions.empty());
|
||||
|
||||
#ifdef DEBUG
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
LBlock *block = graph.getBlock(blockIndex);
|
||||
|
||||
// Check that all instruction inputs and outputs have been assigned an allocation.
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next())
|
||||
JS_ASSERT(!alloc->isUse());
|
||||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
JS_ASSERT_IF(def->policy() != LDefinition::PASSTHROUGH, !def->output()->isUse());
|
||||
|
||||
if (def->output()->isRegister()) {
|
||||
// The live regs for an instruction's safepoint should
|
||||
// exclude the instruction's definitions.
|
||||
LSafepoint *safepoint = ins->safepoint();
|
||||
JS_ASSERT_IF(safepoint, !safepoint->liveRegs().has(def->output()->toRegister()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check that the register assignment and move groups preserve the original
|
||||
// semantics of the virtual registers. Each virtual register has a single
|
||||
// write (owing to the SSA representation), but the allocation may move the
|
||||
// written value around between registers and memory locations along
|
||||
// different paths through the script.
|
||||
//
|
||||
// For each use of an allocation, follow the physical value which is read
|
||||
// backward through the script, along all paths to the value's virtual
|
||||
// register's definition.
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
LBlock *block = graph.getBlock(blockIndex);
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
const InstructionInfo &info = instructions[ins->id()];
|
||||
|
||||
size_t inputIndex = 0;
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
uint32 vreg = info.inputs[inputIndex++];
|
||||
if (vreg == UINT32_MAX)
|
||||
continue;
|
||||
|
||||
// Start checking at the previous instruction, in case this
|
||||
// instruction reuses its input register for an output.
|
||||
LInstructionReverseIterator riter = block->rbegin(ins);
|
||||
riter++;
|
||||
checkIntegrity(block, *riter, vreg, **alloc, populateSafepoints);
|
||||
|
||||
while (!worklist.empty()) {
|
||||
IntegrityItem item = worklist.back();
|
||||
worklist.popBack();
|
||||
checkIntegrity(item.block, *item.block->rbegin(), item.vreg, item.alloc, populateSafepoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IonSpewEnabled(IonSpew_RegAlloc))
|
||||
dump();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AllocationIntegrityState::checkIntegrity(LBlock *block, LInstruction *ins,
|
||||
uint32 vreg, LAllocation alloc, bool populateSafepoints)
|
||||
{
|
||||
for (LInstructionReverseIterator iter(block->rbegin(ins)); iter != block->rend(); iter++) {
|
||||
ins = *iter;
|
||||
|
||||
// Follow values through assignments in move groups. All assignments in
|
||||
// a move group are considered to happen simultaneously, so stop after
|
||||
// the first matching move is found.
|
||||
if (ins->isMoveGroup()) {
|
||||
LMoveGroup *group = ins->toMoveGroup();
|
||||
for (int i = group->numMoves() - 1; i >= 0; i--) {
|
||||
if (*group->getMove(i).to() == alloc) {
|
||||
alloc = *group->getMove(i).from();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InstructionInfo &info = instructions[ins->id()];
|
||||
|
||||
// Make sure the physical location being tracked is not clobbered by
|
||||
// another instruction, and that if the originating vreg definition is
|
||||
// found that it is writing to the tracked location.
|
||||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
if (def->policy() == LDefinition::PASSTHROUGH)
|
||||
continue;
|
||||
if (info.outputs[i] == vreg) {
|
||||
check(*def->output() == alloc,
|
||||
"Found vreg definition, but tracked value does not match");
|
||||
|
||||
// Found the original definition, done scanning.
|
||||
return true;
|
||||
} else {
|
||||
check(*def->output() != alloc,
|
||||
"Tracked value clobbered by intermediate definition");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
LDefinition *temp = ins->getTemp(i);
|
||||
if (!temp->isBogusTemp()) {
|
||||
check(*temp->output() != alloc,
|
||||
"Tracked value clobbered by intermediate temporary");
|
||||
}
|
||||
}
|
||||
|
||||
LSafepoint *safepoint = ins->safepoint();
|
||||
if (!safepoint)
|
||||
continue;
|
||||
|
||||
if (alloc.isRegister()) {
|
||||
AnyRegister reg = alloc.toRegister();
|
||||
if (populateSafepoints)
|
||||
safepoint->addLiveRegister(reg);
|
||||
else
|
||||
check(safepoint->liveRegs().has(reg), "Register not marked in safepoint");
|
||||
}
|
||||
|
||||
LDefinition::Type type = virtualRegisters[vreg]
|
||||
? virtualRegisters[vreg]->type()
|
||||
: LDefinition::GENERAL;
|
||||
|
||||
switch (type) {
|
||||
case LDefinition::OBJECT:
|
||||
if (populateSafepoints)
|
||||
safepoint->addGcPointer(alloc);
|
||||
else
|
||||
check(safepoint->hasGcPointer(alloc), "GC register not marked in safepoint");
|
||||
break;
|
||||
#ifdef JS_NUNBOX32
|
||||
// If a vreg for a value's components are copied in multiple places
|
||||
// then the safepoint information may be incomplete and not reflect
|
||||
// all copies. See SafepointWriter::writeNunboxParts.
|
||||
case LDefinition::TYPE:
|
||||
if (populateSafepoints)
|
||||
safepoint->addNunboxType(vreg, alloc);
|
||||
break;
|
||||
case LDefinition::PAYLOAD:
|
||||
if (populateSafepoints)
|
||||
safepoint->addNunboxPayload(vreg, alloc);
|
||||
break;
|
||||
#else
|
||||
case LDefinition::BOX:
|
||||
if (populateSafepoints)
|
||||
safepoint->addBoxedValue(alloc);
|
||||
else
|
||||
check(safepoint->hasBoxedValue(alloc), "Boxed value not marked in safepoint");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Phis are effectless, but change the vreg we are tracking. Check if there
|
||||
// is one which produced this vreg. We need to follow back through the phi
|
||||
// inputs as it is not guaranteed the register allocator filled in physical
|
||||
// allocations for the inputs and outputs of the phis.
|
||||
for (size_t i = 0; i < block->numPhis(); i++) {
|
||||
InstructionInfo &info = blocks[block->mir()->id()].phis[i];
|
||||
LPhi *phi = block->getPhi(i);
|
||||
if (info.outputs[0] == vreg) {
|
||||
for (size_t j = 0; j < phi->numOperands(); j++) {
|
||||
uint32 newvreg = info.inputs[j];
|
||||
LBlock *predecessor = graph.getBlock(block->mir()->getPredecessor(j)->id());
|
||||
if (!addPredecessor(predecessor, newvreg, alloc))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No phi which defined the vreg we are tracking, follow back through all
|
||||
// predecessors with the existing vreg.
|
||||
for (size_t i = 0; i < block->mir()->numPredecessors(); i++) {
|
||||
LBlock *predecessor = graph.getBlock(block->mir()->getPredecessor(i)->id());
|
||||
if (!addPredecessor(predecessor, vreg, alloc))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AllocationIntegrityState::addPredecessor(LBlock *block, uint32 vreg, LAllocation alloc)
|
||||
{
|
||||
// There is no need to reanalyze if we have already seen this predecessor.
|
||||
// We share the seen allocations across analysis of each use, as there will
|
||||
// likely be common ground between different uses of the same vreg.
|
||||
IntegrityItem item;
|
||||
item.block = block;
|
||||
item.vreg = vreg;
|
||||
item.alloc = alloc;
|
||||
item.index = seen.count();
|
||||
|
||||
IntegrityItemSet::AddPtr p = seen.lookupForAdd(item);
|
||||
if (p)
|
||||
return true;
|
||||
if (!seen.add(p, item))
|
||||
return false;
|
||||
|
||||
return worklist.append(item);
|
||||
}
|
||||
|
||||
void
|
||||
AllocationIntegrityState::check(bool cond, const char *msg)
|
||||
{
|
||||
if (!cond) {
|
||||
if (IonSpewEnabled(IonSpew_RegAlloc))
|
||||
dump();
|
||||
printf("%s\n", msg);
|
||||
JS_NOT_REACHED("Regalloc integrity failure");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllocationIntegrityState::dump()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Register Allocation:\n");
|
||||
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
LBlock *block = graph.getBlock(blockIndex);
|
||||
MBasicBlock *mir = block->mir();
|
||||
|
||||
printf("\nBlock %lu", blockIndex);
|
||||
for (size_t i = 0; i < mir->numSuccessors(); i++)
|
||||
printf(" [successor %u]", mir->getSuccessor(i)->id());
|
||||
printf("\n");
|
||||
|
||||
for (size_t i = 0; i < block->numPhis(); i++) {
|
||||
InstructionInfo &info = blocks[blockIndex].phis[i];
|
||||
LPhi *phi = block->getPhi(i);
|
||||
|
||||
printf("Phi v%u <-", info.outputs[0]);
|
||||
for (size_t j = 0; j < phi->numOperands(); j++)
|
||||
printf(" v%u", info.inputs[j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
InstructionInfo &info = instructions[ins->id()];
|
||||
|
||||
printf("[%s]", ins->opName());
|
||||
|
||||
if (ins->isMoveGroup()) {
|
||||
LMoveGroup *group = ins->toMoveGroup();
|
||||
for (int i = group->numMoves() - 1; i >= 0; i--) {
|
||||
printf(" [");
|
||||
LAllocation::PrintAllocation(stdout, group->getMove(i).from());
|
||||
printf(" -> ");
|
||||
LAllocation::PrintAllocation(stdout, group->getMove(i).to());
|
||||
printf("]");
|
||||
}
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
LDefinition *temp = ins->getTemp(i);
|
||||
if (!temp->isBogusTemp()) {
|
||||
printf(" [temp ");
|
||||
LAllocation::PrintAllocation(stdout, temp->output());
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
printf(" [def v%u ", info.outputs[i]);
|
||||
LAllocation::PrintAllocation(stdout, def->output());
|
||||
printf("]");
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
uint32 vreg = info.inputs[index++];
|
||||
if (vreg == UINT32_MAX)
|
||||
continue;
|
||||
printf(" [use v%u ", vreg);
|
||||
LAllocation::PrintAllocation(stdout, *alloc);
|
||||
printf("]");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nIntermediate Allocations:\n\n");
|
||||
|
||||
// Print discovered allocations at the ends of blocks, in the order they
|
||||
// were discovered.
|
||||
|
||||
Vector<IntegrityItem, 20, SystemAllocPolicy> seenOrdered;
|
||||
for (size_t i = 0; i < seen.count(); i++)
|
||||
seenOrdered.append(IntegrityItem());
|
||||
|
||||
for (IntegrityItemSet::Enum iter(seen); !iter.empty(); iter.popFront()) {
|
||||
IntegrityItem item = iter.front();
|
||||
seenOrdered[item.index] = item;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < seenOrdered.length(); i++) {
|
||||
IntegrityItem item = seenOrdered[i];
|
||||
printf("block %u reg v%u alloc ", item.block->mir()->id(), item.vreg);
|
||||
LAllocation::PrintAllocation(stdout, &item.alloc);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
const CodePosition CodePosition::MAX(UINT_MAX);
|
||||
const CodePosition CodePosition::MIN(0);
|
||||
|
||||
bool
|
||||
RegisterAllocator::init()
|
||||
{
|
||||
if (!insData.init(lir->mir(), graph.numInstructions()))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
LBlock *block = graph.getBlock(i);
|
||||
for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++)
|
||||
insData[*ins].init(*ins, block);
|
||||
for (size_t j = 0; j < block->numPhis(); j++) {
|
||||
LPhi *phi = block->getPhi(j);
|
||||
insData[phi].init(phi, block);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LMoveGroup *
|
||||
RegisterAllocator::getInputMoveGroup(uint32 ins)
|
||||
{
|
||||
InstructionData *data = &insData[ins];
|
||||
JS_ASSERT(!data->ins()->isPhi());
|
||||
JS_ASSERT(!data->ins()->isLabel());
|
||||
|
||||
if (data->inputMoves())
|
||||
return data->inputMoves();
|
||||
|
||||
LMoveGroup *moves = new LMoveGroup;
|
||||
data->setInputMoves(moves);
|
||||
data->block()->insertBefore(data->ins(), moves);
|
||||
|
||||
return moves;
|
||||
}
|
||||
|
||||
LMoveGroup *
|
||||
RegisterAllocator::getMoveGroupAfter(uint32 ins)
|
||||
{
|
||||
InstructionData *data = &insData[ins];
|
||||
JS_ASSERT(!data->ins()->isPhi());
|
||||
|
||||
if (data->movesAfter())
|
||||
return data->movesAfter();
|
||||
|
||||
LMoveGroup *moves = new LMoveGroup;
|
||||
data->setMovesAfter(moves);
|
||||
|
||||
if (data->ins()->isLabel())
|
||||
data->block()->insertAfter(data->block()->getEntryMoveGroup(), moves);
|
||||
else
|
||||
data->block()->insertAfter(data->ins(), moves);
|
||||
return moves;
|
||||
}
|
336
js/src/ion/RegisterAllocator.h
Normal file
336
js/src/ion/RegisterAllocator.h
Normal file
@ -0,0 +1,336 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef js_ion_registerallocator_h__
|
||||
#define js_ion_registerallocator_h__
|
||||
|
||||
#include "Ion.h"
|
||||
#include "MIR.h"
|
||||
#include "MIRGraph.h"
|
||||
#include "InlineList.h"
|
||||
#include "LIR.h"
|
||||
#include "Lowering.h"
|
||||
|
||||
// Generic structures and functions for use by register allocators.
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
// Structure for running a liveness analysis on a finished register allocation.
|
||||
// This analysis can be used for two purposes:
|
||||
//
|
||||
// - Check the integrity of the allocation, i.e. that the reads and writes of
|
||||
// physical values preserve the semantics of the original virtual registers.
|
||||
//
|
||||
// - Populate safepoints with live registers, GC thing and value data, to
|
||||
// streamline the process of prototyping new allocators.
|
||||
struct AllocationIntegrityState
|
||||
{
|
||||
AllocationIntegrityState(LIRGraph &graph)
|
||||
: graph(graph)
|
||||
{}
|
||||
|
||||
// Record all virtual registers in the graph. This must be called before
|
||||
// register allocation, to pick up the original LUses.
|
||||
bool record();
|
||||
|
||||
// Perform the liveness analysis on the graph, and assert on an invalid
|
||||
// allocation. This must be called after register allocation, to pick up
|
||||
// all assigned physical values. If populateSafepoints is specified,
|
||||
// safepoints will be filled in with liveness information.
|
||||
bool check(bool populateSafepoints);
|
||||
|
||||
private:
|
||||
|
||||
LIRGraph &graph;
|
||||
|
||||
// For all instructions and phis in the graph, keep track of the virtual
|
||||
// registers for all inputs and outputs of the nodes. These are overwritten
|
||||
// in place during register allocation. This information is kept on the
|
||||
// side rather than in the instructions and phis themselves to avoid
|
||||
// debug-builds-only bloat in the size of the involved structures.
|
||||
|
||||
struct InstructionInfo {
|
||||
Vector<uint32, 2, SystemAllocPolicy> inputs;
|
||||
Vector<uint32, 1, SystemAllocPolicy> outputs;
|
||||
InstructionInfo() {}
|
||||
InstructionInfo(const InstructionInfo &o) {
|
||||
for (size_t i = 0; i < o.inputs.length(); i++)
|
||||
inputs.append(o.inputs[i]);
|
||||
for (size_t i = 0; i < o.outputs.length(); i++)
|
||||
outputs.append(o.outputs[i]);
|
||||
}
|
||||
};
|
||||
Vector<InstructionInfo, 0, SystemAllocPolicy> instructions;
|
||||
|
||||
struct BlockInfo {
|
||||
Vector<InstructionInfo, 5, SystemAllocPolicy> phis;
|
||||
BlockInfo() {}
|
||||
BlockInfo(const BlockInfo &o) {
|
||||
for (size_t i = 0; i < o.phis.length(); i++)
|
||||
phis.append(o.phis[i]);
|
||||
}
|
||||
};
|
||||
Vector<BlockInfo, 0, SystemAllocPolicy> blocks;
|
||||
|
||||
Vector<LDefinition*, 20, SystemAllocPolicy> virtualRegisters;
|
||||
|
||||
// Describes a correspondence that should hold at the end of a block.
|
||||
// The value which was written to vreg in the original LIR should be
|
||||
// physically stored in alloc after the register allocation.
|
||||
struct IntegrityItem
|
||||
{
|
||||
LBlock *block;
|
||||
uint32 vreg;
|
||||
LAllocation alloc;
|
||||
|
||||
// Order of insertion into seen, for sorting.
|
||||
uint32 index;
|
||||
|
||||
typedef IntegrityItem Lookup;
|
||||
static HashNumber hash(const IntegrityItem &item) {
|
||||
HashNumber hash = item.alloc.hash();
|
||||
hash = JS_ROTATE_LEFT32(hash, 4) ^ item.vreg;
|
||||
hash = JS_ROTATE_LEFT32(hash, 4) ^ HashNumber(item.block->mir()->id());
|
||||
return hash;
|
||||
}
|
||||
static bool match(const IntegrityItem &one, const IntegrityItem &two) {
|
||||
return one.block == two.block
|
||||
&& one.vreg == two.vreg
|
||||
&& one.alloc == two.alloc;
|
||||
}
|
||||
};
|
||||
|
||||
// Items still to be processed.
|
||||
Vector<IntegrityItem, 10, SystemAllocPolicy> worklist;
|
||||
|
||||
// Set of all items that have already been processed.
|
||||
typedef HashSet<IntegrityItem, IntegrityItem, SystemAllocPolicy> IntegrityItemSet;
|
||||
IntegrityItemSet seen;
|
||||
|
||||
bool checkIntegrity(LBlock *block, LInstruction *ins, uint32 vreg, LAllocation alloc,
|
||||
bool populateSafepoints);
|
||||
bool addPredecessor(LBlock *block, uint32 vreg, LAllocation alloc);
|
||||
|
||||
void check(bool cond, const char *msg);
|
||||
void dump();
|
||||
};
|
||||
|
||||
// Represents with better-than-instruction precision a position in the
|
||||
// instruction stream.
|
||||
//
|
||||
// An issue comes up when performing register allocation as to how to represent
|
||||
// information such as "this register is only needed for the input of
|
||||
// this instruction, it can be clobbered in the output". Just having ranges
|
||||
// of instruction IDs is insufficiently expressive to denote all possibilities.
|
||||
// This class solves this issue by associating an extra bit with the instruction
|
||||
// ID which indicates whether the position is the input half or output half of
|
||||
// an instruction.
|
||||
class CodePosition
|
||||
{
|
||||
private:
|
||||
CodePosition(const uint32 &bits)
|
||||
: bits_(bits)
|
||||
{ }
|
||||
|
||||
static const unsigned int INSTRUCTION_SHIFT = 1;
|
||||
static const unsigned int SUBPOSITION_MASK = 1;
|
||||
uint32 bits_;
|
||||
|
||||
public:
|
||||
static const CodePosition MAX;
|
||||
static const CodePosition MIN;
|
||||
|
||||
// This is the half of the instruction this code position represents, as
|
||||
// described in the huge comment above.
|
||||
enum SubPosition {
|
||||
INPUT,
|
||||
OUTPUT
|
||||
};
|
||||
|
||||
CodePosition() : bits_(0)
|
||||
{ }
|
||||
|
||||
CodePosition(uint32 instruction, SubPosition where) {
|
||||
JS_ASSERT(instruction < 0x80000000u);
|
||||
JS_ASSERT(((uint32)where & SUBPOSITION_MASK) == (uint32)where);
|
||||
bits_ = (instruction << INSTRUCTION_SHIFT) | (uint32)where;
|
||||
}
|
||||
|
||||
uint32 ins() const {
|
||||
return bits_ >> INSTRUCTION_SHIFT;
|
||||
}
|
||||
|
||||
uint32 pos() const {
|
||||
return bits_;
|
||||
}
|
||||
|
||||
SubPosition subpos() const {
|
||||
return (SubPosition)(bits_ & SUBPOSITION_MASK);
|
||||
}
|
||||
|
||||
bool operator <(const CodePosition &other) const {
|
||||
return bits_ < other.bits_;
|
||||
}
|
||||
|
||||
bool operator <=(const CodePosition &other) const {
|
||||
return bits_ <= other.bits_;
|
||||
}
|
||||
|
||||
bool operator !=(const CodePosition &other) const {
|
||||
return bits_ != other.bits_;
|
||||
}
|
||||
|
||||
bool operator ==(const CodePosition &other) const {
|
||||
return bits_ == other.bits_;
|
||||
}
|
||||
|
||||
bool operator >(const CodePosition &other) const {
|
||||
return bits_ > other.bits_;
|
||||
}
|
||||
|
||||
bool operator >=(const CodePosition &other) const {
|
||||
return bits_ >= other.bits_;
|
||||
}
|
||||
|
||||
CodePosition previous() const {
|
||||
JS_ASSERT(*this != MIN);
|
||||
return CodePosition(bits_ - 1);
|
||||
}
|
||||
CodePosition next() const {
|
||||
JS_ASSERT(*this != MAX);
|
||||
return CodePosition(bits_ + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Structure to track moves inserted before or after an instruction.
|
||||
class InstructionData
|
||||
{
|
||||
LInstruction *ins_;
|
||||
LBlock *block_;
|
||||
LMoveGroup *inputMoves_;
|
||||
LMoveGroup *movesAfter_;
|
||||
|
||||
public:
|
||||
void init(LInstruction *ins, LBlock *block) {
|
||||
JS_ASSERT(!ins_);
|
||||
JS_ASSERT(!block_);
|
||||
ins_ = ins;
|
||||
block_ = block;
|
||||
}
|
||||
LInstruction *ins() const {
|
||||
return ins_;
|
||||
}
|
||||
LBlock *block() const {
|
||||
return block_;
|
||||
}
|
||||
void setInputMoves(LMoveGroup *moves) {
|
||||
inputMoves_ = moves;
|
||||
}
|
||||
LMoveGroup *inputMoves() const {
|
||||
return inputMoves_;
|
||||
}
|
||||
void setMovesAfter(LMoveGroup *moves) {
|
||||
movesAfter_ = moves;
|
||||
}
|
||||
LMoveGroup *movesAfter() const {
|
||||
return movesAfter_;
|
||||
}
|
||||
};
|
||||
|
||||
// Structure to track all moves inserted next to instructions in a graph.
|
||||
class InstructionDataMap
|
||||
{
|
||||
InstructionData *insData_;
|
||||
uint32 numIns_;
|
||||
|
||||
public:
|
||||
InstructionDataMap()
|
||||
: insData_(NULL),
|
||||
numIns_(0)
|
||||
{ }
|
||||
|
||||
bool init(MIRGenerator *gen, uint32 numInstructions) {
|
||||
insData_ = gen->allocate<InstructionData>(numInstructions);
|
||||
numIns_ = numInstructions;
|
||||
if (!insData_)
|
||||
return false;
|
||||
memset(insData_, 0, sizeof(InstructionData) * numInstructions);
|
||||
return true;
|
||||
}
|
||||
|
||||
InstructionData &operator[](const CodePosition &pos) {
|
||||
JS_ASSERT(pos.ins() < numIns_);
|
||||
return insData_[pos.ins()];
|
||||
}
|
||||
InstructionData &operator[](LInstruction *ins) {
|
||||
JS_ASSERT(ins->id() < numIns_);
|
||||
return insData_[ins->id()];
|
||||
}
|
||||
InstructionData &operator[](uint32 ins) {
|
||||
JS_ASSERT(ins < numIns_);
|
||||
return insData_[ins];
|
||||
}
|
||||
};
|
||||
|
||||
// Common superclass for register allocators.
|
||||
class RegisterAllocator
|
||||
{
|
||||
protected:
|
||||
// Context
|
||||
MIRGenerator *mir;
|
||||
LIRGenerator *lir;
|
||||
LIRGraph &graph;
|
||||
|
||||
// Pool of all registers that should be considered allocateable
|
||||
RegisterSet allRegisters_;
|
||||
|
||||
// Computed data
|
||||
InstructionDataMap insData;
|
||||
|
||||
public:
|
||||
RegisterAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
|
||||
: mir(mir),
|
||||
lir(lir),
|
||||
graph(graph),
|
||||
allRegisters_(RegisterSet::All())
|
||||
{
|
||||
if (FramePointer != InvalidReg && lir->mir()->instrumentedProfiling())
|
||||
allRegisters_.take(AnyRegister(FramePointer));
|
||||
}
|
||||
|
||||
protected:
|
||||
bool init();
|
||||
|
||||
CodePosition outputOf(uint32 pos) {
|
||||
return CodePosition(pos, CodePosition::OUTPUT);
|
||||
}
|
||||
CodePosition outputOf(LInstruction *ins) {
|
||||
return CodePosition(ins->id(), CodePosition::OUTPUT);
|
||||
}
|
||||
CodePosition inputOf(uint32 pos) {
|
||||
return CodePosition(pos, CodePosition::INPUT);
|
||||
}
|
||||
CodePosition inputOf(LInstruction *ins) {
|
||||
return CodePosition(ins->id(), CodePosition::INPUT);
|
||||
}
|
||||
|
||||
LMoveGroup *getInputMoveGroup(uint32 ins);
|
||||
LMoveGroup *getMoveGroupAfter(uint32 ins);
|
||||
|
||||
LMoveGroup *getInputMoveGroup(CodePosition pos) {
|
||||
return getInputMoveGroup(pos.ins());
|
||||
}
|
||||
LMoveGroup *getMoveGroupAfter(CodePosition pos) {
|
||||
return getMoveGroupAfter(pos.ins());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
#endif
|
@ -226,11 +226,26 @@ SafepointWriter::writeNunboxParts(LSafepoint *safepoint)
|
||||
}
|
||||
# endif
|
||||
|
||||
stream_.writeUnsigned(entries.length());
|
||||
// Safepoints are permitted to have partially filled in entries for nunboxes,
|
||||
// provided that only the type is live and not the payload. Omit these from
|
||||
// the written safepoint.
|
||||
//
|
||||
// Note that partial entries typically appear when one part of a nunbox is
|
||||
// stored in multiple places, in which case we will end up with incomplete
|
||||
// information about all the places the value is stored. This will need to
|
||||
// be fixed when the GC is permitted to move structures.
|
||||
uint32 partials = safepoint->partialNunboxes();
|
||||
|
||||
stream_.writeUnsigned(entries.length() - partials);
|
||||
|
||||
for (size_t i = 0; i < entries.length(); i++) {
|
||||
SafepointNunboxEntry &entry = entries[i];
|
||||
|
||||
if (entry.type.isUse() || entry.payload.isUse()) {
|
||||
partials--;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16 header = 0;
|
||||
|
||||
header |= (AllocationToPartKind(entry.type) << TYPE_KIND_SHIFT);
|
||||
@ -256,6 +271,8 @@ SafepointWriter::writeNunboxParts(LSafepoint *safepoint)
|
||||
if (payloadExtra)
|
||||
stream_.writeUnsigned(payloadVal);
|
||||
}
|
||||
|
||||
JS_ASSERT(partials == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
419
js/src/ion/StupidAllocator.cpp
Normal file
419
js/src/ion/StupidAllocator.cpp
Normal file
@ -0,0 +1,419 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "StupidAllocator.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
static inline uint32
|
||||
DefaultStackSlot(uint32 vreg)
|
||||
{
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
return vreg * 2 + 2;
|
||||
#else
|
||||
return vreg + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LAllocation *
|
||||
StupidAllocator::stackLocation(uint32 vreg)
|
||||
{
|
||||
LDefinition *def = virtualRegisters[vreg];
|
||||
if (def->policy() == LDefinition::PRESET && def->output()->kind() == LAllocation::ARGUMENT)
|
||||
return def->output();
|
||||
|
||||
return new LStackSlot(DefaultStackSlot(vreg), def->type() == LDefinition::DOUBLE);
|
||||
}
|
||||
|
||||
StupidAllocator::RegisterIndex
|
||||
StupidAllocator::registerIndex(AnyRegister reg)
|
||||
{
|
||||
for (size_t i = 0; i < registerCount; i++) {
|
||||
if (reg == registers[i].reg)
|
||||
return i;
|
||||
}
|
||||
JS_NOT_REACHED("Bad register");
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
bool
|
||||
StupidAllocator::init()
|
||||
{
|
||||
if (!RegisterAllocator::init())
|
||||
return false;
|
||||
|
||||
if (!virtualRegisters.reserve(graph.numVirtualRegisters()))
|
||||
return false;
|
||||
for (size_t i = 0; i < graph.numVirtualRegisters(); i++)
|
||||
virtualRegisters.infallibleAppend(NULL);
|
||||
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
LBlock *block = graph.getBlock(i);
|
||||
for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
|
||||
for (size_t j = 0; j < ins->numDefs(); j++) {
|
||||
LDefinition *def = ins->getDef(j);
|
||||
if (def->policy() != LDefinition::PASSTHROUGH)
|
||||
virtualRegisters[def->virtualRegister()] = def;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < ins->numTemps(); j++) {
|
||||
LDefinition *def = ins->getTemp(j);
|
||||
if (def->isBogusTemp())
|
||||
continue;
|
||||
virtualRegisters[def->virtualRegister()] = def;
|
||||
}
|
||||
}
|
||||
for (size_t j = 0; j < block->numPhis(); j++) {
|
||||
LPhi *phi = block->getPhi(j);
|
||||
LDefinition *def = phi->getDef(0);
|
||||
uint32 vreg = def->virtualRegister();
|
||||
|
||||
virtualRegisters[vreg] = def;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign physical registers to the tracked allocation.
|
||||
{
|
||||
registerCount = 0;
|
||||
RegisterSet remainingRegisters(allRegisters_);
|
||||
while (!remainingRegisters.empty(/* float = */ false))
|
||||
registers[registerCount++].reg = AnyRegister(remainingRegisters.takeGeneral());
|
||||
while (!remainingRegisters.empty(/* float = */ true))
|
||||
registers[registerCount++].reg = AnyRegister(remainingRegisters.takeFloat());
|
||||
JS_ASSERT(registerCount <= MAX_REGISTERS);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
AllocationRequiresRegister(const LAllocation *alloc, AnyRegister reg)
|
||||
{
|
||||
if (alloc->isRegister() && alloc->toRegister() == reg)
|
||||
return true;
|
||||
if (alloc->isUse()) {
|
||||
const LUse *use = alloc->toUse();
|
||||
if (use->policy() == LUse::FIXED && AnyRegister::FromCode(use->registerCode()) == reg)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
RegisterIsReserved(LInstruction *ins, AnyRegister reg)
|
||||
{
|
||||
// Whether reg is already reserved for an input or output of ins.
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
if (AllocationRequiresRegister(*alloc, reg))
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
if (AllocationRequiresRegister(ins->getTemp(i)->output(), reg))
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
if (AllocationRequiresRegister(ins->getDef(i)->output(), reg))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AnyRegister
|
||||
StupidAllocator::ensureHasRegister(LInstruction *ins, uint32 vreg)
|
||||
{
|
||||
// Ensure that vreg is held in a register before ins.
|
||||
|
||||
// Check if the virtual register is already held in a physical register.
|
||||
RegisterIndex existing = findExistingRegister(vreg);
|
||||
if (existing != UINT32_MAX) {
|
||||
if (RegisterIsReserved(ins, registers[existing].reg)) {
|
||||
evictRegister(ins, existing);
|
||||
} else {
|
||||
registers[existing].age = ins->id();
|
||||
return registers[existing].reg;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterIndex best = allocateRegister(ins, vreg);
|
||||
loadRegister(ins, vreg, best);
|
||||
|
||||
return registers[best].reg;
|
||||
}
|
||||
|
||||
StupidAllocator::RegisterIndex
|
||||
StupidAllocator::allocateRegister(LInstruction *ins, uint32 vreg)
|
||||
{
|
||||
// Pick a register for vreg, evicting an existing register if necessary.
|
||||
// Spill code will be placed before ins, and no existing allocated input
|
||||
// for ins will be touched.
|
||||
JS_ASSERT(ins);
|
||||
|
||||
LDefinition *def = virtualRegisters[vreg];
|
||||
JS_ASSERT(def);
|
||||
|
||||
RegisterIndex best = UINT32_MAX;
|
||||
|
||||
for (size_t i = 0; i < registerCount; i++) {
|
||||
AnyRegister reg = registers[i].reg;
|
||||
|
||||
if (reg.isFloat() != (def->type() == LDefinition::DOUBLE))
|
||||
continue;
|
||||
|
||||
// Skip the register if it is in use for an allocated input or output.
|
||||
if (RegisterIsReserved(ins, reg))
|
||||
continue;
|
||||
|
||||
if (registers[i].vreg == MISSING_ALLOCATION ||
|
||||
best == UINT32_MAX ||
|
||||
registers[best].age > registers[i].age)
|
||||
{
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
evictRegister(ins, best);
|
||||
return best;
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::syncRegister(LInstruction *ins, RegisterIndex index)
|
||||
{
|
||||
if (registers[index].dirty) {
|
||||
LMoveGroup *input = getInputMoveGroup(ins->id());
|
||||
LAllocation *source = new LAllocation(registers[index].reg);
|
||||
|
||||
uint32 existing = registers[index].vreg;
|
||||
LAllocation *dest = stackLocation(existing);
|
||||
input->addAfter(source, dest);
|
||||
|
||||
registers[index].dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::evictRegister(LInstruction *ins, RegisterIndex index)
|
||||
{
|
||||
syncRegister(ins, index);
|
||||
registers[index].set(MISSING_ALLOCATION);
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::loadRegister(LInstruction *ins, uint32 vreg, RegisterIndex index)
|
||||
{
|
||||
// Load a vreg from its stack location to a register.
|
||||
LMoveGroup *input = getInputMoveGroup(ins->id());
|
||||
LAllocation *source = stackLocation(vreg);
|
||||
LAllocation *dest = new LAllocation(registers[index].reg);
|
||||
input->addAfter(source, dest);
|
||||
registers[index].set(vreg, ins);
|
||||
}
|
||||
|
||||
StupidAllocator::RegisterIndex
|
||||
StupidAllocator::findExistingRegister(uint32 vreg)
|
||||
{
|
||||
for (size_t i = 0; i < registerCount; i++) {
|
||||
if (registers[i].vreg == vreg)
|
||||
return i;
|
||||
}
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
bool
|
||||
StupidAllocator::go()
|
||||
{
|
||||
// This register allocator is intended to be as simple as possible, while
|
||||
// still being complicated enough to share properties with more complicated
|
||||
// allocators. Namely, physical registers may be used to carry virtual
|
||||
// registers across LIR instructions, but not across basic blocks.
|
||||
//
|
||||
// This algorithm does not pay any attention to liveness. It is performed
|
||||
// as a single forward pass through the basic blocks in the program. As
|
||||
// virtual registers and temporaries are defined they are assigned physical
|
||||
// registers, evicting existing allocations in an LRU fashion.
|
||||
|
||||
// For virtual registers not carried in a register, a canonical spill
|
||||
// location is used. Each vreg has a different spill location; since we do
|
||||
// not track liveness we cannot determine that two vregs have disjoint
|
||||
// lifetimes. Thus, the maximum stack height is the number of vregs (scaled
|
||||
// by two on 32 bit platforms to allow storing double values).
|
||||
graph.setLocalSlotCount(DefaultStackSlot(graph.numVirtualRegisters() - 1) + 1);
|
||||
|
||||
if (!init())
|
||||
return false;
|
||||
|
||||
for (size_t blockIndex = 0; blockIndex < graph.numBlocks(); blockIndex++) {
|
||||
LBlock *block = graph.getBlock(blockIndex);
|
||||
JS_ASSERT(block->mir()->id() == blockIndex);
|
||||
|
||||
for (size_t i = 0; i < registerCount; i++)
|
||||
registers[i].set(MISSING_ALLOCATION);
|
||||
|
||||
for (LInstructionIterator iter = block->begin(); iter != block->end(); iter++) {
|
||||
LInstruction *ins = *iter;
|
||||
|
||||
if (ins == *block->rbegin())
|
||||
syncForBlockEnd(block, ins);
|
||||
|
||||
allocateForInstruction(ins);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::syncForBlockEnd(LBlock *block, LInstruction *ins)
|
||||
{
|
||||
// Sync any dirty registers, and update the synced state for phi nodes at
|
||||
// each successor of a block. We cannot conflate the storage for phis with
|
||||
// that of their inputs, as we cannot prove the live ranges of the phi and
|
||||
// its input do not overlap. The values for the two may additionally be
|
||||
// different, as the phi could be for the value of the input in a previous
|
||||
// loop iteration.
|
||||
|
||||
for (size_t i = 0; i < registerCount; i++)
|
||||
syncRegister(ins, i);
|
||||
|
||||
LMoveGroup *group = NULL;
|
||||
|
||||
MBasicBlock *successor = block->mir()->successorWithPhis();
|
||||
if (successor) {
|
||||
uint32 position = block->mir()->positionInPhiSuccessor();
|
||||
LBlock *lirsuccessor = graph.getBlock(successor->id());
|
||||
for (size_t i = 0; i < lirsuccessor->numPhis(); i++) {
|
||||
LPhi *phi = lirsuccessor->getPhi(i);
|
||||
|
||||
uint32 sourcevreg = phi->getOperand(position)->toUse()->virtualRegister();
|
||||
uint32 destvreg = phi->getDef(0)->virtualRegister();
|
||||
|
||||
if (sourcevreg == destvreg)
|
||||
continue;
|
||||
|
||||
LAllocation *source = stackLocation(sourcevreg);
|
||||
LAllocation *dest = stackLocation(destvreg);
|
||||
|
||||
if (!group) {
|
||||
// The moves we insert here need to happen simultaneously with
|
||||
// each other, yet after any existing moves before the instruction.
|
||||
LMoveGroup *input = getInputMoveGroup(ins->id());
|
||||
if (input->numMoves() == 0) {
|
||||
group = input;
|
||||
} else {
|
||||
group = new LMoveGroup;
|
||||
block->insertAfter(input, group);
|
||||
}
|
||||
}
|
||||
|
||||
group->add(source, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::allocateForInstruction(LInstruction *ins)
|
||||
{
|
||||
// Sync all registers before making a call.
|
||||
if (ins->isCall()) {
|
||||
for (size_t i = 0; i < registerCount; i++)
|
||||
syncRegister(ins, i);
|
||||
}
|
||||
|
||||
// Allocate for inputs which are required to be in registers.
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
if (!alloc->isUse())
|
||||
continue;
|
||||
LUse *use = alloc->toUse();
|
||||
uint32 vreg = use->virtualRegister();
|
||||
if (use->policy() == LUse::REGISTER) {
|
||||
AnyRegister reg = ensureHasRegister(ins, vreg);
|
||||
alloc.replace(LAllocation(reg));
|
||||
} else if (use->policy() == LUse::FIXED) {
|
||||
AnyRegister reg = AnyRegister::FromCode(use->registerCode());
|
||||
RegisterIndex index = registerIndex(reg);
|
||||
if (registers[index].vreg != vreg) {
|
||||
evictRegister(ins, index);
|
||||
RegisterIndex existing = findExistingRegister(vreg);
|
||||
if (existing != UINT32_MAX)
|
||||
evictRegister(ins, existing);
|
||||
loadRegister(ins, vreg, index);
|
||||
}
|
||||
alloc.replace(LAllocation(reg));
|
||||
} else {
|
||||
// Inputs which are not required to be in a register are not
|
||||
// allocated until after temps/definitions, as the latter may need
|
||||
// to evict registers which hold these inputs.
|
||||
}
|
||||
}
|
||||
|
||||
// Find registers to hold all temporaries and outputs of the instruction.
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
LDefinition *def = ins->getTemp(i);
|
||||
if (!def->isBogusTemp())
|
||||
allocateForDefinition(ins, def);
|
||||
}
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
if (def->policy() != LDefinition::PASSTHROUGH)
|
||||
allocateForDefinition(ins, def);
|
||||
}
|
||||
|
||||
// Allocate for remaining inputs which do not need to be in registers.
|
||||
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
|
||||
if (!alloc->isUse())
|
||||
continue;
|
||||
LUse *use = alloc->toUse();
|
||||
uint32 vreg = use->virtualRegister();
|
||||
JS_ASSERT(use->policy() != LUse::REGISTER && use->policy() != LUse::FIXED);
|
||||
|
||||
RegisterIndex index = findExistingRegister(vreg);
|
||||
if (index == UINT32_MAX) {
|
||||
LAllocation *stack = stackLocation(use->virtualRegister());
|
||||
alloc.replace(*stack);
|
||||
} else {
|
||||
registers[index].age = ins->id();
|
||||
alloc.replace(LAllocation(registers[index].reg));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a call, evict all registers except for those holding outputs.
|
||||
if (ins->isCall()) {
|
||||
for (size_t i = 0; i < registerCount; i++) {
|
||||
if (!registers[i].dirty)
|
||||
registers[i].set(MISSING_ALLOCATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StupidAllocator::allocateForDefinition(LInstruction *ins, LDefinition *def)
|
||||
{
|
||||
uint32 vreg = def->virtualRegister();
|
||||
|
||||
CodePosition from;
|
||||
if ((def->output()->isRegister() && def->policy() == LDefinition::PRESET) ||
|
||||
def->policy() == LDefinition::MUST_REUSE_INPUT)
|
||||
{
|
||||
// Result will be in a specific register, spill any vreg held in
|
||||
// that register before the instruction.
|
||||
RegisterIndex index =
|
||||
registerIndex(def->policy() == LDefinition::PRESET
|
||||
? def->output()->toRegister()
|
||||
: ins->getOperand(def->getReusedInput())->toRegister());
|
||||
evictRegister(ins, index);
|
||||
registers[index].set(vreg, ins, true);
|
||||
def->setOutput(LAllocation(registers[index].reg));
|
||||
} else if (def->policy() == LDefinition::PRESET) {
|
||||
// The result must be a stack location.
|
||||
def->setOutput(*stackLocation(vreg));
|
||||
} else {
|
||||
// Find a register to hold the result of the instruction.
|
||||
RegisterIndex best = allocateRegister(ins, vreg);
|
||||
registers[best].set(vreg, ins, true);
|
||||
def->setOutput(LAllocation(registers[best].reg));
|
||||
}
|
||||
}
|
84
js/src/ion/StupidAllocator.h
Normal file
84
js/src/ion/StupidAllocator.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef js_ion_stupidallocator_h__
|
||||
#define js_ion_stupidallocator_h__
|
||||
|
||||
#include "RegisterAllocator.h"
|
||||
|
||||
// Simple register allocator that only carries registers within basic blocks.
|
||||
|
||||
namespace js {
|
||||
namespace ion {
|
||||
|
||||
class StupidAllocator : public RegisterAllocator
|
||||
{
|
||||
static const uint32 MAX_REGISTERS = Registers::Allocatable + FloatRegisters::Allocatable;
|
||||
static const uint32 MISSING_ALLOCATION = UINT32_MAX;
|
||||
|
||||
struct AllocatedRegister {
|
||||
AnyRegister reg;
|
||||
|
||||
// Virtual register this physical reg backs, or MISSING_ALLOCATION.
|
||||
uint32 vreg;
|
||||
|
||||
// id of the instruction which most recently used this register.
|
||||
uint32 age;
|
||||
|
||||
// Whether the physical register is not synced with the backing stack slot.
|
||||
bool dirty;
|
||||
|
||||
void set(uint32 vreg, LInstruction *ins = NULL, bool dirty = false) {
|
||||
this->vreg = vreg;
|
||||
this->age = ins ? ins->id() : 0;
|
||||
this->dirty = dirty;
|
||||
}
|
||||
};
|
||||
|
||||
// Active allocation for the current code position.
|
||||
AllocatedRegister registers[MAX_REGISTERS];
|
||||
uint32 registerCount;
|
||||
|
||||
// Type indicating an index into registers.
|
||||
typedef uint32 RegisterIndex;
|
||||
|
||||
// Information about each virtual register.
|
||||
Vector<LDefinition*, 0, SystemAllocPolicy> virtualRegisters;
|
||||
|
||||
public:
|
||||
StupidAllocator(MIRGenerator *mir, LIRGenerator *lir, LIRGraph &graph)
|
||||
: RegisterAllocator(mir, lir, graph)
|
||||
{
|
||||
}
|
||||
|
||||
bool go();
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
void syncForBlockEnd(LBlock *block, LInstruction *ins);
|
||||
void allocateForInstruction(LInstruction *ins);
|
||||
void allocateForDefinition(LInstruction *ins, LDefinition *def);
|
||||
|
||||
LAllocation *stackLocation(uint32 vreg);
|
||||
|
||||
RegisterIndex registerIndex(AnyRegister reg);
|
||||
|
||||
AnyRegister ensureHasRegister(LInstruction *ins, uint32 vreg);
|
||||
RegisterIndex allocateRegister(LInstruction *ins, uint32 vreg);
|
||||
|
||||
void syncRegister(LInstruction *ins, RegisterIndex index);
|
||||
void evictRegister(LInstruction *ins, RegisterIndex index);
|
||||
void loadRegister(LInstruction *ins, uint32 vreg, RegisterIndex index);
|
||||
|
||||
RegisterIndex findExistingRegister(uint32 vreg);
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
#endif
|
@ -4850,7 +4850,9 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
||||
|
||||
if (const char *str = op->getStringOption("ion-regalloc")) {
|
||||
if (strcmp(str, "lsra") == 0)
|
||||
ion::js_IonOptions.lsra = true;
|
||||
ion::js_IonOptions.registerAllocator = ion::RegisterAllocator_LSRA;
|
||||
else if (strcmp(str, "stupid") == 0)
|
||||
ion::js_IonOptions.registerAllocator = ion::RegisterAllocator_Stupid;
|
||||
else
|
||||
return OptionFailure("ion-regalloc", str);
|
||||
}
|
||||
@ -5079,7 +5081,8 @@ main(int argc, char **argv, char **envp)
|
||||
"Don't compile very large scripts (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-regalloc", "[mode]",
|
||||
"Specify Ion register allocation:\n"
|
||||
" lsra: Linear Scan register allocation (default)")
|
||||
" lsra: Linear Scan register allocation (default)\n"
|
||||
" stupid: Simple greedy register allocation")
|
||||
|| !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods")
|
||||
#ifdef JS_THREADSAFE
|
||||
|| !op.addStringOption('\0', "ion-parallel-compile", "on/off",
|
||||
|
Loading…
Reference in New Issue
Block a user