TCA emitCall(vixl::MacroAssembler& a, CppCall call) { switch (call.kind()) { case CppCall::Kind::Direct: a. Mov (rHostCallReg, reinterpret_cast<intptr_t>(call.address())); break; case CppCall::Kind::Virtual: a. Ldr (rHostCallReg, argReg(0)[0]); a. Ldr (rHostCallReg, rHostCallReg[call.vtableOffset()]); break; case CppCall::Kind::IndirectReg: case CppCall::Kind::IndirectVreg: // call indirect currently not implemented. It'll be something like // a.Br(x2a(call.getReg())) not_implemented(); always_assert(0); break; case CppCall::Kind::ArrayVirt: case CppCall::Kind::Destructor: not_implemented(); always_assert(0); break; } using namespace vixl; auto fixupAddr = a.frontier(); a. HostCall(6); // Note that the fixup address for a HostCall is directly *before* the // HostCall, not after as in the native case. This is because, in simulation // mode we look at the simulator's PC at the time the fixup is invoked, and it // will still be pointing to the HostCall; it's not advanced past it until the // host call returns. In the native case, by contrast, we'll be looking at // return addresses, which point after the call. return fixupAddr; }
Vpoint emitCall(Vout& v, CppCall call, RegSet args) { PhysReg arg0(argReg(0)); PhysReg rHostCall(rHostCallReg); switch (call.kind()) { case CppCall::Kind::Direct: v << ldimm{reinterpret_cast<intptr_t>(call.address()), rHostCall}; break; case CppCall::Kind::Virtual: v << loadq{arg0[0], rHostCall}; v << loadq{rHostCall[call.vtableOffset()], rHostCall}; break; case CppCall::Kind::IndirectReg: case CppCall::Kind::IndirectVreg: // call indirect currently not implemented. It'll be something like // a.Br(x2a(call.getReg())) not_implemented(); always_assert(0); break; case CppCall::Kind::ArrayVirt: case CppCall::Kind::Destructor: not_implemented(); always_assert(0); break; } uint8_t argc = args.size(); args.add(rHostCall); auto fixupAddr = v.makePoint(); v << hostcall{args, argc, fixupAddr}; return fixupAddr; }
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(); }