backend/rv64: Add minimal toy implementation enough to execute LSLS
This commit is contained in:
parent
62ff78d527
commit
f856ac9f33
7 changed files with 251 additions and 38 deletions
|
@ -405,6 +405,7 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
target_sources(dynarmic PRIVATE
|
target_sources(dynarmic PRIVATE
|
||||||
backend/riscv64/abi.h
|
backend/riscv64/abi.h
|
||||||
backend/riscv64/emit_context.h
|
backend/riscv64/emit_context.h
|
||||||
|
backend/riscv64/emit_riscv64_data_processing.cpp
|
||||||
backend/riscv64/emit_riscv64.cpp
|
backend/riscv64/emit_riscv64.cpp
|
||||||
backend/riscv64/emit_riscv64.h
|
backend/riscv64/emit_riscv64.h
|
||||||
backend/riscv64/reg_alloc.cpp
|
backend/riscv64/reg_alloc.cpp
|
||||||
|
@ -421,6 +422,7 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
backend/riscv64/a32_jitstate.cpp
|
backend/riscv64/a32_jitstate.cpp
|
||||||
backend/riscv64/a32_jitstate.h
|
backend/riscv64/a32_jitstate.h
|
||||||
backend/riscv64/code_block.h
|
backend/riscv64/code_block.h
|
||||||
|
backend/riscv64/emit_riscv64_a32.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ void A32AddressSpace::EmitPrelude() {
|
||||||
as.SD(GPR{i}, i * 8, sp);
|
as.SD(GPR{i}, i * 8, sp);
|
||||||
}
|
}
|
||||||
for (u32 i = 0; i < 32; i += 1) {
|
for (u32 i = 0; i < 32; i += 1) {
|
||||||
as.FSD(FPR{i}, 32 + i * 8, sp);
|
as.FSD(FPR{i}, (32 + i) * 8, sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
as.ADDI(Xstate, a1, 0);
|
as.ADDI(Xstate, a1, 0);
|
||||||
|
@ -92,7 +92,7 @@ void A32AddressSpace::EmitPrelude() {
|
||||||
as.LD(GPR{i}, i * 8, sp);
|
as.LD(GPR{i}, i * 8, sp);
|
||||||
}
|
}
|
||||||
for (u32 i = 0; i < 32; i += 1) {
|
for (u32 i = 0; i < 32; i += 1) {
|
||||||
as.FLD(FPR{i}, 32 + i * 8, sp);
|
as.FLD(FPR{i}, (32 + i) * 8, sp);
|
||||||
}
|
}
|
||||||
as.ADDI(sp, sp, 64 * 8);
|
as.ADDI(sp, sp, 64 * 8);
|
||||||
as.JALR(ra);
|
as.JALR(ra);
|
||||||
|
@ -128,7 +128,7 @@ void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case LinkTarget::ReturnFromRunCode: {
|
case LinkTarget::ReturnFromRunCode: {
|
||||||
std::ptrdiff_t off = prelude_info.return_from_run_code - GetCursorPtr<CodePtr>();
|
std::ptrdiff_t off = prelude_info.return_from_run_code - reinterpret_cast<CodePtr>(a.GetCursorPointer());
|
||||||
a.JAL(x0, off);
|
a.JAL(x0, off);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
namespace Dynarmic::Backend::RV64 {
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
// TODO: We should really move this to biscuit.
|
// TODO: We should really move this to biscuit.
|
||||||
static void Mov64(biscuit::Assembler& as, biscuit::GPR rd, u64 imm) {
|
void Mov64(biscuit::Assembler& as, biscuit::GPR rd, u64 imm) {
|
||||||
if (mcl::bit::sign_extend<32>(imm) == imm) {
|
if (mcl::bit::sign_extend<32>(imm) == imm) {
|
||||||
as.LI(rd, static_cast<u32>(imm));
|
as.LI(rd, static_cast<u32>(imm));
|
||||||
return;
|
return;
|
||||||
|
@ -38,7 +38,7 @@ static void Mov64(biscuit::Assembler& as, biscuit::GPR rd, u64 imm) {
|
||||||
int shift = 12 + std::countr_zero(hi52);
|
int shift = 12 + std::countr_zero(hi52);
|
||||||
hi52 = mcl::bit::sign_extend(shift, hi52 >> (shift - 12));
|
hi52 = mcl::bit::sign_extend(shift, hi52 >> (shift - 12));
|
||||||
Mov64(as, rd, hi52);
|
Mov64(as, rd, hi52);
|
||||||
as.SLLI(rd, rd, shift);
|
as.SLLI64(rd, rd, shift);
|
||||||
if (lo12 != 0) {
|
if (lo12 != 0) {
|
||||||
as.ADDI(rd, rd, lo12);
|
as.ADDI(rd, rd, lo12);
|
||||||
}
|
}
|
||||||
|
@ -49,11 +49,38 @@ void EmitIR(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||||
ASSERT_FALSE("Unimplemented opcode {}", op);
|
ASSERT_FALSE("Unimplemented opcode {}", op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::Void>(biscuit::Assembler&, EmitContext&, IR::Inst*) {}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32GetRegister>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst);
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32SetRegister>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst);
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32SetCpsrNZC>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst);
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::LogicalShiftLeft32>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst);
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::GetCarryFromOp>(biscuit::Assembler&, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::GetCarryFromOp>(biscuit::Assembler&, EmitContext& ctx, IR::Inst* inst) {
|
||||||
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::GetNZFromOp>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
|
||||||
|
auto Xnz = ctx.reg_alloc.WriteX(inst);
|
||||||
|
RegAlloc::Realize(Xvalue, Xnz);
|
||||||
|
|
||||||
|
as.SEQZ(Xnz, Xvalue);
|
||||||
|
as.SLLI(Xnz, Xnz, 30);
|
||||||
|
as.SLT(Xscratch0, Xvalue, biscuit::zero);
|
||||||
|
as.SLLI(Xscratch0, Xscratch0, 31);
|
||||||
|
as.OR(Xnz, Xnz, Xscratch0);
|
||||||
|
}
|
||||||
|
|
||||||
EmittedBlockInfo EmitRV64(biscuit::Assembler& as, IR::Block block, const EmitConfig& emit_conf) {
|
EmittedBlockInfo EmitRV64(biscuit::Assembler& as, IR::Block block, const EmitConfig& emit_conf) {
|
||||||
using namespace biscuit;
|
using namespace biscuit;
|
||||||
|
|
||||||
|
|
63
src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp
Normal file
63
src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2024 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <biscuit/assembler.hpp>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
|
#include "dynarmic/backend/riscv64/a32_jitstate.h"
|
||||||
|
#include "dynarmic/backend/riscv64/abi.h"
|
||||||
|
#include "dynarmic/backend/riscv64/emit_context.h"
|
||||||
|
#include "dynarmic/backend/riscv64/emit_riscv64.h"
|
||||||
|
#include "dynarmic/backend/riscv64/reg_alloc.h"
|
||||||
|
#include "dynarmic/ir/basic_block.h"
|
||||||
|
#include "dynarmic/ir/microinstruction.h"
|
||||||
|
#include "dynarmic/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32GetRegister>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
||||||
|
|
||||||
|
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||||
|
RegAlloc::Realize(Xresult);
|
||||||
|
|
||||||
|
as.LWU(Xresult, offsetof(A32JitState, regs) + sizeof(u32) * static_cast<size_t>(reg), Xstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32SetRegister>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
auto Xvalue = ctx.reg_alloc.ReadX(args[1]);
|
||||||
|
RegAlloc::Realize(Xvalue);
|
||||||
|
|
||||||
|
// TODO: Detect if Gpr vs Fpr is more appropriate
|
||||||
|
|
||||||
|
as.SW(Xvalue, offsetof(A32JitState, regs) + sizeof(u32) * static_cast<size_t>(reg), Xstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::A32SetCpsrNZC>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
// TODO: Add full implementation
|
||||||
|
ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
|
||||||
|
|
||||||
|
auto Xnz = ctx.reg_alloc.ReadX(args[0]);
|
||||||
|
auto Xc = ctx.reg_alloc.ReadX(args[1]);
|
||||||
|
RegAlloc::Realize(Xnz, Xc);
|
||||||
|
|
||||||
|
as.LWU(Xscratch0, offsetof(A32JitState, cpsr_nzcv), Xstate);
|
||||||
|
as.LUI(Xscratch1, 0x10000);
|
||||||
|
as.AND(Xscratch0, Xscratch0, Xscratch1);
|
||||||
|
as.OR(Xscratch0, Xscratch0, Xnz);
|
||||||
|
as.OR(Xscratch0, Xscratch0, Xc);
|
||||||
|
as.SW(Xscratch0, offsetof(A32JitState, cpsr_nzcv), Xstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic::Backend::RV64
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2024 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <biscuit/assembler.hpp>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
|
#include "dynarmic/backend/riscv64/a32_jitstate.h"
|
||||||
|
#include "dynarmic/backend/riscv64/abi.h"
|
||||||
|
#include "dynarmic/backend/riscv64/emit_context.h"
|
||||||
|
#include "dynarmic/backend/riscv64/emit_riscv64.h"
|
||||||
|
#include "dynarmic/backend/riscv64/reg_alloc.h"
|
||||||
|
#include "dynarmic/ir/basic_block.h"
|
||||||
|
#include "dynarmic/ir/microinstruction.h"
|
||||||
|
#include "dynarmic/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::LogicalShiftLeft32>(biscuit::Assembler& as, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto& operand_arg = args[0];
|
||||||
|
auto& shift_arg = args[1];
|
||||||
|
auto& carry_arg = args[2];
|
||||||
|
|
||||||
|
// TODO: Add full implementation
|
||||||
|
ASSERT(carry_inst != nullptr);
|
||||||
|
ASSERT(shift_arg.IsImmediate());
|
||||||
|
|
||||||
|
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||||
|
auto Xcarry_out = ctx.reg_alloc.WriteX(carry_inst);
|
||||||
|
auto Xoperand = ctx.reg_alloc.ReadX(operand_arg);
|
||||||
|
auto Xcarry_in = ctx.reg_alloc.ReadX(carry_arg);
|
||||||
|
RegAlloc::Realize(Xresult, Xcarry_out, Xoperand, Xcarry_in);
|
||||||
|
|
||||||
|
const u8 shift = shift_arg.GetImmediateU8();
|
||||||
|
|
||||||
|
if (shift == 0) {
|
||||||
|
as.ADDW(Xresult, Xoperand, biscuit::zero);
|
||||||
|
as.ADDW(Xcarry_out, Xcarry_in, biscuit::zero);
|
||||||
|
} else if (shift < 32) {
|
||||||
|
as.SRLIW(Xcarry_out, Xoperand, 32 - shift);
|
||||||
|
as.ANDI(Xcarry_out, Xcarry_out, 1);
|
||||||
|
as.SLLIW(Xresult, Xoperand, shift);
|
||||||
|
} else if (shift > 32) {
|
||||||
|
as.MV(Xresult, biscuit::zero);
|
||||||
|
as.MV(Xcarry_out, biscuit::zero);
|
||||||
|
} else {
|
||||||
|
as.ANDI(Xcarry_out, Xresult, 1);
|
||||||
|
as.MV(Xresult, biscuit::zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic::Backend::RV64
|
|
@ -9,10 +9,16 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include <mcl/assert.hpp>
|
#include <mcl/assert.hpp>
|
||||||
|
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||||
#include <mcl/stdint.hpp>
|
#include <mcl/stdint.hpp>
|
||||||
|
|
||||||
|
#include "dynarmic/common/always_false.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::RV64 {
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
|
// TODO: We should really move this to biscuit.
|
||||||
|
void Mov64(biscuit::Assembler& as, biscuit::GPR rd, u64 imm);
|
||||||
|
|
||||||
constexpr size_t spill_offset = offsetof(StackLayout, spill);
|
constexpr size_t spill_offset = offsetof(StackLayout, spill);
|
||||||
constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type);
|
constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type);
|
||||||
|
|
||||||
|
@ -73,6 +79,15 @@ bool HostLocInfo::Contains(const IR::Inst* value) const {
|
||||||
return std::find(values.begin(), values.end(), value) != values.end();
|
return std::find(values.begin(), values.end(), value) != values.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostLocInfo::SetupScratchLocation() {
|
||||||
|
ASSERT(IsCompletelyEmpty());
|
||||||
|
realized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostLocInfo::IsCompletelyEmpty() const {
|
||||||
|
return values.empty() && !locked && !realized && !accumulated_uses && !expected_uses;
|
||||||
|
}
|
||||||
|
|
||||||
RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
||||||
ArgumentInfo ret = {Argument{*this}, Argument{*this}, Argument{*this}, Argument{*this}};
|
ArgumentInfo ret = {Argument{*this}, Argument{*this}, Argument{*this}, Argument{*this}};
|
||||||
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
||||||
|
@ -90,11 +105,35 @@ bool RegAlloc::IsValueLive(IR::Inst* inst) const {
|
||||||
return !!ValueLocation(inst);
|
return !!ValueLocation(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool is_fpr>
|
template<HostLoc::Kind kind>
|
||||||
u32 RegAlloc::RealizeReadImpl(const IR::Inst* value) {
|
u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||||
constexpr HostLoc::Kind required_kind = is_fpr ? HostLoc::Kind::Fpr : HostLoc::Kind::Gpr;
|
// TODO
|
||||||
|
// ASSERT(value.GetType() != IR::Type::U1);
|
||||||
|
|
||||||
const auto current_location = ValueLocation(value);
|
if constexpr (kind == HostLoc::Kind::Gpr) {
|
||||||
|
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
||||||
|
SpillGpr(new_location_index);
|
||||||
|
gprs[new_location_index].SetupScratchLocation();
|
||||||
|
|
||||||
|
Mov64(as, biscuit::GPR{new_location_index}, value.GetImmediateAsU64());
|
||||||
|
|
||||||
|
return new_location_index;
|
||||||
|
} else if constexpr (kind == HostLoc::Kind::Fpr) {
|
||||||
|
ASSERT_FALSE("Unimplemented");
|
||||||
|
} else {
|
||||||
|
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<HostLoc::Kind required_kind>
|
||||||
|
u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
|
||||||
|
if (value.IsImmediate()) {
|
||||||
|
return GenerateImmediate<required_kind>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto current_location = ValueLocation(value.GetInst());
|
||||||
ASSERT(current_location);
|
ASSERT(current_location);
|
||||||
|
|
||||||
if (current_location->kind == required_kind) {
|
if (current_location->kind == required_kind) {
|
||||||
|
@ -105,7 +144,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Inst* value) {
|
||||||
ASSERT(!ValueInfo(*current_location).realized);
|
ASSERT(!ValueInfo(*current_location).realized);
|
||||||
ASSERT(!ValueInfo(*current_location).locked);
|
ASSERT(!ValueInfo(*current_location).locked);
|
||||||
|
|
||||||
if constexpr (is_fpr) {
|
if constexpr (required_kind == HostLoc::Kind::Fpr) {
|
||||||
const u32 new_location_index = AllocateRegister(fprs, fpr_order);
|
const u32 new_location_index = AllocateRegister(fprs, fpr_order);
|
||||||
SpillFpr(new_location_index);
|
SpillFpr(new_location_index);
|
||||||
|
|
||||||
|
@ -124,7 +163,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Inst* value) {
|
||||||
fprs[new_location_index] = std::exchange(ValueInfo(*current_location), {});
|
fprs[new_location_index] = std::exchange(ValueInfo(*current_location), {});
|
||||||
fprs[new_location_index].realized = true;
|
fprs[new_location_index].realized = true;
|
||||||
return new_location_index;
|
return new_location_index;
|
||||||
} else {
|
} else if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||||
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
||||||
SpillGpr(new_location_index);
|
SpillGpr(new_location_index);
|
||||||
|
|
||||||
|
@ -144,10 +183,12 @@ u32 RegAlloc::RealizeReadImpl(const IR::Inst* value) {
|
||||||
gprs[new_location_index] = std::exchange(ValueInfo(*current_location), {});
|
gprs[new_location_index] = std::exchange(ValueInfo(*current_location), {});
|
||||||
gprs[new_location_index].realized = true;
|
gprs[new_location_index].realized = true;
|
||||||
return new_location_index;
|
return new_location_index;
|
||||||
|
} else {
|
||||||
|
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool is_fpr>
|
template<HostLoc::Kind required_kind>
|
||||||
u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||||
ASSERT(!ValueLocation(value));
|
ASSERT(!ValueLocation(value));
|
||||||
|
|
||||||
|
@ -159,23 +200,25 @@ u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
|
||||||
info.expected_uses += value->UseCount();
|
info.expected_uses += value->UseCount();
|
||||||
};
|
};
|
||||||
|
|
||||||
if constexpr (is_fpr) {
|
if constexpr (required_kind == HostLoc::Kind::Fpr) {
|
||||||
const u32 new_location_index = AllocateRegister(fprs, fpr_order);
|
const u32 new_location_index = AllocateRegister(fprs, fpr_order);
|
||||||
SpillFpr(new_location_index);
|
SpillFpr(new_location_index);
|
||||||
setup_location(fprs[new_location_index]);
|
setup_location(fprs[new_location_index]);
|
||||||
return new_location_index;
|
return new_location_index;
|
||||||
} else {
|
} else if constexpr (required_kind == HostLoc::Kind::Gpr) {
|
||||||
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
const u32 new_location_index = AllocateRegister(gprs, gpr_order);
|
||||||
SpillGpr(new_location_index);
|
SpillGpr(new_location_index);
|
||||||
setup_location(gprs[new_location_index]);
|
setup_location(gprs[new_location_index]);
|
||||||
return new_location_index;
|
return new_location_index;
|
||||||
|
} else {
|
||||||
|
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template u32 RegAlloc::RealizeReadImpl<true>(const IR::Inst* value);
|
template u32 RegAlloc::RealizeReadImpl<HostLoc::Kind::Gpr>(const IR::Value& value);
|
||||||
template u32 RegAlloc::RealizeReadImpl<false>(const IR::Inst* value);
|
template u32 RegAlloc::RealizeReadImpl<HostLoc::Kind::Fpr>(const IR::Value& value);
|
||||||
template u32 RegAlloc::RealizeWriteImpl<true>(const IR::Inst* value);
|
template u32 RegAlloc::RealizeWriteImpl<HostLoc::Kind::Gpr>(const IR::Inst* value);
|
||||||
template u32 RegAlloc::RealizeWriteImpl<false>(const IR::Inst* value);
|
template u32 RegAlloc::RealizeWriteImpl<HostLoc::Kind::Fpr>(const IR::Inst* value);
|
||||||
|
|
||||||
void RegAlloc::Unlock(HostLoc host_loc) {
|
void RegAlloc::Unlock(HostLoc host_loc) {
|
||||||
HostLocInfo& info = ValueInfo(host_loc);
|
HostLocInfo& info = ValueInfo(host_loc);
|
||||||
|
|
|
@ -64,35 +64,40 @@ private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct RAReg {
|
struct RAReg {
|
||||||
public:
|
public:
|
||||||
static constexpr bool is_fpr = std::is_base_of_v<biscuit::FPR, T>;
|
static constexpr HostLoc::Kind kind = std::is_base_of_v<biscuit::FPR, T>
|
||||||
|
? HostLoc::Kind::Fpr
|
||||||
|
: HostLoc::Kind::Gpr;
|
||||||
|
|
||||||
operator T() const { return *reg; }
|
operator T() const { return *reg; }
|
||||||
|
|
||||||
T operator*() const { return *reg; }
|
T operator*() const { return *reg; }
|
||||||
|
|
||||||
|
const T* operator->() const { return &*reg; }
|
||||||
|
|
||||||
~RAReg();
|
~RAReg();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class RegAlloc;
|
friend class RegAlloc;
|
||||||
explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Inst* value)
|
explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value);
|
||||||
: reg_alloc{reg_alloc}, write{write}, value{value} {}
|
|
||||||
|
|
||||||
void Realize();
|
void Realize();
|
||||||
|
|
||||||
RegAlloc& reg_alloc;
|
RegAlloc& reg_alloc;
|
||||||
bool write;
|
bool write;
|
||||||
const IR::Inst* value;
|
const IR::Value value;
|
||||||
std::optional<T> reg;
|
std::optional<T> reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HostLocInfo final {
|
struct HostLocInfo final {
|
||||||
std::vector<const IR::Inst*> values;
|
std::vector<const IR::Inst*> values;
|
||||||
bool locked = false;
|
size_t locked = 0;
|
||||||
bool realized = false;
|
bool realized = false;
|
||||||
size_t accumulated_uses = 0;
|
size_t accumulated_uses = 0;
|
||||||
size_t expected_uses = 0;
|
size_t expected_uses = 0;
|
||||||
|
|
||||||
bool Contains(const IR::Inst*) const;
|
bool Contains(const IR::Inst*) const;
|
||||||
|
void SetupScratchLocation();
|
||||||
|
bool IsCompletelyEmpty() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegAlloc {
|
class RegAlloc {
|
||||||
|
@ -105,11 +110,11 @@ public:
|
||||||
ArgumentInfo GetArgumentInfo(IR::Inst* inst);
|
ArgumentInfo GetArgumentInfo(IR::Inst* inst);
|
||||||
bool IsValueLive(IR::Inst* inst) const;
|
bool IsValueLive(IR::Inst* inst) const;
|
||||||
|
|
||||||
auto ReadX(Argument& arg) { return RAReg<biscuit::GPR>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadX(Argument& arg) { return RAReg<biscuit::GPR>{*this, false, arg.value}; }
|
||||||
auto ReadD(Argument& arg) { return RAReg<biscuit::FPR>{*this, false, PreReadImpl(arg.value)}; }
|
auto ReadD(Argument& arg) { return RAReg<biscuit::FPR>{*this, false, arg.value}; }
|
||||||
|
|
||||||
auto WriteX(IR::Inst* inst) { return RAReg<biscuit::GPR>{*this, true, inst}; }
|
auto WriteX(IR::Inst* inst) { return RAReg<biscuit::GPR>{*this, true, IR::Value{inst}}; }
|
||||||
auto WriteD(IR::Inst* inst) { return RAReg<biscuit::FPR>{*this, true, inst}; }
|
auto WriteD(IR::Inst* inst) { return RAReg<biscuit::FPR>{*this, true, IR::Value{inst}}; }
|
||||||
|
|
||||||
void SpillAll();
|
void SpillAll();
|
||||||
|
|
||||||
|
@ -123,14 +128,11 @@ private:
|
||||||
template<typename>
|
template<typename>
|
||||||
friend struct RAReg;
|
friend struct RAReg;
|
||||||
|
|
||||||
const IR::Inst* PreReadImpl(const IR::Value& value) {
|
template<HostLoc::Kind kind>
|
||||||
ValueInfo(value.GetInst()).locked = true;
|
u32 GenerateImmediate(const IR::Value& value);
|
||||||
return value.GetInst();
|
template<HostLoc::Kind kind>
|
||||||
}
|
u32 RealizeReadImpl(const IR::Value& value);
|
||||||
|
template<HostLoc::Kind kind>
|
||||||
template<bool is_fpr>
|
|
||||||
u32 RealizeReadImpl(const IR::Inst* value);
|
|
||||||
template<bool is_fpr>
|
|
||||||
u32 RealizeWriteImpl(const IR::Inst* value);
|
u32 RealizeWriteImpl(const IR::Inst* value);
|
||||||
void Unlock(HostLoc host_loc);
|
void Unlock(HostLoc host_loc);
|
||||||
|
|
||||||
|
@ -154,16 +156,35 @@ private:
|
||||||
mutable std::mt19937 rand_gen;
|
mutable std::mt19937 rand_gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
RAReg<T>::RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value)
|
||||||
|
: reg_alloc{reg_alloc}, write{write}, value{value} {
|
||||||
|
if (!write && !value.IsImmediate()) {
|
||||||
|
reg_alloc.ValueInfo(value.GetInst()).locked++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
RAReg<T>::~RAReg() {
|
RAReg<T>::~RAReg() {
|
||||||
|
if (value.IsImmediate()) {
|
||||||
if (reg) {
|
if (reg) {
|
||||||
reg_alloc.Unlock(HostLoc{is_fpr ? HostLoc::Kind::Fpr : HostLoc::Kind::Gpr, reg->Index()});
|
// Immediate in scratch register
|
||||||
|
HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->Index()});
|
||||||
|
info.locked--;
|
||||||
|
info.realized = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HostLocInfo& info = reg_alloc.ValueInfo(value.GetInst());
|
||||||
|
info.locked--;
|
||||||
|
if (reg) {
|
||||||
|
info.realized = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RAReg<T>::Realize() {
|
void RAReg<T>::Realize() {
|
||||||
reg = T{write ? reg_alloc.RealizeWriteImpl<is_fpr>(value) : reg_alloc.RealizeReadImpl<is_fpr>(value)};
|
reg = T{write ? reg_alloc.RealizeWriteImpl<kind>(value.GetInst()) : reg_alloc.RealizeReadImpl<kind>(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::RV64
|
} // namespace Dynarmic::Backend::RV64
|
||||||
|
|
Loading…
Reference in a new issue