void TraceBuilder::appendBlock(Block* block) { if (!m_curTrace->back()->back()->isTerminal()) { // previous instruction falls through; merge current state with block. saveState(block); } m_curTrace->push_back(block); useState(block); }
void TraceBuilder::endInlining() { auto calleeAR = m_fpValue; gen(InlineReturn, calleeAR); useState(std::move(m_inlineSavedStates.back())); m_inlineSavedStates.pop_back(); // See the comment in beginInlining about generator frames. if (m_curFunc->getValFunc()->isGenerator()) { gen(ReDefGeneratorSP, StackOffset(m_spOffset), m_spValue); } else { gen(ReDefSP, StackOffset(m_spOffset), m_fpValue, m_spValue); } }
/* * reoptimize() runs a trace through a second pass of TraceBuilder * optimizations, like this: * * reset state. * move all blocks to a temporary list. * compute immediate dominators. * for each block in trace order: * if we have a snapshot state for this block: * clear cse entries that don't dominate this block. * use snapshot state. * move all instructions to a temporary list. * for each instruction: * optimizeWork - do CSE and simplify again * if not simplified: * append existing instruction and update state. * else: * if the instruction has a result, insert a mov from the * simplified tmp to the original tmp and discard the instruction. * if the last conditional branch was turned into a jump, remove the * fall-through edge to the next block. */ void TraceBuilder::reoptimize() { FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n"); SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); }; assert(m_curTrace == m_mainTrace.get()); assert(m_savedTraces.empty()); assert(m_inlineSavedStates.empty()); m_enableCse = RuntimeOption::EvalHHIRCse; m_enableSimplification = RuntimeOption::EvalHHIRSimplification; if (!m_enableCse && !m_enableSimplification) return; if (m_mainTrace->blocks().size() > RuntimeOption::EvalHHIRSimplificationMaxBlocks) { // TODO CSEHash::filter is very slow for large block sizes // t2135219 should address that return; } BlockList sortedBlocks = rpoSortCfg(m_mainTrace.get(), m_irFactory); auto const idoms = findDominators(sortedBlocks); clearTrackedState(); auto blocks = std::move(m_mainTrace->blocks()); assert(m_mainTrace->blocks().empty()); while (!blocks.empty()) { Block* block = blocks.front(); blocks.pop_front(); assert(block->trace() == m_mainTrace.get()); FTRACE(5, "Block: {}\n", block->id()); m_mainTrace->push_back(block); if (m_snapshots[block]) { useState(block); } auto instructions = std::move(block->instrs()); assert(block->empty()); while (!instructions.empty()) { auto *inst = &instructions.front(); instructions.pop_front(); // last attempt to elide ActRecs, if we still need the InlineFPAnchor // it will be added back to the trace when we re-add instructions that // rely on it if (inst->op() == InlineFPAnchor) { continue; } // merging state looks at the current marker, and optimizeWork // below may create new instructions. Use the marker from this // instruction. assert(inst->marker().valid()); setMarker(inst->marker()); auto const tmp = optimizeWork(inst, idoms); // Can generate new instrs! if (!tmp) { // Could not optimize; keep the old instruction appendInstruction(inst, block); updateTrackedState(inst); continue; } SSATmp* dst = inst->dst(); if (dst->type() != Type::None && dst != tmp) { // The result of optimization has a different destination than the inst. // Generate a mov(tmp->dst) to get result into dst. If we get here then // assume the last instruction in the block isn't a guard. If it was, // we would have to insert the mov on the fall-through edge. assert(block->empty() || !block->back()->isBlockEnd()); IRInstruction* mov = m_irFactory.mov(dst, tmp, inst->marker()); appendInstruction(mov, block); updateTrackedState(mov); } // Not re-adding inst; remove the inst->taken edge if (inst->taken()) inst->setTaken(nullptr); } if (block->back()->isTerminal()) { // Could have converted a conditional branch to Jmp; clear next. block->setNext(nullptr); } else { // if the last instruction was a branch, we already saved state // for the target in updateTrackedState(). Now save state for // the fall-through path. saveState(block->next()); } } }
void TraceBuilder::useState(Block* block) { assert(m_snapshots[block]); std::unique_ptr<State> state(m_snapshots[block]); m_snapshots[block] = nullptr; useState(std::move(state)); }
/* * reoptimize() runs a trace through a second pass of TraceBuilder * optimizations, like this: * * reset state. * move all blocks to a temporary list. * compute immediate dominators. * for each block in trace order: * if we have a snapshot state for this block: * clear cse entries that don't dominate this block. * use snapshot state. * move all instructions to a temporary list. * for each instruction: * optimizeWork - do CSE and simplify again * if not simplified: * append existing instruction and update state. * else: * if the instruction has a result, insert a mov from the * simplified tmp to the original tmp and discard the instruction. * if the last conditional branch was turned into a jump, remove the * fall-through edge to the next block. */ void TraceBuilder::reoptimize() { m_enableCse = RuntimeOption::EvalHHIRCse; m_enableSimplification = RuntimeOption::EvalHHIRSimplification; if (!m_enableCse && !m_enableSimplification) return; if (m_trace->getBlocks().size() > RuntimeOption::EvalHHIRSimplificationMaxBlocks) { // TODO CSEHash::filter is very slow for large block sizes // t2135219 should address that return; } BlockList sortedBlocks = sortCfg(m_trace.get(), m_irFactory); IdomVector idoms = findDominators(sortedBlocks); clearTrackedState(); auto blocks = std::move(m_trace->getBlocks()); assert(m_trace->getBlocks().empty()); while (!blocks.empty()) { Block* block = blocks.front(); blocks.pop_front(); assert(block->getTrace() == m_trace.get()); m_trace->push_back(block); if (m_snapshots[block]) { useState(block); m_cseHash.filter(block, idoms); } auto instructions = std::move(block->getInstrs()); assert(block->empty()); while (!instructions.empty()) { auto *inst = &instructions.front(); instructions.pop_front(); SSATmp* tmp = optimizeWork(inst); // Can generate new instrs! if (!tmp) { // Could not optimize; keep the old instruction appendInstruction(inst, block); updateTrackedState(inst); continue; } SSATmp* dst = inst->getDst(); if (dst->type() != Type::None && dst != tmp) { // The result of optimization has a different destination than the inst. // Generate a mov(tmp->dst) to get result into dst. If we get here then // assume the last instruction in the block isn't a guard. If it was, // we would have to insert the mov on the fall-through edge. assert(!block->back()->isBlockEnd()); IRInstruction* mov = m_irFactory.mov(dst, tmp); appendInstruction(mov, block); updateTrackedState(mov); } // Not re-adding inst; remove the inst->taken edge if (inst->getTaken()) inst->setTaken(nullptr); } if (block->back()->isTerminal()) { // Could have converted a conditional branch to Jmp; clear next. block->setNext(nullptr); } else { // if the last instruction was a branch, we already saved state // for the target in updateTrackedState(). Now save state for // the fall-through path. saveState(block->getNext()); } } }