mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Get low-level ops compiling
This commit is contained in:
parent
b75f061f4f
commit
dec5c3a062
19 changed files with 447 additions and 296 deletions
|
@ -255,15 +255,12 @@ Int size : Num [ @Int size ]
|
||||||
## decimal floats have [some hardware support](http://speleotrove.com/decimal/)
|
## decimal floats have [some hardware support](http://speleotrove.com/decimal/)
|
||||||
## among the rare processors which support decimal float instructions at all.
|
## among the rare processors which support decimal float instructions at all.
|
||||||
##
|
##
|
||||||
## This specification covers these float formats, all of which Roc supports:
|
## This specification covers several float formats. Here are the ones Roc supports:
|
||||||
##
|
##
|
||||||
## - #F16 (16-bit binary float) # TODO show a table like we do with ints, with the min/max ranges
|
|
||||||
## - #F32 (32-bit binary float)
|
## - #F32 (32-bit binary float)
|
||||||
## - #F64 (64-bit binary float)
|
## - #F64 (64-bit binary float)
|
||||||
## - #F128 (128-bit binary float)
|
|
||||||
## - #D32 (32-bit decimal float)
|
## - #D32 (32-bit decimal float)
|
||||||
## - #D64 (64-bit decimal float)
|
## - #D64 (64-bit decimal float) # TODO show a table like we do with ints, with the min/max ranges
|
||||||
## - #D128 (128-bit decimal float)
|
|
||||||
##
|
##
|
||||||
## Like #Int, it's possible for #Float operations to overflow. Like with ints,
|
## Like #Int, it's possible for #Float operations to overflow. Like with ints,
|
||||||
## you'll typically get a crash when this happens.
|
## you'll typically get a crash when this happens.
|
||||||
|
@ -492,12 +489,21 @@ toU32 : Int * -> U32
|
||||||
toU64 : Int * -> U64
|
toU64 : Int * -> U64
|
||||||
toU128 : Int * -> U128
|
toU128 : Int * -> U128
|
||||||
|
|
||||||
## Convert a #Num to a #F16. If the given number can't be precisely represented in a #F16,
|
## Convert a #Num to a #F32. If the given number can't be precisely represented in a #F32,
|
||||||
## there will be a loss of precision.
|
## there will be a loss of precision.
|
||||||
toF16 : Num * -> F16
|
|
||||||
toF32 : Num * -> F32
|
toF32 : Num * -> F32
|
||||||
|
|
||||||
|
## Convert a #Num to a #F64. If the given number can't be precisely represented in a #F64,
|
||||||
|
## there will be a loss of precision.
|
||||||
toF64 : Num * -> F64
|
toF64 : Num * -> F64
|
||||||
toF128 : Num * -> F128
|
|
||||||
|
## Convert a #Num to a #D32. If the given number can't be precisely represented in a #D32,
|
||||||
|
## there will be a loss of precision.
|
||||||
|
toD32 : Num * -> D32
|
||||||
|
|
||||||
|
## Convert a #Num to a #D64. If the given number can't be precisely represented in a #D64,
|
||||||
|
## there will be a loss of precision.
|
||||||
|
toD64 : Num * -> D64
|
||||||
|
|
||||||
## Divide two integers and #Num.round the resulut.
|
## Divide two integers and #Num.round the resulut.
|
||||||
##
|
##
|
||||||
|
|
|
@ -273,24 +273,6 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
|
|
||||||
// Int module
|
// Int module
|
||||||
|
|
||||||
// isLt or (<) : Num a, Num a -> Bool
|
|
||||||
add_type(
|
|
||||||
Symbol::INT_LT,
|
|
||||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(bool_type())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// equals : Int, Int -> Bool
|
|
||||||
add_type(
|
|
||||||
Symbol::INT_EQ_I64,
|
|
||||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(bool_type())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// notEquals : Int, Int -> Bool
|
|
||||||
add_type(
|
|
||||||
Symbol::INT_NEQ_I64,
|
|
||||||
SolvedType::Func(vec![int_type(), int_type()], Box::new(bool_type())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// abs : Int -> Int
|
// abs : Int -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::INT_ABS,
|
Symbol::INT_ABS,
|
||||||
|
|
|
@ -355,18 +355,6 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)),
|
unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// equals or (==) : Int, Int -> Bool
|
|
||||||
add_type(
|
|
||||||
Symbol::INT_EQ_I64,
|
|
||||||
unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// not equals or (!=) : Int, Int -> Bool
|
|
||||||
add_type(
|
|
||||||
Symbol::INT_NEQ_I64,
|
|
||||||
unique_function(vec![int_type(UVAR1), int_type(UVAR2)], bool_type(UVAR3)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// abs : Int -> Int
|
// abs : Int -> Int
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::INT_ABS,
|
Symbol::INT_ABS,
|
||||||
|
|
|
@ -176,11 +176,14 @@ fn int_is_positive(var_store: &mut VarStore) -> Expr {
|
||||||
fn int_is_zero(var_store: &mut VarStore) -> Expr {
|
fn int_is_zero(var_store: &mut VarStore) -> Expr {
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::Expr::*;
|
||||||
|
|
||||||
let body = call(
|
let body = RunLowLevel {
|
||||||
Symbol::INT_EQ_I64,
|
op: LowLevel::Eq,
|
||||||
vec![Var(Symbol::INT_IS_ZERO_ARG), Int(var_store.fresh(), 0)],
|
args: vec![
|
||||||
var_store,
|
(var_store.fresh(), Var(Symbol::INT_IS_ZERO_ARG)),
|
||||||
);
|
(var_store.fresh(), Int(var_store.fresh(), 0)),
|
||||||
|
],
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
Symbol::INT_IS_ZERO,
|
Symbol::INT_IS_ZERO,
|
||||||
|
@ -194,18 +197,21 @@ fn int_is_zero(var_store: &mut VarStore) -> Expr {
|
||||||
fn int_is_odd(var_store: &mut VarStore) -> Expr {
|
fn int_is_odd(var_store: &mut VarStore) -> Expr {
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::Expr::*;
|
||||||
|
|
||||||
let body = call(
|
let body = RunLowLevel {
|
||||||
Symbol::INT_EQ_I64,
|
op: LowLevel::Eq,
|
||||||
vec![
|
args: vec![
|
||||||
call(
|
(
|
||||||
Symbol::INT_REM_UNSAFE,
|
var_store.fresh(),
|
||||||
vec![Var(Symbol::INT_IS_ODD_ARG), Int(var_store.fresh(), 2)],
|
call(
|
||||||
var_store,
|
Symbol::INT_REM_UNSAFE,
|
||||||
|
vec![Var(Symbol::INT_IS_ODD_ARG), Int(var_store.fresh(), 2)],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Int(var_store.fresh(), 1),
|
(var_store.fresh(), Int(var_store.fresh(), 1)),
|
||||||
],
|
],
|
||||||
var_store,
|
ret_var: var_store.fresh(),
|
||||||
);
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
Symbol::INT_IS_ODD,
|
Symbol::INT_IS_ODD,
|
||||||
|
@ -219,18 +225,14 @@ fn int_is_odd(var_store: &mut VarStore) -> Expr {
|
||||||
fn int_is_even(var_store: &mut VarStore) -> Expr {
|
fn int_is_even(var_store: &mut VarStore) -> Expr {
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::Expr::*;
|
||||||
|
|
||||||
let body = call(
|
let body = RunLowLevel {
|
||||||
Symbol::INT_EQ_I64,
|
op: LowLevel::Eq,
|
||||||
vec![
|
args: vec![
|
||||||
call(
|
(var_store.fresh(), Var(Symbol::INT_IS_EVEN_ARG)),
|
||||||
Symbol::INT_REM_UNSAFE,
|
(var_store.fresh(), Int(var_store.fresh(), 2)),
|
||||||
vec![Var(Symbol::INT_IS_EVEN_ARG), Int(var_store.fresh(), 2)],
|
|
||||||
var_store,
|
|
||||||
),
|
|
||||||
Int(var_store.fresh(), 0),
|
|
||||||
],
|
],
|
||||||
var_store,
|
ret_var: var_store.fresh(),
|
||||||
);
|
};
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
Symbol::INT_IS_EVEN,
|
Symbol::INT_IS_EVEN,
|
||||||
|
@ -246,12 +248,18 @@ fn list_len(var_store: &mut VarStore) -> Expr {
|
||||||
|
|
||||||
// Polymorphic wrapper around LowLevel::ListLen
|
// Polymorphic wrapper around LowLevel::ListLen
|
||||||
let arg = Symbol::LIST_LEN_ARG;
|
let arg = Symbol::LIST_LEN_ARG;
|
||||||
|
let arg_var = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
|
||||||
defn(
|
defn(
|
||||||
Symbol::LIST_LEN,
|
Symbol::LIST_LEN,
|
||||||
vec![arg],
|
vec![arg],
|
||||||
var_store,
|
var_store,
|
||||||
RunLowLevel(LowLevel::ListLen { arg }),
|
RunLowLevel {
|
||||||
|
op: LowLevel::ListLen,
|
||||||
|
args: vec![(arg_var, Var(arg))],
|
||||||
|
ret_var,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,11 +346,14 @@ fn int_rem(var_store: &mut VarStore) -> Expr {
|
||||||
// if condition
|
// if condition
|
||||||
no_region(
|
no_region(
|
||||||
// Int.neq arg1 0
|
// Int.neq arg1 0
|
||||||
call(
|
RunLowLevel {
|
||||||
Symbol::INT_NEQ_I64,
|
op: LowLevel::NotEq,
|
||||||
vec![Var(Symbol::INT_REM_ARG_1), (Int(var_store.fresh(), 0))],
|
args: vec![
|
||||||
var_store,
|
(var_store.fresh(), Var(Symbol::INT_REM_ARG_1)),
|
||||||
),
|
(var_store.fresh(), Int(var_store.fresh(), 0)),
|
||||||
|
],
|
||||||
|
ret_var: var_store.fresh(),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
// arg1 was not zero
|
// arg1 was not zero
|
||||||
no_region(
|
no_region(
|
||||||
|
@ -421,14 +432,14 @@ fn int_div(var_store: &mut VarStore) -> Expr {
|
||||||
// if-condition
|
// if-condition
|
||||||
no_region(
|
no_region(
|
||||||
// Int.neq denominator 0
|
// Int.neq denominator 0
|
||||||
call(
|
RunLowLevel {
|
||||||
Symbol::INT_NEQ_I64,
|
op: LowLevel::NotEq,
|
||||||
vec![
|
args: vec![
|
||||||
Var(Symbol::INT_DIV_ARG_DENOMINATOR),
|
(var_store.fresh(), Var(Symbol::INT_DIV_ARG_DENOMINATOR)),
|
||||||
(Int(var_store.fresh(), 0)),
|
(var_store.fresh(), Int(var_store.fresh(), 0)),
|
||||||
],
|
],
|
||||||
var_store,
|
ret_var: var_store.fresh(),
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
// denominator was not zero
|
// denominator was not zero
|
||||||
no_region(
|
no_region(
|
||||||
|
|
|
@ -90,7 +90,11 @@ pub enum Expr {
|
||||||
Vec<(Variable, Located<Expr>)>,
|
Vec<(Variable, Located<Expr>)>,
|
||||||
CalledVia,
|
CalledVia,
|
||||||
),
|
),
|
||||||
RunLowLevel(LowLevel),
|
RunLowLevel {
|
||||||
|
op: LowLevel,
|
||||||
|
args: Vec<(Variable, Expr)>,
|
||||||
|
ret_var: Variable,
|
||||||
|
},
|
||||||
|
|
||||||
Closure(
|
Closure(
|
||||||
Variable,
|
Variable,
|
||||||
|
|
|
@ -778,9 +778,45 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
exists(vars, And(arg_cons))
|
exists(vars, And(arg_cons))
|
||||||
}
|
}
|
||||||
RuntimeError(_) | RunLowLevel(_) => {
|
RunLowLevel { args, ret_var, op } => {
|
||||||
// RunLowLevel can only be added by the compiler, so it's safe
|
// This is a modified version of what we do for function calls.
|
||||||
// to assume its constraints have already been accounted for.
|
|
||||||
|
// The operation's return type
|
||||||
|
let ret_type = Variable(*ret_var);
|
||||||
|
|
||||||
|
// This will be used in the occurs check
|
||||||
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
vars.push(*ret_var);
|
||||||
|
|
||||||
|
let mut arg_types = Vec::with_capacity(args.len());
|
||||||
|
let mut arg_cons = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
for (index, (arg_var, arg)) in args.iter().enumerate() {
|
||||||
|
let arg_type = Variable(*arg_var);
|
||||||
|
let reason = Reason::LowLevelOpArg {
|
||||||
|
op: *op,
|
||||||
|
arg_index: Index::zero_based(index),
|
||||||
|
};
|
||||||
|
let expected_arg = ForReason(reason, arg_type.clone(), Region::zero());
|
||||||
|
let arg_con = constrain_expr(env, Region::zero(), arg, expected_arg);
|
||||||
|
|
||||||
|
vars.push(*arg_var);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let category = Category::LowLevelOpResult(*op);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, category, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
RuntimeError(_) => {
|
||||||
// Runtime Errors have no constraints because they're going to crash.
|
// Runtime Errors have no constraints because they're going to crash.
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
|
|
|
@ -801,6 +801,54 @@ pub fn constrain_expr(
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
RunLowLevel { op, args, ret_var } => {
|
||||||
|
// This is a modified version of what we do for function calls.
|
||||||
|
|
||||||
|
let ret_type = Variable(*ret_var);
|
||||||
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
vars.push(*ret_var);
|
||||||
|
|
||||||
|
// Canonicalize the function expression and its arguments
|
||||||
|
|
||||||
|
let mut arg_types = Vec::with_capacity(args.len());
|
||||||
|
let mut arg_cons = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
for (index, (arg_var, arg_expr)) in args.iter().enumerate() {
|
||||||
|
let arg_type = Variable(*arg_var);
|
||||||
|
|
||||||
|
let reason = Reason::LowLevelOpArg {
|
||||||
|
op: *op,
|
||||||
|
arg_index: Index::zero_based(index),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_arg = Expected::ForReason(reason, arg_type.clone(), region);
|
||||||
|
let arg_con = constrain_expr(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
var_usage,
|
||||||
|
applied_usage_constraint,
|
||||||
|
Region::zero(),
|
||||||
|
arg_expr,
|
||||||
|
expected_arg,
|
||||||
|
);
|
||||||
|
|
||||||
|
vars.push(*arg_var);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_uniq_type = var_store.fresh();
|
||||||
|
vars.push(expected_uniq_type);
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, Category::LowLevelOpResult(*op), region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
LetRec(defs, loc_ret, var, unlifted_aliases) => {
|
LetRec(defs, loc_ret, var, unlifted_aliases) => {
|
||||||
// NOTE doesn't currently unregister bound symbols
|
// NOTE doesn't currently unregister bound symbols
|
||||||
// may be a problem when symbols are not globally unique
|
// may be a problem when symbols are not globally unique
|
||||||
|
@ -1345,9 +1393,7 @@ pub fn constrain_expr(
|
||||||
And(vec![Eq(fn_type, expected, category, region), record_con]),
|
And(vec![Eq(fn_type, expected, category, region), record_con]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RuntimeError(_) | RunLowLevel(_) => {
|
RuntimeError(_) => {
|
||||||
// RunLowLevel can only be added by the compiler, so it's safe
|
|
||||||
// to assume its constraints have already been accounted for.
|
|
||||||
// Runtime Errors have no constraints because they're going to crash.
|
// Runtime Errors have no constraints because they're going to crash.
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::layout_id::LayoutIds;
|
use crate::layout_id::LayoutIds;
|
||||||
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
basic_type_from_layout, collection, get_fn_type, get_ptr_type, ptr_int,
|
basic_type_from_layout, collection, get_fn_type, get_ptr_type, ptr_int,
|
||||||
};
|
};
|
||||||
|
@ -34,7 +35,7 @@ pub enum OptLevel {
|
||||||
Optimize,
|
Optimize,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scope<'a, 'ctx> = ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>;
|
pub type Scope<'a, 'ctx> = ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>;
|
||||||
|
|
||||||
pub struct Env<'a, 'ctx, 'env> {
|
pub struct Env<'a, 'ctx, 'env> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
|
@ -711,12 +712,7 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
||||||
RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
todo!("LLVM build runtime error of {:?}", expr);
|
todo!("LLVM build runtime error of {:?}", expr);
|
||||||
}
|
}
|
||||||
RunLowLevel(op) => match op {
|
RunLowLevel(op, args) => run_low_level(env, layout_ids, scope, parent, *op, args),
|
||||||
LowLevel::ListLen { arg } => BasicValueEnum::IntValue(load_list_len(
|
|
||||||
env.builder,
|
|
||||||
load_symbol(env, scope, arg).into_struct_value(),
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,9 +847,12 @@ fn build_switch<'a, 'ctx, 'env>(
|
||||||
//
|
//
|
||||||
// they either need to all be i8, or i64
|
// they either need to all be i8, or i64
|
||||||
let int_val = match cond_layout {
|
let int_val = match cond_layout {
|
||||||
|
Layout::Builtin(Builtin::Int128) => context.i128_type().const_int(*int as u64, false), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */
|
||||||
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
|
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
|
||||||
Layout::Builtin(Builtin::Bool) => context.bool_type().const_int(*int as u64, false),
|
Layout::Builtin(Builtin::Int32) => context.i32_type().const_int(*int as u64, false),
|
||||||
Layout::Builtin(Builtin::Byte) => context.i8_type().const_int(*int as u64, false),
|
Layout::Builtin(Builtin::Int16) => context.i16_type().const_int(*int as u64, false),
|
||||||
|
Layout::Builtin(Builtin::Int8) => context.i8_type().const_int(*int as u64, false),
|
||||||
|
Layout::Builtin(Builtin::Int1) => context.bool_type().const_int(*int as u64, false),
|
||||||
_ => panic!("Can't cast to cond_layout = {:?}", cond_layout),
|
_ => panic!("Can't cast to cond_layout = {:?}", cond_layout),
|
||||||
};
|
};
|
||||||
let block = context.append_basic_block(parent, format!("branch{}", int).as_str());
|
let block = context.append_basic_block(parent, format!("branch{}", int).as_str());
|
||||||
|
@ -1266,78 +1265,6 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
Symbol::INT_EQ_I64 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i64",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
|
||||||
}
|
|
||||||
Symbol::INT_NEQ_I64 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::NE,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i64",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
|
||||||
}
|
|
||||||
Symbol::INT_EQ_I1 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i1",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
|
||||||
}
|
|
||||||
Symbol::INT_NEQ_I1 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::NE,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i1",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
|
||||||
}
|
|
||||||
Symbol::INT_EQ_I8 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::EQ,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i8",
|
|
||||||
);
|
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
|
||||||
}
|
|
||||||
Symbol::INT_NEQ_I8 => {
|
|
||||||
debug_assert!(args.len() == 2);
|
|
||||||
|
|
||||||
let int_val = env.builder.build_int_compare(
|
|
||||||
IntPredicate::NE,
|
|
||||||
args[0].0.into_int_value(),
|
|
||||||
args[1].0.into_int_value(),
|
|
||||||
"cmp_i8",
|
|
||||||
);
|
|
||||||
|
|
||||||
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_";
|
||||||
|
@ -1549,7 +1476,7 @@ fn call_intrinsic<'a, 'ctx, 'env>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_list_len<'ctx>(
|
pub fn load_list_len<'ctx>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
wrapper_struct: StructValue<'ctx>,
|
wrapper_struct: StructValue<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
|
@ -1739,3 +1666,44 @@ pub fn get_call_conventions(cc: CallingConvention) -> u32 {
|
||||||
/// Source: https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html
|
/// Source: https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html
|
||||||
pub static C_CALL_CONV: u32 = 0;
|
pub static C_CALL_CONV: u32 = 0;
|
||||||
pub static COLD_CALL_CONV: u32 = 9;
|
pub static COLD_CALL_CONV: u32 = 9;
|
||||||
|
|
||||||
|
fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
scope: &Scope<'a, 'ctx>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
op: LowLevel,
|
||||||
|
args: &[(Expr<'a>, Layout<'a>)],
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
use LowLevel::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
ListLen => {
|
||||||
|
debug_assert!(args.len() == 1);
|
||||||
|
|
||||||
|
let arg = build_expr(env, layout_ids, scope, parent, &args[0].0);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(load_list_len(env.builder, arg.into_struct_value()))
|
||||||
|
}
|
||||||
|
Eq => {
|
||||||
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0);
|
||||||
|
let lhs_layout = &args[0].1;
|
||||||
|
let rhs_arg = build_expr(env, layout_ids, scope, parent, &args[1].0);
|
||||||
|
let rhs_layout = &args[1].1;
|
||||||
|
|
||||||
|
build_eq(env, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||||
|
}
|
||||||
|
NotEq => {
|
||||||
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
let lhs_arg = build_expr(env, layout_ids, scope, parent, &args[0].0);
|
||||||
|
let lhs_layout = &args[0].1;
|
||||||
|
let rhs_arg = build_expr(env, layout_ids, scope, parent, &args[1].0);
|
||||||
|
let rhs_layout = &args[1].1;
|
||||||
|
|
||||||
|
build_neq(env, lhs_arg, rhs_arg, lhs_layout, rhs_layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
112
compiler/gen/src/llvm/compare.rs
Normal file
112
compiler/gen/src/llvm/compare.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
use crate::llvm::build::Env;
|
||||||
|
use inkwell::values::BasicValueEnum;
|
||||||
|
use inkwell::{FloatPredicate, IntPredicate};
|
||||||
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
|
pub fn build_eq<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
lhs_val: BasicValueEnum<'ctx>,
|
||||||
|
rhs_val: BasicValueEnum<'ctx>,
|
||||||
|
lhs_layout: &Layout<'a>,
|
||||||
|
rhs_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
match (lhs_layout, rhs_layout) {
|
||||||
|
(Layout::Builtin(lhs_builtin), Layout::Builtin(rhs_builtin)) => {
|
||||||
|
let int_cmp = |pred, label| {
|
||||||
|
let int_val = env.builder.build_int_compare(
|
||||||
|
pred,
|
||||||
|
lhs_val.into_int_value(),
|
||||||
|
rhs_val.into_int_value(),
|
||||||
|
label,
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
};
|
||||||
|
|
||||||
|
let float_cmp = |pred, label| {
|
||||||
|
let int_val = env.builder.build_float_compare(
|
||||||
|
pred,
|
||||||
|
lhs_val.into_float_value(),
|
||||||
|
rhs_val.into_float_value(),
|
||||||
|
label,
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
};
|
||||||
|
|
||||||
|
match (lhs_builtin, rhs_builtin) {
|
||||||
|
(Builtin::Int128, Builtin::Int128) => int_cmp(IntPredicate::EQ, "eq_i128"),
|
||||||
|
(Builtin::Int64, Builtin::Int64) => int_cmp(IntPredicate::EQ, "eq_i64"),
|
||||||
|
(Builtin::Int32, Builtin::Int32) => int_cmp(IntPredicate::EQ, "eq_i32"),
|
||||||
|
(Builtin::Int16, Builtin::Int16) => int_cmp(IntPredicate::EQ, "eq_i16"),
|
||||||
|
(Builtin::Int8, Builtin::Int8) => int_cmp(IntPredicate::EQ, "eq_i8"),
|
||||||
|
(Builtin::Int1, Builtin::Int1) => int_cmp(IntPredicate::EQ, "eq_i1"),
|
||||||
|
(Builtin::Float64, Builtin::Float64) => float_cmp(FloatPredicate::OEQ, "eq_f64"),
|
||||||
|
(Builtin::Float32, Builtin::Float32) => float_cmp(FloatPredicate::OEQ, "eq_f32"),
|
||||||
|
(b1, b2) => {
|
||||||
|
todo!("Handle equals for builtin layouts {:?} == {:?}", b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(other1, other2) => {
|
||||||
|
// TODO NOTE: This should ultimately have a _ => todo!("type mismatch!") branch
|
||||||
|
todo!("implement equals for layouts {:?} == {:?}", other1, other2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_neq<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
lhs_val: BasicValueEnum<'ctx>,
|
||||||
|
rhs_val: BasicValueEnum<'ctx>,
|
||||||
|
lhs_layout: &Layout<'a>,
|
||||||
|
rhs_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
match (lhs_layout, rhs_layout) {
|
||||||
|
(Layout::Builtin(lhs_builtin), Layout::Builtin(rhs_builtin)) => {
|
||||||
|
let int_cmp = |pred, label| {
|
||||||
|
let int_val = env.builder.build_int_compare(
|
||||||
|
pred,
|
||||||
|
lhs_val.into_int_value(),
|
||||||
|
rhs_val.into_int_value(),
|
||||||
|
label,
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
};
|
||||||
|
|
||||||
|
let float_cmp = |pred, label| {
|
||||||
|
let int_val = env.builder.build_float_compare(
|
||||||
|
pred,
|
||||||
|
lhs_val.into_float_value(),
|
||||||
|
rhs_val.into_float_value(),
|
||||||
|
label,
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
};
|
||||||
|
|
||||||
|
match (lhs_builtin, rhs_builtin) {
|
||||||
|
(Builtin::Int128, Builtin::Int128) => int_cmp(IntPredicate::NE, "neq_i128"),
|
||||||
|
(Builtin::Int64, Builtin::Int64) => int_cmp(IntPredicate::NE, "neq_i64"),
|
||||||
|
(Builtin::Int32, Builtin::Int32) => int_cmp(IntPredicate::NE, "neq_i32"),
|
||||||
|
(Builtin::Int16, Builtin::Int16) => int_cmp(IntPredicate::NE, "neq_i16"),
|
||||||
|
(Builtin::Int8, Builtin::Int8) => int_cmp(IntPredicate::NE, "neq_i8"),
|
||||||
|
(Builtin::Int1, Builtin::Int1) => int_cmp(IntPredicate::NE, "neq_i1"),
|
||||||
|
(Builtin::Float64, Builtin::Float64) => float_cmp(FloatPredicate::ONE, "neq_f64"),
|
||||||
|
(Builtin::Float32, Builtin::Float32) => float_cmp(FloatPredicate::ONE, "neq_f32"),
|
||||||
|
(b1, b2) => {
|
||||||
|
todo!("Handle not equals for builtin layouts {:?} == {:?}", b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(other1, other2) => {
|
||||||
|
// TODO NOTE: This should ultimately have a _ => todo!("type mismatch!") branch
|
||||||
|
todo!(
|
||||||
|
"implement not equals for layouts {:?} == {:?}",
|
||||||
|
other1,
|
||||||
|
other2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,10 +121,14 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
|
Int128 => context.i128_type().as_basic_type_enum(),
|
||||||
Int64 => context.i64_type().as_basic_type_enum(),
|
Int64 => context.i64_type().as_basic_type_enum(),
|
||||||
|
Int32 => context.i32_type().as_basic_type_enum(),
|
||||||
|
Int16 => context.i16_type().as_basic_type_enum(),
|
||||||
|
Int8 => context.i8_type().as_basic_type_enum(),
|
||||||
|
Int1 => context.bool_type().as_basic_type_enum(),
|
||||||
Float64 => context.f64_type().as_basic_type_enum(),
|
Float64 => context.f64_type().as_basic_type_enum(),
|
||||||
Bool => context.bool_type().as_basic_type_enum(),
|
Float32 => context.f32_type().as_basic_type_enum(),
|
||||||
Byte => context.i8_type().as_basic_type_enum(),
|
|
||||||
Str | EmptyStr => context
|
Str | EmptyStr => context
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
pub mod compare;
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::symbol::Symbol;
|
|
||||||
|
|
||||||
/// Low-level operations that get translated directly into e.g. LLVM instructions.
|
/// Low-level operations that get translated directly into e.g. LLVM instructions.
|
||||||
/// These are always wrapped when exposed to end users, and can only make it
|
/// These are always wrapped when exposed to end users, and can only make it
|
||||||
/// into an Expr when added directly by can::builtins
|
/// into an Expr when added directly by can::builtins
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum LowLevel {
|
pub enum LowLevel {
|
||||||
ListLen { arg: Symbol },
|
/// List.len
|
||||||
|
ListLen,
|
||||||
|
Eq,
|
||||||
|
NotEq,
|
||||||
}
|
}
|
||||||
|
|
|
@ -600,35 +600,29 @@ define_builtins! {
|
||||||
6 INT_LOWEST: "lowest"
|
6 INT_LOWEST: "lowest"
|
||||||
7 INT_ADD: "#add"
|
7 INT_ADD: "#add"
|
||||||
8 INT_SUB: "#sub"
|
8 INT_SUB: "#sub"
|
||||||
9 INT_EQ_I64: "#eqi64" // Equality on 64-bit integers, the standard in Roc
|
9 INT_DIV_UNSAFE: "divUnsafe" // TODO remove once we can code gen Result
|
||||||
10 INT_EQ_I1: "#eqi1" // Equality on boolean (theoretically i1) values
|
10 INT_LT: "#lt"
|
||||||
11 INT_EQ_I8: "#eqi8" // Equality on byte (theoretically i8) values
|
11 INT_LTE: "#lte"
|
||||||
12 INT_DIV_UNSAFE: "divUnsafe" // TODO remove once we can code gen Result
|
12 INT_GT: "#gt"
|
||||||
13 INT_LT: "#lt"
|
13 INT_GTE: "#gte"
|
||||||
14 INT_LTE: "#lte"
|
14 INT_DIV_ARG_NUMERATOR: "div#numerator" // The first argument to `//`, the numerator
|
||||||
15 INT_GT: "#gt"
|
15 INT_DIV_ARG_DENOMINATOR: "div#denominator" // The first argument to `//`, the denominator
|
||||||
16 INT_GTE: "#gte"
|
16 INT_ABS: "abs"
|
||||||
17 INT_DIV_ARG_NUMERATOR: "div#numerator" // The first argument to `//`, the numerator
|
17 INT_ABS_ARG: "abs#arg"
|
||||||
18 INT_DIV_ARG_DENOMINATOR: "div#denominator" // The first argument to `//`, the denominator
|
18 INT_REM_UNSAFE: "remUnsafe"
|
||||||
19 INT_NEQ_I64: "#neqi64"
|
19 INT_REM: "rem"
|
||||||
20 INT_NEQ_I1: "#neqi1"
|
20 INT_REM_ARG_0: "rem#arg0"
|
||||||
21 INT_NEQ_I8: "#neqi8"
|
21 INT_REM_ARG_1: "rem#arg1"
|
||||||
22 INT_ABS: "abs"
|
22 INT_IS_ODD: "isOdd"
|
||||||
23 INT_ABS_ARG: "abs#arg"
|
23 INT_IS_ODD_ARG: "isOdd#arg"
|
||||||
24 INT_REM_UNSAFE: "remUnsafe"
|
24 INT_IS_EVEN: "isEven"
|
||||||
25 INT_REM: "rem"
|
25 INT_IS_EVEN_ARG: "isEven#arg"
|
||||||
26 INT_REM_ARG_0: "rem#arg0"
|
26 INT_IS_ZERO: "isZero"
|
||||||
27 INT_REM_ARG_1: "rem#arg1"
|
27 INT_IS_ZERO_ARG: "isZero#arg"
|
||||||
28 INT_IS_ODD: "isOdd"
|
28 INT_IS_POSITIVE: "isPositive"
|
||||||
29 INT_IS_ODD_ARG: "isOdd#arg"
|
29 INT_IS_POSITIVE_ARG: "isPositive#arg"
|
||||||
30 INT_IS_EVEN: "isEven"
|
30 INT_IS_NEGATIVE: "isNegative"
|
||||||
31 INT_IS_EVEN_ARG: "isEven#arg"
|
31 INT_IS_NEGATIVE_ARG: "isNegative#arg"
|
||||||
32 INT_IS_ZERO: "isZero"
|
|
||||||
33 INT_IS_ZERO_ARG: "isZero#arg"
|
|
||||||
34 INT_IS_POSITIVE: "isPositive"
|
|
||||||
35 INT_IS_POSITIVE_ARG: "isPositive#arg"
|
|
||||||
36 INT_IS_NEGATIVE: "isNegative"
|
|
||||||
37 INT_IS_NEGATIVE_ARG: "isNegative#arg"
|
|
||||||
}
|
}
|
||||||
3 FLOAT: "Float" => {
|
3 FLOAT: "Float" => {
|
||||||
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use crate::expr::Env;
|
use crate::expr::Env;
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::expr::Pattern;
|
use crate::expr::Pattern;
|
||||||
use bumpalo::Bump;
|
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
|
||||||
use roc_module::ident::TagName;
|
|
||||||
use roc_module::symbol::Symbol;
|
|
||||||
|
|
||||||
use crate::expr::specialize_equality;
|
|
||||||
use crate::layout::Builtin;
|
use crate::layout::Builtin;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::pattern::{Ctor, RenderAs, TagId, Union};
|
use crate::pattern::{Ctor, RenderAs, TagId, Union};
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
|
use roc_module::ident::TagName;
|
||||||
|
use roc_module::low_level::LowLevel;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
/// COMPILE CASES
|
/// COMPILE CASES
|
||||||
|
|
||||||
|
@ -1030,14 +1029,14 @@ fn test_to_equality<'a>(
|
||||||
let lhs = Expr::Byte(test_byte);
|
let lhs = Expr::Byte(test_byte);
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Byte)));
|
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsBit(test_bit) => {
|
Test::IsBit(test_bit) => {
|
||||||
let lhs = Expr::Bool(test_bit);
|
let lhs = Expr::Bool(test_bit);
|
||||||
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
let rhs = path_to_expr(env, cond_symbol, &path, &cond_layout);
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Bool)));
|
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::IsStr(test_str) => {
|
Test::IsStr(test_str) => {
|
||||||
|
@ -1059,7 +1058,7 @@ fn test_to_equality<'a>(
|
||||||
let lhs = Expr::Bool(true);
|
let lhs = Expr::Bool(true);
|
||||||
let rhs = Expr::Store(stores, env.arena.alloc(expr));
|
let rhs = Expr::Store(stores, env.arena.alloc(expr));
|
||||||
|
|
||||||
tests.push((lhs, rhs, Layout::Builtin(Builtin::Bool)));
|
tests.push((lhs, rhs, Layout::Builtin(Builtin::Int8)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1122,9 +1121,9 @@ fn decide_to_branching<'a>(
|
||||||
let condition = boolean_all(env.arena, tests);
|
let condition = boolean_all(env.arena, tests);
|
||||||
|
|
||||||
let branch_symbol = env.unique_symbol();
|
let branch_symbol = env.unique_symbol();
|
||||||
let stores = [(branch_symbol, Layout::Builtin(Builtin::Bool), condition)];
|
let stores = [(branch_symbol, Layout::Builtin(Builtin::Int8), condition)];
|
||||||
|
|
||||||
let cond_layout = Layout::Builtin(Builtin::Bool);
|
let cond_layout = Layout::Builtin(Builtin::Int8);
|
||||||
|
|
||||||
(
|
(
|
||||||
env.arena.alloc(stores),
|
env.arena.alloc(stores),
|
||||||
|
@ -1203,14 +1202,17 @@ fn boolean_all<'a>(arena: &'a Bump, tests: Vec<(Expr<'a>, Expr<'a>, Layout<'a>)>
|
||||||
let mut expr = Expr::Bool(true);
|
let mut expr = Expr::Bool(true);
|
||||||
|
|
||||||
for (lhs, rhs, layout) in tests.into_iter().rev() {
|
for (lhs, rhs, layout) in tests.into_iter().rev() {
|
||||||
let test = specialize_equality(arena, lhs, rhs, layout.clone());
|
let test = Expr::RunLowLevel(
|
||||||
|
LowLevel::Eq,
|
||||||
|
bumpalo::vec![in arena; (lhs, layout.clone()), (rhs, layout.clone())].into_bump_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
expr = Expr::CallByName {
|
expr = Expr::CallByName {
|
||||||
name: Symbol::BOOL_AND,
|
name: Symbol::BOOL_AND,
|
||||||
layout,
|
layout,
|
||||||
args: arena.alloc([
|
args: arena.alloc([
|
||||||
(test, Layout::Builtin(Builtin::Bool)),
|
(test, Layout::Builtin(Builtin::Int8)),
|
||||||
(expr, Layout::Builtin(Builtin::Bool)),
|
(expr, Layout::Builtin(Builtin::Int8)),
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ pub enum Expr<'a> {
|
||||||
args: &'a [(Expr<'a>, Layout<'a>)],
|
args: &'a [(Expr<'a>, Layout<'a>)],
|
||||||
},
|
},
|
||||||
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>], Layout<'a>),
|
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>], Layout<'a>),
|
||||||
RunLowLevel(LowLevel),
|
RunLowLevel(LowLevel, &'a [(Expr<'a>, Layout<'a>)]),
|
||||||
|
|
||||||
// Exactly two conditional branches, e.g. if/else
|
// Exactly two conditional branches, e.g. if/else
|
||||||
Cond {
|
Cond {
|
||||||
|
@ -572,7 +572,20 @@ fn from_can<'a>(
|
||||||
Expr::FunctionPointer(name, layout)
|
Expr::FunctionPointer(name, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
RunLowLevel(op) => Expr::RunLowLevel(op),
|
RunLowLevel { op, args, .. } => {
|
||||||
|
let mut mono_args = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
|
for (arg_var, arg_expr) in args {
|
||||||
|
let arg = from_can(env, arg_expr, procs, layout_cache);
|
||||||
|
let layout = layout_cache
|
||||||
|
.from_var(env.arena, arg_var, env.subs, env.pointer_size)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||||
|
|
||||||
|
mono_args.push((arg, layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::RunLowLevel(op, mono_args.into_bump_slice())
|
||||||
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||||
|
@ -581,13 +594,8 @@ 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 specialize_builtin_functions(
|
match specialize_builtin_functions(env, proc_name, loc_args.as_slice(), ret_var)
|
||||||
env,
|
{
|
||||||
proc_name,
|
|
||||||
loc_args.as_slice(),
|
|
||||||
ret_var,
|
|
||||||
layout_cache,
|
|
||||||
) {
|
|
||||||
Symbol::LIST_SET => {
|
Symbol::LIST_SET => {
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
// The first arg is the one with the List in it.
|
// The first arg is the one with the List in it.
|
||||||
|
@ -698,6 +706,7 @@ fn from_can<'a>(
|
||||||
final_else,
|
final_else,
|
||||||
} => {
|
} => {
|
||||||
let mut expr = from_can(env, final_else.value, procs, layout_cache);
|
let mut expr = from_can(env, final_else.value, procs, layout_cache);
|
||||||
|
let arena = env.arena;
|
||||||
|
|
||||||
let ret_layout = layout_cache
|
let ret_layout = layout_cache
|
||||||
.from_var(env.arena, branch_var, env.subs, env.pointer_size)
|
.from_var(env.arena, branch_var, env.subs, env.pointer_size)
|
||||||
|
@ -722,8 +731,8 @@ fn from_can<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
expr = Expr::Store(
|
expr = Expr::Store(
|
||||||
env.arena
|
bumpalo::vec![in arena; (branch_symbol, Layout::Builtin(Builtin::Int8), cond)]
|
||||||
.alloc(vec![(branch_symbol, Layout::Builtin(Builtin::Bool), cond)]),
|
.into_bump_slice(),
|
||||||
env.arena.alloc(cond_expr),
|
env.arena.alloc(cond_expr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1469,7 +1478,7 @@ pub fn specialize_all<'a>(
|
||||||
for (name, mut by_layout) in pending_specializations.drain() {
|
for (name, mut by_layout) in pending_specializations.drain() {
|
||||||
for (layout, pending) in by_layout.drain() {
|
for (layout, pending) in by_layout.drain() {
|
||||||
// TODO should pending_procs hold a Rc<Proc>?
|
// TODO should pending_procs hold a Rc<Proc>?
|
||||||
let partial_proc = procs
|
let partial_proc = dbg!(&procs)
|
||||||
.partial_procs
|
.partial_procs
|
||||||
.get(&name)
|
.get(&name)
|
||||||
.unwrap_or_else(|| panic!("Could not find partial_proc for {:?}", name))
|
.unwrap_or_else(|| panic!("Could not find partial_proc for {:?}", name))
|
||||||
|
@ -1847,36 +1856,11 @@ fn from_can_record_destruct<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn specialize_equality<'a>(
|
|
||||||
arena: &'a Bump,
|
|
||||||
lhs: Expr<'a>,
|
|
||||||
rhs: Expr<'a>,
|
|
||||||
layout: Layout<'a>,
|
|
||||||
) -> Expr<'a> {
|
|
||||||
let name = match &layout {
|
|
||||||
Layout::Builtin(builtin) => match builtin {
|
|
||||||
Builtin::Int64 => Symbol::INT_EQ_I64,
|
|
||||||
Builtin::Float64 => Symbol::FLOAT_EQ,
|
|
||||||
Builtin::Byte => Symbol::INT_EQ_I8,
|
|
||||||
Builtin::Bool => Symbol::INT_EQ_I1,
|
|
||||||
other => todo!("Cannot yet compare for equality {:?}", other),
|
|
||||||
},
|
|
||||||
other => todo!("Cannot yet compare for equality {:?}", other),
|
|
||||||
};
|
|
||||||
|
|
||||||
Expr::CallByName {
|
|
||||||
name,
|
|
||||||
layout: layout.clone(),
|
|
||||||
args: arena.alloc([(lhs, layout.clone()), (rhs, layout)]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn specialize_builtin_functions<'a>(
|
fn specialize_builtin_functions<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
loc_args: &[(Variable, Located<roc_can::expr::Expr>)],
|
loc_args: &[(Variable, Located<roc_can::expr::Expr>)],
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
use IntOrFloat::*;
|
use IntOrFloat::*;
|
||||||
|
|
||||||
|
@ -1884,6 +1868,13 @@ fn specialize_builtin_functions<'a>(
|
||||||
// return unchanged
|
// return unchanged
|
||||||
symbol
|
symbol
|
||||||
} else {
|
} else {
|
||||||
|
if true {
|
||||||
|
todo!(
|
||||||
|
"replace specialize_builtin_functions({:?}) with a LowLevel op",
|
||||||
|
symbol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_ADD => match num_to_int_or_float(env.subs, ret_var) {
|
Symbol::NUM_ADD => match num_to_int_or_float(env.subs, ret_var) {
|
||||||
FloatType => Symbol::FLOAT_ADD,
|
FloatType => Symbol::FLOAT_ADD,
|
||||||
|
@ -1909,38 +1900,6 @@ fn specialize_builtin_functions<'a>(
|
||||||
FloatType => Symbol::FLOAT_GT,
|
FloatType => Symbol::FLOAT_GT,
|
||||||
IntType => Symbol::INT_GT,
|
IntType => Symbol::INT_GT,
|
||||||
},
|
},
|
||||||
// TODO make this work for more than just int/float
|
|
||||||
Symbol::BOOL_EQ => {
|
|
||||||
match layout_cache.from_var(env.arena, loc_args[0].0, env.subs, env.pointer_size) {
|
|
||||||
Ok(Layout::Builtin(builtin)) => match builtin {
|
|
||||||
Builtin::Int64 => Symbol::INT_EQ_I64,
|
|
||||||
Builtin::Float64 => Symbol::FLOAT_EQ,
|
|
||||||
Builtin::Bool => Symbol::INT_EQ_I1,
|
|
||||||
Builtin::Byte => Symbol::INT_EQ_I8,
|
|
||||||
_ => panic!("Equality not implemented for {:?}", builtin),
|
|
||||||
},
|
|
||||||
Ok(complex) => panic!(
|
|
||||||
"TODO support equality on complex layouts like {:?}",
|
|
||||||
complex
|
|
||||||
),
|
|
||||||
Err(()) => panic!("Invalid layout"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Symbol::BOOL_NEQ => {
|
|
||||||
match layout_cache.from_var(env.arena, loc_args[0].0, env.subs, env.pointer_size) {
|
|
||||||
Ok(Layout::Builtin(builtin)) => match builtin {
|
|
||||||
Builtin::Int64 => Symbol::INT_NEQ_I64,
|
|
||||||
Builtin::Bool => Symbol::INT_NEQ_I1,
|
|
||||||
Builtin::Byte => Symbol::INT_NEQ_I8,
|
|
||||||
_ => panic!("Not-Equality not implemented for {:?}", builtin),
|
|
||||||
},
|
|
||||||
Ok(complex) => panic!(
|
|
||||||
"TODO support equality on complex layouts like {:?}",
|
|
||||||
complex
|
|
||||||
),
|
|
||||||
Err(()) => panic!("Invalid layout"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => symbol,
|
_ => symbol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,14 @@ pub enum Layout<'a> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Builtin<'a> {
|
pub enum Builtin<'a> {
|
||||||
|
Int128,
|
||||||
Int64,
|
Int64,
|
||||||
|
Int32,
|
||||||
|
Int16,
|
||||||
|
Int8,
|
||||||
|
Int1,
|
||||||
Float64,
|
Float64,
|
||||||
Bool,
|
Float32,
|
||||||
Byte,
|
|
||||||
Str,
|
Str,
|
||||||
Map(&'a Layout<'a>, &'a Layout<'a>),
|
Map(&'a Layout<'a>, &'a Layout<'a>),
|
||||||
Set(&'a Layout<'a>),
|
Set(&'a Layout<'a>),
|
||||||
|
@ -169,10 +173,14 @@ impl<'a> LayoutCache<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builtin<'a> {
|
impl<'a> Builtin<'a> {
|
||||||
|
const I128_SIZE: u32 = std::mem::size_of::<i128>() as u32;
|
||||||
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
||||||
|
const I32_SIZE: u32 = std::mem::size_of::<i32>() as u32;
|
||||||
|
const I16_SIZE: u32 = std::mem::size_of::<i16>() as u32;
|
||||||
|
const I8_SIZE: u32 = std::mem::size_of::<i8>() as u32;
|
||||||
|
const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
||||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||||
const BOOL_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
const F32_SIZE: u32 = std::mem::size_of::<f32>() as u32;
|
||||||
const BYTE_SIZE: u32 = std::mem::size_of::<u8>() as u32;
|
|
||||||
|
|
||||||
/// Number of machine words in an empty one of these
|
/// Number of machine words in an empty one of these
|
||||||
pub const STR_WORDS: u32 = 2;
|
pub const STR_WORDS: u32 = 2;
|
||||||
|
@ -180,7 +188,7 @@ impl<'a> Builtin<'a> {
|
||||||
pub const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value
|
pub const SET_WORDS: u32 = Builtin::MAP_WORDS; // Set is an alias for Map with {} for value
|
||||||
pub const LIST_WORDS: u32 = 2;
|
pub const LIST_WORDS: u32 = 2;
|
||||||
|
|
||||||
/// Layout of collection wrapper for List and Str - a struct of (pointre, length).
|
/// Layout of collection wrapper for List and Str - a struct of (pointer, length).
|
||||||
///
|
///
|
||||||
/// We choose this layout (with pointer first) because it's how
|
/// We choose this layout (with pointer first) because it's how
|
||||||
/// Rust slices are laid out, meaning we can cast to/from them for free.
|
/// Rust slices are laid out, meaning we can cast to/from them for free.
|
||||||
|
@ -191,10 +199,14 @@ impl<'a> Builtin<'a> {
|
||||||
use Builtin::*;
|
use Builtin::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
Int128 => Builtin::I128_SIZE,
|
||||||
Int64 => Builtin::I64_SIZE,
|
Int64 => Builtin::I64_SIZE,
|
||||||
|
Int32 => Builtin::I32_SIZE,
|
||||||
|
Int16 => Builtin::I16_SIZE,
|
||||||
|
Int8 => Builtin::I8_SIZE,
|
||||||
|
Int1 => Builtin::I1_SIZE,
|
||||||
Float64 => Builtin::F64_SIZE,
|
Float64 => Builtin::F64_SIZE,
|
||||||
Bool => Builtin::BOOL_SIZE,
|
Float32 => Builtin::F32_SIZE,
|
||||||
Byte => Builtin::BYTE_SIZE,
|
|
||||||
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
|
||||||
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
Map(_, _) | EmptyMap => Builtin::MAP_WORDS * pointer_size,
|
||||||
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
|
||||||
|
@ -206,7 +218,8 @@ impl<'a> Builtin<'a> {
|
||||||
use Builtin::*;
|
use Builtin::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Int64 | Float64 | Bool | Byte | EmptyStr | EmptyMap | EmptyList | EmptySet => true,
|
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float64 | Float32 | EmptyStr
|
||||||
|
| EmptyMap | EmptyList | EmptySet => true,
|
||||||
Str | Map(_, _) | Set(_) | List(_) => false,
|
Str | Map(_, _) | Set(_) | List(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,8 +494,8 @@ pub fn layout_from_tag_union<'a>(
|
||||||
match variant {
|
match variant {
|
||||||
Never => panic!("TODO gracefully handle trying to instantiate Never"),
|
Never => panic!("TODO gracefully handle trying to instantiate Never"),
|
||||||
Unit => Layout::Struct(&[]),
|
Unit => Layout::Struct(&[]),
|
||||||
BoolUnion { .. } => Layout::Builtin(Builtin::Bool),
|
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||||
ByteUnion(_) => Layout::Builtin(Builtin::Byte),
|
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||||
Unwrapped(field_layouts) => match first_tag.0 {
|
Unwrapped(field_layouts) => match first_tag.0 {
|
||||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||||
let arguments = first_tag.1;
|
let arguments = first_tag.1;
|
||||||
|
|
|
@ -765,6 +765,13 @@ fn to_expr_report<'b>(
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reason::LowLevelOpArg { op, arg_index } => {
|
||||||
|
panic!(
|
||||||
|
"Compiler bug: argument #{} to low-level operation {:?} was the wrong type!",
|
||||||
|
arg_index.ordinal(),
|
||||||
|
op
|
||||||
|
);
|
||||||
|
}
|
||||||
Reason::FloatLiteral | Reason::IntLiteral | Reason::NumLiteral => {
|
Reason::FloatLiteral | Reason::IntLiteral | Reason::NumLiteral => {
|
||||||
unreachable!("I don't think these can be reached")
|
unreachable!("I don't think these can be reached")
|
||||||
}
|
}
|
||||||
|
@ -892,6 +899,12 @@ fn add_category<'b>(
|
||||||
alloc.text(" call produces:"),
|
alloc.text(" call produces:"),
|
||||||
]),
|
]),
|
||||||
CallResult(None) => alloc.concat(vec![this_is, alloc.text(":")]),
|
CallResult(None) => alloc.concat(vec![this_is, alloc.text(":")]),
|
||||||
|
LowLevelOpResult(op) => {
|
||||||
|
panic!(
|
||||||
|
"Compiler bug: invalid return type from low-level op {:?}",
|
||||||
|
op
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Uniqueness => alloc.concat(vec![
|
Uniqueness => alloc.concat(vec![
|
||||||
this_is,
|
this_is,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::subs::{Subs, VarStore, Variable};
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap};
|
use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -630,6 +631,10 @@ pub enum Reason {
|
||||||
name: Option<Symbol>,
|
name: Option<Symbol>,
|
||||||
arity: u8,
|
arity: u8,
|
||||||
},
|
},
|
||||||
|
LowLevelOpArg {
|
||||||
|
op: LowLevel,
|
||||||
|
arg_index: Index,
|
||||||
|
},
|
||||||
FloatLiteral,
|
FloatLiteral,
|
||||||
IntLiteral,
|
IntLiteral,
|
||||||
NumLiteral,
|
NumLiteral,
|
||||||
|
@ -654,6 +659,7 @@ pub enum Reason {
|
||||||
pub enum Category {
|
pub enum Category {
|
||||||
Lookup(Symbol),
|
Lookup(Symbol),
|
||||||
CallResult(Option<Symbol>),
|
CallResult(Option<Symbol>),
|
||||||
|
LowLevelOpResult(LowLevel),
|
||||||
TagApply(TagName),
|
TagApply(TagName),
|
||||||
Lambda,
|
Lambda,
|
||||||
Uniqueness,
|
Uniqueness,
|
||||||
|
|
|
@ -630,8 +630,13 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RunLowLevel(op) => {
|
RunLowLevel { op, args, ret_var } => {
|
||||||
todo!("TODO implement UNIQ RunLowLevel for {:?}", op);
|
todo!(
|
||||||
|
"TODO implement UNIQ RunLowLevel for {:?}({:?}) -> {:?}",
|
||||||
|
op,
|
||||||
|
args,
|
||||||
|
ret_var
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Closure(_, _, _, _, body) => {
|
Closure(_, _, _, _, body) => {
|
||||||
annotate_usage(&body.0.value, usage);
|
annotate_usage(&body.0.value, usage);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue