λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
9 changed files with 137 additions and 65 deletions
Showing only changes of commit 26bd7e90fd - Show all commits

View File

@@ -1,5 +1,7 @@
x = 1; x = 1;
function add_x(y) x + y function add_x(y) x + y
a = 3; a = 3;
print x;
result = add_x(a); result = add_x(a);
print x;
print result; print result;

View File

@@ -32,7 +32,7 @@ void print(char *_ignore, char *variable_name, int64_t vtype, int64_t value) {
printf("%s = <void>\n", variable_name); printf("%s = <void>\n", variable_name);
break; break;
default: default:
printf("%s = UNKNOWN VTYPE %d\n", variable_name, vtype); printf("%s = UNKNOWN VTYPE %lld\n", variable_name, vtype);
} }
} }

View File

@@ -24,28 +24,6 @@ pub enum ReferenceBuilder {
Argument(ConstantType, entities::Value), Argument(ConstantType, entities::Value),
} }
impl ReferenceBuilder {
fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) {
match self {
ReferenceBuilder::Global(ty, gv) => {
let cranelift_type = ir::Type::from(*ty);
let ptr_value = builder.ins().symbol_value(types::I64, *gv);
let value = builder
.ins()
.load(cranelift_type, MemFlags::new(), ptr_value, 0);
(value, *ty)
}
ReferenceBuilder::Local(ty, var) => {
let value = builder.use_var(*var);
(value, *ty)
}
ReferenceBuilder::Argument(ty, val) => (*val, *ty),
}
}
}
impl<M: Module> Backend<M> { impl<M: Module> Backend<M> {
/// Translate the given IR type into an ABI parameter type for cranelift, as /// Translate the given IR type into an ABI parameter type for cranelift, as
/// best as possible. /// best as possible.
@@ -105,6 +83,7 @@ impl<M: Module> Backend<M> {
true, true,
false, false,
)?; )?;
println!("defining {} with primitive type {}", top_level_name, pt);
self.module.define_data(data_id, &pt.blank_data())?; self.module.define_data(data_id, &pt.blank_data())?;
self.defined_symbols self.defined_symbols
.insert(top_level_name, (data_id, pt.into())); .insert(top_level_name, (data_id, pt.into()));
@@ -146,6 +125,7 @@ impl<M: Module> Backend<M> {
argument_types: Vec<Type>, argument_types: Vec<Type>,
return_type: Type, return_type: Type,
) -> Result<FuncId, cranelift_module::ModuleError> { ) -> Result<FuncId, cranelift_module::ModuleError> {
println!("Declaring {:?} function {}", linkage, name);
let basic_signature = Signature { let basic_signature = Signature {
params: argument_types params: argument_types
.iter() .iter()
@@ -176,6 +156,22 @@ impl<M: Module> Backend<M> {
return_type: Type, return_type: Type,
body: Expression<Type>, body: Expression<Type>,
) -> Result<FuncId, BackendError> { ) -> Result<FuncId, BackendError> {
println!("Compiling function {}", function_name);
{
use pretty::{DocAllocator, Pretty};
let allocator = pretty::BoxAllocator;
allocator
.text("Function body:")
.append(allocator.hardline())
.append(body.pretty(&allocator))
.append(allocator.hardline())
.1
.render_colored(
70,
pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto),
)
.expect("rendering works");
}
// reset the next variable counter. this value shouldn't matter; hopefully // reset the next variable counter. this value shouldn't matter; hopefully
// we won't be using close to 2^32 variables! // we won't be using close to 2^32 variables!
self.reset_local_variable_tracker(); self.reset_local_variable_tracker();
@@ -423,24 +419,23 @@ impl<M: Module> Backend<M> {
} }
}, },
Expression::Print(ann, var) => { Expression::Print(_, var) => {
// Get the output buffer (or null) from our general compilation context. // Get the output buffer (or null) from our general compilation context.
let buffer_ptr = self.output_buffer_ptr(); let buffer_ptr = self.output_buffer_ptr();
let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64);
// Get a reference to the string we want to print. // Get a reference to the string we want to print.
let string_data_id = self.string_reference(var.as_ref())?; let var_name = match var {
ValueOrRef::Ref(_, _, ref name) => name.as_ref(),
ValueOrRef::Value(_, _, _) => "<unknown>",
};
let string_data_id = self.string_reference(var_name)?;
let local_name_ref = self let local_name_ref = self
.module .module
.declare_data_in_func(string_data_id, builder.func); .declare_data_in_func(string_data_id, builder.func);
let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref);
// Look up the value for the variable. Because this might be a let (val, vtype) = self.compile_value_or_ref(var, variables, builder)?;
// global variable (and that requires special logic), we just turn
// this into an `Expression` and re-use the logic in that implementation.
let fake_ref =
ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone());
let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?;
let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); let vtype_repr = builder.ins().iconst(types::I64, vtype as i64);
@@ -472,11 +467,31 @@ impl<M: Module> Backend<M> {
let ir_type = ir::Type::from(value_type); let ir_type = ir::Type::from(value_type);
let variable = self.generate_local(); let variable = self.generate_local();
match variables.get(&name) {
Some(ReferenceBuilder::Global(_, global_value)) => {
let pointer = self.module.target_config().pointer_type();
let pointer_to = builder.ins().symbol_value(pointer, *global_value);
println!("STORE {}: cranelift_type {} origin type {:?}", name, value, value_type);
builder.ins().store(MemFlags::new(), value, pointer_to, 0);
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
}
Some(ReferenceBuilder::Argument(_, _)) => {
panic!("Attempt to mutate an argument {}", name)
}
Some(ReferenceBuilder::Local(_, _)) => {
panic!("Attempt to mutate local {}", name);
}
None => {
builder.declare_var(variable, ir_type); builder.declare_var(variable, ir_type);
builder.def_var(variable, value); builder.def_var(variable, value);
variables.insert(name, ReferenceBuilder::Local(value_type, variable)); variables.insert(name, ReferenceBuilder::Local(value_type, variable));
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
} }
}
}
Expression::Call(_, _, function, args) => { Expression::Call(_, _, function, args) => {
let (arguments, _argument_types): (Vec<_>, Vec<_>) = args let (arguments, _argument_types): (Vec<_>, Vec<_>) = args
@@ -508,9 +523,11 @@ impl<M: Module> Backend<M> {
[result] => match result_type { [result] => match result_type {
Type::Primitive(ct) => Ok((*result, ct.into())), Type::Primitive(ct) => Ok((*result, ct.into())),
Type::Function(_, rt) => match *rt { Type::Function(_, rt) => match *rt {
Type::Function(_, _) => panic!("function returns a function?"), Type::Function(_, _) => {
Type::Primitive(ct) => Ok((*result, ct.into())), panic!("function returns a function?")
} }
Type::Primitive(ct) => Ok((*result, ct.into())),
},
}, },
_ => panic!("don't support multi-value returns yet"), _ => panic!("don't support multi-value returns yet"),
} }
@@ -530,6 +547,7 @@ impl<M: Module> Backend<M> {
variables: &HashMap<Variable, ReferenceBuilder>, variables: &HashMap<Variable, ReferenceBuilder>,
builder: &mut FunctionBuilder, builder: &mut FunctionBuilder,
) -> Result<(entities::Value, ConstantType), BackendError> { ) -> Result<(entities::Value, ConstantType), BackendError> {
println!("compile_value_or_ref {:?}", valref);
match valref { match valref {
ValueOrRef::Value(_, _, val) => match val { ValueOrRef::Value(_, _, val) => match val {
Value::I8(_, v) => { Value::I8(_, v) => {
@@ -575,7 +593,25 @@ impl<M: Module> Backend<M> {
}, },
ValueOrRef::Ref(_, _, name) => match variables.get(&name) { ValueOrRef::Ref(_, _, name) => match variables.get(&name) {
None => Err(BackendError::VariableLookupFailure(name)), None => Err(BackendError::VariableLookupFailure(name)),
Some(x) => Ok(x.refer_to(builder)), Some(ReferenceBuilder::Global(ty, gv)) => {
let pointer_to = self.module.target_config().pointer_type();
let pointer_value = builder.ins().symbol_value(pointer_to, *gv);
let cranelift_type = ir::Type::from(*ty);
println!("READ {}: cranelift_type {} origin type {:?}", name, cranelift_type, ty);
let value =
builder
.ins()
.load(cranelift_type, MemFlags::new(), pointer_value, 0);
Ok((value, *ty))
}
Some(ReferenceBuilder::Argument(ctype, val)) => Ok((*val, *ctype)),
Some(ReferenceBuilder::Local(ctype, var)) => {
let value = builder.use_var(*var);
Ok((value, *ctype))
}
}, },
} }
} }
@@ -588,11 +624,11 @@ impl PrimitiveType {
PrimitiveType::U8 => (1, 1), PrimitiveType::U8 => (1, 1),
PrimitiveType::U16 => (2, 2), PrimitiveType::U16 => (2, 2),
PrimitiveType::U32 => (4, 4), PrimitiveType::U32 => (4, 4),
PrimitiveType::U64 => (4, 4), PrimitiveType::U64 => (8, 8),
PrimitiveType::I8 => (1, 1), PrimitiveType::I8 => (1, 1),
PrimitiveType::I16 => (2, 2), PrimitiveType::I16 => (2, 2),
PrimitiveType::I32 => (4, 4), PrimitiveType::I32 => (4, 4),
PrimitiveType::I64 => (4, 4), PrimitiveType::I64 => (8, 8),
}; };
let mut result = DataDescription::new(); let mut result = DataDescription::new();
result.define_zeroinit(size); result.define_zeroinit(size);

View File

@@ -5,6 +5,7 @@ use codespan_reporting::{
files::SimpleFiles, files::SimpleFiles,
term::{self, Config}, term::{self, Config},
}; };
use cranelift_module::Module;
use pretty::termcolor::{ColorChoice, StandardStream}; use pretty::termcolor::{ColorChoice, StandardStream};
use target_lexicon::Triple; use target_lexicon::Triple;
@@ -134,7 +135,25 @@ impl Compiler {
// Finally, send all this to Cranelift for conversion into an object file. // Finally, send all this to Cranelift for conversion into an object file.
let mut backend = Backend::object_file(Triple::host())?; let mut backend = Backend::object_file(Triple::host())?;
let unknown = "<unknown>".to_string();
backend.compile_program("gogogo", ir)?; backend.compile_program("gogogo", ir)?;
println!("FINAL MODULE:");
println!(" FUNCTIONS:");
for (_, decl) in backend.module.declarations().get_functions() {
println!(
" {}: {:?}",
decl.name.as_ref().unwrap_or(&unknown),
decl.linkage
);
}
println!(" DATA:");
for (_, decl) in backend.module.declarations().get_data_objects() {
println!(
" {}: {:?}",
decl.name.as_ref().unwrap_or(&unknown),
decl.linkage
);
}
Ok(Some(backend.bytes()?)) Ok(Some(backend.bytes()?))
} }

View File

@@ -359,19 +359,21 @@ fn generate_random_expression(
.iter() .iter()
.filter_map(|(variable, ty)| { .filter_map(|(variable, ty)| {
if ty.is_printable() { if ty.is_printable() {
Some(variable.clone()) Some((variable.clone(), ty.clone()))
} else { } else {
None None
} }
}) })
.collect::<Vec<Variable>>(); .collect::<Vec<_>>();
if possible_variables.is_empty() { if possible_variables.is_empty() {
generate_random_binding(rng, env) generate_random_binding(rng, env)
} else { } else {
let (variable, var_type) = possible_variables.choose(rng).unwrap();
Expression::Print( Expression::Print(
Location::manufactured(), Location::manufactured(),
possible_variables.choose(rng).unwrap().clone(), ValueOrRef::Ref(Location::manufactured(), var_type.clone(), variable.clone()),
) )
} }
} }

View File

@@ -61,8 +61,9 @@ pub struct Program<Type> {
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program<Type> impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program<Type>
where where
A: 'a, A: 'a,
D: ?Sized + DocAllocator<'a, A>, D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>, &'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{ {
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
let mut result = allocator.nil(); let mut result = allocator.nil();
@@ -116,8 +117,9 @@ impl<T: Clone + TypeWithVoid + TypeWithFunction> TopLevel<T> {
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel<Type> impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel<Type>
where where
A: 'a, A: 'a,
D: ?Sized + DocAllocator<'a, A>, D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>, &'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{ {
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self { match self {
@@ -159,7 +161,7 @@ pub enum Expression<Type> {
Cast(Location, Type, ValueOrRef<Type>), Cast(Location, Type, ValueOrRef<Type>),
Primitive(Location, Type, Primitive, Vec<ValueOrRef<Type>>), Primitive(Location, Type, Primitive, Vec<ValueOrRef<Type>>),
Block(Location, Type, Vec<Expression<Type>>), Block(Location, Type, Vec<Expression<Type>>),
Print(Location, Variable), Print(Location, ValueOrRef<Type>),
Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<Type>>), Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<Type>>),
Bind(Location, Variable, Type, Box<Expression<Type>>), Bind(Location, Variable, Type, Box<Expression<Type>>),
} }
@@ -197,8 +199,9 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression<Type> impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression<Type>
where where
A: 'a, A: 'a,
D: ?Sized + DocAllocator<'a, A>, D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>, &'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{ {
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self { match self {
@@ -235,24 +238,28 @@ where
Some((last, &[])) => last.pretty(allocator), Some((last, &[])) => last.pretty(allocator),
Some((last, start)) => { Some((last, start)) => {
let mut result = allocator.text("{").append(allocator.hardline()); let mut result = allocator.text("{").append(allocator.hardline());
let starts = start.iter().map(|x| {
for stmt in start.iter() { x.pretty(allocator)
result = result
.append(stmt.pretty(allocator))
.append(allocator.text(";")) .append(allocator.text(";"))
.append(allocator.hardline()); .append(allocator.hardline())
.indent(4)
});
let last = last
.pretty(allocator)
.append(allocator.hardline())
.indent(4);
for start in starts {
result = result.append(start);
} }
result result.append(last).append(allocator.text("}"))
.append(last.pretty(allocator))
.append(allocator.hardline())
.append(allocator.text("}"))
} }
}, },
Expression::Print(_, var) => allocator Expression::Print(_, var) => allocator
.text("print") .text("print")
.append(allocator.space()) .append(allocator.space())
.append(allocator.text(var.as_ref().to_string())), .append(var.pretty(allocator)),
Expression::Bind(_, var, ty, expr) => allocator Expression::Bind(_, var, ty, expr) => allocator
.text(var.as_ref().to_string()) .text(var.as_ref().to_string())
.append(allocator.space()) .append(allocator.space())

View File

@@ -89,11 +89,12 @@ where
Ok(result) Ok(result)
} }
Expression::Print(loc, n) => { Expression::Print(_, value) => {
let value = env let n = match value {
.get(n) ValueOrRef::Ref(_, _, ref name) => name.as_str(),
.cloned() ValueOrRef::Value(_, _, _) => "<unknown>",
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string()))?; };
let value = value.eval(env)?;
stdout.push_str(&format!("{} = {}\n", n, value)); stdout.push_str(&format!("{} = {}\n", n, value));
Ok(Value::Void) Ok(Value::Void)
} }

View File

@@ -150,10 +150,11 @@ fn convert_statement(
.get(&final_name) .get(&final_name)
.expect("print variable defined before use") .expect("print variable defined before use")
.clone(); .clone();
println!("varty for {} is {}", final_name, varty);
constraint_db.push(Constraint::Printable(loc.clone(), varty.clone())); constraint_db.push(Constraint::Printable(loc.clone(), varty.clone()));
ir::Expression::Print(loc, final_name) ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name))
} }
syntax::Statement::Binding(loc, name, expr) => { syntax::Statement::Binding(loc, name, expr) => {
@@ -256,6 +257,7 @@ fn convert_expression(
.get(&final_name) .get(&final_name)
.cloned() .cloned()
.expect("variable bound before use"); .expect("variable bound before use");
println!("rtype for {} is {}", final_name, rtype);
let refexp = let refexp =
ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name)); ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name));

View File

@@ -89,7 +89,7 @@ fn finalize_expression(
Expression::Block(loc, finalize_type(ty, resolutions), final_exprs) Expression::Block(loc, finalize_type(ty, resolutions), final_exprs)
} }
Expression::Print(loc, var) => Expression::Print(loc, var), Expression::Print(loc, var) => Expression::Print(loc, finalize_val_or_ref(var, resolutions)),
Expression::Call(loc, ty, fun, args) => Expression::Call( Expression::Call(loc, ty, fun, args) => Expression::Call(
loc, loc,
@@ -114,7 +114,10 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type {
TypeOrVar::Primitive(x) => Type::Primitive(x), TypeOrVar::Primitive(x) => Type::Primitive(x),
TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) { TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) {
None => panic!("Did not resolve type for type variable {}", tvar), None => panic!("Did not resolve type for type variable {}", tvar),
Some(pt) => pt.clone(), Some(pt) => {
println!("Finalizing {} to {}", tvar, pt);
pt.clone()
}
}, },
TypeOrVar::Function(mut args, ret) => Type::Function( TypeOrVar::Function(mut args, ret) => Type::Function(
args.drain(..) args.drain(..)