michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/LIR.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsprf.h" michael@0: michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGenerator.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: LIRGraph::LIRGraph(MIRGraph *mir) michael@0: : blocks_(mir->alloc()), michael@0: constantPool_(mir->alloc()), michael@0: constantPoolMap_(mir->alloc()), michael@0: safepoints_(mir->alloc()), michael@0: nonCallSafepoints_(mir->alloc()), michael@0: numVirtualRegisters_(0), michael@0: numInstructions_(1), // First id is 1. michael@0: localSlotCount_(0), michael@0: argumentSlotCount_(0), michael@0: entrySnapshot_(nullptr), michael@0: osrBlock_(nullptr), michael@0: mir_(*mir) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: LIRGraph::addConstantToPool(const Value &v, uint32_t *index) michael@0: { michael@0: JS_ASSERT(constantPoolMap_.initialized()); michael@0: michael@0: ConstantPoolMap::AddPtr p = constantPoolMap_.lookupForAdd(v); michael@0: if (p) { michael@0: *index = p->value(); michael@0: return true; michael@0: } michael@0: *index = constantPool_.length(); michael@0: return constantPool_.append(v) && constantPoolMap_.add(p, v, *index); michael@0: } michael@0: michael@0: bool michael@0: LIRGraph::noteNeedsSafepoint(LInstruction *ins) michael@0: { michael@0: // Instructions with safepoints must be in linear order. michael@0: JS_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id()); michael@0: if (!ins->isCall() && !nonCallSafepoints_.append(ins)) michael@0: return false; michael@0: return safepoints_.append(ins); michael@0: } michael@0: michael@0: void michael@0: LIRGraph::removeBlock(size_t i) michael@0: { michael@0: blocks_.erase(blocks_.begin() + i); michael@0: } michael@0: michael@0: uint32_t michael@0: LBlock::firstId() michael@0: { michael@0: if (phis_.length()) { michael@0: return phis_[0]->id(); michael@0: } else { michael@0: for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) { michael@0: if (i->id()) michael@0: return i->id(); michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: uint32_t michael@0: LBlock::lastId() michael@0: { michael@0: LInstruction *last = *instructions_.rbegin(); michael@0: JS_ASSERT(last->id()); michael@0: // The last instruction is a control flow instruction which does not have michael@0: // any output. michael@0: JS_ASSERT(last->numDefs() == 0); michael@0: return last->id(); michael@0: } michael@0: michael@0: LMoveGroup * michael@0: LBlock::getEntryMoveGroup(TempAllocator &alloc) michael@0: { michael@0: if (entryMoveGroup_) michael@0: return entryMoveGroup_; michael@0: entryMoveGroup_ = LMoveGroup::New(alloc); michael@0: if (begin()->isLabel()) michael@0: insertAfter(*begin(), entryMoveGroup_); michael@0: else michael@0: insertBefore(*begin(), entryMoveGroup_); michael@0: return entryMoveGroup_; michael@0: } michael@0: michael@0: LMoveGroup * michael@0: LBlock::getExitMoveGroup(TempAllocator &alloc) michael@0: { michael@0: if (exitMoveGroup_) michael@0: return exitMoveGroup_; michael@0: exitMoveGroup_ = LMoveGroup::New(alloc); michael@0: insertBefore(*rbegin(), exitMoveGroup_); michael@0: return exitMoveGroup_; michael@0: } michael@0: michael@0: static size_t michael@0: TotalOperandCount(MResumePoint *mir) michael@0: { michael@0: size_t accum = mir->numOperands(); michael@0: while ((mir = mir->caller())) michael@0: accum += mir->numOperands(); michael@0: return accum; michael@0: } michael@0: michael@0: LRecoverInfo::LRecoverInfo(TempAllocator &alloc) michael@0: : instructions_(alloc), michael@0: recoverOffset_(INVALID_RECOVER_OFFSET) michael@0: { } michael@0: michael@0: LRecoverInfo * michael@0: LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir) michael@0: { michael@0: LRecoverInfo *recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc()); michael@0: if (!recoverInfo || !recoverInfo->init(mir)) michael@0: return nullptr; michael@0: michael@0: IonSpew(IonSpew_Snapshots, "Generating LIR recover info %p from MIR (%p)", michael@0: (void *)recoverInfo, (void *)mir); michael@0: michael@0: return recoverInfo; michael@0: } michael@0: michael@0: bool michael@0: LRecoverInfo::init(MResumePoint *rp) michael@0: { michael@0: MResumePoint *it = rp; michael@0: michael@0: // Sort operations in the order in which we need to restore the stack. This michael@0: // implies that outer frames, as well as operations needed to recover the michael@0: // current frame, are located before the current frame. The inner-most michael@0: // resume point should be the last element in the list. michael@0: do { michael@0: if (!instructions_.append(it)) michael@0: return false; michael@0: it = it->caller(); michael@0: } while (it); michael@0: michael@0: Reverse(instructions_.begin(), instructions_.end()); michael@0: MOZ_ASSERT(mir() == rp); michael@0: return true; michael@0: } michael@0: michael@0: LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind) michael@0: : numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES), michael@0: slots_(nullptr), michael@0: recoverInfo_(recoverInfo), michael@0: snapshotOffset_(INVALID_SNAPSHOT_OFFSET), michael@0: bailoutId_(INVALID_BAILOUT_ID), michael@0: bailoutKind_(kind) michael@0: { } michael@0: michael@0: bool michael@0: LSnapshot::init(MIRGenerator *gen) michael@0: { michael@0: slots_ = gen->allocate(numSlots_); michael@0: return !!slots_; michael@0: } michael@0: michael@0: LSnapshot * michael@0: LSnapshot::New(MIRGenerator *gen, LRecoverInfo *recover, BailoutKind kind) michael@0: { michael@0: LSnapshot *snapshot = new(gen->alloc()) LSnapshot(recover, kind); michael@0: if (!snapshot || !snapshot->init(gen)) michael@0: return nullptr; michael@0: michael@0: IonSpew(IonSpew_Snapshots, "Generating LIR snapshot %p from recover (%p)", michael@0: (void *)snapshot, (void *)recover); michael@0: michael@0: return snapshot; michael@0: } michael@0: michael@0: void michael@0: LSnapshot::rewriteRecoveredInput(LUse input) michael@0: { michael@0: // Mark any operands to this snapshot with the same value as input as being michael@0: // equal to the instruction's result. michael@0: for (size_t i = 0; i < numEntries(); i++) { michael@0: if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister()) michael@0: setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT)); michael@0: } michael@0: } michael@0: michael@0: LPhi * michael@0: LPhi::New(MIRGenerator *gen, MPhi *ins) michael@0: { michael@0: LPhi *phi = new (gen->alloc()) LPhi(); michael@0: LAllocation *inputs = gen->allocate(ins->numOperands()); michael@0: if (!inputs) michael@0: return nullptr; michael@0: michael@0: phi->inputs_ = inputs; michael@0: phi->setMir(ins); michael@0: return phi; michael@0: } michael@0: michael@0: void michael@0: LInstruction::printName(FILE *fp, Opcode op) michael@0: { michael@0: static const char * const names[] = michael@0: { michael@0: #define LIROP(x) #x, michael@0: LIR_OPCODE_LIST(LIROP) michael@0: #undef LIROP michael@0: }; michael@0: const char *name = names[op]; michael@0: size_t len = strlen(name); michael@0: for (size_t i = 0; i < len; i++) michael@0: fprintf(fp, "%c", tolower(name[i])); michael@0: } michael@0: michael@0: void michael@0: LInstruction::printName(FILE *fp) michael@0: { michael@0: printName(fp, op()); michael@0: } michael@0: michael@0: static const char * const TypeChars[] = michael@0: { michael@0: "g", // GENERAL michael@0: "i", // INT32 michael@0: "o", // OBJECT michael@0: "s", // SLOTS michael@0: "f", // FLOAT32 michael@0: "d", // DOUBLE michael@0: #ifdef JS_NUNBOX32 michael@0: "t", // TYPE michael@0: "p" // PAYLOAD michael@0: #elif JS_PUNBOX64 michael@0: "x" // BOX michael@0: #endif michael@0: }; michael@0: michael@0: static void michael@0: PrintDefinition(FILE *fp, const LDefinition &def) michael@0: { michael@0: fprintf(fp, "[%s", TypeChars[def.type()]); michael@0: if (def.virtualRegister()) michael@0: fprintf(fp, ":%d", def.virtualRegister()); michael@0: if (def.policy() == LDefinition::PRESET) { michael@0: fprintf(fp, " (%s)", def.output()->toString()); michael@0: } else if (def.policy() == LDefinition::MUST_REUSE_INPUT) { michael@0: fprintf(fp, " (!)"); michael@0: } else if (def.policy() == LDefinition::PASSTHROUGH) { michael@0: fprintf(fp, " (-)"); michael@0: } michael@0: fprintf(fp, "]"); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: PrintUse(char *buf, size_t size, const LUse *use) michael@0: { michael@0: switch (use->policy()) { michael@0: case LUse::REGISTER: michael@0: JS_snprintf(buf, size, "v%d:r", use->virtualRegister()); michael@0: break; michael@0: case LUse::FIXED: michael@0: // Unfortunately, we don't know here whether the virtual register is a michael@0: // float or a double. Should we steal a bit in LUse for help? For now, michael@0: // nothing defines any fixed xmm registers. michael@0: JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(), michael@0: Registers::GetName(Registers::Code(use->registerCode()))); michael@0: break; michael@0: case LUse::ANY: michael@0: JS_snprintf(buf, size, "v%d:r?", use->virtualRegister()); michael@0: break; michael@0: case LUse::KEEPALIVE: michael@0: JS_snprintf(buf, size, "v%d:*", use->virtualRegister()); michael@0: break; michael@0: case LUse::RECOVERED_INPUT: michael@0: JS_snprintf(buf, size, "v%d:**", use->virtualRegister()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("invalid use policy"); michael@0: } michael@0: } michael@0: michael@0: const char * michael@0: LAllocation::toString() const michael@0: { michael@0: // Not reentrant! michael@0: static char buf[40]; michael@0: michael@0: switch (kind()) { michael@0: case LAllocation::CONSTANT_VALUE: michael@0: case LAllocation::CONSTANT_INDEX: michael@0: return "c"; michael@0: case LAllocation::GPR: michael@0: JS_snprintf(buf, sizeof(buf), "=%s", toGeneralReg()->reg().name()); michael@0: return buf; michael@0: case LAllocation::FPU: michael@0: JS_snprintf(buf, sizeof(buf), "=%s", toFloatReg()->reg().name()); michael@0: return buf; michael@0: case LAllocation::STACK_SLOT: michael@0: JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot()); michael@0: return buf; michael@0: case LAllocation::ARGUMENT_SLOT: michael@0: JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index()); michael@0: return buf; michael@0: case LAllocation::USE: michael@0: PrintUse(buf, sizeof(buf), toUse()); michael@0: return buf; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("what?"); michael@0: } michael@0: } michael@0: #endif // DEBUG michael@0: michael@0: void michael@0: LAllocation::dump() const michael@0: { michael@0: fprintf(stderr, "%s\n", toString()); michael@0: } michael@0: michael@0: void michael@0: LInstruction::printOperands(FILE *fp) michael@0: { michael@0: for (size_t i = 0, e = numOperands(); i < e; i++) { michael@0: fprintf(fp, " (%s)", getOperand(i)->toString()); michael@0: if (i != numOperands() - 1) michael@0: fprintf(fp, ","); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LInstruction::assignSnapshot(LSnapshot *snapshot) michael@0: { michael@0: JS_ASSERT(!snapshot_); michael@0: snapshot_ = snapshot; michael@0: michael@0: #ifdef DEBUG michael@0: if (IonSpewEnabled(IonSpew_Snapshots)) { michael@0: IonSpewHeader(IonSpew_Snapshots); michael@0: fprintf(IonSpewFile, "Assigning snapshot %p to instruction %p (", michael@0: (void *)snapshot, (void *)this); michael@0: printName(IonSpewFile); michael@0: fprintf(IonSpewFile, ")\n"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: LInstruction::dump(FILE *fp) michael@0: { michael@0: fprintf(fp, "{"); michael@0: for (size_t i = 0; i < numDefs(); i++) { michael@0: PrintDefinition(fp, *getDef(i)); michael@0: if (i != numDefs() - 1) michael@0: fprintf(fp, ", "); michael@0: } michael@0: fprintf(fp, "} <- "); michael@0: michael@0: printName(fp); michael@0: michael@0: michael@0: printInfo(fp); michael@0: michael@0: if (numTemps()) { michael@0: fprintf(fp, " t=("); michael@0: for (size_t i = 0; i < numTemps(); i++) { michael@0: PrintDefinition(fp, *getTemp(i)); michael@0: if (i != numTemps() - 1) michael@0: fprintf(fp, ", "); michael@0: } michael@0: fprintf(fp, ")"); michael@0: } michael@0: fprintf(fp, "\n"); michael@0: } michael@0: michael@0: void michael@0: LInstruction::dump() michael@0: { michael@0: return dump(stderr); michael@0: } michael@0: michael@0: void michael@0: LInstruction::initSafepoint(TempAllocator &alloc) michael@0: { michael@0: JS_ASSERT(!safepoint_); michael@0: safepoint_ = new(alloc) LSafepoint(alloc); michael@0: JS_ASSERT(safepoint_); michael@0: } michael@0: michael@0: bool michael@0: LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type) michael@0: { michael@0: #ifdef DEBUG michael@0: JS_ASSERT(*from != *to); michael@0: for (size_t i = 0; i < moves_.length(); i++) michael@0: JS_ASSERT(*to != *moves_[i].to()); michael@0: #endif michael@0: return moves_.append(LMove(from, to, type)); michael@0: } michael@0: michael@0: bool michael@0: LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type) michael@0: { michael@0: // Transform the operands to this move so that performing the result michael@0: // simultaneously with existing moves in the group will have the same michael@0: // effect as if the original move took place after the existing moves. michael@0: michael@0: for (size_t i = 0; i < moves_.length(); i++) { michael@0: if (*moves_[i].to() == *from) { michael@0: from = moves_[i].from(); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (*from == *to) michael@0: return true; michael@0: michael@0: for (size_t i = 0; i < moves_.length(); i++) { michael@0: if (*to == *moves_[i].to()) { michael@0: moves_[i] = LMove(from, to, type); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return add(from, to, type); michael@0: } michael@0: michael@0: void michael@0: LMoveGroup::printOperands(FILE *fp) michael@0: { michael@0: for (size_t i = 0; i < numMoves(); i++) { michael@0: const LMove &move = getMove(i); michael@0: // Use two printfs, as LAllocation::toString is not reentrant. michael@0: fprintf(fp, "[%s", move.from()->toString()); michael@0: fprintf(fp, " -> %s]", move.to()->toString()); michael@0: if (i != numMoves() - 1) michael@0: fprintf(fp, ", "); michael@0: } michael@0: }