void TraceBuilder::appendInstruction(IRInstruction* inst) { if (m_curWhere) { // We have a specific position to insert instructions. assert(!inst->isBlockEnd()); auto& it = m_curWhere.get(); it = m_curBlock->insert(it, inst); ++it; return; } Block* block = m_curTrace->back(); if (!block->empty()) { IRInstruction* prev = block->back(); if (prev->isBlockEnd()) { // start a new block Block* next = m_irFactory.defBlock(m_curFunc->getValFunc()); m_curTrace->push_back(next); if (!prev->isTerminal()) { // new block is reachable from old block so link it. block->setNext(next); } block = next; } } appendInstruction(inst, block); updateTrackedState(inst); }
void TraceBuilder::appendInstruction(IRInstruction* inst) { if (m_curWhere) { // We have a specific position to insert instructions. assert(!inst->isBlockEnd()); auto& it = m_curWhere.get(); it = m_curBlock->insert(it, inst); ++it; return; } Block* block = m_curTrace->back(); if (!block->empty()) { IRInstruction* prev = &block->back(); if (prev->isBlockEnd()) { // start a new block Block* next = m_unit.defBlock(); FTRACE(2, "lazily adding B{}\n", next->id()); m_curTrace->push_back(next); if (!prev->isTerminal()) { // new block is reachable from old block so link it. block->setNext(next); next->setHint(block->hint()); } block = next; } } appendInstruction(inst, block); if (m_savedTraces.empty()) { // We don't track state on non-main traces for now. t2982555 m_state.update(inst); } }
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; }
void TraceBuilder::appendInstruction(IRInstruction* inst) { Block* block = m_trace->back(); IRInstruction* prev = block->back(); if (prev->isBlockEnd()) { // start a new block Block* next = m_irFactory.defBlock(m_curFunc->getValFunc()); m_trace->push_back(next); if (!prev->isTerminal()) { // new block is reachable from old block so link it. block->setNext(next); } block = next; } appendInstruction(inst, block); updateTrackedState(inst); }
/* * 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); } } }
/* * 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(); }
/* * 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); } }
/* ------------------------------------------------------------------------------ */ void readInstructionsFromFile(char *fname){ int i; ins *ip; FILE *fp; char tk[4]={' ', '\t', '\n', '\0'}; char str[80]; char *s1, *s2, *s3, *s4, *s5; i = 0; fp = fopen(fname,"r"); if(fp == NULL){ printf(" *** Error: Cannot open (%s) as instruction input file \n",fname); Code = NULL; return; } Code = createBasicBlock(0); while(fgets(str,79,fp) != NULL){ #ifdef DEBUG // printf("read::: %s", str); #endif s1 = (char*)strtok(str, tk); if(s1 == NULL){ break; } s2 = (char*)strtok(NULL, tk); if(s2[0] != '='){ printf(" *** Error: Could not locate '=' operator in instruction as second field of input\n"); continue; } s3 = (char*)strtok(NULL, tk); s4 = (char*)strtok(NULL, tk); if(s4 != NULL){ s5 = (char*)strtok(NULL, tk); } else { s5 = NULL; } #ifdef DEBUG // printf(" parsed::: %s %s %s %s %s\n",s1, s2, s3, s4, s5); #endif ip = createInstruction(i); if(s1[0] == '*'){ setInstructionDestOperand(ip,'*'); setInstructionDest(ip, &s1[1]); } else { setInstructionDest(ip, &s1[0]); } if((s3[0] == '*') || (s3[0] == '-')){ setInstructionSource1Operand(ip, s3[0]); setInstructionSource1(ip, &s3[1]); } else { setInstructionSource1(ip, &s3[0]); } if(s4 != NULL){ setInstructionSource2Operand(ip, s4[0]); if(s5 != NULL){ setInstructionSource2(ip, s5); } else { printf(" *** Error: Was expecting a second operand in instruction %d (remove infix operand)\n",i); setInstructionSource2Operand(ip,' '); } } appendInstruction(Code,ip); #ifdef DEBUG // printf(" ...dumping instruction::\n"); // dumpInstruction(ip); // printf("\n"); #endif i++; } #ifdef DEBUG // printf(" Read %d instructions \n",i); // printBasicBlock(Code); #endif fclose(fp); }