0.5.0 -- tuples and more optimizations #12

Merged
dbidwell merged 34 commits from 43-tuple-return into master 2025-12-31 17:03:51 -07:00
2 changed files with 262 additions and 106 deletions
Showing only changes of commit e94fc0f5de - Show all commits

View File

@@ -170,10 +170,27 @@ mod test {
"# "#
); );
// Basic structure check - should have the function label and main assert_eq!(
assert!(compiled.contains("getPair:")); compiled,
assert!(compiled.contains("main:")); indoc! {
assert!(compiled.contains("jal getPair")); "
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(()) Ok(())
} }
@@ -190,10 +207,26 @@ mod test {
"# "#
); );
// Basic structure check assert_eq!(
assert!(compiled.contains("getPair:")); compiled,
assert!(compiled.contains("main:")); indoc! {
assert!(compiled.contains("jal getPair")); "
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(()) Ok(())
} }
@@ -210,10 +243,29 @@ mod test {
"# "#
); );
// Basic structure check assert_eq!(
assert!(compiled.contains("getTriple:")); compiled,
assert!(compiled.contains("main:")); indoc! {
assert!(compiled.contains("jal getTriple")); "
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(()) Ok(())
} }
@@ -232,10 +284,33 @@ mod test {
"# "#
); );
// Basic structure check assert_eq!(
assert!(compiled.contains("getPair:")); compiled,
assert!(compiled.contains("main:")); indoc! {
assert!(compiled.contains("jal getPair")); "
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(()) Ok(())
} }

View File

@@ -1099,17 +1099,16 @@ impl<'a> Compiler<'a> {
match &value.node { match &value.node {
Expression::Invocation(invoke_expr) => { Expression::Invocation(invoke_expr) => {
// Execute the function call // 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)?; self.expression_function_invocation_with_invocation(invoke_expr, scope)?;
// For each variable in names, assign from the return registers/stack // First pass: allocate variables in order
// Start from r15-1 (r14) for the first element let mut var_locations = Vec::new();
let mut current_register = VariableScope::PERSIST_REGISTER_COUNT; // r14 = register 14 for name_spanned in names.iter() {
for (_index, name_spanned) in names.iter().enumerate() {
// Skip underscores // Skip underscores
if name_spanned.node.as_ref() == "_" { if name_spanned.node.as_ref() == "_" {
current_register = current_register.saturating_sub(1); var_locations.push(None);
continue; continue;
} }
@@ -1119,27 +1118,19 @@ impl<'a> Compiler<'a> {
LocationRequest::Persist, LocationRequest::Persist,
Some(name_spanned.span), Some(name_spanned.span),
)?; )?;
var_locations.push(Some(var_location));
}
// Move from return register to variable 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)?; let var_reg = self.resolve_register(&var_location)?;
// If we still have return registers available, use them // Pop from stack into the variable's register
if current_register >= 8 {
self.write_instruction( self.write_instruction(
Instruction::Move( Instruction::Pop(Operand::Register(var_reg)),
Operand::Register(var_reg), Some(names[idx].span),
Operand::Register(current_register),
),
Some(name_spanned.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 { match &value.node {
Expression::Invocation(invoke_expr) => { Expression::Invocation(invoke_expr) => {
// Execute the function call // 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)?; self.expression_function_invocation_with_invocation(invoke_expr, scope)?;
// For each variable in names, assign from the return registers // First pass: look up variable locations
let mut current_register = VariableScope::PERSIST_REGISTER_COUNT; // r14 = register 14 let mut var_locs = Vec::new();
for name_spanned in names.iter() {
for (_index, name_spanned) in names.iter().enumerate() {
// Skip underscores // Skip underscores
if name_spanned.node.as_ref() == "_" { if name_spanned.node.as_ref() == "_" {
current_register = current_register.saturating_sub(1); var_locs.push(None);
continue; continue;
} }
@@ -1285,80 +1276,66 @@ impl<'a> Compiler<'a> {
VariableLocation::Temporary(0) 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 {
self.write_instruction(
Instruction::Move(
Operand::Register(reg),
Operand::Register(current_register),
),
Some(name_spanned.span),
)?;
current_register -= 1;
} else {
return Err(Error::Unknown(
"Tuple unpacking with more than 7 elements not yet supported"
.into(),
Some(name_spanned.span),
));
} }
// 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::Pop(Operand::Register(*reg)),
Some(names[idx].span),
)?;
} }
VariableLocation::Stack(offset) => { VariableLocation::Stack(offset) => {
// Load from return register to temp, then store to stack // Pop into temp register, then write to variable stack
if current_register >= 8 {
self.write_instruction( self.write_instruction(
Instruction::Move( Instruction::Pop(Operand::Register(
Operand::Register(0), VariableScope::TEMP_STACK_REGISTER,
Operand::Register(current_register), )),
), Some(names[idx].span),
Some(name_spanned.span),
)?; )?;
current_register -= 1;
} else {
return Err(Error::Unknown(
"Tuple unpacking with more than 7 elements not yet supported"
.into(),
Some(name_spanned.span),
));
}
// Store r0 to stack // Write to variable stack location
self.write_instruction( self.write_instruction(
Instruction::Sub( Instruction::Sub(
Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::Register(0),
Operand::StackPointer, Operand::StackPointer,
Operand::Number(offset.into()), Operand::Number((*offset).into()),
), ),
Some(name_spanned.span), Some(names[idx].span),
)?; )?;
self.write_instruction( self.write_instruction(
Instruction::Put( Instruction::Put(
Operand::Device(Cow::from("db")), Operand::Device(Cow::from("db")),
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
Operand::Register(0), Operand::Register(0),
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
), ),
Some(name_spanned.span), Some(names[idx].span),
)?; )?;
} }
VariableLocation::Constant(_) => { VariableLocation::Constant(_) => {
return Err(Error::ConstAssignment( return Err(Error::ConstAssignment(
name_spanned.node.clone(), names[idx].node.clone(),
name_spanned.span, names[idx].span,
)); ));
} }
VariableLocation::Device(_) => { VariableLocation::Device(_) => {
return Err(Error::DeviceAssignment( return Err(Error::DeviceAssignment(
name_spanned.node.clone(), names[idx].node.clone(),
name_spanned.span, names[idx].span,
)); ));
} }
} }
} }
} }
}
Expression::Tuple(tuple_expr) => { Expression::Tuple(tuple_expr) => {
// Direct tuple literal: (value1, value2, ...) // Direct tuple literal: (value1, value2, ...)
let tuple_elements = &tuple_expr.node; let tuple_elements = &tuple_expr.node;
@@ -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( return Err(Error::Unknown(
format!("Unsupported `return` statement: {:?}", expr), format!("Unsupported `return` statement: {:?}", expr),