From 8387fcb52b8a4a5bc1baa2bf1b9cae28dd804ebb Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Mon, 8 Jun 2020 18:54:48 +0200 Subject: [PATCH] Add value primities. Nicer CLI. --- runtime/src/lib.rs | 17 +++++++-- src/bin/l3jit.rs | 29 +++++++++++--- src/l3/codegen.rs | 87 ++++++++++++++++++++++++++++-------------- src/tests/run_tests.rs | 18 +++++++++ tests/byteread.l3cps | 1 + 5 files changed, 114 insertions(+), 38 deletions(-) create mode 100644 tests/byteread.l3cps diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1b8c29b..c13f511 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -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 } @@ -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) } diff --git a/src/bin/l3jit.rs b/src/bin/l3jit.rs index b438355..47fa968 100644 --- a/src/bin/l3jit.rs +++ b/src/bin/l3jit.rs @@ -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 = 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()); } diff --git a/src/l3/codegen.rs b/src/l3/codegen.rs index 1216c6e..d6ed41a 100644 --- a/src/l3/codegen.rs +++ b/src/l3/codegen.rs @@ -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), @@ -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>) { let cnt = self.all_cnts.get(cnt_name).unwrap(); @@ -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 { @@ -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::>(); - - 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); @@ -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)); } @@ -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); diff --git a/src/tests/run_tests.rs b/src/tests/run_tests.rs index ab69969..72fc93b 100644 --- a/src/tests/run_tests.rs +++ b/src/tests/run_tests.rs @@ -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 +); diff --git a/tests/byteread.l3cps b/tests/byteread.l3cps new file mode 100644 index 0000000..bb6fed4 --- /dev/null +++ b/tests/byteread.l3cps @@ -0,0 +1 @@ +(let* ((x (@byte-read))) (halt x)) \ No newline at end of file