void first_pass(const Index& index, const Context ctx, borrowed_ptr<php::Block> const blk, State state) { std::vector<Bytecode> newBCs; newBCs.reserve(blk->hhbcs.size()); PropertiesInfo props(index, ctx, nullptr); auto interp = Interp { index, ctx, props, blk, state }; for (auto& op : blk->hhbcs) { FTRACE(2, " == {}\n", show(op)); auto gen = [&] (const Bytecode& newb) { newBCs.push_back(newb); newBCs.back().srcLoc = op.srcLoc; FTRACE(2, " + {}\n", show(newBCs.back())); }; auto const flags = step(interp, op); if (flags.calledNoReturn) { gen(op); gen(bc::BreakTraceHint {}); // The rest of this code is going to // be unreachable. // It would be nice to put a fatal here, but we can't because it // will mess up the bytecode invariant about blocks // not-reachable via fallthrough if the stack depth is non-zero. // It can also mess up FPI regions. continue; } if (options.RemoveDeadBlocks && flags.tookBranch) { always_assert(!flags.wasPEI); switch (op.op) { case Op::JmpNZ: blk->fallthrough = op.JmpNZ.target; break; case Op::JmpZ: blk->fallthrough = op.JmpZ.target; break; case Op::IterInit: blk->fallthrough = op.IterInit.target; break; case Op::IterInitK: blk->fallthrough = op.IterInitK.target; break; default: // No support for switch, etc, right now. always_assert(0 && "unsupported tookBranch case"); } /* * We need to pop the cell that was on the stack for the * conditional jump. Note: for jumps this also conceptually * needs to execute any side effects a conversion to bool can * have. (Currently that is none.) */ gen(bc::PopC {}); continue; } if (options.ConstantProp && flags.canConstProp) { if (propagate_constants(op, state, gen)) continue; } if (options.StrengthReduce && flags.strengthReduced) { for (auto& hh : *flags.strengthReduced) gen(hh); continue; } gen(op); } blk->hhbcs = std::move(newBCs); }
struct code* code_analyse(struct prx *p) { struct code *c = code_alloc(); struct subroutine *sub; element el; c->file = p; if (!decode_instructions(c)) { code_free(c); return NULL ; } extract_switches(c); extract_subroutines(c); live_registers(c); el = list_head(c->subroutines); while (el) { sub = element_getvalue(el); if (!sub->import && !sub->haserror) { cfg_traverse(sub, FALSE); if (!sub->haserror) { sub->status |= SUB_STAT_CFG_TRAVERSE; cfg_traverse(sub, TRUE); } if (!sub->haserror) { sub->status |= SUB_STAT_CFG_TRAVERSE_REV; fixup_call_arguments(sub); } if (!sub->haserror) { sub->status |= SUB_STAT_FIXUP_CALL_ARGS; build_ssa(sub); } if (!sub->haserror) { sub->status |= SUB_STAT_SSA; } } el = element_next(el); } live_registers_imports(c); el = list_head(c->subroutines); while (el) { sub = element_getvalue(el); if (!sub->import && !sub->haserror) { if (!(sub->status & SUB_STAT_FIXUP_CALL_ARGS)) { fixup_call_arguments(sub); if (!sub->haserror) { sub->status |= SUB_STAT_FIXUP_CALL_ARGS; build_ssa(sub); } } if (!sub->haserror) { sub->status |= SUB_STAT_SSA; propagate_constants(sub); } if (!sub->haserror) { sub->status |= SUB_STAT_CONSTANTS_EXTRACTED; extract_variables(sub); } if (!sub->haserror) { sub->status |= SUB_STAT_VARIABLES_EXTRACTED; extract_structures(sub); } if (!sub->haserror) { sub->status |= SUB_STAT_STRUCTURES_EXTRACTED; } } el = element_next(el); } return c; }