Code gen numeric comparisons

This commit is contained in:
Richard Feldman 2020-04-28 18:33:50 -04:00
parent d751327914
commit c39b43a265
6 changed files with 197 additions and 13 deletions

View file

@ -241,7 +241,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// isLte or (<=) : Num a, Num a -> Bool
add_type(
Symbol::NUM_LE,
Symbol::NUM_LTE,
SolvedType::Func(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(bool_type()),
@ -259,7 +259,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// isGte or (>=) : Num a, Num a -> Bool
add_type(
Symbol::NUM_GE,
Symbol::NUM_GTE,
SolvedType::Func(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(bool_type()),

View file

@ -309,7 +309,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// isLte or (<=) : Num a, Num a -> Bool
add_type(
Symbol::NUM_LE,
Symbol::NUM_LTE,
unique_function(
vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)],
bool_type(UVAR3),
@ -327,7 +327,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// isGte or (>=) : Num a, Num a -> Bool
add_type(
Symbol::NUM_GE,
Symbol::NUM_GTE,
unique_function(
vec![num_type(UVAR1, TVAR1), num_type(UVAR2, TVAR1)],
bool_type(UVAR3),

View file

@ -1060,11 +1060,22 @@ fn call_with_args<'a, 'ctx, 'env>(
let int_val = env.builder.build_int_sub(
args[0].0.into_int_value(),
args[1].0.into_int_value(),
"sub_I64",
"sub_i64",
);
BasicValueEnum::IntValue(int_val)
}
Symbol::FLOAT_DIV => {
debug_assert!(args.len() == 2);
let float_val = env.builder.build_float_div(
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"div_f64",
);
BasicValueEnum::FloatValue(float_val)
}
Symbol::FLOAT_SUB => {
debug_assert!(args.len() == 2);
@ -1076,16 +1087,101 @@ fn call_with_args<'a, 'ctx, 'env>(
BasicValueEnum::FloatValue(float_val)
}
Symbol::FLOAT_DIV => {
Symbol::INT_GTE | Symbol::NUM_GTE => {
debug_assert!(args.len() == 2);
let float_val = env.builder.build_float_div(
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"div_f64",
let bool_val = env.builder.build_int_compare(
IntPredicate::SGE,
args[0].0.into_int_value(),
args[1].0.into_int_value(),
"gte_i64",
);
BasicValueEnum::FloatValue(float_val)
BasicValueEnum::IntValue(bool_val)
}
Symbol::FLOAT_GTE => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_float_compare(
FloatPredicate::OGE,
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"gte_F64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::INT_GT | Symbol::NUM_GT => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_int_compare(
IntPredicate::SGT,
args[0].0.into_int_value(),
args[1].0.into_int_value(),
"gt_i64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::FLOAT_GT => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_float_compare(
FloatPredicate::OGT,
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"gt_f64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::INT_LTE | Symbol::NUM_LTE => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_int_compare(
IntPredicate::SLE,
args[0].0.into_int_value(),
args[1].0.into_int_value(),
"lte_i64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::FLOAT_LTE => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_float_compare(
FloatPredicate::OLE,
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"lte_f64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::INT_LT | Symbol::NUM_LT => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_int_compare(
IntPredicate::SLT,
args[0].0.into_int_value(),
args[1].0.into_int_value(),
"lt_i64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::FLOAT_LT => {
debug_assert!(args.len() == 2);
let bool_val = env.builder.build_float_compare(
FloatPredicate::OLT,
args[0].0.into_float_value(),
args[1].0.into_float_value(),
"lt_f64",
);
BasicValueEnum::IntValue(bool_val)
}
Symbol::NUM_MUL => {
debug_assert!(args.len() == 2);

View file

@ -172,6 +172,70 @@ mod gen_builtins {
);
}
#[test]
fn lt_i64() {
assert_evals_to!("1 < 2", true, bool);
assert_evals_to!("1 < 1", false, bool);
assert_evals_to!("2 < 1", false, bool);
assert_evals_to!("0 < 0", false, bool);
}
#[test]
fn lte_i64() {
assert_evals_to!("1 <= 1", true, bool);
assert_evals_to!("2 <= 1", false, bool);
assert_evals_to!("1 <= 2", true, bool);
assert_evals_to!("0 <= 0", true, bool);
}
#[test]
fn gt_i64() {
assert_evals_to!("2 > 1", true, bool);
assert_evals_to!("2 > 2", false, bool);
assert_evals_to!("1 > 1", false, bool);
assert_evals_to!("0 > 0", false, bool);
}
#[test]
fn gte_i64() {
assert_evals_to!("1 >= 1", true, bool);
assert_evals_to!("1 >= 2", false, bool);
assert_evals_to!("2 >= 1", true, bool);
assert_evals_to!("0 >= 0", true, bool);
}
#[test]
fn lt_f64() {
assert_evals_to!("1.1 < 1.2", true, bool);
assert_evals_to!("1.1 < 1.1", false, bool);
assert_evals_to!("1.2 < 1.1", false, bool);
assert_evals_to!("0.0 < 0.0", false, bool);
}
#[test]
fn lte_f64() {
assert_evals_to!("1.1 <= 1.1", true, bool);
assert_evals_to!("1.2 <= 1.1", false, bool);
assert_evals_to!("1.1 <= 1.2", true, bool);
assert_evals_to!("0.0 <= 0.0", true, bool);
}
#[test]
fn gt_f64() {
assert_evals_to!("2.2 > 1.1", true, bool);
assert_evals_to!("2.2 > 2.2", false, bool);
assert_evals_to!("1.1 > 2.2", false, bool);
assert_evals_to!("0.0 > 0.0", false, bool);
}
#[test]
fn gte_f64() {
assert_evals_to!("1.1 >= 1.1", true, bool);
assert_evals_to!("1.1 >= 1.2", false, bool);
assert_evals_to!("1.2 >= 1.1", true, bool);
assert_evals_to!("0.0 >= 0.0", true, bool);
}
#[test]
fn gen_order_of_arithmetic_ops() {
assert_evals_to!(

View file

@ -581,9 +581,9 @@ define_builtins! {
5 NUM_SUB: "sub"
6 NUM_MUL: "mul"
7 NUM_LT: "isLt"
8 NUM_LE: "isLte"
8 NUM_LTE: "isLte"
9 NUM_GT: "isGt"
10 NUM_GE: "isGte"
10 NUM_GTE: "isGte"
11 NUM_TO_FLOAT: "toFloat"
}
2 INT: "Int" => {
@ -600,6 +600,10 @@ define_builtins! {
10 INT_EQ_I1: "#eqi1" // Equality on boolean (theoretically i1) values
11 INT_EQ_I8: "#eqi8" // Equality on byte (theoretically i8) values
12 INT_DIV_UNSAFE: "divUnsafe" // TODO remove once we can code gen Result
13 INT_LT: "#lt"
14 INT_LTE: "#lte"
15 INT_GT: "#gt"
16 INT_GTE: "#gte"
}
3 FLOAT: "Float" => {
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
@ -614,6 +618,10 @@ define_builtins! {
9 FLOAT_SUB: "#sub"
10 FLOAT_EQ: "#eq"
11 FLOAT_ROUND: "round"
12 FLOAT_LT: "#lt"
13 FLOAT_LTE: "#lte"
14 FLOAT_GT: "#gt"
15 FLOAT_GTE: "#gte"
}
4 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias

View file

@ -499,6 +499,22 @@ fn from_can<'a>(
FloatType => Symbol::FLOAT_SUB,
IntType => Symbol::INT_SUB,
},
Symbol::NUM_LTE => match to_int_or_float(env.subs, loc_args[0].0) {
FloatType => Symbol::FLOAT_LTE,
IntType => Symbol::INT_LTE,
},
Symbol::NUM_LT => match to_int_or_float(env.subs, loc_args[0].0) {
FloatType => Symbol::FLOAT_LT,
IntType => Symbol::INT_LT,
},
Symbol::NUM_GTE => match to_int_or_float(env.subs, loc_args[0].0) {
FloatType => Symbol::FLOAT_GTE,
IntType => Symbol::INT_GTE,
},
Symbol::NUM_GT => match to_int_or_float(env.subs, loc_args[0].0) {
FloatType => Symbol::FLOAT_GT,
IntType => Symbol::INT_GT,
},
// TODO make this work for more than just int/float
Symbol::BOOL_EQ => {
match Layout::from_var(