Skip to content

Commit

Permalink
improv(compiler) Added float to int conversion, with trap (187 tests …
Browse files Browse the repository at this point in the history
…passes now)
  • Loading branch information
ptitSeb committed Jan 14, 2022
1 parent 9d188cb commit 77e65cd
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 9 deletions.
103 changes: 102 additions & 1 deletion lib/compiler-singlepass/src/emitter_arm64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ pub trait EmitterARM64 {
fn emit_b_label(&mut self, label: Label);
fn emit_cbz_label(&mut self, sz: Size, reg: Location, label: Label);
fn emit_cbnz_label(&mut self, sz: Size, reg: Location, label: Label);
fn emit_tbz_label(&mut self, sz: Size, reg: Location, n: u32, label: Label);
fn emit_tbnz_label(&mut self, sz: Size, reg: Location, n: u32, label: Label);
fn emit_bcond_label(&mut self, condition: Condition, label: Label);
fn emit_b_register(&mut self, reg: GPR);
fn emit_call_label(&mut self, label: Label);
Expand Down Expand Up @@ -195,9 +197,13 @@ pub trait EmitterARM64 {
fn emit_scvtf(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location);
fn emit_ucvtf(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location);
fn emit_fcvt(&mut self, sz_in: Size, src: Location, dst: Location);
fn emit_fcvtzs(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location);
fn emit_fcvtzu(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location);

fn emit_read_fpcr(&mut self, reg: GPR);
fn emit_write_fpcr(&mut self, reg: GPR);
fn emit_read_fpsr(&mut self, reg: GPR);
fn emit_write_fpsr(&mut self, reg: GPR);

fn arch_supports_canonicalize_nan(&self) -> bool {
true
Expand Down Expand Up @@ -1986,6 +1992,38 @@ impl EmitterARM64 for Assembler {
_ => panic!("singlepass can't emit CBNZ {:?} {:?} {:?}", sz, reg, label),
}
}
fn emit_tbz_label(&mut self, sz: Size, reg: Location, n: u32, label: Label) {
match (sz, reg) {
(Size::S32, Location::GPR(reg)) => {
let reg = reg.into_index() as u32;
dynasm!(self ; tbz W(reg), n, =>label);
}
(Size::S64, Location::GPR(reg)) => {
let reg = reg.into_index() as u32;
dynasm!(self ; tbz X(reg), n, =>label);
}
_ => panic!(
"singlepass can't emit TBZ {:?} {:?} {:?} {:?}",
sz, reg, n, label
),
}
}
fn emit_tbnz_label(&mut self, sz: Size, reg: Location, n: u32, label: Label) {
match (sz, reg) {
(Size::S32, Location::GPR(reg)) => {
let reg = reg.into_index() as u32;
dynasm!(self ; tbnz W(reg), n, =>label);
}
(Size::S64, Location::GPR(reg)) => {
let reg = reg.into_index() as u32;
dynasm!(self ; tbnz X(reg), n, =>label);
}
_ => panic!(
"singlepass can't emit TBNZ {:?} {:?} {:?} {:?}",
sz, reg, n, label
),
}
}
fn emit_bcond_label(&mut self, condition: Condition, label: Label) {
match condition {
Condition::Eq => dynasm!(self ; b.eq => label),
Expand Down Expand Up @@ -2332,14 +2370,77 @@ impl EmitterARM64 for Assembler {
),
}
}
// mrs x0, fpcr : 1101010100 1 1 1 011 0100 0100 000 00000 o0=1(op0=3), op1=0b011(3) CRn=0b0100(4) CRm=0b0100(4) op2=0
fn emit_fcvtzs(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location) {
match (sz_in, src, sz_out, dst) {
(Size::S32, Location::SIMD(src), Size::S32, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzs W(dst), S(src));
}
(Size::S64, Location::SIMD(src), Size::S32, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzs W(dst), D(src));
}
(Size::S32, Location::SIMD(src), Size::S64, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzs X(dst), S(src));
}
(Size::S64, Location::SIMD(src), Size::S64, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzs X(dst), D(src));
}
_ => panic!(
"singlepass can't emit FCVTZS {:?} {:?} {:?} {:?}",
sz_in, src, sz_out, dst
),
}
}
fn emit_fcvtzu(&mut self, sz_in: Size, src: Location, sz_out: Size, dst: Location) {
match (sz_in, src, sz_out, dst) {
(Size::S32, Location::SIMD(src), Size::S32, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzu W(dst), S(src));
}
(Size::S64, Location::SIMD(src), Size::S32, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzu W(dst), D(src));
}
(Size::S32, Location::SIMD(src), Size::S64, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzu X(dst), S(src));
}
(Size::S64, Location::SIMD(src), Size::S64, Location::GPR(dst)) => {
let src = src.into_index() as u32;
let dst = dst.into_index() as u32;
dynasm!(self ; fcvtzu X(dst), D(src));
}
_ => panic!(
"singlepass can't emit FCVTZU {:?} {:?} {:?} {:?}",
sz_in, src, sz_out, dst
),
}
}

// 1 011 0100 0100 000 => fpcr
fn emit_read_fpcr(&mut self, reg: GPR) {
dynasm!(self ; mrs X(reg as u32), 0b1_011_0100_0100_000);
}
fn emit_write_fpcr(&mut self, reg: GPR) {
dynasm!(self ; msr 0b1_011_0100_0100_000, X(reg as u32));
}
// 1 011 0100 0100 001 => fpsr
fn emit_read_fpsr(&mut self, reg: GPR) {
dynasm!(self ; mrs X(reg as u32), 0b1_011_0100_0100_001);
}
fn emit_write_fpsr(&mut self, reg: GPR) {
dynasm!(self ; msr 0b1_011_0100_0100_001, X(reg as u32));
}
}

pub fn gen_std_trampoline_arm64(
Expand Down
192 changes: 184 additions & 8 deletions lib/compiler-singlepass/src/machine_arm64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,9 +1024,81 @@ impl MachineARM64 {
self.assembler.emit_write_fpcr(new_fpcr);
old_fpcr
}
fn set_trap_enabled(&mut self, temps: &mut Vec<GPR>) -> GPR {
// temporarly set FPCR to DefaultNan
let old_fpcr = self.acquire_temp_gpr().unwrap();
temps.push(old_fpcr.clone());
self.assembler.emit_read_fpcr(old_fpcr);
let new_fpcr = self.acquire_temp_gpr().unwrap();
temps.push(new_fpcr.clone());
self.assembler
.emit_mov(Size::S64, Location::GPR(old_fpcr), Location::GPR(new_fpcr));
// IOE is bit 8 of FPCR
self.assembler
.emit_bfc(Size::S64, 8, 1, Location::GPR(new_fpcr));
self.assembler.emit_write_fpcr(new_fpcr);
old_fpcr
}
fn restore_fpcr(&mut self, old_fpcr: GPR) {
self.assembler.emit_write_fpcr(old_fpcr);
}

fn reset_exception_fpsr(&mut self) {
// reset exception count in FPSR
let fpsr = self.acquire_temp_gpr().unwrap();
self.assembler.emit_read_fpsr(fpsr);
// IOC is 0
self.assembler
.emit_bfc(Size::S64, 0, 1, Location::GPR(fpsr));
self.assembler.emit_write_fpsr(fpsr);
self.release_gpr(fpsr);
}
fn read_fpsr(&mut self) -> GPR {
let fpsr = self.acquire_temp_gpr().unwrap();
self.assembler.emit_read_fpsr(fpsr);
fpsr
}

fn trap_float_convertion_errors(
&mut self,
old_fpcr: GPR,
sz: Size,
f: Location,
temps: &mut Vec<GPR>,
) {
let trap_badconv = self.assembler.get_label();
let end = self.assembler.get_label();

let fpsr = self.read_fpsr();
temps.push(fpsr.clone());
// no trap, than all good
self.assembler
.emit_tbz_label(Size::S32, Location::GPR(fpsr), 0, end);
// now need to check if it's overflow or NaN
self.assembler
.emit_bfc(Size::S64, 0, 4, Location::GPR(fpsr));
self.restore_fpcr(old_fpcr);
self.assembler.emit_fcmp(sz, f, f);
self.assembler.emit_bcond_label(Condition::Vs, trap_badconv);
// fallthru: trap_overflow
let offset = self.assembler.get_offset().0;
self.trap_table
.offset_to_code
.insert(offset, TrapCode::IntegerOverflow);
self.emit_illegal_op();
self.mark_instruction_address_end(offset);

self.emit_label(trap_badconv);
let offset = self.assembler.get_offset().0;
self.trap_table
.offset_to_code
.insert(offset, TrapCode::BadConversionToInteger);
self.emit_illegal_op();
self.mark_instruction_address_end(offset);

self.emit_label(end);
self.restore_fpcr(old_fpcr);
}
}

impl Machine for MachineARM64 {
Expand Down Expand Up @@ -4240,17 +4312,121 @@ impl Machine for MachineARM64 {
self.release_simd(r);
}
}
fn convert_i64_f64(&mut self, _loc: Location, _ret: Location, _signed: bool, _sat: bool) {
unimplemented!();
fn convert_i64_f64(&mut self, loc: Location, ret: Location, signed: bool, sat: bool) {
let mut gprs = vec![];
let mut neons = vec![];
let src = self.location_to_neon(Size::S64, loc, &mut neons, ImmType::None, true);
let dest = self.location_to_reg(Size::S64, ret, &mut gprs, ImmType::None, false, None);
let old_fpcr = if !sat {
self.reset_exception_fpsr();
self.set_trap_enabled(&mut gprs)
} else {
GPR::XzrSp
};
if signed {
self.assembler.emit_fcvtzs(Size::S64, src, Size::S64, dest);
} else {
self.assembler.emit_fcvtzu(Size::S64, src, Size::S64, dest);
}
if !sat {
self.trap_float_convertion_errors(old_fpcr, Size::S64, src, &mut gprs);
}
if ret != dest {
self.move_location(Size::S64, dest, ret);
}
for r in gprs {
self.release_gpr(r);
}
for r in neons {
self.release_simd(r);
}
}
fn convert_i32_f64(&mut self, _loc: Location, _ret: Location, _signed: bool, _sat: bool) {
unimplemented!();
fn convert_i32_f64(&mut self, loc: Location, ret: Location, signed: bool, sat: bool) {
let mut gprs = vec![];
let mut neons = vec![];
let src = self.location_to_neon(Size::S64, loc, &mut neons, ImmType::None, true);
let dest = self.location_to_reg(Size::S32, ret, &mut gprs, ImmType::None, false, None);
let old_fpcr = if !sat {
self.reset_exception_fpsr();
self.set_trap_enabled(&mut gprs)
} else {
GPR::XzrSp
};
if signed {
self.assembler.emit_fcvtzs(Size::S64, src, Size::S32, dest);
} else {
self.assembler.emit_fcvtzu(Size::S64, src, Size::S32, dest);
}
if !sat {
self.trap_float_convertion_errors(old_fpcr, Size::S64, src, &mut gprs);
}
if ret != dest {
self.move_location(Size::S32, dest, ret);
}
for r in gprs {
self.release_gpr(r);
}
for r in neons {
self.release_simd(r);
}
}
fn convert_i64_f32(&mut self, _loc: Location, _ret: Location, _signed: bool, _sat: bool) {
unimplemented!();
fn convert_i64_f32(&mut self, loc: Location, ret: Location, signed: bool, sat: bool) {
let mut gprs = vec![];
let mut neons = vec![];
let src = self.location_to_neon(Size::S32, loc, &mut neons, ImmType::None, true);
let dest = self.location_to_reg(Size::S64, ret, &mut gprs, ImmType::None, false, None);
let old_fpcr = if !sat {
self.reset_exception_fpsr();
self.set_trap_enabled(&mut gprs)
} else {
GPR::XzrSp
};
if signed {
self.assembler.emit_fcvtzs(Size::S32, src, Size::S64, dest);
} else {
self.assembler.emit_fcvtzu(Size::S32, src, Size::S64, dest);
}
if !sat {
self.trap_float_convertion_errors(old_fpcr, Size::S32, src, &mut gprs);
}
if ret != dest {
self.move_location(Size::S64, dest, ret);
}
for r in gprs {
self.release_gpr(r);
}
for r in neons {
self.release_simd(r);
}
}
fn convert_i32_f32(&mut self, _loc: Location, _ret: Location, _signed: bool, _sat: bool) {
unimplemented!();
fn convert_i32_f32(&mut self, loc: Location, ret: Location, signed: bool, sat: bool) {
let mut gprs = vec![];
let mut neons = vec![];
let src = self.location_to_neon(Size::S32, loc, &mut neons, ImmType::None, true);
let dest = self.location_to_reg(Size::S32, ret, &mut gprs, ImmType::None, false, None);
let old_fpcr = if !sat {
self.reset_exception_fpsr();
self.set_trap_enabled(&mut gprs)
} else {
GPR::XzrSp
};
if signed {
self.assembler.emit_fcvtzs(Size::S32, src, Size::S32, dest);
} else {
self.assembler.emit_fcvtzu(Size::S32, src, Size::S32, dest);
}
if !sat {
self.trap_float_convertion_errors(old_fpcr, Size::S32, src, &mut gprs);
}
if ret != dest {
self.move_location(Size::S32, dest, ret);
}
for r in gprs {
self.release_gpr(r);
}
for r in neons {
self.release_simd(r);
}
}
fn convert_f64_f32(&mut self, loc: Location, ret: Location) {
self.emit_relaxed_binop_neon(Assembler::emit_fcvt, Size::S32, loc, ret, true);
Expand Down

0 comments on commit 77e65cd

Please sign in to comment.