| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 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 "MIR.h" |
| #include "MIRGraph.h" |
| #include "LIR.h" |
| #include "IonSpewer.h" |
| #include "LIR-inl.h" |
| #include "shared/CodeGenerator-shared.h" |
| using namespace js; |
| using namespace js::jit; |
| |
| LIRGraph::LIRGraph(MIRGraph *mir) |
| : numVirtualRegisters_(0), |
| numInstructions_(1), // First id is 1. |
| localSlotCount_(0), |
| argumentSlotCount_(0), |
| entrySnapshot_(NULL), |
| osrBlock_(NULL), |
| mir_(*mir) |
| { |
| } |
| |
| bool |
| LIRGraph::addConstantToPool(const Value &v, uint32_t *index) |
| { |
| *index = constantPool_.length(); |
| return constantPool_.append(v); |
| } |
| |
| bool |
| LIRGraph::noteNeedsSafepoint(LInstruction *ins) |
| { |
| // Instructions with safepoints must be in linear order. |
| JS_ASSERT_IF(safepoints_.length(), safepoints_[safepoints_.length() - 1]->id() < ins->id()); |
| if (!ins->isCall() && !nonCallSafepoints_.append(ins)) |
| return false; |
| return safepoints_.append(ins); |
| } |
| |
| void |
| LIRGraph::removeBlock(size_t i) |
| { |
| blocks_.erase(blocks_.begin() + i); |
| } |
| |
| Label * |
| LBlock::label() |
| { |
| return begin()->toLabel()->label(); |
| } |
| |
| uint32_t |
| LBlock::firstId() |
| { |
| if (phis_.length()) { |
| return phis_[0]->id(); |
| } else { |
| for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) { |
| if (i->id()) |
| return i->id(); |
| } |
| } |
| return 0; |
| } |
| uint32_t |
| LBlock::lastId() |
| { |
| LInstruction *last = *instructions_.rbegin(); |
| JS_ASSERT(last->id()); |
| if (last->numDefs()) |
| return last->getDef(last->numDefs() - 1)->virtualRegister(); |
| return last->id(); |
| } |
| |
| LMoveGroup * |
| LBlock::getEntryMoveGroup() |
| { |
| if (entryMoveGroup_) |
| return entryMoveGroup_; |
| entryMoveGroup_ = new LMoveGroup; |
| JS_ASSERT(begin()->isLabel()); |
| insertAfter(*begin(), entryMoveGroup_); |
| return entryMoveGroup_; |
| } |
| |
| LMoveGroup * |
| LBlock::getExitMoveGroup() |
| { |
| if (exitMoveGroup_) |
| return exitMoveGroup_; |
| exitMoveGroup_ = new LMoveGroup; |
| insertBefore(*rbegin(), exitMoveGroup_); |
| return exitMoveGroup_; |
| } |
| |
| static size_t |
| TotalOperandCount(MResumePoint *mir) |
| { |
| size_t accum = mir->numOperands(); |
| while ((mir = mir->caller())) |
| accum += mir->numOperands(); |
| return accum; |
| } |
| |
| LSnapshot::LSnapshot(MResumePoint *mir, BailoutKind kind) |
| : numSlots_(TotalOperandCount(mir) * BOX_PIECES), |
| slots_(NULL), |
| mir_(mir), |
| snapshotOffset_(INVALID_SNAPSHOT_OFFSET), |
| bailoutId_(INVALID_BAILOUT_ID), |
| bailoutKind_(kind) |
| { } |
| |
| bool |
| LSnapshot::init(MIRGenerator *gen) |
| { |
| slots_ = gen->allocate<LAllocation>(numSlots_); |
| return !!slots_; |
| } |
| |
| LSnapshot * |
| LSnapshot::New(MIRGenerator *gen, MResumePoint *mir, BailoutKind kind) |
| { |
| LSnapshot *snapshot = new LSnapshot(mir, kind); |
| if (!snapshot->init(gen)) |
| return NULL; |
| |
| IonSpew(IonSpew_Snapshots, "Generating LIR snapshot %p from MIR (%p)", |
| (void *)snapshot, (void *)mir); |
| |
| return snapshot; |
| } |
| |
| void |
| LSnapshot::rewriteRecoveredInput(LUse input) |
| { |
| // Mark any operands to this snapshot with the same value as input as being |
| // equal to the instruction's result. |
| for (size_t i = 0; i < numEntries(); i++) { |
| if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) |
| setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT)); |
| } |
| } |
| |
| bool |
| LPhi::init(MIRGenerator *gen) |
| { |
| inputs_ = gen->allocate<LAllocation>(numInputs_); |
| return !!inputs_; |
| } |
| |
| LPhi::LPhi(MPhi *mir) |
| : numInputs_(mir->numOperands()) |
| { |
| } |
| |
| LPhi * |
| LPhi::New(MIRGenerator *gen, MPhi *ins) |
| { |
| LPhi *phi = new LPhi(ins); |
| if (!phi->init(gen)) |
| return NULL; |
| return phi; |
| } |
| |
| void |
| LInstruction::printName(FILE *fp, Opcode op) |
| { |
| static const char * const names[] = |
| { |
| #define LIROP(x) #x, |
| LIR_OPCODE_LIST(LIROP) |
| #undef LIROP |
| }; |
| const char *name = names[op]; |
| size_t len = strlen(name); |
| for (size_t i = 0; i < len; i++) |
| fprintf(fp, "%c", tolower(name[i])); |
| } |
| |
| void |
| LInstruction::printName(FILE *fp) |
| { |
| printName(fp, op()); |
| } |
| |
| static const char * const TypeChars[] = |
| { |
| "i", // INTEGER |
| "o", // OBJECT |
| "f", // DOUBLE |
| #ifdef JS_NUNBOX32 |
| "t", // TYPE |
| "d" // PAYLOAD |
| #elif JS_PUNBOX64 |
| "x" // BOX |
| #endif |
| }; |
| |
| static void |
| PrintDefinition(FILE *fp, const LDefinition &def) |
| { |
| fprintf(fp, "[%s", TypeChars[def.type()]); |
| if (def.virtualRegister()) |
| fprintf(fp, ":%d", def.virtualRegister()); |
| if (def.policy() == LDefinition::PRESET) { |
| fprintf(fp, " (%s)", def.output()->toString()); |
| } else if (def.policy() == LDefinition::MUST_REUSE_INPUT) { |
| fprintf(fp, " (!)"); |
| } else if (def.policy() == LDefinition::PASSTHROUGH) { |
| fprintf(fp, " (-)"); |
| } |
| fprintf(fp, "]"); |
| } |
| |
| #ifdef DEBUG |
| static void |
| PrintUse(char *buf, size_t size, const LUse *use) |
| { |
| switch (use->policy()) { |
| case LUse::REGISTER: |
| JS_snprintf(buf, size, "v%d:r", use->virtualRegister()); |
| break; |
| case LUse::FIXED: |
| // Unfortunately, we don't know here whether the virtual register is a |
| // float or a double. Should we steal a bit in LUse for help? For now, |
| // nothing defines any fixed xmm registers. |
| JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(), |
| Registers::GetName(Registers::Code(use->registerCode()))); |
| break; |
| default: |
| JS_snprintf(buf, size, "v%d:*", use->virtualRegister()); |
| } |
| } |
| |
| const char * |
| LAllocation::toString() const |
| { |
| // Not reentrant! |
| static char buf[40]; |
| |
| switch (kind()) { |
| case LAllocation::CONSTANT_VALUE: |
| case LAllocation::CONSTANT_INDEX: |
| return "c"; |
| case LAllocation::GPR: |
| JS_snprintf(buf, sizeof(buf), "=%s", toGeneralReg()->reg().name()); |
| return buf; |
| case LAllocation::FPU: |
| JS_snprintf(buf, sizeof(buf), "=%s", toFloatReg()->reg().name()); |
| return buf; |
| case LAllocation::STACK_SLOT: |
| JS_snprintf(buf, sizeof(buf), "stack:i%d", toStackSlot()->slot()); |
| return buf; |
| case LAllocation::DOUBLE_SLOT: |
| JS_snprintf(buf, sizeof(buf), "stack:d%d", toStackSlot()->slot()); |
| return buf; |
| case LAllocation::INT_ARGUMENT: |
| JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); |
| return buf; |
| case LAllocation::DOUBLE_ARGUMENT: |
| JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); |
| return buf; |
| case LAllocation::USE: |
| PrintUse(buf, sizeof(buf), toUse()); |
| return buf; |
| default: |
| JS_NOT_REACHED("what?"); |
| return "???"; |
| } |
| } |
| #endif // DEBUG |
| |
| void |
| LInstruction::printOperands(FILE *fp) |
| { |
| for (size_t i = 0; i < numOperands(); i++) { |
| fprintf(fp, " (%s)", getOperand(i)->toString()); |
| if (i != numOperands() - 1) |
| fprintf(fp, ","); |
| } |
| } |
| |
| void |
| LInstruction::assignSnapshot(LSnapshot *snapshot) |
| { |
| JS_ASSERT(!snapshot_); |
| snapshot_ = snapshot; |
| |
| #ifdef DEBUG |
| if (IonSpewEnabled(IonSpew_Snapshots)) { |
| IonSpewHeader(IonSpew_Snapshots); |
| fprintf(IonSpewFile, "Assigning snapshot %p to instruction %p (", |
| (void *)snapshot, (void *)this); |
| printName(IonSpewFile); |
| fprintf(IonSpewFile, ")\n"); |
| } |
| #endif |
| } |
| |
| void |
| LInstruction::print(FILE *fp) |
| { |
| fprintf(fp, "{"); |
| for (size_t i = 0; i < numDefs(); i++) { |
| PrintDefinition(fp, *getDef(i)); |
| if (i != numDefs() - 1) |
| fprintf(fp, ", "); |
| } |
| fprintf(fp, "} <- "); |
| |
| printName(fp); |
| |
| |
| printInfo(fp); |
| |
| if (numTemps()) { |
| fprintf(fp, " t=("); |
| for (size_t i = 0; i < numTemps(); i++) { |
| PrintDefinition(fp, *getTemp(i)); |
| if (i != numTemps() - 1) |
| fprintf(fp, ", "); |
| } |
| fprintf(fp, ")"); |
| } |
| } |
| |
| void |
| LInstruction::initSafepoint() |
| { |
| JS_ASSERT(!safepoint_); |
| safepoint_ = new LSafepoint(); |
| 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) |
| { |
| for (size_t i = 0; i < numMoves(); i++) { |
| const LMove &move = getMove(i); |
| // Use two printfs, as LAllocation::toString is not reentrant. |
| fprintf(fp, "[%s", move.from()->toString()); |
| fprintf(fp, " -> %s]", move.to()->toString()); |
| if (i != numMoves() - 1) |
| fprintf(fp, ", "); |
| } |
| } |