0.5.0 -- tuples and more optimizations #12
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user