diff --git a/compiler/gen/src/crane/build.rs b/compiler/gen/src/crane/build.rs index fcec1ba984..c2a03249aa 100644 --- a/compiler/gen/src/crane/build.rs +++ b/compiler/gen/src/crane/build.rs @@ -52,16 +52,14 @@ pub fn build_expr<'a, B: Backend>( Bool(val) => builder.ins().bconst(types::B1, *val), Byte(val) => builder.ins().iconst(types::I8, *val as i64), Cond { - cond_lhs, - cond_rhs, + cond, pass, fail, cond_layout, ret_layout, } => { let branch = Branch2 { - cond_lhs, - cond_rhs, + cond, pass, fail, cond_layout, @@ -269,8 +267,7 @@ pub fn build_expr<'a, B: Backend>( } struct Branch2<'a> { - cond_lhs: &'a Expr<'a>, - cond_rhs: &'a Expr<'a>, + cond: &'a Expr<'a>, cond_layout: &'a Layout<'a>, pass: &'a Expr<'a>, fail: &'a Expr<'a>, @@ -296,24 +293,13 @@ fn build_branch2<'a, B: Backend>( builder.declare_var(ret, ret_type); - let lhs = build_expr(env, scope, module, builder, branch.cond_lhs, procs); - let rhs = build_expr(env, scope, module, builder, branch.cond_rhs, procs); + let cond = build_expr(env, scope, module, builder, branch.cond, procs); let pass_block = builder.create_block(); let fail_block = builder.create_block(); match branch.cond_layout { - Layout::Builtin(Builtin::Float64) => { - // For floats, first do a `fcmp` comparison to get a bool answer about equality, - // then use `brnz` to branch if that bool equality answer was nonzero (aka true). - let is_eq = builder.ins().fcmp(FloatCC::Equal, lhs, rhs); - - builder.ins().brnz(is_eq, pass_block, &[]); - } - Layout::Builtin(Builtin::Int64) => { - // For ints, we can compare and branch in the same instruction: `icmp` - builder - .ins() - .br_icmp(IntCC::Equal, lhs, rhs, pass_block, &[]); + Layout::Builtin(Builtin::Bool(_, _)) => { + builder.ins().brnz(cond, pass_block, &[]); } other => panic!("I don't know how to build a conditional for {:?}", other), } @@ -589,6 +575,20 @@ fn call_by_name<'a, B: Backend>( builder.ins().ineg(num) } + Symbol::INT_EQ => { + debug_assert!(args.len() == 2); + let a = build_arg(&args[0], env, scope, module, builder, procs); + let b = build_arg(&args[1], env, scope, module, builder, procs); + + builder.ins().icmp(IntCC::Equal, a, b) + } + Symbol::FLOAT_EQ => { + debug_assert!(args.len() == 2); + let a = build_arg(&args[0], env, scope, module, builder, procs); + let b = build_arg(&args[1], env, scope, module, builder, procs); + + builder.ins().fcmp(FloatCC::Equal, a, b) + } Symbol::LIST_GET_UNSAFE => { debug_assert!(args.len() == 2); diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index e530f4baef..5f6e791945 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -46,22 +46,20 @@ pub fn build_expr<'a, 'ctx, 'env>( Int(num) => env.context.i64_type().const_int(*num as u64, true).into(), Float(num) => env.context.f64_type().const_float(*num).into(), Cond { - cond_lhs, - cond_rhs, + cond, pass, fail, ret_layout, .. } => { - let cond = Branch2 { - cond_lhs, - cond_rhs, + let conditional = Branch2 { + cond, pass, fail, ret_layout: ret_layout.clone(), }; - build_branch2(env, scope, parent, cond, procs) + build_branch2(env, scope, parent, conditional, procs) } Branches { .. } => { panic!("TODO build_branches(env, scope, parent, cond_lhs, branches, procs)"); @@ -251,8 +249,7 @@ pub fn build_expr<'a, 'ctx, 'env>( } struct Branch2<'a> { - cond_lhs: &'a Expr<'a>, - cond_rhs: &'a Expr<'a>, + cond: &'a Expr<'a>, pass: &'a Expr<'a>, fail: &'a Expr<'a>, ret_layout: Layout<'a>, @@ -269,9 +266,9 @@ fn build_branch2<'a, 'ctx, 'env>( let ret_layout = cond.ret_layout; let ret_type = basic_type_from_layout(env.context, &ret_layout); - let lhs = build_expr(env, scope, parent, cond.cond_lhs, procs); - let rhs = build_expr(env, scope, parent, cond.cond_rhs, procs); + let cond_expr = build_expr(env, scope, parent, cond.cond, procs); + /* match (lhs, rhs) { (FloatValue(lhs_float), FloatValue(rhs_float)) => { let comparison = @@ -294,6 +291,17 @@ fn build_branch2<'a, 'ctx, 'env>( cond.cond_lhs, cond.cond_rhs ), } + */ + + match cond_expr { + IntValue(value) => build_phi2( + env, scope, parent, value, cond.pass, cond.fail, ret_type, procs, + ), + _ => panic!( + "Tried to make a branch out of an invalid condition: cond_expr = {:?}", + cond_expr, + ), + } } struct SwitchArgs<'a, 'ctx> { @@ -600,6 +608,30 @@ fn call_with_args<'a, 'ctx, 'env>( BasicValueEnum::IntValue(int_val) } + Symbol::INT_EQ => { + debug_assert!(args.len() == 2); + + let int_val = env.builder.build_int_compare( + IntPredicate::EQ, + args[0].into_int_value(), + args[1].into_int_value(), + "cmp_i64", + ); + + BasicValueEnum::IntValue(int_val) + } + Symbol::FLOAT_EQ => { + debug_assert!(args.len() == 2); + + let int_val = env.builder.build_float_compare( + FloatPredicate::OEQ, + args[0].into_float_value(), + args[1].into_float_value(), + "cmp_f64", + ); + + BasicValueEnum::IntValue(int_val) + } Symbol::LIST_GET_UNSAFE => { debug_assert!(args.len() == 2); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index cce62d3652..6ba11959e0 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -591,6 +591,7 @@ define_builtins! { 6 INT_LOWEST: "lowest" 7 INT_ADD: "#add" 8 INT_SUB: "#sub" + 9 INT_EQ: "#eq" } 3 FLOAT: "Float" => { 0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias @@ -603,6 +604,7 @@ define_builtins! { 7 FLOAT_LOWEST: "lowest" 8 FLOAT_ADD: "#add" 9 FLOAT_SUB: "#sub" + 10 FLOAT_EQ: "#eq" } 4 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index 0fd6237211..3a4d3739e2 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -150,8 +150,7 @@ pub enum Expr<'a> { // The left-hand side of the conditional comparison and the right-hand side. // These are stored separately because there are different machine instructions // for e.g. "compare float and jump" vs. "compare integer and jump" - cond_lhs: &'a Expr<'a>, - cond_rhs: &'a Expr<'a>, + cond: &'a Expr<'a>, cond_layout: Layout<'a>, // What to do if the condition either passes or fails pass: &'a Expr<'a>, @@ -575,6 +574,34 @@ fn from_can<'a>( branches, } => from_can_when(env, cond_var, expr_var, *loc_cond, branches, procs), + If { + cond_var, + branch_var, + branches, + final_else, + } => { + let mut expr = from_can(env, final_else.value, procs, None); + + let ret_layout = Layout::from_var(env.arena, branch_var, env.subs, env.pointer_size) + .expect("invalid ret_layout"); + let cond_layout = Layout::from_var(env.arena, cond_var, env.subs, env.pointer_size) + .expect("invalid cond_layout"); + + for (loc_cond, loc_then) in branches.into_iter().rev() { + let cond = from_can(env, loc_cond.value, procs, None); + let then = from_can(env, loc_then.value, procs, None); + expr = Expr::Cond { + cond: env.arena.alloc(cond), + cond_layout: cond_layout.clone(), + pass: env.arena.alloc(then), + fail: env.arena.alloc(expr), + ret_layout: ret_layout.clone(), + }; + } + + expr + } + Record(ext_var, fields) => { let arena = env.arena; let mut field_bodies = Vec::with_capacity_in(fields.len(), arena); @@ -785,6 +812,75 @@ fn from_can_when<'a>( let (loc_when_pat2, loc_else) = iter.next().unwrap(); match (&loc_when_pat1.value, &loc_when_pat2.value) { + (NumLiteral(var, num), Underscore) => { + let cond_lhs = from_can(env, loc_cond.value, procs, None); + + let (fn_symbol, builtin, cond_rhs_expr) = match to_int_or_float(env.subs, *var) + { + IntOrFloat::IntType => (Symbol::INT_EQ, Builtin::Int64, Expr::Int(*num)), + IntOrFloat::FloatType => { + (Symbol::FLOAT_EQ, Builtin::Float64, Expr::Float(*num as f64)) + } + }; + let cond_rhs = cond_rhs_expr; + + let cond = arena.alloc(Expr::CallByName( + fn_symbol, + arena.alloc([ + (cond_lhs, Layout::Builtin(builtin.clone())), + (cond_rhs, Layout::Builtin(builtin)), + ]), + )); + + let pass = arena.alloc(from_can(env, loc_then.value, procs, None)); + let fail = arena.alloc(from_can(env, loc_else.value, procs, None)); + let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size) + .unwrap_or_else(|err| { + panic!("TODO turn this into a RuntimeError {:?}", err) + }); + + Expr::Cond { + cond_layout: Layout::Builtin(Builtin::Bool( + TagName::Global("False".into()), + TagName::Global("True".into()), + )), + cond, + pass, + fail, + ret_layout, + } + } + (FloatLiteral(float), Underscore) => { + let cond_lhs = from_can(env, loc_cond.value, procs, None); + let cond_rhs = Expr::Float(*float); + + let cond = arena.alloc(Expr::CallByName( + Symbol::FLOAT_EQ, + arena.alloc([ + (cond_lhs, Layout::Builtin(Builtin::Float64)), + (cond_rhs, Layout::Builtin(Builtin::Float64)), + ]), + )); + + let pass = arena.alloc(from_can(env, loc_then.value, procs, None)); + let fail = arena.alloc(from_can(env, loc_else.value, procs, None)); + let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size) + .unwrap_or_else(|err| { + panic!("TODO turn this into a RuntimeError {:?}", err) + }); + + Expr::Cond { + cond_layout: Layout::Builtin(Builtin::Bool( + TagName::Global("False".into()), + TagName::Global("True".into()), + )), + cond, + pass, + fail, + ret_layout, + } + } + /* (NumLiteral(var, num), NumLiteral(_, _)) | (NumLiteral(var, num), Underscore) => { let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None)); let (builtin, cond_rhs_expr) = match to_int_or_float(env.subs, *var) { @@ -847,6 +943,7 @@ fn from_can_when<'a>( ret_layout, } } + */ _ => { panic!("TODO handle more conds"); }