λ 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;
function add_x(y) x + y
a = 3;
print x;
result = add_x(a);
print x;
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);
break;
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),
}
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> {
/// Translate the given IR type into an ABI parameter type for cranelift, as
/// best as possible.
@@ -105,6 +83,7 @@ impl<M: Module> Backend<M> {
true,
false,
)?;
println!("defining {} with primitive type {}", top_level_name, pt);
self.module.define_data(data_id, &pt.blank_data())?;
self.defined_symbols
.insert(top_level_name, (data_id, pt.into()));
@@ -146,6 +125,7 @@ impl<M: Module> Backend<M> {
argument_types: Vec<Type>,
return_type: Type,
) -> Result<FuncId, cranelift_module::ModuleError> {
println!("Declaring {:?} function {}", linkage, name);
let basic_signature = Signature {
params: argument_types
.iter()
@@ -176,6 +156,22 @@ impl<M: Module> Backend<M> {
return_type: Type,
body: Expression<Type>,
) -> 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
// we won't be using close to 2^32 variables!
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.
let buffer_ptr = self.output_buffer_ptr();
let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64);
// 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
.module
.declare_data_in_func(string_data_id, builder.func);
let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref);
// Look up the value for the variable. Because this might be a
// 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 (val, vtype) = self.compile_value_or_ref(var, variables, builder)?;
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 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.def_var(variable, value);
variables.insert(name, ReferenceBuilder::Local(value_type, variable));
Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void))
}
}
}
Expression::Call(_, _, function, args) => {
let (arguments, _argument_types): (Vec<_>, Vec<_>) = args
@@ -508,9 +523,11 @@ impl<M: Module> Backend<M> {
[result] => match result_type {
Type::Primitive(ct) => Ok((*result, ct.into())),
Type::Function(_, rt) => match *rt {
Type::Function(_, _) => panic!("function returns a function?"),
Type::Primitive(ct) => Ok((*result, ct.into())),
Type::Function(_, _) => {
panic!("function returns a function?")
}
Type::Primitive(ct) => Ok((*result, ct.into())),
},
},
_ => panic!("don't support multi-value returns yet"),
}
@@ -530,6 +547,7 @@ impl<M: Module> Backend<M> {
variables: &HashMap<Variable, ReferenceBuilder>,
builder: &mut FunctionBuilder,
) -> Result<(entities::Value, ConstantType), BackendError> {
println!("compile_value_or_ref {:?}", valref);
match valref {
ValueOrRef::Value(_, _, val) => match val {
Value::I8(_, v) => {
@@ -575,7 +593,25 @@ impl<M: Module> Backend<M> {
},
ValueOrRef::Ref(_, _, name) => match variables.get(&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::U16 => (2, 2),
PrimitiveType::U32 => (4, 4),
PrimitiveType::U64 => (4, 4),
PrimitiveType::U64 => (8, 8),
PrimitiveType::I8 => (1, 1),
PrimitiveType::I16 => (2, 2),
PrimitiveType::I32 => (4, 4),
PrimitiveType::I64 => (4, 4),
PrimitiveType::I64 => (8, 8),
};
let mut result = DataDescription::new();
result.define_zeroinit(size);

View File

@@ -5,6 +5,7 @@ use codespan_reporting::{
files::SimpleFiles,
term::{self, Config},
};
use cranelift_module::Module;
use pretty::termcolor::{ColorChoice, StandardStream};
use target_lexicon::Triple;
@@ -134,7 +135,25 @@ impl Compiler {
// Finally, send all this to Cranelift for conversion into an object file.
let mut backend = Backend::object_file(Triple::host())?;
let unknown = "<unknown>".to_string();
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()?))
}

View File

@@ -359,19 +359,21 @@ fn generate_random_expression(
.iter()
.filter_map(|(variable, ty)| {
if ty.is_printable() {
Some(variable.clone())
Some((variable.clone(), ty.clone()))
} else {
None
}
})
.collect::<Vec<Variable>>();
.collect::<Vec<_>>();
if possible_variables.is_empty() {
generate_random_binding(rng, env)
} else {
let (variable, var_type) = possible_variables.choose(rng).unwrap();
Expression::Print(
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>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
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>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
@@ -159,7 +161,7 @@ pub enum Expression<Type> {
Cast(Location, Type, ValueOrRef<Type>),
Primitive(Location, Type, Primitive, Vec<ValueOrRef<Type>>),
Block(Location, Type, Vec<Expression<Type>>),
Print(Location, Variable),
Print(Location, ValueOrRef<Type>),
Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<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>
where
A: 'a,
D: ?Sized + DocAllocator<'a, A>,
D: ?Sized + DocAllocator<'a, A> + 'a,
&'b Type: Pretty<'a, D, A>,
pretty::DocBuilder<'a, D, A>: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
match self {
@@ -235,24 +238,28 @@ where
Some((last, &[])) => last.pretty(allocator),
Some((last, start)) => {
let mut result = allocator.text("{").append(allocator.hardline());
for stmt in start.iter() {
result = result
.append(stmt.pretty(allocator))
let starts = start.iter().map(|x| {
x.pretty(allocator)
.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
.append(last.pretty(allocator))
.append(allocator.hardline())
.append(allocator.text("}"))
result.append(last).append(allocator.text("}"))
}
},
Expression::Print(_, var) => allocator
.text("print")
.append(allocator.space())
.append(allocator.text(var.as_ref().to_string())),
.append(var.pretty(allocator)),
Expression::Bind(_, var, ty, expr) => allocator
.text(var.as_ref().to_string())
.append(allocator.space())

View File

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

View File

@@ -150,10 +150,11 @@ fn convert_statement(
.get(&final_name)
.expect("print variable defined before use")
.clone();
println!("varty for {} is {}", final_name, varty);
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) => {
@@ -256,6 +257,7 @@ fn convert_expression(
.get(&final_name)
.cloned()
.expect("variable bound before use");
println!("rtype for {} is {}", final_name, rtype);
let refexp =
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::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(
loc,
@@ -114,7 +114,10 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type {
TypeOrVar::Primitive(x) => Type::Primitive(x),
TypeOrVar::Variable(_, tvar) => match resolutions.get(&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(
args.drain(..)