void emitStaticLocInit(HTS& env, int32_t locId, const StringData* name) { if (curFunc(env)->isPseudoMain()) PUNT(StaticLocInit); auto const ldPMExit = makePseudoMainExit(env); auto const value = popC(env); // Closures and generators from closures don't satisfy the "one static per // source location" rule that the inline fastpath requires auto const box = [&]{ if (curFunc(env)->isClosureBody()) { return gen(env, ClosureStaticLocInit, cns(env, name), fp(env), value); } auto const cachedBox = gen(env, LdStaticLocCached, StaticLocName { curFunc(env), name }); ifThen( env, [&] (Block* taken) { gen(env, CheckStaticLocInit, taken, cachedBox); }, [&] { hint(env, Block::Hint::Unlikely); gen(env, StaticLocInitCached, cachedBox, value); } ); return cachedBox; }(); gen(env, IncRef, box); auto const oldValue = ldLoc(env, locId, ldPMExit, DataTypeSpecific); stLocRaw(env, locId, fp(env), box); gen(env, DecRef, oldValue); // We don't need to decref value---it's a bytecode invariant that // our Cell was not ref-counted. }
void emitCGetL(HTS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); // Mimic hhbc guard relaxation for now. auto cat = curSrcKey(env).op() == OpFPassL ? DataTypeSpecific : DataTypeCountnessInit; pushIncRef(env, ldLocInnerWarn(env, id, ldrefExit, ldPMExit, cat)); }
void emitEmptyL(IRGS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const ld = ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeSpecific); push( env, gen(env, XorBool, gen(env, ConvCellToBool, ld), cns(env, true)) ); }
void emitAGetL(HTS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const src = ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeSpecific); if (src->type().subtypeOfAny(Type::Obj, Type::Str)) { implAGet(env, src); } else { PUNT(AGetL); } }
void emitSetL(HTS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); // since we're just storing the value in a local, this function doesn't care // about the type of the value. stLoc needs to IncRef the value so it may // constrain it further. auto const src = popC(env, DataTypeGeneric); pushStLoc(env, id, ldrefExit, ldPMExit, src); }
void emitIsTypeL(IRGS& env, int32_t id, IsTypeOp subop) { if (subop == IsTypeOp::Scalar) return implIsScalarL(env, id); auto const t = typeOpToDataType(subop); auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const val = ldLocInnerWarn(env, id, ldrefExit, ldPMExit, DataTypeSpecific); if (t == KindOfObject) { push(env, optimizedCallIsObject(env, val)); } else { push(env, gen(env, IsType, Type(t), val)); } }
void emitCGetL2(HTS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const oldTop = pop(env, Type::StkElem); auto const val = ldLocInnerWarn( env, id, ldrefExit, ldPMExit, DataTypeCountnessInit ); pushIncRef(env, val); push(env, oldTop); }
void emitBindL(HTS& env, int32_t id) { if (curFunc(env)->isPseudoMain()) { interpOne(env, Type::BoxedInitCell, 1); return; } auto const ldPMExit = makePseudoMainExit(env); auto const newValue = popV(env); // Note that the IncRef must happen first, for correctness in a // pseudo-main: the destructor could decref the value again after // we've stored it into the local. pushIncRef(env, newValue); auto const oldValue = ldLoc(env, id, ldPMExit, DataTypeSpecific); stLocRaw(env, id, fp(env), newValue); gen(env, DecRef, oldValue); }
void checkTypeLocal(IRGS& env, uint32_t locId, Type type, Offset dest, bool outerOnly) { assertx(type <= TCell || type <= TBoxedInitCell); auto exit = env.irb->guardFailBlock(); if (exit == nullptr) exit = makeExit(env, dest); if (type <= TCell) { profiledGuard(env, type, ProfGuard::CheckLoc, locId, exit); return; } profiledGuard(env, TBoxedInitCell, ProfGuard::CheckLoc, locId, exit); gen(env, HintLocInner, type, LocalId { locId }, fp(env)); if (!outerOnly && type.inner() < TInitCell) { auto const exit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const val = ldLoc(env, locId, ldPMExit, DataTypeSpecific); gen(env, CheckRefInner, env.irb->predictedInnerType(locId), exit, val); } }
void emitStaticLoc(HTS& env, int32_t locId, const StringData* name) { if (curFunc(env)->isPseudoMain()) PUNT(StaticLoc); auto const ldPMExit = makePseudoMainExit(env); auto const box = curFunc(env)->isClosureBody() ? gen(env, ClosureStaticLocInit, cns(env, name), fp(env), cns(env, Type::Uninit)) : gen(env, LdStaticLocCached, StaticLocName { curFunc(env), name }); auto const res = cond( env, 0, [&] (Block* taken) { gen(env, CheckStaticLocInit, taken, box); }, [&] { // Next: the static local is already initialized return cns(env, true); }, [&] { // Taken: need to initialize the static local /* * Even though this path is "cold", we're not marking it * unlikely because the size of the instructions this will * generate is about 10 bytes, which is not much larger than the * 5 byte jump to acold would be. * * One note about StaticLoc: we're literally always going to * generate a fallthrough trace here that is cold (the code that * initializes the static local). TODO(#2894612). */ gen(env, StaticLocInitCached, box, cns(env, Type::InitNull)); return cns(env, false); }); gen(env, IncRef, box); auto const oldValue = ldLoc(env, locId, ldPMExit, DataTypeGeneric); stLocRaw(env, locId, fp(env), box); gen(env, DecRef, oldValue); push(env, res); }
void emitIssetL(IRGS& env, int32_t id) { auto const ldrefExit = makeExit(env); auto const ldPMExit = makePseudoMainExit(env); auto const ld = ldLocInner(env, id, ldrefExit, ldPMExit, DataTypeSpecific); push(env, gen(env, IsNType, TNull, ld)); }