void emitCall(Vout& v, CppCall target, RegSet args) { switch (target.kind()) { case CppCall::Kind::Direct: v << call{static_cast<TCA>(target.address()), args}; return; case CppCall::Kind::Virtual: // Virtual call. Load method's address from proper offset off of object in // rdi, using rax as scratch. v << load{*reg::rdi, reg::rax}; v << callm{reg::rax[target.vtableOffset()], args}; return; case CppCall::Kind::ArrayVirt: { auto const addr = reinterpret_cast<intptr_t>(target.arrayTable()); v << loadzbl{reg::rdi[HeaderKindOffset], reg::eax}; if (deltaFits(addr, sz::dword)) { v << callm{baseless(reg::rax * 8 + addr), args}; } else { auto const base = v.makeReg(); v << ldimmq{addr, base}; v << callm{base[reg::rax * 8], args}; } static_assert(sizeof(HeaderKind) == 1, ""); return; } case CppCall::Kind::Destructor: // this movzbq is only needed because callers aren't required to // zero-extend the type. auto zextType = v.makeReg(); v << movzbq{target.reg(), zextType}; auto dtor_ptr = lookupDestructor(v, zextType); v << callm{dtor_ptr, args}; return; } not_reached(); }
/* * Helper for the freeLocalsHelpers which does the actual work of decrementing * a value's refcount or releasing it. * * This helper is reached via call from the various freeLocalHelpers. It * expects `tv' to be the address of a TypedValue with refcounted type `type' * (though it may be static, and we will do nothing in that case). * * The `live' registers must be preserved across any native calls (and * generally left untouched). */ static TCA emitDecRefHelper(CodeBlock& cb, DataBlock& data, CGMeta& fixups, PhysReg tv, PhysReg type, RegSet live) { return vwrap(cb, data, fixups, [&] (Vout& v) { // We use the first argument register for the TV data because we might pass // it to the native release call. It's not live when we enter the helper. auto const data = rarg(0); v << load{tv[TVOFF(m_data)], data}; auto destroy = [&](Vout& v) { PhysRegSaver prs{v, live}; auto const dword_size = sizeof(int64_t); // saving return value on the stack, but keeping it 16-byte aligned v << mflr{rfuncln()}; v << lea {rsp()[-2 * dword_size], rsp()}; v << store{rfuncln(), rsp()[0]}; // The refcount is exactly 1; release the value. // Avoid 'this' pointer overwriting by reserving it as an argument. v << callm{lookupDestructor(v, type), arg_regs(1)}; // Between where r1 is now and the saved RIP of the call into the // freeLocalsHelpers stub, we have all the live regs we pushed, plus the // stack size reserved for the LR saved right above and the LR offset in // the frame. v << syncpoint{makeIndirectFixup(prs.dwordsPushed())}; // fallthru // restore the return value from the stack v << load{rsp()[0], rfuncln()}; v << lea {rsp()[2 * dword_size], rsp()}; v << mtlr{rfuncln()}; }; auto const sf = emitCmpRefCount(v, OneReference, data); if (one_bit_refcount) { ifThen(v, CC_E, sf, destroy); } else { ifThen(v, CC_NL, sf, [&] (Vout& v) { // The refcount is positive, so the value is refcounted. We need to // either decref or release. ifThen(v, CC_NE, sf, [&] (Vout& v) { // The refcount is greater than 1; decref it. emitDecRefCount(v, data); v << ret{live}; }); destroy(v); }); } // Either we did a decref, or the value was static. v << ret{live}; }); }
/* * Helper for the freeLocalsHelpers which does the actual work of decrementing * a value's refcount or releasing it. * * This helper is reached via call from the various freeLocalHelpers. It * expects `tv' to be the address of a TypedValue with refcounted type `type' * (though it may be static, and we will do nothing in that case). * * The `live' registers must be preserved across any native calls (and * generally left untouched). */ static TCA emitDecRefHelper(CodeBlock& cb, DataBlock& data, CGMeta& fixups, PhysReg tv, PhysReg type, RegSet live) { return vwrap(cb, data, fixups, [&] (Vout& v) { // Set up frame linkage to avoid an indirect fixup. v << pushp{rlr(), rfp()}; v << copy{rsp(), rfp()}; // We use the first argument register for the TV data because we might pass // it to the native release call. It's not live when we enter the helper. auto const data = rarg(0); v << load{tv[TVOFF(m_data)], data}; auto const sf = v.makeReg(); v << cmplim{1, data[FAST_REFCOUNT_OFFSET], sf}; ifThen(v, CC_NL, sf, [&] (Vout& v) { // The refcount is positive, so the value is refcounted. We need to // either decref or release. ifThen(v, CC_NE, sf, [&] (Vout& v) { // The refcount is greater than 1; decref it. v << declm{data[FAST_REFCOUNT_OFFSET], v.makeReg()}; // Pop FP/LR and return v << popp{rfp(), rlr()}; v << ret{live}; }); // Note that the stack is aligned since we called to this helper from an // stack-unaligned stub. PhysRegSaver prs{v, live}; // The refcount is exactly 1; release the value. // Avoid 'this' pointer overwriting by reserving it as an argument. v << callm{lookupDestructor(v, type), arg_regs(1)}; // Between where %rsp is now and the saved RIP of the call into the // freeLocalsHelpers stub, we have all the live regs we pushed, plus the // saved RIP of the call from the stub to this helper. v << syncpoint{makeIndirectFixup(prs.dwordsPushed())}; // fallthru }); // Either we did a decref, or the value was static. // Pop FP/LR and return v << popp{rfp(), rlr()}; v << ret{live}; }); }
/* * Helper for the freeLocalsHelpers which does the actual work of decrementing * a value's refcount or releasing it. * * This helper is reached via call from the various freeLocalHelpers. It * expects `tv' to be the address of a TypedValue with refcounted type `type' * (though it may be static, and we will do nothing in that case). * * The `live' registers must be preserved across any native calls (and * generally left untouched). */ static TCA emitDecRefHelper(CodeBlock& cb, DataBlock& data, CGMeta& fixups, PhysReg tv, PhysReg type, RegSet live) { return vwrap(cb, data, fixups, [&] (Vout& v) { // Set up frame linkage to avoid an indirect fixup. v << stublogue{true}; v << copy{rsp(), rfp()}; // We use the first argument register for the TV data because we might pass // it to the native release call. It's not live when we enter the helper. auto const data = rarg(0); v << load{tv[TVOFF(m_data)], data}; auto destroy = [&](Vout& v) { // Note that the stack is aligned since we called to this helper from an // stack-unaligned stub. PhysRegSaver prs{v, live}; // The refcount is exactly 1; release the value. // Avoid 'this' pointer overwriting by reserving it as an argument. // There's no need for a fixup, because we setup a frame on the c++ // stack. v << callm{lookupDestructor(v, type), arg_regs(1)}; // fallthru }; auto const sf = emitCmpRefCount(v, OneReference, data); if (one_bit_refcount) { ifThen(v, CC_E, sf, destroy); } else { ifThen(v, CC_NL, sf, [&] (Vout& v) { // The refcount is positive, so the value is refcounted. We need to // either decref or release. ifThen(v, CC_NE, sf, [&] (Vout& v) { // The refcount is greater than 1; decref it. emitDecRefCount(v, data); v << stubret{live, true}; }); destroy(v); }); } // Either we did a decref, or the value was static. v << stubret{live, true}; }); }
void emitCall(Vout& v, CallSpec target, RegSet args) { switch (target.kind()) { case CallSpec::Kind::Direct: v << call{static_cast<TCA>(target.address()), args}; return; case CallSpec::Kind::Smashable: v << calls{static_cast<TCA>(target.address()), args}; return; case CallSpec::Kind::ArrayVirt: { auto const addr = reinterpret_cast<intptr_t>(target.arrayTable()); auto const arrkind = v.makeReg(); v << loadzbl{rarg(0)[HeaderKindOffset], arrkind}; if (deltaFits(addr, sz::dword)) { v << callm{baseless(arrkind * 8 + addr), args}; } else { auto const base = v.makeReg(); v << ldimmq{addr, base}; v << callm{base[arrkind * 8], args}; } static_assert(sizeof(HeaderKind) == 1, ""); } return; case CallSpec::Kind::Destructor: { // this movzbq is only needed because callers aren't required to // zero-extend the type. auto zextType = v.makeReg(); v << movzbq{target.reg(), zextType}; auto dtor_ptr = lookupDestructor(v, zextType); v << callm{dtor_ptr, args}; } return; case CallSpec::Kind::Stub: v << callstub{target.stubAddr(), args}; return; } not_reached(); }
void emitCall(Vout& v, CallSpec target, RegSet args) { using K = CallSpec::Kind; switch (target.kind()) { case K::Direct: v << call{static_cast<TCA>(target.address()), args}; return; case K::Smashable: v << calls{static_cast<TCA>(target.address()), args}; return; case K::ArrayVirt: { auto const addr = reinterpret_cast<intptr_t>(target.arrayTable()); auto const arrkind = v.makeReg(); v << loadzbl{rarg(0)[HeaderKindOffset], arrkind}; if (deltaFits(addr, sz::dword)) { v << callm{baseless(arrkind * 8 + addr), args}; } else { auto const base = v.makeReg(); v << ldimmq{addr, base}; v << callm{base[arrkind * 8], args}; } static_assert(sizeof(HeaderKind) == 1, ""); } return; case K::Destructor: { auto dtor = lookupDestructor(v, target.reg()); v << callm{dtor, args}; } return; case K::Stub: v << callstub{target.stubAddr(), args}; return; } not_reached(); }