void cgLdRDSAddr(IRLS& env, const IRInstruction* inst) { ldRDSAddrImpl( vmain(env), inst->extra<LdRDSAddr>()->handle, dstLoc(env, inst, 0).reg() ); }
void cgLookupClsMethodCache(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<ClsMethodData>(); auto const dst = dstLoc(env, inst, 0).reg(); auto const fp = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const ch = StaticMethodCache::alloc( extra->clsName, extra->methodName, ctxName(inst->marker()) ); if (false) { // typecheck UNUSED TypedValue* fake_fp = nullptr; const UNUSED Func* f = StaticMethodCache::lookup( ch, extra->namedEntity, extra->clsName, extra->methodName, fake_fp ); } auto const args = argGroup(env, inst) .imm(ch) .immPtr(extra->namedEntity) .immPtr(extra->clsName) .immPtr(extra->methodName) .reg(fp); // May raise an error if the class is undefined. cgCallHelper(v, env, CallSpec::direct(StaticMethodCache::lookup), callDest(dst), SyncOptions::Sync, args); }
void cgCallArray(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<CallArray>(); auto const sp = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const syncSP = v.makeReg(); v << lea{sp[cellsToBytes(extra->spOffset.offset)], syncSP}; v << syncvmsp{syncSP}; auto const target = extra->numParams == 0 ? mcg->ustubs().fcallArrayHelper : mcg->ustubs().fcallUnpackHelper; auto const pc = v.cns(extra->pc); auto const after = v.cns(extra->after); auto const args = extra->numParams == 0 ? v.makeTuple({pc, after}) : v.makeTuple({pc, after, v.cns(extra->numParams)}); auto const done = v.makeBlock(); v << vcallarray{target, fcall_array_regs(), args, {done, label(env, inst->taken())}}; env.catch_calls[inst->taken()] = CatchCall::PHP; v = done; auto const dst = dstLoc(env, inst, 0); v << defvmret{dst.reg(0), dst.reg(1)}; }
void cgOrdStrIdx(IRLS& env, const IRInstruction* inst) { auto const sd = srcLoc(env, inst, 0).reg(); auto const idx = srcLoc(env, inst, 1).reg(); auto& v = vmain(env); auto const sf = v.makeReg(); auto const length = v.makeReg(); v << loadzlq{sd[StringData::sizeOff()], length}; v << cmpq{idx, length, sf}; unlikelyCond(v, vcold(env), CC_B, sf, dstLoc(env, inst, 0).reg(), [&] (Vout& v) { auto const args = argGroup(env, inst).ssa(0).ssa(1); cgCallHelper(v, env, CallSpec::direct(MInstrHelpers::stringGetI), kVoidDest, SyncOptions::Sync, args); return v.cns(0); }, [&] (Vout& v) { auto const dst = v.makeReg(); auto const data = v.makeReg(); #ifdef NO_M_DATA v << lea{sd[sizeof(StringData)], data}; #else v << load{sd[StringData::dataOff()], data}; #endif v << loadzbq{data[idx], dst}; return dst; } ); }
void cgLookupClsMethodFCache(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<ClsMethodData>(); auto const dst = dstLoc(env, inst, 0).reg(0); auto const cls = inst->src(0)->clsVal(); auto const fp = srcLoc(env, inst, 1).reg(); auto& v = vmain(env); auto const ch = StaticMethodFCache::alloc( cls->name(), extra->methodName, ctxName(inst->marker()) ); assertx(rds::isNormalHandle(ch)); const Func* (*lookup)(rds::Handle, const Class*, const StringData*, TypedValue*) = StaticMethodFCache::lookup; auto const args = argGroup(env, inst) .imm(ch) .immPtr(cls) .immPtr(extra->methodName) .reg(fp); cgCallHelper(v, env, CallSpec::direct(lookup), callDest(dst), SyncOptions::Sync, args); }
void cgInstanceOfIfaceVtable(IRLS& env, const IRInstruction* inst) { auto const iface = inst->extra<InstanceOfIfaceVtable>()->cls; auto const slot = iface->preClass()->ifaceVtableSlot(); auto const dst = dstLoc(env, inst, 0).reg(); auto const rcls = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const sf = v.makeReg(); emitCmpVecLen(v, sf, static_cast<int32_t>(slot), rcls[Class::vtableVecLenOff()]); cond( v, CC_A, sf, dst, [&] (Vout& v) { auto const vtableVec = v.makeReg(); emitLdLowPtr(v, rcls[Class::vtableVecOff()], vtableVec, sizeof(LowPtr<Class::VtableVecSlot>)); auto const ifaceOff = slot * sizeof(Class::VtableVecSlot) + offsetof(Class::VtableVecSlot, iface); auto const sf = v.makeReg(); emitCmpLowPtr<Class>(v, sf, iface, vtableVec[ifaceOff]); auto tmp = v.makeReg(); v << setcc{CC_E, sf, tmp}; return tmp; }, [&] (Vout& v) { return v.cns(false); } ); }
void cgFwdCtxStaticCall(IRLS& env, const IRInstruction* inst) { auto const dstCtx = dstLoc(env, inst, 0).reg(); auto const srcCtx = srcLoc(env, inst, 0).reg(); auto const ty = inst->src(0)->type(); auto& v = vmain(env); auto ctx_from_this = [] (Vout& v, Vreg rthis, Vreg dst) { // Load (this->m_cls | 0x1) into `dst'. auto const cls = emitLdObjClass(v, rthis, v.makeReg()); v << orqi{ActRec::kHasClassBit, cls, dst, v.makeReg()}; return dst; }; if (ty <= TCctx) { v << copy{srcCtx, dstCtx}; } else if (ty <= TObj) { ctx_from_this(v, srcCtx, dstCtx); } else { // If we don't know whether we have a $this, we need to check dynamically. auto const sf = v.makeReg(); v << testqi{ActRec::kHasClassBit, srcCtx, sf}; unlikelyCond(v, vcold(env), CC_NZ, sf, dstCtx, [&] (Vout& v) { return srcCtx; }, [&] (Vout& v) { return ctx_from_this(v, srcCtx, v.makeReg()); } ); } }
void cgLdContActRec(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cont = srcLoc(env, inst, 0).reg(); auto const isAsync = inst->extra<IsAsyncData>()->isAsync; auto const arOff = BaseGenerator::arOff() - genOffset(isAsync); vmain(env) << lea{cont[arOff], dst}; }
void cgLdStaticLoc(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<LdStaticLoc>(); auto const link = rds::bindStaticLocal(extra->func, extra->name); auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); v << lea{rvmtl()[link.handle() + rds::StaticLocalData::ref_offset()], dst}; }
void cgConstructInstance(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cls = inst->extra<ConstructInstance>()->cls; auto const args = argGroup(env, inst).immPtr(cls); cgCallHelper(vmain(env), env, CallSpec::direct(cls->instanceCtor().get()), callDest(dst), SyncOptions::Sync, args); }
void cgMod(IRLS& env, const IRInstruction* inst) { auto const d = dstLoc(env, inst, 0).reg(); auto const dividend = srcLoc(env, inst, 0).reg(); auto const divisor = srcLoc(env, inst, 1).reg(); auto& v = vmain(env); v << srem{dividend, divisor, d}; }
void printDsts(std::ostream& os, const IRInstruction* inst, const RegAllocInfo* regs) { const char* sep = ""; for (unsigned i = 0, n = inst->numDsts(); i < n; i++) { os << punc(sep); print(os, inst->dst(i), dstLoc(regs, inst, i)); sep = ", "; } }
void cgLdLocPseudoMain(IRLS& env, const IRInstruction* inst) { auto const fp = srcLoc(env, inst, 0).reg(); auto const off = localOffset(inst->extra<LdLocPseudoMain>()->locId); auto& v = vmain(env); irlower::emitTypeCheck(v, env, inst->typeParam(), fp[off + TVOFF(m_type)], fp[off + TVOFF(m_data)], inst->taken()); loadTV(v, inst->dst(), dstLoc(env, inst, 0), fp[off]); }
void cgLdClsRef(IRLS& env, const IRInstruction* inst) { auto const fp = srcLoc(env, inst, 0).reg(); auto const dst = dstLoc(env, inst, 0).reg(); auto const off = frame_clsref_offset( funcFromFp(inst->src(0)), inst->extra<ClsRefSlotData>()->slot ); emitLdLowPtr(vmain(env), fp[off], dst, sizeof(LowPtr<Class>)); }
void cgLdWHState(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const obj = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const state = v.makeReg(); v << loadzbq{obj[WH::stateOff()], state}; v << andqi{0x0F, state, dst, v.makeReg()}; }
void cgLdClsCns(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<LdClsCns>(); auto const link = rds::bindClassConstant(extra->clsName, extra->cnsName); auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const sf = checkRDSHandleInitialized(v, link.handle()); fwdJcc(v, env, CC_NE, sf, inst->taken()); v << lea{rvmtl()[link.handle()], dst}; }
void cgLdClsName(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const src = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const preclass = v.makeReg(); v << load{src[Class::preClassOff()], preclass}; emitLdLowPtr(v, preclass[PreClass::nameOffset()], dst, sizeof(LowStringPtr)); }
void cgLdFuncNumParams(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const func = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const tmp = v.makeReg(); // See Func::finishedEmittingParams and Func::numParams. v << loadzlq{func[Func::paramCountsOff()], tmp}; v << shrqi{1, tmp, dst, v.makeReg()}; }
void cgConjure(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0); auto& v = vmain(env); if (dst.hasReg(0)) { v << conjure{dst.reg(0)}; } if (dst.hasReg(1)) { v << conjure{dst.reg(1)}; } }
void cgLdSubClsCns(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<LdSubClsCns>(); auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const slot = extra->slot; auto const tmp = v.makeReg(); v << load{srcLoc(env, inst, 0).reg()[Class::constantsVecOff()], tmp}; v << lea{tmp[slot * sizeof(Class::Const) + offsetof(Class::Const, val)], dst}; }
void cgLdInitRDSAddr(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); ldRDSAddrImpl(v, inst->extra<LdInitRDSAddr>()->handle, dst); auto const sf = v.makeReg(); emitCmpTVType(v, sf, KindOfUninit, dst[TVOFF(m_type)]); v << jcc{CC_Z, sf, {label(env, inst->next()), label(env, inst->taken())}}; }
void cgLdBindAddr(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<LdBindAddr>(); auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); // Emit service request to smash address of SrcKey into 'addr'. auto const addrPtr = v.allocData<TCA>(); v << bindaddr{addrPtr, extra->sk, extra->bcSPOff}; v << loadqd{reinterpret_cast<uint64_t*>(addrPtr), dst}; }
void cgLdFuncVecLen(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cls = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); // A Cctx is a Cls with the bottom bit set; subtract one from the offset to // handle that case. auto const off = Class::funcVecLenOff() - (inst->src(0)->isA(TCctx) ? 1 : 0); v << loadzlq{cls[off], dst}; }
void cgContArIncIdx(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const contAR = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); auto const tmp = v.makeReg(); auto const idxOff = GENDATAOFF(m_index) - Generator::arOff(); v << load{contAR[idxOff], tmp}; v << incq{tmp, dst, v.makeReg()}; v << store{dst, contAR[idxOff]}; }
void cgLdObjInvoke(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cls = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); emitLdLowPtr(v, cls[Class::invokeOff()], dst, sizeof(LowPtr<Func>)); auto const sf = v.makeReg(); v << testq{dst, dst, sf}; v << jcc{CC_Z, sf, {label(env, inst->next()), label(env, inst->taken())}}; }
void cgCheckNonNull(IRLS& env, const IRInstruction* inst) { auto dst = dstLoc(env, inst, 0).reg(); auto src = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); assertx(inst->taken()); auto const sf = v.makeReg(); v << testq{src, src, sf}; fwdJcc(v, env, CC_Z, sf, inst->taken()); v << copy{src, dst}; }
void cgLdGblAddr(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto& v = vmain(env); cgCallHelper(v, env, CallSpec::direct(ldGblAddrHelper), callDest(dst), SyncOptions::None, argGroup(env, inst).ssa(0)); auto const sf = v.makeReg(); v << testq{dst, dst, sf}; v << jcc{CC_Z, sf, {label(env, inst->next()), label(env, inst->taken())}}; }
void cgContValid(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cont = srcLoc(env, inst, 0).reg(); auto const isAsync = inst->extra<IsAsyncData>()->isAsync; auto& v = vmain(env); // Return true if generator state is not Done. auto const sf = v.makeReg(); auto const stateOff = BaseGenerator::stateOff() - genOffset(isAsync); v << cmpbim{int8_t(BaseGenerator::State::Done), cont[stateOff], sf}; v << setcc{CC_NE, sf, dst}; }
void cgContStarted(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cont = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); // Return true if generator state is not in the Created state. auto const sf = v.makeReg(); auto const stateOff = BaseGenerator::stateOff() - genOffset(false /* isAsync */); v << cmpbim{int8_t(BaseGenerator::State::Created), cont[stateOff], sf}; v << setcc{CC_NE, sf, dst}; }
void cgAssertNonNull(IRLS& env, const IRInstruction* inst) { auto dst = dstLoc(env, inst, 0).reg(); auto src = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); if (RuntimeOption::EvalHHIRGenerateAsserts) { auto const sf = v.makeReg(); v << testq{src, src, sf}; ifThen(v, CC_Z, sf, [&](Vout& v) { v << ud2{}; }); } v << copy{src, dst}; }