From fddc699d1a013607e0f7b34c4e27bbf507be9d68 Mon Sep 17 00:00:00 2001 From: IGI-111 Date: Thu, 13 Jul 2023 22:47:47 +0200 Subject: [PATCH] Represent small values as single bytes This change leverages the SB/LB instructions to change the memory representation of all small enough values to make them fit in a single byte instead of a full word. The type size and passing calculations have been changed to align elements of structs and enums to full words. Structs and data section entries are filled with right-padding to align their elements to words. Enums are still left-padded. The Data section generation has been refactored to allow for these two padding modes. Arrays and slices contain no inner padding, byte sequences will now be properly consecutive and packed. Though, as a whole, they may be right padded in certain circumstances to maintain word alignment. Direct usages of LW/SW have been changed to LB/SB where appropriate. The LWDataId virtual instruction has been changed to LoadDataId to better represent the fact that it can load both word and byte sized values. --- sway-core/src/asm_generation/finalized_asm.rs | 2 +- .../allocated_abstract_instruction_set.rs | 4 +- .../src/asm_generation/fuel/data_section.rs | 148 ++++++++++++++---- .../asm_generation/fuel/fuel_asm_builder.rs | 126 ++++++++++----- .../src/asm_generation/fuel/functions.rs | 69 +++++--- .../src/asm_generation/programs/abstract.rs | 2 +- sway-core/src/asm_lang/allocated_ops.rs | 50 ++++-- sway-core/src/asm_lang/mod.rs | 33 +--- sway-core/src/asm_lang/virtual_ops.rs | 16 +- sway-core/src/ir_generation/const_eval.rs | 13 +- sway-core/src/ir_generation/convert.rs | 16 +- sway-core/src/ir_generation/function.rs | 27 +++- sway-ir/src/irtype.rs | 24 ++- sway-ir/src/verify.rs | 17 +- sway-lib-core/src/primitive_conversions.sw | 56 +++---- sway-lib-core/src/raw_ptr.sw | 9 ++ sway-lib-std/src/hash.sw | 50 ++++-- .../configurable_consts/json_abi_oracle.json | 14 +- .../should_pass/stdlib/intrinsics/src/main.sw | 4 +- .../should_pass/stdlib/raw_ptr/src/main.sw | 6 +- .../should_pass/stdlib/raw_slice/test.toml | 2 +- 21 files changed, 461 insertions(+), 227 deletions(-) diff --git a/sway-core/src/asm_generation/finalized_asm.rs b/sway-core/src/asm_generation/finalized_asm.rs index 984a3320c1b..c6ce070e862 100644 --- a/sway-core/src/asm_generation/finalized_asm.rs +++ b/sway-core/src/asm_generation/finalized_asm.rs @@ -118,7 +118,7 @@ fn to_bytecode_mut( // Some LWs are expanded into two ops to allow for data larger than one word, so we calculate // exactly how many ops will be generated to calculate the offset. let offset_to_data_section_in_bytes = ops.iter().fold(0, |acc, item| match &item.opcode { - AllocatedOpcode::LWDataId(_reg, data_label) + AllocatedOpcode::LoadDataId(_reg, data_label) if !data_section .has_copy_type(data_label) .expect("data label references non existent data -- internal error") => diff --git a/sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs b/sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs index 00199d6f2c8..01deb07c8e7 100644 --- a/sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs +++ b/sway-core/src/asm_generation/fuel/allocated_abstract_instruction_set.rs @@ -348,7 +348,7 @@ impl AllocatedAbstractInstructionSet { let data_id = data_section.insert_data_value(Entry::new_word(offset, None, None)); realized_ops.push(RealizedOp { - opcode: AllocatedOpcode::LWDataId(r1, data_id), + opcode: AllocatedOpcode::LoadDataId(r1, data_id), owning_span, comment, }); @@ -414,7 +414,7 @@ impl AllocatedAbstractInstructionSet { Either::Right(Label(_)) => 0, // A special case for LWDataId which may be 1 or 2 ops, depending on the source size. - Either::Left(AllocatedOpcode::LWDataId(_, ref data_id)) => { + Either::Left(AllocatedOpcode::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", diff --git a/sway-core/src/asm_generation/fuel/data_section.rs b/sway-core/src/asm_generation/fuel/data_section.rs index 3ee28de5cb0..e3d1593d889 100644 --- a/sway-core/src/asm_generation/fuel/data_section.rs +++ b/sway-core/src/asm_generation/fuel/data_section.rs @@ -1,10 +1,13 @@ -use crate::asm_generation::from_ir::ir_type_size_in_bytes; +use crate::{ + asm_generation::from_ir::ir_type_size_in_bytes, size_bytes_round_up_to_word_alignment, +}; use sway_ir::{Constant, ConstantValue, Context}; use std::{ collections::BTreeMap, fmt::{self, Write}, + iter::repeat, }; // An entry in the data section. It's important for the size to be correct, especially for unions @@ -12,7 +15,7 @@ use std::{ #[derive(Clone, Debug)] pub struct Entry { value: Datum, - size: usize, + padding: Padding, // It is assumed, for now, that only configuration-time constants have a name. Otherwise, this // is `None`. name: Option, @@ -20,42 +23,68 @@ pub struct Entry { #[derive(Clone, Debug)] pub enum Datum { + Byte(u8), Word(u64), ByteArray(Vec), Collection(Vec), } +#[derive(Clone, Debug)] +pub(crate) enum Padding { + Left { target_size: usize }, + Right { target_size: usize }, +} + +impl Padding { + pub fn target_size(&self) -> usize { + use Padding::*; + match self { + Left { target_size } | Right { target_size } => *target_size, + } + } +} + impl Entry { - pub(crate) fn new_word(value: u64, size: Option, name: Option) -> Entry { + pub(crate) fn new_byte(value: u8, name: Option, padding: Option) -> Entry { + Entry { + value: Datum::Byte(value), + padding: padding.unwrap_or(Padding::Right { target_size: 1 }), + name, + } + } + + pub(crate) fn new_word(value: u64, name: Option, padding: Option) -> Entry { Entry { value: Datum::Word(value), - size: size.unwrap_or(8), + padding: padding.unwrap_or(Padding::Right { target_size: 8 }), name, } } pub(crate) fn new_byte_array( bytes: Vec, - size: Option, name: Option, + padding: Option, ) -> Entry { - let size = size.unwrap_or(bytes.len()); Entry { + padding: padding.unwrap_or(Padding::Right { + target_size: bytes.len(), + }), value: Datum::ByteArray(bytes), - size, name, } } pub(crate) fn new_collection( elements: Vec, - size: Option, name: Option, + padding: Option, ) -> Entry { - let size = size.unwrap_or_else(|| elements.iter().map(|el| el.size).sum()); Entry { + padding: padding.unwrap_or(Padding::Right { + target_size: elements.iter().map(|el| el.padding.target_size()).sum(), + }), value: Datum::Collection(elements), - size, name, } } @@ -64,11 +93,11 @@ impl Entry { context: &Context, constant: &Constant, name: Option, + padding: Option, ) -> Entry { // We have to do some painful special handling here for enums, which are tagged unions. // This really should be handled by the IR more explicitly and is something that will // hopefully be addressed by https://github.com/FuelLabs/sway/issues/2819#issuecomment-1256930392 - let size = Some(ir_type_size_in_bytes(context, &constant.ty) as usize); // Is this constant a tagged union? if constant.ty.is_struct(context) { @@ -81,15 +110,24 @@ impl Entry { // we use unions (otherwise we should be generalising this a bit more). if let ConstantValue::Struct(els) = &constant.value { if els.len() == 2 { - let tag_entry = Entry::from_constant(context, &els[0], None); + let tag_entry = Entry::from_constant(context, &els[0], None, None); // Here's the special case. We need to get the size of the union and // attach it to this constant entry which will be one of the variants. - let mut val_entry = Entry::from_constant(context, &els[1], None); - val_entry.size = ir_type_size_in_bytes(context, &field_tys[1]) as usize; + let val_entry = { + let target_size = size_bytes_round_up_to_word_alignment!( + ir_type_size_in_bytes(context, &field_tys[1]) as usize + ); + Entry::from_constant( + context, + &els[1], + None, + Some(Padding::Left { target_size }), + ) + }; // Return here from our special case. - return Entry::new_collection(vec![tag_entry, val_entry], size, name); + return Entry::new_collection(vec![tag_entry, val_entry], name, padding); } } } @@ -97,19 +135,44 @@ impl Entry { // Not a tagged union, no trickiness required. match &constant.value { - ConstantValue::Undef | ConstantValue::Unit => Entry::new_word(0, size, name), - ConstantValue::Bool(b) => Entry::new_word(u64::from(*b), size, name), - ConstantValue::Uint(u) => Entry::new_word(*u, size, name), - ConstantValue::U256(u) => Entry::new_byte_array(u.to_be_bytes().to_vec(), size, name), - ConstantValue::B256(bs) => Entry::new_byte_array(bs.to_vec(), size, name), - ConstantValue::String(bs) => Entry::new_byte_array(bs.clone(), size, name), - - ConstantValue::Array(els) | ConstantValue::Struct(els) => Entry::new_collection( + ConstantValue::Undef | ConstantValue::Unit => Entry::new_byte(0, name, padding), + ConstantValue::Bool(b) => Entry::new_byte(u8::from(*b), name, padding), + ConstantValue::Uint(u) => { + if constant.ty.is_uint8(context) { + Entry::new_byte(*u as u8, name, padding) + } else { + Entry::new_word(*u, name, padding) + } + } + ConstantValue::U256(u) => { + Entry::new_byte_array(u.to_be_bytes().to_vec(), name, padding) + } + ConstantValue::B256(bs) => Entry::new_byte_array(bs.to_vec(), name, padding), + ConstantValue::String(bs) => Entry::new_byte_array(bs.clone(), name, padding), + + ConstantValue::Array(els) => Entry::new_collection( + els.iter() + .map(|el| Entry::from_constant(context, el, None, None)) + .collect(), + name, + padding, + ), + ConstantValue::Struct(els) => Entry::new_collection( els.iter() - .map(|el| Entry::from_constant(context, el, None)) + .map(|el| { + let target_size = size_bytes_round_up_to_word_alignment!( + ir_type_size_in_bytes(context, &el.ty) as usize + ); + Entry::from_constant( + context, + el, + None, + Some(Padding::Right { target_size }), + ) + }) .collect(), - size, name, + padding, ), } } @@ -117,7 +180,8 @@ impl Entry { /// Converts a literal to a big-endian representation. This is padded to words. pub(crate) fn to_bytes(&self) -> Vec { // Get the big-endian byte representation of the basic value. - let mut bytes = match &self.value { + let bytes = match &self.value { + Datum::Byte(b) => vec![*b], Datum::Word(w) => w.to_be_bytes().to_vec(), Datum::ByteArray(bs) if bs.len() % 8 == 0 => bs.clone(), Datum::ByteArray(bs) => bs @@ -129,18 +193,26 @@ impl Entry { Datum::Collection(els) => els.iter().flat_map(|el| el.to_bytes()).collect(), }; - // Pad the size out to match the specified size. - if self.size > bytes.len() { - let mut pad = vec![0; self.size - bytes.len()]; - pad.append(&mut bytes); - bytes = pad; + match self.padding { + Padding::Left { target_size } => { + let target_size = size_bytes_round_up_to_word_alignment!(target_size); + let left_pad = target_size.saturating_sub(bytes.len()); + [repeat(0u8).take(left_pad).collect(), bytes].concat() + } + Padding::Right { target_size } => { + let target_size = size_bytes_round_up_to_word_alignment!(target_size); + let right_pad = target_size.saturating_sub(bytes.len()); + [bytes, repeat(0u8).take(right_pad).collect()].concat() + } } - - bytes } pub(crate) fn has_copy_type(&self) -> bool { - matches!(self.value, Datum::Word(_)) + matches!(self.value, Datum::Word(_) | Datum::Byte(_)) + } + + pub(crate) fn is_byte(&self) -> bool { + matches!(self.value, Datum::Byte(_)) } pub(crate) fn equiv(&self, entry: &Entry) -> bool { @@ -218,6 +290,13 @@ impl DataSection { .map(|entry| entry.has_copy_type()) } + /// Returns whether a specific [DataId] value has a byte entry. + pub(crate) fn is_byte(&self, id: &DataId) -> Option { + self.value_pairs + .get(id.0 as usize) + .map(|entry| entry.is_byte()) + } + /// When generating code, sometimes a hard-coded data pointer is needed to reference /// static values that have a length longer than one word. /// This method appends pointers to the end of the data section (thus, not altering the data @@ -253,6 +332,7 @@ impl fmt::Display for DataSection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn display_entry(datum: &Datum) -> String { match datum { + Datum::Byte(w) => format!(".byte {w}"), Datum::Word(w) => format!(".word {w}"), Datum::ByteArray(bs) => { let mut hex_str = String::new(); diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index 10e3f8c7215..6d2f772e806 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -906,7 +906,12 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { let field_offs_in_bytes = field_types .iter() .take(idx) - .map(|field_ty| ir_type_size_in_bytes(self.context, field_ty)) + .map(|field_ty| { + size_bytes_round_up_to_word_alignment!(ir_type_size_in_bytes( + self.context, + field_ty + )) + }) .sum::(); (reg, offs + field_offs_in_bytes, field_type) }) @@ -1065,7 +1070,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { Some(Storage::Data(data_id)) => { let instr_reg = self.reg_seqr.next(); self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::LWDataId(instr_reg.clone(), data_id.clone())), + opcode: Either::Left(VirtualOp::LoadDataId(instr_reg.clone(), data_id.clone())), comment: "get local constant".into(), owning_span, }); @@ -1107,32 +1112,54 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { fn compile_load(&mut self, instr_val: &Value, src_val: &Value) -> Result<(), CompileError> { let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); - if src_val + let src_ty = src_val .get_type(self.context) .and_then(|src_ty| src_ty.get_pointee_type(self.context)) - .map_or(true, |inner_ty| !self.is_copy_type(&inner_ty)) - { - Err(CompileError::Internal( - "Attempt to load from non-copy type.", - owning_span.unwrap_or_else(Span::dummy), - )) - } else { - let src_reg = self.value_to_register(src_val)?; - let instr_reg = self.reg_seqr.next(); + .filter(|inner_ty| self.is_copy_type(inner_ty)) + .ok_or_else(|| { + CompileError::Internal( + "Attempt to load from non-copy type.", + owning_span.clone().unwrap_or_else(Span::dummy), + ) + })?; + let byte_len = ir_type_size_in_bytes(self.context, &src_ty); - self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::LW( - instr_reg.clone(), - src_reg, - VirtualImmediate12 { value: 0 }, - )), - comment: "load value".into(), - owning_span, - }); + let src_reg = self.value_to_register(src_val)?; + let instr_reg = self.reg_seqr.next(); - self.reg_map.insert(*instr_val, instr_reg); - Ok(()) + match byte_len { + 0 | 2..=7 => { + return Err(CompileError::Internal( + "Attempt to load {byte_len} bytes sized value.", + owning_span.unwrap_or_else(Span::dummy), + )); + } + 1 => { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::LB( + instr_reg.clone(), + src_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "load value".into(), + owning_span, + }); + } + 8.. => { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::LW( + instr_reg.clone(), + src_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "load value".into(), + owning_span, + }); + } } + + self.reg_map.insert(*instr_val, instr_reg); + Ok(()) } fn compile_mem_copy_bytes( @@ -1588,18 +1615,42 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { )) } } else { + let stored_ty = stored_val.get_type(self.context).unwrap(); + let byte_len = ir_type_size_in_bytes(self.context, &stored_ty); + let dst_reg = self.value_to_register(dst_val)?; let val_reg = self.value_to_register(stored_val)?; - self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::SW( - dst_reg, - val_reg, - VirtualImmediate12 { value: 0 }, - )), - comment: "store value".into(), - owning_span, - }); + match byte_len { + 0 | 2..=7 => { + return Err(CompileError::Internal( + "Attempt to load {byte_len} bytes sized value.", + owning_span.unwrap_or_else(Span::dummy), + )); + } + 1 => { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::SB( + dst_reg, + val_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "store value".into(), + owning_span, + }); + } + 8.. => { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + dst_reg, + val_reg, + VirtualImmediate12 { value: 0 }, + )), + comment: "store value".into(), + owning_span, + }); + } + } Ok(()) } @@ -1644,13 +1695,16 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { _otherwise => { // Get the constant into the namespace. - let entry = Entry::from_constant(self.context, constant, config_name); + let entry = Entry::from_constant(self.context, constant, config_name, None); let data_id = self.data_section.insert_data_value(entry); // Allocate a register for it, and a load instruction. let reg = self.reg_seqr.next(); self.cur_bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::LWDataId(reg.clone(), data_id.clone())), + opcode: either::Either::Left(VirtualOp::LoadDataId( + reg.clone(), + data_id.clone(), + )), comment: "literal instantiation".into(), owning_span: span, }); @@ -1663,7 +1717,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { // // Actually, no, don't. It's possible for constant values to be // reused in the IR, especially with transforms which copy blocks - // around, like inlining. The `LW`/`LWDataId` instruction above + // around, like inlining. The `LW`/`LoadDataId` instruction above // initialises that constant value but it may be in a conditional // block and not actually get evaluated for every possible // execution. So using the register later on by pulling it from @@ -1762,7 +1816,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { .data_section .insert_data_value(Entry::new_word(imm, None, None)); self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::LWDataId(reg.clone(), data_id)), + opcode: Either::Left(VirtualOp::LoadDataId(reg.clone(), data_id)), owning_span: span.clone(), comment: comment.clone(), }); diff --git a/sway-core/src/asm_generation/fuel/functions.rs b/sway-core/src/asm_generation/fuel/functions.rs index 40d71b5d1d3..c4c56557934 100644 --- a/sway-core/src/asm_generation/fuel/functions.rs +++ b/sway-core/src/asm_generation/fuel/functions.rs @@ -611,11 +611,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { fn alloc_locals( &mut self, function: Function, - ) -> ( - u64, - virtual_register::VirtualRegister, - Vec<(u64, u64, DataId)>, - ) { + ) -> (u64, virtual_register::VirtualRegister, Vec) { // If they're immutable and have a constant initialiser then they go in the data section. // // Otherwise they go in runtime allocated space, either a register or on the stack. @@ -632,6 +628,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { self.context, constant, None, + None, )); self.ptr_map.insert(*ptr, Storage::Data(data_id)); (stack_base, init_mut_vars) @@ -639,7 +636,8 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { self.ptr_map.insert(*ptr, Storage::Stack(stack_base)); let ptr_ty = ptr.get_inner_type(self.context); - let var_size = match ptr_ty.get_content(self.context) { + let var_byte_size = ir_type_size_in_bytes(self.context, &ptr_ty); + let var_word_size = match ptr_ty.get_content(self.context) { TypeContent::Uint(256) => 4, TypeContent::Unit | TypeContent::Bool @@ -658,12 +656,18 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { self.context, constant, None, + None, )); - init_mut_vars.push((stack_base, var_size, data_id)); + init_mut_vars.push(InitMutVars { + stack_base, + var_word_size, + var_byte_size, + data_id, + }); } - (stack_base + var_size, init_mut_vars) + (stack_base + var_word_size, init_mut_vars) } }, ); @@ -697,14 +701,20 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { (locals_size, locals_base_reg, init_mut_vars): ( u64, virtual_register::VirtualRegister, - Vec<(u64, u64, DataId)>, + Vec, ), ) { // Initialise that stack variables which require it. - for (var_stack_offs, var_word_size, var_data_id) in init_mut_vars { + for InitMutVars { + stack_base: var_stack_offs, + var_word_size, + var_byte_size, + data_id: var_data_id, + } in init_mut_vars + { // Load our initialiser from the data section. self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::LWDataId( + opcode: Either::Left(VirtualOp::LoadDataId( VirtualRegister::Constant(ConstantRegister::Scratch), var_data_id, )), @@ -755,15 +765,27 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { if var_word_size == 1 { // Initialise by value. - self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::SW( - dst_reg, - VirtualRegister::Constant(ConstantRegister::Scratch), - VirtualImmediate12 { value: 0 }, - )), - comment: "store initializer to local variable".to_owned(), - owning_span: None, - }); + if var_byte_size == 1 { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::SB( + dst_reg, + VirtualRegister::Constant(ConstantRegister::Scratch), + VirtualImmediate12 { value: 0 }, + )), + comment: "store initializer to local variable".to_owned(), + owning_span: None, + }); + } else { + self.cur_bytecode.push(Op { + opcode: Either::Left(VirtualOp::SW( + dst_reg, + VirtualRegister::Constant(ConstantRegister::Scratch), + VirtualImmediate12 { value: 0 }, + )), + comment: "store initializer to local variable".to_owned(), + owning_span: None, + }); + } } else { // Initialise by reference. let var_byte_size = var_word_size * 8; @@ -806,3 +828,10 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { &self.locals_ctxs.last().expect("No locals").1 } } + +struct InitMutVars { + stack_base: u64, + var_word_size: u64, + var_byte_size: u64, + data_id: DataId, +} diff --git a/sway-core/src/asm_generation/programs/abstract.rs b/sway-core/src/asm_generation/programs/abstract.rs index e1320419e10..fd57b182c48 100644 --- a/sway-core/src/asm_generation/programs/abstract.rs +++ b/sway-core/src/asm_generation/programs/abstract.rs @@ -197,7 +197,7 @@ impl AbstractProgram { // Load the data into a register for comparison. asm_buf.ops.push(AllocatedAbstractOp { - opcode: Either::Left(AllocatedOpcode::LWDataId(PROG_SELECTOR_REG, data_label)), + opcode: Either::Left(AllocatedOpcode::LoadDataId(PROG_SELECTOR_REG, data_label)), comment: "load fn selector for comparison".into(), owning_span: None, }); diff --git a/sway-core/src/asm_lang/allocated_ops.rs b/sway-core/src/asm_lang/allocated_ops.rs index 12f186b1693..1b9dfa11e8d 100644 --- a/sway-core/src/asm_lang/allocated_ops.rs +++ b/sway-core/src/asm_lang/allocated_ops.rs @@ -233,7 +233,7 @@ pub(crate) enum AllocatedOpcode { BLOB(VirtualImmediate24), DataSectionOffsetPlaceholder, DataSectionRegisterLoadPlaceholder, - LWDataId(AllocatedRegister, DataId), + LoadDataId(AllocatedRegister, DataId), Undefined, } @@ -347,7 +347,7 @@ impl AllocatedOpcode { DataSectionRegisterLoadPlaceholder => vec![&AllocatedRegister::Constant( ConstantRegister::DataSectionStart, )], - LWDataId(r1, _i) => vec![r1], + LoadDataId(r1, _i) => vec![r1], Undefined => vec![], }) .into_iter() @@ -468,7 +468,7 @@ impl fmt::Display for AllocatedOpcode { ) } DataSectionRegisterLoadPlaceholder => write!(fmtr, "lw $ds $is 1"), - LWDataId(a, b) => write!(fmtr, "lw {a} {b}"), + LoadDataId(a, b) => write!(fmtr, "load {a} {b}"), Undefined => write!(fmtr, "undefined op"), } } @@ -653,8 +653,8 @@ impl AllocatedOp { 1.into(), ) .into(), - LWDataId(a, b) => { - return Either::Left(realize_lw(a, b, data_section, offset_to_data_section)) + LoadDataId(a, b) => { + return Either::Left(realize_load(a, b, data_section, offset_to_data_section)) } Undefined => unreachable!("Sway cannot generate undefined ASM opcodes"), }]) @@ -665,25 +665,40 @@ impl AllocatedOp { /// actual bytewise offsets for use in bytecode. /// Returns one op if the type is less than one word big, but two ops if it has to construct /// a pointer and add it to $is. -fn realize_lw( +fn realize_load( dest: &AllocatedRegister, data_id: &DataId, data_section: &mut DataSection, offset_to_data_section: u64, ) -> Vec { - // all data is word-aligned right now, and `offset_to_id` returns the offset in bytes - let offset_bytes = data_section.data_id_to_offset(data_id) as u64; - let offset_words = offset_bytes / 8; - let offset = match VirtualImmediate12::new(offset_words, Span::new(" ".into(), 0, 0, None).unwrap()) { - Ok(value) => value, - Err(_) => panic!("Unable to offset into the data section more than 2^12 bits. Unsupported data section length.") - }; // if this data is larger than a word, instead of loading the data directly // into the register, we want to load a pointer to the data into the register // this appends onto the data section and mutates it by adding the pointer as a literal 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", ); + + let is_byte = data_section.is_byte(data_id).expect( + "Internal miscalculation in data section -- data id did not match up to any actual data", + ); + + // all data is word-aligned right now, and `offset_to_id` returns the offset in bytes + let offset_bytes = data_section.data_id_to_offset(data_id) as u64; + assert!( + offset_bytes % 8 == 0, + "Internal miscalculation in data section -- data offset is not aligned to a word", + ); + let offset_words = offset_bytes / 8; + + let imm = VirtualImmediate12::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.") + }; + if !has_copy_type { // load the pointer itself into the register // `offset_to_data_section` is in bytes. We want a byte @@ -694,7 +709,7 @@ fn realize_lw( data_section.append_pointer(pointer_offset_from_instruction_start); // now load the pointer we just created into the `dest`ination let mut buf = Vec::with_capacity(2); - buf.append(&mut realize_lw( + buf.append(&mut realize_load( dest, &data_id_for_pointer, data_section, @@ -710,6 +725,13 @@ fn realize_lw( .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(), diff --git a/sway-core/src/asm_lang/mod.rs b/sway-core/src/asm_lang/mod.rs index 14bae74863c..71c4e05544e 100644 --- a/sway-core/src/asm_lang/mod.rs +++ b/sway-core/src/asm_lang/mod.rs @@ -67,35 +67,6 @@ pub(crate) struct RealizedOp { } impl Op { - /// Write value in given [VirtualRegister] `value_to_write` to given memory address that is held within the - /// [VirtualRegister] `destination_address` - pub(crate) fn write_register_to_memory( - destination_address: VirtualRegister, - value_to_write: VirtualRegister, - offset: VirtualImmediate12, - span: Span, - ) -> Self { - Op { - opcode: Either::Left(VirtualOp::SW(destination_address, value_to_write, offset)), - comment: String::new(), - owning_span: Some(span), - } - } - /// Write value in given [VirtualRegister] `value_to_write` to given memory address that is held within the - /// [VirtualRegister] `destination_address`, with the provided comment. - pub(crate) fn write_register_to_memory_comment( - destination_address: VirtualRegister, - value_to_write: VirtualRegister, - offset: VirtualImmediate12, - span: Span, - comment: impl Into, - ) -> Self { - Op { - opcode: Either::Left(VirtualOp::SW(destination_address, value_to_write, offset)), - comment: comment.into(), - owning_span: Some(span), - } - } /// Moves the stack pointer by the given amount (i.e. allocates stack memory) pub(crate) fn unowned_stack_allocate_memory( size_to_allocate_in_bytes: VirtualImmediate24, @@ -148,7 +119,7 @@ impl Op { comment: impl Into, ) -> Self { Op { - opcode: Either::Left(VirtualOp::LWDataId(reg, data)), + opcode: Either::Left(VirtualOp::LoadDataId(reg, data)), comment: comment.into(), owning_span: None, } @@ -1108,7 +1079,7 @@ impl fmt::Display for VirtualOp { DataSectionRegisterLoadPlaceholder => { write!(fmtr, "data section register load placeholder") } - LWDataId(a, b) => write!(fmtr, "lw {a} {b}"), + LoadDataId(a, b) => write!(fmtr, "load {a} {b}"), Undefined => write!(fmtr, "undefined op"), } } diff --git a/sway-core/src/asm_lang/virtual_ops.rs b/sway-core/src/asm_lang/virtual_ops.rs index 912dad6daee..80d94449b02 100644 --- a/sway-core/src/asm_lang/virtual_ops.rs +++ b/sway-core/src/asm_lang/virtual_ops.rs @@ -193,11 +193,11 @@ pub(crate) enum VirtualOp { BLOB(VirtualImmediate24), DataSectionOffsetPlaceholder, DataSectionRegisterLoadPlaceholder, - // LWDataId takes a virtual register and a DataId, which points to a labeled piece + // LoadDataId takes a virtual register and a DataId, which points to a labeled piece // of data in the data section. Note that the ASM op corresponding to a LW is // subtly complex: $rB is in bytes and points to some mem address. The immediate // third argument is a _word_ offset from that byte address. - LWDataId(VirtualRegister, DataId), + LoadDataId(VirtualRegister, DataId), Undefined, } @@ -308,7 +308,7 @@ impl VirtualOp { &VirtualRegister::Constant(ConstantRegister::DataSectionStart), &VirtualRegister::Constant(ConstantRegister::InstructionStart), ], - LWDataId(r1, _i) => vec![r1], + LoadDataId(r1, _i) => vec![r1], Undefined => vec![], }) .into_iter() @@ -421,7 +421,7 @@ impl VirtualOp { DataSectionRegisterLoadPlaceholder => vec![&VirtualRegister::Constant( ConstantRegister::InstructionStart, )], - LWDataId(_r1, _i) => vec![], + LoadDataId(_r1, _i) => vec![], Undefined => vec![], }) .into_iter() @@ -531,7 +531,7 @@ impl VirtualOp { /* Non-VM Instructions */ BLOB(_imm) => vec![], - LWDataId(r1, _i) => vec![r1], + LoadDataId(r1, _i) => vec![r1], DataSectionOffsetPlaceholder => vec![], DataSectionRegisterLoadPlaceholder => vec![&VirtualRegister::Constant( ConstantRegister::DataSectionStart, @@ -943,7 +943,7 @@ impl VirtualOp { BLOB(i) => Self::BLOB(i.clone()), DataSectionOffsetPlaceholder => Self::DataSectionOffsetPlaceholder, DataSectionRegisterLoadPlaceholder => Self::DataSectionRegisterLoadPlaceholder, - LWDataId(r1, i) => Self::LWDataId(update_reg(reg_to_reg_map, r1), i.clone()), + LoadDataId(r1, i) => Self::LoadDataId(update_reg(reg_to_reg_map, r1), i.clone()), Undefined => Self::Undefined, } } @@ -1387,8 +1387,8 @@ impl VirtualOp { DataSectionRegisterLoadPlaceholder => { AllocatedOpcode::DataSectionRegisterLoadPlaceholder } - LWDataId(reg1, label) => { - AllocatedOpcode::LWDataId(map_reg(&mapping, reg1), label.clone()) + LoadDataId(reg1, label) => { + AllocatedOpcode::LoadDataId(map_reg(&mapping, reg1), label.clone()) } Undefined => AllocatedOpcode::Undefined, } diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 43b096e13fb..57d8f3df21c 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -5,11 +5,11 @@ use crate::{ engine_threading::*, language::{ ty::{self, TyConstantDecl, TyIntrinsicFunctionKind}, - CallPath, + CallPath, Literal, }, metadata::MetadataManager, semantic_analysis::*, - UnifyCheck, + TypeInfo, UnifyCheck, }; use super::{ @@ -28,7 +28,7 @@ use sway_ir::{ value::Value, Instruction, Type, }; -use sway_types::{ident::Ident, span::Spanned, Span}; +use sway_types::{ident::Ident, integer_bits::IntegerBits, span::Spanned, Span}; use sway_utils::mapped_stack::MappedStack; enum ConstEvalError { @@ -261,6 +261,13 @@ fn const_eval_typed_expr( expr: &ty::TyExpression, ) -> Result, ConstEvalError> { Ok(match &expr.expression { + ty::TyExpressionVariant::Literal(Literal::Numeric(n)) => { + let implied_lit = match lookup.engines.te().get(expr.return_type) { + TypeInfo::UnsignedInteger(IntegerBits::Eight) => Literal::U8(*n as u8), + _ => Literal::U64(*n), + }; + Some(convert_literal_to_constant(lookup.context, &implied_lit)) + } ty::TyExpressionVariant::Literal(l) => Some(convert_literal_to_constant(lookup.context, l)), ty::TyExpressionVariant::FunctionApplication { arguments, diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index 86d3b72788b..a9327a4d844 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -21,12 +21,13 @@ pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Lite // concerned. // // XXX The above isn't true for other targets. We need to improved this. - Literal::U8(n) => Constant::get_uint(context, 64, *n as u64), + // FIXME + Literal::U8(n) => Constant::get_uint(context, 8, *n as u64), Literal::U16(n) => Constant::get_uint(context, 64, *n as u64), Literal::U32(n) => Constant::get_uint(context, 64, *n as u64), Literal::U64(n) => Constant::get_uint(context, 64, *n), Literal::U256(n) => Constant::get_uint256(context, n.clone()), - Literal::Numeric(n) => Constant::get_uint(context, 64, *n), + Literal::Numeric(_) => unreachable!(), Literal::String(s) => Constant::get_string(context, s.as_str().as_bytes().to_vec()), Literal::Boolean(b) => Constant::get_bool(context, *b), Literal::B256(bs) => Constant::get_b256(context, *bs), @@ -39,12 +40,12 @@ pub(super) fn convert_literal_to_constant( ) -> Constant { match ast_literal { // All integers are `u64`. See comment above. - Literal::U8(n) => Constant::new_uint(context, 64, *n as u64), + Literal::U8(n) => Constant::new_uint(context, 8, *n as u64), Literal::U16(n) => Constant::new_uint(context, 64, *n as u64), Literal::U32(n) => Constant::new_uint(context, 64, *n as u64), Literal::U64(n) => Constant::new_uint(context, 64, *n), Literal::U256(n) => Constant::new_uint256(context, n.clone()), - Literal::Numeric(n) => Constant::new_uint(context, 64, *n), + Literal::Numeric(_) => unreachable!(), Literal::String(s) => Constant::new_string(context, s.as_str().as_bytes().to_vec()), Literal::Boolean(b) => Constant::new_bool(context, *b), Literal::B256(bs) => Constant::new_b256(context, *bs), @@ -102,8 +103,11 @@ fn convert_resolved_type( Ok(match ast_type { // All integers are `u64`, see comment in convert_literal_to_value() above. TypeInfo::UnsignedInteger(IntegerBits::V256) => Type::get_uint256(context), - TypeInfo::UnsignedInteger(_) => Type::get_uint64(context), - TypeInfo::Numeric => Type::get_uint64(context), + TypeInfo::UnsignedInteger(IntegerBits::Eight) => Type::get_uint8(context), + TypeInfo::UnsignedInteger(IntegerBits::Sixteen) + | TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) + | TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) + | TypeInfo::Numeric => Type::get_uint64(context), TypeInfo::Boolean => Type::get_bool(context), TypeInfo::B256 => Type::get_b256(context), TypeInfo::Str(n) => Type::new_string(context, n.val() as u64), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 430ec00d5eb..a906888d8fc 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -25,6 +25,7 @@ use sway_ir::{Context, *}; use sway_types::{ constants, ident::Ident, + integer_bits::IntegerBits, span::{Span, Spanned}, state::StateIndex, Named, @@ -260,6 +261,14 @@ impl<'eng> FnCompiler<'eng> { ) -> Result { let span_md_idx = md_mgr.span_to_md(context, &ast_expr.span); match &ast_expr.expression { + ty::TyExpressionVariant::Literal(Literal::Numeric(n)) => { + let implied_lit = match self.engines.te().get(ast_expr.return_type) { + TypeInfo::UnsignedInteger(IntegerBits::Eight) => Literal::U8(*n as u8), + _ => Literal::U64(*n), + }; + Ok(convert_literal_to_value(context, &implied_lit) + .add_metadatum(context, span_md_idx)) + } ty::TyExpressionVariant::Literal(l) => { Ok(convert_literal_to_value(context, l).add_metadatum(context, span_md_idx)) } @@ -506,11 +515,8 @@ impl<'eng> FnCompiler<'eng> { &exp.span, )?; self.compile_expression_to_value(context, md_mgr, exp)?; - Ok(Constant::get_uint( - context, - 64, - ir_type_size_in_bytes(context, &ir_type), - )) + let size = ir_type_size_in_bytes(context, &ir_type); + Ok(Constant::get_uint(context, 64, size)) } Intrinsic::SizeOfType => { let targ = type_arguments[0].clone(); @@ -620,7 +626,11 @@ impl<'eng> FnCompiler<'eng> { .get_unaliased(target_type.type_id) .is_copy_type() { - Ok(gtf_reg) + Ok(self + .current_block + .ins(context) + .bitcast(gtf_reg, target_ir_type) + .add_metadatum(context, span_md_idx)) } else { let ptr_ty = Type::new_ptr(context, target_ir_type); Ok(self @@ -2109,12 +2119,13 @@ impl<'eng> FnCompiler<'eng> { } insert_values.push(insert_val); - field_types.push(convert_resolved_typeid_no_span( + let field_type = convert_resolved_typeid_no_span( self.engines.te(), self.engines.de(), context, &struct_field.value.return_type, - )?); + )?; + field_types.push(field_type); } // Create the struct. diff --git a/sway-ir/src/irtype.rs b/sway-ir/src/irtype.rs index b463d5c1ba1..4f915863975 100644 --- a/sway-ir/src/irtype.rs +++ b/sway-ir/src/irtype.rs @@ -384,24 +384,38 @@ impl Type { pub fn size_in_bytes(&self, context: &Context) -> u64 { match self.get_content(context) { - TypeContent::Unit | TypeContent::Bool | TypeContent::Pointer(_) => 8, - TypeContent::Uint(bits) => (*bits as u64) / 8, + TypeContent::Uint(8) | TypeContent::Bool | TypeContent::Unit => 1, + // All integers larger than a byte are words since FuelVM only has memory operations on those two units + TypeContent::Uint(16) + | TypeContent::Uint(32) + | TypeContent::Uint(64) + | TypeContent::Pointer(_) => 8, + TypeContent::Uint(256) => 32, + TypeContent::Uint(_) => unreachable!(), TypeContent::Slice => 16, TypeContent::B256 => 32, TypeContent::String(n) => super::size_bytes_round_up_to_word_alignment!(*n), TypeContent::Array(el_ty, cnt) => cnt * el_ty.size_in_bytes(context), TypeContent::Struct(field_tys) => { - // Sum up all the field sizes. + // Sum up all the field sizes, aligned to 8 bytes. field_tys .iter() - .map(|field_ty| field_ty.size_in_bytes(context)) + .map(|field_ty| { + super::size_bytes_round_up_to_word_alignment!( + field_ty.size_in_bytes(context) + ) + }) .sum() } TypeContent::Union(field_tys) => { // Find the max size for field sizes. field_tys .iter() - .map(|field_ty| field_ty.size_in_bytes(context)) + .map(|field_ty| { + super::size_bytes_round_up_to_word_alignment!( + field_ty.size_in_bytes(context) + ) + }) .max() .unwrap_or(0) } diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 8e614d6f148..eaea9556242 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -421,18 +421,29 @@ impl<'a, 'eng> InstructionVerifier<'a, 'eng> { fn verify_binary_op( &self, - _op: &BinaryOpKind, + op: &BinaryOpKind, arg1: &Value, arg2: &Value, ) -> Result<(), IrError> { + use BinaryOpKind::*; let arg1_ty = arg1 .get_type(self.context) .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?; let arg2_ty = arg2 .get_type(self.context) .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?; - if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) { - return Err(IrError::VerifyBinaryOpIncorrectArgType); + + match op { + Add | Sub | Mul | Div | And | Or | Xor | Mod => { + if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) { + return Err(IrError::VerifyBinaryOpIncorrectArgType); + } + } + Rsh | Lsh => { + if !arg1_ty.is_uint(self.context) || !arg2_ty.is_uint64(self.context) { + return Err(IrError::VerifyBinaryOpIncorrectArgType); + } + } } Ok(()) diff --git a/sway-lib-core/src/primitive_conversions.sw b/sway-lib-core/src/primitive_conversions.sw index c6e435cefa1..290da6229ad 100644 --- a/sway-lib-core/src/primitive_conversions.sw +++ b/sway-lib-core/src/primitive_conversions.sw @@ -6,35 +6,35 @@ impl u64 { asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, output: output, r1) { and r1 input off; - sw output r1 i0; + sb output r1 i0; srl r1 input i; and r1 r1 off; - sw output r1 i1; + sb output r1 i1; srl r1 input j; and r1 r1 off; - sw output r1 i2; + sb output r1 i2; srl r1 input k; and r1 r1 off; - sw output r1 i3; + sb output r1 i3; srl r1 input l; and r1 r1 off; - sw output r1 i4; + sb output r1 i4; srl r1 input m; and r1 r1 off; - sw output r1 i5; + sb output r1 i5; srl r1 input n; and r1 r1 off; - sw output r1 i6; + sb output r1 i6; srl r1 input o; and r1 r1 off; - sw output r1 i7; + sb output r1 i7; output: [u8; 8] } @@ -75,35 +75,35 @@ impl u64 { asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, l: 0x20, m: 0x28, n: 0x30, o: 0x38, output: output, r1) { and r1 input off; - sw output r1 i7; + sb output r1 i7; srl r1 input i; and r1 r1 off; - sw output r1 i6; + sb output r1 i6; srl r1 input j; and r1 r1 off; - sw output r1 i5; + sb output r1 i5; srl r1 input k; and r1 r1 off; - sw output r1 i4; + sb output r1 i4; srl r1 input l; and r1 r1 off; - sw output r1 i3; + sb output r1 i3; srl r1 input m; and r1 r1 off; - sw output r1 i2; + sb output r1 i2; srl r1 input n; and r1 r1 off; - sw output r1 i1; + sb output r1 i1; srl r1 input o; and r1 r1 off; - sw output r1 i0; + sb output r1 i0; output: [u8; 8] } @@ -152,19 +152,19 @@ impl u32 { asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, output: output, r1) { and r1 input off; - sw output r1 i0; + sb output r1 i0; srl r1 input i; and r1 r1 off; - sw output r1 i1; + sb output r1 i1; srl r1 input j; and r1 r1 off; - sw output r1 i2; + sb output r1 i2; srl r1 input k; and r1 r1 off; - sw output r1 i3; + sb output r1 i3; output: [u8; 4] } @@ -188,18 +188,18 @@ impl u32 { asm(input: self, off: 0xFF, i: 0x8, j: 0x10, k: 0x18, output: output, r1) { srl r1 input k; and r1 r1 off; - sw output r1 i0; + sb output r1 i0; srl r1 input j; and r1 r1 off; - sw output r1 i1; + sb output r1 i1; srl r1 input i; and r1 r1 off; - sw output r1 i2; + sb output r1 i2; and r1 input off; - sw output r1 i3; + sb output r1 i3; output: [u8; 4] } @@ -236,11 +236,11 @@ impl u16 { asm(input: self, off: 0xFF, i: 0x8, output: output, r1) { and r1 input off; - sw output r1 i0; + sb output r1 i0; srl r1 input i; and r1 r1 off; - sw output r1 i1; + sb output r1 i1; output: [u8; 2] } @@ -259,10 +259,10 @@ impl u16 { asm(input: self, off: 0xFF, i: 0x8, output: output, r1) { srl r1 input i; - sw output r1 i0; + sb output r1 i0; and r1 input off; - sw output r1 i1; + sb output r1 i1; output: [u8; 2] } diff --git a/sway-lib-core/src/raw_ptr.sw b/sway-lib-core/src/raw_ptr.sw index 8a2431a212f..b387793a451 100644 --- a/sway-lib-core/src/raw_ptr.sw +++ b/sway-lib-core/src/raw_ptr.sw @@ -21,6 +21,11 @@ impl raw_ptr { pub fn read(self) -> T { if __is_reference_type::() { asm(ptr: self) { ptr: T } + } else if __eq(__size_of::(), 1) { + asm(ptr: self, val) { + lb val ptr i0; + val: T + } } else { asm(ptr: self, val) { lw val ptr i0; @@ -43,6 +48,10 @@ impl raw_ptr { asm(dst: self, src: val, count: __size_of_val(val)) { mcp dst src count; }; + } else if __eq(__size_of::(), 1) { + asm(ptr: self, val: val) { + sb ptr val i0; + }; } else { asm(ptr: self, val: val) { sw ptr val i0; diff --git a/sway-lib-std/src/hash.sw b/sway-lib-std/src/hash.sw index c68f89a5673..7b8defff093 100644 --- a/sway-lib-std/src/hash.sw +++ b/sway-lib-std/src/hash.sw @@ -5,13 +5,24 @@ library; pub fn sha256(param: T) -> b256 { let mut result_buffer: b256 = b256::min(); if !__is_reference_type::() { - asm(buffer, ptr: param, eight_bytes: 8, hash: result_buffer) { - move buffer sp; // Make `buffer` point to the current top of the stack - cfei i8; // Grow stack by 1 word - sw buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" - s256 hash buffer eight_bytes; // Hash the next eight bytes starting from "buffer" into "hash" - cfsi i8; // Shrink stack by 1 word - hash: b256 // Return + if __eq(__size_of::(), 1) { + asm(buffer, ptr: param, one_byte: 1, hash: result_buffer) { + move buffer sp; // Make `buffer` point to the current top of the stack + cfei i8; // Grow stack by 1 word + sb buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" + s256 hash buffer one_byte; // Hash the next byte starting from "buffer" into "hash" + cfsi i8; // Shrink stack by 1 word + hash: b256 // Return + } + } else { + asm(buffer, ptr: param, eight_bytes: 8, hash: result_buffer) { + move buffer sp; // Make `buffer` point to the current top of the stack + cfei i8; // Grow stack by 1 word + sw buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" + s256 hash buffer eight_bytes; // Hash the next eight bytes starting from "buffer" into "hash" + cfsi i8; // Shrink stack by 1 word + hash: b256 // Return + } } } else { let size = if __is_str_type::() { @@ -30,13 +41,24 @@ pub fn sha256(param: T) -> b256 { pub fn keccak256(param: T) -> b256 { let mut result_buffer: b256 = b256::min(); if !__is_reference_type::() { - asm(buffer, ptr: param, eight_bytes: 8, hash: result_buffer) { - move buffer sp; // Make `buffer` point to the current top of the stack - cfei i8; // Grow stack by 1 word - sw buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" - k256 hash buffer eight_bytes; // Hash the next eight bytes starting from "buffer" into "hash" - cfsi i8; // Shrink stack by 1 word - hash: b256 // Return + if __eq(__size_of::(), 1) { + asm(buffer, ptr: param, one_byte: 1, hash: result_buffer) { + move buffer sp; // Make `buffer` point to the current top of the stack + cfei i8; // Grow stack by 1 word + sb buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" + k256 hash buffer one_byte; // Hash the next byte starting from "buffer" into "hash" + cfsi i8; // Shrink stack by 1 word + hash: b256 // Return + } + } else { + asm(buffer, ptr: param, eight_bytes: 8, hash: result_buffer) { + move buffer sp; // Make `buffer` point to the current top of the stack + cfei i8; // Grow stack by 1 word + sw buffer ptr i0; // Save value in register at "ptr" to memory at "buffer" + k256 hash buffer eight_bytes; // Hash the next eight bytes starting from "buffer" into "hash" + cfsi i8; // Shrink stack by 1 word + hash: b256 // Return + } } } else { let size = if __is_str_type::() { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json index a3c3a3e18f9..84abc6cbf40 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json @@ -7,7 +7,7 @@ "typeArguments": null }, "name": "C0", - "offset": 4212 + "offset": 4476 }, { "configurableType": { @@ -34,7 +34,7 @@ "typeArguments": [] }, "name": "C3", - "offset": 4268 + "offset": 4500 }, { "configurableType": { @@ -43,7 +43,7 @@ "typeArguments": [] }, "name": "C4", - "offset": 4284 + "offset": 4300 }, { "configurableType": { @@ -52,7 +52,7 @@ "typeArguments": [] }, "name": "C5", - "offset": 4300 + "offset": 4516 }, { "configurableType": { @@ -61,7 +61,7 @@ "typeArguments": null }, "name": "C6", - "offset": 4316 + "offset": 4332 }, { "configurableType": { @@ -70,7 +70,7 @@ "typeArguments": null }, "name": "C7", - "offset": 4332 + "offset": 4348 }, { "configurableType": { @@ -79,7 +79,7 @@ "typeArguments": null }, "name": "C9", - "offset": 4380 + "offset": 4396 } ], "functions": [ diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/src/main.sw index 086d78a94cf..4cfeb6a1996 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/src/main.sw @@ -39,7 +39,7 @@ fn main() -> bool { assert(size_of::() == 8); assert(size_of::() == 8); assert(size_of::() == 8); - assert(size_of::() == 8); + assert(size_of::() == 1); assert(size_of::() == 32); assert(size_of::() == 16); assert(size_of::<[u16; 3]>() == 24); @@ -48,7 +48,7 @@ fn main() -> bool { assert(size_of_val(a) == 8); assert(size_of_val(b) == 8); assert(size_of_val(c) == 8); - assert(size_of_val(d) == 8); + assert(size_of_val(d) == 1); assert(size_of_val(e) == 32); assert(size_of_val(f) == 16); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_ptr/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_ptr/src/main.sw index b1e5ef2ec65..9749ad5f62b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_ptr/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_ptr/src/main.sw @@ -45,17 +45,17 @@ fn main() -> bool { assert(foo.uwu == 42); // Read fields of the struct - let uwu_ptr = buf_ptr.add::(1); + let uwu_ptr = buf_ptr.add_uint_offset(8); // struct fields are aligned to words let uwu: u64 = uwu_ptr.read(); assert(uwu == 42); - let boo_ptr = uwu_ptr.sub::(1); + let boo_ptr = uwu_ptr.sub_uint_offset(8); let boo: bool = boo_ptr.read(); assert(boo == true); // Write values into a buffer let buf_ptr = alloc::(2); buf_ptr.write(true); - buf_ptr.add::(1).write(42); + buf_ptr.add_uint_offset(8).write(42); let foo: TestStruct = buf_ptr.read(); assert(foo.boo == true); assert(foo.uwu == 42); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_slice/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_slice/test.toml index 4bb03e6173c..a301445f4dd 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_slice/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/raw_slice/test.toml @@ -1,4 +1,4 @@ category = "run" -expected_result = { action = "return_data", value = "0000000000000001000000000000002a" } +expected_result = { action = "return_data", value = "0100000000000000000000000000002a" } validate_abi = true expected_warnings = 2