Skip to content

Commit

Permalink
Add value primities. Nicer CLI.
Browse files Browse the repository at this point in the history
  • Loading branch information
gsps committed Jun 8, 2020
1 parent ad26dc1 commit 8387fcb
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 38 deletions.
17 changes: 13 additions & 4 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use std::io::{Read, Write};

fn read_byte() -> u8 {
use std::io::Read;
std::io::stdin().bytes().next().unwrap().ok().unwrap()
std::io::stdin()
.bytes()
.next()
.map(|res| res.unwrap_or_else(|e| panic!("Failed to read: {}", e)))
.unwrap_or(0)
}

#[no_mangle]
pub extern "C" fn rt_bytewrite(x: u32) -> u32 {
read_byte();
std::io::stdout()
.write(&[x as u8])
.expect("Failed to write byte");
x
}

Expand All @@ -16,6 +23,8 @@ pub extern "C" fn rt_byteread() -> u32 {

#[no_mangle]
pub extern "C" fn rt_halt(x: u32) -> u32 {
println!("(EXIT! {})", x);
std::io::stdout()
.flush()
.expect("Failed to flush stdout before exit");
std::process::exit(x as i32)
}
29 changes: 23 additions & 6 deletions src/bin/l3jit.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
use ::l3_llvm_backend::l3;

fn run_str<'a>(example_str: &'a str) {
fn run_str<'a>(program_str: &'a str) {
let program =
l3::parser::parse_l3cps_program(example_str).unwrap_or_else(|e| panic!("Parsing error: {}", e));
l3::parser::parse_l3cps_program(program_str).unwrap_or_else(|e| panic!("Parsing error: {}", e));
l3::codegen::compile_and_run_program(&program)
}

fn main() {
use std::io::{self, Read};
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer).expect("Failed to read program");
run_str(buffer.as_str());
use std::io::Read;
use std::path::Path;

let args: Vec<String> = std::env::args().collect();
let program = if args.len() < 2 {
let mut buffer = String::new();
std::io::stdin()
.read_to_string(&mut buffer)
.expect("Failed to read program from stdin");
buffer
} else if args.len() == 2 {
std::fs::read_to_string(Path::new(args.last().unwrap()))
.expect("Failed to read program from file")
} else {
eprintln!(
"Usage: {} [path-to-program]\n (or pass program on standard input)",
args.first().unwrap()
);
std::process::exit(1);
};
run_str(program.as_str());
}
87 changes: 59 additions & 28 deletions src/l3/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
ptr_value
}

fn add_vars_for_locals(&mut self, tree: &Tree<'a>) {
visit_bb(
tree,
|name, prim, args| {
self.add_var(&name);
},
|tree| {},
);
}

fn arg_value(&self, arg: &Atom<'a>) -> IntValue<'ctx> {
match arg {
Atom::AtomL(value) => make_value(self.context, *value),
Expand All @@ -168,6 +178,21 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
self.builder.build_unconditional_branch(*block);
}

fn emit_call(
&self,
name: &'a str,
args: &[BasicValueEnum<'ctx>],
node_name: &str,
) -> IntValue<'ctx> {
let fn_value = get_fn_value(self.module, name);
let call = self.builder.build_call(fn_value, args, node_name);
call
.try_as_basic_value()
.left()
.expect("Failed to generate call")
.into_int_value()
}

// Call a local continuation
fn emit_cnt_call_direct(&self, cnt_name: &Name<'a>, value_opt: Option<IntValue<'ctx>>) {
let cnt = self.all_cnts.get(cnt_name).unwrap();
Expand All @@ -184,12 +209,35 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
}

fn emit_block(&self, tree: &Tree<'a>) {
let b = &self.builder;
visit_bb(
tree,
// Handle value primitives
|name, prim, args| {
// TODO: Handle ValuePrimitives
unimplemented!()
let var = self.vars.get(&name).unwrap();
let mut args = args.clone();
let mut args = args.drain(..);
let mut next_arg = || self.arg_value(&args.next().unwrap());

use ValuePrimitive::*;
let result = match prim {
CPSAdd => b.build_int_add(next_arg(), next_arg(), "cpsadd"),
CPSSub => b.build_int_sub(next_arg(), next_arg(), "cpssub"),
CPSMul => b.build_int_mul(next_arg(), next_arg(), "cpsmul"),
CPSDiv => b.build_int_signed_div(next_arg(), next_arg(), "cpsdiv"),
// TODO: Check that `srem` behaves like L3's `%`
CPSMod => b.build_int_signed_rem(next_arg(), next_arg(), "cpsmod"),
CPSShiftLeft => b.build_left_shift(next_arg(), next_arg(), "cpsshiftleft"),
CPSShiftRight => b.build_right_shift(next_arg(), next_arg(), true, "cpsshiftright"),
CPSXOr => b.build_xor(next_arg(), next_arg(), "cpsxor"),
CPSAnd => b.build_and(next_arg(), next_arg(), "cpsand"),
CPSOr => b.build_or(next_arg(), next_arg(), "cpsor"),
CPSId => next_arg(),
CPSByteRead => self.emit_call("rt_byteread", &[], "cpsbyteread"),
CPSByteWrite => self.emit_call("rt_bytewrite", &[next_arg().into()], "cpsbytewrite"),
_ => unreachable!(),
};
self.builder.build_store(*var, result);
},
// Handle rest of the continuation
|tree| match tree {
Expand All @@ -214,18 +262,12 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
} => {
let fun = self.program.get_function(fun_name);
assert_eq!(fun.params.len(), args.len());
let fn_value = get_fn_value(self.module, fun_name.0);

let args = args
.iter()
.map(|arg| self.arg_value(arg).into())
.collect::<Vec<BasicValueEnum>>();

let result = self.builder.build_call(fn_value, &args, "call_fun");
let result = result
.try_as_basic_value()
.left()
.expect("Failed to generate call")
.into_int_value();
let result = self.emit_call(fun_name.0, &args, "call_fun");

if *ret_cnt == self.ret_cnt_name {
self.emit_cnt_call_indirect(result);
Expand Down Expand Up @@ -260,15 +302,7 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
}

Halt { arg } => {
let fn_value = get_fn_value(self.module, "rt_halt");
let call = self
.builder
.build_call(fn_value, &[self.arg_value(arg).into()], "halt");
let call = call
.try_as_basic_value()
.left()
.expect("Failed to generate halt call")
.into_int_value();
let call = self.emit_call("rt_halt", &[self.arg_value(arg).into()], "halt");
self.builder.build_return(Some(&call));
}

Expand All @@ -294,26 +328,23 @@ impl<'a, 'ctx> Codegen<'a, 'ctx> {
state.builder.position_at_end(entry);

// Create stack allocations for all bindings
// (LLVM should promote these to temporary registers during compilation.)

// Allocate function parameter bindings
// Allocate function parameter bindings and locals
for (param, name) in fn_value.get_param_iter().zip(fun.params.iter()) {
let var = state.add_var(name);
state.builder.build_store(var, param);
}

// Allocate continuation parameter and local bindings, register continuations
state.add_vars_for_locals(&fun.body);

// Allocate continuation parameters and locals, register continuations
visit_cnts(&fun.body, |cnt| {
for param_name in &cnt.params {
state.add_var(param_name);
}

visit_bb(
&cnt.body,
|name, prim, args| {
state.add_var(&name);
},
|tree| {},
);
state.add_vars_for_locals(&cnt.body);

let block = self.context.append_basic_block(fn_value, cnt.name.0);
state.blocks.insert(cnt.name, block);
Expand Down
18 changes: 18 additions & 0 deletions src/tests/run_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,27 @@ test_run!(
None,
1
);
test_run!(
test_app_first,
"(let* ((f (fun (r x y) (r x))) (c (cnt (z) (halt z)))) (f c 1 2))",
None,
1
);
test_run!(
test_app_second,
"(let* ((f (fun (r x y) (r y))) (c (cnt (z) (halt z)))) (f c 1 2))",
None,
2
);
test_run!(
test_prim_add,
"(let* ((x (@+ 1 2))) (halt x))",
None,
3
);
test_run!(
test_prim_bytewrite,
"(let* ((x (@byte-write 43))) (halt x))",
Some("+"),
43
);
1 change: 1 addition & 0 deletions tests/byteread.l3cps
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(let* ((x (@byte-read))) (halt x))

0 comments on commit 8387fcb

Please sign in to comment.