mirror of
https://github.com/imjasonh/nescript
synced 2026-07-18 06:36:57 +00:00
IR codegen: allocate storage for function-local variables
Function bodies can declare local variables with \`var NAME: u8 = …\`. Previously the lowering created a VarId for them but didn't track it on the \`IrFunction.locals\` list, so the IR codegen had no address to map it to and \`LoadVar\` / \`StoreVar\` silently did nothing. The generated function body read and wrote random temp slots. Fixes: - Lowering: replaced the per-function \`locals\` local with a long-lived \`current_locals\` field; \`lower_function\` resets it on entry and moves it into the \`IrFunction\` at exit. Each \`Statement::VarDecl\` inside a function body appends to \`current_locals\`. - IR codegen: iterate every function's \`locals\` list. Params 0..4 still map to \$04-\$07, and the remaining locals get addresses in main RAM starting at \$0300. Each function's locals are disjoint, so nested calls don't corrupt each other's state. - Integration test \`program_with_function_local_variables\` exercises nested calls with function-local state to guard against regression. https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
240da57b54
commit
34b312537d
3 changed files with 69 additions and 11 deletions
|
|
@ -24,6 +24,7 @@ struct LoweringContext {
|
|||
current_blocks: Vec<IrBasicBlock>,
|
||||
current_ops: Vec<IrOp>,
|
||||
current_label: String,
|
||||
current_locals: Vec<IrLocal>,
|
||||
// Loop context for break/continue
|
||||
loop_stack: Vec<LoopContext>,
|
||||
// State metadata captured from the AST
|
||||
|
|
@ -59,6 +60,7 @@ impl LoweringContext {
|
|||
current_blocks: Vec::new(),
|
||||
current_ops: Vec::new(),
|
||||
current_label: String::new(),
|
||||
current_locals: Vec::new(),
|
||||
loop_stack: Vec::new(),
|
||||
state_names: Vec::new(),
|
||||
start_state: String::new(),
|
||||
|
|
@ -208,12 +210,12 @@ impl LoweringContext {
|
|||
fn lower_function(&mut self, fun: &FunDecl) {
|
||||
self.next_temp = 0;
|
||||
self.current_blocks = Vec::new();
|
||||
let mut locals = Vec::new();
|
||||
self.current_locals = Vec::new();
|
||||
|
||||
// Register parameters as locals
|
||||
for param in &fun.params {
|
||||
let var_id = self.get_or_create_var(¶m.name);
|
||||
locals.push(IrLocal {
|
||||
self.current_locals.push(IrLocal {
|
||||
var_id,
|
||||
name: param.name.clone(),
|
||||
size: type_size(¶m.param_type),
|
||||
|
|
@ -237,7 +239,7 @@ impl LoweringContext {
|
|||
self.functions.push(IrFunction {
|
||||
name: fun.name.clone(),
|
||||
blocks: std::mem::take(&mut self.current_blocks),
|
||||
locals,
|
||||
locals: std::mem::take(&mut self.current_locals),
|
||||
param_count: fun.params.len(),
|
||||
has_return: fun.return_type.is_some(),
|
||||
source_span: fun.span,
|
||||
|
|
@ -307,8 +309,18 @@ impl LoweringContext {
|
|||
fn lower_statement(&mut self, stmt: &Statement) {
|
||||
match stmt {
|
||||
Statement::VarDecl(var) => {
|
||||
let var_id = self.get_or_create_var(&var.name);
|
||||
// Track every local declared inside the current
|
||||
// function so the IR codegen can allocate backing
|
||||
// storage (e.g. RAM) for it.
|
||||
if !self.current_locals.iter().any(|l| l.var_id == var_id) {
|
||||
self.current_locals.push(IrLocal {
|
||||
var_id,
|
||||
name: var.name.clone(),
|
||||
size: type_size(&var.var_type),
|
||||
});
|
||||
}
|
||||
if let Some(init) = &var.init {
|
||||
let var_id = self.get_or_create_var(&var.name);
|
||||
let val = self.lower_expr(init);
|
||||
self.emit(IrOp::StoreVar(var_id, val));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue