Ops for sqrt and round

This commit is contained in:
Richard Feldman 2020-06-23 18:05:30 -04:00
parent 9f8c48118f
commit 1c98248b91
6 changed files with 221 additions and 163 deletions

View file

@ -54,6 +54,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::LIST_GET => list_get, Symbol::LIST_GET => list_get,
Symbol::LIST_SET => list_set, Symbol::LIST_SET => list_set,
Symbol::LIST_FIRST => list_first, Symbol::LIST_FIRST => list_first,
Symbol::LIST_IS_EMPTY => list_is_empty,
Symbol::NUM_ADD => num_add, Symbol::NUM_ADD => num_add,
Symbol::NUM_SUB => num_sub, Symbol::NUM_SUB => num_sub,
Symbol::NUM_MUL => num_mul, Symbol::NUM_MUL => num_mul,
@ -68,6 +69,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::NUM_ABS => num_abs, Symbol::NUM_ABS => num_abs,
Symbol::NUM_NEG => num_neg, Symbol::NUM_NEG => num_neg,
Symbol::NUM_REM => num_rem, 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_ODD => num_is_odd,
Symbol::NUM_IS_EVEN => num_is_even, Symbol::NUM_IS_EVEN => num_is_even,
Symbol::NUM_IS_ZERO => num_is_zero, Symbol::NUM_IS_ZERO => num_is_zero,
@ -80,11 +83,12 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
fn bool_eq(symbol: Symbol, var_store: &mut VarStore) -> Def { fn bool_eq(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::Eq, op: LowLevel::Eq,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), (bool_var, Var(Symbol::BOOL_BINOP_LHS)),
(var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), (bool_var, Var(Symbol::BOOL_BINOP_RHS)),
], ],
ret_var: var_store.fresh(), 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 { fn bool_neq(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::NotEq, op: LowLevel::NotEq,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::BOOL_BINOP_LHS)), (bool_var, Var(Symbol::BOOL_BINOP_LHS)),
(var_store.fresh(), Var(Symbol::BOOL_BINOP_RHS)), (bool_var, Var(Symbol::BOOL_BINOP_RHS)),
], ],
ret_var: var_store.fresh(), ret_var: var_store.fresh(),
}; };
@ -254,7 +259,7 @@ fn num_cos(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.tan : Float -> Float /// Num.tan : Float -> Float
fn num_tan(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_tan(symbol: Symbol, var_store: &mut VarStore) -> Def {
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::NumDivUnsafe, op: LowLevel::NumDivUnchecked,
args: vec![ args: vec![
( (
var_store.fresh(), var_store.fresh(),
@ -287,11 +292,12 @@ fn num_tan(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.isZero : Float -> Bool /// Num.isZero : Float -> Bool
fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::Eq, op: LowLevel::Eq,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (bool_var, Var(Symbol::ARG_1)),
(var_store.fresh(), Num(var_store.fresh(), 0)), (bool_var, Num(var_store.fresh(), 0)),
], ],
ret_var: var_store.fresh(), 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 { fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::Eq, op: LowLevel::Eq,
args: vec![ args: vec![
(bool_var, Int(var_store.fresh(), 1)),
( (
var_store.fresh(), bool_var,
RunLowLevel { RunLowLevel {
op: LowLevel::NumRemUnsafe, op: LowLevel::NumRemUnchecked,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_1)),
(var_store.fresh(), Int(var_store.fresh(), 2)), (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(), ret_var: var_store.fresh(),
}, },
), ),
(var_store.fresh(), Int(var_store.fresh(), 1)),
], ],
ret_var: var_store.fresh(), 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 { fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::Eq, op: LowLevel::Eq,
args: vec![ args: vec![
(bool_var, Int(var_store.fresh(), 0)),
( (
var_store.fresh(), bool_var,
RunLowLevel { RunLowLevel {
op: LowLevel::NumRemUnsafe, op: LowLevel::NumRemUnchecked,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_1)),
(var_store.fresh(), Int(var_store.fresh(), 2)), (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(), 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(), ret_var: var_store.fresh(),
}; };
@ -383,29 +439,22 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// List.len : List * -> Int /// List.len : List * -> Int
fn list_len(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_len(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; let body = RunLowLevel {
// 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, op: LowLevel::ListLen,
args: vec![(arg_var, Var(arg))], args: vec![(var_store.fresh(), Var(Symbol::ARG_1))],
ret_var, ret_var: var_store.fresh(),
}, };
)
defn(symbol, vec![Symbol::ARG_1], var_store, body)
} }
/// List.get : List elem, Int -> Result elem [ OutOfBounds ]* /// List.get : List elem, Int -> Result elem [ OutOfBounds ]*
fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; 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 // Perform a bounds check. If it passes, run LowLevel::ListGetUnsafe
let body = If { let body = If {
cond_var: var_store.fresh(), cond_var: var_store.fresh(),
@ -417,12 +466,12 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel { RunLowLevel {
op: LowLevel::NumLt, op: LowLevel::NumLt,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), (var_store.fresh(), Var(arg_index)),
( (
var_store.fresh(), var_store.fresh(),
RunLowLevel { RunLowLevel {
op: LowLevel::ListLen, 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(), ret_var: var_store.fresh(),
}, },
), ),
@ -440,8 +489,8 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel { RunLowLevel {
op: LowLevel::ListGetUnsafe, op: LowLevel::ListGetUnsafe,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_LIST)), (var_store.fresh(), Var(arg_list)),
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), (var_store.fresh(), Var(arg_index)),
], ],
ret_var: var_store.fresh(), ret_var: var_store.fresh(),
}, },
@ -463,19 +512,18 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
), ),
}; };
defn( defn(symbol, vec![Symbol::ARG_1, Symbol::ARG_2], var_store, body)
symbol,
vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX],
var_store,
body,
)
} }
/// List.set : List elem, Int, elem -> List elem /// List.set : List elem, Int, elem -> List elem
fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; 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. // Otherwise, return the list unmodified.
let body = If { let body = If {
cond_var: var_store.fresh(), cond_var: var_store.fresh(),
@ -487,12 +535,12 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel { RunLowLevel {
op: LowLevel::NumLt, op: LowLevel::NumLt,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::LIST_GET_ARG_INDEX)), (var_store.fresh(), Var(arg_index)),
( (
var_store.fresh(), var_store.fresh(),
RunLowLevel { RunLowLevel {
op: LowLevel::ListLen, 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(), ret_var: var_store.fresh(),
}, },
), ),
@ -504,11 +552,11 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
no_region( no_region(
// List.setUnsafe list index // List.setUnsafe list index
RunLowLevel { RunLowLevel {
op: LowLevel::ListSetUnsafe, op: LowLevel::ListSet,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_LIST)), (var_store.fresh(), Var(arg_list)),
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_INDEX)), (var_store.fresh(), Var(arg_index)),
(var_store.fresh(), Var(Symbol::LIST_SET_ARG_ELEM)), (var_store.fresh(), Var(arg_elem)),
], ],
ret_var: var_store.fresh(), ret_var: var_store.fresh(),
}, },
@ -516,13 +564,13 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def {
)], )],
final_else: Box::new( final_else: Box::new(
// else-branch // else-branch
no_region(Var(Symbol::LIST_SET_ARG_LIST)), no_region(Var(arg_list)),
), ),
}; };
defn( defn(
symbol, symbol,
vec![Symbol::LIST_GET_ARG_LIST, Symbol::LIST_GET_ARG_INDEX], vec![Symbol::ARG_1, Symbol::ARG_2, Symbol::ARG_3],
var_store, var_store,
body, 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 { fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = If { let body = If {
branch_var: var_store.fresh(), branch_var: var_store.fresh(),
cond_var: var_store.fresh(), cond_var: var_store.fresh(),
@ -542,8 +591,8 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel { RunLowLevel {
op: LowLevel::NotEq, op: LowLevel::NotEq,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_2)), (bool_var, Var(Symbol::ARG_2)),
(var_store.fresh(), Int(var_store.fresh(), 0)), (bool_var, Int(var_store.fresh(), 0)),
], ],
ret_var: var_store.fresh(), ret_var: var_store.fresh(),
}, },
@ -556,7 +605,7 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
vec![ vec![
// Num.#remUnsafe arg0 arg1 // Num.#remUnsafe arg0 arg1
RunLowLevel { RunLowLevel {
op: LowLevel::NumRemUnsafe, op: LowLevel::NumRemUnchecked,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_1)),
(var_store.fresh(), Var(Symbol::ARG_2)), (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 { fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
use crate::expr::Expr::*; use crate::expr::Expr::*;
let bool_var = var_store.fresh();
let body = If { let body = If {
branch_var: var_store.fresh(), branch_var: var_store.fresh(),
cond_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 { RunLowLevel {
op: LowLevel::NotEq, op: LowLevel::NotEq,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (bool_var, Var(Symbol::ARG_1)),
(var_store.fresh(), Int(var_store.fresh(), 0)), (bool_var, Int(var_store.fresh(), 0)),
], ],
ret_var: var_store.fresh(), ret_var: var_store.fresh(),
}, },
@ -656,7 +706,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
vec![ vec![
// Num.#divUnsafe numerator denominator // Num.#divUnsafe numerator denominator
RunLowLevel { RunLowLevel {
op: LowLevel::NumDivUnsafe, op: LowLevel::NumDivUnchecked,
args: vec![ args: vec![
(var_store.fresh(), Var(Symbol::ARG_1)), (var_store.fresh(), Var(Symbol::ARG_1)),
(var_store.fresh(), Var(Symbol::ARG_2)), (var_store.fresh(), Var(Symbol::ARG_2)),
@ -697,7 +747,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
// List.isEmpty list // List.isEmpty list
RunLowLevel { RunLowLevel {
op: LowLevel::ListIsEmpty, 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(), ret_var: var_store.fresh(),
}, },
), ),
@ -722,7 +772,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
RunLowLevel { RunLowLevel {
op: LowLevel::ListGetUnsafe, op: LowLevel::ListGetUnsafe,
args: vec![ 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)), (var_store.fresh(), Int(var_store.fresh(), 0)),
], ],
ret_var: var_store.fresh(), 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)] #[inline(always)]

View file

@ -1017,21 +1017,10 @@ fn call_with_args<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>, layout: &Layout<'a>,
symbol: Symbol, symbol: Symbol,
parent: FunctionValue<'ctx>, _parent: FunctionValue<'ctx>,
args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)], args: &[(BasicValueEnum<'ctx>, &'a Layout<'a>)],
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match symbol { 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 => { Symbol::NUM_TO_FLOAT => {
// TODO specialize this to be not just for i64! // TODO specialize this to be not just for i64!
let builtin_fn_name = "i64_to_f64_"; let builtin_fn_name = "i64_to_f64_";
@ -1057,10 +1046,6 @@ fn call_with_args<'a, 'ctx, 'env>(
.left() .left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call for builtin {:?}", symbol)) .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 => { Symbol::LIST_SINGLE => {
// List.single : a -> List a // List.single : a -> List a
debug_assert!(args.len() == 1); debug_assert!(args.len() == 1);
@ -1311,7 +1296,7 @@ fn list_set<'a, 'ctx, 'env>(
// List.set : List elem, Int, elem -> List elem // List.set : List elem, Int, elem -> List elem
let builder = env.builder; 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 original_wrapper = args[0].0.into_struct_value();
let elem_index = args[1].0.into_int_value(); let elem_index = args[1].0.into_int_value();
@ -1414,7 +1399,7 @@ fn run_low_level<'a, 'ctx, 'env>(
BasicValueEnum::IntValue(answer) BasicValueEnum::IntValue(answer)
} }
NumAbs | NumNeg => { NumAbs | NumNeg | NumRound | NumSqrt => {
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
let arg = build_expr(env, layout_ids, scope, parent, &args[0].0); 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 NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumSin | NumCos
| NumRemUnsafe | NumDivUnsafe => { | NumRemUnchecked | NumDivUnchecked => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0); 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 => { ListSet => list_set(
todo!("re-implement List#setUnsafe"); 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(), NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(), NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
NumLte => bd.build_int_compare(SLE, lhs, rhs, "int_lte").into(), NumLte => bd.build_int_compare(SLE, lhs, rhs, "int_lte").into(),
NumRemUnsafe => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(), NumRemUnchecked => bd.build_int_signed_rem(lhs, rhs, "rem_int").into(),
NumDivUnsafe => bd.build_int_signed_div(lhs, rhs, "div_int").into(), NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
_ => { _ => {
unreachable!("Unrecognized int binary operation: {:?}", op); 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(), NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(), NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(), NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
NumRemUnsafe => bd.build_float_rem(lhs, rhs, "rem_float").into(), NumRemUnchecked => bd.build_float_rem(lhs, rhs, "rem_float").into(),
NumDivUnsafe => bd.build_float_div(lhs, rhs, "div_float").into(), NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
// Float-specific ops // Float-specific ops
NumSin => call_intrinsic( NumSin => call_intrinsic(
@ -1686,6 +1706,8 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
match op { match op {
NumAdd => bd.build_float_neg(arg, "negate_float").into(), NumAdd => bd.build_float_neg(arg, "negate_float").into(),
NumAbs => call_intrinsic(LLVM_FABS_F64, env, &[(arg.into(), arg_layout)]), 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); unreachable!("Unrecognized int unary operation: {:?}", op);
} }

View file

@ -5,7 +5,8 @@
pub enum LowLevel { pub enum LowLevel {
ListLen, ListLen,
ListGetUnsafe, ListGetUnsafe,
ListSetUnsafe, ListSet,
ListSetInPlace,
ListIsEmpty, ListIsEmpty,
NumAdd, NumAdd,
NumSub, NumSub,
@ -14,12 +15,14 @@ pub enum LowLevel {
NumGte, NumGte,
NumLt, NumLt,
NumLte, NumLte,
NumDivUnsafe, NumDivUnchecked,
NumRemUnsafe, NumRemUnchecked,
NumAbs, NumAbs,
NumNeg, NumNeg,
NumSin, NumSin,
NumCos, NumCos,
NumSqrt,
NumRound,
Eq, Eq,
NotEq, NotEq,
And, And,

View file

@ -644,23 +644,15 @@ define_builtins! {
1 LIST_AT_LIST: "@List" // the List.@List private tag 1 LIST_AT_LIST: "@List" // the List.@List private tag
2 LIST_IS_EMPTY: "isEmpty" 2 LIST_IS_EMPTY: "isEmpty"
3 LIST_GET: "get" 3 LIST_GET: "get"
4 LIST_GET_ARG_LIST: "get#list" 4 LIST_SET: "set"
5 LIST_GET_ARG_INDEX: "get#index" 5 LIST_PUSH: "push"
6 LIST_SET: "set" 6 LIST_MAP: "map"
7 LIST_SET_IN_PLACE: "#setInPlace" 7 LIST_LEN: "len"
8 LIST_PUSH: "push" 8 LIST_FOLDL: "foldl"
9 LIST_MAP: "map" 9 LIST_FOLDR: "foldr"
10 LIST_LEN: "len" 10 LIST_CONCAT: "concat"
11 LIST_LEN_ARG: "len#list" 11 LIST_FIRST: "first"
12 LIST_FOLDL: "foldl" 12 LIST_SINGLE: "single"
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"
} }
5 RESULT: "Result" => { 5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias 0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -514,6 +514,7 @@ fn from_can<'a>(
} }
RunLowLevel { op, args, .. } => { RunLowLevel { op, args, .. } => {
let op = optimize_low_level(env.subs, op, &args);
let mut mono_args = Vec::with_capacity_in(args.len(), env.arena); let mut mono_args = Vec::with_capacity_in(args.len(), env.arena);
for (arg_var, arg_expr) in args { for (arg_var, arg_expr) in args {
@ -535,44 +536,7 @@ fn from_can<'a>(
Expr::Load(proc_name) => { Expr::Load(proc_name) => {
// Some functions can potentially mutate in-place. // Some functions can potentially mutate in-place.
// If we have one of those, switch to the in-place version if appropriate. // 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( call_by_name(
env,
procs,
fn_var,
ret_var,
new_name,
loc_args,
layout_cache,
)
}
_ => call_by_name(
env, env,
procs, procs,
fn_var, fn_var,
@ -580,19 +544,7 @@ fn from_can<'a>(
proc_name, proc_name,
loc_args, loc_args,
layout_cache, layout_cache,
), )
}
}
specialized_proc_symbol => call_by_name(
env,
procs,
fn_var,
ret_var,
specialized_proc_symbol,
loc_args,
layout_cache,
),
}
} }
ptr => { ptr => {
// Call by pointer - the closure was anonymous, e.g. // 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,
}
}

View file

@ -733,7 +733,7 @@ fn annotate_low_level_usage(
} }
} }
ListSetUnsafe => { ListSet | ListSetInPlace => {
match &args[0].1 { match &args[0].1 {
Var(list_var) => { Var(list_var) => {
usage.register_with( usage.register_with(
@ -756,7 +756,8 @@ fn annotate_low_level_usage(
} }
NumAdd | NumSub | NumMul | NumGt | NumGte | NumLt | NumLte | NumAbs | NumNeg 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 { for (_, arg) in args {
annotate_usage(&arg, usage); annotate_usage(&arg, usage);
} }