diff --git a/forge_core/src/compiler/ast_nodes/block.rs b/forge_core/src/compiler/ast_nodes/block.rs index edc8f40..d0e0ea9 100644 --- a/forge_core/src/compiler/ast_nodes/block.rs +++ b/forge_core/src/compiler/ast_nodes/block.rs @@ -65,7 +65,8 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x; asm(&x) { swap 12\nstorew } }")), vec![ - "loadw frame", // Push the addr of x + "pushr", // capture frame ptr + "peekr", // Push the addr of x "swap 12", // The asm body, which swaps 12 behind it and stores it there "storew", ] @@ -78,20 +79,21 @@ mod test { assert_eq!( test_body(state_for("fn test() { repeat(5) c { 2; } var k = 5; return k; }")), vec![ + "pushr", // capture frame ptr "push 0", // Create the 'c' var and store 0 in it - "loadw frame", + "peekr", "storew", "push 5", "#while", // Starting the loop: "dup", // Copy the limit - "loadw frame", // Load c + "peekr", // Load c "loadw", "sub", // Subtract c from a "agt 0", // Are we still positive? "#do", "push 2", // Pointless loop body "pop", - "loadw frame", // Load c as an lvalue + "peekr", // Load c as an lvalue "dup", // Dup it, load it, add 1 "loadw", "add 1", @@ -100,10 +102,12 @@ mod test { "#end", // End of the loop body! "pop", // Drop the limit off the top "push 5", // Push the rvalue we'll put in 'k' - "loadw frame", // THIS IS THE TEST: 'k' should go at frame + 0, because it's + "peekr", // THIS IS THE TEST: 'k' should go at frame + 0, because it's "storew", // taking the same (now freed) frame slot that c took, because c is - "loadw frame", // now out of scope + "peekr", // now out of scope "loadw", + "popr", // Blow away old frame ptr + "pop", "ret", ] .join("\n") diff --git a/forge_core/src/compiler/ast_nodes/call.rs b/forge_core/src/compiler/ast_nodes/call.rs index f172391..fc37067 100644 --- a/forge_core/src/compiler/ast_nodes/call.rs +++ b/forge_core/src/compiler/ast_nodes/call.rs @@ -9,19 +9,27 @@ impl Compilable for Call { // Require a function let sig = sig.expect("lvalue outside a function"); - // We need a stack consisting of the arguments (last on top), followed by the address to call + // We need a stack consisting of the arguments (last on top), followed by the new fn's frame + // pointer, followed by the address to call. // So, first eval the args: for arg in self.args { arg.process(state, Some(sig), loc)?; } + // Now we increment the frame ptr to right after the current frame: + sig.emit("peekr"); + let frame_size = sig.frame_size(); + if frame_size > 0 { + sig.emit_arg("add", frame_size); + } + // Eval the target self.target.0.process(state, Some(sig), loc)?; // Before we actually do the call though, we need to deal with some paperwork around the // frame pointer. We will store the current frame pointer in the rstack: - sig.emit("loadw frame"); - sig.emit("pushr"); + //sig.emit("loadw frame"); + //sig.emit("pushr"); // todo this makes arrays not work. we can no longer statically compute the frame size, we // need to have the frame size stored at runtime somehow, probably rstack. This will mean @@ -37,23 +45,15 @@ impl Compilable for Call { // todo in fact, a larger refactor would just store the frame size as the first thing in the // frame, to make vardecls faster - // Now we increment the frame ptr to right after the current frame: - let frame_size = sig.frame_size(); - if frame_size > 0 { - sig.emit("loadw frame"); - sig.emit_arg("add", frame_size); - sig.emit("storew frame"); - } - - // Frame is now pointing at a safe place, the top of stack is the target, do the call: + // Do the call: sig.emit("call"); // And this is where we'll return to. The function has popped its args and left a word on // the stack as a return value, which someone else will deal with (this is the word that // this expr::call will end up evaluating to). But before we're done, we need to restore - // our frame pointerS - sig.emit("popr"); - sig.emit("storew frame"); + // our frame pointer: + //sig.emit("popr"); + //sig.emit("storew frame"); // And we're finished! Ok(()) @@ -69,22 +69,18 @@ mod test { assert_eq!( test_body(state_for("fn test(a, b) { test(2, 3); }")), vec![ - "loadw frame", // capture arg b + "pushr", + "peekr", // capture arg b "add 3", "storew", - "loadw frame", // capture arg a + "peekr", // capture arg a "storew", "push 2", // evaluating args, in order "push 3", - "push _forge_gensym_1", // evaluating target (this fn) - "loadw frame", // Store the frame ptr - "pushr", - "loadw frame", // Increment the frame ptr + "peekr", // The new fn's frame ptr: "add 6", - "storew frame", + "push _forge_gensym_1", // evaluating target (this fn) "call", // Actually make the call - "popr", // Restore the frame ptr - "storew frame", "pop", // expr-as-statement drops the evaluated value ] .join("\n") diff --git a/forge_core/src/compiler/ast_nodes/conditional.rs b/forge_core/src/compiler/ast_nodes/conditional.rs index 5d79e31..1724938 100644 --- a/forge_core/src/compiler/ast_nodes/conditional.rs +++ b/forge_core/src/compiler/ast_nodes/conditional.rs @@ -31,16 +31,17 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } }")), vec![ + "pushr", "push 3", - "loadw frame", + "peekr", "storew", // x = 3 - "loadw frame", + "peekr", "loadw", "push 2", "agt", // The condition, x > 2 "#if", // The if itself "push 1", - "loadw frame", + "peekr", "storew", // The branch, x = 1 "#end"] .join("\n") @@ -52,20 +53,21 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } else { x = 7; } }")), vec![ + "pushr", "push 3", - "loadw frame", + "peekr", "storew", // x = 3 - "loadw frame", + "peekr", "loadw", "push 2", "agt", // The condition, x > 2 "#if", // The if itself "push 1", - "loadw frame", + "peekr", "storew", // The affirmative branch, x = 1 "#else", // The alternative "push 7", - "loadw frame", + "peekr", "storew", // x = 7 "#end"] .join("\n") diff --git a/forge_core/src/compiler/ast_nodes/expr.rs b/forge_core/src/compiler/ast_nodes/expr.rs index afb1ed2..d087661 100644 --- a/forge_core/src/compiler/ast_nodes/expr.rs +++ b/forge_core/src/compiler/ast_nodes/expr.rs @@ -28,7 +28,6 @@ impl Compilable for Expr { Ok(()) } Expr::Name(name) => { - // todo split into new file match lookup(&name, global_scope, &sig.local_scope) { // Names are treated differently depending on what they are Some(Variable::Literal(val)) => { @@ -51,7 +50,8 @@ impl Compilable for Expr { Some(Variable::Local(offset)) => { // Names of locals are added from the frame pointer let offset = *offset; - sig.emit("loadw frame"); + + sig.emit("peekr"); // Frame ptr is top of rstack if offset > 0 { sig.emit_arg("add", offset); } @@ -158,11 +158,12 @@ mod test { assert_eq!( test_body(state), vec![ + "pushr", "push _forge_gensym_3", - "loadw frame", + "peekr", "storew", // the assignment for x "push _forge_gensym_4", - "loadw frame", + "peekr", "add 3", // the address of y (frame + 3) and put gensym_4 in it "storew", ] @@ -175,11 +176,12 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x = 3; var y = &x; }")), vec![ + "pushr", "push 3", - "loadw frame", + "peekr", "storew", // the assignment for x - "loadw frame", // The addr of x - "loadw frame", + "peekr", // The addr of x + "peekr", "add 3", // the address of y (frame + 3) and put the addr of x (frame) in it "storew", ] @@ -192,10 +194,11 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x = 3; *1000 = *x; }")), vec![ + "pushr", "push 3", - "loadw frame", + "peekr", "storew", // the assignment for x - "loadw frame", // Now we're compiling *x, so load x's value, which is 3 + "peekr", // Now we're compiling *x, so load x's value, which is 3 "loadw", "loadw", // Then load the value at 3 "push 1000", // Push the addr 1000, for the lvalue @@ -210,10 +213,11 @@ mod test { assert_eq!( test_body(state_for("const foo = \"foo\"; fn test() { var x = \"bar\" + 3; }")), vec![ + "pushr", "push _forge_gensym_3", // 1 is the label in the string table for "foo", 2 for "blah," "push 3", // so 3 is the string "bar" "add", // Add 3 to that address - "loadw frame", // Store it in the first var + "peekr", // Store it in the first var "storew", ] .join("\n") diff --git a/forge_core/src/compiler/ast_nodes/function.rs b/forge_core/src/compiler/ast_nodes/function.rs index 02daf01..ea31abd 100644 --- a/forge_core/src/compiler/ast_nodes/function.rs +++ b/forge_core/src/compiler/ast_nodes/function.rs @@ -45,16 +45,17 @@ mod test { assert_eq!( test_body(state_for("fn test(a, b) { b = 17 + a; }")), vec![ - "loadw frame", // Capture var b + "pushr", // Store frame ptr + "peekr", // Capture var b "add 3", "storew", - "loadw frame", // Capture var a + "peekr", // Capture var a "storew", "push 17", // Start calculating the rvalue, push the literal - "loadw frame", // This is looking up the "a" arg, at frame + 0 + "peekr", // This is looking up the "a" arg, at frame + 0 "loadw", "add", // 17 + a - "loadw frame", // Calculate the lvalue + "peekr", // Calculate the lvalue "add 3", // "b" arg is frame + 3 "storew", // Finally store ] diff --git a/forge_core/src/compiler/ast_nodes/lvalue.rs b/forge_core/src/compiler/ast_nodes/lvalue.rs index e678682..c35c020 100644 --- a/forge_core/src/compiler/ast_nodes/lvalue.rs +++ b/forge_core/src/compiler/ast_nodes/lvalue.rs @@ -41,7 +41,7 @@ impl Compilable for Lvalue { } Variable::Local(offset) => { let offset = *offset; - sig.emit_arg("loadw", "frame"); + sig.emit("peekr"); if offset > 0 { sig.emit_arg("add", offset); } diff --git a/forge_core/src/compiler/ast_nodes/repeat_loop.rs b/forge_core/src/compiler/ast_nodes/repeat_loop.rs index b20f13a..c53c50a 100644 --- a/forge_core/src/compiler/ast_nodes/repeat_loop.rs +++ b/forge_core/src/compiler/ast_nodes/repeat_loop.rs @@ -86,37 +86,38 @@ mod test { assert_eq!( test_body(state_for("fn test(a) { var x = 0; repeat(a) c { x = x + c; } return x; }")), vec![ - "loadw frame", // capture arg a + "pushr", + "peekr", // capture arg a "storew", "push 0", // Create the 'x' var and store 0 in it - "loadw frame", + "peekr", "add 3", "storew", "push 0", // Create the 'c' var and store 0 in it - "loadw frame", + "peekr", "add 6", "storew", - "loadw frame", // Load 'a' from args + "peekr", // Load 'a' from args "loadw", "#while", // Starting the loop: "dup", // Copy the limit - "loadw frame", // Load c + "peekr", // Load c "add 6", "loadw", "sub", // Subtract c from a "agt 0", // Are we still positive? "#do", - "loadw frame", // Load x + "peekr", // Load x "add 3", "loadw", - "loadw frame", // Load c + "peekr", // Load c "add 6", "loadw", "add", // Add c to x - "loadw frame", // Load x as an lvalue + "peekr", // Load x as an lvalue "add 3", "storew", // Store c + x into it - "loadw frame", // Load c as an lvalue + "peekr", // Load c as an lvalue "add 6", "dup", // Dup it, load it, add 1 "loadw", @@ -125,9 +126,11 @@ mod test { "storew", "#end", // End of the loop body! "pop", // Drop the limit off the top - "loadw frame", // Load x so we can return it + "peekr", // Load x so we can return it "add 3", "loadw", + "popr", // toss old frame ptr + "pop", "ret", ] .join("\n") @@ -140,32 +143,35 @@ mod test { assert_eq!( test_body(state_for("fn test(a) { var x = 1; repeat(a) { x = x * 2; } return x; }")), vec![ - "loadw frame", // capture arg a + "pushr", + "peekr", // capture arg a "storew", "push 1", // Create the 'x' var and store 1 in it - "loadw frame", + "peekr", "add 3", "storew", - "loadw frame", // Load 'a' from args + "peekr", // Load 'a' from args "loadw", "#while", // Starting the loop: "dup", // Copy the limit "agt 0", // Are we still positive? "#do", - "loadw frame", // Load x + "peekr", // Load x "add 3", "loadw", "push 2", // double it "mul", - "loadw frame", // Load x as an lvalue + "peekr", // Load x as an lvalue "add 3", "storew", // Store 2x into it "sub 1", // decrement the counter "#end", // End of the loop body! "pop", // Drop the limit off the top - "loadw frame", // Load x so we can return it + "peekr", // Load x so we can return it "add 3", "loadw", + "popr", // Toss old frame ptr + "pop", "ret", ].join("\n") ); diff --git a/forge_core/src/compiler/ast_nodes/return.rs b/forge_core/src/compiler/ast_nodes/return.rs index 2bdabf4..46919ae 100644 --- a/forge_core/src/compiler/ast_nodes/return.rs +++ b/forge_core/src/compiler/ast_nodes/return.rs @@ -11,11 +11,20 @@ impl Compilable for Return { match self { Return(None) => { // Returning nothing, so just default to returning a 0: + // (but pop our frame pointer first) + sig.emit("popr"); + sig.emit("pop"); sig.emit_arg("ret", 0) } Return(Some(expr)) => { // Eval the expr and emit a ret for it expr.process(state, Some(sig), loc)?; + + // Now that we're done with it, blow away the frame ptr: + sig.emit("popr"); + sig.emit("pop"); + + // Finally ret sig.emit("ret") } } @@ -33,12 +42,15 @@ mod test { assert_eq!( test_body(state_for("fn test(a) { return a + 3; }")), vec![ - "loadw frame", // capture arg a + "pushr", + "peekr", // capture arg a "storew", - "loadw frame", // Load a + "peekr", // Load a "loadw", "push 3", // Add 3 "add", + "popr", // Toss frame ptr + "pop", "ret", // Return that ] .join("\n") @@ -50,13 +62,16 @@ mod test { assert_eq!( test_body(state_for("fn test(a) { if (a > 0) { return; } }")), vec![ - "loadw frame", // capture arg a + "pushr", + "peekr", // capture arg a "storew", - "loadw frame", // Load a + "peekr", // Load a "loadw", "push 0", // Compare to 0 "agt", "#if", // If statement + "popr", // Toss frame ptr + "pop", "ret 0", // Default return value, for an expr-less return "#end", ] diff --git a/forge_core/src/compiler/ast_nodes/var_decl.rs b/forge_core/src/compiler/ast_nodes/var_decl.rs index 32b5155..c121280 100644 --- a/forge_core/src/compiler/ast_nodes/var_decl.rs +++ b/forge_core/src/compiler/ast_nodes/var_decl.rs @@ -36,16 +36,17 @@ mod test { assert_eq!( test_body(state_for("fn test() { var a; var b = 7; a = b * 2; }")), vec![ + "pushr", "push 7", // Start calculating the rvalue, push the literal - "loadw frame", // "b" is the second local var at frame + 3 + "peekr", // "b" is the second local var at frame + 3 "add 3", "storew", // Do the initialization - "loadw frame", // Now we're evaluating b * 2 + "peekr", // Now we're evaluating b * 2 "add 3", // b is at frame + 3... "loadw", // load it to the stack "push 2", "mul", // b * 2 evaluated - "loadw frame", // Loading "a" as an lvalue + "peekr", // Loading "a" as an lvalue "storew", // doing the assignment ] .join("\n") diff --git a/forge_core/src/compiler/ast_nodes/while_loop.rs b/forge_core/src/compiler/ast_nodes/while_loop.rs index bb37c73..57459d5 100644 --- a/forge_core/src/compiler/ast_nodes/while_loop.rs +++ b/forge_core/src/compiler/ast_nodes/while_loop.rs @@ -27,37 +27,38 @@ mod test { assert_eq!( test_body(state_for("fn test() { var x = 0; var c = 0; while (c < 10) { x = x + c; c = c + 1; } }")), vec![ + "pushr", "push 0", - "loadw frame", + "peekr", "storew", // var x = 0 "push 0", - "loadw frame", + "peekr", "add 3", "storew", // var c = 0 "#while", // Start the loop - "loadw frame", + "peekr", "add 3", "loadw", "push 10", "alt", // c > 10 "#do", // Start loop body - "loadw frame", + "peekr", "loadw", - "loadw frame", + "peekr", "add 3", "loadw", "add", - "loadw frame", + "peekr", "storew", // x = x + c - "loadw frame", + "peekr", "add 3", "loadw", "push 1", "add", - "loadw frame", + "peekr", "add 3", "storew", // c = c + 1 - "#end", // End the loop body + "#end" // End the loop body ] .join("\n") ); diff --git a/forge_core/src/compiler/compiled_fn.rs b/forge_core/src/compiler/compiled_fn.rs index 5d6f7e5..6d50541 100644 --- a/forge_core/src/compiler/compiled_fn.rs +++ b/forge_core/src/compiler/compiled_fn.rs @@ -84,12 +84,15 @@ impl CompiledFn { self.local_scope.len() * 3 } - /// Emit the code to add the arguments' names to the local scopr, and move the arguments off + /// Emit the code to add the arguments' names to the local scope, and move the arguments off /// the stack into the frame. Should be run before the body is compiled, because this code /// has to be the first thing in the body pub(crate) fn handle_args(&mut self, function: &Function) -> Result<(), CompileError> { let mut arg_names: Vec<&str> = Vec::new(); + // Top argument is the frame ptr; copy it to the rstack: + self.emit("pushr"); + // Add each argument as a local for name in function.args.iter() { self.add_local(name.as_str())?; @@ -103,7 +106,7 @@ impl CompiledFn { for name in arg_names { if let Variable::Local(offset) = self.local_scope[name] { - self.emit("loadw frame"); + self.emit("peekr"); if offset != 0 { self.emit_arg("add", offset); } diff --git a/forge_core/src/compiler/mod.rs b/forge_core/src/compiler/mod.rs index 49d1539..7aeb7ea 100644 --- a/forge_core/src/compiler/mod.rs +++ b/forge_core/src/compiler/mod.rs @@ -31,6 +31,9 @@ pub fn build_boot(src: &str) -> Result, CompileError> { // Now we start piling stuff into the vec, starting with an org: asm.push(".org 0x400".into()); + // Main takes no args, but it does take a frame ptr: + asm.push("push stack".into()); + // jmp into main: asm.push(format!("call {}", label)); @@ -48,8 +51,7 @@ pub fn build_boot(src: &str) -> Result, CompileError> { // Final thing is to place a label for the stack: // (this is just a cell with the address of the following word) - asm.push("frame: .db $+1".into()); - asm.push(".db 0".into()); + asm.push("stack: .db 0".into()); Ok(asm) } else { Err(CompileError(0, 0, "Function main not defined (or not a function)".into())) @@ -68,27 +70,33 @@ mod test { let asm = build_boot("fn main() { return 5; }".into()).unwrap(); assert_eq!(asm.join("\n"), vec![ ".org 0x400", + "push stack", "call _forge_gensym_1", "hlt", "_forge_gensym_1:", + "pushr", "push 5", + "popr", + "pop", "ret", - "frame: .db $+1", - ".db 0", + "stack: .db 0", ].join("\n")); // Slightly more complicated, with a global str let asm = build_boot("const str = \"blah\"; fn main() { return str; }".into()).unwrap(); assert_eq!(asm.join("\n"), vec![ ".org 0x400", + "push stack", "call _forge_gensym_2", "hlt", "_forge_gensym_1: .db \"blah\\0\"", "_forge_gensym_2:", + "pushr", "push _forge_gensym_1", + "popr", + "pop", "ret", - "frame: .db $+1", - ".db 0", + "stack: .db 0", ].join("\n")) } }