void FrameState::clear() { clearCse(); clearLocals(*this); m_frameSpansCall = false; m_spValue = m_fpValue = nullptr; m_spOffset = 0; m_thisAvailable = false; m_marker = BCMarker(); m_snapshots.clear(); assert(m_inlineSavedStates.empty()); }
/* * Merge current state into state. Frame pointers and stack depth must match. * If the stack pointer tmps are different, clear the tracked value (we can * make a new one, given fp and spOffset). * * thisIsAvailable remains true if it's true in both states. * local variable values are preserved if the match in both states. * types are combined using Type::unionOf. */ void FrameState::merge(Snapshot& state) { // cannot merge fp or spOffset state, so assert they match assert(state.fpValue == m_fpValue); assert(state.spOffset == m_spOffset); assert(state.curFunc == m_curFunc); if (state.spValue != m_spValue) { // we have two different sp definitions but we know they're equal // because spOffset matched. state.spValue = nullptr; } // this is available iff it's available in both states state.thisAvailable &= m_thisAvailable; assert(m_locals.size() == state.locals.size()); for (unsigned i = 0; i < m_locals.size(); ++i) { auto& local = state.locals[i]; // preserve local values if they're the same in both states, // This would be the place to insert phi nodes (jmps+deflabels) if we want // to avoid clearing state, which triggers a downstream reload. if (local.value != m_locals[i].value) local.value = nullptr; if (local.typeSource != m_locals[i].typeSource) local.typeSource = nullptr; local.type = Type::unionOf(local.type, m_locals[i].type); } // TODO(t3729135): If we are merging states from different bytecode // paths, we must conservatively clear the CSE table. Since the // markers may or may not have been updated, we always clear. What // we need is a global CSE algorithm. if (RuntimeOption::EvalHHIRBytecodeControlFlow) { clearCse(); } // For now, we shouldn't be merging states with different inline states. assert(m_inlineSavedStates == state.inlineSavedStates); }
void FrameState::update(const IRInstruction* inst) { FTRACE(3, "FrameState::update processing {}\n", *inst); if (auto* taken = inst->taken()) { // When we're building the IR, we append a conditional jump after // generating its target block: see emitJmpCondHelper, where we // call makeExit() before gen(JmpZero). It doesn't make sense to // update the target block state at this point, so don't. The // state doesn't have this problem during optimization passes, // because we'll always process the jump before the target block. if (!m_building || taken->empty()) save(taken); } auto const opc = inst->op(); getLocalEffects(inst, *this); switch (opc) { case DefInlineFP: trackDefInlineFP(inst); break; case InlineReturn: trackInlineReturn(inst); break; case Call: m_spValue = inst->dst(); m_frameSpansCall = true; // A call pops the ActRec and pushes a return value. m_spOffset -= kNumActRecCells; m_spOffset += 1; assert(m_spOffset >= 0); clearCse(); break; case CallArray: m_spValue = inst->dst(); m_frameSpansCall = true; // A CallArray pops the ActRec an array arg and pushes a return value. m_spOffset -= kNumActRecCells; assert(m_spOffset >= 0); clearCse(); break; case ContEnter: clearCse(); break; case DefFP: case FreeActRec: m_fpValue = inst->dst(); break; case ReDefResumableSP: m_spValue = inst->dst(); break; case ReDefSP: m_spValue = inst->dst(); m_spOffset = inst->extra<ReDefSP>()->spOffset; break; case DefInlineSP: case DefSP: m_spValue = inst->dst(); m_spOffset = inst->extra<StackOffset>()->offset; break; case AssertStk: case CastStk: case CoerceStk: case CheckStk: case GuardStk: case ExceptionBarrier: m_spValue = inst->dst(); break; case SpillStack: { m_spValue = inst->dst(); // Push the spilled values but adjust for the popped values int64_t stackAdjustment = inst->src(1)->intVal(); m_spOffset -= stackAdjustment; m_spOffset += spillValueCells(inst); break; } case SpillFrame: case CufIterSpillFrame: m_spValue = inst->dst(); m_spOffset += kNumActRecCells; break; case InterpOne: case InterpOneCF: { m_spValue = inst->dst(); auto const& extra = *inst->extra<InterpOneData>(); int64_t stackAdjustment = extra.cellsPopped - extra.cellsPushed; // push the return value if any and adjust for the popped values m_spOffset -= stackAdjustment; break; } case AssertLoc: case GuardLoc: case CheckLoc: m_fpValue = inst->dst(); break; case LdThis: m_thisAvailable = true; break; default: break; } if (inst->modifiesStack()) { m_spValue = inst->modifiedStkPtr(); } // update the CSE table if (m_enableCse && inst->canCSE()) { cseInsert(inst); } // if the instruction kills any of its sources, remove them from the // CSE table if (inst->killsSources()) { for (int i = 0; i < inst->numSrcs(); ++i) { if (inst->killsSource(i)) { cseKill(inst->src(i)); } } } // Save state for each block at the end. if (inst->isTerminal()) { save(inst->block()); } }
void FrameState::update(const IRInstruction* inst) { if (auto* taken = inst->taken()) { save(taken); } auto const opc = inst->op(); getLocalEffects(inst, *this); switch (opc) { case DefInlineFP: trackDefInlineFP(inst); break; case InlineReturn: trackInlineReturn(inst); break; case Call: m_spValue = inst->dst(); m_frameSpansCall = true; // A call pops the ActRec and pushes a return value. m_spOffset -= kNumActRecCells; m_spOffset += 1; assert(m_spOffset >= 0); clearCse(); break; case CallArray: m_spValue = inst->dst(); m_frameSpansCall = true; // A CallArray pops the ActRec an array arg and pushes a return value. m_spOffset -= kNumActRecCells; assert(m_spOffset >= 0); clearCse(); break; case ContEnter: clearCse(); break; case DefFP: case FreeActRec: m_fpValue = inst->dst(); break; case ReDefGeneratorSP: m_spValue = inst->dst(); break; case ReDefSP: m_spValue = inst->dst(); m_spOffset = inst->extra<ReDefSP>()->spOffset; break; case DefInlineSP: case DefSP: m_spValue = inst->dst(); m_spOffset = inst->extra<StackOffset>()->offset; break; case AssertStk: case AssertStkVal: case CastStk: case CoerceStk: case CheckStk: case GuardStk: case ExceptionBarrier: m_spValue = inst->dst(); break; case SpillStack: { m_spValue = inst->dst(); // Push the spilled values but adjust for the popped values int64_t stackAdjustment = inst->src(1)->getValInt(); m_spOffset -= stackAdjustment; m_spOffset += spillValueCells(inst); break; } case SpillFrame: case CufIterSpillFrame: m_spValue = inst->dst(); m_spOffset += kNumActRecCells; break; case InterpOne: case InterpOneCF: { m_spValue = inst->dst(); auto const& extra = *inst->extra<InterpOneData>(); int64_t stackAdjustment = extra.cellsPopped - extra.cellsPushed; // push the return value if any and adjust for the popped values m_spOffset -= stackAdjustment; break; } case AssertLoc: case GuardLoc: case CheckLoc: m_fpValue = inst->dst(); break; case LdThis: m_thisAvailable = true; break; default: break; } if (inst->modifiesStack()) { m_spValue = inst->modifiedStkPtr(); } // update the CSE table if (m_enableCse && inst->canCSE()) { cseInsert(inst); } // if the instruction kills any of its sources, remove them from the // CSE table if (inst->killsSources()) { for (int i = 0; i < inst->numSrcs(); ++i) { if (inst->killsSource(i)) { cseKill(inst->src(i)); } } } }