diff --git a/forge_core/src/compiler/ast_nodes/block.rs b/forge_core/src/compiler/ast_nodes/block.rs index d0e0ea9..331558d 100644 --- a/forge_core/src/compiler/ast_nodes/block.rs +++ b/forge_core/src/compiler/ast_nodes/block.rs @@ -69,6 +69,7 @@ mod test { "peekr", // Push the addr of x "swap 12", // The asm body, which swaps 12 behind it and stores it there "storew", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) @@ -109,6 +110,7 @@ mod test { "popr", // Blow away old frame ptr "pop", "ret", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); diff --git a/forge_core/src/compiler/ast_nodes/call.rs b/forge_core/src/compiler/ast_nodes/call.rs index fc37067..854dfb8 100644 --- a/forge_core/src/compiler/ast_nodes/call.rs +++ b/forge_core/src/compiler/ast_nodes/call.rs @@ -17,6 +17,9 @@ impl Compilable for Call { } // Now we increment the frame ptr to right after the current frame: + // todo this messes with recursive calls: we're hard-coding the frame size _right now,_ + // which may increase later with later vardecls in the fn, which could be _executed_ before + // this call is. We need to store the top of the frame at runtime somehow. sig.emit("peekr"); let frame_size = sig.frame_size(); if frame_size > 0 { @@ -31,29 +34,13 @@ impl Compilable for Call { //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 - // every fn will stick the frame ptr it's given in the rstack in its preamble, and that any - // vardecl will increment that ptr (that the top of the rstack is the end of the current - // frame, essentially). Returns also need to popr/pop before doing the actual ret. - // todo also for that matter, that means that the frame ptr no longer needs to be a global - // symbol: have it be stored in the rstack and passed as the first param in a call. So the - // calling convention is, push the args, push the new fn's frame ptr (current top-of-frame+1), - // call. The receiving fn puts its frame ptr into the rstack, dups it there, puts the args - // (it knows its own arity) into its frame, and then every vardecl grabs the 2nd thing from r, - // increments it by whatever, and puts it back. - // todo in fact, a larger refactor would just store the frame size as the first thing in the - // frame, to make vardecls faster - // 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 pointer: - //sig.emit("popr"); - //sig.emit("storew frame"); + // this expr::call will end up evaluating to). What's left on our rstack now is our frame ptr, + // which is what we want. // And we're finished! Ok(()) @@ -82,6 +69,7 @@ mod test { "push _forge_gensym_1", // evaluating target (this fn) "call", // Actually make the call "pop", // expr-as-statement drops the evaluated value + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); diff --git a/forge_core/src/compiler/ast_nodes/conditional.rs b/forge_core/src/compiler/ast_nodes/conditional.rs index 1724938..427b272 100644 --- a/forge_core/src/compiler/ast_nodes/conditional.rs +++ b/forge_core/src/compiler/ast_nodes/conditional.rs @@ -43,7 +43,9 @@ mod test { "push 1", "peekr", "storew", // The branch, x = 1 - "#end"] + "#end", + "popr", "pop", "ret 0" // Implicit void return + ] .join("\n") ); } @@ -69,7 +71,9 @@ mod test { "push 7", "peekr", "storew", // x = 7 - "#end"] + "#end", + "popr", "pop", "ret 0" // Implicit void return + ] .join("\n") ) } diff --git a/forge_core/src/compiler/ast_nodes/expr.rs b/forge_core/src/compiler/ast_nodes/expr.rs index d087661..8ad02d8 100644 --- a/forge_core/src/compiler/ast_nodes/expr.rs +++ b/forge_core/src/compiler/ast_nodes/expr.rs @@ -166,6 +166,7 @@ mod test { "peekr", "add 3", // the address of y (frame + 3) and put gensym_4 in it "storew", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) @@ -184,6 +185,7 @@ mod test { "peekr", "add 3", // the address of y (frame + 3) and put the addr of x (frame) in it "storew", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) @@ -203,6 +205,7 @@ mod test { "loadw", // Then load the value at 3 "push 1000", // Push the addr 1000, for the lvalue "storew", // Store whatever's at 3 to 1000 + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) @@ -219,6 +222,7 @@ mod test { "add", // Add 3 to that address "peekr", // Store it in the first var "storew", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) diff --git a/forge_core/src/compiler/ast_nodes/function.rs b/forge_core/src/compiler/ast_nodes/function.rs index ea31abd..a0e7a0c 100644 --- a/forge_core/src/compiler/ast_nodes/function.rs +++ b/forge_core/src/compiler/ast_nodes/function.rs @@ -1,4 +1,4 @@ -use crate::ast::{Function, Location}; +use crate::ast::{Function, Location, Return}; use crate::compiler::compilable::Compilable; use crate::compiler::compiled_fn::CompiledFn; use crate::compiler::CompileError; @@ -26,6 +26,10 @@ impl Compilable for Function { // Compile the body, storing all of it in the CompiledFn we just created / added self.body.process(state, Some(&mut sig), loc)?; + // This fn probably has a return statement... but it's not required. As a final catch just + // in case we fall through to this point, we'll generate a void return and compile it: + Return(None).process(state, Some(&mut sig), loc)?; + // This can't fail because if it were a dupe name, adding the global would have failed state.functions.insert(self.name.clone(), sig); @@ -58,6 +62,7 @@ mod test { "peekr", // Calculate the lvalue "add 3", // "b" arg is frame + 3 "storew", // Finally store + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ) diff --git a/forge_core/src/compiler/ast_nodes/repeat_loop.rs b/forge_core/src/compiler/ast_nodes/repeat_loop.rs index c53c50a..8e3ab70 100644 --- a/forge_core/src/compiler/ast_nodes/repeat_loop.rs +++ b/forge_core/src/compiler/ast_nodes/repeat_loop.rs @@ -132,6 +132,7 @@ mod test { "popr", // toss old frame ptr "pop", "ret", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); @@ -173,6 +174,7 @@ mod test { "popr", // Toss old frame ptr "pop", "ret", + "popr", "pop", "ret 0" // Implicit void return ].join("\n") ); } diff --git a/forge_core/src/compiler/ast_nodes/return.rs b/forge_core/src/compiler/ast_nodes/return.rs index 46919ae..38eaa48 100644 --- a/forge_core/src/compiler/ast_nodes/return.rs +++ b/forge_core/src/compiler/ast_nodes/return.rs @@ -52,6 +52,7 @@ mod test { "popr", // Toss frame ptr "pop", "ret", // Return that + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); @@ -74,6 +75,7 @@ mod test { "pop", "ret 0", // Default return value, for an expr-less return "#end", + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); diff --git a/forge_core/src/compiler/ast_nodes/var_decl.rs b/forge_core/src/compiler/ast_nodes/var_decl.rs index c121280..6820a38 100644 --- a/forge_core/src/compiler/ast_nodes/var_decl.rs +++ b/forge_core/src/compiler/ast_nodes/var_decl.rs @@ -48,6 +48,7 @@ mod test { "mul", // b * 2 evaluated "peekr", // Loading "a" as an lvalue "storew", // doing the assignment + "popr", "pop", "ret 0" // Implicit void return ] .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 57459d5..6941bd1 100644 --- a/forge_core/src/compiler/ast_nodes/while_loop.rs +++ b/forge_core/src/compiler/ast_nodes/while_loop.rs @@ -58,7 +58,8 @@ mod test { "peekr", "add 3", "storew", // c = c + 1 - "#end" // End the loop body + "#end", // End the loop body + "popr", "pop", "ret 0" // Implicit void return ] .join("\n") ); diff --git a/forge_core/src/compiler/mod.rs b/forge_core/src/compiler/mod.rs index 7aeb7ea..54e1af4 100644 --- a/forge_core/src/compiler/mod.rs +++ b/forge_core/src/compiler/mod.rs @@ -79,6 +79,7 @@ mod test { "popr", "pop", "ret", + "popr", "pop", "ret 0", // Implicit void return "stack: .db 0", ].join("\n")); @@ -96,6 +97,38 @@ mod test { "popr", "pop", "ret", + "popr", "pop", "ret 0", // Implicit void return + "stack: .db 0", + ].join("\n")) + } + + #[test] + fn test_global_vars() { + + } + + #[test] + fn test_no_return() { + let asm = build_boot("fn foo() { } fn main() { foo(); }".into()).unwrap(); + assert_eq!(asm.join("\n"), vec![ + ".org 0x400", + "push stack", + "call _forge_gensym_2", + "hlt", + "_forge_gensym_1:", // fn foo() + "pushr", // capture frame ptr + "popr", // drop it and ret + "pop", + "ret 0", + "_forge_gensym_2:", // fn main() + "pushr", // capture frame ptr + "peekr", // prep frame ptr to send to foo + "push _forge_gensym_1", // load foo + "call", // call it + "pop", // Throw away its return value + "popr", // drop frame ptr and ret + "pop", + "ret 0", "stack: .db 0", ].join("\n")) }