Skip to content
Open
40 changes: 28 additions & 12 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::instruction_set::InstructionSet;
use super::{
fuel::{checks, data_section::DataSection},
fuel::{checks, compiler_constants::TWELVE_BITS, data_section::DataSection},
ProgramABI, ProgramKind,
};
use crate::asm_generation::fuel::data_section::{Datum, Entry, EntryName};
Expand Down Expand Up @@ -120,14 +120,28 @@ fn to_bytecode_mut(
source_engine: &SourceEngine,
build_config: &BuildConfig,
) -> CompiledBytecode {
fn op_size_in_bytes(data_section: &DataSection, item: &AllocatedOp) -> u64 {
// Snapshot before pointer pre-insertion mutates the data section.
let base_data_section_size = data_section.size_in_bytes() as u64;

fn op_size_in_bytes(
data_section: &DataSection,
base_data_section_size: u64,
item: &AllocatedOp,
) -> u64 {
match &item.opcode {
AllocatedInstruction::LoadDataId(_reg, data_label)
if !data_section
AllocatedInstruction::LoadDataId(_reg, data_label) => {
let has_copy_type = data_section
.has_copy_type(data_label)
.expect("data label references non existent data -- internal error") =>
{
8
.expect("data label references non existent data -- internal error");
if has_copy_type {
let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
let is_byte = data_section.is_byte(data_label).unwrap();
let imm_value = if is_byte { offset_bytes } else { offset_bytes / 8 };
if imm_value > TWELVE_BITS { 12 } else { 4 }
Comment thread
Dnreikronos marked this conversation as resolved.
Outdated
} else {
let pointer_offset_words = base_data_section_size / 8;
if pointer_offset_words > TWELVE_BITS { 16 } else { 8 }
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
}
Comment thread
Dnreikronos marked this conversation as resolved.
}
AllocatedInstruction::AddrDataId(_, _data_id) => 8,
AllocatedInstruction::ConfigurablesOffsetPlaceholder => 8,
Expand All @@ -140,9 +154,9 @@ fn to_bytecode_mut(

// Some instructions may be omitted or expanded into multiple instructions, so we compute,
// using `op_size_in_bytes`, exactly how many ops will be generated to calculate the offset.
let mut offset_to_data_section_in_bytes = ops
.iter()
.fold(0, |acc, item| acc + op_size_in_bytes(data_section, item));
let mut offset_to_data_section_in_bytes = ops.iter().fold(0, |acc, item| {
acc + op_size_in_bytes(data_section, base_data_section_size, item)
});

// A noop is inserted in ASM generation if required, to word-align the data section.
let mut ops_padded = Vec::new();
Expand Down Expand Up @@ -180,7 +194,8 @@ fn to_bytecode_mut(
}
_ => (),
}
offset_from_instr_start += op_size_in_bytes(data_section, op);
offset_from_instr_start +=
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
op_size_in_bytes(data_section, base_data_section_size, op);
}

let mut bytecode = Vec::with_capacity(offset_to_data_section_in_bytes as usize);
Expand All @@ -205,7 +220,8 @@ fn to_bytecode_mut(
offset_from_instr_start,
data_section,
);
offset_from_instr_start += op_size_in_bytes(data_section, op);
offset_from_instr_start +=
op_size_in_bytes(data_section, base_data_section_size, op);

match fuel_op {
FuelAsmData::DatasectionOffset(data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,24 +435,28 @@ impl AllocatedAbstractInstructionSet {
}
}

// Actual size of an instruction.
// Note that this return incorrect values for far jumps, they must be handled separately.
// The return value is in concrete instructions, i.e. units of 4 bytes.
// Actual size of an instruction (units of 4 bytes).
// Incorrect for far jumps — those are handled separately.
// Must be called before pointer pre-insertion in to_bytecode_mut;
// non-copy size estimates depend on data_section.size_in_bytes() being pointer-free.
fn instruction_size_not_far_jump(op: &AllocatedAbstractOp, data_section: &DataSection) -> u64 {
use ControlFlowOp::*;
match op.opcode {
Either::Right(Label(_)) => 0,

// A special case for LoadDataId which may be 1 or 2 ops, depending on the source size.
Either::Left(AllocatedInstruction::LoadDataId(_, ref data_id)) => {
let has_copy_type = data_section.has_copy_type(data_id).expect(
"Internal miscalculation in data section -- \
data id did not match up to any actual data",
);
if has_copy_type {
1
let offset_bytes = data_section.data_id_to_offset(data_id) as u64;
let is_byte = data_section.is_byte(data_id).unwrap();
let imm_value = if is_byte { offset_bytes } else { offset_bytes / 8 };
if imm_value > consts::TWELVE_BITS { 3 } else { 1 }
} else {
2
let pointer_offset_words = data_section.size_in_bytes() as u64 / 8;
if pointer_offset_words > consts::TWELVE_BITS { 4 } else { 2 }
}
}

Expand Down
4 changes: 4 additions & 0 deletions sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ impl DataSection {
})
}

pub(crate) fn size_in_bytes(&self) -> usize {
self.absolute_idx_to_offset(self.num_entries())
}

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());
Expand Down
79 changes: 52 additions & 27 deletions sway-core/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ use super::*;
use crate::{
asm_generation::fuel::{
compiler_constants::{
DATA_SECTION_REGISTER, LOWER_ALLOCATABLE_REGISTER, UPPER_ALLOCATABLE_REGISTER,
DATA_SECTION_REGISTER, EIGHTEEN_BITS, LOWER_ALLOCATABLE_REGISTER,
UPPER_ALLOCATABLE_REGISTER,
},
data_section::{DataId, DataSection},
},
fuel_prelude::fuel_asm::{self, op},
};
use fuel_vm::fuel_asm::{
op::{ADD, MOVI},
Imm18,
Imm12, Imm18,
};
use std::fmt::{self, Write};
use sway_types::span::Span;
Expand Down Expand Up @@ -947,6 +948,11 @@ fn addr_of(
data_section: &DataSection,
) -> Vec<fuel_asm::Instruction> {
let offset_bytes = data_section.data_id_to_offset(data_id) as u64;
assert!(
offset_bytes <= EIGHTEEN_BITS,
"Data section offset {offset_bytes} bytes exceeds 18-bit MOVI immediate limit. \
Data section too large."
);
vec![
fuel_asm::Instruction::MOVI(MOVI::new(
dest.to_reg_id(),
Expand Down Expand Up @@ -990,18 +996,6 @@ fn realize_load(
);
let offset_words = offset_bytes / 8;

let imm = VirtualImmediate12::try_new(
if is_byte { offset_bytes } else { offset_words },
Span::new(" ".into(), 0, 0, None).unwrap(),
);
let offset = match imm {
Ok(value) => value,
Err(_) => panic!(
"Unable to offset into the data section more than 2^12 bits. \
Unsupported data section length: {offset_words} words."
),
};

if !has_copy_type {
// load the pointer itself into the register. `offset_to_data_section` is in bytes.
// The -4 is because $pc is added in the *next* instruction.
Expand Down Expand Up @@ -1032,19 +1026,50 @@ fn realize_load(
.into(),
);
buf
} else if is_byte {
vec![fuel_asm::op::LB::new(
dest.to_reg_id(),
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
offset.value().into(),
)
.into()]
} else {
vec![fuel_asm::op::LW::new(
dest.to_reg_id(),
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
offset.value().into(),
)
.into()]
let imm_value = if is_byte { offset_bytes } else { offset_words };
let imm = VirtualImmediate12::try_new(imm_value, Span::dummy());

if let Ok(offset) = imm {
if is_byte {
vec![fuel_asm::op::LB::new(
dest.to_reg_id(),
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
offset.value().into(),
)
.into()]
} else {
vec![fuel_asm::op::LW::new(
dest.to_reg_id(),
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
offset.value().into(),
)
.into()]
}
} else {
assert!(
offset_bytes <= EIGHTEEN_BITS,
"Data section offset {offset_bytes} bytes exceeds 18-bit MOVI immediate limit. \
Data section too large ({offset_words} words)."
);
vec![
fuel_asm::Instruction::MOVI(MOVI::new(
dest.to_reg_id(),
Imm18::new(offset_bytes.try_into().unwrap()),
)),
fuel_asm::Instruction::ADD(ADD::new(
dest.to_reg_id(),
dest.to_reg_id(),
fuel_asm::RegId::new(DATA_SECTION_REGISTER),
)),
if is_byte {
fuel_asm::op::LB::new(dest.to_reg_id(), dest.to_reg_id(), Imm12::new(0))
.into()
} else {
fuel_asm::op::LW::new(dest.to_reg_id(), dest.to_reg_id(), Imm12::new(0))
.into()
},
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[package]]
name = "large_data_section"
source = "member"
dependencies = ["std"]

[[package]]
name = "std"
source = "path+from-root-E3854248C161EE9C"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "large_data_section"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Loading