Skip to content

Commit

Permalink
allow indirect configurables for dynamic types such as vec, slices et…
Browse files Browse the repository at this point in the history
…c...
  • Loading branch information
xunilrj committed Dec 1, 2024
1 parent 64d1c6e commit 869de4b
Show file tree
Hide file tree
Showing 17 changed files with 373 additions and 116 deletions.
2 changes: 2 additions & 0 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2036,6 +2036,8 @@ fn report_assembly_information(
sway_core::asm_generation::Datum::Collection(items) => {
items.iter().map(calculate_entry_size).sum()
}

sway_core::asm_generation::Datum::OffsetOf(_) => std::mem::size_of::<u64>() as u64,
}
}

Expand Down
14 changes: 10 additions & 4 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,11 +305,12 @@ fn to_bytecode_mut(
println!(".data_section:");

let offset = bytecode.len();
let ds_start = offset as u64;

fn print_entry(indentation: usize, offset: usize, pair: &Entry) {
fn print_entry(ds: &DataSection, indentation: usize, ds_start: u64, offset: usize, entry: &Entry) {
print!("{}{:#010x} ", " ".repeat(indentation), offset);

match &pair.value {
match &entry.value {
Datum::Byte(w) => println!(".byte i{w}, as hex {w:02X}"),
Datum::Word(w) => {
println!(".word i{w}, as hex be bytes ({:02X?})", w.to_be_bytes())
Expand Down Expand Up @@ -347,15 +348,20 @@ fn to_bytecode_mut(
Datum::Collection(els) => {
println!(".collection");
for e in els {
print_entry(indentation + 1, offset, e);
print_entry(ds, indentation + 1, ds_start, offset, e);
}
}
Datum::OffsetOf(_) => {
let offset_as_bytes = entry.to_bytes(ds);
let offset_as_u64 = u64::from_be_bytes(offset_as_bytes.as_slice().try_into().unwrap());
println!(".offset of 0x{:08x}, as word {}, as hex be bytes ({:02X?})", ds_start + offset_as_u64, offset_as_u64, offset_as_bytes);
}
};
}

for (i, entry) in data_section.iter_all_entries().enumerate() {
let entry_offset = data_section.absolute_idx_to_offset(i);
print_entry(indentation, offset + entry_offset, &entry);
print_entry(&data_section, indentation, ds_start, offset + entry_offset, &entry);
}

println!(";; --- END OF TARGET BYTECODE ---\n");
Expand Down
132 changes: 102 additions & 30 deletions sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ use std::{fmt, iter::repeat};
pub enum EntryName {
NonConfigurable,
Configurable(String),
Dynamic(String)
}

impl fmt::Display for EntryName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EntryName::NonConfigurable => write!(f, "NonConfigurable"),
EntryName::Configurable(name) => write!(f, "<Configurable, {}>", name),
EntryName::Configurable(name) => write!(f, "Configurable_{}", name),
EntryName::Dynamic(name) => write!(f, "Dynamic_{}", name),

}
}
}
Expand All @@ -25,6 +28,7 @@ pub struct Entry {
pub value: Datum,
pub padding: Padding,
pub name: EntryName,
pub word_aligned: bool,
}

#[derive(Clone, Debug, serde::Serialize)]
Expand All @@ -34,6 +38,8 @@ pub enum Datum {
ByteArray(Vec<u8>),
Slice(Vec<u8>),
Collection(Vec<Entry>),
/// Offset from the start of the Data Section
OffsetOf(DataId),
}

impl Entry {
Expand All @@ -42,6 +48,7 @@ impl Entry {
value: Datum::Byte(value),
padding: padding.unwrap_or(Padding::default_for_u8(value)),
name,
word_aligned: true
}
}

Expand All @@ -50,6 +57,7 @@ impl Entry {
value: Datum::Word(value),
padding: padding.unwrap_or(Padding::default_for_u64(value)),
name,
word_aligned: true
}
}

Expand All @@ -62,6 +70,7 @@ impl Entry {
padding: padding.unwrap_or(Padding::default_for_byte_array(&bytes)),
value: Datum::ByteArray(bytes),
name,
word_aligned: true,
}
}

Expand All @@ -70,6 +79,7 @@ impl Entry {
padding: padding.unwrap_or(Padding::default_for_byte_array(&bytes)),
value: Datum::Slice(bytes),
name,
word_aligned: true,
}
}

Expand All @@ -84,6 +94,16 @@ impl Entry {
)),
value: Datum::Collection(elements),
name,
word_aligned: true,
}
}

pub(crate) fn new_offset_of(id: DataId, name: EntryName, padding: Option<Padding>) -> Entry {
Entry {
padding: padding.unwrap_or(Padding::default_for_u64(0)),
value: Datum::OffsetOf(id),
name,
word_aligned: true,
}
}

Expand Down Expand Up @@ -158,20 +178,30 @@ impl Entry {
}
}

fn to_bytes_len(&self, ds: &DataSection) -> usize {
let bytes_len = match &self.value {
Datum::Byte(_) => 1,
Datum::Word(_) | Datum::OffsetOf(_) => 8,
Datum::ByteArray(bytes) | Datum::Slice(bytes) if bytes.len() % 8 == 0 => bytes.len(),
Datum::ByteArray(bytes) | Datum::Slice(bytes) => (bytes.len() + 7) & 0xfffffff8_usize,
Datum::Collection(items) => items.iter().map(|el| el.to_bytes_len(ds)).sum(),
};

let final_padding = self.padding.target_size().saturating_sub(bytes_len);
bytes_len + final_padding
}

/// Converts a literal to a big-endian representation. This is padded to words.
pub(crate) fn to_bytes(&self) -> Vec<u8> {
pub(crate) fn to_bytes(&self, ds: &DataSection) -> Vec<u8> {
// Get the big-endian byte representation of the basic value.
let bytes = match &self.value {
Datum::Byte(value) => vec![*value],
Datum::Word(value) => value.to_be_bytes().to_vec(),
Datum::ByteArray(bytes) | Datum::Slice(bytes) if bytes.len() % 8 == 0 => bytes.clone(),
Datum::ByteArray(bytes) | Datum::Slice(bytes) => bytes
.iter()
.chain([0; 8].iter())
.copied()
.take((bytes.len() + 7) & 0xfffffff8_usize)
.collect(),
Datum::Collection(items) => items.iter().flat_map(|el| el.to_bytes()).collect(),
Datum::ByteArray(bytes) | Datum::Slice(bytes) => bytes.clone(),
Datum::Collection(items) => items.iter().flat_map(|el| el.to_bytes(ds)).collect(),
Datum::OffsetOf(id) => {
ds.data_id_to_offset(id).to_be_bytes().to_vec()
}
};

let final_padding = self.padding.target_size().saturating_sub(bytes.len());
Expand Down Expand Up @@ -213,24 +243,26 @@ impl Entry {
}
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize)]
pub enum DataIdEntryKind {
NonConfigurable,
Configurable,
Dynamic,
}

impl fmt::Display for DataIdEntryKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataIdEntryKind::NonConfigurable => write!(f, "NonConfigurable"),
DataIdEntryKind::Configurable => write!(f, "Configurable"),
DataIdEntryKind::Dynamic => write!(f, "Dynamic"),
}
}
}

/// An address which refers to a value in the data section of the asm.
#[derive(Clone, Debug)]
pub(crate) struct DataId {
#[derive(Clone, Debug, serde::Serialize)]
pub struct DataId {
pub(crate) idx: u32,
pub(crate) kind: DataIdEntryKind,
}
Expand All @@ -246,20 +278,22 @@ impl fmt::Display for DataId {
pub struct DataSection {
pub non_configurables: Vec<Entry>,
pub configurables: Vec<Entry>,
pub dynamic: Vec<Entry>,
pub(crate) pointer_id: FxHashMap<u64, DataId>,
}

impl DataSection {
/// Get the number of entries
pub fn num_entries(&self) -> usize {
self.non_configurables.len() + self.configurables.len()
self.non_configurables.len() + self.configurables.len() + self.dynamic.len()
}

/// Iterate over all entries, non-configurables followed by configurables
pub fn iter_all_entries(&self) -> impl Iterator<Item = Entry> + '_ {
self.non_configurables
.iter()
.chain(self.configurables.iter())
.chain(self.dynamic.iter())
.cloned()
}

Expand All @@ -268,14 +302,16 @@ impl DataSection {
match id.kind {
DataIdEntryKind::NonConfigurable => id.idx as usize,
DataIdEntryKind::Configurable => id.idx as usize + self.non_configurables.len(),
DataIdEntryKind::Dynamic => id.idx as usize + self.non_configurables.len() + self.configurables.len(),
}
}

/// Get entry at id
fn get(&self, id: &DataId) -> Option<&Entry> {
pub fn get(&self, id: &DataId) -> Option<&Entry> {
match id.kind {
DataIdEntryKind::NonConfigurable => self.non_configurables.get(id.idx as usize),
DataIdEntryKind::Configurable => self.configurables.get(id.idx as usize),
DataIdEntryKind::Dynamic => self.dynamic.get(id.idx as usize),
}
}

Expand All @@ -290,20 +326,26 @@ impl DataSection {
/// in bytes.
pub(crate) fn absolute_idx_to_offset(&self, idx: usize) -> usize {
self.iter_all_entries().take(idx).fold(0, |offset, entry| {
//entries must be word aligned
size_bytes_round_up_to_word_alignment!(offset + entry.to_bytes().len())
let offset = if entry.word_aligned {
size_bytes_round_up_to_word_alignment!(offset)
} else {
offset
};

offset + entry.to_bytes_len(self)
})
}

pub(crate) fn serialize_to_bytes(&self) -> Vec<u8> {
// not the exact right capacity but serves as a lower bound
let mut buf = Vec::with_capacity(self.num_entries());
for entry in self.iter_all_entries() {
buf.append(&mut entry.to_bytes());
if entry.word_aligned {
let aligned_len = size_bytes_round_up_to_word_alignment!(buf.len());
buf.extend(vec![0u8; aligned_len - buf.len()]);
}

//entries must be word aligned
let aligned_len = size_bytes_round_up_to_word_alignment!(buf.len());
buf.extend(vec![0u8; aligned_len - buf.len()]);
buf.append(&mut entry.to_bytes(self));
}
buf
}
Expand Down Expand Up @@ -353,6 +395,7 @@ impl DataSection {
DataIdEntryKind::NonConfigurable,
),
EntryName::Configurable(_) => (&mut self.configurables, DataIdEntryKind::Configurable),
EntryName::Dynamic(_) => (&mut self.dynamic, DataIdEntryKind::Dynamic),
};
match value_pairs.iter().position(|entry| entry.equiv(&new_entry)) {
Some(num) => DataId {
Expand All @@ -375,6 +418,7 @@ impl DataSection {
let value_pairs = match data_id.kind {
DataIdEntryKind::NonConfigurable => &self.non_configurables,
DataIdEntryKind::Configurable => &self.configurables,
DataIdEntryKind::Dynamic => &self.dynamic,
};
value_pairs.get(data_id.idx as usize).and_then(|entry| {
if let Datum::Word(w) = entry.value {
Expand All @@ -388,7 +432,7 @@ impl DataSection {

impl fmt::Display for DataSection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn display_entry(datum: &Datum) -> String {
fn display_entry(s: &DataSection, datum: &Datum) -> String {
match datum {
Datum::Byte(w) => format!(".byte {w}"),
Datum::Word(w) => format!(".word {w}"),
Expand All @@ -397,23 +441,40 @@ impl fmt::Display for DataSection {
Datum::Collection(els) => format!(
".collection {{ {} }}",
els.iter()
.map(|el| display_entry(&el.value))
.map(|el| display_entry(s, &el.value))
.collect::<Vec<_>>()
.join(", ")
),
Datum::OffsetOf(id) => {
format!(".offset_of data_{}", s.get(id).unwrap().name)
}
}
}

use std::fmt::Write;
let mut data_buf = String::new();
for (ix, entry) in self.iter_all_entries().enumerate() {
writeln!(
data_buf,
"data_{}_{} {}",
entry.name,
ix,
display_entry(&entry.value)
)?;
match entry.name {
EntryName::NonConfigurable => {
writeln!(
data_buf,
"data_{}_{} {}",
entry.name,
ix,
display_entry(self, &entry.value)
)?;
},
EntryName::Configurable(_) | EntryName::Dynamic(_) => {
writeln!(
data_buf,
"data_{} ({}) {}",
entry.name,
ix - self.non_configurables.len(),
display_entry(self, &entry.value)
)?;
}
}

}

write!(f, ".data:\n{data_buf}")
Expand All @@ -433,3 +494,14 @@ fn display_bytes_for_data_section(bs: &Vec<u8>, prefix: &str) -> String {
}
format!("{prefix}[{}] {hex_str} {chr_str}", bs.len())
}

#[test]
fn ok_data_section_to_string() {
let mut ds = DataSection::default();

let vec_u8 = ds.insert_data_value(Entry::new_byte_array(vec![0, 1, 2, 3, 4, 5], EntryName::Dynamic("VEC_U8_BYTES".into()), None));
ds.insert_data_value(Entry::new_offset_of(vec_u8, EntryName::Configurable("VEC_U8_OFFSET".into()), None));
ds.insert_data_value(Entry::new_word(0xffffffffffffffff_u64, EntryName::NonConfigurable, None));

assert_eq!(ds.serialize_to_bytes(), [255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5]);
}
Loading

0 comments on commit 869de4b

Please sign in to comment.