always Cond on a symbol

this will make 'beans' easier
This commit is contained in:
Folkert 2020-03-23 20:00:15 +01:00
parent 3dbdb64a93
commit 2bb69f333f
6 changed files with 192 additions and 104 deletions

View file

@ -83,8 +83,10 @@ pub fn build_expr<'a, B: Backend>(
cond_layout, cond_layout,
ret_layout, ret_layout,
} => { } => {
let cond_value = load_symbol(env, scope, builder, *cond);
let branch = Branch2 { let branch = Branch2 {
cond, cond: cond_value,
pass, pass,
fail, fail,
cond_layout, cond_layout,
@ -115,7 +117,7 @@ pub fn build_expr<'a, B: Backend>(
let mut scope = im_rc::HashMap::clone(scope); let mut scope = im_rc::HashMap::clone(scope);
let cfg = env.cfg; let cfg = env.cfg;
let ptr_size = cfg.pointer_bytes() as u32; let ptr_size = cfg.pointer_bytes() as u32;
for (name, layout, expr) in stores.iter() { for (name, layout, expr) in stores.iter() {
let stack_size = layout.stack_size(ptr_size); let stack_size = layout.stack_size(ptr_size);
@ -127,10 +129,8 @@ pub fn build_expr<'a, B: Backend>(
let val = build_expr(env, &scope, module, builder, &expr, procs); let val = build_expr(env, &scope, module, builder, &expr, procs);
let expr_type = type_from_layout(cfg, &layout); let expr_type = type_from_layout(cfg, &layout);
let slot = builder.create_stack_slot(StackSlotData::new( let slot = builder
StackSlotKind::ExplicitSlot, .create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, stack_size));
stack_size,
));
builder.ins().stack_store(val, slot, Offset32::new(0)); builder.ins().stack_store(val, slot, Offset32::new(0));
@ -175,37 +175,7 @@ pub fn build_expr<'a, B: Backend>(
results[0] results[0]
} }
Load(name) => match scope.get(name) { Load(name) => load_symbol(env, scope, builder, *name),
Some(ScopeEntry::Stack { expr_type, slot }) => {
builder
.ins()
.stack_load(*expr_type, *slot, Offset32::new(0))
}
Some(ScopeEntry::Arg { param, .. }) => *param,
Some(ScopeEntry::Heap { expr_type, ptr }) => {
builder
.ins()
.load(*expr_type, MemFlags::new(), *ptr, Offset32::new(0))
}
Some(ScopeEntry::Func { .. }) => {
panic!("TODO I don't yet know how to return fn pointers")
}
Some(ScopeEntry::ZeroSized) => {
// Create a slot
let slot = builder.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
0
));
builder
.ins()
.stack_addr(env.cfg.pointer_type(), slot, Offset32::new(0))
}
None => panic!(
"Could not resolve lookup for {:?} because no ScopeEntry was found for {:?} in scope {:?}",
name, name, scope
),
},
Struct(sorted_fields) => { Struct(sorted_fields) => {
let cfg = env.cfg; let cfg = env.cfg;
@ -217,10 +187,8 @@ pub fn build_expr<'a, B: Backend>(
} }
// Create a slot // Create a slot
let slot = builder.create_stack_slot(StackSlotData::new( let slot = builder
StackSlotKind::ExplicitSlot, .create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, slot_size));
slot_size
));
// Create instructions for storing each field's expression // Create instructions for storing each field's expression
// NOTE assumes that all fields have the same width! // NOTE assumes that all fields have the same width!
@ -238,7 +206,11 @@ pub fn build_expr<'a, B: Backend>(
.ins() .ins()
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0)) .stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
} }
Tag { tag_layout, arguments, .. } => { Tag {
tag_layout,
arguments,
..
} => {
let cfg = env.cfg; let cfg = env.cfg;
let ptr_bytes = cfg.pointer_bytes() as u32; let ptr_bytes = cfg.pointer_bytes() as u32;
@ -248,10 +220,8 @@ pub fn build_expr<'a, B: Backend>(
let slot_size = tag_layout.stack_size(ptr_bytes); let slot_size = tag_layout.stack_size(ptr_bytes);
// Create a slot // Create a slot
let slot = builder.create_stack_slot(StackSlotData::new( let slot = builder
StackSlotKind::ExplicitSlot, .create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, slot_size));
slot_size
));
// Create instructions for storing each field's expression // Create instructions for storing each field's expression
let mut offset = 0; let mut offset = 0;
@ -260,10 +230,12 @@ pub fn build_expr<'a, B: Backend>(
let val = build_expr(env, &scope, module, builder, field_expr, procs); let val = build_expr(env, &scope, module, builder, field_expr, procs);
let field_size = field_layout.stack_size(ptr_bytes); let field_size = field_layout.stack_size(ptr_bytes);
let field_offset = i32::try_from(offset) let field_offset =
.expect("TODO handle field size conversion to i32"); i32::try_from(offset).expect("TODO handle field size conversion to i32");
builder.ins().stack_store(val, slot, Offset32::new(field_offset)); builder
.ins()
.stack_store(val, slot, Offset32::new(field_offset));
offset += field_size; offset += field_size;
} }
@ -281,8 +253,6 @@ pub fn build_expr<'a, B: Backend>(
let cfg = env.cfg; let cfg = env.cfg;
let mut offset = 0; let mut offset = 0;
for (field_index, field_layout) in field_layouts.iter().enumerate() { for (field_index, field_layout) in field_layouts.iter().enumerate() {
if *index == field_index as u64 { if *index == field_index as u64 {
let offset = i32::try_from(offset) let offset = i32::try_from(offset)
@ -301,8 +271,10 @@ pub fn build_expr<'a, B: Backend>(
offset += field_layout.stack_size(ptr_bytes); offset += field_layout.stack_size(ptr_bytes);
} }
panic!("field access out of bounds: index {:?} in layouts {:?}", index, field_layouts) panic!(
"field access out of bounds: index {:?} in layouts {:?}",
index, field_layouts
)
} }
Str(str_literal) => { Str(str_literal) => {
@ -375,14 +347,18 @@ pub fn build_expr<'a, B: Backend>(
// Store the length // Store the length
{ {
let length = builder.ins().iconst(env.ptr_sized_int(), elems.len() as i64); let length = builder
.ins()
.iconst(env.ptr_sized_int(), elems.len() as i64);
let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32); let offset = Offset32::new((Builtin::WRAPPER_LEN * ptr_bytes) as i32);
builder.ins().stack_store(length, slot, offset); builder.ins().stack_store(length, slot, offset);
} }
// Return the pointer to the wrapper // Return the pointer to the wrapper
builder.ins().stack_addr(cfg.pointer_type(), slot, Offset32::new(0)) builder
.ins()
.stack_addr(cfg.pointer_type(), slot, Offset32::new(0))
} }
_ => { _ => {
panic!("I don't yet know how to crane build {:?}", expr); panic!("I don't yet know how to crane build {:?}", expr);
@ -390,8 +366,47 @@ pub fn build_expr<'a, B: Backend>(
} }
} }
fn load_symbol<'a>(
env: &mut Env<'a>,
scope: &Scope,
builder: &mut FunctionBuilder,
name: Symbol,
) -> Value {
match scope.get(&name) {
Some(ScopeEntry::Stack { expr_type, slot }) => {
builder
.ins()
.stack_load(*expr_type, *slot, Offset32::new(0))
}
Some(ScopeEntry::Arg { param, .. }) => *param,
Some(ScopeEntry::Heap { expr_type, ptr }) => {
builder
.ins()
.load(*expr_type, MemFlags::new(), *ptr, Offset32::new(0))
}
Some(ScopeEntry::Func { .. }) => {
panic!("TODO I don't yet know how to return fn pointers")
}
Some(ScopeEntry::ZeroSized) => {
// Create a slot
let slot = builder.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
0
));
builder
.ins()
.stack_addr(env.cfg.pointer_type(), slot, Offset32::new(0))
}
None => panic!(
"Could not resolve lookup for {:?} because no ScopeEntry was found for {:?} in scope {:?}",
name, name, scope
),
}
}
struct Branch2<'a> { struct Branch2<'a> {
cond: &'a Expr<'a>, cond: Value,
cond_layout: &'a Layout<'a>, cond_layout: &'a Layout<'a>,
pass: &'a Expr<'a>, pass: &'a Expr<'a>,
fail: &'a Expr<'a>, fail: &'a Expr<'a>,
@ -417,7 +432,7 @@ fn build_branch2<'a, B: Backend>(
builder.declare_var(ret, ret_type); builder.declare_var(ret, ret_type);
let cond = build_expr(env, scope, module, builder, branch.cond, procs); let cond = branch.cond;
let pass_block = builder.create_block(); let pass_block = builder.create_block();
let fail_block = builder.create_block(); let fail_block = builder.create_block();
@ -763,8 +778,10 @@ fn call_by_name<'a, B: Backend>(
Symbol::BOOL_OR => { Symbol::BOOL_OR => {
debug_assert!(args.len() == 2); debug_assert!(args.len() == 2);
let cond = build_arg(&args[0], env, scope, module, builder, procs);
let branch2 = Branch2 { let branch2 = Branch2 {
cond: &args[0].0, cond,
cond_layout: &Layout::Builtin(Builtin::Bool), cond_layout: &Layout::Builtin(Builtin::Bool),
pass: &Expr::Bool(true), pass: &Expr::Bool(true),
fail: &args[1].0, fail: &args[1].0,
@ -775,8 +792,10 @@ fn call_by_name<'a, B: Backend>(
Symbol::BOOL_AND => { Symbol::BOOL_AND => {
debug_assert!(args.len() == 2); debug_assert!(args.len() == 2);
let cond = build_arg(&args[0], env, scope, module, builder, procs);
let branch2 = Branch2 { let branch2 = Branch2 {
cond: &args[0].0, cond,
cond_layout: &Layout::Builtin(Builtin::Bool), cond_layout: &Layout::Builtin(Builtin::Bool),
pass: &args[1].0, pass: &args[1].0,
fail: &Expr::Bool(false), fail: &Expr::Bool(false),

View file

@ -46,7 +46,7 @@ pub fn build_expr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>, scope: &Scope<'a, 'ctx>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
expr: &Expr<'a>, expr: &'a Expr<'a>,
procs: &Procs<'a>, procs: &Procs<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use roc_mono::expr::Expr::*; use roc_mono::expr::Expr::*;
@ -193,12 +193,7 @@ pub fn build_expr<'a, 'ctx, 'env>(
.left() .left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
} }
Load(symbol) => match scope.get(symbol) { Load(symbol) => load_symbol(env, scope, symbol),
Some((_, ptr)) => env
.builder
.build_load(*ptr, symbol.ident_string(&env.interns)),
None => panic!("Could not find a var for {:?} in scope {:?}", symbol, scope),
},
Str(str_literal) => { Str(str_literal) => {
if str_literal.is_empty() { if str_literal.is_empty() {
panic!("TODO build an empty string in LLVM"); panic!("TODO build an empty string in LLVM");
@ -514,6 +509,19 @@ pub fn build_expr<'a, 'ctx, 'env>(
} }
} }
fn load_symbol<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
symbol: &Symbol,
) -> BasicValueEnum<'ctx> {
match scope.get(symbol) {
Some((_, ptr)) => env
.builder
.build_load(*ptr, symbol.ident_string(&env.interns)),
None => panic!("Could not find a var for {:?} in scope {:?}", symbol, scope),
}
}
/// Cast a struct to another struct of the same (or smaller?) size /// Cast a struct to another struct of the same (or smaller?) size
fn cast_struct_struct<'ctx>( fn cast_struct_struct<'ctx>(
builder: &Builder<'ctx>, builder: &Builder<'ctx>,
@ -563,7 +571,7 @@ fn extract_tag_discriminant<'a, 'ctx, 'env>(
} }
struct Branch2<'a> { struct Branch2<'a> {
cond: &'a Expr<'a>, cond: &'a Symbol,
pass: &'a Expr<'a>, pass: &'a Expr<'a>,
fail: &'a Expr<'a>, fail: &'a Expr<'a>,
ret_layout: Layout<'a>, ret_layout: Layout<'a>,
@ -579,7 +587,7 @@ fn build_branch2<'a, 'ctx, 'env>(
let ret_layout = cond.ret_layout; let ret_layout = cond.ret_layout;
let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes); let ret_type = basic_type_from_layout(env.arena, env.context, &ret_layout, env.ptr_bytes);
let cond_expr = build_expr(env, scope, parent, cond.cond, procs); let cond_expr = load_symbol(env, scope, cond.cond);
match cond_expr { match cond_expr {
IntValue(value) => { IntValue(value) => {

View file

@ -1105,16 +1105,32 @@ fn decide_to_branching<'a>(
jumps, jumps,
)); ));
let cond = boolean_all(env.arena, tests); let condition = boolean_all(env.arena, tests);
let cond_layout = Layout::Builtin(Builtin::Bool); let cond_layout = Layout::Builtin(Builtin::Bool);
Expr::Cond { if let Expr::Load(symbol) = condition {
cond: env.arena.alloc(cond), Expr::Cond {
cond_layout, cond: symbol,
pass, cond_layout,
fail, pass,
ret_layout, fail,
ret_layout,
}
} else {
let cond_symbol = env.fresh_symbol();
let stores = vec![(cond_symbol, cond_layout.clone(), condition)];
Expr::Store(
env.arena.alloc(stores),
env.arena.alloc(Expr::Cond {
cond: cond_symbol,
cond_layout,
pass,
fail,
ret_layout,
}),
)
} }
} }
FanOut { FanOut {

View file

@ -152,7 +152,7 @@ pub enum Expr<'a> {
// The left-hand side of the conditional comparison and the right-hand side. // The left-hand side of the conditional comparison and the right-hand side.
// These are stored separately because there are different machine instructions // These are stored separately because there are different machine instructions
// for e.g. "compare float and jump" vs. "compare integer and jump" // for e.g. "compare float and jump" vs. "compare integer and jump"
cond: &'a Expr<'a>, cond: Symbol,
cond_layout: Layout<'a>, cond_layout: Layout<'a>,
// What to do if the condition either passes or fails // What to do if the condition either passes or fails
pass: &'a Expr<'a>, pass: &'a Expr<'a>,
@ -646,13 +646,22 @@ fn from_can<'a>(
for (loc_cond, loc_then) in branches.into_iter().rev() { for (loc_cond, loc_then) in branches.into_iter().rev() {
let cond = from_can(env, loc_cond.value, procs, None); let cond = from_can(env, loc_cond.value, procs, None);
let then = from_can(env, loc_then.value, procs, None); let then = from_can(env, loc_then.value, procs, None);
expr = Expr::Cond {
cond: env.arena.alloc(cond), let cond_symbol = env.fresh_symbol();
let cond_expr = Expr::Cond {
cond: cond_symbol,
cond_layout: cond_layout.clone(), cond_layout: cond_layout.clone(),
pass: env.arena.alloc(then), pass: env.arena.alloc(then),
fail: env.arena.alloc(expr), fail: env.arena.alloc(expr),
ret_layout: ret_layout.clone(), ret_layout: ret_layout.clone(),
}; };
expr = Expr::Store(
env.arena
.alloc(vec![(cond_symbol, Layout::Builtin(Builtin::Bool), cond)]),
env.arena.alloc(cond_expr),
);
} }
expr expr

View file

@ -16,6 +16,7 @@ mod test_mono {
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
use roc_mono::expr::Expr::{self, *}; use roc_mono::expr::Expr::{self, *};
use roc_mono::expr::Procs; use roc_mono::expr::Procs;
use roc_mono::layout;
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout};
use roc_types::subs::Subs; use roc_types::subs::Subs;
@ -160,13 +161,23 @@ mod test_mono {
use self::Builtin::*; use self::Builtin::*;
use Layout::Builtin; use Layout::Builtin;
Cond { let home = test_home();
cond: &Expr::Bool(true), let gen_symbol_0 = Interns::from_index(home, 0);
cond_layout: Builtin(Bool),
pass: &Expr::Str("bar"), Store(
fail: &Expr::Str("foo"), &[(
ret_layout: Builtin(Str), gen_symbol_0,
} Layout::Builtin(layout::Builtin::Bool),
Expr::Bool(true),
)],
&Cond {
cond: gen_symbol_0,
cond_layout: Builtin(Bool),
pass: &Expr::Str("bar"),
fail: &Expr::Str("foo"),
ret_layout: Builtin(Str),
},
)
}, },
) )
} }
@ -186,19 +197,37 @@ mod test_mono {
use self::Builtin::*; use self::Builtin::*;
use Layout::Builtin; use Layout::Builtin;
Cond { let home = test_home();
cond: &Expr::Bool(true), let gen_symbol_0 = Interns::from_index(home, 1);
cond_layout: Builtin(Bool), let gen_symbol_1 = Interns::from_index(home, 0);
pass: &Expr::Str("bar"),
fail: &Cond { Store(
cond: &Expr::Bool(false), &[(
gen_symbol_0,
Layout::Builtin(layout::Builtin::Bool),
Expr::Bool(true),
)],
&Cond {
cond: gen_symbol_0,
cond_layout: Builtin(Bool), cond_layout: Builtin(Bool),
pass: &Expr::Str("foo"), pass: &Expr::Str("bar"),
fail: &Expr::Str("baz"), fail: &Store(
&[(
gen_symbol_1,
Layout::Builtin(layout::Builtin::Bool),
Expr::Bool(false),
)],
&Cond {
cond: gen_symbol_1,
cond_layout: Builtin(Bool),
pass: &Expr::Str("foo"),
fail: &Expr::Str("baz"),
ret_layout: Builtin(Str),
},
),
ret_layout: Builtin(Str), ret_layout: Builtin(Str),
}, },
ret_layout: Builtin(Str), )
}
}, },
) )
} }
@ -218,19 +247,27 @@ mod test_mono {
use Layout::Builtin; use Layout::Builtin;
let home = test_home(); let home = test_home();
let gen_symbol_0 = Interns::from_index(home, 1);
let symbol_x = Interns::from_index(home, 0); let symbol_x = Interns::from_index(home, 0);
Store( Store(
&[( &[(
symbol_x, symbol_x,
Builtin(Str), Builtin(Str),
Cond { Store(
cond: &Expr::Bool(true), &[(
cond_layout: Builtin(Bool), gen_symbol_0,
pass: &Expr::Str("bar"), Layout::Builtin(layout::Builtin::Bool),
fail: &Expr::Str("foo"), Expr::Bool(true),
ret_layout: Builtin(Str), )],
}, &Cond {
cond: gen_symbol_0,
cond_layout: Builtin(Bool),
pass: &Expr::Str("bar"),
fail: &Expr::Str("foo"),
ret_layout: Builtin(Str),
},
),
)], )],
&Load(symbol_x), &Load(symbol_x),
) )

View file

@ -114,13 +114,12 @@ mod test_opt {
} }
Cond { Cond {
cond, cond: _,
cond_layout: _, cond_layout: _,
pass, pass,
fail, fail,
ret_layout: _, ret_layout: _,
} => { } => {
extract_named_calls_help(cond, calls, unexpected_calls);
extract_named_calls_help(pass, calls, unexpected_calls); extract_named_calls_help(pass, calls, unexpected_calls);
extract_named_calls_help(fail, calls, unexpected_calls); extract_named_calls_help(fail, calls, unexpected_calls);
} }