diff --git a/src/etnaviv/isa/parser.rs b/src/etnaviv/isa/parser.rs index d3f21944d2c..f4a1bfd00c8 100644 --- a/src/etnaviv/isa/parser.rs +++ b/src/etnaviv/isa/parser.rs @@ -15,6 +15,85 @@ use std::str::FromStr; #[static_rules_file = "static_rules.pest"] struct Isa; +// Copied over from half +// https://github.com/VoidStarKat/half-rs/blob/8cc891f3e4aad956eca7fa79b1f42f87ecd141ae/src/binary16/arch.rs#L556 +// +// In the below function, round to nearest, with ties to even. +// Let us call the most significant bit that will be shifted out the round_bit. +// +// Round up if either +// a) Removed part > tie. +// (mantissa & round_bit) != 0 && (mantissa & (round_bit - 1)) != 0 +// b) Removed part == tie, and retained part is odd. +// (mantissa & round_bit) != 0 && (mantissa & (2 * round_bit)) != 0 +// (If removed part == tie and retained part is even, do not round up.) +// These two conditions can be combined into one: +// (mantissa & round_bit) != 0 && (mantissa & ((round_bit - 1) | (2 * round_bit))) != 0 +// which can be simplified into +// (mantissa & round_bit) != 0 && (mantissa & (3 * round_bit - 1)) != 0 +// +// TODO: Replace with f16 once it's stable in Rust lang. + +#[inline] +fn f32_to_f16_fallback(value: f32) -> u16 { + // Convert to raw bytes + let x: u32 = f32::to_bits(value); + + // Extract IEEE754 components + let sign = x & 0x8000_0000u32; + let exp = x & 0x7F80_0000u32; + let man = x & 0x007F_FFFFu32; + + // Check for all exponent bits being set, which is Infinity or NaN + if exp == 0x7F80_0000u32 { + // Set mantissa MSB for NaN (and also keep shifted mantissa bits) + let nan_bit = if man == 0 { 0 } else { 0x0200u32 }; + return ((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16; + } + + // The number is normalized, start assembling half precision version + let half_sign = sign >> 16; + // Unbias the exponent, then bias for half precision + let unbiased_exp = ((exp >> 23) as i32) - 127; + let half_exp = unbiased_exp + 15; + + // Check for exponent overflow, return +infinity + if half_exp >= 0x1F { + return (half_sign | 0x7C00u32) as u16; + } + + // Check for underflow + if half_exp <= 0 { + // Check mantissa for what we can do + if 14 - half_exp > 24 { + // No rounding possibility, so this is a full underflow, return signed zero + return half_sign as u16; + } + // Don't forget about hidden leading mantissa bit when assembling mantissa + let man = man | 0x0080_0000u32; + let mut half_man = man >> (14 - half_exp); + // Check for rounding (see comment above functions) + let round_bit = 1 << (13 - half_exp); + if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 { + half_man += 1; + } + // No exponent for subnormals + return (half_sign | half_man) as u16; + } + + // Rebias the exponent + let half_exp = (half_exp as u32) << 10; + let half_man = man >> 13; + // Check for rounding (see comment above functions) + let round_bit = 0x0000_1000u32; + if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 { + // Round it + ((half_sign | half_exp | half_man) + 1) as u16 + } else { + (half_sign | half_exp | half_man) as u16 + } +} + fn get_child_rule(item: Pair) -> Rule { item.into_inner().next().unwrap().as_rule() } @@ -162,15 +241,14 @@ fn fill_source(pair: Pair, src: &mut etna_inst_src) { } Rule::Immediate_half_float => { let value: f32 = parse_numeric(item); - let bits = value.to_bits(); + let bits = f32_to_f16_fallback(value); - assert!((bits & 0xfff) == 0); /* 12 lsb cut off */ src.set_rgroup(isa_reg_group::ISA_REG_GROUP_IMMED); let imm_struct = unsafe { &mut src.__bindgen_anon_1.__bindgen_anon_2 }; imm_struct.set_imm_type(3); - imm_struct.set_imm_val(bits >> 12); + imm_struct.set_imm_val(bits as u32); } Rule::Immediate_int => { let value: i32 = parse_numeric(item);