Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/cpu/riscv_fpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,28 @@ static slow_path void riscv_emulate_f_opc_op_impl(rvvm_hart_t* vm, const uint32_
case RISCV_FPU_GEN_RM_CASES(0xD0000000UL):
switch (rs2) {
case 0x00: // fcvt.s.w
riscv_emit_s(vm, rds, fpu_fcvt_i32_to_f32(riscv_read_reg(vm, rs1)));
riscv_emit_s(vm, rds, ((rm == 0x07) ? vm->csr.fcsr >> 5 : rm) == FPU_LIB_ROUND_MM
? fpu_fcvt_i32_to_f32_rmm((int32_t)riscv_read_reg(vm, rs1))
: fpu_fcvt_i32_to_f32(riscv_read_reg(vm, rs1)));
return;
case 0x01: // fcvt.s.wu
riscv_emit_s(vm, rds, fpu_fcvt_u32_to_f32(riscv_read_reg(vm, rs1)));
riscv_emit_s(vm, rds, ((rm == 0x07) ? vm->csr.fcsr >> 5 : rm) == FPU_LIB_ROUND_MM
? fpu_fcvt_u32_to_f32_rmm((uint32_t)riscv_read_reg(vm, rs1))
: fpu_fcvt_u32_to_f32(riscv_read_reg(vm, rs1)));
return;
case 0x02: // fcvt.s.l
if (likely(vm->rv64)) {
riscv_emit_s(vm, rds, fpu_fcvt_i64_to_f32(riscv_read_reg(vm, rs1)));
riscv_emit_s(vm, rds, ((rm == 0x07) ? vm->csr.fcsr >> 5 : rm) == FPU_LIB_ROUND_MM
? fpu_fcvt_i64_to_f32_rmm((int64_t)riscv_read_reg(vm, rs1))
: fpu_fcvt_i64_to_f32(riscv_read_reg(vm, rs1)));
return;
}
break;
case 0x03: // fcvt.s.lu
if (likely(vm->rv64)) {
riscv_emit_s(vm, rds, fpu_fcvt_u64_to_f32(riscv_read_reg(vm, rs1)));
riscv_emit_s(vm, rds, ((rm == 0x07) ? vm->csr.fcsr >> 5 : rm) == FPU_LIB_ROUND_MM
? fpu_fcvt_u64_to_f32_rmm(riscv_read_reg(vm, rs1))
: fpu_fcvt_u64_to_f32(riscv_read_reg(vm, rs1)));
return;
}
break;
Expand All @@ -281,13 +289,15 @@ static slow_path void riscv_emulate_f_opc_op_impl(rvvm_hart_t* vm, const uint32_
return;
case 0x02: // fcvt.d.l
if (likely(vm->rv64)) {
riscv_emit_d(vm, rds, fpu_round_i64_to_f64(riscv_read_reg(vm, rs1), rm));
riscv_emit_d(vm, rds, fpu_round_i64_to_f64(riscv_read_reg(vm, rs1), (rm == 0x07) ? vm->csr.fcsr >> 5 : rm));
return;
}
break;
case 0x03: // fcvt.d.lu
if (likely(vm->rv64)) {
riscv_emit_d(vm, rds, fpu_fcvt_u64_to_f64(riscv_read_reg(vm, rs1)));
riscv_emit_d(vm, rds, ((rm == 0x07) ? vm->csr.fcsr >> 5 : rm) == FPU_LIB_ROUND_MM
? fpu_fcvt_u64_to_f64_rmm(riscv_read_reg(vm, rs1))
: fpu_fcvt_u64_to_f64(riscv_read_reg(vm, rs1)));
return;
}
break;
Expand Down
84 changes: 84 additions & 0 deletions src/util/fpu_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,90 @@ static forceinline fpu_f64_t fpu_round_i64_to_f64(int64_t i, uint32_t rm)
return fpu_bit_u64_to_f64(bits);
}

/*
* RMM (round to nearest, ties to max magnitude) integer -> float conversions.
* Host casts only provide RNE, so the exact-halfway case must be rounded away
* from zero explicitly; NX is raised when the conversion is inexact.
*/
static forceinline fpu_f32_t fpu_fcvt_mag_to_f32_rmm(uint64_t mag, bool neg)
{
if (unlikely(!mag)) {
return fpu_bit_u32_to_f32(neg ? 0x80000000U : 0);
}
uint32_t exp = 0;
uint64_t tmp = mag;
while (tmp >>= 1) {
++exp;
}
uint64_t sig = mag;
if (exp > 23) {
const uint32_t shift = exp - 23;
const uint64_t rem = mag & ((1ULL << shift) - 1);
sig = mag >> shift;
if (rem >= (1ULL << (shift - 1))) {
++sig; // Ties away from zero
}
if (unlikely(rem)) {
fpu_raise_inexact();
}
}
if (unlikely(sig == (1ULL << 24))) {
sig >>= 1;
++exp;
}
uint32_t bits = (neg ? 0x80000000U : 0) | ((exp + 127U) << 23) | (sig & FPU_LIB_FP32_MANTISSA_MASK);
return fpu_bit_u32_to_f32(bits);
}

static forceinline fpu_f32_t fpu_fcvt_i32_to_f32_rmm(int32_t i)
{
return fpu_fcvt_mag_to_f32_rmm((i < 0) ? (0 - (uint64_t)i) : (uint64_t)i, i < 0);
}

static forceinline fpu_f32_t fpu_fcvt_u32_to_f32_rmm(uint32_t u)
{
return fpu_fcvt_mag_to_f32_rmm(u, false);
}

static forceinline fpu_f32_t fpu_fcvt_i64_to_f32_rmm(int64_t i)
{
return fpu_fcvt_mag_to_f32_rmm((i < 0) ? (0 - (uint64_t)i) : (uint64_t)i, i < 0);
}

static forceinline fpu_f32_t fpu_fcvt_u64_to_f32_rmm(uint64_t u)
{
return fpu_fcvt_mag_to_f32_rmm(u, false);
}

static forceinline fpu_f64_t fpu_fcvt_u64_to_f64_rmm(uint64_t u)
{
if (unlikely(!u)) {
return fpu_bit_u64_to_f64(0);
}
uint32_t exp = 0;
uint64_t tmp = u;
while (tmp >>= 1) {
++exp;
}
if (exp <= 52) {
return fpu_wrap_f64((actual_double_t)u);
}
const uint32_t shift = exp - 52;
const uint64_t rem = u & ((1ULL << shift) - 1);
uint64_t sig = u >> shift;
if (rem >= (1ULL << (shift - 1))) {
++sig; // Ties away from zero
}
if (unlikely(rem)) {
fpu_raise_inexact();
}
if (unlikely(sig == (1ULL << 53))) {
sig >>= 1;
++exp;
}
uint64_t bits = ((uint64_t)(exp + 0x3FF) << 52) | (sig & FPU_LIB_FP64_MANTISSA_MASK);
return fpu_bit_u64_to_f64(bits);
}
/*
* Check whether floating-point value fits an integer type, never raises exceptions
*/
Expand Down
Loading