Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metadata in the preamble #6522

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
28 changes: 23 additions & 5 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use sway_core::{
transform::AttributeKind,
write_dwarf, BuildTarget, Engines, FinalizedEntry, LspConfig,
};
use sway_core::{PrintAsm, PrintIr};
use sway_core::{set_bytecode_metadata, PrintAsm, PrintIr};
use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning};
use sway_types::constants::{CORE, PRELUDE, STD};
use sway_types::{Ident, Span, Spanned};
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,6 +1923,11 @@ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 {
Expand All @@ -1934,14 +1939,27 @@ 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 let Some(idx) = configurables.iter().position(|c| &c.name == config) {
configurables[idx].offset = *offset;
}
}
}

// Set metadata to a hash of the ABI.
// TODO: Do the right thing. This is just a placeholder.
let abi_json = serde_json::to_string_pretty(&program_abi)?;
let mut hasher = std::hash::DefaultHasher::new();
abi_json.hash(&mut hasher);
let hash: [u8; 8] = hasher.finish().to_be_bytes();
let hash = [hash, hash, hash, hash].concat();
for (index, byte) in md.iter_mut().enumerate() {
*byte = hash[index];
}
}

set_bytecode_metadata(&mut compiled, &md);

metrics.bytecode_size = compiled.bytecode.len();
let bytecode = BuiltPackageBytecode {
bytes: compiled.bytecode,
Expand Down
31 changes: 20 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,27 @@ 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 i4 ; Jumps to the data section setup.
/// [ 1] noop
/// [ 2] DATA_SECTION_OFFSET[0..32]
/// [ 3] DATA_SECTION_OFFSET[32..64]
/// [ 4] METADATA (0-32)
/// [ 5] METADATA (32-64)
/// [ 6] METADATA (64-96)
/// [ 7] METADATA (96-128)
/// [ 8] METADATA (128-160)
/// [ 9] METADATA (160-192)
/// [10] METADATA (192-224)
/// [11] METADATA (224-256)
/// [12] lw $ds $is 1 ; The data section setup, i.e. where the first ji lands.
/// [13] add $$ds $$ds $is
/// [14] <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
24 changes: 20 additions & 4 deletions sway-core/src/asm_generation/finalized_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{
ProgramABI, ProgramKind,
};
use crate::asm_generation::fuel::data_section::{DataId, Datum, Entry};
use crate::asm_lang::allocated_ops::{AllocatedOp, AllocatedOpcode};
use crate::asm_lang::allocated_ops::{AllocatedOp, AllocatedOpcode, FuelAsmData};
use crate::decl_engine::DeclRefFunction;
use crate::source_map::SourceMap;
use crate::BuildConfig;
Expand All @@ -17,7 +17,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 @@ -117,6 +116,7 @@ fn to_bytecode_mut(
{
8
}
AllocatedOpcode::Metadata => 32,
AllocatedOpcode::DataSectionOffsetPlaceholder => 8,
AllocatedOpcode::BLOB(count) => count.value as u64 * 4,
AllocatedOpcode::CFEI(i) | AllocatedOpcode::CFSI(i) if i.value == 0 => 0,
Expand Down Expand Up @@ -171,7 +171,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 @@ -187,7 +187,23 @@ fn to_bytecode_mut(
bytecode.extend(data.iter().cloned());
half_word_ix += 2;
}
Either::Left(instructions) => {
FuelAsmData::Metadata(data) => {
if build_config.print_bytecode {
print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
println!(
" ;; {:?}",
data
);
}

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

bytecode.extend(data.iter().cloned());
half_word_ix += 8;
}
FuelAsmData::Instructions(instructions) => {
for instruction in instructions {
// Print original source span only once
if build_config.print_bytecode_spans {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,13 @@ impl AllocatedAbstractInstructionSet {
comment: String::new(),
});
}
ControlFlowOp::Metadata => {
realized_ops.push(RealizedOp {
opcode: AllocatedOpcode::Metadata,
owning_span: None,
comment: String::new(),
});
}
ControlFlowOp::LoadLabel(r1, ref lab) => {
// LoadLabel ops are inserted by `rewrite_far_jumps`.
// So the next instruction must be a relative jump.
Expand Down Expand Up @@ -449,6 +456,8 @@ impl AllocatedAbstractInstructionSet {
2
}

Either::Right(Metadata) => 8,

Either::Right(PushAll(_)) | Either::Right(PopAll(_)) => unreachable!(
"fix me, pushall and popall don't really belong in control flow ops \
since they're not about control flow"
Expand Down
36 changes: 31 additions & 5 deletions sway-core/src/asm_generation/fuel/programs/abstract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,33 @@ impl AbstractProgram {
///
/// WORD OP
/// 1 MOV $scratch $pc
/// - JMPF $zero i2
/// - JMPF $zero i10
/// 2 DATA_START (0-32) (in bytes, offset from $is)
/// - DATA_START (32-64)
/// 3 LW $ds $scratch 1
/// 3 METADATA (0-32)
/// - METADATA (32-64)
/// 4 METADATA (64-96)
/// - METADATA (96-128)
/// 5 METADATA (128-160)
/// - METADATA (160-192)
/// 6 METADATA (192-224)
/// - METADATA (224-256)
/// 7 LW $ds $scratch 1
/// - ADD $ds $ds $scratch
/// 4 .program_start:
/// 8 .program_start:
fn build_prologue(&mut self) -> AllocatedAbstractInstructionSet {
const _: () = assert!(
crate::PRELUDE_METADATA_OFFSET_IN_BYTES == 16,
"Inconsistency in the assumption of prelude organisation"
);
const _: () = assert!(
crate::PRELUDE_METADATA_SIZE_IN_BYTES == 32,
"Inconsistency in the assumption of prelude organisation"
);
const _: () = assert!(
crate::PRELUDE_SIZE_IN_BYTES == 56,
"Inconsistency in the assumption of prelude organisation"
);
let label = self.reg_seqr.get_label();
AllocatedAbstractInstructionSet {
ops: [
Expand All @@ -190,12 +210,18 @@ impl AbstractProgram {
comment: "data section offset".into(),
owning_span: None,
},
// word 3 -- 32 bytes placeholder
AllocatedAbstractOp {
opcode: Either::Right(ControlFlowOp::Metadata),
comment: "metadata".into(),
owning_span: None,
},
AllocatedAbstractOp {
opcode: Either::Right(ControlFlowOp::Label(label)),
comment: "end of metadata".into(),
owning_span: None,
},
// word 3 -- load the data offset into $ds
// word 7 -- load the data offset into $ds
AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::LW(
AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
Expand All @@ -205,7 +231,7 @@ impl AbstractProgram {
comment: "".into(),
owning_span: None,
},
// word 3.5 -- add $ds $ds $is
// word 7.5 -- add $ds $ds $is
AllocatedAbstractOp {
opcode: Either::Left(AllocatedOpcode::ADD(
AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
Expand Down
32 changes: 22 additions & 10 deletions sway-core/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use crate::{
},
fuel_prelude::fuel_asm::{self, op},
};
use either::Either;
use fuel_vm::fuel_asm::{op::ADDI, Imm12};
use std::fmt::{self, Write};
use sway_types::span::Span;
Expand Down Expand Up @@ -269,6 +268,7 @@ pub(crate) enum AllocatedOpcode {

/* Non-VM Instructions */
BLOB(VirtualImmediate24),
Metadata,
DataSectionOffsetPlaceholder,
LoadDataId(AllocatedRegister, DataId),
AddrDataId(AllocatedRegister, DataId),
Expand Down Expand Up @@ -393,6 +393,7 @@ impl AllocatedOpcode {

/* Non-VM Instructions */
BLOB(_imm) => vec![],
Metadata => vec![],
DataSectionOffsetPlaceholder => vec![],
LoadDataId(r1, _i) => vec![r1],
AddrDataId(r1, _i) => vec![r1],
Expand Down Expand Up @@ -521,6 +522,7 @@ impl fmt::Display for AllocatedOpcode {

/* Non-VM Instructions */
BLOB(a) => write!(fmtr, "blob {a}"),
Metadata => write!(fmtr, "Metadata"),
DataSectionOffsetPlaceholder => {
write!(
fmtr,
Expand Down Expand Up @@ -558,17 +560,21 @@ impl fmt::Display for AllocatedOp {
}
}

type DoubleWideData = [u8; 8];
pub(crate) enum FuelAsmData {
Metadata([u8; 32]),
DatasectionOffset([u8; 8]),
Instructions(Vec<fuel_asm::Instruction>),
}

impl AllocatedOp {
pub(crate) fn to_fuel_asm(
&self,
offset_to_data_section: u64,
offset_from_instr_start: u64,
data_section: &mut DataSection,
) -> Either<Vec<fuel_asm::Instruction>, DoubleWideData> {
) -> FuelAsmData {
use AllocatedOpcode::*;
Either::Left(vec![match &self.opcode {
FuelAsmData::Instructions(vec![match &self.opcode {
/* Arithmetic/Logic (ALU) Instructions */
ADD(a, b, c) => op::ADD::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
ADDI(a, b, c) => op::ADDI::new(a.to_reg_id(), b.to_reg_id(), c.value.into()).into(),
Expand Down Expand Up @@ -637,9 +643,9 @@ impl AllocatedOp {

/* Memory Instructions */
ALOC(a) => op::ALOC::new(a.to_reg_id()).into(),
CFEI(a) if a.value == 0 => return Either::Left(vec![]),
CFEI(a) if a.value == 0 => return FuelAsmData::Instructions(vec![]),
CFEI(a) => op::CFEI::new(a.value.into()).into(),
CFSI(a) if a.value == 0 => return Either::Left(vec![]),
CFSI(a) if a.value == 0 => return FuelAsmData::Instructions(vec![]),
CFSI(a) => op::CFSI::new(a.value.into()).into(),
CFE(a) => op::CFE::new(a.to_reg_id()).into(),
CFS(a) => op::CFS::new(a.to_reg_id()).into(),
Expand Down Expand Up @@ -723,25 +729,31 @@ impl AllocatedOp {

/* Non-VM Instructions */
BLOB(a) => {
return Either::Left(
return FuelAsmData::Instructions(
std::iter::repeat(op::NOOP::new().into())
.take(a.value as usize)
.collect(),
)
}
Metadata => {
return FuelAsmData::Metadata([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
])
}
DataSectionOffsetPlaceholder => {
return Either::Right(offset_to_data_section.to_be_bytes())
return FuelAsmData::DatasectionOffset(offset_to_data_section.to_be_bytes())
}
LoadDataId(a, b) => {
return Either::Left(realize_load(
return FuelAsmData::Instructions(realize_load(
a,
b,
data_section,
offset_to_data_section,
offset_from_instr_start,
))
}
AddrDataId(a, b) => return Either::Left(addr_of(a, b, data_section)),
AddrDataId(a, b) => return FuelAsmData::Instructions(addr_of(a, b, data_section)),
Undefined => unreachable!("Sway cannot generate undefined ASM opcodes"),
}])
}
Expand Down
Loading
Loading