void MInstrEffects::get(const IRInstruction* inst, const FrameStateMgr& frame, LocalStateHook& hook) { // If the base for this instruction is a local address, the helper call might // have side effects on the local's value auto const base = inst->src(minstrBaseIdx(inst->op())); auto const locInstr = base->inst(); // Right now we require that the address of any affected local is the // immediate source of the base tmp. This isn't actually specified in the ir // spec right now but will intend to make it more general soon. if (locInstr->op() != LdLocAddr) return; auto const locId = locInstr->extra<LdLocAddr>()->locId; auto const baseType = frame.localType(locId); MInstrEffects effects(inst->op(), baseType.ptr(Ptr::Frame)); if (effects.baseTypeChanged || effects.baseValChanged) { auto const ty = effects.baseType.derefIfPtr(); if (ty.isBoxed()) { hook.setLocalType(locId, Type::BoxedInitCell); hook.setBoxedLocalPrediction(locId, ty); } else { hook.setLocalType(locId, ty); } } }
void local_effects(const FrameStateMgr& frameState, const IRInstruction* inst, LocalStateHook& hook) { auto killIterLocals = [&](const std::initializer_list<uint32_t>& ids) { for (auto id : ids) { hook.setLocalValue(id, nullptr); } }; switch (inst->op()) { case CallBuiltin: if (inst->extra<CallBuiltin>()->destroyLocals) hook.clearLocals(); break; case Call: case CallArray: case ContEnter: { auto const callDestroysLocals = (inst->is(CallArray) && inst->extra<CallArray>()->destroyLocals) || (inst->is(Call) && inst->extra<Call>()->destroyLocals); hook.killLocalsForCall(callDestroysLocals); } break; case StRef: hook.updateLocalRefPredictions(inst->src(0), inst->src(1)); break; case StLocNT: case StLoc: hook.setLocalValue(inst->extra<LocalId>()->locId, inst->src(1)); break; case LdLoc: hook.setLocalValue(inst->extra<LdLoc>()->locId, inst->dst()); break; case StLocPseudoMain: hook.predictLocalType(inst->extra<LocalId>()->locId, inst->src(1)->type()); break; case AssertLoc: case GuardLoc: case CheckLoc: { auto id = inst->extra<LocalId>()->locId; if (inst->marker().func()->isPseudoMain()) { hook.predictLocalType(id, inst->typeParam()); } else { hook.refineLocalType(id, inst->typeParam(), TypeSource::makeGuard(inst)); } break; } case HintLocInner: hook.setBoxedLocalPrediction(inst->extra<HintLocInner>()->locId, inst->typeParam()); break; case CheckType: case AssertType: hook.refineLocalValues(inst->src(0), inst->dst()); break; case IterInitK: case WIterInitK: // kill the locals to which this instruction stores iter's key and value killIterLocals({inst->extra<IterData>()->keyId, inst->extra<IterData>()->valId}); break; case IterInit: case WIterInit: // kill the local to which this instruction stores iter's value killIterLocals({inst->extra<IterData>()->valId}); break; case IterNextK: case WIterNextK: // kill the locals to which this instruction stores iter's key and value killIterLocals({inst->extra<IterData>()->keyId, inst->extra<IterData>()->valId}); break; case IterNext: case WIterNext: // kill the local to which this instruction stores iter's value killIterLocals({inst->extra<IterData>()->valId}); break; case InterpOne: case InterpOneCF: { auto const& id = *inst->extra<InterpOneData>(); assert(!id.smashesAllLocals || id.nChangedLocals == 0); if (id.smashesAllLocals || inst->marker().func()->isPseudoMain()) { hook.clearLocals(); } else { auto it = id.changedLocals; auto const end = it + id.nChangedLocals; for (; it != end; ++it) { auto& loc = *it; // If changing the inner type of a boxed local, also drop the // information about inner types for any other boxed locals. if (loc.type.isBoxed()) hook.dropLocalRefsInnerTypes(); hook.setLocalType(loc.id, loc.type); } } break; } default: break; } if (MInstrEffects::supported(inst)) { MInstrEffects::get(inst, frameState, hook); } }