Functions returning tuples somewhat working, but they clobber the popped ra

This commit is contained in:
2025-12-29 23:55:00 -07:00
parent b51800eb77
commit e94fc0f5de
2 changed files with 262 additions and 106 deletions

View File

@@ -170,10 +170,27 @@ mod test {
"#
);
// Basic structure check - should have the function label and main
assert!(compiled.contains("getPair:"));
assert!(compiled.contains("main:"));
assert!(compiled.contains("jal getPair"));
assert_eq!(
compiled,
indoc! {
"
j main
getPair:
push ra
push 10
push 20
move r15 1
j __internal_L1
__internal_L1:
pop ra
j ra
main:
jal getPair
pop r9
pop r8
"
}
);
Ok(())
}
@@ -190,10 +207,26 @@ mod test {
"#
);
// Basic structure check
assert!(compiled.contains("getPair:"));
assert!(compiled.contains("main:"));
assert!(compiled.contains("jal getPair"));
assert_eq!(
compiled,
indoc! {
"
j main
getPair:
push ra
push 5
push 15
move r15 1
j __internal_L1
__internal_L1:
pop ra
j ra
main:
jal getPair
pop r8
"
}
);
Ok(())
}
@@ -210,10 +243,29 @@ mod test {
"#
);
// Basic structure check
assert!(compiled.contains("getTriple:"));
assert!(compiled.contains("main:"));
assert!(compiled.contains("jal getTriple"));
assert_eq!(
compiled,
indoc! {
"
j main
getTriple:
push ra
push 1
push 2
push 3
move r15 1
j __internal_L1
__internal_L1:
pop ra
j ra
main:
jal getTriple
pop r10
pop r9
pop r8
"
}
);
Ok(())
}
@@ -232,10 +284,33 @@ mod test {
"#
);
// Basic structure check
assert!(compiled.contains("getPair:"));
assert!(compiled.contains("main:"));
assert!(compiled.contains("jal getPair"));
assert_eq!(
compiled,
indoc! {
"
j main
getPair:
push ra
push 42
push 84
move r15 1
j __internal_L1
__internal_L1:
pop ra
j ra
main:
move r8 0
move r9 0
push r8
push r9
jal getPair
pop r9
pop r8
pop r9
pop r8
"
}
);
Ok(())
}

View File

@@ -1099,17 +1099,16 @@ impl<'a> Compiler<'a> {
match &value.node {
Expression::Invocation(invoke_expr) => {
// Execute the function call
// Tuple result: r15 points to tuple on stack, individual elements in r14, r13, etc.
// Tuple values are on the stack, sp points after the last pushed value
// Pop them in reverse order (from end to beginning)
self.expression_function_invocation_with_invocation(invoke_expr, scope)?;
// For each variable in names, assign from the return registers/stack
// Start from r15-1 (r14) for the first element
let mut current_register = VariableScope::PERSIST_REGISTER_COUNT; // r14 = register 14
for (_index, name_spanned) in names.iter().enumerate() {
// First pass: allocate variables in order
let mut var_locations = Vec::new();
for name_spanned in names.iter() {
// Skip underscores
if name_spanned.node.as_ref() == "_" {
current_register = current_register.saturating_sub(1);
var_locations.push(None);
continue;
}
@@ -1119,27 +1118,19 @@ impl<'a> Compiler<'a> {
LocationRequest::Persist,
Some(name_spanned.span),
)?;
var_locations.push(Some(var_location));
}
// Move from return register to variable location
let var_reg = self.resolve_register(&var_location)?;
// Second pass: pop in reverse order and assign to locations
for (idx, var_loc_opt) in var_locations.iter().enumerate().rev() {
if let Some(var_location) = var_loc_opt {
let var_reg = self.resolve_register(&var_location)?;
// If we still have return registers available, use them
if current_register >= 8 {
// Pop from stack into the variable's register
self.write_instruction(
Instruction::Move(
Operand::Register(var_reg),
Operand::Register(current_register),
),
Some(name_spanned.span),
Instruction::Pop(Operand::Register(var_reg)),
Some(names[idx].span),
)?;
current_register -= 1;
} else {
// If we run out of registers, we'd need to load from stack
// For now, this is a limitation
return Err(Error::Unknown(
"Tuple unpacking with more than 7 elements not yet supported".into(),
Some(name_spanned.span),
));
}
}
}
@@ -1260,16 +1251,16 @@ impl<'a> Compiler<'a> {
match &value.node {
Expression::Invocation(invoke_expr) => {
// Execute the function call
// Tuple result: r15 points to tuple on stack, individual elements in r14, r13, etc.
// Tuple values are on the stack, sp points after the last pushed value
// Pop them in reverse order (from end to beginning)
self.expression_function_invocation_with_invocation(invoke_expr, scope)?;
// For each variable in names, assign from the return registers
let mut current_register = VariableScope::PERSIST_REGISTER_COUNT; // r14 = register 14
for (_index, name_spanned) in names.iter().enumerate() {
// First pass: look up variable locations
let mut var_locs = Vec::new();
for name_spanned in names.iter() {
// Skip underscores
if name_spanned.node.as_ref() == "_" {
current_register = current_register.saturating_sub(1);
var_locs.push(None);
continue;
}
@@ -1285,76 +1276,62 @@ impl<'a> Compiler<'a> {
VariableLocation::Temporary(0)
}
};
var_locs.push(Some(var_location));
}
// Assign from return register to variable
match var_location {
VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => {
if current_register >= 8 {
// Second pass: pop in reverse order and assign
for (idx, var_loc_opt) in var_locs.iter().enumerate().rev() {
if let Some(var_location) = var_loc_opt {
// Pop from stack and assign to variable
match var_location {
VariableLocation::Temporary(reg)
| VariableLocation::Persistant(reg) => {
// Pop directly into the variable's register
self.write_instruction(
Instruction::Move(
Operand::Register(reg),
Operand::Register(current_register),
),
Some(name_spanned.span),
Instruction::Pop(Operand::Register(*reg)),
Some(names[idx].span),
)?;
current_register -= 1;
} else {
return Err(Error::Unknown(
"Tuple unpacking with more than 7 elements not yet supported"
.into(),
Some(name_spanned.span),
));
}
}
VariableLocation::Stack(offset) => {
// Load from return register to temp, then store to stack
if current_register >= 8 {
VariableLocation::Stack(offset) => {
// Pop into temp register, then write to variable stack
self.write_instruction(
Instruction::Move(
Instruction::Pop(Operand::Register(
VariableScope::TEMP_STACK_REGISTER,
)),
Some(names[idx].span),
)?;
// Write to variable stack location
self.write_instruction(
Instruction::Sub(
Operand::Register(0),
Operand::Register(current_register),
Operand::StackPointer,
Operand::Number((*offset).into()),
),
Some(name_spanned.span),
Some(names[idx].span),
)?;
current_register -= 1;
} else {
return Err(Error::Unknown(
"Tuple unpacking with more than 7 elements not yet supported"
.into(),
Some(name_spanned.span),
self.write_instruction(
Instruction::Put(
Operand::Device(Cow::from("db")),
Operand::Register(0),
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
),
Some(names[idx].span),
)?;
}
VariableLocation::Constant(_) => {
return Err(Error::ConstAssignment(
names[idx].node.clone(),
names[idx].span,
));
}
VariableLocation::Device(_) => {
return Err(Error::DeviceAssignment(
names[idx].node.clone(),
names[idx].span,
));
}
// Store r0 to stack
self.write_instruction(
Instruction::Sub(
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
Operand::StackPointer,
Operand::Number(offset.into()),
),
Some(name_spanned.span),
)?;
self.write_instruction(
Instruction::Put(
Operand::Device(Cow::from("db")),
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
Operand::Register(0),
),
Some(name_spanned.span),
)?;
}
VariableLocation::Constant(_) => {
return Err(Error::ConstAssignment(
name_spanned.node.clone(),
name_spanned.span,
));
}
VariableLocation::Device(_) => {
return Err(Error::DeviceAssignment(
name_spanned.node.clone(),
name_spanned.span,
));
}
}
}
@@ -2563,6 +2540,110 @@ impl<'a> Compiler<'a> {
}
}
}
Expression::Tuple(tuple_expr) => {
let span = expr.span;
let tuple_elements = &tuple_expr.node;
// Record the stack offset where the tuple will start
let tuple_start_offset = scope.stack_offset();
// Allocate space on the stack for each tuple element
for element in tuple_elements.iter() {
match &element.node {
Expression::Literal(lit) => {
let value_operand = extract_literal(lit.node.clone(), false)?;
self.write_instruction(
Instruction::Push(value_operand),
Some(span),
)?;
}
Expression::Variable(var) => {
let var_loc = match scope.get_location_of(&var.node, Some(var.span))
{
Ok(l) => l,
Err(_) => {
self.errors.push(Error::UnknownIdentifier(
var.node.clone(),
var.span,
));
VariableLocation::Temporary(0)
}
};
match &var_loc {
VariableLocation::Temporary(reg)
| VariableLocation::Persistant(reg) => {
self.write_instruction(
Instruction::Push(Operand::Register(*reg)),
Some(span),
)?;
}
VariableLocation::Constant(lit) => {
let value_operand = extract_literal(lit.clone(), false)?;
self.write_instruction(
Instruction::Push(value_operand),
Some(span),
)?;
}
VariableLocation::Stack(offset) => {
self.write_instruction(
Instruction::Sub(
Operand::Register(
VariableScope::TEMP_STACK_REGISTER,
),
Operand::StackPointer,
Operand::Number((*offset).into()),
),
Some(span),
)?;
self.write_instruction(
Instruction::Get(
Operand::Register(
VariableScope::TEMP_STACK_REGISTER,
),
Operand::Device(Cow::from("db")),
Operand::Register(
VariableScope::TEMP_STACK_REGISTER,
),
),
Some(span),
)?;
self.write_instruction(
Instruction::Push(Operand::Register(
VariableScope::TEMP_STACK_REGISTER,
)),
Some(span),
)?;
}
VariableLocation::Device(_) => {
return Err(Error::Unknown(
"You can not return a device from a function.".into(),
Some(var.span),
));
}
}
}
_ => {
// For complex expressions, just push 0 for now
self.write_instruction(
Instruction::Push(Operand::Number(
Number::Integer(0, Unit::None).into(),
)),
Some(span),
)?;
}
}
}
// Store the pointer to the tuple (stack offset) in r15
self.write_instruction(
Instruction::Move(
Operand::Register(VariableScope::RETURN_REGISTER),
Operand::Number(tuple_start_offset.into()),
),
Some(span),
)?;
}
_ => {
return Err(Error::Unknown(
format!("Unsupported `return` statement: {:?}", expr),