use crate::{
asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer},
asm_lang::{
ConstantRegister, Op, VirtualImmediate12, VirtualImmediate24, VirtualOp, VirtualRegister,
},
error::*,
semantic_analysis::ast_node::TypedStructExpressionField,
type_engine::{look_up_type_id, resolve_type, TypeId},
CompileResult, Ident,
};
#[derive(Debug)]
pub(crate) struct StructMemoryLayoutDescriptor {
fields: Vec<StructFieldMemoryLayoutDescriptor>,
}
#[derive(Debug)]
pub(crate) struct StructFieldMemoryLayoutDescriptor {
name_of_field: String,
size: u64,
}
impl StructMemoryLayoutDescriptor {
pub(crate) fn offset_to_field_name<'sc>(&self, name: &Ident<'sc>) -> CompileResult<'sc, u64> {
let field_ix = if let Some(ix) = self.fields.iter().position(
|StructFieldMemoryLayoutDescriptor { name_of_field, .. }| {
name_of_field.as_str() == name.primary_name
},
) {
ix
} else {
return err(vec![],
vec![
CompileError::Internal(
"Attempted to calculate struct memory offset on field that did not exist in struct.",
name.span.clone()
)
]);
};
ok(
self.fields
.iter()
.take(field_ix)
.fold(0, |acc, StructFieldMemoryLayoutDescriptor { size, .. }| {
acc + *size
}),
vec![],
vec![],
)
}
pub(crate) fn total_size(&self) -> u64 {
self.fields
.iter()
.map(|StructFieldMemoryLayoutDescriptor { size, .. }| size)
.sum()
}
}
#[test]
fn test_struct_memory_layout() {
use crate::span::Span;
let first_field_name = Ident {
span: Span {
span: pest::Span::new(" ", 0, 0).unwrap(),
path: None,
},
primary_name: "foo",
};
let second_field_name = Ident {
span: Span {
span: pest::Span::new(" ", 0, 0).unwrap(),
path: None,
},
primary_name: "bar",
};
let numbers = StructMemoryLayoutDescriptor {
fields: vec![
StructFieldMemoryLayoutDescriptor {
name_of_field: first_field_name.primary_name.to_string(),
size: 1,
},
StructFieldMemoryLayoutDescriptor {
name_of_field: second_field_name.primary_name.to_string(),
size: 1,
},
],
};
let mut warnings: Vec<CompileWarning> = Vec::new();
let mut errors: Vec<CompileError> = Vec::new();
assert_eq!(numbers.total_size(), 2u64);
assert_eq!(
numbers
.offset_to_field_name(&first_field_name)
.unwrap(&mut warnings, &mut errors),
0u64
);
assert_eq!(
numbers
.offset_to_field_name(&second_field_name)
.unwrap(&mut warnings, &mut errors),
1u64
);
}
pub(crate) fn get_struct_memory_layout<'sc>(
fields_with_names: &[(TypeId, &str)],
) -> CompileResult<'sc, StructMemoryLayoutDescriptor> {
let span = crate::Span {
span: pest::Span::new("TODO(static span): use Idents instead of Strings", 0, 0).unwrap(),
path: None,
};
let mut fields_with_sizes = vec![];
let warnings = vec![];
let mut errors = vec![];
for (field, name) in fields_with_names {
let ty = look_up_type_id(*field);
let stack_size = match ty.size_in_words(&span) {
Ok(o) => o,
Err(e) => {
errors.push(e);
return err(warnings, errors);
}
};
fields_with_sizes.push(StructFieldMemoryLayoutDescriptor {
name_of_field: name.to_string(),
size: stack_size,
});
}
ok(
StructMemoryLayoutDescriptor {
fields: fields_with_sizes,
},
warnings,
errors,
)
}
pub(crate) fn convert_struct_expression_to_asm<'sc>(
struct_name: &Ident<'sc>,
fields: &[TypedStructExpressionField<'sc>],
struct_beginning_pointer: &VirtualRegister,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let mut warnings = vec![];
let mut errors = vec![];
let mut asm_buf = vec![];
let fields_for_layout = fields
.iter()
.map(|TypedStructExpressionField { name, value }| (value.return_type, name.primary_name))
.collect::<Vec<_>>();
let descriptor = check!(
get_struct_memory_layout(&fields_for_layout[..]),
return err(warnings, errors),
warnings,
errors
);
let total_size = descriptor.total_size();
asm_buf.push(Op::new_comment(format!(
"{} struct initialization",
struct_name.primary_name
)));
if total_size == 0 {
asm_buf.push(Op::new_comment("fields have total size of zero."));
return ok(asm_buf, warnings, errors);
}
asm_buf.push(Op::unowned_register_move(
struct_beginning_pointer.clone(),
VirtualRegister::Constant(ConstantRegister::StackPointer),
));
let twelve_bits = super::compiler_constants::TWELVE_BITS;
let number_of_allocations_necessary = (total_size + (twelve_bits - 1)) / twelve_bits;
for allocation_index in 0..number_of_allocations_necessary {
let left_to_allocate = total_size - (allocation_index * twelve_bits);
let this_allocation = if left_to_allocate > twelve_bits {
twelve_bits
} else {
left_to_allocate
};
asm_buf.push(Op::unowned_stack_allocate_memory(
VirtualImmediate24::new_unchecked(
this_allocation * 8, "struct size was checked manually to be within 12 bits",
),
));
}
let mut offset = 0;
for TypedStructExpressionField { name, value } in fields {
let return_register = register_sequencer.next();
let value_stack_size: u64 = match resolve_type(value.return_type, &name.span) {
Ok(o) => match o.size_in_words(&name.span) {
Ok(o) => o,
Err(e) => {
errors.push(e);
return err(warnings, errors);
}
},
Err(e) => {
errors.push(e.into());
return err(warnings, errors);
}
};
let mut field_instantiation = check!(
convert_expression_to_asm(value, namespace, &return_register, register_sequencer),
vec![],
warnings,
errors
);
asm_buf.append(&mut field_instantiation);
if value_stack_size > 1 {
let address_to_write_to = register_sequencer.next();
asm_buf.push(Op {
opcode: either::Either::Left(VirtualOp::ADDI(
address_to_write_to.clone(),
struct_beginning_pointer.clone(),
VirtualImmediate12::new_unchecked(offset * 8, "struct size is too large"),
)),
owning_span: Some(value.span.clone()),
comment: format!(
"prep struct field reg (size {} for field {})",
value_stack_size, name.primary_name
),
});
asm_buf.push(Op {
opcode: either::Either::Left(VirtualOp::MCPI(
address_to_write_to,
return_register,
VirtualImmediate12::new_unchecked(
value_stack_size * 8,
"struct cannot be this big",
),
)),
owning_span: Some(value.span.clone()),
comment: format!(
"cp type size {} for field {}",
value_stack_size, name.primary_name
),
});
} else {
asm_buf.push(Op::write_register_to_memory(
struct_beginning_pointer.clone(),
return_register,
VirtualImmediate12::new_unchecked(offset, "the whole struct is less than 12 bits so every individual field should be as well."),
name.span.clone(),
));
}
offset += value_stack_size;
}
ok(asm_buf, warnings, errors)
}