@@ -20,7 +20,7 @@ std::string instructionsToString(const std::vector<Instruction>& instructions, b
2020 { Op::LOCALS, { " LOCALS" , { " ID" } } },
2121 { Op::LOCALL, { " LOCALL" , { " ID" } } },
2222 { Op::CALL, { " CALL" , { " NUM_ARGS" } } },
23- { Op::RET, { " RET" , { } } },
23+ { Op::RET, { " RET" , { " NUM_PARAMS " , " NUM_LOCALS " } } },
2424 { Op::PUSH, { " PUSH" , { " VALUE" } } },
2525 { Op::POP, { " POP" , {} } },
2626 { Op::ADD, { " ADD" , {} } },
@@ -108,47 +108,91 @@ ProgramState ByteCodeVM::run(size_t maxInstructions) {
108108 break ;
109109 }
110110 case Op::CALL: {
111- auto return_address = idx;
111+ word_t num_params = inst.arg1 ;
112+ word_t jmp_dest = stack.pop ();
112113
113- auto dest = stack.pop ();
114- idx = dest; // Jump to function address
115-
116- // Bring parameters to locals
117- std::vector<word_t > locals;
118- for (int i = 0 ; i < inst.arg1 ; ++i) {
119- locals.push_back (stack.pop ());
114+ // Save parameters temporarily (they're on stack in order: param0, param1, ...)
115+ std::vector<word_t > params;
116+ for (word_t i = 0 ; i < num_params; ++i) {
117+ params.push_back (stack.pop ());
120118 }
121119
122- callstack.push_back ({locals, return_address});
120+ stack.push (idx); // Push return address
121+ stack.push (function_stack_base); // Push prev function_stack_base
122+ function_stack_base = stack.size (); // Update function_stack_base to current top
123+
124+ // Push parameters in reverse order (so param0 is at function_stack_base+0)
125+ for (auto it = params.rbegin (); it != params.rend (); ++it) {
126+ stack.push (*it);
127+ }
128+
129+ idx = jmp_dest; // Jump to function
123130 break ;
124131 }
125132 case Op::RET: {
126- if (callstack.empty ()){
133+ word_t num_params = inst.arg1 ;
134+ word_t num_locals = inst.arg2 ;
135+
136+ // function_stack_base
137+ // |
138+ // Stack layout: [ret_addr][prev_function_stack_base]^[params...][locals...][return_value?]
139+
140+ word_t expected_stack_size = function_stack_base + num_params + num_locals;
141+ bool has_return_value = false ;
142+ if (stack.size () > expected_stack_size) {
143+ return_value = stack.pop ();
144+ has_return_value = true ;
145+ } else {
146+ return_value = 0 ;
147+ }
148+
149+ // Pop locals
150+ while (stack.size () > function_stack_base + num_params) {
151+ stack.pop ();
152+ }
153+
154+ // Pop parameters
155+ for (word_t i = 0 ; i < num_params; ++i) {
156+ stack.pop ();
157+ }
158+
159+ // Check if this is the main function return
160+ if (function_stack_base == 0 ) {
127161 return ProgramState::Finished;
128162 }
129- idx = callstack.back ().return_address ;
130- callstack.pop_back ();
163+
164+ function_stack_base = stack.pop (); // Restore function_stack_base
165+ idx = stack.pop (); // Jump back to return address
166+
167+ if (has_return_value) {
168+ stack.push (return_value);
169+ }
170+
131171 break ;
132172 }
133173 case Op::LOCALS: {
134- // LOCALS ID
135- auto & locals = callstack. back (). locals ;
174+ // LOCALS n: Store stack top into local variable/parameter n
175+ word_t value = stack. pop () ;
136176
137- auto idx = inst.arg1 + 1 ;
138- if (locals.size () <= idx) {
139- locals.resize (idx); // TODO: Expensive!
177+ word_t localIndex = function_stack_base + inst.arg1 ;
178+
179+ // Expand stack if necessary
180+ while (stack.size () <= localIndex) {
181+ stack.push (0 );
140182 }
141183
142- auto value = stack.pop ();
143- locals[inst.arg1 ] = value; // Store value in local variable
184+ stack.set (localIndex, value);
144185 break ;
145186 }
146187 case Op::LOCALL: {
147- // LOCALL ID
148- ASSURE (!callstack.empty (), " ByteCodeVM: Empty callstack" );
149- auto & locals = callstack.back ().locals ;
150- ASSURE (inst.arg1 < locals.size (), " ByteCodeVM: Local variable index out of bounds" );
151- stack.push (locals[inst.arg1 ]); // Push local variable onto stack
188+ // LOCALL n: Load local variable/parameter n onto stack
189+ word_t localIndex = function_stack_base + inst.arg1 ;
190+
191+ ASSURE (localIndex < stack.size (),
192+ " ByteCodeVM: Local variable index out of bounds" );
193+
194+ word_t value = stack.get (localIndex);
195+ stack.push (value);
152196 break ;
153197 }
154198 case Op::PRINTS: {
@@ -325,11 +369,12 @@ ProgramState ByteCodeVM::run(size_t maxInstructions) {
325369 return ProgramState::Paused;
326370}
327371
328- ByteCodeVM::ByteCodeVM (const Program& program) :
329- idx{0ull },
330- callstack{},
331- stack{},
332- program (program),
372+ ByteCodeVM::ByteCodeVM (const Program& program) :
373+ idx{0ull },
374+ function_stack_base{0ull },
375+ return_value{0ull },
376+ stack{},
377+ program (program),
333378 debug{true },
334379 ffiFunctions{},
335380 ffiArgs{} {}
0 commit comments