diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index ae3842c4a4..f3c31b43ed 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -7,7 +7,7 @@ use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::{InLayout, STLayoutInterner}; -use super::CompareOperation; +use super::{CompareOperation, RegisterWidth}; #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[allow(dead_code)] @@ -854,6 +854,7 @@ impl Assembler for AArch64Assembler { #[inline(always)] fn eq_reg64_reg64_reg64( _buf: &mut Vec<'_, u8>, + _register_width: RegisterWidth, _dst: AArch64GeneralReg, _src1: AArch64GeneralReg, _src2: AArch64GeneralReg, @@ -864,6 +865,7 @@ impl Assembler for AArch64Assembler { #[inline(always)] fn neq_reg64_reg64_reg64( _buf: &mut Vec<'_, u8>, + _register_width: RegisterWidth, _dst: AArch64GeneralReg, _src1: AArch64GeneralReg, _src2: AArch64GeneralReg, @@ -871,26 +873,6 @@ impl Assembler for AArch64Assembler { todo!("registers non-equality for AArch64"); } - #[inline(always)] - fn ilt_reg64_reg64_reg64( - _buf: &mut Vec<'_, u8>, - _dst: AArch64GeneralReg, - _src1: AArch64GeneralReg, - _src2: AArch64GeneralReg, - ) { - todo!("registers signed less than for AArch64"); - } - - #[inline(always)] - fn ult_reg64_reg64_reg64( - _buf: &mut Vec<'_, u8>, - _dst: AArch64GeneralReg, - _src1: AArch64GeneralReg, - _src2: AArch64GeneralReg, - ) { - todo!("registers unsigned less than for AArch64"); - } - #[inline(always)] fn cmp_freg_freg_reg64( _buf: &mut Vec<'_, u8>, @@ -903,26 +885,6 @@ impl Assembler for AArch64Assembler { todo!("registers float comparison for AArch64"); } - #[inline(always)] - fn igt_reg64_reg64_reg64( - _buf: &mut Vec<'_, u8>, - _dst: AArch64GeneralReg, - _src1: AArch64GeneralReg, - _src2: AArch64GeneralReg, - ) { - todo!("registers signed greater than for AArch64"); - } - - #[inline(always)] - fn ugt_reg64_reg64_reg64( - _buf: &mut Vec<'_, u8>, - _dst: AArch64GeneralReg, - _src1: AArch64GeneralReg, - _src2: AArch64GeneralReg, - ) { - todo!("registers unsigned greater than for AArch64"); - } - #[inline(always)] fn to_float_freg64_reg64( _buf: &mut Vec<'_, u8>, @@ -1061,6 +1023,28 @@ impl Assembler for AArch64Assembler { fn sqrt_freg32_freg32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) { todo!("sqrt") } + + fn signed_compare_reg64( + _buf: &mut Vec<'_, u8>, + _register_width: RegisterWidth, + _operation: CompareOperation, + _dst: AArch64GeneralReg, + _src1: AArch64GeneralReg, + _src2: AArch64GeneralReg, + ) { + todo!("signed compare") + } + + fn unsigned_compare_reg64( + _buf: &mut Vec<'_, u8>, + _register_width: RegisterWidth, + _operation: CompareOperation, + _dst: AArch64GeneralReg, + _src1: AArch64GeneralReg, + _src2: AArch64GeneralReg, + ) { + todo!("unsigned compare") + } } impl AArch64Assembler {} diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 81e8f6bc7a..d07da281dd 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -27,6 +27,14 @@ use storage::{RegStorage, StorageManager}; // TODO: on all number functions double check and deal with over/underflow. +#[derive(Debug, Clone, Copy)] +pub enum RegisterWidth { + W8, + W16, + W32, + W64, +} + pub trait CallConv>: Sized + Copy { @@ -390,6 +398,7 @@ pub trait Assembler: Sized + Copy { fn eq_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, dst: GeneralReg, src1: GeneralReg, src2: GeneralReg, @@ -397,20 +406,25 @@ pub trait Assembler: Sized + Copy { fn neq_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, dst: GeneralReg, src1: GeneralReg, src2: GeneralReg, ); - fn ilt_reg64_reg64_reg64( + fn signed_compare_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + operation: CompareOperation, dst: GeneralReg, src1: GeneralReg, src2: GeneralReg, ); - fn ult_reg64_reg64_reg64( + fn unsigned_compare_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + operation: CompareOperation, dst: GeneralReg, src1: GeneralReg, src2: GeneralReg, @@ -425,20 +439,6 @@ pub trait Assembler: Sized + Copy { operation: CompareOperation, ); - fn igt_reg64_reg64_reg64( - buf: &mut Vec<'_, u8>, - dst: GeneralReg, - src1: GeneralReg, - src2: GeneralReg, - ); - - fn ugt_reg64_reg64_reg64( - buf: &mut Vec<'_, u8>, - dst: GeneralReg, - src1: GeneralReg, - src2: GeneralReg, - ); - fn to_float_freg32_reg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: GeneralReg); fn to_float_freg64_reg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: GeneralReg); @@ -769,6 +769,10 @@ impl< // Call function and generate reloc. ASM::call(&mut self.buf, &mut self.relocs, fn_name); + self.move_return_value(dst, ret_layout) + } + + fn move_return_value(&mut self, dst: &Symbol, ret_layout: &InLayout<'a>) { // move return value to dst. match *ret_layout { single_register_integers!() => { @@ -786,6 +790,9 @@ impl< let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]); } + Layout::LambdaSet(lambda_set) => { + self.move_return_value(dst, &lambda_set.runtime_representation()) + } _ => { CC::load_returned_complex_symbol( &mut self.buf, @@ -1191,6 +1198,14 @@ impl< fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) { match *arg_layout { single_register_int_builtins!() | Layout::BOOL => { + let width = match *arg_layout { + Layout::BOOL | Layout::I8 | Layout::U8 => RegisterWidth::W8, + Layout::I16 | Layout::U16 => RegisterWidth::W16, + Layout::U32 | Layout::I32 => RegisterWidth::W32, + Layout::I64 | Layout::U64 => RegisterWidth::W64, + _ => unreachable!(), + }; + let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); let src1_reg = self .storage_manager @@ -1198,7 +1213,7 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::eq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::eq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, src1_reg, src2_reg); } Layout::STR => { // use a zig call @@ -1208,7 +1223,17 @@ impl< &[*src1, *src2], &[Layout::STR, Layout::STR], &Layout::BOOL, - ) + ); + + // mask the result; we pass booleans around as 64-bit values, but branch on 0x0 and 0x1. + // Zig gives back values where not all of the upper bits are zero, so we must clear them ourselves + let tmp = &Symbol::DEV_TMP; + let tmp_reg = self.storage_manager.claim_general_reg(&mut self.buf, tmp); + ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, true as i64); + + let width = RegisterWidth::W8; // we're comparing booleans + let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst); + ASM::eq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, dst_reg, tmp_reg); } x => todo!("NumEq: layout, {:?}", x), } @@ -1217,6 +1242,14 @@ impl< fn build_neq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) { match *arg_layout { single_register_int_builtins!() | Layout::BOOL => { + let width = match *arg_layout { + Layout::BOOL | Layout::I8 | Layout::U8 => RegisterWidth::W8, + Layout::I16 | Layout::U16 => RegisterWidth::W16, + Layout::U32 | Layout::I32 => RegisterWidth::W32, + Layout::I64 | Layout::U64 => RegisterWidth::W64, + _ => unreachable!(), + }; + let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); let src1_reg = self .storage_manager @@ -1224,7 +1257,7 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, src1_reg, src2_reg); } Layout::STR => { self.build_fn_call( @@ -1238,10 +1271,11 @@ impl< // negate the result let tmp = &Symbol::DEV_TMP; let tmp_reg = self.storage_manager.claim_general_reg(&mut self.buf, tmp); - ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, 164); + ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, true as i64); + let width = RegisterWidth::W8; // we're comparing booleans let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst); - ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, tmp_reg); + ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, dst_reg, tmp_reg); } x => todo!("NumNeq: layout, {:?}", x), } @@ -1280,7 +1314,14 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::ilt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::signed_compare_reg64( + &mut self.buf, + RegisterWidth::W64, + CompareOperation::LessThan, + dst_reg, + src1_reg, + src2_reg, + ); } Layout::Builtin(Builtin::Int(IntWidth::U64)) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); @@ -1290,7 +1331,14 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::ult_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::unsigned_compare_reg64( + &mut self.buf, + RegisterWidth::W64, + CompareOperation::LessThan, + dst_reg, + src1_reg, + src2_reg, + ); } Layout::Builtin(Builtin::Float(width)) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); @@ -1326,7 +1374,14 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::igt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::signed_compare_reg64( + &mut self.buf, + RegisterWidth::W64, + CompareOperation::GreaterThan, + dst_reg, + src1_reg, + src2_reg, + ); } Layout::Builtin(Builtin::Int(IntWidth::U64)) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); @@ -1336,7 +1391,14 @@ impl< let src2_reg = self .storage_manager .load_to_general_reg(&mut self.buf, src2); - ASM::ugt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + ASM::unsigned_compare_reg64( + &mut self.buf, + RegisterWidth::W64, + CompareOperation::GreaterThan, + dst_reg, + src1_reg, + src2_reg, + ); } Layout::Builtin(Builtin::Float(width)) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); @@ -2216,7 +2278,13 @@ impl< } fn load_literal(&mut self, sym: &Symbol, layout: &InLayout<'a>, lit: &Literal<'a>) { - match (lit, self.layout_interner.get(*layout)) { + let layout = self.layout_interner.get(*layout); + + if let Layout::LambdaSet(lambda_set) = layout { + return self.load_literal(sym, &lambda_set.runtime_representation(), lit); + } + + match (lit, layout) { ( Literal::Int(x), Layout::Builtin(Builtin::Int( @@ -2263,8 +2331,7 @@ impl< } (Literal::Bool(x), Layout::Builtin(Builtin::Bool)) => { let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym); - let val = [*x as u8; 16]; - ASM::mov_reg64_imm64(&mut self.buf, reg, i128::from_ne_bytes(val) as i64); + ASM::mov_reg64_imm64(&mut self.buf, reg, *x as i64); } (Literal::Float(x), Layout::Builtin(Builtin::Float(FloatWidth::F64))) => { let reg = self.storage_manager.claim_float_reg(&mut self.buf, sym); @@ -2371,6 +2438,9 @@ impl< CC::GENERAL_RETURN_REGS[0], ); } + Layout::LambdaSet(lambda_set) => { + self.return_symbol(sym, &lambda_set.runtime_representation()) + } _ => { internal_error!("All primitive values should fit in a single register"); } diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 7e2eb7d4b4..23ea002f16 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -785,9 +785,22 @@ impl< FloatWidth::F32 => todo!(), }, Builtin::Bool => { - // same as 8-bit integer - let reg = self.load_to_general_reg(buf, sym); - ASM::mov_base32_reg8(buf, to_offset, reg); + // same as 8-bit integer, but we special-case true/false because these symbols + // are thunks and literal values + match *sym { + Symbol::BOOL_FALSE => { + let reg = self.claim_general_reg(buf, sym); + ASM::mov_reg64_imm64(buf, reg, false as i64) + } + Symbol::BOOL_TRUE => { + let reg = self.claim_general_reg(buf, sym); + ASM::mov_reg64_imm64(buf, reg, true as i64) + } + _ => { + let reg = self.load_to_general_reg(buf, sym); + ASM::mov_base32_reg8(buf, to_offset, reg); + } + } } Builtin::Decimal => todo!(), Builtin::Str | Builtin::List(_) => { @@ -1166,9 +1179,9 @@ impl< Some(storages) => storages, None => internal_error!("Jump: unknown point specified to jump to: {:?}", id), }; - for ((sym, layout), wanted_storage) in - args.iter().zip(arg_layouts).zip(param_storage.iter()) - { + + let it = args.iter().zip(arg_layouts).zip(param_storage.iter()); + for ((sym, layout), wanted_storage) in it { // Note: it is possible that the storage we want to move to is in use by one of the args we want to pass. if self.get_storage_for_sym(sym) == wanted_storage { continue; diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index 0f3902e951..78ca139520 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -9,7 +9,7 @@ use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner}; -use super::CompareOperation; +use super::{CompareOperation, RegisterWidth}; // Not sure exactly how I want to represent registers. // If we want max speed, we would likely make them structs that impl the same trait to avoid ifs. @@ -1554,45 +1554,71 @@ impl Assembler for X86_64Assembler { #[inline(always)] fn eq_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, dst: X86_64GeneralReg, src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); + cmp_reg64_reg64(buf, register_width, src1, src2); sete_reg64(buf, dst); } #[inline(always)] fn neq_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, dst: X86_64GeneralReg, src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); + cmp_reg64_reg64(buf, register_width, src1, src2); setne_reg64(buf, dst); } #[inline(always)] - fn ilt_reg64_reg64_reg64( + fn signed_compare_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + operation: CompareOperation, dst: X86_64GeneralReg, src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); - setl_reg64(buf, dst); + match operation { + CompareOperation::LessThan => { + cmp_reg64_reg64(buf, register_width, src1, src2); + setl_reg64(buf, dst); + } + CompareOperation::LessThanOrEqual => todo!(), + CompareOperation::GreaterThan => { + cmp_reg64_reg64(buf, register_width, src1, src2); + setg_reg64(buf, dst); + } + CompareOperation::GreaterThanOrEqual => todo!(), + } } - #[inline(always)] - fn ult_reg64_reg64_reg64( + fn unsigned_compare_reg64( buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + operation: CompareOperation, dst: X86_64GeneralReg, src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); - setb_reg64(buf, dst); + match operation { + CompareOperation::LessThan => { + cmp_reg64_reg64(buf, register_width, src1, src2); + setb_reg64(buf, dst); + } + CompareOperation::LessThanOrEqual => todo!(), + CompareOperation::GreaterThan => { + cmp_reg64_reg64(buf, register_width, src1, src2); + seta_reg64(buf, dst); + } + + CompareOperation::GreaterThanOrEqual => todo!(), + } } #[inline(always)] @@ -1622,28 +1648,6 @@ impl Assembler for X86_64Assembler { }; } - #[inline(always)] - fn igt_reg64_reg64_reg64( - buf: &mut Vec<'_, u8>, - dst: X86_64GeneralReg, - src1: X86_64GeneralReg, - src2: X86_64GeneralReg, - ) { - cmp_reg64_reg64(buf, src1, src2); - setg_reg64(buf, dst); - } - - #[inline(always)] - fn ugt_reg64_reg64_reg64( - buf: &mut Vec<'_, u8>, - dst: X86_64GeneralReg, - src1: X86_64GeneralReg, - src2: X86_64GeneralReg, - ) { - cmp_reg64_reg64(buf, src1, src2); - seta_reg64(buf, dst); - } - #[inline(always)] fn to_float_freg32_reg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64GeneralReg) { cvtsi2ss_freg64_reg64(buf, dst, src); @@ -1671,7 +1675,7 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); + cmp_reg64_reg64(buf, RegisterWidth::W64, src1, src2); setle_reg64(buf, dst); } @@ -1682,7 +1686,7 @@ impl Assembler for X86_64Assembler { src1: X86_64GeneralReg, src2: X86_64GeneralReg, ) { - cmp_reg64_reg64(buf, src1, src2); + cmp_reg64_reg64(buf, RegisterWidth::W64, src1, src2); setge_reg64(buf, dst); } @@ -1847,6 +1851,50 @@ fn add_reg_extension(reg: T, byte: u8) -> u8 { } } +#[inline(always)] +fn binop_reg16_reg16( + op_code: u8, + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, +) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + let src_high = src as u8 > 7; + let src_mod = (src as u8 % 8) << 3; + + if dst_high || src_high { + let rex = add_rm_extension(dst, REX); + let rex = add_reg_extension(src, rex); + + buf.extend([0x66, rex, op_code, 0xC0 | dst_mod | src_mod]) + } else { + buf.extend([0x66, op_code, 0xC0 | dst_mod | src_mod]); + } +} + +#[inline(always)] +fn binop_reg32_reg32( + op_code: u8, + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, +) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + let src_high = src as u8 > 7; + let src_mod = (src as u8 % 8) << 3; + + if dst_high || src_high { + let rex = add_rm_extension(dst, REX); + let rex = add_reg_extension(src, rex); + + buf.extend([rex, op_code, 0xC0 | dst_mod | src_mod]) + } else { + buf.extend([op_code, 0xC0 | dst_mod | src_mod]); + } +} + #[inline(always)] fn binop_reg64_reg64( op_code: u8, @@ -2119,8 +2167,18 @@ fn cmp_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) { /// `CMP r/m64,r64` -> Compare r64 to r/m64. #[inline(always)] -fn cmp_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) { - binop_reg64_reg64(0x39, buf, dst, src); +fn cmp_reg64_reg64( + buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, +) { + match register_width { + RegisterWidth::W8 => binop_reg64_reg64(0x38, buf, dst, src), + RegisterWidth::W16 => binop_reg16_reg16(0x39, buf, dst, src), + RegisterWidth::W32 => binop_reg32_reg32(0x39, buf, dst, src), + RegisterWidth::W64 => binop_reg64_reg64(0x39, buf, dst, src), + } } #[inline(always)] @@ -2419,13 +2477,6 @@ fn mov_base8_offset32_reg8( buf.extend(offset.to_le_bytes()); } -enum RegisterWidth { - W8, - W16, - W32, - W64, -} - #[inline(always)] fn mov_reg_base_offset32( buf: &mut Vec<'_, u8>, @@ -3671,4 +3722,51 @@ mod tests { ALL_FLOAT_REGS ); } + + #[test] + fn test_int_cmp() { + disassembler_test!( + cmp_reg64_reg64, + |_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!( + "cmp {}, {}", + dst.low_8bits_string(), + src.low_8bits_string() + ), + [RegisterWidth::W8], + ALL_GENERAL_REGS, + ALL_GENERAL_REGS + ); + + disassembler_test!( + cmp_reg64_reg64, + |_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!( + "cmp {}, {}", + dbg!(dst.low_16bits_string()), + dbg!(src.low_16bits_string()) + ), + [RegisterWidth::W16], + ALL_GENERAL_REGS, + ALL_GENERAL_REGS + ); + + disassembler_test!( + cmp_reg64_reg64, + |_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!( + "cmp {}, {}", + dbg!(dst.low_32bits_string()), + dbg!(src.low_32bits_string()) + ), + [RegisterWidth::W32], + ALL_GENERAL_REGS, + ALL_GENERAL_REGS + ); + + disassembler_test!( + cmp_reg64_reg64, + |_, dst: X86_64GeneralReg, src: X86_64GeneralReg| format!("cmp {dst}, {src}",), + [RegisterWidth::W64], + ALL_GENERAL_REGS, + ALL_GENERAL_REGS + ); + } } diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index f70b1c7ecc..f4e844e01e 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -213,6 +213,7 @@ trait Backend<'a> { self.free_symbols(stmt); } Stmt::Jump(id, args) => { + self.load_literal_symbols(args); let mut arg_layouts: bumpalo::collections::Vec> = bumpalo::vec![in self.env().arena]; arg_layouts.reserve(args.len()); @@ -1130,6 +1131,9 @@ trait Backend<'a> { ret_layout: &InLayout<'a>, ); + /// Move a returned value into `dst` + fn move_return_value(&mut self, dst: &Symbol, ret_layout: &InLayout<'a>); + /// build_num_abs stores the absolute value of src into dst. fn build_num_abs(&mut self, dst: &Symbol, src: &Symbol, layout: &InLayout<'a>); diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index e1b8a374c7..791bcda754 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -4124,7 +4124,7 @@ fn int_let_generalization() { assert_evals_to!( indoc!( r#" - manyAux : {} -> I32 + manyAux : {} -> I32 manyAux = \_ -> output = \_ -> 42 @@ -4403,17 +4403,17 @@ fn layout_cache_structure_with_multiple_recursive_structures() { LinkedList : [Nil, Cons { first : Chain, rest : LinkedList }] main = - base : LinkedList + base : LinkedList base = Nil walker : LinkedList, Chain -> LinkedList - walker = \rest, first -> Cons { first, rest } + walker = \rest, first -> Cons { first, rest } list : List Chain list = [] r = List.walk list base walker - + if r == base then 11u8 else 22u8 "# ), @@ -4460,3 +4460,23 @@ fn reset_recursive_type_wraps_in_named_type() { RocStr ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn pass_lambda_set_to_function() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + instr = if Bool.true then Num.mul else Num.add + + fn = \a -> instr a a + + main = fn 3 + "# + ), + 3 * 3, + i64 + ); +} diff --git a/crates/compiler/test_gen/src/gen_str.rs b/crates/compiler/test_gen/src/gen_str.rs index dacf7feea6..40cedb9136 100644 --- a/crates/compiler/test_gen/src/gen_str.rs +++ b/crates/compiler/test_gen/src/gen_str.rs @@ -15,6 +15,40 @@ use indoc::indoc; #[allow(unused_imports)] use roc_std::{RocList, RocResult, RocStr}; +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn string_eq() { + // context: the dev backend did not correctly mask the boolean that zig returns here + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + main : I64 + main = if "*" == "*" then 123 else 456 + "# + ), + 123, + u64 + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn string_neq() { + // context: the dev backend did not correctly mask the boolean that zig returns here + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + main : I64 + main = if "*" != "*" then 123 else 456 + "# + ), + 456, + u64 + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_empty_delimiter() { diff --git a/crates/compiler/test_gen/src/gen_tags.rs b/crates/compiler/test_gen/src/gen_tags.rs index 5b9e6ee834..00b76b2107 100644 --- a/crates/compiler/test_gen/src/gen_tags.rs +++ b/crates/compiler/test_gen/src/gen_tags.rs @@ -11,7 +11,7 @@ use crate::helpers::wasm::assert_evals_to; use indoc::indoc; use roc_mono::layout::STLayoutInterner; -#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))] +#[cfg(test)] use roc_std::{RocList, RocStr, U128}; #[test] @@ -52,7 +52,7 @@ fn applied_tag_nothing() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn applied_tag_just() { assert_evals_to!( indoc!( @@ -93,24 +93,8 @@ fn applied_tag_just_enum() { ); } -// #[test] -// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// fn raw_result() { -// assert_evals_to!( -// indoc!( -// r#" -// x : Result I64 I64 -// x = Err 41 - -// x -// "# -// ), -// 0, -// i8 -// ); -// } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn true_is_true() { assert_evals_to!( indoc!( @@ -127,7 +111,7 @@ fn true_is_true() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn false_is_false() { assert_evals_to!( indoc!( @@ -144,7 +128,7 @@ fn false_is_false() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn basic_enum() { assert_evals_to!( indoc!( @@ -165,67 +149,8 @@ fn basic_enum() { ); } -// #[test] -// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// fn linked_list_empty() { -// assert_evals_to!( -// indoc!( -// r#" -// LinkedList a : [Cons a (LinkedList a), Nil] -// -// empty : LinkedList I64 -// empty = Nil -// -// 1 -// "# -// ), -// 1, -// i64 -// ); -// } -// -// #[test] -// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// fn linked_list_singleton() { -// assert_evals_to!( -// indoc!( -// r#" -// LinkedList a : [Cons a (LinkedList a), Nil] -// -// singleton : LinkedList I64 -// singleton = Cons 0x1 Nil -// -// 1 -// "# -// ), -// 1, -// i64 -// ); -// } -// -// #[test] -// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// fn linked_list_is_empty() { -// assert_evals_to!( -// indoc!( -// r#" -// LinkedList a : [Cons a (LinkedList a), Nil] -// -// isEmpty : LinkedList a -> Bool -// isEmpty = \list -> -// when list is -// Nil -> Bool.true -// Cons _ _ -> Bool.false -// -// isEmpty (Cons 4 Nil) -// "# -// ), -// false, -// bool -// ); -// } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn even_odd() { assert_evals_to!( indoc!( @@ -251,7 +176,7 @@ fn even_odd() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn gen_literal_true() { assert_evals_to!( indoc!( @@ -265,7 +190,7 @@ fn gen_literal_true() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn gen_if_float() { assert_evals_to!( indoc!( @@ -480,7 +405,7 @@ fn nested_pattern_match() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_vanilla() { assert_evals_to!( indoc!( @@ -496,7 +421,7 @@ fn if_guard_vanilla() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn when_on_single_value_tag() { assert_evals_to!( indoc!( @@ -512,7 +437,7 @@ fn when_on_single_value_tag() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_multiple() { assert_evals_to!( indoc!( @@ -533,7 +458,7 @@ fn if_guard_multiple() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_constructor_switch() { assert_evals_to!( indoc!( @@ -577,7 +502,7 @@ fn if_guard_constructor_switch() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_constructor_chain() { assert_evals_to!( indoc!( @@ -594,7 +519,7 @@ fn if_guard_constructor_chain() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_pattern_false() { assert_evals_to!( indoc!( @@ -613,7 +538,7 @@ fn if_guard_pattern_false() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_switch() { assert_evals_to!( indoc!( @@ -632,7 +557,7 @@ fn if_guard_switch() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_pattern_true() { assert_evals_to!( indoc!( @@ -651,7 +576,7 @@ fn if_guard_pattern_true() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn if_guard_exhaustiveness() { assert_evals_to!( indoc!( @@ -670,7 +595,7 @@ fn if_guard_exhaustiveness() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn when_on_enum() { assert_evals_to!( indoc!( @@ -692,7 +617,7 @@ fn when_on_enum() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn pattern_matching_unit() { assert_evals_to!( indoc!( @@ -751,7 +676,7 @@ fn pattern_matching_unit() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn one_element_tag() { assert_evals_to!( indoc!( @@ -768,7 +693,7 @@ fn one_element_tag() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn nested_tag_union() { assert_evals_to!( indoc!( @@ -789,7 +714,7 @@ fn nested_tag_union() { ); } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn unit_type() { assert_evals_to!( indoc!( @@ -808,7 +733,7 @@ fn unit_type() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn join_point_if() { assert_evals_to!( indoc!( @@ -825,7 +750,7 @@ fn join_point_if() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn join_point_when() { assert_evals_to!( indoc!( @@ -851,7 +776,7 @@ fn join_point_when() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn join_point_with_cond_expr() { assert_evals_to!( indoc!( @@ -890,7 +815,7 @@ fn join_point_with_cond_expr() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_in_single_tag_construction() { assert_evals_to!(indoc!("Three (1 == 1) 32"), (32i64, true), (i64, bool)); @@ -902,7 +827,7 @@ fn alignment_in_single_tag_construction() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_in_single_tag_pattern_match() { assert_evals_to!( indoc!( @@ -934,7 +859,7 @@ fn alignment_in_single_tag_pattern_match() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_in_multi_tag_construction_two() { assert_evals_to!( indoc!( @@ -952,7 +877,7 @@ fn alignment_in_multi_tag_construction_two() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_in_multi_tag_construction_three() { assert_evals_to!( indoc!( @@ -969,7 +894,7 @@ fn alignment_in_multi_tag_construction_three() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_in_multi_tag_pattern_match() { assert_evals_to!( indoc!( @@ -1056,7 +981,7 @@ fn phantom_polymorphic_record() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn result_never() { assert_evals_to!( indoc!( @@ -1232,7 +1157,7 @@ fn recursive_tag_union_into_flat_tag_union() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn monomorphized_tag() { assert_evals_to!( indoc!( @@ -1249,7 +1174,7 @@ fn monomorphized_tag() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn monomorphized_applied_tag() { assert_evals_to!( indoc!( @@ -1337,7 +1262,7 @@ fn monomorphized_tag_with_polymorphic_arg_and_monomorphic_arg() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_2365_monomorphize_tag_with_non_empty_ext_var() { assert_evals_to!( indoc!( @@ -1362,7 +1287,7 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() { assert_evals_to!( indoc!( @@ -1391,7 +1316,7 @@ fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_2365_monomorphize_tag_with_non_empty_ext_var_wrapped_nested() { assert_evals_to!( indoc!( @@ -1526,7 +1451,7 @@ fn issue_1162() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn polymorphic_tag() { assert_evals_to!( indoc!( @@ -1542,7 +1467,7 @@ fn polymorphic_tag() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_2725_alias_polymorphic_lambda() { assert_evals_to!( indoc!( @@ -1583,7 +1508,7 @@ fn opaque_assign_to_symbol() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_2777_default_branch_codegen() { assert_evals_to!( indoc!( @@ -1812,7 +1737,7 @@ fn issue_3560_nested_tag_constructor_is_record_newtype() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn issue_3560_newtype_tag_constructor_has_nested_constructor_with_no_payload() { assert_evals_to!( indoc!( @@ -1828,7 +1753,7 @@ fn issue_3560_newtype_tag_constructor_has_nested_constructor_with_no_payload() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn alignment_i128() { assert_evals_to!( indoc!( @@ -1939,7 +1864,7 @@ fn issue_2165_recursive_tag_destructure() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn tag_union_let_generalization() { assert_evals_to!( indoc!( @@ -1988,7 +1913,7 @@ fn fit_recursive_union_in_struct_into_recursive_pointer() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn match_on_result_with_uninhabited_error_branch() { assert_evals_to!( indoc!( @@ -2085,7 +2010,7 @@ fn unify_types_with_fixed_fixpoints_outside_fixing_region() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn lambda_set_with_imported_toplevels_issue_4733() { assert_evals_to!( indoc!(