λ Support functions! #5
@@ -1,5 +1,7 @@
|
||||
x = 1;
|
||||
function add_x(y) x + y
|
||||
a = 3;
|
||||
print x;
|
||||
result = add_x(a);
|
||||
print x;
|
||||
print result;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,10 +467,30 @@ impl<M: Module> Backend<M> {
|
||||
let ir_type = ir::Type::from(value_type);
|
||||
let variable = self.generate_local();
|
||||
|
||||
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))
|
||||
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) => {
|
||||
@@ -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::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);
|
||||
|
||||
@@ -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()?))
|
||||
}
|
||||
|
||||
|
||||
@@ -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()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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(..)
|
||||
|
||||
Reference in New Issue
Block a user