mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-03-14 09:52:56 +00:00
Port x64 backend to xbyak
This commit is contained in:
113
src/backend_x64/abi.cpp
Normal file
113
src/backend_x64/abi.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// 24th August 2016: This code was modified for Dynarmic.
|
||||
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/abi.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/iterator_util.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
constexpr size_t GPR_SIZE = 8;
|
||||
constexpr size_t XMM_SIZE = 16;
|
||||
|
||||
struct FrameInfo {
|
||||
size_t stack_subtraction = 0;
|
||||
size_t xmm_offset = 0;
|
||||
};
|
||||
|
||||
static FrameInfo CalculateFrameInfo(size_t num_gprs, size_t num_xmms, size_t frame_size) {
|
||||
FrameInfo frame_info = {};
|
||||
|
||||
size_t rsp_alignment = 8; // We are always 8-byte aligned initially
|
||||
rsp_alignment -= num_gprs * GPR_SIZE;
|
||||
|
||||
if (num_xmms > 0) {
|
||||
frame_info.stack_subtraction = rsp_alignment & 0xF;
|
||||
frame_info.stack_subtraction += num_xmms * XMM_SIZE;
|
||||
}
|
||||
|
||||
size_t xmm_base = frame_info.stack_subtraction;
|
||||
|
||||
frame_info.stack_subtraction += frame_size;
|
||||
frame_info.stack_subtraction += ABI_SHADOW_SPACE;
|
||||
|
||||
rsp_alignment -= frame_info.stack_subtraction;
|
||||
frame_info.stack_subtraction += rsp_alignment & 0xF;
|
||||
|
||||
frame_info.xmm_offset = frame_info.stack_subtraction - xmm_base;
|
||||
|
||||
return frame_info;
|
||||
}
|
||||
|
||||
void ABI_PushCalleeSaveRegistersAndAdjustStack(Xbyak::CodeGenerator* code, size_t frame_size) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = std::count_if(ABI_ALL_CALLEE_SAVE.begin(), ABI_ALL_CALLEE_SAVE.end(), HostLocIsGPR);
|
||||
const size_t num_xmms = std::count_if(ABI_ALL_CALLEE_SAVE.begin(), ABI_ALL_CALLEE_SAVE.end(), HostLocIsXMM);
|
||||
|
||||
FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
||||
|
||||
for (HostLoc gpr : ABI_ALL_CALLEE_SAVE) {
|
||||
if (HostLocIsGPR(gpr)) {
|
||||
code->push(HostLocToReg64(gpr));
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_info.stack_subtraction != 0) {
|
||||
code->sub(rsp, u32(frame_info.stack_subtraction));
|
||||
}
|
||||
|
||||
size_t xmm_offset = frame_info.xmm_offset;
|
||||
for (HostLoc xmm : ABI_ALL_CALLEE_SAVE) {
|
||||
if (HostLocIsXMM(xmm)) {
|
||||
code->movaps(code->xword[rsp + xmm_offset], HostLocToXmm(xmm));
|
||||
xmm_offset += XMM_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ABI_PopCalleeSaveRegistersAndAdjustStack(Xbyak::CodeGenerator* code, size_t frame_size) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = std::count_if(ABI_ALL_CALLEE_SAVE.begin(), ABI_ALL_CALLEE_SAVE.end(), HostLocIsGPR);
|
||||
const size_t num_xmms = std::count_if(ABI_ALL_CALLEE_SAVE.begin(), ABI_ALL_CALLEE_SAVE.end(), HostLocIsXMM);
|
||||
|
||||
FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
|
||||
|
||||
size_t xmm_offset = frame_info.xmm_offset;
|
||||
for (HostLoc xmm : Common::Reverse(ABI_ALL_CALLEE_SAVE)) {
|
||||
if (HostLocIsXMM(xmm)) {
|
||||
code->movaps(HostLocToXmm(xmm), code->xword[rsp + xmm_offset]);
|
||||
xmm_offset += XMM_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_info.stack_subtraction != 0) {
|
||||
code->add(rsp, u32(frame_info.stack_subtraction));
|
||||
}
|
||||
|
||||
for (HostLoc gpr : Common::Reverse(ABI_ALL_CALLEE_SAVE)) {
|
||||
if (HostLocIsGPR(gpr)) {
|
||||
code->pop(HostLocToReg64(gpr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
||||
119
src/backend_x64/abi.h
Normal file
119
src/backend_x64/abi.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "backend_x64/hostloc.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
constexpr HostLoc ABI_RETURN = HostLoc::RAX;
|
||||
|
||||
constexpr HostLoc ABI_PARAM1 = HostLoc::RCX;
|
||||
constexpr HostLoc ABI_PARAM2 = HostLoc::RDX;
|
||||
constexpr HostLoc ABI_PARAM3 = HostLoc::R8;
|
||||
constexpr HostLoc ABI_PARAM4 = HostLoc::R9;
|
||||
|
||||
constexpr std::array<HostLoc, 13> ABI_ALL_CALLER_SAVE = {
|
||||
HostLoc::RAX,
|
||||
HostLoc::RCX,
|
||||
HostLoc::RDX,
|
||||
HostLoc::R8,
|
||||
HostLoc::R9,
|
||||
HostLoc::R10,
|
||||
HostLoc::R11,
|
||||
HostLoc::XMM0,
|
||||
HostLoc::XMM1,
|
||||
HostLoc::XMM2,
|
||||
HostLoc::XMM3,
|
||||
HostLoc::XMM4,
|
||||
HostLoc::XMM5,
|
||||
};
|
||||
|
||||
constexpr std::array<HostLoc, 18> ABI_ALL_CALLEE_SAVE = {
|
||||
HostLoc::RBX,
|
||||
HostLoc::RSI,
|
||||
HostLoc::RDI,
|
||||
HostLoc::RBP,
|
||||
HostLoc::R12,
|
||||
HostLoc::R13,
|
||||
HostLoc::R14,
|
||||
HostLoc::R15,
|
||||
HostLoc::XMM6,
|
||||
HostLoc::XMM7,
|
||||
HostLoc::XMM8,
|
||||
HostLoc::XMM9,
|
||||
HostLoc::XMM10,
|
||||
HostLoc::XMM11,
|
||||
HostLoc::XMM12,
|
||||
HostLoc::XMM13,
|
||||
HostLoc::XMM14,
|
||||
HostLoc::XMM15,
|
||||
};
|
||||
|
||||
constexpr size_t ABI_SHADOW_SPACE = 32; // bytes
|
||||
|
||||
#else
|
||||
|
||||
constexpr HostLoc ABI_RETURN = HostLoc::RAX;
|
||||
|
||||
constexpr HostLoc ABI_PARAM1 = HostLoc::RDI;
|
||||
constexpr HostLoc ABI_PARAM2 = HostLoc::RSI;
|
||||
constexpr HostLoc ABI_PARAM3 = HostLoc::RDX;
|
||||
constexpr HostLoc ABI_PARAM4 = HostLoc::RCX;
|
||||
|
||||
constexpr std::array<HostLoc, 25> ABI_ALL_CALLER_SAVE = {
|
||||
HostLoc::RAX,
|
||||
HostLoc::RCX,
|
||||
HostLoc::RDX,
|
||||
HostLoc::RDI,
|
||||
HostLoc::RSI,
|
||||
HostLoc::R8,
|
||||
HostLoc::R9,
|
||||
HostLoc::R10,
|
||||
HostLoc::R11,
|
||||
HostLoc::XMM0,
|
||||
HostLoc::XMM1,
|
||||
HostLoc::XMM2,
|
||||
HostLoc::XMM3,
|
||||
HostLoc::XMM4,
|
||||
HostLoc::XMM5,
|
||||
HostLoc::XMM6,
|
||||
HostLoc::XMM7,
|
||||
HostLoc::XMM8,
|
||||
HostLoc::XMM9,
|
||||
HostLoc::XMM10,
|
||||
HostLoc::XMM11,
|
||||
HostLoc::XMM12,
|
||||
HostLoc::XMM13,
|
||||
HostLoc::XMM14,
|
||||
HostLoc::XMM15,
|
||||
};
|
||||
|
||||
constexpr std::array<HostLoc, 6> ABI_ALL_CALLEE_SAVE = {
|
||||
HostLoc::RBX,
|
||||
HostLoc::RBP,
|
||||
HostLoc::R12,
|
||||
HostLoc::R13,
|
||||
HostLoc::R14,
|
||||
HostLoc::R15,
|
||||
};
|
||||
|
||||
constexpr size_t ABI_SHADOW_SPACE = 0; // bytes
|
||||
|
||||
#endif
|
||||
|
||||
static_assert(ABI_ALL_CALLER_SAVE.size() + ABI_ALL_CALLEE_SAVE.size() == 31, "Invalid total number of registers");
|
||||
|
||||
void ABI_PushCalleeSaveRegistersAndAdjustStack(Xbyak::CodeGenerator* code, size_t frame_size = 0);
|
||||
void ABI_PopCalleeSaveRegistersAndAdjustStack(Xbyak::CodeGenerator* code, size_t frame_size = 0);
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
||||
@@ -6,27 +6,22 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/abi.h"
|
||||
#include "backend_x64/block_of_code.h"
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/x64/abi.h"
|
||||
|
||||
using namespace Gen;
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
BlockOfCode::BlockOfCode() : Gen::XCodeBlock() {
|
||||
AllocCodeSpace(128 * 1024 * 1024);
|
||||
BlockOfCode::BlockOfCode() : Xbyak::CodeGenerator(128 * 1024 * 1024) {
|
||||
ClearCache(false);
|
||||
}
|
||||
|
||||
void BlockOfCode::ClearCache(bool poison_memory) {
|
||||
if (poison_memory) {
|
||||
ClearCodeSpace();
|
||||
} else {
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
reset();
|
||||
GenConstants();
|
||||
GenRunCode();
|
||||
GenReturnFromRunCode();
|
||||
@@ -42,68 +37,116 @@ size_t BlockOfCode::RunCode(JitState* jit_state, CodePtr basic_block, size_t cyc
|
||||
}
|
||||
|
||||
void BlockOfCode::ReturnFromRunCode(bool MXCSR_switch) {
|
||||
JMP(MXCSR_switch ? return_from_run_code : return_from_run_code_without_mxcsr_switch, true);
|
||||
jmp(MXCSR_switch ? return_from_run_code : return_from_run_code_without_mxcsr_switch);
|
||||
}
|
||||
|
||||
void BlockOfCode::GenConstants() {
|
||||
const_FloatNegativeZero32 = AlignCode16();
|
||||
Write32(0x80000000u);
|
||||
const_FloatNaN32 = AlignCode16();
|
||||
Write32(0x7fc00000u);
|
||||
const_FloatNonSignMask32 = AlignCode16();
|
||||
Write64(0x7fffffffu);
|
||||
const_FloatNegativeZero64 = AlignCode16();
|
||||
Write64(0x8000000000000000u);
|
||||
const_FloatNaN64 = AlignCode16();
|
||||
Write64(0x7ff8000000000000u);
|
||||
const_FloatNonSignMask64 = AlignCode16();
|
||||
Write64(0x7fffffffffffffffu);
|
||||
const_FloatPenultimatePositiveDenormal64 = AlignCode16();
|
||||
Write64(0x000ffffffffffffeu);
|
||||
const_FloatMinS32 = AlignCode16();
|
||||
Write64(0xc1e0000000000000u); // -2147483648 as a double
|
||||
const_FloatMaxS32 = AlignCode16();
|
||||
Write64(0x41dfffffffc00000u); // 2147483647 as a double
|
||||
const_FloatPositiveZero32 = const_FloatPositiveZero64 = const_FloatMinU32 = AlignCode16();
|
||||
Write64(0x0000000000000000u); // 0 as a double
|
||||
const_FloatMaxU32 = AlignCode16();
|
||||
Write64(0x41efffffffe00000u); // 4294967295 as a double
|
||||
AlignCode16();
|
||||
align();
|
||||
L(const_FloatNegativeZero32);
|
||||
dd(0x80000000u);
|
||||
|
||||
align();
|
||||
L(const_FloatNaN32);
|
||||
dd(0x7fc00000u);
|
||||
|
||||
align();
|
||||
L(const_FloatNonSignMask32);
|
||||
dq(0x7fffffffu);
|
||||
|
||||
align();
|
||||
L(const_FloatNegativeZero64);
|
||||
dq(0x8000000000000000u);
|
||||
|
||||
align();
|
||||
L(const_FloatNaN64);
|
||||
dq(0x7ff8000000000000u);
|
||||
|
||||
align();
|
||||
L(const_FloatNonSignMask64);
|
||||
dq(0x7fffffffffffffffu);
|
||||
|
||||
align();
|
||||
L(const_FloatPenultimatePositiveDenormal64);
|
||||
dq(0x000ffffffffffffeu);
|
||||
|
||||
align();
|
||||
L(const_FloatMinS32);
|
||||
dq(0xc1e0000000000000u); // -2147483648 as a double
|
||||
|
||||
align();
|
||||
L(const_FloatMaxS32);
|
||||
dq(0x41dfffffffc00000u); // 2147483647 as a double
|
||||
|
||||
align();
|
||||
L(const_FloatPositiveZero32);
|
||||
L(const_FloatPositiveZero64);
|
||||
L(const_FloatMinU32);
|
||||
dq(0x0000000000000000u); // 0 as a double
|
||||
|
||||
align();
|
||||
L(const_FloatMaxU32);
|
||||
dq(0x41efffffffe00000u); // 4294967295 as a double
|
||||
|
||||
align();
|
||||
}
|
||||
|
||||
void BlockOfCode::GenRunCode() {
|
||||
run_code = reinterpret_cast<RunCodeFuncType>(const_cast<u8*>(GetCodePtr()));
|
||||
align();
|
||||
run_code = getCurr<RunCodeFuncType>();
|
||||
|
||||
// This serves two purposes:
|
||||
// 1. It saves all the registers we as a callee need to save.
|
||||
// 2. It aligns the stack so that the code the JIT emits can assume
|
||||
// that the stack is appropriately aligned for CALLs.
|
||||
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
|
||||
ABI_PushCalleeSaveRegistersAndAdjustStack(this);
|
||||
|
||||
MOV(64, R(R15), R(ABI_PARAM1));
|
||||
mov(r15, ABI_PARAM1);
|
||||
SwitchMxcsrOnEntry();
|
||||
JMPptr(R(ABI_PARAM2));
|
||||
jmp(ABI_PARAM2);
|
||||
}
|
||||
|
||||
void BlockOfCode::GenReturnFromRunCode() {
|
||||
return_from_run_code = GetCodePtr();
|
||||
return_from_run_code = getCurr<const void*>();
|
||||
|
||||
SwitchMxcsrOnExit();
|
||||
|
||||
return_from_run_code_without_mxcsr_switch = GetCodePtr();
|
||||
return_from_run_code_without_mxcsr_switch = getCurr<const void*>();
|
||||
|
||||
ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
|
||||
RET();
|
||||
ABI_PopCalleeSaveRegistersAndAdjustStack(this);
|
||||
ret();
|
||||
}
|
||||
|
||||
void BlockOfCode::SwitchMxcsrOnEntry() {
|
||||
STMXCSR(MDisp(R15, offsetof(JitState, save_host_MXCSR)));
|
||||
LDMXCSR(MDisp(R15, offsetof(JitState, guest_MXCSR)));
|
||||
stmxcsr(dword[r15 + offsetof(JitState, save_host_MXCSR)]);
|
||||
ldmxcsr(dword[r15 + offsetof(JitState, guest_MXCSR)]);
|
||||
}
|
||||
|
||||
void BlockOfCode::SwitchMxcsrOnExit() {
|
||||
STMXCSR(MDisp(R15, offsetof(JitState, guest_MXCSR)));
|
||||
LDMXCSR(MDisp(R15, offsetof(JitState, save_host_MXCSR)));
|
||||
stmxcsr(dword[r15 + offsetof(JitState, guest_MXCSR)]);
|
||||
ldmxcsr(dword[r15 + offsetof(JitState, save_host_MXCSR)]);
|
||||
}
|
||||
|
||||
void BlockOfCode::CallFunction(const void* fn) {
|
||||
u64 distance = u64(fn) - (getCurr<u64>() + 5);
|
||||
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) {
|
||||
// Far call
|
||||
mov(rax, u64(fn));
|
||||
call(rax);
|
||||
} else {
|
||||
call(fn);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockOfCode::SetCodePtr(CodePtr ptr) {
|
||||
// The "size" defines where top_, the insertion point, is.
|
||||
size_t required_size = reinterpret_cast<const u8*>(ptr) - getCode();
|
||||
setSize(required_size);
|
||||
}
|
||||
|
||||
void BlockOfCode::EnsurePatchLocationSize(CodePtr begin, size_t size) {
|
||||
size_t current_size = getCurr<const u8*>() - reinterpret_cast<const u8*>(begin);
|
||||
ASSERT(current_size <= size);
|
||||
nop(size - current_size);
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
|
||||
@@ -7,15 +7,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/x64/emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
class BlockOfCode final : public Gen::XCodeBlock {
|
||||
class BlockOfCode final : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
BlockOfCode();
|
||||
|
||||
@@ -30,73 +32,99 @@ public:
|
||||
void SwitchMxcsrOnEntry();
|
||||
/// Code emitter: Makes saved host MXCSR the current MXCSR
|
||||
void SwitchMxcsrOnExit();
|
||||
/// Code emitter: Calls the function
|
||||
void CallFunction(const void* fn);
|
||||
|
||||
Gen::OpArg MFloatPositiveZero32() const {
|
||||
return Gen::M(const_FloatPositiveZero32);
|
||||
Xbyak::Address MFloatPositiveZero32() {
|
||||
return xword[rip + const_FloatPositiveZero32];
|
||||
}
|
||||
Gen::OpArg MFloatNegativeZero32() const {
|
||||
return Gen::M(const_FloatNegativeZero32);
|
||||
Xbyak::Address MFloatNegativeZero32() {
|
||||
return xword[rip + const_FloatNegativeZero32];
|
||||
}
|
||||
Gen::OpArg MFloatNaN32() const {
|
||||
return Gen::M(const_FloatNaN32);
|
||||
Xbyak::Address MFloatNaN32() {
|
||||
return xword[rip + const_FloatNaN32];
|
||||
}
|
||||
Gen::OpArg MFloatNonSignMask32() const {
|
||||
return Gen::M(const_FloatNonSignMask32);
|
||||
Xbyak::Address MFloatNonSignMask32() {
|
||||
return xword[rip + const_FloatNonSignMask32];
|
||||
}
|
||||
Gen::OpArg MFloatPositiveZero64() const {
|
||||
return Gen::M(const_FloatPositiveZero64);
|
||||
Xbyak::Address MFloatPositiveZero64() {
|
||||
return xword[rip + const_FloatPositiveZero64];
|
||||
}
|
||||
Gen::OpArg MFloatNegativeZero64() const {
|
||||
return Gen::M(const_FloatNegativeZero64);
|
||||
Xbyak::Address MFloatNegativeZero64() {
|
||||
return xword[rip + const_FloatNegativeZero64];
|
||||
}
|
||||
Gen::OpArg MFloatNaN64() const {
|
||||
return Gen::M(const_FloatNaN64);
|
||||
Xbyak::Address MFloatNaN64() {
|
||||
return xword[rip + const_FloatNaN64];
|
||||
}
|
||||
Gen::OpArg MFloatNonSignMask64() const {
|
||||
return Gen::M(const_FloatNonSignMask64);
|
||||
Xbyak::Address MFloatNonSignMask64() {
|
||||
return xword[rip + const_FloatNonSignMask64];
|
||||
}
|
||||
Gen::OpArg MFloatPenultimatePositiveDenormal64() const {
|
||||
return Gen::M(const_FloatPenultimatePositiveDenormal64);
|
||||
Xbyak::Address MFloatPenultimatePositiveDenormal64() {
|
||||
return xword[rip + const_FloatPenultimatePositiveDenormal64];
|
||||
}
|
||||
Gen::OpArg MFloatMinS32() const {
|
||||
return Gen::M(const_FloatMinS32);
|
||||
Xbyak::Address MFloatMinS32() {
|
||||
return xword[rip + const_FloatMinS32];
|
||||
}
|
||||
Gen::OpArg MFloatMaxS32() const {
|
||||
return Gen::M(const_FloatMaxS32);
|
||||
Xbyak::Address MFloatMaxS32() {
|
||||
return xword[rip + const_FloatMaxS32];
|
||||
}
|
||||
Gen::OpArg MFloatMinU32() const {
|
||||
return Gen::M(const_FloatMinU32);
|
||||
Xbyak::Address MFloatMinU32() {
|
||||
return xword[rip + const_FloatMinU32];
|
||||
}
|
||||
Gen::OpArg MFloatMaxU32() const {
|
||||
return Gen::M(const_FloatMaxU32);
|
||||
Xbyak::Address MFloatMaxU32() {
|
||||
return xword[rip + const_FloatMaxU32];
|
||||
}
|
||||
|
||||
CodePtr GetReturnFromRunCodeAddress() const {
|
||||
const void* GetReturnFromRunCodeAddress() const {
|
||||
return return_from_run_code;
|
||||
}
|
||||
|
||||
void int3() { db(0xCC); }
|
||||
void nop(size_t size = 0) {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
db(0x90);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCodePtr(CodePtr ptr);
|
||||
void EnsurePatchLocationSize(CodePtr begin, size_t size);
|
||||
|
||||
#ifdef _WIN32
|
||||
Xbyak::Reg64 ABI_RETURN = rax;
|
||||
Xbyak::Reg64 ABI_PARAM1 = rcx;
|
||||
Xbyak::Reg64 ABI_PARAM2 = rdx;
|
||||
Xbyak::Reg64 ABI_PARAM3 = r8;
|
||||
Xbyak::Reg64 ABI_PARAM4 = r9;
|
||||
#else
|
||||
Xbyak::Reg64 ABI_RETURN = rax;
|
||||
Xbyak::Reg64 ABI_PARAM1 = rdi;
|
||||
Xbyak::Reg64 ABI_PARAM2 = rsi;
|
||||
Xbyak::Reg64 ABI_PARAM3 = rdx;
|
||||
Xbyak::Reg64 ABI_PARAM4 = rcx;
|
||||
#endif
|
||||
|
||||
private:
|
||||
const u8* const_FloatPositiveZero32 = nullptr;
|
||||
const u8* const_FloatNegativeZero32 = nullptr;
|
||||
const u8* const_FloatNaN32 = nullptr;
|
||||
const u8* const_FloatNonSignMask32 = nullptr;
|
||||
const u8* const_FloatPositiveZero64 = nullptr;
|
||||
const u8* const_FloatNegativeZero64 = nullptr;
|
||||
const u8* const_FloatNaN64 = nullptr;
|
||||
const u8* const_FloatNonSignMask64 = nullptr;
|
||||
const u8* const_FloatPenultimatePositiveDenormal64 = nullptr;
|
||||
const u8* const_FloatMinS32 = nullptr;
|
||||
const u8* const_FloatMaxS32 = nullptr;
|
||||
const u8* const_FloatMinU32 = nullptr;
|
||||
const u8* const_FloatMaxU32 = nullptr;
|
||||
Xbyak::Label const_FloatPositiveZero32;
|
||||
Xbyak::Label const_FloatNegativeZero32;
|
||||
Xbyak::Label const_FloatNaN32;
|
||||
Xbyak::Label const_FloatNonSignMask32;
|
||||
Xbyak::Label const_FloatPositiveZero64;
|
||||
Xbyak::Label const_FloatNegativeZero64;
|
||||
Xbyak::Label const_FloatNaN64;
|
||||
Xbyak::Label const_FloatNonSignMask64;
|
||||
Xbyak::Label const_FloatPenultimatePositiveDenormal64;
|
||||
Xbyak::Label const_FloatMinS32;
|
||||
Xbyak::Label const_FloatMaxS32;
|
||||
Xbyak::Label const_FloatMinU32;
|
||||
Xbyak::Label const_FloatMaxU32;
|
||||
void GenConstants();
|
||||
|
||||
using RunCodeFuncType = void(*)(JitState*, CodePtr);
|
||||
RunCodeFuncType run_code = nullptr;
|
||||
void GenRunCode();
|
||||
|
||||
CodePtr return_from_run_code = nullptr;
|
||||
CodePtr return_from_run_code_without_mxcsr_switch = nullptr;
|
||||
const void* return_from_run_code = nullptr;
|
||||
const void* return_from_run_code_without_mxcsr_switch = nullptr;
|
||||
void GenReturnFromRunCode();
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/block_of_code.h"
|
||||
#include "backend_x64/reg_alloc.h"
|
||||
#include "common/x64/emitter.h"
|
||||
#include "frontend/arm_types.h"
|
||||
#include "frontend/ir/basic_block.h"
|
||||
#include "frontend/ir/microinstruction.h"
|
||||
|
||||
35
src/backend_x64/hostloc.cpp
Normal file
35
src/backend_x64/hostloc.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include "backend_x64/hostloc.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
Xbyak::Reg64 HostLocToReg64(HostLoc loc) {
|
||||
DEBUG_ASSERT(HostLocIsGPR(loc));
|
||||
DEBUG_ASSERT(loc != HostLoc::RSP);
|
||||
DEBUG_ASSERT(loc != HostLoc::R15);
|
||||
return Xbyak::Reg64(static_cast<int>(loc));
|
||||
}
|
||||
|
||||
Xbyak::Xmm HostLocToXmm(HostLoc loc) {
|
||||
DEBUG_ASSERT(HostLocIsXMM(loc));
|
||||
return Xbyak::Xmm(static_cast<int>(loc) - static_cast<int>(HostLoc::XMM0));
|
||||
}
|
||||
|
||||
Xbyak::Address SpillToOpArg(HostLoc loc) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
static_assert(std::is_same<decltype(JitState{nullptr}.Spill[0]), u64&>::value, "Spill must be u64");
|
||||
DEBUG_ASSERT(HostLocIsSpill(loc));
|
||||
|
||||
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
|
||||
return qword[r15 + offsetof(JitState, Spill) + i * sizeof(u64)];
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
||||
98
src/backend_x64/hostloc.h
Normal file
98
src/backend_x64/hostloc.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2016 MerryMage
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
enum class HostLoc {
|
||||
// Ordering of the registers is intentional. See also: HostLocToX64.
|
||||
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15,
|
||||
XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
|
||||
XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
|
||||
CF, PF, AF, ZF, SF, OF,
|
||||
FirstSpill,
|
||||
};
|
||||
|
||||
constexpr size_t HostLocCount = static_cast<size_t>(HostLoc::FirstSpill) + SpillCount;
|
||||
|
||||
inline bool HostLocIsGPR(HostLoc reg) {
|
||||
return reg >= HostLoc::RAX && reg <= HostLoc::R14;
|
||||
}
|
||||
|
||||
inline bool HostLocIsXMM(HostLoc reg) {
|
||||
return reg >= HostLoc::XMM0 && reg <= HostLoc::XMM15;
|
||||
}
|
||||
|
||||
inline bool HostLocIsRegister(HostLoc reg) {
|
||||
return HostLocIsGPR(reg) || HostLocIsXMM(reg);
|
||||
}
|
||||
|
||||
inline bool HostLocIsFlag(HostLoc reg) {
|
||||
return reg >= HostLoc::CF && reg <= HostLoc::OF;
|
||||
}
|
||||
|
||||
inline HostLoc HostLocSpill(size_t i) {
|
||||
ASSERT_MSG(i < SpillCount, "Invalid spill");
|
||||
return static_cast<HostLoc>(static_cast<int>(HostLoc::FirstSpill) + i);
|
||||
}
|
||||
|
||||
inline bool HostLocIsSpill(HostLoc reg) {
|
||||
return reg >= HostLoc::FirstSpill && reg <= HostLocSpill(SpillCount - 1);
|
||||
}
|
||||
|
||||
using HostLocList = std::initializer_list<HostLoc>;
|
||||
|
||||
// RSP is preserved for function calls
|
||||
// R15 contains the JitState pointer
|
||||
const HostLocList any_gpr = {
|
||||
HostLoc::RAX,
|
||||
HostLoc::RBX,
|
||||
HostLoc::RCX,
|
||||
HostLoc::RDX,
|
||||
HostLoc::RSI,
|
||||
HostLoc::RDI,
|
||||
HostLoc::RBP,
|
||||
HostLoc::R8,
|
||||
HostLoc::R9,
|
||||
HostLoc::R10,
|
||||
HostLoc::R11,
|
||||
HostLoc::R12,
|
||||
HostLoc::R13,
|
||||
HostLoc::R14,
|
||||
};
|
||||
|
||||
const HostLocList any_xmm = {
|
||||
HostLoc::XMM0,
|
||||
HostLoc::XMM1,
|
||||
HostLoc::XMM2,
|
||||
HostLoc::XMM3,
|
||||
HostLoc::XMM4,
|
||||
HostLoc::XMM5,
|
||||
HostLoc::XMM6,
|
||||
HostLoc::XMM7,
|
||||
HostLoc::XMM8,
|
||||
HostLoc::XMM9,
|
||||
HostLoc::XMM10,
|
||||
HostLoc::XMM11,
|
||||
HostLoc::XMM12,
|
||||
HostLoc::XMM13,
|
||||
HostLoc::XMM14,
|
||||
HostLoc::XMM15,
|
||||
};
|
||||
|
||||
Xbyak::Reg64 HostLocToReg64(HostLoc loc);
|
||||
Xbyak::Xmm HostLocToXmm(HostLoc loc);
|
||||
Xbyak::Address SpillToOpArg(HostLoc loc);
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
||||
@@ -15,7 +15,7 @@ namespace BackendX64 {
|
||||
|
||||
class BlockOfCode;
|
||||
|
||||
constexpr size_t SpillCount = 32;
|
||||
constexpr size_t SpillCount = 64;
|
||||
|
||||
struct JitState {
|
||||
explicit JitState(const BlockOfCode* code) { ResetRSB(code); }
|
||||
@@ -54,7 +54,7 @@ struct JitState {
|
||||
void SetFpscr(u32 FPSCR);
|
||||
};
|
||||
|
||||
using CodePtr = const u8*;
|
||||
using CodePtr = const void*;
|
||||
|
||||
} // namespace BackendX64
|
||||
} // namespace Dynarmic
|
||||
|
||||
@@ -7,52 +7,42 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "backend_x64/reg_alloc.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/x64/emitter.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
static Gen::OpArg ImmediateToOpArg(const IR::Value& imm) {
|
||||
static u32 ImmediateToU32(const IR::Value& imm) {
|
||||
switch (imm.GetType()) {
|
||||
case IR::Type::U1:
|
||||
return Gen::Imm32(imm.GetU1());
|
||||
return u32(imm.GetU1());
|
||||
break;
|
||||
case IR::Type::U8:
|
||||
return Gen::Imm32(imm.GetU8());
|
||||
return u32(imm.GetU8());
|
||||
break;
|
||||
case IR::Type::U32:
|
||||
return Gen::Imm32(imm.GetU32());
|
||||
return u32(imm.GetU32());
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "This should never happen.");
|
||||
}
|
||||
}
|
||||
|
||||
static Gen::X64Reg HostLocToX64(HostLoc loc) {
|
||||
DEBUG_ASSERT(HostLocIsRegister(loc));
|
||||
DEBUG_ASSERT(loc != HostLoc::RSP);
|
||||
// HostLoc is ordered such that the numbers line up.
|
||||
if (HostLocIsGPR(loc)) {
|
||||
return static_cast<Gen::X64Reg>(loc);
|
||||
static Xbyak::Reg HostLocToX64(HostLoc hostloc) {
|
||||
if (HostLocIsGPR(hostloc)) {
|
||||
return HostLocToReg64(hostloc);
|
||||
}
|
||||
if (HostLocIsXMM(loc)) {
|
||||
return static_cast<Gen::X64Reg>(size_t(loc) - size_t(HostLoc::XMM0));
|
||||
if (HostLocIsXMM(hostloc)) {
|
||||
return HostLocToXmm(hostloc);
|
||||
}
|
||||
ASSERT_MSG(false, "This should never happen.");
|
||||
return Gen::INVALID_REG;
|
||||
}
|
||||
|
||||
static Gen::OpArg SpillToOpArg(HostLoc loc) {
|
||||
static_assert(std::is_same<decltype(JitState{nullptr}.Spill[0]), u64&>::value, "Spill must be u64");
|
||||
DEBUG_ASSERT(HostLocIsSpill(loc));
|
||||
|
||||
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
|
||||
return Gen::MDisp(Gen::R15, static_cast<int>(offsetof(JitState, Spill) + i * sizeof(u64)));
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::DefRegister(IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::DefHostLocReg(IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
DEBUG_ASSERT_MSG(!ValueLocation(def_inst), "def_inst has already been defined");
|
||||
|
||||
@@ -66,14 +56,14 @@ Gen::X64Reg RegAlloc::DefRegister(IR::Inst* def_inst, HostLocList desired_locati
|
||||
LocInfo(location).def = def_inst;
|
||||
|
||||
DEBUG_ASSERT(LocInfo(location).IsDef());
|
||||
return HostLocToX64(location);
|
||||
return location;
|
||||
}
|
||||
|
||||
void RegAlloc::RegisterAddDef(IR::Inst* def_inst, const IR::Value& use_inst) {
|
||||
DEBUG_ASSERT_MSG(!ValueLocation(def_inst), "def_inst has already been defined");
|
||||
|
||||
if (use_inst.IsImmediate()) {
|
||||
LoadImmediateIntoRegister(use_inst, DefRegister(def_inst, any_gpr));
|
||||
LoadImmediateIntoHostLocReg(use_inst, DefHostLocReg(def_inst, any_gpr));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,15 +74,15 @@ void RegAlloc::RegisterAddDef(IR::Inst* def_inst, const IR::Value& use_inst) {
|
||||
DEBUG_ASSERT(LocInfo(location).IsIdle());
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseDefRegister(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseDefHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
if (!use_value.IsImmediate()) {
|
||||
return UseDefRegister(use_value.GetInst(), def_inst, desired_locations);
|
||||
return UseDefHostLocReg(use_value.GetInst(), def_inst, desired_locations);
|
||||
}
|
||||
|
||||
return LoadImmediateIntoRegister(use_value, DefRegister(def_inst, desired_locations));
|
||||
return LoadImmediateIntoHostLocReg(use_value, DefHostLocReg(def_inst, desired_locations));
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseDefHostLocReg(IR::Inst* use_inst, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
DEBUG_ASSERT_MSG(!ValueLocation(def_inst), "def_inst has already been defined");
|
||||
DEBUG_ASSERT_MSG(ValueLocation(use_inst), "use_inst has not been defined");
|
||||
@@ -112,9 +102,9 @@ Gen::X64Reg RegAlloc::UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, Hos
|
||||
EmitMove(new_location, current_location);
|
||||
LocInfo(new_location) = LocInfo(current_location);
|
||||
LocInfo(current_location) = {};
|
||||
return HostLocToX64(new_location);
|
||||
return new_location;
|
||||
} else {
|
||||
return HostLocToX64(current_location);
|
||||
return current_location;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,17 +112,17 @@ Gen::X64Reg RegAlloc::UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, Hos
|
||||
bool is_floating_point = HostLocIsXMM(*desired_locations.begin());
|
||||
if (is_floating_point)
|
||||
DEBUG_ASSERT(use_inst->GetType() == IR::Type::F32 || use_inst->GetType() == IR::Type::F64);
|
||||
Gen::X64Reg use_reg = UseRegister(use_inst, is_floating_point ? any_xmm : any_gpr);
|
||||
Gen::X64Reg def_reg = DefRegister(def_inst, desired_locations);
|
||||
HostLoc use_reg = UseHostLocReg(use_inst, is_floating_point ? any_xmm : any_gpr);
|
||||
HostLoc def_reg = DefHostLocReg(def_inst, desired_locations);
|
||||
if (is_floating_point) {
|
||||
code->MOVAPD(def_reg, Gen::R(use_reg));
|
||||
code->movapd(HostLocToXmm(def_reg), HostLocToXmm(use_reg));
|
||||
} else {
|
||||
code->MOV(64, Gen::R(def_reg), Gen::R(use_reg));
|
||||
code->mov(HostLocToReg64(def_reg), HostLocToReg64(use_reg));
|
||||
}
|
||||
return def_reg;
|
||||
}
|
||||
|
||||
std::tuple<Gen::OpArg, Gen::X64Reg> RegAlloc::UseDefOpArg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
std::tuple<OpArg, HostLoc> RegAlloc::UseDefOpArgHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations) {
|
||||
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
DEBUG_ASSERT_MSG(!ValueLocation(def_inst), "def_inst has already been defined");
|
||||
DEBUG_ASSERT_MSG(use_value.IsImmediate() || ValueLocation(use_value.GetInst()), "use_inst has not been defined");
|
||||
@@ -147,37 +137,37 @@ std::tuple<Gen::OpArg, Gen::X64Reg> RegAlloc::UseDefOpArg(IR::Value use_value, I
|
||||
if (HostLocIsSpill(current_location)) {
|
||||
loc_info.is_being_used = true;
|
||||
DEBUG_ASSERT(loc_info.IsUse());
|
||||
return std::make_tuple(SpillToOpArg(current_location), DefRegister(def_inst, desired_locations));
|
||||
return std::make_tuple(SpillToOpArg(current_location), DefHostLocReg(def_inst, desired_locations));
|
||||
} else {
|
||||
loc_info.is_being_used = true;
|
||||
loc_info.def = def_inst;
|
||||
DEBUG_ASSERT(loc_info.IsUseDef());
|
||||
return std::make_tuple(Gen::R(HostLocToX64(current_location)), HostLocToX64(current_location));
|
||||
return std::make_tuple(HostLocToX64(current_location), current_location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gen::OpArg use_oparg = UseOpArg(use_value, any_gpr);
|
||||
Gen::X64Reg def_reg = DefRegister(def_inst, desired_locations);
|
||||
OpArg use_oparg = UseOpArg(use_value, any_gpr);
|
||||
HostLoc def_reg = DefHostLocReg(def_inst, desired_locations);
|
||||
return std::make_tuple(use_oparg, def_reg);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseRegister(IR::Value use_value, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseHostLocReg(IR::Value use_value, HostLocList desired_locations) {
|
||||
if (!use_value.IsImmediate()) {
|
||||
return UseRegister(use_value.GetInst(), desired_locations);
|
||||
return UseHostLocReg(use_value.GetInst(), desired_locations);
|
||||
}
|
||||
|
||||
return LoadImmediateIntoRegister(use_value, ScratchRegister(desired_locations));
|
||||
return LoadImmediateIntoHostLocReg(use_value, ScratchHostLocReg(desired_locations));
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseRegister(IR::Inst* use_inst, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseHostLocReg(IR::Inst* use_inst, HostLocList desired_locations) {
|
||||
HostLoc current_location;
|
||||
bool was_being_used;
|
||||
std::tie(current_location, was_being_used) = UseHostLoc(use_inst, desired_locations);
|
||||
|
||||
if (HostLocIsRegister(current_location)) {
|
||||
return HostLocToX64(current_location);
|
||||
return current_location;
|
||||
} else if (HostLocIsSpill(current_location)) {
|
||||
HostLoc new_location = SelectARegister(desired_locations);
|
||||
if (IsRegisterOccupied(new_location)) {
|
||||
@@ -192,16 +182,15 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Inst* use_inst, HostLocList desired_locati
|
||||
LocInfo(new_location).is_being_used = true;
|
||||
DEBUG_ASSERT(LocInfo(new_location).IsScratch());
|
||||
}
|
||||
return HostLocToX64(new_location);
|
||||
return new_location;
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "Unknown current_location type");
|
||||
return Gen::INVALID_REG;
|
||||
}
|
||||
|
||||
Gen::OpArg RegAlloc::UseOpArg(IR::Value use_value, HostLocList desired_locations) {
|
||||
OpArg RegAlloc::UseOpArg(IR::Value use_value, HostLocList desired_locations) {
|
||||
if (use_value.IsImmediate()) {
|
||||
return ImmediateToOpArg(use_value);
|
||||
return Xbyak::Operand(); // return a None
|
||||
}
|
||||
|
||||
IR::Inst* use_inst = use_value.GetInst();
|
||||
@@ -211,24 +200,23 @@ Gen::OpArg RegAlloc::UseOpArg(IR::Value use_value, HostLocList desired_locations
|
||||
std::tie(current_location, was_being_used) = UseHostLoc(use_inst, desired_locations);
|
||||
|
||||
if (HostLocIsRegister(current_location)) {
|
||||
return Gen::R(HostLocToX64(current_location));
|
||||
return HostLocToX64(current_location);
|
||||
} else if (HostLocIsSpill(current_location)) {
|
||||
return SpillToOpArg(current_location);
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "Unknown current_location type");
|
||||
return Gen::R(Gen::INVALID_REG);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value use_value, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseScratchHostLocReg(IR::Value use_value, HostLocList desired_locations) {
|
||||
if (!use_value.IsImmediate()) {
|
||||
return UseScratchRegister(use_value.GetInst(), desired_locations);
|
||||
return UseScratchHostLocReg(use_value.GetInst(), desired_locations);
|
||||
}
|
||||
|
||||
return LoadImmediateIntoRegister(use_value, ScratchRegister(desired_locations));
|
||||
return LoadImmediateIntoHostLocReg(use_value, ScratchHostLocReg(desired_locations));
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Inst* use_inst, HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::UseScratchHostLocReg(IR::Inst* use_inst, HostLocList desired_locations) {
|
||||
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
DEBUG_ASSERT_MSG(ValueLocation(use_inst), "use_inst has not been defined");
|
||||
ASSERT_MSG(use_inst->HasUses(), "use_inst ran out of uses. (Use-d an IR::Inst* too many times)");
|
||||
@@ -244,7 +232,7 @@ Gen::X64Reg RegAlloc::UseScratchRegister(IR::Inst* use_inst, HostLocList desired
|
||||
LocInfo(new_location).is_being_used = true;
|
||||
DecrementRemainingUses(use_inst);
|
||||
DEBUG_ASSERT(LocInfo(new_location).IsScratch());
|
||||
return HostLocToX64(new_location);
|
||||
return new_location;
|
||||
} else if (HostLocIsRegister(current_location)) {
|
||||
ASSERT(LocInfo(current_location).IsIdle()
|
||||
|| LocInfo(current_location).IsUse()
|
||||
@@ -260,14 +248,13 @@ Gen::X64Reg RegAlloc::UseScratchRegister(IR::Inst* use_inst, HostLocList desired
|
||||
LocInfo(new_location).values.clear();
|
||||
DecrementRemainingUses(use_inst);
|
||||
DEBUG_ASSERT(LocInfo(new_location).IsScratch());
|
||||
return HostLocToX64(new_location);
|
||||
return new_location;
|
||||
}
|
||||
|
||||
ASSERT_MSG(0, "Invalid current_location");
|
||||
return Gen::INVALID_REG;
|
||||
ASSERT_MSG(false, "Invalid current_location");
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::ScratchRegister(HostLocList desired_locations) {
|
||||
HostLoc RegAlloc::ScratchHostLocReg(HostLocList desired_locations) {
|
||||
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||
|
||||
HostLoc location = SelectARegister(desired_locations);
|
||||
@@ -280,7 +267,7 @@ Gen::X64Reg RegAlloc::ScratchRegister(HostLocList desired_locations) {
|
||||
LocInfo(location).is_being_used = true;
|
||||
|
||||
DEBUG_ASSERT(LocInfo(location).IsScratch());
|
||||
return HostLocToX64(location);
|
||||
return location;
|
||||
}
|
||||
|
||||
void RegAlloc::HostCall(IR::Inst* result_def, IR::Value arg0_use, IR::Value arg1_use, IR::Value arg2_use, IR::Value arg3_use) {
|
||||
@@ -300,26 +287,26 @@ void RegAlloc::HostCall(IR::Inst* result_def, IR::Value arg0_use, IR::Value arg1
|
||||
// TODO: This works but almost certainly leads to suboptimal generated code.
|
||||
|
||||
for (HostLoc caller_save : OtherCallerSave) {
|
||||
ScratchRegister({caller_save});
|
||||
ScratchHostLocReg({caller_save});
|
||||
}
|
||||
|
||||
if (result_def) {
|
||||
DefRegister(result_def, {AbiReturn});
|
||||
DefHostLocReg(result_def, {AbiReturn});
|
||||
} else {
|
||||
ScratchRegister({AbiReturn});
|
||||
ScratchHostLocReg({AbiReturn});
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
||||
if (!args[i]->IsEmpty()) {
|
||||
UseScratchRegister(*args[i], {AbiArgs[i]});
|
||||
UseScratchHostLocReg(*args[i], {AbiArgs[i]});
|
||||
} else {
|
||||
ScratchRegister({AbiArgs[i]});
|
||||
ScratchHostLocReg({AbiArgs[i]});
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all xmm registers
|
||||
for (auto xmm : any_xmm) {
|
||||
ScratchRegister({xmm});
|
||||
ScratchHostLocReg({xmm});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,17 +407,17 @@ void RegAlloc::Reset() {
|
||||
|
||||
void RegAlloc::EmitMove(HostLoc to, HostLoc from) {
|
||||
if (HostLocIsXMM(to) && HostLocIsSpill(from)) {
|
||||
code->MOVSD(HostLocToX64(to), SpillToOpArg(from));
|
||||
code->movsd(HostLocToXmm(to), SpillToOpArg(from));
|
||||
} else if (HostLocIsSpill(to) && HostLocIsXMM(from)) {
|
||||
code->MOVSD(SpillToOpArg(to), HostLocToX64(from));
|
||||
code->movsd(SpillToOpArg(to), HostLocToXmm(from));
|
||||
} else if (HostLocIsXMM(to) && HostLocIsXMM(from)) {
|
||||
code->MOVAPS(HostLocToX64(to), Gen::R(HostLocToX64(from)));
|
||||
code->movaps(HostLocToXmm(to), HostLocToXmm(from));
|
||||
} else if (HostLocIsGPR(to) && HostLocIsSpill(from)) {
|
||||
code->MOV(64, Gen::R(HostLocToX64(to)), SpillToOpArg(from));
|
||||
code->mov(HostLocToReg64(to), SpillToOpArg(from));
|
||||
} else if (HostLocIsSpill(to) && HostLocIsGPR(from)) {
|
||||
code->MOV(64, SpillToOpArg(to), Gen::R(HostLocToX64(from)));
|
||||
code->mov(SpillToOpArg(to), HostLocToReg64(from));
|
||||
} else if (HostLocIsGPR(to) && HostLocIsGPR(from)){
|
||||
code->MOV(64, Gen::R(HostLocToX64(to)), Gen::R(HostLocToX64(from)));
|
||||
code->mov(HostLocToReg64(to), HostLocToReg64(from));
|
||||
} else {
|
||||
ASSERT_MSG(false, "Invalid RegAlloc::EmitMove");
|
||||
}
|
||||
@@ -438,7 +425,7 @@ void RegAlloc::EmitMove(HostLoc to, HostLoc from) {
|
||||
|
||||
void RegAlloc::EmitExchange(HostLoc a, HostLoc b) {
|
||||
if (HostLocIsGPR(a) && HostLocIsGPR(b)) {
|
||||
code->XCHG(64, Gen::R(HostLocToX64(a)), Gen::R(HostLocToX64(b)));
|
||||
code->xchg(HostLocToReg64(a), HostLocToReg64(b));
|
||||
} else if (HostLocIsXMM(a) && HostLocIsXMM(b)) {
|
||||
ASSERT_MSG(false, "Exchange is unnecessary for XMM registers");
|
||||
} else {
|
||||
@@ -495,14 +482,17 @@ std::tuple<HostLoc, bool> RegAlloc::UseHostLoc(IR::Inst* use_inst, HostLocList d
|
||||
return std::make_tuple(static_cast<HostLoc>(-1), false);
|
||||
}
|
||||
|
||||
Gen::X64Reg RegAlloc::LoadImmediateIntoRegister(IR::Value imm, Gen::X64Reg reg) {
|
||||
HostLoc RegAlloc::LoadImmediateIntoHostLocReg(IR::Value imm, HostLoc host_loc) {
|
||||
ASSERT_MSG(imm.IsImmediate(), "imm is not an immediate");
|
||||
Gen::OpArg op_arg = ImmediateToOpArg(imm);
|
||||
if (op_arg.GetImmValue() == 0)
|
||||
code->XOR(32, Gen::R(reg), Gen::R(reg));
|
||||
|
||||
Xbyak::Reg64 reg = HostLocToReg64(host_loc);
|
||||
|
||||
u32 imm_value = ImmediateToU32(imm);
|
||||
if (imm_value == 0)
|
||||
code->xor_(reg, reg);
|
||||
else
|
||||
code->MOV(32, Gen::R(reg), op_arg);
|
||||
return reg;
|
||||
code->mov(reg.cvt32(), imm_value);
|
||||
return host_loc;
|
||||
}
|
||||
|
||||
} // namespace BackendX64
|
||||
|
||||
@@ -11,93 +11,55 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <xbyak.h>
|
||||
|
||||
#include "backend_x64/block_of_code.h"
|
||||
#include "backend_x64/hostloc.h"
|
||||
#include "backend_x64/jitstate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/x64/emitter.h"
|
||||
#include "frontend/ir/microinstruction.h"
|
||||
#include "frontend/ir/value.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace BackendX64 {
|
||||
|
||||
enum class HostLoc {
|
||||
// Ordering of the registers is intentional. See also: HostLocToX64.
|
||||
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14,
|
||||
XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
|
||||
XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
|
||||
CF, PF, AF, ZF, SF, OF,
|
||||
FirstSpill,
|
||||
};
|
||||
struct OpArg {
|
||||
OpArg() : type(OPERAND), inner_operand() {}
|
||||
OpArg(const Xbyak::Address& address) : type(ADDRESS), inner_address(address) {}
|
||||
OpArg(const Xbyak::Operand& operand) : type(OPERAND), inner_operand(operand) {}
|
||||
|
||||
constexpr size_t HostLocCount = static_cast<size_t>(HostLoc::FirstSpill) + SpillCount;
|
||||
Xbyak::Operand& operator*() {
|
||||
switch (type) {
|
||||
case ADDRESS:
|
||||
return inner_address;
|
||||
case OPERAND:
|
||||
return inner_operand;
|
||||
}
|
||||
ASSERT_MSG(false, "Unreachable");
|
||||
}
|
||||
|
||||
enum class HostLocState {
|
||||
Idle, Def, Use, Scratch
|
||||
};
|
||||
void setBit(int bits) {
|
||||
switch (type) {
|
||||
case ADDRESS:
|
||||
inner_address.setBit(bits);
|
||||
return;
|
||||
case OPERAND:
|
||||
inner_operand.setBit(bits);
|
||||
return;
|
||||
}
|
||||
ASSERT_MSG(false, "Unreachable");
|
||||
}
|
||||
|
||||
inline bool HostLocIsGPR(HostLoc reg) {
|
||||
return reg >= HostLoc::RAX && reg <= HostLoc::R14;
|
||||
}
|
||||
private:
|
||||
enum {
|
||||
OPERAND,
|
||||
ADDRESS,
|
||||
} type;
|
||||
|
||||
inline bool HostLocIsXMM(HostLoc reg) {
|
||||
return reg >= HostLoc::XMM0 && reg <= HostLoc::XMM15;
|
||||
}
|
||||
|
||||
inline bool HostLocIsRegister(HostLoc reg) {
|
||||
return HostLocIsGPR(reg) || HostLocIsXMM(reg);
|
||||
}
|
||||
|
||||
inline bool HostLocIsFlag(HostLoc reg) {
|
||||
return reg >= HostLoc::CF && reg <= HostLoc::OF;
|
||||
}
|
||||
|
||||
inline HostLoc HostLocSpill(size_t i) {
|
||||
ASSERT_MSG(i < SpillCount, "Invalid spill");
|
||||
return static_cast<HostLoc>(static_cast<int>(HostLoc::FirstSpill) + i);
|
||||
}
|
||||
|
||||
inline bool HostLocIsSpill(HostLoc reg) {
|
||||
return reg >= HostLoc::FirstSpill && reg <= HostLocSpill(SpillCount - 1);
|
||||
}
|
||||
|
||||
using HostLocList = std::initializer_list<HostLoc>;
|
||||
|
||||
const HostLocList any_gpr = {
|
||||
HostLoc::RAX,
|
||||
HostLoc::RBX,
|
||||
HostLoc::RCX,
|
||||
HostLoc::RDX,
|
||||
HostLoc::RSI,
|
||||
HostLoc::RDI,
|
||||
HostLoc::RBP,
|
||||
HostLoc::R8,
|
||||
HostLoc::R9,
|
||||
HostLoc::R10,
|
||||
HostLoc::R11,
|
||||
HostLoc::R12,
|
||||
HostLoc::R13,
|
||||
HostLoc::R14,
|
||||
};
|
||||
|
||||
const HostLocList any_xmm = {
|
||||
HostLoc::XMM0,
|
||||
HostLoc::XMM1,
|
||||
HostLoc::XMM2,
|
||||
HostLoc::XMM3,
|
||||
HostLoc::XMM4,
|
||||
HostLoc::XMM5,
|
||||
HostLoc::XMM6,
|
||||
HostLoc::XMM7,
|
||||
HostLoc::XMM8,
|
||||
HostLoc::XMM9,
|
||||
HostLoc::XMM10,
|
||||
HostLoc::XMM11,
|
||||
HostLoc::XMM12,
|
||||
HostLoc::XMM13,
|
||||
HostLoc::XMM14,
|
||||
HostLoc::XMM15,
|
||||
union {
|
||||
Xbyak::Operand inner_operand;
|
||||
Xbyak::Address inner_address;
|
||||
};
|
||||
};
|
||||
|
||||
class RegAlloc final {
|
||||
@@ -105,21 +67,54 @@ public:
|
||||
RegAlloc(BlockOfCode* code) : code(code) {}
|
||||
|
||||
/// Late-def
|
||||
Gen::X64Reg DefRegister(IR::Inst* def_inst, HostLocList desired_locations);
|
||||
Xbyak::Reg64 DefGpr(IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
||||
return HostLocToReg64(DefHostLocReg(def_inst, desired_locations));
|
||||
}
|
||||
Xbyak::Xmm DefXmm(IR::Inst* def_inst, HostLocList desired_locations = any_xmm) {
|
||||
return HostLocToXmm(DefHostLocReg(def_inst, desired_locations));
|
||||
}
|
||||
void RegisterAddDef(IR::Inst* def_inst, const IR::Value& use_inst);
|
||||
/// Early-use, Late-def
|
||||
Gen::X64Reg UseDefRegister(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
Gen::X64Reg UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
std::tuple<Gen::OpArg, Gen::X64Reg> UseDefOpArg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
Xbyak::Reg64 UseDefGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
||||
return HostLocToReg64(UseDefHostLocReg(use_value, def_inst, desired_locations));
|
||||
}
|
||||
Xbyak::Xmm UseDefXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_xmm) {
|
||||
return HostLocToXmm(UseDefHostLocReg(use_value, def_inst, desired_locations));
|
||||
}
|
||||
std::tuple<OpArg, Xbyak::Reg64> UseDefOpArgGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
||||
OpArg op;
|
||||
HostLoc host_loc;
|
||||
std::tie(op, host_loc) = UseDefOpArgHostLocReg(use_value, def_inst, desired_locations);
|
||||
return std::make_tuple(op, HostLocToReg64(host_loc));
|
||||
}
|
||||
std::tuple<OpArg, Xbyak::Xmm> UseDefOpArgXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
||||
OpArg op;
|
||||
HostLoc host_loc;
|
||||
std::tie(op, host_loc) = UseDefOpArgHostLocReg(use_value, def_inst, desired_locations);
|
||||
return std::make_tuple(op, HostLocToXmm(host_loc));
|
||||
}
|
||||
/// Early-use
|
||||
Gen::X64Reg UseRegister(IR::Value use_value, HostLocList desired_locations);
|
||||
Gen::X64Reg UseRegister(IR::Inst* use_inst, HostLocList desired_locations);
|
||||
Gen::OpArg UseOpArg(IR::Value use_value, HostLocList desired_locations);
|
||||
Xbyak::Reg64 UseGpr(IR::Value use_value, HostLocList desired_locations = any_gpr) {
|
||||
return HostLocToReg64(UseHostLocReg(use_value, desired_locations));
|
||||
}
|
||||
Xbyak::Xmm UseXmm(IR::Value use_value, HostLocList desired_locations = any_xmm) {
|
||||
return HostLocToXmm(UseHostLocReg(use_value, desired_locations));
|
||||
}
|
||||
OpArg UseOpArg(IR::Value use_value, HostLocList desired_locations);
|
||||
/// Early-use, Destroyed
|
||||
Gen::X64Reg UseScratchRegister(IR::Value use_value, HostLocList desired_locations);
|
||||
Gen::X64Reg UseScratchRegister(IR::Inst* use_inst, HostLocList desired_locations);
|
||||
Xbyak::Reg64 UseScratchGpr(IR::Value use_value, HostLocList desired_locations = any_gpr) {
|
||||
return HostLocToReg64(UseScratchHostLocReg(use_value, desired_locations));
|
||||
}
|
||||
Xbyak::Xmm UseScratchXmm(IR::Value use_value, HostLocList desired_locations = any_xmm) {
|
||||
return HostLocToXmm(UseScratchHostLocReg(use_value, desired_locations));
|
||||
}
|
||||
/// Early-def, Late-use, single-use
|
||||
Gen::X64Reg ScratchRegister(HostLocList desired_locations);
|
||||
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr) {
|
||||
return HostLocToReg64(ScratchHostLocReg(desired_locations));
|
||||
}
|
||||
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm) {
|
||||
return HostLocToXmm(ScratchHostLocReg(desired_locations));
|
||||
}
|
||||
|
||||
/// Late-def for result register, Early-use for all arguments, Each value is placed into registers according to host ABI.
|
||||
void HostCall(IR::Inst* result_def = nullptr, IR::Value arg0_use = {}, IR::Value arg1_use = {}, IR::Value arg2_use = {}, IR::Value arg3_use = {});
|
||||
@@ -141,11 +136,20 @@ private:
|
||||
bool IsRegisterAllocated(HostLoc loc) const;
|
||||
bool IsLastUse(IR::Inst* inst) const;
|
||||
|
||||
HostLoc DefHostLocReg(IR::Inst* def_inst, HostLocList desired_locations);
|
||||
HostLoc UseDefHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
HostLoc UseDefHostLocReg(IR::Inst* use_inst, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
std::tuple<OpArg, HostLoc> UseDefOpArgHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
||||
HostLoc UseHostLocReg(IR::Value use_value, HostLocList desired_locations);
|
||||
HostLoc UseHostLocReg(IR::Inst* use_inst, HostLocList desired_locations);
|
||||
std::tuple<HostLoc, bool> UseHostLoc(IR::Inst* use_inst, HostLocList desired_locations);
|
||||
HostLoc UseScratchHostLocReg(IR::Value use_value, HostLocList desired_locations);
|
||||
HostLoc UseScratchHostLocReg(IR::Inst* use_inst, HostLocList desired_locations);
|
||||
HostLoc ScratchHostLocReg(HostLocList desired_locations);
|
||||
|
||||
void EmitMove(HostLoc to, HostLoc from);
|
||||
void EmitExchange(HostLoc a, HostLoc b);
|
||||
Gen::X64Reg LoadImmediateIntoRegister(IR::Value imm, Gen::X64Reg reg);
|
||||
HostLoc LoadImmediateIntoHostLocReg(IR::Value imm, HostLoc reg);
|
||||
|
||||
void SpillRegister(HostLoc loc);
|
||||
HostLoc FindFreeSpill() const;
|
||||
|
||||
Reference in New Issue
Block a user