From a5d5a2673aeb1d26f75532b4b46a1c4d49b00901 Mon Sep 17 00:00:00 2001 From: Vaivaswatha N Date: Tue, 16 Aug 2022 19:49:12 +0530 Subject: [PATCH] Intrinsics for the VM's storage opcodes. (#2508) * Add storage load/store word intrinsics * Storage load/store quad intrinsics * Add asm gen test * Update storage.sw and add exhaustive tests --- sway-ast/src/intrinsics.rs | 12 + sway-core/src/asm_generation/from_ir.rs | 66 ++- sway-core/src/ir_generation/function.rs | 110 +++- .../ast_node/expression/intrinsic_function.rs | 113 ++++ .../storage_store_load_intrinsics.asm | 179 +++++++ .../storage_store_load_intrinsics.ir | 486 ++++++++++++++++++ sway-ir/src/instruction.rs | 9 +- sway-ir/src/verify.rs | 1 + sway-lib-std/src/storage.sw | 38 +- .../call_basic_storage/src/main.sw | 17 +- .../test_abis/basic_storage_abi/Forc.lock | 6 + .../test_abis/basic_storage_abi/src/main.sw | 20 + .../basic_storage/json_abi_oracle.json | 155 ++++++ .../test_contracts/basic_storage/src/main.sw | 135 ++++- 14 files changed, 1289 insertions(+), 58 deletions(-) create mode 100644 sway-core/tests/ir_to_asm/storage_store_load_intrinsics.asm create mode 100644 sway-core/tests/ir_to_asm/storage_store_load_intrinsics.ir diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index f41774a6e05..9429df393e6 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -9,6 +9,10 @@ pub enum Intrinsic { Eq, Gtf, AddrOf, + StateLoadWord, + StateStoreWord, + StateLoadQuad, + StateStoreQuad, } impl fmt::Display for Intrinsic { @@ -21,6 +25,10 @@ impl fmt::Display for Intrinsic { Intrinsic::Eq => "eq", Intrinsic::Gtf => "gtf", Intrinsic::AddrOf => "addr_of", + Intrinsic::StateLoadWord => "state_load_word", + Intrinsic::StateStoreWord => "state_store_word", + Intrinsic::StateLoadQuad => "state_load_quad", + Intrinsic::StateStoreQuad => "state_store_quad", }; write!(f, "{}", s) } @@ -37,6 +45,10 @@ impl Intrinsic { "__eq" => Eq, "__gtf" => Gtf, "__addr_of" => AddrOf, + "__state_load_word" => StateLoadWord, + "__state_store_word" => StateStoreWord, + "__state_load_quad" => StateLoadQuad, + "__state_store_quad" => StateStoreQuad, _ => return None, }) } diff --git a/sway-core/src/asm_generation/from_ir.rs b/sway-core/src/asm_generation/from_ir.rs index e1ebd1c5922..8482f40fbca 100644 --- a/sway-core/src/asm_generation/from_ir.rs +++ b/sway-core/src/asm_generation/from_ir.rs @@ -1598,6 +1598,7 @@ impl<'ir> AsmBuilder<'ir> { key.get_stripped_ptr_type(self.context), Some(Type::B256) )); + let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); let key_ptr = self.resolve_ptr(key); if key_ptr.value.is_none() { @@ -1609,39 +1610,50 @@ impl<'ir> AsmBuilder<'ir> { assert!(offset == 0); assert!(ptr_ty.get_type(self.context).eq(self.context, &Type::B256)); - // Expect ptr_ty here to also be b256 and offset to be whatever... - let val_ptr = self.resolve_ptr(val); - if val_ptr.value.is_none() { - return val_ptr.map(|_| ()); - } - let (val_ptr, ptr_ty, offset) = val_ptr.value.unwrap(); - - // Expect the ptr_ty for val to also be B256 - assert!(ptr_ty.get_type(self.context).eq(self.context, &Type::B256)); + let val_reg = if matches!( + &self.context.values[val.0].value, + ValueDatum::Instruction(Instruction::IntToPtr(..)) + ) { + match self.reg_map.get(val) { + Some(vreg) => vreg.clone(), + None => unreachable!("int_to_ptr instruction doesn't have vreg mapped"), + } + } else { + // Expect ptr_ty here to also be b256 and offset to be whatever... + let val_ptr = self.resolve_ptr(val); + if val_ptr.value.is_none() { + return val_ptr.map(|_| ()); + } + let (val_ptr, ptr_ty, offset) = val_ptr.value.unwrap(); + // Expect the ptr_ty for val to also be B256 + assert!(ptr_ty.get_type(self.context).eq(self.context, &Type::B256)); + match self.ptr_map.get(&val_ptr) { + Some(Storage::Stack(val_offset)) => { + let base_reg = self.stack_base_reg.as_ref().unwrap().clone(); + let val_offset_in_bytes = val_offset * 8 + offset * 32; + self.offset_reg(&base_reg, val_offset_in_bytes, owning_span.clone()) + } + _ => unreachable!("Unexpected storage locations for key and val"), + } + }; - let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); - match (self.ptr_map.get(&val_ptr), self.ptr_map.get(&key_ptr)) { - (Some(Storage::Stack(val_offset)), Some(Storage::Stack(key_offset))) => { + let key_reg = match self.ptr_map.get(&key_ptr) { + Some(Storage::Stack(key_offset)) => { let base_reg = self.stack_base_reg.as_ref().unwrap().clone(); - let val_offset_in_bytes = val_offset * 8 + offset * 32; let key_offset_in_bytes = key_offset * 8; - - let val_reg = self.offset_reg(&base_reg, val_offset_in_bytes, owning_span.clone()); - - let key_reg = self.offset_reg(&base_reg, key_offset_in_bytes, owning_span.clone()); - - self.bytecode.push(Op { - opcode: Either::Left(match access_type { - StateAccessType::Read => VirtualOp::SRWQ(val_reg, key_reg), - StateAccessType::Write => VirtualOp::SWWQ(key_reg, val_reg), - }), - comment: "quad word state access".into(), - owning_span, - }); + self.offset_reg(&base_reg, key_offset_in_bytes, owning_span.clone()) } _ => unreachable!("Unexpected storage locations for key and val"), - } + }; + self.bytecode.push(Op { + opcode: Either::Left(match access_type { + StateAccessType::Read => VirtualOp::SRWQ(val_reg, key_reg), + StateAccessType::Write => VirtualOp::SWWQ(key_reg, val_reg), + }), + comment: "quad word state access".into(), + owning_span, + }); ok((), Vec::new(), Vec::new()) } diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 7e3298ec365..31f4ef19b21 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -8,14 +8,14 @@ use super::{ use crate::{ asm_generation::from_ir::ir_type_size_in_bytes, constants, - error::CompileError, + error::{CompileError, Hint}, ir_generation::const_eval::{ compile_constant_expression, compile_constant_expression_to_constant, }, metadata::MetadataManager, parse_tree::{AsmOp, AsmRegister, LazyOp, Literal}, semantic_analysis::*, - type_system::{look_up_type_id, resolve_type, TypeId, TypeInfo}, + type_system::{look_up_type_id, resolve_type, IntegerBits, TypeId, TypeInfo}, }; use sway_ast::intrinsics::Intrinsic; use sway_ir::{Context, *}; @@ -391,6 +391,41 @@ impl FnCompiler { }: TypedIntrinsicFunctionKind, span: Span, ) -> Result { + fn store_key_in_local_mem( + compiler: &mut FnCompiler, + context: &mut Context, + value: Value, + span_md_idx: Option, + ) -> Result { + // New name for the key + let key_name = "key_for_storage".to_string(); + let alias_key_name = compiler.lexical_map.insert(key_name.as_str().to_owned()); + + // Local pointer for the key + let key_ptr = compiler + .function + .new_local_ptr(context, alias_key_name, Type::B256, true, None) + .map_err(|ir_error| { + CompileError::InternalOwned(ir_error.to_string(), Span::dummy()) + })?; + + // Convert the key pointer to a value using get_ptr + let key_ptr_ty = *key_ptr.get_type(context); + let key_ptr_val = compiler + .current_block + .ins(context) + .get_ptr(key_ptr, key_ptr_ty, 0) + .add_metadatum(context, span_md_idx); + + // Store the value to the key pointer value + compiler + .current_block + .ins(context) + .store(key_ptr_val, value) + .add_metadatum(context, span_md_idx); + Ok(key_ptr_val) + } + // We safely index into arguments and type_arguments arrays below // because the type-checker ensures that the arguments are all there. match kind { @@ -497,6 +532,77 @@ impl FnCompiler { .addr_of(value) .add_metadatum(context, span_md_idx)) } + Intrinsic::StateLoadWord => { + let exp = arguments[0].clone(); + let value = self.compile_expression(context, md_mgr, exp)?; + let span_md_idx = md_mgr.span_to_md(context, &span); + let key_ptr_val = store_key_in_local_mem(self, context, value, span_md_idx)?; + Ok(self + .current_block + .ins(context) + .state_load_word(key_ptr_val) + .add_metadatum(context, span_md_idx)) + } + Intrinsic::StateStoreWord => { + let key_exp = arguments[0].clone(); + let val_exp = arguments[1].clone(); + // Validate that the val_exp is of the right type. We couldn't do it + // earlier during type checking as the type arguments may not have been resolved. + let val_ty = resolve_type(val_exp.return_type, &span).unwrap(); + if !val_ty.is_copy_type() { + return Err(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span, + hint: Hint::new("This argument must be a copy type".to_string()), + }); + } + let key_value = self.compile_expression(context, md_mgr, key_exp)?; + let val_value = self.compile_expression(context, md_mgr, val_exp)?; + let span_md_idx = md_mgr.span_to_md(context, &span); + let key_ptr_val = store_key_in_local_mem(self, context, key_value, span_md_idx)?; + Ok(self + .current_block + .ins(context) + .state_store_word(val_value, key_ptr_val) + .add_metadatum(context, span_md_idx)) + } + Intrinsic::StateLoadQuad | Intrinsic::StateStoreQuad => { + let key_exp = arguments[0].clone(); + let val_exp = arguments[1].clone(); + // Validate that the val_exp is of the right type. We couldn't do it + // earlier during type checking as the type arguments may not have been resolved. + let val_ty = resolve_type(val_exp.return_type, &span).unwrap(); + if val_ty != TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) { + return Err(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span, + hint: Hint::new("This argument must be u64".to_string()), + }); + } + let key_value = self.compile_expression(context, md_mgr, key_exp)?; + let val_value = self.compile_expression(context, md_mgr, val_exp)?; + let span_md_idx = md_mgr.span_to_md(context, &span); + let key_ptr_val = store_key_in_local_mem(self, context, key_value, span_md_idx)?; + // For quad word, the IR instructions take in a pointer rather than a raw u64. + let val_ptr = self + .current_block + .ins(context) + .int_to_ptr(val_value, Type::B256) + .add_metadatum(context, span_md_idx); + match kind { + Intrinsic::StateLoadQuad => Ok(self + .current_block + .ins(context) + .state_load_quad_word(val_ptr, key_ptr_val) + .add_metadatum(context, span_md_idx)), + Intrinsic::StateStoreQuad => Ok(self + .current_block + .ins(context) + .state_store_quad_word(val_ptr, key_ptr_val) + .add_metadatum(context, span_md_idx)), + _ => unreachable!(), + } + } } } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 7bb0c7b4326..e977389c0b1 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -375,6 +375,119 @@ impl TypedIntrinsicFunctionKind { let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); (intrinsic_function, return_type) } + Intrinsic::StateLoadWord => { + if arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + let exp = check!( + TypedExpression::type_check(ctx, arguments[0].clone()), + return err(warnings, errors), + warnings, + errors + ); + let key_ty = resolve_type(exp.return_type, &span).unwrap(); + if key_ty != TypeInfo::B256 { + errors.push(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span, + hint: Hint::new( + "Argument type must be B256, a key into the state storage".to_string(), + ), + }); + return err(warnings, errors); + } + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![exp], + type_arguments: vec![], + span, + }; + let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); + (intrinsic_function, return_type) + } + Intrinsic::StateStoreWord | Intrinsic::StateLoadQuad | Intrinsic::StateStoreQuad => { + if arguments.len() != 2 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 2, + span, + }); + return err(warnings, errors); + } + if type_arguments.len() > 1 { + errors.push(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let mut ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + let key_exp = check!( + TypedExpression::type_check(ctx.by_ref(), arguments[0].clone()), + return err(warnings, errors), + warnings, + errors + ); + let key_ty = resolve_type(key_exp.return_type, &span).unwrap(); + if key_ty != TypeInfo::B256 { + errors.push(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span, + hint: Hint::new( + "Argument type must be B256, a key into the state storage".to_string(), + ), + }); + return err(warnings, errors); + } + let mut ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + let val_exp = check!( + TypedExpression::type_check(ctx.by_ref(), arguments[1].clone()), + return err(warnings, errors), + warnings, + errors + ); + let type_argument = type_arguments.get(0).map(|targ| { + let mut ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + let type_id = check!( + ctx.resolve_type_with_self( + insert_type(resolve_type(targ.type_id, &targ.span).unwrap()), + &targ.span, + EnforceTypeArguments::Yes, + None + ), + insert_type(TypeInfo::ErrorRecovery), + warnings, + errors, + ); + TypeArgument { + type_id, + span: span.clone(), + } + }); + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![key_exp, val_exp], + type_arguments: type_argument.map_or(vec![], |ta| vec![ta]), + span, + }; + let return_type = insert_type(TypeInfo::Tuple(vec![])); + (intrinsic_function, return_type) + } }; ok((intrinsic_function, return_type), warnings, errors) } diff --git a/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.asm b/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.asm new file mode 100644 index 00000000000..9416e17bf47 --- /dev/null +++ b/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.asm @@ -0,0 +1,179 @@ +.program: +ji i4 +noop +DATA_SECTION_OFFSET[0..32] +DATA_SECTION_OFFSET[32..64] +lw $ds $is 1 +add $$ds $$ds $is +lw $r1 $fp i73 ; load input function selector +lw $r0 data_15 ; load fn selector for comparison +eq $r0 $r1 $r0 ; function selector comparison +jnzi $r0 i11 ; jump to selected function +rvrt $zero ; revert if no selectors matched +move $r4 $sp ; save locals base register +cfei i288 ; allocate 288 bytes for all locals +lw $r0 data_0 ; literal instantiation +addi $r1 $r4 i240 ; get offset reg for get_ptr +addi $r1 $r4 i240 ; get store offset +mcpi $r1 $r0 i32 ; store value +lw $r0 data_1 ; literal instantiation +eq $r0 $r0 $zero ; asm block +jnzi $r0 i22 +ji i32 +addi $r0 $r4 i240 ; get offset reg for get_ptr +addi $r3 $r4 i240 ; load address +lw $r1 data_2 ; literal instantiation +lw $r0 data_3 ; literal instantiation +move $r2 $sp ; asm block +cfei i8 ; asm block +sw $r2 $r1 i0 ; asm block +s256 $r3 $r2 $r0 ; asm block +cfsi i8 ; asm block +ji i41 +addi $r0 $r4 i272 ; get offset reg for get_ptr +lw $r0 data_3 ; literal instantiation +sw $r4 $r0 i34 ; store value +addi $r0 $r4 i240 ; get offset reg for get_ptr +addi $r3 $r4 i240 ; load address +addi $r0 $r4 i272 ; get offset reg for get_ptr +lw $r1 $r4 i34 ; load value +lw $r0 data_2 ; literal instantiation +s256 $r3 $r0 $r1 ; asm block +addi $r0 $r4 i0 ; get offset reg for get_ptr +addi $r0 $r4 i0 ; get store offset +mcpi $r0 $r3 i32 ; store value +addi $r0 $r4 i280 ; get offset reg for get_ptr +lw $r0 data_4 ; literal instantiation +sw $r4 $r0 i35 ; store value +addi $r0 $r4 i0 ; get offset reg for get_ptr +addi $r2 $r4 i0 ; load address +addi $r0 $r4 i280 ; get offset reg for get_ptr +lw $r1 $r4 i35 ; load value +addi $r0 $r4 i32 ; get offset reg for get_ptr +addi $r0 $r4 i32 ; get store offset +mcpi $r0 $r2 i32 ; store value +addi $r0 $r4 i32 ; get offset +sww $r0 $r1 ; single word state access +addi $r0 $r4 i0 ; get offset reg for get_ptr +addi $r1 $r4 i0 ; load address +addi $r0 $r4 i64 ; get offset reg for get_ptr +addi $r0 $r4 i64 ; get store offset +mcpi $r0 $r1 i32 ; store value +addi $r0 $r4 i64 ; get offset +srw $r1 $r0 ; single word state access +addi $r0 $r4 i280 ; get offset reg for get_ptr +lw $r0 $r4 i35 ; load value +eq $r0 $r1 $r0 +eq $r0 $r0 $zero ; asm block +jnzi $r0 i69 +ji i73 +lw $r0 data_5 ; literal instantiation +rvrt $r0 ; asm block +lw $r0 data_5 ; literal instantiation +ji i74 +lw $r0 data_5 ; literal instantiation +lw $r0 data_5 ; literal instantiation +addi $r0 $r4 i160 ; get offset reg for get_ptr +move $r1 $sp ; save register for temporary stack value +cfei i32 ; allocate 32 bytes for temporary struct +lw $r0 data_6 ; literal instantiation for aggregate field +sw $r1 $r0 i0 ; initialise aggregate field +lw $r0 data_7 ; literal instantiation for aggregate field +sw $r1 $r0 i1 ; initialise aggregate field +lw $r0 data_8 ; literal instantiation for aggregate field +sw $r1 $r0 i2 ; initialise aggregate field +lw $r0 data_9 ; literal instantiation for aggregate field +sw $r1 $r0 i3 ; initialise aggregate field +addi $r0 $r4 i160 ; get store offset +mcpi $r0 $r1 i32 ; store value +addi $r0 $r4 i200 ; get offset reg for get_ptr +move $r1 $sp ; save register for temporary stack value +cfei i32 ; allocate 32 bytes for temporary struct +lw $r0 data_10 ; literal instantiation for aggregate field +sw $r1 $r0 i0 ; initialise aggregate field +lw $r0 data_11 ; literal instantiation for aggregate field +sw $r1 $r0 i1 ; initialise aggregate field +lw $r0 data_12 ; literal instantiation for aggregate field +sw $r1 $r0 i2 ; initialise aggregate field +lw $r0 data_13 ; literal instantiation for aggregate field +sw $r1 $r0 i3 ; initialise aggregate field +addi $r0 $r4 i200 ; get store offset +mcpi $r0 $r1 i32 ; store value +addi $r1 $r4 i160 ; get offset reg for get_ptr +addi $r0 $r4 i192 ; get offset reg for get_ptr +sw $r4 $r1 i24 ; store value +addi $r1 $r4 i200 ; get offset reg for get_ptr +addi $r0 $r4 i232 ; get offset reg for get_ptr +sw $r4 $r1 i29 ; store value +addi $r0 $r4 i0 ; get offset reg for get_ptr +addi $r2 $r4 i0 ; load address +addi $r0 $r4 i192 ; get offset reg for get_ptr +lw $r1 $r4 i24 ; load value +addi $r0 $r4 i96 ; get offset reg for get_ptr +addi $r0 $r4 i96 ; get store offset +mcpi $r0 $r2 i32 ; store value +addi $r0 $r4 i96 ; get offset +swwq $r0 $r1 ; quad word state access +addi $r0 $r4 i0 ; get offset reg for get_ptr +addi $r2 $r4 i0 ; load address +addi $r0 $r4 i232 ; get offset reg for get_ptr +lw $r1 $r4 i29 ; load value +addi $r0 $r4 i128 ; get offset reg for get_ptr +addi $r0 $r4 i128 ; get store offset +mcpi $r0 $r2 i32 ; store value +addi $r0 $r4 i128 ; get offset +srwq $r1 $r0 ; quad word state access +addi $r0 $r4 i160 ; get offset reg for get_ptr +lw $r1 $r0 i0 ; extract_value @ 0 +addi $r0 $r4 i200 ; get offset reg for get_ptr +lw $r0 $r0 i0 ; extract_value @ 0 +eq $r0 $r1 $r0 +jnzi $r0 i132 +ji i137 +addi $r0 $r4 i160 ; get offset reg for get_ptr +lw $r1 $r0 i1 ; extract_value @ 1 +addi $r0 $r4 i200 ; get offset reg for get_ptr +lw $r0 $r0 i1 ; extract_value @ 1 +eq $r0 $r1 $r0 +jnzi $r0 i139 +ji i144 +addi $r0 $r4 i160 ; get offset reg for get_ptr +lw $r1 $r0 i2 ; extract_value @ 2 +addi $r0 $r4 i200 ; get offset reg for get_ptr +lw $r0 $r0 i2 ; extract_value @ 2 +eq $r0 $r1 $r0 +jnzi $r0 i146 +ji i151 +addi $r0 $r4 i160 ; get offset reg for get_ptr +lw $r1 $r0 i3 ; extract_value @ 3 +addi $r0 $r4 i200 ; get offset reg for get_ptr +lw $r0 $r0 i3 ; extract_value @ 3 +eq $r0 $r1 $r0 +eq $r0 $r0 $zero ; asm block +jnzi $r0 i154 +ji i158 +lw $r0 data_5 ; literal instantiation +rvrt $r0 ; asm block +lw $r0 data_5 ; literal instantiation +ji i159 +lw $r0 data_5 ; literal instantiation +lw $r0 data_5 ; literal instantiation +lw $r0 data_14 ; literal instantiation +ret $r0 +.data: +data_0 .b256 0x0000000000000000000000000000000000000000000000000000000000000000 +data_1 .bool 0x00 +data_2 .u64 0x16 +data_3 .u64 0x08 +data_4 .u64 0x6c +data_5 .u64 0x00 +data_6 .u64 0x01 +data_7 .u64 0x02 +data_8 .u64 0x04 +data_9 .u64 0x64 +data_10 .u64 0x65 +data_11 .u64 0x79 +data_12 .u64 0xe0 +data_13 .u64 0x68 +data_14 .u64 0x80 +data_15 .u32 0xea1a0f91 diff --git a/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.ir b/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.ir new file mode 100644 index 00000000000..3330cc38f6b --- /dev/null +++ b/sway-core/tests/ir_to_asm/storage_store_load_intrinsics.ir @@ -0,0 +1,486 @@ +contract { + fn main() -> u64, !3 { + local ptr b256 key + local mut ptr b256 key_for_storage + local mut ptr b256 key_for_storage_ + local mut ptr b256 key_for_storage__ + local mut ptr b256 key_for_storage___ + local ptr { u64, u64, u64, u64 } q + local ptr u64 q_addr + local ptr { u64, u64, u64, u64 } r + local ptr u64 r_addr + local mut ptr b256 result_buffer + local ptr u64 size + local ptr u64 val + + entry: + v0 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !5 + br block7, !9 + + block7: + v1 = phi(entry: v0), !12 + v2 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !14 + store v1, ptr v2, !15 + v3 = const bool false + v4 = asm(r1: v3, r2) -> bool r2, !17 { + eq r2 r1 zero, !18 + } + br block8, !12 + + block8: + v5 = phi(block7: v4) + cbr v5, sha256_0_block0, sha256_0_block1, !19 + + sha256_0_block0: + v6 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !21 + v7 = load ptr v6, !22 + v8 = const u64 22, !23 + v9 = const u64 8, !24 + v10 = asm(buffer, ptr: v8, eight_bytes: v9, hash: v7) -> b256 hash, !26 { + move buffer sp, !27 + cfei i8, !28 + sw buffer ptr i0, !29 + s256 hash buffer eight_bytes, !30 + cfsi i8, !31 + } + br sha256_0_block2, !7 + + sha256_0_block1: + v11 = get_ptr ptr u64 size, ptr u64, 0, !33 + v12 = const u64 8 + store v12, ptr v11, !34 + v13 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !36 + v14 = load ptr v13, !37 + v15 = get_ptr ptr u64 size, ptr u64, 0, !39 + v16 = load ptr v15, !40 + v8 = const u64 22, !23 + v17 = asm(hash: v14, ptr: v8, bytes: v16) -> b256 hash, !42 { + s256 hash ptr bytes, !43 + } + br sha256_0_block2, !7 + + sha256_0_block2: + v18 = phi(sha256_0_block0: v10, sha256_0_block1: v17), !7 + br block6, !7 + + block6: + v19 = phi(sha256_0_block2: v18), !44 + v20 = get_ptr ptr b256 key, ptr b256, 0, !45 + store v19, ptr v20, !45 + v21 = get_ptr ptr u64 val, ptr u64, 0, !46 + v22 = const u64 108, !47 + store v22, ptr v21, !46 + v23 = get_ptr ptr b256 key, ptr b256, 0, !48 + v24 = load ptr v23, !48 + v25 = get_ptr ptr u64 val, ptr u64, 0, !49 + v26 = load ptr v25, !49 + v27 = get_ptr mut ptr b256 key_for_storage, ptr b256, 0, !50 + store v24, ptr v27, !50 + state_store_word v26, key ptr v27, !50 + v28 = get_ptr ptr b256 key, ptr b256, 0, !51 + v29 = load ptr v28, !51 + v30 = get_ptr mut ptr b256 key_for_storage_, ptr b256, 0, !52 + store v29, ptr v30, !52 + v31 = state_load_word key ptr v30, !52 + v32 = get_ptr ptr u64 val, ptr u64, 0, !53 + v33 = load ptr v32, !53 + v34 = cmp eq v31 v33, !44 + br block9, !44 + + block9: + v35 = phi(block6: v34), !56 + v36 = asm(r1: v35, r2) -> bool r2, !57 { + eq r2 r1 zero, !18 + } + br block11, !58 + + block11: + v37 = phi(block9: v36) + cbr v37, assert_3_block0, assert_3_block1, !59 + + assert_3_block0: + v38 = const u64 0, !60 + v39 = asm(r1: v38) { + rvrt r1, !62 + } + v40 = const unit () + br block12, !64 + + block12: + v41 = phi(assert_3_block0: v40) + br assert_3_block2, !55 + + assert_3_block1: + v42 = const unit () + br assert_3_block2, !55 + + assert_3_block2: + v43 = phi(block12: v41, assert_3_block1: v42), !55 + v44 = const unit () + br block10, !55 + + block10: + v45 = phi(assert_3_block2: v44), !65 + v46 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !66 + v47 = const { u64, u64, u64, u64 } { u64 1, u64 2, u64 4, u64 100 }, !67 + store v47, ptr v46, !66 + v48 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !68 + v49 = const { u64, u64, u64, u64 } { u64 101, u64 121, u64 224, u64 104 }, !69 + store v49, ptr v48, !68 + v50 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !70 + v51 = addr_of v50 +, !71 v52 = get_ptr ptr u64 q_addr, ptr u64, 0, !72 + store v51, ptr v52, !72 + v53 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !73 + v54 = addr_of v53 +, !74 v55 = get_ptr ptr u64 r_addr, ptr u64, 0, !75 + store v54, ptr v55, !75 + v56 = get_ptr ptr b256 key, ptr b256, 0, !76 + v57 = load ptr v56, !76 + v58 = get_ptr ptr u64 q_addr, ptr u64, 0, !77 + v59 = load ptr v58, !77 + v60 = get_ptr mut ptr b256 key_for_storage__, ptr b256, 0, !78 + store v57, ptr v60, !78 + v61 = int_to_ptr v59 to b256, !78 + state_store_quad_word ptr v61, key ptr v60, !78 + v62 = get_ptr ptr b256 key, ptr b256, 0, !79 + v63 = load ptr v62, !79 + v64 = get_ptr ptr u64 r_addr, ptr u64, 0, !80 + v65 = load ptr v64, !80 + v66 = get_ptr mut ptr b256 key_for_storage___, ptr b256, 0, !81 + store v63, ptr v66, !81 + v67 = int_to_ptr v65 to b256, !81 + state_load_quad_word ptr v67, key ptr v66, !81 + v68 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !82 + v69 = extract_value v68, { u64, u64, u64, u64 }, 0, !83 + v70 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !84 + v71 = extract_value v70, { u64, u64, u64, u64 }, 0, !83 + v72 = cmp eq v69 v71, !65 + br block13, !65 + + block13: + v73 = phi(block10: v72) + cbr v73, block0, block1, !85 + + block0: + v74 = phi(block13: v73), !86 + v75 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !87 + v76 = extract_value v75, { u64, u64, u64, u64 }, 1, !88 + v77 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !89 + v78 = extract_value v77, { u64, u64, u64, u64 }, 1, !88 + v79 = cmp eq v76 v78, !86 + br block14, !86 + + block14: + v80 = phi(block0: v79) + br block1, !85 + + block1: + v81 = phi(block13: v73, block14: v80) + cbr v81, block2, block3, !90 + + block2: + v82 = phi(block1: v81), !91 + v83 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !92 + v84 = extract_value v83, { u64, u64, u64, u64 }, 2, !93 + v85 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !94 + v86 = extract_value v85, { u64, u64, u64, u64 }, 2, !93 + v87 = cmp eq v84 v86, !91 + br block15, !91 + + block15: + v88 = phi(block2: v87) + br block3, !90 + + block3: + v89 = phi(block1: v81, block15: v88) + cbr v89, block4, block5, !95 + + block4: + v90 = phi(block3: v89), !96 + v91 = get_ptr ptr { u64, u64, u64, u64 } q, ptr { u64, u64, u64, u64 }, 0, !97 + v92 = extract_value v91, { u64, u64, u64, u64 }, 3, !98 + v93 = get_ptr ptr { u64, u64, u64, u64 } r, ptr { u64, u64, u64, u64 }, 0, !99 + v94 = extract_value v93, { u64, u64, u64, u64 }, 3, !98 + v95 = cmp eq v92 v94, !96 + br block16, !96 + + block16: + v96 = phi(block4: v95) + br block5, !95 + + block5: + v97 = phi(block3: v89, block16: v96), !100 + v98 = asm(r1: v97, r2) -> bool r2, !101 { + eq r2 r1 zero, !18 + } + br block21, !102 + + block21: + v99 = phi(block5: v98) + cbr v99, assert_3_block018, assert_3_block119, !103 + + assert_3_block018: + v38 = const u64 0, !60 + v100 = asm(r1: v38) { + rvrt r1, !62 + } + br block22, !104 + + block22: + v101 = phi(assert_3_block018: v40) + br assert_3_block220, !55 + + assert_3_block119: + br assert_3_block220, !55 + + assert_3_block220: + v102 = phi(block22: v101, assert_3_block119: v42), !55 + br block17, !55 + + block17: + v103 = phi(assert_3_block220: v44) + v104 = const u64 128, !105 + ret u64 v104 + } + + fn sha256_0(param !106: u64) -> b256, !7 { + local mut ptr b256 result_buffer + local ptr u64 size + + entry: + v0 = call min_1(), !8 + v1 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !13 + store v0, ptr v1, !13 + v2 = const bool false + v3 = call not_2(v2), !11 + cbr v3, block0, block1, !11 + + block0: + v4 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !20 + v5 = load ptr v4, !20 + v6 = const u64 8, !24 + v7 = asm(buffer, ptr: param, eight_bytes: v6, hash: v5) -> b256 hash, !25 { + move buffer sp, !27 + cfei i8, !28 + sw buffer ptr i0, !29 + s256 hash buffer eight_bytes, !30 + cfsi i8, !31 + } + br block2 + + block1: + v8 = get_ptr ptr u64 size, ptr u64, 0, !32 + v9 = const u64 8 + store v9, ptr v8, !32 + v10 = get_ptr mut ptr b256 result_buffer, ptr b256, 0, !35 + v11 = load ptr v10, !35 + v12 = get_ptr ptr u64 size, ptr u64, 0, !38 + v13 = load ptr v12, !38 + v14 = asm(hash: v11, ptr: param, bytes: v13) -> b256 hash, !41 { + s256 hash ptr bytes, !43 + } + br block2 + + block2: + v15 = phi(block0: v7, block1: v14) + ret b256 v15 + } + + fn min_1() -> b256, !107 { + entry: + v0 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !5 + ret b256 v0 + } + + fn not_2(a !108: bool) -> bool, !11 { + entry: + v0 = asm(r1: a, r2) -> bool r2, !16 { + eq r2 r1 zero, !18 + } + ret bool v0 + } + + fn assert_3(condition !109: bool) -> (), !55 { + entry: + v0 = call not_4(condition), !11 + cbr v0, block0, block1, !11 + + block0: + v1 = const u64 0, !60 + v2 = call revert_5(v1), !63 + br block2 + + block1: + v3 = const unit () + br block2 + + block2: + v4 = phi(block0: v2, block1: v3) + v5 = const unit () + ret () v5 + } + + fn not_4(a !108: bool) -> bool, !11 { + entry: + v0 = asm(r1: a, r2) -> bool r2, !16 { + eq r2 r1 zero, !18 + } + ret bool v0 + } + + fn revert_5(code !110: u64) -> (), !63 { + entry: + v0 = asm(r1: code) { + rvrt r1, !62 + } + v1 = const unit () + ret () v1 + } + + fn eq_6(self !111: u64, other !112: u64) -> bool, !113 { + entry: + v0 = cmp eq self other + ret bool v0 + } + + fn eq_7(self !111: u64, other !112: u64) -> bool, !113 { + entry: + v0 = cmp eq self other + ret bool v0 + } + + fn eq_8(self !111: u64, other !112: u64) -> bool, !113 { + entry: + v0 = cmp eq self other + ret bool v0 + } + + fn eq_9(self !111: u64, other !112: u64) -> bool, !113 { + entry: + v0 = cmp eq self other + ret bool v0 + } + + fn eq_10(self !111: u64, other !112: u64) -> bool, !113 { + entry: + v0 = cmp eq self other + ret bool v0 + } +} + +!0 = "/home/sway_test/working/sway1/src/main.sw" +!1 = span !0 183 711 +!2 = storage "readswrites" +!3 = (!1 !2) +!4 = "/home/sway_test/sway/sway-lib-core/src/num.sw" +!5 = span !4 1611 1677 +!6 = "/home/sway_test/sway/sway-lib-std/src/hash.sw" +!7 = span !6 79 948 +!8 = span !6 150 162 +!9 = (!7 !8) +!10 = "/home/sway_test/sway/sway-lib-core/src/ops.sw" +!11 = span !10 7224 7355 +!12 = (!7 !11) +!13 = span !6 120 163 +!14 = (!7 !13) +!15 = (!7 !13) +!16 = span !10 7291 7353 +!17 = (!7 !11 !16) +!18 = span !10 7316 7329 +!19 = (!7 !11) +!20 = span !6 255 268 +!21 = (!7 !20) +!22 = (!7 !20) +!23 = span !0 233 235 +!24 = span !6 246 247 +!25 = span !6 209 682 +!26 = (!7 !25) +!27 = span !6 284 298 +!28 = span !6 367 374 +!29 = span !6 412 428 +!30 = span !6 499 527 +!31 = span !6 605 612 +!32 = span !6 704 732 +!33 = (!7 !32) +!34 = (!7 !32) +!35 = span !6 751 764 +!36 = (!7 !35) +!37 = (!7 !35) +!38 = span !6 785 789 +!39 = (!7 !38) +!40 = (!7 !38) +!41 = span !6 741 940 +!42 = (!7 !41) +!43 = span !6 805 824 +!44 = span !0 315 344 +!45 = span !0 216 237 +!46 = span !0 247 261 +!47 = span !0 257 260 +!48 = span !0 289 292 +!49 = span !0 294 297 +!50 = span !0 270 298 +!51 = span !0 333 336 +!52 = span !0 315 337 +!53 = span !0 341 344 +!54 = "/home/sway_test/sway/sway-lib-std/src/assert.sw" +!55 = span !54 308 387 +!56 = (!55 !55 !11) +!57 = (!55 !11 !16) +!58 = (!55 !11) +!59 = (!55 !11) +!60 = span !54 376 377 +!61 = "/home/sway_test/sway/sway-lib-std/src/revert.sw" +!62 = span !61 227 234 +!63 = span !61 172 243 +!64 = (!55 !63) +!65 = span !0 630 642 +!66 = span !0 356 402 +!67 = span !0 364 401 +!68 = span !0 411 463 +!69 = span !0 419 462 +!70 = span !0 495 496 +!71 = span !0 485 497 +!72 = span !0 472 498 +!73 = span !0 530 531 +!74 = span !0 520 532 +!75 = span !0 507 533 +!76 = span !0 561 564 +!77 = span !0 566 572 +!78 = span !0 542 573 +!79 = span !0 601 604 +!80 = span !0 606 612 +!81 = span !0 583 613 +!82 = span !0 630 631 +!83 = span !0 90 97 +!84 = span !0 638 639 +!85 = span !0 630 658 +!86 = span !0 646 658 +!87 = span !0 646 647 +!88 = span !0 101 108 +!89 = span !0 654 655 +!90 = span !0 630 674 +!91 = span !0 662 674 +!92 = span !0 662 663 +!93 = span !0 112 119 +!94 = span !0 670 671 +!95 = span !0 630 690 +!96 = span !0 678 690 +!97 = span !0 678 679 +!98 = span !0 123 130 +!99 = span !0 686 687 +!100 = (!55 !55 !11) +!101 = (!55 !11 !16) +!102 = (!55 !11) +!103 = (!55 !11) +!104 = (!55 !63) +!105 = span !0 702 705 +!106 = span !6 96 101 +!107 = span !4 1580 1683 +!108 = span !10 7235 7236 +!109 = span !54 322 331 +!110 = span !61 186 190 +!111 = span !10 3939 3943 +!112 = span !10 3945 3950 +!113 = span !10 3933 3999 + diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index 90d1fba3506..5c4290e24c0 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -210,11 +210,10 @@ impl Instruction { Instruction::ConditionalBranch { .. } => None, Instruction::Ret(..) => None, - // These write values but don't return one. If we're explicit we could return Unit. - Instruction::StateLoadQuadWord { .. } => None, - Instruction::StateStoreQuadWord { .. } => None, - Instruction::StateStoreWord { .. } => None, - Instruction::Store { .. } => None, + Instruction::StateLoadQuadWord { .. } => Some(Type::Unit), + Instruction::StateStoreQuadWord { .. } => Some(Type::Unit), + Instruction::StateStoreWord { .. } => Some(Type::Unit), + Instruction::Store { .. } => Some(Type::Unit), // No-op is also no-type. Instruction::Nop => None, diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 280d55728aa..77c74b29c8e 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -691,6 +691,7 @@ impl<'a> InstructionVerifier<'a> { ValueDatum::Instruction(Instruction::GetPointer { ptr_ty, .. }) => { Some(*ptr_ty.get_type(self.context)) } + ValueDatum::Instruction(Instruction::IntToPtr(_, ty)) => Some(*ty), ValueDatum::Argument(Type::Pointer(ptr)) => Some(*ptr.get_type(self.context)), ValueDatum::Argument(arg_ty) => match arg_ty.is_copy_type() && !arg_ty.is_ptr_type() { true => None, diff --git a/sway-lib-std/src/storage.sw b/sway-lib-std/src/storage.sw index 8b76be0f8e0..6be246ad879 100644 --- a/sway-lib-std/src/storage.sw +++ b/sway-lib-std/src/storage.sw @@ -9,10 +9,11 @@ use ::result::Result; /// Store a stack variable in storage. #[storage(write)]pub fn store(key: b256, value: T) { if !__is_reference_type::() { - // If copy type, then it's a single word and can be stored with a single SWW. - asm(k: key, v: value) { - sww k v; + // If copy type, then it's a single word + let value = asm (v: value) { + v: u64 }; + __state_store_word(key, value); } else { // If reference type, then it can be more than a word. Loop over every 32 // bytes and store sequentially. @@ -26,10 +27,8 @@ use ::result::Result; }; while size_left > 32 { - // Store a 4 words (32 byte) at a time using `swwq` - asm(k: local_key, v: ptr_to_value) { - swwq k v; - }; + // Store a 4 words (32 byte) at a time + __state_store_quad(local_key, ptr_to_value); // Move by 32 bytes ptr_to_value = ptr_to_value + 32; @@ -40,21 +39,18 @@ use ::result::Result; local_key = sha256(local_key); } - // Store the leftover bytes using a single `swwq` - asm(k: local_key, v: ptr_to_value) { - swwq k v; - }; + // Store the leftover bytes using a single quad store + __state_store_quad(local_key, ptr_to_value); }; } /// Load a stack variable from storage. #[storage(read)]pub fn get(key: b256) -> T { if !__is_reference_type::() { - // If copy type, then it's a single word and can be read with a single - // SRW. - asm(k: key, v) { - srw v k; - v: T + // If copy type, then it's a single word + let loaded_word = __state_load_word(key); + asm (l: loaded_word) { + l: T } } else { // If reference type, then it can be more than a word. Loop over every 32 @@ -66,12 +62,12 @@ use ::result::Result; let result_ptr = stack_ptr(); while size_left > 32 { - // Read 4 words (32 bytes) at a time using `srwq` + // Read 4 words (32 bytes) at a time let current_pointer = stack_ptr(); - asm(k: local_key, v: current_pointer) { + asm() { cfei i32; - srwq v k; }; + __state_load_quad(local_key, current_pointer); // Move by 32 bytes size_left -= 32; @@ -83,10 +79,10 @@ use ::result::Result; // Read the leftover bytes using a single `srwq` let current_pointer = stack_ptr(); - asm(k: local_key, v: current_pointer) { + asm() { cfei i32; - srwq v k; } + __state_load_quad(local_key, current_pointer); // Return the final result as type T asm(res: result_ptr) { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index 1aff68518ac..9b79e5d0315 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -1,9 +1,9 @@ script; -use basic_storage_abi::StoreU64; +use basic_storage_abi::{StoreU64, Quad}; use std::assert::assert; fn main() -> u64 { - let addr = abi(StoreU64, 0x1f0bd65d3c75c5a84652103b7978d64664da6dd85bc9b15f5a2edece57b15b23); + let addr = abi(StoreU64, 0x44e9394f2d3b9ce7ed4899f2bcf28478f1218ca0f310c7d0105d638b95fee171); let key = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; let value = 4242; @@ -12,5 +12,18 @@ fn main() -> u64 { let res = addr.get_u64(key); assert(res == value); + let key = 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + addr.intrinsic_store_word(key, value); + let res = addr.intrinsic_load_word(key); + assert(res == value); + + let key = 0x11ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + let q = Quad { v1: 1, v2: 2, v3: 4, v4: 100 }; + addr.intrinsic_store_quad(key, q); + let r = addr.intrinsic_load_quad(key); + assert(q.v1 == r.v1 && q.v2 == r.v2 && q.v3 == r.v3 && q.v4 == r.v4); + + addr.test_storage_exhaustive(); + res } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/Forc.lock index b21bd2b0d4a..e9baaabdcb2 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/Forc.lock @@ -1,3 +1,9 @@ [[package]] name = 'basic_storage_abi' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-3777C332FD2E3D7A' dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/src/main.sw index 13b050b7873..341e1d32dd4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/basic_storage_abi/src/main.sw @@ -1,8 +1,28 @@ library basic_storage_abi; +pub struct Quad { + v1: u64, + v2: u64, + v3: u64, + v4: u64, +} + abi StoreU64 { #[storage(write)] fn store_u64(key: b256, value: u64); #[storage(read)] fn get_u64(key: b256) -> u64; + + #[storage(write)] + fn intrinsic_store_word(key: b256, value: u64); + #[storage(read)] + fn intrinsic_load_word(key: b256) -> u64; + + #[storage(write)] + fn intrinsic_store_quad(key: b256, value: Quad); + #[storage(read)] + fn intrinsic_load_quad(key: b256) -> Quad; + + #[storage(read, write)] + fn test_storage_exhaustive(); } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json index d0b292a36d2..553a905891b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_abi_oracle.json @@ -44,5 +44,160 @@ } ], "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "key", + "type": "b256", + "typeArguments": null + } + ], + "name": "intrinsic_load_word", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64", + "typeArguments": null + } + ], + "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "key", + "type": "b256", + "typeArguments": null + }, + { + "components": null, + "name": "value", + "type": "u64", + "typeArguments": null + } + ], + "name": "intrinsic_store_word", + "outputs": [ + { + "components": [], + "name": "", + "type": "()", + "typeArguments": null + } + ], + "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "key", + "type": "b256", + "typeArguments": null + } + ], + "name": "intrinsic_load_quad", + "outputs": [ + { + "components": [ + { + "components": null, + "name": "v1", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v2", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v3", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v4", + "type": "u64", + "typeArguments": null + } + ], + "name": "", + "type": "struct Quad", + "typeArguments": null + } + ], + "type": "function" + }, + { + "inputs": [ + { + "components": null, + "name": "key", + "type": "b256", + "typeArguments": null + }, + { + "components": [ + { + "components": null, + "name": "v1", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v2", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v3", + "type": "u64", + "typeArguments": null + }, + { + "components": null, + "name": "v4", + "type": "u64", + "typeArguments": null + } + ], + "name": "value", + "type": "struct Quad", + "typeArguments": null + } + ], + "name": "intrinsic_store_quad", + "outputs": [ + { + "components": [], + "name": "", + "type": "()", + "typeArguments": null + } + ], + "type": "function" + }, + { + "inputs": [], + "name": "test_storage_exhaustive", + "outputs": [ + { + "components": [], + "name": "", + "type": "()", + "typeArguments": null + } + ], + "type": "function" } ] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw index c4cf3b7b892..476bad1f285 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/src/main.sw @@ -1,5 +1,5 @@ contract; -use std::storage::*; +use std::{storage::*, assert::assert}; use basic_storage_abi::*; impl StoreU64 for Contract { @@ -12,4 +12,137 @@ impl StoreU64 for Contract { fn store_u64(key: b256, value: u64) { store(key, value); } + + #[storage(read)] + fn intrinsic_load_word(key: b256) -> u64 { + __state_load_word(key) + } + + #[storage(write)] + fn intrinsic_store_word(key: b256, value: u64) { + __state_store_word(key, value); + } + + #[storage(read)] + fn intrinsic_load_quad(key: b256) -> Quad { + let q = Quad { v1 : 0, v2 : 0, v3 : 0, v4 : 0 }; + let q_addr = __addr_of(q); + __state_load_quad(key, q_addr); + q + } + + #[storage(write)] + fn intrinsic_store_quad(key: b256, value: Quad) { + let addr = __addr_of(value); + __state_store_quad(key, addr) + } + + #[storage(read, write)] + fn test_storage_exhaustive() { + test_storage(); + } +} + +pub struct S { + x: u64, + y: u64, + z: b256, + t: T, +} + +pub struct T { + x: u64, + y: u64, + z: b256, + boolean: bool, + int8: u8, + int16: u16, + int32: u32, +} + +pub enum E { + A: u64, + B: T, +} + +// These inputs are taken from the storage_access_contract test. +#[storage(read, write)] +fn test_storage() { + let key: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101; + + let x: u64 = 64; + store(key, x); + assert(x == get::(key)); + + let y: b256 = 0x1101010101010101010101010101010101010101010101010101010101010101; + store(key, y); + assert(y == get::(key)); + + let s: S = S { + x: 1, + y: 2, + z: 0x0000000000000000000000000000000000000000000000000000000000000003, + t: T { + x: 4, + y: 5, + z: 0x0000000000000000000000000000000000000000000000000000000000000006, + boolean: true, + int8: 7, + int16: 8, + int32: 9, + }, + }; + store(key, s); + let s_ = get::(key); + assert(s.x == s_.x && s.y == s_.y && s.z == s_.z && s.t.x == s_.t.x + && s.t.y == s_.t.y && s.t.z == s_.t.z && s.t.boolean == s_.t.boolean + && s.t.int8 == s_.t.int8 && s.t.int16 == s_.t.int16 && s.t.int32 == s_.t.int32); + + let boolean: bool = true; + store(key, boolean); + assert(boolean == get::(key)); + + let int8: u8 = 8; + store(key, int8); + assert(int8 == get::(key)); + + let int16: u16 = 16; + store(key, int16); + assert(int16 == get::(key)); + + let int32: u32 = 32; + store(key, int32); + assert(int32 == get::(key)); + + let e: E = E::B(T { + x: 1, + y: 2, + z: 0x0000000000000000000000000000000000000000000000000000000000000003, + boolean: true, + int8: 4, + int16: 5, + int32: 6, + }, + ); + store(key, e); + let e_ = get::(key); + match (e, e_) { + (E::B(T {x: x1, y: y1, z: z1, boolean: boolean1, int8: int81, int16: int161, int32: int321}), + E::B(T {x: x2, y: y2, z: z2, boolean: boolean2, int8: int82, int16: int162, int32: int322})) => + { + assert(x1 == x2 && y1 == y2 && z1 == z2 && boolean1 == boolean2 && + int81 == int82 && int161 == int162 && int321 == int322); + } + _ => assert(false), + } + + let e2: E = E::A(777); + store(key, e2); + let e2_ = get::(key); + match (e2, e2_) { + (E::A(i1), E::A(i2)) => { + assert(i1 == i2); + } + _ => assert(false), + } }