[A64] Replace RSQRT CallNativeSafe with direct blr calls
This commit is contained in:
@@ -4393,10 +4393,7 @@ EMITTER_OPCODE_TABLE(OPCODE_SET_ROUNDING_MODE, SET_ROUNDING_MODE);
|
||||
|
||||
// PPC frsqrte lookup table implementation (PowerISA Table E-5).
|
||||
// Matches the x64 backend's EmitFrsqrteHelper.
|
||||
static uint64_t PpcFrsqrte(void* raw_context) {
|
||||
auto* ctx = reinterpret_cast<ppc::PPCContext*>(raw_context);
|
||||
uint64_t bits = ctx->scratch;
|
||||
|
||||
static uint64_t PpcFrsqrte(uint64_t bits) {
|
||||
uint32_t sign = (uint32_t)(bits >> 63);
|
||||
uint32_t exp = (uint32_t)((bits >> 52) & 0x7FF);
|
||||
uint64_t mantissa = bits & 0x000FFFFFFFFFFFFFULL;
|
||||
@@ -4473,7 +4470,7 @@ struct RSQRT_F32 : Sequence<RSQRT_F32, I<OPCODE_RSQRT, F32Op, F32Op>> {
|
||||
struct RSQRT_F64 : Sequence<RSQRT_F64, I<OPCODE_RSQRT, F64Op, F64Op>> {
|
||||
static void Emit(A64Emitter& e, const EmitArgType& i) {
|
||||
// PPC frsqrte uses a specific lookup table, not a high-precision estimate.
|
||||
// Store source bits to PPCContext::scratch, call helper, load result.
|
||||
// Call PpcFrsqrte directly (pure integer math, no FPCR impact).
|
||||
DReg src = i.src1.is_constant ? e.d0 : DReg(i.src1.reg().getIdx());
|
||||
if (i.src1.is_constant) {
|
||||
union {
|
||||
@@ -4485,10 +4482,8 @@ struct RSQRT_F64 : Sequence<RSQRT_F64, I<OPCODE_RSQRT, F64Op, F64Op>> {
|
||||
e.fmov(e.d0, e.x0);
|
||||
}
|
||||
e.fmov(e.x0, src);
|
||||
e.str(e.x0, Xbyak_aarch64::ptr(
|
||||
e.GetContextReg(),
|
||||
static_cast<int32_t>(offsetof(ppc::PPCContext, scratch))));
|
||||
e.CallNative(reinterpret_cast<void*>(PpcFrsqrte));
|
||||
e.mov(e.x9, reinterpret_cast<uint64_t>(PpcFrsqrte));
|
||||
e.blr(e.x9);
|
||||
e.fmov(i.dest, e.x0);
|
||||
}
|
||||
};
|
||||
@@ -4577,34 +4572,27 @@ static uint32_t PpcVrsqrtefpLane(uint32_t bits) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t PpcVrsqrtefpHelper(void* raw_context) {
|
||||
auto* ctx = reinterpret_cast<ppc::PPCContext*>(raw_context);
|
||||
return (uint64_t)PpcVrsqrtefpLane((uint32_t)ctx->scratch);
|
||||
}
|
||||
|
||||
struct RSQRT_V128 : Sequence<RSQRT_V128, I<OPCODE_RSQRT, V128Op, V128Op>> {
|
||||
static void Emit(A64Emitter& e, const EmitArgType& i) {
|
||||
// No hardware FP emitted — the C++ helper does all math.
|
||||
// GuestToHostThunk restores FPCR after the native call.
|
||||
// Save source to stack scratch (survives CallNative).
|
||||
// Call PpcVrsqrtefpLane directly per lane (pure integer math).
|
||||
// Save source to stack scratch, accumulate results there, load at end.
|
||||
int src_idx = SrcVReg(e, i.src1, 0);
|
||||
e.str(QReg(src_idx),
|
||||
Xbyak_aarch64::ptr(e.sp,
|
||||
static_cast<int32_t>(StackLayout::GUEST_SCRATCH)));
|
||||
int dest_idx = i.dest.reg().getIdx();
|
||||
for (int lane = 0; lane < 4; lane++) {
|
||||
// Load float32 bits from saved source.
|
||||
e.ldr(e.w0, Xbyak_aarch64::ptr(
|
||||
e.sp, static_cast<int32_t>(StackLayout::GUEST_SCRATCH) +
|
||||
lane * 4));
|
||||
// Store to PPCContext::scratch for helper.
|
||||
e.str(e.x0, Xbyak_aarch64::ptr(e.GetContextReg(),
|
||||
static_cast<int32_t>(
|
||||
offsetof(ppc::PPCContext, scratch))));
|
||||
e.CallNative(reinterpret_cast<void*>(PpcVrsqrtefpHelper));
|
||||
// Insert result lane into dest (allocated reg, survives CallNative).
|
||||
e.ins(VReg(dest_idx).s4[lane], e.w0);
|
||||
e.mov(e.x9, reinterpret_cast<uint64_t>(PpcVrsqrtefpLane));
|
||||
e.blr(e.x9);
|
||||
e.str(e.w0, Xbyak_aarch64::ptr(
|
||||
e.sp, static_cast<int32_t>(StackLayout::GUEST_SCRATCH) +
|
||||
lane * 4));
|
||||
}
|
||||
e.ldr(QReg(i.dest.reg().getIdx()),
|
||||
Xbyak_aarch64::ptr(e.sp,
|
||||
static_cast<int32_t>(StackLayout::GUEST_SCRATCH)));
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_RSQRT, RSQRT_F32, RSQRT_F64, RSQRT_V128);
|
||||
|
||||
@@ -290,6 +290,90 @@ TEST_CASE("RSQRT_F32", "[arithmetic]") {
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RSQRT F64 — PPC frsqrte (lookup table estimate)
|
||||
// ============================================================================
|
||||
TEST_CASE("RSQRT_F64", "[arithmetic]") {
|
||||
TestFunction test([](HIRBuilder& b) {
|
||||
auto rsqrt = b.RSqrt(LoadFPR(b, 4));
|
||||
StoreFPR(b, 3, rsqrt);
|
||||
b.Return();
|
||||
});
|
||||
// rsqrt(1.0) — table gives an estimate near 1.0
|
||||
test.Run([](PPCContext* ctx) { ctx->f[4] = 1.0; },
|
||||
[](PPCContext* ctx) {
|
||||
// PPC frsqrte returns a table-based estimate, not exact 1.0.
|
||||
// Verify it's in the right ballpark (within ~3% of 1.0).
|
||||
REQUIRE(ctx->f[3] > 0.96);
|
||||
REQUIRE(ctx->f[3] < 1.04);
|
||||
});
|
||||
// rsqrt(4.0) — estimate near 0.5
|
||||
test.Run([](PPCContext* ctx) { ctx->f[4] = 4.0; },
|
||||
[](PPCContext* ctx) {
|
||||
REQUIRE(ctx->f[3] > 0.48);
|
||||
REQUIRE(ctx->f[3] < 0.52);
|
||||
});
|
||||
// rsqrt(+0.0) → +Inf
|
||||
test.Run([](PPCContext* ctx) { ctx->f[4] = 0.0; },
|
||||
[](PPCContext* ctx) {
|
||||
REQUIRE(std::isinf(ctx->f[3]));
|
||||
REQUIRE(ctx->f[3] > 0);
|
||||
});
|
||||
// rsqrt(negative) → QNaN
|
||||
test.Run([](PPCContext* ctx) { ctx->f[4] = -1.0; },
|
||||
[](PPCContext* ctx) { REQUIRE(std::isnan(ctx->f[3])); });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RSQRT V128 — PPC vrsqrtefp (per-lane lookup table estimate)
|
||||
// ============================================================================
|
||||
TEST_CASE("RSQRT_V128", "[vector]") {
|
||||
TestFunction test([](HIRBuilder& b) {
|
||||
StoreVR(b, 3, b.RSqrt(LoadVR(b, 4)));
|
||||
b.Return();
|
||||
});
|
||||
// Normal positive values: estimates should be in the right ballpark.
|
||||
test.Run(
|
||||
[](PPCContext* ctx) { ctx->v[4] = vec128f(1.0f, 4.0f, 16.0f, 100.0f); },
|
||||
[](PPCContext* ctx) {
|
||||
auto r = ctx->v[3];
|
||||
float r0, r1, r2, r3;
|
||||
std::memcpy(&r0, &r.u32[0], 4);
|
||||
std::memcpy(&r1, &r.u32[1], 4);
|
||||
std::memcpy(&r2, &r.u32[2], 4);
|
||||
std::memcpy(&r3, &r.u32[3], 4);
|
||||
// rsqrt(1) ≈ 1.0, rsqrt(4) ≈ 0.5, rsqrt(16) ≈ 0.25, rsqrt(100) ≈ 0.1
|
||||
REQUIRE(r0 > 0.9f);
|
||||
REQUIRE(r0 < 1.1f);
|
||||
REQUIRE(r1 > 0.45f);
|
||||
REQUIRE(r1 < 0.55f);
|
||||
REQUIRE(r2 > 0.22f);
|
||||
REQUIRE(r2 < 0.28f);
|
||||
REQUIRE(r3 > 0.08f);
|
||||
REQUIRE(r3 < 0.12f);
|
||||
});
|
||||
// +0 → +Inf
|
||||
test.Run([](PPCContext* ctx) { ctx->v[4] = vec128f(0.0f, 0.0f, 0.0f, 0.0f); },
|
||||
[](PPCContext* ctx) {
|
||||
auto r = ctx->v[3];
|
||||
float r0;
|
||||
std::memcpy(&r0, &r.u32[0], 4);
|
||||
REQUIRE(std::isinf(r0));
|
||||
REQUIRE(r0 > 0);
|
||||
});
|
||||
// Negative → QNaN
|
||||
test.Run(
|
||||
[](PPCContext* ctx) {
|
||||
ctx->v[4] = vec128f(-1.0f, -4.0f, -16.0f, -100.0f);
|
||||
},
|
||||
[](PPCContext* ctx) {
|
||||
auto r = ctx->v[3];
|
||||
float r0;
|
||||
std::memcpy(&r0, &r.u32[0], 4);
|
||||
REQUIRE(std::isnan(r0));
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// VECTOR_AVERAGE I8 — rounding halving add: (a+b+1)>>1
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user