diff --git a/crates/compiler/builtins/bitcode/src/dec.zig b/crates/compiler/builtins/bitcode/src/dec.zig index e2be7dbe56..e74e361074 100644 --- a/crates/compiler/builtins/bitcode/src/dec.zig +++ b/crates/compiler/builtins/bitcode/src/dec.zig @@ -1125,6 +1125,23 @@ pub fn fromF64C(arg: f64) callconv(.C) i128 { return if (@call(.{ .modifier = always_inline }, RocDec.fromF64, .{arg})) |dec| dec.num else @panic("TODO runtime exception failing convert f64 to RocDec"); } +pub fn exportFromInt(comptime T: type, comptime name: []const u8) void { + comptime var f = struct { + fn func(self: T) callconv(.C) i128 { + const this = @intCast(i128, self); + + var result: i128 = undefined; + + if (@mulWithOverflow(i128, this, RocDec.one_point_zero_i128, &result)) { + @panic("TODO runtime exception failing convert integer to RocDec"); + } else { + return result; + } + } + }.func; + @export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong }); +} + pub fn fromU64C(arg: u64) callconv(.C) i128 { return @call(.{ .modifier = always_inline }, RocDec.fromU64, .{arg}).toI128(); } diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 4d3b8a342e..a93e60314e 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -39,6 +39,10 @@ comptime { exportDecFn(dec.mulC, "mul_with_overflow"); exportDecFn(dec.mulOrPanicC, "mul_or_panic"); exportDecFn(dec.mulSaturatedC, "mul_saturated"); + + inline for (INTEGERS) |T| { + dec.exportFromInt(T, ROC_BUILTINS ++ ".dec.from_int."); + } } // List Module diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index ca356053e6..a0cf671d33 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -390,6 +390,7 @@ pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str"; pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str"; pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64"; pub const DEC_FROM_U64: &str = "roc_builtins.dec.from_u64"; +pub const DEC_FROM_INT: IntrinsicName = int_intrinsic!("roc_builtins.dec.from_int"); pub const DEC_TO_I128: &str = "roc_builtins.dec.to_i128"; pub const DEC_EQ: &str = "roc_builtins.dec.eq"; pub const DEC_NEQ: &str = "roc_builtins.dec.neq"; diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 918302e383..607a41b02c 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -1560,7 +1560,7 @@ impl< .load_to_general_reg(&mut self.buf, src2); ASM::eq_reg_reg_reg(&mut self.buf, width, dst_reg, src1_reg, src2_reg); } - LayoutRepr::U128 | LayoutRepr::I128 => { + LayoutRepr::U128 | LayoutRepr::I128 | LayoutRepr::DEC => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); // put the arguments on the stack @@ -1611,7 +1611,6 @@ impl< ASM::eq_freg_freg_reg64(&mut self.buf, dst_reg, src_reg1, src_reg2, float_width) } - LayoutRepr::DEC => todo!("NumEq: layout, {:?}", self.layout_interner.dbg(Layout::DEC)), LayoutRepr::STR => { // use a zig call self.build_fn_call( @@ -1753,54 +1752,12 @@ impl< arg_layout: &InLayout<'a>, ret_layout: &InLayout<'a>, ) { - let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst); - match ( - self.layout_interner.get_repr(*arg_layout), - self.layout_interner.get_repr(*ret_layout), - ) { - ( - LayoutRepr::Builtin(Builtin::Int(IntWidth::I32 | IntWidth::I64)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)), - ) => { - let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); - ASM::to_float_freg64_reg64(&mut self.buf, dst_reg, src_reg); - } - ( - LayoutRepr::Builtin(Builtin::Int(IntWidth::I32 | IntWidth::I64)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)), - ) => { - let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); - ASM::to_float_freg32_reg64(&mut self.buf, dst_reg, src_reg); - } - ( - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)), - ) => { - let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); - ASM::to_float_freg32_freg64(&mut self.buf, dst_reg, src_reg); - } - ( - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)), - ) => { - let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); - ASM::to_float_freg64_freg32(&mut self.buf, dst_reg, src_reg); - } - ( - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)), - ) => { - let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); - ASM::mov_freg64_freg64(&mut self.buf, dst_reg, src_reg); - } - ( - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)), - LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)), - ) => { - let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); - ASM::mov_freg64_freg64(&mut self.buf, dst_reg, src_reg); - } - (a, r) => todo!("NumToFrac: layout, arg {:?}, ret {:?}", a, r), + match *ret_layout { + Layout::F32 => self.num_to_f32(dst, src, arg_layout), + Layout::F64 => self.num_to_f64(dst, src, arg_layout), + Layout::DEC => self.num_to_dec(dst, src, arg_layout), + + other => todo!("NumToFrac: layout {other:?} is not Frac"), } } @@ -4048,6 +4005,84 @@ impl< (unmasked_symbol, unmasked_reg) } + fn num_to_f32(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>) { + let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst); + match self.layout_interner.get_repr(*arg_layout) { + LayoutRepr::Builtin(Builtin::Int(IntWidth::I32 | IntWidth::I64)) => { + let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); + ASM::to_float_freg32_reg64(&mut self.buf, dst_reg, src_reg); + } + LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)) => { + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::to_float_freg32_freg64(&mut self.buf, dst_reg, src_reg); + } + LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)) => { + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::mov_freg64_freg64(&mut self.buf, dst_reg, src_reg); + } + arg => todo!("NumToFrac: layout, arg {arg:?}, ret {:?}", Layout::F32), + } + } + + fn num_to_f64(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>) { + let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst); + match self.layout_interner.get_repr(*arg_layout) { + LayoutRepr::Builtin(Builtin::Int(IntWidth::I32 | IntWidth::I64)) => { + let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); + ASM::to_float_freg64_reg64(&mut self.buf, dst_reg, src_reg); + } + LayoutRepr::Builtin(Builtin::Float(FloatWidth::F32)) => { + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::to_float_freg64_freg32(&mut self.buf, dst_reg, src_reg); + } + LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)) => { + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::mov_freg64_freg64(&mut self.buf, dst_reg, src_reg); + } + arg => todo!("NumToFrac: layout, arg {arg:?}, ret {:?}", Layout::F64), + } + } + + fn num_to_dec(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>) { + match self.layout_interner.get_repr(*arg_layout) { + LayoutRepr::Builtin(Builtin::Int(int_width)) => { + self.build_fn_call( + dst, + bitcode::DEC_FROM_INT[int_width].to_string(), + &[*src], + &[*arg_layout], + &Layout::DEC, + ); + } + + arg => todo!("NumToFrac: layout, arg {arg:?}, ret {:?}", Layout::DEC), + } + } + + fn compare_128bit( + &mut self, + op: CompareOperation, + dst: &Symbol, + src1: &Symbol, + src2: &Symbol, + width: IntWidth, + ) { + let intrinsic = match op { + CompareOperation::LessThan => &bitcode::NUM_LESS_THAN[width], + CompareOperation::LessThanOrEqual => &bitcode::NUM_LESS_THAN_OR_EQUAL[width], + CompareOperation::GreaterThan => &bitcode::NUM_GREATER_THAN[width], + CompareOperation::GreaterThanOrEqual => &bitcode::NUM_GREATER_THAN_OR_EQUAL[width], + }; + + self.build_fn_call( + &dst, + intrinsic.to_string(), + &[*src1, *src2], + &[Layout::U128, Layout::U128], + &Layout::U8, + ); + } + fn compare( &mut self, op: CompareOperation, @@ -4099,6 +4134,12 @@ impl< op, ); } + LayoutRepr::Builtin(Builtin::Int(width @ (IntWidth::I128 | IntWidth::U128))) => { + self.compare_128bit(op, dst, src1, src2, width); + } + LayoutRepr::Builtin(Builtin::Decimal) => { + self.compare_128bit(op, dst, src1, src2, IntWidth::I128); + } x => todo!("NumLt: layout, {:?}", x), } } diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index a5d969f1d7..cc99cfe8bb 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -681,7 +681,7 @@ impl X64_64SystemVStoreArgs { single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), pointer_layouts!() => self.store_arg_general(buf, storage_manager, sym), single_register_floats!() => self.store_arg_float(buf, storage_manager, sym), - LayoutRepr::I128 | LayoutRepr::U128 => { + LayoutRepr::I128 | LayoutRepr::U128 | LayoutRepr::DEC => { let (offset, _) = storage_manager.stack_offset_and_size(&sym); if self.general_i + 1 < Self::GENERAL_PARAM_REGS.len() { @@ -1004,6 +1004,10 @@ impl X64_64SystemVLoadArgs { storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); self.argument_offset += stack_size as i32; } + LayoutRepr::Builtin(Builtin::Decimal) => { + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } LayoutRepr::Union(UnionLayout::NonRecursive(_)) => { // for now, just also store this on the stack storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index efd734864f..b6e8845793 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -1255,7 +1255,7 @@ trait Backend<'a> { ); debug_assert!( - matches!(*ret_layout, Layout::F32 | Layout::F64), + matches!(*ret_layout, Layout::F32 | Layout::F64 | Layout::DEC), "NumToFrac: expected to have return layout of type Float" ); self.build_num_to_frac(sym, &args[0], &arg_layouts[0], ret_layout) diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index 3d13f4c439..34b4656f35 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -2237,7 +2237,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>( ) } LayoutRepr::Builtin(Builtin::Decimal) => { - call_bitcode_fn(env, &[arg.into()], &bitcode::DEC_FROM_U64) + call_bitcode_fn(env, &[arg.into()], &bitcode::DEC_FROM_INT[arg_width]) } _ => internal_error!("There can only be floats here!"), }