[A64] Dedicate x19 as backend context pointer
Reserves x19 to permanently hold the A64BackendContext pointer (x20 - sizeof(A64BackendContext)), set once in the HostToGuestThunk. Since x19 is AAPCS64 callee-saved, it survives all host calls for free. Eliminates repeated 2-instruction mov+sub sequences in stackpoint push/pop, stack synchronization, FPCR management, and reserved load/store paths. Reduces allocatable GPRs from 8 to 7 (like on x64).
This commit is contained in:
@@ -123,14 +123,15 @@ HostToGuestThunk A64HelperEmitter::EmitHostToGuestThunk() {
|
||||
// Set up guest execution state.
|
||||
// x20 = context (PPCContext*)
|
||||
mov(x20, x1);
|
||||
// x19 = backend context (immediately before PPCContext in memory)
|
||||
sub(x19, x20, static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
// x21 = virtual_membase (loaded from context)
|
||||
ldr(x21, ptr(x20, static_cast<int32_t>(
|
||||
offsetof(ppc::PPCContext, virtual_membase))));
|
||||
// Restore the guest scalar FPCR on every host->guest entry so host-side
|
||||
// work done before the call can't leak a stale rounding / non-IEEE mode.
|
||||
sub(x10, x20, static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
ldr(w11,
|
||||
ptr(x10, static_cast<uint32_t>(offsetof(A64BackendContext, fpcr_fpu))));
|
||||
ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext, fpcr_fpu))));
|
||||
msr(3, 3, 4, 4, 0, x11);
|
||||
// x0 still holds target, x2 holds return address.
|
||||
// The guest function's prolog stores x0 to GUEST_RET_ADDR on its stack
|
||||
@@ -247,9 +248,9 @@ GuestToHostThunk A64HelperEmitter::EmitGuestToHostThunk() {
|
||||
|
||||
// Host callbacks may change FPCR. Restore the guest scalar FPCR before
|
||||
// resuming the JIT so later guest ops observe the cached PPC mode.
|
||||
sub(x10, x20, static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
// x19 (backend context) is callee-saved, so it survives the host call.
|
||||
ldr(w11,
|
||||
ptr(x10, static_cast<uint32_t>(offsetof(A64BackendContext, fpcr_fpu))));
|
||||
ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext, fpcr_fpu))));
|
||||
msr(3, 3, 4, 4, 0, x11);
|
||||
|
||||
code_offsets.epilog = getSize();
|
||||
@@ -365,8 +366,8 @@ ResolveFunctionThunk A64HelperEmitter::EmitResolveFunctionThunk() {
|
||||
// On entry (set by the tail-emitted sync check in the guest function):
|
||||
// x8 = return address (where to jump after fixup)
|
||||
// x9 = caller's stack size (to subtract from restored SP)
|
||||
// x19 = A64BackendContext*
|
||||
// x20 = PPCContext*
|
||||
// The backend context is at (x20 - sizeof(A64BackendContext)).
|
||||
void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
|
||||
using namespace Xbyak_aarch64;
|
||||
struct {
|
||||
@@ -381,15 +382,13 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
|
||||
code_offsets.prolog_stack_alloc = getSize();
|
||||
code_offsets.body = getSize();
|
||||
|
||||
// Load backend context pointer: x17 = x20 - sizeof(A64BackendContext)
|
||||
mov(x17, static_cast<uint64_t>(sizeof(A64BackendContext)));
|
||||
sub(x17, x20, x17);
|
||||
// x19 = backend context pointer (already set up by HostToGuestThunk)
|
||||
|
||||
// x10 = stackpoints array pointer
|
||||
ldr(x10, ptr(x17, static_cast<uint32_t>(
|
||||
ldr(x10, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, stackpoints))));
|
||||
// w11 = current_stackpoint_depth
|
||||
ldr(w11, ptr(x17, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
ldr(w11, ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
current_stackpoint_depth))));
|
||||
|
||||
// w12 = current guest r1
|
||||
@@ -438,7 +437,7 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
|
||||
// Update current_stackpoint_depth = index + 1
|
||||
// (the entry we restored to has been consumed)
|
||||
add(w13, w13, 1);
|
||||
str(w13, ptr(x17, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
str(w13, ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
current_stackpoint_depth))));
|
||||
|
||||
// Jump back to the caller.
|
||||
@@ -587,7 +586,7 @@ bool A64Backend::Initialize(Processor* processor) {
|
||||
|
||||
// Set up machine info for the register allocator.
|
||||
machine_info_.supports_extended_load_store = true;
|
||||
// GPR set: x19, x22-x28 (8 registers, excluding x20=context, x21=membase)
|
||||
// GPR set: x22-x28 (7 registers; x19=backend ctx, x20=context, x21=membase)
|
||||
auto& gpr_set = machine_info_.register_sets[0];
|
||||
gpr_set.id = 0;
|
||||
std::strcpy(gpr_set.name, "gpr");
|
||||
|
||||
@@ -57,10 +57,10 @@ static uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
|
||||
static constexpr size_t kMaxCodeSize = 1_MiB;
|
||||
|
||||
// Register maps:
|
||||
// GPR allocatable registers: x19, x22, x23, x24, x25, x26, x27, x28
|
||||
// (x20=context, x21=membase are reserved)
|
||||
// GPR allocatable registers: x22, x23, x24, x25, x26, x27, x28
|
||||
// (x19=backend context, x20=context, x21=membase are reserved)
|
||||
const uint32_t A64Emitter::gpr_reg_map_[GPR_COUNT] = {
|
||||
19, 22, 23, 24, 25, 26, 27, 28,
|
||||
22, 23, 24, 25, 26, 27, 28,
|
||||
};
|
||||
|
||||
// VEC allocatable registers: v4-v7, v16-v31
|
||||
@@ -170,9 +170,7 @@ bool A64Emitter::Emit(hir::HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||
// for post-call detection (if depth changes, a longjmp skipped frames).
|
||||
PushStackpoint();
|
||||
if (cvars::a64_enable_host_guest_stack_synchronization) {
|
||||
mov(x17, static_cast<uint64_t>(sizeof(A64BackendContext)));
|
||||
sub(x17, x20, x17);
|
||||
ldr(w16, ptr(x17, static_cast<uint32_t>(offsetof(
|
||||
ldr(w16, ptr(x19, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, current_stackpoint_depth))));
|
||||
str(w16, ptr(sp, static_cast<uint32_t>(
|
||||
StackLayout::GUEST_SAVED_STACKPOINT_DEPTH)));
|
||||
@@ -551,14 +549,10 @@ void A64Emitter::PushStackpoint() {
|
||||
if (!cvars::a64_enable_host_guest_stack_synchronization) {
|
||||
return;
|
||||
}
|
||||
// Load backend context pointer: x17 = x20 - sizeof(A64BackendContext)
|
||||
mov(x17, static_cast<uint64_t>(sizeof(A64BackendContext)));
|
||||
sub(x17, x20, x17);
|
||||
|
||||
// x8 = stackpoints array, w9 = current depth
|
||||
ldr(x8, ptr(x17,
|
||||
ldr(x8, ptr(x19,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, stackpoints))));
|
||||
ldr(w9, ptr(x17, static_cast<uint32_t>(
|
||||
ldr(w9, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, current_stackpoint_depth))));
|
||||
|
||||
// Compute offset into array: x10 = w9 * sizeof(A64BackendStackpoint)
|
||||
@@ -581,7 +575,7 @@ void A64Emitter::PushStackpoint() {
|
||||
|
||||
// Increment depth.
|
||||
add(w9, w9, 1);
|
||||
str(w9, ptr(x17, static_cast<uint32_t>(
|
||||
str(w9, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, current_stackpoint_depth))));
|
||||
|
||||
// Check for overflow.
|
||||
@@ -599,12 +593,10 @@ void A64Emitter::PopStackpoint() {
|
||||
return;
|
||||
}
|
||||
// Decrement current_stackpoint_depth.
|
||||
mov(x17, static_cast<uint64_t>(sizeof(A64BackendContext)));
|
||||
sub(x17, x20, x17);
|
||||
ldr(w8, ptr(x17, static_cast<uint32_t>(
|
||||
ldr(w8, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, current_stackpoint_depth))));
|
||||
sub(w8, w8, 1);
|
||||
str(w8, ptr(x17, static_cast<uint32_t>(
|
||||
str(w8, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, current_stackpoint_depth))));
|
||||
}
|
||||
|
||||
@@ -617,9 +609,7 @@ void A64Emitter::EnsureSynchronizedGuestAndHostStack() {
|
||||
// some frames' PopStackpoint never ran.
|
||||
auto& return_from_sync = NewCachedLabel();
|
||||
|
||||
mov(x17, static_cast<uint64_t>(sizeof(A64BackendContext)));
|
||||
sub(x17, x20, x17);
|
||||
ldr(w17, ptr(x17, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
ldr(w17, ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
current_stackpoint_depth))));
|
||||
ldr(w16, ptr(sp, static_cast<uint32_t>(
|
||||
StackLayout::GUEST_SAVED_STACKPOINT_DEPTH)));
|
||||
|
||||
@@ -71,10 +71,10 @@ class A64Emitter : public Xbyak_aarch64::CodeGenerator {
|
||||
std::vector<SourceMapEntry>* out_source_map);
|
||||
|
||||
public:
|
||||
// Reserved: sp, x20 (context), x21 (membase)
|
||||
// Reserved: sp, x19 (backend context), x20 (context), x21 (membase)
|
||||
// Scratch: x0-x18 (caller-saved), v0-v3
|
||||
// Available GPRs for register allocator: x19, x22-x28
|
||||
static constexpr int GPR_COUNT = 8;
|
||||
// Available GPRs for register allocator: x22-x28
|
||||
static constexpr int GPR_COUNT = 7;
|
||||
// Available VEC regs: v4-v7, v16-v31
|
||||
static constexpr int VEC_COUNT = 20;
|
||||
static constexpr size_t kStashOffset = 32;
|
||||
@@ -122,6 +122,9 @@ class A64Emitter : public Xbyak_aarch64::CodeGenerator {
|
||||
void CallNativeSafe(void* fn);
|
||||
void SetReturnAddress(uint64_t value);
|
||||
|
||||
// Backend context register = x19.
|
||||
// Points to A64BackendContext (immediately before PPCContext in memory).
|
||||
const Xbyak_aarch64::XReg& GetBackendCtxReg() const { return x19; }
|
||||
// Context register = x20.
|
||||
const Xbyak_aarch64::XReg& GetContextReg() const { return x20; }
|
||||
// Memory base register = x21.
|
||||
|
||||
@@ -1087,10 +1087,11 @@ EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO, STORE_MMIO_I32);
|
||||
// ============================================================================
|
||||
// OPCODE_RESERVED_LOAD / OPCODE_RESERVED_STORE
|
||||
// ============================================================================
|
||||
// Helper: get pointer to A64BackendContext in x17.
|
||||
static void LoadBackendCtxPtr(A64Emitter& e) {
|
||||
e.sub(e.x17, e.GetContextReg(),
|
||||
static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
// Helper: get pointer to A64BackendContext.
|
||||
// x19 is the dedicated backend context register, so this is a no-op
|
||||
// accessor for readability. The returned register is x19.
|
||||
static const Xbyak_aarch64::XReg& LoadBackendCtxPtr(A64Emitter& e) {
|
||||
return e.GetBackendCtxReg();
|
||||
}
|
||||
|
||||
struct RESERVED_LOAD_I32
|
||||
@@ -1102,20 +1103,20 @@ struct RESERVED_LOAD_I32
|
||||
// Load the value (may clobber addr if dest == addr).
|
||||
e.ldr(i.dest, ptr(e.GetMembaseReg(), addr));
|
||||
// Save reservation: address and value in backend context.
|
||||
LoadBackendCtxPtr(e);
|
||||
auto bctx = LoadBackendCtxPtr(e);
|
||||
// Store the guest address (already saved in x0).
|
||||
e.str(e.x0, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.str(e.x0, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_offset))));
|
||||
// Store the loaded value (zero-extended to 64-bit).
|
||||
e.mov(e.w1, i.dest);
|
||||
e.str(e.x1, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.str(e.x1, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_value_))));
|
||||
// Set the "has reserve" flag (bit 1).
|
||||
e.ldr(e.w1, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.ldr(e.w1,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.orr(e.w1, e.w1, static_cast<uint32_t>(1u << kA64BackendHasReserveBit));
|
||||
e.str(e.w1, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.str(e.w1,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
}
|
||||
};
|
||||
struct RESERVED_LOAD_I64
|
||||
@@ -1127,16 +1128,16 @@ struct RESERVED_LOAD_I64
|
||||
// Load the value (may clobber addr if dest == addr).
|
||||
e.ldr(i.dest, ptr(e.GetMembaseReg(), addr));
|
||||
// Save reservation in backend context.
|
||||
LoadBackendCtxPtr(e);
|
||||
e.str(e.x0, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
auto bctx = LoadBackendCtxPtr(e);
|
||||
e.str(e.x0, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_offset))));
|
||||
e.str(i.dest, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.str(i.dest, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_value_))));
|
||||
e.ldr(e.w1, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.ldr(e.w1,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.orr(e.w1, e.w1, static_cast<uint32_t>(1u << kA64BackendHasReserveBit));
|
||||
e.str(e.w1, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.str(e.w1,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_RESERVED_LOAD, RESERVED_LOAD_I32,
|
||||
@@ -1150,24 +1151,24 @@ struct RESERVED_STORE_I32
|
||||
auto& no_reserve = e.NewCachedLabel();
|
||||
auto& done = e.NewCachedLabel();
|
||||
// Check if we have a reservation.
|
||||
LoadBackendCtxPtr(e);
|
||||
e.ldr(e.w4, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
auto bctx = LoadBackendCtxPtr(e);
|
||||
e.ldr(e.w4,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.tbz(e.w4, kA64BackendHasReserveBit, no_reserve);
|
||||
// Clear the reserve flag.
|
||||
e.and_(e.w4, e.w4,
|
||||
static_cast<uint32_t>(~(1u << kA64BackendHasReserveBit)));
|
||||
e.str(e.w4, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.str(e.w4,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
// Check if address matches.
|
||||
e.ldr(e.x4, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.ldr(e.x4, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_offset))));
|
||||
e.mov(e.w5, WReg(addr.getIdx()));
|
||||
e.cmp(e.x4, e.x5);
|
||||
e.b(Xbyak_aarch64::NE, no_reserve);
|
||||
// Address matches. Do atomic compare-exchange.
|
||||
// Expected value from cached_reserve_value_.
|
||||
e.ldr(e.w5, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.ldr(e.w5, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_value_))));
|
||||
// Desired value.
|
||||
if (i.src2.is_constant) {
|
||||
@@ -1204,21 +1205,21 @@ struct RESERVED_STORE_I64
|
||||
auto addr = ComputeMemoryAddress(e, i.src1);
|
||||
auto& no_reserve = e.NewCachedLabel();
|
||||
auto& done = e.NewCachedLabel();
|
||||
LoadBackendCtxPtr(e);
|
||||
e.ldr(e.w4, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
auto bctx = LoadBackendCtxPtr(e);
|
||||
e.ldr(e.w4,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.tbz(e.w4, kA64BackendHasReserveBit, no_reserve);
|
||||
e.and_(e.w4, e.w4,
|
||||
static_cast<uint32_t>(~(1u << kA64BackendHasReserveBit)));
|
||||
e.str(e.w4, ptr(e.x17,
|
||||
static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.ldr(e.x4, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.str(e.w4,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
e.ldr(e.x4, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_offset))));
|
||||
e.mov(e.w5, WReg(addr.getIdx()));
|
||||
e.cmp(e.x4, e.x5);
|
||||
e.b(Xbyak_aarch64::NE, no_reserve);
|
||||
// 64-bit compare-exchange.
|
||||
e.ldr(e.x5, ptr(e.x17, static_cast<uint32_t>(offsetof(
|
||||
e.ldr(e.x5, ptr(bctx, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, cached_reserve_value_))));
|
||||
if (i.src2.is_constant) {
|
||||
e.mov(e.x6, static_cast<uint64_t>(i.src2.constant()));
|
||||
|
||||
@@ -35,10 +35,9 @@ inline void EmitWithVmxFpcr(A64Emitter& e, Fn&& emit_op) {
|
||||
// and restore around each VMX op so vector code doesn't leak FPCR changes
|
||||
// into later scalar instructions.
|
||||
e.mrs(e.x13, 3, 3, 4, 4, 0);
|
||||
e.sub(e.x14, e.GetContextReg(),
|
||||
static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
e.ldr(e.w15, Xbyak_aarch64::ptr(e.x14, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, fpcr_vmx))));
|
||||
e.ldr(e.w15, Xbyak_aarch64::ptr(e.GetBackendCtxReg(),
|
||||
static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, fpcr_vmx))));
|
||||
e.msr(3, 3, 4, 4, 0, e.x15);
|
||||
emit_op();
|
||||
e.msr(3, 3, 4, 4, 0, e.x13);
|
||||
|
||||
@@ -4434,20 +4434,19 @@ struct SET_ROUNDING_MODE
|
||||
static void Emit(A64Emitter& e, const EmitArgType& i) {
|
||||
// Input is PPC FPSCR bits (already masked to 0-7 by the frontend).
|
||||
// We set FPCR RMode + FZ bits and cache the value in the backend context.
|
||||
// x17 = backend context base pointer.
|
||||
e.sub(e.x17, e.GetContextReg(),
|
||||
static_cast<uint32_t>(sizeof(A64BackendContext)));
|
||||
auto bctx = e.GetBackendCtxReg();
|
||||
|
||||
if (i.src1.is_constant) {
|
||||
uint32_t fpcr_val = fpcr_table[i.src1.constant() & 7];
|
||||
e.mov(e.x0, static_cast<uint64_t>(fpcr_val));
|
||||
e.msr(3, 3, 4, 4, 0, e.x0); // msr FPCR, x0
|
||||
// Cache in backend context.
|
||||
e.str(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
e.str(e.w0, ptr(bctx, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, fpcr_fpu))));
|
||||
// Update NonIEEE flag.
|
||||
e.ldr(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, flags))));
|
||||
e.ldr(
|
||||
e.w0,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
if (i.src1.constant() & 4) {
|
||||
e.orr(e.w0, e.w0, 1u << kA64BackendNonIEEEMode);
|
||||
} else {
|
||||
@@ -4455,8 +4454,9 @@ struct SET_ROUNDING_MODE
|
||||
e.mov(e.w1, 1u << kA64BackendNonIEEEMode);
|
||||
e.bic(e.w0, e.w0, e.w1);
|
||||
}
|
||||
e.str(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, flags))));
|
||||
e.str(
|
||||
e.w0,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
} else {
|
||||
// Dynamic: look up FPCR value from table.
|
||||
e.mov(e.x0, reinterpret_cast<uint64_t>(fpcr_table));
|
||||
@@ -4465,11 +4465,12 @@ struct SET_ROUNDING_MODE
|
||||
// Write FPCR.
|
||||
e.msr(3, 3, 4, 4, 0, e.x0);
|
||||
// Cache in backend context.
|
||||
e.str(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
e.str(e.w0, ptr(bctx, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, fpcr_fpu))));
|
||||
// Update NonIEEE flag based on bit 2 of input.
|
||||
e.ldr(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, flags))));
|
||||
e.ldr(
|
||||
e.w0,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
// Clear bit kA64BackendNonIEEEMode using BIC (avoids bitmask encoding).
|
||||
e.mov(e.w1, 1u << kA64BackendNonIEEEMode);
|
||||
e.bic(e.w0, e.w0, e.w1);
|
||||
@@ -4477,8 +4478,9 @@ struct SET_ROUNDING_MODE
|
||||
e.tst(i.src1, 4);
|
||||
e.csel(e.w1, e.w1, e.wzr, Xbyak_aarch64::Cond::NE);
|
||||
e.orr(e.w0, e.w0, e.w1);
|
||||
e.str(e.w0, ptr(e.x17, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext, flags))));
|
||||
e.str(
|
||||
e.w0,
|
||||
ptr(bctx, static_cast<uint32_t>(offsetof(A64BackendContext, flags))));
|
||||
}
|
||||
e.ChangeFpcrMode(FPCRMode::Fpu, /*already_set=*/true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user