diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e379449fce..bae3f024ab8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,6 +435,14 @@ jobs: run: cargo run --locked --release -p forc -- build --release --locked --path ./test/src/sdk-harness --experimental str_array_no_padding --output-directory ./test/src/sdk-harness/out - name: Run All SDK Tests - ['str_array_no_padding' enabled] (Release) run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture + - name: Build All Test Projects - ['aligned_and_dynamic_storage' enabled] (Debug) + run: cargo run --locked --release -p forc -- build --locked --path ./test/src/sdk-harness --experimental aligned_and_dynamic_storage --output-directory ./test/src/sdk-harness/out + - name: Run All SDK Tests - ['aligned_and_dynamic_storage' enabled] (Debug) + run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --skip can_get_predicate_address --nocapture + - name: Build All Test Projects - ['aligned_and_dynamic_storage' enabled] (Release) + run: cargo run --locked --release -p forc -- build --release --locked --path ./test/src/sdk-harness --experimental aligned_and_dynamic_storage --output-directory ./test/src/sdk-harness/out + - name: Run All SDK Tests - ['aligned_and_dynamic_storage' enabled] (Release) + run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture forc-run-benchmarks: runs-on: warp-ubuntu-latest-x64-4x @@ -509,6 +517,10 @@ jobs: run: forc test --path sway-lib-std --experimental str_array_no_padding - name: Run Std Unit Tests - ['str_array_no_padding' enabled] (Release) run: forc test --release --path sway-lib-std --experimental str_array_no_padding + - name: Run Std Unit Tests - ['aligned_and_dynamic_storage' enabled] (Debug) + run: forc test --path sway-lib-std --experimental aligned_and_dynamic_storage + - name: Run Std Unit Tests - ['aligned_and_dynamic_storage' enabled] (Release) + run: forc test --release --path sway-lib-std --experimental aligned_and_dynamic_storage - name: Run In Language Unit Tests (Debug) run: forc test --error-on-warnings --path test/src/in_language_tests - name: Run In Language Unit Tests (Release) @@ -521,6 +533,10 @@ jobs: run: forc test --error-on-warnings --path test/src/in_language_tests --experimental str_array_no_padding - name: Run In Language Unit Tests - ['str_array_no_padding' enabled] (Release) run: forc test --error-on-warnings --release --path test/src/in_language_tests --experimental str_array_no_padding + - name: Run In Language Unit Tests - ['aligned_and_dynamic_storage' enabled] (Debug) + run: forc test --error-on-warnings --path test/src/in_language_tests --experimental aligned_and_dynamic_storage + - name: Run In Language Unit Tests - ['aligned_and_dynamic_storage' enabled] (Release) + run: forc test --error-on-warnings --release --path test/src/in_language_tests --experimental aligned_and_dynamic_storage forc-pkg-fuels-deps-check: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 4c8b001d46e..6983a273211 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3712,8 +3712,7 @@ dependencies = [ [[package]] name = "fuel-asm" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ae81d896972a39cfbe6e5659624d08aa889d30950d2831cca1c50bce95898f" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "bitflags 2.9.4", "fuel-types 0.65.0", @@ -3724,8 +3723,7 @@ dependencies = [ [[package]] name = "fuel-compression" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55258ae5ef436d6b6c024db4dbe80784c8eafc5062b8b90f365db416e6b4e7bc" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "fuel-derive 0.65.0", "fuel-types 0.65.0", @@ -3801,8 +3799,7 @@ dependencies = [ [[package]] name = "fuel-core-client" version = "0.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd34ff2c9ad4ed347982759b93ee72f9d31492cd49101fe1b0189a3cb32b434" +source = "git+https://github.com/FuelLabs/fuel-core?branch=dento%2Fdynamic-storage#cc74bdcfdde71204884b9ee7397bb8b5e619d2a1" dependencies = [ "anyhow", "base64 0.22.1", @@ -3953,8 +3950,7 @@ dependencies = [ [[package]] name = "fuel-core-storage" version = "0.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e565d1cf1a528868988bb1165a7ccbc2f410dea356ddbf01032931fbde5c11e" +source = "git+https://github.com/FuelLabs/fuel-core?branch=dento%2Fdynamic-storage#cc74bdcfdde71204884b9ee7397bb8b5e619d2a1" dependencies = [ "anyhow", "derive_more 0.99.20", @@ -3996,8 +3992,7 @@ dependencies = [ [[package]] name = "fuel-core-types" version = "0.47.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9726f08ebe75b92c894626b804dd0a8f8efbe5fc3f25ded79a4d292812647e3d" +source = "git+https://github.com/FuelLabs/fuel-core?branch=dento%2Fdynamic-storage#cc74bdcfdde71204884b9ee7397bb8b5e619d2a1" dependencies = [ "anyhow", "bs58", @@ -4008,6 +4003,7 @@ dependencies = [ "fuel-vm 0.65.0", "k256", "parking_lot", + "proptest", "rand 0.8.5", "secrecy", "serde", @@ -4039,8 +4035,7 @@ dependencies = [ [[package]] name = "fuel-crypto" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bbd5d5d9809d517dab5f92a8c915403d83375efb6f3f5dd3994ff1e4ae28fe" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "base64ct", "coins-bip32", @@ -4072,8 +4067,7 @@ dependencies = [ [[package]] name = "fuel-derive" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb4078dbab152ef822bc092bd542796270031152ff8c96008b9006fb8c52ce53" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "proc-macro2", "quote", @@ -4146,8 +4140,7 @@ dependencies = [ [[package]] name = "fuel-merkle" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ecc75b1771d36c86293fef3b63ebcd5fa5bafbc51d356d92ff60ebac59f636" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "derive_more 0.99.20", "digest 0.10.7", @@ -4167,8 +4160,7 @@ checksum = "ca73c81646409e9cacac532ff790567d629bc6c512c10ff986536e2335de22c9" [[package]] name = "fuel-storage" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b46be43225f70c17f9e028f80a583c275fb2dec1e2a4e9100aa6385a2c86a" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" [[package]] name = "fuel-telemetry" @@ -4229,8 +4221,7 @@ dependencies = [ [[package]] name = "fuel-tx" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c26aca0167279221e6ea9bc688f4f9688d2e5a42533c05f1bdf97f1d200b1b" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "bitflags 2.9.4", "derive_more 1.0.0", @@ -4264,8 +4255,7 @@ dependencies = [ [[package]] name = "fuel-types" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b128048cde245d6e895cb7f7824d38d3e4d9f1b4cb5d514c7186bd79308af6a9" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "educe 0.6.0", "fuel-derive 0.65.0", @@ -4312,8 +4302,7 @@ dependencies = [ [[package]] name = "fuel-vm" version = "0.65.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1632d884ec82f140f21f9943f0bcda8dfcdad02a94a032f67051e27eb1e8054" +source = "git+https://github.com/FuelLabs/fuel-vm#ec7761c4319febd9a7a1d76bb3686916ba9dcf21" dependencies = [ "anyhow", "async-trait", @@ -4363,8 +4352,7 @@ dependencies = [ [[package]] name = "fuels" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81901ab28bf46f0398c2b980a2343a3ea6c96ccf638a602cfeedb24db81ae31c" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "fuel-core-client 0.47.1", "fuel-crypto 0.65.0", @@ -4405,8 +4393,7 @@ dependencies = [ [[package]] name = "fuels-accounts" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2af2d57d6b20289978ccf81397ca385a4f0f9220111b7e27109111ba721b9" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "async-trait", "chrono", @@ -4447,8 +4434,7 @@ dependencies = [ [[package]] name = "fuels-code-gen" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6a0c6d15b5bbba10b5862488c34fbeb06e82036409b6e663dedd68430aec9" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "Inflector", "fuel-abi-types", @@ -4493,8 +4479,7 @@ dependencies = [ [[package]] name = "fuels-core" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d857ff8b9a0034c86c63c53c079098ffd674d47089dd1a8c93bda3cf7e976885" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "async-trait", "auto_impl", @@ -4536,8 +4521,7 @@ dependencies = [ [[package]] name = "fuels-macros" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb02dbec62f9441a199d08b584489f6f91f301e189b1299caeb21154c256ffeb" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "fuels-code-gen 0.76.0", "itertools 0.12.1", @@ -4568,8 +4552,7 @@ dependencies = [ [[package]] name = "fuels-programs" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94a8919bc58ac98bd4376ad24b2137b75fad05eb096bf5dffe1d5438d684d1b" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "async-trait", "fuel-abi-types", @@ -4611,8 +4594,7 @@ dependencies = [ [[package]] name = "fuels-test-helpers" version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f277d3dd44e8ebc2aa2a5234ad8fee3433c43b0b58c2a7d7f73449d1e6930c" +source = "git+https://github.com/FuelLabs/fuels-rs?branch=ironcev%2Fclonable-isuseraccountvariables#292073078b6077cf36c3ce3a9bbf7df48a6661a2" dependencies = [ "fuel-core-chain-config 0.47.1", "fuel-core-client 0.47.1", diff --git a/Cargo.toml b/Cargo.toml index a6a88769a68..f43da8ea615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,6 @@ fuel-core-storage = { version = "0.47", default-features = false } fuel-core-types = { version = "0.47", default-features = false } # Dependencies from the `fuels-rs` repository: - fuels = "0.76" fuels-core = "0.76" fuels-accounts = "0.76" @@ -244,3 +243,18 @@ vte = "0.13" walkdir = "2.3" whoami = "1.5" wiremock = "0.6" + +[patch.crates-io] +fuel-core-client = { git = "https://github.com/FuelLabs/fuel-core", branch = "dento/dynamic-storage" } +fuel-core-storage = { git = "https://github.com/FuelLabs/fuel-core", branch = "dento/dynamic-storage" } +fuel-core-types = { git = "https://github.com/FuelLabs/fuel-core", branch = "dento/dynamic-storage" } + +fuel-asm = { git = "https://github.com/FuelLabs/fuel-vm" } +fuel-crypto = { git = "https://github.com/FuelLabs/fuel-vm" } +fuel-types = { git = "https://github.com/FuelLabs/fuel-vm" } +fuel-tx = { git = "https://github.com/FuelLabs/fuel-vm" } +fuel-vm = { git = "https://github.com/FuelLabs/fuel-vm" } + +fuels = { git = "https://github.com/FuelLabs/fuels-rs", branch = "ironcev/clonable-isuseraccountvariables" } +fuels-core = { git = "https://github.com/FuelLabs/fuels-rs", branch = "ironcev/clonable-isuseraccountvariables" } +fuels-accounts = { git = "https://github.com/FuelLabs/fuels-rs", branch = "ironcev/clonable-isuseraccountvariables" } \ No newline at end of file diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index 2f63c26857d..2d670ea7a6d 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -124,14 +124,26 @@ __addr_of(val: T) -> raw_ptr --- +**Without `aligned_and_dynamic_storage` experimental feature enabled** + ```sway __state_load_word(key: b256) -> u64 ``` -**Description:** Reads and returns a single word from storage at key `key`. +**Description:** Reads and returns a single word from storage at key `key`. If the storage slot at key `key` is not set, zero is returned. **Constraints:** None. +**With `aligned_and_dynamic_storage` experimental feature enabled** + +```sway +__state_load_word(key: b256, offset: u64) -> u64 +``` + +**Description:** Reads and returns a single word from storage at key `key` and offset `offset`. If the storage slot at key `key` is not set, zero is returned. + +**Constraints:** `offset` must be a valid word offset inside of the storage slot boundaries. E.g., if the storage slot contains four words, the lowest offset is zero, and the highest is three. + --- ```sway diff --git a/forc-plugins/forc-debug/src/server/handlers/handle_variables.rs b/forc-plugins/forc-debug/src/server/handlers/handle_variables.rs index 435d30e0276..d056bbeb777 100644 --- a/forc-plugins/forc-debug/src/server/handlers/handle_variables.rs +++ b/forc-plugins/forc-debug/src/server/handlers/handle_variables.rs @@ -426,6 +426,7 @@ fn imm(instruction: Instruction) -> Option { Instruction::WQML(op) => imm06_to_string(op.imm06()), Instruction::WDDV(op) => imm06_to_string(op.imm06()), Instruction::WQDV(op) => imm06_to_string(op.imm06()), + Instruction::SRW(op) => imm06_to_string(op.imm06()), _ => None, } } diff --git a/sway-ast/src/expr/op_code.rs b/sway-ast/src/expr/op_code.rs index a2bd2c0ad2d..e3fdf4e21c8 100644 --- a/sway-ast/src/expr/op_code.rs +++ b/sway-ast/src/expr/op_code.rs @@ -310,7 +310,7 @@ define_op_codes!( Srw, SrwOpcode, "srw", - (ret: reg, is_set: reg, state_addr: reg) + (ret: reg, is_set: reg, state_addr: reg, offset: imm) ), ( Srwq, diff --git a/sway-core/src/asm_generation/finalized_asm.rs b/sway-core/src/asm_generation/finalized_asm.rs index fd289585ae5..e7ecd68ffc3 100644 --- a/sway-core/src/asm_generation/finalized_asm.rs +++ b/sway-core/src/asm_generation/finalized_asm.rs @@ -521,6 +521,15 @@ fn print_instruction(op: &Instruction) { Instruction::SRWQ(x) => f("SRWQ", x.unpack()), Instruction::SWW(x) => f("SWW", x.unpack()), Instruction::SWWQ(x) => f("SWWQ", x.unpack()), + Instruction::SCLR(x) => f("SCLR", x.unpack()), + Instruction::SRDD(x) => f("SRDD", x.unpack()), + Instruction::SRDI(x) => f("SRDI", x.unpack()), + Instruction::SWRD(x) => f("SWRD", x.unpack()), + Instruction::SWRI(x) => f("SWRI", x.unpack()), + Instruction::SUPD(x) => f("SUPD", x.unpack()), + Instruction::SUPI(x) => f("SUPI", x.unpack()), + Instruction::SPLD(x) => f("SPLD", x.unpack()), + Instruction::SPCP(x) => f("SPCP", x.unpack()), Instruction::TR(x) => f("TR", x.unpack()), Instruction::TRO(x) => f("TRO", x.unpack()), Instruction::ECK1(x) => f("ECK1", x.unpack()), 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 12caefdbec1..0e0d988eefd 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -447,8 +447,8 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { number_of_slots, StateAccessType::Read, ), - FuelVmInstruction::StateLoadWord(key) => { - self.compile_state_load_word(instr_val, key) + FuelVmInstruction::StateLoadWord { key, offset } => { + self.compile_state_load_word(instr_val, key, offset) } FuelVmInstruction::StateStoreQuadWord { stored_val, @@ -2191,26 +2191,21 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { &mut self, instr_val: &Value, key: &Value, + offset: &u64, ) -> Result<(), CompileError> { let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); - // XXX not required after we have FuelVM specific verifier. - if !key - .get_type(self.context) - .is_none_or(|key_ty| key_ty.is_ptr(self.context)) - { - return Err(CompileError::Internal( - "Key value for state load word is not a pointer.", - owning_span.unwrap_or_else(Span::dummy), - )); - } - let key_reg = self.value_to_register(key)?; let was_slot_set_reg = self.reg_seqr.next(); let load_reg = self.reg_seqr.next(); self.cur_bytecode.push(Op { - opcode: Either::Left(VirtualOp::SRW(load_reg.clone(), was_slot_set_reg, key_reg)), + opcode: Either::Left(VirtualOp::SRW( + load_reg.clone(), + was_slot_set_reg, + key_reg, + VirtualImmediate06::new(*offset), + )), comment: "read single word from contract state".into(), owning_span, }); diff --git a/sway-core/src/asm_lang/allocated_ops.rs b/sway-core/src/asm_lang/allocated_ops.rs index 9c61b8f1ca8..82dcd3c87ec 100644 --- a/sway-core/src/asm_lang/allocated_ops.rs +++ b/sway-core/src/asm_lang/allocated_ops.rs @@ -256,7 +256,12 @@ pub(crate) enum AllocatedInstruction { AllocatedRegister, ), SCWQ(AllocatedRegister, AllocatedRegister, AllocatedRegister), - SRW(AllocatedRegister, AllocatedRegister, AllocatedRegister), + SRW( + AllocatedRegister, + AllocatedRegister, + AllocatedRegister, + VirtualImmediate06, + ), SRWQ( AllocatedRegister, AllocatedRegister, @@ -422,7 +427,7 @@ impl AllocatedInstruction { RVRT(_r1) => vec![], SMO(_r1, _r2, _r3, _r4) => vec![], SCWQ(_r1, r2, _r3) => vec![r2], - SRW(r1, r2, _r3) => vec![r1, r2], + SRW(r1, r2, _r3, _imm) => vec![r1, r2], SRWQ(_r1, r2, _r3, _r4) => vec![r2], SWW(_r1, r2, _r3) => vec![r2], SWWQ(_r1, r2, _r3, _r4) => vec![r2], @@ -557,7 +562,7 @@ impl fmt::Display for AllocatedInstruction { RVRT(a) => write!(fmtr, "rvrt {a}"), SMO(a, b, c, d) => write!(fmtr, "smo {a} {b} {c} {d}"), SCWQ(a, b, c) => write!(fmtr, "scwq {a} {b} {c}"), - SRW(a, b, c) => write!(fmtr, "srw {a} {b} {c}"), + SRW(a, b, c, d) => write!(fmtr, "srw {a} {b} {c} {d}"), SRWQ(a, b, c, d) => write!(fmtr, "srwq {a} {b} {c} {d}"), SWW(a, b, c) => write!(fmtr, "sww {a} {b} {c}"), SWWQ(a, b, c, d) => write!(fmtr, "swwq {a} {b} {c} {d}"), @@ -797,7 +802,13 @@ impl AllocatedOp { op::SMO::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() } SCWQ(a, b, c) => op::SCWQ::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(), - SRW(a, b, c) => op::SRW::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(), + SRW(a, b, c, d) => op::SRW::new( + a.to_reg_id(), + b.to_reg_id(), + c.to_reg_id(), + d.value().into(), + ) + .into(), SRWQ(a, b, c, d) => { op::SRWQ::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.to_reg_id()).into() } diff --git a/sway-core/src/asm_lang/mod.rs b/sway-core/src/asm_lang/mod.rs index cc302e5fe37..d26685a8999 100644 --- a/sway-core/src/asm_lang/mod.rs +++ b/sway-core/src/asm_lang/mod.rs @@ -604,8 +604,8 @@ impl Op { VirtualOp::SCWQ(r1, r2, r3) } "srw" => { - let (r1, r2, r3) = three_regs(handler, args, immediate, whole_op_span)?; - VirtualOp::SRW(r1, r2, r3) + let (r1, r2, r3, imm) = three_regs_imm_06(handler, args, immediate, whole_op_span)?; + VirtualOp::SRW(r1, r2, r3, imm) } "srwq" => { let (r1, r2, r3, r4) = four_regs(handler, args, immediate, whole_op_span)?; @@ -1217,7 +1217,7 @@ impl fmt::Display for VirtualOp { RVRT(a) => write!(fmtr, "rvrt {a}"), SMO(a, b, c, d) => write!(fmtr, "smo {a} {b} {c} {d}"), SCWQ(a, b, c) => write!(fmtr, "scwq {a} {b} {c}"), - SRW(a, b, c) => write!(fmtr, "srw {a} {b} {c}"), + SRW(a, b, c, d) => write!(fmtr, "srw {a} {b} {c} {d}"), SRWQ(a, b, c, d) => write!(fmtr, "srwq {a} {b} {c} {d}"), SWW(a, b, c) => write!(fmtr, "sww {a} {b} {c}"), SWWQ(a, b, c, d) => write!(fmtr, "swwq {a} {b} {c} {d}"), diff --git a/sway-core/src/asm_lang/virtual_ops.rs b/sway-core/src/asm_lang/virtual_ops.rs index f5a6a2dba6d..4f757e58421 100644 --- a/sway-core/src/asm_lang/virtual_ops.rs +++ b/sway-core/src/asm_lang/virtual_ops.rs @@ -177,7 +177,12 @@ pub(crate) enum VirtualOp { VirtualRegister, ), SCWQ(VirtualRegister, VirtualRegister, VirtualRegister), - SRW(VirtualRegister, VirtualRegister, VirtualRegister), + SRW( + VirtualRegister, + VirtualRegister, + VirtualRegister, + VirtualImmediate06, + ), SRWQ( VirtualRegister, VirtualRegister, @@ -471,7 +476,7 @@ impl VirtualOp { RVRT(r1) => vec![r1], SMO(r1, r2, r3, r4) => vec![r1, r2, r3, r4], SCWQ(r1, r2, r3) => vec![r1, r2, r3], - SRW(r1, r2, r3) => vec![r1, r2, r3], + SRW(r1, r2, r3, _i) => vec![r1, r2, r3], SRWQ(r1, r2, r3, r4) => vec![r1, r2, r3, r4], SWW(r1, r2, r3) => vec![r1, r2, r3], SWWQ(r1, r2, r3, r4) => vec![r1, r2, r3, r4], @@ -552,7 +557,7 @@ impl VirtualOp { | BHEI(_) | CSIZ(_, _) | BSIZ(_, _) - | SRW(_, _, _) + | SRW(_, _, _, _) | TIME(_, _) | GM(_, _) | GTF(_, _, _) @@ -705,7 +710,7 @@ impl VirtualOp { | RVRT(_) | SMO(_, _, _, _) | SCWQ(_, _, _) - | SRW(_, _, _) + | SRW(_, _, _, _) | SRWQ(_, _, _, _) | SWW(_, _, _) | SWWQ(_, _, _, _) @@ -816,7 +821,7 @@ impl VirtualOp { RVRT(r1) => vec![r1], SMO(r1, r2, r3, r4) => vec![r1, r2, r3, r4], SCWQ(r1, _r2, r3) => vec![r1, r3], - SRW(_r1, _r2, r3) => vec![r3], + SRW(_r1, _r2, r3, _i) => vec![r3], SRWQ(r1, _r2, r3, r4) => vec![r1, r3, r4], SWW(r1, _r2, r3) => vec![r1, r3], SWWQ(r1, _r2, r3, r4) => vec![r1, r3, r4], @@ -938,7 +943,7 @@ impl VirtualOp { RVRT(r1) => vec![r1], SMO(r1, r2, r3, r4) => vec![r1, r2, r3, r4], SCWQ(r1, _r2, r3) => vec![r1, r3], - SRW(_r1, _r2, r3) => vec![r3], + SRW(_r1, _r2, r3, _i) => vec![r3], SRWQ(r1, _r2, r3, r4) => vec![r1, r3, r4], SWW(r1, _r2, r3) => vec![r1, r3], SWWQ(r1, _r2, r3, r4) => vec![r1, r3, r4], @@ -1056,7 +1061,7 @@ impl VirtualOp { RVRT(_r1) => vec![], SMO(_r1, _r2, _r3, _r4) => vec![], SCWQ(_r1, r2, _r3) => vec![r2], - SRW(r1, r2, _r3) => vec![r1, r2], + SRW(r1, r2, _r3, _i) => vec![r1, r2], SRWQ(_r1, r2, _r3, _r4) => vec![r2], SWW(_r1, r2, _r3) => vec![r2], SWWQ(_r1, r2, _r3, _r4) => vec![r2], @@ -1438,10 +1443,11 @@ impl VirtualOp { update_reg(reg_to_reg_map, r2), update_reg(reg_to_reg_map, r3), ), - SRW(r1, r2, r3) => Self::SRW( + SRW(r1, r2, r3, i) => Self::SRW( update_reg(reg_to_reg_map, r1), update_reg(reg_to_reg_map, r2), update_reg(reg_to_reg_map, r3), + i.clone(), ), SRWQ(r1, r2, r3, r4) => Self::SRWQ( update_reg(reg_to_reg_map, r1), @@ -1843,11 +1849,11 @@ impl VirtualOp { BSIZ(reg1, reg2) => { AllocatedInstruction::BSIZ(map_reg(&mapping, reg1), map_reg(&mapping, reg2)) } - LDC(reg1, reg2, reg3, imm0) => AllocatedInstruction::LDC( + LDC(reg1, reg2, reg3, imm) => AllocatedInstruction::LDC( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), - imm0.clone(), + imm.clone(), ), BLDD(reg1, reg2, reg3, reg4) => AllocatedInstruction::BLDD( map_reg(&mapping, reg1), @@ -1885,10 +1891,11 @@ impl VirtualOp { map_reg(&mapping, reg2), map_reg(&mapping, reg3), ), - SRW(reg1, reg2, reg3) => AllocatedInstruction::SRW( + SRW(reg1, reg2, reg3, imm) => AllocatedInstruction::SRW( map_reg(&mapping, reg1), map_reg(&mapping, reg2), map_reg(&mapping, reg3), + imm.clone(), ), SRWQ(reg1, reg2, reg3, reg4) => AllocatedInstruction::SRWQ( map_reg(&mapping, reg1), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 95bd25b0e57..b8f58d4ba9c 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -1171,16 +1171,13 @@ impl<'a> FnCompiler<'a> { )) } Intrinsic::Gtf => { - // The index is just a Value let index = return_on_termination_or_extract!( self.compile_expression_to_register(context, md_mgr, &arguments[0])? ) .expect_register(); - // The tx field ID has to be a compile-time constant because it becomes an - // immediate - - let tx_field_id_constant = compile_constant_expression_to_constant( + // The tx field ID has to be a compile-time constant, because it becomes an immediate. + let Ok(tx_field_id_const) = compile_constant_expression_to_constant( engines, context, md_mgr, @@ -1188,13 +1185,21 @@ impl<'a> FnCompiler<'a> { None, None, &arguments[1], - )?; - let tx_field_id = match tx_field_id_constant.get_content(context).value { + ) else { + return Err(CompileError::IntrinsicArgNotConstant { + intrinsic: kind.to_string(), + arg: "tx_field_id".to_string(), + expected_type: "u64".to_string(), + span: arguments[1].span.clone(), + }); + }; + + let tx_field_id = match tx_field_id_const.get_content(context).value { ConstantValue::Uint(n) => n, _ => { return Err(CompileError::Internal( - "Transaction field ID for gtf intrinsic is not an integer. \ - This should have been in caught in type checking", + "Transaction field ID for \"__gtf\" intrinsic is not an integer. \ + This should have been caught in type checking.", span, )) } @@ -1305,10 +1310,39 @@ impl<'a> FnCompiler<'a> { .expect_memory() .add_metadatum(context, span_md_idx); + // The offset has to be a compile-time constant, because it becomes an immediate. + let Ok(offset_const) = compile_constant_expression_to_constant( + engines, + context, + md_mgr, + self.module, + None, + None, + &arguments[1], + ) else { + return Err(CompileError::IntrinsicArgNotConstant { + intrinsic: kind.to_string(), + arg: "offset".to_string(), + expected_type: "u64".to_string(), + span: arguments[1].span.clone(), + }); + }; + + let offset = match offset_const.get_content(context).value { + ConstantValue::Uint(n) => n, + _ => { + return Err(CompileError::Internal( + "Offset for \"__state_load_word\" intrinsic is not an integer. \ + This should have been caught in type checking.", + span, + )) + } + }; + let val = self .current_block .append(context) - .state_load_word(key_value) + .state_load_word(key_value, offset) .add_metadatum(context, span_md_idx); Ok(TerminatorValue::new( diff --git a/sway-core/src/ir_generation/purity.rs b/sway-core/src/ir_generation/purity.rs index 79d61df830f..11e820ac9de 100644 --- a/sway-core/src/ir_generation/purity.rs +++ b/sway-core/src/ir_generation/purity.rs @@ -61,7 +61,7 @@ pub(crate) fn check_function_purity( match inst { FuelVmInstruction::StateLoadQuadWord { .. } - | FuelVmInstruction::StateLoadWord(_) => (true, writes), + | FuelVmInstruction::StateLoadWord { .. } => (true, writes), FuelVmInstruction::StateClear { .. } | FuelVmInstruction::StateStoreQuadWord { .. } | FuelVmInstruction::StateStoreWord { .. } => (reads, true), @@ -180,7 +180,7 @@ pub(crate) fn check_function_purity( fn is_store_access_fuel_vm_instruction(inst: &FuelVmInstruction) -> bool { matches!( inst, - FuelVmInstruction::StateLoadWord(_) + FuelVmInstruction::StateLoadWord { .. } | FuelVmInstruction::StateLoadQuadWord { .. } | FuelVmInstruction::StateClear { .. } | FuelVmInstruction::StateStoreWord { .. } @@ -190,7 +190,7 @@ fn is_store_access_fuel_vm_instruction(inst: &FuelVmInstruction) -> bool { fn store_access_fuel_vm_instruction_to_storage_access(inst: &FuelVmInstruction) -> StorageAccess { match inst { - FuelVmInstruction::StateLoadWord(_) => StorageAccess::ReadWord, + FuelVmInstruction::StateLoadWord { .. } => StorageAccess::ReadWord, FuelVmInstruction::StateLoadQuadWord { .. } => StorageAccess::ReadSlots, FuelVmInstruction::StateClear { .. } => StorageAccess::Clear, FuelVmInstruction::StateStoreWord { .. } => StorageAccess::WriteWord, diff --git a/sway-core/src/language/ty/expression/expression.rs b/sway-core/src/language/ty/expression/expression.rs index b0d692fa3d2..fd1367f5ece 100644 --- a/sway-core/src/language/ty/expression/expression.rs +++ b/sway-core/src/language/ty/expression/expression.rs @@ -611,6 +611,15 @@ impl MaterializeConstGenerics for TyExpression { } impl TyExpression { + pub(crate) fn u64_literal(value: u64, span: Span, engines: &Engines) -> TyExpression { + let type_engine = engines.te(); + TyExpression { + expression: TyExpressionVariant::Literal(Literal::U64(value)), + return_type: type_engine.id_of_u64(), + span, + } + } + pub(crate) fn error(err: ErrorEmitted, span: Span, engines: &Engines) -> TyExpression { let type_engine = engines.te(); TyExpression { 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 cd35e917ae0..abffb18f923 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 @@ -1250,8 +1250,8 @@ fn type_check_state_clear( Ok((intrinsic_function, type_engine.id_of_bool())) } -/// Signature: `__state_load_word(key: b256) -> u64` -/// Description: Reads and returns a single word from storage at key `key`. +/// Signature: `__state_load_word(key: b256, offset: u64) -> u64` +/// Description: Reads and returns a single word from storage at key `key` and offset `offset`. /// Constraints: None. fn type_check_state_load_word( handler: &Handler, @@ -1263,19 +1263,25 @@ fn type_check_state_load_word( let type_engine = ctx.engines.te(); let engines = ctx.engines(); - if arguments.len() != 1 { + let num_of_args: u64 = if ctx.experimental.aligned_and_dynamic_storage { + 2 + } else { + 1 + }; + if arguments.len() != num_of_args as usize { return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { name: kind.to_string(), - expected: 1, + expected: num_of_args, span, })); } - let ctx = ctx + + let mut ctx = ctx .with_help_text("") .with_type_annotation(type_engine.new_unknown()); - let exp = ty::TyExpression::type_check(handler, ctx, &arguments[0])?; + let key_exp = ty::TyExpression::type_check(handler, ctx.by_ref(), &arguments[0])?; let key_ty = type_engine - .to_typeinfo(exp.return_type, &span) + .to_typeinfo(key_exp.return_type, &span) .map_err(|e| handler.emit_err(e.into())) .unwrap_or_else(TypeInfo::ErrorRecovery); if !key_ty.eq(&TypeInfo::B256, &PartialEqWithEnginesContext::new(engines)) { @@ -1285,12 +1291,22 @@ fn type_check_state_load_word( hint: "Argument type must be B256, a key into the state storage".to_string(), })); } + + let offset_exp = if num_of_args == 2 { + // Type check the second argument which is the offset. + let ctx = ctx.with_type_annotation(type_engine.id_of_u64()); + ty::TyExpression::type_check(handler, ctx, &arguments[1])? + } else { + ty::TyExpression::u64_literal(0, Span::dummy(), engines) + }; + let intrinsic_function = ty::TyIntrinsicFunctionKind { kind, - arguments: vec![exp], + arguments: vec![key_exp, offset_exp], type_arguments: vec![], span, }; + Ok((intrinsic_function, type_engine.id_of_u64())) } diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 99f10709442..125af91d450 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -931,6 +931,13 @@ pub enum CompileError { expected: u64, span: Span, }, + #[error("\"__{intrinsic}\" intrinsic's argument \"{arg}\" must be a constant of type `{expected_type}`.")] + IntrinsicArgNotConstant { + intrinsic: String, + arg: String, + expected_type: String, + span: Span, + }, #[error("Expected string literal")] ExpectedStringLiteral { span: Span }, #[error("\"break\" used outside of a loop")] @@ -1310,6 +1317,7 @@ impl Spanned for CompileError { IntrinsicUnsupportedArgType { span, .. } => span.clone(), IntrinsicIncorrectNumArgs { span, .. } => span.clone(), IntrinsicIncorrectNumTArgs { span, .. } => span.clone(), + IntrinsicArgNotConstant { span, .. } => span.clone(), BreakOutsideLoop { span } => span.clone(), ContinueOutsideLoop { span } => span.clone(), ContractIdValueNotALiteral { span } => span.clone(), @@ -3307,60 +3315,66 @@ impl ToDiagnostic for CompileError { hints: vec![], help: vec![], }, - MultipleDefinitionsOfConstant { name, old, new } => { - Diagnostic { - reason: Some(Reason::new(code(1), "Multiple definitions of constant".into())), - issue: Issue::error( + MultipleDefinitionsOfConstant { name, old, new } => Diagnostic { + reason: Some(Reason::new(code(1), "Multiple definitions of constant".into())), + issue: Issue::error( + source_engine, + new.clone(), + format!("Constant \"{name}\" was already defined"), + ), + hints: vec![ + Hint::error( source_engine, - new.clone(), - format!("Constant \"{name}\" was already defined"), + old.clone(), + "Its first definition is here.".into(), ), - hints: vec![ - Hint::error( - source_engine, - old.clone(), - "Its first definition is here.".into(), - ), - ], - help: vec![], - } - } - MethodNotFound { called_method, expected_signature, type_name, matching_methods } => { - Diagnostic { - reason: Some(Reason::new(code(1), "Associated function or method is not found".into())), - issue: Issue::error( + ], + help: vec![], + }, + MethodNotFound { called_method, expected_signature, type_name, matching_methods } => Diagnostic { + reason: Some(Reason::new(code(1), "Associated function or method is not found".into())), + issue: Issue::error( + source_engine, + called_method.span(), + format!("\"{expected_signature}\" is not found for type \"{type_name}\"."), + ), + hints: if matching_methods.is_empty() { + vec![] + } else { + Hint::multi_help( source_engine, - called_method.span(), - format!("\"{expected_signature}\" is not found for type \"{type_name}\"."), - ), - hints: if matching_methods.is_empty() { - vec![] - } else { - Hint::multi_help( - source_engine, - &called_method.span(), - std::iter::once( - format!("{} \"{called_method}\" function{} {} implemented for the type:", - singular_plural(matching_methods.len(), "Only this", "These"), - plural_s(matching_methods.len()), - is_are(matching_methods.len()), - ) + &called_method.span(), + std::iter::once( + format!("{} \"{called_method}\" function{} {} implemented for the type:", + singular_plural(matching_methods.len(), "Only this", "These"), + plural_s(matching_methods.len()), + is_are(matching_methods.len()), ) - .chain(matching_methods - .iter() - .map(|m| format!("- {m}")) + ) + .chain(matching_methods + .iter() + .map(|m| format!("- {m}")) + ) + .chain(std::iter::once( + format!("Did you mean to call {}?", + singular_plural(matching_methods.len(), "that function", "one of those functions"), ) - .chain(std::iter::once( - format!("Did you mean to call {}?", - singular_plural(matching_methods.len(), "that function", "one of those functions"), - ) - )) - .collect() - ) - }, - help: vec![], - } - } + )) + .collect() + ) + }, + help: vec![], + }, + IntrinsicArgNotConstant { intrinsic, arg, expected_type, span } => Diagnostic { + reason: Some(Reason::new(code(1), "Intrinsic argument is not a compile-time constant".into())), + issue: Issue::error( + source_engine, + span.clone(), + format!("\"__{intrinsic}\" intrinsic's argument \"{arg}\" must be a constant of type `{expected_type}`."), + ), + hints: vec![], + help: vec![], + }, _ => Diagnostic { // TODO: Temporarily we use `self` here to achieve backward compatibility. // In general, `self` must not be used. All the values for the formatting diff --git a/sway-features/src/lib.rs b/sway-features/src/lib.rs index 391278454af..f19cd1413c4 100644 --- a/sway-features/src/lib.rs +++ b/sway-features/src/lib.rs @@ -174,7 +174,9 @@ features! { new_hashing = true, "https://github.com/FuelLabs/sway/issues/7256", str_array_no_padding = false, - "https://github.com/FuelLabs/sway/issues/7528" + "https://github.com/FuelLabs/sway/issues/7528", + aligned_and_dynamic_storage = false, + "https://github.com/FuelLabs/sway/issues/7560", } #[derive(Clone, Debug, Default, Parser)] diff --git a/sway-ir/src/analysis/memory_utils.rs b/sway-ir/src/analysis/memory_utils.rs index f044fdbca50..1aac891e6e4 100644 --- a/sway-ir/src/analysis/memory_utils.rs +++ b/sway-ir/src/analysis/memory_utils.rs @@ -508,7 +508,9 @@ pub fn get_loaded_ptr_values(context: &Context, inst: Value) -> Vec { log_val: src_val_ptr, .. }) - | InstOp::FuelVm(FuelVmInstruction::StateLoadWord(src_val_ptr)) + | InstOp::FuelVm(FuelVmInstruction::StateLoadWord { + key: src_val_ptr, .. + }) | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { key: src_val_ptr, .. }) @@ -606,7 +608,7 @@ pub fn get_stored_ptr_values(context: &Context, inst: Value) -> Vec { | FuelVmInstruction::Smo { .. } | FuelVmInstruction::StateClear { .. } => vec![], FuelVmInstruction::StateLoadQuadWord { load_val, .. } => vec![*load_val], - FuelVmInstruction::StateLoadWord(_) | FuelVmInstruction::StateStoreWord { .. } => { + FuelVmInstruction::StateLoadWord { .. } | FuelVmInstruction::StateStoreWord { .. } => { vec![] } FuelVmInstruction::StateStoreQuadWord { stored_val: _, .. } => vec![], diff --git a/sway-ir/src/error.rs b/sway-ir/src/error.rs index 07b89cfef74..f3b2a9eccf9 100644 --- a/sway-ir/src/error.rs +++ b/sway-ir/src/error.rs @@ -48,6 +48,7 @@ pub enum IrError { VerifyIntToPtrUnknownSourceType, VerifyAllocCountNotUint64, VerifyInvalidGtfIndexType, + VerifyInvalidGtfTxFieldIdSize(u64), VerifyLoadFromNonPointer(String), VerifyLocalMissingInitializer(String, String), VerifyLogId, @@ -73,6 +74,7 @@ pub enum IrError { VerifyStateDestBadType(String), VerifyStateKeyBadType, VerifyStateKeyNonPointer(String), + VerifyStateLoadWordOffsetSize(u64), VerifyStoreMismatchedTypes(Option), VerifyStoreToNonPointer(String), VerifyUntypedValuePassedToFunction, @@ -396,6 +398,10 @@ impl fmt::Display for IrError { "Verification failed: State access operation must be to a {ty} pointer." ) } + IrError::VerifyStateLoadWordOffsetSize(offset) => write!( + f, + "Verification failed: 'state_load_word' instruction has offset that does not fit in 6 bits: {offset}." + ), IrError::VerifyStoreMismatchedTypes(_) => { write!( f, @@ -411,7 +417,11 @@ impl fmt::Display for IrError { ), IrError::VerifyInvalidGtfIndexType => write!( f, - "Verification failed: An non-integer value has been passed to a 'gtf' instruction." + "Verification failed: A non-integer value has been passed as index to a 'gtf' instruction." + ), + IrError::VerifyInvalidGtfTxFieldIdSize(tx_field_id) => write!( + f, + "Verification failed: 'gtf' instruction has transaction field ID that does not fit in 12 bits: {tx_field_id}." ), IrError::VerifyLogId => { write!(f, "Verification failed: log ID must be an integer.") diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index 5a7faf240aa..60ab4e0ec99 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -365,8 +365,11 @@ pub enum FuelVmInstruction { key: Value, number_of_slots: Value, }, - /// Reads and returns single word from a storage slot. - StateLoadWord(Value), + /// Reads and returns single word from a storage slot at offset `offset`. + StateLoadWord { + key: Value, + offset: u64, + }, /// Stores `number_of_slots` slots (`b256` each) starting at address `stored_val` in memory into /// storage starting at key `key`. `key` must be a `b256`. StateStoreQuadWord { @@ -531,7 +534,9 @@ impl InstOp { InstOp::Nop => None, // State load returns a u64, other state ops return a bool. - InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_)) => Some(Type::get_uint64(context)), + InstOp::FuelVm(FuelVmInstruction::StateLoadWord { .. }) => { + Some(Type::get_uint64(context)) + } InstOp::FuelVm(FuelVmInstruction::StateClear { .. }) | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. }) | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. }) @@ -668,7 +673,7 @@ impl InstOp { key, number_of_slots, } => vec![*load_val, *key, *number_of_slots], - FuelVmInstruction::StateLoadWord(key) => vec![*key], + FuelVmInstruction::StateLoadWord { key, .. } => vec![*key], FuelVmInstruction::StateStoreQuadWord { stored_val, key, @@ -1001,7 +1006,7 @@ impl InstOp { panic!("Invalid index for StateLoadQuadWord"); } } - FuelVmInstruction::StateLoadWord(key) => { + FuelVmInstruction::StateLoadWord { key, .. } => { if idx == 0 { *key = replacement; } else { @@ -1238,7 +1243,7 @@ impl InstOp { replace(key); replace(number_of_slots); } - FuelVmInstruction::StateLoadWord(key) => { + FuelVmInstruction::StateLoadWord { key, .. } => { replace(key); } FuelVmInstruction::StateStoreQuadWord { @@ -1332,7 +1337,7 @@ impl InstOp { | InstOp::ConditionalBranch { .. } | InstOp::FuelVm(FuelVmInstruction::Gtf { .. }) | InstOp::FuelVm(FuelVmInstruction::ReadRegister(_)) - | InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_)) + | InstOp::FuelVm(FuelVmInstruction::StateLoadWord { .. }) | InstOp::GetElemPtr { .. } | InstOp::GetLocal(_) | InstOp::GetGlobal(_) @@ -1846,8 +1851,11 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> { ) } - pub fn state_load_word(self, key: Value) -> Value { - insert_instruction!(self, InstOp::FuelVm(FuelVmInstruction::StateLoadWord(key))) + pub fn state_load_word(self, key: Value, offset: u64) -> Value { + insert_instruction!( + self, + InstOp::FuelVm(FuelVmInstruction::StateLoadWord { key, offset }) + ) } pub fn state_store_quad_word( diff --git a/sway-ir/src/optimize/arg_mutability_tagger.rs b/sway-ir/src/optimize/arg_mutability_tagger.rs index 2d75f6ef0fe..f2db995217f 100644 --- a/sway-ir/src/optimize/arg_mutability_tagger.rs +++ b/sway-ir/src/optimize/arg_mutability_tagger.rs @@ -312,7 +312,7 @@ fn analyse_fn( continue 'analyse_next_arg; } } - FuelVmInstruction::StateLoadWord(_) + FuelVmInstruction::StateLoadWord { .. } | FuelVmInstruction::StateStoreWord { .. } => {} FuelVmInstruction::StateStoreQuadWord { .. } => {} FuelVmInstruction::WideUnaryOp { result, .. } diff --git a/sway-ir/src/optimize/fn_dedup.rs b/sway-ir/src/optimize/fn_dedup.rs index cc524e29de6..86729268a77 100644 --- a/sway-ir/src/optimize/fn_dedup.rs +++ b/sway-ir/src/optimize/fn_dedup.rs @@ -252,9 +252,11 @@ fn hash_fn( | crate::FuelVmInstruction::Smo { .. } | crate::FuelVmInstruction::StateClear { .. } | crate::FuelVmInstruction::StateLoadQuadWord { .. } - | crate::FuelVmInstruction::StateLoadWord(_) | crate::FuelVmInstruction::StateStoreQuadWord { .. } | crate::FuelVmInstruction::StateStoreWord { .. } => (), + crate::FuelVmInstruction::StateLoadWord { offset, .. } => { + offset.hash(state) + } crate::FuelVmInstruction::WideUnaryOp { op, .. } => op.hash(state), crate::FuelVmInstruction::WideBinaryOp { op, .. } => op.hash(state), crate::FuelVmInstruction::WideModularOp { op, .. } => op.hash(state), diff --git a/sway-ir/src/optimize/inline.rs b/sway-ir/src/optimize/inline.rs index cb1e9397824..8f40ec4b6a3 100644 --- a/sway-ir/src/optimize/inline.rs +++ b/sway-ir/src/optimize/inline.rs @@ -547,9 +547,9 @@ fn inline_instruction( map_value(key), map_value(number_of_slots), ), - FuelVmInstruction::StateLoadWord(key) => { - new_block.append(context).state_load_word(map_value(key)) - } + FuelVmInstruction::StateLoadWord { key, offset } => new_block + .append(context) + .state_load_word(map_value(key), offset), FuelVmInstruction::StateStoreQuadWord { stored_val, key, diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index 70fc477a9c6..ea8d166c271 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -510,8 +510,8 @@ mod ir_builder { } rule op_state_load_word() -> IrAstOperation - = "state_load_word" _ "key" _ key:id() { - IrAstOperation::StateLoadWord(key) + = "state_load_word" _ "key" _ key:id() comma() offset:decimal() { + IrAstOperation::StateLoadWord(key, offset) } rule op_state_store_quad_word() -> IrAstOperation @@ -906,7 +906,7 @@ mod ir_builder { Smo(String, String, String, String), StateClear(String, String), StateLoadQuadWord(String, String, String), - StateLoadWord(String), + StateLoadWord(String, u64), StateStoreQuadWord(String, String, String), StateStoreWord(String, String), Store(String, String), @@ -1622,9 +1622,9 @@ mod ir_builder { *val_map.get(&number_of_slots).unwrap(), ) .add_metadatum(context, opt_metadata), - IrAstOperation::StateLoadWord(key) => block + IrAstOperation::StateLoadWord(key, offset) => block .append(context) - .state_load_word(*val_map.get(&key).unwrap()) + .state_load_word(*val_map.get(&key).unwrap(), offset) .add_metadatum(context, opt_metadata), IrAstOperation::StateStoreQuadWord(src, key, number_of_slots) => block .append(context) diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 12d99d61a69..f4b11cef96e 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -918,11 +918,12 @@ fn instruction_to_doc<'a>( .append(md_namer.md_idx_to_doc(context, metadata)), ), ), - FuelVmInstruction::StateLoadWord(key) => Doc::line( + FuelVmInstruction::StateLoadWord { key, offset } => Doc::line( Doc::text(format!( - "{} = state_load_word key {}", + "{} = state_load_word key {}, {}", namer.name(context, ins_value), namer.name(context, key), + offset, )) .append(md_namer.md_idx_to_doc(context, metadata)), ), diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 531350a48dd..98858891dcc 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -309,7 +309,9 @@ impl InstructionVerifier<'_, '_> { key, number_of_slots, } => self.verify_state_clear(key, number_of_slots)?, - FuelVmInstruction::StateLoadWord(key) => self.verify_state_load_word(key)?, + FuelVmInstruction::StateLoadWord { key, offset } => { + self.verify_state_load_word(key, offset)? + } FuelVmInstruction::StateLoadQuadWord { load_val: dst_val, key, @@ -891,13 +893,18 @@ impl InstructionVerifier<'_, '_> { } } - fn verify_gtf(&self, index: &Value, _tx_field_id: &u64) -> Result<(), IrError> { - // We should perhaps verify that _tx_field_id fits in a twelve bit immediate + fn verify_gtf(&self, index: &Value, tx_field_id: &u64) -> Result<(), IrError> { if !index.get_type(self.context).is(Type::is_uint, self.context) { - Err(IrError::VerifyInvalidGtfIndexType) - } else { - Ok(()) + return Err(IrError::VerifyInvalidGtfIndexType); } + + const TWELVE_BITS: u64 = 0b1111_1111_1111; + + if *tx_field_id > TWELVE_BITS { + return Err(IrError::VerifyInvalidGtfTxFieldIdSize(*tx_field_id)); + } + + Ok(()) } fn verify_int_to_ptr(&self, value: &Value, ty: &Type) -> Result<(), IrError> { @@ -1144,13 +1151,19 @@ impl InstructionVerifier<'_, '_> { Ok(()) } - fn verify_state_load_word(&self, key: &Value) -> Result<(), IrError> { + fn verify_state_load_word(&self, key: &Value, offset: &u64) -> Result<(), IrError> { let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?; if !key_type.is_b256(self.context) { - Err(IrError::VerifyStateKeyBadType) - } else { - Ok(()) + return Err(IrError::VerifyStateKeyBadType); + } + + const SIX_BITS: u64 = 0b11_1111; + + if *offset > SIX_BITS { + return Err(IrError::VerifyStateLoadWordOffsetSize(*offset)); } + + Ok(()) } fn verify_state_store_word(&self, dst_val: &Value, key: &Value) -> Result<(), IrError> { diff --git a/sway-ir/tests/serialize/storage_load.ir b/sway-ir/tests/serialize/storage_load.ir index 0e3b61830c1..a83c9b6f09b 100644 --- a/sway-ir/tests/serialize/storage_load.ir +++ b/sway-ir/tests/serialize/storage_load.ir @@ -15,8 +15,8 @@ script { v1 = const b256 0x7fbd1192666bfac3767b890bd4d048c940879d316071e20c7c8c81bce2ca41c5 store v1 to v0 v2 = get_local __ptr u64, value_for_x -// check: $VAR = state_load_word key $VAR - v3 = state_load_word key v0 +// check: $VAR = state_load_word key $VAR, 0 + v3 = state_load_word key v0, 0 store v3 to v2 v4 = get_local __ptr u64, value_for_x v5 = load v4 diff --git a/sway-parse/src/expr/op_code.rs b/sway-parse/src/expr/op_code.rs index 960b6bba51c..51bc53e43ae 100644 --- a/sway-parse/src/expr/op_code.rs +++ b/sway-parse/src/expr/op_code.rs @@ -118,7 +118,7 @@ define_op_codes!( (Rvrt, RvrtOpcode, "rvrt", (value)), (Smo, SmoOpcode, "smo", (addr, len, output, coins)), (Scwq, ScwqOpcode, "scwq", (addr, is_set, len)), - (Srw, SrwOpcode, "srw", (ret, is_set, state_addr)), + (Srw, SrwOpcode, "srw", (ret, is_set, state_addr, offset)), (Srwq, SrwqOpcode, "srwq", (addr, is_set, state_addr, count)), (Sww, SwwOpcode, "sww", (state_addr, is_set, value)), (Swwq, SwwqOpcode, "swwq", (state_addr, is_set, addr, count)), diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/src/main.sw index 8e8085fe9a9..2332bdf0cef 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/src/main.sw @@ -10,8 +10,16 @@ impl MyContract for Contract { } } +#[cfg(experimental_aligned_and_dynamic_storage = false)] #[storage(read)] fn f() -> bool { let _ = __state_load_word(b256::zero()); true } + +#[cfg(experimental_aligned_and_dynamic_storage = true)] +#[storage(read)] +fn f() -> bool { + let _ = __state_load_word(b256::zero(), 0); + true +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap index 325d40fec1b..2fb04dfe4ac 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_args/stdout.snap @@ -191,6 +191,7 @@ error: Attribute argument is invalid | ----- help: Valid arguments are: | ----- help: - program_type | ----- help: - target + | ----- help: - experimental_aligned_and_dynamic_storage | ----- help: - experimental_const_generics | ----- help: - experimental_new_encoding | ----- help: - experimental_new_hashing diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_cfg_arg_with_invalid_item/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_cfg_arg_with_invalid_item/stdout.snap index fc911ec32bc..1c9d4e84ca9 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_cfg_arg_with_invalid_item/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_fail/attributes_invalid_cfg_arg_with_invalid_item/stdout.snap @@ -24,6 +24,7 @@ error: Attribute argument is invalid | - help: Valid arguments are: | - help: - program_type | - help: - target + | - help: - experimental_aligned_and_dynamic_storage | - help: - experimental_const_generics | - help: - experimental_new_encoding | - help: - experimental_new_hashing diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/src/main.sw index 86d0f377fd8..ead355c919d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/src/main.sw @@ -20,8 +20,17 @@ fn bar() { } // Explicitly impure. +#[cfg(experimental_aligned_and_dynamic_storage = false)] #[storage(read)] fn baz() -> u64 { let _ = __state_load_word(b256::zero()); 5 } + +// Explicitly impure. +#[cfg(experimental_aligned_and_dynamic_storage = true)] +#[storage(read)] +fn baz() -> u64 { + let _ = __state_load_word(b256::zero(), 0); + 5 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/predicate_invalid_opcodes/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/predicate_invalid_opcodes/src/main.sw index 4812ea32edb..b813077906d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/predicate_invalid_opcodes/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/predicate_invalid_opcodes/src/main.sw @@ -79,7 +79,7 @@ fn main() -> bool { // with #[storage(read, write)] which is not allowed for predicates /* asm(r1: 0, r2: 0, r3) { - srw r1 r2 r3; + srw r1 r2 r3 i0; } asm(r1: 0, r2: 0, r3: 0, r4: 0) { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/src/main.sw index de537f296c0..2e34b4a6f8f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/src/main.sw @@ -7,6 +7,7 @@ abi Abi { struct S { } impl S { + #[cfg(experimental_aligned_and_dynamic_storage = false)] fn read_intrinsics(self) -> Self { let ptr = asm (p: 0) { p: raw_ptr }; let _ = __state_load_word(b256::zero()); @@ -15,6 +16,15 @@ impl S { self } + #[cfg(experimental_aligned_and_dynamic_storage = true)] + fn read_intrinsics(self) -> Self { + let ptr = asm (p: 0) { p: raw_ptr }; + let _ = __state_load_word(b256::zero(), 0); + let _ = __state_load_quad(b256::zero(), ptr, 1); + + self + } + #[storage(read)] fn write_intrinsics(self) -> Self { let ptr = asm (p: 0) { p: raw_ptr }; @@ -47,7 +57,7 @@ impl Abi for Contract { fn read_asm_instructions() { asm(r1, r2, r3: 0) { - srw r1 r2 r3; + srw r1 r2 r3 i0; } asm(r1: 0, r2, r3: 0, r4: 0) { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/test.toml index fa92c337a22..a077aede218 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/purity_of_asm_instructions_and_intrinsics/test.toml @@ -4,7 +4,7 @@ category = "fail" #sameln: $()Pure function cannot access storage #check: $()fn read_asm_instructions() #nextln: $()Function "read_asm_instructions" is pure and cannot access storage. -#check: $()srw r1 r2 r3; +#check: $()srw r1 r2 r3 i0; #nextln: $()Reading a word from the storage happens here. #check: $()srwq r1 r2 r3 r4; #nextln: $()Reading storage slots happens here. @@ -32,7 +32,7 @@ category = "fail" #sameln: $()Pure function cannot access storage #check: $()fn read_intrinsics(self) -> Self #nextln: $()Function "read_intrinsics" is pure and cannot access storage. -#check: $()let _ = __state_load_word(b256::zero()); +#check: $()let _ = __state_load_word(b256::zero() #nextln: $()Reading a word from the storage happens here. #check: $()let _ = __state_load_quad(b256::zero(), ptr, 1); #nextln: $()Reading storage slots happens here. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/src/do_storage.sw b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/src/do_storage.sw index 35909a57cac..31ade9320c8 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/src/do_storage.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/src/do_storage.sw @@ -5,7 +5,7 @@ const KEY: b256 = 0xfafafafafafafafafafafafafafafafafafafafafafafafafafafafafafa #[storage(read, write)] pub fn side_effects() { asm(key: KEY, is_set, v) { - srw v is_set key; + srw v is_set key i0; sww key is_set v; } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml index 3ada048855f..0ce1eb34659 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml @@ -8,7 +8,7 @@ category = "fail" #nextln: $()Function "side_effects" reads from and writes to the storage. #check: $()Consider declaring the function "main" as `#[storage(read, write)]` -#check: $()srw v is_set key; +#check: $()srw v is_set key i0; #nextln: $()Contract storage cannot be used in an external context. #check: $()sww key is_set v; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/src/main.sw index fae1a32f505..92381ae955a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/src/main.sw @@ -102,7 +102,7 @@ const KEY: b256 = 0xfefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe fn read_storage_word() -> u64 { asm (key: KEY, is_set, res) { - srw res is_set key; + srw res is_set key i0; res: u64 } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/test.toml index f02b083598d..51ef6c5228f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_purity_conflict/test.toml @@ -4,7 +4,7 @@ category = "fail" #sameln: $()Pure function cannot access storage #check: $()fn read_storage_word() -> u64 #nextln: $()Function "read_storage_word" is pure and cannot access storage. -#check: $()srw res is_set key; +#check: $()srw res is_set key i0; #nextln: $()Reading a word from the storage happens here. #check: $()Consider declaring the function "read_storage_word" as `#[storage(read)]` diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/src/main.sw index 5e70bda7632..abc0b4849c0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/src/main.sw @@ -12,11 +12,19 @@ trait A { struct S {} impl A for S { + #[cfg(experimental_aligned_and_dynamic_storage = false)] #[storage(read)] fn f(self) -> bool { let _ = __state_load_word(b256::zero()); true } + + #[cfg(experimental_aligned_and_dynamic_storage = true)] + #[storage(read)] + fn f(self) -> bool { + let _ = __state_load_word(b256::zero(), 0); + true + } } abi Abi { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/stdout.snap index bb1904f3128..707e2390736 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/stdout.snap @@ -1,5 +1,6 @@ --- source: test/src/snapshot/mod.rs +assertion_line: 124 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch exit status: 0 @@ -12,35 +13,35 @@ output: > forc parse-bytecode test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/out/debug/asm_patch.bin exit status: 0 output: - - half-word byte op raw notes - 0 0 MOVE { dst: 0x3c, src: 0x3 } 1a f0 30 00 - 1 4 JMPF { dynamic: 0x0, fixed: 4 } 74 00 00 04 - 2 8 InvalidOpcode 00 00 00 00 data section offset lo (0) - 3 12 InvalidOpcode 00 00 00 68 data section offset hi (104) - 4 16 InvalidOpcode 00 00 00 00 configurables offset lo (0) - 5 20 InvalidOpcode 00 00 00 68 configurables offset hi (104) - 6 24 LW { dst: 0x3f, addr: 0x3c, offset: 1 } 5d ff c0 01 - 7 28 ADD { dst: 0x3f, lhs: 0x3f, rhs: 0x3c } 10 ff ff 00 - 8 32 MOVE { dst: 0x3b, src: 0x5 } 1a ec 50 00 - 9 36 CFEI { amount: 24 } 91 00 00 18 - 10 40 JAL { ret_addr: 0x3e, target: 0x3, offset: 8 } 99 f8 30 08 - 11 44 SW { addr: 0x3b, value: 0x3d, offset: 0 } 5f ef d0 00 - 12 48 SW { addr: 0x3b, value: 0x3b, offset: 1 } 5f ef b0 01 - 13 52 MOVI { dst: 0x34, val: 8 } 72 d0 00 08 - 14 56 SW { addr: 0x3b, value: 0x34, offset: 2 } 5f ef 40 02 - 15 60 LW { dst: 0x34, addr: 0x3b, offset: 1 } 5d d3 b0 01 - 16 64 LW { dst: 0x33, addr: 0x3b, offset: 2 } 5d cf b0 02 - 17 68 RETD { addr: 0x34, len: 0x33 } 25 d3 30 00 - 18 72 PSHH { bitmask: 528384 } 96 08 10 00 - 19 76 ADDI { dst: 0x34, lhs: 0x0, rhs: 3 } 50 d0 00 03 - 20 80 LOG { a: 0x0, b: 0x0, c: 0x0, d: 0x0 } 33 00 00 00 - 21 84 ADDI { dst: 0x34, lhs: 0x34, rhs: 5 } 50 d3 40 05 - 22 88 MOVE { dst: 0x3d, src: 0x34 } 1a f7 40 00 - 23 92 POPH { bitmask: 528384 } 98 08 10 00 - 24 96 JAL { ret_addr: 0x0, target: 0x3e, offset: 0 } 99 03 e0 00 - 25 100 NOOP 47 00 00 00 - + + half-word byte op raw notes + 0 0 MOVE { dst: 0x3c, src: 0x3 } 1a f0 30 00 + 1 4 JMPF { dynamic: 0x0, fixed: 4 } 74 00 00 04 + 2 8 InvalidOpcode 00 00 00 00 data section offset lo (0) + 3 12 InvalidOpcode 00 00 00 68 data section offset hi (104) + 4 16 InvalidOpcode 00 00 00 00 configurables offset lo (0) + 5 20 InvalidOpcode 00 00 00 68 configurables offset hi (104) + 6 24 LW { dst: 0x3f, ptr: 0x3c, offset: 1 } 5d ff c0 01 + 7 28 ADD { dst: 0x3f, lhs: 0x3f, rhs: 0x3c } 10 ff ff 00 + 8 32 MOVE { dst: 0x3b, src: 0x5 } 1a ec 50 00 + 9 36 CFEI { amount: 24 } 91 00 00 18 + 10 40 JAL { ret_ptr: 0x3e, target: 0x3, offset: 8 } 99 f8 30 08 + 11 44 SW { ptr: 0x3b, value: 0x3d, offset: 0 } 5f ef d0 00 + 12 48 SW { ptr: 0x3b, value: 0x3b, offset: 1 } 5f ef b0 01 + 13 52 MOVI { dst: 0x34, val: 8 } 72 d0 00 08 + 14 56 SW { ptr: 0x3b, value: 0x34, offset: 2 } 5f ef 40 02 + 15 60 LW { dst: 0x34, ptr: 0x3b, offset: 1 } 5d d3 b0 01 + 16 64 LW { dst: 0x33, ptr: 0x3b, offset: 2 } 5d cf b0 02 + 17 68 RETD { ptr: 0x34, len: 0x33 } 25 d3 30 00 + 18 72 PSHH { bitmask: 528384 } 96 08 10 00 + 19 76 ADDI { dst: 0x34, lhs: 0x0, rhs: 3 } 50 d0 00 03 + 20 80 LOG { a: 0x0, b: 0x0, c: 0x0, d: 0x0 } 33 00 00 00 + 21 84 ADDI { dst: 0x34, lhs: 0x34, rhs: 5 } 50 d3 40 05 + 22 88 MOVE { dst: 0x3d, src: 0x34 } 1a f7 40 00 + 23 92 POPH { bitmask: 528384 } 98 08 10 00 + 24 96 JAL { ret_ptr: 0x0, target: 0x3e, offset: 0 } 99 03 e0 00 + 25 100 NOOP 47 00 00 00 + > patch-bin debug @@ -48,35 +49,35 @@ output: > forc parse-bytecode test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/out/debug/asm_patch.bin exit status: 0 output: - - half-word byte op raw notes - 0 0 MOVE { dst: 0x3c, src: 0x3 } 1a f0 30 00 - 1 4 JMPF { dynamic: 0x0, fixed: 4 } 74 00 00 04 - 2 8 InvalidOpcode 00 00 00 00 data section offset lo (0) - 3 12 InvalidOpcode 00 00 00 68 data section offset hi (104) - 4 16 InvalidOpcode 00 00 00 00 configurables offset lo (0) - 5 20 InvalidOpcode 00 00 00 68 configurables offset hi (104) - 6 24 LW { dst: 0x3f, addr: 0x3c, offset: 1 } 5d ff c0 01 - 7 28 ADD { dst: 0x3f, lhs: 0x3f, rhs: 0x3c } 10 ff ff 00 - 8 32 MOVE { dst: 0x3b, src: 0x5 } 1a ec 50 00 - 9 36 CFEI { amount: 24 } 91 00 00 18 - 10 40 JAL { ret_addr: 0x3e, target: 0x3, offset: 8 } 99 f8 30 08 - 11 44 SW { addr: 0x3b, value: 0x3d, offset: 0 } 5f ef d0 00 - 12 48 SW { addr: 0x3b, value: 0x3b, offset: 1 } 5f ef b0 01 - 13 52 MOVI { dst: 0x34, val: 8 } 72 d0 00 08 - 14 56 SW { addr: 0x3b, value: 0x34, offset: 2 } 5f ef 40 02 - 15 60 LW { dst: 0x34, addr: 0x3b, offset: 1 } 5d d3 b0 01 - 16 64 LW { dst: 0x33, addr: 0x3b, offset: 2 } 5d cf b0 02 - 17 68 RETD { addr: 0x34, len: 0x33 } 25 d3 30 00 - 18 72 PSHH { bitmask: 528384 } 96 08 10 00 - 19 76 ADDI { dst: 0x34, lhs: 0x0, rhs: 3 } 50 d0 00 03 - 20 80 ADDI { dst: 0x10, lhs: 0x10, rhs: 4 } 50 41 00 04 - 21 84 ADDI { dst: 0x34, lhs: 0x34, rhs: 5 } 50 d3 40 05 - 22 88 ADDI { dst: 0x10, lhs: 0x10, rhs: 4 } 50 41 00 04 - 23 92 POPH { bitmask: 528384 } 98 08 10 00 - 24 96 JAL { ret_addr: 0x0, target: 0x3e, offset: 0 } 99 03 e0 00 - 25 100 NOOP 47 00 00 00 - + + half-word byte op raw notes + 0 0 MOVE { dst: 0x3c, src: 0x3 } 1a f0 30 00 + 1 4 JMPF { dynamic: 0x0, fixed: 4 } 74 00 00 04 + 2 8 InvalidOpcode 00 00 00 00 data section offset lo (0) + 3 12 InvalidOpcode 00 00 00 68 data section offset hi (104) + 4 16 InvalidOpcode 00 00 00 00 configurables offset lo (0) + 5 20 InvalidOpcode 00 00 00 68 configurables offset hi (104) + 6 24 LW { dst: 0x3f, ptr: 0x3c, offset: 1 } 5d ff c0 01 + 7 28 ADD { dst: 0x3f, lhs: 0x3f, rhs: 0x3c } 10 ff ff 00 + 8 32 MOVE { dst: 0x3b, src: 0x5 } 1a ec 50 00 + 9 36 CFEI { amount: 24 } 91 00 00 18 + 10 40 JAL { ret_ptr: 0x3e, target: 0x3, offset: 8 } 99 f8 30 08 + 11 44 SW { ptr: 0x3b, value: 0x3d, offset: 0 } 5f ef d0 00 + 12 48 SW { ptr: 0x3b, value: 0x3b, offset: 1 } 5f ef b0 01 + 13 52 MOVI { dst: 0x34, val: 8 } 72 d0 00 08 + 14 56 SW { ptr: 0x3b, value: 0x34, offset: 2 } 5f ef 40 02 + 15 60 LW { dst: 0x34, ptr: 0x3b, offset: 1 } 5d d3 b0 01 + 16 64 LW { dst: 0x33, ptr: 0x3b, offset: 2 } 5d cf b0 02 + 17 68 RETD { ptr: 0x34, len: 0x33 } 25 d3 30 00 + 18 72 PSHH { bitmask: 528384 } 96 08 10 00 + 19 76 ADDI { dst: 0x34, lhs: 0x0, rhs: 3 } 50 d0 00 03 + 20 80 ADDI { dst: 0x10, lhs: 0x10, rhs: 4 } 50 41 00 04 + 21 84 ADDI { dst: 0x34, lhs: 0x34, rhs: 5 } 50 d3 40 05 + 22 88 ADDI { dst: 0x10, lhs: 0x10, rhs: 4 } 50 41 00 04 + 23 92 POPH { bitmask: 528384 } 98 08 10 00 + 24 96 JAL { ret_ptr: 0x0, target: 0x3e, offset: 0 } 99 03 e0 00 + 25 100 NOOP 47 00 00 00 + > fuel-vm run test/src/e2e_vm_tests/test_programs/should_pass/language/asm/asm_patch/out/debug/asm_patch.bin diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.lock new file mode 100644 index 00000000000..1efb139ebd0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "std" +source = "path+from-root-FA2BC496C8744C39" + +[[package]] +name = "storage_instructions" +source = "member" +dependencies = ["std"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.toml new file mode 100644 index 00000000000..c5d3a97b8bc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "storage_instructions" + +[dependencies] +std = { path = "../../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/src/main.sw new file mode 100644 index 00000000000..8bdcdd9e010 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/src/main.sw @@ -0,0 +1,115 @@ +contract; + +impl Contract { + // Empty slots can be read at any offset, and should always return 0 and not be marked as set. + #[storage(read, write)] + fn srw_empty_slots() { + let slot = b256::zero(); + + let is_set_res = (0u64, 0u64); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i0; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 0); + assert_eq(res, 0); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i42; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 0); + assert_eq(res, 0); + } + + // Occupied slots should be readable at the correct offsets, and marked as set. + #[storage(read, write)] + fn srw_occupied_slots_valid_offset() { + let slot = b256::zero(); + + let slots_data = [42u64, 43u64, 44u64, 45u64]; + __state_store_quad(slot, __addr_of(slots_data), 1); + + let is_set_res = (0u64, 0u64); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i0; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 1); + assert_eq(res, 42); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i1; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 1); + assert_eq(res, 43); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i2; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 1); + assert_eq(res, 44); + + let (is_set, res) = asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i3; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + assert_eq(is_set, 1); + assert_eq(res, 45); + } + + // Reading out of slot bounds must revert. + #[storage(read, write)] + fn srw_occupied_slots_offset_out_of_bounds() { + let slot = b256::zero(); + + let slots_data = [42u64, 43u64, 44u64, 45u64]; + __state_store_quad(slot, __addr_of(slots_data), 1); + + let is_set_res = (0u64, 0u64); + + asm(slot: slot, is_set, res, is_set_res: is_set_res) { + srw res is_set slot i4; + sw is_set_res is_set i0; + sw is_set_res res i1; + is_set_res: (u64, u64) + }; + } +} + +#[inline(never)] +fn poke(_t: T) {} + +#[test] +fn test_srw_empty_slots() { + let caller = abi(StorageInstructionsAbi, CONTRACT_ID); + caller.srw_empty_slots(); +} + +#[test] +fn test_srw_occupied_slots_valid_offsets() { + let caller = abi(StorageInstructionsAbi, CONTRACT_ID); + caller.srw_occupied_slots_valid_offset(); +} + +#[test(should_revert)] +fn test_srw_occupied_slots_offset_out_of_bounds() { + let caller = abi(StorageInstructionsAbi, CONTRACT_ID); + caller.srw_occupied_slots_offset_out_of_bounds(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/test.toml new file mode 100644 index 00000000000..0f3f6d7e866 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm/storage_instructions/test.toml @@ -0,0 +1 @@ +category = "unit_tests_pass" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/pusha_popa_multiple_defreg/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/pusha_popa_multiple_defreg/src/main.sw index 6c6b17340d7..9e12cc78e06 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/pusha_popa_multiple_defreg/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/pusha_popa_multiple_defreg/src/main.sw @@ -37,7 +37,7 @@ fn store_read() -> u64 { movi c i32; aloc c; move slot hp; - srw a b slot; // somehow make b allocate to $r3 + srw a b slot i0; // somehow make b allocate to $r3 movi c i0; add a a slot; sub a a slot; @@ -52,5 +52,5 @@ fn store_read() -> u64 { fn incorrect_pusha_popa() -> () { let c = abi(IncorrectPushaPopa, CONTRACT_ID); c.incorrect_pusha_popa(); - () + () } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_in_asm_block_read/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_in_asm_block_read/src/main.sw index e7a58fd45ca..b601e877a02 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_in_asm_block_read/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_in_asm_block_read/src/main.sw @@ -16,7 +16,7 @@ impl TestAbi for Contract { other_contract.deposit(); // effect -- therefore violation of CEI where effect should go before interaction asm(key: KEY, is_set, res) { - srw res is_set key; + srw res is_set key i0; res: u64 } } 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 6386ef8f3e5..4192bf2f033 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 @@ -42,11 +42,18 @@ impl BasicStorage for Contract { write(key, 0, value); } + #[cfg(experimental_aligned_and_dynamic_storage = false)] #[storage(read)] fn intrinsic_load_word(key: b256) -> u64 { __state_load_word(key) } + #[cfg(experimental_aligned_and_dynamic_storage = true)] + #[storage(read)] + fn intrinsic_load_word(key: b256) -> u64 { + __state_load_word(key, 0) + } + #[storage(write)] fn intrinsic_store_word(key: b256, value: u64) { let _ = __state_store_word(key, value); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.debug.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.debug.json index d10a5c0ec6f..bb3b7680843 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.debug.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.debug.json @@ -84,6 +84,12 @@ }, { "attributes": [ + { + "arguments": [ + "experimental_aligned_and_dynamic_storage" + ], + "name": "cfg" + }, { "arguments": [ "read" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.release.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.release.json index d10a5c0ec6f..bb3b7680843 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.release.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/json_abi_oracle_new_encoding.release.json @@ -84,6 +84,12 @@ }, { "attributes": [ + { + "arguments": [ + "experimental_aligned_and_dynamic_storage" + ], + "name": "cfg" + }, { "arguments": [ "read" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw index d84c9d9ee3b..aa1e8c2751c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_namespace/src/main.sw @@ -36,11 +36,18 @@ impl BasicStorage for Contract { write(key, 0, value); } + #[cfg(experimental_aligned_and_dynamic_storage = false)] #[storage(read)] fn intrinsic_load_word(key: b256) -> u64 { __state_load_word(key) } + #[cfg(experimental_aligned_and_dynamic_storage = true)] + #[storage(read)] + fn intrinsic_load_word(key: b256) -> u64 { + __state_load_word(key, 0) + } + #[storage(write)] fn intrinsic_store_word(key: b256, value: u64) { let _ = __state_store_word(key, value); diff --git a/test/src/ir_generation/tests/state_load_word.sw b/test/src/ir_generation/tests/state_load_word.sw index 9b24e393eb9..f48c7b95964 100644 --- a/test/src/ir_generation/tests/state_load_word.sw +++ b/test/src/ir_generation/tests/state_load_word.sw @@ -9,6 +9,6 @@ fn main() { // check: $(v0=$VAL) = get_local __ptr b256, __anon_0, $(meta=$MD) // check: $(v1=$VAL) = const b256 0x0000000000000000000000000000000000000000000000000000000000000001, // check: store $v1 to $v0 -// check: $(v2=$VAL) = state_load_word key $v0, $meta +// check: $(v2=$VAL) = state_load_word key $v0, 0, $meta // check: $(v3=$VAL) = get_local __ptr u64, _, // check: store $v2 to $v3, diff --git a/test/src/ir_generation/tests/storage_metadata.sw b/test/src/ir_generation/tests/storage_metadata.sw index 0b3345f1265..9e67acb64c3 100644 --- a/test/src/ir_generation/tests/storage_metadata.sw +++ b/test/src/ir_generation/tests/storage_metadata.sw @@ -25,7 +25,7 @@ impl Incrementor for Contract { #[storage(read, write)] fn increment(increment_by: u64) -> u64 { let new_val = asm(key: KEY, is_set, i: increment_by, res) { - srw res is_set key; + srw res is_set key i0; add res res i; sww key is_set res; res: u64 @@ -36,7 +36,7 @@ impl Incrementor for Contract { #[storage(read)] fn get() -> u64 { asm(key: KEY, is_set, res) { - srw res is_set key; + srw res is_set key i0; res: u64 } } @@ -56,7 +56,7 @@ impl Incrementor for Contract { // unordered: $(readwrite_fn_name_md=$MD) = fn_name_span $MD 553 562 // unordered: $(read_md=$MD) = purity "reads" -// unordered: $(read_fn_name_md=$MD) = fn_name_span $MD 833 836 +// unordered: $(read_fn_name_md=$MD) = fn_name_span $MD 836 839 // The span idx is first, then the storage attribute, then the function name attribute.