SSATmp* TraceBuilder::optimizeWork(IRInstruction* inst, const folly::Optional<IdomVector>& idoms) { // Since some of these optimizations inspect tracked state, we don't // perform any of them on non-main traces. if (m_savedTraces.size() > 0) return nullptr; static DEBUG_ONLY __thread int instNest = 0; if (debug) ++instNest; SCOPE_EXIT { if (debug) --instNest; }; DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); }; FTRACE(1, "{}{}\n", indent(), inst->toString()); // First pass of tracebuilder optimizations try to replace an // instruction based on tracked state before we do anything else. // May mutate the IRInstruction in place (and return nullptr) or // return an SSATmp*. if (SSATmp* preOpt = preOptimize(inst)) { FTRACE(1, " {}preOptimize returned: {}\n", indent(), preOpt->inst()->toString()); return preOpt; } if (inst->op() == Nop) return nullptr; // copy propagation on inst source operands copyProp(inst); SSATmp* result = nullptr; if (m_enableCse && inst->canCSE()) { result = cseLookup(inst, idoms); if (result) { // Found a dominating instruction that can be used instead of inst FTRACE(1, " {}cse found: {}\n", indent(), result->inst()->toString()); assert(!inst->consumesReferences()); if (inst->producesReference()) { // Replace with an IncRef FTRACE(1, " {}cse of refcount-producing instruction\n", indent()); return gen(IncRef, result); } else { return result; } } } if (m_enableSimplification) { result = m_simplifier.simplify(inst); if (result) { // Found a simpler instruction that can be used instead of inst FTRACE(1, " {}simplification returned: {}\n", indent(), result->inst()->toString()); assert(inst->hasDst()); return result; } } return nullptr; }
SSATmp* TraceBuilder::optimizeWork(IRInstruction* inst) { static DEBUG_ONLY __thread int instNest = 0; if (debug) ++instNest; SCOPE_EXIT { if (debug) --instNest; }; DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); }; FTRACE(1, "{}{}\n", indent(), inst->toString()); // First pass of tracebuilder optimizations try to replace an // instruction based on tracked state before we do anything else. // May mutate the IRInstruction in place (and return nullptr) or // return an SSATmp*. if (SSATmp* preOpt = preOptimize(inst)) { FTRACE(1, " {}preOptimize returned: {}\n", indent(), preOpt->inst()->toString()); return preOpt; } if (inst->op() == Nop) return nullptr; // copy propagation on inst source operands copyProp(inst); SSATmp* result = nullptr; if (m_enableCse && inst->canCSE()) { result = cseLookup(inst); if (result) { // Found a dominating instruction that can be used instead of inst FTRACE(1, " {}cse found: {}\n", indent(), result->inst()->toString()); return result; } } if (m_enableSimplification) { result = m_simplifier.simplify(inst); if (result) { // Found a simpler instruction that can be used instead of inst FTRACE(1, " {}simplification returned: {}\n", indent(), result->inst()->toString()); assert(inst->hasDst()); return result; } } return nullptr; }
SSATmp* IRBuilder::optimizeWork(IRInstruction* inst, const folly::Optional<IdomVector>& idoms) { // Since some of these optimizations inspect tracked state, we don't // perform any of them on non-main traces. if (m_savedBlocks.size() > 0) return nullptr; static DEBUG_ONLY __thread int instNest = 0; if (debug) ++instNest; SCOPE_EXIT { if (debug) --instNest; }; DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); }; FTRACE(1, "optimizing {}{}\n", indent(), inst->toString()); // First pass of IRBuilder optimizations try to replace an // instruction based on tracked state before we do anything else. // May mutate the IRInstruction in place (and return nullptr) or // return an SSATmp*. if (SSATmp* preOpt = preOptimize(inst)) { FTRACE(1, " {}preOptimize returned: {}\n", indent(), preOpt->inst()->toString()); return preOpt; } if (inst->op() == Nop) return nullptr; // copy propagation on inst source operands copyProp(inst); SSATmp* result = nullptr; if (m_enableSimplification) { result = m_simplifier.simplify(inst); if (result) { inst = result->inst(); if (inst->producesReference(0)) { // This effectively prevents CSE from kicking in below, which // would replace the instruction with an IncRef. That is // correct if the simplifier morphed the instruction, but it's // incorrect if the simplifier returned one of original // instruction sources. We currently have no way to // distinguish the two cases, so we prevent CSE completely for // now. return result; } } } if (m_state.enableCse() && inst->canCSE()) { SSATmp* cseResult = m_state.cseLookup(inst, idoms); if (cseResult) { // Found a dominating instruction that can be used instead of inst FTRACE(1, " {}cse found: {}\n", indent(), cseResult->inst()->toString()); assert(!inst->consumesReferences()); if (inst->producesReference(0)) { // Replace with an IncRef FTRACE(1, " {}cse of refcount-producing instruction\n", indent()); gen(IncRef, cseResult); } return cseResult; } } return result; }
SSATmp* TraceBuilder::optimizeWork(IRInstruction* inst, const folly::Optional<IdomVector>& idoms) { // Since some of these optimizations inspect tracked state, we don't // perform any of them on non-main traces. if (m_savedTraces.size() > 0) return nullptr; static DEBUG_ONLY __thread int instNest = 0; if (debug) ++instNest; SCOPE_EXIT { if (debug) --instNest; }; DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); }; FTRACE(1, "{}{}\n", indent(), inst->toString()); // turn off ActRec optimization for instructions that will require a frame if (m_state.needsFPAnchor(inst)) { m_state.setHasFPAnchor(); always_assert(m_state.fp() != nullptr); gen(InlineFPAnchor, m_state.fp()); FTRACE(2, "Anchor for: {}\n", inst->toString()); } // First pass of tracebuilder optimizations try to replace an // instruction based on tracked state before we do anything else. // May mutate the IRInstruction in place (and return nullptr) or // return an SSATmp*. if (SSATmp* preOpt = preOptimize(inst)) { FTRACE(1, " {}preOptimize returned: {}\n", indent(), preOpt->inst()->toString()); return preOpt; } if (inst->op() == Nop) return nullptr; // copy propagation on inst source operands copyProp(inst); SSATmp* result = nullptr; if (m_state.enableCse() && inst->canCSE()) { result = m_state.cseLookup(inst, idoms); if (result) { // Found a dominating instruction that can be used instead of inst FTRACE(1, " {}cse found: {}\n", indent(), result->inst()->toString()); // CheckType and AssertType are special. They're marked as both PRc and // CRc to placate our refcounting optimizations, for for the purposes of // CSE they're neither. if (inst->is(CheckType, AssertType)) { return result; } assert(!inst->consumesReferences()); if (inst->producesReference()) { // Replace with an IncRef FTRACE(1, " {}cse of refcount-producing instruction\n", indent()); return gen(IncRef, result); } else { return result; } } } if (m_enableSimplification) { result = m_simplifier.simplify(inst); if (result) { // Found a simpler instruction that can be used instead of inst FTRACE(1, " {}simplification returned: {}\n", indent(), result->inst()->toString()); assert(inst->hasDst()); return result; } } return nullptr; }
/* * Performs simplification and CSE on the input instruction. If the input * instruction has a dest, this will return an SSATmp that represents the same * value as dst(0) of the input instruction. If the input instruction has no * dest, this will return nullptr. * * The caller never needs to clone or append; all this has been done. */ SSATmp* IRBuilder::optimizeInst(IRInstruction* inst, CloneFlag doClone, Block* srcBlock, const folly::Optional<IdomVector>& idoms) { static DEBUG_ONLY __thread int instNest = 0; if (debug) ++instNest; SCOPE_EXIT { if (debug) --instNest; }; DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); }; auto doCse = [&] (IRInstruction* cseInput) -> SSATmp* { if (m_state.enableCse() && cseInput->canCSE()) { SSATmp* cseResult = m_state.cseLookup(cseInput, srcBlock, idoms); if (cseResult) { // Found a dominating instruction that can be used instead of input FTRACE(1, " {}cse found: {}\n", indent(), cseResult->inst()->toString()); assert(!cseInput->consumesReferences()); if (cseInput->producesReference(0)) { // Replace with an IncRef FTRACE(1, " {}cse of refcount-producing instruction\n", indent()); gen(IncRef, cseResult); } return cseResult; } } return nullptr; }; auto cloneAndAppendOriginal = [&] () -> SSATmp* { if (inst->op() == Nop) return nullptr; if (auto cseResult = doCse(inst)) { return cseResult; } if (doClone == CloneFlag::Yes) { inst = m_unit.cloneInstruction(inst); } appendInstruction(inst); return inst->dst(0); }; // Since some of these optimizations inspect tracked state, we don't // perform any of them on non-main traces. if (m_savedBlocks.size() > 0) return cloneAndAppendOriginal(); // copy propagation on inst source operands copyProp(inst); // First pass of IRBuilder optimizations try to replace an // instruction based on tracked state before we do anything else. // May mutate the IRInstruction in place (and return nullptr) or // return an SSATmp*. if (SSATmp* preOpt = preOptimize(inst)) { FTRACE(1, " {}preOptimize returned: {}\n", indent(), preOpt->inst()->toString()); return preOpt; } if (inst->op() == Nop) return cloneAndAppendOriginal(); if (!m_enableSimplification) { return cloneAndAppendOriginal(); } auto simpResult = m_simplifier.simplify(inst, shouldConstrainGuards()); // These are the possible outputs: // // ([], nullptr): no optimization possible. Use original inst. // // ([], non-nullptr): passing through a src. Don't CSE. // // ([X, ...], Y): throw away input instruction, append 'X, ...' (CSEing // as we go), return Y. if (!simpResult.instrs.empty()) { // New instructions were generated. Append the new ones, filtering out Nops. for (auto* newInst : simpResult.instrs) { assert(!newInst->isTransient()); if (newInst->op() == Nop) continue; auto cseResult = doCse(newInst); if (cseResult) { appendInstruction(m_unit.mov(newInst->dst(), cseResult, newInst->marker())); } else { appendInstruction(newInst); } } return simpResult.dst; } // No new instructions were generated. Either simplification didn't do // anything, or we're using some other instruction's dst instead of our own. if (simpResult.dst) { // We're using some other instruction's output. Don't append anything, and // don't do any CSE. assert(simpResult.dst->inst() != inst); return simpResult.dst; } // No simplification happened. return cloneAndAppendOriginal(); }