diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 7bfac7f26d..c293ed93a4 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -54,6 +54,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::LIST_GET => list_get, Symbol::LIST_SET => list_set, Symbol::LIST_FIRST => list_first, + Symbol::LIST_IS_EMPTY => list_is_empty, Symbol::NUM_ADD => num_add, Symbol::NUM_SUB => num_sub, Symbol::NUM_MUL => num_mul, @@ -68,6 +69,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::NUM_ABS => num_abs, Symbol::NUM_NEG => num_neg, Symbol::NUM_REM => num_rem, + Symbol::NUM_SQRT => num_sqrt, + Symbol::NUM_ROUND => num_round, Symbol::NUM_IS_ODD => num_is_odd, Symbol::NUM_IS_EVEN => num_is_even, Symbol::NUM_IS_ZERO => num_is_zero, @@ -80,11 +83,12 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { fn bool_eq(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), - (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), + (bool_var, Var(Symbol::BOOL_BINOP_LHS)), + (bool_var, Var(Symbol::BOOL_BINOP_RHS)), ], ret_var: var_store.fresh(), }; @@ -101,11 +105,12 @@ fn bool_eq(symbol: Symbol, var_store: &mut VarStore) -> Def { fn bool_neq(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = RunLowLevel { op: LowLevel::NotEq, args: vec![ - (var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), - (var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), + (bool_var, Var(Symbol::BOOL_BINOP_LHS)), + (bool_var, Var(Symbol::BOOL_BINOP_RHS)), ], ret_var: var_store.fresh(), }; @@ -254,7 +259,7 @@ fn num_cos(symbol: Symbol, var_store: &mut VarStore) -> Def { /// Num.tan : Float -> Float fn num_tan(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { - op: LowLevel::NumDivUnsafe, + op: LowLevel::NumDivUnchecked, args: vec![ ( var_store.fresh(), @@ -287,11 +292,12 @@ fn num_tan(symbol: Symbol, var_store: &mut VarStore) -> Def { /// Num.isZero : Float -> Bool fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def { + let bool_var = var_store.fresh(); let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (var_store.fresh(), Var(Symbol::ARG_1)), - (var_store.fresh(), Num(var_store.fresh(), 0)), + (bool_var, Var(Symbol::ARG_1)), + (bool_var, Num(var_store.fresh(), 0)), ], ret_var: var_store.fresh(), }; @@ -333,13 +339,15 @@ fn num_is_positive(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = RunLowLevel { op: LowLevel::Eq, args: vec![ + (bool_var, Int(var_store.fresh(), 1)), ( - var_store.fresh(), + bool_var, RunLowLevel { - op: LowLevel::NumRemUnsafe, + op: LowLevel::NumRemUnchecked, args: vec![ (var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Int(var_store.fresh(), 2)), @@ -347,7 +355,6 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def { ret_var: var_store.fresh(), }, ), - (var_store.fresh(), Int(var_store.fresh(), 1)), ], ret_var: var_store.fresh(), }; @@ -359,13 +366,15 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = RunLowLevel { op: LowLevel::Eq, args: vec![ + (bool_var, Int(var_store.fresh(), 0)), ( - var_store.fresh(), + bool_var, RunLowLevel { - op: LowLevel::NumRemUnsafe, + op: LowLevel::NumRemUnchecked, args: vec![ (var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Int(var_store.fresh(), 2)), @@ -373,7 +382,54 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def { ret_var: var_store.fresh(), }, ), - (var_store.fresh(), Int(var_store.fresh(), 0)), + ], + ret_var: var_store.fresh(), + }; + + defn(symbol, vec![Symbol::ARG_1], var_store, body) +} + +/// Num.sqrt : Float -> Result Float [ SqrtOfNegative ]* +fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { + use crate::expr::Expr::*; + + let body = RunLowLevel { + op: LowLevel::NumSqrt, + args: vec![(var_store.fresh(), Var(Symbol::ARG_1))], + ret_var: var_store.fresh(), + }; + + defn(symbol, vec![Symbol::ARG_1], var_store, body) +} + +/// Num.round : Float -> Int +fn num_round(symbol: Symbol, var_store: &mut VarStore) -> Def { + use crate::expr::Expr::*; + + let body = RunLowLevel { + op: LowLevel::NumRound, + args: vec![(var_store.fresh(), Var(Symbol::ARG_1))], + ret_var: var_store.fresh(), + }; + + defn(symbol, vec![Symbol::ARG_1], var_store, body) +} + +/// List.isEmpty : List * -> Bool +fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def { + let bool_var = var_store.fresh(); + let body = RunLowLevel { + op: LowLevel::Eq, + args: vec![ + (bool_var, Num(var_store.fresh(), 0)), + ( + bool_var, + RunLowLevel { + op: LowLevel::ListLen, + args: vec![(var_store.fresh(), Var(Symbol::ARG_1))], + ret_var: var_store.fresh(), + }, + ), ], ret_var: var_store.fresh(), }; @@ -383,29 +439,22 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def { /// List.len : List * -> Int fn list_len(symbol: Symbol, var_store: &mut VarStore) -> Def { - use crate::expr::Expr::*; + let body = RunLowLevel { + op: LowLevel::ListLen, + args: vec![(var_store.fresh(), Var(Symbol::ARG_1))], + ret_var: var_store.fresh(), + }; - // Polymorphic wrapper around LowLevel::ListLen - let arg = Symbol::LIST_LEN_ARG; - let arg_var = var_store.fresh(); - let ret_var = var_store.fresh(); - - defn( - symbol, - vec![arg], - var_store, - RunLowLevel { - op: LowLevel::ListLen, - args: vec![(arg_var, Var(arg))], - ret_var, - }, - ) + defn(symbol, vec![Symbol::ARG_1], var_store, body) } /// List.get : List elem, Int -> Result elem [ OutOfBounds ]* fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let arg_list = Symbol::ARG_1; + let arg_index = Symbol::ARG_2; + // Perform a bounds check. If it passes, run LowLevel::ListGetUnsafe let body = If { cond_var: var_store.fresh(), @@ -417,12 +466,12 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NumLt, args: vec![ - (var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), + (var_store.fresh(), Var(arg_index)), ( var_store.fresh(), RunLowLevel { op: LowLevel::ListLen, - args: vec![(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST))], + args: vec![(var_store.fresh(), Var(arg_list))], ret_var: var_store.fresh(), }, ), @@ -440,8 +489,8 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::ListGetUnsafe, args: vec![ - (var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)), - (var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), + (var_store.fresh(), Var(arg_list)), + (var_store.fresh(), Var(arg_index)), ], ret_var: var_store.fresh(), }, @@ -463,19 +512,18 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { ), }; - defn( - symbol, - vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX], - var_store, - body, - ) + defn(symbol, vec![Symbol::ARG_1, Symbol::ARG_2], var_store, body) } /// List.set : List elem, Int, elem -> List elem fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; - // Perform a bounds check. If it passes, run LowLevel::ListSetUnsafe. + let arg_list = Symbol::ARG_1; + let arg_index = Symbol::ARG_2; + let arg_elem = Symbol::ARG_3; + + // Perform a bounds check. If it passes, run LowLevel::ListSet. // Otherwise, return the list unmodified. let body = If { cond_var: var_store.fresh(), @@ -487,12 +535,12 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NumLt, args: vec![ - (var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), + (var_store.fresh(), Var(arg_index)), ( var_store.fresh(), RunLowLevel { op: LowLevel::ListLen, - args: vec![(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST))], + args: vec![(var_store.fresh(), Var(arg_list))], ret_var: var_store.fresh(), }, ), @@ -504,11 +552,11 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { no_region( // List.setUnsafe list index RunLowLevel { - op: LowLevel::ListSetUnsafe, + op: LowLevel::ListSet, args: vec![ - (var_store.fresh(), Var(Symbol::LIST_SET_ARG_LIST)), - (var_store.fresh(), Var(Symbol::LIST_SET_ARG_INDEX)), - (var_store.fresh(), Var(Symbol::LIST_SET_ARG_ELEM)), + (var_store.fresh(), Var(arg_list)), + (var_store.fresh(), Var(arg_index)), + (var_store.fresh(), Var(arg_elem)), ], ret_var: var_store.fresh(), }, @@ -516,13 +564,13 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { )], final_else: Box::new( // else-branch - no_region(Var(Symbol::LIST_SET_ARG_LIST)), + no_region(Var(arg_list)), ), }; defn( symbol, - vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX], + vec![Symbol::ARG_1, Symbol::ARG_2, Symbol::ARG_3], var_store, body, ) @@ -532,6 +580,7 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = If { branch_var: var_store.fresh(), cond_var: var_store.fresh(), @@ -542,8 +591,8 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (var_store.fresh(), Var(Symbol::ARG_2)), - (var_store.fresh(), Int(var_store.fresh(), 0)), + (bool_var, Var(Symbol::ARG_2)), + (bool_var, Int(var_store.fresh(), 0)), ], ret_var: var_store.fresh(), }, @@ -556,7 +605,7 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { vec![ // Num.#remUnsafe arg0 arg1 RunLowLevel { - op: LowLevel::NumRemUnsafe, + op: LowLevel::NumRemUnchecked, args: vec![ (var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_2)), @@ -632,6 +681,7 @@ fn num_abs(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { use crate::expr::Expr::*; + let bool_var = var_store.fresh(); let body = If { branch_var: var_store.fresh(), cond_var: var_store.fresh(), @@ -642,8 +692,8 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (var_store.fresh(), Var(Symbol::ARG_1)), - (var_store.fresh(), Int(var_store.fresh(), 0)), + (bool_var, Var(Symbol::ARG_1)), + (bool_var, Int(var_store.fresh(), 0)), ], ret_var: var_store.fresh(), }, @@ -656,7 +706,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { vec![ // Num.#divUnsafe numerator denominator RunLowLevel { - op: LowLevel::NumDivUnsafe, + op: LowLevel::NumDivUnchecked, args: vec![ (var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_2)), @@ -697,7 +747,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { // List.isEmpty list RunLowLevel { op: LowLevel::ListIsEmpty, - args: vec![(var_store.fresh(), Var(Symbol::LIST_FIRST_ARG))], + args: vec![(var_store.fresh(), Var(Symbol::ARG_1))], ret_var: var_store.fresh(), }, ), @@ -722,7 +772,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::ListGetUnsafe, args: vec![ - (var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)), + (var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Int(var_store.fresh(), 0)), ], ret_var: var_store.fresh(), @@ -734,7 +784,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { ), }; - defn(symbol, vec![Symbol::LIST_FIRST_ARG], var_store, body) + defn(symbol, vec![Symbol::ARG_1], var_store, body) } #[inline(always)] diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 38e75586db..9717f9ca55 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -1017,21 +1017,10 @@ fn call_with_args<'a, 'ctx, 'env>( layout_ids: &mut LayoutIds<'a>, layout: &Layout<'a>, symbol: Symbol, - parent: FunctionValue<'ctx>, + _parent: FunctionValue<'ctx>, args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)], ) -> BasicValueEnum<'ctx> { match symbol { - Symbol::NUM_MUL => { - debug_assert!(args.len() == 2); - - let int_val = env.builder.build_int_mul( - args[0].0.into_int_value(), - args[1].0.into_int_value(), - "mul_i64", - ); - - BasicValueEnum::IntValue(int_val) - } Symbol::NUM_TO_FLOAT => { // TODO specialize this to be not just for i64! let builtin_fn_name = "i64_to_f64_"; @@ -1057,10 +1046,6 @@ fn call_with_args<'a, 'ctx, 'env>( .left() .unwrap_or_else(|| panic!("LLVM error: Invalid call for builtin {:?}", symbol)) } - Symbol::NUM_SQRT => call_intrinsic(LLVM_SQRT_F64, env, args), - Symbol::NUM_ROUND => call_intrinsic(LLVM_LROUND_I64_F64, env, args), - Symbol::LIST_SET => list_set(parent, args, env, InPlace::Clone), - Symbol::LIST_SET_IN_PLACE => list_set(parent, args, env, InPlace::InPlace), Symbol::LIST_SINGLE => { // List.single : a -> List a debug_assert!(args.len() == 1); @@ -1311,7 +1296,7 @@ fn list_set<'a, 'ctx, 'env>( // List.set : List elem, Int, elem -> List elem let builder = env.builder; - debug_assert!(args.len() == 3); + debug_assert_eq!(args.len(), 3); let original_wrapper = args[0].0.into_struct_value(); let elem_index = args[1].0.into_int_value(); @@ -1414,7 +1399,7 @@ fn run_low_level<'a, 'ctx, 'env>( BasicValueEnum::IntValue(answer) } - NumAbs | NumNeg => { + NumAbs | NumNeg | NumRound | NumSqrt => { debug_assert_eq!(args.len(), 1); let arg = build_expr(env, layout_ids, scope, parent, &args[0].0); @@ -1445,7 +1430,7 @@ fn run_low_level<'a, 'ctx, 'env>( } } NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumSin | NumCos - | NumRemUnsafe | NumDivUnsafe => { + | NumRemUnchecked | NumDivUnchecked => { debug_assert_eq!(args.len(), 2); let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0); @@ -1576,9 +1561,44 @@ fn run_low_level<'a, 'ctx, 'env>( } } } - ListSetUnsafe => { - todo!("re-implement List#setUnsafe"); - } + ListSet => list_set( + parent, + &[ + ( + build_expr(env, layout_ids, scope, parent, &args[0].0), + &args[0].1, + ), + ( + build_expr(env, layout_ids, scope, parent, &args[1].0), + &args[1].1, + ), + ( + build_expr(env, layout_ids, scope, parent, &args[2].0), + &args[2].1, + ), + ], + env, + InPlace::Clone, + ), + ListSetInPlace => list_set( + parent, + &[ + ( + build_expr(env, layout_ids, scope, parent, &args[0].0), + &args[0].1, + ), + ( + build_expr(env, layout_ids, scope, parent, &args[1].0), + &args[1].1, + ), + ( + build_expr(env, layout_ids, scope, parent, &args[2].0), + &args[2].1, + ), + ], + env, + InPlace::InPlace, + ), } } @@ -1603,8 +1623,8 @@ fn build_int_binop<'a, 'ctx, 'env>( NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(), NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(), NumLte => bd.build_int_compare(SLE, lhs, rhs, "int_lte").into(), - NumRemUnsafe => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(), - NumDivUnsafe => bd.build_int_signed_div(lhs, rhs, "div_int").into(), + NumRemUnchecked => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(), + NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(), _ => { unreachable!("Unrecognized int binary operation: {:?}", op); } @@ -1632,8 +1652,8 @@ fn build_float_binop<'a, 'ctx, 'env>( NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(), NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(), NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(), - NumRemUnsafe => bd.build_float_rem(lhs, rhs, "rem_float").into(), - NumDivUnsafe => bd.build_float_div(lhs, rhs, "div_float").into(), + NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(), + NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(), // Float-specific ops NumSin => call_intrinsic( @@ -1686,6 +1706,8 @@ fn build_float_unary_op<'a, 'ctx, 'env>( match op { NumAdd => bd.build_float_neg(arg, "negate_float").into(), NumAbs => call_intrinsic(LLVM_FABS_F64, env, &[(arg.into(), arg_layout)]), + NumSqrt => call_intrinsic(LLVM_SQRT_F64, env, &[(arg.into(), arg_layout)]), + NumRound => call_intrinsic(LLVM_LROUND_I64_F64, env, &[(arg.into(), arg_layout)]), _ => { unreachable!("Unrecognized int unary operation: {:?}", op); } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index b535fb544a..14b4678366 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -5,7 +5,8 @@ pub enum LowLevel { ListLen, ListGetUnsafe, - ListSetUnsafe, + ListSet, + ListSetInPlace, ListIsEmpty, NumAdd, NumSub, @@ -14,12 +15,14 @@ pub enum LowLevel { NumGte, NumLt, NumLte, - NumDivUnsafe, - NumRemUnsafe, + NumDivUnchecked, + NumRemUnchecked, NumAbs, NumNeg, NumSin, NumCos, + NumSqrt, + NumRound, Eq, NotEq, And, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 1aa97357f1..0e9cda738c 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -644,23 +644,15 @@ define_builtins! { 1 LIST_AT_LIST: "@List" // the List.@List private tag 2 LIST_IS_EMPTY: "isEmpty" 3 LIST_GET: "get" - 4 LIST_GET_ARG_LIST: "get#list" - 5 LIST_GET_ARG_INDEX: "get#index" - 6 LIST_SET: "set" - 7 LIST_SET_IN_PLACE: "#setInPlace" - 8 LIST_PUSH: "push" - 9 LIST_MAP: "map" - 10 LIST_LEN: "len" - 11 LIST_LEN_ARG: "len#list" - 12 LIST_FOLDL: "foldl" - 13 LIST_FOLDR: "foldr" - 14 LIST_CONCAT: "concat" - 15 LIST_FIRST: "first" - 16 LIST_FIRST_ARG: "first#list" - 17 LIST_SINGLE: "single" - 18 LIST_SET_ARG_LIST: "set#list" - 19 LIST_SET_ARG_INDEX: "set#index" - 20 LIST_SET_ARG_ELEM: "set#elem" + 4 LIST_SET: "set" + 5 LIST_PUSH: "push" + 6 LIST_MAP: "map" + 7 LIST_LEN: "len" + 8 LIST_FOLDL: "foldl" + 9 LIST_FOLDR: "foldr" + 10 LIST_CONCAT: "concat" + 11 LIST_FIRST: "first" + 12 LIST_SINGLE: "single" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index 977f6f3046..54e66a356c 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -514,6 +514,7 @@ fn from_can<'a>( } RunLowLevel { op, args, .. } => { + let op = optimize_low_level(env.subs, op, &args); let mut mono_args = Vec::with_capacity_in(args.len(), env.arena); for (arg_var, arg_expr) in args { @@ -535,64 +536,15 @@ fn from_can<'a>( Expr::Load(proc_name) => { // Some functions can potentially mutate in-place. // If we have one of those, switch to the in-place version if appropriate. - match proc_name { - Symbol::LIST_SET => { - let subs = &env.subs; - // The first arg is the one with the List in it. - // List.set : List elem, Int, elem -> List elem - let (list_arg_var, _) = loc_args.get(0).unwrap(); - - let content = subs.get_without_compacting(*list_arg_var).content; - - match content { - Content::Structure(FlatType::Apply( - Symbol::ATTR_ATTR, - attr_args, - )) => { - debug_assert!(attr_args.len() == 2); - - // If the first argument (the List) is unique, - // then we can safely upgrade to List.set_in_place - let attr_arg_content = - subs.get_without_compacting(attr_args[0]).content; - - let new_name = if attr_arg_content.is_unique(subs) { - Symbol::LIST_SET_IN_PLACE - } else { - Symbol::LIST_SET - }; - - call_by_name( - env, - procs, - fn_var, - ret_var, - new_name, - loc_args, - layout_cache, - ) - } - _ => call_by_name( - env, - procs, - fn_var, - ret_var, - proc_name, - loc_args, - layout_cache, - ), - } - } - specialized_proc_symbol => call_by_name( - env, - procs, - fn_var, - ret_var, - specialized_proc_symbol, - loc_args, - layout_cache, - ), - } + call_by_name( + env, + procs, + fn_var, + ret_var, + proc_name, + loc_args, + layout_cache, + ) } ptr => { // Call by pointer - the closure was anonymous, e.g. @@ -1783,3 +1735,41 @@ fn from_can_record_destruct<'a>( }, } } + +/// Potentially translate LowLevel operations into more efficient ones based on +/// uniqueness type info. +/// +/// For example, turning LowLevel::ListSet to LowLevel::ListSetInPlace if the +/// list is Unique. +fn optimize_low_level( + subs: &Subs, + op: LowLevel, + args: &[(Variable, roc_can::expr::Expr)], +) -> LowLevel { + match op { + LowLevel::ListSet => { + // The first arg is the one with the List in it. + // List.set : List elem, Int, elem -> List elem + let list_arg_var = args[0].0; + let content = subs.get_without_compacting(list_arg_var).content; + + match content { + Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => { + debug_assert_eq!(attr_args.len(), 2); + + // If the first argument (the List) is unique, + // then we can safely upgrade to List.set_in_place + let attr_arg_content = subs.get_without_compacting(attr_args[0]).content; + + if attr_arg_content.is_unique(subs) { + LowLevel::ListSetInPlace + } else { + LowLevel::ListSet + } + } + _ => op, + } + } + _ => op, + } +} diff --git a/compiler/uniq/src/sharing.rs b/compiler/uniq/src/sharing.rs index 6a155b9ff8..e15427ccc2 100644 --- a/compiler/uniq/src/sharing.rs +++ b/compiler/uniq/src/sharing.rs @@ -733,7 +733,7 @@ fn annotate_low_level_usage( } } - ListSetUnsafe => { + ListSet | ListSetInPlace => { match &args[0].1 { Var(list_var) => { usage.register_with( @@ -756,7 +756,8 @@ fn annotate_low_level_usage( } NumAdd | NumSub | NumMul | NumGt | NumGte | NumLt | NumLte | NumAbs | NumNeg - | NumDivUnsafe | NumRemUnsafe | NumSin | NumCos | Eq | NotEq | And | Or | Not => { + | NumDivUnchecked | NumRemUnchecked | NumSqrt | NumRound | NumSin | NumCos | Eq | NotEq + | And | Or | Not => { for (_, arg) in args { annotate_usage(&arg, usage); }