From fb4a796e07484e943514a754a3d032ede5c0ba1d Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 21 Sep 2020 23:38:10 +0200 Subject: [PATCH] add different variants of addition --- cli/src/lib.rs | 6 ++ compiler/builtins/bitcode/src/lib.rs | 9 +++ compiler/builtins/src/std.rs | 6 +- compiler/builtins/src/unique.rs | 6 +- compiler/can/src/builtins.rs | 4 +- compiler/gen/src/llvm/build.rs | 102 ++++++++++++++++++--------- compiler/gen/tests/gen_num.rs | 53 +++++++++++--- compiler/module/src/low_level.rs | 1 + compiler/mono/src/borrow.rs | 2 +- 9 files changed, 136 insertions(+), 53 deletions(-) diff --git a/cli/src/lib.rs b/cli/src/lib.rs index ce97bc90f3..b84b6ed76a 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -210,6 +210,12 @@ fn build_file( "host.rs", "-o", binary_path.as_path().to_str().unwrap(), + // ensure we don't make a position-independent executable + "-C", + "link-arg=-no-pie", + // explicitly link in the c++ stdlib, for exceptions + "-C", + "link-arg=-lc++", ]) .current_dir(cwd) .spawn() diff --git a/compiler/builtins/bitcode/src/lib.rs b/compiler/builtins/bitcode/src/lib.rs index 2316d1265b..7e9031bfef 100644 --- a/compiler/builtins/bitcode/src/lib.rs +++ b/compiler/builtins/bitcode/src/lib.rs @@ -36,3 +36,12 @@ pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 { acc } + +/// Adapted from Rust's core::num module, by the Rust core team, +/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0 +/// +/// Thank you, Rust core team! +#[no_mangle] +pub fn is_finite_(num: f64) -> bool { + f64::is_finite(num) +} diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index acdae6f60a..1c481a0244 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -188,8 +188,8 @@ pub fn types() -> MutMap { ); // addChecked or (+) : Num a, Num a -> Result (Num a) [ IntOverflow ]* - let int_overflow = SolvedType::TagUnion( - vec![(TagName::Global("IntOverflow".into()), vec![])], + let overflow = SolvedType::TagUnion( + vec![(TagName::Global("Overflow".into()), vec![])], Box::new(SolvedType::Wildcard), ); @@ -197,7 +197,7 @@ pub fn types() -> MutMap { Symbol::NUM_ADD_CHECKED, SolvedType::Func( vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], - Box::new(result_type(num_type(flex(TVAR1)), int_overflow)), + Box::new(result_type(num_type(flex(TVAR1)), overflow)), ), ); diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index a5dfb09b6f..e175ee6bf7 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -275,8 +275,8 @@ pub fn types() -> MutMap { }); // addChecked or (+) : Num a, Num a -> Result (Num a) [ IntOverflow ]* - let int_overflow = SolvedType::TagUnion( - vec![(TagName::Global("IntOverflow".into()), vec![])], + let overflow = SolvedType::TagUnion( + vec![(TagName::Global("Overflow".into()), vec![])], Box::new(SolvedType::Wildcard), ); @@ -284,7 +284,7 @@ pub fn types() -> MutMap { let_tvars! { u, v, w, num, result, star }; unique_function( vec![num_type(u, num), num_type(v, num)], - result_type(result, num_type(w, num), lift(star, int_overflow)), + result_type(result, num_type(w, num), lift(star, overflow)), ) }); diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index f5aa06ef8d..d8fba0d7ba 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -258,7 +258,7 @@ fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def { // // if arg_3.b then // # overflow - // Err IntOverflow + // Err Overflow // else // # all is well // Ok arg_3.a @@ -281,7 +281,7 @@ fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def { // overflow! no_region(tag( "Err", - vec![tag("DivByZero", Vec::new(), var_store)], + vec![tag("Overflow", Vec::new(), var_store)], var_store, )), )], diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 5b8156a6c9..8386d5b1cc 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -990,8 +990,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( } } - // TODO verify that this is required (better safe than sorry) if filler > 0 { + // TODO verify that this is required (better safe than sorry) field_types.push(env.context.i8_type().array_type(filler).into()); } @@ -1381,15 +1381,6 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( builder.build_conditional_branch(value, then_block, else_block); - // build then block - builder.position_at_end(then_block); - let then_val = build_exp_stmt(env, layout_ids, scope, parent, pass_stmt); - if then_block.get_terminator().is_none() { - builder.build_unconditional_branch(cont_block); - let then_block = builder.get_insert_block().unwrap(); - blocks.push((&then_val, then_block)); - } - // build else block builder.position_at_end(else_block); let else_val = build_exp_stmt(env, layout_ids, scope, parent, fail_stmt); @@ -1399,6 +1390,15 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( blocks.push((&else_val, else_block)); } + // build then block + builder.position_at_end(then_block); + let then_val = build_exp_stmt(env, layout_ids, scope, parent, pass_stmt); + if then_block.get_terminator().is_none() { + builder.build_unconditional_branch(cont_block); + let then_block = builder.get_insert_block().unwrap(); + blocks.push((&then_val, then_block)); + } + // emit merge block if blocks.is_empty() { // SAFETY there are no other references to this block in this case @@ -2115,7 +2115,7 @@ fn run_low_level<'a, 'ctx, 'env>( list_join(env, inplace, parent, list, outer_list_layout) } NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor - | NumToFloat => { + | NumToFloat | NumIsFinite => { debug_assert_eq!(args.len(), 1); let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]); @@ -2250,6 +2250,7 @@ fn run_low_level<'a, 'ctx, 'env>( ), Float128 | Float64 | Float32 | Float16 => build_float_binop( env, + parent, lhs_arg.into_float_value(), lhs_layout, rhs_arg.into_float_value(), @@ -2504,6 +2505,7 @@ fn call_bitcode_fn<'a, 'ctx, 'env>( fn build_float_binop<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + parent: FunctionValue<'ctx>, lhs: FloatValue<'ctx>, _lhs_layout: &Layout<'a>, rhs: FloatValue<'ctx>, @@ -2516,9 +2518,60 @@ fn build_float_binop<'a, 'ctx, 'env>( let bd = env.builder; match op { - NumAdd => bd.build_float_add(lhs, rhs, "add_float").into(), + NumAdd => { + let builder = env.builder; + let context = env.context; + + let result = bd.build_float_add(lhs, rhs, "add_float"); + + let is_finite = + call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value(); + + let then_block = context.append_basic_block(parent, "then_block"); + let throw_block = context.append_basic_block(parent, "throw_block"); + + builder.build_conditional_branch(is_finite, then_block, throw_block); + + builder.position_at_end(throw_block); + + throw_exception(env, "float addition overflowed!"); + + builder.position_at_end(then_block); + + result.into() + } + NumAddChecked => { + let builder = env.builder; + let context = env.context; + + let result = bd.build_float_add(lhs, rhs, "add_float"); + + let is_finite = + call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value(); + let is_infinite = bd.build_not(is_finite, "negate"); + + let struct_type = context.struct_type( + &[context.f64_type().into(), context.bool_type().into()], + false, + ); + + let struct_value = { + let v1 = struct_type.const_zero(); + + let v2 = builder + .build_insert_value(v1, result, 0, "set_result") + .unwrap(); + + let v3 = builder + .build_insert_value(v2, is_infinite, 1, "set_is_infinite") + .unwrap(); + + v3.into_struct_value() + }; + + struct_value.into() + } NumAddWrap => unreachable!("wrapping addition is not defined on floats"), - NumAddChecked => bd.build_float_add(lhs, rhs, "add_float_checked").into(), NumSub => bd.build_float_sub(lhs, rhs, "sub_float").into(), NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(), NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(), @@ -2622,6 +2675,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>( env.context.i64_type(), "num_floor", ), + NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"), _ => { unreachable!("Unrecognized int unary operation: {:?}", op); } @@ -2632,7 +2686,6 @@ fn define_global_str<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, message: &str, ) -> inkwell::values::GlobalValue<'ctx> { - let context = env.context; let module = env.module; // hash the name so we don't re-define existing messages @@ -2644,29 +2697,12 @@ fn define_global_str<'a, 'ctx, 'env>( message.hash(&mut hasher); let hash = hasher.finish(); - format!("message_{}", hash) + format!("_Error_message_{}", hash) }; match module.get_global(&name) { Some(current) => current, - None => { - let i8_type = context.i8_type(); - - // define the error message as a global constant - let message_global = - module.add_global(i8_type.array_type(message.len() as u32), None, &name); - - let mut message_bytes = Vec::with_capacity_in(message.len(), env.arena); - - for c in message.chars() { - message_bytes.push(i8_type.const_int(c as u64, false)); - } - - let const_array = i8_type.const_array(&message_bytes); - message_global.set_initializer(&const_array); - - message_global - } + None => unsafe { env.builder.build_global_string(message, name.as_str()) }, } } diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index 64f79896a0..b468030030 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -701,34 +701,34 @@ mod gen_num { } #[test] - fn add_checked() { + fn int_add_checked() { assert_evals_to!( indoc!( r#" when Num.addChecked 1 2 is - Ok 3 -> True - _ -> False + Ok v -> v + _ -> -1 "# ), - true, - bool + 3, + i64 ); assert_evals_to!( indoc!( r#" when Num.addChecked 9_223_372_036_854_775_807 1 is - Err IntOverflow -> True - _ -> False + Err Overflow -> -1 + Ok v -> v "# ), - true, - bool + -1, + i64 ); } #[test] - fn add_wrap() { + fn int_add_wrap() { assert_evals_to!( indoc!( r#" @@ -741,11 +741,42 @@ mod gen_num { } #[test] + fn float_add_checked_pass() { + assert_evals_to!( + indoc!( + r#" + when Num.addChecked 1.0 0.0 is + Ok v -> v + Err Overflow -> -1.0 + "# + ), + 1.0, + f64 + ); + } + + #[test] + fn float_add_checked_fail() { + assert_evals_to!( + indoc!( + r#" + when Num.addChecked 1.7976931348623157e308 1.7976931348623157e308 is + Err Overflow -> -1 + Ok v -> v + "# + ), + -1.0, + f64 + ); + } + + #[test] + #[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)] fn float_overflow() { assert_evals_to!( indoc!( r#" - 1.7976931348623157E+308 + 1 + 1.7976931348623157e308 + 1.7976931348623157e308 "# ), 0.0, diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 483f890f9d..2076921ba8 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -42,6 +42,7 @@ pub enum LowLevel { NumCeiling, NumPowInt, NumFloor, + NumIsFinite, Eq, NotEq, And, diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 4ec5a56aee..efc849f2d2 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -526,6 +526,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { | NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]), NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor - | NumToFloat | Not => arena.alloc_slice_copy(&[irrelevant]), + | NumToFloat | Not | NumIsFinite => arena.alloc_slice_copy(&[irrelevant]), } }