diff --git a/compiler/builtins/bitcode/src/dec.zig b/compiler/builtins/bitcode/src/dec.zig index 6160e9276c..464bc90dbe 100644 --- a/compiler/builtins/bitcode/src/dec.zig +++ b/compiler/builtins/bitcode/src/dec.zig @@ -23,6 +23,23 @@ pub const RocDec = extern struct { return .{ .num = num * one_point_zero_i128 }; } + // TODO: There's got to be a better way to do this other than converting to Str + pub fn fromF64(num: f64) RocDec { + var digit_bytes: [19]u8 = undefined; // Max f64 digits + '.' + '-' + + var fbs = std.io.fixedBufferStream(digit_bytes[0..]); + std.fmt.formatFloatDecimal(num, .{}, fbs.writer()) catch + @panic("TODO runtime exception failing to print float!"); + + var dec = RocDec.fromStr(RocStr.init(&digit_bytes, fbs.pos)); + + if (dec) |d| { + return d; + } else { + @panic("TODO runtime exception failing convert f64 to RocDec"); + } + } + pub fn fromStr(roc_str: RocStr) ?RocDec { if (roc_str.isEmpty()) { return null; @@ -58,7 +75,7 @@ pub const RocDec = extern struct { var after_str_len = (length - 1) - pi; if (after_str_len > decimal_places) { - std.debug.panic("TODO runtime exception for too many decimal places!", .{}); + @panic("TODO runtime exception for too many decimal places!"); } var diff_decimal_places = decimal_places - after_str_len; @@ -75,7 +92,7 @@ pub const RocDec = extern struct { var result: i128 = undefined; var overflowed = @mulWithOverflow(i128, before, one_point_zero_i128, &result); if (overflowed) { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } before_val_i128 = result; } @@ -86,7 +103,7 @@ pub const RocDec = extern struct { var result: i128 = undefined; var overflowed = @addWithOverflow(i128, before, after, &result); if (overflowed) { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } dec = .{ .num = result }; } else { @@ -119,7 +136,7 @@ pub const RocDec = extern struct { // We will handle adding the '-' later const is_negative = self.num < 0; const num = if (is_negative) std.math.negate(self.num) catch { - std.debug.panic("TODO runtime exception failing to negate", .{}); + @panic("TODO runtime exception failing to negate"); } else self.num; // Format the backing i128 into an array of digits (u8s) @@ -135,7 +152,7 @@ pub const RocDec = extern struct { before_digits_slice = digit_bytes[0..before_digits_offset]; } else { before_digits_adjust = @intCast(u6, math.absInt(@intCast(i7, num_digits) - decimal_places) catch { - std.debug.panic("TODO runtime exception for overflow when getting abs", .{}); + @panic("TODO runtime exception for overflow when getting abs"); }); before_digits_slice = "0"; } @@ -185,7 +202,7 @@ pub const RocDec = extern struct { // Ideally, we'd use str_len here var str_bytes: [max_digits + 2]u8 = undefined; _ = std.fmt.bufPrint(str_bytes[0..str_len], "{s}{s}.{s}{s}", .{ sign_slice, before_digits_slice, after_zeros_slice, after_digits_slice }) catch { - std.debug.panic("TODO runtime exception failing to print slices", .{}); + @panic("TODO runtime exception failing to print slices"); }; return RocStr.init(&str_bytes, str_len); @@ -203,7 +220,7 @@ pub const RocDec = extern struct { if (!overflowed) { return RocDec{ .num = answer }; } else { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } } @@ -214,7 +231,7 @@ pub const RocDec = extern struct { if (!overflowed) { return RocDec{ .num = answer }; } else { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } } @@ -231,7 +248,7 @@ pub const RocDec = extern struct { } else if (other_i128 == RocDec.one_point_zero.num) { return self; } else { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } }); @@ -241,7 +258,7 @@ pub const RocDec = extern struct { } else if (self_i128 == RocDec.one_point_zero.num) { return other; } else { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } }); @@ -265,7 +282,7 @@ pub const RocDec = extern struct { // (n / 0) is an error if (denominator_i128 == 0) { - std.debug.panic("TODO runtime exception for divide by 0!", .{}); + @panic("TODO runtime exception for divide by 0!"); } // If they're both negative, or if neither is negative, the final answer @@ -293,7 +310,7 @@ pub const RocDec = extern struct { if (denominator_i128 == one_point_zero_i128) { return self; } else { - std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + @panic("TODO runtime exception for overflow when dividing!"); } }; const numerator_u128 = @intCast(u128, numerator_abs_i128); @@ -306,7 +323,7 @@ pub const RocDec = extern struct { if (numerator_i128 == one_point_zero_i128) { return other; } else { - std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + @panic("TODO runtime exception for overflow when dividing!"); } }; const denominator_u128 = @intCast(u128, denominator_abs_i128); @@ -318,7 +335,7 @@ pub const RocDec = extern struct { if (answer.hi == 0 and answer.lo <= math.maxInt(i128)) { unsigned_answer = @intCast(i128, answer.lo); } else { - std.debug.panic("TODO runtime exception for overflow when dividing!", .{}); + @panic("TODO runtime exception for overflow when dividing!"); } return RocDec{ .num = if (is_answer_negative) -unsigned_answer else unsigned_answer }; @@ -438,7 +455,7 @@ fn mul_and_decimalize(a: u128, b: u128) i128 { overflowed = overflowed or @addWithOverflow(u128, d, c_carry4, &d); if (overflowed) { - std.debug.panic("TODO runtime exception for overflow!", .{}); + @panic("TODO runtime exception for overflow!"); } // Final 512bit value is d, c, b, a @@ -653,6 +670,11 @@ test "fromU64" { try expectEqual(RocDec{ .num = 25000000000000000000 }, dec); } +test "fromF64" { + var dec = RocDec.fromF64(25.5); + try expectEqual(RocDec{ .num = 25500000000000000000 }, dec); +} + test "fromStr: empty" { var roc_str = RocStr.init("", 0); var dec = RocDec.fromStr(roc_str); @@ -957,8 +979,10 @@ test "div: 10 / 3" { // exports -const FromStrResult = extern struct { dec: RocDec, is_ok: bool }; - -pub fn fromStrC(arg: RocStr, output: *FromStrResult) callconv(.C) void { - output.* = if (@call(.{ .modifier = always_inline }, RocDec.fromStr, .{arg})) |dec| .{ .dec = dec, .is_ok = true } else .{ .dec = RocDec.one_point_zero, .is_ok = false }; +pub fn fromF64(arg: f64) callconv(.C) i128 { + return @call(.{ .modifier = always_inline }, RocDec.fromF64, .{arg}).num; +} + +pub fn add(arg1: RocDec, arg2: RocDec) callconv(.C) i128 { + return @call(.{ .modifier = always_inline }, RocDec.add, .{ arg1, arg2 }).num; } diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 56524f3f3f..a24b926e0a 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -6,7 +6,8 @@ const testing = std.testing; const dec = @import("dec.zig"); comptime { - // exportDecFn(dec.fromStrC, "from_str"); + exportDecFn(dec.fromF64, "from_f64"); + exportDecFn(dec.add, "add"); } // List Module @@ -112,6 +113,11 @@ fn exportDecFn(comptime func: anytype, comptime func_name: []const u8) void { exportBuiltinFn(func, "dec." ++ func_name); } +pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn { + std.debug.print("{s}", .{message}); + unreachable; +} + // Run all tests in imported modules // https://github.com/ziglang/zig/blob/master/lib/std/std.zig#L94 test "" { diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 3e5d71ebe6..0ad2c317e7 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -65,3 +65,7 @@ pub const LIST_REVERSE: &str = "roc_builtins.list.reverse"; pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with"; pub const LIST_CONCAT: &str = "roc_builtins.list.concat"; pub const LIST_SET: &str = "roc_builtins.list.set"; + +pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64"; +pub const DEC_ADD: &str = "roc_builtins.dec.add"; +pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str"; diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 0b102b723f..d06f42434f 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -362,7 +362,6 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { // https://releases.llvm.org/10.0.0/docs/LangRef.html#standard-c-library-intrinsics let i1_type = ctx.bool_type(); let f64_type = ctx.f64_type(); - let i128_type = ctx.i128_type(); let i64_type = ctx.i64_type(); let i32_type = ctx.i32_type(); let i16_type = ctx.i16_type(); @@ -442,11 +441,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { .fn_type(&[i64_type.into(), i64_type.into()], false) }); - add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I128, { - let fields = [i128_type.into(), i1_type.into()]; - ctx.struct_type(&fields, false) - .fn_type(&[i128_type.into(), i128_type.into()], false) - }); + // TODO: This is now exported by zig. Not sure what to do here? + // add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I128, { + // let fields = [i128_type.into(), i1_type.into()]; + // ctx.struct_type(&fields, false) + // .fn_type(&[i128_type.into(), i128_type.into()], false) + // }); // sub with overflow @@ -474,11 +474,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { .fn_type(&[i64_type.into(), i64_type.into()], false) }); - add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I128, { - let fields = [i128_type.into(), i1_type.into()]; - ctx.struct_type(&fields, false) - .fn_type(&[i128_type.into(), i128_type.into()], false) - }); + // TODO: This is now exported by zig. Not sure what to do here? + // add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I128, { + // let fields = [i128_type.into(), i1_type.into()]; + // ctx.struct_type(&fields, false) + // .fn_type(&[i128_type.into(), i128_type.into()], false) + // }); } static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64"; @@ -637,10 +638,15 @@ pub fn float_with_precision<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, value: f64, precision: &Builtin, -) -> FloatValue<'ctx> { +) -> BasicValueEnum<'ctx> { match precision { - Builtin::Float64 => env.context.f64_type().const_float(value), - Builtin::Float32 => env.context.f32_type().const_float(value), + Builtin::Decimal => call_bitcode_fn( + env, + &[env.context.f64_type().const_float(value).into()], + &bitcode::DEC_FROM_F64, + ), + Builtin::Float64 => env.context.f64_type().const_float(value).into(), + Builtin::Float32 => env.context.f32_type().const_float(value).into(), _ => panic!("Invalid layout for float literal = {:?}", precision), } } @@ -659,7 +665,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>( }, Float(float) => match layout { - Layout::Builtin(builtin) => float_with_precision(env, *float, builtin).into(), + Layout::Builtin(builtin) => float_with_precision(env, *float, builtin), _ => panic!("Invalid layout for float literal = {:?}", layout), }, @@ -5339,6 +5345,9 @@ pub fn build_num_binop<'a, 'ctx, 'env>( rhs_layout, op, ), + Decimal => { + build_dec_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op) + } _ => { unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, lhs_layout); } @@ -5525,6 +5534,23 @@ fn build_float_binop<'a, 'ctx, 'env>( } } +fn build_dec_binop<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + _parent: FunctionValue<'ctx>, + lhs: BasicValueEnum<'ctx>, + _lhs_layout: &Layout<'a>, + rhs: BasicValueEnum<'ctx>, + _rhs_layout: &Layout<'a>, + op: LowLevel, +) -> BasicValueEnum<'ctx> { + use roc_module::low_level::LowLevel::*; + + match op { + NumAdd => call_bitcode_fn(env, &[lhs, rhs], &bitcode::DEC_ADD), + _ => panic!("TODO: Add RocDec function for op"), + } +} + fn int_type_signed_min(int_type: IntType) -> IntValue { let width = int_type.get_bit_width(); diff --git a/compiler/gen_llvm/src/llvm/convert.rs b/compiler/gen_llvm/src/llvm/convert.rs index 1059ad9376..28fc05979d 100644 --- a/compiler/gen_llvm/src/llvm/convert.rs +++ b/compiler/gen_llvm/src/llvm/convert.rs @@ -82,7 +82,7 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>( Int8 => context.i8_type().as_basic_type_enum(), Int1 => context.bool_type().as_basic_type_enum(), Usize => ptr_int(context, ptr_bytes).as_basic_type_enum(), - Decimal => zig_dec_type(env).into(), + Decimal => context.i128_type().as_basic_type_enum(), Float128 => context.f128_type().as_basic_type_enum(), Float64 => context.f64_type().as_basic_type_enum(), Float32 => context.f32_type().as_basic_type_enum(), @@ -182,8 +182,8 @@ pub fn zig_str_type<'a, 'ctx, 'env>( env.module.get_struct_type("str.RocStr").unwrap() } -pub fn zig_dec_type<'a, 'ctx, 'env>( - env: &crate::llvm::build::Env<'a, 'ctx, 'env>, -) -> StructType<'ctx> { - env.module.get_struct_type("dec.RocDec").unwrap() -} +// pub fn zig_dec_type<'a, 'ctx, 'env>( +// env: &crate::llvm::build::Env<'a, 'ctx, 'env>, +// ) -> StructType<'ctx> { +// env.module.get_struct_type("dec.RocDec").unwrap() +// } diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 1b02893cf4..d4a90bef7a 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -4,7 +4,6 @@ use bumpalo::Bump; use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; -use roc_std::RocDec; use roc_types::subs::{Content, FlatType, Subs, Variable}; use roc_types::types::RecordField; use std::collections::HashMap; @@ -870,7 +869,7 @@ impl<'a> Builtin<'a> { const I8_SIZE: u32 = std::mem::size_of::() as u32; const I1_SIZE: u32 = std::mem::size_of::() as u32; const USIZE_SIZE: u32 = std::mem::size_of::() as u32; - const DECIMAL_SIZE: u32 = std::mem::size_of::() as u32; // TODO: Is this right? + const DECIMAL_SIZE: u32 = std::mem::size_of::() as u32; const F128_SIZE: u32 = 16; const F64_SIZE: u32 = std::mem::size_of::() as u32; const F32_SIZE: u32 = std::mem::size_of::() as u32; @@ -927,7 +926,7 @@ impl<'a> Builtin<'a> { Int8 => align_of::() as u32, Int1 => align_of::() as u32, Usize => align_of::() as u32, - Decimal => align_of::() as u32, // TODO: Is this right? + Decimal => align_of::() as u32, Float128 => align_of::() as u32, Float64 => align_of::() as u32, Float32 => align_of::() as u32, diff --git a/compiler/test_gen/src/gen_num.rs b/compiler/test_gen/src/gen_num.rs index 88940c608e..ddb7059391 100644 --- a/compiler/test_gen/src/gen_num.rs +++ b/compiler/test_gen/src/gen_num.rs @@ -3,7 +3,7 @@ mod gen_num { use crate::assert_evals_to; use crate::assert_llvm_evals_to; use indoc::indoc; - use roc_std::{RocDec, RocOrder}; + use roc_std::RocOrder; #[test] fn nat_alias() { @@ -334,10 +334,10 @@ mod gen_num { indoc!( r#" x : Dec - x = 3.6 + x = 2.1 y : Dec - y = 3.4 + y = 3.1 z : Dec z = x + y @@ -345,10 +345,8 @@ mod gen_num { z "# ), - RocDec { - num: 7000000000000000000 - }, - RocDec + 5200000000000000000, + i128 ); } #[test] diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index 0ef3049c43..b54fd6c7b9 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -39,26 +39,6 @@ pub enum RocOrder { // ); //} -#[repr(C)] -pub struct RocDec { - pub num: i128, -} - -impl fmt::Debug for RocDec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // RocList { num: 123 } - f.debug_struct("RocDec").field("num", &self.num).finish() - } -} - -impl PartialEq for RocDec { - fn eq(&self, other: &Self) -> bool { - self.num == other.num - } -} - -impl Eq for RocDec {} - #[repr(C)] pub struct RocList { elements: *mut T,