SSATmp* IRBuilder::optimizeInst(IRInstruction* inst, CloneFlag doClone) { if (auto const tmp = optimizeWork(inst, folly::none)) { return tmp; } // Couldn't CSE or simplify the instruction; clone it and append. if (inst->op() != Nop) { if (doClone == CloneFlag::Yes) inst = m_unit.cloneInstruction(inst); appendInstruction(inst); // returns nullptr if instruction has no dest, returns the first // (possibly only) dest otherwise return inst->dst(0); } return nullptr; }
SSATmp* TraceBuilder::optimizeInst(IRInstruction* inst) { if (SSATmp* tmp = optimizeWork(inst)) { return tmp; } // Couldn't CSE or simplify the instruction; clone it and append. if (inst->op() != Nop) { inst = inst->clone(&m_irFactory); appendInstruction(inst); // returns nullptr if instruction has no dest, returns the first // (possibly only) dest otherwise return inst->getDst(0); } return nullptr; }
/* * reoptimize() runs a trace through a second pass of IRBuilder * 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 IRBuilder::reoptimize() { Timer _t("optimize_reoptimize"); FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n"); SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); }; always_assert(m_savedBlocks.empty()); always_assert(!m_curWhere); always_assert(m_state.inlineDepth() == 0); m_state.setEnableCse(RuntimeOption::EvalHHIRCse); m_enableSimplification = RuntimeOption::EvalHHIRSimplification; if (!m_state.enableCse() && !m_enableSimplification) return; setConstrainGuards(false); auto blocksIds = rpoSortCfgWithIds(m_unit); auto const idoms = findDominators(m_unit, blocksIds); m_state.clear(); for (auto* block : blocksIds.blocks) { FTRACE(5, "Block: {}\n", block->id()); m_state.startBlock(block); m_curBlock = block; auto nextBlock = block->next(); auto backMarker = block->back().marker(); auto instructions = block->moveInstrs(); assert(block->empty()); while (!instructions.empty()) { auto* inst = &instructions.front(); instructions.pop_front(); // 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); continue; } SSATmp* dst = inst->dst(); if (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()); appendInstruction(m_unit.mov(dst, tmp, inst->marker())); } if (inst->isBlockEnd()) { // We're not re-adding the block-end instruction. Unset its edges. inst->setTaken(nullptr); inst->setNext(nullptr); } } if (block->empty() || !block->back().isBlockEnd()) { // Our block-end instruction was eliminated (most likely a Jmp* converted // to a nop). Replace it with a jump to the next block. appendInstruction(m_unit.gen(Jmp, backMarker, nextBlock)); } m_state.finishBlock(block); } }
/* * 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->isMain()); assert(m_savedTraces.empty()); m_state.setEnableCse(RuntimeOption::EvalHHIRCse); m_enableSimplification = RuntimeOption::EvalHHIRSimplification; if (!m_state.enableCse() && !m_enableSimplification) return; always_assert(!m_inReoptimize); m_inReoptimize = true; BlockList sortedBlocks = rpoSortCfg(m_unit); auto const idoms = findDominators(m_unit, sortedBlocks); m_state.clear(); auto& traceBlocks = m_curTrace->blocks(); BlockList blocks(traceBlocks.begin(), traceBlocks.end()); traceBlocks.clear(); for (auto* block : blocks) { assert(block->trace() == m_curTrace); FTRACE(5, "Block: {}\n", block->id()); assert(m_curTrace->isMain()); m_state.startBlock(block); m_curTrace->push_back(block); auto instructions = std::move(block->instrs()); assert(block->empty()); while (!instructions.empty()) { auto *inst = &instructions.front(); instructions.pop_front(); m_state.setMarker(inst->marker()); // 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); m_state.update(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_unit.mov(dst, tmp, inst->marker()); appendInstruction(mov, block); m_state.update(mov); } // Not re-adding inst; remove the inst->taken edge if (inst->taken()) inst->setTaken(nullptr); } if (block->empty()) { // If all the instructions in the block were optimized away, remove it // from the trace. auto it = traceBlocks.end(); --it; assert(*it == block); m_curTrace->unlink(it); } else { if (block->back().isTerminal()) { // Could have converted a conditional branch to Jmp; clear next. block->setNext(nullptr); } m_state.finishBlock(block); } } }
/* * 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()); } } }
/* * 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()); } } }
/* * 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_savedBlocks.empty()); assert(!m_curWhere); m_state.setEnableCse(RuntimeOption::EvalHHIRCse); m_enableSimplification = RuntimeOption::EvalHHIRSimplification; if (!m_state.enableCse() && !m_enableSimplification) return; setConstrainGuards(false); BlockList sortedBlocks = rpoSortCfg(m_unit); auto const idoms = findDominators(m_unit, sortedBlocks); m_state.clear(); for (auto* block : rpoSortCfg(m_unit)) { FTRACE(5, "Block: {}\n", block->id()); m_state.startBlock(block); m_curBlock = block; auto instructions = std::move(block->instrs()); assert(block->empty()); while (!instructions.empty()) { auto *inst = &instructions.front(); instructions.pop_front(); // 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); 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_unit.mov(dst, tmp, inst->marker()); appendInstruction(mov); } if (inst->isBlockEnd()) { // Not re-adding inst; replace it with a jump to the next block. auto next = inst->next(); appendInstruction(m_unit.gen(Jmp, inst->marker(), next)); inst->setTaken(nullptr); inst->setNext(nullptr); } } assert(!block->empty()); m_state.finishBlock(block); } }