add split_map class for mapping keys to values in a way that optimizes for frequent searches and infrequent insertions/removals remove jump table implementation of GetColorRenderTargetFormatComponentCount, it was appearing relatively high in profiles. instead pack the component counts into a single 32 bit word, which is indexed by shifting Add cvar to align all basic blocks to a boundary Add mmio aware load paths liberally apply XE_RESTRICT in ringbuffer related code Removed the IS_TRUE and IS_FALSE opcodes, they were pointless duplicates of COMPARE_EQ/COMPARE_NE and i want to simplify our set of opcodes for future backends More work on LVSR/LVSL/STVR/STVL opcodes Optimized X64 translated code emission, now only compute instrkey once Add code for pre-computing integer division magic numbers Optimized GetHostViewportInfo a little Move args for GetHostViewportInfo into a class, cache the result and compare for future queries. moved GetHostViewportInfo far lower on the profile Add (currently not functional, and very racy) asynchronous memcpy code. will improve it and actually use it in future commits. Add non-temporal memcpy function for huge page-aligned allocations. Used for copying to shared memory/readback hoist are_accumulated_render_targets_valid_ check out of loop in render_target_cache already bound check. Add stosb/movsb code for small constant memcpys/memsets that arent worth the overhead of memcpy/memset
614 lines
20 KiB
C++
614 lines
20 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2018 Xenia Developers. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
#include "xenia/cpu/backend/x64/x64_op.h"
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
namespace backend {
|
|
namespace x64 {
|
|
|
|
volatile int anchor_control = 0;
|
|
template <typename T>
|
|
static void EmitFusedBranch(X64Emitter& e, const T& i) {
|
|
bool valid = i.instr->prev && i.instr->prev->dest == i.src1.value;
|
|
auto opcode = valid ? i.instr->prev->opcode->num : -1;
|
|
if (valid) {
|
|
auto name = i.src2.value->name;
|
|
switch (opcode) {
|
|
case OPCODE_COMPARE_EQ:
|
|
e.je(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_NE:
|
|
e.jne(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_SLT:
|
|
e.jl(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_SLE:
|
|
e.jle(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_SGT:
|
|
e.jg(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_SGE:
|
|
e.jge(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_ULT:
|
|
e.jb(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_ULE:
|
|
e.jbe(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_UGT:
|
|
e.ja(name, e.T_NEAR);
|
|
break;
|
|
case OPCODE_COMPARE_UGE:
|
|
e.jae(name, e.T_NEAR);
|
|
break;
|
|
default:
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(name, e.T_NEAR);
|
|
break;
|
|
}
|
|
} else {
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
}
|
|
// ============================================================================
|
|
// OPCODE_DEBUG_BREAK
|
|
// ============================================================================
|
|
struct DEBUG_BREAK : Sequence<DEBUG_BREAK, I<OPCODE_DEBUG_BREAK, VoidOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) { e.DebugBreak(); }
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK, DEBUG_BREAK);
|
|
|
|
// ============================================================================
|
|
// OPCODE_DEBUG_BREAK_TRUE
|
|
// ============================================================================
|
|
struct DEBUG_BREAK_TRUE_I8
|
|
: Sequence<DEBUG_BREAK_TRUE_I8, I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I8Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
};
|
|
struct DEBUG_BREAK_TRUE_I16
|
|
: Sequence<DEBUG_BREAK_TRUE_I16,
|
|
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I16Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
};
|
|
struct DEBUG_BREAK_TRUE_I32
|
|
: Sequence<DEBUG_BREAK_TRUE_I32,
|
|
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
if (e.IsFeatureEnabled(kX64FastJrcx)) {
|
|
e.mov(e.ecx, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jrcxz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
} else {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
}
|
|
};
|
|
struct DEBUG_BREAK_TRUE_I64
|
|
: Sequence<DEBUG_BREAK_TRUE_I64,
|
|
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
if (e.IsFeatureEnabled(kX64FastJrcx)) {
|
|
e.mov(e.rcx, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jrcxz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
} else {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
}
|
|
};
|
|
struct DEBUG_BREAK_TRUE_F32
|
|
: Sequence<DEBUG_BREAK_TRUE_F32,
|
|
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.vptest(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
};
|
|
struct DEBUG_BREAK_TRUE_F64
|
|
: Sequence<DEBUG_BREAK_TRUE_F64,
|
|
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.vptest(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.DebugBreak();
|
|
e.L(skip);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK_TRUE, DEBUG_BREAK_TRUE_I8,
|
|
DEBUG_BREAK_TRUE_I16, DEBUG_BREAK_TRUE_I32,
|
|
DEBUG_BREAK_TRUE_I64, DEBUG_BREAK_TRUE_F32,
|
|
DEBUG_BREAK_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_TRAP
|
|
// ============================================================================
|
|
struct TRAP : Sequence<TRAP, I<OPCODE_TRAP, VoidOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.Trap(i.instr->flags);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_TRAP, TRAP);
|
|
|
|
// ============================================================================
|
|
// OPCODE_TRAP_TRUE
|
|
// ============================================================================
|
|
struct TRAP_TRUE_I8
|
|
: Sequence<TRAP_TRUE_I8, I<OPCODE_TRAP_TRUE, VoidOp, I8Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
Xbyak::Label& after = e.NewCachedLabel();
|
|
unsigned flags = i.instr->flags;
|
|
Xbyak::Label& dotrap =
|
|
e.AddToTail([flags, &after](X64Emitter& e, Xbyak::Label& me) {
|
|
e.L(me);
|
|
e.Trap(flags);
|
|
// does Trap actually return control to the guest?
|
|
e.jmp(after, X64Emitter::T_NEAR);
|
|
});
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(dotrap, X64Emitter::T_NEAR);
|
|
e.L(after);
|
|
}
|
|
};
|
|
struct TRAP_TRUE_I16
|
|
: Sequence<TRAP_TRUE_I16, I<OPCODE_TRAP_TRUE, VoidOp, I16Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(TRAP_TRUE_I16);
|
|
}
|
|
};
|
|
struct TRAP_TRUE_I32
|
|
: Sequence<TRAP_TRUE_I32, I<OPCODE_TRAP_TRUE, VoidOp, I32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(TRAP_TRUE_I32);
|
|
}
|
|
};
|
|
struct TRAP_TRUE_I64
|
|
: Sequence<TRAP_TRUE_I64, I<OPCODE_TRAP_TRUE, VoidOp, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(TRAP_TRUE_I64);
|
|
}
|
|
};
|
|
struct TRAP_TRUE_F32
|
|
: Sequence<TRAP_TRUE_F32, I<OPCODE_TRAP_TRUE, VoidOp, F32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(TRAP_TRUE_F32);
|
|
}
|
|
};
|
|
struct TRAP_TRUE_F64
|
|
: Sequence<TRAP_TRUE_F64, I<OPCODE_TRAP_TRUE, VoidOp, F64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(TRAP_TRUE_F64);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
|
|
TRAP_TRUE_I32, TRAP_TRUE_I64, TRAP_TRUE_F32,
|
|
TRAP_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_CALL
|
|
// ============================================================================
|
|
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_true(i.src1.value->is_guest());
|
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
|
|
|
// ============================================================================
|
|
// OPCODE_CALL_TRUE
|
|
// ============================================================================
|
|
struct CALL_TRUE_I8
|
|
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_true(i.src2.value->is_guest());
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
|
e.L(skip);
|
|
e.ForgetMxcsrMode();
|
|
}
|
|
};
|
|
struct CALL_TRUE_I16
|
|
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_true(i.src2.value->is_guest());
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
|
e.L(skip);
|
|
e.ForgetMxcsrMode();
|
|
}
|
|
};
|
|
struct CALL_TRUE_I32
|
|
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_true(i.src2.value->is_guest());
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
|
e.L(skip);
|
|
e.ForgetMxcsrMode();
|
|
}
|
|
};
|
|
struct CALL_TRUE_I64
|
|
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_true(i.src2.value->is_guest());
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip);
|
|
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
|
e.L(skip);
|
|
e.ForgetMxcsrMode();
|
|
}
|
|
};
|
|
struct CALL_TRUE_F32
|
|
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(CALL_TRUE_F32);
|
|
}
|
|
};
|
|
|
|
struct CALL_TRUE_F64
|
|
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(CALL_TRUE_F64);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_CALL_TRUE, CALL_TRUE_I8, CALL_TRUE_I16,
|
|
CALL_TRUE_I32, CALL_TRUE_I64, CALL_TRUE_F32,
|
|
CALL_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_CALL_INDIRECT
|
|
// ============================================================================
|
|
struct CALL_INDIRECT
|
|
: Sequence<CALL_INDIRECT, I<OPCODE_CALL_INDIRECT, VoidOp, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.CallIndirect(i.instr, i.src1);
|
|
e.ForgetMxcsrMode();
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT, CALL_INDIRECT);
|
|
|
|
// ============================================================================
|
|
// OPCODE_CALL_INDIRECT_TRUE
|
|
// ============================================================================
|
|
struct CALL_INDIRECT_TRUE_I8
|
|
: Sequence<CALL_INDIRECT_TRUE_I8,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I8Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip, CodeGenerator::T_NEAR);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
}
|
|
};
|
|
struct CALL_INDIRECT_TRUE_I16
|
|
: Sequence<CALL_INDIRECT_TRUE_I16,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I16Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip, CodeGenerator::T_NEAR);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
}
|
|
};
|
|
struct CALL_INDIRECT_TRUE_I32
|
|
: Sequence<CALL_INDIRECT_TRUE_I32,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I32Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
if (e.IsFeatureEnabled(kX64FastJrcx)) {
|
|
e.mov(e.ecx, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jrcxz(skip);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
} else {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip, CodeGenerator::T_NEAR);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
}
|
|
}
|
|
};
|
|
struct CALL_INDIRECT_TRUE_I64
|
|
: Sequence<CALL_INDIRECT_TRUE_I64,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I64Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
if (e.IsFeatureEnabled(kX64FastJrcx)) {
|
|
e.mov(e.rcx, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jrcxz(skip);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
} else {
|
|
e.test(i.src1, i.src1);
|
|
Xbyak::Label skip;
|
|
e.jz(skip, CodeGenerator::T_NEAR);
|
|
e.CallIndirect(i.instr, i.src2);
|
|
e.L(skip);
|
|
}
|
|
}
|
|
};
|
|
struct CALL_INDIRECT_TRUE_F32
|
|
: Sequence<CALL_INDIRECT_TRUE_F32,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F32Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(CALL_INDIRECT_TRUE_F32);
|
|
}
|
|
};
|
|
struct CALL_INDIRECT_TRUE_F64
|
|
: Sequence<CALL_INDIRECT_TRUE_F64,
|
|
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F64Op, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(CALL_INDIRECT_TRUE_F64);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT_TRUE, CALL_INDIRECT_TRUE_I8,
|
|
CALL_INDIRECT_TRUE_I16, CALL_INDIRECT_TRUE_I32,
|
|
CALL_INDIRECT_TRUE_I64, CALL_INDIRECT_TRUE_F32,
|
|
CALL_INDIRECT_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_CALL_EXTERN
|
|
// ============================================================================
|
|
struct CALL_EXTERN
|
|
: Sequence<CALL_EXTERN, I<OPCODE_CALL_EXTERN, VoidOp, SymbolOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.CallExtern(i.instr, i.src1.value);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_CALL_EXTERN, CALL_EXTERN);
|
|
|
|
// ============================================================================
|
|
// OPCODE_RETURN
|
|
// ============================================================================
|
|
struct RETURN : Sequence<RETURN, I<OPCODE_RETURN, VoidOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
// If this is the last instruction in the last block, just let us
|
|
// fall through.
|
|
if (i.instr->next || i.instr->block->next) {
|
|
e.jmp(e.epilog_label(), CodeGenerator::T_NEAR);
|
|
}
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_RETURN, RETURN);
|
|
|
|
// ============================================================================
|
|
// OPCODE_RETURN_TRUE
|
|
// ============================================================================
|
|
struct RETURN_TRUE_I8
|
|
: Sequence<RETURN_TRUE_I8, I<OPCODE_RETURN_TRUE, VoidOp, I8Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
|
}
|
|
};
|
|
struct RETURN_TRUE_I16
|
|
: Sequence<RETURN_TRUE_I16, I<OPCODE_RETURN_TRUE, VoidOp, I16Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
|
}
|
|
};
|
|
struct RETURN_TRUE_I32
|
|
: Sequence<RETURN_TRUE_I32, I<OPCODE_RETURN_TRUE, VoidOp, I32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
|
}
|
|
};
|
|
struct RETURN_TRUE_I64
|
|
: Sequence<RETURN_TRUE_I64, I<OPCODE_RETURN_TRUE, VoidOp, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
|
}
|
|
};
|
|
struct RETURN_TRUE_F32
|
|
: Sequence<RETURN_TRUE_F32, I<OPCODE_RETURN_TRUE, VoidOp, F32Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(RETURN_TRUE_F32);
|
|
}
|
|
};
|
|
struct RETURN_TRUE_F64
|
|
: Sequence<RETURN_TRUE_F64, I<OPCODE_RETURN_TRUE, VoidOp, F64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
assert_impossible_sequence(RETURN_TRUE_F64);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_RETURN_TRUE, RETURN_TRUE_I8, RETURN_TRUE_I16,
|
|
RETURN_TRUE_I32, RETURN_TRUE_I64, RETURN_TRUE_F32,
|
|
RETURN_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_SET_RETURN_ADDRESS
|
|
// ============================================================================
|
|
struct SET_RETURN_ADDRESS
|
|
: Sequence<SET_RETURN_ADDRESS,
|
|
I<OPCODE_SET_RETURN_ADDRESS, VoidOp, I64Op>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.SetReturnAddress(i.src1.constant());
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS);
|
|
|
|
// ============================================================================
|
|
// OPCODE_BRANCH
|
|
// ============================================================================
|
|
struct BRANCH : Sequence<BRANCH, I<OPCODE_BRANCH, VoidOp, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.jmp(i.src1.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_BRANCH, BRANCH);
|
|
|
|
// ============================================================================
|
|
// OPCODE_BRANCH_TRUE
|
|
// ============================================================================
|
|
struct BRANCH_TRUE_I8
|
|
: Sequence<BRANCH_TRUE_I8, I<OPCODE_BRANCH_TRUE, VoidOp, I8Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
EmitFusedBranch(e, i);
|
|
}
|
|
};
|
|
struct BRANCH_TRUE_I16
|
|
: Sequence<BRANCH_TRUE_I16, I<OPCODE_BRANCH_TRUE, VoidOp, I16Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
EmitFusedBranch(e, i);
|
|
}
|
|
};
|
|
struct BRANCH_TRUE_I32
|
|
: Sequence<BRANCH_TRUE_I32, I<OPCODE_BRANCH_TRUE, VoidOp, I32Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
EmitFusedBranch(e, i);
|
|
}
|
|
};
|
|
struct BRANCH_TRUE_I64
|
|
: Sequence<BRANCH_TRUE_I64, I<OPCODE_BRANCH_TRUE, VoidOp, I64Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
EmitFusedBranch(e, i);
|
|
}
|
|
};
|
|
struct BRANCH_TRUE_F32
|
|
: Sequence<BRANCH_TRUE_F32, I<OPCODE_BRANCH_TRUE, VoidOp, F32Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
/*
|
|
chrispy: right now, im not confident that we are always clearing
|
|
the upper 96 bits of registers, making vptest extremely unsafe. many
|
|
ss/sd operations copy over the upper 96 from the source, and for abs we
|
|
negate ALL elements, making the top 64 bits contain 0x80000000 etc
|
|
*/
|
|
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
|
e.vmovd(e.eax, input);
|
|
e.test(e.eax, e.eax);
|
|
e.jnz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_TRUE_F64
|
|
: Sequence<BRANCH_TRUE_F64, I<OPCODE_BRANCH_TRUE, VoidOp, F64Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
|
e.vmovq(e.rax, input);
|
|
e.test(e.rax, e.rax);
|
|
e.jnz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_TRUE, BRANCH_TRUE_I8, BRANCH_TRUE_I16,
|
|
BRANCH_TRUE_I32, BRANCH_TRUE_I64, BRANCH_TRUE_F32,
|
|
BRANCH_TRUE_F64);
|
|
|
|
// ============================================================================
|
|
// OPCODE_BRANCH_FALSE
|
|
// ============================================================================
|
|
struct BRANCH_FALSE_I8
|
|
: Sequence<BRANCH_FALSE_I8, I<OPCODE_BRANCH_FALSE, VoidOp, I8Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_FALSE_I16
|
|
: Sequence<BRANCH_FALSE_I16,
|
|
I<OPCODE_BRANCH_FALSE, VoidOp, I16Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_FALSE_I32
|
|
: Sequence<BRANCH_FALSE_I32,
|
|
I<OPCODE_BRANCH_FALSE, VoidOp, I32Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_FALSE_I64
|
|
: Sequence<BRANCH_FALSE_I64,
|
|
I<OPCODE_BRANCH_FALSE, VoidOp, I64Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
e.test(i.src1, i.src1);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_FALSE_F32
|
|
: Sequence<BRANCH_FALSE_F32,
|
|
I<OPCODE_BRANCH_FALSE, VoidOp, F32Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
|
e.vmovd(e.eax, input);
|
|
e.test(e.eax, e.eax);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
struct BRANCH_FALSE_F64
|
|
: Sequence<BRANCH_FALSE_F64,
|
|
I<OPCODE_BRANCH_FALSE, VoidOp, F64Op, LabelOp>> {
|
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
|
Xmm input = GetInputRegOrConstant(e, i.src1, e.xmm0);
|
|
e.vmovq(e.rax, input);
|
|
e.test(e.rax, e.rax);
|
|
e.jz(i.src2.value->name, e.T_NEAR);
|
|
}
|
|
};
|
|
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_FALSE, BRANCH_FALSE_I8, BRANCH_FALSE_I16,
|
|
BRANCH_FALSE_I32, BRANCH_FALSE_I64, BRANCH_FALSE_F32,
|
|
BRANCH_FALSE_F64);
|
|
|
|
} // namespace x64
|
|
} // namespace backend
|
|
} // namespace cpu
|
|
} // namespace xe
|