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/MIRGraph.h" michael@0: michael@0: #include "jit/AsmJS.h" michael@0: #include "jit/BytecodeAnalysis.h" michael@0: #include "jit/Ion.h" 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: MIRGenerator::MIRGenerator(CompileCompartment *compartment, const JitCompileOptions &options, michael@0: TempAllocator *alloc, MIRGraph *graph, CompileInfo *info, michael@0: const OptimizationInfo *optimizationInfo) michael@0: : compartment(compartment), michael@0: info_(info), michael@0: optimizationInfo_(optimizationInfo), michael@0: alloc_(alloc), michael@0: graph_(graph), michael@0: error_(false), michael@0: cancelBuild_(false), michael@0: maxAsmJSStackArgBytes_(0), michael@0: performsCall_(false), michael@0: performsAsmJSCall_(false), michael@0: minAsmJSHeapLength_(AsmJSAllocationGranularity), michael@0: modifiesFrameArguments_(false), michael@0: options(options) michael@0: { } michael@0: michael@0: bool michael@0: MIRGenerator::abortFmt(const char *message, va_list ap) michael@0: { michael@0: IonSpewVA(IonSpew_Abort, message, ap); michael@0: error_ = true; michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: MIRGenerator::abort(const char *message, ...) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, message); michael@0: abortFmt(message, ap); michael@0: va_end(ap); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MIRGraph::addBlock(MBasicBlock *block) michael@0: { michael@0: JS_ASSERT(block); michael@0: block->setId(blockIdGen_++); michael@0: blocks_.pushBack(block); michael@0: numBlocks_++; michael@0: } michael@0: michael@0: void michael@0: MIRGraph::insertBlockAfter(MBasicBlock *at, MBasicBlock *block) michael@0: { michael@0: block->setId(blockIdGen_++); michael@0: blocks_.insertAfter(at, block); michael@0: numBlocks_++; michael@0: } michael@0: michael@0: void michael@0: MIRGraph::removeBlocksAfter(MBasicBlock *start) michael@0: { michael@0: MBasicBlockIterator iter(begin()); michael@0: iter++; michael@0: while (iter != end()) { michael@0: MBasicBlock *block = *iter; michael@0: iter++; michael@0: michael@0: if (block->id() <= start->id()) michael@0: continue; michael@0: michael@0: // removeBlock will not remove the resumepoints, since michael@0: // it can be shared with outer blocks. So remove them now. michael@0: block->discardAllResumePoints(); michael@0: removeBlock(block); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MIRGraph::removeBlock(MBasicBlock *block) michael@0: { michael@0: // Remove a block from the graph. It will also cleanup the block, michael@0: // except for removing the resumepoints, since multiple blocks can michael@0: // share the same resumepoints and we cannot distinguish between them. michael@0: michael@0: if (block == osrBlock_) michael@0: osrBlock_ = nullptr; michael@0: michael@0: if (returnAccumulator_) { michael@0: size_t i = 0; michael@0: while (i < returnAccumulator_->length()) { michael@0: if ((*returnAccumulator_)[i] == block) michael@0: returnAccumulator_->erase(returnAccumulator_->begin() + i); michael@0: else michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: block->discardAllInstructions(); michael@0: michael@0: // Note: phis are disconnected from the rest of the graph, but are not michael@0: // removed entirely. If the block being removed is a loop header then michael@0: // IonBuilder may need to access these phis to more quickly converge on the michael@0: // possible types in the graph. See IonBuilder::analyzeNewLoopTypes. michael@0: block->discardAllPhiOperands(); michael@0: michael@0: block->markAsDead(); michael@0: blocks_.remove(block); michael@0: numBlocks_--; michael@0: } michael@0: michael@0: void michael@0: MIRGraph::unmarkBlocks() michael@0: { michael@0: for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++) michael@0: i->unmark(); michael@0: } michael@0: michael@0: MDefinition * michael@0: MIRGraph::forkJoinContext() michael@0: { michael@0: // Search the entry block to find a ForkJoinContext instruction. If we do michael@0: // not find one, add one after the Start instruction. michael@0: // michael@0: // Note: the original design used a field in MIRGraph to cache the michael@0: // forkJoinContext rather than searching for it again. However, this michael@0: // could become out of date due to DCE. Given that we do not generally michael@0: // have to search very far to find the ForkJoinContext instruction if it michael@0: // exists, and that we don't look for it that often, I opted to simply michael@0: // eliminate the cache and search anew each time, so that it is that much michael@0: // easier to keep the IR coherent. - nmatsakis michael@0: michael@0: MBasicBlock *entry = entryBlock(); michael@0: JS_ASSERT(entry->info().executionMode() == ParallelExecution); michael@0: michael@0: MInstruction *start = nullptr; michael@0: for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) { michael@0: if (ins->isForkJoinContext()) michael@0: return *ins; michael@0: else if (ins->isStart()) michael@0: start = *ins; michael@0: } michael@0: JS_ASSERT(start); michael@0: michael@0: MForkJoinContext *cx = MForkJoinContext::New(alloc()); michael@0: entry->insertAfter(start, cx); michael@0: return cx; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::New(MIRGraph &graph, BytecodeAnalysis *analysis, CompileInfo &info, michael@0: MBasicBlock *pred, jsbytecode *entryPc, Kind kind) michael@0: { michael@0: JS_ASSERT(entryPc != nullptr); michael@0: michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, kind); michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (!block->inherit(graph.alloc(), analysis, pred, 0)) michael@0: return nullptr; michael@0: michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewPopN(MIRGraph &graph, CompileInfo &info, michael@0: MBasicBlock *pred, jsbytecode *entryPc, Kind kind, uint32_t popped) michael@0: { michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, kind); michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (!block->inherit(graph.alloc(), nullptr, pred, popped)) michael@0: return nullptr; michael@0: michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewWithResumePoint(MIRGraph &graph, CompileInfo &info, michael@0: MBasicBlock *pred, jsbytecode *entryPc, michael@0: MResumePoint *resumePoint) michael@0: { michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, NORMAL); michael@0: michael@0: resumePoint->block_ = block; michael@0: block->entryResumePoint_ = resumePoint; michael@0: michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (!block->inheritResumePoint(pred)) michael@0: return nullptr; michael@0: michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewPendingLoopHeader(MIRGraph &graph, CompileInfo &info, michael@0: MBasicBlock *pred, jsbytecode *entryPc, michael@0: unsigned stackPhiCount) michael@0: { michael@0: JS_ASSERT(entryPc != nullptr); michael@0: michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, PENDING_LOOP_HEADER); michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (!block->inherit(graph.alloc(), nullptr, pred, 0, stackPhiCount)) michael@0: return nullptr; michael@0: michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewSplitEdge(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred) michael@0: { michael@0: return pred->pc() michael@0: ? MBasicBlock::New(graph, nullptr, info, pred, pred->pc(), SPLIT_EDGE) michael@0: : MBasicBlock::NewAsmJS(graph, info, pred, SPLIT_EDGE); michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewAbortPar(MIRGraph &graph, CompileInfo &info, michael@0: MBasicBlock *pred, jsbytecode *entryPc, michael@0: MResumePoint *resumePoint) michael@0: { michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, entryPc, NORMAL); michael@0: michael@0: resumePoint->block_ = block; michael@0: block->entryResumePoint_ = resumePoint; michael@0: michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (!block->addPredecessorWithoutPhis(pred)) michael@0: return nullptr; michael@0: michael@0: block->end(MAbortPar::New(graph.alloc())); michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::NewAsmJS(MIRGraph &graph, CompileInfo &info, MBasicBlock *pred, Kind kind) michael@0: { michael@0: MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, /* entryPC = */ nullptr, kind); michael@0: if (!block->init()) michael@0: return nullptr; michael@0: michael@0: if (pred) { michael@0: block->stackPosition_ = pred->stackPosition_; michael@0: michael@0: if (block->kind_ == PENDING_LOOP_HEADER) { michael@0: size_t nphis = block->stackPosition_; michael@0: michael@0: TempAllocator &alloc = graph.alloc(); michael@0: MPhi *phis = (MPhi*)alloc.allocateArray(nphis); michael@0: if (!phis) michael@0: return nullptr; michael@0: michael@0: for (size_t i = 0; i < nphis; i++) { michael@0: MDefinition *predSlot = pred->getSlot(i); michael@0: michael@0: JS_ASSERT(predSlot->type() != MIRType_Value); michael@0: MPhi *phi = new(phis + i) MPhi(alloc, i, predSlot->type()); michael@0: michael@0: JS_ALWAYS_TRUE(phi->reserveLength(2)); michael@0: phi->addInput(predSlot); michael@0: michael@0: block->addPhi(phi); michael@0: block->setSlot(i, phi); michael@0: } michael@0: } else { michael@0: block->copySlots(pred); michael@0: } michael@0: michael@0: if (!block->predecessors_.append(pred)) michael@0: return nullptr; michael@0: } michael@0: michael@0: return block; michael@0: } michael@0: michael@0: MBasicBlock::MBasicBlock(MIRGraph &graph, CompileInfo &info, jsbytecode *pc, Kind kind) michael@0: : unreachable_(false), michael@0: graph_(graph), michael@0: info_(info), michael@0: predecessors_(graph.alloc()), michael@0: stackPosition_(info_.firstStackSlot()), michael@0: lastIns_(nullptr), michael@0: pc_(pc), michael@0: lir_(nullptr), michael@0: start_(nullptr), michael@0: entryResumePoint_(nullptr), michael@0: successorWithPhis_(nullptr), michael@0: positionInPhiSuccessor_(0), michael@0: kind_(kind), michael@0: loopDepth_(0), michael@0: mark_(false), michael@0: immediatelyDominated_(graph.alloc()), michael@0: immediateDominator_(nullptr), michael@0: numDominated_(0), michael@0: trackedPc_(pc) michael@0: #if defined (JS_ION_PERF) michael@0: , lineno_(0u), michael@0: columnIndex_(0u) michael@0: #endif michael@0: { michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::init() michael@0: { michael@0: return slots_.init(graph_.alloc(), info_.nslots()); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::increaseSlots(size_t num) michael@0: { michael@0: return slots_.growBy(graph_.alloc(), num); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::ensureHasSlots(size_t num) michael@0: { michael@0: size_t depth = stackDepth() + num; michael@0: if (depth > nslots()) { michael@0: if (!increaseSlots(depth - nslots())) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::copySlots(MBasicBlock *from) michael@0: { michael@0: JS_ASSERT(stackPosition_ <= from->stackPosition_); michael@0: michael@0: for (uint32_t i = 0; i < stackPosition_; i++) michael@0: slots_[i] = from->slots_[i]; michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlock *pred, michael@0: uint32_t popped, unsigned stackPhiCount) michael@0: { michael@0: if (pred) { michael@0: stackPosition_ = pred->stackPosition_; michael@0: JS_ASSERT(stackPosition_ >= popped); michael@0: stackPosition_ -= popped; michael@0: if (kind_ != PENDING_LOOP_HEADER) michael@0: copySlots(pred); michael@0: } else { michael@0: uint32_t stackDepth = analysis->info(pc()).stackDepth; michael@0: stackPosition_ = info().firstStackSlot() + stackDepth; michael@0: JS_ASSERT(stackPosition_ >= popped); michael@0: stackPosition_ -= popped; michael@0: } michael@0: michael@0: JS_ASSERT(info_.nslots() >= stackPosition_); michael@0: JS_ASSERT(!entryResumePoint_); michael@0: michael@0: // Propagate the caller resume point from the inherited block. michael@0: MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : nullptr; michael@0: michael@0: // Create a resume point using our initial stack state. michael@0: entryResumePoint_ = new(alloc) MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt); michael@0: if (!entryResumePoint_->init(alloc)) michael@0: return false; michael@0: michael@0: if (pred) { michael@0: if (!predecessors_.append(pred)) michael@0: return false; michael@0: michael@0: if (kind_ == PENDING_LOOP_HEADER) { michael@0: size_t i = 0; michael@0: for (i = 0; i < info().firstStackSlot(); i++) { michael@0: MPhi *phi = MPhi::New(alloc, i); michael@0: if (!phi->addInputSlow(pred->getSlot(i))) michael@0: return false; michael@0: addPhi(phi); michael@0: setSlot(i, phi); michael@0: entryResumePoint()->setOperand(i, phi); michael@0: } michael@0: michael@0: JS_ASSERT(stackPhiCount <= stackDepth()); michael@0: JS_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount); michael@0: michael@0: // Avoid creating new phis for stack values that aren't part of the michael@0: // loop. Note that for loop headers that can OSR, all values on the michael@0: // stack are part of the loop. michael@0: for (; i < stackDepth() - stackPhiCount; i++) { michael@0: MDefinition *val = pred->getSlot(i); michael@0: setSlot(i, val); michael@0: entryResumePoint()->setOperand(i, val); michael@0: } michael@0: michael@0: for (; i < stackDepth(); i++) { michael@0: MPhi *phi = MPhi::New(alloc, i); michael@0: if (!phi->addInputSlow(pred->getSlot(i))) michael@0: return false; michael@0: addPhi(phi); michael@0: setSlot(i, phi); michael@0: entryResumePoint()->setOperand(i, phi); michael@0: } michael@0: } else { michael@0: for (size_t i = 0; i < stackDepth(); i++) michael@0: entryResumePoint()->setOperand(i, getSlot(i)); michael@0: } michael@0: } else { michael@0: /* michael@0: * Don't leave the operands uninitialized for the caller, as it may not michael@0: * initialize them later on. michael@0: */ michael@0: for (size_t i = 0; i < stackDepth(); i++) michael@0: entryResumePoint()->clearOperand(i); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::inheritResumePoint(MBasicBlock *pred) michael@0: { michael@0: // Copy slots from the resume point. michael@0: stackPosition_ = entryResumePoint_->numOperands(); michael@0: for (uint32_t i = 0; i < stackPosition_; i++) michael@0: slots_[i] = entryResumePoint_->getOperand(i); michael@0: michael@0: JS_ASSERT(info_.nslots() >= stackPosition_); michael@0: JS_ASSERT(kind_ != PENDING_LOOP_HEADER); michael@0: JS_ASSERT(pred != nullptr); michael@0: michael@0: if (!predecessors_.append(pred)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::inheritSlots(MBasicBlock *parent) michael@0: { michael@0: stackPosition_ = parent->stackPosition_; michael@0: copySlots(parent); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::initEntrySlots(TempAllocator &alloc) michael@0: { michael@0: // Create a resume point using our initial stack state. michael@0: entryResumePoint_ = MResumePoint::New(alloc, this, pc(), callerResumePoint(), michael@0: MResumePoint::ResumeAt); michael@0: if (!entryResumePoint_) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBasicBlock::getSlot(uint32_t index) michael@0: { michael@0: JS_ASSERT(index < stackPosition_); michael@0: return slots_[index]; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::initSlot(uint32_t slot, MDefinition *ins) michael@0: { michael@0: slots_[slot] = ins; michael@0: if (entryResumePoint()) michael@0: entryResumePoint()->setOperand(slot, ins); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::shimmySlots(int discardDepth) michael@0: { michael@0: // Move all slots above the given depth down by one, michael@0: // overwriting the MDefinition at discardDepth. michael@0: michael@0: JS_ASSERT(discardDepth < 0); michael@0: JS_ASSERT(stackPosition_ + discardDepth >= info_.firstStackSlot()); michael@0: michael@0: for (int i = discardDepth; i < -1; i++) michael@0: slots_[stackPosition_ + i] = slots_[stackPosition_ + i + 1]; michael@0: michael@0: --stackPosition_; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::linkOsrValues(MStart *start) michael@0: { michael@0: JS_ASSERT(start->startType() == MStart::StartType_Osr); michael@0: michael@0: MResumePoint *res = start->resumePoint(); michael@0: michael@0: for (uint32_t i = 0; i < stackDepth(); i++) { michael@0: MDefinition *def = slots_[i]; michael@0: if (i == info().scopeChainSlot()) { michael@0: if (def->isOsrScopeChain()) michael@0: def->toOsrScopeChain()->setResumePoint(res); michael@0: } else if (i == info().returnValueSlot()) { michael@0: if (def->isOsrReturnValue()) michael@0: def->toOsrReturnValue()->setResumePoint(res); michael@0: } else if (info().hasArguments() && i == info().argsObjSlot()) { michael@0: JS_ASSERT(def->isConstant() || def->isOsrArgumentsObject()); michael@0: JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); michael@0: if (def->isOsrArgumentsObject()) michael@0: def->toOsrArgumentsObject()->setResumePoint(res); michael@0: } else { michael@0: JS_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant() || michael@0: def->isParameter()); michael@0: michael@0: // A constant Undefined can show up here for an argument slot when the function uses michael@0: // a heavyweight argsobj, but the argument in question is stored on the scope chain. michael@0: JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); michael@0: michael@0: if (def->isOsrValue()) michael@0: def->toOsrValue()->setResumePoint(res); michael@0: else if (def->isGetArgumentsObjectArg()) michael@0: def->toGetArgumentsObjectArg()->setResumePoint(res); michael@0: else if (def->isParameter()) michael@0: def->toParameter()->setResumePoint(res); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setSlot(uint32_t slot, MDefinition *ins) michael@0: { michael@0: slots_[slot] = ins; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setVariable(uint32_t index) michael@0: { michael@0: JS_ASSERT(stackPosition_ > info_.firstStackSlot()); michael@0: setSlot(index, slots_[stackPosition_ - 1]); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setArg(uint32_t arg) michael@0: { michael@0: setVariable(info_.argSlot(arg)); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setLocal(uint32_t local) michael@0: { michael@0: setVariable(info_.localSlot(local)); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setSlot(uint32_t slot) michael@0: { michael@0: setVariable(slot); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::rewriteSlot(uint32_t slot, MDefinition *ins) michael@0: { michael@0: setSlot(slot, ins); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::rewriteAtDepth(int32_t depth, MDefinition *ins) michael@0: { michael@0: JS_ASSERT(depth < 0); michael@0: JS_ASSERT(stackPosition_ + depth >= info_.firstStackSlot()); michael@0: rewriteSlot(stackPosition_ + depth, ins); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::push(MDefinition *ins) michael@0: { michael@0: JS_ASSERT(stackPosition_ < nslots()); michael@0: slots_[stackPosition_++] = ins; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::pushVariable(uint32_t slot) michael@0: { michael@0: push(slots_[slot]); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::pushArg(uint32_t arg) michael@0: { michael@0: pushVariable(info_.argSlot(arg)); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::pushLocal(uint32_t local) michael@0: { michael@0: pushVariable(info_.localSlot(local)); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::pushSlot(uint32_t slot) michael@0: { michael@0: pushVariable(slot); michael@0: } michael@0: michael@0: MDefinition * michael@0: MBasicBlock::pop() michael@0: { michael@0: JS_ASSERT(stackPosition_ > info_.firstStackSlot()); michael@0: return slots_[--stackPosition_]; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::popn(uint32_t n) michael@0: { michael@0: JS_ASSERT(stackPosition_ - n >= info_.firstStackSlot()); michael@0: JS_ASSERT(stackPosition_ >= stackPosition_ - n); michael@0: stackPosition_ -= n; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBasicBlock::scopeChain() michael@0: { michael@0: return getSlot(info().scopeChainSlot()); michael@0: } michael@0: michael@0: MDefinition * michael@0: MBasicBlock::argumentsObject() michael@0: { michael@0: return getSlot(info().argsObjSlot()); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setScopeChain(MDefinition *scopeObj) michael@0: { michael@0: setSlot(info().scopeChainSlot(), scopeObj); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setArgumentsObject(MDefinition *argsObj) michael@0: { michael@0: setSlot(info().argsObjSlot(), argsObj); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::pick(int32_t depth) michael@0: { michael@0: // pick take an element and move it to the top. michael@0: // pick(-2): michael@0: // A B C D E michael@0: // A B D C E [ swapAt(-2) ] michael@0: // A B D E C [ swapAt(-1) ] michael@0: for (; depth < 0; depth++) michael@0: swapAt(depth); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::swapAt(int32_t depth) michael@0: { michael@0: uint32_t lhsDepth = stackPosition_ + depth - 1; michael@0: uint32_t rhsDepth = stackPosition_ + depth; michael@0: michael@0: MDefinition *temp = slots_[lhsDepth]; michael@0: slots_[lhsDepth] = slots_[rhsDepth]; michael@0: slots_[rhsDepth] = temp; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBasicBlock::peek(int32_t depth) michael@0: { michael@0: JS_ASSERT(depth < 0); michael@0: JS_ASSERT(stackPosition_ + depth >= info_.firstStackSlot()); michael@0: return getSlot(stackPosition_ + depth); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discardLastIns() michael@0: { michael@0: JS_ASSERT(lastIns_); michael@0: discard(lastIns_); michael@0: lastIns_ = nullptr; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::addFromElsewhere(MInstruction *ins) michael@0: { michael@0: JS_ASSERT(ins->block() != this); michael@0: michael@0: // Remove |ins| from its containing block. michael@0: ins->block()->instructions_.remove(ins); michael@0: michael@0: // Add it to this block. michael@0: add(ins); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::moveBefore(MInstruction *at, MInstruction *ins) michael@0: { michael@0: // Remove |ins| from the current block. michael@0: JS_ASSERT(ins->block() == this); michael@0: instructions_.remove(ins); michael@0: michael@0: // Insert into new block, which may be distinct. michael@0: // Uses and operands are untouched. michael@0: at->block()->insertBefore(at, ins); michael@0: } michael@0: michael@0: static inline void michael@0: AssertSafelyDiscardable(MDefinition *def) michael@0: { michael@0: #ifdef DEBUG michael@0: // Instructions captured by resume points cannot be safely discarded, since michael@0: // they are necessary for interpreter frame reconstruction in case of bailout. michael@0: JS_ASSERT(!def->hasUses()); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discard(MInstruction *ins) michael@0: { michael@0: AssertSafelyDiscardable(ins); michael@0: for (size_t i = 0, e = ins->numOperands(); i < e; i++) michael@0: ins->discardOperand(i); michael@0: michael@0: instructions_.remove(ins); michael@0: } michael@0: michael@0: MInstructionIterator michael@0: MBasicBlock::discardAt(MInstructionIterator &iter) michael@0: { michael@0: AssertSafelyDiscardable(*iter); michael@0: for (size_t i = 0, e = iter->numOperands(); i < e; i++) michael@0: iter->discardOperand(i); michael@0: michael@0: return instructions_.removeAt(iter); michael@0: } michael@0: michael@0: MInstructionReverseIterator michael@0: MBasicBlock::discardAt(MInstructionReverseIterator &iter) michael@0: { michael@0: AssertSafelyDiscardable(*iter); michael@0: for (size_t i = 0, e = iter->numOperands(); i < e; i++) michael@0: iter->discardOperand(i); michael@0: michael@0: return instructions_.removeAt(iter); michael@0: } michael@0: michael@0: MDefinitionIterator michael@0: MBasicBlock::discardDefAt(MDefinitionIterator &old) michael@0: { michael@0: MDefinitionIterator iter(old); michael@0: michael@0: if (iter.atPhi()) michael@0: iter.phiIter_ = iter.block_->discardPhiAt(iter.phiIter_); michael@0: else michael@0: iter.iter_ = iter.block_->discardAt(iter.iter_); michael@0: michael@0: return iter; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discardAllInstructions() michael@0: { michael@0: for (MInstructionIterator iter = begin(); iter != end(); ) { michael@0: for (size_t i = 0, e = iter->numOperands(); i < e; i++) michael@0: iter->discardOperand(i); michael@0: iter = instructions_.removeAt(iter); michael@0: } michael@0: lastIns_ = nullptr; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discardAllPhiOperands() michael@0: { michael@0: for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) { michael@0: MPhi *phi = *iter; michael@0: for (size_t i = 0, e = phi->numOperands(); i < e; i++) michael@0: phi->discardOperand(i); michael@0: } michael@0: michael@0: for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++) michael@0: (*pred)->setSuccessorWithPhis(nullptr, 0); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discardAllPhis() michael@0: { michael@0: discardAllPhiOperands(); michael@0: phis_.clear(); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::discardAllResumePoints(bool discardEntry) michael@0: { michael@0: for (MResumePointIterator iter = resumePointsBegin(); iter != resumePointsEnd(); ) { michael@0: MResumePoint *rp = *iter; michael@0: if (rp == entryResumePoint() && !discardEntry) { michael@0: iter++; michael@0: } else { michael@0: rp->discardUses(); michael@0: iter = resumePoints_.removeAt(iter); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::insertBefore(MInstruction *at, MInstruction *ins) michael@0: { michael@0: JS_ASSERT(at->block() == this); michael@0: ins->setBlock(this); michael@0: graph().allocDefinitionId(ins); michael@0: instructions_.insertBefore(at, ins); michael@0: ins->setTrackedPc(at->trackedPc()); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::insertAfter(MInstruction *at, MInstruction *ins) michael@0: { michael@0: JS_ASSERT(at->block() == this); michael@0: ins->setBlock(this); michael@0: graph().allocDefinitionId(ins); michael@0: instructions_.insertAfter(at, ins); michael@0: ins->setTrackedPc(at->trackedPc()); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::add(MInstruction *ins) michael@0: { michael@0: JS_ASSERT(!lastIns_); michael@0: ins->setBlock(this); michael@0: graph().allocDefinitionId(ins); michael@0: instructions_.pushBack(ins); michael@0: ins->setTrackedPc(trackedPc_); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::end(MControlInstruction *ins) michael@0: { michael@0: JS_ASSERT(!lastIns_); // Existing control instructions should be removed first. michael@0: JS_ASSERT(ins); michael@0: add(ins); michael@0: lastIns_ = ins; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::addPhi(MPhi *phi) michael@0: { michael@0: phis_.pushBack(phi); michael@0: phi->setBlock(this); michael@0: graph().allocDefinitionId(phi); michael@0: } michael@0: michael@0: MPhiIterator michael@0: MBasicBlock::discardPhiAt(MPhiIterator &at) michael@0: { michael@0: JS_ASSERT(!phis_.empty()); michael@0: michael@0: for (size_t i = 0, e = at->numOperands(); i < e; i++) michael@0: at->discardOperand(i); michael@0: michael@0: MPhiIterator result = phis_.removeAt(at); michael@0: michael@0: if (phis_.empty()) { michael@0: for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++) michael@0: (*pred)->setSuccessorWithPhis(nullptr, 0); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::addPredecessor(TempAllocator &alloc, MBasicBlock *pred) michael@0: { michael@0: return addPredecessorPopN(alloc, pred, 0); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::addPredecessorPopN(TempAllocator &alloc, MBasicBlock *pred, uint32_t popped) michael@0: { michael@0: JS_ASSERT(pred); michael@0: JS_ASSERT(predecessors_.length() > 0); michael@0: michael@0: // Predecessors must be finished, and at the correct stack depth. michael@0: JS_ASSERT(pred->lastIns_); michael@0: JS_ASSERT(pred->stackPosition_ == stackPosition_ + popped); michael@0: michael@0: for (uint32_t i = 0; i < stackPosition_; i++) { michael@0: MDefinition *mine = getSlot(i); michael@0: MDefinition *other = pred->getSlot(i); michael@0: michael@0: if (mine != other) { michael@0: // If the current instruction is a phi, and it was created in this michael@0: // basic block, then we have already placed this phi and should michael@0: // instead append to its operands. michael@0: if (mine->isPhi() && mine->block() == this) { michael@0: JS_ASSERT(predecessors_.length()); michael@0: if (!mine->toPhi()->addInputSlow(other)) michael@0: return false; michael@0: } else { michael@0: // Otherwise, create a new phi node. michael@0: MPhi *phi; michael@0: if (mine->type() == other->type()) michael@0: phi = MPhi::New(alloc, i, mine->type()); michael@0: else michael@0: phi = MPhi::New(alloc, i); michael@0: addPhi(phi); michael@0: michael@0: // Prime the phi for each predecessor, so input(x) comes from michael@0: // predecessor(x). michael@0: if (!phi->reserveLength(predecessors_.length() + 1)) michael@0: return false; michael@0: michael@0: for (size_t j = 0; j < predecessors_.length(); j++) { michael@0: JS_ASSERT(predecessors_[j]->getSlot(i) == mine); michael@0: phi->addInput(mine); michael@0: } michael@0: phi->addInput(other); michael@0: michael@0: setSlot(i, phi); michael@0: if (entryResumePoint()) michael@0: entryResumePoint()->replaceOperand(i, phi); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return predecessors_.append(pred); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::addPredecessorWithoutPhis(MBasicBlock *pred) michael@0: { michael@0: // Predecessors must be finished. michael@0: JS_ASSERT(pred && pred->lastIns_); michael@0: return predecessors_.append(pred); michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::addImmediatelyDominatedBlock(MBasicBlock *child) michael@0: { michael@0: return immediatelyDominated_.append(child); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::assertUsesAreNotWithin(MUseIterator use, MUseIterator end) michael@0: { michael@0: #ifdef DEBUG michael@0: for (; use != end; use++) { michael@0: JS_ASSERT_IF(use->consumer()->isDefinition(), michael@0: use->consumer()->toDefinition()->block()->id() < id()); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::dominates(const MBasicBlock *other) const michael@0: { michael@0: uint32_t high = domIndex() + numDominated(); michael@0: uint32_t low = domIndex(); michael@0: return other->domIndex() >= low && other->domIndex() <= high; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::setUnreachable() michael@0: { michael@0: unreachable_ = true; michael@0: size_t numDom = numImmediatelyDominatedBlocks(); michael@0: for (size_t d = 0; d < numDom; d++) michael@0: getImmediatelyDominatedBlock(d)->unreachable_ = true; michael@0: } michael@0: michael@0: AbortReason michael@0: MBasicBlock::setBackedge(MBasicBlock *pred) michael@0: { michael@0: // Predecessors must be finished, and at the correct stack depth. michael@0: JS_ASSERT(lastIns_); michael@0: JS_ASSERT(pred->lastIns_); michael@0: JS_ASSERT(pred->stackDepth() == entryResumePoint()->stackDepth()); michael@0: michael@0: // We must be a pending loop header michael@0: JS_ASSERT(kind_ == PENDING_LOOP_HEADER); michael@0: michael@0: bool hadTypeChange = false; michael@0: michael@0: // Add exit definitions to each corresponding phi at the entry. michael@0: for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) { michael@0: MPhi *entryDef = *phi; michael@0: MDefinition *exitDef = pred->slots_[entryDef->slot()]; michael@0: michael@0: // Assert that we already placed phis for each slot. michael@0: JS_ASSERT(entryDef->block() == this); michael@0: michael@0: if (entryDef == exitDef) { michael@0: // If the exit def is the same as the entry def, make a redundant michael@0: // phi. Since loop headers have exactly two incoming edges, we michael@0: // know that that's just the first input. michael@0: // michael@0: // Note that we eliminate later rather than now, to avoid any michael@0: // weirdness around pending continue edges which might still hold michael@0: // onto phis. michael@0: exitDef = entryDef->getOperand(0); michael@0: } michael@0: michael@0: bool typeChange = false; michael@0: michael@0: if (!entryDef->addInputSlow(exitDef, &typeChange)) michael@0: return AbortReason_Alloc; michael@0: michael@0: hadTypeChange |= typeChange; michael@0: michael@0: JS_ASSERT(entryDef->slot() < pred->stackDepth()); michael@0: setSlot(entryDef->slot(), entryDef); michael@0: } michael@0: michael@0: if (hadTypeChange) { michael@0: for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) michael@0: phi->removeOperand(phi->numOperands() - 1); michael@0: return AbortReason_Disable; michael@0: } michael@0: michael@0: // We are now a loop header proper michael@0: kind_ = LOOP_HEADER; michael@0: michael@0: if (!predecessors_.append(pred)) michael@0: return AbortReason_Alloc; michael@0: michael@0: return AbortReason_NoAbort; michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::setBackedgeAsmJS(MBasicBlock *pred) michael@0: { michael@0: // Predecessors must be finished, and at the correct stack depth. michael@0: JS_ASSERT(lastIns_); michael@0: JS_ASSERT(pred->lastIns_); michael@0: JS_ASSERT(stackDepth() == pred->stackDepth()); michael@0: michael@0: // We must be a pending loop header michael@0: JS_ASSERT(kind_ == PENDING_LOOP_HEADER); michael@0: michael@0: // Add exit definitions to each corresponding phi at the entry. michael@0: for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) { michael@0: MPhi *entryDef = *phi; michael@0: MDefinition *exitDef = pred->getSlot(entryDef->slot()); michael@0: michael@0: // Assert that we already placed phis for each slot. michael@0: JS_ASSERT(entryDef->block() == this); michael@0: michael@0: // Assert that the phi already has the correct type. michael@0: JS_ASSERT(entryDef->type() == exitDef->type()); michael@0: JS_ASSERT(entryDef->type() != MIRType_Value); michael@0: michael@0: if (entryDef == exitDef) { michael@0: // If the exit def is the same as the entry def, make a redundant michael@0: // phi. Since loop headers have exactly two incoming edges, we michael@0: // know that that's just the first input. michael@0: // michael@0: // Note that we eliminate later rather than now, to avoid any michael@0: // weirdness around pending continue edges which might still hold michael@0: // onto phis. michael@0: exitDef = entryDef->getOperand(0); michael@0: } michael@0: michael@0: // MBasicBlock::NewAsmJS calls reserveLength(2) for loop header phis. michael@0: entryDef->addInput(exitDef); michael@0: michael@0: JS_ASSERT(entryDef->slot() < pred->stackDepth()); michael@0: setSlot(entryDef->slot(), entryDef); michael@0: } michael@0: michael@0: // We are now a loop header proper michael@0: kind_ = LOOP_HEADER; michael@0: michael@0: return predecessors_.append(pred); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::clearLoopHeader() michael@0: { michael@0: JS_ASSERT(isLoopHeader()); michael@0: kind_ = NORMAL; michael@0: } michael@0: michael@0: size_t michael@0: MBasicBlock::numSuccessors() const michael@0: { michael@0: JS_ASSERT(lastIns()); michael@0: return lastIns()->numSuccessors(); michael@0: } michael@0: michael@0: MBasicBlock * michael@0: MBasicBlock::getSuccessor(size_t index) const michael@0: { michael@0: JS_ASSERT(lastIns()); michael@0: return lastIns()->getSuccessor(index); michael@0: } michael@0: michael@0: size_t michael@0: MBasicBlock::getSuccessorIndex(MBasicBlock *block) const michael@0: { michael@0: JS_ASSERT(lastIns()); michael@0: for (size_t i = 0; i < numSuccessors(); i++) { michael@0: if (getSuccessor(i) == block) michael@0: return i; michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("Invalid successor"); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::replaceSuccessor(size_t pos, MBasicBlock *split) michael@0: { michael@0: JS_ASSERT(lastIns()); michael@0: michael@0: // Note, during split-critical-edges, successors-with-phis is not yet set. michael@0: // During PAA, this case is handled before we enter. michael@0: JS_ASSERT_IF(successorWithPhis_, successorWithPhis_ != getSuccessor(pos)); michael@0: michael@0: lastIns()->replaceSuccessor(pos, split); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::replacePredecessor(MBasicBlock *old, MBasicBlock *split) michael@0: { michael@0: for (size_t i = 0; i < numPredecessors(); i++) { michael@0: if (getPredecessor(i) == old) { michael@0: predecessors_[i] = split; michael@0: michael@0: #ifdef DEBUG michael@0: // The same block should not appear twice in the predecessor list. michael@0: for (size_t j = i; j < numPredecessors(); j++) michael@0: JS_ASSERT(predecessors_[j] != old); michael@0: #endif michael@0: michael@0: return; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("predecessor was not found"); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::clearDominatorInfo() michael@0: { michael@0: setImmediateDominator(nullptr); michael@0: immediatelyDominated_.clear(); michael@0: numDominated_ = 0; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::removePredecessor(MBasicBlock *pred) michael@0: { michael@0: JS_ASSERT(numPredecessors() >= 2); michael@0: michael@0: for (size_t i = 0; i < numPredecessors(); i++) { michael@0: if (getPredecessor(i) != pred) michael@0: continue; michael@0: michael@0: // Adjust phis. Note that this can leave redundant phis michael@0: // behind. michael@0: if (!phisEmpty()) { michael@0: JS_ASSERT(pred->successorWithPhis()); michael@0: JS_ASSERT(pred->positionInPhiSuccessor() == i); michael@0: for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) michael@0: iter->removeOperand(i); michael@0: for (size_t j = i+1; j < numPredecessors(); j++) michael@0: getPredecessor(j)->setSuccessorWithPhis(this, j - 1); michael@0: } michael@0: michael@0: // Remove from pred list. michael@0: MBasicBlock **ptr = predecessors_.begin() + i; michael@0: predecessors_.erase(ptr); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("predecessor was not found"); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::inheritPhis(MBasicBlock *header) michael@0: { michael@0: for (MPhiIterator iter = header->phisBegin(); iter != header->phisEnd(); iter++) { michael@0: MPhi *phi = *iter; michael@0: JS_ASSERT(phi->numOperands() == 2); michael@0: michael@0: // The entry definition is always the leftmost input to the phi. michael@0: MDefinition *entryDef = phi->getOperand(0); michael@0: MDefinition *exitDef = getSlot(phi->slot()); michael@0: michael@0: if (entryDef != exitDef) michael@0: continue; michael@0: michael@0: // If the entryDef is the same as exitDef, then we must propagate the michael@0: // phi down to this successor. This chance was missed as part of michael@0: // setBackedge() because exits are not captured in resume points. michael@0: setSlot(phi->slot(), phi); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MBasicBlock::specializePhis() michael@0: { michael@0: for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) { michael@0: MPhi *phi = *iter; michael@0: if (!phi->specializeType()) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::dumpStack(FILE *fp) michael@0: { michael@0: #ifdef DEBUG michael@0: fprintf(fp, " %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next"); michael@0: fprintf(fp, "-------------------------------------------\n"); michael@0: for (uint32_t i = 0; i < stackPosition_; i++) { michael@0: fprintf(fp, " %-3d", i); michael@0: fprintf(fp, " %-16p\n", (void *)slots_[i]); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: MTest * michael@0: MBasicBlock::immediateDominatorBranch(BranchDirection *pdirection) michael@0: { michael@0: *pdirection = FALSE_BRANCH; michael@0: michael@0: if (numPredecessors() != 1) michael@0: return nullptr; michael@0: michael@0: MBasicBlock *dom = immediateDominator(); michael@0: if (dom != getPredecessor(0)) michael@0: return nullptr; michael@0: michael@0: // Look for a trailing MTest branching to this block. michael@0: MInstruction *ins = dom->lastIns(); michael@0: if (ins->isTest()) { michael@0: MTest *test = ins->toTest(); michael@0: michael@0: JS_ASSERT(test->ifTrue() == this || test->ifFalse() == this); michael@0: if (test->ifTrue() == this && test->ifFalse() == this) michael@0: return nullptr; michael@0: michael@0: *pdirection = (test->ifTrue() == this) ? TRUE_BRANCH : FALSE_BRANCH; michael@0: return test; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: MIRGraph::dump(FILE *fp) michael@0: { michael@0: #ifdef DEBUG michael@0: for (MBasicBlockIterator iter(begin()); iter != end(); iter++) { michael@0: fprintf(fp, "block%d:\n", iter->id()); michael@0: iter->dump(fp); michael@0: fprintf(fp, "\n"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MIRGraph::dump() michael@0: { michael@0: dump(stderr); michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::dump(FILE *fp) michael@0: { michael@0: #ifdef DEBUG michael@0: for (MPhiIterator iter(phisBegin()); iter != phisEnd(); iter++) { michael@0: iter->dump(fp); michael@0: } michael@0: for (MInstructionIterator iter(begin()); iter != end(); iter++) { michael@0: iter->dump(fp); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MBasicBlock::dump() michael@0: { michael@0: dump(stderr); michael@0: }