Skip to content

Commit

Permalink
Fix new size_of for IR. (FuelLabs#857)
Browse files Browse the repository at this point in the history
* Fix IRgen for `SizeOf`.

* Fix ASMgen for string literals from IR.

* Set `size_of` `Contract` to 0 and mark aggregate literals as explicitly unreachable.
  • Loading branch information
otrho authored Mar 8, 2022
1 parent 15f20d5 commit cdecc83
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 81 deletions.
184 changes: 106 additions & 78 deletions sway-core/src/asm_generation/from_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ struct AsmBuilder<'ir> {
stack_base_reg: Option<VirtualRegister>,

// The layouts of each aggregate; their whole size in bytes and field offsets in words.
aggregate_layouts: HashMap<Aggregate, (u64, Vec<FieldLayout>)>,
type_analyzer: TypeAnalyzer,

// IR context we're compiling.
context: &'ir Context,
Expand All @@ -181,11 +181,6 @@ struct AsmBuilder<'ir> {
bytecode: Vec<Op>,
}

struct FieldLayout {
offset_in_words: u64, // Use words because LW/SW do.
size_in_bytes: u64, // Use bytes because CFEI/MCP do.
}

// NOTE: For stack storage we need to be aware:
// - sizes are in bytes; CFEI reserves in bytes.
// - offsets are in 64-bit words; LW/SW reads/writes to word offsets. XXX Wrap in a WordOffset struct.
Expand All @@ -206,7 +201,7 @@ impl<'ir> AsmBuilder<'ir> {
reg_map: HashMap::new(),
ptr_map: HashMap::new(),
stack_base_reg: None,
aggregate_layouts: HashMap::new(),
type_analyzer: TypeAnalyzer::default(),
context,
bytecode: Vec::new(),
}
Expand Down Expand Up @@ -260,22 +255,27 @@ impl<'ir> AsmBuilder<'ir> {
self.ptr_map.insert(*ptr, Storage::Stack(stack_base));

// Reserve space by incrementing the base.
stack_base += size_bytes_in_words!(self.aggregate_size(&aggregate));
stack_base += size_bytes_in_words!(self
.type_analyzer
.aggregate_size(self.context, &aggregate));
}
Type::Struct(aggregate) => {
// Store this aggregate at the current stack base.
self.ptr_map.insert(*ptr, Storage::Stack(stack_base));

// Reserve space by incrementing the base.
stack_base += size_bytes_in_words!(self.aggregate_size(&aggregate));
stack_base += size_bytes_in_words!(self
.type_analyzer
.aggregate_size(self.context, &aggregate));
}
Type::Union(aggregate) => {
// Store this aggregate AND a 64bit tag at the current stack base.
self.ptr_map.insert(*ptr, Storage::Stack(stack_base));

// Reserve space by incrementing the base.
stack_base +=
size_bytes_in_words!(self.aggregate_max_field_size(&aggregate));
stack_base += size_bytes_in_words!(self
.type_analyzer
.aggregate_max_field_size(self.context, &aggregate));
}
Type::ContractCaller(_) => {
self.ptr_map.insert(*ptr, Storage::Stack(stack_base));
Expand Down Expand Up @@ -635,7 +635,9 @@ impl<'ir> AsmBuilder<'ir> {
// See compile_bounds_assertion() in expression/array.rs (or look in Git history).

let instr_reg = self.reg_seqr.next();
let elem_size = self.ir_type_size_in_bytes(&ty.get_elem_type(self.context).unwrap());
let elem_size = self
.type_analyzer
.ir_type_size_in_bytes(self.context, &ty.get_elem_type(self.context).unwrap());
if elem_size <= 8 {
self.bytecode.push(Op {
opcode: Either::Left(VirtualOp::MULI(
Expand Down Expand Up @@ -714,7 +716,9 @@ impl<'ir> AsmBuilder<'ir> {
) {
// Base register should pointer to some stack allocated memory.
let base_reg = self.value_to_register(aggregate);
let (extract_offset, value_size) = self.aggregate_idcs_to_field_layout(ty, indices);
let (extract_offset, value_size) =
self.type_analyzer
.aggregate_idcs_to_field_layout(self.context, ty, indices);

let instr_reg = self.reg_seqr.next();
if value_size <= 8 {
Expand Down Expand Up @@ -857,7 +861,9 @@ impl<'ir> AsmBuilder<'ir> {
// Index value is the array element index, not byte nor word offset.
let index_reg = self.value_to_register(index_val);

let elem_size = self.ir_type_size_in_bytes(&ty.get_elem_type(self.context).unwrap());
let elem_size = self
.type_analyzer
.ir_type_size_in_bytes(self.context, &ty.get_elem_type(self.context).unwrap());
if elem_size <= 8 {
self.bytecode.push(Op {
opcode: Either::Left(VirtualOp::MULI(
Expand Down Expand Up @@ -944,7 +950,9 @@ impl<'ir> AsmBuilder<'ir> {
let base_reg = self.value_to_register(aggregate);

let insert_reg = self.value_to_register(value);
let (insert_offs, value_size) = self.aggregate_idcs_to_field_layout(ty, indices);
let (insert_offs, value_size) =
self.type_analyzer
.aggregate_idcs_to_field_layout(self.context, ty, indices);

let indices_str = indices
.iter()
Expand Down Expand Up @@ -1041,8 +1049,9 @@ impl<'ir> AsmBuilder<'ir> {
return ptr.map(|_| ());
}
let ptr = ptr.value.unwrap();
let load_size_in_words =
size_bytes_in_words!(self.ir_type_size_in_bytes(ptr.get_type(self.context)));
let load_size_in_words = size_bytes_in_words!(self
.type_analyzer
.ir_type_size_in_bytes(self.context, ptr.get_type(self.context)));
let instr_reg = self.reg_seqr.next();
match self.ptr_map.get(&ptr) {
None => unimplemented!("BUG? Uninitialised pointer."),
Expand Down Expand Up @@ -1154,7 +1163,9 @@ impl<'ir> AsmBuilder<'ir> {
});
} else {
let ret_reg = self.value_to_register(ret_val);
let size_in_bytes = self.ir_type_size_in_bytes(ret_type);
let size_in_bytes = self
.type_analyzer
.ir_type_size_in_bytes(self.context, ret_type);

if size_in_bytes <= 8 {
self.bytecode.push(Op {
Expand Down Expand Up @@ -1211,9 +1222,9 @@ impl<'ir> AsmBuilder<'ir> {
}
Storage::Stack(word_offs) => {
let word_offs = *word_offs;
let store_size_in_words = size_bytes_in_words!(
self.ir_type_size_in_bytes(ptr.get_type(self.context))
);
let store_size_in_words = size_bytes_in_words!(self
.type_analyzer
.ir_type_size_in_bytes(self.context, ptr.get_type(self.context)));
match store_size_in_words {
// We can have empty sized types which we can ignore.
0 => (),
Expand Down Expand Up @@ -1526,7 +1537,9 @@ impl<'ir> AsmBuilder<'ir> {

fn constant_size_in_bytes(&mut self, constant: &Constant) -> u64 {
match &constant.value {
ConstantValue::Undef => self.ir_type_size_in_bytes(&constant.ty),
ConstantValue::Undef => self
.type_analyzer
.ir_type_size_in_bytes(self.context, &constant.ty),
ConstantValue::Unit => 8,
ConstantValue::Bool(_) => 8,
ConstantValue::Uint(_) => 8,
Expand Down Expand Up @@ -1556,7 +1569,9 @@ impl<'ir> AsmBuilder<'ir> {
ConstantValue::Undef => {
// We don't need to actually create an initialiser, but we do need to return the
// field size in words.
size_bytes_in_words!(self.ir_type_size_in_bytes(&constant.ty))
size_bytes_in_words!(self
.type_analyzer
.ir_type_size_in_bytes(self.context, &constant.ty))
}
ConstantValue::Unit
| ConstantValue::Bool(_)
Expand Down Expand Up @@ -1687,16 +1702,72 @@ impl<'ir> AsmBuilder<'ir> {
}
}
}
}

fn ir_constant_to_ast_literal(constant: &Constant) -> Literal {
match &constant.value {
ConstantValue::Undef => unreachable!("Cannot convert 'undef' to a literal."),
ConstantValue::Unit => Literal::U64(0), // No unit.
ConstantValue::Bool(b) => Literal::Boolean(*b),
ConstantValue::Uint(n) => Literal::U64(*n),
ConstantValue::B256(bs) => Literal::B256(*bs),
ConstantValue::String(str) => Literal::String(crate::span::Span {
span: pest::Span::new(std::sync::Arc::from(str.as_str()), 0, str.len()).unwrap(),
path: None,
}),
ConstantValue::Array(_) | ConstantValue::Struct(_) => {
unreachable!("Cannot convert aggregates to a literal.")
}
}
}

// -------------------------------------------------------------------------------------------------

#[derive(Default)]
pub struct TypeAnalyzer {
// The layouts of each aggregate; their whole size in bytes and field offsets in words.
aggregate_layouts: HashMap<Aggregate, (u64, Vec<FieldLayout>)>,
}

pub struct FieldLayout {
pub offset_in_words: u64, // Use words because LW/SW do.
pub size_in_bytes: u64, // Use bytes because CFEI/MCP do.
}

impl TypeAnalyzer {
pub fn ir_type_size_in_bytes(&mut self, context: &Context, ty: &Type) -> u64 {
match ty {
Type::Unit | Type::Bool | Type::Uint(_) => 8,
Type::B256 => 32,
Type::String(n) => *n,
Type::Array(aggregate) | Type::Struct(aggregate) => {
self.analyze_aggregate(context, aggregate);
self.aggregate_size(context, aggregate)
}
Type::Union(aggregate) => {
self.analyze_aggregate(context, aggregate);
self.aggregate_max_field_size(context, aggregate)
}
Type::ContractCaller(_) => {
// We only store the address.
32
}
Type::Contract => {
// A Contract is a pseudo-type of no size.
0
}
}
}

// Aggregate size in bytes.
fn aggregate_size(&mut self, aggregate: &Aggregate) -> u64 {
self.analyze_aggregate(aggregate);
pub fn aggregate_size(&mut self, context: &Context, aggregate: &Aggregate) -> u64 {
self.analyze_aggregate(context, aggregate);
self.aggregate_layouts.get(aggregate).unwrap().0
}

// Size of largest aggregate field in bytes.
fn aggregate_max_field_size(&mut self, aggregate: &Aggregate) -> u64 {
self.analyze_aggregate(aggregate);
pub fn aggregate_max_field_size(&mut self, context: &Context, aggregate: &Aggregate) -> u64 {
self.analyze_aggregate(context, aggregate);
self.aggregate_layouts
.get(aggregate)
.unwrap()
Expand All @@ -1708,19 +1779,20 @@ impl<'ir> AsmBuilder<'ir> {
}

// Aggregate (nested) field offset in words and size in bytes.
fn aggregate_idcs_to_field_layout(
pub fn aggregate_idcs_to_field_layout(
&mut self,
context: &Context,
aggregate: &Aggregate,
idcs: &[u64],
) -> (u64, u64) {
self.analyze_aggregate(aggregate);
self.analyze_aggregate(context, aggregate);

idcs.iter()
.fold(
((0, 0), Type::Struct(*aggregate)),
|((offs, _), ty), idx| match ty {
Type::Struct(aggregate) => {
let agg_content = &self.context.aggregates[aggregate.0];
let agg_content = &context.aggregates[aggregate.0];
let field_type = agg_content.field_types()[*idx as usize];

let field_layout =
Expand All @@ -1740,18 +1812,18 @@ impl<'ir> AsmBuilder<'ir> {
.0
}

fn analyze_aggregate(&mut self, aggregate: &Aggregate) {
pub fn analyze_aggregate(&mut self, context: &Context, aggregate: &Aggregate) {
if self.aggregate_layouts.contains_key(aggregate) {
return;
}

match &self.context.aggregates[aggregate.0] {
match &context.aggregates[aggregate.0] {
AggregateContent::FieldTypes(field_types) => {
let (total_in_words, offsets) =
field_types
.iter()
.fold((0, Vec::new()), |(cur_offset, mut layouts), ty| {
let field_size_in_bytes = self.ir_type_size_in_bytes(ty);
let field_size_in_bytes = self.ir_type_size_in_bytes(context, ty);
layouts.push(FieldLayout {
offset_in_words: cur_offset,
size_in_bytes: field_size_in_bytes,
Expand All @@ -1767,56 +1839,12 @@ impl<'ir> AsmBuilder<'ir> {
AggregateContent::ArrayType(el_type, count) => {
// Careful! We *could* wrap the aggregate in Type::Array and call
// ir_type_size_in_bytes() BUT we'd then enter a recursive loop.
let el_size = self.ir_type_size_in_bytes(el_type);
let el_size = self.ir_type_size_in_bytes(context, el_type);
self.aggregate_layouts
.insert(*aggregate, (count * el_size, Vec::new()));
}
}
}

fn ir_type_size_in_bytes(&mut self, ty: &Type) -> u64 {
match ty {
Type::Unit | Type::Bool | Type::Uint(_) => 8,
Type::B256 => 32,
Type::String(n) => *n,
Type::Array(aggregate) | Type::Struct(aggregate) => {
self.analyze_aggregate(aggregate);
self.aggregate_size(aggregate)
}
Type::Union(aggregate) => {
self.analyze_aggregate(aggregate);
self.aggregate_max_field_size(aggregate)
}
Type::ContractCaller(_) => {
// We only store the address.
32
}
Type::Contract => {
unimplemented!("do contract/contract caller have/need a size?")
}
}
}
}

fn ir_constant_to_ast_literal(constant: &Constant) -> Literal {
match &constant.value {
ConstantValue::Undef => unreachable!("Cannot convert 'undef' to a literal."),
ConstantValue::Unit => Literal::U64(0), // No unit.
ConstantValue::Bool(b) => Literal::Boolean(*b),
ConstantValue::Uint(n) => Literal::U64(*n),
ConstantValue::B256(bs) => Literal::B256(*bs),
ConstantValue::String(_) => Literal::String(crate::span::Span {
span: pest::Span::new(
"STRINGS ARE UNIMPLEMENTED UNTIL WE REDO DATASECTION".into(),
0,
51,
)
.unwrap(),
path: None,
}),
ConstantValue::Array(_) => unimplemented!(),
ConstantValue::Struct(_) => unimplemented!(),
}
}

// -------------------------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit cdecc83

Please sign in to comment.