SSATmp* TraceBuilder::preOptimizeLdLocAddr(IRInstruction* inst) { auto const locId = inst->getExtra<LdLocAddr>()->locId; if (getLocalType(locId) != Type::None) { // TODO(#2135185) inst->setTypeParam( Type::mostRefined(getLocalType(locId).ptr(), inst->getTypeParam()) ); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeLdLoc(IRInstruction* inst) { auto const locId = inst->extra<LdLoc>()->locId; if (auto tmp = getLocalValue(locId)) { return tmp; } if (getLocalType(locId) != Type::None) { // TODO(#2135185) inst->setTypeParam( Type::mostRefined(getLocalType(locId), inst->typeParam()) ); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeDecRefLoc(IRInstruction* inst) { auto const locId = inst->getExtra<DecRefLoc>()->locId; /* * Refine the type if we can. * * We can't really rely on the types held in the boxed values since * aliasing stores may change them, and we only guard during LdRef. * So we have to change any boxed type to BoxedCell. */ auto knownType = getLocalType(locId); if (knownType.isBoxed()) { knownType = Type::BoxedCell; } if (knownType != Type::None) { // TODO(#2135185) inst->setTypeParam( Type::mostRefined(knownType, inst->getTypeParam()) ); } /* * If we have the local value in flight, use a DecRef on it instead * of doing it in memory. */ if (auto tmp = getLocalValue(locId)) { gen(DecRef, tmp); inst->convertToNop(); } return nullptr; }
/* * Stores a ref (boxed value) to a local. Also handles unsetting a local. */ void TraceBuilder::genBindLoc(uint32_t id, SSATmp* newValue, bool doRefCount /* = true */) { Type trackedType = getLocalType(id); SSATmp* prevValue = 0; if (trackedType == Type::None) { if (doRefCount) { prevValue = gen(LdLoc, Type::Gen, LocalId(id), m_fpValue); } } else { prevValue = getLocalValue(id); assert(prevValue == nullptr || prevValue->type() == trackedType); if (prevValue == newValue) { // Silent store: local already contains value being stored // NewValue needs to be decref'ed if (!trackedType.notCounted() && doRefCount) { gen(DecRef, prevValue); } return; } if (trackedType.maybeCounted() && !prevValue && doRefCount) { prevValue = gen(LdLoc, trackedType, LocalId(id), m_fpValue); } } bool genStoreType = true; if ((trackedType.isBoxed() && newValue->type().isBoxed()) || (trackedType == newValue->type() && !trackedType.isString())) { // no need to store type with local value genStoreType = false; } gen(genStoreType ? StLoc : StLocNT, LocalId(id), m_fpValue, newValue); if (trackedType.maybeCounted() && doRefCount) { gen(DecRef, prevValue); } }
void TraceBuilder::genDecRefLoc(int id) { Type type = getLocalType(id); // Don't generate code if type is not refcounted if (type != Type::None && type.notCounted()) { return; } type = noneToGen(type); // When a parameter goes out of scope, we need to null // it out so that debug_backtrace can't capture stale // values. bool setNull = id < m_curFunc->getValFunc()->numParams(); SSATmp* val = getLocalValue(id); if (val == nullptr && setNull) { val = gen(LdLoc, type, LocalId(id), m_fpValue); } if (val) { if (setNull) { gen(StLoc, LocalId(id), m_fpValue, genDefUninit()); } gen(DecRef, val); return; } if (type.isBoxed()) { // we can't really rely on the types held in the boxed values since // aliasing stores may change them. We conservatively set the type // of the decref to a boxed cell. type = Type::BoxedCell; } gen(DecRefLoc, type, LocalId(id), m_fpValue); }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = getLocalType(locId); auto const typeParam = inst->typeParam(); if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { if (!prevType.subtypeOf(typeParam)) { /* Task #2553746 * This is triggering for a case where the tracked state says the local is * InitNull but the AssertLoc says it's Str. */ static auto const error = StringData::GetStaticString("Internal error: static analysis was " "wrong about a local variable's type."); auto* errorInst = m_irFactory.gen(RaiseError, inst->marker(), cns(error)); inst->become(&m_irFactory, errorInst); // It's not a disaster to generate this in unreachable code for // now. t2590033. if (false) { assert_log(false, [&]{ IRTrace& mainTrace = trace()->isMain() ? *trace() : *(trace()->main()); return folly::format("\npreOptimizeAssertLoc: prevType: {} " "typeParam: {}\nin instr: {}\nin trace: {}\n", prevType.toString(), typeParam.toString(), inst->toString(), mainTrace.toString()).str(); }); } } else { inst->convertToNop(); } } return nullptr; }
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->getExtra<AssertLoc>()->locId; auto const prevType = getLocalType(locId); auto const typeParam = inst->getTypeParam(); if (!prevType.equals(Type::None) && !typeParam.strictSubtypeOf(prevType)) { assert(prevType.subtypeOf(typeParam)); inst->convertToNop(); } return nullptr; }
/* * Store a cell value to a local that might be boxed. */ SSATmp* TraceBuilder::genStLoc(uint32_t id, SSATmp* newValue, bool doRefCount, bool genStoreType, Trace* exit) { assert(!newValue->type().isBoxed()); /* * If prior value of local is a cell, then re-use genBindLoc. * Otherwise, if prior value of local is a ref: * * prevLocValue = LdLoc<T>{id} fp * prevValue = LdRef [prevLocValue] * newRef = StRef [prevLocValue], newValue * DecRef prevValue * -- track local value in newRef */ Type trackedType = getLocalType(id); assert(trackedType != Type::None); // tracelet guards guarantee a type if (trackedType.notBoxed()) { SSATmp* retVal = doRefCount ? gen(IncRef, newValue) : newValue; genBindLoc(id, newValue, doRefCount); return retVal; } assert(trackedType.isBoxed()); SSATmp* prevRef = getLocalValue(id); assert(prevRef == nullptr || prevRef->type() == trackedType); // prevRef is a ref if (prevRef == nullptr) { // prevRef = ldLoc prevRef = gen(LdLoc, trackedType, LocalId(id), m_fpValue); } SSATmp* prevValue = nullptr; if (doRefCount) { assert(exit); Type innerType = trackedType.innerType(); prevValue = gen(LdRef, innerType, exit, prevRef); } // stref [prevRef] = t1 Opcode opc = genStoreType ? StRef : StRefNT; gen(opc, prevRef, newValue); SSATmp* retVal = newValue; if (doRefCount) { retVal = gen(IncRef, newValue); gen(DecRef, prevValue); } return retVal; }
/** * Returns an SSATmp containing the current value of the given local. * This generates a LdLoc instruction if needed. * * Note: the type of the local must be known already (due to type guards * or assertions). */ SSATmp* TraceBuilder::genLdLoc(uint32_t id) { SSATmp* tmp = getLocalValue(id); if (tmp) { return tmp; } // No prior value for this local is available, so actually generate a LdLoc. auto type = getLocalType(id); assert(type != Type::None); // tracelet guards guarantee we have a type assert(type != Type::Null); // we can get Uninit or InitNull but not both if (type.isNull()) { tmp = cns(type); } else { tmp = gen(LdLoc, type, LocalId(id), m_fpValue); } return tmp; }
SSATmp* TraceBuilder::preOptimizeStLoc(IRInstruction* inst) { auto const curType = getLocalType(inst->getExtra<StLoc>()->locId); auto const newType = inst->getSrc(1)->type(); assert(inst->getTypeParam().equals(Type::None)); // There's no need to store the type if it's going to be the same // KindOfFoo. We still have to store string types because we don't // guard on KindOfStaticString vs. KindOfString. auto const bothBoxed = curType.isBoxed() && newType.isBoxed(); auto const sameUnboxed = curType != Type::None && // TODO(#2135185) curType.isKnownDataType() && curType.equals(newType) && !curType.isString(); if (bothBoxed || sameUnboxed) { inst->setOpcode(StLocNT); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeGuardLoc(IRInstruction* inst) { auto const locId = inst->getExtra<GuardLoc>()->locId; if (auto const prevValue = getLocalValue(locId)) { return gen( GuardType, inst->getTypeParam(), inst->getTaken(), prevValue ); } auto const prevType = getLocalType(locId); if (prevType != Type::None) { // It doesn't make sense to be guarding on something that's deemed // to fail. assert(prevType == inst->getTypeParam()); inst->convertToNop(); } return nullptr; }
SSATmp* TraceBuilder::preOptimizeCheckLoc(IRInstruction* inst) { auto const locId = inst->extra<CheckLoc>()->locId; Type typeParam = inst->typeParam(); if (auto const prevValue = getLocalValue(locId)) { return gen(CheckType, typeParam, inst->taken(), prevValue); } auto const prevType = getLocalType(locId); if (prevType == Type::None) { return nullptr; } if (prevType.subtypeOf(typeParam)) { inst->convertToNop(); } else { // // Normally, it doesn't make sense to be checking something that's // deemed to fail. Incompatible boxed types are ok though, since // we don't track them precisely, but instead check them at every // use. // // However, in JitPGO mode right now, this pathological case can // happen, because profile counters are not accurate and we // currently don't analyze Block post-conditions when picking its // successors during region selection. This can lead to // incompatible types in blocks selected for the same region. // if (!typeParam.isBoxed() || !prevType.isBoxed()) { if ((typeParam & prevType) == Type::Bottom) { assert(RuntimeOption::EvalJitPGO); return gen(Jmp_, inst->taken()); } } } return nullptr; }
SSATmp* TraceBuilder::genLdLocAddr(uint32_t id) { return gen(LdLocAddr, getLocalType(id).ptr(), LocalId(id), getFp()); }