Skip to content

Commit

Permalink
Add configurables section offset in the preamble (#6522)
Browse files Browse the repository at this point in the history
The preamble now contains 8 bytes of offset to the configurables
section. If there is no configurable const in the data-section, then the
value of this integer will be equal to the size of the binary itself.

This also means that we now sort the data-section to have all the
non-configurables first, and then the configurables.

The preamble final asm looks like this (the offset to configurables is
0'd out here in the example):
```
;; ASM: Final program
;; Program kind: Script
.program:
move $$tmp $pc
jmpf $zero i10
DATA_SECTION_OFFSET[0..32]
DATA_SECTION_OFFSET[32..64]
CONFIGURABLES_OFFSET[0..32]
CONFIGURABLES_OFFSET[32..64]
lw   $$ds $$tmp i1
add  $$ds $$ds $$tmp
```
The preamble bytecode looks like this:

```
0x00000000 MOVE R60 $pc                                    ;; [26, 240, 48, 0]
0x00000004 JMPF $zero 0xa                                  ;; [116, 0, 0, 10]
0x00000008                                                 ;; [0, 0, 0, 0, 0, 0, 2, 40]
0x00000010                                                 ;; [0, 0, 0, 0, 0, 0, 0, 0]
0x00000030 LW R63 R60 0x1                                  ;; [93, 255, 192, 1]
0x00000034 ADD R63 R63 R60                                 ;; [16, 255, 255, 0]
...
```

---------

Co-authored-by: Sophie Dankel <[email protected]>
Co-authored-by: IGI-111 <[email protected]>
  • Loading branch information
3 people committed Nov 11, 2024
1 parent 595a6bc commit 60cad51
Show file tree
Hide file tree
Showing 31 changed files with 439 additions and 178 deletions.
23 changes: 18 additions & 5 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use sway_core::{
transform::AttributeKind,
write_dwarf, BuildTarget, Engines, FinalizedEntry, LspConfig,
};
use sway_core::{PrintAsm, PrintIr};
use sway_core::{set_bytecode_configurables_offset, PrintAsm, PrintIr};
use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning};
use sway_features::ExperimentalFeatures;
use sway_types::constants::{CORE, PRELUDE, STD};
Expand Down Expand Up @@ -1914,7 +1914,7 @@ pub fn compile(

let errored = handler.has_errors() || (handler.has_warnings() && profile.error_on_warnings);

let compiled = match bc_res {
let mut compiled = match bc_res {
Ok(compiled) if !errored => compiled,
_ => return fail(handler),
};
Expand All @@ -1923,9 +1923,12 @@ pub fn compile(

print_warnings(engines.se(), terse_mode, &pkg.name, &warnings, &tree_type);

// Metadata to be placed into the binary.
let mut md = [0u8, 0, 0, 0, 0, 0, 0, 0];
// TODO: This should probably be in `fuel_abi_json::generate_json_abi_program`?
// If ABI requires knowing config offsets, they should be inputs to ABI gen.
if let ProgramABI::Fuel(ref mut program_abi) = program_abi {
let mut configurables_offset = compiled.bytecode.len() as u64;
if let Some(ref mut configurables) = program_abi.configurables {
// Filter out all dead configurables (i.e. ones without offsets in the bytecode)
configurables.retain(|c| {
Expand All @@ -1934,12 +1937,22 @@ pub fn compile(
.contains_key(&c.name)
});
// Set the actual offsets in the JSON object
for (config, offset) in compiled.named_data_section_entries_offsets {
if let Some(idx) = configurables.iter().position(|c| c.name == config) {
configurables[idx].offset = offset;
for (config, offset) in &compiled.named_data_section_entries_offsets {
if *offset < configurables_offset {
configurables_offset = *offset;
}
if let Some(idx) = configurables.iter().position(|c| &c.name == config) {
configurables[idx].offset = *offset;
}
}
}

md = configurables_offset.to_be_bytes();
}

// We know to set the metadata only for fuelvm right now.
if let BuildTarget::Fuel = pkg.target {
set_bytecode_configurables_offset(&mut compiled, &md);
}

metrics.bytecode_size = compiled.bytecode.len();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,62 +251,62 @@
{
"name": "BOOL",
"concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903",
"offset": 136
"offset": 240
},
{
"name": "U8",
"concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b",
"offset": 248
"offset": 352
},
{
"name": "U16",
"concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef",
"offset": 192
"offset": 296
},
{
"name": "U32",
"concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc",
"offset": 232
"offset": 336
},
{
"name": "U64",
"concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0",
"offset": 240
"offset": 344
},
{
"name": "U256",
"concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e",
"offset": 200
"offset": 304
},
{
"name": "B256",
"concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b",
"offset": 104
"offset": 208
},
{
"name": "STR_4",
"concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a",
"offset": 176
"offset": 280
},
{
"name": "TUPLE",
"concreteTypeId": "e0128f7be9902d1fe16326cafe703b52038064a7997b03ebfc1c9dd607e1536c",
"offset": 184
"offset": 288
},
{
"name": "ARRAY",
"concreteTypeId": "d9fac01ab38fe10950758ae9604da330d6406a71fda3ef1ea818121261132d56",
"offset": 88
"offset": 192
},
{
"name": "STRUCT",
"concreteTypeId": "563310524b4f4447a10d0e50556310253dfb3b5eb4b29c3773222b737c8b7075",
"offset": 160
"offset": 264
},
{
"name": "ENUM",
"concreteTypeId": "37cd1cba311039a851ac8bfa614cc41359b4ad95c8656fcef2e8f504fe7a1272",
"offset": 144
"offset": 248
}
]
}
8 changes: 4 additions & 4 deletions forc-plugins/forc-client/tests/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ async fn test_simple_deploy() {
node.kill().unwrap();
let expected = vec![DeployedPackage::Contract(DeployedContract {
id: ContractId::from_str(
"5a4d19b92e784817f3e4f7d7c9961c5cba3069e53dcee2a93e8cd723e555c5f3",
"adcd0480deb735ffd8f5bd0683b66c55465e0841076efca4c28be25c767ede5b",
)
.unwrap(),
proxy: None,
Expand Down Expand Up @@ -416,7 +416,7 @@ async fn test_deploy_submit_only() {
node.kill().unwrap();
let expected = vec![DeployedPackage::Contract(DeployedContract {
id: ContractId::from_str(
"5a4d19b92e784817f3e4f7d7c9961c5cba3069e53dcee2a93e8cd723e555c5f3",
"adcd0480deb735ffd8f5bd0683b66c55465e0841076efca4c28be25c767ede5b",
)
.unwrap(),
proxy: None,
Expand Down Expand Up @@ -462,12 +462,12 @@ async fn test_deploy_fresh_proxy() {
node.kill().unwrap();
let impl_contract = DeployedPackage::Contract(DeployedContract {
id: ContractId::from_str(
"5a4d19b92e784817f3e4f7d7c9961c5cba3069e53dcee2a93e8cd723e555c5f3",
"adcd0480deb735ffd8f5bd0683b66c55465e0841076efca4c28be25c767ede5b",
)
.unwrap(),
proxy: Some(
ContractId::from_str(
"f0641246d72044059de56248becf345bd8553c7892df8c12d7df23f461a7f95b",
"5297238a1d867c9d7c8fa83c700e2d0d1c57e1874ec95ff4a67063e222ab1880",
)
.unwrap(),
),
Expand Down
25 changes: 14 additions & 11 deletions forc-test/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use fuel_vm::{
self as vm,
checked_transaction::builder::TransactionBuilderExt,
interpreter::{Interpreter, NotSupportedEcal},
prelude::{Instruction, SecretKey},
prelude::SecretKey,
storage::MemoryStorage,
};
use rand::{Rng, SeedableRng};
Expand Down Expand Up @@ -246,18 +246,21 @@ impl TestExecutor {
/// The following is how the beginning of the bytecode is laid out:
///
/// ```ignore
/// [0] ji i4 ; Jumps to the data section setup.
/// [1] noop
/// [2] DATA_SECTION_OFFSET[0..32]
/// [3] DATA_SECTION_OFFSET[32..64]
/// [4] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands.
/// [5] add $$ds $$ds $is
/// [6] <first-entry-point> ; This is where we want to jump from to our test code!
/// [ 0] ji i(4 + 2) ; Jumps to the data section setup.
/// [ 1] noop
/// [ 2] DATA_SECTION_OFFSET[0..32]
/// [ 3] DATA_SECTION_OFFSET[32..64]
/// [ 4] CONFIGURABLES_OFFSET[0..32]
/// [ 5] CONFIGURABLES_OFFSET[32..64]
/// [ 6] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands.
/// [ 7] add $$ds $$ds $is
/// [ 8] <first-entry-point> ; This is where we want to jump from to our test code!
/// ```
fn patch_test_bytecode(bytecode: &[u8], test_offset: u32) -> std::borrow::Cow<[u8]> {
// TODO: Standardize this or add metadata to bytecode.
const PROGRAM_START_INST_OFFSET: u32 = 6;
const PROGRAM_START_BYTE_OFFSET: usize = PROGRAM_START_INST_OFFSET as usize * Instruction::SIZE;
// Each instruction is 4 bytes,
// so we divide the total byte-size by 4 to get the instruction offset.
const PROGRAM_START_INST_OFFSET: u32 = (sway_core::PRELUDE_SIZE_IN_BYTES / 4) as u32;
const PROGRAM_START_BYTE_OFFSET: usize = sway_core::PRELUDE_SIZE_IN_BYTES;

// If our desired entry point is the program start, no need to jump.
if test_offset == PROGRAM_START_INST_OFFSET {
Expand Down
8 changes: 8 additions & 0 deletions forc/src/cli/commands/parse_bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ pub(crate) fn exec(command: Command) -> ForcResult<()> {
parsed_raw
)
}
Err(fuel_asm::InvalidOpcode) if word_ix == 4 || word_ix == 5 => {
let parsed_raw = u32::from_be_bytes([raw[0], raw[1], raw[2], raw[3]]);
format!(
"configurables offset {} ({})",
if word_ix == 4 { "lo" } else { "hi" },
parsed_raw
)
}
Ok(_) | Err(fuel_asm::InvalidOpcode) => "".into(),
};
table.add_row(Row::new(vec![
Expand Down
8 changes: 4 additions & 4 deletions forc/tests/cli_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ fn test_forc_test_raw_logs() -> Result<(), rexpect::error::Error> {
// Assert that the output is correct
process.exp_string(" test test_log_4")?;
process.exp_string("Raw logs:")?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000004","digest":"8005f02d43fa06e7d0585fb64c961d57e318b27a145c857bcd3a6bdb413ff7fc","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12664,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000004","digest":"8005f02d43fa06e7d0585fb64c961d57e318b27a145c857bcd3a6bdb413ff7fc","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12660,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(" test test_log_2")?;
process.exp_string("Raw logs:")?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000002","digest":"cd04a4754498e06db5a13c5f371f1f04ff6d2470f24aa9bd886540e5dce77f70","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12664,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000002","digest":"cd04a4754498e06db5a13c5f371f1f04ff6d2470f24aa9bd886540e5dce77f70","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12660,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;

process.process.exit()?;
Ok(())
Expand All @@ -74,11 +74,11 @@ fn test_forc_test_both_logs() -> Result<(), rexpect::error::Error> {
process.exp_string(" test test_log_4")?;
process.exp_string("Decoded log value: 4, log rb: 1515152261580153489")?;
process.exp_string("Raw logs:")?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000004","digest":"8005f02d43fa06e7d0585fb64c961d57e318b27a145c857bcd3a6bdb413ff7fc","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12664,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000004","digest":"8005f02d43fa06e7d0585fb64c961d57e318b27a145c857bcd3a6bdb413ff7fc","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12660,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(" test test_log_2")?;
process.exp_string("Decoded log value: 2, log rb: 1515152261580153489")?;
process.exp_string("Raw logs:")?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000002","digest":"cd04a4754498e06db5a13c5f371f1f04ff6d2470f24aa9bd886540e5dce77f70","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12664,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.exp_string(r#"[{"LogData":{"data":"0000000000000002","digest":"cd04a4754498e06db5a13c5f371f1f04ff6d2470f24aa9bd886540e5dce77f70","id":"0000000000000000000000000000000000000000000000000000000000000000","is":10368,"len":8,"pc":12660,"ptr":67107840,"ra":0,"rb":1515152261580153489}}]"#)?;
process.process.exit()?;
Ok(())
}
65 changes: 53 additions & 12 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use super::{
fuel::{checks, data_section::DataSection},
ProgramABI, ProgramKind,
};
use crate::asm_generation::fuel::data_section::{DataId, Datum, Entry};
use crate::asm_lang::allocated_ops::{AllocatedOp, AllocatedOpcode};
use crate::asm_generation::fuel::data_section::{Datum, Entry, EntryName};
use crate::asm_lang::allocated_ops::{AllocatedOp, AllocatedOpcode, FuelAsmData};
use crate::decl_engine::DeclRefFunction;
use crate::source_map::SourceMap;
use crate::BuildConfig;
Expand All @@ -16,7 +16,6 @@ use sway_error::handler::{ErrorEmitted, Handler};
use sway_types::span::Span;
use sway_types::SourceEngine;

use either::Either;
use std::{collections::BTreeMap, fmt};

/// Represents an ASM set which has had register allocation, jump elimination, and optimization
Expand Down Expand Up @@ -143,6 +142,29 @@ fn to_bytecode_mut(
&ops_padded
};

let mut offset_from_instr_start = 0;
for op in ops.iter() {
match &op.opcode {
AllocatedOpcode::LoadDataId(_reg, data_label)
if !data_section
.has_copy_type(data_label)
.expect("data label references non existent data -- internal error") =>
{
// For non-copy type loads, pre-insert pointers into the data_section so that
// from this point on, the data_section remains immutable. This is necessary
// so that when we take addresses of configurables, that address doesn't change
// later on if a non-configurable is added to the data-section.
let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
// The -4 is because $pc is added in the *next* instruction.
let pointer_offset_from_current_instr =
offset_to_data_section_in_bytes - offset_from_instr_start + offset_bytes - 4;
data_section.append_pointer(pointer_offset_from_current_instr);
}
_ => (),
}
offset_from_instr_start += op_size_in_bytes(data_section, op);
}

let mut bytecode = Vec::with_capacity(offset_to_data_section_in_bytes as usize);

if build_config.print_bytecode {
Expand All @@ -168,7 +190,7 @@ fn to_bytecode_mut(
offset_from_instr_start += op_size_in_bytes(data_section, op);

match fuel_op {
Either::Right(data) => {
FuelAsmData::DatasectionOffset(data) => {
if build_config.print_bytecode {
print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
println!(
Expand All @@ -184,7 +206,23 @@ fn to_bytecode_mut(
bytecode.extend(data.iter().cloned());
half_word_ix += 2;
}
Either::Left(instructions) => {
FuelAsmData::ConfigurablesOffset(data) => {
if build_config.print_bytecode {
print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
println!(
" ;; {:?}",
data
);
}

// Static assert to ensure that we're only dealing with ConfigurablesOffsetPlaceholder,
// a 1-word (8 bytes) data within the code. No other uses are known.
let _: [u8; 8] = data;

bytecode.extend(data.iter().cloned());
half_word_ix += 2;
}
FuelAsmData::Instructions(instructions) => {
for instruction in instructions {
// Print original source span only once
if build_config.print_bytecode_spans {
Expand Down Expand Up @@ -297,9 +335,9 @@ fn to_bytecode_mut(
};
}

for (i, entry) in data_section.value_pairs.iter().enumerate() {
let entry_offset = data_section.data_id_to_offset(&DataId(i as u32));
print_entry(indentation, offset + entry_offset, entry);
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);
}

println!(";; --- END OF TARGET BYTECODE ---\n");
Expand All @@ -308,16 +346,19 @@ fn to_bytecode_mut(
assert_eq!(half_word_ix * 4, offset_to_data_section_in_bytes as usize);
assert_eq!(bytecode.len(), offset_to_data_section_in_bytes as usize);

let num_nonconfigurables = data_section.non_configurables.len();
let named_data_section_entries_offsets = data_section
.value_pairs
.configurables
.iter()
.enumerate()
.filter(|entry| entry.1.name.is_some())
.map(|(id, entry)| {
let EntryName::Configurable(name) = &entry.name else {
panic!("Non-configurable in configurables part of datasection");
};
(
entry.name.as_ref().unwrap().clone(),
name.clone(),
offset_to_data_section_in_bytes
+ data_section.raw_data_id_to_offset(id as u32) as u64,
+ data_section.absolute_idx_to_offset(id + num_nonconfigurables) as u64,
)
})
.collect::<BTreeMap<String, u64>>();
Expand Down
Loading

0 comments on commit 60cad51

Please sign in to comment.