Files
l 17d4885c1d emitter: C89 core by default; reference headers out of common/emitter; legacy jumps as macros
Three changes, one intent: bare `make` and bare cmake now build the C89
emitter with nothing to specify, and common/emitter contains only the six
headers the recompilers actually use (c89emit.h, x86emitter.h, x86types.h,
instructions.h, legacy_instructions.h, x86emitter_shim.h).

Default flip. Makefile gains `C89_EMITTER ?= 1` -- set BEFORE the
Makefile.common include, because the source exclusion there reads it. The
first attempt set it next to the CXXFLAGS append at line 839, after the
include: the define applied but the exclusion never fired, and the build
went on linking reference objects it no longer needed. Caught by `make -n`
grep: a bare plan now names zero reference objects, a C89_EMITTER=0 plan
names all eight. CMake's option defaults to ON with the same semantics
(-DC89_EMITTER=OFF for the reference).

Reference headers. internal.h, legacy_internal.h and the thirteen-header
implement/ tree move to tests/emitter/reference beside the sources that
need them. internal.h now includes the implement set itself -- it is the
reference implementation's own root and must be self-sufficient; x86types.h
includes them only under the reference configuration, for the extern
declarations in instructions.h's #else branches. The three G*Type operator
enums move into x86types.h: recompilers name their values, which makes them
API, not implementation. iR5900-32.cpp's MOVQ-workaround include of
internal.h was already reference-gated and is repointed. cmake's installed-
header list is repointed too (the stale entries failed configure).

Legacy jump API. The fourteen rel8/rel32 wrappers become macros over two
static helpers (J8Rel, J32Rel), matching the C89 core's idiom. The helpers
are static -- internal linkage per TU -- so there is no header-linkage
subtlety left to get wrong, where the previous inline definitions carried
one. The speculative conditional mode initially drafted into J32Rel was
removed before commit: only the unconditional form has callers.

The legacy SSE C-API wrappers (SSE_MINSS_XMM_to_XMM and friends) now emit
through E_SSE_RR directly instead of hopping through the C++ shim objects.
Their constants are proven end-to-end in newbind: reference function vs
C89 macro across all register pairs, 2,048 cases within the 110,070 total.

Verified: six suites green, switchover identical, both compile gates 31/31
in both configurations, bare make links at 11,351,104 bytes with zero
reference encoder symbols and boots, cmake configure passes in both
configurations. The cmake three-target build has not completed in this
container (cmake 4 needs -DCMAKE_POLICY_VERSION_MINIMUM=3.5 for the
vendored cpuinfo; GL dev packages were also missing) -- it needs one run on
a normally-provisioned machine. C89_EMITTER=0 static: the gates and suites
compile and byte-verify that configuration; its last full link ran two
commits ago unchanged.
2026-08-18 08:21:02 +02:00

239 lines
6.7 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
/*
* ix86 core v0.9.1
*
* Original Authors (v0.6.2 and prior):
* linuzappz <[email protected]>
* alexey silinov
* goldfinger
* zerofrog(@gmail.com)
*
* Authors of v0.9.1:
* Jake.Stine(@gmail.com)
* cottonvibes(@gmail.com)
* sudonim([email protected])
*/
#include "tests/emitter/reference/internal.h"
namespace x86Emitter
{
void _xMovRtoR(const xRegisterInt& to, const xRegisterInt& from)
{
if (to == from)
return; // ignore redundant MOVs.
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0x88 : 0x89, from, to);
}
void xImpl_Mov::operator()(const xRegisterInt& to, const xRegisterInt& from) const
{
// FIXME WTF?
_xMovRtoR(to, from);
}
void xImpl_Mov::operator()(const xIndirectVoid& dest, const xRegisterInt& from) const
{
// mov eax has a special from when writing directly to a DISP32 address
// (sans any register index/base registers).
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0x88 : 0x89, from, dest);
}
void xImpl_Mov::operator()(const xRegisterInt& to, const xIndirectVoid& src) const
{
// mov eax has a special from when reading directly from a DISP32 address
// (sans any register index/base registers).
xOpWrite(to.GetPrefix16(), to.Is8BitOp() ? 0x8a : 0x8b, to, src);
}
void xImpl_Mov::operator()(const xIndirect64orLess& dest, sptr imm) const
{
xOpWrite(dest.GetPrefix16(), dest.Is8BitOp() ? 0xc6 : 0xc7, 0, dest, dest.GetImmSize());
dest.xWriteImm(imm);
}
// preserve_flags - set to true to disable optimizations which could alter the state of
// the flags (namely replacing mov reg,0 with xor).
void xImpl_Mov::operator()(const xRegisterInt& to, sptr imm, bool preserve_flags) const
{
const xRegisterInt& to_ = to.GetNonWide();
if (!preserve_flags && (imm == 0))
{
xXOR(to_, to_);
}
else if (imm == (sptr)(u32)imm || !(to._operandSize == 8))
{
// Note: MOV does not have (reg16/32,imm8) forms.
// Id & 7, not Id: the high bit of an extended register is
// carried by REX.B, which EmitRex has already emitted. ORing the
// full Id sets bit 3 of the opcode as well, and for the 8-bit
// form that turns 0xb0 into 0xb8 -- mov r8b,imm8 becomes the
// dword-immediate opcode with one byte of immediate after it.
// (The 32-bit form is unaffected only because 0xb8 already has
// bit 3 set.)
u8 opcode = (to_.Is8BitOp() ? 0xb0 : 0xb8) | (to_.Id & 7);
xOpAccWrite(to_.GetPrefix16(), opcode, 0, to_);
to_.xWriteImm(imm);
}
else if (to._operandSize == 8 && imm != (sptr)(s32)imm)
{
// Neither 32-bit form can hold this: B8+r zero-extends and C7 /0
// sign-extends, so anything outside [-2G, 4G) needs the ten-byte
// movabs. Falling through to C7 /0 here silently truncated the
// value, which is a hard fault to trace when the value was a host
// pointer -- see 3fbc3bd and f652552, both of which were exactly
// this, discovered only as a SIGSEGV in emitted code.
const u8 opcode = 0xb8 | (to.Id & 7);
xOpAccWrite(to.GetPrefix16(), opcode, 0, to);
xWrite64(imm);
}
else
{
xOpWrite(to.GetPrefix16(), 0xc7, 0, to);
to.xWriteImm(imm);
}
}
#ifndef PCSX2_C89_EMITTER
const xImpl_Mov xMOV;
#endif
void xImpl_MovImm64::operator()(const xRegister64& to, s64 imm, bool preserve_flags) const
{
if (imm == (u32)imm || imm == (s32)imm)
{
xMOV(to, imm, preserve_flags);
}
else
{
u8 opcode = 0xb8 | to.Id;
xOpAccWrite(to.GetPrefix16(), opcode, 0, to);
xWrite64(imm);
}
}
#ifndef PCSX2_C89_EMITTER
const xImpl_MovImm64 xMOV64;
#endif
// --------------------------------------------------------------------------------------
// CMOVcc
// --------------------------------------------------------------------------------------
void xImpl_CMov::operator()(const xRegister16or32or64& to, const xRegister16or32or64& from) const
{
xOpWrite0F(to->GetPrefix16(), 0x40 | ccType, to, from);
}
void xImpl_CMov::operator()(const xRegister16or32or64& to, const xIndirectVoid& sibsrc) const
{
xOpWrite0F(to->GetPrefix16(), 0x40 | ccType, to, sibsrc);
}
void xImpl_Set::operator()(const xRegister8& to) const
{
xOpWrite0F(0, 0x90 | ccType, 0, to);
}
void xImpl_Set::operator()(const xIndirect8& dest) const
{
xOpWrite0F(0, 0x90 | ccType, 0, dest);
}
void xImpl_MovExtend::operator()(const xRegister16or32or64& to, const xRegister8& from) const
{
xOpWrite0F(
(to->_operandSize == 2) ? 0x66 : 0,
SignExtend ? 0xbe : 0xb6,
to, from);
}
void xImpl_MovExtend::operator()(const xRegister16or32or64& to, const xIndirect8& sibsrc) const
{
xOpWrite0F(
(to->_operandSize == 2) ? 0x66 : 0,
SignExtend ? 0xbe : 0xb6,
to, sibsrc);
}
void xImpl_MovExtend::operator()(const xRegister32or64& to, const xRegister16& from) const
{
xOpWrite0F(0, SignExtend ? 0xbf : 0xb7, to, from);
}
void xImpl_MovExtend::operator()(const xRegister32or64& to, const xIndirect16& sibsrc) const
{
xOpWrite0F(0, SignExtend ? 0xbf : 0xb7, to, sibsrc);
}
void xImpl_MovExtend::operator()(const xRegister64& to, const xRegister32& from) const
{
xOpWrite(0, 0x63, to, from);
}
void xImpl_MovExtend::operator()(const xRegister64& to, const xIndirect32& sibsrc) const
{
xOpWrite(0, 0x63, to, sibsrc);
}
#ifndef PCSX2_C89_EMITTER
const xImpl_MovExtend xMOVSX = {true};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_MovExtend xMOVZX = {false};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVB = {Jcc_Below};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVGE = {Jcc_GreaterOrEqual};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVE = {Jcc_Equal};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVNE = {Jcc_NotEqual};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVS = {Jcc_Signed};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_CMov xCMOVNS = {Jcc_Unsigned};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_Set xSETA = {Jcc_Above};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_Set xSETB = {Jcc_Below};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_Set xSETG = {Jcc_Greater};
#endif
#ifndef PCSX2_C89_EMITTER
const xImpl_Set xSETL = {Jcc_Less};
#endif
} // end namespace x86Emitter