-cpu/drcbex64.cpp: Be nicer to the return address predictor.

-cpu/drcbex86.cpp: Give hints to use short displacements for jumps to local unbound labels.

-util/mfpresolve.h: Use references for some things that must not be null pointers.
This commit is contained in:
Vas Crabb 2025-01-15 00:48:19 +11:00
parent 96736e433e
commit 2ffd6a09af
5 changed files with 55 additions and 55 deletions

View File

@ -911,7 +911,7 @@ drcbe_arm64::drcbe_arm64(drcuml_state &drcuml, device_t &device, drc_cache &cach
auto const resolve_accessor =
[] (resolved_handler &handler, address_space &space, auto accessor)
{
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, &space);
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, space);
handler.func = reinterpret_cast<uint8_t *>(entrypoint);
handler.obj = adjusted;
};

View File

@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
drcbex64.c
drcbex64.cpp
64-bit x64 back-end for the universal machine language.
@ -158,10 +158,10 @@
[rsp+48] - saved r14
[rsp+56] - saved r13
[rsp+64] - saved r12
[rsp+72] - saved ebp
[rsp+80] - saved edi
[rsp+88] - saved esi
[rsp+96] - saved ebx
[rsp+72] - saved rbp
[rsp+80] - saved rdi
[rsp+88] - saved rsi
[rsp+96] - saved rbx
[rsp+104] - ret
***************************************************************************/
@ -696,7 +696,7 @@ drcbe_x64::drcbe_x64(drcuml_state &drcuml, device_t &device, drc_cache &cache, u
auto const resolve_accessor =
[] (resolved_handler &handler, address_space &space, auto accessor)
{
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, &space);
auto const [entrypoint, adjusted] = util::resolve_member_function(accessor, space);
handler.func = reinterpret_cast<x86code *>(entrypoint);
handler.obj = adjusted;
};
@ -837,9 +837,8 @@ void drcbe_x64::reset()
a.emitProlog(frame);
a.emitArgsAssignment(frame, args);
a.sub(rsp, 32);
a.sub(rsp, 40);
a.mov(MABS(&m_near.hashstacksave), rsp);
a.sub(rsp, 8);
a.mov(MABS(&m_near.stacksave), rsp);
a.stmxcsr(MABS(&m_near.ssemode));
a.jmp(Gpq(REG_PARAM2));
@ -849,13 +848,13 @@ void drcbe_x64::reset()
a.bind(a.newNamedLabel("exit_point"));
a.ldmxcsr(MABS(&m_near.ssemode));
a.mov(rsp, MABS(&m_near.hashstacksave));
a.add(rsp, 32);
a.add(rsp, 40);
a.emitEpilog(frame);
// generate a no code point
m_nocode = dst + a.offset();
a.bind(a.newNamedLabel("nocode_point"));
a.ret();
a.jmp(Gpq(REG_PARAM1));
// emit the generated code
size_t bytes = emit(ch);
@ -1612,30 +1611,32 @@ void drcbe_x64::op_hashjmp(Assembler &a, const instruction &inst)
smart_call_m64(a, &m_near.debug_log_hashjmp);
}
// load the stack base one word early so we end up at the right spot after our call below
// load the stack base
Label nocode = a.newLabel();
a.mov(rsp, MABS(&m_near.hashstacksave)); // mov rsp,[hashstacksave]
// fixed mode cases
if (modep.is_immediate() && m_hash.is_mode_populated(modep.immediate()))
{
// a straight immediate jump is direct, though we need the PC in EAX in case of failure
if (pcp.is_immediate())
{
// a straight immediate jump is direct, though we need the PC in EAX in case of failure
uint32_t l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
uint32_t l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
a.call(MABS(&m_hash.base()[modep.immediate()][l1val][l2val])); // call hash[modep][l1val][l2val]
a.short_().lea(Gpq(REG_PARAM1), ptr(nocode)); // lea rcx,[rip+nocode]
a.jmp(MABS(&m_hash.base()[modep.immediate()][l1val][l2val])); // jmp hash[modep][l1val][l2val]
}
// a fixed mode but variable PC
else
{
// a fixed mode but variable PC
mov_reg_param(a, eax, pcp); // mov eax,pcp
a.mov(edx, eax); // mov edx,eax
a.shr(edx, m_hash.l1shift()); // shr edx,l1shift
a.and_(eax, m_hash.l2mask() << m_hash.l2shift()); // and eax,l2mask << l2shift
a.mov(rdx, ptr(rbp, rdx, 3, offset_from_rbp(&m_hash.base()[modep.immediate()][0])));
// mov rdx,hash[modep+edx*8]
a.call(ptr(rdx, rax, 3 - m_hash.l2shift())); // call [rdx+rax*shift]
a.short_().lea(Gpq(REG_PARAM1), ptr(nocode)); // lea rcx,[rip+nocode]
a.jmp(ptr(rdx, rax, 3 - m_hash.l2shift())); // jmp [rdx+rax*shift]
}
}
else
@ -1645,31 +1646,30 @@ void drcbe_x64::op_hashjmp(Assembler &a, const instruction &inst)
mov_reg_param(a, modereg, modep); // mov modereg,modep
a.mov(rcx, ptr(rbp, modereg, 3, offset_from_rbp(m_hash.base()))); // mov rcx,hash[modereg*8]
// fixed PC
if (pcp.is_immediate())
{
// fixed PC
uint32_t l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
uint32_t l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
a.mov(rdx, ptr(rcx, l1val * 8)); // mov rdx,[rcx+l1val*8]
a.call(ptr(rdx, l2val * 8)); // call [l2val*8]
a.short_().lea(Gpq(REG_PARAM1), ptr(nocode)); // lea rcx,[rip+nocode]
a.jmp(ptr(rdx, l2val * 8)); // jmp [l2val*8]
}
// variable PC
else
{
// variable PC
mov_reg_param(a, eax, pcp); // mov eax,pcp
a.mov(edx, eax); // mov edx,eax
a.shr(edx, m_hash.l1shift()); // shr edx,l1shift
a.mov(rdx, ptr(rcx, rdx, 3)); // mov rdx,[rcx+rdx*8]
a.and_(eax, m_hash.l2mask() << m_hash.l2shift()); // and eax,l2mask << l2shift
a.call(ptr(rdx, rax, 3 - m_hash.l2shift())); // call [rdx+rax*shift]
a.short_().lea(Gpq(REG_PARAM1), ptr(nocode)); // lea rcx,[rip+nocode]
a.jmp(ptr(rdx, rax, 3 - m_hash.l2shift())); // jmp [rdx+rax*shift]
}
}
// fix stack alignment if "no code" landing returned from abuse of call with misaligned stack
a.sub(rsp, 8); // sub rsp,8
// in all cases, if there is no code, we return here to generate the exception
a.bind(nocode);
if (LOG_HASHJMPS)
smart_call_m64(a, &m_near.debug_log_hashjmp_fail);

View File

@ -2173,11 +2173,11 @@ void drcbe_x86::emit_rcl_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
a.and_(ecx, 63);
a.popfd();
a.jecxz(skipall);
a.short_().jecxz(skipall);
a.lea(ecx, ptr(ecx, -1));
a.bind(loop);
a.jecxz(skiploop);
a.short_().jecxz(skiploop);
a.lea(ecx, ptr(ecx, -1));
a.rcl(reglo, 1);
a.rcl(reghi, 1);
@ -2231,11 +2231,11 @@ void drcbe_x86::emit_rcr_r64_p64(Assembler &a, Gp const &reglo, Gp const &reghi,
a.and_(ecx, 63);
a.popfd();
a.jecxz(skipall);
a.short_().jecxz(skipall);
a.lea(ecx, ptr(ecx, -1));
a.bind(loop);
a.jecxz(skiploop);
a.short_().jecxz(skiploop);
a.lea(ecx, ptr(ecx, -1));
a.rcr(reghi, 1);
a.rcr(reglo, 1);
@ -2674,9 +2674,9 @@ void drcbe_x86::op_jmp(Assembler &a, const instruction &inst)
jmptarget = a.newNamedLabel(labelName.c_str());
if (inst.condition() == uml::COND_ALWAYS)
a.jmp(jmptarget); // jmp target
a.jmp(jmptarget);
else
a.j(X86_CONDITION(inst.condition()), jmptarget); // jcc target
a.j(X86_CONDITION(inst.condition()), jmptarget);
}
@ -2774,7 +2774,7 @@ void drcbe_x86::op_ret(Assembler &a, const instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
{
skip = a.newLabel();
a.j(X86_NOT_CONDITION(inst.condition()), skip); // jcc skip
a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
}
// return
@ -2811,17 +2811,17 @@ void drcbe_x86::op_callc(Assembler &a, const instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
{
skip = a.newLabel();
a.j(X86_NOT_CONDITION(inst.condition()), skip); // jcc skip
a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
}
// perform the call
a.mov(dword_ptr(esp, 0), imm(paramp.memory())); // mov [esp],paramp
a.call(imm(funcp.cfunc())); // call funcp
a.mov(dword_ptr(esp, 0), imm(paramp.memory()));
a.call(imm(funcp.cfunc()));
// resolve the conditional link
if (inst.condition() != uml::COND_ALWAYS)
{
a.bind(skip); // skip:
a.bind(skip);
reset_last_upper_lower_reg();
}
}
@ -3738,7 +3738,7 @@ void drcbe_x86::op_carry(Assembler &a, const instruction &inst)
Label higher = a.newLabel();
a.cmp(ecx, 32);
a.jge(higher);
a.short_().jge(higher);
if (srcp.is_memory())
{
@ -4795,7 +4795,7 @@ void drcbe_x86::op_divu(Assembler &a, const instruction &inst)
a.add(eax, eax); // add eax,eax
}
Label skip = a.newLabel();
a.jecxz(skip); // jecxz skip
a.short_().jecxz(skip); // jecxz skip
emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
a.xor_(edx, edx); // xor edx,edx
a.div(ecx); // div ecx
@ -4866,7 +4866,7 @@ void drcbe_x86::op_divs(Assembler &a, const instruction &inst)
a.add(eax, eax); // add eax,eax
}
Label skip = a.newLabel();
a.jecxz(skip); // jecxz skip
a.short_().jecxz(skip); // jecxz skip
emit_mov_r32_p32(a, eax, src1p); // mov eax,src1p
a.cdq(); // cdq
a.idiv(ecx); // idiv ecx
@ -5430,7 +5430,7 @@ void drcbe_x86::op_lzcnt(Assembler &a, const instruction &inst)
Label end = a.newLabel();
a.bsr(edx, edx);
a.jz(skip);
a.short_().jz(skip);
a.xor_(edx, 31 ^ 63);
a.mov(dstreg, edx);
a.short_().jmp(end);
@ -5494,7 +5494,7 @@ void drcbe_x86::op_tzcnt(Assembler &a, const instruction &inst)
Label skip = a.newLabel();
emit_mov_r64_p64(a, dstreg, edx, srcp); // mov dstreg:edx,srcp
a.bsf(dstreg, dstreg); // bsf dstreg,dstreg
a.jnz(skip); // jnz skip
a.short_().jnz(skip); // jnz skip
a.mov(ecx, 32); // mov ecx,32
a.bsf(dstreg, edx); // bsf dstreg,edx
a.cmovz(dstreg, ecx); // cmovz dstreg,ecx
@ -6126,21 +6126,21 @@ void drcbe_x86::op_fmov(Assembler &a, const instruction &inst)
if (inst.condition() != uml::COND_ALWAYS)
{
skip = a.newLabel();
a.j(X86_NOT_CONDITION(inst.condition()), skip); // jcc skip
a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);
}
// general case
a.mov(eax, MABS(srcp.memory(0))); // mov eax,[srcp]
a.mov(eax, MABS(srcp.memory(0)));
if (inst.size() == 8)
a.mov(edx, MABS(srcp.memory(4))); // mov edx,[srcp + 4]
a.mov(MABS(dstp.memory(0)), eax); // mov [dstp],eax
a.mov(edx, MABS(srcp.memory(4)));
a.mov(MABS(dstp.memory(0)), eax);
if (inst.size() == 8)
a.mov(MABS(dstp.memory(4)), edx); // mov [dstp + 4],edx
a.mov(MABS(dstp.memory(4)), edx);
// resolve the jump
if (inst.condition() != uml::COND_ALWAYS)
{
a.bind(skip); // skip:
a.bind(skip);
reset_last_upper_lower_reg();
}
}

View File

@ -72,7 +72,7 @@ const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp
delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
{
auto const [entrypoint, adjusted] = detail::resolve_member_function_itanium(m_function, m_this_delta, object);
auto const [entrypoint, adjusted] = resolve_member_function_itanium(m_function, m_this_delta, object);
object = reinterpret_cast<delegate_generic_class *>(adjusted);
return reinterpret_cast<delegate_generic_function>(entrypoint);
}
@ -87,7 +87,7 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
{
auto const [entrypoint, adjusted] = detail::resolve_member_function_msvc(&m_function, m_size, object);
auto const [entrypoint, adjusted] = resolve_member_function_msvc(&m_function, m_size, object);
object = reinterpret_cast<delegate_generic_class *>(adjusted);
return reinterpret_cast<delegate_generic_function>(entrypoint);
}

View File

@ -42,18 +42,18 @@ inline T bypass_member_function_thunks(T entrypoint, U const *object) noexcept
template <typename T, typename Ret, typename... Params>
inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...), T *object) noexcept
inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...), T &object) noexcept
{
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
{
struct { std::uintptr_t ptr; std::ptrdiff_t adj; } equiv;
assert(sizeof(function) == sizeof(equiv));
*reinterpret_cast<decltype(function) *>(&equiv) = function;
return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, object);
return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, &object);
}
else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
{
return detail::resolve_member_function_msvc(&function, sizeof(function), object);
return detail::resolve_member_function_msvc(&function, sizeof(function), &object);
}
else
{
@ -63,18 +63,18 @@ inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T:
template <typename T, typename Ret, typename... Params>
inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...) const, T const *object) noexcept
inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...) const, T const &object) noexcept
{
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
{
struct { std::uintptr_t ptr; std::ptrdiff_t adj; } equiv;
assert(sizeof(function) == sizeof(equiv));
*reinterpret_cast<decltype(function) *>(&equiv) = function;
return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, object);
return detail::resolve_member_function_itanium(equiv.ptr, equiv.adj, &object);
}
else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
{
return detail::resolve_member_function_msvc(&function, sizeof(function), object);
return detail::resolve_member_function_msvc(&function, sizeof(function), &object);
}
else
{