diff --git a/.gitlab/ci/pipelines/before_merging.yml b/.gitlab/ci/pipelines/before_merging.yml index 1c3343613d5602127c0ab285dc1afc39528e00d3..d437da084f52f9b9178ca3d042a1f3d13cccde77 100644 --- a/.gitlab/ci/pipelines/before_merging.yml +++ b/.gitlab/ci/pipelines/before_merging.yml @@ -1175,7 +1175,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -1194,7 +1193,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.build_dsn_node: @@ -2878,92 +2876,6 @@ test_riscv_kernels: SCCACHE_DIR: $CI_PROJECT_DIR/_sccache SCCACHE_CACHE_SIZE: 5G -test_long_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - changes: - - .gitlab-ci.yml - - .gitlab/**/* - - images/**/* - - sdk/rust/**/* - - src/kernel_sdk/**/* - - src/riscv/**/* - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-long - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - -test_miri_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - changes: - - .gitlab-ci.yml - - .gitlab/**/* - - images/**/* - - sdk/rust/**/* - - src/kernel_sdk/**/* - - src/riscv/**/* - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-miri - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - test_evm_compatibility: image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} stage: test diff --git a/.gitlab/ci/pipelines/merge_train.yml b/.gitlab/ci/pipelines/merge_train.yml index 4ad05ec264166c2013da45e913b61b1aa774c44c..fe674c13e380b86b3e8c736430db7d0119df4a9a 100644 --- a/.gitlab/ci/pipelines/merge_train.yml +++ b/.gitlab/ci/pipelines/merge_train.yml @@ -1174,7 +1174,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -1193,7 +1192,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.build_dsn_node: @@ -2877,92 +2875,6 @@ test_riscv_kernels: SCCACHE_DIR: $CI_PROJECT_DIR/_sccache SCCACHE_CACHE_SIZE: 5G -test_long_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - changes: - - .gitlab-ci.yml - - .gitlab/**/* - - images/**/* - - sdk/rust/**/* - - src/kernel_sdk/**/* - - src/riscv/**/* - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-long - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - -test_miri_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - changes: - - .gitlab-ci.yml - - .gitlab/**/* - - images/**/* - - sdk/rust/**/* - - src/kernel_sdk/**/* - - src/riscv/**/* - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-miri - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - test_evm_compatibility: image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} stage: test diff --git a/.gitlab/ci/pipelines/schedule_extended_baker_remote_mode_test.yml b/.gitlab/ci/pipelines/schedule_extended_baker_remote_mode_test.yml index ba93551c40556286d8169782387c07111cb3b98d..0a24aca31d55086453b03766425ed60c1b091dd3 100644 --- a/.gitlab/ci/pipelines/schedule_extended_baker_remote_mode_test.yml +++ b/.gitlab/ci/pipelines/schedule_extended_baker_remote_mode_test.yml @@ -399,7 +399,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -418,7 +417,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.tezt:fetch-records: diff --git a/.gitlab/ci/pipelines/schedule_extended_rpc_test.yml b/.gitlab/ci/pipelines/schedule_extended_rpc_test.yml index ba93551c40556286d8169782387c07111cb3b98d..0a24aca31d55086453b03766425ed60c1b091dd3 100644 --- a/.gitlab/ci/pipelines/schedule_extended_rpc_test.yml +++ b/.gitlab/ci/pipelines/schedule_extended_rpc_test.yml @@ -399,7 +399,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -418,7 +417,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.tezt:fetch-records: diff --git a/.gitlab/ci/pipelines/schedule_extended_test.yml b/.gitlab/ci/pipelines/schedule_extended_test.yml index 454b95e0473e2c423b010de5c0ca930687f076e9..cd4882ad6f4cddad1409ad58019810f29ad00f6b 100644 --- a/.gitlab/ci/pipelines/schedule_extended_test.yml +++ b/.gitlab/ci/pipelines/schedule_extended_test.yml @@ -769,7 +769,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -788,7 +787,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.build_dsn_node: @@ -2512,80 +2510,6 @@ test_riscv_kernels: SCCACHE_DIR: $CI_PROJECT_DIR/_sccache SCCACHE_CACHE_SIZE: 5G -test_long_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - interruptible: false - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-long - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - -test_miri_riscv_kernels: - image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} - stage: test - tags: - - gcp - rules: - - when: on_success - needs: - - oc.docker:rust-toolchain:amd64 - - check_riscv_kernels - dependencies: - - oc.docker:rust-toolchain:amd64 - timeout: 60 minutes - cache: - - key: cargo-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/.cargo/registry/cache - policy: pull-push - - key: sccache-$CI_JOB_NAME_SLUG - paths: - - $CI_PROJECT_DIR/_sccache - policy: pull-push - interruptible: false - before_script: - - . ./scripts/ci/datadog_send_job_info.sh - - . ./scripts/ci/sccache-start.sh - script: - - make -C src/riscv EXTRA_FLAGS='--no-default-features --features ci' test-miri - after_script: - - ./scripts/ci/sccache-stop.sh - variables: - CC: clang - NATIVE_TARGET: x86_64-unknown-linux-musl - CARGO_NET_OFFLINE: "false" - SCCACHE_DIR: $CI_PROJECT_DIR/_sccache - SCCACHE_CACHE_SIZE: 5G - test_evm_compatibility: image: ${rust_toolchain_image_name}:${rust_toolchain_image_tag} stage: test diff --git a/.gitlab/ci/pipelines/schedule_extended_validation_test.yml b/.gitlab/ci/pipelines/schedule_extended_validation_test.yml index ba93551c40556286d8169782387c07111cb3b98d..0a24aca31d55086453b03766425ed60c1b091dd3 100644 --- a/.gitlab/ci/pipelines/schedule_extended_validation_test.yml +++ b/.gitlab/ci/pipelines/schedule_extended_validation_test.yml @@ -399,7 +399,6 @@ oc.build_kernels: script: - make -f kernels.mk build - make -f etherlink.mk evm_kernel.wasm - - make -C src/riscv riscv-dummy.elf after_script: - ./scripts/ci/sccache-stop.sh variables: @@ -418,7 +417,6 @@ oc.build_kernels: - tx_kernel.wasm - tx_kernel_dal.wasm - dal_echo_kernel.wasm - - src/riscv/riscv-dummy.elf when: on_success oc.tezt:fetch-records: diff --git a/ci/bin/code_verification.ml b/ci/bin/code_verification.ml index 9bec76f136231e25ee70b3f684b3cf884a5b1568..610f2453835d04a85da50c0f5a01346a78e68b56 100644 --- a/ci/bin/code_verification.ml +++ b/ci/bin/code_verification.ml @@ -1738,30 +1738,6 @@ let jobs pipeline_type = riscv_ci_flags; ] in - let job_test_long_riscv_kernels : tezos_job = - make_job_kernel - ~__POS__ - ~name:"test_long_riscv_kernels" - ~changes:changeset_riscv_kernels - ~dependencies:(Dependent [Job job_check_riscv_kernels]) - [ - Format.asprintf - "make -C src/riscv EXTRA_FLAGS='%s' test-long" - riscv_ci_flags; - ] - in - let job_test_miri_riscv_kernels : tezos_job = - make_job_kernel - ~__POS__ - ~name:"test_miri_riscv_kernels" - ~changes:changeset_riscv_kernels - ~dependencies:(Dependent [Job job_check_riscv_kernels]) - [ - Format.asprintf - "make -C src/riscv EXTRA_FLAGS='%s' test-miri" - riscv_ci_flags; - ] - in let job_test_evm_compatibility : tezos_job = make_job_kernel ~__POS__ @@ -1785,8 +1761,6 @@ let jobs pipeline_type = job_audit_riscv_deps; job_check_riscv_kernels; job_test_riscv_kernels; - job_test_long_riscv_kernels; - job_test_miri_riscv_kernels; job_test_evm_compatibility; ] in diff --git a/ci/bin/common.ml b/ci/bin/common.ml index c165537b3594c0b2cb9fc7e6358042d80f2d015a..62f44d96c9d2120cc245182aca6b98eadca89fd1 100644 --- a/ci/bin/common.ml +++ b/ci/bin/common.ml @@ -835,11 +835,7 @@ let job_build_kernels ?rules () : tezos_job = ~image:Images.rust_toolchain ~stage:Stages.build ?rules - [ - "make -f kernels.mk build"; - "make -f etherlink.mk evm_kernel.wasm"; - "make -C src/riscv riscv-dummy.elf"; - ] + ["make -f kernels.mk build"; "make -f etherlink.mk evm_kernel.wasm"] ~artifacts: (artifacts ~name:"build-kernels-$CI_COMMIT_REF_SLUG" @@ -852,7 +848,6 @@ let job_build_kernels ?rules () : tezos_job = "tx_kernel.wasm"; "tx_kernel_dal.wasm"; "dal_echo_kernel.wasm"; - "src/riscv/riscv-dummy.elf"; ]) |> enable_kernels |> enable_sccache ~key:"kernels-sccache" ~path:"$CI_PROJECT_DIR/_sccache" diff --git a/etherlink/lib_wasm_runtime/Cargo.lock b/etherlink/lib_wasm_runtime/Cargo.lock index 9c3f1ed3d26d6b59c9d88635710e9e643b39836e..81a4e52a3e8b2505e40d2e71b8287d110ac5d12a 100644 --- a/etherlink/lib_wasm_runtime/Cargo.lock +++ b/etherlink/lib_wasm_runtime/Cargo.lock @@ -1688,14 +1688,14 @@ dependencies = [ "tezos-evm-logging-bifrost", "tezos-evm-runtime-bifrost", "tezos-indexable-storage-bifrost", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-bifrost", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_bifrost", "thiserror", ] @@ -1722,14 +1722,14 @@ dependencies = [ "tezos-evm-logging-calypso", "tezos-evm-runtime-calypso", "tezos-indexable-storage-calypso", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-calypso", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_calypso", "thiserror", ] @@ -1756,14 +1756,14 @@ dependencies = [ "tezos-evm-logging-calypso2", "tezos-evm-runtime-calypso2", "tezos-indexable-storage-calypso2", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-calypso2", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_calypso2", "thiserror", ] @@ -1791,14 +1791,14 @@ dependencies = [ "tezos-evm-logging-dionysus", "tezos-evm-runtime-dionysus", "tezos-indexable-storage-dionysus", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-dionysus", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_dionysus", "thiserror", ] @@ -1844,16 +1844,16 @@ dependencies = [ "tezos-evm-runtime-bifrost", "tezos-indexable-storage-bifrost", "tezos-smart-rollup", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", + "tezos-smart-rollup-encoding 0.2.2", "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-installer-config", "tezos-smart-rollup-storage", "tezos-storage-bifrost", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_bifrost", "thiserror", ] @@ -1880,16 +1880,16 @@ dependencies = [ "tezos-evm-runtime-calypso", "tezos-indexable-storage-calypso", "tezos-smart-rollup", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", + "tezos-smart-rollup-encoding 0.2.2", "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-installer-config", "tezos-smart-rollup-storage", "tezos-storage-calypso", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_calypso", "thiserror", ] @@ -1916,16 +1916,16 @@ dependencies = [ "tezos-evm-runtime-calypso2", "tezos-indexable-storage-calypso2", "tezos-smart-rollup", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", + "tezos-smart-rollup-encoding 0.2.2", "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-installer-config", "tezos-smart-rollup-storage", "tezos-storage-calypso2", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_calypso2", "thiserror", ] @@ -1954,16 +1954,16 @@ dependencies = [ "tezos-execution-dionysus", "tezos-indexable-storage-dionysus", "tezos-smart-rollup", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", + "tezos-smart-rollup-encoding 0.2.2", "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-installer-config", "tezos-smart-rollup-storage", "tezos-storage-dionysus", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "tezos_ethereum_dionysus", "tezos_tezlink_dionysus", "thiserror", @@ -3350,9 +3350,9 @@ dependencies = [ "tezos-evm-runtime-calypso", "tezos-evm-runtime-calypso2", "tezos-evm-runtime-dionysus", - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", - "tezos_crypto_rs", + "tezos-smart-rollup-core 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos_crypto_rs 0.6.0", "wasmer", "wasmer-c-api", "wasmer-compiler-cranelift", @@ -3361,6 +3361,7 @@ dependencies = [ [[package]] name = "octez-riscv" version = "0.0.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "arbitrary-int", "bincode", @@ -3383,9 +3384,9 @@ dependencies = [ "serde_json", "sha2 0.10.8", "strum 0.26.3", - "tezos-smart-rollup-constants", + "tezos-smart-rollup-constants 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", "tezos-smart-rollup-utils", - "tezos_crypto_rs", + "tezos_crypto_rs 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", "thiserror", "try-blocks", "vm-fdt", @@ -4856,11 +4857,11 @@ version = "0.1.0" dependencies = [ "sha3", "tezos-evm-logging-bifrost", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", ] [[package]] @@ -4869,11 +4870,11 @@ version = "0.1.0" dependencies = [ "sha3", "tezos-evm-logging-calypso", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", ] [[package]] @@ -4882,11 +4883,11 @@ version = "0.1.0" dependencies = [ "sha3", "tezos-evm-logging-calypso2", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", ] [[package]] @@ -4895,11 +4896,11 @@ version = "0.1.0" dependencies = [ "sha3", "tezos-evm-logging-dionysus", - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", ] [[package]] @@ -4910,10 +4911,10 @@ dependencies = [ "primitive-types 0.12.2", "tezos-evm-runtime-dionysus", "tezos-smart-rollup", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-storage-dionysus", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", ] [[package]] @@ -4923,8 +4924,8 @@ dependencies = [ "rlp", "tezos-evm-logging-bifrost", "tezos-evm-runtime-bifrost", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-bifrost", "thiserror", @@ -4937,8 +4938,8 @@ dependencies = [ "rlp", "tezos-evm-logging-calypso", "tezos-evm-runtime-calypso", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-calypso", "thiserror", @@ -4951,8 +4952,8 @@ dependencies = [ "rlp", "tezos-evm-logging-calypso2", "tezos-evm-runtime-calypso2", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-calypso2", "thiserror", @@ -4965,8 +4966,8 @@ dependencies = [ "rlp", "tezos-evm-logging-dionysus", "tezos-evm-runtime-dionysus", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-host 0.2.2", + "tezos-smart-rollup-mock 0.2.2", "tezos-smart-rollup-storage", "tezos-storage-dionysus", "thiserror", @@ -4977,17 +4978,17 @@ name = "tezos-smart-rollup" version = "0.2.2" dependencies = [ "hex", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", + "tezos-smart-rollup-build-utils 0.2.2", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", + "tezos-smart-rollup-encoding 0.2.2", "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-macros", - "tezos-smart-rollup-mock", + "tezos-smart-rollup-mock 0.2.2", "tezos-smart-rollup-storage", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", ] [[package]] @@ -4999,29 +5000,70 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "tezos-smart-rollup-build-utils" +version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "tezos-smart-rollup-constants" +version = "0.2.2" + [[package]] name = "tezos-smart-rollup-constants" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" + +[[package]] +name = "tezos-smart-rollup-core" +version = "0.2.2" +dependencies = [ + "tezos-smart-rollup-build-utils 0.2.2", + "tezos-smart-rollup-constants 0.2.2", +] [[package]] name = "tezos-smart-rollup-core" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-constants", + "tezos-smart-rollup-build-utils 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-constants 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", ] [[package]] name = "tezos-smart-rollup-debug" version = "0.2.2" dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", + "tezos-smart-rollup-core 0.2.2", + "tezos-smart-rollup-host 0.2.2", +] + +[[package]] +name = "tezos-smart-rollup-encoding" +version = "0.2.2" +dependencies = [ + "hex", + "nom 7.1.3", + "num-bigint", + "num-traits", + "paste", + "regex", + "tezos-smart-rollup-core 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", + "thiserror", + "time", ] [[package]] name = "tezos-smart-rollup-encoding" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "hex", "nom 7.1.3", @@ -5029,10 +5071,10 @@ dependencies = [ "num-traits", "paste", "regex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos-smart-rollup-core 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-host 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_crypto_rs 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_data_encoding 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", "thiserror", "time", ] @@ -5043,10 +5085,10 @@ version = "0.2.2" dependencies = [ "cfg-if", "dlmalloc", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", + "tezos-smart-rollup-build-utils 0.2.2", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-panic-hook", ] @@ -5054,10 +5096,22 @@ dependencies = [ name = "tezos-smart-rollup-host" version = "0.2.2" dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos-smart-rollup-build-utils 0.2.2", + "tezos-smart-rollup-core 0.2.2", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", + "thiserror", +] + +[[package]] +name = "tezos-smart-rollup-host" +version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" +dependencies = [ + "tezos-smart-rollup-build-utils 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-core 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_crypto_rs 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_data_encoding 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", "thiserror", ] @@ -5069,11 +5123,11 @@ dependencies = [ "nom 7.1.3", "serde", "serde_yaml", - "tezos-smart-rollup-core", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos-smart-rollup-core 0.2.2", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", "thiserror", ] @@ -5085,19 +5139,32 @@ dependencies = [ "quote", "shellexpand", "syn 2.0.96", - "tezos-smart-rollup-build-utils", + "tezos-smart-rollup-build-utils 0.2.2", +] + +[[package]] +name = "tezos-smart-rollup-mock" +version = "0.2.2" +dependencies = [ + "hex", + "tezos-smart-rollup-core 0.2.2", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", ] [[package]] name = "tezos-smart-rollup-mock" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "hex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos-smart-rollup-core 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-encoding 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-host 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_crypto_rs 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_data_encoding 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", ] [[package]] @@ -5105,35 +5172,36 @@ name = "tezos-smart-rollup-panic-hook" version = "0.2.2" dependencies = [ "rustversion", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", + "tezos-smart-rollup-build-utils 0.2.2", + "tezos-smart-rollup-core 0.2.2", ] [[package]] name = "tezos-smart-rollup-storage" version = "0.2.2" dependencies = [ - "tezos-smart-rollup-core", + "tezos-smart-rollup-core 0.2.2", "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", + "tezos-smart-rollup-encoding 0.2.2", + "tezos-smart-rollup-host 0.2.2", "thiserror", ] [[package]] name = "tezos-smart-rollup-utils" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "clap 4.5.29", "hex", "quanta 0.12.5", "serde", "serde_json", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-mock", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos-smart-rollup-build-utils 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-encoding 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos-smart-rollup-mock 0.2.2 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_crypto_rs 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", + "tezos_data_encoding 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", ] [[package]] @@ -5146,9 +5214,9 @@ dependencies = [ "rlp", "sha3", "tezos-evm-runtime-bifrost", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", - "tezos_crypto_rs", + "tezos_crypto_rs 0.6.0", "tezos_ethereum_bifrost", "thiserror", ] @@ -5163,9 +5231,9 @@ dependencies = [ "rlp", "sha3", "tezos-evm-runtime-calypso", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", - "tezos_crypto_rs", + "tezos_crypto_rs 0.6.0", "tezos_ethereum_calypso", "thiserror", ] @@ -5180,9 +5248,9 @@ dependencies = [ "rlp", "sha3", "tezos-evm-runtime-calypso2", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", - "tezos_crypto_rs", + "tezos_crypto_rs 0.6.0", "tezos_ethereum_calypso2", "thiserror", ] @@ -5197,9 +5265,9 @@ dependencies = [ "rlp", "sha3", "tezos-evm-runtime-dionysus", - "tezos-smart-rollup-host", + "tezos-smart-rollup-host 0.2.2", "tezos-smart-rollup-storage", - "tezos_crypto_rs", + "tezos_crypto_rs 0.6.0", "tezos_ethereum_dionysus", "thiserror", ] @@ -5223,7 +5291,32 @@ dependencies = [ "serde", "strum 0.20.0", "strum_macros 0.20.1", - "tezos_data_encoding", + "tezos_data_encoding 0.6.0", + "thiserror", + "zeroize", +] + +[[package]] +name = "tezos_crypto_rs" +version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" +dependencies = [ + "anyhow", + "bs58 0.5.1", + "byteorder", + "cryptoxide", + "ed25519-dalek", + "hex", + "libsecp256k1", + "nom 7.1.3", + "num-bigint", + "num-traits", + "p256", + "rand 0.7.3", + "serde", + "strum 0.20.0", + "strum_macros 0.20.1", + "tezos_data_encoding 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", "thiserror", "zeroize", ] @@ -5240,13 +5333,43 @@ dependencies = [ "num-bigint", "num-traits", "serde", - "tezos_data_encoding_derive", + "tezos_data_encoding_derive 0.6.0", "thiserror", ] +[[package]] +name = "tezos_data_encoding" +version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" +dependencies = [ + "bit-vec", + "bitvec", + "hex", + "lazy_static", + "nom 7.1.3", + "num-bigint", + "num-traits", + "serde", + "tezos_data_encoding_derive 0.6.0 (git+https://github.com/tezos/riscv-pvm.git)", + "thiserror", +] + +[[package]] +name = "tezos_data_encoding_derive" +version = "0.6.0" +dependencies = [ + "lazy_static", + "once_cell", + "parse-display", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tezos_data_encoding_derive" version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "lazy_static", "once_cell", @@ -5269,8 +5392,8 @@ dependencies = [ "primitive-types 0.12.2", "rlp", "sha3", - "tezos-smart-rollup-encoding", - "tezos_crypto_rs", + "tezos-smart-rollup-encoding 0.2.2", + "tezos_crypto_rs 0.6.0", "thiserror", ] @@ -5287,8 +5410,8 @@ dependencies = [ "primitive-types 0.12.2", "rlp", "sha3", - "tezos-smart-rollup-encoding", - "tezos_crypto_rs", + "tezos-smart-rollup-encoding 0.2.2", + "tezos_crypto_rs 0.6.0", "thiserror", ] @@ -5305,8 +5428,8 @@ dependencies = [ "primitive-types 0.12.2", "rlp", "sha3", - "tezos-smart-rollup-encoding", - "tezos_crypto_rs", + "tezos-smart-rollup-encoding 0.2.2", + "tezos_crypto_rs 0.6.0", "thiserror", ] @@ -5323,8 +5446,8 @@ dependencies = [ "primitive-types 0.12.2", "rlp", "sha3", - "tezos-smart-rollup-encoding", - "tezos_crypto_rs", + "tezos-smart-rollup-encoding 0.2.2", + "tezos_crypto_rs 0.6.0", "thiserror", ] @@ -5337,8 +5460,8 @@ dependencies = [ "num-bigint", "primitive-types 0.12.2", "tezos-smart-rollup", - "tezos_crypto_rs", - "tezos_data_encoding", + "tezos_crypto_rs 0.6.0", + "tezos_data_encoding 0.6.0", ] [[package]] diff --git a/src/riscv/.cargo/config.toml b/src/riscv/.cargo/config.toml index 71b190c085da3b3efce3b5d4c06ed42fb44788ec..c39017e7c3fa4601e9b4a6a1581ad18962d2a12f 100644 --- a/src/riscv/.cargo/config.toml +++ b/src/riscv/.cargo/config.toml @@ -5,6 +5,15 @@ rustdocflags = ["--deny", "warnings"] [target.'cfg(target_os = "macos")'] rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"] +[target.riscv64gc-unknown-linux-musl] +linker = "riscv64-unknown-linux-musl-gcc" +rustflags = [ + "-C", + "target-feature=+crt-static", + "-C", + "default-linker-libraries", +] + [profile.profiling] inherits = "release" debug = true diff --git a/src/riscv/Cargo.lock b/src/riscv/Cargo.lock index 6a1c977539b79a02dd546a0762cd0a7bd33aab8c..6ee85f666dcee2d52dd5ba26156f1ac758adb91e 100644 --- a/src/riscv/Cargo.lock +++ b/src/riscv/Cargo.lock @@ -59,7 +59,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -69,7 +69,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -129,15 +129,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - [[package]] name = "bit-vec" version = "0.6.3" @@ -186,19 +177,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blst" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a30d0edd9dd1c60ddb42b80341c7852f6f985279a5c1a83659dcb65899dec99" -dependencies = [ - "cc", - "glob", - "threadpool", - "which", - "zeroize", -] - [[package]] name = "bs58" version = "0.5.1" @@ -208,17 +186,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata 0.1.10", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -292,30 +259,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" -[[package]] -name = "comfy-table" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" -dependencies = [ - "crossterm", - "strum 0.26.2", - "strum_macros 0.26.2", - "unicode-width", -] - -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -454,7 +397,7 @@ dependencies = [ "region", "target-lexicon", "wasmtime-jit-icache-coherence", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -485,28 +428,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.5.0", - "crossterm_winapi", - "libc", - "parking_lot", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crunchy" version = "0.2.2" @@ -641,44 +562,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dlmalloc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - [[package]] name = "ecdsa" version = "0.12.4" @@ -742,12 +625,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "enum-tag" version = "0.3.0" @@ -774,28 +651,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "ff" version = "0.10.1" @@ -812,42 +673,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "gdbstub" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c683a9f13de31432e6097131d5f385898c7f0635c0f392b9d0fa165063c8ac" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "log", - "managed", - "num-traits", - "paste", -] - -[[package]] -name = "gdbstub_arch" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328a9e9425db13770d0d11de6332a608854266e44c53d12776be7b4aa427e3de" -dependencies = [ - "gdbstub", - "num-traits", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -880,24 +711,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "goldenfile" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d5c44265baec620ea19c97b4ce9f068e28f8c3d7faccc483f02968b5e3c587" -dependencies = [ - "scopeguard", - "similar-asserts", - "tempfile", - "yansi", -] - [[package]] name = "group" version = "0.10.0" @@ -945,12 +758,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -970,15 +777,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "ieee-apsqrt" version = "0.1.1" @@ -1049,22 +847,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - [[package]] name = "libsecp256k1" version = "0.7.1" @@ -1110,22 +892,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -1141,21 +907,6 @@ dependencies = [ "libc", ] -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "meansd" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebeb9fa4bb1970b06e47ca340a805677629a5fecfb6114ce5bd173f3b57e86a3" -dependencies = [ - "ordered-float", -] - [[package]] name = "memchr" version = "2.7.2" @@ -1178,16 +929,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -1221,17 +962,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", ] [[package]] @@ -1255,16 +985,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "numfmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7467e47de9fb6ea5b3f47dc34c1cf0b86359f072a46f6278119544cdbd0021" -dependencies = [ - "dtoa", - "itoa", -] - [[package]] name = "ocaml" version = "1.2.0" @@ -1319,6 +1039,7 @@ dependencies = [ [[package]] name = "octez-riscv" version = "0.0.0" +source = "git+https://github.com/tezos/riscv-pvm.git#7a77dcde69a3ef3245333e8538b15214e3454fdd" dependencies = [ "arbitrary-int", "bincode", @@ -1330,27 +1051,21 @@ dependencies = [ "ed25519-dalek", "elf", "enum-tag", - "goldenfile", "hex", "ieee-apsqrt", "itertools", - "lazy_static", "num_enum", "paste", - "proptest", - "rand 0.8.5", "range-collections", "rustc_apfloat", "serde", "serde_json", "sha2 0.10.8", "strum 0.26.2", - "tempfile", "tezos-smart-rollup-constants", "tezos-smart-rollup-utils", "tezos_crypto_rs", "thiserror", - "tracing", "try-blocks", "vm-fdt", ] @@ -1382,27 +1097,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "3.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" -dependencies = [ - "num-traits", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p256" version = "0.9.0" @@ -1414,29 +1108,6 @@ dependencies = [ "sha2 0.9.9", ] -[[package]] -name = "parking_lot" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "parse-display" version = "0.4.1" @@ -1468,12 +1139,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - [[package]] name = "pkcs8" version = "0.10.2" @@ -1505,28 +1170,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.65", -] - [[package]] name = "proc-macro2" version = "1.0.85" @@ -1536,26 +1179,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proptest" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.5.0", - "lazy_static", - "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift", - "regex-syntax 0.8.3", - "rusty-fork", - "tempfile", - "unarray", -] - [[package]] name = "quanta" version = "0.12.3" @@ -1571,12 +1194,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.36" @@ -1598,7 +1215,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "rand_chacha 0.2.2", + "rand_chacha", "rand_core 0.5.1", "rand_hc", ] @@ -1609,8 +1226,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", - "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -1624,16 +1239,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_core" version = "0.5.1" @@ -1658,15 +1263,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "range-collections" version = "0.4.5" @@ -1689,32 +1285,12 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.1" +name = "ref-cast" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", + "ref-cast-impl", ] [[package]] @@ -1749,16 +1325,10 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", + "regex-automata", "regex-syntax 0.8.3", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - [[package]] name = "regex-automata" version = "0.4.6" @@ -1794,28 +1364,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "riscv-sandbox" -version = "0.0.0" -dependencies = [ - "cfg-if", - "clap", - "comfy-table", - "enum-tag", - "gdbstub", - "gdbstub_arch", - "itertools", - "meansd", - "numfmt", - "octez-riscv", - "quanta", - "serde", - "serde_json", - "tezos-smart-rollup", - "tezos-smart-rollup-encoding", - "tracing-subscriber", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -1841,49 +1389,18 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.23" @@ -1945,24 +1462,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1988,26 +1487,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "similar" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" -dependencies = [ - "bstr", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" -dependencies = [ - "console", - "similar", -] - [[package]] name = "slice-group-by" version = "0.3.1" @@ -2122,68 +1601,32 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tezos-smart-rollup" -version = "0.2.2" -dependencies = [ - "hex", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", - "tezos-smart-rollup-macros", - "tezos-smart-rollup-storage", - "tezos-smart-rollup-utils", - "tezos_crypto_rs", - "tezos_data_encoding", -] - [[package]] name = "tezos-smart-rollup-build-utils" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ - "proc-macro2", - "quote", "rustc_version", ] [[package]] name = "tezos-smart-rollup-constants" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" [[package]] name = "tezos-smart-rollup-core" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "tezos-smart-rollup-build-utils", "tezos-smart-rollup-constants", ] -[[package]] -name = "tezos-smart-rollup-debug" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", -] - [[package]] name = "tezos-smart-rollup-encoding" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "hex", "nom", @@ -2199,22 +1642,10 @@ dependencies = [ "time", ] -[[package]] -name = "tezos-smart-rollup-entrypoint" -version = "0.2.2" -dependencies = [ - "cfg-if", - "dlmalloc", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-host", - "tezos-smart-rollup-panic-hook", -] - [[package]] name = "tezos-smart-rollup-host" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "tezos-smart-rollup-build-utils", "tezos-smart-rollup-core", @@ -2223,20 +1654,10 @@ dependencies = [ "thiserror", ] -[[package]] -name = "tezos-smart-rollup-macros" -version = "0.2.2" -dependencies = [ - "proc-macro-error2", - "quote", - "shellexpand", - "syn 2.0.65", - "tezos-smart-rollup-build-utils", -] - [[package]] name = "tezos-smart-rollup-mock" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "hex", "tezos-smart-rollup-core", @@ -2246,29 +1667,10 @@ dependencies = [ "tezos_data_encoding", ] -[[package]] -name = "tezos-smart-rollup-panic-hook" -version = "0.2.2" -dependencies = [ - "rustversion", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", -] - -[[package]] -name = "tezos-smart-rollup-storage" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "thiserror", -] - [[package]] name = "tezos-smart-rollup-utils" version = "0.2.2" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "clap", "hex", @@ -2285,9 +1687,9 @@ dependencies = [ [[package]] name = "tezos_crypto_rs" version = "0.6.0" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "anyhow", - "blst", "bs58", "byteorder", "cryptoxide", @@ -2310,6 +1712,7 @@ dependencies = [ [[package]] name = "tezos_data_encoding" version = "0.6.0" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "bit-vec", "bitvec", @@ -2326,6 +1729,7 @@ dependencies = [ [[package]] name = "tezos_data_encoding_derive" version = "0.6.0" +source = "git+https://gitlab.com/tezos/tezos.git#890eee3a0c86522bd6f29cc75b6dacb531d0e186" dependencies = [ "lazy_static", "once_cell", @@ -2355,25 +1759,6 @@ dependencies = [ "syn 2.0.65", ] -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "time" version = "0.3.36" @@ -2437,76 +1822,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "nu-ansi-term", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", - "tracing-serde", -] - [[package]] name = "try-blocks" version = "0.1.4" @@ -2519,12 +1834,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -2537,24 +1846,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" -[[package]] -name = "unicode-width" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" - [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - [[package]] name = "version_check" version = "0.9.4" @@ -2567,15 +1864,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2645,7 +1933,7 @@ dependencies = [ "anyhow", "cfg-if", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2658,18 +1946,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -2692,37 +1968,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2731,46 +1983,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -2783,48 +2017,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" @@ -2849,12 +2059,6 @@ dependencies = [ "tap", ] -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - [[package]] name = "zerocopy" version = "0.7.34" @@ -2880,17 +2084,3 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", -] diff --git a/src/riscv/Cargo.toml b/src/riscv/Cargo.toml index 3cc9c9fead945b498ba746626d7bb7b383e75c5d..ac2f664e98f598e17dee991b14ced6cbc9544174 100644 --- a/src/riscv/Cargo.toml +++ b/src/riscv/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["lib", "sandbox", "api"] +members = ["api"] exclude = ["jstz", "dummy_kernel"] [workspace.lints.clippy] diff --git a/src/riscv/Makefile b/src/riscv/Makefile index c21aa4a9169d234649a2ae2f4190427658c23b37..ebb4b93c12a28e877dc572a8e6f21265090f65a2 100644 --- a/src/riscv/Makefile +++ b/src/riscv/Makefile @@ -26,23 +26,8 @@ SANDBOX_ENABLE_FEATURES ?= NIGHTLY_VERSION = nightly-2025-01-30 .PHONY: build -build: riscv-dummy.elf riscv-sandbox +build: @cargo build --release --workspace $(NATIVE_OPT) $(EXTRA_FLAGS) - @make -C jstz build - -.PHONY: riscv-sandbox -riscv-sandbox:: - @cargo build --release --package riscv-sandbox $(NATIVE_OPT) $(SANDBOX_ENABLE_FEATURES:%=-F%) - @ln -f target/$(NATIVE_TARGET)/release/riscv-sandbox $@ - -.PHONY: riscv-sandbox.prof -riscv-sandbox.prof:: - @cargo build --profile profiling --package riscv-sandbox $(SANDBOX_ENABLE_FEATURES:%=-F%) - @ln -f target/profiling/riscv-sandbox $@ - -riscv-dummy.elf:: - @make -C dummy_kernel build - @ln -f dummy_kernel/target/riscv64gc-unknown-linux-musl/release/riscv-dummy $@ # Update checked-in binaries .PHONY: update-assets @@ -50,13 +35,13 @@ update-assets: assets/riscv-dummy.elf assets/jstz # Update the compiled dummy kernel used in CI tests assets/riscv-dummy.elf assets/riscv-dummy.elf.checksum:: - @make -C dummy_kernel build - @cp dummy_kernel/target/riscv64gc-unknown-linux-musl/release/riscv-dummy $@ + @cargo install --config .cargo/config.toml --locked --git https://github.com/tezos/riscv-pvm.git --branch ole/github --target riscv64gc-unknown-linux-musl --root assets riscv-dummy + @mv assets/bin/riscv-dummy $@ @sha256sum $@ > $@.checksum assets/jstz assets/jstz.checksum:: - @make -C jstz build-kernel - @cp jstz/target/riscv64gc-unknown-linux-musl/release/jstz $@ + @cargo install --config .cargo/config.toml --locked --git https://github.com/tezos/riscv-pvm.git --branch ole/github --target riscv64gc-unknown-linux-musl --root assets jstz + @mv assets/bin/jstz $@ @sha256sum $@ > $@.checksum .PHONY: build-deps @@ -77,28 +62,12 @@ endif # 'rustup toolchain install' (Rustup 1.28+) will install the toolchain. @find . -iname 'rust-toolchain*' -execdir sh -c "rustup show active-toolchain || rustup toolchain install" \; 2>/dev/null - @make -C jstz build-deps - # Coverage deps @./scripts/isa-suite-coverage.sh -d .PHONY: test test: build @cargo test --workspace $(EXTRA_FLAGS) - @make -C dummy_kernel test - @make -C jstz test - -.PHONY: test-long -test-long: - @cargo test --release $(EXTRA_FLAGS) -- test_jstz_determinism test_jstz_proofs_one_step --nocapture --ignored - -.PHONY: test-miri -test-miri: - @cargo +$(NIGHTLY_VERSION) miri test $(EXTRA_FLAGS) -- miri - -.PHONY: coverage -coverage: - @./scripts/isa-suite-coverage.sh .PHONY: check check: @@ -110,9 +79,6 @@ check: @cargo clippy --workspace --all-targets $(CHECK_FLAGS) $(EXTRA_FLAGS) -- --deny warnings @cargo doc --document-private-items --no-deps $(CHECK_FLAGS) $(EXTRA_FLAGS) - @make -C dummy_kernel check - @make -C jstz check - .PHONY: audit audit: @# We don't add this to the check target because it requires installation @@ -126,6 +92,3 @@ audit: .PHONY: clean clean: @cargo clean - @make -C dummy_kernel clean - @make -C jstz clean - @rm -f riscv-sandbox riscv-dummy.elf diff --git a/src/riscv/api/Cargo.toml b/src/riscv/api/Cargo.toml index 455d57557752c50b880a0f4fe45b48491d3050ae..9b2a605fd51ba40a1127e7115894661f5adea6b1 100644 --- a/src/riscv/api/Cargo.toml +++ b/src/riscv/api/Cargo.toml @@ -14,7 +14,7 @@ strum.workspace = true ocaml.workspace = true [dependencies.octez-riscv] -path = "../lib" +git = "https://github.com/tezos/riscv-pvm.git" [dependencies.ocaml-sys] # We don't care about the version. Ideally this gets pinned to what `ocaml` needs. diff --git a/src/riscv/assets/dummy-kernel-inbox.json b/src/riscv/assets/dummy-kernel-inbox.json deleted file mode 100644 index 73b723079cd7ce74a4f3f19a0b6e9147151c3a27..0000000000000000000000000000000000000000 --- a/src/riscv/assets/dummy-kernel-inbox.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - [ - "0101020304", - "0101040302" - ], - [ - "010101" - ], - [ - "010102" - ], - [ - "0000030b42b419240509ddacd12839700b7f720b4aa55e4e00c1a960784e695a8993bb22b150b7929b9fdb75bdf4e47cb3c43a68b0d48e3094092ca42d713addb5" - ] -] diff --git a/src/riscv/assets/gen_riscv_tests.sh b/src/riscv/assets/gen_riscv_tests.sh deleted file mode 100755 index 1e48d206cf38d2c5cdeab380bc50646afaee52e1..0000000000000000000000000000000000000000 --- a/src/riscv/assets/gen_riscv_tests.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2320,SC3044 - -# This script builds the riscv unit tests, located here: https://github.com/trilitech/riscv-tests -# It assumes that a GNU riscv toolchin is installed locally. -# To install the GNU riscv toolchain, follow the instructions here: https://github.com/riscv-collab/riscv-gnu-toolchain - -set -e - -build_dir=$(mktemp -d) - -self=$(realpath "$(dirname "$0")") -target_dir=$(realpath "$self"/generated) - -clean_up() { - ARG=$? - echo "exit($ARG) signal caught, cleaning up" - popd || exit - rm -fr "$build_dir" - exit "$ARG" -} -trap clean_up EXIT - -echo "Building riscv unit tests in temporary directory $build_dir" - -pushd "$build_dir" || exit - -git clone --recursive https://github.com/trilitech/riscv-tests . || { - echo "cloning failed" >&2 - clean_up 1 -} - -echo "Saving commit hash" -git rev-parse HEAD > "$self"/riscv-tests.commit - -autoconf || { - echo "autoconf failed" >&2 - clean_up 1 -} - -git apply "$self/riscv-tests-entry-0.patch" -git apply "$self/riscv-tests-setup.patch" - -# If this cross-compilation toolchain is in scope, use it. -# This would be the case in the Nix shell for example. -if which riscv64-unknown-linux-musl-gcc > /dev/null; then - export CC=riscv64-unknown-linux-musl-cc - export RISCV_PREFIX=riscv64-unknown-linux-musl- -fi - -./configure || { - echo "configure failed" >&2 - clean_up 1 -} - -make -j 8 -C isa rv64ui rv64uc rv64um rv64ua rv64uf rv64ud || { - echo "make isa failed" >&2 - clean_up 1 -} - -echo "Copying rv64 binaries from $build_dir/isa to $target_dir" - -# The `make` command generates the binaries in `isa/` but that directory also contains objdumps and sub-directories. -# We only copy those files which are neither. -for file in isa/rv64u*; do - if [ -f "$file" ]; then - case "$file" in - *dump) ;; - *) cp "$file" "$target_dir"/ ;; - esac - fi -done - -echo "Build finished" diff --git a/src/riscv/assets/generated/rv64ua-p-amoadd_d b/src/riscv/assets/generated/rv64ua-p-amoadd_d deleted file mode 100644 index 2fe8d21b1de469ead8bbbca7ec9cb180931c7ae3..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoadd_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoadd_w b/src/riscv/assets/generated/rv64ua-p-amoadd_w deleted file mode 100644 index 8c5361ecd712a6cd52a4347abe13f672d2184ae1..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoadd_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoand_d b/src/riscv/assets/generated/rv64ua-p-amoand_d deleted file mode 100644 index 991519802a6d25caa954f5664d71bc9f6a92bfc8..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoand_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoand_w b/src/riscv/assets/generated/rv64ua-p-amoand_w deleted file mode 100644 index 7bf1540c0ff149ce144a5b99003eb75f7a3ad1c5..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoand_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomax_d b/src/riscv/assets/generated/rv64ua-p-amomax_d deleted file mode 100644 index 73a97875e38c6d76519892d30b0c399f268a85d6..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomax_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomax_w b/src/riscv/assets/generated/rv64ua-p-amomax_w deleted file mode 100644 index 4aa663cf22b7fb96988e3d3736c0aa55b4a29197..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomax_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomaxu_d b/src/riscv/assets/generated/rv64ua-p-amomaxu_d deleted file mode 100644 index 9a91fac70ca83ae94a24c6f1f5a3aa8fba74f9dc..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomaxu_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomaxu_w b/src/riscv/assets/generated/rv64ua-p-amomaxu_w deleted file mode 100644 index da43c06c27bd3dd5fc6d5f25b46990055a7e67cc..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomaxu_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomin_d b/src/riscv/assets/generated/rv64ua-p-amomin_d deleted file mode 100644 index f80795668f7cf176f1ec494714389a9cbb4e5bd4..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomin_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amomin_w b/src/riscv/assets/generated/rv64ua-p-amomin_w deleted file mode 100644 index 061a519ca26f702c54db2d59a733d03a582ce876..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amomin_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amominu_d b/src/riscv/assets/generated/rv64ua-p-amominu_d deleted file mode 100644 index 7dc20d2497311547d987cd64bb3d9f5f6b76a733..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amominu_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amominu_w b/src/riscv/assets/generated/rv64ua-p-amominu_w deleted file mode 100644 index 7ce5f1d70daf7eaee6eaaee9c98a03e7ed077947..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amominu_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoor_d b/src/riscv/assets/generated/rv64ua-p-amoor_d deleted file mode 100644 index eaac4ac9deaa55f0a9b37d73ebda5a1a525ffcf2..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoor_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoor_w b/src/riscv/assets/generated/rv64ua-p-amoor_w deleted file mode 100644 index 87249216bf7c1cac68653187d2a7cdfb47892525..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoor_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoswap_d b/src/riscv/assets/generated/rv64ua-p-amoswap_d deleted file mode 100644 index 4decd07a18ec4e0de0686e8a18156f3634932c92..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoswap_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoswap_w b/src/riscv/assets/generated/rv64ua-p-amoswap_w deleted file mode 100644 index 209c12c6533f2f44304e959f1dd3e9af8bc53acf..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoswap_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoxor_d b/src/riscv/assets/generated/rv64ua-p-amoxor_d deleted file mode 100644 index f8a28cb0554a8cee94c93ae4c3d23d65567e2be3..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoxor_d and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-amoxor_w b/src/riscv/assets/generated/rv64ua-p-amoxor_w deleted file mode 100644 index cd1215370d9a2d47ee98da20eaf70b42a44153c3..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-amoxor_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ua-p-lrsc b/src/riscv/assets/generated/rv64ua-p-lrsc deleted file mode 100644 index a388cb9382502ac4a210409bd650ac3cb1995192..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ua-p-lrsc and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uc-p-rvc b/src/riscv/assets/generated/rv64uc-p-rvc deleted file mode 100644 index afb3022027a595464bd8416f9f25807a0824f22b..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uc-p-rvc and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fadd b/src/riscv/assets/generated/rv64ud-p-fadd deleted file mode 100644 index 531423ee557dad18a91eec58ced2ffcdd57a911e..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fadd and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fclass b/src/riscv/assets/generated/rv64ud-p-fclass deleted file mode 100644 index 82a3dcc29d26b9937a66d0a930e8e6b318e180b6..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fclass and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fcmp b/src/riscv/assets/generated/rv64ud-p-fcmp deleted file mode 100644 index 35ef0d20659f220498b7d1ee2b0c487ae93b7bdc..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fcmp and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fcvt b/src/riscv/assets/generated/rv64ud-p-fcvt deleted file mode 100644 index 7bf19be16099b8dd45456c14b0a99dc00ada26f8..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fcvt and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fcvt_w b/src/riscv/assets/generated/rv64ud-p-fcvt_w deleted file mode 100644 index 7953eea71b7dfff8616b7878745490d9330a528f..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fcvt_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fdiv b/src/riscv/assets/generated/rv64ud-p-fdiv deleted file mode 100644 index b0924529c9705903650ff132cf681505ae1b24d2..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fdiv and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fmadd b/src/riscv/assets/generated/rv64ud-p-fmadd deleted file mode 100644 index c4f9ee02df7fe02f2a97e6dd7ee019283bf31f80..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fmadd and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-fmin b/src/riscv/assets/generated/rv64ud-p-fmin deleted file mode 100644 index a64456a8782258e11f3831599401f343b3251923..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-fmin and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-ldst b/src/riscv/assets/generated/rv64ud-p-ldst deleted file mode 100644 index 56cb89efaabf6ac5e0c1703cb3bfa6fc849319bb..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-ldst and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-move b/src/riscv/assets/generated/rv64ud-p-move deleted file mode 100644 index 0653ce15e7cbe8ca146dd903c62ef5823b332ba1..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-move and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-recoding b/src/riscv/assets/generated/rv64ud-p-recoding deleted file mode 100644 index 2f672e37b8a5e1c43707e396d5039ca4eb65aa73..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-recoding and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ud-p-structural b/src/riscv/assets/generated/rv64ud-p-structural deleted file mode 100644 index a3e100fe6b01d03311c7587e537977231b967c58..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ud-p-structural and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fadd b/src/riscv/assets/generated/rv64uf-p-fadd deleted file mode 100644 index 49c510d06b2c3c372b84ecdf07188c64be72599f..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fadd and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fclass b/src/riscv/assets/generated/rv64uf-p-fclass deleted file mode 100644 index 7974325da44df9109e3752ad40ddcdf7dfb4dc33..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fclass and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fcmp b/src/riscv/assets/generated/rv64uf-p-fcmp deleted file mode 100644 index f0b3a0bd97f513650a5b6e727e956a87dc1eb778..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fcmp and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fcvt b/src/riscv/assets/generated/rv64uf-p-fcvt deleted file mode 100644 index 988bfefcb0a7c192c5ab47996d128c953603a271..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fcvt and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fcvt_w b/src/riscv/assets/generated/rv64uf-p-fcvt_w deleted file mode 100644 index 5992713107436f6ddf5f7fce638bda9dfcb2889c..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fcvt_w and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fdiv b/src/riscv/assets/generated/rv64uf-p-fdiv deleted file mode 100644 index e909d057d214778ba5174244573e4b9a1fd404a6..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fdiv and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fmadd b/src/riscv/assets/generated/rv64uf-p-fmadd deleted file mode 100644 index f00f221803e75f6bffe84e497dd7553e6e0adfaf..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fmadd and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-fmin b/src/riscv/assets/generated/rv64uf-p-fmin deleted file mode 100644 index e0531ed36c635245410f4fbd08ea9e6432730f43..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-fmin and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-ldst b/src/riscv/assets/generated/rv64uf-p-ldst deleted file mode 100644 index 9c9f397bf9fc86586116b441ca1891a473e0299d..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-ldst and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-move b/src/riscv/assets/generated/rv64uf-p-move deleted file mode 100644 index af5ce291d6fa6ad9b090c8d1ff7d1ee2a5305593..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-move and /dev/null differ diff --git a/src/riscv/assets/generated/rv64uf-p-recoding b/src/riscv/assets/generated/rv64uf-p-recoding deleted file mode 100644 index 8ae0a2a0207c13a2c0c8ba86f677a75474991df9..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64uf-p-recoding and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-add b/src/riscv/assets/generated/rv64ui-p-add deleted file mode 100644 index d1d36832d43cd00159aa5bd0d33bd3b0604130ac..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-add and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-addi b/src/riscv/assets/generated/rv64ui-p-addi deleted file mode 100644 index 57918d453db829d79a788f970971d5c3fff36967..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-addi and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-addiw b/src/riscv/assets/generated/rv64ui-p-addiw deleted file mode 100644 index fcf683b26495ecb93626f65f7eb06c721dd29ef4..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-addiw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-addw b/src/riscv/assets/generated/rv64ui-p-addw deleted file mode 100644 index 4e5d5514a4ddf3052fcd6bc231a630594d1843ad..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-addw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-and b/src/riscv/assets/generated/rv64ui-p-and deleted file mode 100644 index 7f1fc21b93f05b8c9f915887ab36c50af91fbbe2..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-and and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-andi b/src/riscv/assets/generated/rv64ui-p-andi deleted file mode 100644 index 900d1b8635379835e181462400878d306aa08d4e..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-andi and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-auipc b/src/riscv/assets/generated/rv64ui-p-auipc deleted file mode 100644 index c077fbf46c63d7ddcddb3eda07e6fc387c713965..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-auipc and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-beq b/src/riscv/assets/generated/rv64ui-p-beq deleted file mode 100644 index ed2004fe52548aa22d55cac7443a1484b40ce7c7..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-beq and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-bge b/src/riscv/assets/generated/rv64ui-p-bge deleted file mode 100644 index 960b4ec473bceb00dfd5944d2336fb840f025c39..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-bge and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-bgeu b/src/riscv/assets/generated/rv64ui-p-bgeu deleted file mode 100644 index ff553c7efe4720ea2e6491eb57fb0a900059412f..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-bgeu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-blt b/src/riscv/assets/generated/rv64ui-p-blt deleted file mode 100644 index db5cecbbfd9c1b0f3ce21dd5b18df064e0995dac..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-blt and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-bltu b/src/riscv/assets/generated/rv64ui-p-bltu deleted file mode 100644 index f9c334ddd03eb3c555e6ae8fa86fca4a15cb1e58..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-bltu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-bne b/src/riscv/assets/generated/rv64ui-p-bne deleted file mode 100644 index dc39786ca0dd76276bb69880ce33c8b87940c840..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-bne and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-fence_i b/src/riscv/assets/generated/rv64ui-p-fence_i deleted file mode 100644 index bf292917a0232fe3a47b45865e75db56fd4af7fb..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-fence_i and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-jal b/src/riscv/assets/generated/rv64ui-p-jal deleted file mode 100644 index a910a3d93225725fb59f50b56a4c2de2553306e3..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-jal and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-jalr b/src/riscv/assets/generated/rv64ui-p-jalr deleted file mode 100644 index b81ffec6b9c1aa27d813a7c4233cf3db33d2cdc6..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-jalr and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lb b/src/riscv/assets/generated/rv64ui-p-lb deleted file mode 100644 index 1a8e14d49b48ce210087852e772e22f28a2958d3..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lb and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lbu b/src/riscv/assets/generated/rv64ui-p-lbu deleted file mode 100644 index 6ecf21436ca86d7f1b747aacb19cda8bb63b52b5..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lbu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-ld b/src/riscv/assets/generated/rv64ui-p-ld deleted file mode 100644 index 0f6976adac9a3074f694b87f5c5a3bcba9bc6371..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-ld and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-ld_st b/src/riscv/assets/generated/rv64ui-p-ld_st deleted file mode 100755 index 8868c2118d140696951764febb58248c0ec8a740..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-ld_st and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lh b/src/riscv/assets/generated/rv64ui-p-lh deleted file mode 100644 index 86b4ce9f35f974dbbf16d9c138011898fc20c2f0..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lh and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lhu b/src/riscv/assets/generated/rv64ui-p-lhu deleted file mode 100644 index a5179019fca61b1aac9bb0cb54912fbc32b04c8a..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lhu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lui b/src/riscv/assets/generated/rv64ui-p-lui deleted file mode 100644 index b56d8e4f2a5a3ff263445433e21d252bcf4c5df2..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lui and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lw b/src/riscv/assets/generated/rv64ui-p-lw deleted file mode 100644 index def1be4f9bbafd4193d2f5b0284ebd5c43f2aec9..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-lwu b/src/riscv/assets/generated/rv64ui-p-lwu deleted file mode 100644 index 7c8fed2df436c07d0a121fe03bb99e18732abc08..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-lwu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-ma_data b/src/riscv/assets/generated/rv64ui-p-ma_data deleted file mode 100644 index 71943d82deb887c80949c18493fe982fc8db87d0..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-ma_data and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-or b/src/riscv/assets/generated/rv64ui-p-or deleted file mode 100644 index 503a51d1011b7fa1592934a14d5e638430858883..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-or and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-ori b/src/riscv/assets/generated/rv64ui-p-ori deleted file mode 100644 index a117c59051597aa31db1969ec4ddf620388e7c94..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-ori and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sb b/src/riscv/assets/generated/rv64ui-p-sb deleted file mode 100644 index e16df63c007a961e2fc535cf26509b97857c2bcf..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sb and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sd b/src/riscv/assets/generated/rv64ui-p-sd deleted file mode 100644 index fdbfcaded222c80171ccbe6545259d684e34c7dc..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sd and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sh b/src/riscv/assets/generated/rv64ui-p-sh deleted file mode 100644 index 08a57419886819dfd1418078730b890e2015c9ad..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sh and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-simple b/src/riscv/assets/generated/rv64ui-p-simple deleted file mode 100644 index 55c950050b6443274a88f5b257fe6cb0ff958aa7..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-simple and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sll b/src/riscv/assets/generated/rv64ui-p-sll deleted file mode 100644 index 3d496befa3b8bf71927e30b9774c7c4b33dc5cc8..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sll and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-slli b/src/riscv/assets/generated/rv64ui-p-slli deleted file mode 100644 index 38f194e31b84c6f0a504923477e44e3a770a6a61..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-slli and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-slliw b/src/riscv/assets/generated/rv64ui-p-slliw deleted file mode 100644 index c94e74fadce5fba8e28419bb9e0e1bfee3b19fec..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-slliw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sllw b/src/riscv/assets/generated/rv64ui-p-sllw deleted file mode 100644 index 270455665fe3a8192f5b758f101bf07eb5876ff1..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sllw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-slt b/src/riscv/assets/generated/rv64ui-p-slt deleted file mode 100644 index de5f71a9560d7ac03d4ee0e9f97bfbe1946ce090..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-slt and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-slti b/src/riscv/assets/generated/rv64ui-p-slti deleted file mode 100644 index cd4eaedef07aef7cf7a93cb91be5ca72224f043e..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-slti and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sltiu b/src/riscv/assets/generated/rv64ui-p-sltiu deleted file mode 100644 index 790edc61f450c68957cd10bb20381a65f2cfa4cf..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sltiu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sltu b/src/riscv/assets/generated/rv64ui-p-sltu deleted file mode 100644 index 9cb3b02d30d0020961f84597c9f0a739219297ba..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sltu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sra b/src/riscv/assets/generated/rv64ui-p-sra deleted file mode 100644 index ffa2f8bc59402935ebf3391dc7840fbe821c16c5..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sra and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-srai b/src/riscv/assets/generated/rv64ui-p-srai deleted file mode 100644 index 01e285c093f04eefbbd5d4bcd975527a2e7d3da7..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-srai and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sraiw b/src/riscv/assets/generated/rv64ui-p-sraiw deleted file mode 100644 index cdb848b62a0d365bf85fd20cd0b8b218b26cb13e..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sraiw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sraw b/src/riscv/assets/generated/rv64ui-p-sraw deleted file mode 100644 index 40e284c472ccf2ea8e4ac9358db536eae0181556..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sraw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-srl b/src/riscv/assets/generated/rv64ui-p-srl deleted file mode 100644 index 646aa9904f63559e4cef4f7155e3af0fc76df346..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-srl and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-srli b/src/riscv/assets/generated/rv64ui-p-srli deleted file mode 100644 index 7dd0c1b71b1c85e27036a34be37ec5e93bcffb0c..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-srli and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-srliw b/src/riscv/assets/generated/rv64ui-p-srliw deleted file mode 100644 index 9b2c649532e4feb0317a414d9141fdeabd3ad779..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-srliw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-srlw b/src/riscv/assets/generated/rv64ui-p-srlw deleted file mode 100644 index 6b7cb9c487c16bac275b790f9cf042c1683ff0ba..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-srlw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-st_ld b/src/riscv/assets/generated/rv64ui-p-st_ld deleted file mode 100755 index c1d7feacf2cd3a442ab480ec403a8e42785ec5a6..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-st_ld and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sub b/src/riscv/assets/generated/rv64ui-p-sub deleted file mode 100644 index 54886fe0091fbe8cafaf975533da5599c4d9278b..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sub and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-subw b/src/riscv/assets/generated/rv64ui-p-subw deleted file mode 100644 index a82e6aba6b7df3b5263130f2c0e1a08b1b38ecc0..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-subw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-sw b/src/riscv/assets/generated/rv64ui-p-sw deleted file mode 100644 index e19be03dfff44b6981de17abf19bf4c100fe97c7..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-sw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-xor b/src/riscv/assets/generated/rv64ui-p-xor deleted file mode 100644 index 96acae4fc88317f455f4234ea2038c60d09f51bc..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-xor and /dev/null differ diff --git a/src/riscv/assets/generated/rv64ui-p-xori b/src/riscv/assets/generated/rv64ui-p-xori deleted file mode 100644 index 3553e9872c8de2dcd9a38a4708fa8016aa94f662..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64ui-p-xori and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-div b/src/riscv/assets/generated/rv64um-p-div deleted file mode 100644 index 675311fae52aaa6b65bdaba9f0cac5cc732f8ad4..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-div and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-divu b/src/riscv/assets/generated/rv64um-p-divu deleted file mode 100644 index 48a990082a6ecc13ba39661a8c9fbc44bd718744..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-divu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-divuw b/src/riscv/assets/generated/rv64um-p-divuw deleted file mode 100644 index 03c76c6b84f3590bc5391b27cb734cc8eb65764c..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-divuw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-divw b/src/riscv/assets/generated/rv64um-p-divw deleted file mode 100644 index 194086d00884addbc77b28a537de60ce5b1b546f..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-divw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-mul b/src/riscv/assets/generated/rv64um-p-mul deleted file mode 100644 index 4db7fa9bea401e33625cdc4ac0935d4e2e07ae2f..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-mul and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-mulh b/src/riscv/assets/generated/rv64um-p-mulh deleted file mode 100644 index 6d1ccdfd35ecf7ce900e027878726cdd3213d556..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-mulh and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-mulhsu b/src/riscv/assets/generated/rv64um-p-mulhsu deleted file mode 100644 index f9805cc04f485aebafd2ea0b1314061be3ea8031..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-mulhsu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-mulhu b/src/riscv/assets/generated/rv64um-p-mulhu deleted file mode 100644 index dbe6ae3e2155a3d95181c030fc69a5894a9851b4..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-mulhu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-mulw b/src/riscv/assets/generated/rv64um-p-mulw deleted file mode 100644 index dd8688a4447ebb6555c177eaecdc5e37db7d9c47..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-mulw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-rem b/src/riscv/assets/generated/rv64um-p-rem deleted file mode 100644 index 6db0fcdc9e788b41275e31d619be47cc817ece33..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-rem and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-remu b/src/riscv/assets/generated/rv64um-p-remu deleted file mode 100644 index e19ec01d958f4453a7586a067d788eb79a553c19..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-remu and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-remuw b/src/riscv/assets/generated/rv64um-p-remuw deleted file mode 100644 index 9e35c918424aec25725ea9ac4419cbf475d5d1f4..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-remuw and /dev/null differ diff --git a/src/riscv/assets/generated/rv64um-p-remw b/src/riscv/assets/generated/rv64um-p-remw deleted file mode 100644 index 97d3ef86be866e1a555813005a8155a4a6f09e1b..0000000000000000000000000000000000000000 Binary files a/src/riscv/assets/generated/rv64um-p-remw and /dev/null differ diff --git a/src/riscv/assets/jstz b/src/riscv/assets/jstz index fa45f74fa3bebf79d65fce0f4f93e3f0a64f9099..fbc4359721a416767d0aa004931e6a0a701a64eb 100755 Binary files a/src/riscv/assets/jstz and b/src/riscv/assets/jstz differ diff --git a/src/riscv/assets/jstz.checksum b/src/riscv/assets/jstz.checksum index 89d5bd908b7e997bd741c70f81c58228b5dcc9da..2b63170872b2472f104735d151780d189c49c99c 100644 --- a/src/riscv/assets/jstz.checksum +++ b/src/riscv/assets/jstz.checksum @@ -1 +1 @@ -0ee345890aa9a3e992bb8118800b282b125d28ce196558f33f344f302bcb80d7 assets/jstz +afe9ed859f505d6f4a510b9c40014bbf4da13f1563f2e3f47f6f3e8aa7800f10 assets/jstz diff --git a/src/riscv/assets/regression-inbox.json b/src/riscv/assets/regression-inbox.json deleted file mode 100644 index 152c32e0cdebdc6e5ea0bde8a89b973b47a85718..0000000000000000000000000000000000000000 --- a/src/riscv/assets/regression-inbox.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - [ - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000004fd8b650f46128bf5c7edfe3c72de5ae420da3b4a78843eca103e14514efc6c337819ebef88b499d613371d6d3e6bb081745562b87172fe41105cd759eb75100000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395000000000000000000000000b80e00000000000066756e6374696f6e206f2865297b72657475726e20747970656f6620653d3d22737472696e67227d66756e6374696f6e206128652c72297b72657475726e2041727261792e697341727261792872292626722e72656475636528286e2c74293d3e6e2626652874292c2130297d66756e6374696f6e20692865297b72657475726e20747970656f6620653d3d226e756d6265722226264e756d6265722e6973496e74656765722865297d66756e6374696f6e206b2865297b6c657420723d653b7472797b72657475726e206f28722e746f2926266928722e746f6b656e5f69642926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20772865297b6c657420723d653b7472797b72657475726e206f28722e66726f6d29262661286b2c722e7472616e7366657273297d63617463687b72657475726e21317d7d66756e6374696f6e20412865297b6c657420723d653b7472797b72657475726e28722e6f7065726174696f6e3d3d3d226164645f6f70657261746f72227c7c722e6f7065726174696f6e3d3d3d2272656d6f76655f6f70657261746f72222926266f28722e6f776e65722926266f28722e6f70657261746f722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e20702865297b6c657420723d653b7472797b72657475726e206f28722e6f776e65722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e205f2865297b6c657420723d653b7472797b72657475726e206128702c722e7265717565737473297d63617463687b72657475726e21317d7d66756e6374696f6e20712865297b6c657420723d653b7472797b72657475726e207028722e726571756573742926264e756d6265722e6973496e746567657228722e62616c616e6365297d63617463687b72657475726e21317d7d66756e6374696f6e20672865297b6c657420723d653b7472797b72657475726e206928722e746f6b656e5f69642926266f28722e6f776e65722926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20642865297b72657475726e60746f6b656e2f247b657d607d66756e6374696f6e20522865297b6c657420723d642865293b6966284b762e676574287229297468726f77224641325f544f4b454e5f49445f455849535453223b4b762e73657428642865292c2130297d66756e6374696f6e204f2865297b696628214b762e676574286428652929297468726f77224641325f544f4b454e5f554e444546494e4544227d66756e6374696f6e206c28652c72297b72657475726e6062616c616e63652f247b657d2f247b727d607d66756e6374696f6e206d28652c72297b72657475726e204b762e676574286c28652c7229297c7c307d66756e6374696f6e206828652c722c6e297b6966286e3c30297468726f77224641325f494e53554646494349454e545f42414c414e4345223b4b762e736574286c28652c72292c6e297d66756e6374696f6e207528652c722c6e297b6c657420743d6d28652c72293b6828652c722c742b6e297d66756e6374696f6e207928652c722c6e2c74297b7528652c6e2c2d74292c7528722c6e2c74297d66756e6374696f6e206628652c722c6e297b72657475726e606f776e65722f247b657d2f247b727d2f247b6e7d607d66756e6374696f6e204928652c722c6e297b4b762e736574286628652c722c6e292c2130297d66756e6374696f6e206228652c722c6e297b4b762e64656c657465286628652c722c6e29297d66756e6374696f6e205428652c722c6e297b6966282128653d3d3d727c7c4b762e676574286628652c722c6e292929297468726f77224641325f4e4f545f4f50455241544f52227d66756e6374696f6e204228652c72297b69662865213d3d72297468726f7720636f6e736f6c652e6c6f672860247b657d20213d3d20247b727d60292c224641325f4e4f545f4f574e4552227d66756e6374696f6e204e28652c722c6e297b4f286e2e746f6b656e5f6964292c5428652c722c6e2e746f6b656e5f6964292c7928652c6e2e746f2c6e2e746f6b656e5f69642c6e2e616d6f756e74297d66756e6374696f6e207828652c72297b722e666f7245616368286e3d3e6e2e7472616e73666572732e666f724561636828743d3e4e286e2e66726f6d2c652c742929297d66756e6374696f6e207628652c72297b73776974636828722e6f7065726174696f6e297b63617365226164645f6f70657261746f72223a4228722e6f776e65722c65292c4928722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964293b627265616b3b636173652272656d6f76655f6f70657261746f72223a5428722e6f776e65722c652c722e746f6b656e5f6964292c6228722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964297d7d66756e6374696f6e20452865297b6c657420723d6d28652e6f776e65722c652e746f6b656e5f6964293b72657475726e20636f6e736f6c652e6c6f672860247b652e6f776e65727d2068617320247b727d206f6620746f6b656e20247b652e746f6b656e5f69647d60292c7b726571756573743a652c62616c616e63653a727d7d66756e6374696f6e20532865297b72657475726e20652e72657175657374732e6d61702845297d66756e6374696f6e204b2865297b5228652e746f6b656e5f6964292c7528652e6f776e65722c652e746f6b656e5f69642c652e616d6f756e74297d6173796e632066756e6374696f6e20552865297b6c657420723d6e65772055524c28652e75726c292c6e3d722e706174686e616d653b7472797b737769746368286e297b63617365222f70696e67223a72657475726e20636f6e736f6c652e6c6f67282248656c6c6f2066726f6d2072756e6e657220736d6172742066756e6374696f6e205c757b31463434427d22292c6e657720526573706f6e73652822506f6e6722293b63617365222f62616c616e63655f6f66223a696628652e6d6574686f643d3d3d2247455422297b6c657420733d7b72657175657374733a4a534f4e2e70617273652861746f6228722e736561726368506172616d732e67657428227265717565737473222929297d3b6966285f287329297b6c657420633d532873293b72657475726e20526573706f6e73652e6a736f6e2863297d656c73652072657475726e20636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c73292c526573706f6e73652e6572726f7228297d656c73657b6c657420733d222f62616c616e63655f6f662069732061204745542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7472616e73666572223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128772c73293f287828652e686561646572732e67657428225265666572657222292c73292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7472616e73666572206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f6d696e745f6e6577223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128672c73293f28732e666f7245616368284b292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f6d696e745f6e6577206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7570646174655f6f70657261746f7273223a696628652e6d6574686f643d3d3d2250555422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128412c73293f28732e666f724561636828633d3e7628652e686561646572732e67657428225265666572657222292c6329292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7570646174655f6f70657261746f72732069732061205055542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d64656661756c743a6c657420743d60556e7265636f676e6973656420656e747279706f696e7420247b6e7d603b72657475726e20636f6e736f6c652e6572726f722874292c6e657720526573706f6e736528742c7b7374617475733a3430347d297d7d63617463682874297b7468726f7720636f6e736f6c652e6572726f722874292c747d7d76617220463d553b6578706f72747b462061732064656661756c742c5f20617320697342616c616e63654f662c7020617320697342616c616e6365526571756573742c7120617320697342616c616e6365526573706f6e73652c672061732069734d696e744e65772c69206173206973546f6b656e49642c6b2061732069735472616e736665722c772061732069735472616e73666572732c412061732069735570646174654f70657261746f727d3b0a0000000000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a00000000400000000000000089c6f2b594c450d63b90b275909a7044d48da52235c1c26d2920cc939259976d828a1d528acdbd3b8dc9f8f7583ca89ff8e7b827a6c399831b38ec161e2fae0e000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f6d696e745f6e65770400000000000000504f53540000000000000000016e010000000000005b7b22746f6b656e5f6964223a302c226f776e6572223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c22616d6f756e74223a367d2c7b22746f6b656e5f6964223a312c226f776e6572223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c22616d6f756e74223a367d2c7b22746f6b656e5f6964223a322c226f776e6572223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244222c22616d6f756e74223a367d2c7b22746f6b656e5f6964223a332c226f776e6572223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59222c22616d6f756e74223a367d2c7b22746f6b656e5f6964223a342c226f776e6572223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956222c22616d6f756e74223a367d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000009d47ad305e68c4336d6656b95e03334a91af70693ac451aec99bbb6cd39fef8ae48012e722d766f02bbb3a8d0b068472c31ab864b5f34606d26c953309c63f04000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a342c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a90000000040000000000000007b5d4872fb3bfb5b8dc32fcc536531ae671f69b605fd34bd8b6ac6d11e81ada5c5dedfe6a189dd15e229128217dcda9751b9880bc8d3f894ab5cea226424ce09000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040000000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a332c22746f223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000a4a9d0ee7287dc01ffc893ed1e92d124ff7016582b75d9198f726aee8ef4c82c000000004000000000000000c34ea9b48581748be012a2ccb4235e9578b228e728d95c75c93fdbe7a1bc449aca3ce97812eeecbb03a4278e32bd15c248396981896c4699a67518297214a60d000000001400000000000000436ee1b964a1980f95e6982a9f0986c98f60c262000000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a322c22746f223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000c7341b3d72e68dd16b3a5092c5cd3f2e7bc6fabef66e4437e19c6df12a5f541f000000004000000000000000e37767e9e4142ae801d939865a4f2e1fe19f424425d135a624e46ee17bdfb1dfc5281993a3d0845d4da0a190d17c3971b15ec81f2aa4cdb41c0328c854dddc06000000001400000000000000b43782e0b9a39e8b287beb4461ebeb38361a3dbe000000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a312c22746f223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000c9a675887f96a918d1b5a7bea1a3865422925d5af3e7136845af5856922357a702fc43a4ff73c26bcf38b3f7408e2091e8048c3b320c47901a89d4bcc975fa0c000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c227472616e7366657273223a5b7b22746f6b656e5f6964223a312c22616d6f756e74223a342c22746f223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000a4a9d0ee7287dc01ffc893ed1e92d124ff7016582b75d9198f726aee8ef4c82c00000000400000000000000027dbb5573f874e49e6a0e05cdf73997b7c247d36394ba5bdaa5afe9bdc8fc12c907b2d79f2ea2452219c22dd6f73422296f08d3f8c83d3955b801b86384d970b000000001400000000000000436ee1b964a1980f95e6982a9f0986c98f60c262010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244222c227472616e7366657273223a5b7b22746f6b656e5f6964223a312c22616d6f756e74223a332c22746f223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000c7341b3d72e68dd16b3a5092c5cd3f2e7bc6fabef66e4437e19c6df12a5f541f000000004000000000000000233ad1e83c0b1202adb277cac62ff954e90869693c499cb691b8249534833598c750df9fef9340d3fd6f5ad26e4f2baa3d358f5dc46619f7412e9c52bd3aac09000000001400000000000000b43782e0b9a39e8b287beb4461ebeb38361a3dbe010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59222c227472616e7366657273223a5b7b22746f6b656e5f6964223a312c22616d6f756e74223a322c22746f223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000001111d9372d7379349767deed08bdcfa3e873fcc28f4ccdc088be6e365a7113c2000000004000000000000000245190f0850541afdf95878099db1763c53e8f5b5b00524c817dbdc416365edcf238c650d07eb209e1a8ebee76cb91311f04c4ee6bca271cc7b767232178da07000000001400000000000000f74bdcec747a41b6e942df565c8b6fb7ecaded44000000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956222c227472616e7366657273223a5b7b22746f6b656e5f6964223a312c22616d6f756e74223a312c22746f223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000a4a9d0ee7287dc01ffc893ed1e92d124ff7016582b75d9198f726aee8ef4c82c00000000400000000000000032f0a12367a70249557d712a0da65586367a7b1c0038870bd245fd91064a655b7e70b60cfedf806d54100f48bffaa8a45fbee4996b776616881c1cdd0b61860c000000001400000000000000436ee1b964a1980f95e6982a9f0986c98f60c262020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31526e616f416f64657478534d4775527542515275714270394e736e647867343244222c227472616e7366657273223a5b7b22746f6b656e5f6964223a322c22616d6f756e74223a342c22746f223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000c7341b3d72e68dd16b3a5092c5cd3f2e7bc6fabef66e4437e19c6df12a5f541f000000004000000000000000c619c36f537cbe88e911f774684bc0b815de779e62492c1073e860c515bbb52788a1031786ad5e5e1675fdc25e32a8a8208b41faeb188ec3aeb92630885d9a0d000000001400000000000000b43782e0b9a39e8b287beb4461ebeb38361a3dbe020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59222c227472616e7366657273223a5b7b22746f6b656e5f6964223a322c22616d6f756e74223a332c22746f223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000001111d9372d7379349767deed08bdcfa3e873fcc28f4ccdc088be6e365a7113c2000000004000000000000000da779f407b08b04e962ecea59c4acec95a32241a4a003179d1aa99dd293507a51de9e60dde393ca1ac4dd3cb6e6706f0806dd86994f88a1ab50acafc7394c401000000001400000000000000f74bdcec747a41b6e942df565c8b6fb7ecaded44010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956222c227472616e7366657273223a5b7b22746f6b656e5f6964223a322c22616d6f756e74223a322c22746f223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000ef0f756456223f1ab2c584eb440fdcb2d836d594122331b612301de9d16eab8a1736778a808f3543570d0e0578063f829ac9f771b4324512cd7e0b0a7dc69b00000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395030000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a322c22616d6f756e74223a312c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000c7341b3d72e68dd16b3a5092c5cd3f2e7bc6fabef66e4437e19c6df12a5f541f000000004000000000000000867eb708bf4f4f562e4f1ac201dad3d62f5c4eaae5b8957c33426473844bdce22d29939c4d4866dffc24a1a96199c2b9ffa51980b95792b5e08e4d49dee4e607000000001400000000000000b43782e0b9a39e8b287beb4461ebeb38361a3dbe030000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a316334767064764872734e44424250736a614b45647a434a647141644b6f6a504a59222c227472616e7366657273223a5b7b22746f6b656e5f6964223a332c22616d6f756e74223a342c22746f223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000001111d9372d7379349767deed08bdcfa3e873fcc28f4ccdc088be6e365a7113c2000000004000000000000000aab6b3edd0f3a6d5c30683ea2b9db5ddd10756c37a9f46ef1b346c75ce20332f001d3a24c07068031c4136b178afcebd31f309a1dbb8715a7bb6e417be73af05000000001400000000000000f74bdcec747a41b6e942df565c8b6fb7ecaded44020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a31694263555a353967535057586f6654384231686755785548534b72437151696956222c227472616e7366657273223a5b7b22746f6b656e5f6964223a332c22616d6f756e74223a332c22746f223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000f1bf97a206dc27c578b3ebf0004bb2212fb7131458031852643680bf01965653d999ff7517f447ad57504dd94987760808a6d7e6298f5f127f031ca10b2c110d000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395040000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a332c22616d6f756e74223a322c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000003c542a28dec8f8a5e6d2977dc92e1f4bcc959a3c341a7e5a30368dd3261450ea86ee73402c60ce293e62c0200dcd50aeac55ca10b3786b85e3f0549a378b2301000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395050000000000000001000000e10100000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e58676966537837496e52766132567558326c6b496a6f794c434a766432356c63694936496e52364d57564261314e58655752344d6b5a46516a4e45556d4e705a584e6e4f464a6a5332525a62336842636a6b3165434a394c487369644739725a573566615751694f6a4d73496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364e437769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e5867696656303d0300000000000000474554000000000000000000a086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000460fa773b9137d5ad0ae566c3db3fc41c9d1b31fbbd9521d22bc7b339e4a94d6248095b42551aab1721a688cde8c1468156d28aeab3a9df88242e0d6d652b505000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040020000000000000001000000e10100000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d61474579544559346546456966537837496e52766132567558326c6b496a6f794c434a766432356c63694936496e52364d5768516557673265477468616d464d596c524c616b683253334d344d32746c6155786f59544a4d526a683455534a394c487369644739725a573566615751694f6a4d73496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364e437769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d6147457954455934654645696656303d0300000000000000474554000000000000000000a086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000a4a9d0ee7287dc01ffc893ed1e92d124ff7016582b75d9198f726aee8ef4c82c0000000040000000000000004913fee172dc43d1cca34c585ce47dac902ff723c58ad753b6167e109d27e48b8712c375df5a4fc9b3900489b46de15c375759bf21941525097028beb4cf5603000000001400000000000000436ee1b964a1980f95e6982a9f0986c98f60c262030000000000000001000000e10100000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f78556d3568623046765a47563065464e4e5233565364554a52556e5678516e4135546e4e755a48686e4e444a45496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a4653626d46765157396b5a5852345530314864564a31516c46536458464363446c4f6332356b654763304d6b516966537837496e52766132567558326c6b496a6f794c434a766432356c63694936496e52364d564a75595739426232526c6448685454556431556e564355564a3163554a774f55357a626d52345a7a517952434a394c487369644739725a573566615751694f6a4d73496d3933626d5679496a6f6964486f78556d3568623046765a47563065464e4e5233565364554a52556e5678516e4135546e4e755a48686e4e444a45496e307365794a306232746c626c39705a4349364e437769623364755a5849694f694a30656a4653626d46765157396b5a5852345530314864564a31516c46536458464363446c4f6332356b654763304d6b51696656303d0300000000000000474554000000000000000000a086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb5000000002000000000000000c7341b3d72e68dd16b3a5092c5cd3f2e7bc6fabef66e4437e19c6df12a5f541f00000000400000000000000091c8cde2be43cb610a351fbb6c655db93200774a04f085ac6111f638ed579a135f58c940da33e2b21dc58743593c4d7564da7e3686517def1cf52a26b6a70f0e000000001400000000000000b43782e0b9a39e8b287beb4461ebeb38361a3dbe040000000000000001000000e10100000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f78597a52326347523253484a7a546b5243516c427a616d464c525752365130706b6355466b533239715545705a496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466a4e485a775a485a49636e4e4f52454a4355484e71595574465a487044536d52785157524c62327051536c6b6966537837496e52766132567558326c6b496a6f794c434a766432356c63694936496e52364d574d30646e426b646b687963303545516b4a51633270685330566b656b4e4b5a4846425a457476616c424b57534a394c487369644739725a573566615751694f6a4d73496d3933626d5679496a6f6964486f78597a52326347523253484a7a546b5243516c427a616d464c525752365130706b6355466b533239715545705a496e307365794a306232746c626c39705a4349364e437769623364755a5849694f694a30656a466a4e485a775a485a49636e4e4f52454a4355484e71595574465a487044536d52785157524c62327051536c6b696656303d0300000000000000474554000000000000000000a086010000000000" - }, - { - "external": "00f4e47cb3c43a68b0d48e3094092ca42d713addb50000000020000000000000001111d9372d7379349767deed08bdcfa3e873fcc28f4ccdc088be6e365a7113c20000000040000000000000001654d91e3be9fbf455863e1a49742853bcaab7265861245cd1e79d69e989c52f641341200a9669a3cf768d5c82c90dc0b9f7de750afa197c1324fe6ed4f08a04000000001400000000000000f74bdcec747a41b6e942df565c8b6fb7ecaded44030000000000000001000000e10100000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f7861554a6a56566f314f5764545546645962325a554f4549786147645665465649553074795133465261576c57496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a4670516d4e56576a55355a314e51563168765a6c5134516a466f5a3156345655685453334a44635646706156596966537837496e52766132567558326c6b496a6f794c434a766432356c63694936496e52364d576c43593156614e546c6e553142585747396d564468434d57686e5658685653464e4c636b4e7855576c7056694a394c487369644739725a573566615751694f6a4d73496d3933626d5679496a6f6964486f7861554a6a56566f314f5764545546645962325a554f4549786147645665465649553074795133465261576c57496e307365794a306232746c626c39705a4349364e437769623364755a5849694f694a30656a4670516d4e56576a55355a314e51563168765a6c5134516a466f5a3156345655685453334a4463564670615659696656303d0300000000000000474554000000000000000000a086010000000000" - } - ] -] \ No newline at end of file diff --git a/src/riscv/assets/riscv-dummy.elf b/src/riscv/assets/riscv-dummy.elf index 6ad6eaacba0d667c655ee3787d0d79ef2af11bca..c496e2f9bfa2d6d73b0c64b05a7cb1871f4073d3 100755 Binary files a/src/riscv/assets/riscv-dummy.elf and b/src/riscv/assets/riscv-dummy.elf differ diff --git a/src/riscv/assets/riscv-dummy.elf.checksum b/src/riscv/assets/riscv-dummy.elf.checksum index 61a9e8d262051202943b50f685934b76ee524aff..c110ce43e2f044b1123e2019b9e4b225cf105c51 100644 --- a/src/riscv/assets/riscv-dummy.elf.checksum +++ b/src/riscv/assets/riscv-dummy.elf.checksum @@ -1 +1 @@ -c0047e2a561e90aa2e1ae691c9e12798d4c44cccf991f1505a7da59b74f37e83 assets/riscv-dummy.elf +3be75e2383b68ba09e834d80b1bcd4710649fdebce130bfeb784719821453249 assets/riscv-dummy.elf diff --git a/src/riscv/assets/riscv-tests-entry-0.patch b/src/riscv/assets/riscv-tests-entry-0.patch deleted file mode 100644 index e6f4dc6734f4a66fe375675160dd4f24ed94800d..0000000000000000000000000000000000000000 --- a/src/riscv/assets/riscv-tests-entry-0.patch +++ /dev/null @@ -1,27 +0,0 @@ -diff --git a/env/encoding.h b/env/encoding.h -index 01889d1..cb84001 100644 ---- a/env/encoding.h -+++ b/env/encoding.h -@@ -300,7 +300,7 @@ - #define CLINT_BASE 0x02000000 - #define CLINT_SIZE 0x000c0000 - #define EXT_IO_BASE 0x40000000 --#define DRAM_BASE 0x80000000 -+#define DRAM_BASE 0x0 - - /* page table entry (PTE) fields */ - #define PTE_V 0x001 /* Valid */ -diff --git a/env/p/link.ld b/env/p/link.ld -index b3e315e..c6efc56 100644 ---- a/env/p/link.ld -+++ b/env/p/link.ld -@@ -3,7 +3,7 @@ ENTRY(_start) - - SECTIONS - { -- . = 0x80000000; -+ . = 0x0; - .text.init : { *(.text.init) } - . = ALIGN(0x1000); - .tohost : { *(.tohost) } - diff --git a/src/riscv/assets/riscv-tests-setup.patch b/src/riscv/assets/riscv-tests-setup.patch deleted file mode 100644 index 43ad161ca79e44684bf55f7d99438f40c2d44fdc..0000000000000000000000000000000000000000 --- a/src/riscv/assets/riscv-tests-setup.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff --git i/env/p/riscv_test.h w/env/p/riscv_test.h -index 7bf35cf..a01a0f4 100644 ---- i/env/p/riscv_test.h -+++ w/env/p/riscv_test.h -@@ -15,7 +15,6 @@ - - #define RVTEST_RV64UF \ - .macro init; \ -- RVTEST_FP_ENABLE; \ - .endm - - #define RVTEST_RV64UV \ -@@ -203,34 +202,11 @@ handle_exception: \ - j write_tohost; \ - reset_vector: \ - INIT_XREG; \ -- RISCV_MULTICORE_DISABLE; \ -- INIT_RNMI; \ -- INIT_SATP; \ -- INIT_PMP; \ -- DELEGATE_NO_TRAPS; \ - li TESTNUM, 0; \ -- la t0, trap_vector; \ -- csrw mtvec, t0; \ - CHECK_XLEN; \ -- /* if an stvec_handler is defined, delegate exceptions to it */ \ -- la t0, stvec_handler; \ -- beqz t0, 1f; \ -- csrw stvec, t0; \ -- li t0, (1 << CAUSE_LOAD_PAGE_FAULT) | \ -- (1 << CAUSE_STORE_PAGE_FAULT) | \ -- (1 << CAUSE_FETCH_PAGE_FAULT) | \ -- (1 << CAUSE_MISALIGNED_FETCH) | \ -- (1 << CAUSE_USER_ECALL) | \ -- (1 << CAUSE_BREAKPOINT); \ -- csrw medeleg, t0; \ --1: csrwi mstatus, 0; \ - init; \ - EXTRA_INIT; \ - EXTRA_INIT_TIMER; \ -- la t0, 1f; \ -- csrw mepc, t0; \ -- csrr a0, mhartid; \ -- mret; \ - 1: - - //----------------------------------------------------------------------- diff --git a/src/riscv/assets/riscv-tests.commit b/src/riscv/assets/riscv-tests.commit deleted file mode 100644 index 0cc79fe09013037973468e9e60d09630cb954cb1..0000000000000000000000000000000000000000 --- a/src/riscv/assets/riscv-tests.commit +++ /dev/null @@ -1 +0,0 @@ -16b2c2a9291deb7110264b2a35093f7ebefe1150 diff --git a/src/riscv/dummy_kernel/.cargo/config.toml b/src/riscv/dummy_kernel/.cargo/config.toml deleted file mode 100644 index a99cbc88976d0a154f42c580417110122dff73b5..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/.cargo/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -[build] -target = "riscv64gc-unknown-linux-musl" - -[target.riscv64gc-unknown-linux-musl] -linker = "riscv64-unknown-linux-musl-gcc" -rustflags = [ - "-C", - "target-feature=+crt-static", - "-C", - "default-linker-libraries", -] diff --git a/src/riscv/dummy_kernel/Cargo.lock b/src/riscv/dummy_kernel/Cargo.lock deleted file mode 100644 index c893e8e8329bf56ab1bc75fc1223150bf9b53831..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/Cargo.lock +++ /dev/null @@ -1,1218 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8658c15c5d921ddf980f7fe25b1e82f4b7a4083b2c4985fea4922edb8e43e07d" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "cryptoxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version", - "subtle", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "der" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" - -[[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys", -] - -[[package]] -name = "dlmalloc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" -dependencies = [ - "libc", -] - -[[package]] -name = "ecdsa" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" -dependencies = [ - "der", - "elliptic-curve", - "hmac", - "signature 1.3.2", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "signature 2.2.0", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" -dependencies = [ - "curve25519-dalek", - "ed25519", - "sha2 0.10.8", - "subtle", -] - -[[package]] -name = "elliptic-curve" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5c176479da93a0983f0a6fdc3c1b8e7d5be0d7fe3fe05a99f15b96582b9a8" -dependencies = [ - "crypto-bigint", - "ff", - "generic-array", - "group", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "ff" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "p256" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186" -dependencies = [ - "ecdsa", - "elliptic-curve", - "sha2 0.9.9", -] - -[[package]] -name = "parse-display" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7271152b3c46c07c729698e7a5248e2744466b3446d222c97a0b1315925a97b1" -dependencies = [ - "once_cell", - "parse-display-derive", - "regex", -] - -[[package]] -name = "parse-display-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a9f3e41b237b77c99c09686481c235964ff5878229412b226c451f3e809f4f" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "regex", - "regex-syntax 0.6.29", - "syn 1.0.109", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "rand_chacha", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-automata" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "riscv-dummy" -version = "0.0.0" -dependencies = [ - "hex", - "libc", - "tezos-smart-rollup", - "tezos-smart-rollup-constants", - "tezos_crypto_rs", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "serde" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.203" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - -[[package]] -name = "signature" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" - -[[package]] -name = "strum" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" - -[[package]] -name = "strum_macros" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tezos-smart-rollup" -version = "0.2.2" -dependencies = [ - "hex", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", - "tezos-smart-rollup-macros", - "tezos-smart-rollup-mock", - "tezos_data_encoding", -] - -[[package]] -name = "tezos-smart-rollup-build-utils" -version = "0.2.2" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", -] - -[[package]] -name = "tezos-smart-rollup-constants" -version = "0.2.2" - -[[package]] -name = "tezos-smart-rollup-core" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-constants", -] - -[[package]] -name = "tezos-smart-rollup-debug" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", -] - -[[package]] -name = "tezos-smart-rollup-encoding" -version = "0.2.2" -dependencies = [ - "hex", - "nom", - "num-bigint", - "num-traits", - "paste", - "regex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", - "thiserror", - "time", -] - -[[package]] -name = "tezos-smart-rollup-entrypoint" -version = "0.2.2" -dependencies = [ - "cfg-if", - "dlmalloc", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", - "tezos-smart-rollup-panic-hook", -] - -[[package]] -name = "tezos-smart-rollup-host" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos_crypto_rs", - "tezos_data_encoding", - "thiserror", -] - -[[package]] -name = "tezos-smart-rollup-macros" -version = "0.2.2" -dependencies = [ - "proc-macro-error2", - "quote", - "shellexpand", - "syn 2.0.66", - "tezos-smart-rollup-build-utils", -] - -[[package]] -name = "tezos-smart-rollup-mock" -version = "0.2.2" -dependencies = [ - "hex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "tezos-smart-rollup-panic-hook" -version = "0.2.2" -dependencies = [ - "rustversion", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", -] - -[[package]] -name = "tezos_crypto_rs" -version = "0.6.0" -dependencies = [ - "anyhow", - "bs58", - "byteorder", - "cryptoxide", - "ed25519-dalek", - "hex", - "libsecp256k1", - "nom", - "num-bigint", - "num-traits", - "p256", - "rand 0.7.3", - "serde", - "strum", - "strum_macros", - "tezos_data_encoding", - "thiserror", - "zeroize", -] - -[[package]] -name = "tezos_data_encoding" -version = "0.6.0" -dependencies = [ - "bit-vec", - "bitvec", - "hex", - "lazy_static", - "nom", - "num-bigint", - "num-traits", - "serde", - "tezos_data_encoding_derive", - "thiserror", -] - -[[package]] -name = "tezos_data_encoding_derive" -version = "0.6.0" -dependencies = [ - "lazy_static", - "once_cell", - "parse-display", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/src/riscv/dummy_kernel/Cargo.toml b/src/riscv/dummy_kernel/Cargo.toml deleted file mode 100644 index 86d1b40b282b506a1428011d2c3ba420b6a647d0..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "riscv-dummy" -version = "0.0.0" -edition = "2021" - -[dependencies.tezos_crypto_rs] -version = "0.6.0" -path = "../../../sdk/rust/crypto" -default-features = false - -[dependencies.tezos-smart-rollup] -path = "../../kernel_sdk/sdk" -version = "0.2.2" -default-features = false -features = [ - "debug_alloc", - "proto-alpha", - "panic-hook", - "data-encoding", - "alloc", - "experimental-host-in-memory-store", -] - -[dependencies.tezos-smart-rollup-constants] -path = "../../kernel_sdk/constants" - -[dependencies] -hex = "0.4.3" - -[target.'cfg(target_os = "linux")'.dependencies] -libc = "0.2.169" diff --git a/src/riscv/dummy_kernel/Makefile b/src/riscv/dummy_kernel/Makefile deleted file mode 100644 index 411dfab3ea9be7a83e50d2ae11801a8e2fb7ef86..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: 2023 TriliTech -# -# SPDX-License-Identifier: MIT - -.PHONY: all -all: build test check - -.PHONY: build -build: - @cargo build --release - -.PHONY: test -test: - @# This executable compiles to RISC-V. It should be run in the RISC-V sandbox. - -.PHONY: check -check: - @exec ../scripts/format.sh --check - @cargo check --workspace --locked - @cargo clippy -- --deny warnings - @cargo doc --all-features --document-private-items --no-deps - -.PHONY: clean -clean: - @cargo clean diff --git a/src/riscv/dummy_kernel/rust-toolchain.toml b/src/riscv/dummy_kernel/rust-toolchain.toml deleted file mode 100644 index 1dc80e64a3ae93c9b1733abd707d0617b8f4c518..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "1.86.0" -components = ["rustfmt", "clippy"] -targets = ["riscv64gc-unknown-linux-musl"] diff --git a/src/riscv/dummy_kernel/src/linux_only.rs b/src/riscv/dummy_kernel/src/linux_only.rs deleted file mode 100644 index 9281236dcce81a5e86ba5ceead3861db577459ed..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/src/linux_only.rs +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::ffi::CStr; -use std::time::Instant; -use std::time::SystemTime; - -unsafe fn test_write(msg: &CStr) { - let message_endl = msg.to_bytes(); - let written = libc::write(1, message_endl.as_ptr().cast(), message_endl.len()); - assert_eq!(written as usize, message_endl.len()); -} - -unsafe fn test_writev(msgs: [&CStr; N]) { - let mut total_length = 0; - let mut iovecs = msgs.map(|msg| { - let bytes = msg.to_bytes(); - total_length += bytes.len(); - libc::iovec { - iov_base: bytes.as_ptr().cast_mut().cast(), - iov_len: bytes.len(), - } - }); - - loop { - let written = libc::writev(1, iovecs.as_ptr(), iovecs.len() as i32); - assert!(written > 0); - - let mut remove = written as usize; - total_length -= remove; - - for iov in iovecs.iter_mut() { - let to_remove = iov.iov_len.min(remove); - iov.iov_len -= to_remove; - iov.iov_base = iov.iov_base.add(to_remove); - remove -= to_remove; - } - - if total_length == 0 { - break; - } - } -} - -fn random(mut buffer: &mut [u8]) { - while !buffer.is_empty() { - let len = unsafe { libc::getrandom(buffer.as_mut_ptr().cast(), buffer.len(), 0) }; - assert!(len > 0); - buffer = &mut buffer[len as usize..]; - } -} - -fn fit_ascii(c: u8) -> u8 { - const A: u8 = b'a'; - const Z: u8 = b'z'; - - if !(A..=Z).contains(&c) { - c % (Z - A + 1) + A - } else { - c - } -} - -#[expect(dead_code)] -#[derive(Debug, Default)] -struct Foo { - a: usize, - b: u64, - c: u8, -} - -/// Do some dummy stuff to test the kernel -pub fn dummy() { - unsafe { - test_write(c"Hello World\n"); - test_writev([c"Hello\n", c"World\n"]); - } - - let mut chars = [0; 6]; - random(&mut chars[..5]); - let chars = chars.map(fit_ascii); - - let letters = std::str::from_utf8(&chars).unwrap(); - eprintln!("Random letters: {letters}"); - - println!("Hello World"); - - unsafe { - let mut buffer = [0u8; 256]; - libc::getcwd(buffer.as_mut_ptr().cast(), buffer.len()); - - let cstr = CStr::from_bytes_with_nul_unchecked(&buffer); - - test_write(c"The current working directory is: "); - test_write(cstr); - test_write(c"\n"); - } - - let boxed = Box::new(Foo::default()); - println!("Debug {boxed:#?}"); - - let time = SystemTime::now(); - println!("System time: {time:?}"); - - let instant = Instant::now(); - println!("Instant: {instant:?}"); -} diff --git a/src/riscv/dummy_kernel/src/main.rs b/src/riscv/dummy_kernel/src/main.rs deleted file mode 100644 index eab166e9e794687d6812fc7e575a326672b0ba23..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/src/main.rs +++ /dev/null @@ -1,125 +0,0 @@ -mod sbi_crypto; - -#[cfg(target_os = "linux")] -mod linux_only; - -use tezos_crypto_rs::blake2b::digest_256; -use tezos_smart_rollup::entrypoint; -use tezos_smart_rollup::inbox::InboxMessage; -use tezos_smart_rollup::inbox::InternalInboxMessage; -use tezos_smart_rollup::michelson::MichelsonUnit; -use tezos_smart_rollup::prelude::*; -use tezos_smart_rollup::storage::path::OwnedPath; -use tezos_smart_rollup::types::SmartRollupAddress; -use tezos_smart_rollup_constants::core::PREIMAGE_HASH_SIZE; -use tezos_smart_rollup_constants::riscv::SBI_FIRMWARE_TEZOS; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_REVEAL; -use tezos_smart_rollup_constants::riscv::SbiError; - -#[entrypoint::main] -pub fn entry(host: &mut impl Runtime) { - #[cfg(target_os = "linux")] - linux_only::dummy(); - - let msg = "Hello World\n"; - debug_msg!(host, "{}", msg); - - let meta = host.reveal_metadata(); - debug_msg!(host, "I am {}\n", SmartRollupAddress::new(meta.address())); - debug_msg!(host, "{:#?}\n", meta); - - let hash = digest_256(msg.as_bytes()); - debug_msg!(host, "{:02X?}\n", hash); - - unsafe { - assert_eq!(hash, sbi_crypto::blake2b_hash256(msg.as_bytes())); - } - - let path: OwnedPath = "/hello".as_bytes().to_vec().try_into().unwrap(); - let () = host - .store_write(&path, msg.as_bytes(), 0) - .expect("Could not write to storage"); - let read_msg = host - .store_read(&path, 0, msg.len()) - .expect("Could not read from storage"); - assert_eq!(read_msg.as_slice(), msg.as_bytes()); - - unsafe { - let public_key: [u8; 32] = [ - 171, 32, 104, 249, 65, 125, 118, 36, 210, 237, 61, 116, 43, 133, 16, 15, 177, 4, 114, - 245, 84, 7, 13, 184, 49, 110, 76, 46, 147, 20, 137, 69, - ]; - let secret_key: [u8; 32] = [ - 147, 228, 82, 23, 29, 245, 139, 43, 80, 32, 225, 196, 0, 239, 143, 245, 138, 203, 227, - 208, 158, 63, 121, 41, 8, 220, 224, 224, 33, 132, 133, 237, - ]; - - let data = "Hello World".as_bytes(); - - let sig = sbi_crypto::ed25519_sign(&secret_key, data); - debug_msg!(host, "Signature is {sig:?}\n"); - - assert!(sbi_crypto::ed25519_verify(&public_key, &sig, data)); - } - - host.write_debug("Reveal metadata...\n"); - let result = host.reveal_metadata(); - host.write_debug("Reveal metadata succeeded, result: \n"); - host.write_debug(&format!("Rollup address: {:?}\n", result)); - - host.write_debug("Reveal preimage...\n"); - - let hash: [u8; PREIMAGE_HASH_SIZE] = [ - // tag byte of preimage hash - vec![0u8], - digest_256(hex::decode("cafebabe").unwrap().as_slice()), - ] - .concat() - .try_into() - .unwrap(); - let mut buffer = [0u8; 4096]; - let result_size = host.reveal_preimage(&hash, &mut buffer[..]).unwrap(); - host.write_debug(&format!( - "Preimage: {:?}\n", - hex::encode(&buffer[..result_size]) - )); - - host.write_debug("Invalid reveal request...\n"); - - let payload = [255u8]; - let mut buffer = [0u8; 4096]; - - // An ecall is made via inline assembly to trigger the invalid reveal request. - let result: isize; - unsafe { - core::arch::asm!( - "ecall", - in("a0") &payload, - in("a1") std::mem::size_of_val(&payload), - in("a2") &mut buffer, - in("a3") std::mem::size_of_val(&buffer), - in("a6") SBI_TEZOS_REVEAL, - in("a7") SBI_FIRMWARE_TEZOS, - lateout("a0") result, - ); - } - - assert!(result == SbiError::InvalidParam as isize); - - debug_msg!(host, "Reveals Done\n"); - - while let Some(msg) = host.read_input().expect("Want message") { - let (_, msg) = InboxMessage::::parse(msg.as_ref()) - .expect("Failed to parse inbox message"); - match msg { - // When running a rollup node with this kernel through Tezt tests - // `InfoPerlevel` messages are not identical for each run. This can - // result in slight changes to the number of ticks used to - // format them, which causes regression tests to fail. - InboxMessage::Internal(InternalInboxMessage::InfoPerLevel(_)) => { - debug_msg!(host, "Internal(InfoPerLevel)\n") - } - msg => debug_msg!(host, "{:#?}\n", msg), - } - } -} diff --git a/src/riscv/dummy_kernel/src/sbi_crypto.rs b/src/riscv/dummy_kernel/src/sbi_crypto.rs deleted file mode 100644 index 5227a7356ac7e6c23695109fa6a7d42d9d49ecaf..0000000000000000000000000000000000000000 --- a/src/riscv/dummy_kernel/src/sbi_crypto.rs +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -// TODO: RV-121: We want to access the crypto functions through the Tezos crypto crate instead of -// needing to define them here. - -use tezos_smart_rollup_constants::riscv::SBI_FIRMWARE_TEZOS; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_BLAKE2B_HASH256; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_ED25519_SIGN; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_ED25519_VERIFY; - -pub unsafe fn ed25519_verify(pk: &[u8; 32], sig: &[u8; 64], msg: &[u8]) -> bool { - let result: isize; - - core::arch::asm!( - "ecall", - in("a6") SBI_TEZOS_ED25519_VERIFY, - in("a7") SBI_FIRMWARE_TEZOS, - in("a0") pk.as_ptr(), - in("a1") sig.as_ptr(), - in("a2") msg.as_ptr(), - in("a3") msg.len(), - lateout("a0") result, - ); - - result == 1 -} - -pub unsafe fn ed25519_sign(sk: &[u8; 32], msg: &[u8]) -> [u8; 64] { - let mut out = [0u8; 64]; - let result: isize; - - core::arch::asm!( - "ecall", - in("a6") SBI_TEZOS_ED25519_SIGN, - in("a7") SBI_FIRMWARE_TEZOS, - in("a0") sk.as_ptr(), - in("a1") msg.as_ptr(), - in("a2") msg.len(), - in("a3") out.as_mut_ptr(), - lateout("a0") result, - ); - - assert_eq!( - result, 64, - "SBI_TEZOS_ED25519_SIGN call returned unexpected value: {result}" - ); - - out -} - -pub unsafe fn blake2b_hash256(msg: &[u8]) -> [u8; 32] { - let mut out = [0u8; 32]; - let result: isize; - - core::arch::asm!( - "ecall", - in("a6") SBI_TEZOS_BLAKE2B_HASH256, - in("a7") SBI_FIRMWARE_TEZOS, - in("a0") out.as_mut_ptr(), - in("a1") msg.as_ptr(), - in("a2") msg.len(), - lateout("a0") result, - ); - - assert_eq!( - result, 32, - "SBI_TEZOS_BLAKE2B_HASH256 call returned unexpected value: {result}" - ); - - out -} diff --git a/src/riscv/jstz/.cargo/config.toml b/src/riscv/jstz/.cargo/config.toml deleted file mode 100644 index 50cfe51f1d6de4303442280d699cc336436b991e..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/.cargo/config.toml +++ /dev/null @@ -1,15 +0,0 @@ -[build] -target = "riscv64gc-unknown-linux-musl" - -[profile.profiling] -inherits = "release" -debug = true - -[target.riscv64gc-unknown-linux-musl] -linker = "riscv64-unknown-linux-musl-gcc" -rustflags = [ - "-C", - "target-feature=+crt-static", - "-C", - "default-linker-libraries", -] diff --git a/src/riscv/jstz/.gitignore b/src/riscv/jstz/.gitignore deleted file mode 100644 index b37614f9d49ada69535f1238b75ae6969126554c..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -inbox-bench -inbox.json diff --git a/src/riscv/jstz/Cargo.lock b/src/riscv/jstz/Cargo.lock deleted file mode 100644 index 2fa6fe13600ba02925e38160874dd8e974742c09..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/Cargo.lock +++ /dev/null @@ -1,2784 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" -dependencies = [ - "bincode_derive", - "serde", -] - -[[package]] -name = "bincode_derive" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" -dependencies = [ - "virtue", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "boa_ast" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "arbitrary", - "bitflags", - "boa_interner", - "boa_macros", - "boa_string", - "indexmap", - "num-bigint", - "rustc-hash", -] - -[[package]] -name = "boa_engine" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "arrayvec", - "bitflags", - "boa_ast", - "boa_gc", - "boa_interner", - "boa_macros", - "boa_parser", - "boa_profiler", - "boa_string", - "bytemuck", - "cfg-if", - "dashmap", - "fast-float", - "hashbrown 0.14.5", - "icu_normalizer", - "indexmap", - "intrusive-collections", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "num_enum", - "once_cell", - "pollster", - "portable-atomic", - "rand 0.8.5", - "regress", - "rustc-hash", - "ryu-js", - "serde", - "serde_json", - "sptr", - "static_assertions", - "tap", - "thin-vec", - "thiserror", - "time", -] - -[[package]] -name = "boa_gc" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "boa_macros", - "boa_profiler", - "boa_string", - "hashbrown 0.14.5", - "thin-vec", -] - -[[package]] -name = "boa_interner" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "arbitrary", - "boa_gc", - "boa_macros", - "hashbrown 0.14.5", - "indexmap", - "once_cell", - "phf", - "rustc-hash", - "static_assertions", -] - -[[package]] -name = "boa_macros" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", - "synstructure", -] - -[[package]] -name = "boa_parser" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "bitflags", - "boa_ast", - "boa_interner", - "boa_macros", - "boa_profiler", - "fast-float", - "icu_properties", - "num-bigint", - "num-traits", - "regress", - "rustc-hash", -] - -[[package]] -name = "boa_profiler" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" - -[[package]] -name = "boa_string" -version = "0.19.0" -source = "git+https://github.com/trilitech/boa.git?branch=felix%40enable-gc-reset#65bccba01ce63a150ce22135668bdce5c7f8fd91" -dependencies = [ - "fast-float", - "paste", - "rustc-hash", - "sptr", - "static_assertions", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "bytemuck" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" -dependencies = [ - "num-traits", -] - -[[package]] -name = "clap" -version = "4.5.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" - -[[package]] -name = "crypto-bigint" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8658c15c5d921ddf980f7fe25b1e82f4b7a4083b2c4985fea4922edb8e43e07d" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "cryptoxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version", - "subtle", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "der" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "derive_more" -version = "0.99.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.98", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "dlmalloc" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "ecdsa" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" -dependencies = [ - "der", - "elliptic-curve", - "hmac", - "signature 1.3.2", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "signature 2.2.0", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "sha2 0.10.8", - "subtle", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "elliptic-curve" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5c176479da93a0983f0a6fdc3c1b8e7d5be0d7fe3fe05a99f15b96582b9a8" -dependencies = [ - "crypto-bigint", - "ff", - "generic-array", - "group", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - -[[package]] -name = "fast-float" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "ff" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-serde" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" -dependencies = [ - "http", - "serde", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "inbox-bench" -version = "0.1.0" -dependencies = [ - "base64 0.22.1", - "bincode", - "clap", - "hex", - "http", - "jstz_crypto", - "jstz_proto", - "regex", - "serde", - "serde_json", - "tezos-smart-rollup", - "tezos_data_encoding", -] - -[[package]] -name = "indexmap" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" -dependencies = [ - "equivalent", - "hashbrown 0.15.2", - "serde", -] - -[[package]] -name = "intrusive-collections" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" -dependencies = [ - "memoffset", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jstz" -version = "0.0.0" -dependencies = [ - "bincode", - "boa_gc", - "hex", - "jstz_crypto", - "jstz_kernel", - "serde", - "serde_json", - "tezos-smart-rollup", - "tezos_crypto_rs", -] - -[[package]] -name = "jstz_api" -version = "0.1.0-alpha.0" -source = "git+https://github.com/jstz-dev/jstz.git#cc184e1111566ab608fde7755bb0eec508932cbd" -dependencies = [ - "base64 0.21.7", - "boa_engine", - "boa_gc", - "bytes", - "clap", - "derive_more", - "encoding_rs", - "fastrand", - "form_urlencoded", - "http", - "jstz_core", - "jstz_crypto", - "serde", - "serde_json", - "tezos-smart-rollup", - "url", - "urlpattern", - "utoipa", -] - -[[package]] -name = "jstz_core" -version = "0.1.0-alpha.0" -source = "git+https://github.com/jstz-dev/jstz.git#cc184e1111566ab608fde7755bb0eec508932cbd" -dependencies = [ - "bincode", - "boa_engine", - "boa_gc", - "chrono", - "derive_more", - "erased-serde", - "getrandom", - "jstz_crypto", - "nom", - "serde", - "tezos-smart-rollup", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "jstz_crypto" -version = "0.1.0-alpha.0" -source = "git+https://github.com/jstz-dev/jstz.git#cc184e1111566ab608fde7755bb0eec508932cbd" -dependencies = [ - "anyhow", - "bincode", - "boa_gc", - "derive_more", - "hex", - "serde", - "serde_json", - "tezos_crypto_rs", - "utoipa", -] - -[[package]] -name = "jstz_kernel" -version = "0.1.0-alpha.0" -source = "git+https://github.com/jstz-dev/jstz.git#cc184e1111566ab608fde7755bb0eec508932cbd" -dependencies = [ - "bincode", - "boa_engine", - "http", - "http-serde", - "jstz_api", - "jstz_core", - "jstz_crypto", - "jstz_proto", - "num-traits", - "serde", - "tezos-smart-rollup", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "jstz_proto" -version = "0.1.0-alpha.0" -source = "git+https://github.com/jstz-dev/jstz.git#cc184e1111566ab608fde7755bb0eec508932cbd" -dependencies = [ - "bincode", - "boa_engine", - "boa_gc", - "derive_more", - "either", - "erased-serde", - "http", - "http-serde", - "jstz_api", - "jstz_core", - "jstz_crypto", - "serde", - "serde_json", - "tezos-smart-rollup", - "tezos_crypto_rs", - "tezos_data_encoding", - "utoipa", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.169" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "arbitrary", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "once_cell" -version = "1.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "p256" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186" -dependencies = [ - "ecdsa", - "elliptic-curve", - "sha2 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "parse-display" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7271152b3c46c07c729698e7a5248e2744466b3446d222c97a0b1315925a97b1" -dependencies = [ - "once_cell", - "parse-display-derive", - "regex", -] - -[[package]] -name = "parse-display-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a9f3e41b237b77c99c09686481c235964ff5878229412b226c451f3e809f4f" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "regex", - "regex-syntax 0.6.29", - "syn 1.0.109", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - -[[package]] -name = "portable-atomic" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "proc-macro2" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quanta" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-cpuid" -version = "11.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529468c1335c1c03919960dfefdb1b3648858c20d7ec2d0663e728e4a717efbc" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "regress" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ef7fa9ed0256d64a688a3747d0fef7a88851c18a5e1d57f115f38ec2e09366" -dependencies = [ - "hashbrown 0.15.2", - "memchr", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" - -[[package]] -name = "ryu" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" - -[[package]] -name = "ryu-js" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" - -[[package]] -name = "serde" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "serde_json" -version = "1.0.138" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - -[[package]] -name = "signature" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "smallvec" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" - -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" - -[[package]] -name = "strum_macros" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tezos-smart-rollup" -version = "0.2.2" -dependencies = [ - "hex", - "serde_json", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-entrypoint", - "tezos-smart-rollup-host", - "tezos-smart-rollup-macros", - "tezos-smart-rollup-mock", - "tezos-smart-rollup-storage", - "tezos-smart-rollup-utils", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "tezos-smart-rollup-build-utils" -version = "0.2.2" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", -] - -[[package]] -name = "tezos-smart-rollup-constants" -version = "0.2.2" - -[[package]] -name = "tezos-smart-rollup-core" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-constants", -] - -[[package]] -name = "tezos-smart-rollup-debug" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", -] - -[[package]] -name = "tezos-smart-rollup-encoding" -version = "0.2.2" -dependencies = [ - "hex", - "nom", - "num-bigint", - "num-traits", - "paste", - "regex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", - "thiserror", - "time", -] - -[[package]] -name = "tezos-smart-rollup-entrypoint" -version = "0.2.2" -dependencies = [ - "cfg-if", - "dlmalloc", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-host", - "tezos-smart-rollup-mock", - "tezos-smart-rollup-panic-hook", - "tezos-smart-rollup-utils", -] - -[[package]] -name = "tezos-smart-rollup-host" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", - "tezos_crypto_rs", - "tezos_data_encoding", - "thiserror", -] - -[[package]] -name = "tezos-smart-rollup-macros" -version = "0.2.2" -dependencies = [ - "proc-macro-error2", - "quote", - "shellexpand", - "syn 2.0.98", - "tezos-smart-rollup-build-utils", -] - -[[package]] -name = "tezos-smart-rollup-mock" -version = "0.2.2" -dependencies = [ - "hex", - "tezos-smart-rollup-core", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "tezos-smart-rollup-panic-hook" -version = "0.2.2" -dependencies = [ - "rustversion", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-core", -] - -[[package]] -name = "tezos-smart-rollup-storage" -version = "0.2.2" -dependencies = [ - "tezos-smart-rollup-core", - "tezos-smart-rollup-debug", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-host", - "thiserror", -] - -[[package]] -name = "tezos-smart-rollup-utils" -version = "0.2.2" -dependencies = [ - "clap", - "hex", - "quanta", - "serde", - "serde_json", - "tezos-smart-rollup-build-utils", - "tezos-smart-rollup-encoding", - "tezos-smart-rollup-mock", - "tezos_crypto_rs", - "tezos_data_encoding", -] - -[[package]] -name = "tezos_crypto_rs" -version = "0.6.0" -dependencies = [ - "anyhow", - "bs58", - "byteorder", - "cryptoxide", - "ed25519-dalek", - "hex", - "libsecp256k1", - "nom", - "num-bigint", - "num-traits", - "p256", - "rand 0.7.3", - "serde", - "strum", - "strum_macros", - "tezos_data_encoding", - "thiserror", - "zeroize", -] - -[[package]] -name = "tezos_data_encoding" -version = "0.6.0" -dependencies = [ - "bit-vec", - "bitvec", - "hex", - "lazy_static", - "nom", - "num-bigint", - "num-traits", - "serde", - "tezos_data_encoding_derive", - "thiserror", -] - -[[package]] -name = "tezos_data_encoding_derive" -version = "0.6.0" -dependencies = [ - "lazy_static", - "once_cell", - "parse-display", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "thin-vec" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "time" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.22.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - -[[package]] -name = "typeid" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicode-ident" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "urlpattern" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609" -dependencies = [ - "derive_more", - "regex", - "serde", - "unic-ucd-ident", - "url", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "utoipa" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0" -dependencies = [ - "indexmap", - "serde", - "serde_json", - "utoipa-gen", -] - -[[package]] -name = "utoipa-gen" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.98", - "url", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtue" -version = "0.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.98", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" -dependencies = [ - "memchr", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] diff --git a/src/riscv/jstz/Cargo.toml b/src/riscv/jstz/Cargo.toml deleted file mode 100644 index 7f7ac71dc98f5253a2dc1a54c084c839307e8d21..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/Cargo.toml +++ /dev/null @@ -1,79 +0,0 @@ -[workspace] -members = ["bench", "kernel"] -resolver = "2" - -[patch.crates-io] -tezos-smart-rollup = { path = "../../kernel_sdk/sdk" } -tezos-smart-rollup-host = { path = "../../kernel_sdk/host" } -tezos-smart-rollup-core = { path = "../../kernel_sdk/core" } -tezos-smart-rollup-mock = { path = "../../kernel_sdk/mock" } -tezos-smart-rollup-encoding = { path = "../../kernel_sdk/encoding" } -tezos-smart-rollup-entrypoint = { path = "../../kernel_sdk/entrypoint" } -tezos-smart-rollup-debug = { path = "../../kernel_sdk/debug" } -tezos-smart-rollup-panic-hook = { path = "../../kernel_sdk/panic-hook" } -tezos-smart-rollup-storage = { path = "../../kernel_sdk/storage" } -tezos_crypto_rs = { path = "../../../sdk/rust/crypto" } -tezos_data_encoding = { path = "../../../sdk/rust/encoding" } -# Patches from the jstz repo are dropped, copying them here -boa_ast = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_engine = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_gc = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_interner = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_macros = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_parser = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -boa_profiler = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } - -[workspace.dependencies] -base64 = "0.22.0" -# The above boa patches apply only for dependencies of dependencies. -# Patch needs to be here to take effect as a direct dependency as well. -boa_gc = { git = "https://github.com/trilitech/boa.git", branch = "felix@enable-gc-reset" } -http = "1.1.0" -bincode = "2.0.0-rc.3" -regex = "1.10.4" -serde_json = "1.0.115" - -[workspace.dependencies.tezos-smart-rollup] -version = "0.2.2" -default-features = false -features = [ - "std", - "crypto", - "panic-hook", - "data-encoding", - "proto-alpha", - "experimental-host-in-memory-store", - "extra", - "native-kernel", -] - -[workspace.dependencies.jstz_kernel] -git = "https://github.com/jstz-dev/jstz.git" - -[workspace.dependencies.jstz_crypto] -git = "https://github.com/jstz-dev/jstz.git" - -[workspace.dependencies.jstz_proto] -git = "https://github.com/jstz-dev/jstz.git" - -[workspace.dependencies.serde] -version = "1.0.197" -features = ["derive"] - -[workspace.dependencies.hex] -version = "0.4.3" -features = ["serde"] - -[workspace.dependencies.clap] -version = "4.4.6" -features = ["derive"] - -[workspace.dependencies.tezos_crypto_rs] -version = "0.6.0" -path = "../../../sdk/rust/crypto" -default-features = false - -[workspace.dependencies.tezos_data_encoding] -version = "0.6.0" -path = "../../../sdk/rust/encoding" -default-features = false diff --git a/src/riscv/jstz/Makefile b/src/riscv/jstz/Makefile deleted file mode 100644 index df7fef102718f618f15c73321b94c497298ed78a..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2025 TriliTech -# -# SPDX-License-Identifier: MIT - -NATIVE_TARGET ?= $(shell rustc -vV | grep 'host:' | awk '{print $$2}') -ifneq ($(NATIVE_TARGET),) -NATIVE_OPT := --target "$(NATIVE_TARGET)" -endif - -INBOX_FILE ?= $(shell mktemp) - -.PHONY: all -all: build test check inbox-bench - -.PHONY: print-native-target -print-native-target: - $(info $(NATIVE_TARGET)) - -.PHONY: build-deps -build-deps: -ifneq ($(NATIVE_TARGET),) - @rustup target add $(NATIVE_TARGET) -endif - -.PHONY: build -build: build-kernel inbox-bench - -.PHONY: build-kernel -build-kernel: - @cargo build -p jstz --release - -.PHONY: build-kernel-static -build-kernel-static: - @INBOX_FILE=$(INBOX_FILE) cargo build -p jstz --release --features static-inbox - -.PHONY: build-kernel-native -build-kernel-native: - @INBOX_FILE=$(INBOX_FILE) cargo build -p jstz --release --features static-inbox $(NATIVE_OPT) - -.PHONY: test -test: - @cargo test --no-default-features $(NATIVE_OPT) - @../scripts/jstz-bench.sh -t 1 - @../scripts/jstz-bench.sh -t 1 -s - @../scripts/jstz-bench.sh -t 1 -sn - -.PHONY: run -run: - @cargo run --no-default-features - -.PHONY: check -check: - @exec ../scripts/format.sh --check - @INBOX_FILE=$(INBOX_FILE) cargo check --all-features --workspace --locked - @INBOX_FILE=$(INBOX_FILE) cargo clippy --all-features -- --deny warnings - @INBOX_FILE=$(INBOX_FILE) cargo doc --all-features --document-private-items --no-deps - -.PHONY: inbox-bench -inbox-bench: - @cargo build $(NATIVE_OPT) --bin inbox-bench --release - @cp target/$(NATIVE_TARGET)/release/inbox-bench . - -.PHONY: clean -clean: - @cargo clean diff --git a/src/riscv/jstz/bench/Cargo.toml b/src/riscv/jstz/bench/Cargo.toml deleted file mode 100644 index 7e2a5b791fa2ef9c7438c0c4ebd272a8f8703b8d..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/bench/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "inbox-bench" -version = "0.1.0" -edition = "2021" - -[dependencies] -clap.workspace = true -hex.workspace = true -jstz_crypto.workspace = true -jstz_proto.workspace = true -tezos-smart-rollup.workspace = true -serde.workspace = true -serde_json.workspace = true -base64.workspace = true -http.workspace = true -bincode.workspace = true -regex.workspace = true -tezos_data_encoding.workspace = true diff --git a/src/riscv/jstz/bench/src/generate.rs b/src/riscv/jstz/bench/src/generate.rs deleted file mode 100644 index 6e153736892beab7c111c4dbf04bc85f19fd1e17..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/bench/src/generate.rs +++ /dev/null @@ -1,332 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::path::Path; - -use base64::Engine; -use base64::engine::general_purpose::URL_SAFE; -use bincode::config::Configuration; -use bincode::config::Fixint; -use bincode::config::LittleEndian; -use http::HeaderMap; -use http::Method; -use http::Uri; -use jstz_crypto::hash::Hash; -use jstz_crypto::keypair_from_passphrase; -use jstz_crypto::public_key::PublicKey; -use jstz_crypto::public_key_hash::PublicKeyHash; -use jstz_crypto::secret_key::SecretKey; -use jstz_crypto::smart_function_hash::SmartFunctionHash; -use jstz_proto::context::account::Address; -use jstz_proto::context::account::Addressable; -use jstz_proto::context::account::Nonce; -use jstz_proto::context::account::ParsedCode; -use jstz_proto::operation::Content; -use jstz_proto::operation::DeployFunction; -use jstz_proto::operation::Operation; -use jstz_proto::operation::RunFunction; -use jstz_proto::operation::SignedOperation; -use serde::Serialize; -use serde::Serializer; -use tezos_data_encoding::enc::BinWriter; -use tezos_smart_rollup::inbox::ExternalMessageFrame; -use tezos_smart_rollup::types::SmartRollupAddress; -use tezos_smart_rollup::utils::inbox::file::InboxFile; -use tezos_smart_rollup::utils::inbox::file::Message; - -const BINCODE_CONFIGURATION: Configuration = bincode::config::legacy(); - -const FA2: &str = include_str!("../../fa2.js"); - -const DEFAULT_GAS_LIMIT: u32 = 100_000; - -// tag + 20 byte address -const EXTERNAL_FRAME_SIZE: usize = 21; - -type Result = std::result::Result>; - -/// Generate the requested 'FA2 transfers', writing to `./inbox.json`. -/// -/// This includes setup (contract deployment/minting) as well as balance checks at the end. -/// The transfers are generated with a 'follow on' strategy. For example 'account 0' will -/// have `num_accounts` minted of 'token 0'. It will then transfer all of them to 'account 1', -/// which will transfer `num_accounts - 1` to the next account, etc. -pub fn handle_generate(rollup_addr: &str, inbox_file: &Path, transfers: usize) -> Result<()> { - let inbox = generate_inbox(rollup_addr, transfers)?; - inbox.save(inbox_file)?; - Ok(()) -} - -/// Like [`handle_generate`] but writes the inbox as a shell script. -pub fn handle_generate_script( - rollup_addr: &str, - script_file: &Path, - transfers: usize, -) -> Result<()> { - let inbox = generate_inbox(rollup_addr, transfers)?; - inbox.save_script(script_file)?; - Ok(()) -} - -/// Generate the inbox for the given rollup address and number of transfers. -fn generate_inbox(rollup_addr: &str, transfers: usize) -> Result { - let rollup_addr = SmartRollupAddress::from_b58check(rollup_addr)?; - - let accounts = accounts_for_transfers(transfers); - - if accounts == 0 { - return Err("--transfers must be greater than zero".into()); - } - - let mut accounts = gen_keys(accounts)?; - - // Part 1 - setup - let (fa2_address, deploy) = deploy_fa2(rollup_addr.clone(), accounts.first_mut().unwrap())?; - let batch_mint = batch_mint(rollup_addr.clone(), &mut accounts, &fa2_address)?; - - let mut messages = vec![deploy, batch_mint]; - - // Part 2 - transfers - let len = accounts.len(); - let expected_len = messages.len() + transfers; - - 'outer: for token_id in 0..len { - for (from, amount) in (token_id..(token_id + len)).zip(1..len) { - if expected_len == messages.len() { - break 'outer; - } - - let to = accounts[(from + 1) % len].address.clone(); - let transfer = Transfer { - token_id, - amount: len - amount, - to, - }; - - let account = &mut accounts[from % len]; - let op = transfer_op(rollup_addr.clone(), account, &fa2_address, &transfer)?; - - messages.push(op); - } - } - - // Part 3 - checking - let tokens = 0..accounts.len(); - for balance in accounts - .iter_mut() - .map(|a| balance(rollup_addr.clone(), a, &fa2_address, tokens.clone())) - { - messages.push(balance?); - } - - // Output inbox file - let inbox = InboxFile(vec![messages]); - Ok(inbox) -} - -#[derive(Debug, Serialize)] -struct MintNew<'a> { - token_id: usize, - #[serde(serialize_with = "address_ser")] - owner: &'a Address, - amount: usize, -} - -#[derive(Debug, Serialize)] -struct BalanceRequest<'a> { - token_id: usize, - #[serde(serialize_with = "address_ser")] - owner: &'a Address, -} - -#[derive(Debug, Serialize)] -struct Transfer { - token_id: usize, - amount: usize, - #[serde(serialize_with = "address_ser")] - to: Address, -} - -#[derive(Debug, Serialize)] -struct TransferToken<'a> { - #[serde(serialize_with = "address_ser")] - from: &'a Address, - transfers: &'a [&'a Transfer], -} - -fn address_ser(address: &Address, ser: S) -> std::result::Result -where - S: Serializer, -{ - let address = address.to_base58(); - String::serialize(&address, ser) -} - -fn transfer_op( - rollup_addr: SmartRollupAddress, - account: &mut Account, - fa2: &Address, - transfer: &Transfer, -) -> Result { - let transfer = [TransferToken { - from: &account.address, - transfers: &[transfer], - }]; - - let body = serde_json::ser::to_vec(&transfer)?; - - let content = Content::RunFunction(RunFunction { - uri: Uri::try_from(format!("tezos://{fa2}/transfer"))?, - method: Method::POST, - headers: HeaderMap::default(), - body: Some(body), - gas_limit: DEFAULT_GAS_LIMIT.try_into()?, - }); - - account.operation_to_message(rollup_addr, content) -} - -fn balance( - rollup_addr: SmartRollupAddress, - account: &mut Account, - fa2: &Address, - tokens: std::ops::Range, -) -> Result { - let reqs: Vec<_> = tokens - .map(|i| BalanceRequest { - owner: &account.address, - token_id: i, - }) - .collect(); - let query = serde_json::ser::to_vec(&reqs)?; - let query = URL_SAFE.encode(query); - - let content = Content::RunFunction(RunFunction { - uri: Uri::try_from(format!("tezos://{fa2}/balance_of?requests={query}"))?, - method: Method::GET, - headers: HeaderMap::default(), - body: None, - gas_limit: DEFAULT_GAS_LIMIT.try_into()?, - }); - - account.operation_to_message(rollup_addr, content) -} - -fn batch_mint( - rollup_addr: SmartRollupAddress, - accounts: &mut [Account], - fa2: &Address, -) -> Result { - let amount = accounts.len() + 1; - let mints: Vec<_> = accounts - .iter() - .enumerate() - .map(|(i, a)| MintNew { - token_id: i, - owner: &a.address, - amount, - }) - .collect(); - - let body = serde_json::ser::to_vec(&mints)?; - let account = &mut accounts[0]; - - let content = Content::RunFunction(RunFunction { - uri: Uri::try_from(format!("tezos://{fa2}/mint_new"))?, - method: Method::POST, - headers: HeaderMap::default(), - body: Some(body), - gas_limit: DEFAULT_GAS_LIMIT.try_into()?, - }); - - account.operation_to_message(rollup_addr, content) -} - -fn deploy_fa2( - rollup_addr: SmartRollupAddress, - account: &mut Account, -) -> Result<(Address, Message)> { - let code: ParsedCode = FA2.to_string().try_into()?; - - let address = Address::SmartFunction(SmartFunctionHash::digest( - format!("{}{}{}", &account.address, code, account.nonce.next()).as_bytes(), - )?); - - let content = Content::DeployFunction(DeployFunction { - function_code: code, - account_credit: 0, - }); - - let message = account.operation_to_message(rollup_addr, content)?; - - Ok((address, message)) -} - -fn gen_keys(num: usize) -> Result> { - let mut res = Vec::with_capacity(num); - - for i in 0..num { - let (sk, pk) = keypair_from_passphrase(&i.to_string())?; - let address = Address::User(PublicKeyHash::from(&pk)); - let account = Account { - address, - sk, - pk, - nonce: Default::default(), - }; - res.push(account) - } - - Ok(res) -} - -struct Account { - nonce: Nonce, - sk: SecretKey, - pk: PublicKey, - address: Address, -} - -impl Account { - fn operation_to_message( - &mut self, - rollup_addr: SmartRollupAddress, - content: Content, - ) -> Result { - let Address::User(source) = &self.address else { - return Err("Expected a user address".into()); - }; - - let op = Operation { - source: source.clone(), - nonce: self.nonce, - content, - }; - - let hash = op.hash(); - let signed_op = SignedOperation::new(self.pk.clone(), self.sk.sign(hash)?, op); - - let bytes = bincode::encode_to_vec(signed_op, BINCODE_CONFIGURATION)?; - let mut external = Vec::with_capacity(bytes.len() + EXTERNAL_FRAME_SIZE); - - let frame = ExternalMessageFrame::Targetted { - contents: bytes, - address: rollup_addr, - }; - - frame.bin_write(&mut external)?; - - self.nonce = self.nonce.next(); - let message = Message::External { external }; - - Ok(message) - } -} - -/// The generation strategy supports up to `num_accounts ^ 2` transfers, -/// find the smallest number of accounts which will allow for this. -fn accounts_for_transfers(transfers: usize) -> usize { - f64::sqrt(transfers as f64).ceil() as usize + 1 -} diff --git a/src/riscv/jstz/bench/src/main.rs b/src/riscv/jstz/bench/src/main.rs deleted file mode 100644 index 6a3b076f232cad519d91fb3b4b64c8688c67d287..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/bench/src/main.rs +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::path::Path; - -use clap::Parser; -use clap::Subcommand; -use generate::handle_generate; -use generate::handle_generate_script; -use results::handle_results; - -mod generate; -mod results; - -const DEFAULT_ROLLUP_ADDRESS: &str = "sr163Lv22CdE8QagCwf48PWDTquk6isQwv57"; - -type Result = std::result::Result>; - -#[derive(Debug, Parser)] -#[command(long_about = None)] -pub struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Debug, Subcommand)] -enum Commands { - #[command(about = "Generate inbox.json file")] - Generate { - #[arg(long, default_value = DEFAULT_ROLLUP_ADDRESS)] - address: String, - #[arg(long)] - transfers: usize, - #[arg(long, default_value = "inbox.json")] - inbox_file: Box, - }, - #[command(about = "Generate inbox.sh script")] - GenerateScript { - #[arg(long, default_value = DEFAULT_ROLLUP_ADDRESS)] - address: String, - #[arg(long)] - transfers: usize, - #[arg(long, default_value = "inbox.sh")] - script_file: Box, - }, - #[command(about = "Extract results from inbox.json & log file")] - Results { - #[arg(long)] - inbox_file: Box, - #[arg(long)] - log_file: Vec>, - #[arg(long)] - expected_transfers: usize, - }, -} - -fn main() -> Result<()> { - match Cli::parse().command { - Commands::Generate { - address, - inbox_file, - transfers, - } => handle_generate(&address, &inbox_file, transfers)?, - Commands::GenerateScript { - address, - script_file, - transfers, - } => handle_generate_script(&address, &script_file, transfers)?, - Commands::Results { - inbox_file, - log_file, - expected_transfers, - } => handle_results(inbox_file, log_file, expected_transfers)?, - } - - Ok(()) -} diff --git a/src/riscv/jstz/bench/src/results.rs b/src/riscv/jstz/bench/src/results.rs deleted file mode 100644 index d556d3b09a9a04cf6866fd4879ee57a2249b074e..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/bench/src/results.rs +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::collections::HashSet; -use std::fmt; -use std::fs::read_to_string; -use std::path::Path; -use std::time::Duration; - -use regex::Regex; -use serde::Deserialize; -use tezos_smart_rollup::utils::inbox::file::InboxFile; -use tezos_smart_rollup::utils::inbox::file::Message; - -use crate::Result; - -// Three sets of messages: -// 1. Deployment -// 2. Minting & Transfers -// 3. Balance Checks -// ... but all contained in one level -const EXPECTED_LEVELS: usize = 1; - -pub fn handle_results( - inbox: Box, - all_logs: Vec>, - expected_transfers: usize, -) -> Result<()> { - let inbox = InboxFile::load(&inbox)?; - - let all_metrics = all_logs - .iter() - .map(|logs| { - let logs = read_to_string(logs)? - .lines() - .map(serde_json::from_str) - .filter_map(|l| l.map(LogLine::classify).transpose()) - .collect::, _>>()?; - - let levels = logs_to_levels(logs)?; - - if inbox.0.len() != levels.len() || levels.len() != EXPECTED_LEVELS { - return Err(format!( - "InboxFile contains {} levels, found {} in logs, expected {EXPECTED_LEVELS}", - inbox.0.len(), - levels.len() - ) - .into()); - } - - let [results]: [_; EXPECTED_LEVELS] = levels.try_into().unwrap(); - - check_deploy(&results)?; - let metrics = check_transfer_metrics(&results, expected_transfers)?; - check_balances( - &results, - &inbox.0[0][2 + expected_transfers..], - expected_transfers, - )?; - - Ok(metrics) - }) - .collect::>>()?; - - if all_metrics.len() > 1 { - let len = all_metrics.len(); - - for (num, metrics) in all_metrics.iter().enumerate() { - println!("Run {} / {len} => {metrics}", num + 1); - } - - let agg_metrics = TransferMetrics::aggregate(&all_metrics); - println!("\nAggregate => {agg_metrics}"); - } else if let Some(metrics) = all_metrics.first() { - println!("{metrics}"); - } - - Ok(()) -} - -fn check_deploy(level: &Level) -> Result<()> { - if level.deployments.len() != 1 { - return Err("Expected FA2 contract deployment".into()); - } - - if level.executions.is_empty() { - return Err("Expected FA2 token minting".into()); - } - - Ok(()) -} - -#[derive(Clone, Debug, Default)] -struct TransferMetrics { - transfers: usize, - duration: Duration, - tps: f64, -} - -impl TransferMetrics { - fn aggregate(metrics: &[TransferMetrics]) -> TransferMetrics { - let summed = metrics.iter().fold(Self::default(), |acc, m| Self { - transfers: acc.transfers + m.transfers, - duration: acc.duration + m.duration, - tps: acc.tps + m.tps, - }); - - Self { - tps: summed.tps / metrics.len() as f64, - ..summed - } - } -} - -impl fmt::Display for TransferMetrics { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{} FA2 transfers took {:?} @ {:.3} TPS", - self.transfers, self.duration, self.tps - ) - } -} - -fn check_transfer_metrics(level: &Level, expected_transfers: usize) -> Result { - if expected_transfers + 1 != level.executions.len() { - return Err(format!( - "Expected {expected_transfers} transfers, got {}", - level.executions.len() - 1 - ) - .into()); - } - - let transfers = level.executions.len() - 1; - // The first execution is the minting call. We collect the time elapsed at the _end_ of the - // minting, all the way up to the _end_ of the last execution (transfer). - let duration = level.executions[transfers].elapsed - level.executions[0].elapsed; - let tps = (transfers as f64) / duration.as_secs_f64(); - - Ok(TransferMetrics { - transfers, - duration, - tps, - }) -} - -// The generated transfers (for a number of accounts N), has a target final state: -// Every account should hold one of every token. -// -// This requires (N - 1) * num_tokens transfers. -// -// Therefore, if an account has `0` of a token, there's a transfer missing below this maximum -// number. -fn check_balances(level: &Level, messages: &[Message], num_transfers: usize) -> Result<()> { - let re = Regex::new(r#"^.*"([\w0-9]+) has ([0-9]+) of token ([0-9]+)".*$"#).unwrap(); - - let mut accounts = HashSet::new(); - let mut tokens = HashSet::new(); - let mut skipped_receives = 0; - - for m in level.balance_checks.iter().map(|l| &l.message) { - for (_, [address, balance, token]) in re.captures_iter(m).map(|c| c.extract()) { - accounts.insert(address); - tokens.insert(token.parse::()?); - - let balance = balance.parse::()?; - - if balance == 0 { - skipped_receives += 1; - } - } - } - - // Checks - if accounts.len() != tokens.len() { - return Err(format!( - "Expected {} accounts to equal {} tokens", - accounts.len(), - tokens.len() - ) - .into()); - } - - if accounts.len() != messages.len() { - return Err(format!( - "Have {} accounts but only {} messages for checking balances", - accounts.len(), - messages.len() - ) - .into()); - } - - let expected_transfers = (accounts.len() - 1) * tokens.len() - skipped_receives; - - if expected_transfers != num_transfers { - return Err(format!( - "Found {} transfer messages, vs {} transfers completed", - num_transfers, expected_transfers - ) - .into()); - } - - Ok(()) -} - -fn logs_to_levels(logs: Vec) -> Result> { - let mut levels = Vec::new(); - - let mut level = Level::default(); - - let mut balance_checks = Vec::new(); - for line in logs.into_iter() { - match line { - LogType::StartOfLevel(_) => { - if level != Level::default() { - return Err( - format!("StartOfLevel message not at start of level {level:?}").into(), - ); - } - } - LogType::EndOfLevel(_) => { - levels.push(level); - level = Default::default(); - } - LogType::Deploy(l) => level.deployments.push(l), - LogType::Success(l) if balance_checks.is_empty() => level.executions.push(l), - LogType::Success(_) => level.balance_checks.append(&mut balance_checks), - LogType::SmartFunctionLog(l) => balance_checks.push(l), - } - } - - if level != Level::default() { - return Err("Final level missing EndOfLevel message {last:?}".into()); - } - - Ok(levels) -} - -#[derive(Deserialize, Debug, PartialEq)] -struct LogLine { - elapsed: Duration, - message: String, -} - -impl LogLine { - fn classify(self) -> Option { - let m = &self.message; - - if m.starts_with(SOL) { - Some(LogType::StartOfLevel(self)) - } else if m.starts_with(EOL) { - Some(LogType::EndOfLevel(self)) - } else if m.starts_with(DEPLOY) { - Some(LogType::Deploy(self)) - } else if m.starts_with(SUCCESS) { - Some(LogType::Success(self)) - } else if m.starts_with(LOG) { - Some(LogType::SmartFunctionLog(self)) - } else { - None - } - } -} - -#[derive(Debug)] -enum LogType { - StartOfLevel(#[allow(unused)] LogLine), - Deploy(LogLine), - Success(LogLine), - EndOfLevel(#[allow(unused)] LogLine), - SmartFunctionLog(LogLine), -} - -const SOL: &str = "Message: Internal(StartOfLevel)"; -const DEPLOY: &str = "[📜] Smart function deployed"; -const SUCCESS: &str = "🚀 Smart function executed successfully"; -const EOL: &str = "Internal message: end of level"; -const LOG: &str = "[JSTZ:SMART_FUNCTION:LOG]"; - -#[derive(Default, Debug, PartialEq)] -struct Level { - deployments: Vec, - executions: Vec, - balance_checks: Vec, -} diff --git a/src/riscv/jstz/fa2.js b/src/riscv/jstz/fa2.js deleted file mode 100644 index 9636d84506dbd61524ffd200cf2d91c5f849f939..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/fa2.js +++ /dev/null @@ -1 +0,0 @@ -function o(e){return typeof e=="string"}function a(e,r){return Array.isArray(r)&&r.reduce((n,t)=>n&&e(t),!0)}function i(e){return typeof e=="number"&&Number.isInteger(e)}function k(e){let r=e;try{return o(r.to)&&i(r.token_id)&&Number.isInteger(r.amount)}catch{return!1}}function w(e){let r=e;try{return o(r.from)&&a(k,r.transfers)}catch{return!1}}function A(e){let r=e;try{return(r.operation==="add_operator"||r.operation==="remove_operator")&&o(r.owner)&&o(r.operator)&&i(r.token_id)}catch{return!1}}function p(e){let r=e;try{return o(r.owner)&&i(r.token_id)}catch{return!1}}function _(e){let r=e;try{return a(p,r.requests)}catch{return!1}}function q(e){let r=e;try{return p(r.request)&&Number.isInteger(r.balance)}catch{return!1}}function g(e){let r=e;try{return i(r.token_id)&&o(r.owner)&&Number.isInteger(r.amount)}catch{return!1}}function d(e){return`token/${e}`}function R(e){let r=d(e);if(Kv.get(r))throw"FA2_TOKEN_ID_EXISTS";Kv.set(d(e),!0)}function O(e){if(!Kv.get(d(e)))throw"FA2_TOKEN_UNDEFINED"}function l(e,r){return`balance/${e}/${r}`}function m(e,r){return Kv.get(l(e,r))||0}function h(e,r,n){if(n<0)throw"FA2_INSUFFICIENT_BALANCE";Kv.set(l(e,r),n)}function u(e,r,n){let t=m(e,r);h(e,r,t+n)}function y(e,r,n,t){u(e,n,-t),u(r,n,t)}function f(e,r,n){return`owner/${e}/${r}/${n}`}function I(e,r,n){Kv.set(f(e,r,n),!0)}function b(e,r,n){Kv.delete(f(e,r,n))}function T(e,r,n){if(!(e===r||Kv.get(f(e,r,n))))throw"FA2_NOT_OPERATOR"}function B(e,r){if(e!==r)throw console.log(`${e} !== ${r}`),"FA2_NOT_OWNER"}function N(e,r,n){O(n.token_id),T(e,r,n.token_id),y(e,n.to,n.token_id,n.amount)}function x(e,r){r.forEach(n=>n.transfers.forEach(t=>N(n.from,e,t)))}function v(e,r){switch(r.operation){case"add_operator":B(r.owner,e),I(r.owner,r.operator,r.token_id);break;case"remove_operator":T(r.owner,e,r.token_id),b(r.owner,r.operator,r.token_id)}}function E(e){let r=m(e.owner,e.token_id);return console.log(`${e.owner} has ${r} of token ${e.token_id}`),{request:e,balance:r}}function S(e){return e.requests.map(E)}function K(e){R(e.token_id),u(e.owner,e.token_id,e.amount)}async function U(e){let r=new URL(e.url),n=r.pathname;try{switch(n){case"/ping":return console.log("Hello from runner smart function \u{1F44B}"),new Response("Pong");case"/balance_of":if(e.method==="GET"){let s={requests:JSON.parse(atob(r.searchParams.get("requests")))};if(_(s)){let c=S(s);return Response.json(c)}else return console.error("Invalid parameters",s),Response.error()}else{let s="/balance_of is a GET request";return console.error(s),new Response(s,{status:500})}case"/transfer":if(e.method==="POST"){let s=await e.json();return a(w,s)?(x(e.headers.get("Referer"),s),new Response("Success!")):(console.error("Invalid parameters",JSON.stringify(s)),Response.error())}else{let s="/transfer is a POST request";return console.error(s),new Response(s,{status:500})}case"/mint_new":if(e.method==="POST"){let s=await e.json();return a(g,s)?(s.forEach(K),new Response("Success!")):(console.error("Invalid parameters",JSON.stringify(s)),Response.error())}else{let s="/mint_new is a POST request";return console.error(s),new Response(s,{status:500})}case"/update_operators":if(e.method==="PUT"){let s=await e.json();return a(A,s)?(s.forEach(c=>v(e.headers.get("Referer"),c)),new Response("Success!")):(console.error("Invalid parameters",JSON.stringify(s)),Response.error())}else{let s="/update_operators is a PUT request";return console.error(s),new Response(s,{status:500})}default:let t=`Unrecognised entrypoint ${n}`;return console.error(t),new Response(t,{status:404})}}catch(t){throw console.error(t),t}}var F=U;export{F as default,_ as isBalanceOf,p as isBalanceRequest,q as isBalanceResponse,g as isMintNew,i as isTokenId,k as isTransfer,w as isTransfers,A as isUpdateOperator}; diff --git a/src/riscv/jstz/kernel/Cargo.toml b/src/riscv/jstz/kernel/Cargo.toml deleted file mode 100644 index 416c6348bf8d2beeb9e5f44089ad1e4e0571158c..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/kernel/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "jstz" -version = "0.0.0" -edition = "2021" - -[dependencies] -bincode.workspace = true -hex.workspace = true -jstz_kernel.workspace = true -jstz_crypto.workspace = true -serde.workspace = true -serde_json.workspace = true -tezos_crypto_rs.workspace = true -tezos-smart-rollup.workspace = true -boa_gc.workspace = true - -[features] -default = [] -static-inbox = [] diff --git a/src/riscv/jstz/kernel/build.rs b/src/riscv/jstz/kernel/build.rs deleted file mode 100644 index 99135c1212d66216dab2629ce45b5b51e7755c0b..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/kernel/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -fn main() { - println!("cargo:rerun-if-env-changed=INBOX_FILE"); -} diff --git a/src/riscv/jstz/kernel/src/main.rs b/src/riscv/jstz/kernel/src/main.rs deleted file mode 100644 index 0668fcd0e0d2eed2f4952799975bcc33e38ccad4..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/kernel/src/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::sync::Once; - -use jstz_crypto::hash::Hash; -use jstz_crypto::smart_function_hash::SmartFunctionHash; -use jstz_kernel::TICKETER; -use tezos_smart_rollup::entrypoint; -use tezos_smart_rollup::prelude::Runtime; - -#[entrypoint::main] -#[cfg_attr( - feature = "static-inbox", - entrypoint::runtime(static_inbox = "$INBOX_FILE") -)] -pub fn entry(host: &mut impl Runtime) { - // We need to setup the ticketer (bridge address that funds Jstz) for Jstz to not panic. - { - static ONCE: Once = Once::new(); - - ONCE.call_once(|| { - let config = bincode::config::legacy(); - - let ticketer = - SmartFunctionHash::from_base58("KT1HbQepzV1nVGg8QVznG7z4RcHseD5kwqBn").unwrap(); - - host.store_write( - &TICKETER, - bincode::encode_to_vec(ticketer, config).unwrap().as_slice(), - 0, - ) - .unwrap(); - }); - } - - // Delegate to Jstz kernel - jstz_kernel::entry(host); - - // Forcibly reset the garbage collector, to prevent the slowdown over time. - // - // It's safe to do so at this point, as jstz does not maintain references to any part of boa - // between runs. - boa_gc::gc_reset(); -} diff --git a/src/riscv/jstz/rust-toolchain.toml b/src/riscv/jstz/rust-toolchain.toml deleted file mode 100644 index 1dc80e64a3ae93c9b1733abd707d0617b8f4c518..0000000000000000000000000000000000000000 --- a/src/riscv/jstz/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "1.86.0" -components = ["rustfmt", "clippy"] -targets = ["riscv64gc-unknown-linux-musl"] diff --git a/src/riscv/lib/Cargo.toml b/src/riscv/lib/Cargo.toml deleted file mode 100644 index 5bf027214cd76b63a92e9db17ab3b2069cc9d5d3..0000000000000000000000000000000000000000 --- a/src/riscv/lib/Cargo.toml +++ /dev/null @@ -1,53 +0,0 @@ -[package] -name = "octez-riscv" -version = "0.0.0" -edition = "2024" -rust-version = "1.86.0" - -[lints] -workspace = true - -[dependencies] -arbitrary-int.workspace = true -bincode.workspace = true -cranelift.workspace = true -cranelift-jit.workspace = true -cranelift-module.workspace = true -cranelift-native.workspace = true -derive_more.workspace = true -ed25519-dalek.workspace = true -enum-tag.workspace = true -hex.workspace = true -ieee-apsqrt.workspace = true -num_enum.workspace = true -paste.workspace = true -rustc_apfloat.workspace = true -serde.workspace = true -serde_json.workspace = true -sha2.workspace = true -strum.workspace = true -tezos_crypto_rs.workspace = true -tezos-smart-rollup-constants.workspace = true -tezos-smart-rollup-utils.workspace = true -thiserror.workspace = true -try-blocks.workspace = true -vm-fdt.workspace = true -itertools.workspace = true -range-collections.workspace = true -elf.workspace = true - -[dependencies.__tracing_do_not_use_directly] -workspace = true -optional = true - -[dev-dependencies] -rand.workspace = true -proptest.workspace = true -lazy_static.workspace = true -tempfile.workspace = true -goldenfile.workspace = true - -[features] -default = [] -metrics = [] -log = ["dep:__tracing_do_not_use_directly"] diff --git a/src/riscv/lib/src/array_utils.rs b/src/riscv/lib/src/array_utils.rs deleted file mode 100644 index 863c40f5c17ec4f08ebefa7c339a39b05abe8fad..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/array_utils.rs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -/// Return a boxed array without first allocating it on the stack -macro_rules! boxed_array { - ($elem:expr; $n:expr) => { - Box::try_from(vec![$elem; $n]) - .map_err(|_| { - unreachable!("Converting a vector to an array of the same size always succeeds") - }) - .unwrap() - }; -} - -pub(crate) use boxed_array; - -/// Create a boxed array from a function. -pub fn boxed_from_fn(mut f: impl FnMut() -> T) -> Box<[T; LEN]> { - let mut entries = Vec::with_capacity(LEN); - entries.resize_with(LEN, &mut f); - entries - .into_boxed_slice() - .try_into() - .map_err(|_| unreachable!("Converting vec into boxed slice of same length always succeeds")) - .unwrap() -} diff --git a/src/riscv/lib/src/bits.rs b/src/riscv/lib/src/bits.rs deleted file mode 100644 index 6bc7fe30a7ae7cd0f06a7e013136fe04c6520941..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/bits.rs +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::fmt; - -macro_rules! bits_builtin { - ($t:tt, $size:expr) => { - #[allow(clippy::allow_attributes, reason = "As the macro generates methods regardless of whether they're used or not, we need to be able to silence some warnings")] - pub mod $t { - /// Creates a bitmask from a range of bits for a strict subset of a value. - /// Panics if the range covers the whole value. - #[inline(always)] - pub const fn mask_subset(start: usize, end: usize) -> $t { - debug_assert!(start < $size); - debug_assert!(end <= start); - debug_assert!(!(start == $size - 1 && end == 0)); - let top = 1 << (1 + start - end); - (top - 1) << end - } - - /// Creates a bitmask from a range of bits. - #[allow(dead_code)] - #[inline(always)] - pub const fn mask(start: usize, end: usize) -> $t { - if start == $size - 1 && end == 0 { - <$t>::MAX - } else { - mask_subset(start, end) - } - } - - /// Returns the [bit] of `v` as a boolean. - #[inline(always)] - pub const fn bit(v: $t, bit: usize) -> bool { - ((v >> bit) & 1) != 0 - } - - /// Returns a strict subset of bits of `v`. - /// Panics if the range covers the whole value. - #[inline(always)] - pub const fn bits_subset(v: $t, start: usize, end: usize) -> $t { - (v & mask_subset(start, end)) >> end - } - - /// Sets [bit] in `v` to `value`. - #[allow(dead_code)] - #[inline(always)] - pub const fn set_bit(v: $t, bit: usize, value: bool) -> $t { - let mask = 1 << bit; - (v & !mask) | (if value { 1 } else { 0 } << bit) - } - - /// Replaces a strict subset of bits of `v` with `bits`. - /// If `bits` is larger than the range, the highest bits will be truncated. - /// Panics if the range covers the whole value. - #[allow(dead_code)] - #[inline(always)] - pub const fn replace_subset(v: $t, start: usize, end: usize, bits: $t) -> $t { - let mask = mask_subset(start, end); - (v & !mask) | ((bits << end) & mask) - } - } - }; -} - -bits_builtin!(u16, 16); -bits_builtin!(u64, 64); - -/// Implementations can be converted to and from a binary [prim@u64] representation -pub trait Bits64 { - const WIDTH: usize; - - /// Convert from the [prim@u64] binary representation. - fn from_bits(value: u64) -> Self; - - /// Serialise to the [prim@u64] binary representation. - fn to_bits(&self) -> u64; -} - -impl Bits64 for bool { - const WIDTH: usize = 1; - - #[inline(always)] - fn from_bits(value: u64) -> Self { - u64::bit(value, 0) - } - - #[inline(always)] - fn to_bits(&self) -> u64 { - if *self { 1 } else { 0 } - } -} - -macro_rules! bits64_builtin { - ( $t:ty ) => { - impl Bits64 for $t { - const WIDTH: usize = { <$t>::BITS as usize }; - - #[inline(always)] - fn from_bits(value: u64) -> Self { - value as $t - } - - #[inline(always)] - fn to_bits(&self) -> u64 { - *self as u64 - } - } - }; -} - -bits64_builtin!(u8); -bits64_builtin!(u16); -bits64_builtin!(u32); -bits64_builtin!(u64); - -bits64_builtin!(i8); -bits64_builtin!(i16); -bits64_builtin!(i32); -bits64_builtin!(i64); - -/// Helper type for [Bits64] that always inhabits a default value of `WIDTH` bits -#[derive(Copy, Clone)] -pub struct ConstantBits; - -impl Bits64 for ConstantBits { - const WIDTH: usize = WIDTH; - - fn from_bits(_value: u64) -> Self { - Self - } - - fn to_bits(&self) -> u64 { - VALUE - } -} - -impl fmt::Debug for ConstantBits { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "0b{VALUE:b}/0x{VALUE:x}") - } -} - -/// Like [prim@u64] but limited in width -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct FixedWidthBits(u64); - -impl Bits64 for FixedWidthBits { - const WIDTH: usize = WIDTH; - - fn from_bits(value: u64) -> Self { - Self(u64::bits_subset(value, WIDTH - 1, 0)) - } - - fn to_bits(&self) -> u64 { - u64::bits_subset(self.0, WIDTH - 1, 0) - } -} - -/// Get the bitmask formed of `n` ones. -pub const fn ones(n: u64) -> u64 { - // This function should not panic - let sh_amt = 64_u64.saturating_sub(n); - match n { - 0 => 0, - _ => !0 >> sh_amt, - } -} - -#[cfg(test)] -mod tests { - use crate::bits::u16; - - #[test] - fn mask_middle() { - assert_eq!(u16::mask_subset(4, 2), 0b0000_0000_0001_1100); - } - - #[test] - fn mask_top() { - assert_eq!(u16::mask_subset(15, 11), 0b1111_1000_0000_0000); - } - - #[test] - fn mask_bottom() { - assert_eq!(u16::mask_subset(2, 0), 0b0000_0000_0000_0111); - } - - #[test] - fn mask_full() { - assert_eq!(u16::mask(15, 0), 0b1111_1111_1111_1111); - } - - #[test] - #[cfg(debug_assertions)] - #[should_panic(expected = "assertion failed")] - fn mask_reversed() { - u16::mask_subset(2, 4); - } - - #[test] - #[cfg(debug_assertions)] - #[should_panic(expected = "assertion failed")] - fn mask_overflow() { - u16::mask_subset(99, 2); - } - - #[test] - fn bit() { - let bytes: u16 = 0b0000_0010_1001_0001; - - let mut bits = [false; 16]; - for (i, bit) in bits.iter_mut().enumerate() { - *bit = u16::bit(bytes, 15 - i); - } - - assert_eq!(bits, [ - false, false, false, false, false, false, true, false, true, false, false, true, false, - false, false, true - ]); - } - - #[test] - fn bits_middle() { - assert_eq!( - u16::bits_subset(0b0010_1110_1001_0011, 10, 3), - 0b0000_0000_1101_0010 - ); - } - - #[test] - fn bits_top() { - assert_eq!( - u16::bits_subset(0b1110_0011_0011_1111, 15, 12), - 0b0000_0000_0000_1110 - ); - } - - #[test] - fn bits_bottom() { - assert_eq!( - u16::bits_subset(0b0111_1011_1000_0110, 6, 0), - 0b0000_0000_0000_0110 - ); - } - - #[test] - fn unset_false_bit() { - assert_eq!( - u16::set_bit(0b1100_1010_0111_1000, 13, false), - 0b1100_1010_0111_1000 - ); - } - - #[test] - fn unset_true_bit() { - assert_eq!( - u16::set_bit(0b1100_1010_0111_1000, 14, false), - 0b1000_1010_0111_1000 - ); - } - - #[test] - fn set_false_bit() { - assert_eq!( - u16::set_bit(0b1100_1010_0111_1000, 1, true), - 0b1100_1010_0111_1010 - ); - } - - #[test] - fn set_true_bit() { - assert_eq!( - u16::set_bit(0b1100_1010_0111_1000, 3, true), - 0b1100_1010_0111_1000 - ); - } - - #[test] - fn replace_middle() { - assert_eq!( - u16::replace_subset(0b0111_0010_1100_1101, 11, 5, 0b011_0011), - 0b0111_0110_0110_1101 - ); - } - - #[test] - fn replace_top() { - assert_eq!( - u16::replace_subset(0b0011_1100_0101_0110, 15, 10, 0b11_0101), - 0b1101_0100_0101_0110 - ); - } - - #[test] - fn replace_bottom() { - assert_eq!( - u16::replace_subset(0b1111_1001_0100_1100, 7, 0, 0b1110_1110), - 0b1111_1001_1110_1110 - ); - } - - #[test] - fn replace_overlong() { - assert_eq!( - u16::replace_subset(0b0000_0000_0000_0000, 7, 4, 0b1111_1111_1111), - 0b0000_0000_1111_0000 - ); - } -} diff --git a/src/riscv/lib/src/cache_utils.rs b/src/riscv/lib/src/cache_utils.rs deleted file mode 100644 index 6398316f708b4dca9620d41e8ff52bfb0252f2e8..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/cache_utils.rs +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::convert::Infallible; -use std::marker::PhantomData; - -use crate::default::ConstDefault; -use crate::machine_state::memory::Address; -use crate::state_backend::AllocatedOf; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FromProofResult; -use crate::state_backend::Layout; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::Many; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::storage::Hash; -use crate::storage::HashError; - -/// Integer to keep track of the fence counter -#[derive( - Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, -)] -pub struct FenceCounter(pub u32); - -impl FenceCounter { - /// Initial fence counter - pub const INITIAL: Self = Self(0); - - /// Maximum fence counter value - pub const MAX: Self = Self(u32::MAX); - - /// Increment the fence counter. - #[inline] - pub fn next(self) -> Self { - Self(self.0.wrapping_add(1)) - } -} - -impl ConstDefault for FenceCounter { - const DEFAULT: Self = Self::INITIAL; -} - -/// Configuration object for the size of a cache indexed by physical address. -/// -/// *NB* you should ensure `SIZE == 1 << BITS`, otherwise a compilation error will occur. -#[derive(Clone)] -pub struct Sizes( - PhantomData, - Infallible, -); - -impl Sizes { - pub const CACHE_SIZE: usize = if 1 << BITS == SIZE { - SIZE - } else { - panic!("BITS parameter does not match SIZE parameter"); - }; - - const CACHE_MASK: usize = { - Self::fence_counter_wrapping_protection(); - Self::CACHE_SIZE - 1 - }; - - // We know that phys_addr here is always u16-aligned. - // Therefore, we can safely halve the number of buckets we - // look at. - #[inline(always)] - pub const fn cache_index(phys_addr: Address) -> usize { - (phys_addr >> 1) as usize & Self::CACHE_MASK - } - - /// Assert that the fence counter would not wrap before every cache entry has been invalidated - /// _at least_ once. - const fn fence_counter_wrapping_protection() { - let invalidation_count_until_wrapping = FenceCounter::MAX.0 as usize; - let cache_entries = Self::CACHE_SIZE; - - assert!( - invalidation_count_until_wrapping > cache_entries, - "The fence counter does a full cycle before all cache entries could be invalidated!" - ); - } -} - -impl Layout - for Sizes -{ - type Allocated = as Layout>::Allocated; -} - -impl CommitmentLayout - for Sizes -{ - fn state_hash(state: AllocatedOf) -> Result { - Many::::state_hash(state) - } -} - -impl ProofLayout - for Sizes -{ - fn to_merkle_tree( - state: crate::state_backend::RefProofGenOwnedAlloc, - ) -> Result { - Many::::to_merkle_tree(state) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - Many::::from_proof(proof) - } - - fn partial_state_hash( - state: crate::state_backend::RefVerifierAlloc, - proof: crate::state_backend::ProofTree, - ) -> Result { - Many::::partial_state_hash(state, proof) - } -} diff --git a/src/riscv/lib/src/default.rs b/src/riscv/lib/src/default.rs deleted file mode 100644 index 4990f9a866a3927c6989f44ed0e593694f70145f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/default.rs +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::ops::Range; - -/// Like [`Default`] but uses an associated constant instead of a function to produce the default -/// value. -pub trait ConstDefault { - /// Default value - const DEFAULT: Self; -} - -impl ConstDefault for () { - const DEFAULT: Self = (); -} - -impl ConstDefault for bool { - const DEFAULT: Self = false; -} - -impl ConstDefault for [T; LEN] { - const DEFAULT: Self = [T::DEFAULT; LEN]; -} - -impl ConstDefault for Range { - const DEFAULT: Self = T::DEFAULT..T::DEFAULT; -} - -macro_rules! impl_const_default_int { - ( $t:ty ) => { - impl ConstDefault for $t { - const DEFAULT: Self = 0; - } - }; -} - -impl_const_default_int!(u8); -impl_const_default_int!(u16); -impl_const_default_int!(u32); -impl_const_default_int!(u64); - -impl_const_default_int!(i8); -impl_const_default_int!(i16); -impl_const_default_int!(i32); -impl_const_default_int!(i64); diff --git a/src/riscv/lib/src/devicetree.rs b/src/riscv/lib/src/devicetree.rs deleted file mode 100644 index 6b8a16e844c83113fad625c802f38bbfe2c62554..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/devicetree.rs +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: 2023 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Additional resources on device trees: -//! - -//! - - -use vm_fdt::FdtWriter; - -use crate::machine_state::memory; - -/// Information about the initial ramdisk. -pub struct InitialRamDisk { - /// Start address of the initrd - pub start: u64, - - /// Number of bytes in the initrd - pub length: u64, -} - -/// Create a new node scope in the device tree. -macro_rules! node { - ( $i:ident, $name:expr, $inner:block ) => { - let __current_node = $i.begin_node($name)?; - - { - $inner - } - - $i.end_node(__current_node)?; - }; - - ( $i:ident, $inner:block ) => { - node!($i, "", $inner) - }; - - ( $i:ident, $name:expr, $addr:expr, $inner:block ) => { - node!($i, format!("{}@{:x}", $name, $addr).as_str(), $inner) - }; -} - -/// Generate a Flattened Device Tree for a custom hardware configuration. -pub fn generate_custom( - main_memory_start: u64, - main_memory_length: u64, - initrd: Option, -) -> Result, vm_fdt::Error> { - let mut fdt = FdtWriter::new()?; - - // / - node!(fdt, { - // Cells are made up of multiple 32-bit unsigned integers. - // The following specifies that address and size cells should be made up - // of two elements. This makes them 64-bit wide. - // - // The `vm_fdt` crate takes care of translating 64-bit integers into - // multiple 32-bit integers. - // - // Note, some cells combine address and size. With this configuration - // below, a combined cell would be a 2-element array of 64-bit unsigned - // integers that will be translated into a cell of 4 32-bit unsigned - // integers. - fdt.property_u32("#address-cells", 2)?; - fdt.property_u32("#size-cells", 2)?; - - // Technically we're not emulating `virtio` in QEMU. However, this - // seems to work with HermitOS so far. - fdt.property_string("compatible", "riscv-virtio")?; - fdt.property_string("model", "riscv-virtio,qemu")?; - - // /chosen - node!(fdt, "chosen", { - // HermitOS loader wants an initial ramdisk. - if let Some(initrd) = initrd { - // End pointer is exclusive (i.e. after the initrd). - fdt.property_u64("linux,initrd-end", initrd.start + initrd.length)?; - fdt.property_u64("linux,initrd-start", initrd.start)?; - } - }); - - // /memory@? - node!(fdt, "memory", main_memory_start, { - fdt.property_string("device_type", "memory")?; - fdt.property_array_u64("reg", &[main_memory_start, main_memory_length])?; - }); - - // /cpus - node!(fdt, "cpus", { - // Addresses are 32-bit unsigned integers, and there are no sizes in - // use in this section. - fdt.property_u32("#address-cells", 1)?; - fdt.property_u32("#size-cells", 0)?; - - // Supervisors will use this to figure out passing of time. - fdt.property_u32("timebase-frequency", 10000000)?; - - // /cpus/cpu@0 - node!(fdt, "cpu", 0, { - fdt.property_phandle(0x1)?; - fdt.property_string("device_type", "cpu")?; - fdt.property_u32("reg", 0x0)?; - fdt.property_string("status", "okay")?; - fdt.property_string("compatible", "riscv")?; - }); - }); - }); - - fdt.finish() -} - -/// Generate a Flattened Device Tree for the given hardware configuration. -pub fn generate( - initrd: Option, -) -> Result, vm_fdt::Error> { - generate_custom(memory::FIRST_ADDRESS, MC::TOTAL_BYTES as u64, initrd) -} diff --git a/src/riscv/lib/src/instruction_context.rs b/src/riscv/lib/src/instruction_context.rs deleted file mode 100644 index 1f2b7134c935e9098a14bd6ff97970f0ac847b19..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/instruction_context.rs +++ /dev/null @@ -1,548 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! The instruction context forms the building blocks used for executing RISC-V instructions. -//! -//! By providing these building blocks for various execution formats, the same implementation can -//! be used for both interpretation and compilation of instructions. - -pub(super) mod arithmetic; -pub(super) mod comparable; - -use arithmetic::Arithmetic; -use comparable::Comparable; -use cranelift::codegen::ir; -use cranelift::codegen::ir::types::I64; - -use crate::jit::state_access::JitStateAccess; -use crate::jit::{self}; -use crate::machine_state::MachineCoreState; -use crate::machine_state::ProgramCounterUpdate; -use crate::machine_state::instruction::Args; -use crate::machine_state::memory::Address; -use crate::machine_state::memory::BadMemoryAccess; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::XValue; -use crate::machine_state::registers::XValue32; -use crate::parser::XRegisterParsed; -use crate::parser::instruction::InstrWidth; -use crate::parser::split_x0; -use crate::state_backend::ManagerReadWrite; -use crate::traps::Exception; - -/// Type of function that may be used to lower [`Instructions`] to IR. -/// -/// [`Instructions`]: crate::machine_state::instruction::Instruction -pub type IcbLoweringFn = unsafe fn(&Args, &mut I) -> IcbFnResult; - -/// Result of lowering an instruction. -pub type IcbFnResult = ::IResult::XValue>>; - -/// PhiValue allows the conversion of values to and from cranelift primitive -/// [`ir::Value`] when in the context of [`JIT`] compilation. It represents a chosen -/// correct value from multiple control flows possible in `ICB::branch_merge`. -/// -/// These methods have no relevance in the context of interpreted mode. -/// -/// [`JIT`]: crate::jit::JIT -pub(super) trait PhiValue { - /// Represents the generic representation of the value in the ICB. - type IcbValue: Sized; - - /// In JIT, convert the value to an iterator of [`ir::Value`]s. - fn to_ir_vals( - icb_repr: Self::IcbValue>, - ) -> impl IntoIterator; - - /// Convert [`ir::Value`]s back to itself. - fn from_ir_vals<'a, MC: MemoryConfig, JSA: JitStateAccess>( - params: &[ir::Value], - ) -> Self::IcbValue>; - - /// The cranelift primitive types of the IR values representing this value in JIT. - const IR_TYPES: &'static [ir::Type]; -} - -impl PhiValue for () { - type IcbValue = (); - - fn to_ir_vals( - _: Self::IcbValue>, - ) -> impl IntoIterator { - [] - } - - fn from_ir_vals<'a, MC: MemoryConfig, JSA: JitStateAccess>( - _: &[ir::Value], - ) -> Self::IcbValue> { - } - - const IR_TYPES: &'static [ir::Type] = &[]; -} - -impl PhiValue for XValue { - type IcbValue = I::XValue; - - fn to_ir_vals( - icb_repr: Self::IcbValue>, - ) -> impl IntoIterator { - [icb_repr.0] - } - - fn from_ir_vals<'a, MC: MemoryConfig, JSA: JitStateAccess>( - params: &[ir::Value], - ) -> Self::IcbValue> { - jit::builder::X64(params[0]) - } - - const IR_TYPES: &'static [ir::Type] = &[I64]; -} - -/// Instruction Context Builder contains operations required to -/// execute RISC-V instructions. -#[expect(clippy::upper_case_acronyms, reason = "ICB looks cooler than Icb")] -pub(crate) trait ICB { - /// A 64-bit value stored in [`XRegisters`]. - /// - /// [`XRegisters`]: crate::machine_state::registers::XRegisters - type XValue: Arithmetic + Comparable; - - /// Perform a read to a [`NonZeroXRegister`], with the given value. - /// This is a specialized version of `xregister_read` that is only used for - /// registers that are guaranteed not to be x0. - fn xregister_read_nz(&mut self, reg: NonZeroXRegister) -> Self::XValue; - - /// Perform a write to a [`NonZeroXRegister`], with the given value. - /// This is a specialized version of `xregister_write` that is only used for - /// registers that are guaranteed not to be x0. - fn xregister_write_nz(&mut self, reg: NonZeroXRegister, value: Self::XValue); - - /// Construct an [`ICB::XValue`] from an `imm: i64`. - fn xvalue_of_imm(&mut self, imm: i64) -> Self::XValue; - - /// Perform a read of the program counter. - fn pc_read(&mut self) -> Self::XValue; - - /// Type for boolean operations. - type Bool; - - /// Perform a logical `and` operation of two [`ICB::Bool`] values. - fn bool_and(&mut self, lhs: Self::Bool, rhs: Self::Bool) -> Self::Bool; - - /// A 32-bit value to be used only in word-width operations. - type XValue32: Arithmetic + Comparable; - - /// Convert an [`XValue`] to a [`XValue32`]. - fn narrow(&mut self, value: Self::XValue) -> Self::XValue32; - - /// Sign-extend an [`XValue32`] to an [`XValue`]. - fn extend_signed(&mut self, value: Self::XValue32) -> Self::XValue; - - /// Zero-extend an [`XValue32`] to an [`XValue`]. - #[expect(dead_code)] - fn extend_unsigned(&mut self, value: Self::XValue32) -> Self::XValue; - - /// Multiply two [`XValue`] values and return the high 64 bits of the result, with - /// the appropriate sign-extension passed in as 2 boolean arguments. - fn mul_high( - &mut self, - lhs: Self::XValue, - rhs: Self::XValue, - mul_high_type: MulHighType, - ) -> Self::XValue; - - /// Convert a boolean value to an xvalue. - /// - /// Coerces to the following: - /// - `true -> 1` - /// - `false -> 0` - fn xvalue_from_bool(&mut self, value: Self::Bool) -> Self::XValue; - - /// Branching instruction. - /// - /// If `condition` is true, the branch will be taken. The PC update - /// will be to the address returned by `take_branch`. - /// - /// If false, the PC update is to the next instruction. - fn branch( - &mut self, - condition: Self::Bool, - offset: i64, - instr_width: InstrWidth, - ) -> ProgramCounterUpdate; - - /// Take a branch based on the given condition and return to a common line of execution. - /// - /// This is used for situations where we have a common execution path following branching. - /// The `cond` is the condition to branch on, and the `true_branch` and `false_branch` are the - /// functions to execute for the left and right branches, respectively. - /// - /// Semantically, this function returns the caller into the context of the common - /// execution path with the resulting value of the branch that was taken. - fn branch_merge( - &mut self, - cond: Self::Bool, - true_branch: OnTrue, - false_branch: OnFalse, - ) -> Phi::IcbValue - where - OnTrue: FnOnce(&mut Self) -> Phi::IcbValue, - OnFalse: FnOnce(&mut Self) -> Phi::IcbValue; - - /// Representation for the manipulation of fallible operations. - type IResult; - - /// Wrap a value as a fallible value. - fn ok(&mut self, val: Value) -> Self::IResult; - - /// Raise an [`Exception::IllegalInstruction`] error. - fn err_illegal_instruction(&mut self) -> Self::IResult; - - /// Raise an [`Exception::StoreAMOAccessFault`] error if `address` is not - /// aligned to the given [`LoadStoreWidth`]. - fn atomic_access_fault_guard( - &mut self, - address: Self::XValue, - width: LoadStoreWidth, - ) -> Self::IResult<()>; - - /// Map the fallible-value into a fallible-value of a different type. - fn map(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(Value) -> Next; - - /// Run a fallible operation over the fallible-value as input. - fn and_then(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(Value) -> Self::IResult; - - /// Exception to perform an ECall at the current mode - fn ecall(&mut self) -> Self::IResult>; - - /// Write value to main memory, at the given address. - /// - /// The value is truncated to the width given by [`LoadStoreWidth`]. - fn main_memory_store( - &mut self, - phys_address: Self::XValue, - value: Self::XValue, - width: LoadStoreWidth, - ) -> Self::IResult<()>; - - /// Read value from main memory, at the given address. - /// - /// The value is truncated to the width given by [`LoadStoreWidth`]. - fn main_memory_load( - &mut self, - phys_address: Self::XValue, - signed: bool, - width: LoadStoreWidth, - ) -> Self::IResult; - - // ---------------- - // Provided Methods - // ---------------- - - /// Read a value from an [`XRegister`]. - /// - /// If the register is `x0`, the value read is always zero. - fn xregister_read(&mut self, reg: XRegister) -> Self::XValue { - match split_x0(reg) { - XRegisterParsed::X0 => self.xvalue_of_imm(0), - XRegisterParsed::NonZero(reg) => self.xregister_read_nz(reg), - } - } - - /// Write a value to an [`XRegister`]. - /// - /// If the register is `x0`, this is a no-op. - fn xregister_write(&mut self, reg: XRegister, value: Self::XValue) { - if let XRegisterParsed::NonZero(reg) = split_x0(reg) { - self.xregister_write_nz(reg, value) - } - } -} - -impl ICB for MachineCoreState { - type XValue = XValue; - - #[inline(always)] - fn xregister_read_nz(&mut self, reg: NonZeroXRegister) -> Self::XValue { - self.hart.xregisters.read_nz(reg) - } - - #[inline(always)] - fn xregister_read(&mut self, reg: XRegister) -> Self::XValue { - self.hart.xregisters.read(reg) - } - - #[inline(always)] - fn xregister_write_nz(&mut self, reg: NonZeroXRegister, value: Self::XValue) { - self.hart.xregisters.write_nz(reg, value) - } - - #[inline(always)] - fn xregister_write(&mut self, reg: XRegister, value: Self::XValue) { - self.hart.xregisters.write(reg, value) - } - - #[inline(always)] - fn xvalue_of_imm(&mut self, imm: i64) -> Self::XValue { - imm as u64 - } - - #[inline(always)] - fn pc_read(&mut self) -> Self::XValue { - self.hart.pc.read() - } - - type Bool = bool; - - #[inline(always)] - fn bool_and(&mut self, lhs: Self::Bool, rhs: Self::Bool) -> Self::Bool { - lhs && rhs - } - - type XValue32 = XValue32; - - #[inline(always)] - fn narrow(&mut self, value: Self::XValue) -> Self::XValue32 { - value as u32 - } - - #[inline(always)] - fn extend_signed(&mut self, value: Self::XValue32) -> Self::XValue { - value as i32 as u64 - } - - #[inline(always)] - fn extend_unsigned(&mut self, value: Self::XValue32) -> Self::XValue { - value as u64 - } - - #[inline(always)] - fn mul_high( - &mut self, - lhs: Self::XValue, - rhs: Self::XValue, - mul_high_type: MulHighType, - ) -> Self::XValue { - let (lhs, rhs) = match mul_high_type { - MulHighType::Signed => (lhs as i64 as i128 as u128, rhs as i64 as i128 as u128), - MulHighType::Unsigned => (lhs as u128, rhs as u128), - MulHighType::SignedUnsigned => (lhs as i64 as i128 as u128, rhs as u128), - }; - let result = lhs.wrapping_mul(rhs); - - (result >> 64) as u64 - } - - #[inline(always)] - fn xvalue_from_bool(&mut self, value: Self::Bool) -> Self::XValue { - value as XValue - } - - #[inline(always)] - fn branch( - &mut self, - predicate: Self::Bool, - offset: i64, - instr_width: InstrWidth, - ) -> ProgramCounterUpdate { - if predicate { - let pc = self.pc_read(); - let address = pc.wrapping_add(offset as u64); - ProgramCounterUpdate::Set(address) - } else { - ProgramCounterUpdate::Next(instr_width) - } - } - - #[inline(always)] - fn branch_merge( - &mut self, - cond: Self::Bool, - true_branch: OnTrue, - false_branch: OnFalse, - ) -> Phi::IcbValue - where - OnTrue: FnOnce(&mut Self) -> Phi::IcbValue, - OnFalse: FnOnce(&mut Self) -> Phi::IcbValue, - { - if cond { - true_branch(self) - } else { - false_branch(self) - } - } - - #[inline(always)] - fn atomic_access_fault_guard( - &mut self, - address: Address, - width: LoadStoreWidth, - ) -> Self::IResult<()> { - let width = self.xvalue_of_imm(width as i64); - let remainder = address.modulus(width, self); - let zero = self.xvalue_of_imm(0); - - if remainder.compare(zero, Predicate::NotEqual, self) { - Err(Exception::StoreAMOAccessFault(address)) - } else { - Ok(()) - } - } - - type IResult = Result; - - #[inline(always)] - fn ok(&mut self, val: In) -> Self::IResult { - Ok(val) - } - - #[inline(always)] - fn err_illegal_instruction(&mut self) -> Self::IResult { - Err(Exception::IllegalInstruction) - } - - #[inline(always)] - fn map(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(In) -> Out, - { - res.map(f) - } - - #[inline(always)] - fn and_then(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(In) -> Self::IResult, - { - res.and_then(f) - } - - fn ecall(&mut self) -> Self::IResult> { - Err(Exception::EnvCall) - } - - #[inline(always)] - fn main_memory_store( - &mut self, - address: Self::XValue, - value: Self::XValue, - width: LoadStoreWidth, - ) -> Self::IResult<()> { - let res = match width { - LoadStoreWidth::Byte => self.main_memory.write::(address, value as u8), - LoadStoreWidth::Half => self.main_memory.write::(address, value as u16), - LoadStoreWidth::Word => self.main_memory.write::(address, value as u32), - LoadStoreWidth::Double => self.main_memory.write::(address, value), - }; - - res.map_err(|_: BadMemoryAccess| Exception::StoreAMOAccessFault(address)) - } - - #[inline(always)] - fn main_memory_load( - &mut self, - address: Self::XValue, - signed: bool, - width: LoadStoreWidth, - ) -> Self::IResult { - let res = match (signed, width) { - (true, LoadStoreWidth::Byte) => { - self.main_memory.read::(address).map(|v| v as i8 as u64) - } - (true, LoadStoreWidth::Half) => self - .main_memory - .read::(address) - .map(|v| v as i16 as u64), - (true, LoadStoreWidth::Word) => self - .main_memory - .read::(address) - .map(|v| v as i32 as u64), - (_, LoadStoreWidth::Double) => self.main_memory.read::(address), - (false, LoadStoreWidth::Byte) => self.main_memory.read::(address).map(|v| v as u64), - (false, LoadStoreWidth::Half) => { - self.main_memory.read::(address).map(|v| v as u64) - } - (false, LoadStoreWidth::Word) => { - self.main_memory.read::(address).map(|v| v as u64) - } - }; - - res.map_err(|_: BadMemoryAccess| Exception::LoadAccessFault(address)) - } -} - -/// Operators for producing a boolean from two values. -pub enum Predicate { - Equal, - NotEqual, - LessThanSigned, - LessThanUnsigned, - LessThanOrEqualSigned, - GreaterThanSigned, - GreaterThanOrEqualSigned, - GreaterThanOrEqualUnsigned, -} - -/// The type of shift operation to perform. -pub enum Shift { - /// Logical left shift. Zeroes are shifted into the least significant bits. - Left, - /// Logical right shift. Zeroes are shifted into the most significant bits. - RightUnsigned, - /// Arithmetic right shift. Sign-bits (ones) are shifted into the most significant bits. - RightSigned, -} - -/// The type of X64 mul_high operation to perform. -pub enum MulHighType { - Signed, - Unsigned, - SignedUnsigned, -} - -/// Supported value widths for loading from/storing to main memory for XRegisters. -/// -/// **NB** This type may be passed over C-FFI. See [state_access] for more -/// information. -/// -/// For now, the approach taken chooses to pass enums as integers, and parse -/// them back into the Enum variant on the rust side - to avoid potential UB -/// should an incorrect discriminant be parsed. We therefore choose explicit -/// constants for each - so that we know very precisely what values are expected. -/// -/// [state_access]: crate::jit::state_access -#[derive(Debug)] -#[repr(u8)] -pub enum LoadStoreWidth { - Byte = Self::BYTE_WIDTH, - Half = Self::HALF_WIDTH, - Word = Self::WORD_WIDTH, - Double = Self::DOUBLE_WIDTH, -} - -impl LoadStoreWidth { - const BYTE_WIDTH: u8 = std::mem::size_of::() as u8; - const HALF_WIDTH: u8 = std::mem::size_of::() as u8; - const WORD_WIDTH: u8 = std::mem::size_of::() as u8; - const DOUBLE_WIDTH: u8 = std::mem::size_of::() as u8; - - /// Convert a value-width in bytes to the appropriate - /// `LoadStoreWidth`, if supported. - pub fn new(val: u8) -> Option { - match val { - Self::BYTE_WIDTH => Some(Self::Byte), - Self::HALF_WIDTH => Some(Self::Half), - Self::WORD_WIDTH => Some(Self::Word), - Self::DOUBLE_WIDTH => Some(Self::Double), - _ => None, - } - } -} diff --git a/src/riscv/lib/src/instruction_context/arithmetic.rs b/src/riscv/lib/src/instruction_context/arithmetic.rs deleted file mode 100644 index 2593e176b5a549925a5766ca8fd372716181bc8e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/instruction_context/arithmetic.rs +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Arithmetic operations required in the [`ICB`], including implementations for interpreted mode. - -use super::ICB; -use super::Shift; -use crate::machine_state::registers::XValue; -use crate::machine_state::registers::XValue32; - -/// Trait for arithmetic operations on **XValues** used in the [`ICB`]. -pub trait Arithmetic: Copy { - /// Perform a wrapping add of two **XValues**, returning the new value. - /// - /// This behaves identically for both signed & unsigned values. - fn add(self, other: Self, icb: &mut I) -> Self; - - /// Perform a wrapping sub of two **XValues**, returning the new value. - /// - /// This behaves identically for both signed & unsigned values. - fn sub(self, other: Self, icb: &mut I) -> Self; - - /// Perform a bitwise and of two **XValues**, returning the new value. - fn and(self, other: Self, icb: &mut I) -> Self; - - /// Perform a bitwise or of two **XValues**, returning the new value. - fn or(self, other: Self, icb: &mut I) -> Self; - - /// Perform a bitwise xor of two **XValues**, returning the new value. - fn xor(self, other: Self, icb: &mut I) -> Self; - - /// Perform a bitwise multiplication of two **XValues**, returning the new value. - /// - /// This behaves identically for both signed & unsigned values. - fn mul(self, other: Self, icb: &mut I) -> Self; - - /// Perform the signed integer division of **self** by **other**, returning the new value. - /// - /// This panics if **other** is zero. - fn div_signed(self, other: Self, icb: &mut I) -> Self; - - /// Negate the value of the **XValue**. - fn negate(self, icb: &mut I) -> Self; - - /// Perform a shift of the **XValue** as determined by the given [`Shift`]. - fn shift(self, shift: Shift, amount: Self, icb: &mut I) -> Self; - - /// Find the signed remainder of the division of **self** by **other**. - /// Panics if **other** is zero. - fn modulus(self, other: Self, icb: &mut I) -> Self; -} - -impl Arithmetic for XValue { - fn add(self, other: Self, _: &mut I) -> Self { - self.wrapping_add(other) - } - - fn sub(self, other: Self, _: &mut I) -> Self { - self.wrapping_sub(other) - } - - fn and(self, other: Self, _: &mut I) -> Self { - self & other - } - - fn or(self, other: Self, _: &mut I) -> Self { - self | other - } - - fn xor(self, other: Self, _: &mut I) -> Self { - self ^ other - } - - fn mul(self, other: Self, _: &mut I) -> Self { - self.wrapping_mul(other) - } - - fn div_signed(self, other: Self, _: &mut I) -> Self { - ((self as i64) / (other as i64)) as Self - } - - fn negate(self, _: &mut I) -> Self { - 0_u64.wrapping_sub(self) - } - - fn shift(self, shift: Shift, amount: Self, _: &mut I) -> Self { - match shift { - Shift::Left => self << amount, - Shift::RightUnsigned => self >> amount, - Shift::RightSigned => (self as i64 >> amount) as Self, - } - } - - fn modulus(self, other: Self, _: &mut I) -> Self { - self % other - } -} - -impl Arithmetic for XValue32 { - fn add(self, other: Self, _: &mut I) -> Self { - self.wrapping_add(other) - } - - fn sub(self, other: Self, _: &mut I) -> Self { - self.wrapping_sub(other) - } - - fn and(self, other: Self, _: &mut I) -> Self { - self & other - } - - fn or(self, other: Self, _: &mut I) -> Self { - self | other - } - - fn xor(self, other: Self, _: &mut I) -> Self { - self ^ other - } - - fn mul(self, other: Self, _: &mut I) -> Self { - self.wrapping_mul(other) - } - - fn div_signed(self, other: Self, _: &mut I) -> Self { - ((self as i32) / (other as i32)) as Self - } - - fn negate(self, _: &mut I) -> Self { - 0_u32.wrapping_sub(self) - } - - fn shift(self, shift: Shift, amount: Self, _: &mut I) -> Self { - match shift { - Shift::Left => self << amount, - Shift::RightUnsigned => self >> amount, - Shift::RightSigned => (self as i32 >> amount) as Self, - } - } - - fn modulus(self, other: Self, _: &mut I) -> Self { - self % other - } -} diff --git a/src/riscv/lib/src/instruction_context/comparable.rs b/src/riscv/lib/src/instruction_context/comparable.rs deleted file mode 100644 index 9cf0a23d940546738cd71633729ac7130b972a8f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/instruction_context/comparable.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Comparison operations required in the [`ICB`], including implementations for interpreted mode. - -use super::ICB; -use super::Predicate; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::MemoryConfig; -use crate::state_backend::ManagerReadWrite; - -/// Trait for comparison operations on **XValues** used in the [`ICB`]. -pub trait Comparable { - /// Compare two values, given the operation to compare them with. - fn compare(self, other: Self, predicate: Predicate, icb: &mut I) -> I::Bool; -} - -impl Comparable> - for as ICB>::XValue -{ - #[inline(always)] - fn compare( - self, - other: Self, - predicate: Predicate, - _: &mut MachineCoreState, - ) -> as ICB>::Bool { - match predicate { - Predicate::Equal => self == other, - Predicate::NotEqual => self != other, - Predicate::LessThanSigned => (self as i64) < (other as i64), - Predicate::LessThanUnsigned => self < other, - Predicate::LessThanOrEqualSigned => (self as i64) <= (other as i64), - Predicate::GreaterThanSigned => (self as i64) > (other as i64), - Predicate::GreaterThanOrEqualSigned => (self as i64) >= (other as i64), - Predicate::GreaterThanOrEqualUnsigned => self >= other, - } - } -} - -impl Comparable> - for as ICB>::XValue32 -{ - #[inline(always)] - fn compare( - self, - other: Self, - predicate: Predicate, - _: &mut MachineCoreState, - ) -> as ICB>::Bool { - match predicate { - Predicate::Equal => self == other, - Predicate::NotEqual => self != other, - Predicate::LessThanSigned => (self as i32) < (other as i32), - Predicate::LessThanUnsigned => self < other, - Predicate::LessThanOrEqualSigned => (self as i32) <= (other as i32), - Predicate::GreaterThanSigned => (self as i32) > (other as i32), - Predicate::GreaterThanOrEqualSigned => (self as i32) >= (other as i32), - Predicate::GreaterThanOrEqualUnsigned => self >= other, - } - } -} diff --git a/src/riscv/lib/src/interpreter.rs b/src/riscv/lib/src/interpreter.rs deleted file mode 100644 index ffbe833abccc49d7f6f82ac207016dd9c34aa444..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter.rs +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -pub mod atomics; -pub mod branching; -mod common_memory; -pub mod float; -pub mod integer; -pub mod load_store; -pub mod rv32a; -pub mod rv32c; -pub mod rv32i; -pub mod rv32m; -pub mod rv64a; -pub mod rv64c; -pub mod rv64d; -pub mod rv64dc; -pub mod rv64f; -pub mod rv64m; -pub mod rv64priv; -pub mod rv64zicsr; -pub mod rv64zifencei; diff --git a/src/riscv/lib/src/interpreter/atomics.rs b/src/riscv/lib/src/interpreter/atomics.rs deleted file mode 100644 index b616c5a682646f2393dcd1de7e173f5aa44910e1..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/atomics.rs +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Core logic for atomic instructions - -use std::mem; - -use crate::instruction_context::ICB; -use crate::instruction_context::LoadStoreWidth; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::registers::XRegister; -use crate::state_backend as backend; -use crate::traps::Exception; - -pub const SC_SUCCESS: u64 = 0; -pub const SC_FAILURE: u64 = 1; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// Loads a word or a double from the address in `rs1`, places the - /// sign-extended value in `rd`, and registers a reservation set for - /// that address. - /// See also [crate::machine_state::reservation_set]. - pub(super) fn run_lr( - &mut self, - rs1: XRegister, - rd: XRegister, - from: fn(T) -> u64, - ) -> Result<(), Exception> { - let address_rs1 = self.hart.xregisters.read(rs1); - - // "The A extension requires that the address held in rs1 be naturally - // aligned to the size of the operand (i.e., eight-byte aligned for - // 64-bit words and four-byte aligned for 32-bit words). If the address - // is not naturally aligned, an address-misaligned exception or - // an access-fault exception will be generated." - if address_rs1 % mem::size_of::() as u64 != 0 { - return Err(Exception::LoadAccessFault(address_rs1)); - } - - // Load the value from address in rs1 - let value_rs1: T = self.read_from_address(address_rs1)?; - - // Register a reservation set for the address in rs1 and write - // the value at that address to rd - self.hart.reservation_set.set(address_rs1); - self.hart.xregisters.write(rd, from(value_rs1)); - - Ok(()) - } - - /// `SC.W` R-type instruction - /// - /// Conditionally writes a word in `rs2` to the address in `rs1`. - /// SC.W succeeds only if the reservation is still valid and - /// the reservation set contains the bytes being written. - /// In case of success, write 0 in `rd`, otherwise write 1. - /// See also [crate::machine_state::reservation_set]. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub(super) fn run_sc( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - to: fn(u64) -> T, - ) -> Result<(), Exception> { - let address_rs1 = self.hart.xregisters.read(rs1); - - // "The A extension requires that the address held in rs1 be naturally - // aligned to the size of the operand (i.e., eight-byte aligned for - // 64-bit words and four-byte aligned for 32-bit words). If the address - // is not naturally aligned, an address-misaligned exception or - // an access-fault exception will be generated." - if address_rs1 % mem::size_of::() as u64 != 0 { - self.hart.reservation_set.reset(); - return Err(Exception::StoreAMOAccessFault(address_rs1)); - } - - if self.hart.reservation_set.test_and_unset(address_rs1) { - // If the address in rs1 belongs to a valid reservation, write - // the value in rs2 to this address and return success. - let value_rs2 = to(self.hart.xregisters.read(rs2)); - self.hart.xregisters.write(rd, SC_SUCCESS); - self.write_to_address(address_rs1, value_rs2) - } else { - // If the address in rs1 does not belong to a valid reservation or - // there is no valid reservation set on the hart, do not write to - // memory and return failure. - self.hart.xregisters.write(rd, SC_FAILURE); - Ok(()) - } - } - - /// Generic implementation of any atomic memory operation, implementing - /// read-modify-write operations for multi-processor synchronisation - /// (Section 8.4) - fn run_amo( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - f: fn(T, T) -> T, - to: fn(u64) -> T, - from: fn(T) -> u64, - ) -> Result<(), Exception> { - let address_rs1 = self.hart.xregisters.read(rs1); - - // "The A extension requires that the address held in rs1 be naturally - // aligned to the size of the operand (i.e., eight-byte aligned for - // 64-bit words and four-byte aligned for 32-bit words). If the address - // is not naturally aligned, an address-misaligned exception or - // an access-fault exception will be generated." - if address_rs1 % mem::size_of::() as u64 != 0 { - return Err(Exception::StoreAMOAccessFault(address_rs1)); - } - - // Load the value from address in rs1 - let value_rs1: T = self.read_from_address(address_rs1)?; - - // Apply the binary operation to the loaded value and the value in rs2 - let value_rs2 = to(self.hart.xregisters.read(rs2)); - let value = f(value_rs1, value_rs2); - - // Write the value read fom the address in rs1 in rd - self.hart.xregisters.write(rd, from(value_rs1)); - - // Store the resulting value to the address in rs1 - self.write_to_address(address_rs1, value) - } - - /// Generic implementation of an atomic memory operation which works on - /// 32-bit values - pub(super) fn run_amo_w( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - f: fn(i32, i32) -> i32, - ) -> Result<(), Exception> { - self.run_amo(rs1, rs2, rd, f, |x| x as i32, |x| x as u64) - } - - /// Generic implementation of an atomic memory operation which works on - /// 64-bit values - pub(super) fn run_amo_d( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - f: fn(u64, u64) -> u64, - ) -> Result<(), Exception> { - self.run_amo(rs1, rs2, rd, f, |x| x, |x| x) - } -} - -/// Generic implementation of any atomic memory operation which works on 64-bit values, -/// implementing read-modify-write operations for multi-processor synchronisation -/// (Section 8.4) -fn run_x64_atomic( - icb: &mut I, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - f: fn(I::XValue, I::XValue, &mut I) -> I::XValue, -) -> I::IResult<()> { - let address_rs1 = icb.xregister_read(rs1); - - // Handle the case where the address is not aligned. - let result = icb.atomic_access_fault_guard(address_rs1, LoadStoreWidth::Double); - - // Continue with the operation if the address is aligned. - let val_rs1_result = I::and_then(result, |_| { - icb.main_memory_load(address_rs1, false, LoadStoreWidth::Double) - }); - - // Continue with the operation if the load was successful. - I::and_then(val_rs1_result, |val_rs1| { - // Apply the binary operation to the loaded value and the value in rs2 - let val_rs2 = icb.xregister_read(rs2); - let res = f(val_rs1, val_rs2, icb); - - // Write the value read fom the address in rs1 in rd - icb.xregister_write(rd, val_rs1); - - // Store the resulting value to the address in rs1 - icb.main_memory_store(address_rs1, res, LoadStoreWidth::Double) - }) -} - -/// Loads in rd the value from the address in rs1 and stores the result of -/// adding it to val(rs2) back to the address in rs1. -#[expect( - unused_variables, - reason = "The `aq` and `rl` bits specify additional memory constraints - in multi-hart environments so they are currently ignored." -)] -pub fn run_x64_atomic_add( - icb: &mut I, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - aq: bool, - rl: bool, -) -> I::IResult<()> { - run_x64_atomic(icb, rs1, rs2, rd, |x, y, icb| x.add(y, icb)) -} - -#[cfg(test)] -mod test { - use proptest::prelude::*; - - use crate::backend_test; - use crate::machine_state::MachineCoreState; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - - macro_rules! test_atomic { - ($(#[$m:meta])* $name: ident, $instr: path, $f: expr, $align: expr, $t: ty) => { - backend_test!($name, F, { - use $crate::machine_state::memory::M4K; - use $crate::state::NewState; - - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - r1_addr in (0..1023_u64/$align).prop_map(|x| x * $align), - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - state.hart.xregisters.write(a0, r1_addr); - state.write_to_bus(0, a0, r1_val)?; - state.hart.xregisters.write(a1, r2_val); - match $instr(&mut *state, a0, a1, a2, false, false) { - Ok(_) => {} - Err(e) => panic!("Error: {:?}", e), - } - let res: $t = state.read_from_address(r1_addr)?; - - prop_assert_eq!( - state.hart.xregisters.read(a2) as $t, r1_val as $t); - - let f = $f; - let expected = f(r1_val as $t, r2_val as $t); - prop_assert_eq!(res, expected); - }) - }); - - } - } - - test_atomic!( - test_run_x64_atomic_add, - super::run_x64_atomic_add, - u64::wrapping_add, - 8, - u64 - ); -} diff --git a/src/riscv/lib/src/interpreter/branching.rs b/src/riscv/lib/src/interpreter/branching.rs deleted file mode 100644 index 152ff2e92603e7b645a230c5b5f2f848f567ce6d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/branching.rs +++ /dev/null @@ -1,588 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of branching and jumping instructions for RISC-V over the ICB. -// TODO: RV-520: Update remaining 'jump' handlers in the file to work over the ICB. - -use crate::instruction_context::ICB; -use crate::instruction_context::Predicate; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::instruction_context::comparable::Comparable; -use crate::machine_state::ProgramCounterUpdate; -use crate::machine_state::registers::NonZeroXRegister; -use crate::parser::instruction::InstrWidth; - -/// Performs an unconditional control transfer. The immediate is added to -/// the pc to form the jump target address. -/// -/// Relevant RISC-V opcodes: -/// - C.J -/// - JAL -/// - BEQ -/// - C.BEQZ -pub fn run_j(icb: &mut I, imm: i64) -> ::XValue { - let imm = icb.xvalue_of_imm(imm); - let current_pc = icb.pc_read(); - current_pc.add(imm, icb) -} - -/// Performs an unconditional control transfer to the address in register `rs1`. -pub fn run_jr(icb: &mut I, rs1: NonZeroXRegister) -> ::XValue { - // The target address is obtained by setting the - // least-significant bit of the address in rs1 to zero - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(!1); - lhs.and(rhs, icb) -} - -/// Performs an unconditional control transfer to the target address, -pub fn run_jr_imm(icb: &mut I, imm: i64, rs1: NonZeroXRegister) -> ::XValue { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(imm); - let lhs = lhs.add(rhs, icb); - - // The target address is obtained by setting the - // least-significant bit of the address in rs1 to zero - let rhs = icb.xvalue_of_imm(!1); - lhs.and(rhs, icb) -} - -/// Jump to Absolute Address `imm`. -/// Performs an unconditional control transfer to the target address, -/// formed by setting the least significant bit to zero. -pub fn run_j_absolute(icb: &mut I, imm: i64) -> ::XValue { - let imm = icb.xvalue_of_imm(imm); - let mask = icb.xvalue_of_imm(!1); - imm.and(mask, icb) -} - -/// Store the next instruction address in `rd` and jump to the target address. -/// Always returns the target address (current program counter + imm) -pub fn run_jal( - icb: &mut I, - imm: i64, - rd: NonZeroXRegister, - width: InstrWidth, -) -> ::XValue { - // The return address to be saved in `rd` is that of the instruction following this one - let current_pc = icb.pc_read(); - let width = icb.xvalue_of_imm(width as i64); - let return_address = current_pc.add(width, icb); - - let imm = icb.xvalue_of_imm(imm); - // The target address is obtained by adding the imm to the current PC - let target_address = current_pc.add(imm, icb); - - // Store the return address in rd - icb.xregister_write_nz(rd, return_address); - - target_address -} - -/// Performs an unconditional control transfer to the address in register `rs1` -/// and stores the address of the instruction following the jump in register `rd`. -pub fn run_jalr( - icb: &mut I, - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - width: InstrWidth, -) -> ::XValue { - // The return address to be saved in `rd` is that of the instruction following this one - let current_pc = icb.pc_read(); - let width = icb.xvalue_of_imm(width as i64); - let return_address = current_pc.add(width, icb); - - // The target address is obtained by setting the - // least-significant bit of the address in rs1 to zero - let target_address = icb.xregister_read_nz(rs1); - let mask = icb.xvalue_of_imm(!1); - let target_address = target_address.and(mask, icb); - - // Store the return address in rd - icb.xregister_write_nz(rd, return_address); - - target_address -} - -/// Performs an unconditional control transfer to the target address, -/// `target_address = val(rs1) + imm` and stores the address of the instruction -/// following the jump in register `rd`. -pub fn run_jalr_imm( - icb: &mut I, - imm: i64, - rs1: NonZeroXRegister, - rd: NonZeroXRegister, - width: InstrWidth, -) -> ::XValue { - // The return address to be saved in `rd` is that of the instruction following this one - let current_pc = icb.pc_read(); - let width = icb.xvalue_of_imm(width as i64); - let return_address = current_pc.add(width, icb); - - // The target address is obtained by adding the sign-extended - // 12-bit I-immediate to the register rs1, then setting - // the least-significant bit of the result to zero - let target_address = icb.xregister_read_nz(rs1); - let imm = icb.xvalue_of_imm(imm); - let target_address = target_address.add(imm, icb); - let mask = icb.xvalue_of_imm(!1); - let target_address = target_address.and(mask, icb); - - // Store the return address in rd - icb.xregister_write_nz(rd, return_address); - - target_address -} - -/// Jump to absolute address `imm` and link register. -/// Store the next instruction address in `rd` and jump to the target address. -/// Always returns the target address formed by sign extending the immediate and setting -/// the least significant bit to 0. -pub fn run_jalr_absolute( - icb: &mut I, - imm: i64, - rd: NonZeroXRegister, - width: InstrWidth, -) -> ::XValue { - // The return address to be saved in `rd` is that of the instruction following this one - let current_pc = icb.pc_read(); - let width = icb.xvalue_of_imm(width as i64); - let return_address = current_pc.add(width, icb); - - // The target address is obtained by setting the - // least-significant bit of the immediate to zero - let target_address = icb.xvalue_of_imm(imm); - let mask = icb.xvalue_of_imm(!1); - let target_address = target_address.and(mask, icb); - - // Store the return address in rd - icb.xregister_write_nz(rd, return_address); - - target_address -} - -/// Add the immediate `imm` to the PC and store the result in `rd`. -/// -/// Relevant RISC-V opcodes: -/// - AUIPC -pub fn run_add_immediate_to_pc(icb: &mut impl ICB, imm: i64, rd: NonZeroXRegister) { - let lhs = icb.pc_read(); - let rhs = icb.xvalue_of_imm(imm); - let lhs = lhs.add(rhs, icb); - icb.xregister_write_nz(rd, lhs); -} - -/// Performs a conditional ( `predicate(val(rs1), val(rs2))` ) control transfer. -/// If condition met, the offset is sign-extended and added to the pc to form the branch -/// target address that is then set, otherwise indicates to proceed to the next instruction. -/// -/// Relevant RISC-V opcodes: -/// - `BEQ` -/// - `BNE` -/// - `BLT` -/// - `BLTU` -/// - `BGE` -/// - `BGEU` -#[inline(always)] -pub fn run_branch( - icb: &mut I, - predicate: Predicate, - imm: i64, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, -) -> ProgramCounterUpdate<::XValue> { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - let cond = lhs.compare(rhs, predicate, icb); - - icb.branch(cond, imm, width) -} - -/// Performs a conditional ( `predicate(val(rs1), 0)` ) control transfer. -/// If condition met, the offset is sign-extended and added to the pc to form the branch -/// target address that is then set, otherwise indicates to proceed to the next instruction. -/// -/// Relevant RISC-V opcodes: -/// - `BEQ` -/// - `BNE` -/// - `BLT` -/// - `BLTU` -/// - `BGE` -/// - `BGEU` -/// - `C.BEQZ` -/// - `C.BNEZ` -#[inline(always)] -pub fn run_branch_compare_zero( - icb: &mut I, - predicate: Predicate, - imm: i64, - rs1: NonZeroXRegister, - width: InstrWidth, -) -> ProgramCounterUpdate<::XValue> { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(0); - let cond = lhs.compare(rhs, predicate, icb); - - icb.branch(cond, imm, width) -} - -#[cfg(test)] -mod tests { - use proptest::prelude::*; - - use crate::backend_test; - use crate::instruction_context::Predicate; - use crate::interpreter::branching::run_j_absolute; - use crate::interpreter::branching::run_jal; - use crate::interpreter::branching::run_jalr; - use crate::interpreter::branching::run_jalr_absolute; - use crate::interpreter::branching::run_jalr_imm; - use crate::interpreter::branching::run_jr_imm; - use crate::machine_state::MachineCoreState; - use crate::machine_state::ProgramCounterUpdate; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::nz; - use crate::parser::instruction::InstrWidth; - use crate::state::NewState; - - backend_test!(test_jalr, F, { - let ipc_imm_irs1_rs1_rd_fpc_frd = [ - (42, 42, 4, nz::a2, nz::t1, 46, 46), - (0, 1001, 100, nz::a1, nz::t1, 1100, 4), - ( - u64::MAX - 1, - 100, - -200_i64 as u64, - nz::a2, - nz::a2, - -100_i64 as u64, - 2, - ), - ( - 1_000_000_000_000, - 1_000_000_000_000, - u64::MAX - 1_000_000_000_000 + 3, - nz::a2, - nz::t2, - 2, - 1_000_000_000_004, - ), - ]; - for (init_pc, imm, init_rs1, rs1, rd, res_pc, res_rd) in ipc_imm_irs1_rs1_rd_fpc_frd { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // TEST JalrImm - state.hart.pc.write(init_pc); - state.hart.xregisters.write_nz(rs1, init_rs1); - let new_pc = run_jalr_imm(&mut state, imm, rs1, rd, InstrWidth::Uncompressed); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, res_pc); - assert_eq!(state.hart.xregisters.read_nz(rd), res_rd); - - // TEST JAbsolute - state.hart.pc.write(init_pc); - let new_pc = run_j_absolute(&mut state, imm); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, imm as u64 & !1); - - // TEST Jal - state.hart.pc.write(init_pc); - let new_pc = run_jal(&mut state, imm, rd, InstrWidth::Uncompressed); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, init_pc.wrapping_add(imm as u64)); - assert_eq!(state.hart.xregisters.read_nz(rd), res_rd); - - // TEST JalrAbsolute - state.hart.pc.write(init_pc); - let new_pc = run_jalr_absolute(&mut state, imm, rd, InstrWidth::Uncompressed); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, imm as u64 & !1); - assert_eq!(state.hart.xregisters.read_nz(rd), res_rd); - - // TEST JrImm - state.hart.pc.write(init_pc); - state.hart.xregisters.write_nz(rs1, init_rs1); - let new_pc = run_jr_imm(&mut state, imm, rs1); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, res_pc); - - // TEST Jalr - state.hart.pc.write(init_pc); - state.hart.xregisters.write_nz(rs1, init_rs1); - let new_pc = run_jalr(&mut state, rd, rs1, InstrWidth::Uncompressed); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, init_rs1 & !1); - assert_eq!(state.hart.xregisters.read_nz(rd), res_rd); - } - }); - - backend_test!(test_auipc, F, { - let pc_imm_res_rd = [ - (0, 0, 0, nz::a2), - (0, 0xFF_FFF0_0000, 0xFF_FFF0_0000, nz::a0), - (0x000A_AAAA, 0xFF_FFF0_0000, 0xFF_FFFA_AAAA, nz::a1), - (0xABCD_AAAA_FBC0_D3FE, 0, 0xABCD_AAAA_FBC0_D3FE, nz::t5), - (0xFFFF_FFFF_FFF0_0000, 0x10_000F, 15, nz::t6), - ]; - - for (init_pc, imm, res, rd) in pc_imm_res_rd { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.pc.write(init_pc); - super::run_add_immediate_to_pc(&mut state, imm, rd); - - let read_pc = state.hart.xregisters.read_nz(rd); - - assert_eq!(read_pc, res); - } - }); - - macro_rules! test_branch_compare_zero { - ($state:ident, $predicate:expr, $imm:expr, - $rs1:path, $r1_val:expr, $width:expr, - $init_pc:ident, $expected_pc:expr - ) => { - $state.hart.pc.write($init_pc); - $state.hart.xregisters.write_nz($rs1, $r1_val); - - let new_pc = - super::run_branch_compare_zero(&mut $state, $predicate, $imm, $rs1, $width); - prop_assert_eq!(&new_pc, $expected_pc); - }; - } - - macro_rules! test_branch { - ($state:ident, $predicate:expr, $imm:expr, - $rs1:path, $r1_val:expr, - $rs2:path, $r2_val:expr, $width:expr, - $init_pc:ident, $expected_pc:expr - ) => { - $state.hart.pc.write($init_pc); - $state.hart.xregisters.write_nz($rs1, $r1_val); - $state.hart.xregisters.write_nz($rs2, $r2_val); - - let new_pc = super::run_branch(&mut $state, $predicate, $imm, $rs1, $rs2, $width); - prop_assert_eq!(&new_pc, $expected_pc); - }; - } - - backend_test!(test_beq_bne, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - r2_val in any::(), - )| { - // to ensure different behaviour for tests - prop_assume!(r1_val != r2_val); - // to ensure branch_pc, init_pc, next_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = MachineCoreState::::new(&mut F::manager()); - - // BEQ: different - test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); - // BEQ: equal - test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &branch_pcu); - - // BNE: different - test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &branch_pcu); - // BNE: equal - test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); - - // BEQ: different - imm = 0 - test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); - // BEQ: equal - imm = 0 - test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &init_pcu); - - // BNE: different - imm = 0 - test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &init_pcu); - // BNE: equal - imm = 0 - test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); - - // BEQ: same register - imm = 0 - test_branch!(state, Predicate::Equal, 0, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &init_pcu); - // BEQ: same register - test_branch!(state, Predicate::Equal, imm, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &branch_pcu); - - // BNE: same register - imm = 0 - test_branch!(state, Predicate::NotEqual, 0, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &next_pcu); - // BNE: same register - test_branch!(state, Predicate::NotEqual, imm, nz::t1, r1_val, nz::t1, r2_val, width, init_pc, &next_pcu); - }); - }); - - backend_test!(test_bge_blt, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - )| { - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = MachineCoreState::::new(&mut F::manager()); - - // lhs < rhs - test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, 0, nz::t2, 1, width, init_pc, &branch_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, i64::MIN as u64, nz::t2, i64::MAX as u64, width, init_pc, &next_pcu); - - // lhs > rhs - test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, nz::t2, i64::MAX as u64, width, init_pc, &branch_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 0, nz::t2, -123_123i64 as u64, width, init_pc, &branch_pcu); - - // lhs = rhs - test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, 0, nz::t2, 0, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, i64::MAX as u64, nz::t2, i64::MAX as u64, width, init_pc, &branch_pcu); - - // same register - test_branch!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t2, 0, nz::t2, 0, width, init_pc, &branch_pcu); - - // imm 0 - // lhs < rhs - test_branch!(state, Predicate::LessThanSigned, 0, nz::t1, 100, nz::t2, i64::MAX as u64, width, init_pc, &init_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, 0, nz::t1, -1_i64 as u64, nz::t2, i64::MIN as u64, width, init_pc, &init_pcu); - - // same register - test_branch!(state, Predicate::LessThanSigned, 0, nz::t1, 123_123_123, nz::t1, 123_123_123, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualSigned, 0, nz::t2, -1_i64 as u64, nz::t2, -1_i64 as u64, width, init_pc, &init_pcu); - }); - }); - - backend_test!(test_b, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - )| { - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - - let mut state = MachineCoreState::::new(&mut F::manager()); - - // lhs < 0 - test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, -1_i64 as u64, width, init_pc, &next_pcu); - - // lhs > 0 - test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, 1, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 1, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, 1, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, 1, width, init_pc, &branch_pcu); - - // lhs = 0 - test_branch_compare_zero!(state, Predicate::LessThanSigned, imm, nz::t1, 0, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanOrEqualSigned, imm, nz::t1, 0, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::LessThanOrEqualSigned, imm, nz::t1, 0, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::GreaterThanSigned, imm, nz::t1, 0, width, init_pc, &next_pcu); - }) - }); - - backend_test!(test_bge_ble_u, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - r2_val in any::(), - )| { - prop_assume!(r1_val < r2_val); - // to ensure branch_pc and init_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let pc_update_init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = MachineCoreState::::new(&mut F::manager()); - - // lhs < rhs - test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &branch_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); - - // lhs > rhs - test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &branch_pcu); - - // lhs = rhs - test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t1, r2_val, nz::t2, r2_val, width, init_pc, &branch_pcu); - - // same register - test_branch!(state, Predicate::LessThanUnsigned, imm, nz::t1, r1_val, nz::t1, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, imm, nz::t2, r1_val, nz::t2, r1_val, width, init_pc, &branch_pcu); - - // imm 0 - // lhs < rhs - test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &pc_update_init_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t1, r1_val, nz::t2, r2_val, width, init_pc, &next_pcu); - - // lhs > rhs - test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t1, r2_val, nz::t2, r1_val, width, init_pc, &pc_update_init_pcu); - - // lhs = rhs - test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t2, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t2, r2_val, nz::t1, r2_val, width, init_pc, &pc_update_init_pcu); - - // same register - test_branch!(state, Predicate::LessThanUnsigned, 0, nz::t1, r1_val, nz::t1, r1_val, width, init_pc, &next_pcu); - test_branch!(state, Predicate::GreaterThanOrEqualUnsigned, 0, nz::t2, r1_val, nz::t2, r1_val, width, init_pc, &pc_update_init_pcu); - - }); - }); - - backend_test!(test_beqz_bnez, F, { - proptest!(|( - init_pc in any::(), - imm in any::(), - r1_val in any::(), - )| { - // to ensure branch_pc, init_pc, next_pc are different - prop_assume!(imm > 10); - let branch_pcu = ProgramCounterUpdate::Set(init_pc.wrapping_add(imm as u64)); - let width = InstrWidth::Uncompressed; - let next_pcu = ProgramCounterUpdate::Next(InstrWidth::Uncompressed); - let init_pcu = ProgramCounterUpdate::Set(init_pc); - - let mut state = MachineCoreState::::new(&mut F::manager()); - - // BEQZ - if r1_val == 0 { - test_branch_compare_zero!(state, Predicate::Equal, imm, nz::t1, r1_val, width, init_pc, &branch_pcu); - test_branch_compare_zero!(state, Predicate::NotEqual, imm, nz::t1, r1_val, width, init_pc, &next_pcu); - } else { - test_branch_compare_zero!(state, Predicate::Equal, imm, nz::t1, r1_val, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::NotEqual, imm, nz::t1, r1_val, width, init_pc, &branch_pcu); - } - - // BEQZ when imm = 0 - if r1_val == 0 { - test_branch_compare_zero!(state, Predicate::Equal, 0, nz::t1, r1_val, width, init_pc, &init_pcu); - test_branch_compare_zero!(state, Predicate::NotEqual, 0, nz::t1, r1_val, width, init_pc, &next_pcu); - } else { - test_branch_compare_zero!(state, Predicate::Equal, 0, nz::t1, r1_val, width, init_pc, &next_pcu); - test_branch_compare_zero!(state, Predicate::NotEqual, 0, nz::t1, r1_val, width, init_pc, &init_pcu); - } - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/common_memory.rs b/src/riscv/lib/src/interpreter/common_memory.rs deleted file mode 100644 index 53c55076684d0636bfbbabd609ae30aef7cdbf02..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/common_memory.rs +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: 2023 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::memory::BadMemoryAccess; -use crate::machine_state::memory::Memory; -use crate::machine_state::registers::XRegister; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// Generic read function for loading `mem::size_of` bytes from `address` - pub(super) fn read_from_address( - &mut self, - address: u64, - ) -> Result { - self.main_memory - .read(address) - .map_err(|_: BadMemoryAccess| Exception::LoadAccessFault(address)) - } - - /// Generic read function for loading `mem::size_of` bytes from address val(rs1) + imm - pub(super) fn read_from_bus( - &mut self, - imm: i64, - rs1: XRegister, - ) -> Result { - let address = self.hart.xregisters.read(rs1).wrapping_add(imm as u64); - self.read_from_address(address) - } - - /// Generic store-operation for writing `mem::size_of` bytes starting at `address` - pub(super) fn write_to_address( - &mut self, - address: u64, - value: T, - ) -> Result<(), Exception> { - self.main_memory - .write(address, value) - .map_err(|_: BadMemoryAccess| Exception::StoreAMOAccessFault(address)) - } - - /// Generic store operation for writing `mem::size_of` bytes starting at address val(rs1) + imm - pub(super) fn write_to_bus( - &mut self, - imm: i64, - rs1: XRegister, - value: T, - ) -> Result<(), Exception> { - let address = self.hart.xregisters.read(rs1).wrapping_add(imm as u64); - self.write_to_address(address, value) - } -} diff --git a/src/riscv/lib/src/interpreter/float.rs b/src/riscv/lib/src/interpreter/float.rs deleted file mode 100644 index d1bc8a14ca02e12105b3eca24c74206da33a4864..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/float.rs +++ /dev/null @@ -1,634 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Core-logic implementation of F/D instructions. - -use std::fmt; -use std::fmt::Display; -use std::ops::Neg; - -use rustc_apfloat::Float; -use rustc_apfloat::FloatConvert; -use rustc_apfloat::Round; -use rustc_apfloat::Status; -use rustc_apfloat::StatusAnd; - -use crate::machine_state::csregisters::CSRRepr; -use crate::machine_state::csregisters::CSRegister; -use crate::machine_state::csregisters::CSRegisters; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::FValue; -use crate::machine_state::registers::XRegister; -use crate::parser::instruction::InstrRoundingMode; -use crate::state_backend as backend; -use crate::traps::Exception; - -pub trait FloatExt: Float + Into + Copy + Neg + From { - /// The canonical NaN has a positive sign and all - /// significand bits clear expect the MSB (the quiet bit). - fn canonical_nan() -> Self; - - /// Canonicalise floating-point values to the canonical nan. - fn canonicalise(self) -> Self { - if self.is_nan() { - Self::canonical_nan() - } else { - self - } - } -} - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// `FCLASS.*` instruction. - /// - /// Examines the value in the floating-point register rs1 and writes - /// a 10-bit mask to the integer register `rd`, which indicates the - /// class of the floating-point number. - /// - /// Exactly one bit in `rd` will be set, all other bits are cleared. - /// - /// Does not set the floating-point exception flags. - pub(super) fn run_fclass(&mut self, rs1: FRegister, rd: XRegister) { - let rval: F = self.fregisters.read(rs1).into(); - - let is_neg = rval.is_negative(); - - let res: u64 = match rval { - _ if rval.is_neg_infinity() => 1, - _ if is_neg && rval.is_normal() => 1 << 1, - _ if is_neg && rval.is_denormal() => 1 << 2, - _ if rval.is_neg_zero() => 1 << 3, - _ if rval.is_pos_zero() => 1 << 4, - _ if rval.is_denormal() => 1 << 5, - _ if rval.is_normal() => 1 << 6, - _ if rval.is_pos_infinity() => 1 << 7, - _ if rval.is_signaling() => 1 << 8, - _ => 1 << 9, - }; - - self.xregisters.write(rd, res); - } - - /// `FEQ.*` instruction. - /// - /// Writes `1` to `rd` if equal, `0` if not. - /// - /// Performs a quiet comparison: only sets the invalid operation exception flag - /// if either input is a signalling NaN. - /// - /// If either input is `NaN`, the result is `0`. - pub(super) fn run_feq(&mut self, rs1: FRegister, rs2: FRegister, rd: XRegister) { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - if rval1.is_signaling() || rval2.is_signaling() { - self.csregisters.set_exception_flag(Fflag::NV); - } - - let res = if rval1 == rval2 { 1 } else { 0 }; - - self.xregisters.write(rd, res); - } - - /// `FLT.*` instruction. - /// - /// Writes `1` to `rd` if `rs1 < rs2`, `0` if not. - /// - /// If either input is `NaN`, the result is `0`, and the invalid operation exception - /// flag is set. - pub(super) fn run_flt(&mut self, rs1: FRegister, rs2: FRegister, rd: XRegister) { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - if rval1.is_nan() || rval2.is_nan() { - self.csregisters.set_exception_flag(Fflag::NV); - } - - let res = if rval1 < rval2 { 1 } else { 0 }; - - self.xregisters.write(rd, res); - } - - /// `FLE.*` instruction. - /// - /// Writes `1` to `rd` if `rs1 <= rs2`, `0` if not. - /// - /// If either input is `NaN`, the result is `0`, and the invalid operation exception - /// flag is set. - pub(super) fn run_fle(&mut self, rs1: FRegister, rs2: FRegister, rd: XRegister) { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - if rval1.is_nan() || rval2.is_nan() { - self.csregisters.set_exception_flag(Fflag::NV); - } - - let res = if rval1 <= rval2 { 1 } else { 0 }; - - self.xregisters.write(rd, res); - } - - /// `FADD.*` instruction. - /// - /// Adds `rs1` to `rs2`, writing the result in `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fadd( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.f_arith_2(rs1, rs2, rm, rd, F::add_r) - } - - /// `FSUB.*` instruction. - /// - /// Subtracts `rs2` from `rs1`, writing the result in `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fsub( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.f_arith_2(rs1, rs2, rm, rd, F::sub_r) - } - - /// `FMUL.*` instruction. - /// - /// Multiplies `rs1` by `rs2`, writing the result in `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fmul( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.f_arith_2(rs1, rs2, rm, rd, F::mul_r) - } - - /// `FDIV.*` instruction. - /// - /// Divides `rs1` by `rs2`, writing the result in `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fdiv( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.f_arith_2(rs1, rs2, rm, rd, F::div_r) - } - - /// `FMIN.*` instruction. - /// - /// Writes the smaller of `rs1`, `rs2` to `rd`. **NB** `-0.0 < +0.0`. - /// - /// If both inputs are NaNs, the result is the canonical NaN. If only one is a NaN, - /// the result is the non-NaN operand. - /// - /// Signaling NaNs set the invalid operation exception flag. - pub(super) fn run_fmin(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { - self.min_max(rs1, rs2, rd, F::minimum) - } - - /// `FMAX.*` instruction. - /// - /// Writes the larger of `rs1`, `rs2` to `rd`. **NB** `-0.0 < +0.0`. - /// - /// If both inputs are NaNs, the result is the canonical NaN. If only one is a NaN, - /// the result is the non-NaN operand. - /// - /// Signaling NaNs set the invalid operation exception flag. - pub(super) fn run_fmax(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { - self.min_max(rs1, rs2, rd, F::maximum) - } - - /// `FMADD.*` instruction. - /// - /// `(rs1 x rs2) + rs3`, writing the result to `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fmadd( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.f_arith_3(rs1, rs2, rs3, rm, rd, F::mul_add_r) - } - - /// `FMSUB.*` instruction. - /// - /// `(rs1 x rs2) - rs3`, writing the result to `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fmsub( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - let f = |rv1, rv2, rv3: F, rm| F::mul_add_r(rv1, rv2, -rv3, rm); - self.f_arith_3(rs1, rs2, rs3, rm, rd, f) - } - - /// `FNMSUB.*` instruction. - /// - /// `- (rs1 x rs2) + rs3`, writing the result to `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fnmsub( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - let f = |rv1: F, rv2, rv3, rm| F::mul_add_r(-rv1, rv2, rv3, rm); - self.f_arith_3(rs1, rs2, rs3, rm, rd, f) - } - - /// `FNMADD.*` instruction. - /// - /// `- (rs1 x rs2) - rs3`, writing the result to `rd`. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fnmadd( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - let f = |rv1: F, rv2, rv3: F, rm| F::mul_add_r(-rv1, rv2, -rv3, rm); - self.f_arith_3(rs1, rs2, rs3, rm, rd, f) - } - - /// `FCVT.int.fmt` instruction. - /// - /// Converts a 32 or 64 bit float, into a 32 or 64 bit integer, with rounding. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fcvt_int_fmt( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - cast: fn(u64) -> T, - cvt: fn(T, Round) -> StatusAnd, - ) -> Result<(), Exception> { - let rval = self.xregisters.read(rs1); - let rval = cast(rval); - - let rm = self.f_rounding_mode(rm)?; - - let StatusAnd { status, value } = cvt(rval, rm); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, value.into()); - - Ok(()) - } - - /// `FCVT.fmt.fmt` instruction. - /// - /// Conversion between f32/f64 values. - /// - /// Returns `Exception::IllegalInstruction` on an invalid rounding mode. - pub(super) fn run_fcvt_fmt_fmt, T: FloatExt>( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - let rval: F = self.fregisters.read(rs1).into(); - - let rm = self.f_rounding_mode(rm)?; - - // ignored - all information comes from status. - let mut loses_info = false; - - let StatusAnd { status, value } = rval.convert_r(rm, &mut loses_info).map(T::canonicalise); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, value.into()); - - Ok(()) - } - - pub(super) fn run_fcvt_fmt_int( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - cast: fn(T) -> u64, - cvt: fn(F, Round) -> StatusAnd, - ) -> Result<(), Exception> { - let rval: F = self.fregisters.read(rs1).into(); - - let rm = self.f_rounding_mode(rm)?; - - // spec requires returning the same as for +ve infinity for nans, - // which differs from impl in rustc_apfloat - let rval = if rval.is_nan() { F::INFINITY } else { rval }; - - let StatusAnd { status, value } = cvt(rval, rm); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.xregisters.write(rd, cast(value)); - - Ok(()) - } - - /// `FSGNJ.*` instruction. - /// - /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. - /// The sign bit is taken from `rs2`. - pub(super) fn run_fsgnj(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { - self.f_sign_injection::(rs1, rs2, rd, |_x, y| y); - } - - /// `FSGNJN.*` instruction. - /// - /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. - /// The sign bit is taken from the negative of `rs2`. - pub(super) fn run_fsgnjn( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) { - self.f_sign_injection::(rs1, rs2, rd, |_x, y| !y); - } - - /// `FSGNJX.*` instruction. - /// - /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. - /// The sign bit is taken from the bitwise XOR of the sign bits from `rs1` & `rs2`. - pub(super) fn run_fsgnjx( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) { - self.f_sign_injection::(rs1, rs2, rd, |x, y| x ^ y); - } - - // perform fused 3-argument floating-point arithmetic - fn f_arith_3( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - f: fn(F, F, F, Round) -> StatusAnd, - ) -> Result<(), Exception> { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - let rval3: F = self.fregisters.read(rs3).into(); - - let rm = self.f_rounding_mode(rm)?; - - let StatusAnd { status, value } = f(rval1, rval2, rval3, rm).map(F::canonicalise); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, value.into()); - Ok(()) - } - - // perform 2-argument floating-point arithmetic - fn f_arith_2( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - f: fn(F, F, Round) -> StatusAnd, - ) -> Result<(), Exception> { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - let rm = self.f_rounding_mode(rm)?; - - let StatusAnd { status, value } = f(rval1, rval2, rm).map(F::canonicalise); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, value.into()); - Ok(()) - } - - pub(super) fn f_rounding_mode(&self, rm: InstrRoundingMode) -> Result { - let rm = match rm { - InstrRoundingMode::Static(rm) => rm, - InstrRoundingMode::Dynamic => self - .csregisters - .read::(CSRegister::frm) - .try_into()?, - }; - - Ok(rm.into()) - } - - fn f_sign_injection( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - pick_sign: fn(bool, bool) -> bool, - ) { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - let sign_bit_1 = rval1.is_negative(); - let sign_bit_2 = rval2.is_negative(); - - let sign_bit = pick_sign(sign_bit_1, sign_bit_2); - - let res = if sign_bit == sign_bit_1 { - rval1 - } else { - -rval1 - }; - - self.fregisters.write(rd, res.into()); - } - - fn min_max( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - cmp: fn(F, F) -> F, - ) { - let rval1: F = self.fregisters.read(rs1).into(); - let rval2: F = self.fregisters.read(rs2).into(); - - let rval1_nan = rval1.is_nan(); - let rval2_nan = rval2.is_nan(); - - let res = match (rval1_nan, rval2_nan) { - (true, true) => F::canonical_nan(), - (true, false) => rval2, - (false, true) => rval1, - (false, false) => cmp(rval1, rval2), - }; - - if (rval1_nan || rval2_nan) && (rval1.is_signaling() || rval2.is_signaling()) { - self.csregisters.set_exception_flag(Fflag::NV); - } - - self.fregisters.write(rd, res.into()); - } - - /// FS bits must not be set to 'Off' in mstatus register. - pub(super) fn check_fs_on(&self) -> Result<(), Exception> { - if self.csregisters.floating_disabled() { - Err(Exception::IllegalInstruction) - } else { - Ok(()) - } - } -} - -/// There are 5 supported rounding modes -#[expect(clippy::upper_case_acronyms, reason = "Matches the RISC-V spec")] -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub enum RoundingMode { - /// Round to Nearest, ties to Even - RNE, - /// Round towards Zero - RTZ, - /// Round Down (towards -∞) - RDN, - /// Round Up (towrads +∞) - RUP, - /// Round to Nearest, ties to Max Magnitude - RMM, -} - -impl RoundingMode { - pub const fn from_csrrepr(value: CSRRepr) -> Result { - match value { - 0b000 => Ok(Self::RNE), - 0b001 => Ok(Self::RTZ), - 0b010 => Ok(Self::RDN), - 0b011 => Ok(Self::RUP), - 0b100 => Ok(Self::RMM), - _ => Err(Exception::IllegalInstruction), - } - } -} - -impl Display for RoundingMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let res = match self { - Self::RNE => "rne", - Self::RTZ => "rtz", - Self::RDN => "rdn", - Self::RUP => "rup", - Self::RMM => "rmm", - }; - - f.write_str(res) - } -} - -impl TryFrom for RoundingMode { - type Error = Exception; - - fn try_from(value: CSRRepr) -> Result { - Self::from_csrrepr(value) - } -} - -impl From for Round { - fn from(value: RoundingMode) -> Round { - match value { - RoundingMode::RNE => Round::NearestTiesToEven, - RoundingMode::RTZ => Round::TowardZero, - RoundingMode::RUP => Round::TowardPositive, - RoundingMode::RDN => Round::TowardNegative, - RoundingMode::RMM => Round::NearestTiesToAway, - } - } -} - -#[cfg_attr(not(test), expect(dead_code, reason = "Only used in tests"))] -pub enum Fflag { - /// Inexact - NX = 0, - /// Underflow - UF = 1, - /// Overflow - OF = 2, - /// Divide by Zero - DZ = 3, - /// Invalid Operation - NV = 4, -} - -impl CSRegisters { - fn set_exception_flag(&mut self, mask: Fflag) { - self.set_bits(CSRegister::fflags, 1 << mask as usize); - } - - pub(super) fn set_exception_flag_status(&mut self, status: Status) { - let bits = status_to_bits(status); - self.set_bits(CSRegister::fflags, bits as u64); - } -} - -const fn status_to_bits(status: Status) -> u8 { - status.bits().reverse_bits() >> 3 -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_status_to_bits() { - assert_eq!(0, status_to_bits(Status::OK)); - assert_eq!(1 << Fflag::NX as usize, status_to_bits(Status::INEXACT)); - assert_eq!(1 << Fflag::UF as usize, status_to_bits(Status::UNDERFLOW)); - assert_eq!(1 << Fflag::OF as usize, status_to_bits(Status::OVERFLOW)); - assert_eq!(1 << Fflag::DZ as usize, status_to_bits(Status::DIV_BY_ZERO)); - assert_eq!(1 << Fflag::NV as usize, status_to_bits(Status::INVALID_OP)); - } -} diff --git a/src/riscv/lib/src/interpreter/integer.rs b/src/riscv/lib/src/interpreter/integer.rs deleted file mode 100644 index 2eb5b170e3fd1d69e0daca95775e265d3877fd2a..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/integer.rs +++ /dev/null @@ -1,1144 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of integer arithmetic operations for RISC-V over the ICB. - -use crate::instruction_context::ICB; -use crate::instruction_context::MulHighType; -use crate::instruction_context::Predicate; -use crate::instruction_context::Shift; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::instruction_context::comparable::Comparable; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::XValue; -use crate::parser::SHIFT_BITMASK; - -/// Moves the two's complement of `val(rs1)` into `rd`. -/// -/// Relevant RISC-V opcodes: -/// - SUB -pub fn run_neg(icb: &mut impl ICB, rd: NonZeroXRegister, rs1: NonZeroXRegister) { - let rs1_val = icb.xregister_read_nz(rs1); - let result = rs1_val.negate(icb); - icb.xregister_write_nz(rd, result) -} - -/// Copies the value in register `rs2` into register `rd_rs1`. -/// -/// Relevant RISC-V opcodes: -/// - C.MV -/// - ADD -/// - SUB -/// - OR -/// - XOR -/// - SLL -/// - SRL -/// - SRA -pub fn run_mv(icb: &mut impl ICB, rd_rs1: NonZeroXRegister, rs2: NonZeroXRegister) { - let rs2_val = icb.xregister_read_nz(rs2); - icb.xregister_write_nz(rd_rs1, rs2_val) -} - -/// Does nothing. -/// -/// Relevant RISC-V opcodes: -/// - C.NOP -/// - ADDI -/// - C.ADDI4SPN -/// - C.ANDI -/// - C.SRLI -/// - C.SRAI -/// - C.AND -/// - C.OR -/// - C.XOR -/// - BNE -/// - C.BNEZ -/// - C.SUB -pub fn run_nop(_icb: &mut impl ICB) {} - -/// Perform `val(rs1) + val(rs2)` and store the result in `rd` -/// -/// Relevant RISC-V opcodes: -/// - ADD -/// - C.ADD -pub fn run_add( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - // Wrapped addition in two's complement behaves the same for signed and unsigned - let result = lhs.add(rhs, icb); - icb.xregister_write_nz(rd, result) -} - -/// Perform `val(rs1) + val(rs2)` but only on lowest 32 bits -/// and store the sign-extended result in `rd`. -pub fn run_add_word(icb: &mut impl ICB, rs1: XRegister, rs2: XRegister, rd: NonZeroXRegister) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xregister_read(rs2); - - let sum = lhs.add(rhs, icb); - - let res = icb.narrow(sum); - let res = icb.extend_signed(res); - - icb.xregister_write_nz(rd, res) -} - -/// Perform [`val(rs1) - val(rs2)`] and store the result in `rd` -/// -/// Relevant RISC-V opcodes: -/// - SUB -/// - C.SUB -pub fn run_sub( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - // Wrapped subtraction in two's complement behaves the same for signed and unsigned - let result = lhs.sub(rhs, icb); - icb.xregister_write_nz(rd, result) -} - -/// Perform `val(rs1) - val(rs2)` but only on lowest 32 bits -/// and store the sign-extended result in `rd`. -/// -/// Relevant RISC-V opcodes: -/// - SUBW -/// - C.SUBW -pub fn run_sub_word(icb: &mut impl ICB, rs1: XRegister, rs2: XRegister, rd: NonZeroXRegister) { - // We do not need to explicitly truncate for the lower bits since wrapping_sub - // has the same semantics & result on the lower 32 bits irrespective of bit width - let lhs = icb.xregister_read(rs1); - let rhs = icb.xregister_read(rs2); - let subtraction = lhs.sub(rhs, icb); - - // Truncate result to use only the lower 32 bits, then sign-extend to 64 bits. - let res = icb.narrow(subtraction); - let res = icb.extend_signed(res); - icb.xregister_write_nz(rd, res) -} -/// Saves in `rd` the bitwise AND between the value in `rs1` and `rs2` -/// -/// Relevant RISC-V opcodes: -/// - `AND` -/// - `C.AND` -pub fn run_and( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - - let res = lhs.and(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// Saves in `rd` the bitwise OR between the value in `rs1` and `rs2` -/// -/// Relevant RISC-V opcodes: -/// - `OR` -/// - `C.OR` -pub fn run_or( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - - let res = lhs.or(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// Perform `val(rs1) | imm` and store the result in `rd` -pub fn run_x64_or_immediate( - icb: &mut impl ICB, - imm: i64, - rs1: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(imm); - let res = lhs.or(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// Perform `val(rs1) ^ val(rs2)` and store the result in `rd` -pub fn run_x64_xor( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - let res = lhs.xor(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// Perform `val(rs1) ^ imm` and store the result in `rd` -pub fn run_x64_xor_immediate( - icb: &mut impl ICB, - imm: i64, - rs1: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(imm); - let res = lhs.xor(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// Add `imm` to val(rs1) and store the result in `rd` -/// -/// Relevant RISC-V opcodes: -/// - `ADDI` -/// - `C.ADDI` -/// - `C.ADDI4SPN` -/// - `C.ADDI16SP` -pub fn run_addi(icb: &mut impl ICB, imm: i64, rs1: NonZeroXRegister, rd: NonZeroXRegister) { - // Return the lower XLEN (64 bits in our case) bits of the addition - // Irrespective of sign, the result is the same, casting to u64 for addition; - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(imm); - let result = lhs.add(rhs, icb); - icb.xregister_write_nz(rd, result) -} - -/// Perform `val(rs1) + imm` but only on lowest 32 bits -/// and store the sign-extended result in `rd` -pub fn run_add_word_immediate(icb: &mut impl ICB, imm: i64, rs1: XRegister, rd: NonZeroXRegister) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xvalue_of_imm(imm); - - let sum = lhs.add(rhs, icb); - - let res = icb.narrow(sum); - let res = icb.extend_signed(res); - - icb.xregister_write_nz(rd, res) -} - -/// Saves in `rd` the bitwise AND between the value in `rs1` and `imm` -/// -/// Relevant RISC-V opcodes: -/// - `ANDI` -/// - `C.ANDI` -pub fn run_andi(icb: &mut impl ICB, imm: i64, rs1: NonZeroXRegister, rd: NonZeroXRegister) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xvalue_of_imm(imm); - let res = lhs.and(rhs, icb); - icb.xregister_write_nz(rd, res); -} - -/// `SLTI` I-type instruction -/// -/// Places the value 1 in `rd` if val(rs1) is less than the immediate -/// when treated as signed integers, 0 otherwise -/// -/// Relevant RISC-V opcodes: -/// - SLTI -pub fn run_set_less_than_immediate_signed( - icb: &mut impl ICB, - imm: i64, - rs1: XRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xvalue_of_imm(imm); - - let cmp = lhs.compare(rhs, Predicate::LessThanSigned, icb); - let res = icb.xvalue_from_bool(cmp); - - icb.xregister_write_nz(rd, res); -} - -/// Places the value 1 in `rd` if val(rs1) is less than the immediate -/// when treated as unsigned integers, 0 otherwise -/// -/// Relevant RISC-V opcodes: -/// - SLTIU -pub fn run_set_less_than_immediate_unsigned( - icb: &mut impl ICB, - imm: i64, - rs1: XRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xvalue_of_imm(imm); - - let cmp = lhs.compare(rhs, Predicate::LessThanUnsigned, icb); - let res = icb.xvalue_from_bool(cmp); - - icb.xregister_write_nz(rd, res); -} - -/// Places the value 1 in `rd` if val(rs1) < val(rs2) -/// when treated as signed integers, 0 otherwise -/// -/// Relevant RISC-V opcodes: -/// - SLT -pub fn run_set_less_than_signed( - icb: &mut impl ICB, - rs1: XRegister, - rs2: XRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xregister_read(rs2); - - let cmp = lhs.compare(rhs, Predicate::LessThanSigned, icb); - let res = icb.xvalue_from_bool(cmp); - - icb.xregister_write_nz(rd, res); -} - -/// Places the value 1 in `rd` if val(rs1) < val(rs2) -/// when treated as unsigned integers, 0 otherwise -/// -/// Relevant RISC-V opcodes: -/// - SLTU -pub fn run_set_less_than_unsigned( - icb: &mut impl ICB, - rs1: XRegister, - rs2: XRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read(rs1); - let rhs = icb.xregister_read(rs2); - - let cmp = lhs.compare(rhs, Predicate::LessThanUnsigned, icb); - let res = icb.xvalue_from_bool(cmp); - - icb.xregister_write_nz(rd, res); -} - -/// Multiply val(rs1) with val(rs2) and store the lower 64 bits of the result -/// in register `rd`. -/// -/// Relevant RISC-V opcodes: -/// - MUL -pub fn run_mul( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - let result = lhs.mul(rhs, icb); - icb.xregister_write_nz(rd, result) -} - -/// Multiply `val(rs1)` with `val(rs2)` and store the lower 32 bits of the result -/// in register `rd`. -pub fn run_x32_mul(icb: &mut impl ICB, rs1: XRegister, rs2: XRegister, rd: NonZeroXRegister) { - let lhs = icb.xregister_read(rs1); - let lhs = icb.narrow(lhs); - - let rhs = icb.xregister_read(rs2); - let rhs = icb.narrow(rhs); - - let result = lhs.mul(rhs, icb); - - let result = icb.extend_signed(result); - - icb.xregister_write_nz(rd, result) -} - -// Extend `val(rs1)` and `val(rs2)` to 128 bits, multiply the 128 bits value and store the upper 64 bits in `rd` -#[inline] -pub fn run_x64_mul_high( - icb: &mut impl ICB, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, - mul_high_type: MulHighType, -) { - let lhs = icb.xregister_read_nz(rs1); - let rhs = icb.xregister_read_nz(rs2); - - let result = icb.mul_high(lhs, rhs, mul_high_type); - - icb.xregister_write_nz(rd, result) -} - -/// Signed integer division `⌊ val(rs1) / val(rs2) ⌋`, storing the result in `rd`. -/// -/// If `val(rs2) == 0`, the result is `-1`. -/// If `val(rs2) == -1` and `val(rs1) == i64::MIN`, the result is `i64::MIN`. -/// -/// All values are _signed integers_. -pub fn run_x64_div_signed( - icb: &mut impl ICB, - rs1: XRegister, - rs2: XRegister, - rd: NonZeroXRegister, -) { - let rval1 = icb.xregister_read(rs1); - let rval2 = icb.xregister_read(rs2); - let zero = icb.xvalue_of_imm(0); - let cond = rval2.compare(zero, Predicate::Equal, icb); - - let result = icb.branch_merge::( - cond, - |icb| icb.xvalue_of_imm(-1), - |icb| { - let minimum = icb.xvalue_of_imm(i64::MIN); - let minus_one = icb.xvalue_of_imm(-1); - - let cond1 = rval2.compare(minus_one, Predicate::Equal, icb); - let cond2 = rval1.compare(minimum, Predicate::Equal, icb); - let cond = icb.bool_and(cond1, cond2); - - icb.branch_merge::( - cond, - |icb| icb.xvalue_of_imm(i64::MIN), - |icb| rval1.div_signed(rval2, icb), - ) - }, - ); - - icb.xregister_write_nz(rd, result); -} - -/// Shift bits in `rs1` by `shift_amount = val(rs2)\[5:0\]` in the method specified by `shift` -/// saving the result in `rd`. -/// -/// Relevant opcodes: -/// - `SLL` -/// - `SRL` -/// - `SRA` -#[inline] -pub fn run_shift( - icb: &mut impl ICB, - shift: Shift, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let bitmask = icb.xvalue_of_imm(SHIFT_BITMASK); - let lhs = icb.xregister_read_nz(rs2); - let shift_amount = lhs.and(bitmask, icb); - - let lhs = icb.xregister_read_nz(rs1); - let result = lhs.shift(shift, shift_amount, icb); - - icb.xregister_write_nz(rd, result); -} - -/// Shift bits in `rs1` by `shift_amount = imm` in the method specified by `shift` -/// saving the result in `rd`. -/// -/// Relevant opcodes: -/// - `SLLI` -/// - `SRLI` -/// - `SRAI` -/// - `C.SLLI` -/// - `C.SRLI` -/// - `C.SRAI` -#[inline] -pub fn run_shift_immediate( - icb: &mut impl ICB, - shift: Shift, - imm: i64, - rs1: NonZeroXRegister, - rd: NonZeroXRegister, -) { - let shift_amount = icb.xvalue_of_imm(imm); - let lhs = icb.xregister_read_nz(rs1); - let result = lhs.shift(shift, shift_amount, icb); - - icb.xregister_write_nz(rd, result); -} - -/// Shift only lowest 32 bits in `rs1` by `shift_amount = val(rs2)\[5:0\]` in the method specified by `shift` -/// saving the result in `rd`. -pub fn run_x32_shift( - icb: &mut impl ICB, - shift: Shift, - rs1: XRegister, - rs2: XRegister, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read(rs1); - let shift_amount = icb.xregister_read(rs2); - let shift_amount = shift_amount.and(icb.xvalue_of_imm(0b1_1111), icb); - let shift_amount = icb.narrow(shift_amount); - - let lhs = icb.narrow(lhs); - - let result = lhs.shift(shift, shift_amount, icb); - - let result = icb.extend_signed(result); - - icb.xregister_write_nz(rd, result); -} - -/// Shift only lowest 32 bits in `rs1` by `shift_amount = imm` in the method specified by `shift` -/// saving the result in `rd`. -pub fn run_x32_shift_immediate( - icb: &mut impl ICB, - shift: Shift, - rs1: NonZeroXRegister, - imm: i64, - rd: NonZeroXRegister, -) { - let lhs = icb.xregister_read_nz(rs1); - let shift_amount = icb.xvalue_of_imm(imm); - let shift_amount = shift_amount.and(icb.xvalue_of_imm(0b1_1111), icb); - let shift_amount = icb.narrow(shift_amount); - - let lhs = icb.narrow(lhs); - - let result = lhs.shift(shift, shift_amount, icb); - - let result = icb.extend_signed(result); - - icb.xregister_write_nz(rd, result); -} - -#[cfg(test)] -mod tests { - use proptest::arbitrary::any; - use proptest::prop_assert_eq; - use proptest::proptest; - - use super::*; - use crate::backend_test; - use crate::instruction_context::Shift; - use crate::machine_state::MachineCoreState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::a3; - use crate::machine_state::registers::nz; - use crate::machine_state::registers::t0; - use crate::machine_state::registers::t1; - use crate::machine_state::registers::t2; - use crate::machine_state::registers::t3; - use crate::state::NewState; - - backend_test!(test_negate, F, { - let rs2val_rd_res = [ - (0_u64, nz::t3, 0_u64), - (0xFFFF_FFFF_FFF0_0420, nz::t2, 0x0000_0000_000F_FBE0), - (0xFFFF_FFFF_FFFF_FFFF, nz::t4, 0x0000_0000_0000_0001), - ]; - - for (rs2, rd, res) in rs2val_rd_res { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.xregisters.write_nz(nz::a0, rs2); - run_neg(&mut state, rd, nz::a0); - assert_eq!(state.hart.xregisters.read_nz(rd), res); - } - }); - - backend_test!(test_add_mv, F, { - let imm_rs1_res = [ - (0_i64, 0_u64, 0_u64), - (0, 0xFFF0_0420, 0xFFF0_0420), - (-1, 0, 0xFFFF_FFFF_FFFF_FFFF), - (1_000_000, -123_000_987_i64 as u64, -122_000_987_i64 as u64), - (1_000_000, 123_000_987, 124_000_987), - (-1, -321_000_000_000_i64 as u64, -321_000_000_001_i64 as u64), - ]; - - for (imm, rs1, res) in imm_rs1_res { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.xregisters.write_nz(nz::a3, rs1); - state.hart.xregisters.write_nz(nz::a4, imm as u64); - - run_add(&mut state, nz::a3, nz::a4, nz::a3); - assert_eq!(state.hart.xregisters.read_nz(nz::a3), res); - run_mv(&mut state, nz::a4, nz::a3); - assert_eq!(state.hart.xregisters.read_nz(nz::a4), res); - } - }); - - backend_test!(test_add_sub, F, { - let imm_rs1_rd_res = [ - (0_i64, 0_u64, nz::t3, 0_u64), - (0, 0xFFF0_0420, nz::t2, 0xFFF0_0420), - (-1, 0, nz::t4, 0xFFFF_FFFF_FFFF_FFFF), - ( - 1_000_000, - -123_000_987_i64 as u64, - nz::a2, - -122_000_987_i64 as u64, - ), - (1_000_000, 123_000_987, nz::a2, 124_000_987), - ( - -1, - -321_000_000_000_i64 as u64, - nz::a1, - -321_000_000_001_i64 as u64, - ), - ]; - - for (imm, rs1, rd, res) in imm_rs1_rd_res { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.xregisters.write_nz(nz::a0, rs1); - state.hart.xregisters.write_nz(nz::t0, imm as u64); - run_addi(&mut state, imm, nz::a0, rd); - assert_eq!(state.hart.xregisters.read_nz(rd), res); - run_add(&mut state, nz::a0, nz::t0, nz::a0); - assert_eq!(state.hart.xregisters.read_nz(nz::a0), res); - // test sub with: res - imm = rs1 and res - rs1 = imm - state.hart.xregisters.write_nz(nz::a0, res); - state.hart.xregisters.write_nz(nz::t0, imm as u64); - run_sub(&mut state, nz::a0, nz::t0, nz::a1); - assert_eq!(state.hart.xregisters.read_nz(nz::a1), rs1); - // now rs1 is in register a1 - run_sub(&mut state, nz::a0, nz::a1, nz::a1); - assert_eq!(state.hart.xregisters.read_nz(nz::a1), imm as u64); - } - }); - - backend_test!(test_sub_word, F, { - proptest!(|( - v1 in any::(), - v2 in any::())| - { - let mut state = MachineCoreState::::new(&mut F::manager()); - state.hart.xregisters.write(t0, v1 as u64); - state.hart.xregisters.write(a0, v2 as u64); - run_sub_word(&mut state, t0, a0, nz::a1); - // check against wrapping subtraction performed on the lowest 32 bits - let v1_u32 = v1 as u32; - let v2_u32 = v2 as u32; - prop_assert_eq!( - state.hart.xregisters.read(a1), - v1_u32.wrapping_sub(v2_u32) as i32 as i64 as u64 - ); - }); - }); - - backend_test!(test_set_less_than, F, { - let mut core = MachineCoreState::::new(&mut F::manager()); - - let v1_v2_exp_expu = [ - (0, 0, 0, 0), - (-1_i64 as u64, 0, 1, 0), - (123123123, -1_i64 as u64, 0, 1), - (123, 123123, 1, 1), - ]; - - for (val1, val2, expected, expected_unsigned) in v1_v2_exp_expu { - core.hart.xregisters.write(a1, val1); - core.hart.xregisters.write(a2, val2); - super::run_set_less_than_signed(&mut core, a1, a2, nz::t0); - assert_eq!(core.hart.xregisters.read(t0), expected); - super::run_set_less_than_unsigned(&mut core, a1, a2, nz::t1); - assert_eq!(core.hart.xregisters.read(t1), expected_unsigned); - super::run_set_less_than_immediate_signed(&mut core, val2 as i64, a1, nz::t0); - assert_eq!(core.hart.xregisters.read(t0), expected); - super::run_set_less_than_immediate_unsigned(&mut core, val2 as i64, a1, nz::t0); - assert_eq!(core.hart.xregisters.read(t0), expected_unsigned); - } - }); - - macro_rules! test_shift_instr { - ($state:ident, $shift:expr, $imm:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - $state.hart.xregisters.write_nz(nz::$rs1, $r1_val); - run_shift_immediate(&mut $state, $shift, $imm, nz::$rs1, nz::$rd); - let new_val = $state.hart.xregisters.read($rd); - assert_eq!(new_val, $expected_val); - }; - } - - macro_rules! test_shift_reg_instr { - ($state:ident, $shift:expr, - $rs2:ident, $r2_val:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - $state.hart.xregisters.write($rs2, $r2_val); - $state.hart.xregisters.write($rs1, $r1_val); - run_shift(&mut $state, $shift, nz::$rs1, nz::$rs2, nz::$rd); - let new_val = $state.hart.xregisters.read($rd); - assert_eq!(new_val, $expected_val); - }; - } - - macro_rules! test_both_shift_instr { - ($state:ident, $shift_reg:expr, - $rs2:ident, $r2_val:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - test_shift_instr!( - $state, - $shift_reg, - $r2_val, - $rs1, - $r1_val, - $rd, - $expected_val - ); - test_shift_reg_instr!( - $state, - $shift_reg, - $rs2, - $r2_val, - $rs1, - $r1_val, - $rd, - $expected_val - ); - }; - } - - backend_test!(test_shift, F, { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // imm = 0 - test_both_shift_instr!(state, Shift::Left, t0, 0, a0, 0x1234_ABEF, a1, 0x1234_ABEF); - test_both_shift_instr!( - state, - Shift::RightUnsigned, - t1, - 0, - a0, - 0x1234_ABEF, - a0, - 0x1234_ABEF - ); - test_both_shift_instr!( - state, - Shift::RightSigned, - t3, - 0, - a0, - 0xFFFF_DEAD_1234_ABEF, - a1, - 0xFFFF_DEAD_1234_ABEF - ); - - // small imm (< 32)) - test_both_shift_instr!( - state, - Shift::Left, - a2, - 20, - a0, - 0x1234_ABEF, - a1, - 0x1_234A_BEF0_0000 - ); - test_both_shift_instr!( - state, - Shift::RightUnsigned, - a2, - 10, - a0, - 0x44_1234_ABEF, - a1, - 0x1104_8D2A - ); - test_both_shift_instr!( - state, - Shift::RightUnsigned, - a2, - 14, - t0, - -1_i64 as u64, - a0, - 0x0003_FFFF_FFFF_FFFF - ); - test_both_shift_instr!( - state, - Shift::RightSigned, - t0, - 10, - a0, - 0xFFFF_F0FF_FFF0_FF00, - a0, - 0xFFFF_FFFC_3FFF_FC3F - ); - - // big imm (>= 32)) - test_both_shift_instr!( - state, - Shift::Left, - t0, - 40, - a0, - 0x1234_ABEF, - a0, - 0x34AB_EF00_0000_0000 - ); - test_both_shift_instr!( - state, - Shift::RightUnsigned, - a1, - 40, - a0, - 0x1234_ABEF, - a0, - 0x0 - ); - test_both_shift_instr!( - state, - Shift::RightSigned, - a2, - 40, - a0, - 0x8000_FAFF_1234_ABEF, - a1, - 0xFFFF_FFFF_FF80_00FA - ); - - // Use same register for shift and source - test_shift_reg_instr!( - state, - Shift::Left, - a1, - 0b1001_0101, - a1, - 0b1001_0101, - a2, - 0x12A0_0000 - ); - // Use same register for shift and destination - test_shift_reg_instr!( - state, - Shift::Left, - a1, - 0b1001_0101, - a2, - 0b1101_0101, - a1, - 0x1AA0_0000 - ); - // Use same register for shift, source and destination - test_shift_reg_instr!( - state, - Shift::Left, - a1, - 0b1101_0101, - a1, - 0b1101_0101, - a1, - 0x1AA0_0000 - ); - }); - - macro_rules! test_shift_word_imm_instr { - ($state:ident, $shift:expr, $imm:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - $state.hart.xregisters.write_nz(nz::$rs1, $r1_val); - run_x32_shift_immediate(&mut $state, $shift, nz::$rs1, $imm, nz::$rd); - let new_val = $state.hart.xregisters.read($rd); - assert_eq!(new_val, $expected_val); - }; - } - - macro_rules! test_shift_word_reg_instr { - ($state:ident, $shift:expr, - $rs2:ident, $r2_val:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - $state.hart.xregisters.write($rs2, $r2_val); - $state.hart.xregisters.write($rs1, $r1_val); - run_x32_shift(&mut $state, $shift, $rs1, $rs2, nz::$rd); - let new_val = $state.hart.xregisters.read($rd); - assert_eq!(new_val, $expected_val); - }; - } - - macro_rules! test_both_shift_word_instr { - ($state:ident, $shift:expr, - $rs2:ident, $r2_val:expr, - $rs1:ident, $r1_val:expr, - $rd:ident, $expected_val:expr - ) => { - test_shift_word_imm_instr!($state, $shift, $r2_val, $rs1, $r1_val, $rd, $expected_val); - test_shift_word_reg_instr!( - $state, - $shift, - $rs2, - $r2_val, - $rs1, - $r1_val, - $rd, - $expected_val - ); - }; - } - - backend_test!(test_shift_word, F, { - let mut state = MachineCoreState::::new(&mut F::manager()); - - test_both_shift_word_instr!( - state, - Shift::Left, - t0, - 0, - a0, - 0xEDDD_1234_ABEF, - a1, - 0x1234_ABEF - ); - test_both_shift_word_instr!( - state, - Shift::RightUnsigned, - t0, - 0, - a0, - 0x1234_ABEF, - a0, - 0x1234_ABEF - ); - test_both_shift_word_instr!( - state, - Shift::RightSigned, - a2, - 0, - a0, - 0xFFFF_DEAD_1234_ABEF, - a1, - 0x1234_ABEF - ); - test_both_shift_word_instr!( - state, - Shift::Left, - a3, - 20, - a0, - 0x1F0B_FFFF, - a0, - 0xFFFF_FFFF_FFF0_0000 - ); - test_both_shift_word_instr!( - state, - Shift::RightUnsigned, - t0, - 10, - a0, - 0x44_1234_ABEF, - a1, - 0x4_8D2A - ); - test_both_shift_word_instr!( - state, - Shift::RightUnsigned, - a1, - 16, - t0, - -1_i64 as u64, - a0, - 0xFFFF - ); - test_both_shift_word_instr!( - state, - Shift::RightSigned, - a1, - 10, - a0, - 0xFFFF_F0FF_FFF0_FF00, - a0, - 0xFFFF_FFFF_FFFF_FC3F - ); - test_both_shift_word_instr!( - state, - Shift::Left, - t0, - 31, - a0, - 0x1234_ABEF, - a0, - 0xFFFF_FFFF_8000_0000 - ); - test_both_shift_word_instr!( - state, - Shift::RightSigned, - t2, - 31, - a0, - 0x8234_ABEF, - a1, - 0xFFFF_FFFF_FFFF_FFFF - ); - test_shift_word_reg_instr!( - state, - Shift::Left, - a1, - 0b1001_0101, - a1, - 0b1001_0101, - a2, - 0x12A0_0000 - ); - test_shift_word_reg_instr!( - state, - Shift::Left, - a1, - 0b1001_0101, - a2, - 0b1101_0101, - a1, - 0x1AA0_0000 - ); - test_shift_word_reg_instr!( - state, - Shift::Left, - a1, - 0b0100_1101_0101, - a1, - 0b0100_1101_0101, - a1, - 0xFFFF_FFFF_9AA0_0000 - ); - }); - - backend_test!(test_bitwise_intruction, F, { - proptest!(|(val1 in any::(), val2 in any::())| { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // The sign-extension of an immediate on 12 bits has bits 31:11 equal the sign-bit - let prefix_mask = 0xFFFF_FFFF_FFFF_F800; - let negative_imm = val2 | prefix_mask; - let positive_imm = val2 & !prefix_mask; - - state.hart.xregisters.write(a0, val1); - run_x64_or_immediate(&mut state, negative_imm as i64, nz::a0, nz::a0); - prop_assert_eq!(state.hart.xregisters.read(a0), val1 | negative_imm); - - state.hart.xregisters.write(a0, val1); - run_x64_or_immediate(&mut state, positive_imm as i64, nz::a0, nz::a1); - prop_assert_eq!(state.hart.xregisters.read(a1), val1 | positive_imm); - - state.hart.xregisters.write(t2, val1); - run_x64_xor_immediate(&mut state, negative_imm as i64, nz::t2, nz::t2); - prop_assert_eq!(state.hart.xregisters.read(t2), val1 ^ negative_imm); - - state.hart.xregisters.write(t2, val1); - run_x64_xor_immediate(&mut state, positive_imm as i64, nz::t2, nz::t1); - prop_assert_eq!(state.hart.xregisters.read(t1), val1 ^ positive_imm); - - state.hart.xregisters.write(t2, val1); - state.hart.xregisters.write(t3, val2); - run_x64_xor(&mut state, nz::t3, nz::t2, nz::t1); - prop_assert_eq!(state.hart.xregisters.read(t1), val1 ^ val2); - }) - }); - - backend_test!(test_x64_div_signed, F, { - proptest!(|( - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.xregisters.write(a0, r1_val); - state.hart.xregisters.write(a1, r2_val); - run_x64_div_signed(&mut state, a0, a1, nz::a2); - state.hart.xregisters.run_rem(a0, a1, a3); - - prop_assert_eq!( - state.hart.xregisters.read(a0), - state.hart.xregisters.read(a1) - .wrapping_mul(state.hart.xregisters.read(a2)) - .wrapping_add(state.hart.xregisters.read(a3))); - }) - }); - - macro_rules! test_x64_mul_high { - ($state:ident, $r1_val:expr, $r2_val:expr, $mul_high_type:expr, $expected_val:expr) => { - $state.hart.xregisters.write_nz(nz::a0, $r1_val); - $state.hart.xregisters.write_nz(nz::a1, $r2_val); - run_x64_mul_high(&mut $state, nz::a0, nz::a1, nz::a2, $mul_high_type); - assert_eq!($state.hart.xregisters.read_nz(nz::a2), $expected_val); - }; - } - - backend_test!(test_x64_mul_high, F, { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // MULH (Signed × Signed) - test_x64_mul_high!( - state, - i64::MAX as u64, - i64::MAX as u64, - MulHighType::Signed, - (((i64::MAX as i128) * (i64::MAX as i128)) >> 64) as u64 - ); - test_x64_mul_high!( - state, - i64::MIN as u64, - i64::MIN as u64, - MulHighType::Signed, - (((i64::MIN as i128) * (i64::MIN as i128)) >> 64) as u64 - ); - test_x64_mul_high!( - state, - i64::MIN as u64, - i64::MAX as u64, - MulHighType::Signed, - (((i64::MIN as i128) * (i64::MAX as i128)) >> 64) as u64 - ); - - // MULHSU (Signed × Unsigned) - test_x64_mul_high!( - state, - i64::MAX as u64, - u64::MAX, - MulHighType::SignedUnsigned, - (((i64::MAX as i128) * (u64::MAX as u128) as i128) >> 64) as u64 - ); - test_x64_mul_high!( - state, - i64::MIN as u64, - u64::MAX, - MulHighType::SignedUnsigned, - (((i64::MIN as i128) * (u64::MAX as u128) as i128) >> 64) as u64 - ); - test_x64_mul_high!( - state, - -1i64 as u64, - u64::MAX, - MulHighType::SignedUnsigned, - (-((u64::MAX as u128) as i128) >> 64) as u64 - ); - - // MULHU (Unsigned × Unsigned) - test_x64_mul_high!( - state, - u64::MAX, - u64::MAX, - MulHighType::Unsigned, - (((u64::MAX as u128) * (u64::MAX as u128)) >> 64) as u64 - ); - test_x64_mul_high!( - state, - i64::MIN as u64, - 2u64, - MulHighType::Unsigned, - (((i64::MIN as u64 as u128) * (2u128)) >> 64) as u64 - ); - test_x64_mul_high!(state, 0u64, u64::MAX, MulHighType::Unsigned, 0u64); - }); -} diff --git a/src/riscv/lib/src/interpreter/load_store.rs b/src/riscv/lib/src/interpreter/load_store.rs deleted file mode 100644 index 53e5f2c52d92779ce9df804fa824fb702495a69b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/load_store.rs +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of load and store instructions for RISC-V over the ICB. - -use crate::instruction_context::ICB; -use crate::instruction_context::LoadStoreWidth; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; - -/// Loads the immediate `imm` into register `rd_rs1`. -/// -/// Relevant RISC-V opcodes: -/// - C.LI -/// - C.LUI -/// - ADD -/// - ADDI -/// - ANDI -/// - ORI -/// - XORI -/// - SLLI -/// - SRLI -/// - SRAI -/// - AND -/// - C.AND -/// - OR -/// - XOR -/// - SLL -/// - SRL -/// - SRA -/// - SUB -pub fn run_li(icb: &mut impl ICB, imm: i64, rd_rs1: NonZeroXRegister) { - let imm = icb.xvalue_of_imm(imm); - icb.xregister_write_nz(rd_rs1, imm) -} - -/// Stores a value to the address starting at `val(rs1) + imm`. -/// -/// The value is taken from `rs2`, but only the lowest `width` bytes -/// are written to memory. -#[inline(always)] -pub fn run_store( - icb: &mut I, - imm: i64, - rs1: XRegister, - rs2: XRegister, - width: LoadStoreWidth, -) -> I::IResult<()> { - let base_address = icb.xregister_read(rs1); - let offset = icb.xvalue_of_imm(imm); - - let address = base_address.add(offset, icb); - - let value = icb.xregister_read(rs2); - - icb.main_memory_store(address, value, width) -} - -/// Loads a value from the address starting at `val(rs1) + imm`. -/// -/// Only `width` bytes are read from memory and then extended to the full register size -/// using the appropriate signed/unsigned extension. -/// -/// The result is written to `rd`. -#[inline(always)] -pub fn run_load( - icb: &mut I, - imm: i64, - rs1: XRegister, - rd: XRegister, - signed: bool, - width: LoadStoreWidth, -) -> I::IResult<()> { - let base_address = icb.xregister_read(rs1); - let offset = icb.xvalue_of_imm(imm); - - let address = base_address.add(offset, icb); - - let value = icb.main_memory_load(address, signed, width); - I::and_then(value, |value| { - icb.xregister_write(rd, value); - icb.ok(()) - }) -} - -#[cfg(test)] -mod test { - use proptest::arbitrary::any; - use proptest::prop_assert; - use proptest::prop_assert_eq; - use proptest::proptest; - - use super::*; - use crate::backend_test; - use crate::instruction_context::LoadStoreWidth; - use crate::machine_state::MachineCoreState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::a3; - use crate::machine_state::registers::a4; - use crate::machine_state::registers::nz; - use crate::machine_state::registers::t0; - use crate::machine_state::registers::t1; - use crate::machine_state::registers::t2; - use crate::machine_state::registers::t3; - use crate::machine_state::registers::t4; - use crate::state::NewState; - use crate::traps::Exception; - - backend_test!(test_run_li, F, { - let imm_rdrs1_res = [ - (0_i64, nz::t3, 0_u64), - (0xFFF0_0420, nz::t2, 0xFFF0_0420), - (-1, nz::t4, 0xFFFF_FFFF_FFFF_FFFF), - ]; - - for (imm, rd_rs1, res) in imm_rdrs1_res { - let mut state = MachineCoreState::::new(&mut F::manager()); - super::run_li(&mut state, imm, rd_rs1); - assert_eq!(state.hart.xregisters.read_nz(rd_rs1), res); - } - }); - - backend_test!(test_lui, F, { - proptest!(|(imm in any::())| { - let mut state = MachineCoreState::::new(&mut F::manager()); - state.hart.xregisters.write(a2, 0); - state.hart.xregisters.write(a4, 0); - - // U-type immediate sets imm[31:20] - let imm = imm & 0xFFFF_F000; - super::run_li(&mut state, imm, nz::a3); - // read value is the expected one - prop_assert_eq!(state.hart.xregisters.read(a3), imm as u64); - // it doesn't modify other registers - prop_assert_eq!(state.hart.xregisters.read(a2), 0); - prop_assert_eq!(state.hart.xregisters.read(a4), 0); - }); - }); - - backend_test!(test_load_store, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - v_1 in any::(), - v_2 in any::(), - v_3 in any::(), - v_4 in any::(), - )| - { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - let mut perform_test = |offset: u64, signed: bool| -> Result<(), Exception> { - // Save test values v_i in registers ai - state.hart.xregisters.write(a4, v_4); - state.hart.xregisters.write(a3, v_3 as u64); - state.hart.xregisters.write(a2, v_2 as u64); - state.hart.xregisters.write(a1, v_1 as u64); - - // t0 will hold the "global" offset of all loads / stores we are going to make - state.hart.xregisters.write(t0, offset); - - // Perform the stores - run_store(&mut *state, 0, t0, a4, LoadStoreWidth::Double)?; - run_store(&mut *state, 8, t0, a3, LoadStoreWidth::Word)?; - run_store(&mut *state, 12, t0, a2, LoadStoreWidth::Half)?; - run_store(&mut *state, 14, t0, a1, LoadStoreWidth::Byte)?; - - run_load(&mut *state, 0, t0, t4, true, LoadStoreWidth::Double)?; - run_load(&mut *state, 8, t0, t3, signed, LoadStoreWidth::Word)?; - run_load(&mut *state, 12, t0, t2, signed, LoadStoreWidth::Half)?; - run_load(&mut *state, 14, t0, t1, signed, LoadStoreWidth::Byte)?; - - assert_eq!(state.hart.xregisters.read(t4), v_4); - - // Converting the expected result we are also checking the sign-extension behaviour - if signed { - assert_eq!(state.hart.xregisters.read(t3), v_3 as i32 as u64); - assert_eq!(state.hart.xregisters.read(t2), v_2 as i16 as u64); - assert_eq!(state.hart.xregisters.read(t1), v_1 as i8 as u64); - } else { - assert_eq!(state.hart.xregisters.read(t3), v_3 as u64); - assert_eq!(state.hart.xregisters.read(t2), v_2 as u64); - assert_eq!(state.hart.xregisters.read(t1), v_1 as u64); - }; - - Ok(()) - }; - - let invalid_offset = 0u64.wrapping_sub(1024); - let aligned_offset = 512; - let misaligned_offset = 513; - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset, true).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset, true).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset, true).is_ok()); - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset, false).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset, false).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset, false).is_ok()); - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv32a.rs b/src/riscv/lib/src/interpreter/rv32a.rs deleted file mode 100644 index 07cc7af2e8649287aea9206fe4c710413e4a5916..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv32a.rs +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_32_A extension for RISC-V -//! -//! Chapter 8 - Unprivileged spec - -use std::ops::BitAnd; -use std::ops::BitOr; -use std::ops::BitXor; - -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::registers::XRegister; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `LR.W` R-type instruction - /// - /// See [Self::run_lr]. - /// The value in `rs2` is always 0 so it is ignored. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_lrw( - &mut self, - rs1: XRegister, - _rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_lr::(rs1, rd, |x| x as u64) - } - - /// `SC.W` R-type instruction - /// - /// Conditionally writes a word in `rs2` to the address in `rs1`. - /// SC.W succeeds only if the reservation is still valid and - /// the reservation set contains the bytes being written. - /// In case of success, write 0 in `rd`, otherwise write 1. - /// See also [crate::machine_state::reservation_set]. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_scw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_sc::(rs1, rs2, rd, |x| x as i32) - } - - /// `AMOSWAP.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and writes val(rs2) - /// back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoswapw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, |_, value_rs2| value_rs2) - } - - /// `AMOADD.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// adding it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoaddw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::wrapping_add) - } - - /// `AMOXOR.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// XORing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoxorw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::bitxor) - } - - /// `AMOAND.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// ANDing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoandw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::bitand) - } - - /// `AMOOR.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// ORing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoorw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::bitor) - } - - /// `AMOMIN.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the minimum - /// between it and val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amominw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::min) - } - - /// `AMOMAX.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the maximum - /// between it and val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amomaxw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, i32::max) - } - - /// `AMOMINU.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the minimum - /// between it and val(rs2) back to the address in rs1, treating both as - /// unsigned values. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amominuw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, |value_rs1, value_rs2| { - (value_rs1 as u32).min(value_rs2 as u32) as i32 - }) - } - - /// `AMOMAXU.W` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the maximum - /// between it and val(rs2) back to the address in rs1, treating both as - /// unsigned values. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amomaxuw( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_w(rs1, rs2, rd, |value_rs1, value_rs2| { - (value_rs1 as u32).max(value_rs2 as u32) as i32 - }) - } -} - -#[cfg(test)] -pub(super) mod test { - use std::ops::BitAnd; - use std::ops::BitOr; - use std::ops::BitXor; - - use proptest::prelude::*; - - use crate::backend_test; - use crate::interpreter::atomics::SC_FAILURE; - use crate::interpreter::atomics::SC_SUCCESS; - use crate::interpreter::integer::run_addi; - use crate::machine_state::MachineCoreState; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - - macro_rules! test_lrsc { - ($name:ident, $lr: ident, $sc: ident, $align: expr, $t: ident) => { - backend_test!($name, F, { - use $crate::machine_state::registers::nz; - use $crate::machine_state::memory::M4K; - use $crate::state::NewState; - - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - r1_addr in (0..1023_u64/$align).prop_map(|x| x * $align), - r1_val in any::(), - imm in any::(), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - state.hart.xregisters.write(a0, r1_addr); - state.write_to_bus(0, a0, r1_val)?; - - // SC.x fails when no reservation is set on the hart - state.$sc(a0, a1, a2, false, false)?; - let res = state.hart.xregisters.read(a2); - prop_assert_eq!(res, SC_FAILURE); - - // Correct sequence of LR.x / SC.y instructions - // SC.x succeeds and stores the expected value - state.$lr(a0, a1, a2, false, false)?; - run_addi(&mut *state, imm, nz::a2, nz::a1); - state.$sc(a0, a1, a2, false, false)?; - let res = state.hart.xregisters.read(a2); - let val: $t = state.read_from_address(r1_addr)?; - prop_assert_eq!(res, SC_SUCCESS); - prop_assert_eq!(val, r1_val.wrapping_add(imm as u64) as $t); - - // SC.x fails when a previous SC.x has been executed - state.$sc(a0, a1, a2, false, false)?; - let res = state.hart.xregisters.read(a2); - prop_assert_eq!(res, SC_FAILURE); - }) - }); - } - } - - pub(crate) use test_lrsc; - - macro_rules! test_amo { - ($instr: ident, $f: expr, $align: expr, $t: ident) => { - backend_test!($instr, F, { - use $crate::machine_state::memory::M4K; - use $crate::state::NewState; - - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - r1_addr in (0..1023_u64/$align).prop_map(|x| x * $align), - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - state.hart.xregisters.write(a0, r1_addr); - state.write_to_bus(0, a0, r1_val)?; - state.hart.xregisters.write(a1, r2_val); - state.$instr(a0, a1, a2, false, false)?; - let res: $t = state.read_from_address(r1_addr)?; - - prop_assert_eq!( - state.hart.xregisters.read(a2) as $t, r1_val as $t); - // avoids redundant_closure_call warnings - let f = $f; - prop_assert_eq!(res, f(r1_val, r2_val)) - }) - }); - - } - } - - pub(crate) use test_amo; - - test_lrsc!(test_lrw_scw, run_lrw, run_scw, 4, u32); - - test_amo!(run_amoswapw, |_, r2_val| r2_val as i32, 4, i32); - - test_amo!( - run_amoaddw, - |r1_val, r2_val| (r1_val as i32).wrapping_add(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amoxorw, - |r1_val, r2_val| (r1_val as i32).bitxor(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amoandw, - |r1_val, r2_val| (r1_val as i32).bitand(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amoorw, - |r1_val, r2_val| (r1_val as i32).bitor(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amominw, - |r1_val, r2_val| (r1_val as i32).min(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amomaxw, - |r1_val, r2_val| (r1_val as i32).max(r2_val as i32), - 4, - i32 - ); - - test_amo!( - run_amominuw, - |r1_val, r2_val| (r1_val as u32).min(r2_val as u32) as i32, - 4, - i32 - ); - - test_amo!( - run_amomaxuw, - |r1_val, r2_val| (r1_val as u32).max(r2_val as u32) as i32, - 4, - i32 - ); -} diff --git a/src/riscv/lib/src/interpreter/rv32c.rs b/src/riscv/lib/src/interpreter/rv32c.rs deleted file mode 100644 index 4c5c357f4e1a414f69a0e6c48de186fc53cc07e5..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv32c.rs +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_32_C extension for RISC-V -//! -//! U:C-16 - -use crate::machine_state::hart_state::HartState; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// `C.EBREAK` compressed instruction - /// - /// Equivalent to `EBREAK`. - pub fn run_cebreak(&self) -> Exception { - Exception::Breakpoint - } -} - -#[cfg(test)] -mod tests { - use crate::backend_test; - use crate::interpreter::branching::run_j; - use crate::interpreter::branching::run_jr; - use crate::machine_state::MachineCoreState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::nz; - use crate::state::NewState; - - backend_test!(test_run_j, F, { - let test_case = [ - (42, 42, 84), - (0, 1000, 1000), - (100, -50, 50), - (50, -100, -50_i64 as u64), - (u64::MAX - 1, 100, 98_i64 as u64), - ]; - for (init_pc, imm, res_pc) in test_case { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.pc.write(init_pc); - let new_pc = run_j(&mut state, imm); - - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, res_pc); - } - }); - - backend_test!(test_cjr, F, { - let scenarios = [ - (42, 2, nz::a2, 2), - (u64::MAX - 1, -200_i64 as u64, nz::a2, -200_i64 as u64), - ( - 1_000_000_000_000, - u64::MAX - 1_000_000_000_000 + 3, - nz::a2, - u64::MAX - 1_000_000_000_000 + 3, - ), - ]; - for (init_pc, init_rs1, rs1, res_pc) in scenarios { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // Test C.JR - // save program counter and value for rs1. - state.hart.pc.write(init_pc); - state.hart.xregisters.write_nz(rs1, init_rs1); - let new_pc = run_jr(&mut state, rs1); - - // check the program counter hasn't changed and the returned - // value for the program counter is correct. - assert_eq!(state.hart.pc.read(), init_pc); - assert_eq!(new_pc, res_pc); - } - }); -} diff --git a/src/riscv/lib/src/interpreter/rv32i.rs b/src/riscv/lib/src/interpreter/rv32i.rs deleted file mode 100644 index f03bec69da014976a9f73f5739901dc418e56145..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv32i.rs +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_32_I extension for RISC-V -//! -//! Chapter 2 - Unprivileged spec - -use crate::machine_state::MachineCoreState; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::memory; -use crate::parser::instruction::FenceSet; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// `EBREAK` instruction - pub fn run_ebreak(&self) -> Exception { - Exception::Breakpoint - } - - /// `ECALL` instruction - pub fn run_ecall(&self) -> Exception { - Exception::EnvCall - } -} - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerBase, -{ - /// `FENCE` I-Type instruction - /// - /// Orders Device I/O, Memory R/W operations. For all harts, for all instructions in the successor sets, instructions in the predecessor sets are visible. - /// NOTE: Since our interpreter is single-threaded (only one hart), the `FENCE` instruction is a no-op - #[inline(always)] - pub fn run_fence(&self, _pred: FenceSet, _succ: FenceSet) { - // no-op - } -} - -#[cfg(test)] -mod tests { - use proptest::prelude::any; - use proptest::prelude::prop; - use proptest::prop_assert_eq; - use proptest::proptest; - - use crate::backend_test; - use crate::interpreter::integer::run_and; - use crate::interpreter::integer::run_andi; - use crate::interpreter::integer::run_or; - use crate::machine_state::MachineCoreState; - use crate::machine_state::hart_state::HartState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::fa0; - use crate::machine_state::registers::nz; - use crate::machine_state::registers::t1; - use crate::machine_state::registers::t3; - use crate::parser::instruction::FenceSet; - use crate::state::NewState; - use crate::traps::Exception; - - backend_test!(test_bitwise, F, { - proptest!(|(val in any::(), imm in any::())| { - let mut state = MachineCoreState::::new(&mut F::manager()); - - // The sign-extension of an immediate on 12 bits has bits 31:11 equal the sign-bit - let prefix_mask = 0xFFFF_FFFF_FFFF_F800; - let negative_imm = imm | prefix_mask; - let positive_imm = imm & !prefix_mask; - - state.hart.xregisters.write(a0, val); - run_andi(&mut state, negative_imm as i64, nz::a0, nz::a1); - prop_assert_eq!(state.hart.xregisters.read(a1), val & negative_imm); - - state.hart.xregisters.write(a1, val); - run_andi(&mut state, positive_imm as i64, nz::a1, nz::a2); - prop_assert_eq!(state.hart.xregisters.read(a2), val & positive_imm); - }) - }); - - backend_test!(test_bitwise_reg, F, { - // TODO: RV-512: move to integer.rs once all are supported. - proptest!(|(v1 in any::(), v2 in any::())| { - let mut state = MachineCoreState::::new(&mut F::manager()); - - state.hart.xregisters.write(a0, v1); - state.hart.xregisters.write(t3, v2); - run_and(&mut state, nz::t3, nz::a0, nz::a1); - prop_assert_eq!(state.hart.xregisters.read(a1), v1 & v2); - - state.hart.xregisters.write(a0, v1); - state.hart.xregisters.write(t3, v2); - run_or(&mut state, nz::t3, nz::a0, nz::a0); - prop_assert_eq!(state.hart.xregisters.read(a0), v1 | v2); - - // Same register - state.hart.xregisters.write(a0, v1); - run_and(&mut state, nz::a0, nz::a0, nz::a1); - prop_assert_eq!(state.hart.xregisters.read(a1), v1); - run_or(&mut state, nz::a0, nz::a0, nz::a1); - prop_assert_eq!(state.hart.xregisters.read(a1), v1); - }); - }); - - backend_test!(test_ebreak, F, { - let state = MachineCoreState::::new(&mut F::manager()); - - let ret_val = state.hart.run_ebreak(); - assert_eq!(ret_val, Exception::Breakpoint); - }); - - backend_test!(test_fence, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - pred in prop::array::uniform4(any::()), - succ in prop::array::uniform4(any::()) - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - - let pred = FenceSet { i: pred[0], o: pred[1], r: pred[2], w: pred[3] }; - let succ = FenceSet { i: succ[0], o: succ[1], r: succ[2], w: succ[3] }; - - state.hart.xregisters.write(t1, 123); - state.hart.fregisters.write(fa0, 0.1_f64.to_bits().into()); - state.run_fence(pred, succ); - assert_eq!(state.hart.xregisters.read(t1), 123); - assert_eq!(state.hart.fregisters.read(fa0), 0.1_f64.to_bits().into()); - }); - }); - - backend_test!(test_ecall, F, { - let state = HartState::new(&mut F::manager()); - - let instr_res = state.run_ecall(); - assert!(instr_res == Exception::EnvCall); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv32m.rs b/src/riscv/lib/src/interpreter/rv32m.rs deleted file mode 100644 index eee6bd672d95a3b336e8b186a0e39876b9a8d80a..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv32m.rs +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_32_M extension for RISC-V -//! -//! Chapter 7 - Unprivileged spec - -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::XRegisters; -use crate::state_backend as backend; - -impl XRegisters -where - M: backend::ManagerReadWrite, -{ - /// `REM` R-type instruction - /// - /// Compute the remainder of val(rs1) divided by val(rs2). Store result in `rd`. - /// In case val(rs2) is zero, result is val(rs1). In case of overflow, when - /// val(rs2) is -1 and val(rs1) is the minimum of signed 32 bit integer, result is - /// zero. All values are _signed integers_. - pub fn run_rem(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1) as i64; - let rval2 = self.read(rs2) as i64; - - let result = if rval2 == 0 { - rval1 - } else if rval2 == -1 && rval1 == i64::MIN { - 0 - } else { - rval1 % rval2 - }; - - self.write(rd, result as u64); - } - - /// `REMU` R-type instruction - /// - /// Compute the remainder of val(rs1) divided by val(rs2) and store the result - /// in register `rd`. In case val(rs2) is zero, the result is val(rs1). All - /// values are _unsigned integers_. - pub fn run_remu(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1); - let rval2 = self.read(rs2); - - let result = if rval2 == 0 { rval1 } else { rval1 % rval2 }; - - self.write(rd, result); - } - - /// `DIVU` R-type instruction - /// - /// Divide val(rs1) by val(rs2). The result is stored in `rd`. In case val(rs2) - /// is zero, the result is `u64::MAX`. All values are _unsigned integers_. - pub fn run_divu(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1); - let rval2 = self.read(rs2); - - let result = if rval2 == 0 { u64::MAX } else { rval1 / rval2 }; - - self.write(rd, result); - } -} - -#[cfg(test)] -mod test { - use proptest::prelude::any; - use proptest::prop_assert_eq; - use proptest::proptest; - - use crate::backend_test; - use crate::machine_state::registers::XRegisters; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::a3; - use crate::state::NewState; - - backend_test!(test_divu_remu_invariant, F, { - proptest!(|( - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = XRegisters::new(&mut F::manager()); - - state.write(a0, r1_val); - state.write(a1, r2_val); - state.run_divu(a0, a1, a2); - state.run_remu(a0, a1, a3); - - prop_assert_eq!( - state.read(a0), - state.read(a1) - .wrapping_mul(state.read(a2)) - .wrapping_add(state.read(a3))); - }) - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64a.rs b/src/riscv/lib/src/interpreter/rv64a.rs deleted file mode 100644 index 284025942608e70ab0441840b970663628667471..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64a.rs +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_A extension for RISC-V -//! -//! Chapter 8 - Unprivileged spec - -use std::ops::BitAnd; -use std::ops::BitOr; -use std::ops::BitXor; - -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::registers::XRegister; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `LR.D` R-type instruction - /// - /// See [Self::run_lr]. - /// The value in `rs2` is always 0 so it is ignored. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_lrd( - &mut self, - rs1: XRegister, - _rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_lr::(rs1, rd, |x| x) - } - - /// `SC.D` R-type instruction - /// - /// Conditionally writes a double in `rs2` to the address in `rs1`. - /// SC.D succeeds only if the reservation is still valid and - /// the reservation set contains the bytes being written. - /// In case of success, write 0 in `rd`, otherwise write 1. - /// See also [crate::machine_state::reservation_set]. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_scd( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_sc::(rs1, rs2, rd, |x| x) - } - - /// `AMOSWAP.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and writes val(rs2) - /// back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoswapd( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, |_, value_rs2| value_rs2) - } - - /// `AMOXOR.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// XORing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoxord( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, u64::bitxor) - } - - /// `AMOAND.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// ANDing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoandd( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, u64::bitand) - } - - /// `AMOOR.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the result of - /// ORing it to val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amoord( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, u64::bitor) - } - - /// `AMOMIN.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the minimum - /// between it and val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amomind( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, |value_rs1, value_rs2| { - (value_rs1 as i64).min(value_rs2 as i64) as u64 - }) - } - - /// `AMOMAX.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the maximum - /// between it and val(rs2) back to the address in rs1. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amomaxd( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, |value_rs1, value_rs2| { - (value_rs1 as i64).max(value_rs2 as i64) as u64 - }) - } - - /// `AMOMINU.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the minimum - /// between it and val(rs2) back to the address in rs1, treating both as - /// unsigned values. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amominud( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, u64::min) - } - - /// `AMOMAXU.D` R-type instruction - /// - /// Loads in rd the value from the address in rs1 and stores the maximum - /// between it and val(rs2) back to the address in rs1, treating both as - /// unsigned values. - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - pub fn run_amomaxud( - &mut self, - rs1: XRegister, - rs2: XRegister, - rd: XRegister, - _rl: bool, - _aq: bool, - ) -> Result<(), Exception> { - self.run_amo_d(rs1, rs2, rd, u64::max) - } -} - -#[cfg(test)] -mod test { - use std::ops::BitAnd; - use std::ops::BitOr; - use std::ops::BitXor; - - use proptest::prelude::*; - - use crate::backend_test; - use crate::interpreter::atomics::SC_FAILURE; - use crate::interpreter::atomics::SC_SUCCESS; - use crate::interpreter::integer::run_addi; - use crate::interpreter::rv32a::test::test_amo; - use crate::interpreter::rv32a::test::test_lrsc; - use crate::machine_state::MachineCoreState; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - - test_lrsc!(test_lrd_scd, run_lrd, run_scd, 8, u64); - - test_lrsc!(test_lrd_scw, run_lrd, run_scw, 8, u32); - - test_lrsc!(test_lrw_scd, run_lrw, run_scd, 8, u32); - - test_amo!(run_amoswapd, |_, r2_val| r2_val, 8, u64); - - test_amo!(run_amoxord, u64::bitxor, 8, u64); - - test_amo!(run_amoandd, u64::bitand, 8, u64); - - test_amo!(run_amoord, u64::bitor, 8, u64); - - test_amo!( - run_amomind, - |r1_val, r2_val| i64::min(r1_val as i64, r2_val as i64) as u64, - 8, - u64 - ); - - test_amo!( - run_amomaxd, - |r1_val, r2_val| i64::max(r1_val as i64, r2_val as i64) as u64, - 8, - u64 - ); - - test_amo!(run_amominud, u64::min, 8, u64); - - test_amo!(run_amomaxud, u64::max, 8, u64); -} diff --git a/src/riscv/lib/src/interpreter/rv64c.rs b/src/riscv/lib/src/interpreter/rv64c.rs deleted file mode 100644 index 3cb4744d46e1b605b767f19be0d2506a6d1b83d7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64c.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_C extension for RISC-V -//! -//! U:C-16 - -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegisters; -use crate::state_backend as backend; - -impl XRegisters -where - M: backend::ManagerReadWrite, -{ - /// `C.ADDIW` CI-type compressed instruction - /// - /// Adds the non-zero sign-extended 6-bit `imm` to the value in `rd_rs1`, - /// producing a 32-bit result which is then sign-extended to 64 bits and - /// written back to `rd_rs1`. - pub fn run_caddiw(&mut self, imm: i64, rd_rs1: NonZeroXRegister) { - // We do not need to explicitly truncate for the lower bits since wrapping_add - // has the same semantics & result on the lower 32 bits irrespective of bit width - let rval = self.read_nz(rd_rs1); - let result = rval.wrapping_add(imm as u64); - // Truncate result to use only the lower 32 bits, then sign-extend to 64 bits. - let result = result as i32 as u64; - self.write_nz(rd_rs1, result); - } -} - -#[cfg(test)] -mod tests { - use proptest::arbitrary::any; - use proptest::prop_assert_eq; - use proptest::proptest; - - use crate::backend_test; - use crate::machine_state::hart_state::HartState; - use crate::machine_state::registers::nz; - use crate::state::NewState; - - backend_test!(test_caddiw, F, { - proptest!(|( - imm in any::(), - reg_val in any::())| - { - let mut state = HartState::new(&mut F::manager()); - - state.xregisters.write_nz(nz::a0, reg_val as u64); - state.xregisters.run_caddiw(imm, nz::a0); - // check against wrapping addition performed on the lowest 32 bits - let r_val = reg_val as u32; - let i_val = imm as u32; - prop_assert_eq!( - state.xregisters.read_nz(nz::a0), - r_val.wrapping_add(i_val) as i32 as i64 as u64 - ); - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64d.rs b/src/riscv/lib/src/interpreter/rv64d.rs deleted file mode 100644 index 12d91fca59261e98d88a4792c44445c29021bde7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64d.rs +++ /dev/null @@ -1,650 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_UF extension for RISC-V -//! -//! Chapter 12 - "D" Standard Extension for Double-Precision Floating-Point - -use rustc_apfloat::Float; -use rustc_apfloat::Status; -use rustc_apfloat::StatusAnd; -use rustc_apfloat::ieee::Double; -use rustc_apfloat::ieee::Single; - -use super::float::FloatExt; -use crate::machine_state::MachineCoreState; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::memory; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::FValue; -use crate::machine_state::registers::XRegister; -use crate::parser::instruction::InstrRoundingMode; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl From for FValue { - fn from(f: Double) -> Self { - (f.to_bits() as u64).into() - } -} - -impl From for Double { - fn from(f: FValue) -> Self { - let val: u64 = f.into(); - Double::from_bits(val as u128) - } -} - -const CANONICAL_NAN_BITS: u64 = 0x7ff8000000000000; - -impl FloatExt for Double { - fn canonical_nan() -> Self { - Self::from_bits(CANONICAL_NAN_BITS as u128) - } -} - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// `FCLASS.D` D-type instruction. - /// - /// See [Self::run_fclass]. - pub fn run_fclass_d(&mut self, rs1: FRegister, rd: XRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fclass::(rs1, rd); - Ok(()) - } - - /// `FEQ.D` R-type instruction. - /// - /// See [Self::run_feq]. - pub fn run_feq_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_feq::(rs1, rs2, rd); - Ok(()) - } - - /// `FLE.D` R-type instruction. - /// - /// See [Self::run_fle]. - pub fn run_fle_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fle::(rs1, rs2, rd); - Ok(()) - } - - /// `FLT.D` R-type instruction. - /// - /// See [Self::run_flt]. - pub fn run_flt_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_flt::(rs1, rs2, rd); - Ok(()) - } - - /// `FADD.D` R-type instruction. - /// - /// See [Self::run_fadd]. - pub fn run_fadd_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fadd::(rs1, rs2, rm, rd) - } - - /// `FSUB.D` R-type instruction. - /// - /// See [Self::run_fsub]. - pub fn run_fsub_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsub::(rs1, rs2, rm, rd) - } - - /// `FMUL.D` R-type instruction. - /// - /// See [Self::run_fmul]. - pub fn run_fmul_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmul::(rs1, rs2, rm, rd) - } - - /// `FDIV.D` R-type instruction. - /// - /// See [Self::run_fdiv]. - pub fn run_fdiv_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fdiv::(rs1, rs2, rm, rd) - } - - /// `FSQRT.D` R-type instruction. - pub fn run_fsqrt_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval: u64 = self.fregisters.read(rs1).into(); - - let rm = self.f_rounding_mode(rm)?; - - let (StatusAnd { status, value }, _iterations) = ieee_apsqrt::sqrt_accurate(rval, rm); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, value.into()); - - Ok(()) - } - - /// `FMIN.D` R-type instruction. - /// - /// See [Self::run_fmin]. - pub fn run_fmin_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmin::(rs1, rs2, rd); - Ok(()) - } - - /// `FMAX.D` R-type instruction. - /// - /// See [Self::run_fmax]. - pub fn run_fmax_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmax::(rs1, rs2, rd); - Ok(()) - } - - /// `FMADD.D` instruction. - /// - /// See [Self::run_fmadd]. - pub fn run_fmadd_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmadd::(rs1, rs2, rs3, rm, rd) - } - - /// `FMSUB.D` instruction. - /// - /// See [Self::run_fmsub]. - pub fn run_fmsub_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmsub::(rs1, rs2, rs3, rm, rd) - } - - /// `FNMSUB.D` instruction. - /// - /// See [Self::run_fnmsub]. - pub fn run_fnmsub_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fnmsub::(rs1, rs2, rs3, rm, rd) - } - - /// `FNMADD.D` instruction. - /// - /// See [Self::run_fnmadd]. - pub fn run_fnmadd_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fnmadd::(rs1, rs2, rs3, rm, rd) - } - - /// `FCVT.D.W` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_d_w( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as i32 as i128, Double::from_i128_r) - } - - /// `FCVT.D.WU` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_d_wu( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as u32 as u128, Double::from_u128_r) - } - - /// `FCVT.D.W` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_d_l( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as i64 as i128, Double::from_i128_r) - } - - /// `FCVT.D.WU` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_d_lu( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as u128, Double::from_u128_r) - } - - /// `FCVT.D.S` R-type instruction. - /// - /// See [Self::run_fcvt_fmt_fmt]. - pub fn run_fcvt_d_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_fmt::(rs1, rm, rd) - } - - /// `FCVT.S.D` R-type instruction. - /// - /// See [Self::run_fcvt_fmt_fmt]. - pub fn run_fcvt_s_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_fmt::(rs1, rm, rd) - } - - /// `FCVT.S.W` R-type instruction. - pub fn run_fcvt_w_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as i32 as u64, - |f, rm| Double::to_i128_r(f, 32, rm, &mut false), - ) - } - - /// `FCVT.S.WU` R-type instruction. - pub fn run_fcvt_wu_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as i32 as u64, - |f, rm| Double::to_u128_r(f, 32, rm, &mut false), - ) - } - - /// `FCVT.S.W` R-type instruction. - pub fn run_fcvt_l_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as u64, - |f, rm| Double::to_i128_r(f, 64, rm, &mut false), - ) - } - - /// `FCVT.S.WU` R-type instruction. - pub fn run_fcvt_lu_d( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as u64, - |f, rm| Double::to_u128_r(f, 64, rm, &mut false), - ) - } - - /// `FSGNJ.D` R-type instruction. - /// - /// See [Self::run_fsgnj]. - pub fn run_fsgnj_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnj::(rs1, rs2, rd); - Ok(()) - } - - /// `FSGNJN.D` R-type instruction. - /// - /// See [Self::run_fsgnjn]. - pub fn run_fsgnjn_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnjn::(rs1, rs2, rd); - Ok(()) - } - - /// `FSGNJX.D` R-type instruction. - /// - /// See [Self::run_fsgnjx]. - pub fn run_fsgnjx_d( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnjx::(rs1, rs2, rd); - Ok(()) - } - - /// `FMV.D.X` D-type instruction - /// - /// Moves the single-precision value encoded in IEEE 754-2008 standard - /// encoding from the integer register `rs1` to the floating-point register - /// `rd`. - /// - /// The bits are not modified in the transfer, the payloads of non-canonical - /// NaNs are preserved. - pub fn run_fmv_d_x(&mut self, rs1: XRegister, rd: FRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval = self.xregisters.read(rs1).into(); - self.fregisters.write(rd, rval); - Ok(()) - } - - /// `FMV.X.D` D-type instruction - /// - /// Moves the double-precision value in floating-point register `rs1` - /// represented in IEEE 754-2008 encoding to the integer register `rd`. - /// - /// The bits are not modified in the transfer, the payloads of non-canonical - /// NaNs are preserved. - pub fn run_fmv_x_d(&mut self, rs1: FRegister, rd: XRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval = self.fregisters.read(rs1).into(); - self.xregisters.write(rd, rval); - Ok(()) - } -} - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `FLD` I-type instruction. - /// - /// Loads a double-precision floating point value from memory into `rd`. - /// It uses the same address format as integer-base ISA. - pub fn run_fld(&mut self, imm: i64, rs1: XRegister, rd: FRegister) -> Result<(), Exception> { - self.hart.check_fs_on()?; - - let val: u64 = self.read_from_bus(imm, rs1)?; - - self.hart.fregisters.write(rd, val.into()); - Ok(()) - } - - /// `FSD` S-type instruction. - /// - /// Stores a double-precision floating point value into memory from `rs2`. - /// It uses the same address format as integer-base ISA. - pub fn run_fsd(&mut self, imm: i64, rs1: XRegister, rs2: FRegister) -> Result<(), Exception> { - self.hart.check_fs_on()?; - - let val: u64 = self.hart.fregisters.read(rs2).into(); - self.write_to_bus(imm, rs1, val) - } -} - -#[cfg(test)] -mod tests { - use arbitrary_int::u5; - use proptest::prelude::*; - - use crate::backend_test; - use crate::bits::Bits64; - use crate::machine_state::MachineCoreState; - use crate::machine_state::csregisters::CSRegister; - use crate::machine_state::csregisters::xstatus::ExtensionValue; - use crate::machine_state::csregisters::xstatus::MStatus; - use crate::machine_state::hart_state::HartState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::fa2; - use crate::machine_state::registers::fa3; - use crate::machine_state::registers::parse_fregister; - use crate::machine_state::registers::parse_xregister; - use crate::machine_state::registers::t0; - use crate::state::NewState; - use crate::traps::Exception; - - backend_test!(test_fmv_d, F, { - let state = HartState::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - d in any::().prop_map(f64::to_bits), - rs1 in (1_u8..31).prop_map(u5::new).prop_map(parse_xregister), - rs1_f in (0_u8..31).prop_map(u5::new).prop_map(parse_fregister), - rs2 in (1_u8..31).prop_map(u5::new).prop_map(parse_xregister), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(0); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.csregisters.write(CSRegister::mstatus, mstatus); - - state.xregisters.write(rs1, d); - assert!(state.run_fmv_d_x(rs1, rs1_f).is_ok()); - - assert_eq!(d, u64::from(state.fregisters.read(rs1_f)), "Expected bits to be moved to fregister"); - - assert!(state.run_fmv_x_d(rs1_f, rs2).is_ok()); - - assert_eq!(d, state.xregisters.read(rs2), "Expected bits to be moved to xregister"); - }); - }); - - backend_test!(test_load_store, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - val in any::().prop_map(f64::to_bits), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.hart.csregisters.write(CSRegister::mstatus, mstatus); - - let mut perform_test = |offset: u64| -> Result<(), Exception> { - // Save test values v_i in registers ai - state.hart.fregisters.write(fa2, val.into()); - - // t0 will hold the "global" offset of all loads / stores we are going to make - state.hart.xregisters.write(t0, offset); - - // Perform the stores - state.run_fsd(-4, t0, fa2)?; - - state.run_fld(-4, t0, fa3)?; - - assert_eq!(state.hart.fregisters.read(fa3), val.into()); - Ok(()) - }; - - let invalid_offset = 0u64.wrapping_sub(1024); - let aligned_offset = 512; - let misaligned_offset = 513; - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset).is_ok()); - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset).is_ok()); - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64dc.rs b/src/riscv/lib/src/interpreter/rv64dc.rs deleted file mode 100644 index b978fee93259eca7528e274ce5037e29abf60a41..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64dc.rs +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_DC extension for RISC-V -//! -//! U:C-16 - -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::sp; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `C.FLD` CL-type compressed instruction - /// - /// Loads a double-precision floating-point value from memory into - /// floating-point register `rd`. It computes an effective address by - /// adding the immediate to the base address in register `rs1`. - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - pub fn run_cfld(&mut self, imm: i64, rs1: XRegister, rd: FRegister) -> Result<(), Exception> { - debug_assert!(imm >= 0 && imm % 8 == 0); - self.run_fld(imm, rs1, rd) - } - - /// `C.FLDSP` CI-type compressed instruction - /// - /// Loads a double-precision floating-point value from memory into - /// floating-point register `rd`. It computes an effective address by - /// adding the immediate to the stack pointer. - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - pub fn run_cfldsp(&mut self, imm: i64, rd_rs1: FRegister) -> Result<(), Exception> { - debug_assert!(imm >= 0 && imm % 8 == 0); - self.run_fld(imm, sp, rd_rs1) - } - - /// `C.FSD` CS-type compressed instruction - /// - /// Stores a double-precision floating-point value in floating-point - /// register `rs2` to memory. It computes an effective address by adding - /// the immediate to the base address in register `rs1`. - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - pub fn run_cfsd(&mut self, imm: i64, rs1: XRegister, rs2: FRegister) -> Result<(), Exception> { - debug_assert!(imm >= 0 && imm % 8 == 0); - self.run_fsd(imm, rs1, rs2) - } - - /// `C.FSDSP` CSS-type compressed instruction - /// - /// Stores a double-precision floating-point value in floating-point - /// register `rs2` to memory. It computes an effective address by adding - /// the immediate to the stack pointer. - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - pub fn run_cfsdsp(&mut self, imm: i64, rs2: FRegister) -> Result<(), Exception> { - debug_assert!(imm >= 0 && imm % 8 == 0); - self.run_fsd(imm, sp, rs2) - } -} - -#[cfg(test)] -mod test { - use arbitrary_int::u5; - use proptest::prelude::*; - - use crate::backend_test; - use crate::bits::Bits64; - use crate::machine_state::MachineCoreState; - use crate::machine_state::csregisters::CSRegister; - use crate::machine_state::csregisters::xstatus::ExtensionValue; - use crate::machine_state::csregisters::xstatus::MStatus; - use crate::machine_state::memory::M4K; - use crate::machine_state::memory::MemoryConfig; - use crate::machine_state::registers::fa2; - use crate::machine_state::registers::fa3; - use crate::machine_state::registers::parse_xregister; - use crate::machine_state::registers::sp; - use crate::state::NewState; - use crate::traps::Exception; - - const ZERO_OFFSET: i64 = 0; - - type MC = M4K; - - const OUT_OF_BOUNDS_OFFSET: i64 = MC::TOTAL_BYTES as i64; - - backend_test!(test_cfsd_cfld, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - base_addr in (0..504_u64), - base_imm in (0..=64i64).prop_map(|x| x * 8), // multiples of 8 in the 0..512 range - val in any::().prop_map(f64::to_bits), - rs1 in (1_u8..31).prop_map(u5::new).prop_map(parse_xregister), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.hart.csregisters.write(CSRegister::mstatus, mstatus); - - let mut perform_test = |offset: i64| -> Result<(), Exception> { - state.hart.fregisters.write(fa2, val.into()); - state.hart.xregisters.write(rs1, base_addr); - - let imm = base_imm + offset; - state.run_cfsd(imm, rs1, fa2)?; - state.run_cfld(imm, rs1, fa3)?; - - assert_eq!(state.hart.fregisters.read(fa3), val.into()); - Ok(()) - }; - - // Aligned and unaligned loads / stores - prop_assert!(perform_test(ZERO_OFFSET).is_ok()); - - // Out of bounds loads / stores - prop_assert!(perform_test(OUT_OF_BOUNDS_OFFSET).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - }); - }); - - backend_test!(test_cfsdsp_cfldsp, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - base_addr in (0..504_u64), - base_imm in (0..=64i64).prop_map(|x| x * 8), // multiples of 8 in the 0..512 range - val in any::().prop_map(f64::to_bits), - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.hart.csregisters.write(CSRegister::mstatus, mstatus); - - let mut perform_test = |offset: i64| -> Result<(), Exception> { - state.hart.fregisters.write(fa2, val.into()); - state.hart.xregisters.write(sp, base_addr); - - let imm = base_imm + offset; - state.run_cfsdsp(imm, fa2)?; - state.run_cfldsp(imm, fa3)?; - - assert_eq!(state.hart.fregisters.read(fa3), val.into()); - Ok(()) - }; - - // Aligned and unaligned loads / stores - prop_assert!(perform_test(ZERO_OFFSET).is_ok()); - - // Out of bounds loads / stores - prop_assert!(perform_test(OUT_OF_BOUNDS_OFFSET).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64f.rs b/src/riscv/lib/src/interpreter/rv64f.rs deleted file mode 100644 index a04bc32de5e59ebecfe9f3a13af27e19df82115e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64f.rs +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_UF extension for RISC-V -//! -//! Chapter 11 - "F" Standard Extension for Single-Precision Floating-Point - -use rustc_apfloat::Float; -use rustc_apfloat::Status; -use rustc_apfloat::StatusAnd; -use rustc_apfloat::ieee::Single; - -use super::float::FloatExt; -use crate::machine_state::MachineCoreState; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::memory; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::FValue; -use crate::machine_state::registers::XRegister; -use crate::parser::instruction::InstrRoundingMode; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl From for FValue { - fn from(f: Single) -> Self { - let val = f.to_bits(); - f32_to_fvalue(val as u32) - } -} - -impl From for Single { - fn from(f: FValue) -> Self { - let val: u64 = f.into(); - - // Check value correctly NaN boxed: - // all upper bits must be set to 1 - if val >> 32 != 0xffffffff { - Single::canonical_nan() - } else { - Single::from_bits(val as u32 as u128) - } - } -} - -const CANONICAL_NAN_BITS: u32 = 0x7fc00000; - -impl FloatExt for Single { - fn canonical_nan() -> Self { - Self::from_bits(CANONICAL_NAN_BITS as u128) - } -} - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// `FCLASS.S` F-type instruction. - /// - /// See [Self::run_fclass]. - pub fn run_fclass_s(&mut self, rs1: FRegister, rd: XRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fclass::(rs1, rd); - Ok(()) - } - - /// `FEQ.S` R-type instruction. - /// - /// See [Self::run_feq]. - pub fn run_feq_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_feq::(rs1, rs2, rd); - Ok(()) - } - - /// `FLE.S` R-type instruction. - /// - /// See [Self::run_fle]. - pub fn run_fle_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fle::(rs1, rs2, rd); - Ok(()) - } - - /// `FLT.S` R-type instruction. - /// - /// See [Self::run_flt]. - pub fn run_flt_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_flt::(rs1, rs2, rd); - Ok(()) - } - - /// `FADD.S` R-type instruction. - /// - /// See [Self::run_fadd]. - pub fn run_fadd_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fadd::(rs1, rs2, rm, rd) - } - - /// `FSUB.S` R-type instruction. - /// - /// See [Self::run_fsub]. - pub fn run_fsub_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsub::(rs1, rs2, rm, rd) - } - - /// `FMUL.S` R-type instruction. - /// - /// See [Self::run_fmul]. - pub fn run_fmul_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmul::(rs1, rs2, rm, rd) - } - - /// `FDIV.S` R-type instruction. - /// - /// See [Self::run_fdiv]. - pub fn run_fdiv_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fdiv::(rs1, rs2, rm, rd) - } - - /// `FSQRT.S` R-type instruction. - pub fn run_fsqrt_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval = self.fregisters.read(rs1); - let rm = self.f_rounding_mode(rm)?; - - let rval = fvalue_to_f32_bits(rval); - - let (StatusAnd { status, value }, _iterations) = ieee_apsqrt::sqrt_accurate(rval, rm); - - if status != Status::OK { - self.csregisters.set_exception_flag_status(status); - } - - self.fregisters.write(rd, f32_to_fvalue(value)); - - Ok(()) - } - - /// `FMIN.S` R-type instruction. - /// - /// See [Self::run_fmin]. - pub fn run_fmin_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmin::(rs1, rs2, rd); - Ok(()) - } - - /// `FMAX.S` R-type instruction. - /// - /// See [Self::run_fmax]. - pub fn run_fmax_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmax::(rs1, rs2, rd); - Ok(()) - } - - /// `FMADD.S` instruction. - /// - /// See [Self::run_fmadd]. - pub fn run_fmadd_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmadd::(rs1, rs2, rs3, rm, rd) - } - - /// `FMSUB.S` instruction. - /// - /// See [Self::run_fmsub]. - pub fn run_fmsub_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fmsub::(rs1, rs2, rs3, rm, rd) - } - - /// `FNMSUB.S` instruction. - /// - /// See [Self::run_fnmsub]. - pub fn run_fnmsub_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fnmsub::(rs1, rs2, rs3, rm, rd) - } - - /// `FNMADD.S` instruction. - /// - /// See [Self::run_fnmadd]. - pub fn run_fnmadd_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rs3: FRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fnmadd::(rs1, rs2, rs3, rm, rd) - } - - /// `FCVT.S.W` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_s_w( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as i32 as i128, Single::from_i128_r) - } - - /// `FCVT.S.WU` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_s_wu( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as u32 as u128, Single::from_u128_r) - } - - /// `FCVT.S.W` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_s_l( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as i64 as i128, Single::from_i128_r) - } - - /// `FCVT.S.WU` R-type instruction. - /// - /// See [Self::run_fcvt_int_fmt]. - pub fn run_fcvt_s_lu( - &mut self, - rs1: XRegister, - rm: InstrRoundingMode, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_int_fmt(rs1, rm, rd, |u| u as u128, Single::from_u128_r) - } - - /// `FCVT.S.W` R-type instruction. - pub fn run_fcvt_w_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as i32 as u64, - |f, rm| Single::to_i128_r(f, 32, rm, &mut false), - ) - } - - /// `FCVT.S.WU` R-type instruction. - pub fn run_fcvt_wu_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as i32 as u64, - |f, rm| Single::to_u128_r(f, 32, rm, &mut false), - ) - } - - /// `FCVT.S.W` R-type instruction. - pub fn run_fcvt_l_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as u64, - |f, rm| Single::to_i128_r(f, 64, rm, &mut false), - ) - } - - /// `FCVT.S.WU` R-type instruction. - pub fn run_fcvt_lu_s( - &mut self, - rs1: FRegister, - rm: InstrRoundingMode, - rd: XRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fcvt_fmt_int( - rs1, - rm, - rd, - |u| u as u64, - |f, rm| Single::to_u128_r(f, 64, rm, &mut false), - ) - } - - /// `FSGNJ.S` R-type instruction. - /// - /// See [Self::run_fsgnj]. - pub fn run_fsgnj_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnj::(rs1, rs2, rd); - Ok(()) - } - - /// `FSGNJN.S` R-type instruction. - /// - /// See [Self::run_fsgnjn]. - pub fn run_fsgnjn_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnjn::(rs1, rs2, rd); - Ok(()) - } - - /// `FSGNJX.S` R-type instruction. - /// - /// See [Self::run_fsgnjx]. - pub fn run_fsgnjx_s( - &mut self, - rs1: FRegister, - rs2: FRegister, - rd: FRegister, - ) -> Result<(), Exception> { - self.check_fs_on()?; - - self.run_fsgnjx::(rs1, rs2, rd); - Ok(()) - } - - /// `FMV.X.W` F-type instruction - /// - /// Moves the single-precision value in floating-point register `rs1` - /// represented in IEEE 754-2008 encoding to the lower 32 bits of - /// integer register `rd`. - /// - /// The bits are not modified in the transfer, - /// and in particular, the payloads of non-canonical NaNs are preserved. - /// - /// The higher 32 bits of the destination register are filled with copies - /// of the floating-point number’s sign bit. - pub fn run_fmv_x_w(&mut self, rs1: FRegister, rd: XRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval: u64 = self.fregisters.read(rs1).into(); - let rval = rval as i32 as u64; - - self.xregisters.write(rd, rval); - Ok(()) - } - - /// `FMV.W.X` F-type instruction - /// - /// Moves the single-precision value encoded in IEEE 754-2008 standard - /// encoding from the lower 32 bits of integer register `rs1` to the - /// floating-point register `rd`. - /// - /// The bits are not modified in the transfer, - /// and in particular, the payloads of non-canonical NaNs are preserved. - pub fn run_fmv_w_x(&mut self, rs1: XRegister, rd: FRegister) -> Result<(), Exception> { - self.check_fs_on()?; - - let rval = self.xregisters.read(rs1) as u32; - let rval = f32_to_fvalue(rval); - - self.fregisters.write(rd, rval); - Ok(()) - } -} - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `FLW` I-type instruction. - /// - /// Loads a single-precision floating point value from memory into `rd`. - /// It uses the same address format as integer-base ISA. - pub fn run_flw(&mut self, imm: i64, rs1: XRegister, rd: FRegister) -> Result<(), Exception> { - self.hart.check_fs_on()?; - - let val: u32 = self.read_from_bus(imm, rs1)?; - let val = f32_to_fvalue(val); - - self.hart.fregisters.write(rd, val); - Ok(()) - } - - /// `FSW` S-type instruction. - /// - /// Stores a single-precision floating point value into memory from `rs2`. - /// It uses the same address format as integer-base ISA. - pub fn run_fsw(&mut self, imm: i64, rs1: XRegister, rs2: FRegister) -> Result<(), Exception> { - self.hart.check_fs_on()?; - - let val: u64 = self.hart.fregisters.read(rs2).into(); - self.write_to_bus(imm, rs1, val as u32) - } -} - -/// The upper 32 bits are set to `1` for any `f32` write. -#[inline(always)] -fn f32_to_fvalue(val: u32) -> FValue { - (val as u64 | 0xffffffff00000000).into() -} - -fn fvalue_to_f32_bits(f: FValue) -> u32 { - let val: u64 = f.into(); - - // Check value correctly NaN boxed: - // all upper bits must be set to 1 - if val >> 32 != 0xffffffff { - CANONICAL_NAN_BITS - } else { - val as u32 - } -} - -#[cfg(test)] -mod tests { - use arbitrary_int::u5; - use proptest::prelude::*; - use rustc_apfloat::Float; - use rustc_apfloat::ieee::Double; - use rustc_apfloat::ieee::Single; - - use super::f32_to_fvalue; - use crate::backend_test; - use crate::bits::Bits64; - use crate::machine_state::MachineCoreState; - use crate::machine_state::csregisters::CSRegister; - use crate::machine_state::csregisters::xstatus::ExtensionValue; - use crate::machine_state::csregisters::xstatus::MStatus; - use crate::machine_state::hart_state::HartState; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::fa1; - use crate::machine_state::registers::fa4; - use crate::machine_state::registers::parse_fregister; - use crate::machine_state::registers::parse_xregister; - use crate::machine_state::registers::t0; - use crate::state::NewState; - use crate::traps::Exception; - - backend_test!(test_fmv_f, F, { - proptest!(|( - f in any::().prop_map(f32::to_bits), - rs1 in (1_u8..31).prop_map(u5::new).prop_map(parse_xregister), - rs1_f in (1_u8..31).prop_map(u5::new).prop_map(parse_fregister), - rs2 in (1_u8..31).prop_map(u5::new).prop_map(parse_xregister), - )| { - let mut state = HartState::new(&mut F::manager()); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.csregisters.write(CSRegister::mstatus, mstatus); - - state.xregisters.write(rs1, f as u64); - - assert!(state.run_fmv_w_x(rs1, rs1_f).is_ok()); - - let read: u64 = state.fregisters.read(rs1_f).into(); - assert_eq!(f, read as u32, "Expected bits to be moved to fregister"); - - let f_64 = Double::from_bits(read as u128); - assert!(f_64.is_nan() && !f_64.is_signaling()); - - assert!(state.run_fmv_x_w(rs1_f, rs2).is_ok()); - - let read = state.xregisters.read(rs2); - assert_eq!(f, read as u32, "Expected bits to be moved to xregister"); - - let f_32 = Single::from_bits(f as u128); - if f_32.is_negative() { - assert_eq!(read, (f as u64) | 0xffffffff00000000, "Expected sign byte to be extended"); - } else { - assert_eq!(read, f as u64, "Expected no sign byte to be extended"); - } - }); - }); - - backend_test!(test_load_store, F, { - let state = MachineCoreState::::new(&mut F::manager()); - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - val in any::().prop_map(f32::to_bits), - )| - { - let mut state = state_cell.borrow_mut(); - state.reset(); - state.main_memory.set_all_readable_writeable(); - - // Turn fs on - let mstatus = MStatus::from_bits(0u64).with_fs(ExtensionValue::Dirty); - state.hart.csregisters.write(CSRegister::mstatus, mstatus); - - let mut perform_test = |offset: u64| -> Result<(), Exception> { - // Save test values v_i in registers ai - state.hart.fregisters.write(fa1, f32_to_fvalue(val)); - - // t0 will hold the "global" offset of all loads / stores we are going to make - state.hart.xregisters.write(t0, offset); - - // Perform the stores - state.run_fsw(4, t0, fa1)?; - - state.run_flw(4, t0, fa4)?; - - assert_eq!(state.hart.fregisters.read(fa4), f32_to_fvalue(val)); - Ok(()) - }; - - let invalid_offset = 0u64.wrapping_sub(1024); - let aligned_offset = 512; - let misaligned_offset = 513; - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset).is_ok()); - - // Out of bounds loads / stores - prop_assert!(perform_test(invalid_offset).is_err_and(|e| - matches!(e, Exception::StoreAMOAccessFault(_)) - )); - // Aligned loads / stores - prop_assert!(perform_test(aligned_offset).is_ok()); - // Unaligned loads / stores - prop_assert!(perform_test(misaligned_offset).is_ok()); - }); - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64m.rs b/src/riscv/lib/src/interpreter/rv64m.rs deleted file mode 100644 index e39f8206842d4def66bf492f3b834c181417e9c1..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64m.rs +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of RV_64_M extension for RISC-V -//! -//! Chapter 7 - Unprivileged spec - -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::XRegisters; -use crate::state_backend as backend; - -impl XRegisters -where - M: backend::ManagerReadWrite, -{ - /// `REMW` R-type instruction - /// - /// Compute the remainder of val(rs1) divided by val(rs2), but only consider - /// the lower 32 bits of each value. Store result in `rd`. In case the lower - /// 32 bits of val(rs2) is zero, the result is val(rs1). In case of overflow - /// the result is zero. All values used in the operation are _signed integers_. - pub fn run_remw(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1) as i32; - let rval2 = self.read(rs2) as i32; - - let result = if rval2 == 0 { - rval1 - } else if rval2 == -1 && rval1 == i32::MIN { - 0 - } else { - rval1 % rval2 - }; - - self.write(rd, result as u64); - } - - /// `REMUW` R-type instruction - /// - /// Compute the remainder of val(rs1) divided by val(rs2) and store the result - /// in register `rd`. In case val(rs2) is zero, the result is val(rs1). All - /// values are _unsigned integers_. - pub fn run_remuw(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1) as u32; - let rval2 = self.read(rs2) as u32; - - let result = if rval2 == 0 { rval1 } else { rval1 % rval2 }; - - self.write(rd, result as i32 as u64); - } - - /// `DIVW` R-type instruction - /// - /// Divide the lower 32 bits of val(rs1) by the lower 32 bits of val(rs2). - /// The result is stored in `rd`. In case the divisor is zero, the result is - /// `-1`. In case the dividend is `i32::MIN`, and the divisor is `-1`, then - /// the result is `i32::MIN` as well. All values are _signed integers_. - pub fn run_divw(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1) as i32; - let rval2 = self.read(rs2) as i32; - - let result = if rval2 == 0 { - -1 - } else if rval2 == -1 && rval1 == i32::MIN { - i32::MIN - } else { - rval1 / rval2 - }; - - self.write(rd, result as u64); - } - - /// `DIVUW` R-type instruction - /// - /// Divide lower 32 bits of val(rs1) by the lower 32 bits of val(rs2). - /// The result is stored in `rd`. In case the divisor is zero, the result is - /// u32::MAX. All values are _unsigned integers_. - pub fn run_divuw(&mut self, rs1: XRegister, rs2: XRegister, rd: XRegister) { - let rval1 = self.read(rs1) as u32; - let rval2 = self.read(rs2) as u32; - - let result = if rval2 == 0 { u32::MAX } else { rval1 / rval2 }; - - self.write(rd, result as i32 as u64); - } -} - -#[cfg(test)] -mod test { - use proptest::prelude::any; - use proptest::prop_assert_eq; - use proptest::proptest; - - use crate::backend_test; - use crate::machine_state::registers::XRegisters; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::a3; - use crate::state::NewState; - - backend_test!(test_div_rem_invariant, F, { - proptest!(|( - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = XRegisters::new(&mut F::manager()); - - state.write(a0, r1_val); - state.write(a1, r2_val); - state.run_divw(a0, a1, a2); - state.run_remw(a0, a1, a3); - - prop_assert_eq!( - state.read(a0) as i32, - (state.read(a1) as i32) - .wrapping_mul(state.read(a2) as i32) - .wrapping_add(state.read(a3) as i32)); - }) - }); - - backend_test!(test_divu_remu_invariant, F, { - proptest!(|( - r1_val in any::(), - r2_val in any::(), - )| { - let mut state = XRegisters::new(&mut F::manager()); - - state.write(a0, r1_val); - state.write(a1, r2_val); - state.run_divuw(a0, a1, a2); - state.run_remuw(a0, a1, a3); - - prop_assert_eq!( - state.read(a0) as u32, - (state.read(a1) as u32) - .wrapping_mul(state.read(a2) as u32) - .wrapping_add(state.read(a3) as u32)); - }) - }); -} diff --git a/src/riscv/lib/src/interpreter/rv64priv.rs b/src/riscv/lib/src/interpreter/rv64priv.rs deleted file mode 100644 index d7d7e8d12908ebf8e14b41f3373fcdb9c900493a..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64priv.rs +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory; -use crate::machine_state::registers::XRegister; -use crate::state_backend as backend; -use crate::traps::Exception; - -impl MachineCoreState -where - MC: memory::MemoryConfig, - M: backend::ManagerReadWrite, -{ - /// `SFENCE.VMA` instruction - /// - /// The supervisor memory-management fence instruction SFENCE.VMA is used to - /// synchronize updates to in-memory memory-management data structures - /// with current execution. See sections 3.1.6.5, 3.7.2. - /// - /// Section 5.2.1: It is always legal to over-fence. - #[inline(always)] - pub fn run_sfence_vma(&mut self, _asid: XRegister, _vaddr: XRegister) -> Result<(), Exception> { - // Even if we over-fence, thus ignoring asid and vaddr, this instruction - // is still no-op since memory loads/stores are not cached currently. - Ok(()) - } -} diff --git a/src/riscv/lib/src/interpreter/rv64zicsr.rs b/src/riscv/lib/src/interpreter/rv64zicsr.rs deleted file mode 100644 index a256ca9708d72e5e67d4363e2ff468f3c9688d0b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64zicsr.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Implementation of Zicsr extension for RISC-V -//! -//! Chapter 9 - Unprivileged spec - -use crate::machine_state::csregisters; -use crate::machine_state::hart_state::HartState; -use crate::machine_state::registers; -use crate::state_backend as backend; - -impl HartState -where - M: backend::ManagerReadWrite, -{ - /// Execute a `CSRRW` instruction. - #[inline(always)] - pub fn run_csrrw( - &mut self, - csr: csregisters::CSRegister, - rs1: registers::XRegister, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - let value = self.xregisters.read(rs1); - self.csr_replace(csr, value, rd) - } - - /// Execute a `CSRRWI` instruction. - #[inline(always)] - pub fn run_csrrwi( - &mut self, - csr: csregisters::CSRegister, - imm: registers::XValue, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - self.csr_replace(csr, imm & 0b11111, rd) - } - - /// Replace the value in `csr` with `value` and write the previous value to `rd`. - /// When `rd = x0`, no read side effects are triggered. - #[inline(always)] - fn csr_replace( - &mut self, - csr: csregisters::CSRegister, - value: registers::XValue, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - csregisters::check_write(csr)?; - csregisters::access_checks(csr, self)?; - - // When `rd = x0`, we don't want to trigger any CSR read effects. - if rd.is_zero() { - self.csregisters.write(csr, value); - } else { - let old: registers::XValue = self.csregisters.replace(csr, value); - self.xregisters.write(rd, old); - } - Ok(()) - } - - /// Execute the `CSRRS` instruction. - #[inline(always)] - pub fn run_csrrs( - &mut self, - csr: csregisters::CSRegister, - rs1: registers::XRegister, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - csregisters::access_checks(csr, self)?; - - // When `rs1 = x0`, we don't want to trigger any CSR write effects. - let old = if rs1.is_zero() { - self.csregisters.read(csr) - } else { - csregisters::check_write(csr)?; - - let value = self.xregisters.read(rs1); - self.csregisters.set_bits(csr, value) - }; - - self.xregisters.write(rd, old.repr()); - Ok(()) - } - - /// Execute the `CSRRSI` instruction. - #[inline(always)] - pub fn run_csrrsi( - &mut self, - csr: csregisters::CSRegister, - imm: registers::XValue, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - let imm = imm & 0b11111; - csregisters::access_checks(csr, self)?; - - // When `imm = 0`, we don't want to trigger any CSR write effects. - let old = if imm == 0 { - self.csregisters.read(csr) - } else { - csregisters::check_write(csr)?; - - self.csregisters.set_bits(csr, imm) - }; - - self.xregisters.write(rd, old.repr()); - Ok(()) - } - - /// Execute the `CSRRC` instruction. - #[inline(always)] - pub fn run_csrrc( - &mut self, - csr: csregisters::CSRegister, - rs1: registers::XRegister, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - csregisters::access_checks(csr, self)?; - - // When `rs1 = x0`, we don't want to trigger any CSR write effects. - let old = if rs1.is_zero() { - self.csregisters.read(csr) - } else { - csregisters::check_write(csr)?; - - let value = self.xregisters.read(rs1); - self.csregisters.clear_bits(csr, value) - }; - - self.xregisters.write(rd, old.repr()); - Ok(()) - } - - /// Execute the `CSRRCI` instruction. - #[inline(always)] - pub fn run_csrrci( - &mut self, - csr: csregisters::CSRegister, - imm: registers::XValue, - rd: registers::XRegister, - ) -> csregisters::Result<()> { - let imm = imm & 0b11111; - csregisters::access_checks(csr, self)?; - - // When `imm = 0`, we don't want to trigger any CSR write effects. - let old = if imm == 0 { - self.csregisters.read(csr) - } else { - csregisters::check_write(csr)?; - - self.csregisters.clear_bits(csr, imm) - }; - - self.xregisters.write(rd, old.repr()); - Ok(()) - } -} diff --git a/src/riscv/lib/src/interpreter/rv64zifencei.rs b/src/riscv/lib/src/interpreter/rv64zifencei.rs deleted file mode 100644 index ab3b80c5f791f3e3ede1a353165aa6f35bd73222..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/interpreter/rv64zifencei.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of Zifencei extension for RISC-V - -use crate::machine_state::CacheLayouts; -use crate::machine_state::MachineState; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::memory::MemoryConfig; -use crate::state_backend; - -impl MachineState -where - MC: MemoryConfig, - CL: CacheLayouts, - B: Block, - M: state_backend::ManagerReadWrite, -{ - /// Execute a `fence.i` instruction. - #[inline(always)] - pub fn run_fencei(&mut self) { - self.block_cache.invalidate(); - } -} diff --git a/src/riscv/lib/src/jit.rs b/src/riscv/lib/src/jit.rs deleted file mode 100644 index 6b55e8244a9fd3b0786a44c93d71cd37b3dc6629..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit.rs +++ /dev/null @@ -1,2482 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! A JIT library for compilation of sequences (or blocks) of RISC-V -//! instructions to native code. - -pub(crate) mod builder; -pub(crate) mod state_access; - -use std::collections::HashMap; -use std::ffi::c_void; - -use cranelift::codegen::CodegenError; -use cranelift::codegen::ir::types::I64; -use cranelift::codegen::settings::SetError; -use cranelift::frontend::FunctionBuilderContext; -use cranelift::prelude::*; -use cranelift_jit::JITBuilder; -use cranelift_jit::JITModule; -use cranelift_module::Linkage; -use cranelift_module::Module; -use cranelift_module::ModuleError; -use state_access::JitStateAccess; -use thiserror::Error; - -use self::builder::Builder; -use self::state_access::JsaCalls; -use self::state_access::JsaImports; -use self::state_access::register_jsa_symbols; -use crate::machine_state::MachineCoreState; -use crate::machine_state::block_cache::metrics::block_metrics; -use crate::machine_state::instruction::Instruction; -use crate::machine_state::memory::MemoryConfig; -use crate::state_backend::hash::Hash; -use crate::traps::EnvironException; - -/// Alias for the function signature produced by the JIT compilation. -/// -/// This must have the same Abi as [`DispatchFn`], which is used by -/// the block dispatch mechanism in the block cache. -/// -/// The JitFn does not inspect the first and last parameters here, however. -/// These parameters are needed by the initial dispatch mechanism to enable -/// JIT-compilation & hot-swapping. To avoid over-specifying these parameters here -/// (which can among other things cause type-checking issues), we replace the parameters -/// with pointers to `c_void` - which in the C abi map to the same parameter type as the -/// thin-references to the actual variables passed. -/// -/// [`DispatchFn`]: crate::machine_state::block_cache::block::DispatchFn -#[expect( - improper_ctypes_definitions, - reason = "The receiving functions know the layout of the referenced types" -)] -pub type JitFn = unsafe extern "C" fn( - // ignored - *const c_void, - &mut MachineCoreState, - u64, - &mut usize, - &mut Result<(), EnvironException>, - // ignored - *const c_void, -); - -/// Errors that may arise from the initialisation of the JIT. -#[derive(Debug, Error)] -pub enum JitError { - /// Failures setting flags. - #[error("Failed to set flag {0}")] - Setting(#[from] SetError), - /// Native compilation unsupported on the current arch/os. - #[error("Native platform unsupported: {0}")] - UnsupportedPlatform(&'static str), - /// Constructing the Cranelift builder failed. - #[error("Unable to initialise builder {0}")] - BuilderFailure(#[from] CodegenError), - /// Unable to register external [`JitStateAccess`] functionality. - #[error("Unable to register external JSA functions: {0}")] - JsaRegistration(#[from] ModuleError), -} - -/// The JIT is responsible for compiling blocks of instructions to machine code, -/// returning a function that can be run over the [`MachineCoreState`]. -pub struct JIT { - /// The function builder context, which is reused across multiple - /// [`FunctionBuilder`] instances. - builder_context: FunctionBuilderContext, - - /// The main Cranelift context, which holds the state for codegen. Cranelift - /// separates this from `Module` to allow for parallel compilation, with a - /// context per thread, though this isn't in the simple demo here. - ctx: codegen::Context, - - /// The module, with the jit backend, which manages the JIT'd - /// functions. - module: JITModule, - - /// Imported [JitStateAccess] functions. - jsa_imports: JsaImports, - - /// Cache of compilation results. - cache: HashMap>>, -} - -impl JIT { - /// Create a new instance of the JIT, which will be able to - /// produce functions that can be run over the current - /// memory configuration and manager. - pub fn new() -> Result { - if std::mem::size_of::() != std::mem::size_of::() { - return Err(JitError::UnsupportedPlatform( - "octez-riscv JIT only supports 64-bit architectures", - )); - } - - let mut flag_builder = settings::builder(); - flag_builder.set("use_colocated_libcalls", "false")?; - flag_builder.set("is_pic", "false")?; - - let isa_builder = cranelift_native::builder().map_err(JitError::UnsupportedPlatform)?; - let isa = isa_builder.finish(settings::Flags::new(flag_builder))?; - - let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); - register_jsa_symbols::(&mut builder); - - let mut module = JITModule::new(builder); - let jsa_imports = JsaImports::declare_in_module(&mut module)?; - - Ok(Self { - builder_context: FunctionBuilderContext::new(), - ctx: codegen::Context::new(), - module, - jsa_imports, - cache: Default::default(), - }) - } - - /// Compile a sequence of instructions to a callable native function. - /// - /// Not all instructions are currently supported. For blocks containing - /// unsupported instructions, `None` will be returned. - pub fn compile(&mut self, instr: &[Instruction]) -> Option> { - let Ok(hash) = Hash::blake2b_hash(instr) else { - return None; - }; - - if let Some(compilation_result) = self.cache.get(&hash) { - return *compilation_result; - } - - let mut builder = self.start(); - - for i in instr { - let Some(lower) = i.opcode.to_lowering() else { - builder.fail(); - self.clear(); - self.cache.insert(hash, None); - return None; - }; - - let pc_update = unsafe { - // # SAFETY: lower is called with args from the same instruction that it - // was derived - (lower)(i.args(), &mut builder) - }; - - let Some(pc_update) = pc_update else { - builder.end_unconditional_exception(); - - let jcall = self.produce_function(&hash); - - return Some(jcall); - }; - - if !builder.complete_step(pc_update) { - // We have encountered an unconditional jump, exit the block. - break; - } - } - - builder.end(); - let jcall = self.produce_function(&hash); - - Some(jcall) - } - - /// Setup the builder, ensuring the entry block of the function is correct. - /// - /// # Input Args - /// - /// | `core: &mut MachineCoreState` | `int (ptr) -> MachineCoreState` | - /// | `pc: Address` | `I64` | - /// | `steps: &mut usize` | `int (ptr) -> int` | - /// - /// # Return - /// - /// | `steps: usize` | `int` | - fn start(&mut self) -> Builder<'_, MC, JSA> { - let ptr = self.module.target_config().pointer_type(); - - // first param ignored - self.ctx.func.signature.params.push(AbiParam::new(ptr)); - // params - self.ctx.func.signature.params.push(AbiParam::new(ptr)); - self.ctx.func.signature.params.push(AbiParam::new(I64)); - self.ctx.func.signature.params.push(AbiParam::new(ptr)); - self.ctx.func.signature.params.push(AbiParam::new(ptr)); - // last param ignored - self.ctx.func.signature.params.push(AbiParam::new(ptr)); - - let builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context); - let jsa_call = JsaCalls::func_calls(&mut self.module, &self.jsa_imports, ptr); - - Builder::<'_, MC, JSA>::new(ptr, builder, jsa_call) - } - - /// Finalise and cache the function under construction. - fn produce_function(&mut self, hash: &Hash) -> JitFn { - let name = hex::encode(hash); - - let fun = self.finalise(&name); - - self.cache.insert(*hash, Some(fun)); - block_metrics!(hash = hash, record_jitted); - - fun - } - - /// Finalise the function currently under construction. - fn finalise(&mut self, name: &str) -> JitFn { - let id = self - .module - .declare_function(name.as_ref(), Linkage::Export, &self.ctx.func.signature) - .map_err(|e| e.to_string()) - .unwrap(); - - // define the function to jit - self.module.define_function(id, &mut self.ctx).unwrap(); - - // finalise the function - self.module.finalize_definitions().unwrap(); - let code = self.module.get_finalized_function(id); - - self.clear(); - - // SAFETY: the signature of a JitFn matches exactly the abi we specified in the - // entry block. Compilation has succeeded & therefore this produced code - // is safe to call. - unsafe { std::mem::transmute(code) } - } - - /// Clear the current context to allow a new function to be compiled - fn clear(&mut self) { - self.module.clear_context(&mut self.ctx) - } -} - -// TODO: https://linear.app/tezos/issue/RV-496 -// `Block::BlockBuilder` should not require Default, as it -// does not allow for potential fallilibility -impl Default for JIT { - fn default() -> Self { - Self::new().expect("JIT is supported on all octez-riscv supported platforms") - } -} - -#[cfg(test)] -mod tests { - use std::ptr::null; - - use Instruction as I; - - use super::*; - use crate::backend_test; - use crate::instruction_context::LoadStoreWidth; - use crate::machine_state::MachineCoreState; - use crate::machine_state::block_cache::block::Block; - use crate::machine_state::block_cache::block::Interpreted; - use crate::machine_state::block_cache::block::InterpretedBlockBuilder; - use crate::machine_state::memory::M4K; - use crate::machine_state::memory::Memory; - use crate::machine_state::memory::MemoryConfig; - use crate::machine_state::registers::NonZeroXRegister; - use crate::machine_state::registers::XRegister; - use crate::machine_state::registers::nz; - use crate::parser::instruction::InstrWidth; - use crate::parser::instruction::InstrWidth::*; - use crate::state::NewState; - use crate::state_backend::FnManagerIdent; - use crate::state_backend::ManagerRead; - use crate::state_backend::test_helpers::TestBackendFactory; - use crate::state_backend::test_helpers::assert_eq_struct; - - fn instructions(block: &Interpreted) -> Vec - where - M: ManagerRead, - { - let instr = block.instr(); - instr.iter().map(|cell| cell.read_stored()).collect() - } - - type SetupHook = dyn Fn(&mut MachineCoreState::Manager>); - type AssertHook = dyn Fn(&MachineCoreState::Manager>); - - struct Scenario { - initial_pc: Option, - expected_steps: Option, - instructions: Vec, - setup_hook: Option>>, - assert_hook: Option>>, - } - - impl Scenario { - fn simple(instructions: &[Instruction]) -> Self { - Scenario { - initial_pc: None, - expected_steps: None, - instructions: instructions.to_vec(), - setup_hook: None, - assert_hook: None, - } - } - - /// Run a test scenario over both the Interpreted & JIT modes of compilation, - /// to ensure they behave identically. - fn run( - &self, - jit: &mut JIT, - interpreted_bb: &mut InterpretedBlockBuilder, - ) { - // Create the states for the interpreted and jitted runs. - let mut manager = F::manager(); - let mut interpreted = MachineCoreState::::new(&mut manager); - interpreted.main_memory.set_all_readable_writeable(); - - let mut jitted = MachineCoreState::::new(&mut manager); - jitted.main_memory.set_all_readable_writeable(); - - // Create the block of instructions. - let mut block = Interpreted::::new(&mut manager); - block.start_block(); - for instr in self.instructions.iter() { - block.push_instr(*instr); - } - - // Run the setup hooks. - if let Some(hook) = &self.setup_hook { - (hook)(&mut interpreted); - (hook)(&mut jitted) - } - - // initialise starting parameters: pc, steps - let initial_pc = self.initial_pc.unwrap_or_default(); - let mut interpreted_steps = 0; - let mut jitted_steps = 0; - interpreted.hart.pc.write(initial_pc); - jitted.hart.pc.write(initial_pc); - - // Create the JIT function. - let fun = jit - .compile(instructions(&block).as_slice()) - .expect("Compilation of block should succeed."); - - // Run the block in both interpreted and jitted mode. - let interpreted_res = unsafe { - // SAFETY: interpreted blocks are always callable - block.run_block( - &mut interpreted, - initial_pc, - &mut interpreted_steps, - interpreted_bb, - ) - }; - - let mut jitted_res = Ok(()); - unsafe { - // # Safety - the block builder is alive for at least - // the duration of the `run` function. - (fun)( - null(), - &mut jitted, - initial_pc, - &mut jitted_steps, - &mut jitted_res, - null(), - ) - }; - - // Assert state equality. - assert_eq!(jitted_res, interpreted_res); - assert_eq!( - interpreted_steps, jitted_steps, - "Interpreted mode ran for {interpreted_steps}, compared to jit-mode of {jitted_steps}" - ); - assert_eq_struct( - &interpreted.struct_ref::(), - &jitted.struct_ref::(), - ); - - // Only check steps against one state, as we know both interpreted/jit steps are equal. - let expected_steps = self.expected_steps.unwrap_or(self.instructions.len()); - assert_eq!( - interpreted_steps, expected_steps, - "Scenario ran for {interpreted_steps} steps, but expected {expected_steps}" - ); - - // Run the assert hooks. Since we have already verified that the states are equal, - // we can run the assert hooks on just the interpreted state. - if let Some(hook) = &self.assert_hook { - (hook)(&mut interpreted); - } - } - } - - /// A builder for creating scenarios. - struct ScenarioBuilder { - initial_pc: Option, - expected_steps: Option, - instructions: Vec, - setup_hook: Option>>, - assert_hook: Option>>, - } - - impl Default for ScenarioBuilder { - fn default() -> Self { - ScenarioBuilder { - initial_pc: None, - expected_steps: None, - instructions: Vec::new(), - setup_hook: None, - assert_hook: None, - } - } - } - - impl ScenarioBuilder { - fn set_instructions(mut self, instructions: &[Instruction]) -> Self { - self.instructions = instructions.to_vec(); - self - } - - fn set_initial_pc(mut self, initial_pc: u64) -> Self { - self.initial_pc = Some(initial_pc); - self - } - - fn set_expected_steps(mut self, expected_steps: usize) -> Self { - self.expected_steps = Some(expected_steps); - self - } - - fn set_assert_hook(mut self, assert_hook: Box>) -> Self { - self.assert_hook = Some(assert_hook); - self - } - - fn set_setup_hook(mut self, setup_hook: Box>) -> Self { - self.setup_hook = Some(setup_hook); - self - } - - fn build(self) -> Scenario { - Scenario { - initial_pc: self.initial_pc, - expected_steps: self.expected_steps, - instructions: self.instructions, - setup_hook: self.setup_hook, - assert_hook: self.assert_hook, - } - } - } - - macro_rules! setup_hook { - ($core:ident, $F:ident, $block:expr) => { - Box::new(move |$core: &mut MachineCoreState| $block) - }; - } - - macro_rules! assert_hook { - ($core:ident, $F:ident, $block:expr) => { - Box::new(move |$core: &MachineCoreState| $block) - }; - } - - backend_test!(test_cnop, F, { - let scenarios: &[Scenario] = &[ - Scenario::simple(&[I::new_nop(Compressed)]), - Scenario::simple(&[I::new_nop(Compressed), I::new_nop(Uncompressed)]), - Scenario::simple(&[ - I::new_nop(Uncompressed), - I::new_nop(Compressed), - I::new_nop(Uncompressed), - ]), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_cmv, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x2_is_one = assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 1); - }); - - // Arrange - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[I::new_li(x1, 1, Compressed), I::new_mv(x2, x1, Compressed)]) - .set_assert_hook(assert_x2_is_one.clone()) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 1, Uncompressed), - I::new_mv(x2, x1, Uncompressed), - ]) - .set_assert_hook(assert_x2_is_one.clone()) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 1, Compressed), - I::new_mv(x2, x1, Compressed), - I::new_mv(x3, x2, Compressed), - ]) - .set_assert_hook(assert_x2_is_one) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_negate, F, { - use Instruction as I; - - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_x2_equal = assert_hook!(core, F, { - assert_eq!( - core.hart.xregisters.read_nz(x1), - core.hart.xregisters.read_nz(x2) - ); - }); - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, -1, Compressed), - I::new_li(x3, 1, Compressed), - I::new_neg(x2, x3, Compressed), - ]) - .set_assert_hook(assert_x1_x2_equal.clone()) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 1, Uncompressed), - I::new_neg(x3, x1, Uncompressed), - I::new_neg(x2, x3, Compressed), - ]) - .set_assert_hook(assert_x1_x2_equal) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, i64::MIN, Uncompressed), - I::new_neg(x2, x1, Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), i64::MIN as u64); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_add, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_is_five = assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), 5); - }); - - let scenario: Scenario = ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 1, Uncompressed), - I::new_add(x2, x2, x1, Compressed), - I::new_add(x1, x1, x2, Uncompressed), - I::new_add(x2, x2, x1, Uncompressed), - I::new_add(x1, x1, x2, Compressed), - ]) - .set_assert_hook(assert_x1_is_five) - .build(); - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - scenario.run(&mut jit, &mut interpreted_bb); - }); - - backend_test!(test_add_word, F, { - use Instruction as I; - - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::nz; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, 10, Uncompressed), - I::new_li(nz::a1, 1, Compressed), - I::new_add_word(nz::a2, a0, a1, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a2), 11); - })) - .build(), - // Test that we wrap around and truncate before sign extending. This - // operation 0xFFFFFFFF + 0xFFFFFFFF should produce a different result - // for 32-bit (truncated sum with sign extension) vs 64-bit operations. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, 0xFFFFFFFF, Compressed), - I::new_li(nz::a1, 0xFFFFFFFF, Uncompressed), - I::new_add_word(nz::a2, a0, a1, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - // In 32-bit addition: - // 0xFFFFFFFF + 0xFFFFFFFF = 0x1FFFFFFFE - // Truncated to 32 bits: 0xFFFFFFFE - // Sign extended to 64 bits: 0xFFFFFFFFFFFFFFFE - assert_eq!(core.hart.xregisters.read_nz(nz::a2), -2i64 as u64); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_add_word_i, F, { - use Instruction as I; - - use crate::machine_state::registers::a0; - use crate::machine_state::registers::nz; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, 10, Uncompressed), - I::new_add_word_immediate(nz::a1, a0, 1_i64, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a1), 11); - })) - .build(), - // Test that we wrap around and truncate before sign extending. This - // operation 0xFFFFFFFF + 0xFFFFFFFF should produce a different result - // for 32-bit (truncated sum with sign extension) vs 64-bit operations. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, 0xFFFFFFFF, Compressed), - I::new_add_word_immediate(nz::a1, a0, 0xFFFFFFFF_i64, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - // In 32-bit addition: - // 0xFFFFFFFF + 0xFFFFFFFF = 0x1FFFFFFFE - // Truncated to 32 bits: 0xFFFFFFFE - // Sign extended to 64 bits: 0xFFFFFFFFFFFFFFFE - assert_eq!(core.hart.xregisters.read_nz(nz::a1), -2i64 as u64); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_sub, F, { - use Instruction as I; - - use crate::machine_state::registers::NonZeroXRegister::*; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 10, Uncompressed), - I::new_sub(x2, x1, x1, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 0); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 10, Compressed), - I::new_li(x3, -10, Uncompressed), - I::new_sub(x2, x1, x3, Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 20); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 10, Uncompressed), - I::new_li(x3, 100, Compressed), - I::new_sub(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), (-90_i64) as u64); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_sub_word, F, { - use Instruction as I; - - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::nz; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, 10, Uncompressed), - I::new_li(nz::a1, 1, Compressed), - I::new_sub_word(nz::a2, a0, a1, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a2), 9); - })) - .build(), - // Test that we wrap around 0 and truncate before sign extending. This - // operation 0xFFFFFFFFFFFFFFFF - 0xFFFFFFFF00000000 should produce a - // different result for 32-bit (all 1s) and 64-bit operations (only lower 32-bits - // as 1s). - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, !0, Compressed), - I::new_li(nz::a1, 0xFFFFFFFF00000000u64 as i64, Uncompressed), - I::new_sub_word(nz::a2, a0, a1, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a2), !0); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_and, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_and_x2_equal = assert_hook!(core, F, { - assert_eq!( - core.hart.xregisters.read_nz(x1), - core.hart.xregisters.read_nz(x2) - ); - }); - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - // Bitwise and with all ones is self. - .set_instructions(&[ - I::new_li(x1, 13872, Uncompressed), - I::new_li(x3, !0, Compressed), - I::new_and(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - ScenarioBuilder::default() - // Bitwise and with itself is self. - .set_instructions(&[ - I::new_li(x1, 49666, Uncompressed), - I::new_and(x2, x1, x1, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - ScenarioBuilder::default() - // Bitwise and with 0 is 0. - .set_instructions(&[ - I::new_li(x1, 0, Uncompressed), - I::new_li(x3, 540921, Compressed), - I::new_and(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_or, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_and_x2_equal = assert_hook!(core, F, { - assert_eq!( - core.hart.xregisters.read_nz(x1), - core.hart.xregisters.read_nz(x2) - ); - }); - - let scenarios: &[Scenario] = &[ - // Bitwise or with all ones is all-ones. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, !0, Uncompressed), - I::new_li(x3, 13872, Compressed), - I::new_or(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - // Bitwise or with itself is self. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 49666, Uncompressed), - I::new_or(x2, x1, x1, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - // Bitwise or with 0 is self. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 540921, Uncompressed), - I::new_li(x3, 0, Compressed), - I::new_or(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 0xF0F0, Compressed), - I::new_x64_or_immediate(x2, x1, 0x0F0F, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 0xFFFF); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 0x0000, Compressed), - I::new_x64_or_immediate(x2, x1, 0x5555, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 0x5555); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_x64_mul, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 5, Uncompressed), - I::new_li(x3, 10, Compressed), - I::new_mul(x2, x1, x3, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), 50); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, !0, Compressed), - I::new_mul(x2, x1, x1, Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - core.hart.xregisters.read_nz(x2), - u64::MAX.wrapping_mul(u64::MAX) - ); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, -20, Compressed), - I::new_li(x3, 40, Uncompressed), - I::new_mul(x2, x1, x3, Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), -800i64 as u64); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_x32_mul, F, { - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - - let test_x32_mul = |value1: i64, - value2: i64, - expected_result: u64, - instruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, value1, instruction_width), - I::new_li(nz::a1, value2, instruction_width), - I::new_x32_mul(a2, a0, a1, instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a2), expected_result); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - test_x32_mul(10, 5, 50, Uncompressed), - // Test that we truncate to 32 bits before sign extending - // 2^32 * 2 = 2^33, but truncated to 32 bits = 0 - test_x32_mul(0x1_0000_0000, 2, 0, Compressed), - // Test with negative numbers - // -10 * 5 = -50, sign extended to 64 bits - test_x32_mul(-10, 5, -50i64 as u64, Compressed), - // Test with large 32-bit values that overflow - // INT32_MAX * 2 = 0xFFFFFFFE (truncated to 32 bits) - // Sign extended to 64 bits: 0xFFFFFFFFFFFFFFFE - test_x32_mul(0x7FFFFFFF, 2, -2i64 as u64, Compressed), - // Test with values that would produce different results in 32-bit vs 64-bit multiplication - // In 32-bit: INT32_MIN * INT32_MIN = 0 (truncated) - // In 64-bit: would be 0x4000000000000000 - test_x32_mul(0x80000000, 0x80000000, 0, Compressed), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_div_jit, F, { - use crate::machine_state::registers::nz; - use crate::machine_state::registers::*; - - let test_division = |numerator: i64, denominator: i64, expected: u64| { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(nz::a0, numerator, Uncompressed), - I::new_li(nz::a1, denominator, Compressed), - I::new_x64_div_signed(nz::a2, a0, a1, Compressed), - I::new_nop(Uncompressed), - ]) - .set_expected_steps(4) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(nz::a2), expected); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // check standard division - test_division(10, 2, 5), - // check signed division. - test_division(10, -2, -5_i64 as u64), - test_division(-10, -2, 5), - // check division by zero - test_division(i64::MAX, 0, -1_i64 as u64), - // check when `val(rs2) == -1` but `val(rs1) != i64::MIN`. - test_division(38294, -1, -38294_i64 as u64), - // check when `val(rs1) == i64::MIN` but `val(rs2) != -1`. - test_division(i64::MIN, i64::MIN, 1), - // check when `val(rs1) == i64::MIN` and `val(rs2) == -1`. - test_division(i64::MIN, -1, i64::MIN as u64), - // check division of a smaller-magnitude number by a larger-magnitude number. - test_division(40, -80, 0), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_j, F, { - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - // Jumping to the next instruction should exit the block - .set_instructions(&[ - I::new_nop(Compressed), - I::new_nop(Compressed), - I::new_j(2, Compressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 6); - })) - .set_expected_steps(3) - .build(), - ScenarioBuilder::default() - // Jump past 0 - in both worlds we should wrap around. - .set_instructions(&[I::new_j(-4, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), u64::MAX - 3); - })) - .set_expected_steps(1) - .build(), - ScenarioBuilder::default() - // Jump past u64::MAX - in both worlds we should wrap around but not - // execute functions past the end of the block (the jump). - .set_instructions(&[ - I::new_nop(Uncompressed), - I::new_nop(Uncompressed), - I::new_j(i64::MAX, Uncompressed), - I::new_nop(Compressed), - I::new_nop(Uncompressed), - ]) - .set_initial_pc((i64::MAX - 5) as u64) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 1); - })) - .set_expected_steps(3) - .build(), - ScenarioBuilder::default() - // jump by nothing - .set_instructions(&[ - I::new_nop(Compressed), - I::new_j(0, Compressed), - I::new_nop(Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 2); - })) - .set_expected_steps(2) - .build(), - ScenarioBuilder::default() - // jumping to start of the block should exit the block in both interpreted and jitted world - .set_instructions(&[ - I::new_nop(Compressed), - I::new_nop(Compressed), - I::new_j(-4, Compressed), - I::new_nop(Uncompressed), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), 0); - })) - .set_expected_steps(3) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_jump_instructions, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let test_jr = |base_reg: NonZeroXRegister, - base_val: i64, - expected_pc: u64, - instruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(base_reg, base_val, instruction_width), - I::new_jr(base_reg, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), expected_pc); - })) - .set_expected_steps(2) - .build() - }; - - let test_jr_imm = |base_reg: NonZeroXRegister, - base_val: i64, - offset: i64, - expected_pc: u64, - instruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(base_reg, base_val, instruction_width), - I::new_jr_imm(base_reg, offset, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), expected_pc); - })) - .set_expected_steps(2) - .build() - }; - - let test_jalr = |base_reg: NonZeroXRegister, - base_val: i64, - rd: NonZeroXRegister, - expected_pc: u64, - expected_rd: u64, - instruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(base_reg, base_val, instruction_width), - I::new_jalr(rd, base_reg, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), expected_pc); - assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); - })) - .set_expected_steps(2) - .build() - }; - - let test_jalr_imm = |base_reg: NonZeroXRegister, - base_val: i64, - offset: i64, - rd: NonZeroXRegister, - expected_pc: u64, - expected_rd: u64, - instruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(base_reg, base_val, instruction_width), - I::new_jalr_imm(rd, base_reg, offset, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), expected_pc); - assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); - })) - .set_expected_steps(2) - .build() - }; - - let test_jalr_absolute = |target: i64, - rd: NonZeroXRegister, - instruction_width: InstrWidth, - expected_rd: u64| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_jalr_absolute(rd, target, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), target as u64); - assert_eq!(core.hart.xregisters.read_nz(rd), expected_rd); - })) - .set_expected_steps(1) - .build() - }; - - let test_j_absolute = |target: i64, instruction_width: InstrWidth| -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[ - I::new_j_absolute(target, instruction_width), - I::new_nop(instruction_width), - ]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), target as u64); - })) - .set_expected_steps(1) - .build() - }; - - let test_jal = |offset: i64, - initial_pc: u64, - expected_pc: u64, - expected_x1: u64, - intruction_width: InstrWidth| - -> Scenario { - ScenarioBuilder::default() - .set_instructions(&[I::new_jal(x1, offset, intruction_width)]) - .set_initial_pc(initial_pc) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.pc.read(), expected_pc); - assert_eq!(core.hart.xregisters.read_nz(x1), expected_x1); - })) - .set_expected_steps(1) - .build() - }; - - let scenarios: &[Scenario] = &[ - // Test jr - test_jr(x2, 10, 10, Compressed), - test_jr(x6, 0, 0, Uncompressed), - // Test jr_imm - test_jr_imm(x2, 10, 10, 20, Compressed), - test_jr_imm(x6, 10, -10, 0, Uncompressed), - // Test jalr - test_jalr(x2, 100_000, x1, 100_000, 8, Uncompressed), - test_jalr(x6, 0, x3, 0, 4, Compressed), - // Test jalr_imm - test_jalr_imm(x1, 10, 10, x2, 20, 4, Compressed), - test_jalr_imm(x1, 1000, -10, x2, 990, 8, Uncompressed), - // Test jalr_absolute - test_jalr_absolute(10, x1, Compressed, 2), - test_jalr_absolute(0, x3, Uncompressed, 4), - // Test j_absolute - test_j_absolute(10, Compressed), - test_j_absolute(0, Uncompressed), - // Test jal - test_jal(10, 0, 10, 2, Compressed), - test_jal(-10, 10, 0, 12, Compressed), - test_jal(1000, 1000, 2000, 1004, Uncompressed), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_addi, F, { - use Instruction as I; - - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_is_five = assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), 5); - }); - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_instructions(&[ - I::new_addi(x1, x1, 2, Compressed), - I::new_addi(x1, x1, 3, Uncompressed), - ]) - .set_assert_hook(assert_x1_is_five.clone()) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_addi(x1, x1, i64::MAX, Compressed), - I::new_addi(x1, x1, i64::MAX, Compressed), - I::new_addi(x1, x1, 7, Uncompressed), - ]) - .set_assert_hook(assert_x1_is_five.clone()) - .build(), - ScenarioBuilder::default() - .set_instructions(&[ - I::new_addi(x1, x3, 7, Compressed), - I::new_addi(x1, x1, -2, Uncompressed), - ]) - .set_assert_hook(assert_x1_is_five) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_andi, F, { - use Instruction as I; - - use crate::machine_state::registers::NonZeroXRegister::*; - - let assert_x1_and_x2_equal = assert_hook!(core, F, { - assert_eq!( - core.hart.xregisters.read_nz(x1), - core.hart.xregisters.read_nz(x2) - ); - }); - - let scenarios: &[Scenario] = &[ - // Bitwise and with all ones is self. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 13872, Uncompressed), - I::new_andi(x2, x1, !0, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - // Bitwise and with itself is self. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 49666, Uncompressed), - I::new_andi(x2, x1, 49666, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal.clone()) - .build(), - // Bitwise and with 0 is 0. - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(x1, 0, Uncompressed), - I::new_andi(x2, x1, 50230, Compressed), - ]) - .set_assert_hook(assert_x1_and_x2_equal) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_set_less_than, F, { - use crate::machine_state::registers::XRegister::*; - - const TRUE: u64 = 1; - const FALSE: u64 = 0; - - let test_slt = |constructor: fn(NonZeroXRegister, XRegister, XRegister) -> I, - lhs: (XRegister, i64), - rhs: (XRegister, i64), - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write(lhs.0, lhs.1 as u64); - core.hart.xregisters.write(rhs.0, rhs.1 as u64); - })) - .set_instructions(&[constructor(nz::ra, lhs.0, rhs.0)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected, - core.hart.xregisters.read_nz(nz::ra), - "Expected {expected} for Slt* lhs: {lhs:?}, rhs: {rhs:?}" - ) - })) - .build() - }; - - let test_slt_imm = |constructor: fn(NonZeroXRegister, XRegister, i64) -> I, - lhs: (XRegister, i64), - rhs: i64, - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write(lhs.0, lhs.1 as u64); - })) - .set_instructions(&[constructor(nz::ra, lhs.0, rhs)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected, - core.hart.xregisters.read_nz(nz::ra), - "Expected {expected} for Slt* lhs: {lhs:?}, rhs: {rhs:?}" - ) - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // ------------------------- - // equal values always false - // ------------------------- - // Slt - test_slt(I::new_set_less_than_signed, (x1, 1), (x2, 1), FALSE), - test_slt(I::new_set_less_than_signed, (x0, 1), (x2, 0), FALSE), - test_slt(I::new_set_less_than_signed, (x3, -1), (x2, -1), FALSE), - // Sltu - test_slt(I::new_set_less_than_unsigned, (x1, 1), (x2, 1), FALSE), - test_slt(I::new_set_less_than_unsigned, (x0, 1), (x2, 0), FALSE), - test_slt(I::new_set_less_than_unsigned, (x3, -1), (x2, -1), FALSE), - // Slti - test_slt_imm(I::new_set_less_than_immediate_signed, (x1, 1), 1, FALSE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x0, 1), 0, FALSE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x3, -1), -1, FALSE), - // Sltiu - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x1, 1), 1, FALSE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x0, 1), 0, FALSE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x3, -1), -1, FALSE), - // -------------------------------- - // greater than values always false - // -------------------------------- - // Slt - test_slt(I::new_set_less_than_signed, (x1, 3), (x2, 1), FALSE), - test_slt(I::new_set_less_than_signed, (x0, 0), (x2, -2), FALSE), - test_slt(I::new_set_less_than_signed, (x3, -1), (x2, -5), FALSE), - // Sltu - test_slt(I::new_set_less_than_unsigned, (x1, 1), (x2, 1), FALSE), - test_slt(I::new_set_less_than_unsigned, (x2, 5), (x0, 0), FALSE), - test_slt(I::new_set_less_than_unsigned, (x3, -1), (x2, 2), FALSE), - // Slti - test_slt_imm(I::new_set_less_than_immediate_signed, (x1, 2), 1, FALSE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x5, 1), 0, FALSE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x3, -5), -6, FALSE), - // Sltiu - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x1, 5), 1, FALSE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x3, -1), 15, FALSE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x3, -1), -6, FALSE), - // ---------------------------- - // less than values always true - // ---------------------------- - // Slt - test_slt(I::new_set_less_than_signed, (x1, 2), (x2, 5), TRUE), - test_slt(I::new_set_less_than_signed, (x0, 0), (x2, 3), TRUE), - test_slt(I::new_set_less_than_signed, (x3, -5), (x2, -3), TRUE), - // Sltu - test_slt(I::new_set_less_than_unsigned, (x1, 1), (x2, -1), TRUE), - test_slt(I::new_set_less_than_unsigned, (x0, 0), (x3, 5), TRUE), - test_slt(I::new_set_less_than_unsigned, (x3, -2), (x2, -1), TRUE), - // Slti - test_slt_imm(I::new_set_less_than_immediate_signed, (x1, 2), 5, TRUE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x5, 0), 3, TRUE), - test_slt_imm(I::new_set_less_than_immediate_signed, (x3, -6), -5, TRUE), - // Sltiu - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x1, 3), 5, TRUE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x3, 5), -15, TRUE), - test_slt_imm(I::new_set_less_than_immediate_unsigned, (x3, -7), -6, TRUE), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_branch, F, { - let test_branch = - |non_branch: fn(NonZeroXRegister, NonZeroXRegister, i64, InstrWidth) -> I, - branch: fn(NonZeroXRegister, NonZeroXRegister, i64, InstrWidth) -> I, - lhs: i64, - rhs: i64| - -> Scenario { - let initial_pc: u64 = 0x1000; - let imm: i64 = -0x2000; - let expected_pc_branch = initial_pc.wrapping_add(imm as u64).wrapping_add(8); - - ScenarioBuilder::default() - .set_initial_pc(initial_pc) - .set_instructions(&[ - I::new_li(nz::a1, lhs, InstrWidth::Compressed), - I::new_li(nz::a2, rhs, InstrWidth::Compressed), - non_branch(nz::a1, nz::a2, imm, InstrWidth::Uncompressed), - branch(nz::a1, nz::a2, imm, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(4) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected_pc_branch, - core.hart.pc.read(), - "Expected {expected_pc_branch} pc for B*Zero cmp {lhs}, {rhs}" - ) - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // Equality - test_branch(I::new_branch_equal, I::new_branch_not_equal, 2, 3), - test_branch(I::new_branch_not_equal, I::new_branch_equal, 2, 2), - test_branch(I::new_branch_equal, I::new_branch_not_equal, 2, -3), - // LessThanUnsigned + GreaterThanOrEqualUnsigned - test_branch( - I::new_branch_less_than_unsigned, - I::new_branch_greater_than_or_equal_unsigned, - 3, - 2, - ), - test_branch( - I::new_branch_less_than_unsigned, - I::new_branch_greater_than_or_equal_unsigned, - 2, - 2, - ), - test_branch( - I::new_branch_greater_than_or_equal_unsigned, - I::new_branch_less_than_unsigned, - 2, - -3, - ), - // LessThanSigned + GreaterThanOrEqualSigned - test_branch( - I::new_branch_less_than_signed, - I::new_branch_greater_than_or_equal_signed, - 3, - 2, - ), - test_branch( - I::new_branch_less_than_signed, - I::new_branch_greater_than_or_equal_signed, - 2, - 2, - ), - test_branch( - I::new_branch_less_than_signed, - I::new_branch_greater_than_or_equal_signed, - 2, - -3, - ), - test_branch( - I::new_branch_greater_than_or_equal_signed, - I::new_branch_less_than_signed, - -4, - -3, - ), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_branch_compare_zero, F, { - let test_branch_compare_zero = |non_branch: fn(NonZeroXRegister, i64, InstrWidth) -> I, - branch: fn(NonZeroXRegister, i64, InstrWidth) -> I, - val: i64| - -> Scenario { - let initial_pc: u64 = 0x1000; - let imm: i64 = 0x2000; - let expected_pc_branch = initial_pc + imm as u64 + 4; - - ScenarioBuilder::default() - .set_initial_pc(initial_pc) - .set_instructions(&[ - I::new_li(nz::ra, val, InstrWidth::Compressed), - non_branch(nz::ra, imm, InstrWidth::Compressed), - branch(nz::ra, imm, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(3) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected_pc_branch, - core.hart.pc.read(), - "Expected {expected_pc_branch} pc for B*Zero cmp {val:?}" - ) - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // Equality - test_branch_compare_zero(I::new_branch_equal_zero, I::new_branch_not_equal_zero, 12), - test_branch_compare_zero(I::new_branch_not_equal_zero, I::new_branch_equal_zero, 0), - test_branch_compare_zero(I::new_branch_equal_zero, I::new_branch_not_equal_zero, -12), - // LessThan + GreaterThanOrEqual - test_branch_compare_zero( - I::new_branch_less_than_zero, - I::new_branch_greater_than_or_equal_zero, - 12, - ), - test_branch_compare_zero( - I::new_branch_less_than_zero, - I::new_branch_greater_than_or_equal_zero, - 0, - ), - test_branch_compare_zero( - I::new_branch_greater_than_or_equal_zero, - I::new_branch_less_than_zero, - -12, - ), - // LessThanOrEqual + GreaterThan - test_branch_compare_zero( - I::new_branch_less_than_or_equal_zero, - I::new_branch_greater_than_zero, - 12, - ), - test_branch_compare_zero( - I::new_branch_greater_than_zero, - I::new_branch_less_than_or_equal_zero, - 0, - ), - test_branch_compare_zero( - I::new_branch_greater_than_zero, - I::new_branch_less_than_or_equal_zero, - -12, - ), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_unknown, F, { - let scenarios: &[Scenario] = &[ScenarioBuilder::default() - .set_expected_steps(2) - .set_instructions(&[ - I::new_nop(Uncompressed), - I::new_unknown(Compressed), - I::new_nop(Uncompressed), - ]) - .build()]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_ecall, F, { - let scenario: Scenario = ScenarioBuilder::default() - .set_expected_steps(1) - .set_instructions(&[ - I::new_nop(Uncompressed), - I::new_ecall(), - I::new_nop(Uncompressed), - ]) - .build(); - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - scenario.run(&mut jit, &mut interpreted_bb); - }); - - backend_test!(test_jit_recovers_from_compilation_failure, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - // Arrange - let failure_scenarios: &[&[I]] = &[ - &[ - // does not currently support lowering. - I::new_fadds(x1, x1, x1, Uncompressed), - ], - &[ - I::new_nop(Uncompressed), - // does not currently support lowering. - I::new_fadds(x1, x1, x1, Uncompressed), - ], - ]; - - let success: &[I] = &[I::new_nop(Compressed)]; - - let mut manager = F::manager(); - - for failure in failure_scenarios.iter() { - let mut jit = JIT::::new().unwrap(); - - let mut jitted = MachineCoreState::::new(&mut manager); - let mut block = Interpreted::::new(&mut manager); - - block.start_block(); - for instr in failure.iter() { - block.push_instr(*instr); - } - - let mut jitted_steps = 0; - - let initial_pc = 0; - jitted.hart.pc.write(initial_pc); - - jitted.hart.xregisters.write_nz(x1, 1); - - // Act - let res = jit.compile(instructions(&block).as_slice()); - - assert!( - res.is_none(), - "Compilation of unsupported instruction should fail" - ); - - block.start_block(); - for instr in success.iter() { - block.push_instr(*instr); - } - - let fun = jit - .compile(instructions(&block).as_slice()) - .expect("Compilation of subsequent functions should succeed"); - - let mut jitted_res = Ok(()); - unsafe { - // # Safety - the jit is not dropped until after we - // exit the block. - (fun)( - null(), - &mut jitted, - initial_pc, - &mut jitted_steps, - &mut jitted_res, - null(), - ) - }; - - assert!(jitted_res.is_ok()); - assert_eq!(jitted_steps, success.len()); - } - }); - - backend_test!(test_add_immediate_to_pc, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let scenarios: &[Scenario] = &[ - ScenarioBuilder::default() - .set_initial_pc(1000) - .set_instructions(&[I::new_add_immediate_to_pc(x1, 4096, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), 5096); - })) - .build(), - ScenarioBuilder::default() - .set_instructions(&[I::new_add_immediate_to_pc(x1, 0xFFFFF000, Uncompressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), 0xFFFFF000); - })) - .build(), - ScenarioBuilder::default() - .set_initial_pc(1000) - .set_instructions(&[I::new_add_immediate_to_pc(x1, -4096, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), -3096_i64 as u64); - })) - .build(), - ScenarioBuilder::default() - .set_initial_pc(1000) - .set_instructions(&[I::new_add_immediate_to_pc(x1, 20, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x1), 1020); - })) - .build(), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_shift_reg, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let shift_reg = |constructor: fn( - NonZeroXRegister, - NonZeroXRegister, - NonZeroXRegister, - InstrWidth, - ) -> I, - lhs: (NonZeroXRegister, i64), - rhs: (NonZeroXRegister, i64), - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write_nz(lhs.0, lhs.1 as u64); - core.hart.xregisters.write_nz(rhs.0, rhs.1 as u64); - })) - .set_instructions(&[constructor(x2, lhs.0, rhs.0, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected, - core.hart.xregisters.read_nz(x2), - "Expected {expected} for Shift* lhs: {lhs:?}, rhs: {rhs:?}" - ) - })) - .build() - }; - - let shift_reg_word = - |constructor: fn(NonZeroXRegister, XRegister, XRegister, InstrWidth) -> I, - lhs: (XRegister, i64), - rhs: (XRegister, i64), - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write(lhs.0, lhs.1 as u64); - core.hart.xregisters.write(rhs.0, rhs.1 as u64); - })) - .set_instructions(&[constructor(x2, lhs.0, rhs.0, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected, - core.hart.xregisters.read_nz(x2), - "Expected {expected} for Shift* lhs: {lhs:?}, rhs: {rhs:?}" - ) - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - shift_reg(I::new_shift_left, (x1, 1), (x3, 1), 2), - shift_reg(I::new_shift_left, (x1, 1), (x3, 63), 0x8000_0000_0000_0000), - shift_reg(I::new_shift_left, (x1, 2), (x3, 63), 0), - shift_reg(I::new_shift_left, (x1, 1), (x3, 126), 0x4000_0000_0000_0000), - shift_reg(I::new_shift_left, (x1, -16), (x3, 2), -64_i64 as u64), - shift_reg(I::new_shift_right_unsigned, (x1, 2), (x3, 1), 1), - shift_reg(I::new_shift_right_unsigned, (x1, !0), (x3, 63), 1), - shift_reg( - I::new_shift_right_unsigned, - (x1, 0x7FFF_FFFF_FFFF_FFFF), - (x3, 63), - 0, - ), - shift_reg(I::new_shift_right_unsigned, (x1, !0), (x3, 126), 3), - shift_reg( - I::new_shift_right_unsigned, - (x1, -8), - (x3, 2), - 0x3FFF_FFFF_FFFF_FFFE, - ), - shift_reg(I::new_shift_right_signed, (x1, 2), (x3, 1), 1), - shift_reg(I::new_shift_right_signed, (x1, !0), (x3, 63), !0), - shift_reg( - I::new_shift_right_signed, - (x1, 0x7FFF_FFFF_FFFF_FFFF), - (x3, 62), - 1, - ), - shift_reg(I::new_shift_right_signed, (x1, !0), (x3, 126), !0), - shift_reg(I::new_shift_right_signed, (x1, -8), (x3, 2), -2_i64 as u64), - // X32ShiftLeft tests - shift_reg_word( - I::new_x32_shift_left, - (XRegister::x1, 1), - (XRegister::x3, 1), - 2, - ), - shift_reg_word( - I::new_x32_shift_left, - (XRegister::x1, 1), - (XRegister::x3, 31), - 0xFFFF_FFFF_8000_0000, - ), - shift_reg_word( - I::new_x32_shift_left, - (XRegister::x1, 2), - (XRegister::x3, 31), - 0, - ), - shift_reg_word( - I::new_x32_shift_left, - (XRegister::x1, 1), - (XRegister::x3, 95), - 0xFFFF_FFFF_8000_0000, - ), - // X32ShiftRightUnsigned tests - shift_reg_word( - I::new_x32_shift_right_unsigned, - (XRegister::x1, 2), - (XRegister::x3, 1), - 1, - ), - shift_reg_word( - I::new_x32_shift_right_unsigned, - (XRegister::x1, 0x80000000), - (XRegister::x3, 31), - 1, - ), - shift_reg_word( - I::new_x32_shift_right_unsigned, - (XRegister::x1, 1), - (XRegister::x3, 31), - 0, - ), - shift_reg_word( - I::new_x32_shift_right_unsigned, - (XRegister::x1, 0x80000000), - (XRegister::x3, 95), - 1, - ), - // X32ShiftRightSigned tests - shift_reg_word( - I::new_x32_shift_right_signed, - (XRegister::x1, 2), - (XRegister::x3, 1), - 1, - ), - shift_reg_word( - I::new_x32_shift_right_signed, - (XRegister::x1, 0x80000000), - (XRegister::x3, 31), - 0xFFFF_FFFF_FFFF_FFFF, - ), - shift_reg_word( - I::new_x32_shift_right_signed, - (XRegister::x1, 0x40000000), - (XRegister::x3, 31), - 0, - ), - shift_reg_word( - I::new_x32_shift_right_signed, - (XRegister::x1, 0x80000000), - (XRegister::x3, 95), - 0xFFFF_FFFF_FFFF_FFFF, - ), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_shift_imm, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let shift_imm = - |constructor: fn(NonZeroXRegister, NonZeroXRegister, i64, InstrWidth) -> I, - lhs: (NonZeroXRegister, i64), - imm: i64, - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write_nz(lhs.0, lhs.1 as u64); - })) - .set_instructions(&[constructor(x2, lhs.0, imm, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!( - expected, - core.hart.xregisters.read_nz(x2), - "Expected {expected} for Shift* lhs: {lhs:?}, imm: {imm}" - ) - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - shift_imm(I::new_shift_left_immediate, (x1, 1), 1, 2), - shift_imm( - I::new_shift_left_immediate, - (x1, 1), - 63, - 0x8000_0000_0000_0000, - ), - shift_imm(I::new_shift_left_immediate, (x1, 2), 63, 0), - shift_imm(I::new_shift_left_immediate, (x1, -16), 2, -64_i64 as u64), - shift_imm(I::new_shift_right_immediate_unsigned, (x1, 2), 1, 1), - shift_imm(I::new_shift_right_immediate_unsigned, (x1, !0), 63, 1), - shift_imm( - I::new_shift_right_immediate_unsigned, - (x1, 0x7FFF_FFFF_FFFF_FFFF), - 63, - 0, - ), - shift_imm( - I::new_shift_right_immediate_unsigned, - (x1, -8), - 2, - 0x3FFF_FFFF_FFFF_FFFE, - ), - shift_imm(I::new_shift_right_immediate_signed, (x1, 2), 1, 1), - shift_imm(I::new_shift_right_immediate_signed, (x1, !0), 63, !0), - shift_imm( - I::new_shift_right_immediate_signed, - (x1, 0x7FFF_FFFF_FFFF_FFFF), - 62, - 1, - ), - shift_imm( - I::new_shift_right_immediate_signed, - (x1, -8), - 2, - -2_i64 as u64, - ), - // X32ShiftLeftImmediate tests - shift_imm(I::new_x32_shift_left_immediate, (x1, 1), 1, 2), - shift_imm( - I::new_x32_shift_left_immediate, - (x1, 1), - 31, - 0xFFFF_FFFF_8000_0000, - ), - shift_imm(I::new_x32_shift_left_immediate, (x1, 2), 31, 0), - // X32ShiftRightImmediateUnsigned tests - shift_imm(I::new_x32_shift_right_immediate_unsigned, (x1, 2), 1, 1), - shift_imm( - I::new_x32_shift_right_immediate_unsigned, - (x1, 0x80000000), - 31, - 1, - ), - shift_imm(I::new_x32_shift_right_immediate_unsigned, (x1, 1), 31, 0), - // X32ShiftRightImmediateSigned tests - shift_imm(I::new_x32_shift_right_immediate_signed, (x1, 2), 1, 1), - shift_imm( - I::new_x32_shift_right_immediate_signed, - (x1, 0x80000000), - 31, - 0xFFFF_FFFF_FFFF_FFFF, - ), - shift_imm( - I::new_x32_shift_right_immediate_signed, - (x1, 0x40000000), - 31, - 0, - ), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_store, F, { - use crate::machine_state::registers::NonZeroXRegister as NZ; - use crate::machine_state::registers::XRegister::*; - - type ConstructStoreFn = - fn(rs1: XRegister, rs2: XRegister, imm: i64, width: InstrWidth) -> I; - - const MEMORY_SIZE: u64 = M4K::TOTAL_BYTES as u64; - const XREG_VALUE: u64 = 0xFFEEDDCCBBAA9988; - - let valid_store = |constructor: ConstructStoreFn, imm: u64, expected: u64| { - const STORE_ADDRESS_BASE: u64 = MEMORY_SIZE / 2; - - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(NZ::x1, STORE_ADDRESS_BASE as i64, InstrWidth::Compressed), - I::new_li(NZ::x2, XREG_VALUE as i64, InstrWidth::Compressed), - constructor(x1, x2, imm as i64, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(4) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.main_memory.read(STORE_ADDRESS_BASE + imm).unwrap(); - - assert_eq!(value, expected, "Found {value:x}, expected {expected:x}"); - })) - .build() - }; - - let invalid_store = |constructor: ConstructStoreFn, width: LoadStoreWidth| { - // an address that, with the immediate of 16, will be out of bounds by one byte - let store_address_base = MEMORY_SIZE - 15 - width as u64; - let store_address_offset = 16; - - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(NZ::x1, store_address_base as i64, InstrWidth::Compressed), - I::new_li(NZ::x2, XREG_VALUE as i64, InstrWidth::Compressed), - constructor( - x1, - x2, - store_address_offset as i64, - InstrWidth::Uncompressed, - ), - I::new_nop(InstrWidth::Compressed), - ]) - // the load will fail due to being out of bounds - .set_expected_steps(3) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.main_memory.read(MEMORY_SIZE - 8).unwrap(); - - assert_eq!(value, 0, "Found {value:x}, but expected store to fail"); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // check stores - differing imm value to ensure both - // aligned & unaligned stores are supported - valid_store(I::new_x64_store, 8, XREG_VALUE), - valid_store(I::new_x64_store, 5, XREG_VALUE), - valid_store(I::new_x32_store, 4, XREG_VALUE as u32 as u64), - valid_store(I::new_x32_store, 3, XREG_VALUE as u32 as u64), - valid_store(I::new_x16_store, 2, XREG_VALUE as u16 as u64), - valid_store(I::new_x16_store, 1, XREG_VALUE as u16 as u64), - // byte load always aligned - valid_store(I::new_x8_store, 0, XREG_VALUE as u8 as u64), - // invalid stores: out of bounds - invalid_store(I::new_x64_store, LoadStoreWidth::Double), - invalid_store(I::new_x32_store, LoadStoreWidth::Word), - invalid_store(I::new_x16_store, LoadStoreWidth::Half), - invalid_store(I::new_x8_store, LoadStoreWidth::Byte), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_load, F, { - use crate::machine_state::registers::NonZeroXRegister as NZ; - use crate::machine_state::registers::XRegister::*; - - type ConstructLoadFn = fn(rd: XRegister, rs1: XRegister, imm: i64, width: InstrWidth) -> I; - - const MEMORY_SIZE: u64 = M4K::TOTAL_BYTES as u64; - - let valid_load = |new_load: ConstructLoadFn, imm: u64, expected: u64| { - const LOAD_ADDRESS_BASE: u64 = MEMORY_SIZE / 2; - - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.main_memory - .write(LOAD_ADDRESS_BASE + imm, expected) - .unwrap(); - })) - .set_instructions(&[ - I::new_li(NZ::x1, LOAD_ADDRESS_BASE as i64, InstrWidth::Compressed), - new_load(x2, x1, imm as i64, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(3) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.hart.xregisters.read(x2); - assert_eq!(value, expected, "Found {value:x}, expected {expected:x}"); - })) - .build() - }; - - let invalid_load = |new_load: ConstructLoadFn, width: LoadStoreWidth| { - // an address that, with the immediate of 16, will be out of bounds by one byte - let load_address_base = MEMORY_SIZE - 15 - width as u64; - let load_address_offset = 16; - - ScenarioBuilder::default() - .set_instructions(&[ - I::new_li(NZ::x1, load_address_base as i64, InstrWidth::Compressed), - new_load(x2, x1, load_address_offset as i64, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - // the load will fail due to being out of bounds - .set_expected_steps(2) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.hart.xregisters.read(x2); - assert_eq!(value, 0, "Found {value:x}, but expected load to fail"); - })) - .build() - }; - - const XREG_VALUE: u64 = 0xFFEEDDCCBBAA9988; - - let scenarios: &[Scenario] = &[ - // check loads - differing imm value to ensure both - // aligned & unaligned loads are supported - valid_load(I::new_x64_load_signed, 8, XREG_VALUE), - valid_load(I::new_x64_load_signed, 5, XREG_VALUE), - valid_load(I::new_x32_load_signed, 4, XREG_VALUE as i32 as u64), - valid_load(I::new_x32_load_unsigned, 4, XREG_VALUE as u32 as u64), - valid_load(I::new_x32_load_signed, 3, XREG_VALUE as i32 as u64), - valid_load(I::new_x32_load_unsigned, 3, XREG_VALUE as u32 as u64), - valid_load(I::new_x16_load_signed, 2, XREG_VALUE as i16 as u64), - valid_load(I::new_x16_load_unsigned, 2, XREG_VALUE as u16 as u64), - valid_load(I::new_x16_load_signed, 1, XREG_VALUE as i16 as u64), - valid_load(I::new_x16_load_unsigned, 1, XREG_VALUE as u16 as u64), - // byte load always aligned - valid_load(I::new_x8_load_signed, 0, XREG_VALUE as i8 as u64), - valid_load(I::new_x8_load_unsigned, 0, XREG_VALUE as u8 as u64), - // invalid loads: out of bounds - invalid_load(I::new_x64_load_signed, LoadStoreWidth::Double), - invalid_load(I::new_x32_load_signed, LoadStoreWidth::Word), - invalid_load(I::new_x32_load_unsigned, LoadStoreWidth::Word), - invalid_load(I::new_x16_load_signed, LoadStoreWidth::Half), - invalid_load(I::new_x16_load_unsigned, LoadStoreWidth::Half), - invalid_load(I::new_x8_load_signed, LoadStoreWidth::Byte), - invalid_load(I::new_x8_load_unsigned, LoadStoreWidth::Byte), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_xor, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let bitwise_test_xor_immediate = - |lhs_reg: NonZeroXRegister, lhs_val: u64, imm: i64, expected: u64| -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write_nz(lhs_reg, lhs_val); - })) - .set_instructions(&[I::new_x64_xor_immediate(x2, lhs_reg, imm, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), expected); - })) - .build() - }; - - let bitwise_test_xor = |lhs_reg: NonZeroXRegister, - lhs_val: u64, - rhs_reg: NonZeroXRegister, - rhs_val: u64, - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write_nz(lhs_reg, lhs_val); - core.hart.xregisters.write_nz(rhs_reg, rhs_val); - })) - .set_instructions(&[I::new_x64_xor(x2, lhs_reg, rhs_reg, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), expected); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // XOR immediate tests - bitwise_test_xor_immediate(x1, 0xF0F0, 0x0F0F, 0xFFFF), - bitwise_test_xor_immediate(x1, 0xAAAA, 0x5555, 0xFFFF), - bitwise_test_xor_immediate(x1, 0xFFF0, 0x0FFF, 0xF00F), - // XOR register tests - bitwise_test_xor(x1, 0xF0F0, x3, 0x0F0F, 0xFFFF), - bitwise_test_xor(x1, 0xAAAA, x3, 0x5555, 0xFFFF), - bitwise_test_xor(x1, 0xFFF0, x3, 0x0FFF, 0xF00F), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_x64_atomic, F, { - use crate::machine_state::registers::NonZeroXRegister as NZ; - use crate::machine_state::registers::XRegister::*; - - type ConstructAtomicFn = fn( - rd: XRegister, - rs1: XRegister, - rs2: XRegister, - aq: bool, - rl: bool, - width: InstrWidth, - ) -> I; - - const MEMORY_SIZE: u64 = M4K::TOTAL_BYTES as u64; - - const ADDRESS_BASE_ATOMICS: u64 = MEMORY_SIZE / 2; - - let valid_x64_atomic = |constructor: ConstructAtomicFn, - val1: u64, - val2: u64, - fun: fn(u64, u64) -> u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.main_memory.write(ADDRESS_BASE_ATOMICS, val1).unwrap(); - })) - .set_instructions(&[ - I::new_li(NZ::x1, ADDRESS_BASE_ATOMICS as i64, InstrWidth::Compressed), - I::new_li(NZ::x2, val2 as i64, InstrWidth::Compressed), - constructor(x3, x1, x2, false, false, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(4) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.hart.xregisters.read(x3); - assert_eq!(value, val1); - - let res: u64 = core.main_memory.read(ADDRESS_BASE_ATOMICS).unwrap(); - let expected = fun(val1, val2); - assert_eq!(res, expected, "Found {value:x}, expected {expected:x}"); - })) - .build() - }; - - let invalid_x64_atomic = |constructor: ConstructAtomicFn, - val1: u64, - val2: u64, - fun: fn(u64, u64) -> u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.main_memory - .write(ADDRESS_BASE_ATOMICS + 4, val1) - .unwrap(); - })) - .set_instructions(&[ - I::new_li( - NZ::x1, - (ADDRESS_BASE_ATOMICS + 4) as i64, - InstrWidth::Compressed, - ), - I::new_li(NZ::x2, val2 as i64, InstrWidth::Compressed), - constructor(x3, x1, x2, false, false, InstrWidth::Uncompressed), - I::new_nop(InstrWidth::Compressed), - ]) - .set_expected_steps(3) - .set_assert_hook(assert_hook!(core, F, { - let value: u64 = core.hart.xregisters.read(x3); - assert_eq!(value, 0); - - let res: u64 = core.main_memory.read(ADDRESS_BASE_ATOMICS + 4).unwrap(); - let expected = fun(val1, val2); - assert_eq!(res, val1, "Found {value:x}, expected {expected:x}"); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - valid_x64_atomic(I::new_x64_atomic_add, 10, 30, u64::wrapping_add), - invalid_x64_atomic(I::new_x64_atomic_add, 10, 30, u64::wrapping_add), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); - - backend_test!(test_mul_high, F, { - use crate::machine_state::registers::NonZeroXRegister::*; - - let test_mul_high = |constructor: fn( - NonZeroXRegister, - NonZeroXRegister, - NonZeroXRegister, - InstrWidth, - ) -> I, - lhs_reg: NonZeroXRegister, - lhs_val: u64, - rhs_reg: NonZeroXRegister, - rhs_val: u64, - expected: u64| - -> Scenario { - ScenarioBuilder::default() - .set_setup_hook(setup_hook!(core, F, { - core.hart.xregisters.write_nz(lhs_reg, lhs_val); - core.hart.xregisters.write_nz(rhs_reg, rhs_val); - })) - .set_instructions(&[constructor(x2, lhs_reg, rhs_reg, Compressed)]) - .set_assert_hook(assert_hook!(core, F, { - assert_eq!(core.hart.xregisters.read_nz(x2), expected); - })) - .build() - }; - - let scenarios: &[Scenario] = &[ - // MULH (Signed × Signed) - test_mul_high( - I::new_x64_mul_high_signed, - x1, - i64::MAX as u64, - x3, - i64::MAX as u64, - (((i64::MAX as i128) * (i64::MAX as i128)) >> 64) as u64, - ), - test_mul_high( - I::new_x64_mul_high_signed, - x1, - i64::MIN as u64, - x3, - i64::MIN as u64, - (((i64::MIN as i128) * (i64::MIN as i128)) >> 64) as u64, - ), - test_mul_high( - I::new_x64_mul_high_signed, - x1, - i64::MIN as u64, - x3, - i64::MAX as u64, - (((i64::MIN as i128) * (i64::MAX as i128)) >> 64) as u64, - ), - // MULHSU (Signed × Unsigned) - test_mul_high( - I::new_x64_mul_high_signed_unsigned, - x1, - i64::MAX as u64, - x3, - u64::MAX, - (((i64::MAX as i128) * (u64::MAX as u128) as i128) >> 64) as u64, - ), - test_mul_high( - I::new_x64_mul_high_signed_unsigned, - x1, - i64::MIN as u64, - x3, - u64::MAX, - (((i64::MIN as i128) * (u64::MAX as u128) as i128) >> 64) as u64, - ), - test_mul_high( - I::new_x64_mul_high_signed_unsigned, - x1, - -1i64 as u64, - x3, - u64::MAX, - (-((u64::MAX as u128) as i128) >> 64) as u64, - ), - // MULHU (Unsigned × Unsigned) - test_mul_high( - I::new_x64_mul_high_unsigned, - x1, - u64::MAX, - x3, - u64::MAX, - (((u64::MAX as u128) * (u64::MAX as u128)) >> 64) as u64, - ), - test_mul_high( - I::new_x64_mul_high_unsigned, - x1, - i64::MIN as u64, - x3, - 2u64, - (((i64::MIN as u64 as u128) * (2u128)) >> 64) as u64, - ), - test_mul_high(I::new_x64_mul_high_unsigned, x1, 0u64, x3, u64::MAX, 0u64), - ]; - - let mut jit = JIT::::new().unwrap(); - let mut interpreted_bb = InterpretedBlockBuilder; - - for scenario in scenarios { - scenario.run(&mut jit, &mut interpreted_bb); - } - }); -} diff --git a/src/riscv/lib/src/jit/builder.rs b/src/riscv/lib/src/jit/builder.rs deleted file mode 100644 index 11a3d041db6e69e63d83bcbd385484e9ae72c963..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/builder.rs +++ /dev/null @@ -1,532 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Builder for turning [instructions] into functions. -//! -//! [instructions]: crate::machine_state::instruction::Instruction - -pub(super) mod arithmetic; -pub(super) mod block_state; -pub(super) mod comparable; -pub(super) mod errno; - -use cranelift::codegen::ir; -use cranelift::codegen::ir::InstBuilder; -use cranelift::codegen::ir::MemFlags; -use cranelift::codegen::ir::Type; -use cranelift::codegen::ir::Value; -use cranelift::codegen::ir::condcodes::IntCC; -use cranelift::codegen::ir::types::I32; -use cranelift::codegen::ir::types::I64; -use cranelift::frontend::FunctionBuilder; -use cranelift::prelude::types::I128; -use errno::AtomicAccessGuard; - -use self::block_state::DynamicValues; -use self::errno::Errno; -use super::state_access::JitStateAccess; -use super::state_access::JsaCalls; -use crate::instruction_context::ICB; -use crate::instruction_context::LoadStoreWidth; -use crate::instruction_context::MulHighType; -use crate::instruction_context::PhiValue; -use crate::instruction_context::Predicate; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::instruction_context::comparable::Comparable; -use crate::jit::builder::block_state::PCUpdate; -use crate::machine_state::ProgramCounterUpdate; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::NonZeroXRegister; -use crate::parser::instruction::InstrWidth; - -/// A newtype for wrapping [`Value`], representing a 64-bit value in the JIT context. -#[derive(Copy, Clone, Debug)] -pub struct X64(pub Value); - -/// A newtype for wrapping [`Value`], representing a 32-bit value in the JIT context. -#[derive(Copy, Clone, Debug)] -pub struct X32(pub Value); - -/// Builder context used when lowering individual instructions within a block. -pub(crate) struct Builder<'a, MC: MemoryConfig, JSA: JitStateAccess> { - /// Cranelift function builder - builder: FunctionBuilder<'a>, - - /// Helpers for calling locally imported [JitStateAccess] methods. - jsa_call: JsaCalls<'a, MC, JSA>, - - /// The IR-type of pointers on the current native platform - ptr: Type, - - /// Value representing a pointer to [`MachineCoreState`] - /// - /// [`MachineCoreState`]: crate::machine_state::MachineCoreState - core_ptr_val: Value, - - /// Value representing a pointer to `steps: usize` - steps_ptr_val: Value, - - /// Values that are dynamically updated throughout lowering. - dynamic: DynamicValues, - - /// The final Cranelift-IR block that is last executed on exit from a JIT-compiled - /// block cache block. - /// - /// It is responsible for writing the final values of `steps` and the `instr_pc` back to the state. - /// *N.B.* the end block can be jumped-to from multiple places, for example by every branching - /// point, and also once all instructions have been executed--if no branching took place. - end_block: Option, - - /// Value representing a pointer to `result: Result<(), EnvironException>` - result_ptr_val: Value, -} - -impl<'a, MC: MemoryConfig, JSA: JitStateAccess> Builder<'a, MC, JSA> { - /// Create a new block builder. - /// - /// The function constructed after compilation takes - /// `core_ptr`, `instr_pc` & `steps_ptr` as arguments. - pub(super) fn new( - ptr: ir::Type, - mut builder: FunctionBuilder<'a>, - jsa_call: JsaCalls<'a, MC, JSA>, - ) -> Self { - // Create the entry block, to start emitting code in. - let entry_block = builder.create_block(); - builder.append_block_params_for_function_params(entry_block); - builder.switch_to_block(entry_block); - builder.seal_block(entry_block); - - // first param ignored - let core_ptr_val = builder.block_params(entry_block)[1]; - let pc_val = X64(builder.block_params(entry_block)[2]); - let steps_ptr_val = builder.block_params(entry_block)[3]; - let result_ptr_val = builder.block_params(entry_block)[4]; - // last param ignored - - Self { - ptr, - builder, - jsa_call, - core_ptr_val, - steps_ptr_val, - result_ptr_val, - dynamic: DynamicValues::new(pc_val), - end_block: None, - } - } - - /// Construct the end block - which writes the updated `pc` and `steps` - /// back to the state. - /// - /// Since the end block can be jumped to from multiple places, it takes - /// `pc` and `steps` as dynamic-parameters. These are provided by the caller. - fn finalise_end_block(&mut self, end_block: ir::Block) { - if self.end_block.is_some() { - return; - } - - self.builder.switch_to_block(end_block); - // We will pass the instr_pc & steps as parameters - let pc_val = self.builder.append_block_param(end_block, I64); - let steps_val = self.builder.append_block_param(end_block, I64); - - // write steps back to the `steps: &mut usize` reference. - self.builder - .ins() - .store(MemFlags::trusted(), steps_val, self.steps_ptr_val, 0); - - // write the final pc to the state. - self.jsa_call - .pc_write(&mut self.builder, self.core_ptr_val, X64(pc_val)); - - self.builder.ins().return_(&[]); - - self.end_block = Some(end_block); - } - - /// Consume the builder, allowing for the function under construction to be [`finalised`]. - /// - /// [`finalised`]: super::JIT::finalise - pub(super) fn end(mut self) { - self.jump_to_end(); - self.builder.seal_all_blocks(); - self.builder.finalize(); - } - - pub(super) fn end_unconditional_exception(mut self) { - self.builder.seal_all_blocks(); - self.builder.finalize(); - } - - /// Clear the builder context on failure. - pub(super) fn fail(mut self) { - // On failure, the context must be cleared to ensure a clean context for the next block to - // be compiled. - - // Before clearing the context, we need to ensure that - // the block compiled so far matches the ABI of the function - // - // In this case, we must ensure that we explicitly declare - // a lack of return values. - self.builder.ins().return_(&[]); - - // Clearing the context is done via `finalize`, which internally clears the - // buffers to allow re-use. - self.builder.seal_all_blocks(); - self.builder.finalize(); - } - - /// Complete a step, updating the program counter in the process. - /// - /// Returns `false` if an unconditional exit from the block occurs, in which case compilation - /// should be finalised without proceeding to the following instruction. - pub(super) fn complete_step>(&mut self, pc_update: U) -> bool { - self.dynamic.complete_step(pc_update) - } - - /// Jump from the current block to the end block, exiting the function. - fn jump_to_end(&mut self) { - // update steps taken so far - let steps_val = - self.builder - .ins() - .load(self.ptr, ir::MemFlags::trusted(), self.steps_ptr_val, 0); - let steps_val = self - .builder - .ins() - .iadd_imm(steps_val, self.dynamic.steps() as i64); - - // get the new value of the pc to write back to the state - let pc_val = self.dynamic.read_pc(&mut self.builder); - - let end_block = self - .end_block - .unwrap_or_else(|| self.builder.create_block()); - - self.builder.ins().jump(end_block, &[pc_val.0, steps_val]); - self.finalise_end_block(end_block) - } - - /// Handle an exception that has occurred. - fn handle_exception(&mut self, exception_ptr: Value) { - let current_pc = self.pc_read(); - - let outcome = self.jsa_call.handle_exception( - &mut self.builder, - self.core_ptr_val, - exception_ptr, - self.result_ptr_val, - current_pc, - ); - - // if handled -> jump to end with updated pc_ptr. Add steps += 1 - self.exit_on_branch(outcome.handled, |builder| { - builder.complete_step(PCUpdate::Absolute(outcome.new_pc)); - }); - - // if !handled -> exit directly; environ needs to be consulted. - // pc has been committed - self.jump_to_end(); - } - - /// Branch and exit if the given condition holds. - /// - /// When needing to exit the 'block-cache' block - on either a `run_branch` or due to - /// an exception occuring, we jump to the end of the compiled function. - /// - /// There is a little cleanup to do, before doing so, however: potentially needing to - /// complete the current step. - /// - /// Semantically, this function returns the caller into the context of the 'fallthrough' - /// block - ie, the where the branch condition fails. - fn exit_on_branch(&mut self, cond: Value, on_branching: impl FnOnce(&mut Self)) { - let on_branch = self.builder.create_block(); - let fallthrough = self.builder.create_block(); - - self.builder - .ins() - .brif(cond, on_branch, &[], fallthrough, &[]); - - // both IR blocks need access to the dynamic values at this point in time. - // These are modified by the jump to the end block below, and possibly by the - // `on_branching` function. - let snapshot = self.dynamic; - - self.builder.switch_to_block(on_branch); - - on_branching(self); - self.jump_to_end(); - self.builder.seal_block(on_branch); - - // Restore the dynamic values to the branching point, for the fallthrough block. - self.dynamic = snapshot; - self.builder.switch_to_block(fallthrough); - } -} - -impl ICB for Builder<'_, MC, JSA> { - type XValue = X64; - type IResult = Option; - - /// An `I8` width value. - type Bool = Value; - - fn bool_and(&mut self, lhs: Self::Bool, rhs: Self::Bool) -> Self::Bool { - self.builder.ins().band(lhs, rhs) - } - - type XValue32 = X32; - - fn narrow(&mut self, value: Self::XValue) -> Self::XValue32 { - X32(self.builder.ins().ireduce(I32, value.0)) - } - - fn extend_signed(&mut self, value: Self::XValue32) -> Self::XValue { - X64(self.builder.ins().sextend(I64, value.0)) - } - - fn extend_unsigned(&mut self, value: Self::XValue32) -> Self::XValue { - X64(self.builder.ins().uextend(I64, value.0)) - } - - fn mul_high( - &mut self, - lhs: Self::XValue, - rhs: Self::XValue, - mul_high_type: MulHighType, - ) -> Self::XValue { - let (lhs, rhs) = match mul_high_type { - MulHighType::Signed => ( - self.builder.ins().sextend(I128, lhs.0), - self.builder.ins().sextend(I128, rhs.0), - ), - MulHighType::Unsigned => ( - self.builder.ins().uextend(I128, lhs.0), - self.builder.ins().uextend(I128, rhs.0), - ), - MulHighType::SignedUnsigned => ( - self.builder.ins().sextend(I128, lhs.0), - self.builder.ins().uextend(I128, rhs.0), - ), - }; - let result = self.builder.ins().imul(lhs, rhs); - let (_low, high) = self.builder.ins().isplit(result); - - X64(high) - } - - fn xregister_read_nz(&mut self, reg: NonZeroXRegister) -> Self::XValue { - JSA::ir_xreg_read( - &mut self.jsa_call, - &mut self.builder, - self.core_ptr_val, - reg, - ) - } - - fn xregister_write_nz(&mut self, reg: NonZeroXRegister, value: Self::XValue) { - JSA::ir_xreg_write( - &mut self.jsa_call, - &mut self.builder, - self.core_ptr_val, - reg, - value, - ) - } - - fn xvalue_of_imm(&mut self, imm: i64) -> Self::XValue { - X64(self.builder.ins().iconst(I64, imm)) - } - - fn xvalue_from_bool(&mut self, value: Self::Bool) -> Self::XValue { - // unsigned extension works as boolean can never be negative (only 0 or 1) - X64(self.builder.ins().uextend(I64, value)) - } - - /// Read the effective current program counter by adding `self.pc_offset` (due to instructions - /// already lowered into this block) to `self.pc_val` (the initial value of the program counter - /// for the block). - fn pc_read(&mut self) -> Self::XValue { - self.dynamic.read_pc(&mut self.builder) - } - - fn branch( - &mut self, - condition: Self::Bool, - offset: i64, - instr_width: InstrWidth, - ) -> ProgramCounterUpdate { - self.exit_on_branch(condition, |builder| { - builder.complete_step(block_state::PCUpdate::Offset(offset)); - }); - - ProgramCounterUpdate::Next(instr_width) - } - - fn branch_merge( - &mut self, - cond: Self::Bool, - true_branch: OnTrue, - false_branch: OnFalse, - ) -> Phi::IcbValue - where - OnTrue: FnOnce(&mut Self) -> Phi::IcbValue, - OnFalse: FnOnce(&mut Self) -> Phi::IcbValue, - { - // Set up parallel blocks. - let true_block = self.builder.create_block(); - let false_block = self.builder.create_block(); - - // set up common post-block. - let post_block = self.builder.create_block(); - - // Add a parameter to the post-block for each parameter returned by the closures. - Phi::IR_TYPES.iter().for_each(|v| { - self.builder.append_block_param(post_block, *v); - }); - - self.builder - .ins() - .brif(cond, true_block, &[], false_block, &[]); - - // both IR blocks need access to the dynamic values at this point in time. - // These can be modified by the `true_branch` and `false_branch` functions. - let snapshot = self.dynamic; - self.builder.switch_to_block(true_block); - - let res_val = Phi::to_ir_vals(true_branch(self)); - self.builder - .ins() - .jump(post_block, &res_val.into_iter().collect::>()); - self.builder.seal_block(true_block); - - self.dynamic = snapshot; - self.builder.switch_to_block(false_block); - - let res_val = Phi::to_ir_vals(false_branch(self)); - self.builder - .ins() - .jump(post_block, &res_val.into_iter().collect::>()); - self.builder.seal_block(false_block); - - // The post-block is the common exit point for both branches. - self.builder.switch_to_block(post_block); - let params = self.builder.block_params(post_block); - - Phi::from_ir_vals(params) - } - - fn atomic_access_fault_guard( - &mut self, - address: Self::XValue, - width: LoadStoreWidth, - ) -> Self::IResult<()> { - let width = self.xvalue_of_imm(width as i64); - let remainder = address.modulus(width, self); - - // The steps of taking the comparison are technically not needed, as cranelift will - // treat any non-zero value as a take-branch (i.e. raise exception) value, so we could - // pass the remainder directly. However for completeness and clarity, we are keeping the - // comparison here. - let zero = self.xvalue_of_imm(0); - let not_aligned = remainder.compare(zero, Predicate::NotEqual, self); - let errno = AtomicAccessGuard::new(not_aligned, address.0); - errno.handle(self); - - Some(()) - } - - fn ok(&mut self, val: Value) -> Self::IResult { - Some(val) - } - - fn err_illegal_instruction(&mut self) -> Self::IResult { - let exception_ptr = self - .jsa_call - .raise_illegal_instruction_exception(&mut self.builder); - - self.handle_exception(exception_ptr); - - None - } - - fn map(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(Value) -> Next, - { - res.map(f) - } - - fn and_then(res: Self::IResult, f: F) -> Self::IResult - where - F: FnOnce(Value) -> Self::IResult, - { - match res { - Some(value) => f(value), - None => None, - } - } - - fn ecall(&mut self) -> Self::IResult> { - let exception_ptr = self.jsa_call.ecall(&mut self.builder, self.core_ptr_val); - - self.handle_exception(exception_ptr); - - None - } - - fn main_memory_store( - &mut self, - phys_address: Self::XValue, - value: Self::XValue, - width: LoadStoreWidth, - ) -> Self::IResult<()> { - let errno = self.jsa_call.memory_store( - &mut self.builder, - self.core_ptr_val, - phys_address, - value, - width, - ); - - errno.handle(self); - - Some(()) - } - - fn main_memory_load( - &mut self, - phys_address: Self::XValue, - signed: bool, - width: LoadStoreWidth, - ) -> Self::IResult { - let errno = self.jsa_call.memory_load( - &mut self.builder, - self.core_ptr_val, - phys_address, - signed, - width, - ); - - let res = errno.handle(self); - - Some(res) - } -} - -impl From for IntCC { - fn from(value: Predicate) -> Self { - match value { - Predicate::Equal => IntCC::Equal, - Predicate::NotEqual => IntCC::NotEqual, - Predicate::LessThanSigned => IntCC::SignedLessThan, - Predicate::LessThanUnsigned => IntCC::UnsignedLessThan, - Predicate::LessThanOrEqualSigned => IntCC::SignedLessThanOrEqual, - Predicate::GreaterThanSigned => IntCC::SignedGreaterThan, - Predicate::GreaterThanOrEqualSigned => IntCC::SignedGreaterThanOrEqual, - Predicate::GreaterThanOrEqualUnsigned => IntCC::UnsignedGreaterThanOrEqual, - } - } -} diff --git a/src/riscv/lib/src/jit/builder/arithmetic.rs b/src/riscv/lib/src/jit/builder/arithmetic.rs deleted file mode 100644 index 1266cf9977fd1c6a3a6d2b2285eb6de5653594df..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/builder/arithmetic.rs +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of arithmetic instructions in JIT mode. - -use cranelift::codegen::ir::InstBuilder; - -use super::Builder; -use super::X32; -use super::X64; -use crate::instruction_context::Shift; -use crate::instruction_context::arithmetic::Arithmetic; -use crate::jit::state_access::JitStateAccess; -use crate::machine_state::memory::MemoryConfig; - -impl Arithmetic> for X64 { - fn add(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().iadd(self.0, other.0); - X64(res) - } - - fn sub(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().isub(self.0, other.0); - X64(res) - } - - fn and(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().band(self.0, other.0); - X64(res) - } - - fn or(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().bor(self.0, other.0); - X64(res) - } - - fn xor(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().bxor(self.0, other.0); - X64(res) - } - - fn mul(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().imul(self.0, other.0); - X64(res) - } - - fn div_signed(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().sdiv(self.0, other.0); - X64(res) - } - - fn negate(self, icb: &mut Builder<'_, MC, JSA>) -> Self { - X64(icb.builder.ins().ineg(self.0)) - } - - fn shift(self, shift: Shift, amount: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - match shift { - Shift::Left => X64(icb.builder.ins().ishl(self.0, amount.0)), - Shift::RightUnsigned => X64(icb.builder.ins().ushr(self.0, amount.0)), - Shift::RightSigned => X64(icb.builder.ins().sshr(self.0, amount.0)), - } - } - - fn modulus(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - X64(icb.builder.ins().srem(self.0, other.0)) - } -} - -impl Arithmetic> for X32 { - fn add(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().iadd(self.0, other.0); - X32(res) - } - - fn sub(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().isub(self.0, other.0); - X32(res) - } - - fn and(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().band(self.0, other.0); - X32(res) - } - - fn or(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().bor(self.0, other.0); - X32(res) - } - - fn xor(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().bxor(self.0, other.0); - X32(res) - } - - fn mul(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().imul(self.0, other.0); - X32(res) - } - - fn div_signed(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - let res = icb.builder.ins().sdiv(self.0, other.0); - X32(res) - } - - fn negate(self, icb: &mut Builder<'_, MC, JSA>) -> Self { - X32(icb.builder.ins().ineg(self.0)) - } - - fn shift(self, shift: Shift, amount: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - match shift { - Shift::Left => X32(icb.builder.ins().ishl(self.0, amount.0)), - Shift::RightUnsigned => X32(icb.builder.ins().ushr(self.0, amount.0)), - Shift::RightSigned => X32(icb.builder.ins().sshr(self.0, amount.0)), - } - } - - fn modulus(self, other: Self, icb: &mut Builder<'_, MC, JSA>) -> Self { - X32(icb.builder.ins().srem(self.0, other.0)) - } -} diff --git a/src/riscv/lib/src/jit/builder/block_state.rs b/src/riscv/lib/src/jit/builder/block_state.rs deleted file mode 100644 index d233c2585b973595e62f093ea32927b73ac1c0af..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/builder/block_state.rs +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! State that is kept per Cranelift-IR block. - -use cranelift::codegen::ir::InstBuilder; -use cranelift::frontend::FunctionBuilder; - -use super::X64; -use crate::machine_state::ProgramCounterUpdate; - -/// Program Counter update, within the context of JIT-compilation. -/// -/// This is distinct from [`ProgramCounterUpdate`] - in that the offset -/// is not restricted to merely instruction widths. Namely, branching -/// instructions also produce such a PC-relative offset when the condition -/// holds. -#[derive(Debug)] -pub enum PCUpdate { - /// Relative offset to the current program counter. - Offset(i64), - /// The program counter is set to an absolute address, irrelevant to the - /// current value of the program counter. - Absolute(X64), -} - -impl From> for PCUpdate { - fn from(value: ProgramCounterUpdate) -> Self { - match value { - ProgramCounterUpdate::Next(width) => Self::Offset(width as i64), - ProgramCounterUpdate::Set(address) => Self::Absolute(address), - } - } -} - -/// Dynamic values that are updated during lowering of instructions. -/// -/// This is especially relevant when dealing with code that may branch -/// (including for error handling) - due to switching between cranelift blocks. -/// -/// Whilst in the context of one block (e.g. the branching block), these values may -/// be updated - but *must not* impact the values of the non-branching block while -/// doing so. Keeping all such state together, simplifies snapshotting & restoring -/// these values when switching between such blocks. -/// -/// The non-branching block, can otherwise be considered as the 'fallthrough' block. -/// That is to say, the fallthrough block is the block taken when execution 'falls through' -/// a branching instruction--the branch is not taken. -#[derive(Debug, Clone, Copy)] -pub struct DynamicValues { - /// The number of steps taken within the current compilation context. - steps: usize, - - /// Value representing the last-updated value of `instr_pc`. - /// - /// Whenever the program counter is read, we implicitly update this value - /// to take into account the `pc_offset` accrued (e.g. by having lowered a few instructions). - /// - /// Doing this offset-mechanism allows us to avoid updating the `pc` on every step, effectively - /// updating it in one go, only when it matters. - pc_val: X64, - - /// The current offset of the pc, as a result of steps taken - /// so far. - pc_offset: i64, -} - -impl DynamicValues { - /// Create a new set of values, given an initial program counter. - pub fn new(pc_val: X64) -> Self { - Self { - pc_val, - steps: 0, - pc_offset: 0, - } - } - - /// The current value of the program counter may be offset from the original - /// instruction counter given during construction. - pub fn read_pc(&mut self, builder: &mut FunctionBuilder<'_>) -> X64 { - if self.pc_offset == 0 { - return self.pc_val; - } - - let new_pc = builder.ins().iadd_imm(self.pc_val.0, self.pc_offset); - - self.pc_val = X64(new_pc); - self.pc_offset = 0; - - X64(new_pc) - } - - /// Complete a step, updating the program counter in the process. - /// - /// Returns `true` if compilation should continue with the next instructions. - /// If `false` is returned, this indicates an unconditional exit from the instruction - /// block and compilation can be finalised. - pub fn complete_step>(&mut self, pc_update: U) -> bool { - self.steps += 1; - - match pc_update.into() { - PCUpdate::Offset(offset) => { - self.pc_offset = self.pc_offset.wrapping_add(offset); - true - } - PCUpdate::Absolute(address) => { - self.pc_offset = 0; - self.pc_val = address; - false - } - } - } - - /// The number of steps that have been taken thus far in the compilation process, - /// where 'step' maps to the lowering of an instruction. - pub fn steps(&self) -> usize { - self.steps - } -} diff --git a/src/riscv/lib/src/jit/builder/comparable.rs b/src/riscv/lib/src/jit/builder/comparable.rs deleted file mode 100644 index 8c82b9ce1adfbbfbb14be817ff6697246004ae5c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/builder/comparable.rs +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementation of comparison operations in JIT mode. - -use cranelift::codegen::ir::InstBuilder; - -use super::Builder; -use crate::instruction_context::ICB; -use crate::instruction_context::Predicate; -use crate::instruction_context::comparable::Comparable; -use crate::jit::state_access::JitStateAccess; -use crate::machine_state::memory::MemoryConfig; - -impl<'a, MC: MemoryConfig, JSA: JitStateAccess> Comparable> - for as ICB>::XValue -{ - // icmp returns 1 if the condition holds, 0 if it does not. - // - // This matches the required semantics of bool - namely that it coerces to XValue with - // - true => 1 - // - false => 0 - // - // See - // - fn compare( - self, - other: Self, - comparison: Predicate, - icb: &mut Builder<'a, MC, JSA>, - ) -> as ICB>::Bool { - icb.builder.ins().icmp(comparison, self.0, other.0) - } -} - -impl<'a, MC: MemoryConfig, JSA: JitStateAccess> Comparable> - for as ICB>::XValue32 -{ - // icmp returns 1 if the condition holds, 0 if it does not. - // - // This matches the required semantics of bool - namely that it coerces to XValue with - // - true => 1 - // - false => 0 - // - // See - // - fn compare( - self, - other: Self, - comparison: Predicate, - icb: &mut Builder<'a, MC, JSA>, - ) -> as ICB>::Bool { - icb.builder.ins().icmp(comparison, self.0, other.0) - } -} diff --git a/src/riscv/lib/src/jit/builder/errno.rs b/src/riscv/lib/src/jit/builder/errno.rs deleted file mode 100644 index afaf403c9cebc9bf11972c1711a39101519907f7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/builder/errno.rs +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Wrapper for C-style error handling with out-parameters. -//! -//! Any fallible function will return an 'error code' - either -//! `1` for failure or `0` for success. -//! -//! Any returned values are written via 'out-pointers' - and should only be -//! loaded on success. [`Errno`] provides a wrapper for this mechanism, -//! that ensures safety. - -use cranelift::codegen::ir; -use cranelift::codegen::ir::InstBuilder; -use cranelift::frontend::FunctionBuilder; - -use crate::jit::state_access::JitStateAccess; -use crate::machine_state::memory::MemoryConfig; - -/// Wrapper for `Errno`-type returns. -/// -/// This is formed of two parts, the 'code', and a output function. -/// The output function will only be called on success - ie any -/// out parameters (with the exception of the exception ptr) - can be assumed -/// to be initialised, if the externally-called function guarantees it. -/// -/// *NB* a trait is used, rather than a structure, to allow us to naturally use -/// _unboxed closures_ for the function that returns the output values, without needing -/// to thread extra generics. -pub(crate) trait Errno { - /// Insert exception handling branch that will be triggered at runtime - /// when `errno` indicates failure. - /// - /// The caller of this function is put back into the context of the happy path - - /// ie, where `errno` indicates success and no exception occurred. - fn handle(self, builder: &mut super::Builder<'_, MC, JSA>) -> T; -} - -/// Helper type for ensuring fallible operations are handled correctly. -/// -/// The errno is constructed out of three pieces: -/// - whether or not a failure occurred -/// - if yes, the pointer to the exception in memory that has been written with the failure kind -/// - if no, a handler to load any state that was returned in `out-params` that is now safe to -/// access. -/// -/// The only way to access the values that will be returned on success, is via the -/// [`Errno::handle`] method. -pub(crate) struct ErrnoImpl -where - F: FnOnce(&mut FunctionBuilder<'_>) -> T, -{ - errno: ir::Value, - exception_ptr: ir::Value, - on_ok: F, -} - -impl ErrnoImpl -where - F: FnOnce(&mut FunctionBuilder<'_>) -> T, -{ - /// Construct a new `Errno` that must be handled. - pub(crate) fn new(errno: ir::Value, exception_ptr: ir::Value, on_ok: F) -> Self { - Self { - errno, - exception_ptr, - on_ok, - } - } -} - -impl Errno for ErrnoImpl -where - F: FnOnce(&mut FunctionBuilder<'_>) -> T, -{ - fn handle(self, builder: &mut super::Builder<'_, MC, JSA>) -> T { - let errno_code = self.errno; - - let on_error = builder.builder.create_block(); - let on_ok = builder.builder.create_block(); - - builder - .builder - .ins() - .brif(errno_code, on_error, &[], on_ok, &[]); - - let snapshot = builder.dynamic; - - builder.builder.switch_to_block(on_error); - // Handle the exception in the 'error occurred' block - builder.handle_exception(self.exception_ptr); - - builder.builder.seal_block(on_error); - - // Go back to the 'no error occurred' block, any returned - // values are ok to load - builder.builder.switch_to_block(on_ok); - builder.dynamic = snapshot; - - (self.on_ok)(&mut builder.builder) - } -} - -/// Helper type for producing the required IR for the address alignment check at the beginning -/// of an atomic operation. -/// -/// The only way to access the values that will be returned on success, is via the -/// [`Errno::handle`] method. -pub(crate) struct AtomicAccessGuard { - errno: ir::Value, - address: ir::Value, -} - -impl AtomicAccessGuard { - /// Construct a new `Errno` that must be handled. - pub(crate) fn new(errno: ir::Value, address: ir::Value) -> Self { - Self { errno, address } - } -} - -impl Errno<(), MC, JSA> for AtomicAccessGuard { - fn handle(self, builder: &mut super::Builder<'_, MC, JSA>) { - let error_branch = builder.builder.create_block(); - let fallthrough = builder.builder.create_block(); - - builder - .builder - .ins() - .brif(self.errno, error_branch, &[], fallthrough, &[]); - - // both IR blocks need access to the dynamic values at this point in time. - // These are modified by the jump to the end block below, and possibly by the - // `on_branching` function. - let snapshot = builder.dynamic; - - builder.builder.switch_to_block(error_branch); - - let exception_ptr = builder - .jsa_call - .raise_store_amo_access_fault_exception(&mut builder.builder, self.address); - builder.handle_exception(exception_ptr); - - builder.builder.seal_block(error_branch); - - // Restore the dynamic values to the branching point, for the fallthrough block. - builder.dynamic = snapshot; - builder.builder.switch_to_block(fallthrough) - } -} diff --git a/src/riscv/lib/src/jit/state_access.rs b/src/riscv/lib/src/jit/state_access.rs deleted file mode 100644 index a3a243e918b4f07de3141aeac1fc510175588c6d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/state_access.rs +++ /dev/null @@ -1,607 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! JIT-compiled blocks must be able to interact with the -//! RISC-V [`MachineCoreState`] passed to them. -//! -//! In Cranelift, this works in two stages. -//! -//! First, the `extern "C"` function pointers must be -//! registered as external symbols in the [jit builder] & the corresponding signatures declared -//! in the [jit module]. This allows generated code to link with these functions. -//! -//! The second step occurs _during jit compilation_ itself. The linked functions must be re-declared -//! within the [function builder] itself. This then allows for a [direct function call] to be issued, -//! which will indeed perform the function call at runtime. -//! -//! [jit builder]: JITBuilder -//! [jit module]: cranelift_jit::JITModule -//! [function builder]: cranelift::frontend::FunctionBuilderContext -//! [direct function call]: cranelift::codegen::ir::InstBuilder::call - -mod abi; -mod stack; - -use std::marker::PhantomData; -use std::mem::MaybeUninit; - -use abi::AbiCall; -use cranelift::codegen::ir; -use cranelift::codegen::ir::FuncRef; -use cranelift::codegen::ir::InstBuilder; -use cranelift::codegen::ir::Type; -use cranelift::codegen::ir::Value; -use cranelift::codegen::ir::types::I8; -use cranelift::frontend::FunctionBuilder; -use cranelift::prelude::MemFlags; -use cranelift_jit::JITBuilder; -use cranelift_jit::JITModule; -use cranelift_module::FuncId; -use cranelift_module::Module; -use cranelift_module::ModuleResult; - -use super::builder::X64; -use super::builder::errno::Errno; -use super::builder::errno::ErrnoImpl; -use crate::instruction_context::ICB; -use crate::instruction_context::LoadStoreWidth; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Address; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegisters; -use crate::machine_state::registers::XValue; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::ProofGen; -use crate::traps::EnvironException; -use crate::traps::Exception; - -macro_rules! register_jsa_functions { - ($($name:ident => ($field:path, $fn:path)),* $(,)?) => { - /// Register state access symbols in the builder. - pub(super) fn register_jsa_symbols( - builder: &mut JITBuilder, - ) { - $(builder.symbol(stringify!($field), $field as *const u8);)* - } - - /// Identifications of globally imported [`JitStateAccess`] methods. - pub(super) struct JsaImports { - $( - pub $name: FuncId, - )* - _pd: PhantomData<(MC, JSA)>, - } - - impl JsaImports { - /// Register external functions within the JIT Module. - pub(super) fn declare_in_module(module: &mut JITModule) -> ModuleResult { - let ptr_type = module.target_config().pointer_type(); - let call_conv = module.target_config().default_call_conv; - - $( - let abi = $fn($field); - let $name = abi.declare_function(module, stringify!($field), ptr_type, call_conv)?; - )* - - Ok(Self { - $( - $name, - )* - _pd: PhantomData, - }) - } - } - }; -} - -register_jsa_functions!( - pc_write => (JSA::pc_write::, AbiCall<2>::args), - xreg_read => (JSA::xregister_read::, AbiCall<2>::args), - xreg_write => (JSA::xregister_write::, AbiCall<3>::args), - handle_exception => (JSA::handle_exception::, AbiCall<4>::args), - raise_illegal_instruction_exception => (JSA::raise_illegal_instruction_exception, AbiCall<1>::args), - raise_store_amo_access_fault_exception => (JSA::raise_store_amo_access_fault_exception, AbiCall<2>::args), - ecall_from_mode => (JSA::ecall::, AbiCall<2>::args), - memory_store => (JSA::memory_store::, AbiCall<5>::args), - memory_load => (JSA::memory_load::, AbiCall::<6>::args) -); - -/// State Access that a JIT-compiled block may use. -/// -/// In future, this will come in two parts: -/// - `extern "C"` functions that can be registered in the JIT module -/// - a way of calling those functions from within JIT-compiled code -pub trait JitStateAccess: ManagerReadWrite { - /// Update the instruction pc in the state. - extern "C" fn pc_write(core: &mut MachineCoreState, pc: u64) { - core.hart.pc.write(pc) - } - /// Read the value of the given [`NonZeroXRegister`]. - extern "C" fn xregister_read( - core: &mut MachineCoreState, - reg: NonZeroXRegister, - ) -> XValue { - core.hart.xregisters.read_nz(reg) - } - - /// Write the given value to the given [`NonZeroXRegister`]. - extern "C" fn xregister_write( - core: &mut MachineCoreState, - reg: NonZeroXRegister, - val: XValue, - ) { - core.hart.xregisters.write_nz(reg, val) - } - - /// Handle an [`Exception`]. - /// - /// If the exception is succesfully handled, the - /// `current_pc` is updated to the new value, and returns true. The `current_pc` - /// remains initialised to its previous value otherwise. - /// - /// If the exception needs to be treated by the execution environment, - /// `result` is updated with the `EnvironException` and `false` is - /// returned. - /// - /// # Panics - /// - /// Panics if the exception does not have `Some(_)` value. - /// - /// See [`MachineCoreState::address_on_exception`]. - extern "C" fn handle_exception( - core: &mut MachineCoreState, - current_pc: &mut Address, - exception: &Exception, - result: &mut Result<(), EnvironException>, - ) -> bool { - let res = core.address_on_exception(*exception, *current_pc); - - match res { - Err(e) => { - *result = Err(e); - false - } - Ok(address) => { - *current_pc = address; - true - } - } - } - - /// Raise an [`Exception::IllegalInstruction`]. - /// - /// Writes the instruction to the given exception memory, after which it would be safe to - /// assume it is initialised. - extern "C" fn raise_illegal_instruction_exception(exception_out: &mut MaybeUninit) { - exception_out.write(Exception::IllegalInstruction); - } - - /// Raise an [`Exception::StoreAMOAccessFault`]. - /// - /// Writes the instruction to the given exception memory, after which it would be safe to - /// assume it is initialised. - extern "C" fn raise_store_amo_access_fault_exception( - exception_out: &mut MaybeUninit, - address: u64, - ) { - exception_out.write(Exception::StoreAMOAccessFault(address)); - } - - /// Raise the appropriate environment-call exception given the current machine mode. - /// - /// Writes the exception to the given exception memory, after which it would be safe to - /// assume it is initialised. - extern "C" fn ecall( - core: &mut MachineCoreState, - exception_out: &mut MaybeUninit, - ) { - exception_out.write(core.hart.run_ecall()); - } - - /// Store the lowest `width` bytes of the given value to memory, at the physical address. - /// - /// If the store is successful, `false` is returned to indicate no exception handling is necessary. - /// - /// If the store fails (due to out of bouds etc) then an exception will be written - /// to `exception_out` and `true` returned to indicate exception handling will be necessary. - /// - /// # Panics - /// - /// Panics if the `width` passed is not a supported [`LoadStoreWidth`]. - extern "C" fn memory_store( - core: &mut MachineCoreState, - address: u64, - value: u64, - width: u8, - exception_out: &mut MaybeUninit, - ) -> bool { - let Some(width) = LoadStoreWidth::new(width) else { - panic!("The given width {width} is not a supported LoadStoreWidth"); - }; - - match as ICB>::main_memory_store(core, address, value, width) { - Ok(()) => false, - Err(exception) => { - exception_out.write(exception); - true - } - } - } - - /// Load `width` bytes from memory, at the physical address, into lowest `width` bytes of an - /// `XValue`, with (un)signed extension. - /// - /// If the load is successful, `false` is returned to indicate no exception handling is - /// necessary. - /// - /// If the load fails (due to out of bouds etc) then an exception will be written - /// to `exception_out` and `true` returned to indicate exception handling will be necessary. - /// - /// # Panics - /// - /// Panics if the `width` passed is not a supported [`LoadStoreWidth`]. - extern "C" fn memory_load( - core: &mut MachineCoreState, - address: u64, - width: u8, - signed: bool, - xval_out: &mut MaybeUninit, - exception_out: &mut MaybeUninit, - ) -> bool { - let Some(width) = LoadStoreWidth::new(width) else { - panic!("The given width {width} is not a supported LoadStoreWidth"); - }; - - match as ICB>::main_memory_load(core, address, signed, width) { - Ok(value) => { - xval_out.write(value); - false - } - Err(exception) => { - exception_out.write(exception); - true - } - } - } - - // ------------- - // IR Generation - // ------------- - - /// Emit the required IR to read the value from the given xregister. - fn ir_xreg_read( - jsa_calls: &mut JsaCalls<'_, MC, Self>, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - reg: NonZeroXRegister, - ) -> X64 { - let xreg_read = jsa_calls.xreg_read.get_or_insert_with(|| { - jsa_calls - .module - .declare_func_in_func(jsa_calls.imports.xreg_read, builder.func) - }); - let reg = builder.ins().iconst(I8, reg as i64); - let call = builder.ins().call(*xreg_read, &[core_ptr, reg]); - X64(builder.inst_results(call)[0]) - } - - /// Emit the required IR to write the value to the given xregister. - fn ir_xreg_write( - jsa_calls: &mut JsaCalls<'_, MC, Self>, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - reg: NonZeroXRegister, - value: X64, - ) { - let xreg_write = jsa_calls.xreg_write.get_or_insert_with(|| { - jsa_calls - .module - .declare_func_in_func(jsa_calls.imports.xreg_write, builder.func) - }); - let reg = builder.ins().iconst(I8, reg as i64); - builder.ins().call(*xreg_write, &[core_ptr, reg, value.0]); - } -} - -impl JitStateAccess for Owned { - fn ir_xreg_read( - _jsa_calls: &mut JsaCalls<'_, MC, Self>, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - reg: NonZeroXRegister, - ) -> X64 { - let offset = std::mem::offset_of!(MachineCoreState, hart.xregisters) - + XRegisters::::xregister_offset(reg); - - // memory access corresponds directly to the xregister value - // - known to be aligned and non-trapping - let val = builder - .ins() - .load(ir::types::I64, MemFlags::trusted(), core_ptr, offset as i32); - - X64(val) - } - - fn ir_xreg_write( - _jsa_calls: &mut JsaCalls<'_, MC, Self>, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - reg: NonZeroXRegister, - value: X64, - ) { - let offset = std::mem::offset_of!(MachineCoreState, hart.xregisters) - + XRegisters::::xregister_offset(reg); - - // memory access corresponds directly to the xregister value - // - known to be aligned and non-trapping - builder - .ins() - .store(MemFlags::trusted(), value.0, core_ptr, offset as i32); - } -} - -impl JitStateAccess for ProofGen {} - -/// References to locally imported [`JitStateAccess`] methods, used to directly call -/// these accessor methods in the JIT-compilation context. -pub struct JsaCalls<'a, MC: MemoryConfig, JSA: JitStateAccess> { - module: &'a mut JITModule, - imports: &'a JsaImports, - ptr_type: Type, - pc_write: Option, - xreg_read: Option, - xreg_write: Option, - handle_exception: Option, - raise_illegal_instruction_exception: Option, - raise_store_amo_access_fault_exception: Option, - ecall_from_mode: Option, - memory_store: Option, - memory_load: Option, - _pd: PhantomData<(MC, JSA)>, -} - -impl<'a, MC: MemoryConfig, JSA: JitStateAccess> JsaCalls<'a, MC, JSA> { - /// Wrapper to simplify calling JSA methods from within the function under construction. - pub(super) fn func_calls( - module: &'a mut JITModule, - imports: &'a JsaImports, - ptr_type: Type, - ) -> Self { - Self { - module, - imports, - ptr_type, - pc_write: None, - xreg_read: None, - xreg_write: None, - handle_exception: None, - raise_illegal_instruction_exception: None, - raise_store_amo_access_fault_exception: None, - ecall_from_mode: None, - memory_store: None, - memory_load: None, - _pd: PhantomData, - } - } - - /// Emit the required IR to set the pc to the given value. - pub(super) fn pc_write( - &mut self, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - pc_val: X64, - ) { - let pc_write = self.pc_write.get_or_insert_with(|| { - self.module - .declare_func_in_func(self.imports.pc_write, builder.func) - }); - builder.ins().call(*pc_write, &[core_ptr, pc_val.0]); - } - - /// Emit the required IR to call `handle_exception`. - /// - /// # Panics - /// - /// The call to `handle_exception` will panic (at runtime) if no exception - /// has occurred so-far in the JIT-compiled function, if the error-handling - /// code is triggerred. - pub(super) fn handle_exception( - &mut self, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - exception_ptr: Value, - result_ptr: Value, - current_pc: X64, - ) -> ExceptionHandledOutcome { - let handle_exception = self.handle_exception.get_or_insert_with(|| { - self.module - .declare_func_in_func(self.imports.handle_exception, builder.func) - }); - - let pc_slot = stack::Slot::::new(self.ptr_type, builder); - pc_slot.store(builder, current_pc.0); - - let pc_ptr = pc_slot.ptr(builder); - - let call = builder.ins().call(*handle_exception, &[ - core_ptr, - pc_ptr, - exception_ptr, - result_ptr, - ]); - - let handled = builder.inst_results(call)[0]; - // Safety: the pc is initialised prior to the call, and is guaranteed to - // remain initialised regardless of the result of external call. - let new_pc = unsafe { pc_slot.load(builder) }; - - ExceptionHandledOutcome { - handled, - new_pc: X64(new_pc), - } - } - - /// Emit the required IR to call `raise_illegal_exception`. - /// - /// This returns an initialised pointer to the exception. - pub(super) fn raise_illegal_instruction_exception( - &mut self, - builder: &mut FunctionBuilder<'_>, - ) -> Value { - let exception_slot = stack::Slot::::new(self.ptr_type, builder); - let exception_ptr = exception_slot.ptr(builder); - - let raise_illegal = self - .raise_illegal_instruction_exception - .get_or_insert_with(|| { - self.module.declare_func_in_func( - self.imports.raise_illegal_instruction_exception, - builder.func, - ) - }); - - builder.ins().call(*raise_illegal, &[exception_ptr]); - - exception_ptr - } - - /// Emit the required IR to call `raise_store_amo_access_fault_exception`. - /// - /// This returns an initialised pointer to the exception. - pub(super) fn raise_store_amo_access_fault_exception( - &mut self, - builder: &mut FunctionBuilder<'_>, - address: Value, - ) -> Value { - let exception_slot = stack::Slot::::new(self.ptr_type, builder); - let exception_ptr = exception_slot.ptr(builder); - - let raise_store_amo_access_fault = self - .raise_store_amo_access_fault_exception - .get_or_insert_with(|| { - self.module.declare_func_in_func( - self.imports.raise_store_amo_access_fault_exception, - builder.func, - ) - }); - - builder - .ins() - .call(*raise_store_amo_access_fault, &[exception_ptr, address]); - - exception_ptr - } - - /// Emit the required IR to call `ecall`. - /// - /// This returns an initialised pointer to the appropriate environment - /// call exception for the current machine mode. - pub(super) fn ecall(&mut self, builder: &mut FunctionBuilder<'_>, core_ptr: Value) -> Value { - let exception_slot = stack::Slot::::new(self.ptr_type, builder); - let exception_ptr = exception_slot.ptr(builder); - - let ecall_from_mode = self.ecall_from_mode.get_or_insert_with(|| { - self.module - .declare_func_in_func(self.imports.ecall_from_mode, builder.func) - }); - - builder - .ins() - .call(*ecall_from_mode, &[core_ptr, exception_ptr]); - - exception_ptr - } - - /// Emit the required IR to call `memory_store`. - /// - /// Returns `errno` - on success, no additional values are returned. - pub(super) fn memory_store( - &mut self, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - phys_address: X64, - value: X64, - width: LoadStoreWidth, - ) -> impl Errno<(), MC, JSA> + 'static { - let memory_store = self.memory_store.get_or_insert_with(|| { - self.module - .declare_func_in_func(self.imports.memory_store, builder.func) - }); - - let exception_slot = stack::Slot::::new(self.ptr_type, builder); - let exception_ptr = exception_slot.ptr(builder); - - let width = builder.ins().iconst(I8, width as u8 as i64); - - let call = builder.ins().call(*memory_store, &[ - core_ptr, - phys_address.0, - value.0, - width, - exception_ptr, - ]); - - let errno = builder.inst_results(call)[0]; - - ErrnoImpl::new(errno, exception_ptr, |_| {}) - } - - /// Emit the required IR to call `memory_load`. - /// - /// Returns `errno` - on success, the loaded value is returned. - pub(super) fn memory_load( - &mut self, - builder: &mut FunctionBuilder<'_>, - core_ptr: Value, - phys_address: X64, - signed: bool, - width: LoadStoreWidth, - ) -> impl Errno + 'static { - let memory_load = self.memory_load.get_or_insert_with(|| { - self.module - .declare_func_in_func(self.imports.memory_load, builder.func) - }); - - let exception_slot = stack::Slot::::new(self.ptr_type, builder); - let exception_ptr = exception_slot.ptr(builder); - - let xval_slot = stack::Slot::::new(self.ptr_type, builder); - let xval_ptr = xval_slot.ptr(builder); - - let width = builder.ins().iconst(I8, width as u8 as i64); - let signed = builder.ins().iconst(I8, signed as i64); - - let call = builder.ins().call(*memory_load, &[ - core_ptr, - phys_address.0, - width, - signed, - xval_ptr, - exception_ptr, - ]); - - let errno = builder.inst_results(call)[0]; - - ErrnoImpl::new(errno, exception_ptr, move |builder| { - // Safety: the xval is initialised prior to the call, and is guaranteed to - // remain initialised regardless of the result of external call. - let xval = unsafe { xval_slot.load(builder) }; - X64(xval) - }) - } -} - -/// Outcome of handling an exception. -pub(super) struct ExceptionHandledOutcome { - /// Whether the exception was succesfully handled. - /// - /// - If true, the exception was handled and the step is completed. - /// - If false, the exception must be instead handled by the environment. - /// The step is not complete. - pub handled: Value, - /// The new value of the instruction pc, after exception handling. - pub new_pc: X64, -} diff --git a/src/riscv/lib/src/jit/state_access/abi.rs b/src/riscv/lib/src/jit/state_access/abi.rs deleted file mode 100644 index 2cbaad309010abf0c4aa1f211dc2ebdba6dbbb16..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/state_access/abi.rs +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Helper functionality for generating ABI interface code. - -use cranelift::codegen::ir::AbiParam; -use cranelift::codegen::ir::Signature; -use cranelift::codegen::ir::Type; -use cranelift::codegen::ir::types::I8; -use cranelift::codegen::ir::types::I16; -use cranelift::codegen::ir::types::I32; -use cranelift::codegen::ir::types::I64; -use cranelift::prelude::isa::CallConv; -use cranelift_jit::JITModule; -use cranelift_module::FuncId; -use cranelift_module::Linkage; -use cranelift_module::Module; -use cranelift_module::ModuleResult; - -use crate::machine_state::registers::NonZeroXRegister; - -/// This struct is used to produce and declare function signatures for external function calls. -/// -/// It is generic over the number of parameters `T` and the return type of the function call. -pub(super) struct AbiCall { - params: [CraneliftRepr; T], - ret: Option, -} - -impl AbiCall { - /// Declare a function with the given name in the JIT module so it can be called from a JIT function. - pub(super) fn declare_function( - &self, - module: &mut JITModule, - name: &str, - ptr_type: Type, - call_conv: CallConv, - ) -> ModuleResult { - let params = self - .params - .iter() - .map(|param| match param { - CraneliftRepr::Ptr => AbiParam::new(ptr_type), - CraneliftRepr::Value(ty) => AbiParam::new(*ty), - }) - .collect(); - - let returns = match &self.ret { - Some(param) => vec![match param { - CraneliftRepr::Ptr => AbiParam::new(ptr_type), - CraneliftRepr::Value(ty) => AbiParam::new(*ty), - }], - None => vec![], - }; - - let sig = Signature { - params, - returns, - call_conv, - }; - module.declare_function(name, Linkage::Import, &sig) - } -} - -/// This macro is used to implement the functions that produce the [`AbiCall`] struct -/// for external function calls with different numbers of arguments. -macro_rules! impl_abicall { - ($($arg:ident),*) => { - pub(super) fn args<$($arg: ToCraneliftRepr),*, Ret: Returnable>(_fun: extern "C" fn($($arg),*) -> Ret) - -> Self - { - let params = [$($arg::CRANELIFT_TYPE,)*]; - let ret = Ret::convert(); - AbiCall { params, ret } - } - }; -} - -impl AbiCall<1> { - impl_abicall!(A1); -} -impl AbiCall<2> { - impl_abicall!(A1, A2); -} -impl AbiCall<3> { - impl_abicall!(A1, A2, A3); -} - -impl AbiCall<4> { - impl_abicall!(A1, A2, A3, A4); -} - -impl AbiCall<5> { - impl_abicall!(A1, A2, A3, A4, A5); -} - -impl AbiCall<6> { - impl_abicall!(A1, A2, A3, A4, A5, A6); -} - -/// Holds the IR representation of a function parameter's type, which is needed for -/// registering the function's [`Signature`] in the [`JITModule`]. -pub(super) enum CraneliftRepr { - /// Pointer for reference type parameters. This will be mapped to the default - /// cranelift IR pointer type for the target architecture. - Ptr, - /// Value-type parameters that will be represented directly by a cranelift IR type. - Value(Type), -} - -/// Match the size of the type to a cranelift IR type. -const fn get_repr() -> CraneliftRepr { - struct SupportedSize(T); - impl SupportedSize { - const SIZE: usize = std::mem::size_of::(); - - const IS_SUPPORTED: () = - assert!(Self::SIZE == 1 || Self::SIZE == 2 || Self::SIZE == 4 || Self::SIZE == 8); - } - - #[expect(clippy::let_unit_value)] - let _ = SupportedSize::::IS_SUPPORTED; - - match SupportedSize::::SIZE { - 1 => CraneliftRepr::Value(I8), - 2 => CraneliftRepr::Value(I16), - 4 => CraneliftRepr::Value(I32), - 8 => CraneliftRepr::Value(I64), - _ => unreachable!(), - } -} - -/// This Type can be transformed into a cranelift IR Type. -pub(super) trait ToCraneliftRepr { - /// Associated constant to determine the Cranelift type at compile time. - const CRANELIFT_TYPE: CraneliftRepr; -} - -impl ToCraneliftRepr for u64 { - const CRANELIFT_TYPE: CraneliftRepr = get_repr::(); -} - -impl ToCraneliftRepr for bool { - const CRANELIFT_TYPE: CraneliftRepr = get_repr::(); -} - -impl ToCraneliftRepr for NonZeroXRegister { - const CRANELIFT_TYPE: CraneliftRepr = get_repr::(); -} - -impl ToCraneliftRepr for u8 { - const CRANELIFT_TYPE: CraneliftRepr = get_repr::(); -} - -impl ToCraneliftRepr for &T { - const CRANELIFT_TYPE: CraneliftRepr = CraneliftRepr::Ptr; -} - -impl ToCraneliftRepr for &mut T { - const CRANELIFT_TYPE: CraneliftRepr = CraneliftRepr::Ptr; -} - -/// A valid return type for an external function call. -pub(super) trait Returnable { - /// Convert the return type of a function to a cranelift IR type. - /// If the type is `()`, return `None` to indicate that there is no return value. - fn convert() -> Option; -} - -impl Returnable for () { - fn convert() -> Option { - None - } -} - -impl Returnable for T { - fn convert() -> Option { - Some(T::CRANELIFT_TYPE) - } -} diff --git a/src/riscv/lib/src/jit/state_access/stack.rs b/src/riscv/lib/src/jit/state_access/stack.rs deleted file mode 100644 index 65442b767be6bf55a879d5c8dfcdc9ab24416435..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/jit/state_access/stack.rs +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Loading and storing of temporary values to/from the stack. -//! -//! Certain information is occassionally required to be on the stack, when executing -//! a JIT-compiled block. -//! -//! See for example [`handle_exception`]: -//! -//! ```ignore -//! use crate::state_backend::ManagerBase; -//! use crate::machine_state::MachineCoreState; -//! use crate::machine_state::memory::Address; -//! use crate::machine_state::memory::MemoryConfig; -//! use crate::traps::EnvironException; -//! use crate::traps::Exception; -//! -//! trait JitStateAccess: ManagerBase { -//! extern "C" fn handle_exception( -//! core: &mut MachineCoreState, -//! current_pc: &mut Address, -//! exception: &Option, -//! result: &mut Result<(), EnvironException>, -//! ) -> bool; -//! } -//! ``` -//! -//! The boolean return indicates whether the function has failed. If it has, then an exception will -//! have been written to the place in memory specified by `exception`. `current_pc` is also an out -//! pointer - if the exception is successfully handled, an updated pc is returned. -//! -//! The most natural place to point to in memory, is the function stack. Certain values can safely -//! live in the registers, and so can be loaded from/stored to the stack. -//! -//! Larger/more complex types cannot be moved between registers and the stack safely. Exceptions -//! are a good example of this. For now, we choose to pass pointers from values created in rust -//! code, for safety. -//! -//! [`handle_exception`]: super::JitStateAccess::handle_exception - -use std::marker::PhantomData; - -use cranelift::codegen::ir::InstBuilder; -use cranelift::codegen::ir::StackSlot; -use cranelift::codegen::ir::StackSlotData; -use cranelift::codegen::ir::StackSlotKind; -use cranelift::codegen::ir::Type; -use cranelift::codegen::ir::Value; -use cranelift::codegen::ir::types::I64; -use cranelift::frontend::FunctionBuilder; - -use crate::machine_state::registers::XValue; -use crate::traps::Exception; - -/// Any value of type `T: StackAddressable` may be placed on the stack, and -/// a pointer to it obtained. -pub(super) trait StackAddressable { - /// The underlying 'runtime' type of the value - e.g. a u64/u32 etc. - /// - /// Separating these allows us to explicitly use different stack slot kinds, for different - /// 'semantic' values (e.g. Address vs XValue), despite the underlying type being shared. - /// - /// This offers an additional level of type-safety. - type Underlying: Sized; - - /// The size, in bytes, of the underlying value. - const SIZE: u32 = std::mem::size_of::() as u32; - - /// Require `1 << ALIGN_SHIFT == align(Underlying)`, since cranelift - /// consumes the _shift_ rather than the raw align. - const ALIGN_SHIFT: u8 = { - let align = std::mem::align_of::(); - - if !align.is_power_of_two() { - panic!("Cranelift requires values stored on the stack to be align to a power of two"); - } - - align.trailing_zeros() as u8 - }; - - /// Declaration of how a stack slot should be placed - including size and alignment. - /// - /// This is consumed by Cranelift's builder when declaring a stack slot. - const STACK_SLOT_DATA: StackSlotData = StackSlotData { - kind: StackSlotKind::ExplicitSlot, - size: Self::SIZE, - align_shift: Self::ALIGN_SHIFT, - }; -} - -/// Any value of type `T: Stackable` may be loaded to/from the stack, by JIT-compiled code. -pub(super) trait Stackable: StackAddressable { - /// The type's representation in Cranelift IR. - const IR_TYPE: Type; -} - -/// Helper definition for storing/loading an address to/from the stack. -pub(super) struct Address; - -impl StackAddressable for Address { - type Underlying = u64; -} - -impl Stackable for Address { - const IR_TYPE: Type = I64; -} - -impl StackAddressable for XValue { - type Underlying = u64; -} - -impl Stackable for XValue { - const IR_TYPE: Type = I64; -} - -impl StackAddressable for Exception { - type Underlying = Exception; -} - -/// Dedicated space on the stack to store a value of the underlying type. -pub(super) struct Slot { - slot: StackSlot, - ptr_type: Type, - _pd: PhantomData, -} - -impl Slot { - /// Create a new slot on the stack, to hold a value of the underlying type of T. - pub(super) fn new(ptr_type: Type, builder: &mut FunctionBuilder<'_>) -> Self { - let slot = builder.create_sized_stack_slot(T::STACK_SLOT_DATA); - - Self { - slot, - ptr_type, - _pd: PhantomData, - } - } - - /// Get a ptr to the (potentially uninitialised) memory of the slot. - pub(super) fn ptr(&self, builder: &mut FunctionBuilder<'_>) -> Value { - builder.ins().stack_addr(self.ptr_type, self.slot, 0) - } -} - -impl Slot { - /// Emit IR to store a value to the current stack slot. - pub(super) fn store(&self, builder: &mut FunctionBuilder<'_>, value: Value) { - builder.ins().stack_store(value, self.slot, 0); - } - - /// Emit IR to load a value from the current stack slot. - /// - /// # Safety - /// - /// The caller must ensure that the memory is initialised - otherwise arbitrary bytes - /// (from uninitialised memory) will be returned. - pub(super) unsafe fn load(&self, builder: &mut FunctionBuilder<'_>) -> Value { - builder.ins().stack_load(T::IR_TYPE, self.slot, 0) - } -} diff --git a/src/riscv/lib/src/kernel_loader.rs b/src/riscv/lib/src/kernel_loader.rs deleted file mode 100644 index f9013a17c9c74afba16e8555cc0278682253940c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/kernel_loader.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::io::Cursor; -use std::io::Seek; -use std::io::SeekFrom; -use std::io::Write; - -use elf::ElfBytes; -use elf::abi::DT_RELA; -use elf::abi::DT_RELASZ; -use elf::abi::ET_DYN; -use elf::abi::ET_EXEC; -use elf::abi::PF_R; -use elf::abi::PF_W; -use elf::abi::PF_X; -use elf::abi::PT_LOAD; -use elf::abi::R_RISCV_RELATIVE; -use elf::endian::LittleEndian; -use elf::file::Class::ELF64; -use elf::file::FileHeader; -use elf::relocation::RelaIterator; -use elf::segment::ProgramHeader; - -use crate::machine_state::memory::Permissions; - -/// Error when parsing and loading the user kernel ELF file -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// Failed to write to memory - #[error("At address {addr:#x}: {msg:?}")] - Write { msg: Option, addr: u64 }, - - /// Failed to parse the ELF file - #[error("Failed to parse ELF file: {0}")] - Elf(#[from] elf::ParseError), -} - -/// Permissions for program regions in memory -pub struct MemoryPermissions { - /// Starting address of the memory region - pub start_address: u64, - - /// Length of the memory region - pub length: u64, - - /// Permissions for the memory region - pub permissions: Permissions, -} - -impl MemoryPermissions { - /// Extract the memory permissions from a load segment. `reloc` can be used to offset the - /// memory region. - pub fn from_load_segment(reloc: Option, segment: &ProgramHeader) -> Self { - let start_address = match reloc { - Some(offset) => offset.wrapping_add(segment.p_vaddr), - None => segment.p_paddr, - }; - - let perms_read = segment.p_flags & PF_R != 0; - let perms_write = segment.p_flags & PF_W != 0; - let perms_exec = segment.p_flags & PF_X != 0; - - MemoryPermissions { - start_address, - length: segment.p_memsz, - - // This is a simplified version of the permissions. It is aligned with what to - // expect in an ELF file. - permissions: if perms_exec { - Permissions::ReadExec - } else if perms_write { - Permissions::ReadWrite - } else if perms_read { - Permissions::Read - } else { - Permissions::None - }, - } - } -} - -/// Program headers extracted from the ELF file -pub struct ProgramHeaders<'a> { - /// Size of each entry in the program headers - pub entry_size: u64, - - /// Number of program headers entries - pub num_entries: u64, - - /// Raw contents - pub contents: &'a [u8], - - /// Memory permissions for each segment - pub permissions: Vec, -} - -/// [LoadResult] is the outcome of loading an ELF file -pub struct LoadResult<'a> { - /// Address at which the entrypoint of the program is located - pub entry: u64, - - /// Raw program headers - pub program_headers: ProgramHeaders<'a>, -} - -/// [Memory] is an interface to the linear array of bytes of the RISC-V virtual machine -pub trait Memory { - /// Writes a slice of bytes at the given physical address in the memory of the virtual machine. - fn write_bytes(&mut self, paddr: u64, bytes: &[u8]) -> Result<(), Error>; - - /// Writes a given number of zeroes at the given physical address. - fn set_zero(&mut self, paddr: u64, len: u64) -> Result<(), Error> { - let bytes = vec![0u8; len as usize]; - self.write_bytes(paddr, &bytes) - } -} - -/// Loads an executable ELF file in the memory of the virtual machine -pub fn load_elf_nonreloc<'a>( - mem: &mut impl Memory, - elf: &ElfBytes<'a, LittleEndian>, - contents: &'a [u8], -) -> Result, Error> { - let loadable_segments = elf - .segments() - .into_iter() - .flat_map(|headers| headers.iter()) - .filter(|header| header.p_type == PT_LOAD); - - // Capacity of 4 should cover most cases: - // .text => rx - // .rodata => r - // .data => rw - // .bss => rw - let mut permissions = Vec::with_capacity(4); - - for segment in loadable_segments { - permissions.push(MemoryPermissions::from_load_segment(None, &segment)); - - // Copy the region from the file to memory. - let segment_contents = &contents[segment.p_offset as usize..][..segment.p_filesz as usize]; - mem.write_bytes(segment.p_paddr, segment_contents)?; - - // If the target memory region is larger than the source region, - // we must fill the gap with 0s. - if segment.p_memsz > segment.p_filesz { - let first_zero = segment.p_paddr.wrapping_add(segment.p_filesz); - let num_zeroes = segment.p_memsz.saturating_sub(segment.p_filesz); - mem.set_zero(first_zero, num_zeroes)?; - } - } - - let program_headers = extract_program_headers(&elf.ehdr, contents, permissions); - - Ok(LoadResult { - entry: elf.ehdr.e_entry, - program_headers, - }) -} - -/// Load a relocatable ELF file at the given `start` address. `elf` is the partially parsed -/// ELF file, while `contents` is the raw ELF file. -pub fn load_elf_reloc<'a>( - mem: &mut impl Memory, - start: u64, - elf: &ElfBytes<'a, LittleEndian>, - contents: &'a [u8], -) -> Result, Error> { - let loadable_segments = elf - .segments() - .into_iter() - .flat_map(|headers| headers.iter()) - .filter(|header| header.p_type == PT_LOAD); - - // Capacity of 4 should cover most cases: - // .text => rx - // .rodata => r - // .data => rw - // .bss => rw - let mut permissions = Vec::with_capacity(4); - - for segment in loadable_segments { - permissions.push(MemoryPermissions::from_load_segment(Some(start), &segment)); - - // Copy the region from the file to memory. - let start_addr = start.wrapping_add(segment.p_vaddr); - let segment_contents = &contents[segment.p_offset as usize..][..segment.p_filesz as usize]; - mem.write_bytes(start_addr, segment_contents)?; - - // If the target memory region is larger than the source region, - // we must fill the gap with 0s. - if segment.p_memsz > segment.p_filesz { - let first_zero = start_addr.wrapping_add(segment.p_filesz); - let num_zeroes = segment.p_memsz.saturating_sub(segment.p_filesz); - mem.set_zero(first_zero, num_zeroes)?; - } - } - - let mut relas_addr = None; - let mut relas_size = None; - - let dynamic_infos = elf - .find_common_data() - .into_iter() - .flat_map(|common| common.dynamic.into_iter()) - .flat_map(|dyn_table| dyn_table.iter()); - - for dyn_info in dynamic_infos { - if dyn_info.d_tag == DT_RELA { - relas_addr = Some(dyn_info.d_ptr()); - } else if dyn_info.d_tag == DT_RELASZ { - relas_size = Some(dyn_info.d_val()); - } - } - - if let Some((addr, size)) = relas_addr.zip(relas_size) { - let relas_data = &contents[addr as usize..][..size as usize]; - let relas = RelaIterator::new(LittleEndian, ELF64, relas_data); - - for rela in relas { - match rela.r_type { - R_RISCV_RELATIVE => { - let relocated = (start as i64 + rela.r_addend).to_ne_bytes(); - let buf = &relocated[..]; - mem.write_bytes(start + rela.r_offset, buf)?; - } - unknown_type => unimplemented!("Unsupported relocation directive {unknown_type}"), - } - } - } - - let program_headers = extract_program_headers(&elf.ehdr, contents, permissions); - - Ok(LoadResult { - entry: elf.ehdr.e_entry + start, - program_headers, - }) -} - -/// Loads an ELF file. If the file is relocatable, loads it at the given `start` address in the memory of the VM. -/// If it is not relocatable, `start` is ignored. -pub fn load_elf<'a>( - mem: &mut impl Memory, - start: u64, - contents: &'a [u8], -) -> Result, Error> { - let elf = ElfBytes::::minimal_parse(contents)?; - - match elf.ehdr.e_type { - ET_EXEC => load_elf_nonreloc(mem, &elf, contents), - ET_DYN => load_elf_reloc(mem, start, &elf, contents), - t => todo!("ELF type {t} is not yet supported"), - } -} - -impl Memory for Cursor -where - Cursor: Write + Seek, -{ - fn write_bytes(&mut self, paddr: u64, bytes: &[u8]) -> Result<(), Error> { - // XXX: Use try-block when it becomes stable. - // https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html - (|| { - self.seek(SeekFrom::Start(paddr))?; - self.write_all(bytes) - })() - .map_err(|err| Error::Write { - msg: Some(err.to_string()), - addr: paddr, - }) - } -} - -/// Extract the raw program headers from the ELF file. -fn extract_program_headers<'a>( - header: &FileHeader, - contents: &'a [u8], - permissions: Vec, -) -> ProgramHeaders<'a> { - let start = header.e_phoff as usize; - let length = header.e_phentsize as usize * header.e_phnum as usize; - let contents = &contents[start..][..length]; - - ProgramHeaders { - entry_size: header.e_phentsize as u64, - num_entries: header.e_phnum as u64, - contents, - permissions, - } -} diff --git a/src/riscv/lib/src/lib.rs b/src/riscv/lib/src/lib.rs deleted file mode 100644 index c8739a130ea28d788060c27085745430055a9df6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -mod array_utils; -mod bits; -mod cache_utils; -mod default; -mod devicetree; -mod instruction_context; -mod interpreter; -pub mod jit; -mod kernel_loader; -pub mod log; -pub mod machine_state; -pub mod parser; -mod program; -pub mod pvm; -mod range_utils; -mod state; -pub mod state_backend; -pub mod stepper; -pub mod storage; -mod traps; diff --git a/src/riscv/lib/src/log.rs b/src/riscv/lib/src/log.rs deleted file mode 100644 index 8105b7d29b95668ddedc045d5c2fed243d27348d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/log.rs +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -#![allow(unused_imports, unused_macros, reason = "Not all events may be used")] - -#[cfg(feature = "log")] -#[doc(hidden)] -pub(crate) mod implementation { - // Usage of [`__tracing_do_not_use_directly`] is allowed here - - pub(crate) use __tracing_do_not_use_directly::debug; - pub(crate) use __tracing_do_not_use_directly::error; - pub(crate) use __tracing_do_not_use_directly::info; - pub(crate) use __tracing_do_not_use_directly::trace; - // Rename `warn` to avoid conflict with a Rust attribute of the same name - pub(crate) use __tracing_do_not_use_directly::warn as warning; -} - -#[cfg(not(feature = "log"))] -#[doc(hidden)] -pub(crate) mod implementation { - macro_rules! error { - ($($ignore:tt)*) => {}; - } - - macro_rules! warning { - ($($ignore:tt)*) => {}; - } - - macro_rules! info { - ($($ignore:tt)*) => {}; - } - - macro_rules! debug { - ($($ignore:tt)*) => {}; - } - - macro_rules! trace { - ($($ignore:tt)*) => {}; - } - - // Export the macros from this module - pub(crate) use debug; - pub(crate) use error; - pub(crate) use info; - pub(crate) use trace; - pub(crate) use warning; -} - -/// Helper macro for logging -/// -/// It is mainly used to: -/// - Define the shape of the macro inputs for logging -/// - Eagerly evaluate expressions used when logging -/// - Forward the macro input to the right logging macro (this avoids duplicating macros) -macro_rules! __log_proxy { - ( - [$macro:ident] - $( - $name:ident - $(= $value:expr)? - , - )* - $msg:literal - $(, $msg_param:expr)* - $(,)? - ) => { - { - // This is a workaround to avoid unused variable warnings - if false { - $( - $( let _ = $value; )? - )* - $( - let _ = $msg_param; - )* - } - - $crate::log::implementation::$macro!( - $($name $(= $value)? ,)* - $msg - $(, $msg_param)* - ); - } - }; -} - -macro_rules! error { - ($($body:tt)*) => { - $crate::log::__log_proxy!([error] $($body)*); - }; -} - -macro_rules! warning { - ($($body:tt)*) => { - $crate::log::__log_proxy!([warning] $($body)*); - }; -} - -macro_rules! info { - ($($body:tt)*) => { - $crate::log::__log_proxy!([info] $($body)*); - }; -} - -macro_rules! debug { - ($($body:tt)*) => { - $crate::log::__log_proxy!([debug] $($body)*); - }; -} - -macro_rules! trace { - ($($body:tt)*) => { - $crate::log::__log_proxy!([trace] $($body)*); - }; -} - -#[doc(hidden)] -pub(crate) use __log_proxy; -#[cfg(feature = "log")] -#[doc(hidden)] -// Exporting the `tracing` crate for use in the API or Sandbox helps -// - use the same `tracing` version across crates -// - avoid a Cargo manifest mess where the same dependency has multiple names -pub use __tracing_do_not_use_directly as tracing_internal; -pub(crate) use debug; -pub(crate) use error; -pub(crate) use info; -pub(crate) use trace; -pub(crate) use warning; diff --git a/src/riscv/lib/src/machine_state.rs b/src/riscv/lib/src/machine_state.rs deleted file mode 100644 index 0bee352eedce9d19d18eb1cbc8ec87df811c441e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state.rs +++ /dev/null @@ -1,1158 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -pub mod block_cache; -mod cache_layouts; -pub(crate) mod csregisters; -pub(crate) mod hart_state; -pub mod instruction; -pub mod memory; -pub(crate) mod mode; -pub(crate) mod registers; -pub(crate) mod reservation_set; - -#[cfg(test)] -extern crate proptest; - -use std::ops::Bound; - -use block_cache::BlockCache; -use block_cache::block::Block; -pub use cache_layouts::CacheLayouts; -pub use cache_layouts::DefaultCacheLayouts; -pub use cache_layouts::TestCacheLayouts; -use csregisters::values::CSRValue; -use hart_state::HartState; -use hart_state::HartStateLayout; -use instruction::Instruction; -use memory::Address; -use memory::BadMemoryAccess; -use memory::Memory; -use memory::MemoryConfig; -use memory::MemoryGovernanceError; - -use crate::bits::u64; -use crate::devicetree; -use crate::parser::instruction::Instr; -use crate::parser::instruction::InstrCacheable; -use crate::parser::instruction::InstrUncacheable; -use crate::parser::instruction::InstrWidth; -use crate::parser::is_compressed; -use crate::parser::parse_compressed_instruction; -use crate::parser::parse_uncompressed_instruction; -use crate::program::Program; -use crate::range_utils::bound_saturating_sub; -use crate::range_utils::less_than_bound; -use crate::range_utils::unwrap_bound; -use crate::state::NewState; -use crate::state_backend as backend; -use crate::state_backend::ManagerReadWrite; -use crate::traps::EnvironException; -use crate::traps::Exception; - -/// Layout for the machine 'run state' - which contains everything required for the running of -/// instructions. -pub type MachineCoreStateLayout = (HartStateLayout, ::Layout); - -/// The part of the machine state required to run (almost all) instructions. -/// -/// Namely, things that are required to fetch instructions, but not run them, should be placed -/// elsewhere in [`MachineState`]. -/// -/// Certain instructions (e.g. `FENCE.I` may invalidate other parts of the state, but this are -/// small in number). -pub struct MachineCoreState { - pub hart: HartState, - pub main_memory: MC::State, -} - -impl MachineCoreState { - /// Handle an [`Exception`] if one was risen during execution - /// of an instruction (also known as synchronous exception) by taking a trap. - /// - /// Return the new address of the program counter, becoming the address of a trap handler. - /// Throw [`EnvironException`] if the exception needs to be treated by the execution enviroment. - pub(crate) fn address_on_exception( - &mut self, - exception: Exception, - current_pc: Address, - ) -> Result - where - M: backend::ManagerReadWrite, - { - if let Ok(exc) = EnvironException::try_from(&exception) { - // We need to commit the PC before returning because the caller (e.g. - // [step]) doesn't commit it eagerly. - self.hart.pc.write(current_pc); - - return Err(exc); - } - - Ok(self.hart.take_trap(exception, current_pc)) - } - - #[inline] - fn handle_step_result( - &mut self, - instr_pc: Address, - result: Result, Exception>, - ) -> Result - where - M: backend::ManagerReadWrite, - { - let pc_update = match result { - Err(exc) => ProgramCounterUpdate::Set(self.address_on_exception(exc, instr_pc)?), - Ok(upd) => upd, - }; - - // Update program couter - let pc = match pc_update { - ProgramCounterUpdate::Set(address) => address, - ProgramCounterUpdate::Next(width) => instr_pc + width as u64, - }; - - self.hart.pc.write(pc); - - Ok(pc) - } - - /// Bind the machine state to the given allocated space. - pub fn bind(space: backend::AllocatedOf, M>) -> Self { - Self { - hart: HartState::bind(space.0), - main_memory: MC::bind(space.1), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf, F::Output> { - ( - self.hart.struct_ref::(), - MC::struct_ref::<_, F>(&self.main_memory), - ) - } - - /// Reset the machine state. - pub fn reset(&mut self) - where - M: backend::ManagerReadWrite, - { - self.hart.reset(memory::FIRST_ADDRESS); - self.main_memory.reset(); - } - - /// Advance [`MachineState`] by executing an [`InstrCacheable`]. - fn run_instr_cacheable( - &mut self, - instr: &InstrCacheable, - ) -> Result, Exception> - where - M: backend::ManagerReadWrite, - { - Instruction::from(instr).run(self) - } -} - -impl NewState for MachineCoreState { - fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - Self { - hart: HartState::new(manager), - main_memory: NewState::new(manager), - } - } -} - -impl Clone for MachineCoreState { - fn clone(&self) -> Self { - Self { - hart: self.hart.clone(), - main_memory: self.main_memory.clone(), - } - } -} - -/// Layout for the machine state - everything required to fetch & run instructions. -pub type MachineStateLayout = ( - MachineCoreStateLayout, - ::BlockCacheLayout, -); - -/// The machine state contains everything required to fetch & run instructions. -pub struct MachineState< - MC: memory::MemoryConfig, - CL: CacheLayouts, - B: Block, - M: backend::ManagerBase, -> { - pub core: MachineCoreState, - pub block_cache: BlockCache, -} - -impl + Clone, M: backend::ManagerClone> - Clone for MachineState -{ - fn clone(&self) -> Self { - Self { - core: self.core.clone(), - block_cache: self.block_cache.clone(), - } - } -} - -/// How to modify the program counter -#[derive(Debug, PartialEq)] -pub enum ProgramCounterUpdate { - /// Jump to a fixed address - Set(AddressRepr), - /// Offset to the next instruction by current instruction width - Next(InstrWidth), -} - -/// Result type when running multiple steps at a time with [`MachineState::step_max`] -#[derive(Debug)] -pub struct StepManyResult { - pub steps: usize, - pub error: Option, -} - -/// Runs a syscall instruction (ecall, ebreak) -macro_rules! run_syscall_instr { - ($state: ident, $run_fn: ident) => {{ Err($state.hart.$run_fn()) }}; -} - -/// Runs a no-arguments instruction (wfi, fenceI) -macro_rules! run_no_args_instr { - ($state: ident, $instr: ident, $run_fn: ident) => {{ - $state.$run_fn(); - Ok(Next($instr.width())) - }}; -} - -impl, M: backend::ManagerBase> - MachineState -{ - /// Allocate a new machine state. - pub fn new(manager: &mut M, block_builder: B::BlockBuilder) -> Self - where - M: backend::ManagerAlloc, - { - Self { - core: MachineCoreState::new(manager), - block_cache: BlockCache::new(manager, block_builder), - } - } - - /// Bind the block cache to the given allocated state and the given [block builder]. - /// - /// [block builder]: Block::BlockBuilder - pub fn bind( - space: backend::AllocatedOf, M>, - block_builder: B::BlockBuilder, - ) -> Self - where - M::ManagerRoot: ManagerReadWrite, - { - Self { - core: MachineCoreState::bind(space.0), - block_cache: BlockCache::bind(space.1, block_builder), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf, F::Output> { - ( - self.core.struct_ref::(), - self.block_cache.struct_ref::(), - ) - } - - /// Reset the machine state. - pub fn reset(&mut self) - where - M: backend::ManagerReadWrite, - { - self.core.reset(); - self.block_cache.reset(); - } - - /// Fetch the 16 bits of an instruction at the given physical address. - #[inline(always)] - fn fetch_instr_halfword(&self, phys_addr: Address) -> Result - where - M: backend::ManagerRead, - { - // TODO: RV-527: Enforce that `Instruction` came from executable part of memory. - // The link to an `Instruction` is too weak. We want statically-typed guarantees that these - // bytes are only used for instructions. - self.core - .main_memory - .read_exec(phys_addr) - .map_err(|_: BadMemoryAccess| Exception::InstructionAccessFault(phys_addr)) - } - - /// Fetch instruction from the address given by program counter - /// The spec stipulates translation is performed for each byte respectively. - /// However, we assume the `raw_pc` is 2-byte aligned. - #[inline] - fn fetch_instr(&mut self, addr: Address) -> Result - where - M: backend::ManagerReadWrite, - { - let first_halfword = self.fetch_instr_halfword(addr)?; - - // The reasons to provide the second half in the lambda is - // because those bytes may be inaccessible or may trigger an exception when read. - // Hence we can't read all 4 bytes eagerly. - let instr = if is_compressed(first_halfword) { - let instr = parse_compressed_instruction(first_halfword); - if let Instr::Cacheable(instr) = instr { - let instr = Instruction::from(&instr); - self.block_cache.push_instr_compressed(addr, instr); - } - - instr - } else { - let next_addr = addr + 2; - let upper = self.fetch_instr_halfword(next_addr)?; - - let combined = ((upper as u32) << 16) | (first_halfword as u32); - let instr = parse_uncompressed_instruction(combined); - if let Instr::Cacheable(instr) = instr { - let instr = Instruction::from(&instr); - self.block_cache.push_instr_uncompressed(addr, instr); - } - - instr - }; - - Ok(instr) - } - - /// Advance [`MachineState`] by executing an [`InstrUncacheable`]. - fn run_instr_uncacheable( - &mut self, - instr: &InstrUncacheable, - ) -> Result, Exception> - where - M: backend::ManagerReadWrite, - { - use ProgramCounterUpdate::Next; - - let core = &mut self.core; - - match instr { - InstrUncacheable::Fence(args) => { - self.core.run_fence(args.pred, args.succ); - Ok(Next(instr.width())) - } - InstrUncacheable::FenceTso(_args) => Err(Exception::IllegalInstruction), - InstrUncacheable::Ebreak => run_syscall_instr!(core, run_ebreak), - - // Privileged instructions - // Trap-Return - InstrUncacheable::Mret => Err(Exception::IllegalInstruction), - InstrUncacheable::Sret => Err(Exception::IllegalInstruction), - // Currently not implemented instruction (part of Smrnmi extension) - InstrUncacheable::Mnret => Err(Exception::IllegalInstruction), - // Supervisor Memory-Management - InstrUncacheable::SFenceVma { asid, vaddr } => { - self.core.run_sfence_vma(*asid, *vaddr)?; - Ok(ProgramCounterUpdate::Next(instr.width())) - } - - // RV32C compressed instructions - InstrUncacheable::CEbreak => run_syscall_instr!(core, run_cebreak), - - // Zifencei instructions - InstrUncacheable::FenceI => run_no_args_instr!(self, instr, run_fencei), - } - } - - /// Advance [`MachineState`] by executing an [`Instr`] - fn run_instr(&mut self, instr: &Instr) -> Result, Exception> - where - M: backend::ManagerReadWrite, - { - match instr { - Instr::Cacheable(i) => self.core.run_instr_cacheable(i), - Instr::Uncacheable(i) => self.run_instr_uncacheable(i), - } - } - - /// Fetch & run the instruction located at address `instr_pc` - fn run_instr_at(&mut self, addr: Address) -> Result, Exception> - where - M: backend::ManagerReadWrite, - { - self.fetch_instr(addr) - .and_then(|instr| self.run_instr(&instr)) - } - - /// Take an interrupt if available, and then - /// perform precisely one [`Instr`] and handle the traps that may rise as a side-effect. - /// - /// The [`Err`] case represents an [`Exception`] to be handled by - /// the execution environment, narrowed down by the type [`EnvironException`]. - #[inline] - pub fn step(&mut self) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - self.step_max_inner(&mut 0, 1) - } - - fn step_max_inner( - &mut self, - steps: &mut usize, - max_steps: usize, - ) -> Result<(), EnvironException> - where - M: backend::ManagerReadWrite, - { - self.block_cache - .complete_current_block(&mut self.core, steps, max_steps)?; - - 'run: while *steps < max_steps { - // Obtain the pc for the next instruction to be executed - let instr_pc = self.core.hart.pc.read(); - - let res = match self.block_cache.get_block(instr_pc) { - Some(mut block) => { - block.run_block(&mut self.core, instr_pc, steps, max_steps)?; - - continue 'run; - } - None => self.run_instr_at(instr_pc), - }; - - self.core.handle_step_result(instr_pc, res)?; - *steps += 1; - } - - Ok(()) - } - - /// Perform as many steps as the given `max_steps` bound allows. Returns the number of retired - /// instructions. - #[inline] - pub fn step_max(&mut self, max_steps: Bound) -> StepManyResult - where - M: backend::ManagerReadWrite, - { - let mut steps = 0; - - loop { - match self.step_max_inner(&mut steps, unwrap_bound(max_steps)) { - Ok(_) => {} - Err(e) => { - return StepManyResult { - steps, - error: Some(e), - }; - } - }; - - if !less_than_bound(steps, max_steps) { - break; - } - } - - StepManyResult { steps, error: None } - } - - /// Similar to [`Self::step_max`] but lets the user handle environment exceptions inside the - /// inner step loop. - #[inline] - pub fn step_max_handle( - &mut self, - mut step_bounds: Bound, - mut handle: impl FnMut(&mut Self, EnvironException) -> Result, - ) -> StepManyResult - where - M: backend::ManagerReadWrite, - { - let mut steps = 0usize; - - let error = loop { - let result = self.step_max(step_bounds); - - steps = steps.saturating_add(result.steps); - step_bounds = bound_saturating_sub(step_bounds, result.steps); - - match result.error { - Some(cause) => { - // Raising the exception is not a completed step. Trying to handle it is. - // We don't have to check against `max_steps` because running the - // instruction that triggered the exception meant that `max_steps > 0`. - steps = steps.saturating_add(1); - step_bounds = bound_saturating_sub(step_bounds, 1); - - match handle(self, cause) { - Ok(may_continue) => { - if !may_continue { - break None; - } - } - - Err(error) => break Some(error), - } - } - None => break None, - } - }; - - StepManyResult { steps, error } - } - - /// Install a program and set the program counter to its start. - pub fn setup_boot( - &mut self, - program: &Program, - initrd: Option<&[u8]>, - ) -> Result<(), MachineError> - where - M: backend::ManagerReadWrite, - { - // Reset hart state & set pc to entrypoint - self.core.hart.reset(program.entrypoint); - // Write program to main memory and point the PC at its start - for (addr, data) in program.segments.iter() { - self.core.main_memory.write_all(*addr, data)?; - } - - // Set booting Hart ID (a0) to 0 - self.core.hart.xregisters.write(registers::a0, 0); - - // Load the initial program into memory - let initrd_addr = program - .segments - .iter() - .map(|(base, data)| base + data.len() as Address) - .max() - .unwrap_or(memory::FIRST_ADDRESS); - - // Write initial ramdisk, if any - let (dtb_addr, initrd) = if let Some(initrd) = initrd { - self.core.main_memory.write_all(initrd_addr, initrd)?; - let length = initrd.len() as u64; - let dtb_options = devicetree::InitialRamDisk { - start: initrd_addr, - length, - }; - let dtb_addr = initrd_addr + length; - (dtb_addr, Some(dtb_options)) - } else { - (initrd_addr, None) - }; - - // Write device tree to memory - let fdt = devicetree::generate::(initrd)?; - self.core.main_memory.write_all(dtb_addr, fdt.as_slice())?; - - // Point DTB boot argument (a1) at the written device tree - self.core.hart.xregisters.write(registers::a1, dtb_addr); - - // Make sure to forward all exceptions and interrupts to supervisor mode - self.core - .hart - .csregisters - .write(csregisters::CSRegister::medeleg, CSRValue::from(!0u64)); - self.core - .hart - .csregisters - .write(csregisters::CSRegister::mideleg, CSRValue::from(!0u64)); - - Ok(()) - } -} - -/// Errors that occur from interacting with the [MachineState] -#[derive(Debug, thiserror::Error)] -pub enum MachineError { - #[error("Error while accessing memory")] - MemoryError(#[from] BadMemoryAccess), - - #[error("Error while govering memory")] - MemoryGovernanceError(#[from] MemoryGovernanceError), - - #[error("Device tree error: {0}")] - DeviceTreeError(#[from] vm_fdt::Error), - - #[error("Memory too small to properly configure the machine")] - MemoryTooSmall, -} - -#[cfg(test)] -mod tests { - use std::ops::Bound; - - use proptest::prop_assert_eq; - use proptest::proptest; - - use super::MachineState; - use super::MachineStateLayout; - use super::block_cache::block::Interpreted; - use super::block_cache::block::InterpretedBlockBuilder; - use super::instruction::Instruction; - use super::instruction::OpCode; - use super::instruction::tagged_instruction::TaggedArgs; - use super::instruction::tagged_instruction::TaggedInstruction; - use super::instruction::tagged_instruction::TaggedRegister; - use super::registers::XRegister; - use crate::backend_test; - use crate::bits::u16; - use crate::default::ConstDefault; - use crate::machine_state::DefaultCacheLayouts; - use crate::machine_state::TestCacheLayouts; - use crate::machine_state::csregisters::CSRRepr; - use crate::machine_state::csregisters::CSRegister; - use crate::machine_state::csregisters::xstatus; - use crate::machine_state::csregisters::xstatus::MStatus; - use crate::machine_state::memory; - use crate::machine_state::memory::M1M; - use crate::machine_state::memory::M4K; - use crate::machine_state::memory::M8K; - use crate::machine_state::memory::Memory; - use crate::machine_state::memory::PAGE_SIZE; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::nz; - use crate::machine_state::registers::ra; - use crate::machine_state::registers::t0; - use crate::machine_state::registers::t1; - use crate::machine_state::registers::t2; - use crate::parser::XRegisterParsed::*; - use crate::parser::instruction::Instr; - use crate::parser::instruction::InstrCacheable; - use crate::parser::instruction::InstrWidth; - use crate::parser::instruction::SBTypeArgs; - use crate::parser::instruction::SplitITypeArgs; - use crate::parser::parse_block; - use crate::state_backend::FnManagerIdent; - use crate::state_backend::test_helpers::TestBackendFactory; - use crate::state_backend::test_helpers::assert_eq_struct; - use crate::state_backend::test_helpers::copy_via_serde; - use crate::traps::EnvironException; - use crate::traps::Exception; - use crate::traps::TrapContext; - - backend_test!(test_step, F, { - let state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - pc_addr_offset in 0..250_u64, - jump_addr in 0..250_u64, - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - - let init_pc_addr = memory::FIRST_ADDRESS + pc_addr_offset * 4; - let jump_addr = memory::FIRST_ADDRESS + jump_addr * 4; - - // Instruction which performs a unit op (AUIPC with t0) - const T2_ENC: u32 = 0b0_0111; // x7 - - state.core.hart.pc.write(init_pc_addr); - state.core.main_memory.write_instruction_unchecked::(init_pc_addr, (T2_ENC << 7) | - 0b0010111).expect("Storing instruction should succeed"); - state.step().expect("should not raise trap to EE"); - prop_assert_eq!(state.core.hart.xregisters.read(t2), init_pc_addr); - prop_assert_eq!(state.core.hart.pc.read(), init_pc_addr + 4); - - // Instruction which updates pc by returning an address - // t3 = jump_addr, (JALR imm=0, rs1=t3, rd=t0) - const T0_ENC: u32 = 0b00101; // x5 - const OP_JALR: u32 = 0b110_0111; - const F3_0: u32 = 0b000; - - state.core.hart.pc.write(init_pc_addr); - state.core.main_memory.write_instruction_unchecked(init_pc_addr, (T2_ENC << 15) | (F3_0 << 12) | (T0_ENC << 7) | OP_JALR).unwrap(); - state.core.hart.xregisters.write(t2, jump_addr); - - // Since we've written a new instruction to the init_pc_addr - we need to - // invalidate the instruction cache. - state.run_fencei(); - - state.step().expect("should not raise trap to EE"); - prop_assert_eq!(state.core.hart.xregisters.read(t0), init_pc_addr + 4); - prop_assert_eq!(state.core.hart.pc.read(), jump_addr); - }); - }); - - backend_test!(test_step_env_exc, F, { - let state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - pc_addr_offset in 0..200_u64, - stvec_offset in 10..20_u64, - mtvec_offset in 25..35_u64, - )| { - let mut state = state_cell.borrow_mut(); - state.reset(); - - let init_pc_addr = memory::FIRST_ADDRESS + pc_addr_offset * 4; - let stvec_addr = init_pc_addr + 4 * stvec_offset; - let mtvec_addr = init_pc_addr + 4 * mtvec_offset; - - const ECALL: u32 = 0b111_0011; - - // stvec is in DIRECT mode - state.core.hart.csregisters.write(CSRegister::stvec, stvec_addr); - // mtvec is in VECTORED mode - state.core.hart.csregisters.write(CSRegister::mtvec, mtvec_addr | 1); - - // TEST: Raise ECALL exception ==>> environment exception - state.core.hart.pc.write(init_pc_addr); - state.core.main_memory.write_instruction_unchecked(init_pc_addr, ECALL).unwrap(); - let e = state.step() - .expect_err("should raise Environment Exception"); - assert_eq!(e, EnvironException::EnvCall); - prop_assert_eq!(state.core.hart.pc.read(), init_pc_addr); - }); - }); - - backend_test!(test_step_exc_us, F, { - let state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let state_cell = std::cell::RefCell::new(state); - - proptest!(|( - pc_addr_offset in 0..200_u64, - stvec_offset in 10..20_u64, - )| { - // Raise exception, take trap from U-mode to S-mode (test delegation takes place) - let mut state = state_cell.borrow_mut(); - state.reset(); - - let init_pc_addr = memory::FIRST_ADDRESS + pc_addr_offset * 4; - let stvec_addr = init_pc_addr + 4 * stvec_offset; - - // stvec is in VECTORED mode - state.core.hart.csregisters.write(CSRegister::stvec, stvec_addr | 1); - - let bad_address = memory::FIRST_ADDRESS.wrapping_sub((pc_addr_offset + 10) * 4); - let medeleg_val = (1 << Exception::IllegalInstruction.exception_code()) | (1 << Exception::InstructionAccessFault(bad_address).exception_code()); - state.core.hart.pc.write(bad_address); - state.core.hart.csregisters.write(CSRegister::medeleg, medeleg_val); - - state.step().expect("should not raise environment exception"); - // pc should be stvec_addr since exceptions aren't offsetted - // even in VECTORED mode, only interrupts - let mstatus: MStatus = state.core.hart.csregisters.read(CSRegister::mstatus); - assert_eq!(state.core.hart.pc.read(), stvec_addr); - assert_eq!(mstatus.spp(), xstatus::SPPValue::User); - assert_eq!(state.core.hart.csregisters.read::(CSRegister::sepc), bad_address); - assert_eq!(state.core.hart.csregisters.read::(CSRegister::scause), 1); - }); - }); - - // Test that the machine state does not behave differently when potential ephermeral state is - // reset that may impact instruction caching. - backend_test!(test_instruction_cache, F, { - // Instruction that writes the value in t1 to the address t0. - const I_WRITE_T1_TO_ADDRESS_T0: u32 = 0b0011000101010000000100011; - assert_eq!(parse_block(&I_WRITE_T1_TO_ADDRESS_T0.to_le_bytes()), [ - Instr::Cacheable(InstrCacheable::Sw(SBTypeArgs { - rs1: t0, - rs2: t1, - imm: 0, - })) - ]); - - // Instruction that loads 6 into t2. - const I_LOAD_6_INTO_T2: u32 = 0b11000000000001110010011; - assert_eq!(parse_block(&I_LOAD_6_INTO_T2.to_le_bytes()), [ - Instr::Cacheable(InstrCacheable::Addi(SplitITypeArgs { - rd: NonZero(nz::t2), - rs1: X0, - imm: 6, - })) - ]); - - // Instruction that loads 5 into t2. - const I_LOAD_5_INTO_T2: u32 = 0b10100000000001110010011; - assert_eq!(parse_block(&I_LOAD_5_INTO_T2.to_le_bytes()), [ - Instr::Cacheable(InstrCacheable::Addi(SplitITypeArgs { - rd: NonZero(nz::t2), - rs1: X0, - imm: 5, - })) - ]); - - type LocalLayout = MachineStateLayout; - - type BlockRunner = Interpreted::Manager>; - - type LocalMachineState = - MachineState, ::Manager>; - - // Configure the machine state. - let base_state = { - let mut state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - state.reset(); - state.core.main_memory.set_all_readable_writeable(); - - let start_ram = memory::FIRST_ADDRESS; - - // Write the instructions to the beginning of the main memory and point the program - // counter at the first instruction. - state.core.hart.pc.write(start_ram); - let instrs: [u8; 8] = [I_WRITE_T1_TO_ADDRESS_T0, I_LOAD_5_INTO_T2] - .into_iter() - .flat_map(u32::to_le_bytes) - .collect::>() - .try_into() - .unwrap(); - state - .core - .main_memory - .write_all(start_ram, &instrs) - .unwrap(); - - // Configure the machine in such a way that the first instruction will override the - // second instruction. The second instruction behaves differently. - let address_to_write = start_ram + 4; - state.core.hart.xregisters.write(t0, address_to_write); - state - .core - .hart - .xregisters - .write(t1, I_LOAD_6_INTO_T2 as u64); - - state - }; - - // Perform 2 steps consecutively in one backend. - let state = { - let mut state = LocalMachineState::::bind( - copy_via_serde::(&base_state.struct_ref::()), - InterpretedBlockBuilder, - ); - - state.step().unwrap(); - state.step().unwrap(); - - state - }; - - // Perform 2 steps separately in another backend by re-binding the state between steps. - let alt_state = { - let alt_state = { - let mut state = LocalMachineState::::bind( - copy_via_serde::(&base_state.struct_ref::()), - InterpretedBlockBuilder, - ); - state.step().unwrap(); - state - }; - - { - let mut state = LocalMachineState::::bind( - copy_via_serde::(&alt_state.struct_ref::()), - InterpretedBlockBuilder, - ); - state.step().unwrap(); - state - } - }; - - // The two backends should have the same state. - assert_eq!( - state.core.hart.xregisters.read(t2), - alt_state.core.hart.xregisters.read(t2) - ); - - assert_eq_struct( - &state.struct_ref::(), - &alt_state.struct_ref::(), - ); - }); - - // Ensure that cloning the machine state does not result in a stack overflow - backend_test!(test_machine_state_cloneable, F, { - let state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let second = state.clone(); - - assert_eq_struct( - &state.struct_ref::(), - &second.struct_ref::(), - ); - }); - - backend_test!(test_block_cache_crossing_pages_creates_new_block, F, { - let mut state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let uncompressed_bytes = 0x5307b3; - - let uncompressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::Add, - args: TaggedArgs { - rd: nz::a5.into(), - rs1: nz::t1.into(), - rs2: nz::t0.into(), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let start_ram = memory::FIRST_ADDRESS; - - // Write the instructions to the beginning of the main memory and point the program - // counter at the first instruction. - let phys_addr = start_ram + PAGE_SIZE.get() - 6; - - state.core.hart.pc.write(phys_addr); - - for offset in 0..3 { - state - .core - .main_memory - .write_instruction_unchecked(phys_addr + offset * 4, uncompressed_bytes) - .unwrap(); - } - - state.step_max(Bound::Included(3)); - - assert!(state.block_cache.get_block(phys_addr).is_some()); - assert_eq!( - vec![uncompressed], - state.block_cache.get_block_instr(phys_addr) - ); - - assert!(state.block_cache.get_block(phys_addr + 4).is_none()); - - assert!(state.block_cache.get_block(phys_addr + 8).is_some()); - assert_eq!( - vec![uncompressed], - state.block_cache.get_block_instr(phys_addr + 8) - ); - }); - - // The block cache & instruction cache have separate cache invalidation/overwriting. - // While running with an unbounded number of steps this is fine, there is a potential for - // divergence when the number of steps to be run is less than the block size of the next block. - // - // Namely: - // - we create a block at A, which jumps to address B - // - fetch instructions at B overwrites some of the _instruction cache_ entries of Block(A), - // without affecting the block itself, and then jumps back to A - // - we don't have enough steps to execute Block(A) directly, so fall back to fetch/parse/run - // - A different set of instructions is executed than would be if there were enough steps - // remaining to run Block(A). - backend_test!(test_block_cache_instruction_cache_determinism, F, { - // We make the wrap-around of the instruction cache much smaller than the wrap-around - // of the block-cache, allowing instruction cache entries to be overwritten without - // overwriting the related block-cache entries. - // - // Note that we are jumping 2 * CACHE_SIZE to achieve the wrap-around, as non u16-aligned - // addresses cannot be cached, so CACHE_SIZE corresponds to 2*CACHE_SIZE addresses. - let auipc_bytes: u32 = 0x517; - let cj_bytes: u16 = 0xa8b5; - - let block_a = [ - // Store current instruction counter - Instruction::try_from(TaggedInstruction { - opcode: OpCode::AddImmediateToPC, - args: TaggedArgs { - rd: nz::a0.into(), - imm: 0, - rs1: TaggedRegister::X(XRegister::x1), - rs2: TaggedRegister::X(XRegister::x1), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - // this represents a CJ instruction. - Instruction::try_from(TaggedInstruction { - opcode: OpCode::J, - args: TaggedArgs { - imm: 128 - 4, - width: InstrWidth::Compressed, - rd: nz::ra.into(), - rs1: nz::ra.into(), - rs2: nz::ra.into(), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - ]; - - // InstrCacheable::CJ(CJTypeArgs { imm: 1024 }) - let overwrite_bytes = 0xa101; - - let clui_bytes: u16 = 0x65a9; - let addiw_bytes: u32 = 0x1015859b; - let csw_bytes: u16 = 0xc10c; - let jalr_bytes: u32 = 0x50067; - let block_b = [ - Instruction::try_from(TaggedInstruction { - opcode: OpCode::Li, - args: TaggedArgs { - rd: nz::a1.into(), - imm: (u16::bits_subset(overwrite_bytes, 15, 12) as i64) << 12, - rs1: nz::ra.into(), - rs2: nz::ra.into(), - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - Instruction::try_from(TaggedInstruction { - opcode: OpCode::AddWordImmediate, - args: TaggedArgs { - rd: nz::a1.into(), - rs1: a1.into(), - imm: u16::bits_subset(overwrite_bytes, 11, 0) as i64, - rs2: TaggedRegister::X(XRegister::x1), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - Instruction::try_from(TaggedInstruction { - opcode: OpCode::X32Store, - args: TaggedArgs { - rd: ra.into(), - rs1: a0.into(), - rs2: a1.into(), - imm: 0, - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - Instruction::try_from(TaggedInstruction { - opcode: OpCode::Jr, - args: TaggedArgs { - rd: nz::ra.into(), - rs1: nz::a0.into(), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - ]; - - let phys_addr = memory::FIRST_ADDRESS; - - let block_b_addr = phys_addr + 128; - - let mut state = MachineState::, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - state.core.main_memory.set_all_readable_writeable(); - - // Write the instructions to the beginning of the main memory and point the program - // counter at the first instruction. - state.core.hart.pc.write(phys_addr); - - // block A - state - .core - .main_memory - .write_instruction_unchecked(phys_addr, auipc_bytes) - .unwrap(); - state - .core - .main_memory - .write_instruction_unchecked(phys_addr + 4, cj_bytes) - .unwrap(); - - // block B - state - .core - .main_memory - .write_instruction_unchecked(block_b_addr, clui_bytes) - .unwrap(); - state - .core - .main_memory - .write_instruction_unchecked(block_b_addr + 2, addiw_bytes) - .unwrap(); - state - .core - .main_memory - .write_instruction_unchecked(block_b_addr + 6, csw_bytes) - .unwrap(); - state - .core - .main_memory - .write_instruction_unchecked(block_b_addr + 8, jalr_bytes) - .unwrap(); - - // Overwritten jump dest - state - .core - .main_memory - .write_instruction_unchecked(phys_addr + 1024, overwrite_bytes) - .unwrap(); - - state.step_max(Bound::Included(block_a.len())); - assert_eq!(block_b_addr, state.core.hart.pc.read()); - - state.step_max(Bound::Included(block_b.len())); - assert_eq!(phys_addr, state.core.hart.pc.read()); - - assert!(state.block_cache.get_block(phys_addr).is_some()); - assert_eq!( - block_a.as_slice(), - state.block_cache.get_block_instr(phys_addr).as_slice() - ); - - assert!(state.block_cache.get_block(block_b_addr).is_some()); - assert_eq!( - block_b.as_slice(), - state.block_cache.get_block_instr(block_b_addr).as_slice() - ); - - let mut state_step = state.clone(); - - let result = state.step_max(Bound::Included(block_a.len())); - assert_eq!(result.steps, block_a.len()); - - for _ in 0..block_a.len() { - let result = state_step.step_max(Bound::Included(1)); - assert_eq!(result.steps, 1); - } - - assert_eq_struct( - &state.struct_ref::(), - &state_step.struct_ref::(), - ); - }); -} diff --git a/src/riscv/lib/src/machine_state/block_cache.rs b/src/riscv/lib/src/machine_state/block_cache.rs deleted file mode 100644 index b5c4a4b05be86dc0e06f14112fbea522bd15bb52..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/block_cache.rs +++ /dev/null @@ -1,1387 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! The block cache maps certain physical addresses to contiguous sequences of instructions, or -//! 'blocks'. These blocks will never cross page boundaries. -//! -//! Blocks are sets of instructions that are often - but not always - executed immediately after each -//! other. The main exceptions to this are: -//! - instructions may trigger exceptions (e.g. `BadMemoryAccess` when interacting with memory) -//! - branching instructions -//! -//! Specifically, blocks that contain a backwards branching instruction will often terminate with -//! that instruction, and blocks that contain a forwards jump will often contain those internally. -//! -//! This is due to the recommended behaviour mentioned in the ISA: -//! > Software should also assume that backward branches will be predicted taken and forward branches -//! > as not taken, at least the first time they are encountered. -//! -//! Therefore, sets of instructions that form tight loops will naturally form a block, and -//! sequences that may branch elsewhere - but normally fall-through - also form a block. -//! -//! Blocks must never cross page boundaries, as two pages that lie next to each other in physical -//! memory have no guarantees of being consecutive in virtual memory - even if they are at one -//! point. -//! -//! # Determinism -//! -//! Some special care is required when thinking about block execution -//! with respect to determinism. -//! -//! Blocks fundamentally rely on being executed 'as a whole'. This is -//! required to be able to make the most of various optimisations we -//! can apply to groups of instructions, while ensuring that we are -//! compatible with the remaining infrastructure of the stepper at block -//! entry/exit. -//! For example, this could include something as simple as not -//! updating the step counter until we exit a block. -//! -//! Only ever running blocks when sufficient steps remain, however, -//! causes determinism issues. Namely, when insufficient steps remain -//! to run a block, what do we do? -//! -//! The obvious solution is to fall back to the instruction cache, and -//! continue our *fetch/parse/run* cycle. This exact solution, however, -//! causes divergence: the instructions we end up executing from the -//! instruction cache, could be completely different to those stored in -//! the block cache, for the same physical address. -//! -//! This can occur due to the differring nature of cache entries between -//! the instruction & block cache: entries in the instruction cache are -//! 1 instruction per slot, whereas in the block cache it's instead a -//! block (a sequence of instructions) per slot. Therefore, an entry -//! getting overriden in the instruction/block cache at address *A*, does -//! not invalidate all instructions in the block cache that correspond -//! to *A*, as they could also exist in blocks at nearby preceding -//! addresses. -//! -//! ## Solution -//! -//! Instead, we introduce the notion of a [`PartialBlock`], that can -//! remember that we were executing a _specific_ entry in the block cache -//! - and indeed the progress made. -//! -//! Then, when insufficient steps are remaining to run a block in full, -//! we proceed to run the block anyway, but step-by-step. Once we -//! exhaust any remaining steps, we save progress in a partial block, -//! and execute the remainder of it with [`BlockCache::complete_current_block`] -//! on the next iteration. -//! -//! Since we now guarantee that we always execute the _same_ set of instructions, -//! no matter how many steps are remaining, we solve this possible divergence. -//! -//! # Dispatch -//! -//! The method of dispatch for Blocks can be one of several mechanisms, the current -//! default being [`Interpreted`]. -//! -//! [`Interpreted`]: block::Interpreted - -pub mod block; -pub mod metrics; - -use std::marker::PhantomData; - -use block::Block; - -use super::MachineCoreState; -use super::ProgramCounterUpdate; -use super::instruction::Instruction; -use super::instruction::RunInstr; -use super::memory::Address; -use super::memory::MemoryConfig; -use crate::cache_utils::FenceCounter; -use crate::cache_utils::Sizes; -use crate::machine_state::instruction::Args; -use crate::machine_state::memory::OFFSET_MASK; -use crate::machine_state::memory::PAGE_SIZE; -use crate::parser::instruction::InstrWidth; -use crate::state::NewState; -use crate::state_backend; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::EnrichedCell; -use crate::state_backend::EnrichedValue; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::ManagerWrite; -use crate::state_backend::Ref; -use crate::state_backend::proof_backend; -use crate::storage::Hash; -use crate::storage::HashError; -use crate::traps::EnvironException; -use crate::traps::Exception; - -/// The maximum number of instructions that may be contained in a block. -pub const CACHE_INSTR: usize = 20; - -/// Bindings for deriving an [`ICall`] from an [`Instruction`] via the [`EnrichedCell`] mechanism. -pub struct ICallPlaced { - _pd0: PhantomData, - _pd1: PhantomData, -} - -impl EnrichedValue for ICallPlaced { - type E = Instruction; - - type D = ICall; -} - -/// A function derived from an [OpCode] that can be directly run over the [MachineCoreState]. -/// -/// This allows static dispatch of this function during block construction, -/// rather than for each instruction, during each block execution. -/// -/// [OpCode]: super::instruction::OpCode -pub struct ICall { - run_instr: RunInstr, -} - -impl Clone for ICall { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ICall {} - -impl ICall { - // SAFETY: This function must be called with an `Args` belonging to the same `OpCode` as - // the one used to dispatch this function. - #[inline(always)] - unsafe fn run( - &self, - args: &Args, - core: &mut MachineCoreState, - ) -> Result, Exception> { - unsafe { (self.run_instr)(args, core) } - } -} - -impl<'a, MC: MemoryConfig, M: ManagerReadWrite> From<&'a Instruction> for ICall { - fn from(value: &'a Instruction) -> Self { - let run_instr = value.opcode.to_run::(); - Self { run_instr } - } -} - -/// Layout for an address cell -pub struct AddressCellLayout; - -impl state_backend::Layout for AddressCellLayout { - type Allocated = Cell; -} - -impl state_backend::CommitmentLayout for AddressCellLayout { - fn state_hash(state: AllocatedOf) -> Result { - as state_backend::CommitmentLayout>::state_hash(state) - } -} - -impl state_backend::ProofLayout for AddressCellLayout { - fn to_merkle_tree( - state: state_backend::RefProofGenOwnedAlloc, - ) -> Result { - as state_backend::ProofLayout>::to_merkle_tree(state) - } - - fn from_proof(proof: state_backend::ProofTree) -> state_backend::FromProofResult { - as state_backend::ProofLayout>::from_proof(proof) - } - - fn partial_state_hash( - state: state_backend::RefVerifierAlloc, - proof: state_backend::ProofTree, - ) -> Result { - as state_backend::ProofLayout>::partial_state_hash(state, proof) - } -} - -/// The layout of block cache entries, see [`Cached`] for more information. -pub type CachedLayout = ( - AddressCellLayout, - Atom, - Atom, - [Atom; CACHE_INSTR], -); - -/// Block cache entry. -/// -/// Contains the physical address & fence counter for validity checks, the -/// underlying [`Block`] state. -pub struct Cached, M: ManagerBase> { - block: B, - address: Cell, - fence_counter: Cell, - _pd: PhantomData, -} - -impl, M: ManagerBase> Cached { - fn bind(space: AllocatedOf) -> Self - where - M::ManagerRoot: ManagerReadWrite, - { - Self { - address: space.0, - fence_counter: space.1, - block: B::bind((space.2, space.3)), - _pd: PhantomData, - } - } - - fn invalidate(&mut self) - where - M: ManagerWrite, - { - self.address.write(!0); - self.block.invalidate(); - } - - fn reset(&mut self) - where - M: ManagerReadWrite, - { - self.address.write(!0); - self.fence_counter.write(FenceCounter::INITIAL); - self.block.reset(); - } - - fn start_block(&mut self, block_addr: Address, fence_counter: FenceCounter) - where - M: ManagerWrite, - { - self.address.write(block_addr); - self.block.start_block(); - self.fence_counter.write(fence_counter); - } - - fn struct_ref<'a, F: FnManager>>(&'a self) -> AllocatedOf { - let (len_instr, instr) = self.block.struct_ref::<'a, F>(); - ( - self.address.struct_ref::(), - self.fence_counter.struct_ref::(), - len_instr, - instr, - ) - } -} - -impl, M: ManagerBase> NewState for Cached { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - address: Cell::new_with(manager, !0), - block: B::new(manager), - fence_counter: Cell::new(manager), - _pd: PhantomData, - } - } -} - -impl + Clone, M: ManagerClone> Clone for Cached { - fn clone(&self) -> Self { - Self { - address: self.address.clone(), - block: self.block.clone(), - fence_counter: self.fence_counter.clone(), - _pd: PhantomData, - } - } -} - -/// The default instruction cache index bits. -pub const DEFAULT_CACHE_BITS: usize = 20; - -/// The default instruction cache size. -pub const DEFAULT_CACHE_SIZE: usize = 1 << DEFAULT_CACHE_BITS; - -/// The default instruction cache index bits for tests. -pub const TEST_CACHE_BITS: usize = 12; - -/// The default instruction cache for tests. -pub const TEST_CACHE_SIZE: usize = 1 << TEST_CACHE_BITS; - -/// Layout of a partial block. -pub type PartialBlockLayout = (Atom
, Atom, Atom); - -/// Structure used to remember that a block was only partway executed -/// before needing to pause due to `steps == steps_max`. -/// -/// If a block is being partially executed, if either: -/// - an error occurs -/// - a jump or branch occurs then the partial block is reset, and execution will continue with a -/// potentially different block. -pub struct PartialBlock { - phys_addr: Cell, - in_progress: Cell, - progress: Cell, -} - -impl Clone for PartialBlock { - fn clone(&self) -> Self { - Self { - phys_addr: self.phys_addr.clone(), - in_progress: self.in_progress.clone(), - progress: self.progress.clone(), - } - } -} - -impl PartialBlock { - fn bind(space: AllocatedOf) -> Self { - Self { - phys_addr: space.0, - in_progress: space.1, - progress: space.2, - } - } - - fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf { - ( - self.phys_addr.struct_ref::(), - self.in_progress.struct_ref::(), - self.progress.struct_ref::(), - ) - } - - fn reset(&mut self) - where - M: ManagerWrite, - { - self.in_progress.write(false); - self.phys_addr.write(0); - self.progress.write(0); - } - - /// Run a block against the machine state. - /// - /// When calling this function, there must be no partial block in progress. To ensure - /// this, you must always run [`BlockCache::complete_current_block`] prior to fetching - /// and running a new block. - #[cold] - fn run_block_partial, MC: MemoryConfig>( - &mut self, - core: &mut MachineCoreState, - steps: &mut usize, - max_steps: usize, - entry: &mut Cached, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - // start a new block - self.in_progress.write(true); - self.progress.write(0); - self.phys_addr.write(entry.address.read()); - - self.run_partial_inner(core, steps, max_steps, entry) - } - - fn run_partial_inner, MC: MemoryConfig>( - &mut self, - core: &mut MachineCoreState, - steps: &mut usize, - max_steps: usize, - entry: &mut Cached, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - // Protect against partial blocks being executed when - // no steps are remaining - if *steps >= max_steps { - return Ok(()); - } - - let mut progress = self.progress.read(); - let mut instr_pc = core.hart.pc.read(); - - let range = progress as usize..; - for instr in entry.block.instr()[range].iter() { - match run_instr(instr, core) { - Ok(ProgramCounterUpdate::Next(width)) => { - instr_pc += width as u64; - core.hart.pc.write(instr_pc); - *steps += 1; - progress += 1; - - if *steps >= max_steps { - break; - } - } - Ok(ProgramCounterUpdate::Set(instr_pc)) => { - // Setting the instr_pc implies execution continuing - // elsewhere - and no longer within the current block. - core.hart.pc.write(instr_pc); - *steps += 1; - self.reset(); - return Ok(()); - } - Err(e) => { - self.reset(); - // Exceptions lead to a new address being set to handle it, - // with no guarantee of it being the next instruction. - core.handle_step_result(instr_pc, Err(e))?; - // If we succesfully handled an error, need to increment steps one more. - *steps += 1; - return Ok(()); - } - } - } - - if progress as usize == entry.block.num_instr() { - // We finished the block in exactly the number of steps left - self.reset(); - } else { - // Remember the progress made through the block, when we later - // continue executing it - self.progress.write(progress); - } - - Ok(()) - } -} - -impl NewState for PartialBlock { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - phys_addr: Cell::new(manager), - in_progress: Cell::new(manager), - progress: Cell::new(manager), - } - } -} - -/// Trait for capturing the different possible layouts of the instruction cache (i.e. -/// controlling the number of cache entries present). -pub trait BlockCacheLayout: state_backend::CommitmentLayout + state_backend::ProofLayout { - type Entries, M: ManagerBase>: NewState; - - type Sizes; - - fn bind( - space: state_backend::AllocatedOf, - block_builder: B::BlockBuilder, - ) -> BlockCache - where - Self: Sized, - M: state_backend::ManagerBase, - M::ManagerRoot: ManagerReadWrite, - MC: MemoryConfig, - B: Block; - - fn entry, M: ManagerBase>( - entries: &Self::Entries, - phys_addr: Address, - ) -> &Cached; - - fn entry_mut, M: ManagerBase>( - entries: &mut Self::Entries, - phys_addr: Address, - ) -> &mut Cached; - - fn entries_reset, M: ManagerReadWrite>( - entries: &mut Self::Entries, - ); - - fn struct_ref<'a, MC: MemoryConfig, B: Block, M: ManagerBase, F: FnManager>>( - cache: &'a BlockCache, - ) -> AllocatedOf - where - Self: Sized; - - fn clone_entries + Clone, M: ManagerClone>( - entries: &Self::Entries, - ) -> Self::Entries; -} - -/// The layout of the block cache. -pub type Layout = ( - AddressCellLayout, - AddressCellLayout, - Atom, - PartialBlockLayout, - Sizes, -); - -impl BlockCacheLayout for Layout { - type Entries, M: ManagerBase> = Box<[Cached; SIZE]>; - - type Sizes = Sizes; - - fn bind( - space: state_backend::AllocatedOf, - block_builder: B::BlockBuilder, - ) -> BlockCache - where - Self: Sized, - M: state_backend::ManagerBase, - M::ManagerRoot: ManagerReadWrite, - MC: MemoryConfig, - B: Block, - { - BlockCache { - current_block_addr: space.0, - next_instr_addr: space.1, - fence_counter: space.2, - partial_block: PartialBlock::bind(space.3), - entries: space - .4 - .into_iter() - .map(|c| Cached::bind(c)) - .collect::>() - .try_into() - .map_err(|_| "mismatching vector lengths for instruction cache") - .unwrap(), - block_builder, - } - } - - fn entry, M: ManagerBase>( - entries: &Self::Entries, - phys_addr: Address, - ) -> &Cached { - &entries[Self::Sizes::cache_index(phys_addr)] - } - - fn entry_mut, M: ManagerBase>( - entries: &mut Self::Entries, - phys_addr: Address, - ) -> &mut Cached { - &mut entries[Self::Sizes::cache_index(phys_addr)] - } - - fn entries_reset, M: ManagerReadWrite>( - entries: &mut Self::Entries, - ) { - entries.iter_mut().for_each(Cached::reset) - } - - fn struct_ref< - 'a, - MC: MemoryConfig, - B: Block, - M: ManagerBase, - F: FnManager>, - >( - cache: &'a BlockCache, - ) -> AllocatedOf { - ( - cache.current_block_addr.struct_ref::(), - cache.next_instr_addr.struct_ref::(), - cache.fence_counter.struct_ref::(), - cache.partial_block.struct_ref::(), - cache - .entries - .iter() - .map(|entry| entry.struct_ref::()) - .collect(), - ) - } - - fn clone_entries + Clone, M: ManagerClone>( - entries: &Self::Entries, - ) -> Self::Entries { - entries - .to_vec() - .try_into() - .map_err(|_| "mismatching vector lengths in block cache") - .unwrap() - } -} - -/// The block cache - caching sequences of instructions by physical address. -/// -/// The number of entries is controlled by the `BCL` layout parameter. -pub struct BlockCache, MC: MemoryConfig, M: ManagerBase> { - current_block_addr: Cell, - next_instr_addr: Cell, - fence_counter: Cell, - partial_block: PartialBlock, - entries: BCL::Entries, - /// The block builder is the mechanism used to construct blocks for calling in a - /// (potentially) more efficient manner. For example - by JIT compiling them. - pub block_builder: B::BlockBuilder, -} - -impl, MC: MemoryConfig, M: ManagerBase> - BlockCache -{ - /// Allocate a new block cache. - pub fn new(manager: &mut M, block_builder: B::BlockBuilder) -> Self - where - M: ManagerAlloc, - { - Self { - current_block_addr: Cell::new_with(manager, !0), - next_instr_addr: Cell::new_with(manager, !0), - fence_counter: Cell::new(manager), - partial_block: PartialBlock::new(manager), - entries: NewState::new(manager), - block_builder, - } - } - - /// Bind the block cache to the given allocated state and the given [block builder]. - /// - /// [block builder]: Block::BlockBuilder - pub fn bind(space: AllocatedOf, block_builder: B::BlockBuilder) -> Self - where - M::ManagerRoot: ManagerReadWrite, - { - BCL::bind(space, block_builder) - } - - /// Invalidate all entries in the block cache. - pub fn invalidate(&mut self) - where - M: ManagerReadWrite, - { - let counter = self.fence_counter.read(); - self.fence_counter.write(counter.next()); - self.reset_to(!0); - BCL::entry_mut(&mut self.entries, counter.0 as Address).invalidate(); - } - - /// Reset the underlying storage. - pub fn reset(&mut self) - where - M: ManagerReadWrite, - { - self.fence_counter.write(FenceCounter::INITIAL); - self.reset_to(!0); - BCL::entries_reset(&mut self.entries); - self.partial_block.reset(); - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>(&'a self) -> AllocatedOf { - BCL::struct_ref::<_, _, _, F>(self) - } - - /// Push a compressed instruction to the block cache. - pub fn push_instr_compressed(&mut self, phys_addr: Address, instr: Instruction) - where - M: ManagerReadWrite, - { - debug_assert_eq!( - instr.width(), - InstrWidth::Compressed, - "expected compressed instruction, found: {instr:?}" - ); - - let next_addr = self.next_instr_addr.read(); - - // If the instruction is at the start of the page, we _must_ start a new block, - // as we cannot allow blocks to cross page boundaries. - if phys_addr & OFFSET_MASK == 0 || phys_addr != next_addr { - self.reset_to(phys_addr); - } - - self.cache_inner::<{ InstrWidth::Compressed as u64 }>(phys_addr, instr); - } - - /// Push an uncompressed instruction to the block cache. - pub fn push_instr_uncompressed(&mut self, phys_addr: Address, instr: Instruction) - where - M: ManagerReadWrite, - { - debug_assert_eq!( - instr.width(), - InstrWidth::Uncompressed, - "expected uncompressed instruction, found: {instr:?}" - ); - - // ensure uncompressed does not cross page boundaries - const END_OF_PAGE: Address = PAGE_SIZE.get() - 2; - if phys_addr % PAGE_SIZE.get() == END_OF_PAGE { - return; - } - - let next_addr = self.next_instr_addr.read(); - - // If the instruction is at the start of the page, we _must_ start a new block, - // as we cannot allow blocks to cross page boundaries. - if phys_addr & OFFSET_MASK == 0 || phys_addr != next_addr { - self.reset_to(phys_addr); - } - - self.cache_inner::<{ InstrWidth::Uncompressed as u64 }>(phys_addr, instr); - } - - fn reset_to(&mut self, phys_addr: Address) - where - M: ManagerWrite, - { - self.current_block_addr.write(phys_addr); - self.next_instr_addr.write(0); - } - - /// Add the instruction into a block. - /// - /// If the block is full, a new block will be started. - /// - /// If there is a block at the next address, we will - /// merge it into the current block if possible. We - /// ensure it is valid (it is part of the same fence, - /// on the same page and that the combined block will - /// not exceed the maximum number of instructions). - fn cache_inner(&mut self, phys_addr: Address, instr: Instruction) - where - M: ManagerReadWrite, - { - let mut block_addr = self.current_block_addr.read(); - let fence_counter = self.fence_counter.read(); - - let mut entry = BCL::entry_mut(&mut self.entries, block_addr); - let start = entry.address.read(); - - let mut len_instr = entry.block.num_instr(); - - if start != block_addr || start == phys_addr { - entry.start_block(block_addr, fence_counter); - len_instr = 0; - } else if len_instr == CACHE_INSTR { - // The current block is full, start a new one - self.reset_to(phys_addr); - block_addr = phys_addr; - - entry = BCL::entry_mut(&mut self.entries, block_addr); - entry.start_block(block_addr, fence_counter); - - len_instr = 0; - } - - entry.block.push_instr(instr); - let new_len = len_instr + 1; - - let next_phys_addr = phys_addr + WIDTH; - self.next_instr_addr.write(next_phys_addr); - - let possible_block = BCL::entry(&self.entries, next_phys_addr); - let adjacent_block_found = possible_block.address.read() == next_phys_addr - && possible_block.fence_counter.read() == fence_counter - && next_phys_addr & OFFSET_MASK != 0 - && possible_block.block.num_instr() + new_len <= CACHE_INSTR; - - if adjacent_block_found { - let num_instr = possible_block.block.num_instr(); - for i in 0..num_instr { - // Need to resolve the adjacent block again because we may only keep one reference at a time - // to `self.entries`. - let new_block = BCL::entry(&self.entries, next_phys_addr); - let new_instr = new_block.block.instr()[i].read_stored(); - // Need to resolve the target block again because we may only keep one reference at a time - // to `self.entries`. - let current_entry = BCL::entry_mut(&mut self.entries, block_addr); - current_entry.block.push_instr(new_instr); - } - self.next_instr_addr.write(!0); - self.current_block_addr.write(!0); - } - } - - /// Lookup a block by a physical address. - /// - /// If one is found it can then be executed with [`BlockCall::run_block`]. - /// - /// *NB* before running any block, you must ensure no partial block - /// is in progress with [`BlockCache::complete_current_block`]. - #[inline(always)] - pub fn get_block(&mut self, phys_addr: Address) -> Option> - where - M: ManagerRead, - { - debug_assert!( - !self.partial_block.in_progress.read(), - "Get block was called with a partial block in progress" - ); - - let entry = BCL::entry_mut(&mut self.entries, phys_addr); - - if entry.address.read() == phys_addr - && self.fence_counter.read() == entry.fence_counter.read() - && entry.block.num_instr() > 0 - { - Some(BlockCall { - entry, - builder: &mut self.block_builder, - partial: &mut self.partial_block, - }) - } else { - None - } - } - - /// Complete a block that was only partially executed. - /// - /// This can happen when `steps + block.len_instr() > steps_max`, in - /// which case we only executed instructions until `steps == steps_max`. - pub fn complete_current_block( - &mut self, - core: &mut MachineCoreState, - steps: &mut usize, - max_steps: usize, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - if !self.partial_block.in_progress.read() { - return Ok(()); - } - - let entry = BCL::entry_mut(&mut self.entries, self.partial_block.phys_addr.read()); - - self.partial_block - .run_partial_inner(core, steps, max_steps, entry) - } - - /// *TEST ONLY* - retrieve the underlying instructions contained in the entry at the given - /// address. - #[cfg(test)] - pub(crate) fn get_block_instr(&mut self, phys_addr: Address) -> Vec - where - M: ManagerRead, - { - let entry = BCL::entry_mut(&mut self.entries, phys_addr); - - let instr = entry.block.instr(); - instr.iter().map(|cell| cell.read_stored()).collect() - } -} - -impl + Clone, MC: MemoryConfig, M: ManagerClone> Clone - for BlockCache -{ - fn clone(&self) -> Self { - Self { - current_block_addr: self.current_block_addr.clone(), - fence_counter: self.fence_counter.clone(), - next_instr_addr: self.next_instr_addr.clone(), - partial_block: self.partial_block.clone(), - entries: BCL::clone_entries(&self.entries), - block_builder: Default::default(), - } - } -} - -/// A block that is available to be run. -/// -/// If there are sufficiently many steps remaining, the entire block is executed in one go. -/// Otherwise, it will fall back to partial evaluation. -/// -/// As a result, before starting to run blocks from the block cache, you must first ensure that -/// any left-over partially-run block is cleared up with [`BlockCache::complete_current_block`]. -pub struct BlockCall<'a, B: Block, MC: MemoryConfig, M: ManagerBase> { - entry: &'a mut Cached, - /// # Safety - /// - /// The same block builder must always be passed through to `run_block`. - builder: &'a mut B::BlockBuilder, - partial: &'a mut PartialBlock, -} - -impl, MC: MemoryConfig, M: ManagerReadWrite> BlockCall<'_, B, MC, M> { - /// Run a block, either fully or partially, depending on the number of steps remaining. - #[inline(always)] - pub fn run_block( - &mut self, - core: &mut MachineCoreState, - instr_pc: Address, - steps: &mut usize, - max_steps: usize, - ) -> Result<(), EnvironException> { - if *steps + self.entry.block.num_instr() <= max_steps { - // Safety: the same block builder is passed through every time. - unsafe { - self.entry - .block - .run_block(core, instr_pc, steps, self.builder) - } - } else { - self.partial - .run_block_partial(core, steps, max_steps, self.entry) - } - } -} - -#[inline(always)] -fn run_instr( - instr: &EnrichedCell, M>, - core: &mut MachineCoreState, -) -> Result, Exception> { - let args = instr.read_ref_stored().args(); - let icall = instr.read_derived(); - - // SAFETY: This is safe, as the function we are calling is derived directly from the - // same instruction as the `Args` we are calling with. Therefore `args` will be of the - // required shape. - unsafe { icall.run(args, core) } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::backend_test; - use crate::default::ConstDefault; - use crate::machine_state::MachineCoreState; - use crate::machine_state::MachineState; - use crate::machine_state::TestCacheLayouts; - use crate::machine_state::block_cache::block::Interpreted; - use crate::machine_state::block_cache::block::InterpretedBlockBuilder; - use crate::machine_state::instruction::Instruction; - use crate::machine_state::instruction::OpCode; - use crate::machine_state::instruction::tagged_instruction::TaggedArgs; - use crate::machine_state::instruction::tagged_instruction::TaggedInstruction; - use crate::machine_state::instruction::tagged_instruction::TaggedRegister; - use crate::machine_state::memory; - use crate::machine_state::memory::M4K; - use crate::machine_state::registers::XRegister; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::nz; - use crate::machine_state::registers::t0; - use crate::machine_state::registers::t1; - use crate::state_backend::owned_backend::Owned; - - pub type TestLayout = Layout; - - // writing CACHE_INSTR to the block cache creates new block - backend_test!(test_writing_full_block_fetchable_uncompressed, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let uncompressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::X64Store, - args: TaggedArgs { - rs1: t1.into(), - rs2: t0.into(), - rd: TaggedRegister::X(XRegister::x1), - imm: 8, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 10; - - for offset in 0..(CACHE_INSTR as u64) { - state.push_instr_uncompressed(phys_addr + offset * 4, uncompressed); - } - - let block = state.get_block(phys_addr); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR, block.unwrap().entry.block.num_instr()); - }); - - backend_test!(test_writing_full_block_fetchable_compressed, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let compressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::Li, - args: TaggedArgs { - rd: nz::a0.into(), - imm: 1, - rs1: nz::ra.into(), - rs2: nz::ra.into(), - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 10; - - for offset in 0..(CACHE_INSTR as u64) { - state.push_instr_compressed(phys_addr + offset * 2, compressed); - } - - let block = state.get_block(phys_addr); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR, block.unwrap().entry.block.num_instr()); - }); - - // writing instructions immediately creates block - backend_test!(test_writing_half_block_fetchable_compressed, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let compressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::Li, - args: TaggedArgs { - rd: nz::a0.into(), - imm: 1, - rs1: nz::ra.into(), - rs2: nz::ra.into(), - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 10; - - for offset in 0..((CACHE_INSTR / 2) as u64) { - state.push_instr_compressed(phys_addr + offset * 2, compressed); - } - - let block = state.get_block(phys_addr); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR / 2, block.unwrap().entry.block.num_instr()); - }); - - backend_test!(test_writing_two_blocks_fetchable_compressed, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let compressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::Li, - args: TaggedArgs { - rd: nz::a0.into(), - imm: 1, - rs1: nz::ra.into(), - rs2: nz::ra.into(), - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 10; - - for offset in 0..((CACHE_INSTR * 2) as u64) { - state.push_instr_compressed(phys_addr + offset * 2, compressed); - } - - let block = state.get_block(phys_addr); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR, block.unwrap().entry.block.num_instr()); - - let block = state.get_block(phys_addr + 2 * CACHE_INSTR as u64); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR, block.unwrap().entry.block.num_instr()); - }); - - // writing across pages offset two blocks next to each other - backend_test!(test_crossing_page_exactly_creates_new_block, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let compressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::Li, - args: TaggedArgs { - rd: nz::a0.into(), - imm: 1, - rs1: nz::ra.into(), - rs2: nz::ra.into(), - width: InstrWidth::Compressed, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = PAGE_SIZE.get() - 10; - - for offset in 0..10 { - state.push_instr_compressed(phys_addr + offset * 2, compressed); - } - - let block = state.get_block(phys_addr); - assert!(block.is_some()); - assert_eq!(5, block.unwrap().entry.block.num_instr()); - - let block = state.get_block(phys_addr + 10); - assert!(block.is_some()); - assert_eq!(5, block.unwrap().entry.block.num_instr()); - }); - - backend_test!(test_partial_block_executes, F, { - let mut manager = F::manager(); - let mut core_state = MachineCoreState::::new(&mut manager); - let mut block_state = BlockCache::, _, _>::new( - &mut manager, - InterpretedBlockBuilder, - ); - - let addiw = Instruction::try_from(TaggedInstruction { - opcode: OpCode::AddWordImmediate, - args: TaggedArgs { - rd: nz::a1.into(), - rs1: a1.into(), - imm: 257, - rs2: TaggedRegister::X(XRegister::x1), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let block_addr = memory::FIRST_ADDRESS; - - for offset in 0..10 { - block_state.push_instr_uncompressed(block_addr + offset * 4, addiw); - } - - core_state.hart.pc.write(block_addr); - - // Execute the first 5 instructions - let mut steps = 0; - let block = block_state.get_block(block_addr).unwrap(); - block - .partial - .run_block_partial(&mut core_state, &mut steps, 5, block.entry) - .unwrap(); - - assert_eq!(steps, 5); - assert!(block_state.partial_block.in_progress.read()); - assert_eq!(5, block_state.partial_block.progress.read()); - assert_eq!(block_addr, block_state.partial_block.phys_addr.read()); - assert_eq!(block_addr + 5 * 4, core_state.hart.pc.read()); - - // Execute no steps - let mut steps = 0; - block_state - .complete_current_block(&mut core_state, &mut steps, 0) - .unwrap(); - - assert_eq!(steps, 0); - assert!(block_state.partial_block.in_progress.read()); - assert_eq!(5, block_state.partial_block.progress.read()); - assert_eq!(block_addr, block_state.partial_block.phys_addr.read()); - assert_eq!(block_addr + 5 * 4, core_state.hart.pc.read()); - - // Execute the next 2 instructions - let mut steps = 0; - block_state - .complete_current_block(&mut core_state, &mut steps, 2) - .unwrap(); - - assert_eq!(steps, 2); - assert!(block_state.partial_block.in_progress.read()); - assert_eq!(7, block_state.partial_block.progress.read()); - assert_eq!(block_addr, block_state.partial_block.phys_addr.read()); - assert_eq!(block_addr + 7 * 4, core_state.hart.pc.read()); - - // Finish the block. We don't consume all the steps - let mut steps = 0; - block_state - .complete_current_block(&mut core_state, &mut steps, 5) - .unwrap(); - - assert_eq!(steps, 3); - assert!(!block_state.partial_block.in_progress.read()); - assert_eq!(0, block_state.partial_block.progress.read()); - assert_eq!(0, block_state.partial_block.phys_addr.read()); - assert_eq!(block_addr + 10 * 4, core_state.hart.pc.read()); - }); - - backend_test!(test_concat_blocks_suitable, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let uncompressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::X64Store, - args: TaggedArgs { - rs1: t1.into(), - rs2: t0.into(), - rd: TaggedRegister::X(XRegister::x1), - imm: 8, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 30; - let preceding_num_instr: u64 = 5; - - for offset in 0..(CACHE_INSTR as u64 - preceding_num_instr) { - state.push_instr_uncompressed(phys_addr + offset * 4, uncompressed); - } - - for offset in 0..preceding_num_instr { - state.push_instr_uncompressed( - phys_addr - preceding_num_instr * 4 + offset * 4, - uncompressed, - ); - } - - let block = state.get_block(phys_addr - 20); - assert!(block.is_some()); - assert_eq!(CACHE_INSTR, block.unwrap().entry.block.num_instr()); - - let old_block = state.get_block(phys_addr); - assert!(old_block.is_some()); - assert_eq!(15, old_block.unwrap().entry.block.num_instr()); - }); - - backend_test!(test_concat_blocks_too_big, F, { - let mut state = BlockCache::, _, _>::new( - &mut F::manager(), - InterpretedBlockBuilder, - ); - - let uncompressed = Instruction::try_from(TaggedInstruction { - opcode: OpCode::X64Store, - args: TaggedArgs { - rs1: t1.into(), - rs2: t0.into(), - rd: TaggedRegister::X(XRegister::x1), - imm: 8, - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(); - - let phys_addr = 30; - let preceding_num_instr: u64 = 5; - - for offset in 0..(CACHE_INSTR as u64 - preceding_num_instr + 1) { - state.push_instr_uncompressed(phys_addr + offset * 4, uncompressed); - } - - for offset in 0..preceding_num_instr { - state.push_instr_uncompressed( - phys_addr - preceding_num_instr * 4 + offset * 4, - uncompressed, - ); - } - - let first_block = state.get_block(phys_addr - preceding_num_instr * 4); - assert!(first_block.is_some()); - assert_eq!( - preceding_num_instr, - first_block.unwrap().entry.block.num_instr() as u64 - ); - - let second_block = state.get_block(phys_addr); - assert!(second_block.is_some()); - assert_eq!( - CACHE_INSTR - preceding_num_instr as usize + 1, - second_block.unwrap().entry.block.num_instr() - ); - }); - - /// The initialised block cache must not return any blocks. This is especially important for - /// blocks at address 0 which at one point were accidentally valid but empty which caused loops. - #[test] - fn test_init_block() { - type Layout = super::Layout; - - // This test only makes sense if the test cache size isn't 0. - if TEST_CACHE_SIZE < 1 { - panic!("Test cache size must be at least 1"); - } - - let check_block = |block: &mut BlockCache| { - for i in 0..TEST_CACHE_SIZE { - assert!(block.get_block(i as Address).is_none()); - } - }; - - let populate_block = |block: &mut BlockCache| { - for i in 0..TEST_CACHE_SIZE { - block.push_instr_uncompressed( - i as Address, - Instruction::try_from(TaggedInstruction { - opcode: OpCode::Add, - args: TaggedArgs { - rd: nz::a1.into(), - rs1: nz::a1.into(), - rs2: nz::a2.into(), - ..TaggedArgs::DEFAULT - }, - }) - .unwrap(), - ); - } - }; - - let mut block: BlockCache, M4K, Owned> = - BlockCache::new(&mut Owned, InterpretedBlockBuilder); - - // The initial block cache should not return any blocks. - check_block(&mut block); - - populate_block(&mut block); - block.reset(); - - // The reset block cache should not return any blocks. - check_block(&mut block); - - // We check the invalidation logic multiple times because it works progressively, not in - // one go. - for _ in 0..TEST_CACHE_SIZE { - populate_block(&mut block); - block.invalidate(); - - // The invalidated block cache should not return any blocks. - check_block(&mut block); - } - } - - /// The initialised block cache has an entry for address 0. The block at address 0 happens to - /// be empty, which causes the step function to loop indefinitely when it runs the block. - #[test] - fn test_run_addr_zero() { - let mut state: MachineState, Owned> = - MachineState::new(&mut Owned, InterpretedBlockBuilder); - - // Encoding of ECALL instruction - const ECALL: u32 = 0b1110011; - - state.core.hart.pc.write(0); - state - .core - .main_memory - .write_instruction_unchecked(0, ECALL) - .unwrap(); - - let result = state.step(); - assert_eq!(result, Err(EnvironException::EnvCall)); - } - - /// The initialised block cache has an entry for address 0. The block at address 0 happens to - /// be empty, which causes the step function to loop indefinitely when it runs the block. - #[test] - fn test_get_empty_block_fails() { - type Layout = super::Layout; - - let mut block_cache: BlockCache, M4K, Owned> = - BlockCache::new(&mut Owned, InterpretedBlockBuilder); - - // Fetching empty block fails - assert!(block_cache.get_block(0).is_none()); - - block_cache.push_instr_compressed(0, Instruction::new_nop(InstrWidth::Compressed)); - - // Fetching non-empty block succeeds - assert!(block_cache.get_block(0).is_some()); - } -} diff --git a/src/riscv/lib/src/machine_state/block_cache/block.rs b/src/riscv/lib/src/machine_state/block_cache/block.rs deleted file mode 100644 index bdaa6dbbeee4753f162bc9c58eccc2ace4fb0b40..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/block_cache/block.rs +++ /dev/null @@ -1,493 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Switching of execution strategy for blocks. - -mod dispatch; - -use dispatch::DispatchCompiler; -use dispatch::DispatchTarget; -pub use dispatch::OutlineCompiler; - -use super::CACHE_INSTR; -use super::ICallPlaced; -use super::run_instr; -use crate::default::ConstDefault; -use crate::jit::state_access::JitStateAccess; -use crate::machine_state::MachineCoreState; -use crate::machine_state::ProgramCounterUpdate; -use crate::machine_state::instruction::Instruction; -use crate::machine_state::memory::Address; -use crate::machine_state::memory::MemoryConfig; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::EnrichedCell; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; -use crate::state_backend::Ref; -use crate::traps::EnvironException; -use crate::traps::Exception; - -/// State Layout for Blocks -pub type BlockLayout = (Atom, [Atom; CACHE_INSTR]); - -/// Functionality required to construct & execute blocks. -/// -/// A block is a sequence of at least one instruction, which may be executed sequentially. -/// Blocks will never contain more than [`CACHE_INSTR`] instructions. -pub trait Block: NewState { - /// Block construction may require additional state not kept in storage, - /// this is then passed as a parameter to [`Block::run_block`]. - /// - /// `Sized` bound is required to ensure any reference to `BlockBuilder` will be thin - - /// see [`DispatchFn`]. - type BlockBuilder: Default + Sized; - - /// Bind the block to the given allocated state. - fn bind(allocated: AllocatedOf) -> Self - where - M::ManagerRoot: ManagerReadWrite; - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - fn struct_ref<'a, F: FnManager>>(&'a self) -> AllocatedOf; - - /// Ready a block for construction. - /// - /// Previous instructions are removed. - fn start_block(&mut self) - where - M: ManagerWrite; - - /// Push an instruction to the block. - fn push_instr(&mut self, instr: Instruction) - where - M: ManagerReadWrite; - - fn num_instr(&self) -> usize - where - M: ManagerRead; - - /// Invalidate a block, meaning it should no longer be run. - fn invalidate(&mut self) - where - M: ManagerWrite; - - /// Reset a block to the default state, it should no longer be run. - fn reset(&mut self) - where - M: ManagerReadWrite; - - /// Returns the underlying slice of instructions stored in the block. - fn instr(&self) -> &[EnrichedCell, M>] - where - M: ManagerRead; - - /// Run a block against the machine state. - /// - /// When calling this function, there must be no partial block in progress. To ensure - /// this, you must always run [`BlockCache::complete_current_block`] prior to fetching - /// and running a new block. - /// - /// There _must_ also be sufficient steps remaining, to execute the block in full. - /// - /// # Safety - /// - /// The `block_builder` must be the same as the block builder given to the `compile` call that - /// (may) have natively compiled this block to machine code. - /// - /// This ensures that the builder in question is guaranteed to be alive, for at least as long - /// as this block may be run. - /// - /// [`BlockCache::complete_current_block`]: super::BlockCache::complete_current_block - unsafe fn run_block( - &mut self, - core: &mut MachineCoreState, - instr_pc: Address, - steps: &mut usize, - block_builder: &mut Self::BlockBuilder, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite; -} - -/// Interpreted blocks are built automatically, and require no additional context. -#[derive(Debug, Default)] -pub struct InterpretedBlockBuilder; - -/// Blocks that are executed via intepreting the individual instructions. -/// -/// Interpreted blocks use the [`EnrichedCell`] mechanism, in order to dispatch -/// opcode to function statically during block construction. This saves time over -/// dispatching on every 'instruction run'. See [`ICall`] for more information. -/// -/// [`ICall`]: super::ICall -pub struct Interpreted { - instr: [EnrichedCell, M>; CACHE_INSTR], - len_instr: Cell, -} - -impl NewState for Interpreted { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - len_instr: Cell::new(manager), - instr: NewState::new(manager), - } - } -} - -impl Block for Interpreted { - type BlockBuilder = InterpretedBlockBuilder; - - fn num_instr(&self) -> usize - where - M: ManagerRead, - { - self.len_instr.read() as usize - } - - #[inline] - fn instr(&self) -> &[EnrichedCell, M>] - where - M: ManagerRead, - { - &self.instr[..self.num_instr()] - } - - fn invalidate(&mut self) - where - M: ManagerWrite, - { - self.len_instr.write(0); - } - - fn push_instr(&mut self, instr: Instruction) - where - M: ManagerReadWrite, - { - let len = self.len_instr.read(); - self.instr[len as usize].write(instr); - self.len_instr.write(len + 1); - } - - fn reset(&mut self) - where - M: ManagerReadWrite, - { - self.len_instr.write(0); - self.instr - .iter_mut() - .for_each(|lc| lc.write(Instruction::DEFAULT)); - } - - fn start_block(&mut self) - where - M: ManagerWrite, - { - self.len_instr.write(0); - } - - fn bind(space: AllocatedOf) -> Self - where - M::ManagerRoot: ManagerReadWrite, - { - Self { - len_instr: space.0, - instr: space.1.map(EnrichedCell::bind), - } - } - - fn struct_ref<'a, F: FnManager>>(&'a self) -> AllocatedOf { - ( - self.len_instr.struct_ref::(), - self.instr.each_ref().map(|entry| entry.struct_ref::()), - ) - } - - /// # SAFETY - /// - /// This function is always safe to call. - #[inline(always)] - unsafe fn run_block( - &mut self, - core: &mut MachineCoreState, - mut instr_pc: Address, - steps: &mut usize, - _block_builder: &mut Self::BlockBuilder, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - if let Err(e) = run_block_inner(self.instr(), core, &mut instr_pc, steps) { - core.handle_step_result(instr_pc, Err(e))?; - // If we succesfully handled an error, need to increment steps one more. - *steps += 1; - } - - Ok(()) - } -} - -impl Clone for Interpreted { - fn clone(&self) -> Self { - Self { - len_instr: self.len_instr.clone(), - instr: self.instr.clone(), - } - } -} - -/// The function signature for dispatching a block run. -/// -/// Internally, this may be interpreted, just-in-time compiled, or do -/// additional work over just execution. -/// -/// The first and last parameters must be thin-references, for ABI-compatability reasons. -#[expect( - improper_ctypes_definitions, - reason = "The receiving functions know the layout of the referenced types" -)] -pub type DispatchFn = unsafe extern "C" fn( - &mut Jitted, - &mut MachineCoreState, - Address, - &mut usize, - &mut Result<(), EnvironException>, - &mut as Block>::BlockBuilder, -); - -/// Blocks that are compiled to native code for execution, when possible. -/// -/// Not all instructions are currently supported, when a block contains -/// unsupported instructions, a fallback to [`Interpreted`] mode occurs. -/// -/// Blocks are compiled upon calling [`Block::run_block`], in a *stop the world* fashion. -pub struct Jitted, MC: MemoryConfig, M: JitStateAccess> { - fallback: Interpreted, - dispatch: DispatchTarget, -} - -impl, MC: MemoryConfig, M: JitStateAccess> Jitted { - /// The default initial dispatcher for inline jit. - /// - /// This will run the block in interpreted mode by default, but will attempt to JIT-compile - /// the block. - /// - /// # SAFETY - /// - /// The `block_builder` must be the same every time this function is called. - /// - /// This ensures that the builder in question is guaranteed to be alive, for at least as long - /// as this block may be run via [`Block::run_block`]. - unsafe extern "C" fn run_block_interpreted( - &mut self, - core: &mut MachineCoreState, - instr_pc: Address, - steps: &mut usize, - result: &mut Result<(), EnvironException>, - block_builder: &mut >::BlockBuilder, - ) { - if !block_builder.0.should_compile(&mut self.dispatch) { - unsafe { self.run_block_not_compiled(core, instr_pc, steps, result, block_builder) } - return; - } - - // trigger JIT compilation - let instr = self - .fallback - .instr - .iter() - .take(>::num_instr(self)) - .map(|i| i.read_stored()) - .collect::>(); - - let fun = block_builder.0.compile(&mut self.dispatch, instr); - - // Safety: the block builder passed to this function is always the same for the - // lifetime of the block - unsafe { (fun)(self, core, instr_pc, steps, result, block_builder) } - } - - /// Run a block where JIT-compilation has been attempted, but failed for any reason. - /// - /// # SAFETY - /// - /// The `block_builder` must be the same every time this function is called. - /// - /// This ensures that the builder in question is guaranteed to be alive, for at least as long - /// as this block may be run via [`Block::run_block`]. - unsafe extern "C" fn run_block_not_compiled( - &mut self, - core: &mut MachineCoreState, - instr_pc: Address, - steps: &mut usize, - result: &mut Result<(), EnvironException>, - block_builder: &mut >::BlockBuilder, - ) { - *result = unsafe { - // Safety: this function is always safe to call - self.fallback - .run_block(core, instr_pc, steps, &mut block_builder.1) - }; - } -} - -impl, MC: MemoryConfig, M: JitStateAccess> NewState - for Jitted -{ - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - fallback: Interpreted::new(manager), - dispatch: DispatchTarget::default(), - } - } -} - -impl, MC: MemoryConfig, M: JitStateAccess> Block - for Jitted -{ - type BlockBuilder = (D, InterpretedBlockBuilder); - - fn start_block(&mut self) - where - M: ManagerWrite, - { - self.dispatch.reset(); - self.fallback.start_block() - } - - fn invalidate(&mut self) - where - M: ManagerWrite, - { - self.dispatch.reset(); - self.fallback.invalidate() - } - - fn reset(&mut self) - where - M: ManagerReadWrite, - { - self.dispatch.reset(); - self.fallback.reset() - } - - fn push_instr(&mut self, instr: Instruction) - where - M: ManagerReadWrite, - { - self.dispatch.reset(); - self.fallback.push_instr(instr) - } - - fn instr(&self) -> &[EnrichedCell, M>] - where - M: ManagerRead, - { - self.fallback.instr() - } - - fn bind(allocated: AllocatedOf) -> Self { - Self { - fallback: Interpreted::bind(allocated), - dispatch: DispatchTarget::default(), - } - } - - fn struct_ref<'a, F: FnManager>>(&'a self) -> AllocatedOf { - self.fallback.struct_ref::() - } - - /// Run a block, using the currently selected dispatch mechanism - /// - /// # SAFETY - /// - /// The `block_builder` must be the same every time this function is called. - /// - /// This ensures that the builder in question is guaranteed to be alive, for at least as long - /// as this block may be run via [`Block::run_block`]. - unsafe fn run_block( - &mut self, - core: &mut MachineCoreState, - instr_pc: Address, - steps: &mut usize, - block_builder: &mut Self::BlockBuilder, - ) -> Result<(), EnvironException> - where - M: ManagerReadWrite, - { - let mut result = Ok(()); - - let fun = self.dispatch.get(); - - // Safety: the block builder is always the same instance, guarantee-ing that any - // jit-compiled function is still alive. - unsafe { (fun)(self, core, instr_pc, steps, &mut result, block_builder) }; - - result - } - - fn num_instr(&self) -> usize - where - M: ManagerRead, - { - self.fallback.num_instr() - } -} - -impl, MC: MemoryConfig, M: JitStateAccess + ManagerClone> Clone - for Jitted -{ - fn clone(&self) -> Self { - Self { - fallback: self.fallback.clone(), - dispatch: DispatchTarget::default(), - } - } -} - -fn run_block_inner( - instr: &[EnrichedCell, M>], - core: &mut MachineCoreState, - instr_pc: &mut Address, - steps: &mut usize, -) -> Result<(), Exception> { - for instr in instr.iter() { - match run_instr(instr, core) { - Ok(ProgramCounterUpdate::Next(width)) => { - *instr_pc += width as u64; - core.hart.pc.write(*instr_pc); - *steps += 1; - } - Ok(ProgramCounterUpdate::Set(instr_pc)) => { - // Setting the instr_pc implies execution continuing - // elsewhere - and no longer within the current block. - core.hart.pc.write(instr_pc); - *steps += 1; - break; - } - Err(e) => { - // Exceptions lead to a new address being set to handle it, - // with no guarantee of it being the next instruction. - return Err(e); - } - } - } - - Ok(()) -} diff --git a/src/riscv/lib/src/machine_state/block_cache/block/dispatch.rs b/src/riscv/lib/src/machine_state/block_cache/block/dispatch.rs deleted file mode 100644 index ee3b403f92a75a5c967e1c21e04f50e30ea488f0..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/block_cache/block/dispatch.rs +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech - -//! Dispatching of blocks under JIT is done via hot-swappable -//! function pointers. -//! -//! This module exposes wrappers for the style of dispatch and compilation that is done. -//! -//! Currently, this is only 'inline' jit, but will soon be expanded to 'outline' jit also; -//! where 'outline' means any JIT compilation occurs in a separate thread. - -use std::marker::PhantomData; -use std::sync::Arc; -use std::sync::Mutex; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::mpsc; -use std::sync::mpsc::Sender; - -use super::DispatchFn; -use super::Jitted; -use crate::jit::JIT; -use crate::jit::JitFn; -use crate::jit::state_access::JitStateAccess; -use crate::machine_state::instruction::Instruction; -use crate::machine_state::memory::MemoryConfig; - -/// Dispatch target that wraps a [`DispatchFn`]. -/// -/// This is the target used for compilation - see [`DispatchCompiler::compile`]. -pub struct DispatchTarget, MC: MemoryConfig, M: JitStateAccess> { - /// Function pointer stored as an atomic usize. - /// - /// This will allow the `fun` to be updated from a background thread. - /// See for - /// considerations taken whilst converting pointer <--> usize. - fun: Arc, - called_times: usize, - _pd: PhantomData<(D, MC, M)>, -} - -impl, MC: MemoryConfig, M: JitStateAccess> DispatchTarget { - /// Reset the dispatch target to the interpreted dispatch mechanism. - pub fn reset(&mut self) { - // in resetting the block, we must allocated a new Arc. - // - // If we just reset the current arc, outline jit could update it from the background thread - // after reset it - meaning a reset/under construction block could now have a jitted function for - // a completely different set of instructions. - self.fun = Arc::new(AtomicUsize::new( - Jitted::::run_block_interpreted as usize, - )); - - self.called_times = 0; - } - - /// Set the dispatch target to use the given `block_run` function. - pub fn set(&self, fun: DispatchFn) { - // casting a function pointer as usize is ok to do. - let fun = fun as usize; - - // store using Release ordering - any subsequent loading with Acquire will see the new ptr. - self.fun.store(fun, Ordering::Release); - } - - /// Get the dispatch target's current `block_run` function. - pub fn get(&self) -> DispatchFn { - // load using Acquire ordering - so that it will see the previous store which was with - // Release. - let fun = self.fun.load(Ordering::Acquire); - - // to avoid problematic integer -> pointer conversion, we must cast it as a pointer first. - let fun = fun as *const (); - - // Safety: the pointer is indeed a function pointer with an ABI matching `DispatchFn`. - unsafe { std::mem::transmute::<*const (), DispatchFn>(fun) } - } -} - -impl, MC: MemoryConfig, M: JitStateAccess> Default - for DispatchTarget -{ - fn default() -> Self { - Self { - fun: Arc::new(AtomicUsize::new( - Jitted::::run_block_interpreted as usize, - )), - called_times: 0, - _pd: PhantomData, - } - } -} - -/// A compiler that can JIT-compile blocks of instructions, and hot-swap the execution of -/// said block in the given dispatch target. -pub trait DispatchCompiler: Default + Sized { - /// Whether compilation should be attempted for the block. - fn should_compile(&self, target: &mut DispatchTarget) -> bool; - - /// Compile a block, hot-swapping the `run_block` function contained in `target` in - /// the process. This could be to an interpreted execution method, and/or jit-compiled - /// function. - /// - /// NB - the hot-swapping of JIT-compiled blocks may occur at any time, and is not - /// guaranteed to be contained within the call-time of this function. (This is true for - /// outline jit, especially). - fn compile( - &mut self, - target: &mut DispatchTarget, - instr: Vec, - ) -> DispatchFn; -} - -impl DispatchCompiler for JIT { - #[inline] - fn should_compile(&self, _target: &mut DispatchTarget) -> bool { - // every block must be compiled immediately for inline jit, as it's used for testing - // jit compatibility with interpreted mode. - true - } - - fn compile( - &mut self, - target: &mut DispatchTarget, - instr: Vec, - ) -> DispatchFn { - let fun = match self.compile(&instr) { - Some(jitfn) => { - // Safety: the two function signatures are identical, apart from the first and - // last parameters. These are both thin-pointers, and ignored by the JitFn. - // - // It's therefore safe to cast this function pointer to an identical ABI, where - // this first and last parameter are thin-references to any value. This is the - // case for both `Jitted` and `Jitted::BlockBuilder` which are both Sized. - // - // See for more - // information on ABI compatability. - unsafe { std::mem::transmute::, DispatchFn>(jitfn) } - } - None => Jitted::run_block_not_compiled, - }; - - target.set(fun); - - fun - } -} - -/// JIT compiler for blocks that performs compilation in a -/// background thread. -pub struct OutlineCompiler { - // We will not touch the jit from the execution thread, however we must maintain - // a reference to it - to ensure it is not dropped before we are done with execution, - // even if the background compilation thread panics. - #[expect(unused)] - jit: Arc>>, - sender: Sender, -} - -impl OutlineCompiler { - fn new() -> Self { - let (sender, receiver) = mpsc::channel(); - let jit: Arc>> = Default::default(); - - let compiler = Self { - jit: jit.clone(), - sender, - }; - - std::thread::spawn(move || { - { - let mut jit = jit.lock().expect("Only this thread locks the JIT"); - - while let Ok(msg) = receiver.recv() { - if let Some(jitfn) = jit.compile(&msg.instr) { - // Safety: this function will be retrieved as a DispatchFn, rather than a - // JitFn. The two function signatures are identical, apart from the first and - // last parameters. These are both thin-pointers, and ignored by the JitFn. - // - // It's therefore safe to cast this function pointer to an identical ABI, where - // this first and last parameter are thin-references to any value. This is the - // case for both `Jitted` and `Jitted::BlockBuilder` which are both Sized. - // - // See for more - // information on ABI compatability. - msg.fun.store(jitfn as usize, Ordering::Release); - }; - } - } - // because we used blocking recv with an asynchronous channel, this only fails when the - // other end of the channel has been dropped. - // - // This means the BlockBuilder has been dropped - and thus execution has stopped. - // We are therefore safe to drop the JIT. - }); - - compiler - } -} - -impl Default - for OutlineCompiler -{ - fn default() -> Self { - Self::new() - } -} - -impl DispatchCompiler - for OutlineCompiler -{ - fn should_compile(&self, target: &mut DispatchTarget) -> bool { - const COMPILATION_THRESHOLD: usize = 1000; - - target.called_times += 1; - target.called_times > COMPILATION_THRESHOLD - } - - fn compile( - &mut self, - target: &mut DispatchTarget, - instr: Vec, - ) -> DispatchFn { - let request = CompilationRequest { - instr, - fun: target.fun.clone(), - }; - - // This will always succeed, unless the compilation thread has panicked - // (as this would result in the receiving end of the channel being closed). - // - // If it has, execution must still continue - but everything will fallback - // to interpreted mode. - // - // NB - any blocks already JIT compiled are safe to keep calling, as the - // data behind the mutex (the JIT) is kept alive for as long as we maintain - // our reference to it, despite the lock itself being poisoned. - let _ = self.sender.send(request); - - let fun = Jitted::run_block_not_compiled; - target.set(fun); - fun - } -} - -struct CompilationRequest { - instr: Vec, - fun: Arc, -} diff --git a/src/riscv/lib/src/machine_state/block_cache/metrics.rs b/src/riscv/lib/src/machine_state/block_cache/metrics.rs deleted file mode 100644 index c5bba4cdec45f6eaa7c9575dd9ea2b96e4eef17f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/block_cache/metrics.rs +++ /dev/null @@ -1,418 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! This module implements block metrics. -//! -//! Namely: -//! - the op-codes contained in a block -//! - the number of times it has been constructed and called -//! - whether or not the block is supported by the JIT -//! -//! The macro [`block_metrics`] is used to record metrics. - -/// Record a block metric - namely that a block has been -/// - constructed -/// - successfully JIT-compiled -/// - called -/// -/// This macro has no effect when the `metrics` feature is disabled. -#[cfg(not(feature = "metrics"))] -macro_rules! block_metrics { - (hash = $hash:expr, constructed = $block:expr) => {}; - - (hash = $hash:expr, record_jitted) => {}; - - (hash = $hash:expr, record_called) => {}; -} - -#[cfg(feature = "metrics")] -pub(crate) use core::block_metrics; - -#[cfg(not(feature = "metrics"))] -pub(crate) use block_metrics; - -use super::ICallPlaced; -use super::block::Block; -use crate::machine_state::memory::MemoryConfig; -use crate::state::NewState; -use crate::state_backend::EnrichedCell; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerRead; -use crate::storage::Hash; - -#[cfg(feature = "metrics")] -#[doc(hidden)] -pub mod core { - use std::cmp::Ordering; - use std::collections::BTreeSet; - use std::collections::HashMap; - use std::path::Path; - - use super::BlockHash; - use crate::machine_state::MachineCoreState; - use crate::machine_state::block_cache::block::Block; - use crate::machine_state::instruction::Instruction; - use crate::machine_state::memory::M4K; - use crate::machine_state::memory::MemoryConfig; - use crate::state_backend::ManagerRead; - use crate::state_backend::hash::Hash; - use crate::state_backend::owned_backend::Owned; - - /// Record a block metric - namely that a block has been - /// - constructed - /// - successfully JIT-compiled - /// - called - /// - /// This macro has no effect when the `metrics` feature is disabled. - macro_rules! block_metrics { - (hash = $hash:expr, constructed = $block:expr) => { - $crate::machine_state::block_cache::metrics::core::BlockCacheMetrics::with_borrow_mut( - |bm| bm.record_constructed($hash, $block), - ); - }; - - (hash = $hash:expr, record_jitted) => { - $crate::machine_state::block_cache::metrics::core::BlockCacheMetrics::with_borrow_mut( - |bm| bm.record_jitted($hash), - ); - }; - - (hash = $hash:expr, record_called) => { - $crate::machine_state::block_cache::metrics::core::BlockCacheMetrics::with_borrow_mut( - |bm| bm.record_called($hash), - ); - }; - } - - pub(crate) use block_metrics; - - /// Write block metrics to the given file, in the following format: - /// ``` - /// [OpCodes] | JIT-compiled | times_constructed | times_called - /// ``` - /// - /// If `exclude_supported_instructions = true` is passed, any opcode that is supported - /// by the JIT is not written in the output. - #[macro_export] - macro_rules! dump_block_metrics { - (file = $file:expr, exclude_supported_instructions = $exclude:expr) => { - $crate::machine_state::block_cache::metrics::core::BlockCacheMetrics::with_borrow( - |bm| bm.write_to_file($file, !$exclude), - ); - }; - } - - /// Mapping of blocks (identified by their hash) to their metrics. - /// - /// See [`BlockMetrics`]. - #[derive(Default)] - pub struct BlockCacheMetrics { - entries: HashMap, - } - - impl BlockCacheMetrics { - pub fn with_borrow_mut(run: impl Fn(&mut BlockCacheMetrics) -> T) -> T { - thread_local! { - /// Static variable used by [`block_metrics`] as a place to record metrics. - static BLOCK_METRICS: std::cell::RefCell = Default::default(); - } - - BLOCK_METRICS.with_borrow_mut(run) - } - - pub fn with_borrow(run: impl Fn(&BlockCacheMetrics) -> T) -> T { - Self::with_borrow_mut(|metrics| run(metrics)) - } - - /// Record that the given block has been constructed. - pub fn record_constructed, M: ManagerRead>( - &mut self, - hash: &Hash, - block: &B, - ) { - if let Some(entry) = self.entries.get_mut(hash) { - entry.constructed_count += 1; - return; - } - - let instr = block - .instr() - .iter() - .map(|i| i.read_stored()) - .collect::>(); - - let metrics = BlockMetrics { - instr, - jit_compiled: false, - called_count: 0, - constructed_count: 1, - }; - - self.entries.insert(*hash, metrics); - } - - /// Record that the block identified by `hash` has been - /// successfully jit-compiled. - pub fn record_jitted(&mut self, hash: &Hash) { - let entry = self - .entries - .get_mut(hash) - .expect("all blocks called are first constructed"); - - entry.jit_compiled = true; - } - - /// Record that the block identified by `hash` has been called. - pub fn record_called(&mut self, hash: &BlockHash) { - let BlockHash::Runnable(hash) = hash else { - panic!("Called blocks must be runnable"); - }; - - let entry = self - .entries - .get_mut(hash) - .expect("all blocks called are first constructed"); - - entry.called_count += 1; - } - - /// Write block metrics to the given file. - /// - /// These are ordered top-down by descending order of most frequently called. - pub fn write_to_file( - &self, - filename: &Path, - include_supported_instructions: bool, - ) -> Result<(), Box> { - use std::io::Write; - - let mut stats = BTreeSet::::new(); - - for stat in self.entries.values() { - stats.insert(stat.clone()); - } - - let mut stats_file = std::fs::File::create(filename)?; - - for stats in stats.into_iter().rev() { - let instr = stats - .instr - .iter() - .map(|instr| instr.opcode) - .filter(|opcode| { - include_supported_instructions - || opcode - .to_lowering::>() - .is_none() - }) - .collect::>(); - writeln!( - &mut stats_file, - "{:?} | {} | {} | {}", - instr, stats.jit_compiled, stats.constructed_count, stats.called_count - )?; - } - - Ok(()) - } - } - - /// Corresponds to a line in the block metrics output. - /// - /// Exclude `jit_compiled` from the implementations of `Eq` and `Ord`, - /// to ensure consistent ordering between JIT-enabled and Interpreted - /// metric files (as `jit_compiled` is always false in Interpreted runs). - #[derive(Default, Clone)] - pub struct BlockMetrics { - instr: Vec, - constructed_count: usize, - called_count: usize, - jit_compiled: bool, - } - - impl PartialEq for BlockMetrics { - fn eq(&self, other: &Self) -> bool { - self.called_count == other.called_count - && self.constructed_count == other.constructed_count - && Hash::blake2b_hash(&self.instr) - .unwrap() - .eq(&Hash::blake2b_hash(&other.instr).unwrap()) - } - } - - impl Eq for BlockMetrics {} - - impl PartialOrd for BlockMetrics { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl Ord for BlockMetrics { - fn cmp(&self, other: &Self) -> Ordering { - let cmp = self.called_count.cmp(&other.called_count); - if cmp != Ordering::Equal { - return cmp; - } - - let cmp = self.constructed_count.cmp(&other.constructed_count); - if cmp != Ordering::Equal { - return cmp; - } - - Hash::blake2b_hash(self.instr.as_slice()) - .unwrap() - .cmp(&Hash::blake2b_hash(&other.instr).unwrap()) - } - } -} - -/// Wrapper type that can be used to instrument any `B: Block` with metrics. -pub struct BlockMetrics { - block: B, - #[cfg(test)] - pub(crate) block_hash: BlockHash, - #[cfg(not(test))] - block_hash: BlockHash, -} - -impl, M: ManagerBase> NewState for BlockMetrics { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - block: B::new(manager), - block_hash: BlockHash::Dirty, - } - } -} - -impl, MC: MemoryConfig, M: ManagerBase> Block for BlockMetrics { - type BlockBuilder = B::BlockBuilder; - - fn reset(&mut self) - where - M: crate::state_backend::ManagerReadWrite, - { - self.block.reset(); - self.block_hash = BlockHash::Dirty; - } - - fn instr(&self) -> &[crate::state_backend::EnrichedCell, M>] - where - M: crate::state_backend::ManagerRead, - { - self.block.instr() - } - - /// # Safety - /// - /// The `block_builder` parameter must always be the same one, for the lifetime of the block. - unsafe fn run_block( - &mut self, - core: &mut crate::machine_state::MachineCoreState, - instr_pc: crate::machine_state::memory::Address, - steps: &mut usize, - block_builder: &mut Self::BlockBuilder, - ) -> Result<(), crate::traps::EnvironException> - where - M: crate::state_backend::ManagerReadWrite, - { - if let BlockHash::Dirty = self.block_hash { - let hash = block_hash(self.block.instr()); - block_metrics!(hash = &hash, constructed = self); - - self.block_hash = BlockHash::Runnable(hash); - } - - block_metrics!(hash = &self.block_hash, record_called); - - unsafe { self.block.run_block(core, instr_pc, steps, block_builder) } - } - - fn num_instr(&self) -> usize - where - M: crate::state_backend::ManagerRead, - { - self.block.num_instr() - } - - fn struct_ref<'a, F: crate::state_backend::FnManager>>( - &'a self, - ) -> crate::state_backend::AllocatedOf { - self.block.struct_ref::() - } - - fn push_instr(&mut self, instr: crate::machine_state::instruction::Instruction) - where - M: crate::state_backend::ManagerReadWrite, - { - self.block.push_instr(instr); - self.block_hash = BlockHash::Dirty; - } - - fn invalidate(&mut self) - where - M: crate::state_backend::ManagerWrite, - { - self.block.invalidate(); - self.block_hash = BlockHash::Dirty; - } - - fn bind(allocated: crate::state_backend::AllocatedOf) -> Self - where - ::ManagerRoot: crate::state_backend::ManagerReadWrite, - { - Self { - block: B::bind(allocated), - block_hash: BlockHash::Dirty, - } - } - - fn start_block(&mut self) - where - M: crate::state_backend::ManagerWrite, - { - self.block.start_block(); - self.block_hash = BlockHash::Dirty; - } -} - -impl Clone for BlockMetrics { - fn clone(&self) -> Self { - Self { - block: self.block.clone(), - block_hash: BlockHash::Dirty, - } - } -} - -/// The hash of a block is by default `Dirty` - ie it may be under construction. -/// -/// Only once blocks are made callable, within the specific context of the current backend, is -/// the hash calculated. At this point the block is declared `Runnable`. -#[derive(Debug, PartialEq, Eq)] -pub enum BlockHash { - /// This block may be under construction. - /// - /// In order for any such block to run, it may be made runnable. First by calculating - /// its block hash and triggering any side effects (such as JIT compilation). - Dirty, - /// This block can be run. - Runnable(Hash), -} - -/// Construct a block hash from the contained instructions. -fn block_hash( - block: &[EnrichedCell, M>], -) -> Hash { - let instr = block - .iter() - .map(|i| i.read_ref_stored()) - .collect::>(); - - Hash::blake2b_hash(instr).expect("Hashing instructions always succeeds") -} diff --git a/src/riscv/lib/src/machine_state/cache_layouts.rs b/src/riscv/lib/src/machine_state/cache_layouts.rs deleted file mode 100644 index 3f9e05aab0db2145b66cabc601b022b85e3b79df..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/cache_layouts.rs +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! This module provides a wrapper for controlling the sizes of various caches -//! (currently just the block cache). -//! -//! In future, this may be expanded to other compile-time-sized types that form -//! part of the machine state (e.g. main memory). - -use super::block_cache; -use super::block_cache::BlockCacheLayout; - -/// Configuration bucket for the size of caches. -pub enum Sizes {} - -/// Wrapping trait for a bucket containing layouts for various caches used by the machine state. -pub trait CacheLayouts { - /// Layout for the block cache - controlling the number of entries. - type BlockCacheLayout: BlockCacheLayout; -} - -impl CacheLayouts - for Sizes -{ - type BlockCacheLayout = block_cache::Layout; -} - -/// The default configuration of cache layouts. -pub type DefaultCacheLayouts = - Sizes<{ block_cache::DEFAULT_CACHE_BITS }, { block_cache::DEFAULT_CACHE_SIZE }>; - -/// The default configuration of cache layouts for tests. -pub type TestCacheLayouts = - Sizes<{ block_cache::TEST_CACHE_BITS }, { block_cache::TEST_CACHE_SIZE }>; diff --git a/src/riscv/lib/src/machine_state/csregisters.rs b/src/riscv/lib/src/machine_state/csregisters.rs deleted file mode 100644 index 0e52d9b4692a70e8cfee8b81c6683e82ef25d4ba..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters.rs +++ /dev/null @@ -1,2048 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 TriliTech -// -// SPDX-License-Identifier: MIT - -#![allow(non_upper_case_globals)] - -pub mod bits; -pub mod effects; -mod root; -pub mod satp; -pub mod values; -pub mod xstatus; - -use num_enum::TryFromPrimitive; -use root::RootCSRegister; -use strum::IntoEnumIterator; -use values::CSRValues; -use values::CSRValuesLayout; -use values::MStatusValue; - -use self::bits::NormaliseFields; -use self::satp::Satp; -use self::values::CSRValue; -use self::xstatus::ExtensionValue; -use self::xstatus::MNStatus; -use self::xstatus::MStatus; -use self::xstatus::SStatus; -use super::hart_state::HartState; -use super::memory::Address; -use super::mode::TrapMode; -use crate::bits::Bits64; -use crate::bits::ones; -use crate::bits::u64; -use crate::machine_state::mode::Mode; -use crate::state::NewState; -use crate::state_backend as backend; -use crate::state_backend::ManagerRead; -use crate::traps::Exception; -use crate::traps::Interrupt; -use crate::traps::TrapContext; -use crate::traps::TrapKind; - -/// Privilege required to access a CSR -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Privilege { - Unprivileged = 0, - Supervisor = 1, - Hypervisor = 2, - Machine = 3, -} - -/// CSR index -#[expect(non_camel_case_types, reason = "Consistent with RISC-V spec")] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - strum::EnumIter, - TryFromPrimitive, - strum::Display, - Hash, - serde::Serialize, - serde::Deserialize, -)] -#[repr(usize)] -pub enum CSRegister { - // Unprivileged Floating-Point CSRs - fflags = 0x001, - frm = 0x002, - fcsr = 0x003, - - // Unprivileged Counter/Timers - cycle = 0xC00, - time = 0xC01, - instret = 0xC02, - hpmcounter3 = 0xC03, - hpmcounter4 = 0xC04, - hpmcounter5 = 0xC05, - hpmcounter6 = 0xC06, - hpmcounter7 = 0xC07, - hpmcounter8 = 0xC08, - hpmcounter9 = 0xC09, - hpmcounter10 = 0xC0A, - hpmcounter11 = 0xC0B, - hpmcounter12 = 0xC0C, - hpmcounter13 = 0xC0D, - hpmcounter14 = 0xC0E, - hpmcounter15 = 0xC0F, - hpmcounter16 = 0xC10, - hpmcounter17 = 0xC11, - hpmcounter18 = 0xC12, - hpmcounter19 = 0xC13, - hpmcounter20 = 0xC14, - hpmcounter21 = 0xC15, - hpmcounter22 = 0xC16, - hpmcounter23 = 0xC17, - hpmcounter24 = 0xC18, - hpmcounter25 = 0xC19, - hpmcounter26 = 0xC1A, - hpmcounter27 = 0xC1B, - hpmcounter28 = 0xC1C, - hpmcounter29 = 0xC1D, - hpmcounter30 = 0xC1E, - hpmcounter31 = 0xC1F, - - // TODO: Remove everything in CSRegisters that requires privilieged access - // Supervisor Trap Setup - sstatus = 0x100, - sie = 0x104, - stvec = 0x105, - scounteren = 0x106, - - // Supervisor Configuration - senvcfg = 0x10A, - - // Supervisor Trap Handling - sscratch = 0x140, - sepc = 0x141, - scause = 0x142, - stval = 0x143, - sip = 0x144, - - // Supervisor Protection and Translation - satp = 0x180, - - // Supervisor Debug/Trace Registers - scontext = 0x5A8, - - // Hypervisor Trap Setup - hstatus = 0x600, - hedeleg = 0x602, - hideleg = 0x603, - hie = 0x604, - hcounteren = 0x606, - hgeie = 0x607, - - // Hypervisor Trap Handling - htval = 0x643, - hip = 0x644, - hvip = 0x645, - htinst = 0x64A, - hgeip = 0xE12, - - // Hypervisor Configuration - henvcfg = 0x60A, - - // Hypervisor Protection and Translation - hgatp = 0x680, - - // Hypervisor Debug/ Trace Registers - hcontext = 0x6A8, - - // Hypervisor Counter/Timer Virtualization Registers - htimedelta = 0x605, - - // Virtual Supervisor Registers - vsstatus = 0x200, - vsie = 0x204, - vstvec = 0x205, - vsscratch = 0x240, - vsepc = 0x241, - vscause = 0x242, - vstval = 0x243, - vsip = 0x244, - vsatp = 0x280, - - // Machine Information Registers - mvendorid = 0xF11, - marchid = 0xF12, - mimpid = 0xF13, - mhartid = 0xF14, - mconfigptr = 0xF15, - - // Machine Trap Setup - mstatus = 0x300, - misa = 0x301, - medeleg = 0x302, - mideleg = 0x303, - mie = 0x304, - mtvec = 0x305, - mcounteren = 0x306, - - // Machine Trap Handling - mscratch = 0x340, - mepc = 0x341, - mcause = 0x342, - mtval = 0x343, - mip = 0x344, - mtinst = 0x34A, - mtval2 = 0x34B, - - // Machine Configuration - menvcfg = 0x30A, - mseccfg = 0x747, - - // Machine Memory Protection - pmpcfg0 = 0x3A0, - pmpcfg2 = 0x3A2, - pmpcfg4 = 0x3A4, - pmpcfg6 = 0x3A6, - pmpcfg8 = 0x3A8, - pmpcfg10 = 0x3AA, - pmpcfg12 = 0x3AC, - pmpcfg14 = 0x3AE, - pmpaddr0 = 0x3B0, - pmpaddr1 = 0x3B1, - pmpaddr2 = 0x3B2, - pmpaddr3 = 0x3B3, - pmpaddr4 = 0x3B4, - pmpaddr5 = 0x3B5, - pmpaddr6 = 0x3B6, - pmpaddr7 = 0x3B7, - pmpaddr8 = 0x3B8, - pmpaddr9 = 0x3B9, - pmpaddr10 = 0x3BA, - pmpaddr11 = 0x3BB, - pmpaddr12 = 0x3BC, - pmpaddr13 = 0x3BD, - pmpaddr14 = 0x3BE, - pmpaddr15 = 0x3BF, - pmpaddr16 = 0x3C0, - pmpaddr17 = 0x3C1, - pmpaddr18 = 0x3C2, - pmpaddr19 = 0x3C3, - pmpaddr20 = 0x3C4, - pmpaddr21 = 0x3C5, - pmpaddr22 = 0x3C6, - pmpaddr23 = 0x3C7, - pmpaddr24 = 0x3C8, - pmpaddr25 = 0x3C9, - pmpaddr26 = 0x3CA, - pmpaddr27 = 0x3CB, - pmpaddr28 = 0x3CC, - pmpaddr29 = 0x3CD, - pmpaddr30 = 0x3CE, - pmpaddr31 = 0x3CF, - pmpaddr32 = 0x3D0, - pmpaddr33 = 0x3D1, - pmpaddr34 = 0x3D2, - pmpaddr35 = 0x3D3, - pmpaddr36 = 0x3D4, - pmpaddr37 = 0x3D5, - pmpaddr38 = 0x3D6, - pmpaddr39 = 0x3D7, - pmpaddr40 = 0x3D8, - pmpaddr41 = 0x3D9, - pmpaddr42 = 0x3DA, - pmpaddr43 = 0x3DB, - pmpaddr44 = 0x3DC, - pmpaddr45 = 0x3DD, - pmpaddr46 = 0x3DE, - pmpaddr47 = 0x3DF, - pmpaddr48 = 0x3E0, - pmpaddr49 = 0x3E1, - pmpaddr50 = 0x3E2, - pmpaddr51 = 0x3E3, - pmpaddr52 = 0x3E4, - pmpaddr53 = 0x3E5, - pmpaddr54 = 0x3E6, - pmpaddr55 = 0x3E7, - pmpaddr56 = 0x3E8, - pmpaddr57 = 0x3E9, - pmpaddr58 = 0x3EA, - pmpaddr59 = 0x3EB, - pmpaddr60 = 0x3EC, - pmpaddr61 = 0x3ED, - pmpaddr62 = 0x3EE, - pmpaddr63 = 0x3EF, - - // Machine Non-Maskable Interrupt Handling - mnscratch = 0x740, - mnepc = 0x741, - mncause = 0x742, - mnstatus = 0x744, - - // Machine Counter/Timers - mcycle = 0xB00, - minstret = 0xB02, - mhpmcounter3 = 0xB03, - mhpmcounter4 = 0xB04, - mhpmcounter5 = 0xB05, - mhpmcounter6 = 0xB06, - mhpmcounter7 = 0xB07, - mhpmcounter8 = 0xB08, - mhpmcounter9 = 0xB09, - mhpmcounter10 = 0xB0A, - mhpmcounter11 = 0xB0B, - mhpmcounter12 = 0xB0C, - mhpmcounter13 = 0xB0D, - mhpmcounter14 = 0xB0E, - mhpmcounter15 = 0xB0F, - mhpmcounter16 = 0xB10, - mhpmcounter17 = 0xB11, - mhpmcounter18 = 0xB12, - mhpmcounter19 = 0xB13, - mhpmcounter20 = 0xB14, - mhpmcounter21 = 0xB15, - mhpmcounter22 = 0xB16, - mhpmcounter23 = 0xB17, - mhpmcounter24 = 0xB18, - mhpmcounter25 = 0xB19, - mhpmcounter26 = 0xB1A, - mhpmcounter27 = 0xB1B, - mhpmcounter28 = 0xB1C, - mhpmcounter29 = 0xB1D, - mhpmcounter30 = 0xB1E, - mhpmcounter31 = 0xB1F, - - // Machine Counter Setup - mcountinhibit = 0x320, - mhpmevent3 = 0x323, - mhpmevent4 = 0x324, - mhpmevent5 = 0x325, - mhpmevent6 = 0x326, - mhpmevent7 = 0x327, - mhpmevent8 = 0x328, - mhpmevent9 = 0x329, - mhpmevent10 = 0x32A, - mhpmevent11 = 0x32B, - mhpmevent12 = 0x32C, - mhpmevent13 = 0x32D, - mhpmevent14 = 0x32E, - mhpmevent15 = 0x32F, - mhpmevent16 = 0x330, - mhpmevent17 = 0x331, - mhpmevent18 = 0x332, - mhpmevent19 = 0x333, - mhpmevent20 = 0x334, - mhpmevent21 = 0x335, - mhpmevent22 = 0x336, - mhpmevent23 = 0x337, - mhpmevent24 = 0x338, - mhpmevent25 = 0x339, - mhpmevent26 = 0x33A, - mhpmevent27 = 0x33B, - mhpmevent28 = 0x33C, - mhpmevent29 = 0x33D, - mhpmevent30 = 0x33E, - mhpmevent31 = 0x33F, - - // Debug/Trace Registers (shared with Debug Mode) - tselect = 0x7A0, - tdata1 = 0x7A1, - tdata2 = 0x7A2, - tdata3 = 0x7A3, - tcontrol = 0x7A5, - mcontext = 0x7A8, - - // Debug Mode Registers - dcsr = 0x7B0, - dpc = 0x7B1, - dscratch0 = 0x7B2, - dscratch1 = 0x7B3, -} - -#[expect( - clippy::identity_op, - reason = "Shifts by 0 are allowed for clarity and consistency" -)] -impl CSRegister { - // Since read-only misa.MXL = 0b10, we have MXLEN = 64 from table 3.1 - const MXLEN: u64 = 64; - const SXLEN: u64 = CSRegister::MXLEN; - const MXL_ENCODING: CSRRepr = 0b10; - - /// Determine the priviledge level required to access this CSR. - #[inline(always)] - pub fn privilege(self) -> Privilege { - match self as usize { - 0x000..=0x0FF - | 0x400..=0x4FF - | 0x800..=0x8FF - | 0xC00..=0xC7F - | 0xC80..=0xCBF - | 0xCC0..=0xCFF => Privilege::Unprivileged, - - 0x100..=0x1FF - | 0x500..=0x57F - | 0x580..=0x5BF - | 0x5C0..=0x5FF - | 0x900..=0x97F - | 0x980..=0x9BF - | 0x9C0..=0x9FF - | 0xD00..=0xD7F - | 0xD80..=0xDBF - | 0xDC0..=0xDFF => Privilege::Supervisor, - - 0x200..=0x2FF - | 0x600..=0x67F - | 0x680..=0x6BF - | 0x6C0..=0x6FF - | 0xA00..=0xA7F - | 0xA80..=0xABF - | 0xAC0..=0xAFF - | 0xE00..=0xE7F - | 0xE80..=0xEBF - | 0xEC0..=0xEFF => Privilege::Hypervisor, - - 0x300..=0x3FF - | 0x700..=0x77F - | 0x780..=0x79F - | 0x7A0..=0x7AF - | 0x7B0..=0x7BF - | 0x7C0..=0x7FF - | 0xB00..=0xB7F - | 0xB80..=0xBBF - | 0xBC0..=0xBFF - | 0xF00..=0xF7F - | 0xF80..=0xFBF - | 0xFC0..=0xFFF => Privilege::Machine, - - reg => unreachable!("Invalid CSR {reg:#x}",), - } - } - - /// Determines if the register is read-only - #[inline(always)] - pub fn is_read_only(self) -> bool { - // Rules & Table of read-write / read-only ranges are in section 2.1 & table 2.1 - (self as usize >> 10) & 0b11 == 0b11 - } - - /// Enforce the WPRI and WLRL field specifications. - /// - /// Either return the value to be written, or None to signify that no write is necessary, - /// leaving the existing value in its place. - #[inline(always)] - pub fn make_value_writable(self, value: CSRRepr) -> Option { - // respect the reserved WPRI fields, setting them to 0 - let value = self.clear_wpri_fields(value); - // apply WARL rules - let value = self.transform_warl_fields(value)?; - // check if value is legal w.r.t. WLRL fields - self.is_legal(value).then_some(value) - } - - const WPRI_MASK_EMPTY: CSRRepr = CSRRepr::MAX; - - const WPRI_MASK_MSTATUS: CSRRepr = - !((ones(1) << 0) | (ones(1) << 2) | (ones(1) << 4) | (ones(9) << 23) | (ones(25) << 38)); - - const WPRI_MASK_MENVCFG: CSRRepr = !((ones(3) << 1) | (ones(54) << 8)); - - const WPRI_MASK_MSECCFG: CSRRepr = !((ones(5) << 3) | (ones(CSRegister::MXLEN - 10) << 10)); - - const WPRI_MASK_SSTATUS: CSRRepr = !((ones(1) << 0) - | (ones(3) << 2) - | (ones(1) << 7) - | (ones(2) << 11) - | (ones(1) << 17) - | (ones(12) << 20) - | (ones(29) << 34)); - - const WPRI_MASK_SENVCFG: CSRRepr = !((ones(3) << 1) | (ones(CSRegister::SXLEN - 8) << 8)); - - const WPRI_MASK_MNCAUSE: CSRRepr = !(ones(1) << (CSRegister::MXLEN - 1)); - - const WPRI_MASK_MNSTATUS: CSRRepr = - !((ones(3) << 0) | (ones(3) << 4) | (ones(3) << 8) | (ones(CSRegister::MXLEN - 13) << 13)); - - /// Return the mask of non reserved bits, (WPRI bits are 0) - /// Relevant section 2.3 - privileged spec - #[inline(always)] - pub fn wpri_mask(self) -> CSRRepr { - match self { - CSRegister::mstatus => CSRegister::WPRI_MASK_MSTATUS, - CSRegister::menvcfg => CSRegister::WPRI_MASK_MENVCFG, - CSRegister::mseccfg => CSRegister::WPRI_MASK_MSECCFG, - CSRegister::sstatus => CSRegister::WPRI_MASK_SSTATUS, - CSRegister::senvcfg => CSRegister::WPRI_MASK_SENVCFG, - CSRegister::mncause => CSRegister::WPRI_MASK_MNCAUSE, - CSRegister::mnstatus => CSRegister::WPRI_MASK_MNSTATUS, - _ => CSRegister::WPRI_MASK_EMPTY, - } - } - - /// Ensures that WPRI fields are kept read-only zero. - /// - /// Conforming to Section 2.3 - privileged spec - #[inline(always)] - pub fn clear_wpri_fields(self, new_value: CSRRepr) -> CSRRepr { - new_value & self.wpri_mask() - } - - /// Possible `mcause` values, table 3.6 - const WLRL_MCAUSE_VALUES: [CSRRepr; 20] = { - const INTERRUPT_BIT: CSRRepr = 1 << (CSRRepr::BITS - 1); - [ - // interrupt exception codes - INTERRUPT_BIT | 1, // Supervisor software interrupt - INTERRUPT_BIT | 3, // Machine software interrupt - INTERRUPT_BIT | 5, // Supervisor timer interrupt - INTERRUPT_BIT | 7, // Machine timer interrupt - INTERRUPT_BIT | 9, // Supervisor external interrupt - INTERRUPT_BIT | 11, // Machine external interrupt - // other values between 0-15 are reserved - // values >= 16 for platform use, we treat them as reserved - - // non-interrupt exception codes - 0, // Instruction address misaligned - 1, // Instruction address fault - 2, // Illegal instruction - 3, // Breakpoint - 4, // Load address misaligned - 5, // Load access fault - 6, // Store/AMO address misaligned - 7, // Store/AMO access fault - 8, // Environment call from U-mode - 9, // Environment call from S-mode - 11, // Environment call from M-mode - 12, // Instruction page fault - 13, // Load page fault - 15, // Store/AMO page fault - // other values between 0-15 are reserved - // values 16-23, 32-47, >= 64 are reserved - // values 24-31, 48-63 are for custom use, we treat them as reserved - ] - }; - - /// Possible `scause` values, table 4.2 - const WLRL_SCAUSE_VALUES: [u64; 16] = { - const INTERRUPT_BIT: u64 = 1 << 63; - [ - // interrupt exception codes - INTERRUPT_BIT | 1, // Supervisor software interrupt - INTERRUPT_BIT | 5, // Supervisor timer interrupt - INTERRUPT_BIT | 9, // Supervisor external interrupt - // other values between 0-15 are reserved - // values >= 16 for platform use, we treat them as reserved - - // non-interrupt exception codes - 0, // Instruction address misaligned - 1, // Instruction address fault - 2, // Illegal instruction - 3, // Breakpoint - 4, // Load address misaligned - 5, // Load access fault - 6, // Store/AMO address misaligned - 7, // Store/AMO access fault - 8, // Environment call from U-mode - 9, // Environment call from S-mode - 12, // Instruction page fault - 13, // Load page fault - 15, // Store/AMO page fault - // other values between 0-15 are reserved - // values 16-23, 32-47, >= 64 are reserved - // values 24-31, 48-63 are for custom use, we treat them as reserved - ] - }; - - /// Obtain the legal values for a register. - /// - /// If all the values are legal or the register is not WLRL, return an empty list. - #[inline(always)] - pub fn legal_values(self) -> &'static [u64] { - match self { - CSRegister::mcause => &CSRegister::WLRL_MCAUSE_VALUES, - CSRegister::scause => &CSRegister::WLRL_SCAUSE_VALUES, - _ => &[], - } - } - - /// If the register is WLRL, return if `new_value` is legal, false otherwise - /// - /// Section 2.3 - privileged spec - #[inline(always)] - pub fn is_legal(self, new_value: CSRRepr) -> bool { - let legal_values = self.legal_values(); - // if no legal values are defined, then the register is not WLRL - legal_values.is_empty() || legal_values.contains(&new_value) - } - - /// Value for CSR `misa`, see section 3.1.1 & tables 3.1 (MXL) & 3.2 (Extensions) - const WARL_MISA_VALUE: CSRRepr = { - /* MXLEN encoding of 64 bits */ - const MXL_MASK: u64 = CSRegister::MXL_ENCODING << 62; - /* Extensions (A + C + D + F + I + M + S + U) */ - const ATOMIC_EXT: u64 = 1 << 0; - const COMPRESSED_EXT: u64 = 1 << 2; - const DOUBLE_EXT: u64 = 1 << 3; - const SINGLE_EXT: u64 = 1 << 5; - const RV64I_ISA_EXT: u64 = 1 << 8; - const MULT_DIV_EXT: u64 = 1 << 12; - const SUPERVISOR_EXT: u64 = 1 << 18; - const USER_EXT: u64 = 1 << 20; - /* MXL */ - MXL_MASK | - /* Extensions */ - ATOMIC_EXT | - COMPRESSED_EXT | - DOUBLE_EXT | - SINGLE_EXT | - RV64I_ISA_EXT | - MULT_DIV_EXT | - SUPERVISOR_EXT | - USER_EXT - }; - - /// Ensures WARL registers / fields are respected - /// - /// Section 2.3 - privileged spec - /// - /// If `None` is returned, then no update must take place - #[inline(always)] - pub fn transform_warl_fields(self, new_value: CSRRepr) -> Option { - let write_value = match self { - CSRegister::misa => CSRegister::WARL_MISA_VALUE, - CSRegister::medeleg => new_value & CSRegister::WARL_MASK_MEDELEG, - CSRegister::mideleg => new_value & CSRegister::WARL_MASK_MIDELEG, - CSRegister::mtvec | CSRegister::stvec => new_value & CSRegister::WARL_MASK_XTVEC, - CSRegister::mip | CSRegister::mie => new_value & CSRegister::WARL_MASK_MIP_MIE, - CSRegister::sip | CSRegister::sie => new_value & CSRegister::WARL_MASK_SIP_SIE, - CSRegister::mepc | CSRegister::sepc | CSRegister::mnepc => { - new_value & CSRegister::WARL_MASK_XEPC - } - CSRegister::satp => Satp::from_bits(new_value).normalise().to_bits(), - CSRegister::mstatus => MStatus::from_bits(new_value).normalise().to_bits(), - CSRegister::sstatus => SStatus::from_bits(new_value).normalise().to_bits(), - CSRegister::mnstatus => MNStatus::from_bits(new_value).normalise().to_bits(), - _ => new_value, - }; - Some(write_value) - } - - /// See section 3.1.8 and table 3.6 - /// - /// Exception codes to delegate. - /// If an exception can't be thrown from a lower privilege mode, set it here read-only 0 - const WARL_MASK_MEDELEG: CSRRepr = !( - (ones(1) << 10) | (ones(1) << 11) | (ones(1) << 14) | (ones(CSRegister::MXLEN - 16) << 16) - // reserved & custom use - ); - - /// See section 3.1.8 and table 3.6 - /// - /// Interrupt codes to delegate. - /// If an interrupt can't be thrown from a lower privilege mode, set it here read-only 0 - const WARL_MASK_MIDELEG: CSRRepr = !( - (ones(1) << 0) - | (ones(1) << 2) - | (ones(1) << 4) - | (ones(1) << 6) - | (ones(1) << 8) - | (ones(1) << 10) - | (ones(4) << 12) - | (ones(CSRegister::MXLEN - 16) << 16) - // custom use - ); - - /// `mtvec.MODE = mtvec[1:0]`. - /// Only `0` and `1` values are allowed for `MODE`, so we treat `MODE[1]` as read-only 0 - /// - /// `mtvec.BASE = mtvec[MXLEN-1:2] << 2` (since it has to be 4-byte aligned). - /// The same applies for stvec. Sections 3.1.7 & 4.1.2 - const WARL_MASK_XTVEC: CSRRepr = !(ones(1) << 1); - - /// WARL mask for mip/mie interrupt bits. - /// - /// 0-15 are for standard interrupts. The rest are for custom used and are treated as reserved - const WARL_MASK_MIP_MIE: CSRRepr = Interrupt::MACHINE_BIT_MASK | Interrupt::SUPERVISOR_BIT_MASK; - - /// WARL mask for sip/sie interrupt bits. - /// - /// 0-15 are for standard interrupts. The rest are for custom used and are treated as reserved - const WARL_MASK_SIP_SIE: CSRRepr = Interrupt::SUPERVISOR_BIT_MASK; - - /// WARL mask for mepc/sepc/mnepc addresses. - /// - /// Since extension C is supported, we only make the low bit read-only 0 - const WARL_MASK_XEPC: CSRRepr = !1; - - /// FCSR mask - const FCSR_MASK: CSRRepr = Self::FRM_MASK | Self::FFLAGS_MASK; - - /// FRM mask - const FRM_MASK: CSRRepr = 0b111 << Self::FRM_SHIFT; - - /// FRM is bits 5..7 - const FRM_SHIFT: usize = 5; - - /// FFLAGS mask - const FFLAGS_MASK: CSRRepr = 0b11111; - - /// Get the default value for the register. - fn default_value(&self) -> u64 { - match self { - CSRegister::mnscratch => 0, - CSRegister::mnepc => 0, - CSRegister::mnstatus => MNStatus::from_bits(0).normalise().to_bits(), - CSRegister::mncause => { - // The interrupt bit of mncause is always 1 - ones(1) << 31 - } - - CSRegister::cycle - | CSRegister::time - | CSRegister::instret - | CSRegister::mcycle - | CSRegister::minstret => { - // Default is that the machine starts at 0 - 0 - } - - CSRegister::hpmcounter3 - | CSRegister::hpmcounter4 - | CSRegister::hpmcounter5 - | CSRegister::hpmcounter6 - | CSRegister::hpmcounter7 - | CSRegister::hpmcounter8 - | CSRegister::hpmcounter9 - | CSRegister::hpmcounter10 - | CSRegister::hpmcounter11 - | CSRegister::hpmcounter12 - | CSRegister::hpmcounter13 - | CSRegister::hpmcounter14 - | CSRegister::hpmcounter15 - | CSRegister::hpmcounter16 - | CSRegister::hpmcounter17 - | CSRegister::hpmcounter18 - | CSRegister::hpmcounter19 - | CSRegister::hpmcounter20 - | CSRegister::hpmcounter21 - | CSRegister::hpmcounter22 - | CSRegister::hpmcounter23 - | CSRegister::hpmcounter24 - | CSRegister::hpmcounter25 - | CSRegister::hpmcounter26 - | CSRegister::hpmcounter27 - | CSRegister::hpmcounter28 - | CSRegister::hpmcounter29 - | CSRegister::hpmcounter30 - | CSRegister::hpmcounter31 - | CSRegister::mhpmcounter3 - | CSRegister::mhpmcounter4 - | CSRegister::mhpmcounter5 - | CSRegister::mhpmcounter6 - | CSRegister::mhpmcounter7 - | CSRegister::mhpmcounter8 - | CSRegister::mhpmcounter9 - | CSRegister::mhpmcounter10 - | CSRegister::mhpmcounter11 - | CSRegister::mhpmcounter12 - | CSRegister::mhpmcounter13 - | CSRegister::mhpmcounter14 - | CSRegister::mhpmcounter15 - | CSRegister::mhpmcounter16 - | CSRegister::mhpmcounter17 - | CSRegister::mhpmcounter18 - | CSRegister::mhpmcounter19 - | CSRegister::mhpmcounter20 - | CSRegister::mhpmcounter21 - | CSRegister::mhpmcounter22 - | CSRegister::mhpmcounter23 - | CSRegister::mhpmcounter24 - | CSRegister::mhpmcounter25 - | CSRegister::mhpmcounter26 - | CSRegister::mhpmcounter27 - | CSRegister::mhpmcounter28 - | CSRegister::mhpmcounter29 - | CSRegister::mhpmcounter30 - | CSRegister::mhpmcounter31 => { - // All counters shall start at 0 again - 0 - } - - CSRegister::mhpmevent3 - | CSRegister::mhpmevent4 - | CSRegister::mhpmevent5 - | CSRegister::mhpmevent6 - | CSRegister::mhpmevent7 - | CSRegister::mhpmevent8 - | CSRegister::mhpmevent9 - | CSRegister::mhpmevent10 - | CSRegister::mhpmevent11 - | CSRegister::mhpmevent12 - | CSRegister::mhpmevent13 - | CSRegister::mhpmevent14 - | CSRegister::mhpmevent15 - | CSRegister::mhpmevent16 - | CSRegister::mhpmevent17 - | CSRegister::mhpmevent18 - | CSRegister::mhpmevent19 - | CSRegister::mhpmevent20 - | CSRegister::mhpmevent21 - | CSRegister::mhpmevent22 - | CSRegister::mhpmevent23 - | CSRegister::mhpmevent24 - | CSRegister::mhpmevent25 - | CSRegister::mhpmevent26 - | CSRegister::mhpmevent27 - | CSRegister::mhpmevent28 - | CSRegister::mhpmevent29 - | CSRegister::mhpmevent30 - | CSRegister::mhpmevent31 => { - // Zero means "no event" - 0 - } - - CSRegister::mcountinhibit => { - // All counter are enabled - 0 - } - - CSRegister::scounteren | CSRegister::mcounteren => { - // All counters are readable in all privilege levels - ones(32) - } - - CSRegister::fflags => { - // Resets accrued floating-point exceptions - 0b00000 - } - - CSRegister::frm => { - // 000 = RNE aka "round to nearest, ties to even" - 0b000 - } - - CSRegister::fcsr => { - // fcsr is a combination of fflags and fcsr - CSRegister::fflags.default_value() & (CSRegister::frm.default_value() << 5) - } - - CSRegister::pmpcfg0 - | CSRegister::pmpcfg2 - | CSRegister::pmpcfg4 - | CSRegister::pmpcfg6 - | CSRegister::pmpcfg8 - | CSRegister::pmpcfg10 - | CSRegister::pmpcfg12 - | CSRegister::pmpcfg14 => { - // Physical-memory protection configuration is off initially - 0 - } - - CSRegister::pmpaddr0 - | CSRegister::pmpaddr1 - | CSRegister::pmpaddr2 - | CSRegister::pmpaddr3 - | CSRegister::pmpaddr4 - | CSRegister::pmpaddr5 - | CSRegister::pmpaddr6 - | CSRegister::pmpaddr7 - | CSRegister::pmpaddr8 - | CSRegister::pmpaddr9 - | CSRegister::pmpaddr10 - | CSRegister::pmpaddr11 - | CSRegister::pmpaddr12 - | CSRegister::pmpaddr13 - | CSRegister::pmpaddr14 - | CSRegister::pmpaddr15 - | CSRegister::pmpaddr16 - | CSRegister::pmpaddr17 - | CSRegister::pmpaddr18 - | CSRegister::pmpaddr19 - | CSRegister::pmpaddr20 - | CSRegister::pmpaddr21 - | CSRegister::pmpaddr22 - | CSRegister::pmpaddr23 - | CSRegister::pmpaddr24 - | CSRegister::pmpaddr25 - | CSRegister::pmpaddr26 - | CSRegister::pmpaddr27 - | CSRegister::pmpaddr28 - | CSRegister::pmpaddr29 - | CSRegister::pmpaddr30 - | CSRegister::pmpaddr31 - | CSRegister::pmpaddr32 - | CSRegister::pmpaddr33 - | CSRegister::pmpaddr34 - | CSRegister::pmpaddr35 - | CSRegister::pmpaddr36 - | CSRegister::pmpaddr37 - | CSRegister::pmpaddr38 - | CSRegister::pmpaddr39 - | CSRegister::pmpaddr40 - | CSRegister::pmpaddr41 - | CSRegister::pmpaddr42 - | CSRegister::pmpaddr43 - | CSRegister::pmpaddr44 - | CSRegister::pmpaddr45 - | CSRegister::pmpaddr46 - | CSRegister::pmpaddr47 - | CSRegister::pmpaddr48 - | CSRegister::pmpaddr49 - | CSRegister::pmpaddr50 - | CSRegister::pmpaddr51 - | CSRegister::pmpaddr52 - | CSRegister::pmpaddr53 - | CSRegister::pmpaddr54 - | CSRegister::pmpaddr55 - | CSRegister::pmpaddr56 - | CSRegister::pmpaddr57 - | CSRegister::pmpaddr58 - | CSRegister::pmpaddr59 - | CSRegister::pmpaddr60 - | CSRegister::pmpaddr61 - | CSRegister::pmpaddr62 - | CSRegister::pmpaddr63 => { - // Physical-memory protection configuration is off initially - 0 - } - - // We're always on hart 0 - CSRegister::mhartid => 0, - - // Vendor ID is not implemented - CSRegister::mvendorid => 0, - - // Arch ID is not implemented - CSRegister::marchid => 0, - - // Implementation ID is not implemented - CSRegister::mimpid => 0, - - // misa is pretty much fixed - CSRegister::misa => CSRegister::WARL_MISA_VALUE, - - // Scratch registers are 0 by default - CSRegister::mscratch | CSRegister::sscratch => 0, - - // Project view from mstatus - CSRegister::sstatus => SStatus::default().to_bits(), - CSRegister::mstatus => MStatus::default().to_bits(), - - // Trap handling shall not be set up initially - CSRegister::stvec | CSRegister::mtvec => 0, - - // No interrupts are enabled - CSRegister::sie | CSRegister::mie => 0, - - // No address translation initially - CSRegister::satp => Satp::default().to_bits(), - - // No exception or trap inflight or pending - CSRegister::scause | CSRegister::mcause => 0, - CSRegister::sepc | CSRegister::mepc => 0, - CSRegister::stval | CSRegister::mtval | CSRegister::mtval2 => 0, - CSRegister::sip | CSRegister::mip => 0, - CSRegister::mtinst => 0, - - // No specific environment configuration - CSRegister::senvcfg | CSRegister::menvcfg => 0, - - // No hardware configuration supported - CSRegister::mconfigptr => 0, - - // Delegate all exceptions and interrupts to S mode - CSRegister::medeleg => ones(CSRegister::MXLEN), - CSRegister::mideleg => ones(CSRegister::MXLEN), - - // Security extensions are not enabled - CSRegister::mseccfg => 0, - - // Unsupported debug, hypervisor and virtual supervisor extensions - CSRegister::scontext => 0, - CSRegister::hstatus => 0, - CSRegister::hedeleg => 0, - CSRegister::hideleg => 0, - CSRegister::hie => 0, - CSRegister::hcounteren => 0, - CSRegister::hgeie => 0, - CSRegister::htval => 0, - CSRegister::hip => 0, - CSRegister::hvip => 0, - CSRegister::htinst => 0, - CSRegister::hgeip => 0, - CSRegister::henvcfg => 0, - CSRegister::hgatp => 0, - CSRegister::hcontext => 0, - CSRegister::htimedelta => 0, - CSRegister::vsstatus => 0, - CSRegister::vsie => 0, - CSRegister::vstvec => 0, - CSRegister::vsscratch => 0, - CSRegister::vsepc => 0, - CSRegister::vscause => 0, - CSRegister::vstval => 0, - CSRegister::vsip => 0, - CSRegister::vsatp => 0, - CSRegister::tselect => 0, - CSRegister::tdata1 => 0, - CSRegister::tdata2 => 0, - CSRegister::tdata3 => 0, - CSRegister::tcontrol => 0, - CSRegister::mcontext => 0, - CSRegister::dcsr => 0, - CSRegister::dpc => 0, - CSRegister::dscratch0 => 0, - CSRegister::dscratch1 => 0, - } - } - - /// Attempt to parse the 32-bit integer as a register identifier. - pub const fn try_parse(r: u32) -> Option { - use CSRegister::*; - - match r { - // Unprivileged Floating-Point CSRs - 0x001 => Some(fflags), - 0x002 => Some(frm), - 0x003 => Some(fcsr), - - // Unprivileged Counter/Timers - 0xC00 => Some(cycle), - 0xC01 => Some(time), - 0xC02 => Some(instret), - 0xC03 => Some(hpmcounter3), - 0xC04 => Some(hpmcounter4), - 0xC05 => Some(hpmcounter5), - 0xC06 => Some(hpmcounter6), - 0xC07 => Some(hpmcounter7), - 0xC08 => Some(hpmcounter8), - 0xC09 => Some(hpmcounter9), - 0xC0A => Some(hpmcounter10), - 0xC0B => Some(hpmcounter11), - 0xC0C => Some(hpmcounter12), - 0xC0D => Some(hpmcounter13), - 0xC0E => Some(hpmcounter14), - 0xC0F => Some(hpmcounter15), - 0xC10 => Some(hpmcounter16), - 0xC11 => Some(hpmcounter17), - 0xC12 => Some(hpmcounter18), - 0xC13 => Some(hpmcounter19), - 0xC14 => Some(hpmcounter20), - 0xC15 => Some(hpmcounter21), - 0xC16 => Some(hpmcounter22), - 0xC17 => Some(hpmcounter23), - 0xC18 => Some(hpmcounter24), - 0xC19 => Some(hpmcounter25), - 0xC1A => Some(hpmcounter26), - 0xC1B => Some(hpmcounter27), - 0xC1C => Some(hpmcounter28), - 0xC1D => Some(hpmcounter29), - 0xC1E => Some(hpmcounter30), - 0xC1F => Some(hpmcounter31), - - // Supervisor Trap Setup - 0x100 => Some(sstatus), - 0x104 => Some(sie), - 0x105 => Some(stvec), - 0x106 => Some(scounteren), - - // Supervisor Configuration - 0x10A => Some(senvcfg), - - // Supervisor Trap Handling - 0x140 => Some(sscratch), - 0x141 => Some(sepc), - 0x142 => Some(scause), - 0x143 => Some(stval), - 0x144 => Some(sip), - - // Supervisor Protection and Translation - 0x180 => Some(satp), - - // Supervisor Debug/Trace Registers - 0x5A8 => Some(scontext), - - // Hypervisor Trap Setup - 0x600 => Some(hstatus), - 0x602 => Some(hedeleg), - 0x603 => Some(hideleg), - 0x604 => Some(hie), - 0x606 => Some(hcounteren), - 0x607 => Some(hgeie), - - // Hypervisor Trap Handling - 0x643 => Some(htval), - 0x644 => Some(hip), - 0x645 => Some(hvip), - 0x64A => Some(htinst), - 0xE12 => Some(hgeip), - - // Hypervisor Configuration - 0x60A => Some(henvcfg), - - // Hypervisor Protection and Translation - 0x680 => Some(hgatp), - - // Hypervisor Debug/ Trace Registers - 0x6A8 => Some(hcontext), - - // Hypervisor Counter/Timer Virtualization Registers - 0x605 => Some(htimedelta), - - // Virtual Supervisor Registers - 0x200 => Some(vsstatus), - 0x204 => Some(vsie), - 0x205 => Some(vstvec), - 0x240 => Some(vsscratch), - 0x241 => Some(vsepc), - 0x242 => Some(vscause), - 0x243 => Some(vstval), - 0x244 => Some(vsip), - 0x280 => Some(vsatp), - - // Machine Information Registers - 0xF11 => Some(mvendorid), - 0xF12 => Some(marchid), - 0xF13 => Some(mimpid), - 0xF14 => Some(mhartid), - 0xF15 => Some(mconfigptr), - - // Machine Trap Setup - 0x300 => Some(mstatus), - 0x301 => Some(misa), - 0x302 => Some(medeleg), - 0x303 => Some(mideleg), - 0x304 => Some(mie), - 0x305 => Some(mtvec), - 0x306 => Some(mcounteren), - - // Machine Trap Handling - 0x340 => Some(mscratch), - 0x341 => Some(mepc), - 0x342 => Some(mcause), - 0x343 => Some(mtval), - 0x344 => Some(mip), - 0x34A => Some(mtinst), - 0x34B => Some(mtval2), - - // Machine Configuration - 0x30A => Some(menvcfg), - 0x747 => Some(mseccfg), - - // Machine Memory Protection - 0x3A0 => Some(pmpcfg0), - 0x3A2 => Some(pmpcfg2), - 0x3A4 => Some(pmpcfg4), - 0x3A6 => Some(pmpcfg6), - 0x3A8 => Some(pmpcfg8), - 0x3AA => Some(pmpcfg10), - 0x3AC => Some(pmpcfg12), - 0x3AE => Some(pmpcfg14), - 0x3B0 => Some(pmpaddr0), - 0x3B1 => Some(pmpaddr1), - 0x3B2 => Some(pmpaddr2), - 0x3B3 => Some(pmpaddr3), - 0x3B4 => Some(pmpaddr4), - 0x3B5 => Some(pmpaddr5), - 0x3B6 => Some(pmpaddr6), - 0x3B7 => Some(pmpaddr7), - 0x3B8 => Some(pmpaddr8), - 0x3B9 => Some(pmpaddr9), - 0x3BA => Some(pmpaddr10), - 0x3BB => Some(pmpaddr11), - 0x3BC => Some(pmpaddr12), - 0x3BD => Some(pmpaddr13), - 0x3BE => Some(pmpaddr14), - 0x3BF => Some(pmpaddr15), - 0x3C0 => Some(pmpaddr16), - 0x3C1 => Some(pmpaddr17), - 0x3C2 => Some(pmpaddr18), - 0x3C3 => Some(pmpaddr19), - 0x3C4 => Some(pmpaddr20), - 0x3C5 => Some(pmpaddr21), - 0x3C6 => Some(pmpaddr22), - 0x3C7 => Some(pmpaddr23), - 0x3C8 => Some(pmpaddr24), - 0x3C9 => Some(pmpaddr25), - 0x3CA => Some(pmpaddr26), - 0x3CB => Some(pmpaddr27), - 0x3CC => Some(pmpaddr28), - 0x3CD => Some(pmpaddr29), - 0x3CE => Some(pmpaddr30), - 0x3CF => Some(pmpaddr31), - 0x3D0 => Some(pmpaddr32), - 0x3D1 => Some(pmpaddr33), - 0x3D2 => Some(pmpaddr34), - 0x3D3 => Some(pmpaddr35), - 0x3D4 => Some(pmpaddr36), - 0x3D5 => Some(pmpaddr37), - 0x3D6 => Some(pmpaddr38), - 0x3D7 => Some(pmpaddr39), - 0x3D8 => Some(pmpaddr40), - 0x3D9 => Some(pmpaddr41), - 0x3DA => Some(pmpaddr42), - 0x3DB => Some(pmpaddr43), - 0x3DC => Some(pmpaddr44), - 0x3DD => Some(pmpaddr45), - 0x3DE => Some(pmpaddr46), - 0x3DF => Some(pmpaddr47), - 0x3E0 => Some(pmpaddr48), - 0x3E1 => Some(pmpaddr49), - 0x3E2 => Some(pmpaddr50), - 0x3E3 => Some(pmpaddr51), - 0x3E4 => Some(pmpaddr52), - 0x3E5 => Some(pmpaddr53), - 0x3E6 => Some(pmpaddr54), - 0x3E7 => Some(pmpaddr55), - 0x3E8 => Some(pmpaddr56), - 0x3E9 => Some(pmpaddr57), - 0x3EA => Some(pmpaddr58), - 0x3EB => Some(pmpaddr59), - 0x3EC => Some(pmpaddr60), - 0x3ED => Some(pmpaddr61), - 0x3EE => Some(pmpaddr62), - 0x3EF => Some(pmpaddr63), - - // Machine Non-Maskable Interrupt Handling - // The draft `Smrnmi` extension is not supported in objdump, printing - // CSR address directly instead - 0x740 => Some(mnscratch), - 0x741 => Some(mnepc), - 0x742 => Some(mncause), - 0x744 => Some(mnstatus), - - // Machine Counter/Timers - 0xB00 => Some(mcycle), - 0xB02 => Some(minstret), - 0xB03 => Some(mhpmcounter3), - 0xB04 => Some(mhpmcounter4), - 0xB05 => Some(mhpmcounter5), - 0xB06 => Some(mhpmcounter6), - 0xB07 => Some(mhpmcounter7), - 0xB08 => Some(mhpmcounter8), - 0xB09 => Some(mhpmcounter9), - 0xB0A => Some(mhpmcounter10), - 0xB0B => Some(mhpmcounter11), - 0xB0C => Some(mhpmcounter12), - 0xB0D => Some(mhpmcounter13), - 0xB0E => Some(mhpmcounter14), - 0xB0F => Some(mhpmcounter15), - 0xB10 => Some(mhpmcounter16), - 0xB11 => Some(mhpmcounter17), - 0xB12 => Some(mhpmcounter18), - 0xB13 => Some(mhpmcounter19), - 0xB14 => Some(mhpmcounter20), - 0xB15 => Some(mhpmcounter21), - 0xB16 => Some(mhpmcounter22), - 0xB17 => Some(mhpmcounter23), - 0xB18 => Some(mhpmcounter24), - 0xB19 => Some(mhpmcounter25), - 0xB1A => Some(mhpmcounter26), - 0xB1B => Some(mhpmcounter27), - 0xB1C => Some(mhpmcounter28), - 0xB1D => Some(mhpmcounter29), - 0xB1E => Some(mhpmcounter30), - 0xB1F => Some(mhpmcounter31), - - // Machine Counter Setup - 0x320 => Some(mcountinhibit), - 0x323 => Some(mhpmevent3), - 0x324 => Some(mhpmevent4), - 0x325 => Some(mhpmevent5), - 0x326 => Some(mhpmevent6), - 0x327 => Some(mhpmevent7), - 0x328 => Some(mhpmevent8), - 0x329 => Some(mhpmevent9), - 0x32A => Some(mhpmevent10), - 0x32B => Some(mhpmevent11), - 0x32C => Some(mhpmevent12), - 0x32D => Some(mhpmevent13), - 0x32E => Some(mhpmevent14), - 0x32F => Some(mhpmevent15), - 0x330 => Some(mhpmevent16), - 0x331 => Some(mhpmevent17), - 0x332 => Some(mhpmevent18), - 0x333 => Some(mhpmevent19), - 0x334 => Some(mhpmevent20), - 0x335 => Some(mhpmevent21), - 0x336 => Some(mhpmevent22), - 0x337 => Some(mhpmevent23), - 0x338 => Some(mhpmevent24), - 0x339 => Some(mhpmevent25), - 0x33A => Some(mhpmevent26), - 0x33B => Some(mhpmevent27), - 0x33C => Some(mhpmevent28), - 0x33D => Some(mhpmevent29), - 0x33E => Some(mhpmevent30), - 0x33F => Some(mhpmevent31), - - // Debug/Trace Registers (shared with Debug Mode) - 0x7A0 => Some(tselect), - 0x7A1 => Some(tdata1), - 0x7A2 => Some(tdata2), - 0x7A3 => Some(tdata3), - 0x7A5 => Some(tcontrol), - 0x7A8 => Some(mcontext), - - // Debug Mode Registers - 0x7B0 => Some(dcsr), - 0x7B1 => Some(dpc), - 0x7B2 => Some(dscratch0), - 0x7B3 => Some(dscratch1), - _ => None, - } - } -} - -/// Representation of a value in a CSR -pub use values::CSRRepr; - -/// Return type of read/write operations -pub type Result = core::result::Result; - -/// Checks that `mode` can access the register `reg`. -/// -/// Throws [`Exception::IllegalInstruction`] in case of insufficient privilege. -/// Section 2.1 - privileged spec -#[inline(always)] -fn check_privilege(reg: CSRegister) -> Result<()> { - if Privilege::Unprivileged < reg.privilege() { - return Err(Exception::IllegalInstruction); - } - - Ok(()) -} - -/// Checks that `reg` is write-able. -/// -/// Throws [`Exception::IllegalInstruction`] in case of wrong access rights. -/// Section 2.1 - privileged spec -#[inline(always)] -pub fn check_write(reg: CSRegister) -> Result<()> { - if reg.is_read_only() { - return Err(Exception::IllegalInstruction); - } - - Ok(()) -} - -/// Check if access to SATP is valid, conforming to TVM flag. -/// -/// See section 3.1.6.5 -fn check_satp_access(csr: CSRegister, tvm_field: bool) -> Result<()> { - if tvm_field && csr == CSRegister::satp { - return Err(Exception::IllegalInstruction); - } - - Ok(()) -} - -fn check_fs_access(csr: CSRegister, fs_field: ExtensionValue) -> Result<()> { - use CSRegister::*; - - if matches!(csr, fcsr | frm | fflags) && fs_field == ExtensionValue::Off { - Err(Exception::IllegalInstruction) - } else { - Ok(()) - } -} - -/// Perform general checks on all read or write operations on a CSR. -/// -/// Examples of checks: Privilege checks, SATP trapping -pub fn access_checks(csr: CSRegister, hart_state: &HartState) -> Result<()> { - check_privilege(csr)?; - let mstatus = hart_state.csregisters.mstatus(); - let tvm = mstatus.tvm.read(); - check_satp_access(csr, tvm)?; - let fs = mstatus.fs.read(); - check_fs_access(csr, fs) -} - -/// CSRs -pub struct CSRegisters { - registers: CSRValues, -} - -impl CSRegisters { - #[inline(always)] - pub const fn mstatus(&self) -> &MStatusValue { - &self.registers.mstatus - } - - #[inline(always)] - pub fn mstatus_mut(&mut self) -> &mut MStatusValue { - &mut self.registers.mstatus - } - - /// Transform the write operation to account for shadow registers. - /// (e.g. `sstatus` register) - /// - /// Sections 3.1.6 & 4.1.1 - #[inline(always)] - fn transform_write(&self, reg: CSRegister, value: CSRRepr) -> CSRRepr - where - M: backend::ManagerRead, - { - // the update of a shadow register follows the steps: - // 1. keep the shadowed fields from [value] - // 2. all the other, non-shadowed fields are the underlying register - // masked with the inverse of the shadowed fields mask - // Note: This works because currently there are no shadowed WLRL registers - match reg { - CSRegister::sstatus => { - let mstatus = self.registers.mstatus.read(); - let mstatus = MStatus::from_bits(mstatus); - let sstatus = SStatus::from_bits(value); - let mstatus = sstatus.to_mstatus(mstatus); - mstatus.to_bits() - } - CSRegister::sip => { - let mip = self.registers.mip.read(); - let sip_only = value & CSRegister::WARL_MASK_SIP_SIE; - let mip_only = mip & !CSRegister::WARL_MASK_SIP_SIE; - sip_only | mip_only - } - CSRegister::sie => { - let mie = self.registers.mie.read(); - let sie_only = value & CSRegister::WARL_MASK_SIP_SIE; - let mie_only = mie & !CSRegister::WARL_MASK_SIP_SIE; - sie_only | mie_only - } - CSRegister::fcsr => value & CSRegister::FCSR_MASK, - CSRegister::frm => { - let fcsr = self.registers.fcsr.read(); - let fcsr = fcsr & !CSRegister::FRM_MASK; - ((value << CSRegister::FRM_SHIFT) & CSRegister::FRM_MASK) | fcsr - } - CSRegister::fflags => { - let fcsr = self.registers.fcsr.read(); - let fcsr = fcsr & !CSRegister::FFLAGS_MASK; - (value & CSRegister::FFLAGS_MASK) | fcsr - } - _ => value, - } - } - - /// Transform a read operation to account for shadow registers. - /// (e.g. `sstatus`, `sie` register) - /// - /// `source_reg_value` holds the value of the register which is the ground truth for `reg` - /// if known, `None` otherwise. - /// - /// e.g.: `mstatus` is read only if `sstatus` is requested and `mstatus` is not known already - /// - /// Sections 3.1.6 & 4.1.1 - #[inline(always)] - fn transform_read(&self, reg: CSRegister, source_reg_value: Option) -> CSRRepr - where - M: backend::ManagerRead, - { - let source_reg_value = source_reg_value.unwrap_or_else(|| { - // If reg is a shadow, obtain the underlying ground truth for that register - self.general_raw_read(reg.into()) - }); - - // modify the value according to the shadowing rules of each register - match reg { - CSRegister::sstatus => MStatus::from_bits(source_reg_value).to_sstatus().to_bits(), - CSRegister::sip => source_reg_value & CSRegister::WARL_MASK_SIP_SIE, - CSRegister::sie => source_reg_value & CSRegister::WARL_MASK_SIP_SIE, - CSRegister::fcsr => source_reg_value & CSRegister::FCSR_MASK, - CSRegister::frm => (source_reg_value & CSRegister::FRM_MASK) >> CSRegister::FRM_SHIFT, - CSRegister::fflags => source_reg_value & CSRegister::FFLAGS_MASK, - _ => source_reg_value, - } - } - - /// Write to a CSR. - #[inline(always)] - pub fn write(&mut self, reg: CSRegister, value: V) - where - M: backend::ManagerReadWrite, - { - if let Some(value) = reg.make_value_writable(value.to_bits()) { - let value = self.transform_write(reg, value); - let source_reg: RootCSRegister = reg.into(); - self.general_raw_write(source_reg, value); - } - } - - /// Read from a CSR. - #[inline(always)] - pub fn read(&self, reg: CSRegister) -> V - where - M: backend::ManagerRead, - { - // sstatus is just a restricted view of mstatus. - // to maintain consistency, when reading sstatus - // just return mstatus with only the sstatus fields, making the other fields 0 - V::from_bits(self.transform_read(reg, None)) - } - - /// Replace the CSR value, returning the previous value. - #[inline(always)] - pub fn replace(&mut self, reg: CSRegister, value: V) -> V - where - M: backend::ManagerReadWrite, - { - if let Some(value) = reg.make_value_writable(value.to_bits()) { - let value = self.transform_write(reg, value); - let source_reg: RootCSRegister = reg.into(); - let old_value = self.general_raw_replace(source_reg, value); - let old_value = self.transform_read(reg, Some(old_value)); - V::from_bits(old_value) - } else { - self.read(reg) - } - } - - /// Set bits in the CSR. - #[inline(always)] - pub fn set_bits(&mut self, reg: CSRegister, bits: CSRRepr) -> CSRValue - where - M: backend::ManagerReadWrite, - { - let old_value: CSRValue = self.read(reg); - let new_value = old_value.repr() | bits; - self.write(reg, new_value); - old_value - } - - /// Clear bits in the CSR. - #[inline(always)] - pub fn clear_bits(&mut self, reg: CSRegister, bits: CSRRepr) -> CSRValue - where - M: backend::ManagerReadWrite, - { - let old_value: CSRValue = self.read(reg); - let new_value = old_value.repr() & !bits; - self.write(reg, new_value); - old_value - } - - /// Get a mask of possible interrupts when in `current_mode`. - pub fn possible_interrupts(&self, current_mode: Mode) -> CSRRepr - where - M: backend::ManagerRead, - { - // 3.1.6.1 Privilege and Global Interrupt-Enable Stack in mstatus register - // "When a hart is executing in privilege mode x, interrupts are globally enabled when - // xIE=1 and globally disabled when xIE=0. - // Interrupts for lower-privilege modes, wx, are always globally enabled - // regardless of the setting of the global yIE - // bit for the higher-privilege mode." - match current_mode { - Mode::User => Interrupt::SUPERVISOR_BIT_MASK | Interrupt::MACHINE_BIT_MASK, - Mode::Supervisor => { - let ie_supervisor = match self.mstatus().sie.read() { - true => self.read(CSRegister::sie), - false => 0, - }; - - ie_supervisor | Interrupt::MACHINE_BIT_MASK - } - Mode::Machine => match self.mstatus().mie.read() { - true => self.read(CSRegister::mie), - false => 0, - }, - } - } - - /// Determine the mode where this trap would go to. - pub fn get_trap_mode(&self, trap_source: &TC) -> TrapMode - where - M: backend::ManagerRead, - { - // Section 3.1.8: Machine Trap Delegation Registers (medeleg and mideleg) - // - // "By default, all traps at any privilege level are handled in machine mode" - // "To increase performance, implementations can provide individual read/write bits within - // medeleg and mideleg to indicate that certain exceptions and interrupts should be - // processed directly by a lower privilege level." - // - // "medeleg has a bit position allocated for every synchronous exception - // shown in Table 3.6, with the index of the bit position equal to the value - // returned in the mcause register (i.e., setting bit 8 allows user-mode environment calls - // to be delegated to a lower-privilege trap handler)." - // - // Traps never transition from a more-privileged mode to a - // less-privileged mode. For example, if M-mode has delegated illegal instruction - // exceptions to S-mode, and M-mode software later executes - // an illegal instruction, the trap is taken in M-mode, - // rather than being delegated to S-mode. - - // Section 3.1.9: An interrupt i will trap to M-mode - // (causing the privilege mode to change to M-mode) - // if all of the following are true: - // (a) either the current privilege mode is M and the MIE bit in the mstatus - // register is set, or the current privilege mode has less privilege than M-mode; - // (b) bit i is set in both mip and mie; and - // (c) if register mideleg exists, bit i is not set in mideleg. - - // An interrupt i will trap to S-mode if both of the following are true: - // (a) either the current privilege mode is S and - // the SIE bit in the sstatus register is set, - // or the current privilege mode has less privilege than S-mode; and - // (b) bit i is set in both sip and sie. - - // The (b) check that the trap can be taken by looking at mip&mie / sip&sie - // is already done by get_pending_interrupt() - // only checking if delegation takes place is left. - - let deleg = match TC::kind() { - TrapKind::Interrupt => CSRegister::mideleg, - TrapKind::Exception => CSRegister::medeleg, - }; - let deleg_val: CSRRepr = self.read(deleg); - - match u64::bit(deleg_val, trap_source.exception_code() as usize) { - true => TrapMode::Supervisor, - false => TrapMode::Machine, - } - } - - /// Retrieve the address of the trap handler. - pub fn get_trap_handler( - &self, - trap_source: &TC, - trap_mode: TrapMode, - ) -> Address - where - M: backend::ManagerRead, - { - let xtvec = self.read(match trap_mode { - TrapMode::Supervisor => CSRegister::stvec, - TrapMode::Machine => CSRegister::mtvec, - }); - trap_source.trap_handler_address(xtvec) - } -} - -/// Layout for [CSRegisters] -pub type CSRegistersLayout = CSRValuesLayout; - -impl CSRegisters { - /// Bind the CSR state to the allocated space. - pub fn bind(space: backend::AllocatedOf) -> Self { - Self { - registers: values::CSRValues::bind(space), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf { - self.registers.struct_ref::() - } - - /// Reset the control and state registers. - pub fn reset(&mut self) - where - M: backend::ManagerWrite, - { - // Try to reset known CSRs to known default values. - for reg in CSRegister::iter() { - self.general_raw_write(reg.into(), reg.default_value()); - } - } - - /// Check whether floating point extension is disabled. - pub fn floating_disabled(&self) -> bool - where - M: backend::ManagerRead, - { - let fs = self.mstatus().fs.read(); - fs == ExtensionValue::Off - } -} - -impl NewState for CSRegisters { - fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - Self { - registers: CSRValues::new(manager), - } - } -} - -impl Clone for CSRegisters { - fn clone(&self) -> Self { - Self { - registers: self.registers.clone(), - } - } -} - -#[cfg(test)] -#[expect( - clippy::identity_op, - reason = "Used for some bit-wise operations to make bit patterns more legible" -)] -mod tests { - use strum::IntoEnumIterator; - - use crate::backend_test; - use crate::bits::Bits64; - use crate::machine_state::csregisters::CSRRepr; - use crate::machine_state::csregisters::CSRegister; - use crate::machine_state::csregisters::CSRegisters; - use crate::machine_state::csregisters::values::CSRValue; - use crate::machine_state::csregisters::xstatus::MStatus; - use crate::state::NewState; - use crate::traps::Interrupt; - use crate::traps::TrapContext; - - #[test] - fn test_read_write_access() { - use crate::machine_state::csregisters::CSRegister as csreg; - use crate::machine_state::csregisters::Exception; - use crate::machine_state::csregisters::check_write as check; - - let is_illegal_instr = |e| -> bool { e == Exception::IllegalInstruction }; - - // Machine registers - assert!(check(csreg::mcause).is_ok()); - assert!(check(csreg::mhartid).is_err_and(is_illegal_instr)); - - // Supervisor registers - assert!(check(csreg::stvec).is_ok()); - - // Hypervisor registers - assert!(check(csreg::henvcfg).is_ok()); - assert!(check(csreg::hgeip).is_err_and(is_illegal_instr)); - - // User registers - assert!(check(csreg::fcsr).is_ok()); - assert!(check(csreg::instret).is_err_and(is_illegal_instr)); - assert!(check(csreg::cycle).is_err_and(is_illegal_instr)); - } - - #[test] - fn test_wpri() { - use crate::machine_state::csregisters::CSRegister as csreg; - - let check = |reg: csreg, value| reg.clear_wpri_fields(value); - - // Machine registers - assert!(check(csreg::menvcfg, 0) == 0); - assert!(check(csreg::mstatus, 0xFFFF_FFFF_FFFF_FFFF) == 0x8000_003F_007F_FFEA); - - // Supervisor registers - assert!(check(csreg::senvcfg, 0b1010_0101_1010_0101) == 0b0000_0000_1010_0001); - assert!( - check(csreg::sstatus, 0b1100_0011_0101_1010_0110_1001) - == 0b0000_0001_0100_0010_0110_0000 - ); - } - - #[test] - fn test_wlrl() { - use crate::machine_state::csregisters::CSRegister as csreg; - - // Additionally check if value remains legal after using `make_value_writable` - let check = - |reg: csreg, value| reg.is_legal(value) && reg.make_value_writable(value).is_some(); - - // Registers that are not xcause should always be ok - assert!(check(csreg::mstatus, 0xFFFF_FFFF_FFFF_FFFF)); - assert!(check(csreg::sstatus, 0x0)); - assert!(check(csreg::time, 0x0)); - - // scause & mcause tests - assert!(check(csreg::mcause, 0x8000_0000_0000_0003)); - assert!(!check(csreg::mcause, 0x8000_0000_0000_0008)); - assert!(check(csreg::mcause, 0x8000_0000_0000_000B)); - assert!(check(csreg::mcause, 0x0002)); - assert!(check(csreg::mcause, 0x000F)); - assert!(!check(csreg::mcause, 0x000A)); - assert!(!check(csreg::mcause, 0x0000_FFF0_00F0_0002)); - - assert!(check(csreg::scause, 0x0000)); - assert!(check(csreg::scause, 0x8000_0000_0000_0001)); - assert!(!check(csreg::scause, 0x8000_F0F0_0000_0003)); - assert!(!check(csreg::scause, 0x8000_0000_0000_000B)); - assert!(!check(csreg::scause, 0x0000_0F00_0000_F0F0)); - assert!(!check(csreg::scause, 0x000A)); - } - - #[test] - fn test_writable_warl() { - use crate::machine_state::csregisters::CSRegister as csreg; - - let check_wrapped = |reg: csreg, value| reg.make_value_writable(value); - let check = |reg: csreg, value| reg.make_value_writable(value).unwrap(); - - // misa field - assert_eq!( - check(csreg::misa, 0xFFFF_FFFF_FFFF_FFFF), - 0x8000_0000_0014_112D - ); - assert_eq!(check(csreg::misa, 0x0), 0x8000_0000_0014_112D); - - // medeleg / mideleg - assert!(check(csreg::medeleg, 0x0) == 0x0); - assert!(check(csreg::medeleg, 0x0000_FFFF_0000_FFFF) == 0x0000_0000_0000_B3FF); - assert!(check(csreg::mideleg, 0x0) == 0x0); - assert!(check(csreg::mideleg, 0xFFFF_0000_FFFF_FFFF) == 0x0000_0000_0000_0AAA); - - // mtvec / stvec field - assert!(check(csreg::mtvec, 0x0) == 0x0); - assert!(check(csreg::mtvec, 0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFD); - assert!(check(csreg::stvec, 0x0) == 0x0); - assert!(check(csreg::stvec, 0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFD); - - // mip / mie - assert!(check(csreg::mip, 0x0) == 0x0); - assert!(check(csreg::mip, 0xFFFF_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0AAA); - assert!(check(csreg::mie, 0x0) == 0x0); - assert!(check(csreg::mie, 0xFFFF_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0AAA); - - // sip / sie - assert!(check(csreg::sip, 0x0) == 0x0); - assert!(check(csreg::sip, 0xFFFF_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0222); - assert!(check(csreg::sie, 0x0) == 0x0); - assert!(check(csreg::sie, 0xFFFF_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0222); - - // mepc / sepc / mnepc - assert!(check(csreg::mepc, 0x0) == 0x0); - assert!(check(csreg::mepc, 0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFE); - assert!(check(csreg::sepc, 0x0) == 0x0); - assert!(check(csreg::sepc, 0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFE); - assert!(check(csreg::mnepc, 0x0) == 0x0); - assert!(check(csreg::mnepc, 0xFFFF_FFFF_FFFF_FFFF) == 0xFFFF_FFFF_FFFF_FFFE); - - // satp - assert_eq!(check_wrapped(csreg::satp, 0x0), Some(0x0)); - assert_eq!(check_wrapped(csreg::satp, 0x0000_FFFF_0000_FFFF), Some(0x0)); - // Invalid mode is read as 0 - assert_eq!(check_wrapped(csreg::satp, 0x4200_FFFF_FFFF_FFFF), Some(0x0)); - assert_eq!( - check_wrapped(csreg::satp, 0x90F0_0000_FFFF_0000), - Some(0x90F0_0000_FFFF_0000) - ); - - // mstatus - // uxl & sxl fields are set - assert_eq!(check(csreg::mstatus, 0x0), 0x0000_000A_0000_0000); - // besides uxl & sxl changing, wpri fields get set to 0 - assert_eq!( - check(csreg::mstatus, !0u64), - 0b1000000000000000000000000011101000000000011111111111100111101010u64 - ); - // check SD bit set from XS=00, FS=10, VS=00, and MPP gets changed to 0b00 from 0b01, - // and FS gets changed to 11 (dirty) - assert_eq!( - check( - csreg::mstatus, - 0b111111111111000000000000000000000000000000000101011111111111u64 - ), - 0b1000000000000000000000000000101000000000000000000110000111101010u64 - ); - - // sstatus - // uxl & sxl fields are set - assert_eq!(check(csreg::sstatus, 0x0), 0x0000_0002_0000_0000); - // besides uxl changing, wpri fields get set to 0 - assert_eq!( - check(csreg::sstatus, !0u64), - 0b1000000000000000000000000000001000000000000011011110000101100010u64, - ); - // check sd bit set from XS=00, FS=10, VS=11 - assert_eq!( - check( - csreg::sstatus, - 0b111111111111000000000000000000000000000000000101011111111111u64 - ), - 0b1000000000000000000000000000001000000000000000000110000101100010u64, - ); - - // mnstatus - // check NMIE bit is set - assert_eq!(check(csreg::mnstatus, 0x0), 0x0000_0000_0000_0008); - // check WPRI fields & MNPV read-only 0 - assert_eq!( - check(csreg::mnstatus, 0xFFFF_FFFF_FFFF_FFFF), - 0x0000_0000_0000_1808 - ); - - // non warl register - assert!(check(csreg::instret, 0x42) == 0x42); - } - - /// Ensure that parsing CSRs matches the values assigned in the enum. - #[test] - fn test_csr_parser_roundtrip() { - for csr in CSRegister::iter() { - let value = csr as usize; - let result = CSRegister::try_parse(value as u32); - - assert_eq!(Some(csr), result, "Expected {csr}, got {result:?}"); - } - } - - backend_test!(test_write_read, F, { - let mut csrs = CSRegisters::new(&mut F::manager()); - - // write to MBE, SXL, UXL, MPP, MPIE, XS, SPP (through mstatus) - csrs.write( - CSRegister::mstatus, - (1u64 << 37) - | (0b01 << 34) - | (0b11 << 32) - | (0b11 << 15) - | (0b11 << 11) - | (1 << 8) - | (1 << 7), - ); - // SXL, UXL should be set to MXL (WARL), SD bit should be 1 - let read_mstatus: MStatus = csrs.read(CSRegister::mstatus); - assert_eq!( - read_mstatus.to_bits(), - (1u64 << 63) - | (1 << 37) - | (0b10 << 34) - | (0b10 << 32) - | (0b11 << 15) - | (0b11 << 11) - | (1 << 8) - | (1 << 7) - ); - // SXL should be 0 (WPRI), MBE, MPP, MPIE should be 0 (WPRI for sstatus), SD bit also 1 - let read_sstatus: CSRValue = csrs.read(CSRegister::sstatus); - assert_eq!( - read_sstatus.repr(), - (1u64 << 63) | (0b10 << 32) | (0b11 << 15) | (1 << 8) - ); - - // write to MBE, SXL, UXL, MPP, MPIE, VS, SPP, (through sstatus, M-fields should be ignored, being WPRI) - csrs.write( - CSRegister::sstatus, - (0u64 << 37) - | (0b11 << 34) - | (0b01 << 32) - | (0b11 << 11) - | (0 << 9) - | (0 << 7) - | (1 << 8), - ); - // setting VS to 0, SD bit becomes 0. Otherwise, only UXL and SPP fields are non-zero. - let second_read_sstatus: CSRValue = csrs.read(CSRegister::sstatus); - assert_eq!(second_read_sstatus.repr(), (0b10u64 << 32) | (1 << 8)); - // MBE remained 1, SXL, UXL are constant, MPP remained 0b11, VS is 0 due to the sstatus change, SPP and MPIE remained 1, - let read_mstatus: CSRValue = csrs.read(CSRegister::mstatus); - assert_eq!( - read_mstatus.repr(), - (1u64 << 37) - | (0b10 << 34) - | (0b10 << 32) - | (0b11 << 11) - | (0 << 9) - | (1 << 8) - | (1 << 7) - ); - - assert_eq!( - csrs.read::(CSRegister::sstatus), - (0b10 << 32) | (1 << 8) - ); - assert_eq!( - csrs.read::(CSRegister::mstatus), - (1u64 << 37) - | (0b10 << 34) - | (0b10 << 32) - | (0b11 << 11) - | (0 << 9) - | (1 << 8) - | (1 << 7) - ); - - // write to MBE, SXL, UXL, MPP, VS, SPP, MPIE (through sstatus) - let old_sstatus: CSRValue = csrs.replace( - CSRegister::sstatus, - ((1u64 << 37) - | (0b01 << 34) - | (0b11 << 32) - | (0b11 << 15) - | (0b11 << 11) - | (0 << 8) - | (0 << 7)) - .into(), - ); - assert_eq!(old_sstatus, second_read_sstatus); - // SXL, UXL should be set to MXL (WARL), SD bit should be 1 - let read_mstatus: CSRValue = csrs.read(CSRegister::mstatus); - assert_eq!( - read_mstatus.repr(), - (1u64 << 63) - | (1 << 37) - | (0b10 << 34) - | (0b10 << 32) - | (0b11 << 15) - | (0b11 << 11) - | (0 << 8) - | (1 << 7) - ); - // SXL should be 0 (WPRI), MBE, MPP, MPIE should be 0 (WPRI for sstatus), SD bit also 1 - let read_sstatus: CSRValue = csrs.read(CSRegister::sstatus); - assert_eq!( - read_sstatus.repr(), - (1u64 << 63) | (0b10 << 32) | (0b11 << 15) | (0 << 8) - ); - }); - - backend_test!(test_xip_xie, F, { - let mut csrs = CSRegisters::new(&mut F::manager()); - - let mtip: u64 = 1 << Interrupt::MachineTimer.exception_code(); - let msip: u64 = 1 << Interrupt::MachineSoftware.exception_code(); - let seip: u64 = 1 << Interrupt::SupervisorExternal.exception_code(); - let stip: u64 = 1 << Interrupt::SupervisorTimer.exception_code(); - - // Writing to MIP does nothing. - csrs.write(CSRegister::mip, mtip | seip); - assert_eq!(csrs.read::(CSRegister::mip), 0); - assert_eq!(csrs.read::(CSRegister::sip), 0); - - // MSIP bit should not be written - csrs.write(CSRegister::sie, stip | seip | msip); - assert_eq!(csrs.read::(CSRegister::mie), stip | seip); - assert_eq!(csrs.read::(CSRegister::sie), stip | seip); - }); - - backend_test!(test_fcsr, F, { - let mut csrs = CSRegisters::new(&mut F::manager()); - - // check starting values - assert_eq!(0, csrs.read::(CSRegister::fcsr)); - assert_eq!(0, csrs.read::(CSRegister::frm)); - assert_eq!(0, csrs.read::(CSRegister::fflags)); - - // writing to fcsr is reflected in frm/fflags - csrs.write(CSRegister::fcsr, u64::MAX); - - assert_eq!(0xff, csrs.read::(CSRegister::fcsr)); - assert_eq!(0b111, csrs.read::(CSRegister::frm)); - assert_eq!(0b11111, csrs.read::(CSRegister::fflags)); - - // writing to frm is reflected in fcsr - csrs.write(CSRegister::frm, 0b010); - - assert_eq!(0b01011111, csrs.read::(CSRegister::fcsr)); - assert_eq!(0b010, csrs.read::(CSRegister::frm)); - assert_eq!(0b11111, csrs.read::(CSRegister::fflags)); - - // writing to fflags is reflected in fcsr - csrs.write(CSRegister::fflags, 0b01010); - - assert_eq!(0b01001010, csrs.read::(CSRegister::fcsr)); - assert_eq!(0b010, csrs.read::(CSRegister::frm)); - assert_eq!(0b01010, csrs.read::(CSRegister::fflags)); - }); -} diff --git a/src/riscv/lib/src/machine_state/csregisters/bits.rs b/src/riscv/lib/src/machine_state/csregisters/bits.rs deleted file mode 100644 index 16b081c6a4f82aa6f45144d8ef59c56654c48b08..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/bits.rs +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -/// The `csr!` macro generates a type describing a CSR, its value, -/// associated traits and methods. -#[macro_export] -macro_rules! csr { - { - pub struct $group:ident { - $( $name:ident: $type:ty ),+ - $( , )? - } - } => { - #[derive(Clone, Copy, PartialEq, Eq)] - pub struct $group(u64); - $crate::csr_bits!($group;; $( $name: $type ),+); - $crate::csr_debug!($group;; $( $name ),+); - $crate::csr_new!($group;; $( $name: $type ),+); - $crate::csr_fields!(0;; $group;; $( $name: $type ),+); - }; - - { - struct $group:ident { - $( $name:ident: $type:ty ),+ - } - } => { - #[derive(Clone, Copy, PartialEq, Eq)] - struct $group(u64); - $crate::csr_bits!($group;; $( $name: $type ),+); - $crate::csr_debug!($group;; $( $name ),+); - $crate::csr_new!($group;; $( $name: $type ),+); - $crate::csr_fields!(0;; $group;; $( $name: $type ),+); - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! csr_bits { - ( $group:ident;; $( $name:ident: $type:ty ),+ ) => { - impl $crate::bits::Bits64 for $group { - const WIDTH: usize = {{ $crate::csr_width!($($type),+) }}; - - #[inline(always)] - fn from_bits(value: u64) -> Self { - let mut new_self = Self(0); - let fake_self = Self(value); - - paste::paste! { - $( - new_self = new_self.[]( - fake_self.[<$name:lower>]() - ); - )+ - } - - new_self - } - - #[inline(always)] - fn to_bits(&self) -> u64 { - self.0 - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! csr_width { - ( $type:ty ) => { - <$type>::WIDTH - }; - - ( $type0:ty, $( $type1:ty ),+ ) => { - $crate::csr_width!($type0) + $crate::csr_width!($($type1),+) - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! csr_fields { - ( $accum:expr;; $group:ident;; $name:ident: $type:ty ) => { - paste::paste! { - #[allow(clippy::allow_attributes, reason = "Macro may trigger warnings conditionally in certain contexts")] - #[allow(dead_code, reason = "Macro may generate unused code")] - impl $group { - pub const [<$name:upper _OFFSET>]: usize = { $accum }; - - #[inline(always)] - pub fn [<$name:lower>](self) -> $type { - let offset = <$type as $crate::bits::Bits64>::WIDTH.saturating_sub(1); - let bits = $crate::bits::u64::bits_subset(self.0, ($accum + offset), ($accum)); - <$type as $crate::bits::Bits64>::from_bits(bits) - } - - #[inline(always)] - pub fn [](self, value: $type) -> Self { - let offset = <$type as $crate::bits::Bits64>::WIDTH.saturating_sub(1); - let new_self = $crate::bits::u64::replace_subset(self.0, ($accum + offset), ($accum), $crate::bits::Bits64::to_bits(&value)); - $group(new_self) - } - - #[inline(always)] - pub fn [](&mut self, value: $type) -> &mut Self { - let offset = <$type as $crate::bits::Bits64>::WIDTH.saturating_sub(1); - self.0 = $crate::bits::u64::replace_subset(self.0, ($accum + offset), ($accum), $crate::bits::Bits64::to_bits(&value)); - self - } - } - } - }; - - ( $accum:expr;; $group:ident;; $name:ident: $type:ty, $( $names:ident: $types:ty ),+ ) => { - $crate::csr_fields!($accum;; $group;; $name: $type); - $crate::csr_fields!($accum + <$type as $crate::bits::Bits64>::WIDTH;; $group;; $( $names: $types ),+); - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! csr_debug { - ( $group:ident;; $( $name:ident ),*) => { - impl std::fmt::Debug for $group { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut str = f.debug_struct(stringify!($group)); - - paste::paste! { - $( - str.field( - stringify!($name), - &self.[<$name:lower>](), - ); - )* - } - - str.finish() - } - } - }; -} - -#[macro_export] -#[doc(hidden)] -macro_rules! csr_new { - ( $group:ident;; $( $name:ident: $type:ty ),* ) => { - #[allow(clippy::allow_attributes, reason = "Macro may trigger warnings conditionally in certain contexts")] - #[allow(clippy::too_many_arguments, reason = "Macro may generate too many arguments")] - #[allow(non_snake_case, reason = "Macro may generate non snake case names")] - impl $group { - pub fn new( - $( - $name: $type, - )* - ) -> Self { - let mut myself = ::from_bits(0u64); - - paste::paste! { - $( - myself = myself.[]($name); - )* - } - - myself - } - } - }; -} - -/// Normalise the fields for a Control or State register using WARL/WPRI -pub trait NormaliseFields { - fn normalise(self) -> Self; -} - -#[cfg(test)] -mod tests { - use crate::bits::Bits64; - use crate::bits::ConstantBits; - - csr! { - pub struct Test { - A: bool, - reserved: ConstantBits<1, 1>, - C: bool - } - } - - #[test] - fn reserved_works() { - let test = Test::from_bits(0u64); - assert_eq!(test.to_bits(), 0b010); - - let test = Test::new(true, ConstantBits, false); - assert_eq!(test.to_bits(), 0b011); - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/effects.rs b/src/riscv/lib/src/machine_state/csregisters/effects.rs deleted file mode 100644 index 70f22ad755d59e81f7cafffae87b53a62c08ccd8..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/effects.rs +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::CSRegisters; -use crate::state_backend::ManagerBase; - -/// Type representing CSR side effects. (on read/write/replace) -/// Effects to be handled by function [`handle_csr_effect`]. -#[derive(PartialEq, Debug, Copy, Clone)] -pub enum CSREffect { - Xie, - Xip, -} - -macro_rules! create_effect_getter { - ($struct_name:ident, $effect:expr) => { - pub struct $struct_name; - - impl $crate::state_backend::EffectGetter for $struct_name { - type Effect = CSREffect; - - const EFFECT: Option = $effect; - } - }; -} - -create_effect_getter!(NoEffect, None); -create_effect_getter!(XieEffect, Some(CSREffect::Xie)); -create_effect_getter!(XipEffect, Some(CSREffect::Xip)); - -#[inline(always)] -pub fn handle_csr_effect(_state: &mut CSRegisters, effect: Option) { - if let Some(effect) = effect { - match effect { - CSREffect::Xie | CSREffect::Xip => {} - } - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/root.rs b/src/riscv/lib/src/machine_state/csregisters/root.rs deleted file mode 100644 index f44c21c2bfb2364cb693f226b62e43c8a8b66d2d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/root.rs +++ /dev/null @@ -1,554 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::CSRegister; - -/// [`RootCSRegister`] is the set of unshadowed CSRs, whereas -/// the simple enum [`CSRegister`] is the public-facing API which contains shadowed CSRs. -/// -/// For example, sstatus exists as a [`CSRegister`] but the value is actually derived from -/// mstatus, which is also a [`RootCSRegister`]. Similarly for sip/mip, sie/mie, fflags/frm/fcsr. -#[expect( - non_camel_case_types, - reason = "We want to use the register names from the RISC-V specification" -)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, strum::EnumIter, strum::Display)] -pub(super) enum RootCSRegister { - // Unprivileged Floating-Point CSRs - fcsr, - - // Unprivileged Counter/Timers - cycle, - time, - instret, - hpmcounter3, - hpmcounter4, - hpmcounter5, - hpmcounter6, - hpmcounter7, - hpmcounter8, - hpmcounter9, - hpmcounter10, - hpmcounter11, - hpmcounter12, - hpmcounter13, - hpmcounter14, - hpmcounter15, - hpmcounter16, - hpmcounter17, - hpmcounter18, - hpmcounter19, - hpmcounter20, - hpmcounter21, - hpmcounter22, - hpmcounter23, - hpmcounter24, - hpmcounter25, - hpmcounter26, - hpmcounter27, - hpmcounter28, - hpmcounter29, - hpmcounter30, - hpmcounter31, - - // Supervisor Trap Setup - stvec, - scounteren, - - // Supervisor Configuration - senvcfg, - - // Supervisor Trap Handling - sscratch, - sepc, - scause, - stval, - // sip, Shadow of mip - - // Supervisor Protection and Translation - satp, - - // Supervisor Debug/Trace Registers - scontext, - - // Hypervisor Trap Setup - hstatus, - hedeleg, - hideleg, - hie, - hcounteren, - hgeie, - - // Hypervisor Trap Handling - htval, - hip, - hvip, - htinst, - hgeip, - - // Hypervisor Configuration - henvcfg, - - // Hypervisor Protection and Translation - hgatp, - - // Hypervisor Debug/ Trace Registers - hcontext, - - // Hypervisor Counter/Timer Virtualization Registers - htimedelta, - - // Virtual Supervisor Registers - vsstatus, - vsie, - vstvec, - vsscratch, - vsepc, - vscause, - vstval, - vsip, - vsatp, - - // Machine Information Registers - mvendorid, - marchid, - mimpid, - mhartid, - mconfigptr, - - // Machine Trap Setup - mstatus, - misa, - medeleg, - mideleg, - mie, - mtvec, - mcounteren, - - // Machine Trap Handling - mscratch, - mepc, - mcause, - mtval, - mip, - mtinst, - mtval2, - - // Machine Configuration - menvcfg, - mseccfg, - - // Machine Memory Protection - pmpcfg0, - pmpcfg2, - pmpcfg4, - pmpcfg6, - pmpcfg8, - pmpcfg10, - pmpcfg12, - pmpcfg14, - pmpaddr0, - pmpaddr1, - pmpaddr2, - pmpaddr3, - pmpaddr4, - pmpaddr5, - pmpaddr6, - pmpaddr7, - pmpaddr8, - pmpaddr9, - pmpaddr10, - pmpaddr11, - pmpaddr12, - pmpaddr13, - pmpaddr14, - pmpaddr15, - pmpaddr16, - pmpaddr17, - pmpaddr18, - pmpaddr19, - pmpaddr20, - pmpaddr21, - pmpaddr22, - pmpaddr23, - pmpaddr24, - pmpaddr25, - pmpaddr26, - pmpaddr27, - pmpaddr28, - pmpaddr29, - pmpaddr30, - pmpaddr31, - pmpaddr32, - pmpaddr33, - pmpaddr34, - pmpaddr35, - pmpaddr36, - pmpaddr37, - pmpaddr38, - pmpaddr39, - pmpaddr40, - pmpaddr41, - pmpaddr42, - pmpaddr43, - pmpaddr44, - pmpaddr45, - pmpaddr46, - pmpaddr47, - pmpaddr48, - pmpaddr49, - pmpaddr50, - pmpaddr51, - pmpaddr52, - pmpaddr53, - pmpaddr54, - pmpaddr55, - pmpaddr56, - pmpaddr57, - pmpaddr58, - pmpaddr59, - pmpaddr60, - pmpaddr61, - pmpaddr62, - pmpaddr63, - - // Machine Non-Maskable Interrupt Handling - // The draft `Smrnmi` extension is not supported in objdump, printing - // CSR address directly instead - mnscratch, - mnepc, - mncause, - mnstatus, - - // Machine Counter/Timers - mcycle, - minstret, - mhpmcounter3, - mhpmcounter4, - mhpmcounter5, - mhpmcounter6, - mhpmcounter7, - mhpmcounter8, - mhpmcounter9, - mhpmcounter10, - mhpmcounter11, - mhpmcounter12, - mhpmcounter13, - mhpmcounter14, - mhpmcounter15, - mhpmcounter16, - mhpmcounter17, - mhpmcounter18, - mhpmcounter19, - mhpmcounter20, - mhpmcounter21, - mhpmcounter22, - mhpmcounter23, - mhpmcounter24, - mhpmcounter25, - mhpmcounter26, - mhpmcounter27, - mhpmcounter28, - mhpmcounter29, - mhpmcounter30, - mhpmcounter31, - - // Machine Counter Setup - mcountinhibit, - mhpmevent3, - mhpmevent4, - mhpmevent5, - mhpmevent6, - mhpmevent7, - mhpmevent8, - mhpmevent9, - mhpmevent10, - mhpmevent11, - mhpmevent12, - mhpmevent13, - mhpmevent14, - mhpmevent15, - mhpmevent16, - mhpmevent17, - mhpmevent18, - mhpmevent19, - mhpmevent20, - mhpmevent21, - mhpmevent22, - mhpmevent23, - mhpmevent24, - mhpmevent25, - mhpmevent26, - mhpmevent27, - mhpmevent28, - mhpmevent29, - mhpmevent30, - mhpmevent31, - - // Debug/Trace Registers (shared with Debug Mode) - tselect, - tdata1, - tdata2, - tdata3, - tcontrol, - mcontext, - - // Debug Mode Registers - dcsr, - dpc, - dscratch0, - dscratch1, -} - -impl From for RootCSRegister { - #[inline(always)] - fn from(value: CSRegister) -> Self { - match value { - // fflags is a shadow of fcsr - CSRegister::fflags => RootCSRegister::fcsr, - // frm is a shadow of fcsr - CSRegister::frm => RootCSRegister::fcsr, - CSRegister::fcsr => RootCSRegister::fcsr, - CSRegister::cycle => RootCSRegister::cycle, - CSRegister::time => RootCSRegister::time, - CSRegister::instret => RootCSRegister::instret, - CSRegister::hpmcounter3 => RootCSRegister::hpmcounter3, - CSRegister::hpmcounter4 => RootCSRegister::hpmcounter4, - CSRegister::hpmcounter5 => RootCSRegister::hpmcounter5, - CSRegister::hpmcounter6 => RootCSRegister::hpmcounter6, - CSRegister::hpmcounter7 => RootCSRegister::hpmcounter7, - CSRegister::hpmcounter8 => RootCSRegister::hpmcounter8, - CSRegister::hpmcounter9 => RootCSRegister::hpmcounter9, - CSRegister::hpmcounter10 => RootCSRegister::hpmcounter10, - CSRegister::hpmcounter11 => RootCSRegister::hpmcounter11, - CSRegister::hpmcounter12 => RootCSRegister::hpmcounter12, - CSRegister::hpmcounter13 => RootCSRegister::hpmcounter13, - CSRegister::hpmcounter14 => RootCSRegister::hpmcounter14, - CSRegister::hpmcounter15 => RootCSRegister::hpmcounter15, - CSRegister::hpmcounter16 => RootCSRegister::hpmcounter16, - CSRegister::hpmcounter17 => RootCSRegister::hpmcounter17, - CSRegister::hpmcounter18 => RootCSRegister::hpmcounter18, - CSRegister::hpmcounter19 => RootCSRegister::hpmcounter19, - CSRegister::hpmcounter20 => RootCSRegister::hpmcounter20, - CSRegister::hpmcounter21 => RootCSRegister::hpmcounter21, - CSRegister::hpmcounter22 => RootCSRegister::hpmcounter22, - CSRegister::hpmcounter23 => RootCSRegister::hpmcounter23, - CSRegister::hpmcounter24 => RootCSRegister::hpmcounter24, - CSRegister::hpmcounter25 => RootCSRegister::hpmcounter25, - CSRegister::hpmcounter26 => RootCSRegister::hpmcounter26, - CSRegister::hpmcounter27 => RootCSRegister::hpmcounter27, - CSRegister::hpmcounter28 => RootCSRegister::hpmcounter28, - CSRegister::hpmcounter29 => RootCSRegister::hpmcounter29, - CSRegister::hpmcounter30 => RootCSRegister::hpmcounter30, - CSRegister::hpmcounter31 => RootCSRegister::hpmcounter31, - // sstatus is a shadow of mstatus - CSRegister::sstatus => RootCSRegister::mstatus, - // sie is a shadow of mie - CSRegister::sie => RootCSRegister::mie, - CSRegister::stvec => RootCSRegister::stvec, - CSRegister::scounteren => RootCSRegister::scounteren, - CSRegister::senvcfg => RootCSRegister::senvcfg, - CSRegister::sscratch => RootCSRegister::sscratch, - CSRegister::sepc => RootCSRegister::sepc, - CSRegister::scause => RootCSRegister::scause, - CSRegister::stval => RootCSRegister::stval, - // sip is a shadow of mip - CSRegister::sip => RootCSRegister::mip, - CSRegister::satp => RootCSRegister::satp, - CSRegister::scontext => RootCSRegister::scontext, - CSRegister::hstatus => RootCSRegister::hstatus, - CSRegister::hedeleg => RootCSRegister::hedeleg, - CSRegister::hideleg => RootCSRegister::hideleg, - CSRegister::hie => RootCSRegister::hie, - CSRegister::hcounteren => RootCSRegister::hcounteren, - CSRegister::hgeie => RootCSRegister::hgeie, - CSRegister::htval => RootCSRegister::htval, - CSRegister::hip => RootCSRegister::hip, - CSRegister::hvip => RootCSRegister::hvip, - CSRegister::htinst => RootCSRegister::htinst, - CSRegister::hgeip => RootCSRegister::hgeip, - CSRegister::henvcfg => RootCSRegister::henvcfg, - CSRegister::hgatp => RootCSRegister::hgatp, - CSRegister::hcontext => RootCSRegister::hcontext, - CSRegister::htimedelta => RootCSRegister::htimedelta, - CSRegister::vsstatus => RootCSRegister::vsstatus, - CSRegister::vsie => RootCSRegister::vsie, - CSRegister::vstvec => RootCSRegister::vstvec, - CSRegister::vsscratch => RootCSRegister::vsscratch, - CSRegister::vsepc => RootCSRegister::vsepc, - CSRegister::vscause => RootCSRegister::vscause, - CSRegister::vstval => RootCSRegister::vstval, - CSRegister::vsip => RootCSRegister::vsip, - CSRegister::vsatp => RootCSRegister::vsatp, - CSRegister::mvendorid => RootCSRegister::mvendorid, - CSRegister::marchid => RootCSRegister::marchid, - CSRegister::mimpid => RootCSRegister::mimpid, - CSRegister::mhartid => RootCSRegister::mhartid, - CSRegister::mconfigptr => RootCSRegister::mconfigptr, - CSRegister::mstatus => RootCSRegister::mstatus, - CSRegister::misa => RootCSRegister::misa, - CSRegister::medeleg => RootCSRegister::medeleg, - CSRegister::mideleg => RootCSRegister::mideleg, - CSRegister::mie => RootCSRegister::mie, - CSRegister::mtvec => RootCSRegister::mtvec, - CSRegister::mcounteren => RootCSRegister::mcounteren, - CSRegister::mscratch => RootCSRegister::mscratch, - CSRegister::mepc => RootCSRegister::mepc, - CSRegister::mcause => RootCSRegister::mcause, - CSRegister::mtval => RootCSRegister::mtval, - CSRegister::mip => RootCSRegister::mip, - CSRegister::mtinst => RootCSRegister::mtinst, - CSRegister::mtval2 => RootCSRegister::mtval2, - CSRegister::menvcfg => RootCSRegister::menvcfg, - CSRegister::mseccfg => RootCSRegister::mseccfg, - CSRegister::pmpcfg0 => RootCSRegister::pmpcfg0, - CSRegister::pmpcfg2 => RootCSRegister::pmpcfg2, - CSRegister::pmpcfg4 => RootCSRegister::pmpcfg4, - CSRegister::pmpcfg6 => RootCSRegister::pmpcfg6, - CSRegister::pmpcfg8 => RootCSRegister::pmpcfg8, - CSRegister::pmpcfg10 => RootCSRegister::pmpcfg10, - CSRegister::pmpcfg12 => RootCSRegister::pmpcfg12, - CSRegister::pmpcfg14 => RootCSRegister::pmpcfg14, - CSRegister::pmpaddr0 => RootCSRegister::pmpaddr0, - CSRegister::pmpaddr1 => RootCSRegister::pmpaddr1, - CSRegister::pmpaddr2 => RootCSRegister::pmpaddr2, - CSRegister::pmpaddr3 => RootCSRegister::pmpaddr3, - CSRegister::pmpaddr4 => RootCSRegister::pmpaddr4, - CSRegister::pmpaddr5 => RootCSRegister::pmpaddr5, - CSRegister::pmpaddr6 => RootCSRegister::pmpaddr6, - CSRegister::pmpaddr7 => RootCSRegister::pmpaddr7, - CSRegister::pmpaddr8 => RootCSRegister::pmpaddr8, - CSRegister::pmpaddr9 => RootCSRegister::pmpaddr9, - CSRegister::pmpaddr10 => RootCSRegister::pmpaddr10, - CSRegister::pmpaddr11 => RootCSRegister::pmpaddr11, - CSRegister::pmpaddr12 => RootCSRegister::pmpaddr12, - CSRegister::pmpaddr13 => RootCSRegister::pmpaddr13, - CSRegister::pmpaddr14 => RootCSRegister::pmpaddr14, - CSRegister::pmpaddr15 => RootCSRegister::pmpaddr15, - CSRegister::pmpaddr16 => RootCSRegister::pmpaddr16, - CSRegister::pmpaddr17 => RootCSRegister::pmpaddr17, - CSRegister::pmpaddr18 => RootCSRegister::pmpaddr18, - CSRegister::pmpaddr19 => RootCSRegister::pmpaddr19, - CSRegister::pmpaddr20 => RootCSRegister::pmpaddr20, - CSRegister::pmpaddr21 => RootCSRegister::pmpaddr21, - CSRegister::pmpaddr22 => RootCSRegister::pmpaddr22, - CSRegister::pmpaddr23 => RootCSRegister::pmpaddr23, - CSRegister::pmpaddr24 => RootCSRegister::pmpaddr24, - CSRegister::pmpaddr25 => RootCSRegister::pmpaddr25, - CSRegister::pmpaddr26 => RootCSRegister::pmpaddr26, - CSRegister::pmpaddr27 => RootCSRegister::pmpaddr27, - CSRegister::pmpaddr28 => RootCSRegister::pmpaddr28, - CSRegister::pmpaddr29 => RootCSRegister::pmpaddr29, - CSRegister::pmpaddr30 => RootCSRegister::pmpaddr30, - CSRegister::pmpaddr31 => RootCSRegister::pmpaddr31, - CSRegister::pmpaddr32 => RootCSRegister::pmpaddr32, - CSRegister::pmpaddr33 => RootCSRegister::pmpaddr33, - CSRegister::pmpaddr34 => RootCSRegister::pmpaddr34, - CSRegister::pmpaddr35 => RootCSRegister::pmpaddr35, - CSRegister::pmpaddr36 => RootCSRegister::pmpaddr36, - CSRegister::pmpaddr37 => RootCSRegister::pmpaddr37, - CSRegister::pmpaddr38 => RootCSRegister::pmpaddr38, - CSRegister::pmpaddr39 => RootCSRegister::pmpaddr39, - CSRegister::pmpaddr40 => RootCSRegister::pmpaddr40, - CSRegister::pmpaddr41 => RootCSRegister::pmpaddr41, - CSRegister::pmpaddr42 => RootCSRegister::pmpaddr42, - CSRegister::pmpaddr43 => RootCSRegister::pmpaddr43, - CSRegister::pmpaddr44 => RootCSRegister::pmpaddr44, - CSRegister::pmpaddr45 => RootCSRegister::pmpaddr45, - CSRegister::pmpaddr46 => RootCSRegister::pmpaddr46, - CSRegister::pmpaddr47 => RootCSRegister::pmpaddr47, - CSRegister::pmpaddr48 => RootCSRegister::pmpaddr48, - CSRegister::pmpaddr49 => RootCSRegister::pmpaddr49, - CSRegister::pmpaddr50 => RootCSRegister::pmpaddr50, - CSRegister::pmpaddr51 => RootCSRegister::pmpaddr51, - CSRegister::pmpaddr52 => RootCSRegister::pmpaddr52, - CSRegister::pmpaddr53 => RootCSRegister::pmpaddr53, - CSRegister::pmpaddr54 => RootCSRegister::pmpaddr54, - CSRegister::pmpaddr55 => RootCSRegister::pmpaddr55, - CSRegister::pmpaddr56 => RootCSRegister::pmpaddr56, - CSRegister::pmpaddr57 => RootCSRegister::pmpaddr57, - CSRegister::pmpaddr58 => RootCSRegister::pmpaddr58, - CSRegister::pmpaddr59 => RootCSRegister::pmpaddr59, - CSRegister::pmpaddr60 => RootCSRegister::pmpaddr60, - CSRegister::pmpaddr61 => RootCSRegister::pmpaddr61, - CSRegister::pmpaddr62 => RootCSRegister::pmpaddr62, - CSRegister::pmpaddr63 => RootCSRegister::pmpaddr63, - CSRegister::mnscratch => RootCSRegister::mnscratch, - CSRegister::mnepc => RootCSRegister::mnepc, - CSRegister::mncause => RootCSRegister::mncause, - CSRegister::mnstatus => RootCSRegister::mnstatus, - CSRegister::mcycle => RootCSRegister::mcycle, - CSRegister::minstret => RootCSRegister::minstret, - CSRegister::mhpmcounter3 => RootCSRegister::mhpmcounter3, - CSRegister::mhpmcounter4 => RootCSRegister::mhpmcounter4, - CSRegister::mhpmcounter5 => RootCSRegister::mhpmcounter5, - CSRegister::mhpmcounter6 => RootCSRegister::mhpmcounter6, - CSRegister::mhpmcounter7 => RootCSRegister::mhpmcounter7, - CSRegister::mhpmcounter8 => RootCSRegister::mhpmcounter8, - CSRegister::mhpmcounter9 => RootCSRegister::mhpmcounter9, - CSRegister::mhpmcounter10 => RootCSRegister::mhpmcounter10, - CSRegister::mhpmcounter11 => RootCSRegister::mhpmcounter11, - CSRegister::mhpmcounter12 => RootCSRegister::mhpmcounter12, - CSRegister::mhpmcounter13 => RootCSRegister::mhpmcounter13, - CSRegister::mhpmcounter14 => RootCSRegister::mhpmcounter14, - CSRegister::mhpmcounter15 => RootCSRegister::mhpmcounter15, - CSRegister::mhpmcounter16 => RootCSRegister::mhpmcounter16, - CSRegister::mhpmcounter17 => RootCSRegister::mhpmcounter17, - CSRegister::mhpmcounter18 => RootCSRegister::mhpmcounter18, - CSRegister::mhpmcounter19 => RootCSRegister::mhpmcounter19, - CSRegister::mhpmcounter20 => RootCSRegister::mhpmcounter20, - CSRegister::mhpmcounter21 => RootCSRegister::mhpmcounter21, - CSRegister::mhpmcounter22 => RootCSRegister::mhpmcounter22, - CSRegister::mhpmcounter23 => RootCSRegister::mhpmcounter23, - CSRegister::mhpmcounter24 => RootCSRegister::mhpmcounter24, - CSRegister::mhpmcounter25 => RootCSRegister::mhpmcounter25, - CSRegister::mhpmcounter26 => RootCSRegister::mhpmcounter26, - CSRegister::mhpmcounter27 => RootCSRegister::mhpmcounter27, - CSRegister::mhpmcounter28 => RootCSRegister::mhpmcounter28, - CSRegister::mhpmcounter29 => RootCSRegister::mhpmcounter29, - CSRegister::mhpmcounter30 => RootCSRegister::mhpmcounter30, - CSRegister::mhpmcounter31 => RootCSRegister::mhpmcounter31, - CSRegister::mcountinhibit => RootCSRegister::mcountinhibit, - CSRegister::mhpmevent3 => RootCSRegister::mhpmevent3, - CSRegister::mhpmevent4 => RootCSRegister::mhpmevent4, - CSRegister::mhpmevent5 => RootCSRegister::mhpmevent5, - CSRegister::mhpmevent6 => RootCSRegister::mhpmevent6, - CSRegister::mhpmevent7 => RootCSRegister::mhpmevent7, - CSRegister::mhpmevent8 => RootCSRegister::mhpmevent8, - CSRegister::mhpmevent9 => RootCSRegister::mhpmevent9, - CSRegister::mhpmevent10 => RootCSRegister::mhpmevent10, - CSRegister::mhpmevent11 => RootCSRegister::mhpmevent11, - CSRegister::mhpmevent12 => RootCSRegister::mhpmevent12, - CSRegister::mhpmevent13 => RootCSRegister::mhpmevent13, - CSRegister::mhpmevent14 => RootCSRegister::mhpmevent14, - CSRegister::mhpmevent15 => RootCSRegister::mhpmevent15, - CSRegister::mhpmevent16 => RootCSRegister::mhpmevent16, - CSRegister::mhpmevent17 => RootCSRegister::mhpmevent17, - CSRegister::mhpmevent18 => RootCSRegister::mhpmevent18, - CSRegister::mhpmevent19 => RootCSRegister::mhpmevent19, - CSRegister::mhpmevent20 => RootCSRegister::mhpmevent20, - CSRegister::mhpmevent21 => RootCSRegister::mhpmevent21, - CSRegister::mhpmevent22 => RootCSRegister::mhpmevent22, - CSRegister::mhpmevent23 => RootCSRegister::mhpmevent23, - CSRegister::mhpmevent24 => RootCSRegister::mhpmevent24, - CSRegister::mhpmevent25 => RootCSRegister::mhpmevent25, - CSRegister::mhpmevent26 => RootCSRegister::mhpmevent26, - CSRegister::mhpmevent27 => RootCSRegister::mhpmevent27, - CSRegister::mhpmevent28 => RootCSRegister::mhpmevent28, - CSRegister::mhpmevent29 => RootCSRegister::mhpmevent29, - CSRegister::mhpmevent30 => RootCSRegister::mhpmevent30, - CSRegister::mhpmevent31 => RootCSRegister::mhpmevent31, - CSRegister::tselect => RootCSRegister::tselect, - CSRegister::tdata1 => RootCSRegister::tdata1, - CSRegister::tdata2 => RootCSRegister::tdata2, - CSRegister::tdata3 => RootCSRegister::tdata3, - CSRegister::tcontrol => RootCSRegister::tcontrol, - CSRegister::mcontext => RootCSRegister::mcontext, - CSRegister::dcsr => RootCSRegister::dcsr, - CSRegister::dpc => RootCSRegister::dpc, - CSRegister::dscratch0 => RootCSRegister::dscratch0, - CSRegister::dscratch1 => RootCSRegister::dscratch1, - } - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/satp.rs b/src/riscv/lib/src/machine_state/csregisters/satp.rs deleted file mode 100644 index e79e74751f9b1a90df91c53415013cbd4ef7f53e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/satp.rs +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -// Allow unused setters & getters & constants -#![allow(dead_code)] -// Allow non snake case for setters & getters & constants -#![allow(non_snake_case)] - -use super::CSRRepr; -use crate::bits::Bits64; -use crate::bits::FixedWidthBits; -use crate::csr; - -// allowed `MODE` for `satp` register. -// Section 4.1.11 -/// `satp.MODE = satp[63:60]` -const SATP_MODE_OFFSET: u64 = 60; -const MODE_BARE: CSRRepr = 0; -const MODE_SV39: CSRRepr = 8; -const MODE_SV48: CSRRepr = 9; -const MODE_SV57: CSRRepr = 10; - -/// Default value is BARE mode, (all fields of SATP are zero.) -pub const DEFAULT_VALUE: CSRRepr = MODE_BARE << SATP_MODE_OFFSET; - -/// Which flavour of the address virtualization is used. -/// -/// `SvXY` represents a virtualization where the virtual address is `XY` bits wide. -#[derive(PartialEq, Debug, Clone, Copy)] -#[repr(usize)] -pub enum SvLength { - Sv39 = 0, - Sv48 = 1, - Sv57 = 2, -} - -/// `MODE` field of the `satp` register. See table 5.4 -#[derive(PartialEq, Debug, Copy, Clone)] -#[repr(u8)] -pub enum TranslationAlgorithm { - Bare = 0, - Sv39 = 8, - Sv48 = 9, - Sv57 = 10, -} - -impl TranslationAlgorithm { - pub const fn enc(&self) -> CSRRepr { - *self as u8 as u64 - } -} - -/// `Err` represents that the value of SATP.mode is reserved or -/// that we do not care about it / is irrelevant. -impl Bits64 for TranslationAlgorithm { - const WIDTH: usize = 4; - - fn from_bits(value: u64) -> Self { - use TranslationAlgorithm::*; - - match value & 0b1111 { - MODE_BARE => Bare, - MODE_SV39 => Sv39, - MODE_SV48 => Sv48, - MODE_SV57 => Sv57, - // The satp.mode is a WARL field. - // This allows us to treat any illegal value as - // a legal one of our choice or to throw an exception. - // We are choosing to treat illegal values as `Bare` mode. - // Note: Reading an illegal value is only be possible if the raw memory is - // tampered with as the `reset` method and any write also leaves a legal value. - _ => Bare, - } - } - - fn to_bits(&self) -> u64 { - self.enc() - } -} - -csr! { - pub struct Satp { - PPN: FixedWidthBits<44>, - ASID: FixedWidthBits<16>, - MODE: TranslationAlgorithm, - } -} - -impl Default for Satp { - fn default() -> Self { - Self::from_bits(DEFAULT_VALUE) - } -} - -impl Satp { - /// Normalise the SATP value. - pub fn normalise(self) -> Self { - use TranslationAlgorithm::*; - match self.mode() { - Bare => { - // When no address translation algo is selected, the other fields - // have no meaning and shall therefore be reset. - self.with_ppn(FixedWidthBits::from_bits(0)) - .with_asid(FixedWidthBits::from_bits(0)) - } - Sv39 | Sv48 | Sv57 => self, - } - } -} - -#[cfg(test)] -mod tests { - use crate::bits::Bits64; - use crate::machine_state::csregisters::satp::Satp; - use crate::machine_state::csregisters::satp::TranslationAlgorithm; - - #[test] - fn test_satp_fields() { - let field = u64::from_bits(0xF0F0_0BC0_AAAA_DEAD); - assert_eq!(field.to_bits(), 0xF0F0_0BC0_AAAA_DEAD); - - type AlgoField = TranslationAlgorithm; - - let field = ::from_bits(0x0000); - assert_eq!(field, TranslationAlgorithm::Bare); - - // This `FieldValue` looks at only at the 4 least significant bits - let field = ::from_bits(0xFFFF_FFF0); - assert_eq!(field, TranslationAlgorithm::Bare); - - let field = ::from_bits(0x0002); - assert_eq!(field, TranslationAlgorithm::Bare); - - let field = ::from_bits(0x0008); - assert_eq!(field, TranslationAlgorithm::Sv39); - - let field = ::from_bits(0x0009); - assert_eq!(field, TranslationAlgorithm::Sv48); - - let field = ::from_bits(0x000A); - assert_eq!(field, TranslationAlgorithm::Sv57); - - let field = ::from_bits(0x000B); - assert_eq!(field, TranslationAlgorithm::Bare); - } - - #[test] - fn test_satp_rw() { - let satp = Satp::from_bits((8u64 << 60) | (0xD07 << 44) | 0xABC_DEAD_0BAD); - let mode = satp.mode(); - let asid = satp.asid(); - let ppn = satp.ppn(); - assert_eq!(mode, TranslationAlgorithm::Sv39); - assert_eq!(asid.to_bits(), 0xD07); - assert_eq!(ppn.to_bits(), 0xABC_DEAD_0BAD); - - let satp = satp.with_mode(TranslationAlgorithm::Bare); - let mode = satp.mode(); - let asid = satp.asid(); - let ppn = satp.ppn(); - assert_eq!(mode, TranslationAlgorithm::Bare); - assert_eq!(asid.to_bits(), 0xD07); - assert_eq!(ppn.to_bits(), 0xABC_DEAD_0BAD); - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/values.rs b/src/riscv/lib/src/machine_state/csregisters/values.rs deleted file mode 100644 index 95a114a7de7e8236cbdd9810db86694197b29a96..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/values.rs +++ /dev/null @@ -1,2064 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -mod mstatus; -mod xip; - -use mstatus::MStatusLayout; -pub(super) use mstatus::MStatusValue; -use xip::XipCell; -use xip::XipCellLayout; - -use super::CSRegisters; -use super::effects::NoEffect; -use super::effects::handle_csr_effect; -use super::root::RootCSRegister; -use crate::bits::Bits64; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Cell; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::EffectCell; -use crate::state_backend::EffectCellLayout; -use crate::state_backend::FnManager; -use crate::state_backend::FromProofResult; -use crate::state_backend::Layout; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::ManagerWrite; -use crate::state_backend::PartialHashError; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofPart; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::RefProofGenOwnedAlloc; -use crate::state_backend::RefVerifierAlloc; -use crate::state_backend::hash::Hash; -use crate::state_backend::hash::HashError; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::merkle::AccessInfoAggregatable; -use crate::state_backend::proof_backend::merkle::MerkleTree; -use crate::state_backend::verify_backend; -use crate::storage::binary; - -/// Representation of a value in a CSR -pub type CSRRepr = u64; - -/// Value of a Control or State register -#[derive( - Copy, - Clone, - Debug, - derive_more::Display, - derive_more::From, - derive_more::Into, - PartialEq, - Eq, - PartialOrd, - Ord, -)] -#[repr(transparent)] -pub struct CSRValue(CSRRepr); - -impl CSRValue { - /// Access the underlying representation. - pub fn repr(self) -> CSRRepr { - self.0 - } -} - -impl Bits64 for CSRValue { - const WIDTH: usize = CSRRepr::WIDTH; - - fn from_bits(value: u64) -> Self { - Self(value) - } - - fn to_bits(&self) -> u64 { - self.repr() - } -} - -type RawValue = EffectCell; - -/// Values of all control and state registers -pub type CSRValues = CSRValuesF, MStatusValue, XipCell>; - -impl CSRValues { - /// Bind the CSR values to the given allocated regions. - pub fn bind(space: AllocatedOf) -> Self { - space.map(MStatusValue::bind, XipCell::bind::, EffectCell::bind) - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf { - self.as_ref().map( - |mstatus| mstatus.struct_ref::(), - |xip| xip.struct_ref(), - |raw| raw.struct_ref::(), - ) - } -} - -impl NewState for CSRValues { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - let manager = std::cell::RefCell::new(manager); - CSRValuesF::new_with( - || MStatusValue::new(*manager.borrow_mut()), - || XipCell, - || RawValue::new(*manager.borrow_mut()), - ) - } -} - -impl CSRegisters { - /// Perform a general read of a CSR. - #[inline(always)] - pub(super) fn general_raw_read(&self, csr: RootCSRegister) -> CSRRepr - where - M: ManagerRead, - { - self.registers - .select_ref(csr, MStatusValue::read, XipCell::read, RawValue::read) - } - - /// Perform a general write of a CSR. - #[inline(always)] - pub(super) fn general_raw_write(&mut self, csr: RootCSRegister, value: CSRRepr) - where - M: ManagerWrite, - { - let effect = self.registers.select_mut( - csr, - |mstatus| mstatus.write(value), - |mip| mip.write(value), - |raw| raw.write(value), - ); - - handle_csr_effect(self, effect); - } - - /// Perform a general replace of a CSR. - #[inline(always)] - pub(super) fn general_raw_replace(&mut self, csr: RootCSRegister, value: CSRRepr) -> CSRRepr - where - M: ManagerReadWrite, - { - let (old_value, effect) = self.registers.select_mut( - csr, - |mstatus| mstatus.replace(value), - |mip| mip.replace(value), - |raw| raw.replace(value), - ); - - handle_csr_effect(self, effect); - - old_value - } -} - -/// Layout for the values of CSRs -pub struct CSRValuesLayout; - -impl Layout for CSRValuesLayout { - type Allocated = CSRValuesF< - AllocatedOf, M>, - AllocatedOf, - AllocatedOf, - >; -} - -impl CommitmentLayout for CSRValuesLayout { - fn state_hash(state: AllocatedOf) -> Result { - Hash::blake2b_hash(state) - } -} - -impl ProofLayout for CSRValuesLayout { - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let serialised = binary::serialise(&state)?; - MerkleTree::make_merkle_leaf(serialised, state.aggregate_access_info()) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - fn make_absent() -> AllocatedOf { - CSRValuesF::new_with( - || mstatus::MStatusLayoutF { - sie: Cell::absent(), - mie: Cell::absent(), - spie: Cell::absent(), - ube: Cell::absent(), - mpie: Cell::absent(), - spp: Cell::absent(), - mpp: Cell::absent(), - fs: Cell::absent(), - xs: Cell::absent(), - mprv: Cell::absent(), - sum: Cell::absent(), - mxr: Cell::absent(), - tvm: Cell::absent(), - tw: Cell::absent(), - tsr: Cell::absent(), - uxl: Cell::absent(), - sxl: Cell::absent(), - sbe: Cell::absent(), - mbe: Cell::absent(), - }, - || (), - || Cell::bind(verify_backend::Region::Absent), - ) - } - - let leaf = proof.into_leaf()?; - let cell = match leaf { - ProofPart::Absent => make_absent(), - ProofPart::Present(data) => { - let values: AllocatedOf = binary::deserialise(data)?; - values.map( - |mstatus| mstatus::MStatusLayoutF { - sie: Cell::from_owned(mstatus.sie), - mie: Cell::from_owned(mstatus.mie), - spie: Cell::from_owned(mstatus.spie), - ube: Cell::from_owned(mstatus.ube), - mpie: Cell::from_owned(mstatus.mpie), - spp: Cell::from_owned(mstatus.spp), - mpp: Cell::from_owned(mstatus.mpp), - fs: Cell::from_owned(mstatus.fs), - xs: Cell::from_owned(mstatus.xs), - mprv: Cell::from_owned(mstatus.mprv), - sum: Cell::from_owned(mstatus.sum), - mxr: Cell::from_owned(mstatus.mxr), - tvm: Cell::from_owned(mstatus.tvm), - tw: Cell::from_owned(mstatus.tw), - tsr: Cell::from_owned(mstatus.tsr), - uxl: Cell::from_owned(mstatus.uxl), - sxl: Cell::from_owned(mstatus.sxl), - sbe: Cell::from_owned(mstatus.sbe), - mbe: Cell::from_owned(mstatus.mbe), - }, - |()| (), - Cell::from_owned, - ) - } - }; - - Ok(cell) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - match state.try_map::<_, _, _, PartialHashError>( - |mstatus| { - Ok(mstatus::MStatusLayoutF { - sie: Cell::<_, Owned>::try_from(mstatus.sie)?, - mie: Cell::<_, Owned>::try_from(mstatus.mie)?, - spie: Cell::<_, Owned>::try_from(mstatus.spie)?, - ube: Cell::<_, Owned>::try_from(mstatus.ube)?, - mpie: Cell::<_, Owned>::try_from(mstatus.mpie)?, - spp: Cell::<_, Owned>::try_from(mstatus.spp)?, - mpp: Cell::<_, Owned>::try_from(mstatus.mpp)?, - fs: Cell::<_, Owned>::try_from(mstatus.fs)?, - xs: Cell::<_, Owned>::try_from(mstatus.xs)?, - mprv: Cell::<_, Owned>::try_from(mstatus.mprv)?, - sum: Cell::<_, Owned>::try_from(mstatus.sum)?, - mxr: Cell::<_, Owned>::try_from(mstatus.mxr)?, - tvm: Cell::<_, Owned>::try_from(mstatus.tvm)?, - tw: Cell::<_, Owned>::try_from(mstatus.tw)?, - tsr: Cell::<_, Owned>::try_from(mstatus.tsr)?, - uxl: Cell::<_, Owned>::try_from(mstatus.uxl)?, - sxl: Cell::<_, Owned>::try_from(mstatus.sxl)?, - sbe: Cell::<_, Owned>::try_from(mstatus.sbe)?, - mbe: Cell::<_, Owned>::try_from(mstatus.mbe)?, - }) - }, - |()| Ok(()), - Cell::<_, Owned>::try_from, - ) { - Ok(state) => Ok(Self::state_hash(state)?), - Err(_) => proof.partial_hash_leaf(), - } - } -} - -#[derive(Default, Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct CSRValuesF { - pub mstatus: MStatus, - pub mnscratch: Raw, - pub mnepc: Raw, - pub mncause: Raw, - pub mnstatus: Raw, - pub cycle: Raw, - pub time: Raw, - pub instret: Raw, - pub mcycle: Raw, - pub minstret: Raw, - pub hpmcounter3: Raw, - pub hpmcounter4: Raw, - pub hpmcounter5: Raw, - pub hpmcounter6: Raw, - pub hpmcounter7: Raw, - pub hpmcounter8: Raw, - pub hpmcounter9: Raw, - pub hpmcounter10: Raw, - pub hpmcounter11: Raw, - pub hpmcounter12: Raw, - pub hpmcounter13: Raw, - pub hpmcounter14: Raw, - pub hpmcounter15: Raw, - pub hpmcounter16: Raw, - pub hpmcounter17: Raw, - pub hpmcounter18: Raw, - pub hpmcounter19: Raw, - pub hpmcounter20: Raw, - pub hpmcounter21: Raw, - pub hpmcounter22: Raw, - pub hpmcounter23: Raw, - pub hpmcounter24: Raw, - pub hpmcounter25: Raw, - pub hpmcounter26: Raw, - pub hpmcounter27: Raw, - pub hpmcounter28: Raw, - pub hpmcounter29: Raw, - pub hpmcounter30: Raw, - pub hpmcounter31: Raw, - pub mhpmcounter3: Raw, - pub mhpmcounter4: Raw, - pub mhpmcounter5: Raw, - pub mhpmcounter6: Raw, - pub mhpmcounter7: Raw, - pub mhpmcounter8: Raw, - pub mhpmcounter9: Raw, - pub mhpmcounter10: Raw, - pub mhpmcounter11: Raw, - pub mhpmcounter12: Raw, - pub mhpmcounter13: Raw, - pub mhpmcounter14: Raw, - pub mhpmcounter15: Raw, - pub mhpmcounter16: Raw, - pub mhpmcounter17: Raw, - pub mhpmcounter18: Raw, - pub mhpmcounter19: Raw, - pub mhpmcounter20: Raw, - pub mhpmcounter21: Raw, - pub mhpmcounter22: Raw, - pub mhpmcounter23: Raw, - pub mhpmcounter24: Raw, - pub mhpmcounter25: Raw, - pub mhpmcounter26: Raw, - pub mhpmcounter27: Raw, - pub mhpmcounter28: Raw, - pub mhpmcounter29: Raw, - pub mhpmcounter30: Raw, - pub mhpmcounter31: Raw, - pub mhpmevent3: Raw, - pub mhpmevent4: Raw, - pub mhpmevent5: Raw, - pub mhpmevent6: Raw, - pub mhpmevent7: Raw, - pub mhpmevent8: Raw, - pub mhpmevent9: Raw, - pub mhpmevent10: Raw, - pub mhpmevent11: Raw, - pub mhpmevent12: Raw, - pub mhpmevent13: Raw, - pub mhpmevent14: Raw, - pub mhpmevent15: Raw, - pub mhpmevent16: Raw, - pub mhpmevent17: Raw, - pub mhpmevent18: Raw, - pub mhpmevent19: Raw, - pub mhpmevent20: Raw, - pub mhpmevent21: Raw, - pub mhpmevent22: Raw, - pub mhpmevent23: Raw, - pub mhpmevent24: Raw, - pub mhpmevent25: Raw, - pub mhpmevent26: Raw, - pub mhpmevent27: Raw, - pub mhpmevent28: Raw, - pub mhpmevent29: Raw, - pub mhpmevent30: Raw, - pub mhpmevent31: Raw, - pub mcountinhibit: Raw, - pub scounteren: Raw, - pub mcounteren: Raw, - pub fcsr: Raw, - pub pmpcfg0: Raw, - pub pmpcfg2: Raw, - pub pmpcfg4: Raw, - pub pmpcfg6: Raw, - pub pmpcfg8: Raw, - pub pmpcfg10: Raw, - pub pmpcfg12: Raw, - pub pmpcfg14: Raw, - pub pmpaddr0: Raw, - pub pmpaddr1: Raw, - pub pmpaddr2: Raw, - pub pmpaddr3: Raw, - pub pmpaddr4: Raw, - pub pmpaddr5: Raw, - pub pmpaddr6: Raw, - pub pmpaddr7: Raw, - pub pmpaddr8: Raw, - pub pmpaddr9: Raw, - pub pmpaddr10: Raw, - pub pmpaddr11: Raw, - pub pmpaddr12: Raw, - pub pmpaddr13: Raw, - pub pmpaddr14: Raw, - pub pmpaddr15: Raw, - pub pmpaddr16: Raw, - pub pmpaddr17: Raw, - pub pmpaddr18: Raw, - pub pmpaddr19: Raw, - pub pmpaddr20: Raw, - pub pmpaddr21: Raw, - pub pmpaddr22: Raw, - pub pmpaddr23: Raw, - pub pmpaddr24: Raw, - pub pmpaddr25: Raw, - pub pmpaddr26: Raw, - pub pmpaddr27: Raw, - pub pmpaddr28: Raw, - pub pmpaddr29: Raw, - pub pmpaddr30: Raw, - pub pmpaddr31: Raw, - pub pmpaddr32: Raw, - pub pmpaddr33: Raw, - pub pmpaddr34: Raw, - pub pmpaddr35: Raw, - pub pmpaddr36: Raw, - pub pmpaddr37: Raw, - pub pmpaddr38: Raw, - pub pmpaddr39: Raw, - pub pmpaddr40: Raw, - pub pmpaddr41: Raw, - pub pmpaddr42: Raw, - pub pmpaddr43: Raw, - pub pmpaddr44: Raw, - pub pmpaddr45: Raw, - pub pmpaddr46: Raw, - pub pmpaddr47: Raw, - pub pmpaddr48: Raw, - pub pmpaddr49: Raw, - pub pmpaddr50: Raw, - pub pmpaddr51: Raw, - pub pmpaddr52: Raw, - pub pmpaddr53: Raw, - pub pmpaddr54: Raw, - pub pmpaddr55: Raw, - pub pmpaddr56: Raw, - pub pmpaddr57: Raw, - pub pmpaddr58: Raw, - pub pmpaddr59: Raw, - pub pmpaddr60: Raw, - pub pmpaddr61: Raw, - pub pmpaddr62: Raw, - pub pmpaddr63: Raw, - pub mhartid: Raw, - pub mvendorid: Raw, - pub marchid: Raw, - pub mimpid: Raw, - pub misa: Raw, - pub mscratch: Raw, - pub sscratch: Raw, - pub stvec: Raw, - pub mtvec: Raw, - pub mie: Raw, - pub satp: Raw, - pub scause: Raw, - pub mcause: Raw, - pub sepc: Raw, - pub mepc: Raw, - pub stval: Raw, - pub mtval: Raw, - pub mtval2: Raw, - pub mip: MIP, - pub mtinst: Raw, - pub senvcfg: Raw, - pub menvcfg: Raw, - pub mconfigptr: Raw, - pub medeleg: Raw, - pub mideleg: Raw, - pub mseccfg: Raw, - pub scontext: Raw, - pub hstatus: Raw, - pub hedeleg: Raw, - pub hideleg: Raw, - pub hie: Raw, - pub hcounteren: Raw, - pub hgeie: Raw, - pub htval: Raw, - pub hip: Raw, - pub hvip: Raw, - pub htinst: Raw, - pub hgeip: Raw, - pub henvcfg: Raw, - pub hgatp: Raw, - pub hcontext: Raw, - pub htimedelta: Raw, - pub vsstatus: Raw, - pub vsie: Raw, - pub vstvec: Raw, - pub vsscratch: Raw, - pub vsepc: Raw, - pub vscause: Raw, - pub vstval: Raw, - pub vsip: Raw, - pub vsatp: Raw, - pub tselect: Raw, - pub tdata1: Raw, - pub tdata2: Raw, - pub tdata3: Raw, - pub tcontrol: Raw, - pub mcontext: Raw, - pub dcsr: Raw, - pub dpc: Raw, - pub dscratch0: Raw, - pub dscratch1: Raw, -} - -impl CSRValuesF { - /// Create a new CSR values structure. The given functions are used to initialise each CSR - /// value. - #[inline] - fn new_with( - make_mstatus: impl FnOnce() -> MStatus, - make_mip: impl FnOnce() -> MIP, - mut make_raw: impl FnMut() -> Raw, - ) -> Self { - CSRValuesF::<(), (), ()>::default().map(|_| make_mstatus(), |_| make_mip(), |_| make_raw()) - } - - /// Transform each field representing a CSR value into a different type. - #[inline] - fn map( - self, - map_mstatus: impl FnOnce(MStatus) -> MStatus2, - map_mip: impl FnOnce(MIP) -> MIP2, - mut map_raw: impl FnMut(Raw) -> Raw2, - ) -> CSRValuesF { - CSRValuesF { - mstatus: map_mstatus(self.mstatus), - mnscratch: map_raw(self.mnscratch), - mnepc: map_raw(self.mnepc), - mncause: map_raw(self.mncause), - mnstatus: map_raw(self.mnstatus), - cycle: map_raw(self.cycle), - time: map_raw(self.time), - instret: map_raw(self.instret), - mcycle: map_raw(self.mcycle), - minstret: map_raw(self.minstret), - hpmcounter3: map_raw(self.hpmcounter3), - hpmcounter4: map_raw(self.hpmcounter4), - hpmcounter5: map_raw(self.hpmcounter5), - hpmcounter6: map_raw(self.hpmcounter6), - hpmcounter7: map_raw(self.hpmcounter7), - hpmcounter8: map_raw(self.hpmcounter8), - hpmcounter9: map_raw(self.hpmcounter9), - hpmcounter10: map_raw(self.hpmcounter10), - hpmcounter11: map_raw(self.hpmcounter11), - hpmcounter12: map_raw(self.hpmcounter12), - hpmcounter13: map_raw(self.hpmcounter13), - hpmcounter14: map_raw(self.hpmcounter14), - hpmcounter15: map_raw(self.hpmcounter15), - hpmcounter16: map_raw(self.hpmcounter16), - hpmcounter17: map_raw(self.hpmcounter17), - hpmcounter18: map_raw(self.hpmcounter18), - hpmcounter19: map_raw(self.hpmcounter19), - hpmcounter20: map_raw(self.hpmcounter20), - hpmcounter21: map_raw(self.hpmcounter21), - hpmcounter22: map_raw(self.hpmcounter22), - hpmcounter23: map_raw(self.hpmcounter23), - hpmcounter24: map_raw(self.hpmcounter24), - hpmcounter25: map_raw(self.hpmcounter25), - hpmcounter26: map_raw(self.hpmcounter26), - hpmcounter27: map_raw(self.hpmcounter27), - hpmcounter28: map_raw(self.hpmcounter28), - hpmcounter29: map_raw(self.hpmcounter29), - hpmcounter30: map_raw(self.hpmcounter30), - hpmcounter31: map_raw(self.hpmcounter31), - mhpmcounter3: map_raw(self.mhpmcounter3), - mhpmcounter4: map_raw(self.mhpmcounter4), - mhpmcounter5: map_raw(self.mhpmcounter5), - mhpmcounter6: map_raw(self.mhpmcounter6), - mhpmcounter7: map_raw(self.mhpmcounter7), - mhpmcounter8: map_raw(self.mhpmcounter8), - mhpmcounter9: map_raw(self.mhpmcounter9), - mhpmcounter10: map_raw(self.mhpmcounter10), - mhpmcounter11: map_raw(self.mhpmcounter11), - mhpmcounter12: map_raw(self.mhpmcounter12), - mhpmcounter13: map_raw(self.mhpmcounter13), - mhpmcounter14: map_raw(self.mhpmcounter14), - mhpmcounter15: map_raw(self.mhpmcounter15), - mhpmcounter16: map_raw(self.mhpmcounter16), - mhpmcounter17: map_raw(self.mhpmcounter17), - mhpmcounter18: map_raw(self.mhpmcounter18), - mhpmcounter19: map_raw(self.mhpmcounter19), - mhpmcounter20: map_raw(self.mhpmcounter20), - mhpmcounter21: map_raw(self.mhpmcounter21), - mhpmcounter22: map_raw(self.mhpmcounter22), - mhpmcounter23: map_raw(self.mhpmcounter23), - mhpmcounter24: map_raw(self.mhpmcounter24), - mhpmcounter25: map_raw(self.mhpmcounter25), - mhpmcounter26: map_raw(self.mhpmcounter26), - mhpmcounter27: map_raw(self.mhpmcounter27), - mhpmcounter28: map_raw(self.mhpmcounter28), - mhpmcounter29: map_raw(self.mhpmcounter29), - mhpmcounter30: map_raw(self.mhpmcounter30), - mhpmcounter31: map_raw(self.mhpmcounter31), - mhpmevent3: map_raw(self.mhpmevent3), - mhpmevent4: map_raw(self.mhpmevent4), - mhpmevent5: map_raw(self.mhpmevent5), - mhpmevent6: map_raw(self.mhpmevent6), - mhpmevent7: map_raw(self.mhpmevent7), - mhpmevent8: map_raw(self.mhpmevent8), - mhpmevent9: map_raw(self.mhpmevent9), - mhpmevent10: map_raw(self.mhpmevent10), - mhpmevent11: map_raw(self.mhpmevent11), - mhpmevent12: map_raw(self.mhpmevent12), - mhpmevent13: map_raw(self.mhpmevent13), - mhpmevent14: map_raw(self.mhpmevent14), - mhpmevent15: map_raw(self.mhpmevent15), - mhpmevent16: map_raw(self.mhpmevent16), - mhpmevent17: map_raw(self.mhpmevent17), - mhpmevent18: map_raw(self.mhpmevent18), - mhpmevent19: map_raw(self.mhpmevent19), - mhpmevent20: map_raw(self.mhpmevent20), - mhpmevent21: map_raw(self.mhpmevent21), - mhpmevent22: map_raw(self.mhpmevent22), - mhpmevent23: map_raw(self.mhpmevent23), - mhpmevent24: map_raw(self.mhpmevent24), - mhpmevent25: map_raw(self.mhpmevent25), - mhpmevent26: map_raw(self.mhpmevent26), - mhpmevent27: map_raw(self.mhpmevent27), - mhpmevent28: map_raw(self.mhpmevent28), - mhpmevent29: map_raw(self.mhpmevent29), - mhpmevent30: map_raw(self.mhpmevent30), - mhpmevent31: map_raw(self.mhpmevent31), - mcountinhibit: map_raw(self.mcountinhibit), - scounteren: map_raw(self.scounteren), - mcounteren: map_raw(self.mcounteren), - fcsr: map_raw(self.fcsr), - pmpcfg0: map_raw(self.pmpcfg0), - pmpcfg2: map_raw(self.pmpcfg2), - pmpcfg4: map_raw(self.pmpcfg4), - pmpcfg6: map_raw(self.pmpcfg6), - pmpcfg8: map_raw(self.pmpcfg8), - pmpcfg10: map_raw(self.pmpcfg10), - pmpcfg12: map_raw(self.pmpcfg12), - pmpcfg14: map_raw(self.pmpcfg14), - pmpaddr0: map_raw(self.pmpaddr0), - pmpaddr1: map_raw(self.pmpaddr1), - pmpaddr2: map_raw(self.pmpaddr2), - pmpaddr3: map_raw(self.pmpaddr3), - pmpaddr4: map_raw(self.pmpaddr4), - pmpaddr5: map_raw(self.pmpaddr5), - pmpaddr6: map_raw(self.pmpaddr6), - pmpaddr7: map_raw(self.pmpaddr7), - pmpaddr8: map_raw(self.pmpaddr8), - pmpaddr9: map_raw(self.pmpaddr9), - pmpaddr10: map_raw(self.pmpaddr10), - pmpaddr11: map_raw(self.pmpaddr11), - pmpaddr12: map_raw(self.pmpaddr12), - pmpaddr13: map_raw(self.pmpaddr13), - pmpaddr14: map_raw(self.pmpaddr14), - pmpaddr15: map_raw(self.pmpaddr15), - pmpaddr16: map_raw(self.pmpaddr16), - pmpaddr17: map_raw(self.pmpaddr17), - pmpaddr18: map_raw(self.pmpaddr18), - pmpaddr19: map_raw(self.pmpaddr19), - pmpaddr20: map_raw(self.pmpaddr20), - pmpaddr21: map_raw(self.pmpaddr21), - pmpaddr22: map_raw(self.pmpaddr22), - pmpaddr23: map_raw(self.pmpaddr23), - pmpaddr24: map_raw(self.pmpaddr24), - pmpaddr25: map_raw(self.pmpaddr25), - pmpaddr26: map_raw(self.pmpaddr26), - pmpaddr27: map_raw(self.pmpaddr27), - pmpaddr28: map_raw(self.pmpaddr28), - pmpaddr29: map_raw(self.pmpaddr29), - pmpaddr30: map_raw(self.pmpaddr30), - pmpaddr31: map_raw(self.pmpaddr31), - pmpaddr32: map_raw(self.pmpaddr32), - pmpaddr33: map_raw(self.pmpaddr33), - pmpaddr34: map_raw(self.pmpaddr34), - pmpaddr35: map_raw(self.pmpaddr35), - pmpaddr36: map_raw(self.pmpaddr36), - pmpaddr37: map_raw(self.pmpaddr37), - pmpaddr38: map_raw(self.pmpaddr38), - pmpaddr39: map_raw(self.pmpaddr39), - pmpaddr40: map_raw(self.pmpaddr40), - pmpaddr41: map_raw(self.pmpaddr41), - pmpaddr42: map_raw(self.pmpaddr42), - pmpaddr43: map_raw(self.pmpaddr43), - pmpaddr44: map_raw(self.pmpaddr44), - pmpaddr45: map_raw(self.pmpaddr45), - pmpaddr46: map_raw(self.pmpaddr46), - pmpaddr47: map_raw(self.pmpaddr47), - pmpaddr48: map_raw(self.pmpaddr48), - pmpaddr49: map_raw(self.pmpaddr49), - pmpaddr50: map_raw(self.pmpaddr50), - pmpaddr51: map_raw(self.pmpaddr51), - pmpaddr52: map_raw(self.pmpaddr52), - pmpaddr53: map_raw(self.pmpaddr53), - pmpaddr54: map_raw(self.pmpaddr54), - pmpaddr55: map_raw(self.pmpaddr55), - pmpaddr56: map_raw(self.pmpaddr56), - pmpaddr57: map_raw(self.pmpaddr57), - pmpaddr58: map_raw(self.pmpaddr58), - pmpaddr59: map_raw(self.pmpaddr59), - pmpaddr60: map_raw(self.pmpaddr60), - pmpaddr61: map_raw(self.pmpaddr61), - pmpaddr62: map_raw(self.pmpaddr62), - pmpaddr63: map_raw(self.pmpaddr63), - mhartid: map_raw(self.mhartid), - mvendorid: map_raw(self.mvendorid), - marchid: map_raw(self.marchid), - mimpid: map_raw(self.mimpid), - misa: map_raw(self.misa), - mscratch: map_raw(self.mscratch), - sscratch: map_raw(self.sscratch), - stvec: map_raw(self.stvec), - mtvec: map_raw(self.mtvec), - mie: map_raw(self.mie), - satp: map_raw(self.satp), - scause: map_raw(self.scause), - mcause: map_raw(self.mcause), - sepc: map_raw(self.sepc), - mepc: map_raw(self.mepc), - stval: map_raw(self.stval), - mtval: map_raw(self.mtval), - mtval2: map_raw(self.mtval2), - mip: map_mip(self.mip), - mtinst: map_raw(self.mtinst), - senvcfg: map_raw(self.senvcfg), - menvcfg: map_raw(self.menvcfg), - mconfigptr: map_raw(self.mconfigptr), - medeleg: map_raw(self.medeleg), - mideleg: map_raw(self.mideleg), - mseccfg: map_raw(self.mseccfg), - scontext: map_raw(self.scontext), - hstatus: map_raw(self.hstatus), - hedeleg: map_raw(self.hedeleg), - hideleg: map_raw(self.hideleg), - hie: map_raw(self.hie), - hcounteren: map_raw(self.hcounteren), - hgeie: map_raw(self.hgeie), - htval: map_raw(self.htval), - hip: map_raw(self.hip), - hvip: map_raw(self.hvip), - htinst: map_raw(self.htinst), - hgeip: map_raw(self.hgeip), - henvcfg: map_raw(self.henvcfg), - hgatp: map_raw(self.hgatp), - hcontext: map_raw(self.hcontext), - htimedelta: map_raw(self.htimedelta), - vsstatus: map_raw(self.vsstatus), - vsie: map_raw(self.vsie), - vstvec: map_raw(self.vstvec), - vsscratch: map_raw(self.vsscratch), - vsepc: map_raw(self.vsepc), - vscause: map_raw(self.vscause), - vstval: map_raw(self.vstval), - vsip: map_raw(self.vsip), - vsatp: map_raw(self.vsatp), - tselect: map_raw(self.tselect), - tdata1: map_raw(self.tdata1), - tdata2: map_raw(self.tdata2), - tdata3: map_raw(self.tdata3), - tcontrol: map_raw(self.tcontrol), - mcontext: map_raw(self.mcontext), - dcsr: map_raw(self.dcsr), - dpc: map_raw(self.dpc), - dscratch0: map_raw(self.dscratch0), - dscratch1: map_raw(self.dscratch1), - } - } - - /// Attempt to transform each field representing a CSR value into a different type. - #[inline] - fn try_map( - self, - try_map_mstatus: impl FnOnce(MStatus) -> Result, - try_map_mip: impl FnOnce(MIP) -> Result, - mut try_map_raw: impl FnMut(Raw) -> Result, - ) -> Result, Err> { - Ok(CSRValuesF { - mstatus: try_map_mstatus(self.mstatus)?, - mnscratch: try_map_raw(self.mnscratch)?, - mnepc: try_map_raw(self.mnepc)?, - mncause: try_map_raw(self.mncause)?, - mnstatus: try_map_raw(self.mnstatus)?, - cycle: try_map_raw(self.cycle)?, - time: try_map_raw(self.time)?, - instret: try_map_raw(self.instret)?, - mcycle: try_map_raw(self.mcycle)?, - minstret: try_map_raw(self.minstret)?, - hpmcounter3: try_map_raw(self.hpmcounter3)?, - hpmcounter4: try_map_raw(self.hpmcounter4)?, - hpmcounter5: try_map_raw(self.hpmcounter5)?, - hpmcounter6: try_map_raw(self.hpmcounter6)?, - hpmcounter7: try_map_raw(self.hpmcounter7)?, - hpmcounter8: try_map_raw(self.hpmcounter8)?, - hpmcounter9: try_map_raw(self.hpmcounter9)?, - hpmcounter10: try_map_raw(self.hpmcounter10)?, - hpmcounter11: try_map_raw(self.hpmcounter11)?, - hpmcounter12: try_map_raw(self.hpmcounter12)?, - hpmcounter13: try_map_raw(self.hpmcounter13)?, - hpmcounter14: try_map_raw(self.hpmcounter14)?, - hpmcounter15: try_map_raw(self.hpmcounter15)?, - hpmcounter16: try_map_raw(self.hpmcounter16)?, - hpmcounter17: try_map_raw(self.hpmcounter17)?, - hpmcounter18: try_map_raw(self.hpmcounter18)?, - hpmcounter19: try_map_raw(self.hpmcounter19)?, - hpmcounter20: try_map_raw(self.hpmcounter20)?, - hpmcounter21: try_map_raw(self.hpmcounter21)?, - hpmcounter22: try_map_raw(self.hpmcounter22)?, - hpmcounter23: try_map_raw(self.hpmcounter23)?, - hpmcounter24: try_map_raw(self.hpmcounter24)?, - hpmcounter25: try_map_raw(self.hpmcounter25)?, - hpmcounter26: try_map_raw(self.hpmcounter26)?, - hpmcounter27: try_map_raw(self.hpmcounter27)?, - hpmcounter28: try_map_raw(self.hpmcounter28)?, - hpmcounter29: try_map_raw(self.hpmcounter29)?, - hpmcounter30: try_map_raw(self.hpmcounter30)?, - hpmcounter31: try_map_raw(self.hpmcounter31)?, - mhpmcounter3: try_map_raw(self.mhpmcounter3)?, - mhpmcounter4: try_map_raw(self.mhpmcounter4)?, - mhpmcounter5: try_map_raw(self.mhpmcounter5)?, - mhpmcounter6: try_map_raw(self.mhpmcounter6)?, - mhpmcounter7: try_map_raw(self.mhpmcounter7)?, - mhpmcounter8: try_map_raw(self.mhpmcounter8)?, - mhpmcounter9: try_map_raw(self.mhpmcounter9)?, - mhpmcounter10: try_map_raw(self.mhpmcounter10)?, - mhpmcounter11: try_map_raw(self.mhpmcounter11)?, - mhpmcounter12: try_map_raw(self.mhpmcounter12)?, - mhpmcounter13: try_map_raw(self.mhpmcounter13)?, - mhpmcounter14: try_map_raw(self.mhpmcounter14)?, - mhpmcounter15: try_map_raw(self.mhpmcounter15)?, - mhpmcounter16: try_map_raw(self.mhpmcounter16)?, - mhpmcounter17: try_map_raw(self.mhpmcounter17)?, - mhpmcounter18: try_map_raw(self.mhpmcounter18)?, - mhpmcounter19: try_map_raw(self.mhpmcounter19)?, - mhpmcounter20: try_map_raw(self.mhpmcounter20)?, - mhpmcounter21: try_map_raw(self.mhpmcounter21)?, - mhpmcounter22: try_map_raw(self.mhpmcounter22)?, - mhpmcounter23: try_map_raw(self.mhpmcounter23)?, - mhpmcounter24: try_map_raw(self.mhpmcounter24)?, - mhpmcounter25: try_map_raw(self.mhpmcounter25)?, - mhpmcounter26: try_map_raw(self.mhpmcounter26)?, - mhpmcounter27: try_map_raw(self.mhpmcounter27)?, - mhpmcounter28: try_map_raw(self.mhpmcounter28)?, - mhpmcounter29: try_map_raw(self.mhpmcounter29)?, - mhpmcounter30: try_map_raw(self.mhpmcounter30)?, - mhpmcounter31: try_map_raw(self.mhpmcounter31)?, - mhpmevent3: try_map_raw(self.mhpmevent3)?, - mhpmevent4: try_map_raw(self.mhpmevent4)?, - mhpmevent5: try_map_raw(self.mhpmevent5)?, - mhpmevent6: try_map_raw(self.mhpmevent6)?, - mhpmevent7: try_map_raw(self.mhpmevent7)?, - mhpmevent8: try_map_raw(self.mhpmevent8)?, - mhpmevent9: try_map_raw(self.mhpmevent9)?, - mhpmevent10: try_map_raw(self.mhpmevent10)?, - mhpmevent11: try_map_raw(self.mhpmevent11)?, - mhpmevent12: try_map_raw(self.mhpmevent12)?, - mhpmevent13: try_map_raw(self.mhpmevent13)?, - mhpmevent14: try_map_raw(self.mhpmevent14)?, - mhpmevent15: try_map_raw(self.mhpmevent15)?, - mhpmevent16: try_map_raw(self.mhpmevent16)?, - mhpmevent17: try_map_raw(self.mhpmevent17)?, - mhpmevent18: try_map_raw(self.mhpmevent18)?, - mhpmevent19: try_map_raw(self.mhpmevent19)?, - mhpmevent20: try_map_raw(self.mhpmevent20)?, - mhpmevent21: try_map_raw(self.mhpmevent21)?, - mhpmevent22: try_map_raw(self.mhpmevent22)?, - mhpmevent23: try_map_raw(self.mhpmevent23)?, - mhpmevent24: try_map_raw(self.mhpmevent24)?, - mhpmevent25: try_map_raw(self.mhpmevent25)?, - mhpmevent26: try_map_raw(self.mhpmevent26)?, - mhpmevent27: try_map_raw(self.mhpmevent27)?, - mhpmevent28: try_map_raw(self.mhpmevent28)?, - mhpmevent29: try_map_raw(self.mhpmevent29)?, - mhpmevent30: try_map_raw(self.mhpmevent30)?, - mhpmevent31: try_map_raw(self.mhpmevent31)?, - mcountinhibit: try_map_raw(self.mcountinhibit)?, - scounteren: try_map_raw(self.scounteren)?, - mcounteren: try_map_raw(self.mcounteren)?, - fcsr: try_map_raw(self.fcsr)?, - pmpcfg0: try_map_raw(self.pmpcfg0)?, - pmpcfg2: try_map_raw(self.pmpcfg2)?, - pmpcfg4: try_map_raw(self.pmpcfg4)?, - pmpcfg6: try_map_raw(self.pmpcfg6)?, - pmpcfg8: try_map_raw(self.pmpcfg8)?, - pmpcfg10: try_map_raw(self.pmpcfg10)?, - pmpcfg12: try_map_raw(self.pmpcfg12)?, - pmpcfg14: try_map_raw(self.pmpcfg14)?, - pmpaddr0: try_map_raw(self.pmpaddr0)?, - pmpaddr1: try_map_raw(self.pmpaddr1)?, - pmpaddr2: try_map_raw(self.pmpaddr2)?, - pmpaddr3: try_map_raw(self.pmpaddr3)?, - pmpaddr4: try_map_raw(self.pmpaddr4)?, - pmpaddr5: try_map_raw(self.pmpaddr5)?, - pmpaddr6: try_map_raw(self.pmpaddr6)?, - pmpaddr7: try_map_raw(self.pmpaddr7)?, - pmpaddr8: try_map_raw(self.pmpaddr8)?, - pmpaddr9: try_map_raw(self.pmpaddr9)?, - pmpaddr10: try_map_raw(self.pmpaddr10)?, - pmpaddr11: try_map_raw(self.pmpaddr11)?, - pmpaddr12: try_map_raw(self.pmpaddr12)?, - pmpaddr13: try_map_raw(self.pmpaddr13)?, - pmpaddr14: try_map_raw(self.pmpaddr14)?, - pmpaddr15: try_map_raw(self.pmpaddr15)?, - pmpaddr16: try_map_raw(self.pmpaddr16)?, - pmpaddr17: try_map_raw(self.pmpaddr17)?, - pmpaddr18: try_map_raw(self.pmpaddr18)?, - pmpaddr19: try_map_raw(self.pmpaddr19)?, - pmpaddr20: try_map_raw(self.pmpaddr20)?, - pmpaddr21: try_map_raw(self.pmpaddr21)?, - pmpaddr22: try_map_raw(self.pmpaddr22)?, - pmpaddr23: try_map_raw(self.pmpaddr23)?, - pmpaddr24: try_map_raw(self.pmpaddr24)?, - pmpaddr25: try_map_raw(self.pmpaddr25)?, - pmpaddr26: try_map_raw(self.pmpaddr26)?, - pmpaddr27: try_map_raw(self.pmpaddr27)?, - pmpaddr28: try_map_raw(self.pmpaddr28)?, - pmpaddr29: try_map_raw(self.pmpaddr29)?, - pmpaddr30: try_map_raw(self.pmpaddr30)?, - pmpaddr31: try_map_raw(self.pmpaddr31)?, - pmpaddr32: try_map_raw(self.pmpaddr32)?, - pmpaddr33: try_map_raw(self.pmpaddr33)?, - pmpaddr34: try_map_raw(self.pmpaddr34)?, - pmpaddr35: try_map_raw(self.pmpaddr35)?, - pmpaddr36: try_map_raw(self.pmpaddr36)?, - pmpaddr37: try_map_raw(self.pmpaddr37)?, - pmpaddr38: try_map_raw(self.pmpaddr38)?, - pmpaddr39: try_map_raw(self.pmpaddr39)?, - pmpaddr40: try_map_raw(self.pmpaddr40)?, - pmpaddr41: try_map_raw(self.pmpaddr41)?, - pmpaddr42: try_map_raw(self.pmpaddr42)?, - pmpaddr43: try_map_raw(self.pmpaddr43)?, - pmpaddr44: try_map_raw(self.pmpaddr44)?, - pmpaddr45: try_map_raw(self.pmpaddr45)?, - pmpaddr46: try_map_raw(self.pmpaddr46)?, - pmpaddr47: try_map_raw(self.pmpaddr47)?, - pmpaddr48: try_map_raw(self.pmpaddr48)?, - pmpaddr49: try_map_raw(self.pmpaddr49)?, - pmpaddr50: try_map_raw(self.pmpaddr50)?, - pmpaddr51: try_map_raw(self.pmpaddr51)?, - pmpaddr52: try_map_raw(self.pmpaddr52)?, - pmpaddr53: try_map_raw(self.pmpaddr53)?, - pmpaddr54: try_map_raw(self.pmpaddr54)?, - pmpaddr55: try_map_raw(self.pmpaddr55)?, - pmpaddr56: try_map_raw(self.pmpaddr56)?, - pmpaddr57: try_map_raw(self.pmpaddr57)?, - pmpaddr58: try_map_raw(self.pmpaddr58)?, - pmpaddr59: try_map_raw(self.pmpaddr59)?, - pmpaddr60: try_map_raw(self.pmpaddr60)?, - pmpaddr61: try_map_raw(self.pmpaddr61)?, - pmpaddr62: try_map_raw(self.pmpaddr62)?, - pmpaddr63: try_map_raw(self.pmpaddr63)?, - mhartid: try_map_raw(self.mhartid)?, - mvendorid: try_map_raw(self.mvendorid)?, - marchid: try_map_raw(self.marchid)?, - mimpid: try_map_raw(self.mimpid)?, - misa: try_map_raw(self.misa)?, - mscratch: try_map_raw(self.mscratch)?, - sscratch: try_map_raw(self.sscratch)?, - stvec: try_map_raw(self.stvec)?, - mtvec: try_map_raw(self.mtvec)?, - mie: try_map_raw(self.mie)?, - satp: try_map_raw(self.satp)?, - scause: try_map_raw(self.scause)?, - mcause: try_map_raw(self.mcause)?, - sepc: try_map_raw(self.sepc)?, - mepc: try_map_raw(self.mepc)?, - stval: try_map_raw(self.stval)?, - mtval: try_map_raw(self.mtval)?, - mtval2: try_map_raw(self.mtval2)?, - mip: try_map_mip(self.mip)?, - mtinst: try_map_raw(self.mtinst)?, - senvcfg: try_map_raw(self.senvcfg)?, - menvcfg: try_map_raw(self.menvcfg)?, - mconfigptr: try_map_raw(self.mconfigptr)?, - medeleg: try_map_raw(self.medeleg)?, - mideleg: try_map_raw(self.mideleg)?, - mseccfg: try_map_raw(self.mseccfg)?, - scontext: try_map_raw(self.scontext)?, - hstatus: try_map_raw(self.hstatus)?, - hedeleg: try_map_raw(self.hedeleg)?, - hideleg: try_map_raw(self.hideleg)?, - hie: try_map_raw(self.hie)?, - hcounteren: try_map_raw(self.hcounteren)?, - hgeie: try_map_raw(self.hgeie)?, - htval: try_map_raw(self.htval)?, - hip: try_map_raw(self.hip)?, - hvip: try_map_raw(self.hvip)?, - htinst: try_map_raw(self.htinst)?, - hgeip: try_map_raw(self.hgeip)?, - henvcfg: try_map_raw(self.henvcfg)?, - hgatp: try_map_raw(self.hgatp)?, - hcontext: try_map_raw(self.hcontext)?, - htimedelta: try_map_raw(self.htimedelta)?, - vsstatus: try_map_raw(self.vsstatus)?, - vsie: try_map_raw(self.vsie)?, - vstvec: try_map_raw(self.vstvec)?, - vsscratch: try_map_raw(self.vsscratch)?, - vsepc: try_map_raw(self.vsepc)?, - vscause: try_map_raw(self.vscause)?, - vstval: try_map_raw(self.vstval)?, - vsip: try_map_raw(self.vsip)?, - vsatp: try_map_raw(self.vsatp)?, - tselect: try_map_raw(self.tselect)?, - tdata1: try_map_raw(self.tdata1)?, - tdata2: try_map_raw(self.tdata2)?, - tdata3: try_map_raw(self.tdata3)?, - tcontrol: try_map_raw(self.tcontrol)?, - mcontext: try_map_raw(self.mcontext)?, - dcsr: try_map_raw(self.dcsr)?, - dpc: try_map_raw(self.dpc)?, - dscratch0: try_map_raw(self.dscratch0)?, - dscratch1: try_map_raw(self.dscratch1)?, - }) - } - - /// Create a referencing structure of the CSR values. - #[inline] - fn as_ref(&self) -> CSRValuesF<&Raw, &MStatus, &MIP> { - CSRValuesF { - mstatus: &self.mstatus, - mnscratch: &self.mnscratch, - mnepc: &self.mnepc, - mncause: &self.mncause, - mnstatus: &self.mnstatus, - cycle: &self.cycle, - time: &self.time, - instret: &self.instret, - mcycle: &self.mcycle, - minstret: &self.minstret, - hpmcounter3: &self.hpmcounter3, - hpmcounter4: &self.hpmcounter4, - hpmcounter5: &self.hpmcounter5, - hpmcounter6: &self.hpmcounter6, - hpmcounter7: &self.hpmcounter7, - hpmcounter8: &self.hpmcounter8, - hpmcounter9: &self.hpmcounter9, - hpmcounter10: &self.hpmcounter10, - hpmcounter11: &self.hpmcounter11, - hpmcounter12: &self.hpmcounter12, - hpmcounter13: &self.hpmcounter13, - hpmcounter14: &self.hpmcounter14, - hpmcounter15: &self.hpmcounter15, - hpmcounter16: &self.hpmcounter16, - hpmcounter17: &self.hpmcounter17, - hpmcounter18: &self.hpmcounter18, - hpmcounter19: &self.hpmcounter19, - hpmcounter20: &self.hpmcounter20, - hpmcounter21: &self.hpmcounter21, - hpmcounter22: &self.hpmcounter22, - hpmcounter23: &self.hpmcounter23, - hpmcounter24: &self.hpmcounter24, - hpmcounter25: &self.hpmcounter25, - hpmcounter26: &self.hpmcounter26, - hpmcounter27: &self.hpmcounter27, - hpmcounter28: &self.hpmcounter28, - hpmcounter29: &self.hpmcounter29, - hpmcounter30: &self.hpmcounter30, - hpmcounter31: &self.hpmcounter31, - mhpmcounter3: &self.mhpmcounter3, - mhpmcounter4: &self.mhpmcounter4, - mhpmcounter5: &self.mhpmcounter5, - mhpmcounter6: &self.mhpmcounter6, - mhpmcounter7: &self.mhpmcounter7, - mhpmcounter8: &self.mhpmcounter8, - mhpmcounter9: &self.mhpmcounter9, - mhpmcounter10: &self.mhpmcounter10, - mhpmcounter11: &self.mhpmcounter11, - mhpmcounter12: &self.mhpmcounter12, - mhpmcounter13: &self.mhpmcounter13, - mhpmcounter14: &self.mhpmcounter14, - mhpmcounter15: &self.mhpmcounter15, - mhpmcounter16: &self.mhpmcounter16, - mhpmcounter17: &self.mhpmcounter17, - mhpmcounter18: &self.mhpmcounter18, - mhpmcounter19: &self.mhpmcounter19, - mhpmcounter20: &self.mhpmcounter20, - mhpmcounter21: &self.mhpmcounter21, - mhpmcounter22: &self.mhpmcounter22, - mhpmcounter23: &self.mhpmcounter23, - mhpmcounter24: &self.mhpmcounter24, - mhpmcounter25: &self.mhpmcounter25, - mhpmcounter26: &self.mhpmcounter26, - mhpmcounter27: &self.mhpmcounter27, - mhpmcounter28: &self.mhpmcounter28, - mhpmcounter29: &self.mhpmcounter29, - mhpmcounter30: &self.mhpmcounter30, - mhpmcounter31: &self.mhpmcounter31, - mhpmevent3: &self.mhpmevent3, - mhpmevent4: &self.mhpmevent4, - mhpmevent5: &self.mhpmevent5, - mhpmevent6: &self.mhpmevent6, - mhpmevent7: &self.mhpmevent7, - mhpmevent8: &self.mhpmevent8, - mhpmevent9: &self.mhpmevent9, - mhpmevent10: &self.mhpmevent10, - mhpmevent11: &self.mhpmevent11, - mhpmevent12: &self.mhpmevent12, - mhpmevent13: &self.mhpmevent13, - mhpmevent14: &self.mhpmevent14, - mhpmevent15: &self.mhpmevent15, - mhpmevent16: &self.mhpmevent16, - mhpmevent17: &self.mhpmevent17, - mhpmevent18: &self.mhpmevent18, - mhpmevent19: &self.mhpmevent19, - mhpmevent20: &self.mhpmevent20, - mhpmevent21: &self.mhpmevent21, - mhpmevent22: &self.mhpmevent22, - mhpmevent23: &self.mhpmevent23, - mhpmevent24: &self.mhpmevent24, - mhpmevent25: &self.mhpmevent25, - mhpmevent26: &self.mhpmevent26, - mhpmevent27: &self.mhpmevent27, - mhpmevent28: &self.mhpmevent28, - mhpmevent29: &self.mhpmevent29, - mhpmevent30: &self.mhpmevent30, - mhpmevent31: &self.mhpmevent31, - mcountinhibit: &self.mcountinhibit, - scounteren: &self.scounteren, - mcounteren: &self.mcounteren, - fcsr: &self.fcsr, - pmpcfg0: &self.pmpcfg0, - pmpcfg2: &self.pmpcfg2, - pmpcfg4: &self.pmpcfg4, - pmpcfg6: &self.pmpcfg6, - pmpcfg8: &self.pmpcfg8, - pmpcfg10: &self.pmpcfg10, - pmpcfg12: &self.pmpcfg12, - pmpcfg14: &self.pmpcfg14, - pmpaddr0: &self.pmpaddr0, - pmpaddr1: &self.pmpaddr1, - pmpaddr2: &self.pmpaddr2, - pmpaddr3: &self.pmpaddr3, - pmpaddr4: &self.pmpaddr4, - pmpaddr5: &self.pmpaddr5, - pmpaddr6: &self.pmpaddr6, - pmpaddr7: &self.pmpaddr7, - pmpaddr8: &self.pmpaddr8, - pmpaddr9: &self.pmpaddr9, - pmpaddr10: &self.pmpaddr10, - pmpaddr11: &self.pmpaddr11, - pmpaddr12: &self.pmpaddr12, - pmpaddr13: &self.pmpaddr13, - pmpaddr14: &self.pmpaddr14, - pmpaddr15: &self.pmpaddr15, - pmpaddr16: &self.pmpaddr16, - pmpaddr17: &self.pmpaddr17, - pmpaddr18: &self.pmpaddr18, - pmpaddr19: &self.pmpaddr19, - pmpaddr20: &self.pmpaddr20, - pmpaddr21: &self.pmpaddr21, - pmpaddr22: &self.pmpaddr22, - pmpaddr23: &self.pmpaddr23, - pmpaddr24: &self.pmpaddr24, - pmpaddr25: &self.pmpaddr25, - pmpaddr26: &self.pmpaddr26, - pmpaddr27: &self.pmpaddr27, - pmpaddr28: &self.pmpaddr28, - pmpaddr29: &self.pmpaddr29, - pmpaddr30: &self.pmpaddr30, - pmpaddr31: &self.pmpaddr31, - pmpaddr32: &self.pmpaddr32, - pmpaddr33: &self.pmpaddr33, - pmpaddr34: &self.pmpaddr34, - pmpaddr35: &self.pmpaddr35, - pmpaddr36: &self.pmpaddr36, - pmpaddr37: &self.pmpaddr37, - pmpaddr38: &self.pmpaddr38, - pmpaddr39: &self.pmpaddr39, - pmpaddr40: &self.pmpaddr40, - pmpaddr41: &self.pmpaddr41, - pmpaddr42: &self.pmpaddr42, - pmpaddr43: &self.pmpaddr43, - pmpaddr44: &self.pmpaddr44, - pmpaddr45: &self.pmpaddr45, - pmpaddr46: &self.pmpaddr46, - pmpaddr47: &self.pmpaddr47, - pmpaddr48: &self.pmpaddr48, - pmpaddr49: &self.pmpaddr49, - pmpaddr50: &self.pmpaddr50, - pmpaddr51: &self.pmpaddr51, - pmpaddr52: &self.pmpaddr52, - pmpaddr53: &self.pmpaddr53, - pmpaddr54: &self.pmpaddr54, - pmpaddr55: &self.pmpaddr55, - pmpaddr56: &self.pmpaddr56, - pmpaddr57: &self.pmpaddr57, - pmpaddr58: &self.pmpaddr58, - pmpaddr59: &self.pmpaddr59, - pmpaddr60: &self.pmpaddr60, - pmpaddr61: &self.pmpaddr61, - pmpaddr62: &self.pmpaddr62, - pmpaddr63: &self.pmpaddr63, - mhartid: &self.mhartid, - mvendorid: &self.mvendorid, - marchid: &self.marchid, - mimpid: &self.mimpid, - misa: &self.misa, - mscratch: &self.mscratch, - sscratch: &self.sscratch, - stvec: &self.stvec, - mtvec: &self.mtvec, - mie: &self.mie, - satp: &self.satp, - scause: &self.scause, - mcause: &self.mcause, - sepc: &self.sepc, - mepc: &self.mepc, - stval: &self.stval, - mtval: &self.mtval, - mtval2: &self.mtval2, - mip: &self.mip, - mtinst: &self.mtinst, - senvcfg: &self.senvcfg, - menvcfg: &self.menvcfg, - mconfigptr: &self.mconfigptr, - medeleg: &self.medeleg, - mideleg: &self.mideleg, - mseccfg: &self.mseccfg, - scontext: &self.scontext, - hstatus: &self.hstatus, - hedeleg: &self.hedeleg, - hideleg: &self.hideleg, - hie: &self.hie, - hcounteren: &self.hcounteren, - hgeie: &self.hgeie, - htval: &self.htval, - hip: &self.hip, - hvip: &self.hvip, - htinst: &self.htinst, - hgeip: &self.hgeip, - henvcfg: &self.henvcfg, - hgatp: &self.hgatp, - hcontext: &self.hcontext, - htimedelta: &self.htimedelta, - vsstatus: &self.vsstatus, - vsie: &self.vsie, - vstvec: &self.vstvec, - vsscratch: &self.vsscratch, - vsepc: &self.vsepc, - vscause: &self.vscause, - vstval: &self.vstval, - vsip: &self.vsip, - vsatp: &self.vsatp, - tselect: &self.tselect, - tdata1: &self.tdata1, - tdata2: &self.tdata2, - tdata3: &self.tdata3, - tcontrol: &self.tcontrol, - mcontext: &self.mcontext, - dcsr: &self.dcsr, - dpc: &self.dpc, - dscratch0: &self.dscratch0, - dscratch1: &self.dscratch1, - } - } - - /// Select a field representing a CSR value and apply a function to it, returing its result. - #[inline(always)] - fn select_ref( - &self, - csr: RootCSRegister, - fold_mstatus: impl FnOnce(&MStatus) -> R, - fold_mip: impl FnOnce(&MIP) -> R, - fold_raw: impl FnOnce(&Raw) -> R, - ) -> R { - match csr { - RootCSRegister::mstatus => fold_mstatus(&self.mstatus), - RootCSRegister::mip => fold_mip(&self.mip), - RootCSRegister::mnscratch => fold_raw(&self.mnscratch), - RootCSRegister::mnepc => fold_raw(&self.mnepc), - RootCSRegister::mncause => fold_raw(&self.mncause), - RootCSRegister::mnstatus => fold_raw(&self.mnstatus), - RootCSRegister::cycle => fold_raw(&self.cycle), - RootCSRegister::time => fold_raw(&self.time), - RootCSRegister::instret => fold_raw(&self.instret), - RootCSRegister::mcycle => fold_raw(&self.mcycle), - RootCSRegister::minstret => fold_raw(&self.minstret), - RootCSRegister::hpmcounter3 => fold_raw(&self.hpmcounter3), - RootCSRegister::hpmcounter4 => fold_raw(&self.hpmcounter4), - RootCSRegister::hpmcounter5 => fold_raw(&self.hpmcounter5), - RootCSRegister::hpmcounter6 => fold_raw(&self.hpmcounter6), - RootCSRegister::hpmcounter7 => fold_raw(&self.hpmcounter7), - RootCSRegister::hpmcounter8 => fold_raw(&self.hpmcounter8), - RootCSRegister::hpmcounter9 => fold_raw(&self.hpmcounter9), - RootCSRegister::hpmcounter10 => fold_raw(&self.hpmcounter10), - RootCSRegister::hpmcounter11 => fold_raw(&self.hpmcounter11), - RootCSRegister::hpmcounter12 => fold_raw(&self.hpmcounter12), - RootCSRegister::hpmcounter13 => fold_raw(&self.hpmcounter13), - RootCSRegister::hpmcounter14 => fold_raw(&self.hpmcounter14), - RootCSRegister::hpmcounter15 => fold_raw(&self.hpmcounter15), - RootCSRegister::hpmcounter16 => fold_raw(&self.hpmcounter16), - RootCSRegister::hpmcounter17 => fold_raw(&self.hpmcounter17), - RootCSRegister::hpmcounter18 => fold_raw(&self.hpmcounter18), - RootCSRegister::hpmcounter19 => fold_raw(&self.hpmcounter19), - RootCSRegister::hpmcounter20 => fold_raw(&self.hpmcounter20), - RootCSRegister::hpmcounter21 => fold_raw(&self.hpmcounter21), - RootCSRegister::hpmcounter22 => fold_raw(&self.hpmcounter22), - RootCSRegister::hpmcounter23 => fold_raw(&self.hpmcounter23), - RootCSRegister::hpmcounter24 => fold_raw(&self.hpmcounter24), - RootCSRegister::hpmcounter25 => fold_raw(&self.hpmcounter25), - RootCSRegister::hpmcounter26 => fold_raw(&self.hpmcounter26), - RootCSRegister::hpmcounter27 => fold_raw(&self.hpmcounter27), - RootCSRegister::hpmcounter28 => fold_raw(&self.hpmcounter28), - RootCSRegister::hpmcounter29 => fold_raw(&self.hpmcounter29), - RootCSRegister::hpmcounter30 => fold_raw(&self.hpmcounter30), - RootCSRegister::hpmcounter31 => fold_raw(&self.hpmcounter31), - RootCSRegister::mhpmcounter3 => fold_raw(&self.mhpmcounter3), - RootCSRegister::mhpmcounter4 => fold_raw(&self.mhpmcounter4), - RootCSRegister::mhpmcounter5 => fold_raw(&self.mhpmcounter5), - RootCSRegister::mhpmcounter6 => fold_raw(&self.mhpmcounter6), - RootCSRegister::mhpmcounter7 => fold_raw(&self.mhpmcounter7), - RootCSRegister::mhpmcounter8 => fold_raw(&self.mhpmcounter8), - RootCSRegister::mhpmcounter9 => fold_raw(&self.mhpmcounter9), - RootCSRegister::mhpmcounter10 => fold_raw(&self.mhpmcounter10), - RootCSRegister::mhpmcounter11 => fold_raw(&self.mhpmcounter11), - RootCSRegister::mhpmcounter12 => fold_raw(&self.mhpmcounter12), - RootCSRegister::mhpmcounter13 => fold_raw(&self.mhpmcounter13), - RootCSRegister::mhpmcounter14 => fold_raw(&self.mhpmcounter14), - RootCSRegister::mhpmcounter15 => fold_raw(&self.mhpmcounter15), - RootCSRegister::mhpmcounter16 => fold_raw(&self.mhpmcounter16), - RootCSRegister::mhpmcounter17 => fold_raw(&self.mhpmcounter17), - RootCSRegister::mhpmcounter18 => fold_raw(&self.mhpmcounter18), - RootCSRegister::mhpmcounter19 => fold_raw(&self.mhpmcounter19), - RootCSRegister::mhpmcounter20 => fold_raw(&self.mhpmcounter20), - RootCSRegister::mhpmcounter21 => fold_raw(&self.mhpmcounter21), - RootCSRegister::mhpmcounter22 => fold_raw(&self.mhpmcounter22), - RootCSRegister::mhpmcounter23 => fold_raw(&self.mhpmcounter23), - RootCSRegister::mhpmcounter24 => fold_raw(&self.mhpmcounter24), - RootCSRegister::mhpmcounter25 => fold_raw(&self.mhpmcounter25), - RootCSRegister::mhpmcounter26 => fold_raw(&self.mhpmcounter26), - RootCSRegister::mhpmcounter27 => fold_raw(&self.mhpmcounter27), - RootCSRegister::mhpmcounter28 => fold_raw(&self.mhpmcounter28), - RootCSRegister::mhpmcounter29 => fold_raw(&self.mhpmcounter29), - RootCSRegister::mhpmcounter30 => fold_raw(&self.mhpmcounter30), - RootCSRegister::mhpmcounter31 => fold_raw(&self.mhpmcounter31), - RootCSRegister::mhpmevent3 => fold_raw(&self.mhpmevent3), - RootCSRegister::mhpmevent4 => fold_raw(&self.mhpmevent4), - RootCSRegister::mhpmevent5 => fold_raw(&self.mhpmevent5), - RootCSRegister::mhpmevent6 => fold_raw(&self.mhpmevent6), - RootCSRegister::mhpmevent7 => fold_raw(&self.mhpmevent7), - RootCSRegister::mhpmevent8 => fold_raw(&self.mhpmevent8), - RootCSRegister::mhpmevent9 => fold_raw(&self.mhpmevent9), - RootCSRegister::mhpmevent10 => fold_raw(&self.mhpmevent10), - RootCSRegister::mhpmevent11 => fold_raw(&self.mhpmevent11), - RootCSRegister::mhpmevent12 => fold_raw(&self.mhpmevent12), - RootCSRegister::mhpmevent13 => fold_raw(&self.mhpmevent13), - RootCSRegister::mhpmevent14 => fold_raw(&self.mhpmevent14), - RootCSRegister::mhpmevent15 => fold_raw(&self.mhpmevent15), - RootCSRegister::mhpmevent16 => fold_raw(&self.mhpmevent16), - RootCSRegister::mhpmevent17 => fold_raw(&self.mhpmevent17), - RootCSRegister::mhpmevent18 => fold_raw(&self.mhpmevent18), - RootCSRegister::mhpmevent19 => fold_raw(&self.mhpmevent19), - RootCSRegister::mhpmevent20 => fold_raw(&self.mhpmevent20), - RootCSRegister::mhpmevent21 => fold_raw(&self.mhpmevent21), - RootCSRegister::mhpmevent22 => fold_raw(&self.mhpmevent22), - RootCSRegister::mhpmevent23 => fold_raw(&self.mhpmevent23), - RootCSRegister::mhpmevent24 => fold_raw(&self.mhpmevent24), - RootCSRegister::mhpmevent25 => fold_raw(&self.mhpmevent25), - RootCSRegister::mhpmevent26 => fold_raw(&self.mhpmevent26), - RootCSRegister::mhpmevent27 => fold_raw(&self.mhpmevent27), - RootCSRegister::mhpmevent28 => fold_raw(&self.mhpmevent28), - RootCSRegister::mhpmevent29 => fold_raw(&self.mhpmevent29), - RootCSRegister::mhpmevent30 => fold_raw(&self.mhpmevent30), - RootCSRegister::mhpmevent31 => fold_raw(&self.mhpmevent31), - RootCSRegister::mcountinhibit => fold_raw(&self.mcountinhibit), - RootCSRegister::scounteren => fold_raw(&self.scounteren), - RootCSRegister::mcounteren => fold_raw(&self.mcounteren), - RootCSRegister::fcsr => fold_raw(&self.fcsr), - RootCSRegister::pmpcfg0 => fold_raw(&self.pmpcfg0), - RootCSRegister::pmpcfg2 => fold_raw(&self.pmpcfg2), - RootCSRegister::pmpcfg4 => fold_raw(&self.pmpcfg4), - RootCSRegister::pmpcfg6 => fold_raw(&self.pmpcfg6), - RootCSRegister::pmpcfg8 => fold_raw(&self.pmpcfg8), - RootCSRegister::pmpcfg10 => fold_raw(&self.pmpcfg10), - RootCSRegister::pmpcfg12 => fold_raw(&self.pmpcfg12), - RootCSRegister::pmpcfg14 => fold_raw(&self.pmpcfg14), - RootCSRegister::pmpaddr0 => fold_raw(&self.pmpaddr0), - RootCSRegister::pmpaddr1 => fold_raw(&self.pmpaddr1), - RootCSRegister::pmpaddr2 => fold_raw(&self.pmpaddr2), - RootCSRegister::pmpaddr3 => fold_raw(&self.pmpaddr3), - RootCSRegister::pmpaddr4 => fold_raw(&self.pmpaddr4), - RootCSRegister::pmpaddr5 => fold_raw(&self.pmpaddr5), - RootCSRegister::pmpaddr6 => fold_raw(&self.pmpaddr6), - RootCSRegister::pmpaddr7 => fold_raw(&self.pmpaddr7), - RootCSRegister::pmpaddr8 => fold_raw(&self.pmpaddr8), - RootCSRegister::pmpaddr9 => fold_raw(&self.pmpaddr9), - RootCSRegister::pmpaddr10 => fold_raw(&self.pmpaddr10), - RootCSRegister::pmpaddr11 => fold_raw(&self.pmpaddr11), - RootCSRegister::pmpaddr12 => fold_raw(&self.pmpaddr12), - RootCSRegister::pmpaddr13 => fold_raw(&self.pmpaddr13), - RootCSRegister::pmpaddr14 => fold_raw(&self.pmpaddr14), - RootCSRegister::pmpaddr15 => fold_raw(&self.pmpaddr15), - RootCSRegister::pmpaddr16 => fold_raw(&self.pmpaddr16), - RootCSRegister::pmpaddr17 => fold_raw(&self.pmpaddr17), - RootCSRegister::pmpaddr18 => fold_raw(&self.pmpaddr18), - RootCSRegister::pmpaddr19 => fold_raw(&self.pmpaddr19), - RootCSRegister::pmpaddr20 => fold_raw(&self.pmpaddr20), - RootCSRegister::pmpaddr21 => fold_raw(&self.pmpaddr21), - RootCSRegister::pmpaddr22 => fold_raw(&self.pmpaddr22), - RootCSRegister::pmpaddr23 => fold_raw(&self.pmpaddr23), - RootCSRegister::pmpaddr24 => fold_raw(&self.pmpaddr24), - RootCSRegister::pmpaddr25 => fold_raw(&self.pmpaddr25), - RootCSRegister::pmpaddr26 => fold_raw(&self.pmpaddr26), - RootCSRegister::pmpaddr27 => fold_raw(&self.pmpaddr27), - RootCSRegister::pmpaddr28 => fold_raw(&self.pmpaddr28), - RootCSRegister::pmpaddr29 => fold_raw(&self.pmpaddr29), - RootCSRegister::pmpaddr30 => fold_raw(&self.pmpaddr30), - RootCSRegister::pmpaddr31 => fold_raw(&self.pmpaddr31), - RootCSRegister::pmpaddr32 => fold_raw(&self.pmpaddr32), - RootCSRegister::pmpaddr33 => fold_raw(&self.pmpaddr33), - RootCSRegister::pmpaddr34 => fold_raw(&self.pmpaddr34), - RootCSRegister::pmpaddr35 => fold_raw(&self.pmpaddr35), - RootCSRegister::pmpaddr36 => fold_raw(&self.pmpaddr36), - RootCSRegister::pmpaddr37 => fold_raw(&self.pmpaddr37), - RootCSRegister::pmpaddr38 => fold_raw(&self.pmpaddr38), - RootCSRegister::pmpaddr39 => fold_raw(&self.pmpaddr39), - RootCSRegister::pmpaddr40 => fold_raw(&self.pmpaddr40), - RootCSRegister::pmpaddr41 => fold_raw(&self.pmpaddr41), - RootCSRegister::pmpaddr42 => fold_raw(&self.pmpaddr42), - RootCSRegister::pmpaddr43 => fold_raw(&self.pmpaddr43), - RootCSRegister::pmpaddr44 => fold_raw(&self.pmpaddr44), - RootCSRegister::pmpaddr45 => fold_raw(&self.pmpaddr45), - RootCSRegister::pmpaddr46 => fold_raw(&self.pmpaddr46), - RootCSRegister::pmpaddr47 => fold_raw(&self.pmpaddr47), - RootCSRegister::pmpaddr48 => fold_raw(&self.pmpaddr48), - RootCSRegister::pmpaddr49 => fold_raw(&self.pmpaddr49), - RootCSRegister::pmpaddr50 => fold_raw(&self.pmpaddr50), - RootCSRegister::pmpaddr51 => fold_raw(&self.pmpaddr51), - RootCSRegister::pmpaddr52 => fold_raw(&self.pmpaddr52), - RootCSRegister::pmpaddr53 => fold_raw(&self.pmpaddr53), - RootCSRegister::pmpaddr54 => fold_raw(&self.pmpaddr54), - RootCSRegister::pmpaddr55 => fold_raw(&self.pmpaddr55), - RootCSRegister::pmpaddr56 => fold_raw(&self.pmpaddr56), - RootCSRegister::pmpaddr57 => fold_raw(&self.pmpaddr57), - RootCSRegister::pmpaddr58 => fold_raw(&self.pmpaddr58), - RootCSRegister::pmpaddr59 => fold_raw(&self.pmpaddr59), - RootCSRegister::pmpaddr60 => fold_raw(&self.pmpaddr60), - RootCSRegister::pmpaddr61 => fold_raw(&self.pmpaddr61), - RootCSRegister::pmpaddr62 => fold_raw(&self.pmpaddr62), - RootCSRegister::pmpaddr63 => fold_raw(&self.pmpaddr63), - RootCSRegister::mhartid => fold_raw(&self.mhartid), - RootCSRegister::mvendorid => fold_raw(&self.mvendorid), - RootCSRegister::marchid => fold_raw(&self.marchid), - RootCSRegister::mimpid => fold_raw(&self.mimpid), - RootCSRegister::misa => fold_raw(&self.misa), - RootCSRegister::mscratch => fold_raw(&self.mscratch), - RootCSRegister::sscratch => fold_raw(&self.sscratch), - RootCSRegister::stvec => fold_raw(&self.stvec), - RootCSRegister::mtvec => fold_raw(&self.mtvec), - RootCSRegister::mie => fold_raw(&self.mie), - RootCSRegister::satp => fold_raw(&self.satp), - RootCSRegister::scause => fold_raw(&self.scause), - RootCSRegister::mcause => fold_raw(&self.mcause), - RootCSRegister::sepc => fold_raw(&self.sepc), - RootCSRegister::mepc => fold_raw(&self.mepc), - RootCSRegister::stval => fold_raw(&self.stval), - RootCSRegister::mtval => fold_raw(&self.mtval), - RootCSRegister::mtval2 => fold_raw(&self.mtval2), - RootCSRegister::mtinst => fold_raw(&self.mtinst), - RootCSRegister::senvcfg => fold_raw(&self.senvcfg), - RootCSRegister::menvcfg => fold_raw(&self.menvcfg), - RootCSRegister::mconfigptr => fold_raw(&self.mconfigptr), - RootCSRegister::medeleg => fold_raw(&self.medeleg), - RootCSRegister::mideleg => fold_raw(&self.mideleg), - RootCSRegister::mseccfg => fold_raw(&self.mseccfg), - RootCSRegister::scontext => fold_raw(&self.scontext), - RootCSRegister::hstatus => fold_raw(&self.hstatus), - RootCSRegister::hedeleg => fold_raw(&self.hedeleg), - RootCSRegister::hideleg => fold_raw(&self.hideleg), - RootCSRegister::hie => fold_raw(&self.hie), - RootCSRegister::hcounteren => fold_raw(&self.hcounteren), - RootCSRegister::hgeie => fold_raw(&self.hgeie), - RootCSRegister::htval => fold_raw(&self.htval), - RootCSRegister::hip => fold_raw(&self.hip), - RootCSRegister::hvip => fold_raw(&self.hvip), - RootCSRegister::htinst => fold_raw(&self.htinst), - RootCSRegister::hgeip => fold_raw(&self.hgeip), - RootCSRegister::henvcfg => fold_raw(&self.henvcfg), - RootCSRegister::hgatp => fold_raw(&self.hgatp), - RootCSRegister::hcontext => fold_raw(&self.hcontext), - RootCSRegister::htimedelta => fold_raw(&self.htimedelta), - RootCSRegister::vsstatus => fold_raw(&self.vsstatus), - RootCSRegister::vsie => fold_raw(&self.vsie), - RootCSRegister::vstvec => fold_raw(&self.vstvec), - RootCSRegister::vsscratch => fold_raw(&self.vsscratch), - RootCSRegister::vsepc => fold_raw(&self.vsepc), - RootCSRegister::vscause => fold_raw(&self.vscause), - RootCSRegister::vstval => fold_raw(&self.vstval), - RootCSRegister::vsip => fold_raw(&self.vsip), - RootCSRegister::vsatp => fold_raw(&self.vsatp), - RootCSRegister::tselect => fold_raw(&self.tselect), - RootCSRegister::tdata1 => fold_raw(&self.tdata1), - RootCSRegister::tdata2 => fold_raw(&self.tdata2), - RootCSRegister::tdata3 => fold_raw(&self.tdata3), - RootCSRegister::tcontrol => fold_raw(&self.tcontrol), - RootCSRegister::mcontext => fold_raw(&self.mcontext), - RootCSRegister::dcsr => fold_raw(&self.dcsr), - RootCSRegister::dpc => fold_raw(&self.dpc), - RootCSRegister::dscratch0 => fold_raw(&self.dscratch0), - RootCSRegister::dscratch1 => fold_raw(&self.dscratch1), - } - } - - /// Select a field representing a CSR value and apply a function to it, returing its result. - #[inline(always)] - fn select_mut( - &mut self, - csr: RootCSRegister, - fold_mstatus: impl FnOnce(&mut MStatus) -> R, - fold_mip: impl FnOnce(&mut MIP) -> R, - fold_raw: impl FnOnce(&mut Raw) -> R, - ) -> R { - match csr { - RootCSRegister::mstatus => fold_mstatus(&mut self.mstatus), - RootCSRegister::mip => fold_mip(&mut self.mip), - RootCSRegister::mnscratch => fold_raw(&mut self.mnscratch), - RootCSRegister::mnepc => fold_raw(&mut self.mnepc), - RootCSRegister::mncause => fold_raw(&mut self.mncause), - RootCSRegister::mnstatus => fold_raw(&mut self.mnstatus), - RootCSRegister::cycle => fold_raw(&mut self.cycle), - RootCSRegister::time => fold_raw(&mut self.time), - RootCSRegister::instret => fold_raw(&mut self.instret), - RootCSRegister::mcycle => fold_raw(&mut self.mcycle), - RootCSRegister::minstret => fold_raw(&mut self.minstret), - RootCSRegister::hpmcounter3 => fold_raw(&mut self.hpmcounter3), - RootCSRegister::hpmcounter4 => fold_raw(&mut self.hpmcounter4), - RootCSRegister::hpmcounter5 => fold_raw(&mut self.hpmcounter5), - RootCSRegister::hpmcounter6 => fold_raw(&mut self.hpmcounter6), - RootCSRegister::hpmcounter7 => fold_raw(&mut self.hpmcounter7), - RootCSRegister::hpmcounter8 => fold_raw(&mut self.hpmcounter8), - RootCSRegister::hpmcounter9 => fold_raw(&mut self.hpmcounter9), - RootCSRegister::hpmcounter10 => fold_raw(&mut self.hpmcounter10), - RootCSRegister::hpmcounter11 => fold_raw(&mut self.hpmcounter11), - RootCSRegister::hpmcounter12 => fold_raw(&mut self.hpmcounter12), - RootCSRegister::hpmcounter13 => fold_raw(&mut self.hpmcounter13), - RootCSRegister::hpmcounter14 => fold_raw(&mut self.hpmcounter14), - RootCSRegister::hpmcounter15 => fold_raw(&mut self.hpmcounter15), - RootCSRegister::hpmcounter16 => fold_raw(&mut self.hpmcounter16), - RootCSRegister::hpmcounter17 => fold_raw(&mut self.hpmcounter17), - RootCSRegister::hpmcounter18 => fold_raw(&mut self.hpmcounter18), - RootCSRegister::hpmcounter19 => fold_raw(&mut self.hpmcounter19), - RootCSRegister::hpmcounter20 => fold_raw(&mut self.hpmcounter20), - RootCSRegister::hpmcounter21 => fold_raw(&mut self.hpmcounter21), - RootCSRegister::hpmcounter22 => fold_raw(&mut self.hpmcounter22), - RootCSRegister::hpmcounter23 => fold_raw(&mut self.hpmcounter23), - RootCSRegister::hpmcounter24 => fold_raw(&mut self.hpmcounter24), - RootCSRegister::hpmcounter25 => fold_raw(&mut self.hpmcounter25), - RootCSRegister::hpmcounter26 => fold_raw(&mut self.hpmcounter26), - RootCSRegister::hpmcounter27 => fold_raw(&mut self.hpmcounter27), - RootCSRegister::hpmcounter28 => fold_raw(&mut self.hpmcounter28), - RootCSRegister::hpmcounter29 => fold_raw(&mut self.hpmcounter29), - RootCSRegister::hpmcounter30 => fold_raw(&mut self.hpmcounter30), - RootCSRegister::hpmcounter31 => fold_raw(&mut self.hpmcounter31), - RootCSRegister::mhpmcounter3 => fold_raw(&mut self.mhpmcounter3), - RootCSRegister::mhpmcounter4 => fold_raw(&mut self.mhpmcounter4), - RootCSRegister::mhpmcounter5 => fold_raw(&mut self.mhpmcounter5), - RootCSRegister::mhpmcounter6 => fold_raw(&mut self.mhpmcounter6), - RootCSRegister::mhpmcounter7 => fold_raw(&mut self.mhpmcounter7), - RootCSRegister::mhpmcounter8 => fold_raw(&mut self.mhpmcounter8), - RootCSRegister::mhpmcounter9 => fold_raw(&mut self.mhpmcounter9), - RootCSRegister::mhpmcounter10 => fold_raw(&mut self.mhpmcounter10), - RootCSRegister::mhpmcounter11 => fold_raw(&mut self.mhpmcounter11), - RootCSRegister::mhpmcounter12 => fold_raw(&mut self.mhpmcounter12), - RootCSRegister::mhpmcounter13 => fold_raw(&mut self.mhpmcounter13), - RootCSRegister::mhpmcounter14 => fold_raw(&mut self.mhpmcounter14), - RootCSRegister::mhpmcounter15 => fold_raw(&mut self.mhpmcounter15), - RootCSRegister::mhpmcounter16 => fold_raw(&mut self.mhpmcounter16), - RootCSRegister::mhpmcounter17 => fold_raw(&mut self.mhpmcounter17), - RootCSRegister::mhpmcounter18 => fold_raw(&mut self.mhpmcounter18), - RootCSRegister::mhpmcounter19 => fold_raw(&mut self.mhpmcounter19), - RootCSRegister::mhpmcounter20 => fold_raw(&mut self.mhpmcounter20), - RootCSRegister::mhpmcounter21 => fold_raw(&mut self.mhpmcounter21), - RootCSRegister::mhpmcounter22 => fold_raw(&mut self.mhpmcounter22), - RootCSRegister::mhpmcounter23 => fold_raw(&mut self.mhpmcounter23), - RootCSRegister::mhpmcounter24 => fold_raw(&mut self.mhpmcounter24), - RootCSRegister::mhpmcounter25 => fold_raw(&mut self.mhpmcounter25), - RootCSRegister::mhpmcounter26 => fold_raw(&mut self.mhpmcounter26), - RootCSRegister::mhpmcounter27 => fold_raw(&mut self.mhpmcounter27), - RootCSRegister::mhpmcounter28 => fold_raw(&mut self.mhpmcounter28), - RootCSRegister::mhpmcounter29 => fold_raw(&mut self.mhpmcounter29), - RootCSRegister::mhpmcounter30 => fold_raw(&mut self.mhpmcounter30), - RootCSRegister::mhpmcounter31 => fold_raw(&mut self.mhpmcounter31), - RootCSRegister::mhpmevent3 => fold_raw(&mut self.mhpmevent3), - RootCSRegister::mhpmevent4 => fold_raw(&mut self.mhpmevent4), - RootCSRegister::mhpmevent5 => fold_raw(&mut self.mhpmevent5), - RootCSRegister::mhpmevent6 => fold_raw(&mut self.mhpmevent6), - RootCSRegister::mhpmevent7 => fold_raw(&mut self.mhpmevent7), - RootCSRegister::mhpmevent8 => fold_raw(&mut self.mhpmevent8), - RootCSRegister::mhpmevent9 => fold_raw(&mut self.mhpmevent9), - RootCSRegister::mhpmevent10 => fold_raw(&mut self.mhpmevent10), - RootCSRegister::mhpmevent11 => fold_raw(&mut self.mhpmevent11), - RootCSRegister::mhpmevent12 => fold_raw(&mut self.mhpmevent12), - RootCSRegister::mhpmevent13 => fold_raw(&mut self.mhpmevent13), - RootCSRegister::mhpmevent14 => fold_raw(&mut self.mhpmevent14), - RootCSRegister::mhpmevent15 => fold_raw(&mut self.mhpmevent15), - RootCSRegister::mhpmevent16 => fold_raw(&mut self.mhpmevent16), - RootCSRegister::mhpmevent17 => fold_raw(&mut self.mhpmevent17), - RootCSRegister::mhpmevent18 => fold_raw(&mut self.mhpmevent18), - RootCSRegister::mhpmevent19 => fold_raw(&mut self.mhpmevent19), - RootCSRegister::mhpmevent20 => fold_raw(&mut self.mhpmevent20), - RootCSRegister::mhpmevent21 => fold_raw(&mut self.mhpmevent21), - RootCSRegister::mhpmevent22 => fold_raw(&mut self.mhpmevent22), - RootCSRegister::mhpmevent23 => fold_raw(&mut self.mhpmevent23), - RootCSRegister::mhpmevent24 => fold_raw(&mut self.mhpmevent24), - RootCSRegister::mhpmevent25 => fold_raw(&mut self.mhpmevent25), - RootCSRegister::mhpmevent26 => fold_raw(&mut self.mhpmevent26), - RootCSRegister::mhpmevent27 => fold_raw(&mut self.mhpmevent27), - RootCSRegister::mhpmevent28 => fold_raw(&mut self.mhpmevent28), - RootCSRegister::mhpmevent29 => fold_raw(&mut self.mhpmevent29), - RootCSRegister::mhpmevent30 => fold_raw(&mut self.mhpmevent30), - RootCSRegister::mhpmevent31 => fold_raw(&mut self.mhpmevent31), - RootCSRegister::mcountinhibit => fold_raw(&mut self.mcountinhibit), - RootCSRegister::scounteren => fold_raw(&mut self.scounteren), - RootCSRegister::mcounteren => fold_raw(&mut self.mcounteren), - RootCSRegister::fcsr => fold_raw(&mut self.fcsr), - RootCSRegister::pmpcfg0 => fold_raw(&mut self.pmpcfg0), - RootCSRegister::pmpcfg2 => fold_raw(&mut self.pmpcfg2), - RootCSRegister::pmpcfg4 => fold_raw(&mut self.pmpcfg4), - RootCSRegister::pmpcfg6 => fold_raw(&mut self.pmpcfg6), - RootCSRegister::pmpcfg8 => fold_raw(&mut self.pmpcfg8), - RootCSRegister::pmpcfg10 => fold_raw(&mut self.pmpcfg10), - RootCSRegister::pmpcfg12 => fold_raw(&mut self.pmpcfg12), - RootCSRegister::pmpcfg14 => fold_raw(&mut self.pmpcfg14), - RootCSRegister::pmpaddr0 => fold_raw(&mut self.pmpaddr0), - RootCSRegister::pmpaddr1 => fold_raw(&mut self.pmpaddr1), - RootCSRegister::pmpaddr2 => fold_raw(&mut self.pmpaddr2), - RootCSRegister::pmpaddr3 => fold_raw(&mut self.pmpaddr3), - RootCSRegister::pmpaddr4 => fold_raw(&mut self.pmpaddr4), - RootCSRegister::pmpaddr5 => fold_raw(&mut self.pmpaddr5), - RootCSRegister::pmpaddr6 => fold_raw(&mut self.pmpaddr6), - RootCSRegister::pmpaddr7 => fold_raw(&mut self.pmpaddr7), - RootCSRegister::pmpaddr8 => fold_raw(&mut self.pmpaddr8), - RootCSRegister::pmpaddr9 => fold_raw(&mut self.pmpaddr9), - RootCSRegister::pmpaddr10 => fold_raw(&mut self.pmpaddr10), - RootCSRegister::pmpaddr11 => fold_raw(&mut self.pmpaddr11), - RootCSRegister::pmpaddr12 => fold_raw(&mut self.pmpaddr12), - RootCSRegister::pmpaddr13 => fold_raw(&mut self.pmpaddr13), - RootCSRegister::pmpaddr14 => fold_raw(&mut self.pmpaddr14), - RootCSRegister::pmpaddr15 => fold_raw(&mut self.pmpaddr15), - RootCSRegister::pmpaddr16 => fold_raw(&mut self.pmpaddr16), - RootCSRegister::pmpaddr17 => fold_raw(&mut self.pmpaddr17), - RootCSRegister::pmpaddr18 => fold_raw(&mut self.pmpaddr18), - RootCSRegister::pmpaddr19 => fold_raw(&mut self.pmpaddr19), - RootCSRegister::pmpaddr20 => fold_raw(&mut self.pmpaddr20), - RootCSRegister::pmpaddr21 => fold_raw(&mut self.pmpaddr21), - RootCSRegister::pmpaddr22 => fold_raw(&mut self.pmpaddr22), - RootCSRegister::pmpaddr23 => fold_raw(&mut self.pmpaddr23), - RootCSRegister::pmpaddr24 => fold_raw(&mut self.pmpaddr24), - RootCSRegister::pmpaddr25 => fold_raw(&mut self.pmpaddr25), - RootCSRegister::pmpaddr26 => fold_raw(&mut self.pmpaddr26), - RootCSRegister::pmpaddr27 => fold_raw(&mut self.pmpaddr27), - RootCSRegister::pmpaddr28 => fold_raw(&mut self.pmpaddr28), - RootCSRegister::pmpaddr29 => fold_raw(&mut self.pmpaddr29), - RootCSRegister::pmpaddr30 => fold_raw(&mut self.pmpaddr30), - RootCSRegister::pmpaddr31 => fold_raw(&mut self.pmpaddr31), - RootCSRegister::pmpaddr32 => fold_raw(&mut self.pmpaddr32), - RootCSRegister::pmpaddr33 => fold_raw(&mut self.pmpaddr33), - RootCSRegister::pmpaddr34 => fold_raw(&mut self.pmpaddr34), - RootCSRegister::pmpaddr35 => fold_raw(&mut self.pmpaddr35), - RootCSRegister::pmpaddr36 => fold_raw(&mut self.pmpaddr36), - RootCSRegister::pmpaddr37 => fold_raw(&mut self.pmpaddr37), - RootCSRegister::pmpaddr38 => fold_raw(&mut self.pmpaddr38), - RootCSRegister::pmpaddr39 => fold_raw(&mut self.pmpaddr39), - RootCSRegister::pmpaddr40 => fold_raw(&mut self.pmpaddr40), - RootCSRegister::pmpaddr41 => fold_raw(&mut self.pmpaddr41), - RootCSRegister::pmpaddr42 => fold_raw(&mut self.pmpaddr42), - RootCSRegister::pmpaddr43 => fold_raw(&mut self.pmpaddr43), - RootCSRegister::pmpaddr44 => fold_raw(&mut self.pmpaddr44), - RootCSRegister::pmpaddr45 => fold_raw(&mut self.pmpaddr45), - RootCSRegister::pmpaddr46 => fold_raw(&mut self.pmpaddr46), - RootCSRegister::pmpaddr47 => fold_raw(&mut self.pmpaddr47), - RootCSRegister::pmpaddr48 => fold_raw(&mut self.pmpaddr48), - RootCSRegister::pmpaddr49 => fold_raw(&mut self.pmpaddr49), - RootCSRegister::pmpaddr50 => fold_raw(&mut self.pmpaddr50), - RootCSRegister::pmpaddr51 => fold_raw(&mut self.pmpaddr51), - RootCSRegister::pmpaddr52 => fold_raw(&mut self.pmpaddr52), - RootCSRegister::pmpaddr53 => fold_raw(&mut self.pmpaddr53), - RootCSRegister::pmpaddr54 => fold_raw(&mut self.pmpaddr54), - RootCSRegister::pmpaddr55 => fold_raw(&mut self.pmpaddr55), - RootCSRegister::pmpaddr56 => fold_raw(&mut self.pmpaddr56), - RootCSRegister::pmpaddr57 => fold_raw(&mut self.pmpaddr57), - RootCSRegister::pmpaddr58 => fold_raw(&mut self.pmpaddr58), - RootCSRegister::pmpaddr59 => fold_raw(&mut self.pmpaddr59), - RootCSRegister::pmpaddr60 => fold_raw(&mut self.pmpaddr60), - RootCSRegister::pmpaddr61 => fold_raw(&mut self.pmpaddr61), - RootCSRegister::pmpaddr62 => fold_raw(&mut self.pmpaddr62), - RootCSRegister::pmpaddr63 => fold_raw(&mut self.pmpaddr63), - RootCSRegister::mhartid => fold_raw(&mut self.mhartid), - RootCSRegister::mvendorid => fold_raw(&mut self.mvendorid), - RootCSRegister::marchid => fold_raw(&mut self.marchid), - RootCSRegister::mimpid => fold_raw(&mut self.mimpid), - RootCSRegister::misa => fold_raw(&mut self.misa), - RootCSRegister::mscratch => fold_raw(&mut self.mscratch), - RootCSRegister::sscratch => fold_raw(&mut self.sscratch), - RootCSRegister::stvec => fold_raw(&mut self.stvec), - RootCSRegister::mtvec => fold_raw(&mut self.mtvec), - RootCSRegister::mie => fold_raw(&mut self.mie), - RootCSRegister::satp => fold_raw(&mut self.satp), - RootCSRegister::scause => fold_raw(&mut self.scause), - RootCSRegister::mcause => fold_raw(&mut self.mcause), - RootCSRegister::sepc => fold_raw(&mut self.sepc), - RootCSRegister::mepc => fold_raw(&mut self.mepc), - RootCSRegister::stval => fold_raw(&mut self.stval), - RootCSRegister::mtval => fold_raw(&mut self.mtval), - RootCSRegister::mtval2 => fold_raw(&mut self.mtval2), - RootCSRegister::mtinst => fold_raw(&mut self.mtinst), - RootCSRegister::senvcfg => fold_raw(&mut self.senvcfg), - RootCSRegister::menvcfg => fold_raw(&mut self.menvcfg), - RootCSRegister::mconfigptr => fold_raw(&mut self.mconfigptr), - RootCSRegister::medeleg => fold_raw(&mut self.medeleg), - RootCSRegister::mideleg => fold_raw(&mut self.mideleg), - RootCSRegister::mseccfg => fold_raw(&mut self.mseccfg), - RootCSRegister::scontext => fold_raw(&mut self.scontext), - RootCSRegister::hstatus => fold_raw(&mut self.hstatus), - RootCSRegister::hedeleg => fold_raw(&mut self.hedeleg), - RootCSRegister::hideleg => fold_raw(&mut self.hideleg), - RootCSRegister::hie => fold_raw(&mut self.hie), - RootCSRegister::hcounteren => fold_raw(&mut self.hcounteren), - RootCSRegister::hgeie => fold_raw(&mut self.hgeie), - RootCSRegister::htval => fold_raw(&mut self.htval), - RootCSRegister::hip => fold_raw(&mut self.hip), - RootCSRegister::hvip => fold_raw(&mut self.hvip), - RootCSRegister::htinst => fold_raw(&mut self.htinst), - RootCSRegister::hgeip => fold_raw(&mut self.hgeip), - RootCSRegister::henvcfg => fold_raw(&mut self.henvcfg), - RootCSRegister::hgatp => fold_raw(&mut self.hgatp), - RootCSRegister::hcontext => fold_raw(&mut self.hcontext), - RootCSRegister::htimedelta => fold_raw(&mut self.htimedelta), - RootCSRegister::vsstatus => fold_raw(&mut self.vsstatus), - RootCSRegister::vsie => fold_raw(&mut self.vsie), - RootCSRegister::vstvec => fold_raw(&mut self.vstvec), - RootCSRegister::vsscratch => fold_raw(&mut self.vsscratch), - RootCSRegister::vsepc => fold_raw(&mut self.vsepc), - RootCSRegister::vscause => fold_raw(&mut self.vscause), - RootCSRegister::vstval => fold_raw(&mut self.vstval), - RootCSRegister::vsip => fold_raw(&mut self.vsip), - RootCSRegister::vsatp => fold_raw(&mut self.vsatp), - RootCSRegister::tselect => fold_raw(&mut self.tselect), - RootCSRegister::tdata1 => fold_raw(&mut self.tdata1), - RootCSRegister::tdata2 => fold_raw(&mut self.tdata2), - RootCSRegister::tdata3 => fold_raw(&mut self.tdata3), - RootCSRegister::tcontrol => fold_raw(&mut self.tcontrol), - RootCSRegister::mcontext => fold_raw(&mut self.mcontext), - RootCSRegister::dcsr => fold_raw(&mut self.dcsr), - RootCSRegister::dpc => fold_raw(&mut self.dpc), - RootCSRegister::dscratch0 => fold_raw(&mut self.dscratch0), - RootCSRegister::dscratch1 => fold_raw(&mut self.dscratch1), - } - } -} - -impl AccessInfoAggregatable for CSRValuesF -where - Raw: AccessInfoAggregatable + serde::Serialize, - MStatus: AccessInfoAggregatable + serde::Serialize, - MIP: AccessInfoAggregatable + serde::Serialize, -{ - fn aggregate_access_info(&self) -> bool { - let children = [ - self.mstatus.aggregate_access_info(), - self.mip.aggregate_access_info(), - self.mnscratch.aggregate_access_info(), - self.mnepc.aggregate_access_info(), - self.mncause.aggregate_access_info(), - self.mnstatus.aggregate_access_info(), - self.cycle.aggregate_access_info(), - self.time.aggregate_access_info(), - self.instret.aggregate_access_info(), - self.mcycle.aggregate_access_info(), - self.minstret.aggregate_access_info(), - self.hpmcounter3.aggregate_access_info(), - self.hpmcounter4.aggregate_access_info(), - self.hpmcounter5.aggregate_access_info(), - self.hpmcounter6.aggregate_access_info(), - self.hpmcounter7.aggregate_access_info(), - self.hpmcounter8.aggregate_access_info(), - self.hpmcounter9.aggregate_access_info(), - self.hpmcounter10.aggregate_access_info(), - self.hpmcounter11.aggregate_access_info(), - self.hpmcounter12.aggregate_access_info(), - self.hpmcounter13.aggregate_access_info(), - self.hpmcounter14.aggregate_access_info(), - self.hpmcounter15.aggregate_access_info(), - self.hpmcounter16.aggregate_access_info(), - self.hpmcounter17.aggregate_access_info(), - self.hpmcounter18.aggregate_access_info(), - self.hpmcounter19.aggregate_access_info(), - self.hpmcounter20.aggregate_access_info(), - self.hpmcounter21.aggregate_access_info(), - self.hpmcounter22.aggregate_access_info(), - self.hpmcounter23.aggregate_access_info(), - self.hpmcounter24.aggregate_access_info(), - self.hpmcounter25.aggregate_access_info(), - self.hpmcounter26.aggregate_access_info(), - self.hpmcounter27.aggregate_access_info(), - self.hpmcounter28.aggregate_access_info(), - self.hpmcounter29.aggregate_access_info(), - self.hpmcounter30.aggregate_access_info(), - self.hpmcounter31.aggregate_access_info(), - self.mhpmcounter3.aggregate_access_info(), - self.mhpmcounter4.aggregate_access_info(), - self.mhpmcounter5.aggregate_access_info(), - self.mhpmcounter6.aggregate_access_info(), - self.mhpmcounter7.aggregate_access_info(), - self.mhpmcounter8.aggregate_access_info(), - self.mhpmcounter9.aggregate_access_info(), - self.mhpmcounter10.aggregate_access_info(), - self.mhpmcounter11.aggregate_access_info(), - self.mhpmcounter12.aggregate_access_info(), - self.mhpmcounter13.aggregate_access_info(), - self.mhpmcounter14.aggregate_access_info(), - self.mhpmcounter15.aggregate_access_info(), - self.mhpmcounter16.aggregate_access_info(), - self.mhpmcounter17.aggregate_access_info(), - self.mhpmcounter18.aggregate_access_info(), - self.mhpmcounter19.aggregate_access_info(), - self.mhpmcounter20.aggregate_access_info(), - self.mhpmcounter21.aggregate_access_info(), - self.mhpmcounter22.aggregate_access_info(), - self.mhpmcounter23.aggregate_access_info(), - self.mhpmcounter24.aggregate_access_info(), - self.mhpmcounter25.aggregate_access_info(), - self.mhpmcounter26.aggregate_access_info(), - self.mhpmcounter27.aggregate_access_info(), - self.mhpmcounter28.aggregate_access_info(), - self.mhpmcounter29.aggregate_access_info(), - self.mhpmcounter30.aggregate_access_info(), - self.mhpmcounter31.aggregate_access_info(), - self.mhpmevent3.aggregate_access_info(), - self.mhpmevent4.aggregate_access_info(), - self.mhpmevent5.aggregate_access_info(), - self.mhpmevent6.aggregate_access_info(), - self.mhpmevent7.aggregate_access_info(), - self.mhpmevent8.aggregate_access_info(), - self.mhpmevent9.aggregate_access_info(), - self.mhpmevent10.aggregate_access_info(), - self.mhpmevent11.aggregate_access_info(), - self.mhpmevent12.aggregate_access_info(), - self.mhpmevent13.aggregate_access_info(), - self.mhpmevent14.aggregate_access_info(), - self.mhpmevent15.aggregate_access_info(), - self.mhpmevent16.aggregate_access_info(), - self.mhpmevent17.aggregate_access_info(), - self.mhpmevent18.aggregate_access_info(), - self.mhpmevent19.aggregate_access_info(), - self.mhpmevent20.aggregate_access_info(), - self.mhpmevent21.aggregate_access_info(), - self.mhpmevent22.aggregate_access_info(), - self.mhpmevent23.aggregate_access_info(), - self.mhpmevent24.aggregate_access_info(), - self.mhpmevent25.aggregate_access_info(), - self.mhpmevent26.aggregate_access_info(), - self.mhpmevent27.aggregate_access_info(), - self.mhpmevent28.aggregate_access_info(), - self.mhpmevent29.aggregate_access_info(), - self.mhpmevent30.aggregate_access_info(), - self.mhpmevent31.aggregate_access_info(), - self.mcountinhibit.aggregate_access_info(), - self.scounteren.aggregate_access_info(), - self.mcounteren.aggregate_access_info(), - self.fcsr.aggregate_access_info(), - self.pmpcfg0.aggregate_access_info(), - self.pmpcfg2.aggregate_access_info(), - self.pmpcfg4.aggregate_access_info(), - self.pmpcfg6.aggregate_access_info(), - self.pmpcfg8.aggregate_access_info(), - self.pmpcfg10.aggregate_access_info(), - self.pmpcfg12.aggregate_access_info(), - self.pmpcfg14.aggregate_access_info(), - self.pmpaddr0.aggregate_access_info(), - self.pmpaddr1.aggregate_access_info(), - self.pmpaddr2.aggregate_access_info(), - self.pmpaddr3.aggregate_access_info(), - self.pmpaddr4.aggregate_access_info(), - self.pmpaddr5.aggregate_access_info(), - self.pmpaddr6.aggregate_access_info(), - self.pmpaddr7.aggregate_access_info(), - self.pmpaddr8.aggregate_access_info(), - self.pmpaddr9.aggregate_access_info(), - self.pmpaddr10.aggregate_access_info(), - self.pmpaddr11.aggregate_access_info(), - self.pmpaddr12.aggregate_access_info(), - self.pmpaddr13.aggregate_access_info(), - self.pmpaddr14.aggregate_access_info(), - self.pmpaddr15.aggregate_access_info(), - self.pmpaddr16.aggregate_access_info(), - self.pmpaddr17.aggregate_access_info(), - self.pmpaddr18.aggregate_access_info(), - self.pmpaddr19.aggregate_access_info(), - self.pmpaddr20.aggregate_access_info(), - self.pmpaddr21.aggregate_access_info(), - self.pmpaddr22.aggregate_access_info(), - self.pmpaddr23.aggregate_access_info(), - self.pmpaddr24.aggregate_access_info(), - self.pmpaddr25.aggregate_access_info(), - self.pmpaddr26.aggregate_access_info(), - self.pmpaddr27.aggregate_access_info(), - self.pmpaddr28.aggregate_access_info(), - self.pmpaddr29.aggregate_access_info(), - self.pmpaddr30.aggregate_access_info(), - self.pmpaddr31.aggregate_access_info(), - self.pmpaddr32.aggregate_access_info(), - self.pmpaddr33.aggregate_access_info(), - self.pmpaddr34.aggregate_access_info(), - self.pmpaddr35.aggregate_access_info(), - self.pmpaddr36.aggregate_access_info(), - self.pmpaddr37.aggregate_access_info(), - self.pmpaddr38.aggregate_access_info(), - self.pmpaddr39.aggregate_access_info(), - self.pmpaddr40.aggregate_access_info(), - self.pmpaddr41.aggregate_access_info(), - self.pmpaddr42.aggregate_access_info(), - self.pmpaddr43.aggregate_access_info(), - self.pmpaddr44.aggregate_access_info(), - self.pmpaddr45.aggregate_access_info(), - self.pmpaddr46.aggregate_access_info(), - self.pmpaddr47.aggregate_access_info(), - self.pmpaddr48.aggregate_access_info(), - self.pmpaddr49.aggregate_access_info(), - self.pmpaddr50.aggregate_access_info(), - self.pmpaddr51.aggregate_access_info(), - self.pmpaddr52.aggregate_access_info(), - self.pmpaddr53.aggregate_access_info(), - self.pmpaddr54.aggregate_access_info(), - self.pmpaddr55.aggregate_access_info(), - self.pmpaddr56.aggregate_access_info(), - self.pmpaddr57.aggregate_access_info(), - self.pmpaddr58.aggregate_access_info(), - self.pmpaddr59.aggregate_access_info(), - self.pmpaddr60.aggregate_access_info(), - self.pmpaddr61.aggregate_access_info(), - self.pmpaddr62.aggregate_access_info(), - self.pmpaddr63.aggregate_access_info(), - self.mhartid.aggregate_access_info(), - self.mvendorid.aggregate_access_info(), - self.marchid.aggregate_access_info(), - self.mimpid.aggregate_access_info(), - self.misa.aggregate_access_info(), - self.mscratch.aggregate_access_info(), - self.sscratch.aggregate_access_info(), - self.stvec.aggregate_access_info(), - self.mtvec.aggregate_access_info(), - self.mie.aggregate_access_info(), - self.satp.aggregate_access_info(), - self.scause.aggregate_access_info(), - self.mcause.aggregate_access_info(), - self.sepc.aggregate_access_info(), - self.mepc.aggregate_access_info(), - self.stval.aggregate_access_info(), - self.mtval.aggregate_access_info(), - self.mtval2.aggregate_access_info(), - self.mtinst.aggregate_access_info(), - self.senvcfg.aggregate_access_info(), - self.menvcfg.aggregate_access_info(), - self.mconfigptr.aggregate_access_info(), - self.medeleg.aggregate_access_info(), - self.mideleg.aggregate_access_info(), - self.mseccfg.aggregate_access_info(), - self.scontext.aggregate_access_info(), - self.hstatus.aggregate_access_info(), - self.hedeleg.aggregate_access_info(), - self.hideleg.aggregate_access_info(), - self.hie.aggregate_access_info(), - self.hcounteren.aggregate_access_info(), - self.hgeie.aggregate_access_info(), - self.htval.aggregate_access_info(), - self.hip.aggregate_access_info(), - self.hvip.aggregate_access_info(), - self.htinst.aggregate_access_info(), - self.hgeip.aggregate_access_info(), - self.henvcfg.aggregate_access_info(), - self.hgatp.aggregate_access_info(), - self.hcontext.aggregate_access_info(), - self.htimedelta.aggregate_access_info(), - self.vsstatus.aggregate_access_info(), - self.vsie.aggregate_access_info(), - self.vstvec.aggregate_access_info(), - self.vsscratch.aggregate_access_info(), - self.vsepc.aggregate_access_info(), - self.vscause.aggregate_access_info(), - self.vstval.aggregate_access_info(), - self.vsip.aggregate_access_info(), - self.vsatp.aggregate_access_info(), - self.tselect.aggregate_access_info(), - self.tdata1.aggregate_access_info(), - self.tdata2.aggregate_access_info(), - self.tdata3.aggregate_access_info(), - self.tcontrol.aggregate_access_info(), - self.mcontext.aggregate_access_info(), - self.dcsr.aggregate_access_info(), - self.dpc.aggregate_access_info(), - self.dscratch0.aggregate_access_info(), - self.dscratch1.aggregate_access_info(), - ]; - children.iter().any(|&x| x) - } -} - -#[cfg(test)] -mod tests { - use std::sync::atomic::AtomicUsize; - use std::sync::atomic::Ordering; - - use strum::IntoEnumIterator; - - use super::*; - - /// Ensure that [`CSRValues::fold_ref`] and [`CSRValuesF::fold_mut`] refer to the same CSR - /// value field provided the same [`RootCSRegister`]. - #[test] - fn fold_ref_mut_consistent() { - let counter = AtomicUsize::new(0); - - let mut example = CSRValuesF::new_with( - || counter.fetch_add(1, Ordering::SeqCst), - || counter.fetch_add(1, Ordering::SeqCst), - || counter.fetch_add(1, Ordering::SeqCst), - ); - - for csr in RootCSRegister::iter() { - let lhs = example.select_ref(csr, |x| *x, |x| *x, |x| *x); - let rhs = example.select_mut(csr, |x| *x, |x| *x, |x| *x); - assert_eq!(lhs, rhs); - } - } - - /// Ensure that [`CSRValues::as_ref`] obtained correct references. - #[test] - fn as_ref_consistent() { - let counter = AtomicUsize::new(0); - - let example = CSRValuesF::new_with( - || counter.fetch_add(1, Ordering::SeqCst), - || counter.fetch_add(1, Ordering::SeqCst), - || counter.fetch_add(1, Ordering::SeqCst), - ); - - let example_copy = example.as_ref().map(|x| *x, |x| *x, |x| *x); - - assert_eq!(example, example_copy); - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs b/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs deleted file mode 100644 index bda60126c51ae3588e685985aa1862900533efd7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/values/mstatus.rs +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::CSRRepr; -use crate::bits::Bits64; -use crate::bits::ConstantBits; -use crate::machine_state::csregisters::effects::CSREffect; -use crate::machine_state::csregisters::effects::XieEffect; -use crate::machine_state::csregisters::xstatus::ExtensionValue; -use crate::machine_state::csregisters::xstatus::MPPValue; -use crate::machine_state::csregisters::xstatus::MStatus; -use crate::machine_state::csregisters::xstatus::SPPValue; -use crate::machine_state::csregisters::xstatus::XLenValue; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::EffectCell; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; -use crate::state_backend::Ref; -use crate::struct_layout; - -/// RISCV CSRegister.mstatus register state. -/// Accounts for CSR rules like WPRI, WARL, WLRL. -/// Contains only real fields (no shadows) hence it is a public field in [`super::CSRValues`] -pub struct MStatusValue { - // Individual fields can be public since they are well typed and respect the WPRI, WARL, WLRL rules. - // Except for fields which have side-effects. These ones have custom read/write/replace methods - // to return side-effects to be accounted for - pub sie: EffectCell, - pub mie: EffectCell, - pub spie: Cell, - pub ube: Cell, - pub mpie: Cell, - pub spp: Cell, - pub mpp: Cell, - pub fs: Cell, - pub xs: Cell, - // vs is always OFF as we do not support the virtualisation extension - pub mprv: Cell, - pub sum: Cell, - pub mxr: Cell, - pub tvm: Cell, - pub tw: Cell, - pub tsr: Cell, - pub uxl: Cell, - pub sxl: Cell, - pub sbe: Cell, - pub mbe: Cell, -} - -impl MStatusValue { - pub fn bind(space: AllocatedOf) -> Self { - Self { - sie: EffectCell::bind(space.sie), - mie: EffectCell::bind(space.mie), - spie: space.spie, - ube: space.ube, - mpie: space.mpie, - spp: space.spp, - mpp: space.mpp, - fs: space.fs, - xs: space.xs, - mprv: space.mprv, - sum: space.sum, - mxr: space.mxr, - tvm: space.tvm, - tw: space.tw, - tsr: space.tsr, - uxl: space.uxl, - sxl: space.sxl, - sbe: space.sbe, - mbe: space.mbe, - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf { - MStatusLayoutF { - sie: self.sie.struct_ref::(), - mie: self.mie.struct_ref::(), - spie: self.spie.struct_ref::(), - ube: self.ube.struct_ref::(), - mpie: self.mpie.struct_ref::(), - spp: self.spp.struct_ref::(), - mpp: self.mpp.struct_ref::(), - fs: self.fs.struct_ref::(), - xs: self.xs.struct_ref::(), - mprv: self.mprv.struct_ref::(), - sum: self.sum.struct_ref::(), - mxr: self.mxr.struct_ref::(), - tvm: self.tvm.struct_ref::(), - tw: self.tw.struct_ref::(), - tsr: self.tsr.struct_ref::(), - uxl: self.uxl.struct_ref::(), - sxl: self.sxl.struct_ref::(), - sbe: self.sbe.struct_ref::(), - mbe: self.mbe.struct_ref::(), - } - } -} - -impl NewState for MStatusValue { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - MStatusValue { - sie: EffectCell::new(manager), - mie: EffectCell::new(manager), - spie: Cell::new(manager), - ube: Cell::new(manager), - mpie: Cell::new(manager), - spp: Cell::new(manager), - mpp: Cell::new(manager), - fs: Cell::new(manager), - xs: Cell::new(manager), - mprv: Cell::new(manager), - sum: Cell::new(manager), - mxr: Cell::new(manager), - tvm: Cell::new(manager), - tw: Cell::new(manager), - tsr: Cell::new(manager), - uxl: Cell::new(manager), - sxl: Cell::new(manager), - sbe: Cell::new(manager), - mbe: Cell::new(manager), - } - } -} - -impl Clone for MStatusValue { - fn clone(&self) -> Self { - Self { - sie: self.sie.clone(), - mie: self.mie.clone(), - spie: self.spie.clone(), - ube: self.ube.clone(), - mpie: self.mpie.clone(), - spp: self.spp.clone(), - mpp: self.mpp.clone(), - fs: self.fs.clone(), - xs: self.xs.clone(), - mprv: self.mprv.clone(), - sum: self.sum.clone(), - mxr: self.mxr.clone(), - tvm: self.tvm.clone(), - tw: self.tw.clone(), - tsr: self.tsr.clone(), - uxl: self.uxl.clone(), - sxl: self.sxl.clone(), - sbe: self.sbe.clone(), - mbe: self.mbe.clone(), - } - } -} - -struct_layout! { - pub struct MStatusLayout { - pub sie: Atom, - pub mie: Atom, - pub spie: Atom, - pub ube: Atom, - pub mpie: Atom, - pub spp: Atom, - pub mpp: Atom, - pub fs: Atom, - pub xs: Atom, - pub mprv: Atom, - pub sum: Atom, - pub mxr: Atom, - pub tvm: Atom, - pub tw: Atom, - pub tsr: Atom, - pub uxl: Atom, - pub sxl: Atom, - pub sbe: Atom, - pub mbe: Atom, - } -} - -#[inline(always)] -fn compute_sd(fs: ExtensionValue, xs: ExtensionValue) -> bool { - fs == ExtensionValue::Dirty || xs == ExtensionValue::Dirty -} - -// Impl block for fields which are derived from other values or do not need to be stored in the backend. -impl MStatusValue { - /// Read mstatus.fs field - #[inline(always)] - pub fn read_sd(&self) -> bool - where - M: ManagerRead, - { - compute_sd(self.fs.read(), self.xs.read()) - } - - /// Read `mstatus.vs` field. For our implementation, this is a constant. - #[inline(always)] - pub const fn read_vs(&self) -> ExtensionValue { - ExtensionValue::Off - } -} - -// This impl block is here for compatibility with the bits api. -impl MStatusValue { - /// Read mstatus as in its 64 bit representation - #[inline] - pub fn read(&self) -> CSRRepr - where - M: ManagerRead, - { - let mstatus = &self; - let fs = mstatus.fs.read(); - let xs = mstatus.xs.read(); - MStatus::new( - ConstantBits, - mstatus.sie.read(), - ConstantBits, - mstatus.mie.read(), - ConstantBits, - mstatus.spie.read(), - mstatus.ube.read(), - mstatus.mpie.read(), - mstatus.spp.read(), - ConstantBits, - mstatus.mpp.read(), - fs, - xs, - mstatus.mprv.read(), - mstatus.sum.read(), - mstatus.mxr.read(), - mstatus.tvm.read(), - mstatus.tw.read(), - mstatus.tsr.read(), - ConstantBits, - mstatus.uxl.read(), - mstatus.sxl.read(), - mstatus.sbe.read(), - mstatus.mbe.read(), - ConstantBits, - compute_sd(xs, fs), - ) - .to_bits() - } - - /// Write to mstatus the `value` given in 64 bit representation - #[inline] - pub fn write(&mut self, value: CSRRepr) -> Option - where - M: ManagerWrite, - { - let value = MStatus::from_bits(value); - let mstatus = self; - - let effect_sie = mstatus.sie.write(value.sie()); - let effect_mie = mstatus.mie.write(value.mie()); - debug_assert_eq!(effect_sie, Some(CSREffect::Xie)); - debug_assert_eq!(effect_mie, Some(CSREffect::Xie)); - - mstatus.spie.write(value.spie()); - mstatus.ube.write(value.ube()); - mstatus.mpie.write(value.mpie()); - mstatus.spp.write(value.spp()); - mstatus.mpp.write(value.mpp()); - mstatus.fs.write(value.fs()); - mstatus.xs.write(value.xs()); - mstatus.mprv.write(value.mprv()); - mstatus.sum.write(value.sum()); - mstatus.mxr.write(value.mxr()); - mstatus.tvm.write(value.tvm()); - mstatus.tw.write(value.tw()); - mstatus.tsr.write(value.tsr()); - mstatus.uxl.write(value.uxl()); - mstatus.sxl.write(value.sxl()); - mstatus.sbe.write(value.sbe()); - mstatus.mbe.write(value.mbe()); - - Some(CSREffect::Xie) - } - - /// Replace mstatus with `value` given in 64 bit representation - #[inline] - pub fn replace(&mut self, value: CSRRepr) -> (CSRRepr, Option) - where - M: ManagerReadWrite, - { - let value = MStatus::from_bits(value); - let mstatus = self; - - let (sie, effect_sie) = mstatus.sie.replace(value.sie()); - let (mie, effect_mie) = mstatus.mie.replace(value.mie()); - debug_assert_eq!(effect_sie, Some(CSREffect::Xie)); - debug_assert_eq!(effect_mie, Some(CSREffect::Xie)); - - let spie = mstatus.spie.replace(value.spie()); - let ube = mstatus.ube.replace(value.ube()); - let mpie = mstatus.mpie.replace(value.mpie()); - let spp = mstatus.spp.replace(value.spp()); - let mpp = mstatus.mpp.replace(value.mpp()); - let fs = mstatus.fs.replace(value.fs()); - let xs = mstatus.xs.replace(value.xs()); - let mprv = mstatus.mprv.replace(value.mprv()); - let sum = mstatus.sum.replace(value.sum()); - let mxr = mstatus.mxr.replace(value.mxr()); - let tvm = mstatus.tvm.replace(value.tvm()); - let tw = mstatus.tw.replace(value.tw()); - let tsr = mstatus.tsr.replace(value.tsr()); - let uxl = mstatus.uxl.replace(value.uxl()); - let sxl = mstatus.sxl.replace(value.sxl()); - let sbe = mstatus.sbe.replace(value.sbe()); - let mbe = mstatus.mbe.replace(value.mbe()); - let sd = compute_sd(fs, xs); - - let old_value = MStatus::new( - ConstantBits::from_bits(0), - sie, - ConstantBits::from_bits(0), - mie, - ConstantBits::from_bits(0), - spie, - ube, - mpie, - spp, - ConstantBits::from_bits(0), - mpp, - fs, - xs, - mprv, - sum, - mxr, - tvm, - tw, - tsr, - ConstantBits::from_bits(0), - uxl, - sxl, - sbe, - mbe, - ConstantBits::from_bits(0), - sd, - ) - .to_bits(); - - (old_value, Some(CSREffect::Xie)) - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/values/xip.rs b/src/riscv/lib/src/machine_state/csregisters/values/xip.rs deleted file mode 100644 index 68ab606d92460e83b13edd711419bcb5b3384509..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/values/xip.rs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use crate::machine_state::csregisters::effects::CSREffect; -use crate::state_backend::AllocatedOf; -use crate::state_backend::ManagerBase; - -/// Layout for [`XipCell`] -pub type XipCellLayout = (); - -/// Dummy cell for MIP and SIP (effectively always 0) -#[derive(Clone)] -pub struct XipCell; - -impl XipCell { - /// Bind the given allocated regions. - pub fn bind(_space: AllocatedOf) -> Self { - XipCell - } - - /// Does nothing. - pub fn struct_ref(&self) {} - - /// Always returns 0. - #[inline(always)] - pub const fn read(&self) -> u64 { - 0 - } - - /// Does nothing but trigger the XIP effect. - #[inline(always)] - pub fn write(&mut self, _: u64) -> Option { - Some(CSREffect::Xip) - } - - /// Does nothing but trigger the XIP effect. - #[inline(always)] - pub fn replace(&mut self, _: u64) -> (u64, Option) { - (0, Some(CSREffect::Xip)) - } -} diff --git a/src/riscv/lib/src/machine_state/csregisters/xstatus.rs b/src/riscv/lib/src/machine_state/csregisters/xstatus.rs deleted file mode 100644 index 827e75378333e8dc59e1f9daba271bbc0f92417b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/csregisters/xstatus.rs +++ /dev/null @@ -1,375 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Module containing helpers for `mstatus` and `sstatus` registers. -//! -//! The `sstatus` register is a subset of the `mstatus` register. -//! This mechanism is described as "shadow" CSRs in RISC-V spec. - -// Allow unused setters & getters -#![allow(dead_code)] -// Allow non snake case for setters & getters -#![allow(non_snake_case)] - -use super::bits::NormaliseFields; -use crate::bits::Bits64; -use crate::bits::ConstantBits; -use crate::csr; -use crate::default::ConstDefault; -use crate::machine_state::mode::Mode; - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] -#[repr(u8)] -pub enum MPPValue { - User = 0b00, - #[default] - Supervisor = 0b01, - Machine = 0b11, -} - -impl ConstDefault for MPPValue { - const DEFAULT: Self = MPPValue::Supervisor; -} - -impl From for Mode { - fn from(other: MPPValue) -> Mode { - match other { - MPPValue::User => Mode::User, - MPPValue::Supervisor => Mode::Supervisor, - MPPValue::Machine => Mode::Machine, - } - } -} - -impl From for u8 { - fn from(value: MPPValue) -> Self { - value as u8 - } -} - -impl From for MPPValue { - fn from(value: u8) -> Self { - match value & 0b11 { - 0b00 => MPPValue::User, - 0b01 => MPPValue::Supervisor, - 0b11 => MPPValue::Machine, - // WARL field, invalid value is considered User - _ => MPPValue::User, - } - } -} - -impl Bits64 for MPPValue { - const WIDTH: usize = 2; - - fn from_bits(value: u64) -> Self { - >::from(value as u8) - } - - fn to_bits(&self) -> u64 { - >::into(*self) as u64 - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] -#[repr(u8)] -pub enum SPPValue { - User = 0b0, - #[default] - Supervisor = 0b1, -} - -impl ConstDefault for SPPValue { - const DEFAULT: Self = SPPValue::Supervisor; -} - -impl From for SPPValue { - fn from(value: u8) -> Self { - match value & 1 { - 0b0 => SPPValue::User, - 0b1 => SPPValue::Supervisor, - _ => unreachable!(), - } - } -} - -impl From for u8 { - fn from(value: SPPValue) -> Self { - value as u8 - } -} - -impl Bits64 for SPPValue { - const WIDTH: usize = 1; - - fn from_bits(value: u64) -> Self { - >::from(value as u8) - } - - fn to_bits(&self) -> u64 { - >::into(*self) as u64 - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] -#[repr(u64)] -pub enum XLenValue { - #[default] - MXL64 = 0b10, -} - -impl ConstDefault for XLenValue { - const DEFAULT: Self = XLenValue::MXL64; -} - -impl From for u8 { - fn from(value: XLenValue) -> Self { - value as u8 - } -} - -impl From for XLenValue { - #[inline(always)] - fn from(_value: u8) -> Self { - Self::MXL64 - } -} - -impl Bits64 for XLenValue { - const WIDTH: usize = 2; - - #[inline(always)] - fn from_bits(value: u64) -> Self { - >::from(value as u8) - } - - fn to_bits(&self) -> u64 { - >::into(*self) as u64 - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] -#[repr(u64)] -pub enum ExtensionValue { - Off = 0b00, - #[default] - Dirty = 0b11, -} - -impl ConstDefault for ExtensionValue { - const DEFAULT: Self = ExtensionValue::Dirty; -} - -impl From for u8 { - fn from(value: ExtensionValue) -> Self { - value as u8 - } -} - -impl From for ExtensionValue { - fn from(value: u8) -> Self { - match value & 0b11 { - 0b00 => ExtensionValue::Off, - _ => ExtensionValue::Dirty, - } - } -} - -impl Bits64 for ExtensionValue { - const WIDTH: usize = 2; - - fn from_bits(value: u64) -> Self { - >::from(value as u8) - } - - fn to_bits(&self) -> u64 { - >::into(*self) as u64 - } -} - -csr! { - pub struct MStatus { - WPRI1: ConstantBits<1>, - SIE: bool, - WPRI2: ConstantBits<1>, - MIE: bool, - WPRI3: ConstantBits<1>, - SPIE: bool, - UBE: bool, - MPIE: bool, - SPP: SPPValue, - VS: ConstantBits<2>, - MPP: MPPValue, - FS: ExtensionValue, - XS: ExtensionValue, - MPRV: bool, - SUM: bool, - MXR: bool, - TVM: bool, - TW: bool, - TSR: bool, - WPRI4: ConstantBits<9>, - UXL: XLenValue, - SXL: XLenValue, - SBE: bool, - MBE: bool, - WPRI5: ConstantBits<25>, - SD: bool, - } -} - -impl MStatus { - pub fn to_sstatus(self) -> SStatus { - SStatus::from_bits(self.to_bits()) - } -} - -impl NormaliseFields for MStatus { - fn normalise(self) -> Self { - SStatus::normalise(self.to_sstatus()).to_mstatus(self) - } -} - -impl Default for MStatus { - fn default() -> Self { - MStatus::from_bits(0u64) - // Interrupts are off - .with_sie(false) - .with_mie(false) - // Interrupts were off before - .with_spie(false) - .with_mpie(false) - // Previous privilege mode was supervisor - .with_spp(SPPValue::default()) - .with_mpp(MPPValue::default()) - // Endianness is little-endian - .with_ube(false) - .with_sbe(false) - .with_mbe(false) - // Set register dirtiness - .with_fs(ExtensionValue::default()) - .with_xs(ExtensionValue::default()) - .with_sd(false) - // Registers are also 64-bit wide in user and supervisor mode - .with_uxl(XLenValue::default()) - .with_sxl(XLenValue::default()) - // Load and stores should use current effective privilege - .with_mprv(false) - // Supervisor mode shall have access to user page mappings - .with_sum(true) - // Make instruction loads from executable pages fail - .with_mxr(false) - // Allow virtual-memory management configuration - .with_tvm(false) - // WFI instruction works normally - .with_tw(false) - // Allow SRET to work normally - .with_tsr(false) - } -} - -csr! { - pub struct SStatus { - WPRI1: ConstantBits<1>, - SIE: bool, - WPRI2: ConstantBits<3>, - SPIE: bool, - UBE: bool, - WPRI3: ConstantBits<1>, - SPP: SPPValue, - VS: ConstantBits<2>, - WPRI4: ConstantBits<2>, - FS: ExtensionValue, - XS: ExtensionValue, - WPRI5: ConstantBits<1>, - SUM: bool, - MXR: bool, - WPRI6: ConstantBits<12>, - UXL: XLenValue, - WPRI7: ConstantBits<29>, - SD: bool, - } -} - -impl Default for SStatus { - fn default() -> Self { - MStatus::default().to_sstatus() - } -} - -impl SStatus { - pub fn to_mstatus(self, mstatus: MStatus) -> MStatus { - mstatus - .with_sie(self.sie()) - .with_spie(self.spie()) - .with_ube(self.ube()) - .with_spp(self.spp()) - .with_vs(self.vs()) - .with_fs(self.fs()) - .with_xs(self.xs()) - .with_sum(self.sum()) - .with_mxr(self.mxr()) - .with_uxl(self.uxl()) - .with_sd(self.sd()) - } -} - -impl NormaliseFields for SStatus { - fn normalise(self) -> Self { - let any_dirty = self.fs() == ExtensionValue::Dirty || self.xs() == ExtensionValue::Dirty; - self.with_sd(any_dirty) - } -} - -csr! { - pub struct MNStatus { - WPRI1: ConstantBits<3>, - NMIE: ConstantBits<1, 1>, - WPRI2: ConstantBits<3>, - MNPV: ConstantBits<1, 0>, - WPRI3: ConstantBits<3>, - MNPP: MPPValue, - WPRI4: ConstantBits<51>, - } -} - -impl NormaliseFields for MNStatus { - fn normalise(self) -> Self { - self - } -} - -#[cfg(test)] -mod tests { - use crate::bits::Bits64; - use crate::machine_state::csregisters::xstatus::ExtensionValue; - use crate::machine_state::csregisters::xstatus::MPPValue; - use crate::machine_state::csregisters::xstatus::SPPValue; - use crate::machine_state::csregisters::xstatus::XLenValue; - - #[test] - fn test_status_fields() { - let field = bool::from_bits(0xF0F0_0000_AAAA_0001); - assert!(field); - - let field = bool::from_bits(0x0002); - assert!(!field); - - let field = ExtensionValue::from_bits(0b1111_0010); - assert_eq!(field, ExtensionValue::Dirty); - assert_eq!(field.to_bits(), 0b11); - - let field = XLenValue::from_bits(0b01); - assert_eq!(field, XLenValue::MXL64); - assert_eq!(field.to_bits(), 0b10); - - let field = MPPValue::from_bits(0b1010); - assert_eq!(field, MPPValue::User); - assert_eq!(field.to_bits(), 0b00); - - let field = SPPValue::from_bits(0b111); - assert_eq!(field, SPPValue::Supervisor); - assert_eq!(field.to_bits(), 0b1); - } -} diff --git a/src/riscv/lib/src/machine_state/hart_state.rs b/src/riscv/lib/src/machine_state/hart_state.rs deleted file mode 100644 index 96c65173a50c335eca05552efb2f87811d383d57..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/hart_state.rs +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use super::csregisters::xstatus::MStatus; -use crate::bits::u64; -use crate::machine_state::csregisters; -use crate::machine_state::csregisters::CSRegister; -use crate::machine_state::csregisters::xstatus; -use crate::machine_state::memory::Address; -use crate::machine_state::mode::Mode; -use crate::machine_state::mode::TrapMode; -use crate::machine_state::registers; -use crate::machine_state::reservation_set; -use crate::machine_state::reservation_set::ReservationSet; -use crate::state::NewState; -use crate::state_backend as backend; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::traps::Interrupt; -use crate::traps::TrapContext; - -/// RISC-V hart state -pub struct HartState { - /// Integer registers - pub xregisters: registers::XRegisters, - - /// Floating-point number registers - pub fregisters: registers::FRegisters, - - /// Control and state registers - pub csregisters: csregisters::CSRegisters, - - /// Program counter - pub pc: Cell, - - /// Reservation set address - pub reservation_set: ReservationSet, -} - -/// Layout of [HartState] -pub type HartStateLayout = ( - registers::XRegistersLayout, - registers::FRegistersLayout, - csregisters::CSRegistersLayout, - Atom
, // Program counter layout - reservation_set::ReservationSetLayout, // Reservation set layout -); - -impl HartState { - /// Bind the hart state to the given allocated space. - pub fn bind(space: backend::AllocatedOf) -> Self { - Self { - xregisters: registers::XRegisters::bind(space.0), - fregisters: registers::FRegisters::bind(space.1), - csregisters: csregisters::CSRegisters::bind(space.2), - pc: space.3, - reservation_set: ReservationSet::bind(space.4), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf { - ( - self.xregisters.struct_ref::(), - self.fregisters.struct_ref::(), - self.csregisters.struct_ref::(), - self.pc.struct_ref::(), - self.reservation_set.struct_ref::(), - ) - } - - /// Reset the hart state. - pub fn reset(&mut self, pc: Address) - where - M: backend::ManagerWrite, - { - self.xregisters.reset(); - self.fregisters.reset(); - self.csregisters.reset(); - self.pc.write(pc); - self.reservation_set.reset(); - } - - /// Given a trap source and a return address, take a trap on the machine. - pub fn take_trap(&mut self, trap_source: TC, return_pc: u64) -> u64 - where - M: backend::ManagerReadWrite, - { - let trap_mode = self.csregisters.get_trap_mode(&trap_source); - let (xtvec_reg, xepc_reg, xcause_reg, xtval_reg) = match trap_mode { - TrapMode::Supervisor => ( - CSRegister::stvec, - CSRegister::sepc, - CSRegister::scause, - CSRegister::stval, - ), - TrapMode::Machine => ( - CSRegister::mtvec, - CSRegister::mepc, - CSRegister::mcause, - CSRegister::mtval, - ), - }; - - // Setting xepc allows the trap handler to resume the previous computation - self.csregisters.write(xepc_reg, return_pc); - - // The trap handler wants to know what caused the trap - self.csregisters.write(xcause_reg, trap_source.xcause()); - self.csregisters.write(xtval_reg, trap_source.xtval()); - - // Configure machine status for the trap handler - let mstatus: MStatus = self.csregisters.read(CSRegister::mstatus); - let mstatus = match trap_mode { - TrapMode::Supervisor => { - // Remember whether interupts were enabled before taking the trap - let interrupts_enabled = mstatus.sie(); - let mstatus = mstatus.with_spie(interrupts_enabled); - - // Disable interrupts for the trap handler - let mstatus = mstatus.with_sie(false); - - // Remember the previous privilege mode - mstatus.with_spp(xstatus::SPPValue::User) - } - - TrapMode::Machine => { - // Remember whether interupts were enabled before taking the trap - let interrupts_enabled = mstatus.mie(); - let mstatus = mstatus.with_mpie(interrupts_enabled); - - // Disable interrupts for the trap handler - let mstatus = mstatus.with_mie(false); - - // Remember the previous privilege mode - mstatus.with_mpp(xstatus::MPPValue::User) - } - }; - self.csregisters.write(CSRegister::mstatus, mstatus); - - trap_source.trap_handler_address(self.csregisters.read(xtvec_reg)) - } - - /// Return the current [`Interrupt`] with highest priority to be handled - /// or [`None`] if there isn't any available - #[inline(always)] - pub fn get_pending_interrupt(&mut self, _current_mode: Mode) -> Option - where - M: backend::ManagerRead, - { - // Interrupts are not implemented yet. - None - } -} - -impl NewState for HartState { - fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - Self { - xregisters: registers::XRegisters::new(manager), - fregisters: registers::FRegisters::new(manager), - csregisters: csregisters::CSRegisters::new(manager), - pc: Cell::new(manager), - reservation_set: ReservationSet::new(manager), - } - } -} - -impl Clone for HartState { - fn clone(&self) -> Self { - Self { - xregisters: self.xregisters.clone(), - fregisters: self.fregisters.clone(), - csregisters: self.csregisters.clone(), - pc: self.pc.clone(), - reservation_set: self.reservation_set.clone(), - } - } -} diff --git a/src/riscv/lib/src/machine_state/instruction.rs b/src/riscv/lib/src/machine_state/instruction.rs deleted file mode 100644 index 5987b7326a6881e0d5ba3f5d4aa45e442ac15210..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/instruction.rs +++ /dev/null @@ -1,2746 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! A replacement for [`InstrCacheable`] instructions. -//! -//! Rather than dispatching on a giant instruction enum, we instead split the instruction into -//! two: an [`OpCode`] and an [`Args`]. -//! -//! This allows us to dispatch the operation over the state directly from the opcode - both a -//! simpler match statement and, ultimately, paves the way to pre-dispatch these functions -//! when blocks are built in the block cache. This avoids the runtime overhead caused by -//! dispatching every time an instruction is run. - -mod constructors; -pub(crate) mod tagged_instruction; - -use std::fmt; -use std::fmt::Debug; -use std::fmt::Formatter; - -use serde::Deserialize; -use serde::Serialize; -use tagged_instruction::ArgsShape; -use tagged_instruction::TaggedInstruction; -use tagged_instruction::opcode_to_argsshape; - -use super::MachineCoreState; -use super::ProgramCounterUpdate; -use super::csregisters::CSRegister; -use super::memory::Address; -use super::memory::MemoryConfig; -use super::registers::FRegister; -use super::registers::NonZeroXRegister; -use super::registers::XRegister; -use super::registers::nz; -use super::registers::sp; -use crate::default::ConstDefault; -use crate::instruction_context::ICB; -use crate::instruction_context::IcbFnResult; -use crate::instruction_context::IcbLoweringFn; -use crate::instruction_context::LoadStoreWidth; -use crate::instruction_context::MulHighType; -use crate::instruction_context::Predicate; -use crate::instruction_context::Shift; -use crate::interpreter::atomics; -use crate::interpreter::branching; -use crate::interpreter::integer; -use crate::interpreter::load_store; -use crate::machine_state::ProgramCounterUpdate::Next; -use crate::parser::instruction::AmoArgs; -use crate::parser::instruction::CIBDTypeArgs; -use crate::parser::instruction::CIBNZTypeArgs; -use crate::parser::instruction::CIBTypeArgs; -use crate::parser::instruction::CJTypeArgs; -use crate::parser::instruction::CNZRTypeArgs; -use crate::parser::instruction::CRJTypeArgs; -use crate::parser::instruction::CRTypeArgs; -use crate::parser::instruction::CSSDTypeArgs; -use crate::parser::instruction::CSSTypeArgs; -use crate::parser::instruction::CsrArgs; -use crate::parser::instruction::CsriArgs; -use crate::parser::instruction::FCmpArgs; -use crate::parser::instruction::FLoadArgs; -use crate::parser::instruction::FR1ArgWithRounding; -use crate::parser::instruction::FR2ArgsWithRounding; -use crate::parser::instruction::FR3ArgsWithRounding; -use crate::parser::instruction::FRArgs; -use crate::parser::instruction::FRegToXRegArgs; -use crate::parser::instruction::FRegToXRegArgsWithRounding; -use crate::parser::instruction::FStoreArgs; -use crate::parser::instruction::InstrCacheable; -use crate::parser::instruction::InstrRoundingMode; -use crate::parser::instruction::InstrWidth; -use crate::parser::instruction::NonZeroRdRTypeArgs; -use crate::parser::instruction::NonZeroRdUJTypeArgs; -use crate::parser::instruction::RTypeArgs; -use crate::parser::instruction::UJTypeArgs; -use crate::parser::instruction::XRegToFRegArgs; -use crate::parser::instruction::XRegToFRegArgsWithRounding; -use crate::state_backend::ManagerReadWrite; -use crate::traps::Exception; - -/// An instruction formed of an opcode and flat arguments. -/// -/// This is preferred within the caches, as it enables 'pre-dispatch' of functions -/// -/// Instructions are constructable from [`InstrCacheable`] instructions. -#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] -#[serde(try_from = "TaggedInstruction", into = "TaggedInstruction")] -pub struct Instruction { - /// The operation (over the machine state) that this instruction represents. - pub opcode: OpCode, - /// Arguments that are passed to the opcode-function. As a flat structure, it contains - /// all possible arguments. Each instruction will only use a subset. - args: Args, -} - -impl Debug for Instruction { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - struct DebugArgs<'a>(&'a dyn Fn(&mut Formatter<'_>) -> fmt::Result); - - impl Debug for DebugArgs<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - (self.0)(f) - } - } - - let debug_args = |f: &mut Formatter<'_>| { - let mut debug_struct = f.debug_struct("Args"); - // SAFETY: The variants used are validated on construction and deserialisation - // to be the opcode's associated register types, so this is safe. - unsafe { - match opcode_to_argsshape(&self.opcode) { - ArgsShape::XSrcXDest => { - debug_struct.field("rd", &self.args.rd.x); - debug_struct.field("rs1", &self.args.rs1.x); - debug_struct.field("rs2", &self.args.rs2.x); - } - ArgsShape::FSrcFDest => { - debug_struct.field("rd", &self.args.rd.f); - debug_struct.field("rs1", &self.args.rs1.f); - debug_struct.field("rs2", &self.args.rs2.f); - } - ArgsShape::XSrcFDest => { - debug_struct.field("rd", &self.args.rd.f); - debug_struct.field("rs1", &self.args.rs1.x); - debug_struct.field("rs2", &self.args.rs2.x); - } - ArgsShape::FSrcXDest => { - debug_struct.field("rd", &self.args.rd.x); - debug_struct.field("rs1", &self.args.rs1.f); - debug_struct.field("rs2", &self.args.rs2.f); - } - ArgsShape::XSrcFSrc => { - debug_struct.field("rd", &self.args.rd.x); - debug_struct.field("rs1", &self.args.rs1.x); - debug_struct.field("rs2", &self.args.rs2.f); - } - ArgsShape::NZXSrcNZXDest => { - debug_struct.field("rd", &self.args.rd.nzx); - debug_struct.field("rs1", &self.args.rs1.nzx); - debug_struct.field("rs2", &self.args.rs2.nzx); - } - ArgsShape::XSrcNZXDest => { - debug_struct.field("rd", &self.args.rd.nzx); - debug_struct.field("rs1", &self.args.rs1.x); - debug_struct.field("rs2", &self.args.rs2.x); - } - } - } - debug_struct - .field("imm", &self.args.imm) - .field("csr", &self.args.csr) - .field("rs3f", &self.args.rs3f) - .field("rm", &self.args.rm) - .field("aq", &self.args.aq) - .field("rl", &self.args.rl) - .field("width", &self.args.width) - .finish() - }; - let debug_args = DebugArgs(&debug_args); - - f.debug_struct("Instruction") - .field("opcode", &self.opcode) - .field("args", &debug_args) - .finish() - } -} - -impl Instruction { - /// Returns the width of the instruction: either compressed or uncompressed. - pub const fn width(&self) -> InstrWidth { - self.args.width - } - - /// Returns a reference to the arguments of an instruction. - pub fn args(&self) -> &Args { - &self.args - } -} - -impl ConstDefault for Instruction { - const DEFAULT: Self = Instruction { - opcode: OpCode::Unknown, - args: Args::DEFAULT, - }; -} - -/// alias for the function signature of an instruction run. -/// -/// SAFETY: This function must be called with an `Args` belonging to the same `OpCode` as -/// the one used to dispatch this function. -pub type RunInstr = unsafe fn( - &Args, - &mut MachineCoreState, -) -> Result, Exception>; - -/// Opcodes map to the operation performed over the state - allowing us to -/// decouple these from the parsed instructions down the line. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub enum OpCode { - Unknown, - - // RV64I R-type instructions - Add, - Sub, - X64Xor, - Or, - And, - ShiftLeft, - ShiftRightUnsigned, - ShiftRightSigned, - SetLessThanSigned, - SetLessThanUnsigned, - AddWord, - SubWord, - X32ShiftLeft, - X32ShiftRightSigned, - X32ShiftRightUnsigned, - - // RV64I I-type instructions - Addi, - AddWordImmediate, - X64XorImm, - X64OrImm, - Andi, - ShiftLeftImmediate, - ShiftRightImmediateUnsigned, - ShiftRightImmediateSigned, - X32ShiftLeftImm, - X32ShiftRightImmSigned, - X32ShiftRightImmUnsigned, - SetLessThanImmediateSigned, - SetLessThanImmediateUnsigned, - X8LoadSigned, - X16LoadSigned, - X32LoadSigned, - X8LoadUnsigned, - X16LoadUnsigned, - X32LoadUnsigned, - X64LoadSigned, - - // RV64I S-type instructions - X8Store, - X16Store, - X32Store, - X64Store, - - // RV64I B-type instructions - BranchEqual, - BranchNotEqual, - BranchLessThanSigned, - BranchGreaterThanOrEqualSigned, - BranchLessThanUnsigned, - BranchGreaterThanOrEqualUnsigned, - - // RV64I U-type instructions - AddImmediateToPC, - - // RV64I jump instructions - Jal, - /// Previous `Jalr`. Same as current `Jalr` except jump to `val(rs1) + imm`. - JalrImm, - - // RV64A R-type atomic instructions - Lrw, - Scw, - Amoswapw, - Amoaddw, - Amoxorw, - Amoandw, - Amoorw, - Amominw, - Amomaxw, - Amominuw, - Amomaxuw, - Lrd, - Scd, - Amoswapd, - X64AtomicAdd, - Amoxord, - Amoandd, - Amoord, - Amomind, - Amomaxd, - Amominud, - Amomaxud, - - // RV64M division instructions - Rem, - Remu, - Remw, - Remuw, - X64DivSigned, - Divu, - Divw, - Divuw, - Mul, - X64MulHighSigned, - X64MulHighSignedUnsigned, - X64MulHighUnsigned, - X32Mul, - - // RV64F instructions - FclassS, - Feqs, - Fles, - Flts, - Fadds, - Fsubs, - Fmuls, - Fdivs, - Fsqrts, - Fmins, - Fmaxs, - Fmadds, - Fmsubs, - Fnmsubs, - Fnmadds, - Flw, - Fsw, - Fcvtsw, - Fcvtswu, - Fcvtsl, - Fcvtslu, - Fcvtws, - Fcvtwus, - Fcvtls, - Fcvtlus, - Fsgnjs, - Fsgnjns, - Fsgnjxs, - FmvXW, - FmvWX, - - // RV64D instructions - FclassD, - Feqd, - Fled, - Fltd, - Faddd, - Fsubd, - Fmuld, - Fdivd, - Fsqrtd, - Fmind, - Fmaxd, - Fmaddd, - Fmsubd, - Fnmsubd, - Fnmaddd, - Fld, - Fsd, - Fcvtdw, - Fcvtdwu, - Fcvtdl, - Fcvtdlu, - Fcvtds, - Fcvtsd, - Fcvtwd, - Fcvtwud, - Fcvtld, - Fcvtlud, - Fsgnjd, - Fsgnjnd, - Fsgnjxd, - FmvXD, - FmvDX, - - // Zicsr instructions - Csrrw, - Csrrs, - Csrrc, - Csrrwi, - Csrrsi, - Csrrci, - - /// Jumps to val(rs1) - Jr, - /// Effects are to store the next instruction address in rd and jump to val(rs1). - Jalr, - - // RV64DC compressed instructions - CFld, - CFldsp, - CFsd, - CFsdsp, - - // Internal OpCodes - BranchEqualZero, - BranchNotEqualZero, - J, - Mv, - Li, - Nop, - Neg, - /// Jump to absolute address (internal `J` opcode jumps to `val(rs1) + imm`, - /// whilst this just jumps to `imm`). - JAbsolute, - /// Jump to absolute address `imm` and link register. - /// Same as `JAbsolute` but also stores next instr address in rd. - JalrAbsolute, - /// Same as `Jr` but jumps to `val(rs1) + imm`. - JrImm, - /// Jump to `pc + imm` if `val(rs2) < 0`. - BranchLessThanZero, - /// Jump to `pc + imm` if `val(rs2) >= 0`. - BranchGreaterThanOrEqualZero, - /// Jump to `pc + imm` if `val(rs2) <= 0`. - BranchLessThanOrEqualZero, - /// Jump to `pc + imm` if `val(rs2) > 0`. - BranchGreaterThanZero, - - /// Performs an environment call, from the current - /// machine mode. - ECall, -} - -impl OpCode { - /// Dispatch an opcode to the function that will run over the machine state. - /// - /// # SAFETY - /// Calling the returned function **must** correspond to an `Args` belonging to an - /// instruction where the `OpCode` is the same as the `OpCode` of the current instruction. - #[inline(always)] - pub(super) fn to_run(self) -> RunInstr { - match self { - Self::Add => Args::run_add, - Self::Sub => Args::run_sub, - Self::Neg => Args::run_neg, - Self::X64Xor => Args::run_x64_xor, - Self::Or => Args::run_or, - Self::And => Args::run_and, - Self::ShiftLeft => Args::run_shift_left, - Self::ShiftRightUnsigned => Args::run_shift_right_unsigned, - Self::ShiftRightSigned => Args::run_shift_right_signed, - Self::SetLessThanSigned => Args::run_set_less_than_signed, - Self::SetLessThanUnsigned => Args::run_set_less_than_unsigned, - Self::AddWord => Args::run_add_word, - Self::SubWord => Args::run_sub_word, - Self::X32ShiftLeft => Args::run_x32_shift_left, - Self::X32ShiftRightUnsigned => Args::run_x32_shift_right_unsigned, - Self::X32ShiftRightSigned => Args::run_x32_shift_right_signed, - Self::Addi => Args::run_addi, - Self::AddWordImmediate => Args::run_add_word_immediate, - Self::X64XorImm => Args::run_x64_xor_immediate, - Self::X64OrImm => Args::run_x64_or_immediate, - Self::Andi => Args::run_andi, - Self::ShiftLeftImmediate => Args::run_shift_left_immediate, - Self::ShiftRightImmediateUnsigned => Args::run_shift_right_immediate_unsigned, - Self::ShiftRightImmediateSigned => Args::run_shift_right_immediate_signed, - Self::X32ShiftLeftImm => Args::run_x32_shift_left_imm, - Self::X32ShiftRightImmUnsigned => Args::run_x32_shift_right_imm_unsigned, - Self::X32ShiftRightImmSigned => Args::run_x32_shift_right_imm_signed, - Self::SetLessThanImmediateSigned => Args::run_set_less_than_immediate_signed, - Self::SetLessThanImmediateUnsigned => Args::run_set_less_than_immediate_unsigned, - Self::X8LoadSigned => Args::run_x8_load_signed, - Self::X16LoadSigned => Args::run_x16_load_signed, - Self::X32LoadSigned => Args::run_x32_load_signed, - Self::X8LoadUnsigned => Args::run_x8_load_unsigned, - Self::X16LoadUnsigned => Args::run_x16_load_unsigned, - Self::X32LoadUnsigned => Args::run_x32_load_unsigned, - Self::X64LoadSigned => Args::run_x64_load_signed, - Self::X8Store => Args::run_x8_store, - Self::X16Store => Args::run_x16_store, - Self::X32Store => Args::run_x32_store, - Self::X64Store => Args::run_x64_store, - Self::BranchEqual => Args::run_branch_equal, - Self::BranchNotEqual => Args::run_branch_not_equal, - Self::BranchLessThanSigned => Args::run_branch_less_than_signed, - Self::BranchGreaterThanOrEqualSigned => Args::run_branch_greater_than_or_equal_signed, - Self::BranchLessThanZero => Args::run_branch_less_than_zero, - Self::BranchGreaterThanOrEqualZero => Args::run_branch_greater_than_or_equal_zero, - Self::BranchLessThanOrEqualZero => Args::run_branch_less_than_equal_zero, - Self::BranchGreaterThanZero => Args::run_branch_greater_than_zero, - Self::BranchLessThanUnsigned => Args::run_branch_less_than_unsigned, - Self::BranchGreaterThanOrEqualUnsigned => { - Args::run_branch_greater_than_or_equal_unsigned - } - Self::AddImmediateToPC => Args::run_add_immediate_to_pc, - Self::Jal => Args::run_jal, - Self::JalrImm => Args::run_jalr_imm, - Self::JrImm => Args::run_jr_imm, - Self::JalrAbsolute => Args::run_jalr_absolute, - Self::Lrw => Args::run_lrw, - Self::Scw => Args::run_scw, - Self::Amoswapw => Args::run_amoswapw, - Self::Amoaddw => Args::run_amoaddw, - Self::Amoxorw => Args::run_amoxorw, - Self::Amoandw => Args::run_amoandw, - Self::Amoorw => Args::run_amoorw, - Self::Amominw => Args::run_amominw, - Self::Amomaxw => Args::run_amomaxw, - Self::Amominuw => Args::run_amominuw, - Self::Amomaxuw => Args::run_amomaxuw, - Self::Lrd => Args::run_lrd, - Self::Scd => Args::run_scd, - Self::Amoswapd => Args::run_amoswapd, - Self::X64AtomicAdd => Args::run_x64_atomic_add, - Self::Amoxord => Args::run_amoxord, - Self::Amoandd => Args::run_amoandd, - Self::Amoord => Args::run_amoord, - Self::Amomind => Args::run_amomind, - Self::Amomaxd => Args::run_amomaxd, - Self::Amominud => Args::run_amominud, - Self::Amomaxud => Args::run_amomaxud, - Self::Rem => Args::run_rem, - Self::Remu => Args::run_remu, - Self::Remw => Args::run_remw, - Self::Remuw => Args::run_remuw, - Self::X64DivSigned => Args::run_x64_div_signed, - Self::Divu => Args::run_divu, - Self::Divw => Args::run_divw, - Self::Divuw => Args::run_divuw, - Self::Mul => Args::run_mul, - Self::X64MulHighSigned => Args::run_x64_mul_high_signed, - Self::X64MulHighSignedUnsigned => Args::run_x64_mul_high_signed_unsigned, - Self::X64MulHighUnsigned => Args::run_x64_mul_high_unsigned, - Self::X32Mul => Args::run_x32_mul, - Self::FclassS => Args::run_fclass_s, - Self::Feqs => Args::run_feq_s, - Self::Fles => Args::run_fle_s, - Self::Flts => Args::run_flt_s, - Self::Fadds => Args::run_fadd_s, - Self::Fsubs => Args::run_fsub_s, - Self::Fmuls => Args::run_fmul_s, - Self::Fdivs => Args::run_fdiv_s, - Self::Fsqrts => Args::run_fsqrt_s, - Self::Fmins => Args::run_fmin_s, - Self::Fmaxs => Args::run_fmax_s, - Self::Fmadds => Args::run_fmadd_s, - Self::Fmsubs => Args::run_fmsub_s, - Self::Fnmsubs => Args::run_fnmsub_s, - Self::Fnmadds => Args::run_fnmadd_s, - Self::Flw => Args::run_flw, - Self::Fsw => Args::run_fsw, - Self::Fcvtsw => Args::run_fcvt_s_w, - Self::Fcvtswu => Args::run_fcvt_s_wu, - Self::Fcvtsl => Args::run_fcvt_s_l, - Self::Fcvtslu => Args::run_fcvt_s_lu, - Self::Fcvtws => Args::run_fcvt_w_s, - Self::Fcvtwus => Args::run_fcvt_wu_s, - Self::Fcvtls => Args::run_fcvt_l_s, - Self::Fcvtlus => Args::run_fcvt_lu_s, - Self::Fsgnjs => Args::run_fsgnj_s, - Self::Fsgnjns => Args::run_fsgnjn_s, - Self::Fsgnjxs => Args::run_fsgnjx_s, - Self::FmvXW => Args::run_fmv_x_w, - Self::FmvWX => Args::run_fmv_w_x, - Self::FclassD => Args::run_fclass_d, - Self::Feqd => Args::run_feq_d, - Self::Fled => Args::run_fle_d, - Self::Fltd => Args::run_flt_d, - Self::Faddd => Args::run_fadd_d, - Self::Fsubd => Args::run_fsub_d, - Self::Fmuld => Args::run_fmul_d, - Self::Fdivd => Args::run_fdiv_d, - Self::Fsqrtd => Args::run_fsqrt_d, - Self::Fmind => Args::run_fmin_d, - Self::Fmaxd => Args::run_fmax_d, - Self::Fmaddd => Args::run_fmadd_d, - Self::Fmsubd => Args::run_fmsub_d, - Self::Fnmsubd => Args::run_fnmsub_d, - Self::Fnmaddd => Args::run_fnmadd_d, - Self::Fld => Args::run_fld, - Self::Fsd => Args::run_fsd, - Self::Fcvtdw => Args::run_fcvt_d_w, - Self::Fcvtdwu => Args::run_fcvt_d_wu, - Self::Fcvtdl => Args::run_fcvt_d_l, - Self::Fcvtdlu => Args::run_fcvt_d_lu, - Self::Fcvtds => Args::run_fcvt_d_s, - Self::Fcvtsd => Args::run_fcvt_s_d, - Self::Fcvtwd => Args::run_fcvt_w_d, - Self::Fcvtwud => Args::run_fcvt_wu_d, - Self::Fcvtld => Args::run_fcvt_l_d, - Self::Fcvtlud => Args::run_fcvt_lu_d, - Self::Fsgnjd => Args::run_fsgnj_d, - Self::Fsgnjnd => Args::run_fsgnjn_d, - Self::Fsgnjxd => Args::run_fsgnjx_d, - Self::FmvXD => Args::run_fmv_x_d, - Self::FmvDX => Args::run_fmv_d_x, - Self::Csrrw => Args::run_csrrw, - Self::Csrrs => Args::run_csrrs, - Self::Csrrc => Args::run_csrrc, - Self::Csrrwi => Args::run_csrrwi, - Self::Csrrsi => Args::run_csrrsi, - Self::Csrrci => Args::run_csrrci, - Self::J => Args::run_j, - Self::JAbsolute => Args::run_j_absolute, - Self::Jr => Args::run_jr, - Self::Jalr => Args::run_jalr, - Self::BranchEqualZero => Args::run_branch_equal_zero, - Self::BranchNotEqualZero => Args::run_branch_not_equal_zero, - Self::Li => Args::run_li, - Self::Mv => Args::run_mv, - Self::Nop => Args::run_nop, - Self::CFld => Args::run_cfld, - Self::CFldsp => Args::run_cfldsp, - Self::CFsd => Args::run_cfsd, - Self::CFsdsp => Args::run_cfsdsp, - Self::Unknown => Args::run_illegal, - Self::ECall => Args::run_ecall, - } - } - - /// Dispatch an opcode to the function that can 'lower' the instruction to the JIT IR. - /// - /// This mechanism leverages the [InstructionContextBuilder] to do so. - /// - /// TODO (RV-394): this can be removed once all opcodes are supported, with [`OpCode::to_run`] being - /// used instead. - /// - /// # SAFETY - /// Calling the returned function **must** correspond to an `Args` belonging to an - /// instruction where the `OpCode` is the same as the `OpCode` of the current instruction. - /// - /// [InstructionContextBuilder]: ICB - #[inline(always)] - pub(crate) fn to_lowering(self) -> Option> { - match self { - Self::Mv => Some(Args::run_mv), - Self::Neg => Some(Args::run_neg), - Self::Nop => Some(Args::run_nop), - Self::Add => Some(Args::run_add), - Self::AddWord => Some(Args::run_add_word), - Self::AddWordImmediate => Some(Args::run_add_word_immediate), - Self::Sub => Some(Args::run_sub), - Self::SubWord => Some(Args::run_sub_word), - Self::And => Some(Args::run_and), - Self::Or => Some(Args::run_or), - Self::X64OrImm => Some(Args::run_x64_or_immediate), - Self::X64Xor => Some(Args::run_x64_xor), - Self::X64XorImm => Some(Args::run_x64_xor_immediate), - Self::Mul => Some(Args::run_mul), - Self::X32Mul => Some(Args::run_x32_mul), - Self::X64MulHighSigned => Some(Args::run_x64_mul_high_signed), - Self::X64MulHighSignedUnsigned => Some(Args::run_x64_mul_high_signed_unsigned), - Self::X64MulHighUnsigned => Some(Args::run_x64_mul_high_unsigned), - Self::X64DivSigned => Some(Args::run_x64_div_signed), - Self::Li => Some(Args::run_li), - Self::AddImmediateToPC => Some(Args::run_add_immediate_to_pc), - Self::J => Some(Args::run_j), - Self::Jr => Some(Args::run_jr), - Self::JrImm => Some(Args::run_jr_imm), - Self::JAbsolute => Some(Args::run_j_absolute), - Self::Jal => Some(Args::run_jal), - Self::Jalr => Some(Args::run_jalr), - Self::JalrImm => Some(Args::run_jalr_imm), - Self::JalrAbsolute => Some(Args::run_jalr_absolute), - Self::Addi => Some(Args::run_addi), - Self::Andi => Some(Args::run_andi), - Self::SetLessThanSigned => Some(Args::run_set_less_than_signed), - Self::SetLessThanUnsigned => Some(Args::run_set_less_than_unsigned), - Self::SetLessThanImmediateSigned => Some(Args::run_set_less_than_immediate_signed), - Self::SetLessThanImmediateUnsigned => Some(Args::run_set_less_than_immediate_unsigned), - // Branching instructions - Self::BranchEqual => Some(Args::run_branch_equal), - Self::BranchEqualZero => Some(Args::run_branch_equal_zero), - Self::BranchNotEqual => Some(Args::run_branch_not_equal), - Self::BranchNotEqualZero => Some(Args::run_branch_not_equal_zero), - - Self::BranchLessThanSigned => Some(Args::run_branch_less_than_signed), - Self::BranchLessThanUnsigned => Some(Args::run_branch_less_than_unsigned), - Self::BranchLessThanZero => Some(Args::run_branch_less_than_zero), - Self::BranchLessThanOrEqualZero => Some(Args::run_branch_less_than_equal_zero), - - Self::BranchGreaterThanOrEqualSigned => { - Some(Args::run_branch_greater_than_or_equal_signed) - } - Self::BranchGreaterThanOrEqualUnsigned => { - Some(Args::run_branch_greater_than_or_equal_unsigned) - } - Self::BranchGreaterThanOrEqualZero => Some(Args::run_branch_greater_than_or_equal_zero), - Self::BranchGreaterThanZero => Some(Args::run_branch_greater_than_zero), - - Self::ShiftLeft => Some(Args::run_shift_left), - Self::ShiftRightUnsigned => Some(Args::run_shift_right_unsigned), - Self::ShiftRightSigned => Some(Args::run_shift_right_signed), - Self::ShiftLeftImmediate => Some(Args::run_shift_left_immediate), - Self::ShiftRightImmediateUnsigned => Some(Args::run_shift_right_immediate_unsigned), - Self::ShiftRightImmediateSigned => Some(Args::run_shift_right_immediate_signed), - Self::X32ShiftLeft => Some(Args::run_x32_shift_left), - Self::X32ShiftRightUnsigned => Some(Args::run_x32_shift_right_unsigned), - Self::X32ShiftRightSigned => Some(Args::run_x32_shift_right_signed), - Self::X32ShiftLeftImm => Some(Args::run_x32_shift_left_imm), - Self::X32ShiftRightImmUnsigned => Some(Args::run_x32_shift_right_imm_unsigned), - Self::X32ShiftRightImmSigned => Some(Args::run_x32_shift_right_imm_signed), - - // Stores - Self::X64Store => Some(Args::run_x64_store), - Self::X32Store => Some(Args::run_x32_store), - Self::X16Store => Some(Args::run_x16_store), - Self::X8Store => Some(Args::run_x8_store), - - // Loads - Self::X64LoadSigned => Some(Args::run_x64_load_signed), - Self::X32LoadSigned => Some(Args::run_x32_load_signed), - Self::X32LoadUnsigned => Some(Args::run_x32_load_unsigned), - Self::X16LoadSigned => Some(Args::run_x16_load_signed), - Self::X16LoadUnsigned => Some(Args::run_x16_load_unsigned), - Self::X8LoadSigned => Some(Args::run_x8_load_signed), - Self::X8LoadUnsigned => Some(Args::run_x8_load_unsigned), - - // Atomic instructions - Self::X64AtomicAdd => Some(Args::run_x64_atomic_add), - - // Errors - Self::Unknown => Some(Args::run_illegal), - Self::ECall => Some(Args::run_ecall), - _ => None, - } - } -} - -impl Instruction { - /// Run an instruction over the machine core state. - pub(super) fn run( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - // SAFETY: Unsafe accesses in this function are due to using the [Register] union, - // which is safe as the registers used are validated against the opcode. - unsafe { (self.opcode.to_run())(&self.args, core) } - } -} - -/// A union of the X and F registers, which are both represented as u8. -#[derive(Clone, Copy)] -pub union Register { - pub x: XRegister, - pub f: FRegister, - pub nzx: NonZeroXRegister, -} - -impl ConstDefault for Register { - const DEFAULT: Self = Self { - nzx: NonZeroXRegister::x1, - }; -} - -impl From for Register { - fn from(x: XRegister) -> Self { - Self { x } - } -} - -impl From for Register { - fn from(f: FRegister) -> Self { - Self { f } - } -} - -impl From for Register { - fn from(nzx: NonZeroXRegister) -> Self { - Self { nzx } - } -} - -impl PartialEq for Register { - fn eq(&self, other: &Self) -> bool { - // SAFETY: XRegister and FRegister are the same size as u8 and directly mappable to it. - unsafe { - std::mem::transmute::<&Self, &u8>(self) == std::mem::transmute::<&Self, &u8>(other) - } - } -} - -impl Eq for Register {} - -/// Contains all possible arguments used by opcode-functions. -/// -/// Each opcode will only touch a subset of these. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Args { - pub rd: Register, - pub rs1: Register, - pub rs2: Register, - pub imm: i64, - pub csr: CSRegister, - pub rs3f: FRegister, - pub rm: InstrRoundingMode, - pub aq: bool, - pub rl: bool, - pub width: InstrWidth, -} - -impl ConstDefault for Args { - const DEFAULT: Self = Self { - rd: Register::DEFAULT, - rs1: Register::DEFAULT, - rs2: Register::DEFAULT, - imm: 0, - csr: CSRegister::fflags, - rs3f: FRegister::f0, - rm: InstrRoundingMode::Dynamic, - aq: false, - rl: false, - width: InstrWidth::Uncompressed, - }; -} - -macro_rules! impl_r_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .xregisters - .$fn(unsafe { self.rs1.x }, unsafe { self.rs2.x }, unsafe { - self.rd.x - }); - Ok(Next(self.width)) - } - }; - - ($fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .xregisters - .$fn(unsafe { self.rs1.nzx }, unsafe { self.rs2.nzx }, unsafe { - self.rd.nzx - }); - Ok(Next(self.width)) - } - }; - - ($fn: ident, non_zero_rd) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .xregisters - .$fn(unsafe { self.rs1.x }, unsafe { self.rs2.x }, unsafe { - self.rd.nzx - }); - Ok(Next(self.width)) - } - }; - - ($impl: path, $fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl( - icb, - unsafe { self.rs1.nzx }, - unsafe { self.rs2.nzx }, - unsafe { self.rd.nzx }, - ); - let pcu = ProgramCounterUpdate::Next(self.width); - icb.ok(pcu) - } - }; - - ($impl: path, $fn: ident, non_zero_rd) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl(icb, unsafe { self.rs1.x }, unsafe { self.rs2.x }, unsafe { - self.rd.nzx - }); - icb.ok(Next(self.width)) - } - }; - - ($fn: ident, $shift: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - integer::run_shift( - icb, - Shift::$shift, - unsafe { self.rs1.nzx }, - unsafe { self.rs2.nzx }, - unsafe { self.rd.nzx }, - ); - icb.ok(Next(self.width)) - } - }; -} - -macro_rules! impl_x32_shift_type { - ($shift: ident, $fn: ident, reg) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let rs1 = unsafe { self.rs1.x }; - let rs2 = unsafe { self.rs2.x }; - let rd = unsafe { self.rd.nzx }; - integer::run_x32_shift(icb, Shift::$shift, rs1, rs2, rd); - icb.ok(Next(self.width)) - } - }; - - ($shift: ident, $fn: ident, imm) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let rs1 = unsafe { self.rs1.nzx }; - let rd = unsafe { self.rd.nzx }; - integer::run_x32_shift_immediate(icb, Shift::$shift, rs1, self.imm, rd); - icb.ok(Next(self.width)) - } - }; -} - -macro_rules! impl_x64_mul_high_type { - ($fn: ident, $mul_high_type: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - integer::run_x64_mul_high( - icb, - unsafe { self.rs1.nzx }, - unsafe { self.rs2.nzx }, - unsafe { self.rd.nzx }, - MulHighType::$mul_high_type, - ); - icb.ok(Next(self.width)) - } - }; -} - -macro_rules! impl_i_type { - ($fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .xregisters - .$fn(self.imm, unsafe { self.rs1.nzx }, unsafe { self.rd.nzx }); - Ok(Next(self.width)) - } - }; - - ($fn: ident, non_zero_rd) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .xregisters - .$fn(self.imm, unsafe { self.rs1.x }, unsafe { self.rd.nzx }); - Ok(Next(self.width)) - } - }; - - ($impl: path, $fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl(icb, self.imm, unsafe { self.rs1.nzx }, unsafe { - self.rd.nzx - }); - icb.ok(Next(self.width)) - } - }; - - ($impl: path, $fn: ident, non_zero_rd) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl(icb, self.imm, unsafe { self.rs1.x }, unsafe { self.rd.nzx }); - icb.ok(Next(self.width)) - } - }; - - ($fn: ident, $shift: path) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - integer::run_shift_immediate(icb, $shift, self.imm, unsafe { self.rs1.nzx }, unsafe { - self.rd.nzx - }); - icb.ok(Next(self.width)) - } - }; -} - -macro_rules! impl_fload_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rs1.x }, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; -} -macro_rules! impl_load_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rs1.x }, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; - - ($fn: ident, $width: expr, $signed: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let res = load_store::run_load( - icb, - self.imm, - unsafe { self.rs1.x }, - unsafe { self.rd.x }, - $signed, - $width, - ); - I::map(res, |_| Next(self.width)) - } - }; -} - -macro_rules! impl_cfload_sp_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_store_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rs1.x }, unsafe { self.rs2.x }) - .map(|_| Next(self.width)) - } - }; - - ($fn: ident, $width: expr) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let res = load_store::run_store( - icb, - self.imm, - unsafe { self.rs1.x }, - unsafe { self.rs2.x }, - $width, - ); - I::map(res, |_| Next(self.width)) - } - }; -} -macro_rules! impl_fstore_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rs1.x }, unsafe { self.rs2.f }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_branch { - ($fn: ident, $predicate: expr) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let pcu = branching::run_branch( - icb, - $predicate, - self.imm, - unsafe { self.rs1.nzx }, - unsafe { self.rs2.nzx }, - self.width, - ); - icb.ok(pcu) - } - }; -} - -macro_rules! impl_branch_compare_zero { - ($fn: ident, $predicate: expr) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let pcu = branching::run_branch_compare_zero( - icb, - $predicate, - self.imm, - unsafe { self.rs1.nzx }, - self.width, - ); - icb.ok(pcu) - } - }; -} - -macro_rules! impl_amo_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn( - unsafe { self.rs1.x }, - unsafe { self.rs2.x }, - unsafe { self.rd.x }, - self.rl, - self.aq, - ) - .map(|_| Next(self.width)) - } - }; - - ($impl: path, $fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - let res = $impl( - icb, - unsafe { self.rs1.x }, - unsafe { self.rs2.x }, - unsafe { self.rd.x }, - self.rl, - self.aq, - ); - I::map(res, |_| Next(self.width)) - } - }; -} - -macro_rules! impl_ci_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart.xregisters.$fn(self.imm, unsafe { self.rd.x }); - Ok(ProgramCounterUpdate::Next(self.width)) - } - }; - - ($fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart.xregisters.$fn(self.imm, unsafe { self.rd.nzx }); - Ok(ProgramCounterUpdate::Next(self.width)) - } - }; - - ($impl: path, $fn: ident, non_zero) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl(icb, self.imm, unsafe { self.rd.nzx }); - let pcu = ProgramCounterUpdate::Next(self.width); - icb.ok(pcu) - } - }; -} - -macro_rules! impl_cr_nz_type { - ($impl: path, $fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn(&self, icb: &mut I) -> IcbFnResult { - $impl(icb, unsafe { self.rd.nzx }, unsafe { self.rs2.nzx }); - let pcu = ProgramCounterUpdate::Next(self.width); - icb.ok(pcu) - } - }; -} - -macro_rules! impl_fcss_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.$fn(self.imm, unsafe { self.rs2.f }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_csr_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(self.csr, unsafe { self.rs1.x }, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_csr_imm_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(self.csr, self.imm as u64, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_f_x_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.x }, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; - - ($fn:ident, rm) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.x }, self.rm, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_x_f_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; - - ($fn:ident, rm) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, self.rm, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; -} - -macro_rules! impl_f_r_type { - ($fn: ident) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, unsafe { self.rs2.f }, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; - - ($fn: ident, (rd, x)) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, unsafe { self.rs2.f }, unsafe { self.rd.x }) - .map(|_| Next(self.width)) - } - }; - - ($fn: ident, rm) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, self.rm, unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; - - ($fn: ident, (rs2, f), $($field: ident),+) => { - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn $fn( - &self, - core: &mut MachineCoreState, - ) -> Result, Exception> { - core.hart - .$fn(unsafe { self.rs1.f }, unsafe { self.rs2.f }, $(self.$field,)* unsafe { self.rd.f }) - .map(|_| Next(self.width)) - } - }; -} - -impl Args { - // RV64I R-type instructions - impl_r_type!(integer::run_add, run_add, non_zero); - impl_r_type!(integer::run_sub, run_sub, non_zero); - impl_r_type!(integer::run_x64_xor, run_x64_xor, non_zero); - impl_r_type!(integer::run_and, run_and, non_zero); - impl_r_type!(integer::run_or, run_or, non_zero); - impl_r_type!(run_shift_left, Left); - impl_r_type!(run_shift_right_unsigned, RightUnsigned); - impl_r_type!(run_shift_right_signed, RightSigned); - impl_r_type!( - integer::run_set_less_than_signed, - run_set_less_than_signed, - non_zero_rd - ); - impl_r_type!( - integer::run_set_less_than_unsigned, - run_set_less_than_unsigned, - non_zero_rd - ); - impl_r_type!(integer::run_add_word, run_add_word, non_zero_rd); - impl_r_type!(integer::run_sub_word, run_sub_word, non_zero_rd); - impl_x32_shift_type!(Left, run_x32_shift_left, reg); - impl_x32_shift_type!(RightUnsigned, run_x32_shift_right_unsigned, reg); - impl_x32_shift_type!(RightSigned, run_x32_shift_right_signed, reg); - - // RV64I I-type instructions - impl_i_type!(integer::run_addi, run_addi, non_zero); - impl_i_type!( - integer::run_add_word_immediate, - run_add_word_immediate, - non_zero_rd - ); - impl_i_type!( - integer::run_x64_xor_immediate, - run_x64_xor_immediate, - non_zero - ); - impl_i_type!( - integer::run_x64_or_immediate, - run_x64_or_immediate, - non_zero - ); - impl_i_type!(integer::run_andi, run_andi, non_zero); - impl_i_type!(run_shift_left_immediate, Shift::Left); - impl_i_type!(run_shift_right_immediate_unsigned, Shift::RightUnsigned); - impl_i_type!(run_shift_right_immediate_signed, Shift::RightSigned); - impl_x32_shift_type!(Left, run_x32_shift_left_imm, imm); - impl_x32_shift_type!(RightUnsigned, run_x32_shift_right_imm_unsigned, imm); - impl_x32_shift_type!(RightSigned, run_x32_shift_right_imm_signed, imm); - impl_i_type!( - integer::run_set_less_than_immediate_signed, - run_set_less_than_immediate_signed, - non_zero_rd - ); - impl_i_type!( - integer::run_set_less_than_immediate_unsigned, - run_set_less_than_immediate_unsigned, - non_zero_rd - ); - impl_load_type!(run_x8_load_unsigned, LoadStoreWidth::Byte, false); - impl_load_type!(run_x16_load_unsigned, LoadStoreWidth::Half, false); - impl_load_type!(run_x32_load_unsigned, LoadStoreWidth::Word, false); - impl_load_type!(run_x64_load_signed, LoadStoreWidth::Double, true); - impl_load_type!(run_x32_load_signed, LoadStoreWidth::Word, true); - impl_load_type!(run_x16_load_signed, LoadStoreWidth::Half, true); - impl_load_type!(run_x8_load_signed, LoadStoreWidth::Byte, true); - - // RV64I S-type instructions - impl_store_type!(run_x64_store, LoadStoreWidth::Double); - impl_store_type!(run_x32_store, LoadStoreWidth::Word); - impl_store_type!(run_x16_store, LoadStoreWidth::Half); - impl_store_type!(run_x8_store, LoadStoreWidth::Byte); - - // Branching instructions - impl_branch!(run_branch_equal, Predicate::Equal); - impl_branch!(run_branch_not_equal, Predicate::NotEqual); - impl_branch!(run_branch_less_than_signed, Predicate::LessThanSigned); - impl_branch!(run_branch_less_than_unsigned, Predicate::LessThanUnsigned); - impl_branch!( - run_branch_greater_than_or_equal_signed, - Predicate::GreaterThanOrEqualSigned - ); - impl_branch!( - run_branch_greater_than_or_equal_unsigned, - Predicate::GreaterThanOrEqualUnsigned - ); - impl_branch_compare_zero!(run_branch_equal_zero, Predicate::Equal); - impl_branch_compare_zero!(run_branch_not_equal_zero, Predicate::NotEqual); - impl_branch_compare_zero!(run_branch_less_than_zero, Predicate::LessThanSigned); - impl_branch_compare_zero!( - run_branch_greater_than_or_equal_zero, - Predicate::GreaterThanOrEqualSigned - ); - impl_branch_compare_zero!( - run_branch_less_than_equal_zero, - Predicate::LessThanOrEqualSigned - ); - impl_branch_compare_zero!(run_branch_greater_than_zero, Predicate::GreaterThanSigned); - - // RV64I U-type instructions - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_add_immediate_to_pc(&self, icb: &mut I) -> IcbFnResult { - branching::run_add_immediate_to_pc(icb, self.imm, unsafe { self.rd.nzx }); - icb.ok(Next(self.width)) - } - - // RV64A atomic instructions - impl_amo_type!(run_lrw); - impl_amo_type!(run_scw); - impl_amo_type!(run_amoswapw); - impl_amo_type!(run_amoaddw); - impl_amo_type!(run_amoxorw); - impl_amo_type!(run_amoandw); - impl_amo_type!(run_amoorw); - impl_amo_type!(run_amominw); - impl_amo_type!(run_amomaxw); - impl_amo_type!(run_amominuw); - impl_amo_type!(run_amomaxuw); - impl_amo_type!(run_lrd); - impl_amo_type!(run_scd); - impl_amo_type!(run_amoswapd); - impl_amo_type!(atomics::run_x64_atomic_add, run_x64_atomic_add); - impl_amo_type!(run_amoxord); - impl_amo_type!(run_amoandd); - impl_amo_type!(run_amoord); - impl_amo_type!(run_amomind); - impl_amo_type!(run_amomaxd); - impl_amo_type!(run_amominud); - impl_amo_type!(run_amomaxud); - - // RV64M multiplication and division instructions - impl_r_type!(run_rem); - impl_r_type!(run_remu); - impl_r_type!(run_remw); - impl_r_type!(run_remuw); - impl_r_type!(integer::run_x64_div_signed, run_x64_div_signed, non_zero_rd); - impl_r_type!(run_divu); - impl_r_type!(run_divw); - impl_r_type!(run_divuw); - impl_r_type!(integer::run_mul, run_mul, non_zero); - impl_x64_mul_high_type!(run_x64_mul_high_signed, Signed); - impl_x64_mul_high_type!(run_x64_mul_high_signed_unsigned, SignedUnsigned); - impl_x64_mul_high_type!(run_x64_mul_high_unsigned, Unsigned); - impl_r_type!(integer::run_x32_mul, run_x32_mul, non_zero_rd); - - // RV64F instructions - impl_fload_type!(run_flw); - impl_fstore_type!(run_fsw); - impl_f_r_type!(run_feq_s, (rd, x)); - impl_f_r_type!(run_fle_s, (rd, x)); - impl_f_r_type!(run_flt_s, (rd, x)); - impl_f_r_type!(run_fadd_s, (rs2, f), rm); - impl_f_r_type!(run_fsub_s, (rs2, f), rm); - impl_f_r_type!(run_fmul_s, (rs2, f), rm); - impl_f_r_type!(run_fdiv_s, (rs2, f), rm); - impl_f_r_type!(run_fsqrt_s, rm); - impl_f_r_type!(run_fmin_s); - impl_f_r_type!(run_fmax_s); - impl_f_r_type!(run_fsgnj_s); - impl_f_r_type!(run_fsgnjn_s); - impl_f_r_type!(run_fsgnjx_s); - impl_f_r_type!(run_fmadd_s, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fmsub_s, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fnmsub_s, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fnmadd_s, (rs2, f), rs3f, rm); - impl_x_f_type!(run_fclass_s); - impl_x_f_type!(run_fmv_x_w); - impl_f_x_type!(run_fmv_w_x); - impl_f_x_type!(run_fcvt_s_w, rm); - impl_f_x_type!(run_fcvt_s_wu, rm); - impl_f_x_type!(run_fcvt_s_l, rm); - impl_f_x_type!(run_fcvt_s_lu, rm); - impl_x_f_type!(run_fcvt_w_s, rm); - impl_x_f_type!(run_fcvt_wu_s, rm); - impl_x_f_type!(run_fcvt_l_s, rm); - impl_x_f_type!(run_fcvt_lu_s, rm); - - // RV64D instructions - impl_fload_type!(run_fld); - impl_fstore_type!(run_fsd); - impl_f_r_type!(run_feq_d, (rd, x)); - impl_f_r_type!(run_fle_d, (rd, x)); - impl_f_r_type!(run_flt_d, (rd, x)); - impl_f_r_type!(run_fadd_d, (rs2, f), rm); - impl_f_r_type!(run_fsub_d, (rs2, f), rm); - impl_f_r_type!(run_fmul_d, (rs2, f), rm); - impl_f_r_type!(run_fdiv_d, (rs2, f), rm); - impl_f_r_type!(run_fsqrt_d, rm); - impl_f_r_type!(run_fmin_d); - impl_f_r_type!(run_fmax_d); - impl_f_r_type!(run_fsgnj_d); - impl_f_r_type!(run_fsgnjn_d); - impl_f_r_type!(run_fsgnjx_d); - impl_f_r_type!(run_fcvt_d_s, rm); - impl_f_r_type!(run_fcvt_s_d, rm); - impl_f_r_type!(run_fmadd_d, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fmsub_d, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fnmsub_d, (rs2, f), rs3f, rm); - impl_f_r_type!(run_fnmadd_d, (rs2, f), rs3f, rm); - impl_x_f_type!(run_fclass_d); - impl_f_x_type!(run_fcvt_d_w, rm); - impl_f_x_type!(run_fcvt_d_wu, rm); - impl_f_x_type!(run_fcvt_d_l, rm); - impl_f_x_type!(run_fcvt_d_lu, rm); - impl_x_f_type!(run_fcvt_w_d, rm); - impl_x_f_type!(run_fcvt_wu_d, rm); - impl_x_f_type!(run_fcvt_l_d, rm); - impl_x_f_type!(run_fcvt_lu_d, rm); - impl_x_f_type!(run_fmv_x_d); - impl_f_x_type!(run_fmv_d_x); - - // Zicsr instructions - impl_csr_type!(run_csrrw); - impl_csr_type!(run_csrrs); - impl_csr_type!(run_csrrc); - impl_csr_imm_type!(run_csrrwi); - impl_csr_imm_type!(run_csrrsi); - impl_csr_imm_type!(run_csrrci); - - // RV32C compressed instructions - impl_cr_nz_type!(integer::run_mv, run_mv); - impl_cr_nz_type!(integer::run_neg, run_neg); - impl_ci_type!(load_store::run_li, run_li, non_zero); - - fn run_j(&self, icb: &mut I) -> IcbFnResult { - let addr = branching::run_j(icb, self.imm); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - fn run_j_absolute(&self, icb: &mut I) -> IcbFnResult { - let addr = branching::run_j_absolute(icb, self.imm); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jr(&self, icb: &mut I) -> IcbFnResult { - let rs1 = unsafe { self.rs1.nzx }; - let addr = branching::run_jr(icb, rs1); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jr_imm(&self, icb: &mut I) -> IcbFnResult { - let rs1 = unsafe { self.rs1.nzx }; - let addr = branching::run_jr_imm(icb, self.imm, rs1); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jal(&self, icb: &mut I) -> IcbFnResult { - let rd = unsafe { self.rd.nzx }; - let addr = branching::run_jal(icb, self.imm, rd, self.width); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jalr(&self, icb: &mut I) -> IcbFnResult { - let rd = unsafe { self.rd.nzx }; - let rs1 = unsafe { self.rs1.nzx }; - let addr = branching::run_jalr(icb, rd, rs1, self.width); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jalr_imm(&self, icb: &mut I) -> IcbFnResult { - let rs1 = unsafe { self.rs1.nzx }; - let rd = unsafe { self.rd.nzx }; - let addr = branching::run_jalr_imm(icb, self.imm, rs1, rd, self.width); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - /// SAFETY: This function must only be called on an `Args` belonging - /// to the same OpCode as the OpCode used to derive this function. - unsafe fn run_jalr_absolute(&self, icb: &mut I) -> IcbFnResult { - let rd = unsafe { self.rd.nzx }; - let addr = branching::run_jalr_absolute(icb, self.imm, rd, self.width); - let pcu = ProgramCounterUpdate::Set(addr); - icb.ok(pcu) - } - - fn run_nop(&self, icb: &mut I) -> IcbFnResult { - integer::run_nop(icb); - let pcu = ProgramCounterUpdate::Next(self.width); - icb.ok(pcu) - } - - fn run_ecall(&self, icb: &mut I) -> IcbFnResult { - icb.ecall() - } - - // RV64C compressed instructions - impl_fload_type!(run_cfld); - impl_cfload_sp_type!(run_cfldsp); - impl_fstore_type!(run_cfsd); - impl_fcss_type!(run_cfsdsp); - - // Unknown - fn run_illegal(&self, icb: &mut I) -> IcbFnResult { - icb.err_illegal_instruction() - } -} - -impl From<&InstrCacheable> for Instruction { - fn from(value: &InstrCacheable) -> Self { - match value { - // RV64I R-type instructions - InstrCacheable::Add(args) => Instruction::from_ic_add(args), - InstrCacheable::Sub(args) => Instruction::from_ic_sub(args), - InstrCacheable::Xor(args) => Instruction::from_ic_xor(args), - InstrCacheable::Or(args) => Instruction::from_ic_or(args), - InstrCacheable::And(args) => Instruction::from_ic_and(args), - InstrCacheable::Sll(args) => Instruction::from_ic_sll(args), - InstrCacheable::Srl(args) => Instruction::from_ic_srl(args), - InstrCacheable::Sra(args) => Instruction::from_ic_sra(args), - InstrCacheable::Slt(args) => { - Instruction::new_set_less_than_signed(args.rd, args.rs1, args.rs2) - } - InstrCacheable::Sltu(args) => { - Instruction::new_set_less_than_unsigned(args.rd, args.rs1, args.rs2) - } - InstrCacheable::Addw(args) => { - Instruction::new_add_word(args.rd, args.rs1, args.rs2, InstrWidth::Uncompressed) - } - InstrCacheable::Subw(args) => { - Instruction::new_sub_word(args.rd, args.rs1, args.rs2, InstrWidth::Uncompressed) - } - InstrCacheable::Sllw(args) => Instruction::new_x32_shift_left( - args.rd, - args.rs1, - args.rs2, - InstrWidth::Uncompressed, - ), - InstrCacheable::Srlw(args) => Instruction::new_x32_shift_right_unsigned( - args.rd, - args.rs1, - args.rs2, - InstrWidth::Uncompressed, - ), - InstrCacheable::Sraw(args) => Instruction::new_x32_shift_right_signed( - args.rd, - args.rs1, - args.rs2, - InstrWidth::Uncompressed, - ), - - // RV64I I-type instructions - InstrCacheable::Addi(args) => Instruction::from_ic_addi(args), - InstrCacheable::Addiw(args) => Instruction::new_add_word_immediate( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Xori(args) => Instruction::from_ic_xori(args), - InstrCacheable::Ori(args) => Instruction::from_ic_ori(args), - InstrCacheable::Andi(args) => Instruction::from_ic_andi(args), - InstrCacheable::Slli(args) => Instruction::from_ic_slli(args), - InstrCacheable::Srli(args) => Instruction::from_ic_srli(args), - InstrCacheable::Srai(args) => Instruction::from_ic_srai(args), - InstrCacheable::Slliw(args) => Instruction::from_ic_x32_shift_left_immediate(args), - InstrCacheable::Srliw(args) => { - Instruction::from_ic_x32_shift_right_immediate_unsigned(args) - } - InstrCacheable::Sraiw(args) => { - Instruction::from_ic_x32_shift_right_immediate_signed(args) - } - InstrCacheable::Slti(args) => { - Instruction::new_set_less_than_immediate_signed(args.rd, args.rs1, args.imm) - } - InstrCacheable::Sltiu(args) => { - Instruction::new_set_less_than_immediate_unsigned(args.rd, args.rs1, args.imm) - } - InstrCacheable::Lb(args) => Instruction::new_x8_load_signed( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Lh(args) => Instruction::new_x16_load_signed( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Lw(args) => Instruction::new_x32_load_signed( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Lbu(args) => Instruction::new_x8_load_unsigned( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Lhu(args) => Instruction::new_x16_load_unsigned( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Lwu(args) => Instruction::new_x32_load_unsigned( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - InstrCacheable::Ld(args) => Instruction::new_x64_load_signed( - args.rd, - args.rs1, - args.imm, - InstrWidth::Uncompressed, - ), - // RV64I S-type instructions - InstrCacheable::Sb(args) => { - Instruction::new_x8_store(args.rs1, args.rs2, args.imm, InstrWidth::Uncompressed) - } - InstrCacheable::Sh(args) => { - Instruction::new_x16_store(args.rs1, args.rs2, args.imm, InstrWidth::Uncompressed) - } - InstrCacheable::Sw(args) => { - Instruction::new_x32_store(args.rs1, args.rs2, args.imm, InstrWidth::Uncompressed) - } - InstrCacheable::Sd(args) => { - Instruction::new_x64_store(args.rs1, args.rs2, args.imm, InstrWidth::Uncompressed) - } - - // RV64I B-type instructions - InstrCacheable::Beq(args) => Instruction::from_ic_beq(args), - InstrCacheable::Bne(args) => Instruction::from_ic_bne(args), - InstrCacheable::Blt(args) => Instruction::from_ic_blt(args), - InstrCacheable::Bge(args) => Instruction::from_ic_bge(args), - InstrCacheable::Bltu(args) => Instruction::from_ic_bltu(args), - InstrCacheable::Bgeu(args) => Instruction::from_ic_bgeu(args), - - // RV64I U-type instructions - InstrCacheable::Lui(args) => { - Instruction::new_li(args.rd, args.imm, InstrWidth::Uncompressed) - } - InstrCacheable::Auipc(args) => { - Instruction::new_add_immediate_to_pc(args.rd, args.imm, InstrWidth::Uncompressed) - } - - // RV64I jump instructions - InstrCacheable::Jal(args) => Instruction::from_ic_jal(args), - InstrCacheable::Jalr(args) => Instruction::from_ic_jalr(args), - - // RV64A atomic instructions - InstrCacheable::Lrw(args) => Instruction { - opcode: OpCode::Lrw, - args: args.into(), - }, - InstrCacheable::Scw(args) => Instruction { - opcode: OpCode::Scw, - args: args.into(), - }, - InstrCacheable::Amoswapw(args) => Instruction { - opcode: OpCode::Amoswapw, - args: args.into(), - }, - InstrCacheable::Amoaddw(args) => Instruction { - opcode: OpCode::Amoaddw, - args: args.into(), - }, - InstrCacheable::Amoxorw(args) => Instruction { - opcode: OpCode::Amoxorw, - args: args.into(), - }, - InstrCacheable::Amoandw(args) => Instruction { - opcode: OpCode::Amoandw, - args: args.into(), - }, - InstrCacheable::Amoorw(args) => Instruction { - opcode: OpCode::Amoorw, - args: args.into(), - }, - InstrCacheable::Amominw(args) => Instruction { - opcode: OpCode::Amominw, - args: args.into(), - }, - InstrCacheable::Amomaxw(args) => Instruction { - opcode: OpCode::Amomaxw, - args: args.into(), - }, - InstrCacheable::Amominuw(args) => Instruction { - opcode: OpCode::Amominuw, - args: args.into(), - }, - InstrCacheable::Amomaxuw(args) => Instruction { - opcode: OpCode::Amomaxuw, - args: args.into(), - }, - InstrCacheable::Lrd(args) => Instruction { - opcode: OpCode::Lrd, - args: args.into(), - }, - InstrCacheable::Scd(args) => Instruction { - opcode: OpCode::Scd, - args: args.into(), - }, - InstrCacheable::Amoswapd(args) => Instruction { - opcode: OpCode::Amoswapd, - args: args.into(), - }, - InstrCacheable::Amoaddd(args) => Instruction::new_x64_atomic_add( - args.rd, - args.rs1, - args.rs2, - args.aq, - args.rl, - InstrWidth::Uncompressed, - ), - InstrCacheable::Amoxord(args) => Instruction { - opcode: OpCode::Amoxord, - args: args.into(), - }, - InstrCacheable::Amoandd(args) => Instruction { - opcode: OpCode::Amoandd, - args: args.into(), - }, - InstrCacheable::Amoord(args) => Instruction { - opcode: OpCode::Amoord, - args: args.into(), - }, - InstrCacheable::Amomind(args) => Instruction { - opcode: OpCode::Amomind, - args: args.into(), - }, - InstrCacheable::Amomaxd(args) => Instruction { - opcode: OpCode::Amomaxd, - args: args.into(), - }, - InstrCacheable::Amominud(args) => Instruction { - opcode: OpCode::Amominud, - args: args.into(), - }, - InstrCacheable::Amomaxud(args) => Instruction { - opcode: OpCode::Amomaxud, - args: args.into(), - }, - - // RV64M multiplication and division instructions - InstrCacheable::Rem(args) => Instruction { - opcode: OpCode::Rem, - args: args.into(), - }, - InstrCacheable::Remu(args) => Instruction { - opcode: OpCode::Remu, - args: args.into(), - }, - InstrCacheable::Remw(args) => Instruction { - opcode: OpCode::Remw, - args: args.into(), - }, - InstrCacheable::Remuw(args) => Instruction { - opcode: OpCode::Remuw, - args: args.into(), - }, - InstrCacheable::Div(args) => Instruction::from_ic_div(args), - InstrCacheable::Divu(args) => Instruction { - opcode: OpCode::Divu, - args: args.into(), - }, - InstrCacheable::Divw(args) => Instruction { - opcode: OpCode::Divw, - args: args.into(), - }, - InstrCacheable::Divuw(args) => Instruction { - opcode: OpCode::Divuw, - args: args.into(), - }, - InstrCacheable::Mul(args) => Instruction::from_ic_mul(args), - InstrCacheable::Mulh(args) => Instruction::from_ic_mulh(args), - InstrCacheable::Mulhsu(args) => Instruction::from_ic_mulhsu(args), - InstrCacheable::Mulhu(args) => Instruction::from_ic_mulhu(args), - InstrCacheable::Mulw(args) => { - Instruction::new_x32_mul(args.rd, args.rs1, args.rs2, InstrWidth::Uncompressed) - } - - // RV64F instructions - InstrCacheable::Flw(args) => Instruction { - opcode: OpCode::Flw, - args: args.to_args(InstrWidth::Uncompressed), - }, - InstrCacheable::Fsw(args) => Instruction { - opcode: OpCode::Fsw, - args: args.to_args(InstrWidth::Uncompressed), - }, - InstrCacheable::Feqs(args) => Instruction { - opcode: OpCode::Feqs, - args: args.into(), - }, - InstrCacheable::Fles(args) => Instruction { - opcode: OpCode::Fles, - args: args.into(), - }, - InstrCacheable::Flts(args) => Instruction { - opcode: OpCode::Flts, - args: args.into(), - }, - InstrCacheable::Fadds(args) => Instruction { - opcode: OpCode::Fadds, - args: args.into(), - }, - InstrCacheable::Fsubs(args) => Instruction { - opcode: OpCode::Fsubs, - args: args.into(), - }, - InstrCacheable::Fmuls(args) => Instruction { - opcode: OpCode::Fmuls, - args: args.into(), - }, - InstrCacheable::Fdivs(args) => Instruction { - opcode: OpCode::Fdivs, - args: args.into(), - }, - InstrCacheable::Fsqrts(args) => Instruction { - opcode: OpCode::Fsqrts, - args: args.into(), - }, - InstrCacheable::Fmins(args) => Instruction { - opcode: OpCode::Fmins, - args: args.into(), - }, - InstrCacheable::Fmaxs(args) => Instruction { - opcode: OpCode::Fmaxs, - args: args.into(), - }, - InstrCacheable::Fsgnjs(args) => Instruction { - opcode: OpCode::Fsgnjs, - args: args.into(), - }, - InstrCacheable::Fsgnjns(args) => Instruction { - opcode: OpCode::Fsgnjns, - args: args.into(), - }, - InstrCacheable::Fsgnjxs(args) => Instruction { - opcode: OpCode::Fsgnjxs, - args: args.into(), - }, - InstrCacheable::Fmadds(args) => Instruction { - opcode: OpCode::Fmadds, - args: args.into(), - }, - InstrCacheable::Fmsubs(args) => Instruction { - opcode: OpCode::Fmsubs, - args: args.into(), - }, - InstrCacheable::Fnmsubs(args) => Instruction { - opcode: OpCode::Fnmsubs, - args: args.into(), - }, - InstrCacheable::Fnmadds(args) => Instruction { - opcode: OpCode::Fnmadds, - args: args.into(), - }, - InstrCacheable::FclassS(args) => Instruction { - opcode: OpCode::FclassS, - args: args.into(), - }, - InstrCacheable::FmvXW(args) => Instruction { - opcode: OpCode::FmvXW, - args: args.into(), - }, - InstrCacheable::FmvWX(args) => Instruction { - opcode: OpCode::FmvWX, - args: args.into(), - }, - InstrCacheable::Fcvtsw(args) => Instruction { - opcode: OpCode::Fcvtsw, - args: args.into(), - }, - InstrCacheable::Fcvtswu(args) => Instruction { - opcode: OpCode::Fcvtswu, - args: args.into(), - }, - InstrCacheable::Fcvtsl(args) => Instruction { - opcode: OpCode::Fcvtsl, - args: args.into(), - }, - InstrCacheable::Fcvtslu(args) => Instruction { - opcode: OpCode::Fcvtslu, - args: args.into(), - }, - InstrCacheable::Fcvtws(args) => Instruction { - opcode: OpCode::Fcvtws, - args: args.into(), - }, - InstrCacheable::Fcvtwus(args) => Instruction { - opcode: OpCode::Fcvtwus, - args: args.into(), - }, - InstrCacheable::Fcvtls(args) => Instruction { - opcode: OpCode::Fcvtls, - args: args.into(), - }, - InstrCacheable::Fcvtlus(args) => Instruction { - opcode: OpCode::Fcvtlus, - args: args.into(), - }, - - // RV64D instructions - InstrCacheable::Fld(args) => Instruction { - opcode: OpCode::Fld, - args: args.to_args(InstrWidth::Uncompressed), - }, - InstrCacheable::Fsd(args) => Instruction { - opcode: OpCode::Fsd, - args: args.to_args(InstrWidth::Uncompressed), - }, - InstrCacheable::Feqd(args) => Instruction { - opcode: OpCode::Feqd, - args: args.into(), - }, - InstrCacheable::Fled(args) => Instruction { - opcode: OpCode::Fled, - args: args.into(), - }, - InstrCacheable::Fltd(args) => Instruction { - opcode: OpCode::Fltd, - args: args.into(), - }, - InstrCacheable::Faddd(args) => Instruction { - opcode: OpCode::Faddd, - args: args.into(), - }, - InstrCacheable::Fsubd(args) => Instruction { - opcode: OpCode::Fsubd, - args: args.into(), - }, - InstrCacheable::Fmuld(args) => Instruction { - opcode: OpCode::Fmuld, - args: args.into(), - }, - InstrCacheable::Fdivd(args) => Instruction { - opcode: OpCode::Fdivd, - args: args.into(), - }, - InstrCacheable::Fsqrtd(args) => Instruction { - opcode: OpCode::Fsqrtd, - args: args.into(), - }, - InstrCacheable::Fmind(args) => Instruction { - opcode: OpCode::Fmind, - args: args.into(), - }, - InstrCacheable::Fmaxd(args) => Instruction { - opcode: OpCode::Fmaxd, - args: args.into(), - }, - InstrCacheable::Fsgnjd(args) => Instruction { - opcode: OpCode::Fsgnjd, - args: args.into(), - }, - InstrCacheable::Fsgnjnd(args) => Instruction { - opcode: OpCode::Fsgnjnd, - args: args.into(), - }, - InstrCacheable::Fsgnjxd(args) => Instruction { - opcode: OpCode::Fsgnjxd, - args: args.into(), - }, - InstrCacheable::Fcvtds(args) => Instruction { - opcode: OpCode::Fcvtds, - args: args.into(), - }, - InstrCacheable::Fcvtsd(args) => Instruction { - opcode: OpCode::Fcvtsd, - args: args.into(), - }, - InstrCacheable::Fmaddd(args) => Instruction { - opcode: OpCode::Fmaddd, - args: args.into(), - }, - InstrCacheable::Fmsubd(args) => Instruction { - opcode: OpCode::Fmsubd, - args: args.into(), - }, - InstrCacheable::Fnmsubd(args) => Instruction { - opcode: OpCode::Fnmsubd, - args: args.into(), - }, - InstrCacheable::Fnmaddd(args) => Instruction { - opcode: OpCode::Fnmaddd, - args: args.into(), - }, - InstrCacheable::FclassD(args) => Instruction { - opcode: OpCode::FclassD, - args: args.into(), - }, - InstrCacheable::Fcvtdw(args) => Instruction { - opcode: OpCode::Fcvtdw, - args: args.into(), - }, - InstrCacheable::Fcvtdwu(args) => Instruction { - opcode: OpCode::Fcvtdwu, - args: args.into(), - }, - InstrCacheable::Fcvtdl(args) => Instruction { - opcode: OpCode::Fcvtdl, - args: args.into(), - }, - InstrCacheable::Fcvtdlu(args) => Instruction { - opcode: OpCode::Fcvtdlu, - args: args.into(), - }, - InstrCacheable::Fcvtwd(args) => Instruction { - opcode: OpCode::Fcvtwd, - args: args.into(), - }, - InstrCacheable::Fcvtwud(args) => Instruction { - opcode: OpCode::Fcvtwud, - args: args.into(), - }, - InstrCacheable::Fcvtld(args) => Instruction { - opcode: OpCode::Fcvtld, - args: args.into(), - }, - InstrCacheable::Fcvtlud(args) => Instruction { - opcode: OpCode::Fcvtlud, - args: args.into(), - }, - InstrCacheable::FmvXD(args) => Instruction { - opcode: OpCode::FmvXD, - args: args.into(), - }, - InstrCacheable::FmvDX(args) => Instruction { - opcode: OpCode::FmvDX, - args: args.into(), - }, - - // Zicsr instructions - InstrCacheable::Csrrw(args) => Instruction { - opcode: OpCode::Csrrw, - args: args.into(), - }, - InstrCacheable::Csrrs(args) => Instruction { - opcode: OpCode::Csrrs, - args: args.into(), - }, - InstrCacheable::Csrrc(args) => Instruction { - opcode: OpCode::Csrrc, - args: args.into(), - }, - InstrCacheable::Csrrwi(args) => Instruction { - opcode: OpCode::Csrrwi, - args: args.into(), - }, - InstrCacheable::Csrrsi(args) => Instruction { - opcode: OpCode::Csrrsi, - args: args.into(), - }, - InstrCacheable::Csrrci(args) => Instruction { - opcode: OpCode::Csrrci, - args: args.into(), - }, - - // RV32C compressed instructions - InstrCacheable::CLw(args) => { - debug_assert!(args.imm >= 0 && args.imm % 4 == 0); - Instruction::new_x32_load_signed( - args.rd.into(), - args.rs1.into(), - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CLwsp(args) => { - debug_assert!(args.imm >= 0 && args.imm % 4 == 0); - Instruction::new_x32_load_signed( - args.rd_rs1.into(), - sp, - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CSw(args) => { - debug_assert!(args.imm >= 0 && args.imm % 4 == 0); - Instruction::new_x32_store( - args.rs1.into(), - args.rs2.into(), - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CSwsp(args) => { - debug_assert!(args.imm >= 0 && args.imm % 4 == 0); - Instruction::new_x32_store(sp, args.rs2, args.imm, InstrWidth::Compressed) - } - InstrCacheable::CJ(args) => Instruction::new_j(args.imm, InstrWidth::Compressed), - InstrCacheable::CJr(args) => Instruction::new_jr(args.rs1, InstrWidth::Compressed), - InstrCacheable::CJalr(args) => { - Instruction::new_jalr(nz::ra, args.rs1, InstrWidth::Compressed) - } - InstrCacheable::CBeqz(args) => Instruction::from_ic_cbeqz(args), - InstrCacheable::CBnez(args) => Instruction::from_ic_cbnez(args), - InstrCacheable::CLi(args) => { - Instruction::new_li(args.rd_rs1, args.imm, InstrWidth::Compressed) - } - InstrCacheable::CLui(args) => { - Instruction::new_li(args.rd_rs1, args.imm, InstrWidth::Compressed) - } - InstrCacheable::CAddi(args) => { - Instruction::new_addi(args.rd_rs1, args.rd_rs1, args.imm, InstrWidth::Compressed) - } - InstrCacheable::CAddi16sp(args) => Instruction::new_addi( - NonZeroXRegister::x2, - NonZeroXRegister::x2, - args.imm, - InstrWidth::Compressed, - ), - InstrCacheable::CAddi4spn(args) => Instruction::from_ic_caddi4spn(args), - InstrCacheable::CSlli(args) => Instruction::new_shift_left_immediate( - args.rd_rs1, - args.rd_rs1, - args.imm, - InstrWidth::Compressed, - ), - InstrCacheable::CSrli(args) => Instruction::from_ic_csrli(args), - InstrCacheable::CSrai(args) => Instruction::from_ic_csrai(args), - InstrCacheable::CAndi(args) => Instruction::from_ic_candi(args), - InstrCacheable::CMv(args) => { - Instruction::new_mv(args.rd_rs1, args.rs2, InstrWidth::Compressed) - } - InstrCacheable::CAdd(args) => { - Instruction::new_add(args.rd_rs1, args.rd_rs1, args.rs2, InstrWidth::Compressed) - } - InstrCacheable::CAnd(args) => Instruction::from_ic_cand(args), - InstrCacheable::CXor(args) => Instruction::from_ic_cxor(args), - InstrCacheable::COr(args) => Instruction::from_ic_cor(args), - InstrCacheable::CSub(args) => Instruction::from_ic_csub(args), - InstrCacheable::CNop => Instruction::new_nop(InstrWidth::Compressed), - - // RV64C compressed instructions - InstrCacheable::CLd(args) => { - debug_assert!(args.imm >= 0 && args.imm % 8 == 0); - Instruction::new_x64_load_signed( - args.rd.into(), - args.rs1.into(), - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CLdsp(args) => { - debug_assert!(args.imm >= 0 && args.imm % 8 == 0); - Instruction::new_x64_load_signed( - args.rd_rs1.into(), - sp, - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CSd(args) => { - debug_assert!(args.imm >= 0 && args.imm % 8 == 0); - Instruction::new_x64_store( - args.rs1.into(), - args.rs2.into(), - args.imm, - InstrWidth::Compressed, - ) - } - InstrCacheable::CSdsp(args) => { - debug_assert!(args.imm >= 0 && args.imm % 8 == 0); - Instruction::new_x64_store(sp, args.rs2, args.imm, InstrWidth::Compressed) - } - InstrCacheable::CAddiw(args) => Instruction::new_add_word_immediate( - args.rd_rs1, - args.rd_rs1.into(), - args.imm, - InstrWidth::Compressed, - ), - InstrCacheable::CAddw(args) => Instruction::from_ic_caddw(args), - InstrCacheable::CSubw(args) => Instruction::from_ic_csubw(args), - - // RV64DC compressed instructions - InstrCacheable::CFld(args) => Instruction { - opcode: OpCode::CFld, - args: args.to_args(InstrWidth::Compressed), - }, - InstrCacheable::CFldsp(args) => Instruction { - opcode: OpCode::CFldsp, - args: args.into(), - }, - InstrCacheable::CFsd(args) => Instruction { - opcode: OpCode::CFsd, - args: args.to_args(InstrWidth::Compressed), - }, - InstrCacheable::CFsdsp(args) => Instruction { - opcode: OpCode::CFsdsp, - args: args.into(), - }, - - InstrCacheable::Unknown { instr: _ } => { - Instruction::new_unknown(InstrWidth::Uncompressed) - } - InstrCacheable::UnknownCompressed { instr: _ } => { - Instruction::new_unknown(InstrWidth::Compressed) - } - - InstrCacheable::Hint { instr: _ } => Instruction::new_nop(InstrWidth::Uncompressed), - InstrCacheable::HintCompressed { instr: _ } => { - Instruction::new_nop(InstrWidth::Compressed) - } - - // Interrupt-Management - InstrCacheable::Wfi => Instruction::new_nop(InstrWidth::Uncompressed), - - InstrCacheable::Ecall => Instruction::new_ecall(), - } - } -} - -impl From<&RTypeArgs> for Args { - fn from(value: &RTypeArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&NonZeroRdRTypeArgs> for Args { - fn from(value: &NonZeroRdRTypeArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&UJTypeArgs> for Args { - fn from(value: &UJTypeArgs) -> Self { - Self { - rd: value.rd.into(), - imm: value.imm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&NonZeroRdUJTypeArgs> for Args { - fn from(value: &NonZeroRdUJTypeArgs) -> Self { - Self { - rd: value.rd.into(), - imm: value.imm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&AmoArgs> for Args { - fn from(value: &AmoArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - aq: value.aq, - rl: value.rl, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&CIBTypeArgs> for Args { - fn from(value: &CIBTypeArgs) -> Self { - Self { - rd: value.rd_rs1.into(), - imm: value.imm, - rs1: value.rd_rs1.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CIBNZTypeArgs> for Args { - fn from(value: &CIBNZTypeArgs) -> Self { - Self { - rd: value.rd_rs1.into(), - imm: value.imm, - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CRTypeArgs> for Args { - fn from(value: &CRTypeArgs) -> Self { - Self { - rd: value.rd_rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CNZRTypeArgs> for Args { - fn from(value: &CNZRTypeArgs) -> Self { - Self { - rd: value.rd_rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CJTypeArgs> for Args { - fn from(value: &CJTypeArgs) -> Self { - Self { - imm: value.imm, - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CRJTypeArgs> for Args { - fn from(value: &CRJTypeArgs) -> Self { - Self { - rs1: value.rs1.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CSSTypeArgs> for Args { - fn from(value: &CSSTypeArgs) -> Self { - Self { - rs2: value.rs2.into(), - imm: value.imm, - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CsrArgs> for Args { - fn from(value: &CsrArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - csr: value.csr, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&CsriArgs> for Args { - fn from(value: &CsriArgs) -> Self { - Self { - rd: value.rd.into(), - imm: value.imm, - csr: value.csr, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl FLoadArgs { - fn to_args(self, width: InstrWidth) -> Args { - Args { - rd: self.rd.into(), - rs1: self.rs1.into(), - imm: self.imm, - width, - ..Args::DEFAULT - } - } -} - -impl FStoreArgs { - fn to_args(self, width: InstrWidth) -> Args { - Args { - rs1: self.rs1.into(), - rs2: self.rs2.into(), - imm: self.imm, - width, - ..Args::DEFAULT - } - } -} - -impl From<&CSSDTypeArgs> for Args { - fn from(value: &CSSDTypeArgs) -> Self { - Self { - imm: value.imm, - rs2: value.rs2.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&CIBDTypeArgs> for Args { - fn from(value: &CIBDTypeArgs) -> Self { - Self { - imm: value.imm, - rd: value.rd_rs1.into(), - width: InstrWidth::Compressed, - ..Self::DEFAULT - } - } -} - -impl From<&XRegToFRegArgs> for Args { - fn from(value: &XRegToFRegArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&XRegToFRegArgsWithRounding> for Args { - fn from(value: &XRegToFRegArgsWithRounding) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rm: value.rm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FRegToXRegArgs> for Args { - fn from(value: &FRegToXRegArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FRegToXRegArgsWithRounding> for Args { - fn from(value: &FRegToXRegArgsWithRounding) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rm: value.rm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FR3ArgsWithRounding> for Args { - fn from(value: &FR3ArgsWithRounding) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - rs3f: value.rs3, - rm: value.rm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FRArgs> for Args { - fn from(value: &FRArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FR1ArgWithRounding> for Args { - fn from(value: &FR1ArgWithRounding) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rm: value.rm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FR2ArgsWithRounding> for Args { - fn from(value: &FR2ArgsWithRounding) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - rm: value.rm, - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -impl From<&FCmpArgs> for Args { - fn from(value: &FCmpArgs) -> Self { - Self { - rd: value.rd.into(), - rs1: value.rs1.into(), - rs2: value.rs2.into(), - width: InstrWidth::Uncompressed, - ..Self::DEFAULT - } - } -} - -#[cfg(test)] -mod test { - use super::Register; - use crate::default::ConstDefault; - use crate::machine_state::registers::FRegister; - use crate::machine_state::registers::NonZeroXRegister; - use crate::machine_state::registers::XRegister; - - // Ensure no undefined behaviour when reading any field from `Register::DEFAULT` - #[test] - #[cfg(miri)] - fn miri_test_register_default_safety() { - let default = Register::DEFAULT; - - assert_eq!(XRegister::x1, unsafe { default.x }); - assert_eq!(NonZeroXRegister::x1, unsafe { default.nzx }); - // FRegisters are not offset by one as f0 is stored in state, unlike x0 - assert_eq!(FRegister::f0, unsafe { default.f }); - } - - // The default register should be valid for all variants - #[test] - fn miri_test_register_default() { - let default = Register::DEFAULT; - - assert!(default == Register { x: XRegister::x1 }); - assert!( - default - == Register { - nzx: NonZeroXRegister::x1 - } - ); - // FRegisters are not offset by one as f0 is stored in state, unlike x0 - assert!(default == Register { f: FRegister::f0 }); - } -} diff --git a/src/riscv/lib/src/machine_state/instruction/constructors.rs b/src/riscv/lib/src/machine_state/instruction/constructors.rs deleted file mode 100644 index 63e27c6d17c99bf965acebb80f8e047783f25e71..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/instruction/constructors.rs +++ /dev/null @@ -1,2096 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::Args; -use super::Instruction; -use super::OpCode; -use crate::default::ConstDefault; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::nz; -use crate::parser::XRegisterParsed; -use crate::parser::instruction::CIBTypeArgs; -use crate::parser::instruction::CRTypeArgs; -use crate::parser::instruction::ITypeArgs; -use crate::parser::instruction::InstrWidth; -use crate::parser::instruction::NonZeroRdITypeArgs; -use crate::parser::instruction::NonZeroRdRTypeArgs; -use crate::parser::instruction::RTypeArgs; -use crate::parser::instruction::SBTypeArgs; -use crate::parser::instruction::SplitITypeArgs; -use crate::parser::instruction::UJTypeArgs; -use crate::parser::split_x0; - -impl Instruction { - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for the [`OpCode::Add`]. - pub(crate) fn new_add( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Add, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::AddWord`]. - pub(crate) fn new_add_word( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::AddWord, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::AddWordImmediate`]. - pub(crate) fn new_add_word_immediate( - rd: NonZeroXRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::AddWordImmediate, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for the [`OpCode::Sub`]. - pub(crate) fn new_sub( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Sub, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::SubWord`]. - pub(crate) fn new_sub_word( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::SubWord, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for the [`OpCode::Neg`]. - pub(crate) fn new_neg(rd: NonZeroXRegister, rs2: NonZeroXRegister, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Neg, - args: Args { - rd: rd.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Mv`]. - pub(crate) fn new_mv(rd: NonZeroXRegister, rs2: NonZeroXRegister, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Mv, - args: Args { - rd: rd.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Li`]. - pub(crate) fn new_li(rd: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Li, - args: Args { - rd: rd.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Nop`]. - pub(crate) fn new_nop(width: InstrWidth) -> Self { - Self { - opcode: OpCode::Nop, - args: Args { - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::Unknown`]. - pub(crate) fn new_unknown(width: InstrWidth) -> Self { - Self { - opcode: OpCode::Unknown, - args: Args { - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Addi`]. - pub(crate) fn new_addi( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Addi, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Andi`]. - pub(crate) fn new_andi( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Andi, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64OrImm`]. - pub(crate) fn new_x64_or_immediate( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64OrImm, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64XorImm`]. - pub(crate) fn new_x64_xor_immediate( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64XorImm, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftLeftImmediate`]. - pub(crate) fn new_shift_left_immediate( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftLeftImmediate, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftRightImmediateUnsigned`]. - pub(crate) fn new_shift_right_immediate_unsigned( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftRightImmediateUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftRightImmediateSigned`]. - pub(crate) fn new_shift_right_immediate_signed( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftRightImmediateSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftLeft`]. - pub(crate) fn new_x32_shift_left( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftLeft, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftRightUnsigned`]. - pub(crate) fn new_x32_shift_right_unsigned( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftRightUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftRightSigned`]. - pub(crate) fn new_x32_shift_right_signed( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftRightSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftLeftImm`]. - pub(crate) fn new_x32_shift_left_immediate( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftLeftImm, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftRightImmUnsigned`]. - pub(crate) fn new_x32_shift_right_immediate_unsigned( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftRightImmUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32ShiftRightImmSigned`]. - pub(crate) fn new_x32_shift_right_immediate_signed( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32ShiftRightImmSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::SetLessThanSigned`]. - pub(crate) fn new_set_less_than_signed( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - ) -> Self { - Self { - opcode: OpCode::SetLessThanSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width: InstrWidth::Uncompressed, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::SetLessThanUnsigned`]. - pub(crate) fn new_set_less_than_unsigned( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - ) -> Self { - Self { - opcode: OpCode::SetLessThanUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width: InstrWidth::Uncompressed, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::SetLessThanImmediateSigned`]. - pub(crate) fn new_set_less_than_immediate_signed( - rd: NonZeroXRegister, - rs1: XRegister, - imm: i64, - ) -> Self { - Self { - opcode: OpCode::SetLessThanImmediateSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width: InstrWidth::Uncompressed, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::SetLessThanImmediateUnsigned`]. - pub(crate) fn new_set_less_than_immediate_unsigned( - rd: NonZeroXRegister, - rs1: XRegister, - imm: i64, - ) -> Self { - Self { - opcode: OpCode::SetLessThanImmediateUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width: InstrWidth::Uncompressed, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::And`]. - pub(crate) fn new_and( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::And, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Or`]. - pub(crate) fn new_or( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Or, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64Xor`]. - pub(crate) fn new_x64_xor( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64Xor, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftLeft`]. - pub(crate) fn new_shift_left( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftLeft, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftRightUnsigned`]. - pub(crate) fn new_shift_right_unsigned( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftRightUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ShiftRightSigned`]. - pub(crate) fn new_shift_right_signed( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::ShiftRightSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::J`]. - pub(crate) fn new_j(imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::J, - args: Args { - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Jal`]. - pub(crate) fn new_jal(rd: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Jal, - args: Args { - rd: rd.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchEqual`]. - pub(crate) fn new_branch_equal( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchEqual, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchEqualZero`]. - pub(crate) fn new_branch_equal_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchEqualZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchNotEqual`]. - pub(crate) fn new_branch_not_equal( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchNotEqual, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchNotEqualZero`]. - pub(crate) fn new_branch_not_equal_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchNotEqualZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::JAbsolute`]. - pub(crate) fn new_j_absolute(imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::JAbsolute, - args: Args { - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::JalrImm`]. - pub(crate) fn new_jalr_imm( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::JalrImm, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::JalrAbsolute`]. - pub(crate) fn new_jalr_absolute(rd: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::JalrAbsolute, - args: Args { - rd: rd.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::JrImm`]. - pub(crate) fn new_jr_imm(rs1: NonZeroXRegister, imm: i64, width: InstrWidth) -> Self { - Self { - opcode: OpCode::JrImm, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Jr`]. - pub(crate) fn new_jr(rs1: NonZeroXRegister, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Jr, - args: Args { - rs1: rs1.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Jalr`]. - pub(crate) fn new_jalr(rd: NonZeroXRegister, rs1: NonZeroXRegister, width: InstrWidth) -> Self { - Self { - opcode: OpCode::Jalr, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64LoadSigned`]. - pub(crate) fn new_x64_load_signed( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64LoadSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64Store`]. - pub(crate) fn new_x64_store( - rs1: XRegister, - rs2: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64Store, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32LoadSigned`]. - pub(crate) fn new_x32_load_signed( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32LoadSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32LoadUnsigned`]. - pub(crate) fn new_x32_load_unsigned( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32LoadUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32Store`]. - pub(crate) fn new_x32_store( - rs1: XRegister, - rs2: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32Store, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X16LoadSigned`]. - pub(crate) fn new_x16_load_signed( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X16LoadSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X16LoadUnsigned`]. - pub(crate) fn new_x16_load_unsigned( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X16LoadUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X16Store`]. - pub(crate) fn new_x16_store( - rs1: XRegister, - rs2: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X16Store, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X8LoadSigned`]. - pub(crate) fn new_x8_load_signed( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X8LoadSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X8LoadUnsigned`]. - pub(crate) fn new_x8_load_unsigned( - rd: XRegister, - rs1: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X8LoadUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X8Store`]. - pub(crate) fn new_x8_store( - rs1: XRegister, - rs2: XRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X8Store, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchLessThanSigned`]. - pub(crate) fn new_branch_less_than_signed( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchLessThanSigned, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchLessThanZero`]. - pub(crate) fn new_branch_less_than_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchLessThanZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchLessThanOrEqualZero`]. - pub(crate) fn new_branch_less_than_or_equal_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchLessThanOrEqualZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchGreaterThanOrEqualSigned`]. - pub(crate) fn new_branch_greater_than_or_equal_signed( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchGreaterThanOrEqualSigned, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchGreaterThanOrEqualZero`]. - pub(crate) fn new_branch_greater_than_or_equal_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchGreaterThanOrEqualZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchGreaterThanZero`]. - pub(crate) fn new_branch_greater_than_zero( - rs1: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchGreaterThanZero, - args: Args { - rs1: rs1.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchLessThanUnsigned`]. - pub(crate) fn new_branch_less_than_unsigned( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchLessThanUnsigned, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for - /// [`OpCode::BranchGreaterThanOrEqualUnsigned`]. - pub(crate) fn new_branch_greater_than_or_equal_unsigned( - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::BranchGreaterThanOrEqualUnsigned, - args: Args { - rs1: rs1.into(), - rs2: rs2.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::AddImmediateToPC`]. - pub(crate) fn new_add_immediate_to_pc( - rd: NonZeroXRegister, - imm: i64, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::AddImmediateToPC, - args: Args { - rd: rd.into(), - imm, - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Mul`]. - pub(crate) fn new_mul( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Mul, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X32Mul`]. - pub(crate) fn new_x32_mul( - rd: XRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X32Mul, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64MulHighSigned`]. - pub(crate) fn new_x64_mul_high_signed( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64MulHighSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64MulHighSignedUnsigned`]. - pub(crate) fn new_x64_mul_high_signed_unsigned( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64MulHighSignedUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64MulHighUnsigned`]. - pub(crate) fn new_x64_mul_high_unsigned( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64MulHighUnsigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64DivSigned`]. - pub(crate) fn new_x64_div_signed( - rd: NonZeroXRegister, - rs1: XRegister, - rs2: XRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64DivSigned, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::ECall`]. - pub(crate) fn new_ecall() -> Self { - Self { - opcode: OpCode::ECall, - args: Args { - width: InstrWidth::Uncompressed, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::Fadds`]. - /// The fadds instruction has not been lowered yet, and this function is only used for testing. - #[cfg(test)] - pub(crate) fn new_fadds( - rd: NonZeroXRegister, - rs1: NonZeroXRegister, - rs2: NonZeroXRegister, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::Fadds, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - width, - ..Args::DEFAULT - }, - } - } - - /// Create a new [`Instruction`] with the appropriate [`super::ArgsShape`] for [`OpCode::X64AtomicAdd`]. - pub(crate) fn new_x64_atomic_add( - rd: XRegister, - rs1: XRegister, - rs2: XRegister, - aq: bool, - rl: bool, - width: InstrWidth, - ) -> Self { - Self { - opcode: OpCode::X64AtomicAdd, - args: Args { - rd: rd.into(), - rs1: rs1.into(), - rs2: rs2.into(), - aq, - rl, - width, - ..Args::DEFAULT - }, - } - } -} - -impl Instruction { - /// Convert [`InstrCacheable::Add`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Add`]: crate::parser::instruction::InstrCacheable::Add - pub(super) fn from_ic_add(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, X::X0) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_add(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CAddw`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::CAddw`]: crate::parser::instruction::InstrCacheable::CAddw - pub(super) fn from_ic_caddw(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_add_word(rd_rs1, args.rd_rs1, args.rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Sub`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Sub`]: crate::parser::instruction::InstrCacheable::Sub - pub(super) fn from_ic_sub(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, X::X0) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // `rd = rs1 - 0` is equivalent to moving rs1 to rd. - (X::NonZero(rs2), X::X0) => Instruction::new_mv(args.rd, rs2, InstrWidth::Uncompressed), - // `rd = 0 - rs2` is equivalent to negating rs2 and moving to rd. - (X::X0, X::NonZero(rs2)) => { - Instruction::new_neg(args.rd, rs2, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_sub(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Csub`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::CSub`]: crate::parser::instruction::InstrCacheable::CSub - pub(super) fn from_ic_csub(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd_rs1), split_x0(args.rs2)) { - // Storing anything in x0 or Subtracting 0 from anything is a NOP. - (X::X0, _) | (_, X::X0) => Instruction::new_nop(InstrWidth::Compressed), - (X::NonZero(rd_rs1), X::NonZero(rs2)) => { - Instruction::new_sub(rd_rs1, rd_rs1, rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::CSubw`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::CSubw`]: crate::parser::instruction::InstrCacheable::CSubw - pub(super) fn from_ic_csubw(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_sub_word(rd_rs1, args.rd_rs1, args.rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Addi`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Addi`]: crate::parser::instruction::InstrCacheable::Addi - pub(super) fn from_ic_addi(args: &SplitITypeArgs) -> Instruction { - use XRegisterParsed as X; - match (args.rd, args.rs1) { - (X::X0, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), X::X0) => Instruction::new_li(rd, args.imm, InstrWidth::Uncompressed), - (X::NonZero(rd), X::NonZero(rs1)) => { - Instruction::new_addi(rd, rs1, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CAddi4spn`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CAddi4spn`]: crate::parser::instruction::InstrCacheable::CAddi4spn - pub(super) fn from_ic_caddi4spn(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_addi(rd_rs1, nz::sp, args.imm, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Andi`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Andi`]: crate::parser::instruction::InstrCacheable::Andi - pub(super) fn from_ic_andi(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Bitwise AND with zero is zero: `x & 0 == 0` - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - X::NonZero(rs1) => { - Instruction::new_andi(args.rd, rs1, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CAndi`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CAndi`]: crate::parser::instruction::InstrCacheable::CAndi - pub(super) fn from_ic_candi(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_andi(rd_rs1, rd_rs1, args.imm, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Ori`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Ori`]: crate::parser::instruction::InstrCacheable::Ori - pub(super) fn from_ic_ori(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Bitwise OR with zero is identity: `x | 0 == x` - X::X0 => Instruction::new_li(args.rd, args.imm, InstrWidth::Uncompressed), - X::NonZero(rs1) => { - Instruction::new_x64_or_immediate(args.rd, rs1, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Xori`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Xori`]: crate::parser::instruction::InstrCacheable::Xori - pub(super) fn from_ic_xori(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Bitwise XOR with zero is identity: `x ^ 0 == x` - X::X0 => Instruction::new_li(args.rd, args.imm, InstrWidth::Uncompressed), - X::NonZero(rs1) => { - Instruction::new_x64_xor_immediate(args.rd, rs1, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Slli`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Slli`]: crate::parser::instruction::InstrCacheable::Slli - pub(super) fn from_ic_slli(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - X::NonZero(rs1) => Instruction::new_shift_left_immediate( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::Srli`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Srli`]: crate::parser::instruction::InstrCacheable::Srli - pub(super) fn from_ic_srli(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - X::NonZero(rs1) => Instruction::new_shift_right_immediate_unsigned( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::CSrli`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CSrli`]: crate::parser::instruction::InstrCacheable::CSrli - pub(super) fn from_ic_csrli(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => Instruction::new_shift_right_immediate_unsigned( - rd_rs1, - rd_rs1, - args.imm, - InstrWidth::Compressed, - ), - } - } - - /// Convert [`InstrCacheable::Srai`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Srai`]: crate::parser::instruction::InstrCacheable::Srai - pub(super) fn from_ic_srai(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - X::NonZero(rs1) => Instruction::new_shift_right_immediate_signed( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::CSrai`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::CSrai`]: crate::parser::instruction::InstrCacheable::CSrai - pub(super) fn from_ic_csrai(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => Instruction::new_shift_right_immediate_signed( - rd_rs1, - rd_rs1, - args.imm, - InstrWidth::Compressed, - ), - } - } - - /// Convert [`InstrCacheable::Slliw`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Slliw`]: crate::parser::instruction::InstrCacheable::Slliw - pub(super) fn from_ic_x32_shift_left_immediate(args: &NonZeroRdITypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // For non-zero rs1, perform the actual shift operation - X::NonZero(rs1) => Instruction::new_x32_shift_left_immediate( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::Srliw`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Srliw`]: crate::parser::instruction::InstrCacheable::Srliw - pub(super) fn from_ic_x32_shift_right_immediate_unsigned( - args: &NonZeroRdITypeArgs, - ) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // For non-zero rs1, perform the actual shift operation - X::NonZero(rs1) => Instruction::new_x32_shift_right_immediate_unsigned( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::Sraiw`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Sraiw`]: crate::parser::instruction::InstrCacheable::Sraiw - pub(super) fn from_ic_x32_shift_right_immediate_signed( - args: &NonZeroRdITypeArgs, - ) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rs1) { - // Shifting 0 by any amount is 0. - X::X0 => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // For non-zero rs1, perform the actual shift operation - X::NonZero(rs1) => Instruction::new_x32_shift_right_immediate_signed( - args.rd, - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::And`] according to whether register is non-zero. - /// - /// [`InstrCacheable::And`]: crate::parser::instruction::InstrCacheable::And - pub(super) fn from_ic_and(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // Bitwise AND with zero is zero: `x & 0 == 0` - (X::X0, _) | (_, X::X0) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_and(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CAnd`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CAnd`]: crate::parser::instruction::InstrCacheable::CAnd - pub(super) fn from_ic_cand(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd_rs1), split_x0(args.rs2)) { - (X::X0, _) => Instruction::new_nop(InstrWidth::Compressed), - // Bitwise AND with zero is zero: `x & 0 == 0` - (X::NonZero(rd_rs1), X::X0) => Instruction::new_li(rd_rs1, 0, InstrWidth::Compressed), - (X::NonZero(rd_rs1), X::NonZero(rs2)) => { - Instruction::new_and(rd_rs1, rd_rs1, rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Or`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Or`]: crate::parser::instruction::InstrCacheable::Or - pub(super) fn from_ic_or(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, X::X0) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_or(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::COr`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::COr`]: crate::parser::instruction::InstrCacheable::COr - pub(super) fn from_ic_cor(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd_rs1), split_x0(args.rs2)) { - // if rd is 0, then the instruction is a NOP. - // if rs2 is 0, it is the same as moving rs1 to rd, which are the same register. - (X::X0, _) | (_, X::X0) => Instruction::new_nop(InstrWidth::Compressed), - (X::NonZero(rd_rs1), X::NonZero(rs2)) => { - Instruction::new_or(rd_rs1, rd_rs1, rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Xor`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Xor`]: crate::parser::instruction::InstrCacheable::Xor - pub(super) fn from_ic_xor(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, X::X0) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_x64_xor(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CXor`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CXor`]: crate::parser::instruction::InstrCacheable::CXor - pub(super) fn from_ic_cxor(args: &CRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd_rs1), split_x0(args.rs2)) { - // if rd is 0, then the instruction is a NOP. - // if rs2 is 0, it is the same as moving rs1 to rd, which are the same register. - (X::X0, _) | (_, X::X0) => Instruction::new_nop(InstrWidth::Compressed), - (X::NonZero(rd_rs1), X::NonZero(rs2)) => { - Instruction::new_x64_xor(rd_rs1, rd_rs1, rs2, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Sll`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Sll`]: crate::parser::instruction::InstrCacheable::Sll - pub(super) fn from_ic_sll(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // Shifting 0 by any amount is 0. - (X::X0, _) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // Shifting by 0 and storing in rd is equivalent to moving the value to rd. - (X::NonZero(rs1), X::X0) => Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_shift_left(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Srl`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Srl`]: crate::parser::instruction::InstrCacheable::Srl - pub(super) fn from_ic_srl(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // Shifting 0 by any amount is 0. - (X::X0, _) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // Shifting by 0 and storing in rd is equivalent to moving the value to rd. - (X::NonZero(rs1), X::X0) => Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_shift_right_unsigned(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Sra`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Sra`]: crate::parser::instruction::InstrCacheable::Sra - pub(super) fn from_ic_sra(args: &NonZeroRdRTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // Shifting 0 by any amount is 0. - (X::X0, _) => Instruction::new_li(args.rd, 0, InstrWidth::Uncompressed), - // Shifting by 0 and storing in rd is equivalent to moving the value to rd. - (X::NonZero(rs1), X::X0) => Instruction::new_mv(args.rd, rs1, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_shift_right_signed(args.rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert ['InstrCacheable::Jal'] according to whether register is non-zero. - /// - /// ['InstrCacheable::Jal']: crate::parser::instruction::InstrCacheable::Jal - pub(super) fn from_ic_jal(args: &UJTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd) { - // If rd is 0, we are just doing an unconditional jump and not storing the current pc. - X::X0 => Instruction::new_j(args.imm, InstrWidth::Uncompressed), - X::NonZero(rd) => Instruction::new_jal(rd, args.imm, InstrWidth::Uncompressed), - } - } - - /// Convert [`InstrCacheable::Beq`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Beq`]: crate::parser::instruction::InstrCacheable::Beq - pub(super) fn from_ic_beq(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If the registers are the same, then the instruction is an unconditional jump. - (X::X0, X::X0) => Instruction::new_j(args.imm, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_j(args.imm, InstrWidth::Uncompressed) - } - // If either register is x0, then the condition to branch is whether the other register stores 0. - (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_branch_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_branch_equal(rs1, rs2, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CBeqz`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CBeqz`]: crate::parser::instruction::InstrCacheable::CBeqz - pub(super) fn from_ic_cbeqz(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - // If `rd_rs1` is zero, the result is an unconditional jump - X::X0 => Instruction::new_j(args.imm, InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_branch_equal_zero(rd_rs1, args.imm, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Bne`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Bne`]: crate::parser::instruction::InstrCacheable::Bne - pub(super) fn from_ic_bne(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If the registers are the same, they are equal so we don't branch. - (X::X0, X::X0) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_nop(InstrWidth::Uncompressed) - } - // If either register is x0, then the condition to branch is whether the other register doesn't store 0. - (X::NonZero(rs1), X::X0) | (X::X0, X::NonZero(rs1)) => { - Instruction::new_branch_not_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_branch_not_equal(rs1, rs2, args.imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::CBnez`] according to whether register is non-zero. - /// - /// [`InstrCacheable::CBnez`]: crate::parser::instruction::InstrCacheable::CBnez - pub(super) fn from_ic_cbnez(args: &CIBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match split_x0(args.rd_rs1) { - // If `rd_rs1 == x0`, this will never branch. - X::X0 => Instruction::new_nop(InstrWidth::Compressed), - X::NonZero(rd_rs1) => { - Instruction::new_branch_not_equal_zero(rd_rs1, args.imm, InstrWidth::Compressed) - } - } - } - - /// Convert [`InstrCacheable::Jalr`] according to whether registers and imm are non-zero. - /// - /// [`InstrCacheable::Jalr`]: crate::parser::instruction::InstrCacheable::Jalr - pub(super) fn from_ic_jalr(args: &ITypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), args.imm) { - // if rd and rs1 are x0, the only effect is an unconditional jump to the absolute address `imm`. - (X::X0, X::X0, imm) => Instruction::new_j_absolute(imm, InstrWidth::Uncompressed), - // if rd is x0 and imm is 0, the only effect is an unconditional jump to the address stored in rs1. - (X::X0, X::NonZero(rs1), 0) => Instruction::new_jr(rs1, InstrWidth::Uncompressed), - // if rd is x0 and imm is non-zero, the only effect is an unconditional jump to the address stored in rs1 + imm. - (X::X0, X::NonZero(rs1), imm) => { - Instruction::new_jr_imm(rs1, imm, InstrWidth::Uncompressed) - } - // if rd is non-x0 and imm is 0, the effect is a jump to the absolute address `imm` - // and storing `pc + 4` in the register `rd`. - (X::NonZero(rd), X::X0, imm) => { - Instruction::new_jalr_absolute(rd, imm, InstrWidth::Uncompressed) - } - // if rd and rs1 are non-x0 and imm is 0, the effect is a jump to the address stored in rs1 - // and storing `pc + 4` in the register `rd`. - (X::NonZero(rd), X::NonZero(rs1), 0) => { - Instruction::new_jalr(rd, rs1, InstrWidth::Uncompressed) - } - // if all non-zero, the effect is a jump to the address stored in rs1 + imm - // and storing `pc + 4` in the register `rd`. - (X::NonZero(rd), X::NonZero(rs1), imm) => { - Instruction::new_jalr_imm(rd, rs1, imm, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Blt`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Blt`]: crate::parser::instruction::InstrCacheable::Blt - pub(super) fn from_ic_blt(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If the registers are the same, the values are the same so we don't branch. - (X::X0, X::X0) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_nop(InstrWidth::Uncompressed) - } - // If rs1 is x0, the condition to branch is whether `val(rs2) > 0`. - (X::X0, X::NonZero(rs1)) => { - Instruction::new_branch_greater_than_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - // If rs2 is x0, the condition to branch is whether `val(rs1) < 0`. - (X::NonZero(rs1), X::X0) => { - Instruction::new_branch_less_than_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => Instruction::new_branch_less_than_signed( - rs1, - rs2, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::Bge`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Bge`]: crate::parser::instruction::InstrCacheable::Bge - pub(super) fn from_ic_bge(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If the registers are the same, the values are the same so we always branch. - (X::X0, X::X0) => Instruction::new_j(args.imm, InstrWidth::Uncompressed), - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_j(args.imm, InstrWidth::Uncompressed) - } - // If rs1 is x0, the condition to branch is whether `val(rs2) <= 0`. - (X::X0, X::NonZero(rs1)) => Instruction::new_branch_less_than_or_equal_zero( - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - // If rs2 is x0, the condition to branch is whether `val(rs1) >= 0`. - (X::NonZero(rs1), X::X0) => Instruction::new_branch_greater_than_or_equal_zero( - rs1, - args.imm, - InstrWidth::Uncompressed, - ), - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_branch_greater_than_or_equal_signed( - rs1, - rs2, - args.imm, - InstrWidth::Uncompressed, - ) - } - } - } - - /// Convert [`InstrCacheable::Bltu`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Bltu`]: crate::parser::instruction::InstrCacheable::Bltu - pub(super) fn from_ic_bltu(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If rs2 is x0, rs1 is either the same or greater than rs2, so we don't branch. - (_, X::X0) => Instruction::new_nop(InstrWidth::Uncompressed), - // If the registers are the same, the values are the same so we don't branch. - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_nop(InstrWidth::Uncompressed) - } - // If rs1 is x0, the condition to branch is whether `val(rs2) != 0`. - (X::X0, X::NonZero(rs1)) => { - Instruction::new_branch_not_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => Instruction::new_branch_less_than_unsigned( - rs1, - rs2, - args.imm, - InstrWidth::Uncompressed, - ), - } - } - - /// Convert [`InstrCacheable::Bgeu`] according to whether register is non-zero. - /// - /// [`InstrCacheable::Bgeu`]: crate::parser::instruction::InstrCacheable::Bgeu - pub(super) fn from_ic_bgeu(args: &SBTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rs1), split_x0(args.rs2)) { - // If rs2 is x0, rs1 is either the same or more than rs2, so we always branch. - (_, X::X0) => Instruction::new_j(args.imm, InstrWidth::Uncompressed), - // If the registers are the same, the values are the same so we always branch. - (X::NonZero(rs1), X::NonZero(rs2)) if rs1 == rs2 => { - Instruction::new_j(args.imm, InstrWidth::Uncompressed) - } - // If rs1 is x0, the condition to branch is whether `val(rs2) == 0`. - (X::X0, X::NonZero(rs1)) => { - Instruction::new_branch_equal_zero(rs1, args.imm, InstrWidth::Uncompressed) - } - (X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_branch_greater_than_or_equal_unsigned( - rs1, - rs2, - args.imm, - InstrWidth::Uncompressed, - ) - } - } - } - - /// Convert [`InstrCacheable::Mul`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Mul`]: crate::parser::instruction::InstrCacheable::Mul - pub(super) fn from_ic_mul(args: &RTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, _, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), X::X0, _) | (X::NonZero(rd), _, X::X0) => { - Instruction::new_li(rd, 0, InstrWidth::Uncompressed) - } - (X::NonZero(rd), X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_mul(rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Converts [`InstrCacheable::Mulh`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Mulh`]: crate::parser::instruction::InstrCacheable::Mulh - pub(crate) fn from_ic_mulh(args: &RTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, _, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), X::X0, _) | (X::NonZero(rd), _, X::X0) => { - Instruction::new_li(rd, 0, InstrWidth::Uncompressed) - } - (X::NonZero(rd), X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_x64_mul_high_signed(rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Converts [`InstrCacheable::Mulhsu`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Mulhsu`]: crate::parser::instruction::InstrCacheable::Mulhsu - pub(crate) fn from_ic_mulhsu(args: &RTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, _, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), X::X0, _) | (X::NonZero(rd), _, X::X0) => { - Instruction::new_li(rd, 0, InstrWidth::Uncompressed) - } - (X::NonZero(rd), X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_x64_mul_high_signed_unsigned( - rd, - rs1, - rs2, - InstrWidth::Uncompressed, - ) - } - } - } - - /// Converts [`InstrCacheable::Mulhu`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Mulhu`]: crate::parser::instruction::InstrCacheable::Mulhu - pub(crate) fn from_ic_mulhu(args: &RTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), split_x0(args.rs2)) { - (X::X0, _, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), X::X0, _) | (X::NonZero(rd), _, X::X0) => { - Instruction::new_li(rd, 0, InstrWidth::Uncompressed) - } - (X::NonZero(rd), X::NonZero(rs1), X::NonZero(rs2)) => { - Instruction::new_x64_mul_high_unsigned(rd, rs1, rs2, InstrWidth::Uncompressed) - } - } - } - - /// Convert [`InstrCacheable::Div`] according to whether registers are non-zero. - /// - /// [`InstrCacheable::Div`]: crate::parser::instruction::InstrCacheable::Div - pub(super) fn from_ic_div(args: &RTypeArgs) -> Instruction { - use XRegisterParsed as X; - match (split_x0(args.rd), split_x0(args.rs1), split_x0(args.rs2)) { - // This holds as `div` is non-trapping in the case of division by zero. - (X::X0, _, _) => Instruction::new_nop(InstrWidth::Uncompressed), - (X::NonZero(rd), _, _) => { - Instruction::new_x64_div_signed(rd, args.rs1, args.rs2, InstrWidth::Uncompressed) - } - } - } -} diff --git a/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs b/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs deleted file mode 100644 index d7ae8947a3663ced680c4e77072c00b707dea965..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/instruction/tagged_instruction.rs +++ /dev/null @@ -1,575 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fmt::Debug; - -use serde::Deserialize; -use serde::Serialize; -use thiserror::Error; - -use super::Args; -use super::CSRegister; -use super::FRegister; -use super::InstrRoundingMode; -use super::Instruction; -use super::NonZeroXRegister; -use super::OpCode; -use super::XRegister; -use crate::default::ConstDefault; -use crate::parser::instruction::InstrWidth; - -#[derive(Error, Debug)] -/// Errors that may be returned when converting between tagged and untagged registers. -pub enum TaggedError { - #[error("Expected XRegister got {0:?}")] - UnwrapX(TaggedRegister), - #[error("Expected FRegister got {0:?}")] - UnwrapF(TaggedRegister), - #[error("Expected NonZeroXRegister got {0:?}")] - UnwrapNZX(TaggedRegister), -} - -impl TryFrom for Instruction { - type Error = TaggedError; - - fn try_from(value: TaggedInstruction) -> Result { - let args = match opcode_to_argsshape(&value.opcode) { - ArgsShape::XSrcXDest => Args { - rd: value.args.rd.unwrap_x()?.into(), - rs1: value.args.rs1.unwrap_x()?.into(), - rs2: value.args.rs2.unwrap_x()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::FSrcFDest => Args { - rd: value.args.rd.unwrap_f()?.into(), - rs1: value.args.rs1.unwrap_f()?.into(), - rs2: value.args.rs2.unwrap_f()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcFDest => Args { - rd: value.args.rd.unwrap_f()?.into(), - rs1: value.args.rs1.unwrap_x()?.into(), - rs2: value.args.rs2.unwrap_x()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::FSrcXDest => Args { - rd: value.args.rd.unwrap_x()?.into(), - rs1: value.args.rs1.unwrap_f()?.into(), - rs2: value.args.rs2.unwrap_f()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcFSrc => Args { - rd: value.args.rd.unwrap_x()?.into(), - rs1: value.args.rs1.unwrap_x()?.into(), - rs2: value.args.rs2.unwrap_f()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::NZXSrcNZXDest => Args { - rd: value.args.rd.unwrap_nzx()?.into(), - rs1: value.args.rs1.unwrap_nzx()?.into(), - rs2: value.args.rs2.unwrap_nzx()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcNZXDest => Args { - rd: value.args.rd.unwrap_nzx()?.into(), - rs1: value.args.rs1.unwrap_x()?.into(), - rs2: value.args.rs2.unwrap_x()?.into(), - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - }; - Ok(Instruction { - opcode: value.opcode, - args, - }) - } -} - -/// An intermediate object for creating and deserialising Instructions. -/// This is necessary because we can't directly create or deserialise into -/// an [Instruction] due to the [Register] Union. -/// -/// [Register]: super::Register -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct TaggedInstruction { - pub opcode: OpCode, - pub args: TaggedArgs, -} - -impl From for TaggedInstruction { - fn from(value: Instruction) -> Self { - let args = match opcode_to_argsshape(&value.opcode) { - ArgsShape::XSrcXDest => TaggedArgs { - rd: unsafe { value.args.rd.x.into() }, - rs1: unsafe { value.args.rs1.x.into() }, - rs2: unsafe { value.args.rs2.x.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::FSrcFDest => TaggedArgs { - rd: unsafe { value.args.rd.f.into() }, - rs1: unsafe { value.args.rs1.f.into() }, - rs2: unsafe { value.args.rs2.f.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcFDest => TaggedArgs { - rd: unsafe { value.args.rd.f.into() }, - rs1: unsafe { value.args.rs1.x.into() }, - rs2: unsafe { value.args.rs2.x.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::FSrcXDest => TaggedArgs { - rd: unsafe { value.args.rd.x.into() }, - rs1: unsafe { value.args.rs1.f.into() }, - rs2: unsafe { value.args.rs2.f.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcFSrc => TaggedArgs { - rd: unsafe { value.args.rd.x.into() }, - rs1: unsafe { value.args.rs1.x.into() }, - rs2: unsafe { value.args.rs2.f.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::NZXSrcNZXDest => TaggedArgs { - rd: unsafe { value.args.rd.nzx.into() }, - rs1: unsafe { value.args.rs1.nzx.into() }, - rs2: unsafe { value.args.rs2.nzx.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - ArgsShape::XSrcNZXDest => TaggedArgs { - rd: unsafe { value.args.rd.nzx.into() }, - rs1: unsafe { value.args.rs1.x.into() }, - rs2: unsafe { value.args.rs2.x.into() }, - imm: value.args.imm, - csr: value.args.csr, - rs3f: value.args.rs3f, - rm: value.args.rm, - aq: value.args.aq, - rl: value.args.rl, - width: value.args.width, - }, - }; - TaggedInstruction { - opcode: value.opcode, - args, - } - } -} - -/// Integer or floating-point register. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum TaggedRegister { - X(XRegister), - F(FRegister), - NonZeroX(NonZeroXRegister), -} - -impl From for TaggedRegister { - fn from(value: XRegister) -> Self { - TaggedRegister::X(value) - } -} - -impl From for TaggedRegister { - fn from(value: FRegister) -> Self { - TaggedRegister::F(value) - } -} - -impl From for TaggedRegister { - fn from(value: NonZeroXRegister) -> Self { - TaggedRegister::NonZeroX(value) - } -} - -impl TaggedRegister { - fn unwrap_x(self) -> Result { - match self { - Self::X(x) => Ok(x), - _ => Err(TaggedError::UnwrapX(self)), - } - } - - fn unwrap_f(self) -> Result { - match self { - Self::F(f) => Ok(f), - _ => Err(TaggedError::UnwrapF(self)), - } - } - - fn unwrap_nzx(self) -> Result { - match self { - Self::NonZeroX(nzx) => Ok(nzx), - _ => Err(TaggedError::UnwrapNZX(self)), - } - } -} - -/// Intermediate object for instantiating [Instruction] objects and -/// serialising [Args]. This is necessary because we can't -/// directly serialise Args due to the [super::Register] Union. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub struct TaggedArgs { - pub rd: TaggedRegister, - pub rs1: TaggedRegister, - pub rs2: TaggedRegister, - pub imm: i64, - pub csr: CSRegister, - pub rs3f: FRegister, - pub rm: InstrRoundingMode, - pub aq: bool, - pub rl: bool, - pub width: InstrWidth, -} - -impl ConstDefault for TaggedArgs { - const DEFAULT: Self = Self { - rd: TaggedRegister::NonZeroX(NonZeroXRegister::x1), - rs1: TaggedRegister::NonZeroX(NonZeroXRegister::x1), - rs2: TaggedRegister::NonZeroX(NonZeroXRegister::x1), - imm: 0, - csr: CSRegister::fflags, - rs3f: FRegister::f0, - rm: InstrRoundingMode::Dynamic, - aq: false, - rl: false, - width: InstrWidth::Uncompressed, - }; -} - -/// This enum represents the different forms of registers that are present -/// in the args for each opcode. -pub enum ArgsShape { - // rd, rs1, rs2 => X - XSrcXDest, - // rd, rs1, rs2 => F - FSrcFDest, - // rd => F | rs1, rs2 => X - XSrcFDest, - // rd => X | rs1, rs2 => F - FSrcXDest, - // rd, rs1 => X | rs2 => F - XSrcFSrc, - // rd, rs1, rs2 => NZX - NZXSrcNZXDest, - // rd => NZX | rs1, rs2 => X - XSrcNZXDest, -} - -/// This function maps each opcode to the corresponding ArgsShape that the opcode uses -/// i.e. the types of registers used. -/// This is used to determine how to serialise and deserialise the Args for each opcode. -pub fn opcode_to_argsshape(opcode: &OpCode) -> ArgsShape { - use OpCode::*; - match opcode { - X8LoadSigned | X16LoadSigned | X32LoadSigned | X8LoadUnsigned | X16LoadUnsigned - | X32LoadUnsigned | X64LoadSigned | X8Store | X16Store | X32Store | X64Store | Lrw - | Scw | Amoswapw | Amoaddw | Amoxorw | Amoandw | Amoorw | Amominw | Amomaxw | Amominuw - | Amomaxuw | Lrd | Scd | Amoswapd | X64AtomicAdd | Amoxord | Amoandd | Amoord | Amomind - | Amomaxd | Amominud | Amomaxud | Rem | Remu | Remw | Remuw | Divu | Divw | Divuw - | X32Mul | Csrrw | Csrrs | Csrrc | Csrrwi | Csrrsi | Csrrci => ArgsShape::XSrcXDest, - - Fadds | Fsubs | Fmuls | Fdivs | Fsqrts | Fmins | Fmaxs | Fsgnjs | Fsgnjns | Fsgnjxs - | Fmadds | Fmsubs | Fnmsubs | Fnmadds | Faddd | Fsubd | Fmuld | Fdivd | Fsqrtd | Fmind - | Fmaxd | Fsgnjd | Fsgnjnd | Fsgnjxd | Fcvtsd | Fcvtds | Fmaddd | Fmsubd | Fnmsubd - | Fnmaddd => ArgsShape::FSrcFDest, - - Flw | Fld | FmvWX | Fcvtsw | Fcvtswu | Fcvtsl | Fcvtslu | FmvDX | Fcvtdw | Fcvtdwu - | Fcvtdl | Fcvtdlu | CFld | CFldsp => ArgsShape::XSrcFDest, - - Feqs | Fles | Flts | Feqd | Fled | Fltd | FclassS | FmvXW | Fcvtws | Fcvtwus | Fcvtls - | Fcvtlus | FclassD | FmvXD | Fcvtwd | Fcvtwud | Fcvtld | Fcvtlud => ArgsShape::FSrcXDest, - - Fsw | Fsd | CFsd | CFsdsp => ArgsShape::XSrcFSrc, - - Addi - | Andi - | X64OrImm - | X64XorImm - | ShiftLeftImmediate - | ShiftRightImmediateUnsigned - | ShiftRightImmediateSigned - | Add - | Sub - | Mul - | X64MulHighSigned - | X64MulHighSignedUnsigned - | X64MulHighUnsigned - | Mv - | Neg - | And - | Or - | X64Xor - | ShiftLeft - | ShiftRightUnsigned - | ShiftRightSigned - | X32ShiftLeftImm - | X32ShiftRightImmUnsigned - | X32ShiftRightImmSigned - | Jal - | J - | JrImm - | JAbsolute - | JalrAbsolute - | Jr - | Jalr - | Li - | BranchEqual - | BranchEqualZero - | BranchNotEqual - | BranchNotEqualZero - | BranchLessThanSigned - | BranchGreaterThanOrEqualSigned - | BranchLessThanUnsigned - | BranchGreaterThanOrEqualUnsigned - | BranchLessThanZero - | BranchGreaterThanOrEqualZero - | BranchLessThanOrEqualZero - | BranchGreaterThanZero - | JalrImm - | Nop - | ECall - | Unknown => ArgsShape::NZXSrcNZXDest, - - AddWordImmediate - | AddWord - | SubWord - | X32ShiftLeft - | X32ShiftRightUnsigned - | X32ShiftRightSigned - | SetLessThanSigned - | SetLessThanUnsigned - | SetLessThanImmediateSigned - | SetLessThanImmediateUnsigned - | AddImmediateToPC - | X64DivSigned => ArgsShape::XSrcNZXDest, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::default::ConstDefault; - use crate::machine_state::instruction::Args; - use crate::machine_state::registers::FRegister; - use crate::machine_state::registers::XRegister; - - #[test] - fn test_miri_instruction_serde() { - let instr_xsrc_xdst = Instruction { - opcode: OpCode::Add, - args: Args { - rd: NonZeroXRegister::x1.into(), - rs1: NonZeroXRegister::x2.into(), - rs2: NonZeroXRegister::x2.into(), - ..Args::DEFAULT - }, - }; - - let instr_xsrc_xdst_ser = bincode::serialize(&instr_xsrc_xdst).unwrap(); - let instr_xsrc_xdst_de: Instruction = bincode::deserialize(&instr_xsrc_xdst_ser).unwrap(); - - assert_eq!(instr_xsrc_xdst, instr_xsrc_xdst_de); - - let instr_fsrc_fdst = Instruction { - opcode: OpCode::Fadds, - args: Args { - rd: FRegister::f0.into(), - rs1: FRegister::f0.into(), - rs2: FRegister::f0.into(), - ..Args::DEFAULT - }, - }; - - let instr_fsrc_fdst_ser = bincode::serialize(&instr_fsrc_fdst).unwrap(); - let instr_fsrc_fdst_de: Instruction = bincode::deserialize(&instr_fsrc_fdst_ser).unwrap(); - - assert_eq!(instr_fsrc_fdst, instr_fsrc_fdst_de); - - let instr_xsrc_fdst = Instruction { - opcode: OpCode::Flw, - args: Args { - rd: FRegister::f0.into(), - rs1: XRegister::x0.into(), - ..Args::DEFAULT - }, - }; - - let instr_xsrc_fdst_ser = bincode::serialize(&instr_xsrc_fdst).unwrap(); - let instr_xsrc_fdst_de: Instruction = bincode::deserialize(&instr_xsrc_fdst_ser).unwrap(); - - assert_eq!(instr_xsrc_fdst, instr_xsrc_fdst_de); - - let instr_fsrc_xdst = Instruction { - opcode: OpCode::Feqs, - args: Args { - rd: XRegister::x0.into(), - rs1: FRegister::f0.into(), - rs2: FRegister::f0.into(), - ..Args::DEFAULT - }, - }; - - let instr_fsrc_xdst_ser = bincode::serialize(&instr_fsrc_xdst).unwrap(); - let instr_fsrc_xdst_de: Instruction = bincode::deserialize(&instr_fsrc_xdst_ser).unwrap(); - - assert_eq!(instr_fsrc_xdst, instr_fsrc_xdst_de); - - let instr_xsrc_fsrc = Instruction { - opcode: OpCode::Fsw, - args: Args { - rs1: XRegister::x0.into(), - rs2: FRegister::f0.into(), - ..Args::DEFAULT - }, - }; - - let instr_xsrc_fsrc_ser = bincode::serialize(&instr_xsrc_fsrc).unwrap(); - let instr_xsrc_fsrc_de: Instruction = bincode::deserialize(&instr_xsrc_fsrc_ser).unwrap(); - - assert_eq!(instr_xsrc_fsrc, instr_xsrc_fsrc_de); - - let instr_nzxsrc_nzxdest = Instruction { - opcode: OpCode::Jr, - args: Args { - rs1: NonZeroXRegister::x2.into(), - ..Args::DEFAULT - }, - }; - - let instr_nzxsrc_nzxdest_ser = bincode::serialize(&instr_nzxsrc_nzxdest).unwrap(); - let instr_nzxsrc_nzxdest_de: Instruction = - bincode::deserialize(&instr_nzxsrc_nzxdest_ser).unwrap(); - - assert_eq!(instr_nzxsrc_nzxdest, instr_nzxsrc_nzxdest_de); - - // ensure width of all serialised instructions are the same - let ser_len = instr_xsrc_xdst_ser.len(); - assert_eq!(instr_fsrc_fdst_ser.len(), ser_len); - assert_eq!(instr_xsrc_fdst_ser.len(), ser_len); - assert_eq!(instr_fsrc_xdst_ser.len(), ser_len); - assert_eq!(instr_xsrc_fsrc_ser.len(), ser_len); - assert_eq!(instr_nzxsrc_nzxdest_ser.len(), ser_len); - } - - #[test] - fn test_incorrectly_tagged_instructions_fail_conversion_fx() { - let tagged_instr = TaggedInstruction { - opcode: OpCode::Add, - args: TaggedArgs { - rd: FRegister::f0.into(), - rs1: FRegister::f0.into(), - rs2: FRegister::f0.into(), - imm: 0, - csr: CSRegister::fflags, - rs3f: FRegister::f0, - rm: InstrRoundingMode::Dynamic, - aq: false, - rl: false, - width: InstrWidth::Uncompressed, - }, - }; - - let instr = Instruction::try_from(tagged_instr); - assert!(instr.is_err()); - } - - #[test] - fn test_incorrectly_tagged_instructions_fail_conversion_nzxx() { - let tagged_instr = TaggedInstruction { - opcode: OpCode::Add, - args: TaggedArgs { - rd: XRegister::x0.into(), - rs1: XRegister::x0.into(), - rs2: NonZeroXRegister::x1.into(), - imm: 0, - csr: CSRegister::fflags, - rs3f: FRegister::f0, - rm: InstrRoundingMode::Dynamic, - aq: false, - rl: false, - width: InstrWidth::Uncompressed, - }, - }; - - let instr = Instruction::try_from(tagged_instr); - assert!(instr.is_err()); - } -} diff --git a/src/riscv/lib/src/machine_state/memory.rs b/src/riscv/lib/src/machine_state/memory.rs deleted file mode 100644 index b2664250649a579258745d53b5c8a21f7e4feafa..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory.rs +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -mod buddy; -mod config; -mod protection; -mod state; - -use std::num::NonZeroU64; - -use tezos_smart_rollup_constants::riscv::SbiError; - -use super::registers::XValue; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::Elem; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; -use crate::state_backend::ProofLayout; -use crate::state_backend::Ref; - -/// Number of bits needed so you can address every byte in a page -pub const OFFSET_BITS: u64 = 12; - -/// Bit mask to keep only the page offset -pub const OFFSET_MASK: u64 = (1 << OFFSET_BITS) - 1; - -/// Size of a page -pub const PAGE_SIZE: NonZeroU64 = { - const PAGE_SIZE: u64 = 1 << OFFSET_BITS; - - // Compile-time check: Page size must be positive - const _: () = { - if PAGE_SIZE < 1 { - panic!() - } - }; - - match NonZeroU64::new(PAGE_SIZE) { - Some(page_size) => page_size, - None => { - // SAFETY: The compile-time check above ensures this branch cannot be reached - unsafe { std::hint::unreachable_unchecked() } - } - } -}; - -/// Memory address -pub type Address = XValue; - -/// Lowest address -pub const FIRST_ADDRESS: Address = 0; - -/// Memory access permissions -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Permissions { - None, - Read, - Write, - ReadWrite, - ReadExec, - #[cfg(test)] - ReadWriteExec, -} - -impl Permissions { - /// Do the permissions allow reading? - pub const fn can_read(&self) -> bool { - match self { - Self::None | Self::Write => false, - Self::Read | Self::ReadWrite | Self::ReadExec => true, - #[cfg(test)] - Self::ReadWriteExec => true, - } - } - - /// Do the permissions allow writing? - pub const fn can_write(&self) -> bool { - match self { - Self::None | Self::Read | Self::ReadExec => false, - Self::ReadWrite | Self::Write => true, - #[cfg(test)] - Self::ReadWriteExec => true, - } - } - - /// Do the permissions allow execution? - pub const fn can_exec(&self) -> bool { - match self { - Self::None | Self::Read | Self::ReadWrite | Self::Write => false, - Self::ReadExec => true, - #[cfg(test)] - Self::ReadWriteExec => true, - } - } -} - -impl TryFrom for Permissions { - type Error = crate::pvm::linux::error::Error; - - fn try_from(value: XValue) -> Result { - const READ: u64 = 0b1; - const WRITE: u64 = 0b10; - const EXEC: u64 = 0b100; - const READ_WRITE: u64 = READ | WRITE; - const READ_EXEC: u64 = READ | EXEC; - const NONE: u64 = 0; - - match value { - READ_WRITE => Ok(Self::ReadWrite), - READ_EXEC => Ok(Self::ReadExec), - READ => Ok(Self::Read), - WRITE => Ok(Self::Write), - NONE => Ok(Self::None), - - // Unknown protections value - _ => Err(crate::pvm::linux::error::Error::InvalidArgument), - } - } -} - -/// Something went wrong when accessing the memory -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, thiserror::Error)] -#[error("Bad memory access")] -pub struct BadMemoryAccess; - -impl From for SbiError { - fn from(_value: BadMemoryAccess) -> Self { - SbiError::InvalidAddress - } -} - -/// Something went wrong when allocating memory or changing permissions -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, thiserror::Error)] -#[error("Error during memory governance")] -pub struct MemoryGovernanceError; - -/// Instance of memory -pub trait Memory: NewState + Sized { - /// Read an element in the region. `address` is in bytes. - fn read(&self, address: Address) -> Result - where - E: Elem, - M: ManagerRead; - - /// Read an element in the region that will be used in execution. `address` is in bytes. - fn read_exec(&self, address: Address) -> Result - where - E: Elem, - M: ManagerRead; - - /// Read elements from the region. `address` is in bytes. - fn read_all(&self, address: Address, values: &mut [E]) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerRead; - - /// Update an element in the region. `address` is in bytes. - fn write(&mut self, address: Address, value: E) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerReadWrite; - - /// Update multiple elements in the region. `address` is in bytes. - fn write_all(&mut self, address: Address, values: &[E]) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerReadWrite; - - /// Clone all memory. - fn clone(&self) -> Self - where - M: ManagerClone; - - /// Zero-out all memory. - fn reset(&mut self) - where - M: ManagerWrite; - - /// Protect the pages that belong to the given address range. - fn protect_pages( - &mut self, - address: Address, - length: usize, - perms: Permissions, - ) -> Result<(), MemoryGovernanceError> - where - M: ManagerWrite; - - /// Allocate pages for the given address range. - fn allocate_pages( - &mut self, - address_hint: Option
, - length: usize, - allow_replace: bool, - ) -> Result - where - M: ManagerReadWrite; - - /// Allocate pages for the given address range. - fn deallocate_pages( - &mut self, - address: Address, - length: usize, - ) -> Result<(), MemoryGovernanceError> - where - M: ManagerReadWrite; - - /// Allocate pages for the given address range and amend the protections for them. - fn allocate_and_protect_pages( - &mut self, - address_hint: Option
, - length: usize, - perms: Permissions, - allow_replace: bool, - ) -> Result - where - M: ManagerReadWrite; - - /// Free the pages in that address range and make sure the range is no longer accessible. - fn deallocate_and_protect_pages( - &mut self, - address: Address, - length: usize, - ) -> Result<(), MemoryGovernanceError> - where - M: ManagerReadWrite, - { - self.deallocate_pages(address, length)?; - self.protect_pages(address, length, Permissions::None) - } -} - -/// Memory configuration -pub trait MemoryConfig: 'static { - /// Number of bytes in the memory - const TOTAL_BYTES: usize; - - /// Layout for memory instance's state - type Layout: CommitmentLayout + ProofLayout; - - /// Memory instance - type State: Memory; - - /// Bind the allocated regions to produce a memory instance. - fn bind(space: AllocatedOf) -> Self::State; - - /// Given a manager morphism `f : &M -> N`, return the memory instance layout's allocated - /// structure containing the constituents of `N` that were produced from the constituents of - /// `&M`. - fn struct_ref<'a, M, F>(instance: &'a Self::State) -> AllocatedOf - where - M: ManagerBase, - F: FnManager>; -} - -// Re-export memory configurations -pub use config::M1G; -pub use config::M1M; -pub use config::M4G; -pub use config::M4K; -pub use config::M8K; -pub use config::M64M; diff --git a/src/riscv/lib/src/machine_state/memory/buddy.rs b/src/riscv/lib/src/machine_state/memory/buddy.rs deleted file mode 100644 index 8d7016f984ba44a0bed18653af1d395f767f2344..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/buddy.rs +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Buddy-style memory manager -//! -//! Memory managers let you allocate and deallocate memory so it can be re-used later. It removes -//! the need to bookkeep memory allocations from the user, allowing them to only focus on "how much" -//! memory they need. -//! -//! A Buddy allocator employs a tree to represent the allocations across the entire memory range. -//! This allows it to have logarithmic time complexity for finding allocations. This sub-linear -//! complexity is achieved by splitting the memory range into halves, and recursively splitting. -//! Each branch of the tree manages the two halves of its memory range. This splitting stops once we -//! go below a certain memory size for a branch. In this case the branch becomes a leaf which is -//! represented by a bit array where each bit represents the occupancy state of a memory page. -//! -//! See: - -mod branch; -mod branch_combinations; -mod leaf; -mod proxy; - -pub use proxy::BuddyLayoutProxy; - -use crate::state::NewState; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ProofLayout; -use crate::state_backend::Ref; - -/// Layout for a Buddy-style memory manager -pub trait BuddyLayout: CommitmentLayout + ProofLayout { - /// Buddy-style memory manager implementation - type Buddy: Buddy; - - /// Bind the allocated space. - fn bind(space: Self::Allocated) -> Self::Buddy; - - /// Given a manager morphism `F : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - fn struct_ref<'a, F, M: ManagerBase>(space: &'a Self::Buddy) -> Self::Allocated - where - F: FnManager>; -} - -/// Buddy-style memory manager -pub trait Buddy: NewState { - /// Number of pages being managed - const PAGES: u64; - - /// Allocate a number of pages. Returns the index of the first page in the allocated range. - fn allocate(&mut self, pages: u64) -> Option - where - M: ManagerReadWrite; - - /// Allocate a fixed range of pages. If `replace` is `true`, the range is allocated even if it - /// overlaps an existing allocation. - fn allocate_fixed(&mut self, idx: u64, pages: u64, replace: bool) -> Option<()> - where - M: ManagerReadWrite; - - /// Deallocate a range of pages. - fn deallocate(&mut self, idx: u64, pages: u64) - where - M: ManagerReadWrite; - - /// Count the largest sequence of free pages. - fn longest_free_sequence(&self) -> u64 - where - M: ManagerRead; - - /// Count the number of free pages at the start of the managed area. - fn count_free_start(&self) -> u64 - where - M: ManagerRead; - - /// Count the number of free pages at the end of the managed area. - fn count_free_end(&self) -> u64 - where - M: ManagerRead; - - /// Clone the memory manager state. - fn clone(&self) -> Self - where - M: ManagerClone; -} - -#[cfg(test)] -mod tests { - use rand::Rng; - use rand::seq::SliceRandom; - - use super::*; - use crate::backend_test; - - fn distribute(mut total: u64, min: u64) -> Vec { - let mut rng = rand::thread_rng(); - let mut steps: Vec = std::iter::from_fn(|| { - if total == 0 { - return None; - } - - let steps = total.div_euclid(2).max(min + 1); - let steps = rng.gen_range(min..=steps).min(total); - - total = total.saturating_sub(steps); - - Some(steps) - }) - .collect(); - - steps.shuffle(&mut rng); - steps - } - - backend_test!(buddy_alloc_only, F, { - type BuddyHeapLayout = BuddyLayoutProxy<{ 1024 * 1024 }>; - - let mut manager = F::manager(); - let mut state = ::Buddy::new(&mut manager); - - let total_pages = state.longest_free_sequence(); - - for _ in 0..100 { - // Create a distribution of allocation sizes that when used together would allocate all - // available memory - let allocations = distribute(total_pages, 1); - - let mut expected_idx = 0; - for length in allocations { - let idx = state.allocate(length); - assert_eq!(idx, Some(expected_idx)); - expected_idx += length; - } - - assert_eq!(state.allocate(1), None); - state.deallocate(0, total_pages); - } - }); - - backend_test!(buddy_alloc_dealloc, F, { - type BuddyHeapLayout = BuddyLayoutProxy<{ 1024 * 1024 }>; - - let mut manager = F::manager(); - let mut state = ::Buddy::new(&mut manager); - - let total_pages = state.longest_free_sequence(); - - let mut rng = rand::thread_rng(); - for _ in 0..100 { - // Create a distribution of allocation sizes that when used together would allocate all - // available memory - let allocations = distribute(total_pages, 1); - - let mut occupied = Vec::new(); - - for length in allocations { - // When we accumulated at least 3 allocations, we randomly free one of them. This - // is to cause fragmentation and test the allocator's ability to reuse freed memory. - if occupied.len() >= 3 { - let index = rng.gen_range(0..occupied.len()); - let (idx, length) = occupied.remove(index); - state.deallocate(idx, length); - } - - let idx = state.allocate(length).unwrap(); - occupied.push((idx, length)); - } - - for (idx, length) in occupied { - state.deallocate(idx, length); - } - } - }); - - backend_test!(buddy_alloc_fixed, F, { - type BuddyHeapLayout = BuddyLayoutProxy<{ 1024 * 1024 }>; - - let mut manager = F::manager(); - let mut state = ::Buddy::new(&mut manager); - - // Create a distribution of allocation sizes that when used together would allocate all - // available memory - let allocations = distribute(state.longest_free_sequence(), 1); - - for length in allocations { - let idx = state.allocate(length).unwrap(); - - // Re-allocating the same range should fail - assert!(state.allocate_fixed(idx, length, false).is_none()); - - // Overriding the range allocation must succeed - state.allocate_fixed(idx, length, true).unwrap(); - - // After freeing the range, it should be possible to allocate it again - state.deallocate(idx, length); - state.allocate_fixed(idx, length, false).unwrap(); - } - }); -} diff --git a/src/riscv/lib/src/machine_state/memory/buddy/branch.rs b/src/riscv/lib/src/machine_state/memory/buddy/branch.rs deleted file mode 100644 index 56bc99a48a2587bea5a43f08f050063c32c8f066..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/buddy/branch.rs +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Branch of a tree that forms a Buddy-style memory manager - -use serde::Deserialize; -use serde::Serialize; - -use super::Buddy; -use super::BuddyLayout; -use crate::state::NewState; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerDeserialise; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::Ref; -use crate::struct_layout; - -/// Information about what is free in each buddy -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FreeInfo { - /// Length of the longest sequence of free pages in the left buddy - left_longest_free_sequence: u64, - - /// Number of free pages at the start of the left buddy - left_free_start: u64, - - /// Number of free pages at the end of the left buddy - left_free_end: u64, - - /// Length of the longest sequence of free pages in the right buddy - right_longest_free_sequence: u64, - - /// Number of free pages at the start of the right buddy - right_free_start: u64, - - /// Number of free pages at the end of the right buddy - right_free_end: u64, -} - -struct_layout! { - pub struct BuddyBranch2Layout { - free_info: Atom, - left: Box, - right: Box, - } -} - -impl BuddyLayout for BuddyBranch2Layout { - type Buddy = BuddyBranch2, M>; - - fn bind(space: Self::Allocated) -> Self::Buddy { - BuddyBranch2 { - free_info: space.free_info, - left: Box::new(B::bind(*space.left)), - right: Box::new(B::bind(*space.right)), - } - } - - fn struct_ref<'a, F, M: ManagerBase>(space: &'a Self::Buddy) -> Self::Allocated - where - F: FnManager>, - { - BuddyBranch2LayoutF { - free_info: space.free_info.struct_ref::(), - left: Box::new(B::struct_ref::(&space.left)), - right: Box::new(B::struct_ref::(&space.right)), - } - } -} - -/// Branch in a Buddy-style memory manager tree -pub struct BuddyBranch2 { - free_info: Cell, - left: Box, - right: Box, -} - -impl, M: ManagerBase> BuddyBranch2 { - fn refresh(&mut self) - where - M: ManagerReadWrite, - { - self.free_info.write(FreeInfo { - left_longest_free_sequence: self.left.longest_free_sequence(), - left_free_start: self.left.count_free_start(), - left_free_end: self.left.count_free_end(), - right_longest_free_sequence: self.right.longest_free_sequence(), - right_free_start: self.right.count_free_start(), - right_free_end: self.right.count_free_end(), - }); - } -} - -impl NewState for BuddyBranch2 -where - B: Buddy, - M: ManagerBase, -{ - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - free_info: Cell::new_with(manager, FreeInfo { - left_longest_free_sequence: B::PAGES, - left_free_start: B::PAGES, - left_free_end: B::PAGES, - right_longest_free_sequence: B::PAGES, - right_free_start: B::PAGES, - right_free_end: B::PAGES, - }), - left: Box::new(B::new(manager)), - right: Box::new(B::new(manager)), - } - } -} - -impl Buddy for BuddyBranch2 -where - B: Buddy, - M: ManagerBase, -{ - const PAGES: u64 = B::PAGES * 2; - - fn allocate(&mut self, pages: u64) -> Option - where - M: ManagerReadWrite, - { - if !(1..=Self::PAGES).contains(&pages) { - return None; - } - - // The requested range can be allocated entirely in the left buddy - if pages <= self.free_info.left_longest_free_sequence { - let idx = self.left.allocate(pages)?; - self.refresh(); - return Some(idx); - } - - let left_free_end = self.free_info.left_free_end; - let right_free_start = self.free_info.right_free_start; - let overlap = left_free_end.saturating_add(right_free_start); - - // There might be space that covers the end of the left buddy and the start of the right - // buddy. We may use this space as it is continuous. - if pages <= overlap && left_free_end > 0 { - let idx = B::PAGES.checked_sub(left_free_end)?; - self.left.allocate_fixed(idx, left_free_end, true)?; - - let right_pages = pages.saturating_sub(left_free_end); - - // Allocate the right buddy. Be aware, if that fails, we need to back out of the left - // buddy allocation. - if right_pages > 0 && self.right.allocate_fixed(0, right_pages, true).is_none() { - self.left.deallocate(idx, left_free_end); - return None; - } - - self.refresh(); - return Some(idx); - } - - // Try allocating in the right buddy if there's enough space - if pages <= self.free_info.right_longest_free_sequence { - let idx = self.right.allocate(pages)?; - self.refresh(); - - // Return adjusted index to reflect position in the combined buddy - return Some(idx + B::PAGES); - } - - None - } - - fn allocate_fixed(&mut self, idx: u64, pages: u64, replace: bool) -> Option<()> - where - M: ManagerReadWrite, - { - if pages == 0 || pages > Self::PAGES.saturating_sub(idx) { - return None; - } - - let left_pages = B::PAGES.saturating_sub(idx).min(pages); - let right_pages = pages.saturating_sub(left_pages); - - // The range covers the left buddy - if left_pages > 0 { - self.left.allocate_fixed(idx, left_pages, replace)?; - } - - // The range covers the right buddy - if right_pages > 0 { - let right_idx = idx.saturating_sub(B::PAGES); - let right_res = self.right.allocate_fixed(right_idx, right_pages, replace); - - // If the right allocation failed, we might need to do some clean up on the left buddy - if right_res.is_none() { - if left_pages > 0 { - self.left.deallocate(idx, left_pages); - } - - return None; - } - } - - // Need to refresh the free counters - self.refresh(); - - Some(()) - } - - fn deallocate(&mut self, idx: u64, mut pages: u64) - where - M: ManagerReadWrite, - { - // Defer to only the right buddy if the range does not cover the left side - if idx >= B::PAGES { - self.right.deallocate(idx - B::PAGES, pages); - } else { - let end = pages.saturating_add(idx); - - // If the range crosses from left to right buddy - if end > B::PAGES { - let overhang = end.saturating_sub(B::PAGES); - self.right.deallocate(0, overhang); - pages = pages.saturating_sub(overhang); - } - - self.left.deallocate(idx, pages); - } - - // Need to refresh the free counters - self.refresh(); - } - - fn longest_free_sequence(&self) -> u64 - where - M: ManagerRead, - { - self.free_info - .left_free_end - .saturating_add(self.free_info.right_free_start) - .max(self.free_info.left_longest_free_sequence) - .max(self.free_info.right_longest_free_sequence) - } - - fn count_free_start(&self) -> u64 - where - M: ManagerRead, - { - let free_start = self.free_info.left_free_start; - - if free_start < B::PAGES { - return free_start; - } - - self.free_info.right_free_start.saturating_add(B::PAGES) - } - - fn count_free_end(&self) -> u64 - where - M: ManagerRead, - { - let free_end = self.free_info.right_free_end; - - if free_end < B::PAGES { - return free_end; - } - - self.free_info.left_free_end.saturating_add(B::PAGES) - } - - fn clone(&self) -> Self - where - M: ManagerClone, - { - Self { - free_info: self.free_info.clone(), - left: Box::new(self.left.clone()), - right: Box::new(self.right.clone()), - } - } -} - -impl Serialize for BuddyBranch2 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - BuddyBranch2LayoutF { - free_info: &self.free_info, - left: &self.left, - right: &self.right, - } - .serialize(serializer) - } -} - -impl<'de, B: Deserialize<'de>, M: ManagerDeserialise> Deserialize<'de> for BuddyBranch2 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let inner: BuddyBranch2LayoutF<_, _, _> = Deserialize::deserialize(deserializer)?; - Ok(Self { - free_info: inner.free_info, - left: inner.left, - right: inner.right, - }) - } -} - -impl PartialEq for BuddyBranch2 { - fn eq(&self, other: &Self) -> bool { - self.free_info.eq(&other.free_info) - && self.left.eq(&other.left) - && self.right.eq(&other.right) - } -} diff --git a/src/riscv/lib/src/machine_state/memory/buddy/branch_combinations.rs b/src/riscv/lib/src/machine_state/memory/buddy/branch_combinations.rs deleted file mode 100644 index c880c1262e31772014cfa7c540db1391fc3b20ba..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/buddy/branch_combinations.rs +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Like [`super::branch`] but for various branch sizes -//! -//! Introducing more types instead of composing [`BuddyBranch2`]/[`BuddyBranch2Layout`] makes type -//! checking much faster. - -use serde::Deserialize; -use serde::Serialize; - -use super::Buddy; -use super::BuddyLayout; -use super::branch::BuddyBranch2; -use super::branch::BuddyBranch2Layout; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FnManager; -use crate::state_backend::FromProofResult; -use crate::state_backend::Layout; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerDeserialise; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::PartialHashError; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::RefProofGenOwnedAlloc; -use crate::state_backend::RefVerifierAlloc; -use crate::state_backend::proof_backend::merkle::MerkleTree; -use crate::storage::Hash; -use crate::storage::HashError; - -/// Generate a new combined Buddy branch. -macro_rules! combined_buddy_branch { - ($name:ident = $buddy1:ident * $buddy2:ident) => { - paste::paste! { - /// Allocated combined Buddy branch - pub struct [<$name Alloc>](AllocatedOf<[<$buddy1 Layout>]<[<$buddy2 Layout>]>, M>); - - // Passthrough implementation, default derive macro can't derive this ... - impl PartialEq for [<$name Alloc>] - where - B: Layout, - M: ManagerSerialise, - AllocatedOf<[<$buddy1 Layout>]<[<$buddy2 Layout>]>, M>: PartialEq, - { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } - } - - // Passthrough implementation, default derive macro can't derive this ... - impl Serialize for [<$name Alloc>] - where - B: Layout, - M: ManagerSerialise, - AllocatedOf<[<$buddy1 Layout>]<[<$buddy2 Layout>]>, M>: Serialize, - { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.0.serialize(serializer) - } - } - - - // Passthrough implementation, default derive macro can't derive this ... - impl<'de, B, M> Deserialize<'de> for [<$name Alloc>] - where - B: Layout, - M: ManagerDeserialise, - AllocatedOf<[<$buddy1 Layout>]<[<$buddy2 Layout>]>, M>: Deserialize<'de>, - { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(Self(Deserialize::deserialize(deserializer)?)) - } - } - - // NOTE: We can't use `struct_layout!` to help us define the type and impls below. The - // macro doesn't define new type aliases for the combined layouts which results in a - // massive type tree to traverse. This makes type checking super slow. - - /// Layout for a combined Buddy branch - pub struct [<$name Layout>](B); - - impl Layout for [<$name Layout>] { - type Allocated = [<$name Alloc>]; - - } - - impl CommitmentLayout for [<$name Layout>] { - fn state_hash(state: AllocatedOf) -> Result { - <[<$buddy1 Layout>]<[<$buddy2 Layout>]>>::state_hash(state.0) - } - } - - impl ProofLayout for [<$name Layout>] { - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - <[<$buddy1 Layout>]<[<$buddy2 Layout>]>>::to_merkle_tree(state.0) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let inner = <[<$buddy1 Layout>]<[<$buddy2 Layout>]>>::from_proof(proof)?; - Ok([<$name Alloc>](inner)) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - <[<$buddy1 Layout>]<[<$buddy2 Layout>]>>::partial_state_hash(state.0, proof) - } - } - - impl BuddyLayout for [<$name Layout>] { - type Buddy = $name, M>; - - fn bind(space: Self::Allocated) -> Self::Buddy { - let inner = <[<$buddy1 Layout>]<[<$buddy2 Layout>]> as BuddyLayout>::bind(space.0); - $name(inner) - } - - fn struct_ref<'a, F, M: ManagerBase>(space: &'a Self::Buddy) -> Self::Allocated - where - F: FnManager>, - { - let inner = <[<$buddy1 Layout>]<[<$buddy2 Layout>]> as BuddyLayout>::struct_ref::(&space.0); - [<$name Alloc>](inner) - } - } - } - - /// Combined Buddy branch - pub struct $name($buddy1<$buddy2, M>); - - // Passthrough implementation, default derive macro can't derive this ... - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - Serialize::serialize(&self.0, serializer) - } - } - - // Passthrough implementation, default derive macro can't derive this ... - impl<'de, B: Deserialize<'de>, M: ManagerDeserialise> Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(Self(Deserialize::deserialize(deserializer)?)) - } - } - - // Passthrough implementation, default derive macro can't derive this ... - impl PartialEq for $name - where - B: PartialEq, - { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } - } - - impl NewState for $name - where - B: Buddy, - M: ManagerBase, - { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self(NewState::new(manager)) - } - } - - impl Buddy for $name - where - B: Buddy, - M: ManagerBase, - { - const PAGES: u64 = <$buddy1<$buddy2, M> as Buddy>::PAGES; - - fn allocate(&mut self, pages: u64) -> Option - where - M: ManagerReadWrite, - { - self.0.allocate(pages) - } - - fn allocate_fixed(&mut self, idx: u64, pages: u64, replace: bool) -> Option<()> - where - M: ManagerReadWrite, - { - self.0.allocate_fixed(idx, pages, replace) - } - - fn deallocate(&mut self, idx: u64, pages: u64) - where - M: ManagerReadWrite, - { - self.0.deallocate(idx, pages) - } - - fn longest_free_sequence(&self) -> u64 - where - M: ManagerRead, - { - self.0.longest_free_sequence() - } - - fn count_free_start(&self) -> u64 - where - M: ManagerRead, - { - self.0.count_free_start() - } - - fn count_free_end(&self) -> u64 - where - M: ManagerRead, - { - self.0.count_free_end() - } - - fn clone(&self) -> Self - where - M: ManagerClone, - { - $name(self.0.clone()) - } - } - }; -} - -combined_buddy_branch!(BuddyBranch4 = BuddyBranch2 * BuddyBranch2); -combined_buddy_branch!(BuddyBranch8 = BuddyBranch4 * BuddyBranch2); -combined_buddy_branch!(BuddyBranch16 = BuddyBranch4 * BuddyBranch4); -combined_buddy_branch!(BuddyBranch32 = BuddyBranch4 * BuddyBranch8); -combined_buddy_branch!(BuddyBranch64 = BuddyBranch8 * BuddyBranch8); -combined_buddy_branch!(BuddyBranch128 = BuddyBranch16 * BuddyBranch8); -combined_buddy_branch!(BuddyBranch256 = BuddyBranch16 * BuddyBranch16); -combined_buddy_branch!(BuddyBranch1Ki = BuddyBranch32 * BuddyBranch32); -combined_buddy_branch!(BuddyBranch256Ki = BuddyBranch256 * BuddyBranch1Ki); diff --git a/src/riscv/lib/src/machine_state/memory/buddy/leaf.rs b/src/riscv/lib/src/machine_state/memory/buddy/leaf.rs deleted file mode 100644 index a252d71c3eba80766a390efc1c6c016ba598ce84..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/buddy/leaf.rs +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Leaf of a tree that forms a Buddy-style memory manager - -use serde::Deserialize; -use serde::Serialize; - -use super::Buddy; -use super::BuddyLayout; -use crate::bits::ones; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FnManager; -use crate::state_backend::FromProofResult; -use crate::state_backend::Layout; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerDeserialise; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::PartialHashError; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::RefProofGenOwnedAlloc; -use crate::state_backend::RefVerifierAlloc; -use crate::state_backend::proof_backend::merkle::MerkleTree; -use crate::storage::Hash; -use crate::storage::HashError; - -/// Layout for a leaf of a tree that forms a Buddy-style memory manager -pub struct BuddyLeafLayout; - -impl Layout for BuddyLeafLayout { - type Allocated = BuddyLeaf; -} - -impl CommitmentLayout for BuddyLeafLayout { - fn state_hash(state: AllocatedOf) -> Result { - Atom::state_hash(state.set) - } -} - -impl ProofLayout for BuddyLeafLayout { - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - Atom::to_merkle_tree(state.set) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - Ok(Self::Allocated { - set: Atom::from_proof(proof)?, - }) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - Atom::partial_state_hash(state.set, proof) - } -} - -impl BuddyLayout for BuddyLeafLayout { - type Buddy = BuddyLeaf; - - fn bind(space: Self::Allocated) -> Self::Buddy { - space - } - - fn struct_ref<'a, F, M: ManagerBase>(space: &'a Self::Buddy) -> Self::Allocated - where - F: FnManager>, - { - BuddyLeaf { - set: space.set.struct_ref::(), - } - } -} - -/// Leaf of a tree that forms a Buddy-style memory manager -pub struct BuddyLeaf { - /// Each bit of the `u64` represents a page. - /// The least significant bit is the page with index 0. - set: Cell, -} - -impl NewState for BuddyLeaf { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - set: Cell::new(manager), - } - } -} - -impl Buddy for BuddyLeaf { - const PAGES: u64 = PAGES; - - fn allocate(&mut self, pages: u64) -> Option - where - M: ManagerReadWrite, - { - if pages == 0 || pages > Self::PAGES { - return None; - } - - let set = self.set.read(); - - for start in 0..=(Self::PAGES - pages) { - let mask = ones(pages) << start; - - // Since the mask projects only the bits representing the current page range, none of - // the bits may be set. If they are, then there is an existing overlapping allocation - // in place already. - if (set & mask) == 0 { - self.set.write(set | mask); - return Some(start); - } - } - - None - } - - fn allocate_fixed(&mut self, idx: u64, pages: u64, replace: bool) -> Option<()> - where - M: ManagerReadWrite, - { - if pages == 0 || pages > Self::PAGES.saturating_sub(idx) { - return None; - } - - // Shortcut to avoid state reads - if idx == 0 && pages == Self::PAGES && replace { - self.set.write(!0); - return Some(()); - } - - // Sequence of `pages` 1s starting at bit `idx` - let mask = ones(pages) << idx; - - let set = self.set.read(); - - if !replace && (set & mask) != 0 { - // If none of the bits representing the to-be-mapped pages are set, then - // `already_mapped` should be 0 after applying the mask - return None; - } - - self.set.write(set | mask); - - Some(()) - } - - fn deallocate(&mut self, idx: u64, pages: u64) - where - M: ManagerReadWrite, - { - if pages == 0 || pages > Self::PAGES.saturating_sub(idx) { - return; - } - - // Shortcut to avoid state reads - if idx == 0 && pages == Self::PAGES { - self.set.write(0); - return; - } - - // Sequence of `pages` 1s starting at bit `idx` - let mask = ones(pages) << idx; - - // Clear the bits representing the page range - let set = self.set.read(); - self.set.write(set & !mask); - } - - fn longest_free_sequence(&self) -> u64 - where - M: ManagerRead, - { - let set = self.set.read(); - - if set == 0 { - return Self::PAGES; - } - - // Find the longest sequence of 0s - (0..Self::PAGES).fold(0, |longest_seq, shift| { - let free_max_pages = Self::PAGES - shift; - let free_end = (set >> shift).trailing_zeros() as u64; - free_end.min(free_max_pages).max(longest_seq) - }) - } - - fn count_free_start(&self) -> u64 - where - M: ManagerRead, - { - Self::PAGES.min(self.set.read().trailing_zeros() as u64) - } - - fn count_free_end(&self) -> u64 - where - M: ManagerRead, - { - let leading_unused_bits = (u64::BITS as u64) - .checked_sub(Self::PAGES) - .expect("PAGES must not be larger than 64"); - (self.set.read().leading_zeros() as u64).saturating_sub(leading_unused_bits) - } - - fn clone(&self) -> Self - where - M: ManagerClone, - { - Self { - set: self.set.clone(), - } - } -} - -impl Serialize for BuddyLeaf { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.set.serialize(serializer) - } -} - -impl<'de, const PAGES: u64, M: ManagerDeserialise> Deserialize<'de> for BuddyLeaf { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(Self { - set: Deserialize::deserialize(deserializer)?, - }) - } -} - -impl PartialEq for BuddyLeaf { - fn eq(&self, other: &Self) -> bool { - self.set.eq(&other.set) - } -} diff --git a/src/riscv/lib/src/machine_state/memory/buddy/proxy.rs b/src/riscv/lib/src/machine_state/memory/buddy/proxy.rs deleted file mode 100644 index e871caab97267f8de31818970650e33c889524e7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/buddy/proxy.rs +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Simplified [`BuddyLayout`] selection using const-generics - -use super::BuddyLayout; -use super::branch_combinations::BuddyBranch1KiLayout; -use super::branch_combinations::BuddyBranch4Layout; -use super::branch_combinations::BuddyBranch16Layout; -use super::branch_combinations::BuddyBranch256Layout; -use super::leaf::BuddyLeafLayout; -use crate::state_backend::AllocatedOf; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FnManager; -use crate::state_backend::FromProofResult; -use crate::state_backend::Layout; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::PartialHashError; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::RefProofGenOwnedAlloc; -use crate::state_backend::RefVerifierAlloc; -use crate::state_backend::proof_backend::merkle::MerkleTree; -use crate::storage::Hash; -use crate::storage::HashError; - -/// Proxy for a [`BuddyLayout`] that manages the specified number of `PAGES` -pub struct BuddyLayoutProxy; - -impl Layout for BuddyLayoutProxy -where - (): BuddyLayoutMatch, -{ - type Allocated = as Layout>::Allocated; -} - -impl CommitmentLayout for BuddyLayoutProxy -where - (): BuddyLayoutMatch, -{ - fn state_hash(state: AllocatedOf) -> Result { - as CommitmentLayout>::state_hash(state) - } -} - -impl ProofLayout for BuddyLayoutProxy -where - (): BuddyLayoutMatch, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - as ProofLayout>::to_merkle_tree(state) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - as ProofLayout>::from_proof(proof) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - as ProofLayout>::partial_state_hash(state, proof) - } -} - -impl BuddyLayout for BuddyLayoutProxy -where - (): BuddyLayoutMatch, -{ - type Buddy = as BuddyLayout>::Buddy; - - fn bind(space: Self::Allocated) -> Self::Buddy { - as BuddyLayout>::bind(space) - } - - fn struct_ref<'a, F, M: ManagerBase>(space: &'a Self::Buddy) -> Self::Allocated - where - F: FnManager>, - { - as BuddyLayout>::struct_ref::(space) - } -} - -/// Picks a [`BuddyLayout`] given a number of pages -type PickLayout = >::AssocLayout; - -/// Link between a number of pages and a specific [`BuddyLayout`] -pub trait BuddyLayoutMatch { - type AssocLayout: BuddyLayout; -} - -impl BuddyLayoutMatch<1> for T { - type AssocLayout = BuddyLeafLayout<1>; -} - -impl BuddyLayoutMatch<2> for T { - type AssocLayout = BuddyLeafLayout<2>; -} - -impl BuddyLayoutMatch<64> for T { - type AssocLayout = BuddyLeafLayout<64>; -} - -impl BuddyLayoutMatch<256> for T { - type AssocLayout = BuddyBranch4Layout>; -} - -impl BuddyLayoutMatch<1024> for T { - type AssocLayout = BuddyBranch4Layout>; -} - -impl BuddyLayoutMatch<{ 16 * 1024 }> for T { - type AssocLayout = BuddyBranch16Layout>; -} - -impl BuddyLayoutMatch<{ 256 * 1024 }> for T { - type AssocLayout = BuddyBranch256Layout>; -} - -impl BuddyLayoutMatch<{ 1024 * 1024 }> for T { - type AssocLayout = BuddyBranch1KiLayout>; -} diff --git a/src/riscv/lib/src/machine_state/memory/config.rs b/src/riscv/lib/src/machine_state/memory/config.rs deleted file mode 100644 index 73516e3cb7d436e0d1fdd59dcfb4c5e019f62157..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/config.rs +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::buddy::BuddyLayout; -use super::buddy::BuddyLayoutProxy; -use super::protection::PagePermissions; -use super::protection::PagePermissionsLayout; -use super::state::MemoryImpl; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::DynArray; -use crate::state_backend::DynCells; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::Ref; - -/// State layout for the memory component -pub struct MemoryConfig; - -impl NewState - for MemoryImpl -where - B: NewState, - M: ManagerBase, -{ - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - MemoryImpl { - data: DynCells::new(manager), - readable_pages: PagePermissions::new(manager), - writable_pages: PagePermissions::new(manager), - executable_pages: PagePermissions::new(manager), - allocated_pages: B::new(manager), - } - } -} - -impl super::MemoryConfig - for MemoryConfig -where - BuddyLayoutProxy: BuddyLayout + 'static, -{ - const TOTAL_BYTES: usize = TOTAL_BYTES; - - type Layout = ( - DynArray, - PagePermissionsLayout, - PagePermissionsLayout, - PagePermissionsLayout, - BuddyLayoutProxy, - ); - - type State = - MemoryImpl as BuddyLayout>::Buddy, M>; - - fn bind(space: AllocatedOf) -> Self::State { - if TOTAL_BYTES == 0 { - panic!("Memory size must be positive"); - } - - if PAGES.checked_mul(super::PAGE_SIZE.get() as usize) != Some(TOTAL_BYTES) { - panic!( - "Memory size {} must be a non-overflowing multiple of the page size {}", - TOTAL_BYTES, - super::PAGE_SIZE - ); - } - - MemoryImpl { - data: space.0, - readable_pages: PagePermissions::bind(space.1), - writable_pages: PagePermissions::bind(space.2), - executable_pages: PagePermissions::bind(space.3), - allocated_pages: as BuddyLayout>::bind(space.4), - } - } - - fn struct_ref<'a, M, F>(instance: &'a Self::State) -> AllocatedOf - where - M: ManagerBase, - F: FnManager>, - { - ( - instance.data.struct_ref::(), - instance.readable_pages.struct_ref::(), - instance.writable_pages.struct_ref::(), - instance.executable_pages.struct_ref::(), - as BuddyLayout>::struct_ref::(&instance.allocated_pages), - ) - } -} - -/// Generates a valid memory configuration. -macro_rules! gen_memory_layout { - ($name:ident = $size_in_g:literal GiB) => { - pub type $name = - MemoryConfig<{ $size_in_g * 1024 * 256 }, { $size_in_g * 1024 * 1024 * 1024 }>; - }; - - ($name:ident = $size_in_m:literal MiB) => { - pub type $name = MemoryConfig<{ $size_in_m * 256 }, { $size_in_m * 1024 * 1024 }>; - }; - - ($name:ident = $size_in_k:literal KiB) => { - pub type $name = MemoryConfig<{ $size_in_k / 4 }, { $size_in_k * 1024 }>; - }; -} - -gen_memory_layout!(M4K = 4 KiB); -gen_memory_layout!(M8K = 8 KiB); -gen_memory_layout!(M1M = 1 MiB); -gen_memory_layout!(M64M = 64 MiB); -gen_memory_layout!(M1G = 1 GiB); -gen_memory_layout!(M4G = 4 GiB); diff --git a/src/riscv/lib/src/machine_state/memory/protection.rs b/src/riscv/lib/src/machine_state/memory/protection.rs deleted file mode 100644 index f5f0f824b961d84159a941a49d4ee1c2e83af2b6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/protection.rs +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::Address; -use crate::array_utils::boxed_from_fn; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerDeserialise; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerSerialise; -use crate::state_backend::ManagerWrite; -use crate::state_backend::Many; -use crate::state_backend::Ref; - -/// State layout for page permissions -pub type PagePermissionsLayout = Many, PAGES>; - -/// Tracks access permissions for each page -pub struct PagePermissions { - pages: Box<[Cell; PAGES]>, -} - -impl PagePermissions { - /// Bind the given allocated space as a page protections state value. - pub fn bind(space: AllocatedOf, M>) -> Self { - Self { - pages: space.try_into().unwrap_or_else(|_| { - unreachable!("Converting a vector into an array of the same length always succeeds") - }), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf, F::Output> { - self.pages - .iter() - .map(|item| item.struct_ref::()) - .collect() - } - - /// Check if the memory at `address..address+length` can be accessed. - /// - /// # Safety - /// - /// The address and length must be valid for an address space consisting of a number of `PAGES`. - /// This function is not defined for address and length combinations which are out of bounds. - #[inline] - pub unsafe fn can_access(&self, address: Address, length: usize) -> bool - where - M: ManagerRead, - { - // Zero-sized accesses are always valid - if length < 1 { - return true; - } - - let address = address as usize; - - // Extract the page index range from using the start and end addresses - let start_page = address >> super::OFFSET_BITS; - let end_page = address.wrapping_add(length).wrapping_sub(1) >> super::OFFSET_BITS; - - for page in start_page..=end_page { - if unsafe { !self.pages.get_unchecked(page).read() } { - return false; - } - } - - true - } - - /// Change the access permissions for the given range. - pub fn modify_access(&mut self, address: Address, length: usize, accessible: bool) - where - M: ManagerWrite, - { - if length < 1 { - return; - } - - let address = address as usize; - let start_page = address >> super::OFFSET_BITS; - let end_page = address.wrapping_add(length).wrapping_sub(1) >> super::OFFSET_BITS; - - (start_page..=end_page) - .filter(|&page| page < PAGES) - .for_each(|page| { - self.pages[page].write(accessible); - }) - } -} - -impl NewState for PagePermissions { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - PagePermissions { - pages: boxed_from_fn(|| Cell::new(manager)), - } - } -} - -impl<'de, const PAGES: usize, M: ManagerDeserialise> serde::Deserialize<'de> - for PagePermissions -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - Ok(Self::bind( - , M>>::deserialize(deserializer)?, - )) - } -} - -impl serde::Serialize for PagePermissions { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.pages.serialize(serializer) - } -} - -impl Clone for PagePermissions { - fn clone(&self) -> Self { - Self { - pages: self.pages.clone(), - } - } -} diff --git a/src/riscv/lib/src/machine_state/memory/state.rs b/src/riscv/lib/src/machine_state/memory/state.rs deleted file mode 100644 index 48cc6bcd4a796e81a3cfb13c20e778cb7f4585d6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/memory/state.rs +++ /dev/null @@ -1,440 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::mem; - -use super::Address; -use super::BadMemoryAccess; -use super::Memory; -use super::PAGE_SIZE; -use super::Permissions; -use super::buddy::Buddy; -use super::protection::PagePermissions; -use crate::state_backend::DynCells; -use crate::state_backend::Elem; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; - -/// Machine's memory -pub struct MemoryImpl { - /// Memory contents - pub(super) data: DynCells, - - /// Read permissions per page - pub(super) readable_pages: PagePermissions, - - /// Write permissions per page - pub(super) writable_pages: PagePermissions, - - /// Execute permissions per page - pub(super) executable_pages: PagePermissions, - - /// Allocation tracker - pub(super) allocated_pages: B, -} - -impl - MemoryImpl -{ - /// Ensure the access is within bounds. - #[inline] - fn check_bounds(address: Address, length: usize, error: E) -> Result<(), E> { - if length > TOTAL_BYTES.saturating_sub(address as usize) { - return Err(error); - } - - Ok(()) - } - - /// Mark the whole memory as readable and writeable - #[cfg(test)] - pub(crate) fn set_all_readable_writeable(&mut self) - where - B: Buddy, - M: ManagerReadWrite, - { - self.protect_pages(0, TOTAL_BYTES, Permissions::ReadWrite) - .unwrap(); - } - - /// Update an element in the region without checking memory protections. `address` is in bytes. - #[cfg(test)] - pub(crate) fn write_instruction_unchecked( - &mut self, - address: Address, - value: E, - ) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerWrite, - { - let length = mem::size_of::(); - Self::check_bounds(address, length, BadMemoryAccess)?; - - self.data.write(address as usize, value); - self.readable_pages.modify_access(address, length, true); - self.executable_pages.modify_access(address, length, true); - Ok(()) - } -} - -impl Memory - for MemoryImpl -where - B: Buddy, - M: ManagerBase, -{ - #[inline] - fn read(&self, address: Address) -> Result - where - E: Elem, - M: ManagerRead, - { - Self::check_bounds(address, mem::size_of::(), BadMemoryAccess)?; - - // SAFETY: The bounds check above ensures the access check below is safe - unsafe { - if !self.readable_pages.can_access(address, mem::size_of::()) { - return Err(BadMemoryAccess); - } - } - - Ok(self.data.read(address as usize)) - } - - #[inline] - fn read_exec(&self, address: Address) -> Result - where - E: Elem, - M: ManagerRead, - { - Self::check_bounds(address, mem::size_of::(), BadMemoryAccess)?; - - // SAFETY: The bounds check above ensures the access check below is safe - unsafe { - // Checking for executable access is sufficient as that implies read access - if !self - .executable_pages - .can_access(address, mem::size_of::()) - { - return Err(BadMemoryAccess); - } - } - - Ok(self.data.read(address as usize)) - } - - fn read_all(&self, address: Address, values: &mut [E]) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerRead, - { - Self::check_bounds(address, mem::size_of_val(values), BadMemoryAccess)?; - - // SAFETY: The bounds check above ensures the access check below is safe - unsafe { - if !self - .readable_pages - .can_access(address, mem::size_of_val(values)) - { - return Err(BadMemoryAccess); - } - } - - self.data.read_all(address as usize, values); - Ok(()) - } - - #[inline] - fn write(&mut self, address: Address, value: E) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerReadWrite, - { - Self::check_bounds(address, mem::size_of::(), BadMemoryAccess)?; - - // SAFETY: The bounds check above ensures the access check below is safe - unsafe { - if !self.writable_pages.can_access(address, mem::size_of::()) { - return Err(BadMemoryAccess); - } - } - - self.data.write(address as usize, value); - Ok(()) - } - - fn write_all(&mut self, address: Address, values: &[E]) -> Result<(), BadMemoryAccess> - where - E: Elem, - M: ManagerReadWrite, - { - Self::check_bounds(address, mem::size_of_val(values), BadMemoryAccess)?; - - // SAFETY: The bounds check above ensures the access check below is safe - unsafe { - if !self - .writable_pages - .can_access(address, mem::size_of_val(values)) - { - return Err(BadMemoryAccess); - } - } - - self.data.write_all(address as usize, values); - Ok(()) - } - - fn clone(&self) -> Self - where - M: ManagerClone, - { - Self { - data: self.data.clone(), - readable_pages: self.readable_pages.clone(), - writable_pages: self.writable_pages.clone(), - executable_pages: self.executable_pages.clone(), - allocated_pages: self.allocated_pages.clone(), - } - } - - fn reset(&mut self) - where - M: ManagerWrite, - { - const SIZE_OF_U64: usize = mem::size_of::(); - - let mut address = 0; - let mut outstanding = TOTAL_BYTES; - - // Write 64-bit chunks - while outstanding >= SIZE_OF_U64 { - self.data.write(address, 0u64); - address += SIZE_OF_U64; - outstanding -= SIZE_OF_U64; - } - - // Write remaining bytes - for i in 0..outstanding { - self.data.write(address.saturating_add(i), 0u8); - } - } - - fn protect_pages( - &mut self, - address: Address, - length: usize, - perms: Permissions, - ) -> Result<(), super::MemoryGovernanceError> - where - M: ManagerWrite, - { - Self::check_bounds(address, length, super::MemoryGovernanceError)?; - - self.readable_pages - .modify_access(address, length, perms.can_read()); - self.writable_pages - .modify_access(address, length, perms.can_write()); - self.executable_pages - .modify_access(address, length, perms.can_exec()); - - Ok(()) - } - - fn deallocate_pages( - &mut self, - address: Address, - length: usize, - ) -> Result<(), super::MemoryGovernanceError> - where - M: ManagerReadWrite, - { - Self::check_bounds(address, length, super::MemoryGovernanceError)?; - - // See RV-561: Use `u64` for indices and lengths that come from the PVM - let pages = (length as u64).div_ceil(super::PAGE_SIZE.get()); - - // Buddy memory manager works on page indices, not addresses - let idx = address >> super::OFFSET_BITS; - self.allocated_pages.deallocate(idx, pages); - - Ok(()) - } - - fn allocate_pages( - &mut self, - address_hint: Option
, - length: usize, - allow_replace: bool, - ) -> Result - where - M: ManagerReadWrite, - { - // The interface works on usize at the moment, however, going forward we'll convert all - // length types to u64 to avoid machine-specific behavior for lengths. - // See RV-561: Use `u64` for indices and lengths that come from the PVM - let pages = (length as u64).div_ceil(super::PAGE_SIZE.get()); - - match address_hint { - // Caller wants to allocate at a specific address - Some(address) => { - Self::check_bounds(address, length, super::MemoryGovernanceError)?; - - // Buddy memory manager works on page indices, not addresses - let idx = address >> super::OFFSET_BITS; - self.allocated_pages - .allocate_fixed(idx, pages, allow_replace) - .map(|()| address) - } - - // Allocate anywhere - None => self.allocated_pages.allocate(pages).map(|idx| { - // Convert page index to address - idx << super::OFFSET_BITS - }), - } - .ok_or(super::MemoryGovernanceError) - } - - fn allocate_and_protect_pages( - &mut self, - address_hint: Option
, - length: usize, - perms: Permissions, - allow_replace: bool, - ) -> Result - where - M: ManagerReadWrite, - { - // Mark the page range as occupied - let address = self.allocate_pages(address_hint, length, allow_replace)?; - - // Configure the permissions on the page range - if self.protect_pages(address, length, perms).is_err() { - self.deallocate_pages(address, length)?; - } - - // Zero initialise in 8-byte chunks. Using larger writes first, means we do fewer writes - // altogether. This speeds things up. - // As we allocate in multiples of pages, we must also clear in multiples of pages. - let mut remaining = length - .div_ceil(PAGE_SIZE.get() as usize) - .saturating_mul(PAGE_SIZE.get() as usize); - - while remaining >= 8 { - remaining -= 8; - let address = (address as usize).saturating_add(remaining); - self.data.write(address, 0u64); - } - - // Zero initialise the tail byte by byte - for i in 0..remaining { - let address = (address as usize).saturating_add(i); - self.data.write(address, 0u8); - } - - Ok(address) - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::backend_test; - use crate::machine_state::memory::M4K; - use crate::machine_state::memory::MemoryConfig; - use crate::state::NewState; - use crate::state_backend::owned_backend::Owned; - - #[test] - fn bounds_check() { - type OwnedM0 = MemoryImpl<0, 0, (), Owned>; - type OwnedM4K = ::State; - - // Zero-sized reads or writes are always valid - assert!(OwnedM0::check_bounds(0, 0, ()).is_ok()); - assert!(OwnedM0::check_bounds(1, 0, ()).is_ok()); - assert!(OwnedM4K::check_bounds(0, 0, ()).is_ok()); - assert!(OwnedM4K::check_bounds(4096, 0, ()).is_ok()); - assert!(OwnedM4K::check_bounds(2 * 4096, 0, ()).is_ok()); - - // Bounds checks - assert!(OwnedM0::check_bounds(0, 1, ()).is_err()); - assert!(OwnedM0::check_bounds(1, 1, ()).is_err()); - assert!(OwnedM4K::check_bounds(4096, 1, ()).is_err()); - assert!(OwnedM4K::check_bounds(2 * 4096, 1, ()).is_err()); - } - - // This test verifies that memory is fully zeroed up to the page boundary, not just the - // requested length, when allocating memory. - backend_test!(test_memory_fully_zeroed_on_allocation, F, { - use crate::machine_state::memory::PAGE_SIZE; - use crate::machine_state::memory::Permissions; - - let mut manager = F::manager(); - let mut memory = <::State<_>>::new(&mut manager); - - // Write a pattern to ensure memory contains non-zero values - for i in 0..PAGE_SIZE.get() { - memory.data.write(i as usize, 0xFFu8); - } - - // Request size that's not a multiple of page size - let requested_size = (PAGE_SIZE.get() as usize) - 100; - let address = memory - .allocate_and_protect_pages(None, requested_size, Permissions::ReadWrite, false) - .expect("Memory allocation should succeed"); - - // Verify that memory is zeroed for the entire page, not just the requested length - for i in 0..PAGE_SIZE.get() { - let offset = i as usize; - let value = memory.data.read::((address as usize) + offset); - assert_eq!( - value, - 0, - "Memory at offset {} (address: {:#x}) should be zero, found {:#x}", - offset, - address + i, - value - ); - } - }); - - backend_test!(test_endianess, F, { - let mut manager = F::manager(); - let mut memory = <::State<_>>::new(&mut manager); - - memory - .write_instruction_unchecked(0, 0x1122334455667788u64) - .unwrap(); - - macro_rules! check_address { - ($ty:ty, $addr:expr, $value:expr) => { - assert_eq!(memory.read::<$ty>($addr), Ok($value)); - }; - } - - check_address!(u64, 0, 0x1122334455667788); - - check_address!(u32, 0, 0x55667788); - check_address!(u32, 4, 0x11223344); - - check_address!(u16, 0, 0x7788); - check_address!(u16, 2, 0x5566); - check_address!(u16, 4, 0x3344); - check_address!(u16, 6, 0x1122); - - check_address!(u8, 0, 0x88); - check_address!(u8, 1, 0x77); - check_address!(u8, 2, 0x66); - check_address!(u8, 3, 0x55); - check_address!(u8, 4, 0x44); - check_address!(u8, 5, 0x33); - check_address!(u8, 6, 0x22); - check_address!(u8, 7, 0x11); - }); -} diff --git a/src/riscv/lib/src/machine_state/mode.rs b/src/riscv/lib/src/machine_state/mode.rs deleted file mode 100644 index 7193985bfcf7c665a58f272c2e7656f5ab783e07..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/mode.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use crate::default::ConstDefault; -use crate::machine_state::csregisters::Privilege; - -/// Modes the hardware state can be in when running code -#[derive( - Debug, - PartialEq, - PartialOrd, - Eq, - Copy, - Clone, - strum::EnumIter, - serde::Serialize, - serde::Deserialize, -)] -pub enum Mode { - User, - Supervisor, - Machine, -} - -impl Mode { - /// Obtain the corresponding [`Privilege`] for [`Mode`]. - pub fn privilege(&self) -> Privilege { - match self { - Mode::User => Privilege::Unprivileged, - Mode::Supervisor => Privilege::Supervisor, - Mode::Machine => Privilege::Machine, - } - } -} - -impl ConstDefault for Mode { - const DEFAULT: Self = Self::Machine; -} - -impl From for u8 { - #[inline] - fn from(value: Mode) -> Self { - value as u8 - } -} - -/// Modes the hardware state can trap into, a sub-enum of [`Mode`] -#[derive(Debug, PartialEq, PartialOrd, Eq, Copy, Clone, strum::EnumIter)] -pub enum TrapMode { - Supervisor, - Machine, -} - -impl TrapMode { - /// Construct the mode corresponding to the trap mode. - pub fn as_mode(&self) -> Mode { - match self { - Self::Supervisor => Mode::Supervisor, - Self::Machine => Mode::Machine, - } - } -} diff --git a/src/riscv/lib/src/machine_state/registers.rs b/src/riscv/lib/src/machine_state/registers.rs deleted file mode 100644 index c760aebc8722f5f055254bca1e9d8ac2710eea90..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/registers.rs +++ /dev/null @@ -1,786 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 TriliTech -// -// SPDX-License-Identifier: MIT - -// We have several globals whose names we want to align with the RISC-V -// specification. -#![allow(non_upper_case_globals)] -#![expect( - dead_code, - reason = "Aliases for register ABI names might not be used everywhere" -)] - -use std::fmt; - -use arbitrary_int::u5; - -use crate::default::ConstDefault; -use crate::machine_state::backend; -use crate::state::NewState; -use crate::state_backend::owned_backend::Owned; - -/// Integer register index -#[expect(non_camel_case_types, reason = "Consistent with RISC-V spec")] -#[repr(u8)] -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - serde::Serialize, - serde::Deserialize, - strum::EnumIter, -)] -pub enum XRegister { - // The `usize` representation of these constructors shall be used as an - // index into the 31-element array holding the registers. - // x0 represents no entry in this array because it is handled separately. - // Therefore we assign a dummy index to x0. - x0 = u8::MAX, - x1 = 0, - x2, - x3, - x4, - x5, - x6, - x7, - x8, - x9, - x10, - x11, - x12, - x13, - x14, - x15, - x16, - x17, - x18, - x19, - x20, - x21, - x22, - x23, - x24, - x25, - x26, - x27, - x28, - x29, - x30, - x31, -} - -// We would like to have the constructors at the module top-level. -pub use XRegister::*; - -// ABI register names -pub const zero: XRegister = x0; -pub const ra: XRegister = x1; -pub const sp: XRegister = x2; -pub const gp: XRegister = x3; -pub const tp: XRegister = x4; -pub const t0: XRegister = x5; -pub const t1: XRegister = x6; -pub const t2: XRegister = x7; -pub const s0: XRegister = x8; -pub const fp: XRegister = x8; -pub const s1: XRegister = x9; -pub const a0: XRegister = x10; -pub const a1: XRegister = x11; -pub const a2: XRegister = x12; -pub const a3: XRegister = x13; -pub const a4: XRegister = x14; -pub const a5: XRegister = x15; -pub const a6: XRegister = x16; -pub const a7: XRegister = x17; -pub const s2: XRegister = x18; -pub const s3: XRegister = x19; -pub const s4: XRegister = x20; -pub const s5: XRegister = x21; -pub const s6: XRegister = x22; -pub const s7: XRegister = x23; -pub const s8: XRegister = x24; -pub const s9: XRegister = x25; -pub const s10: XRegister = x26; -pub const s11: XRegister = x27; -pub const t3: XRegister = x28; -pub const t4: XRegister = x29; -pub const t5: XRegister = x30; -pub const t6: XRegister = x31; - -#[inline(always)] -pub const fn parse_xregister(r: u5) -> XRegister { - let r = r.value().wrapping_sub(1); - - // SAFETY: the bounds of u5, under a wrapping decrement - // are known to correspond to valid values of - // XRegister's underlying representation. - unsafe { std::mem::transmute(r) } -} - -impl fmt::Display for XRegister { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let name = match *self { - x0 => "zero", - x1 => "ra", - x2 => "sp", - x3 => "gp", - x4 => "tp", - x5 => "t0", - x6 => "t1", - x7 => "t2", - x8 => "s0", - x9 => "s1", - x10 => "a0", - x11 => "a1", - x12 => "a2", - x13 => "a3", - x14 => "a4", - x15 => "a5", - x16 => "a6", - x17 => "a7", - x18 => "s2", - x19 => "s3", - x20 => "s4", - x21 => "s5", - x22 => "s6", - x23 => "s7", - x24 => "s8", - x25 => "s9", - x26 => "s10", - x27 => "s11", - x28 => "t3", - x29 => "t4", - x30 => "t5", - x31 => "t6", - }; - f.write_str(name) - } -} - -impl XRegister { - #[inline] - pub fn is_zero(self) -> bool { - self == x0 - } -} - -impl From for XRegister { - fn from(r: NonZeroXRegister) -> Self { - // SAFETY: XRegister is a superset of NonZeroXRegister, - // so any value of NonZeroXRegister is known to be - // a valid value of XRegister. - unsafe { std::mem::transmute(r) } - } -} - -/// Integer register value -pub type XValue = u64; - -/// Integer value for 32-bit operations. -pub type XValue32 = u32; - -/// Layout for [XRegisters] -pub type XRegistersLayout = backend::Array; - -/// Integer registers -pub struct XRegisters { - registers: backend::Cells, -} - -impl XRegisters { - /// Bind the integer register state to the given allocated space. - pub fn bind(space: backend::AllocatedOf) -> Self { - XRegisters { registers: space } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf { - self.registers.struct_ref::() - } - - /// Try to read a 64-bit value from the registers and coerce it to another type. - #[inline] - pub fn try_read>(&self, reg: XRegister) -> Result - where - M: backend::ManagerRead, - { - self.read(reg).try_into() - } - - /// Read an integer from the registers. - #[inline] - pub fn read(&self, reg: XRegister) -> XValue - where - M: backend::ManagerRead, - { - if reg.is_zero() { - return 0; - } - - self.registers.read(reg as usize) - } - - /// Write an integer to the registers. - #[inline] - pub fn write(&mut self, reg: XRegister, val: XValue) - where - M: backend::ManagerWrite, - { - if reg.is_zero() { - return; - } - - self.registers.write(reg as usize, val) - } - - /// Read an integer from the registers without checking if it is 0 first. - #[inline] - pub fn read_nz(&self, reg: NonZeroXRegister) -> XValue - where - M: backend::ManagerRead, - { - self.registers.read(reg as usize) - } - - /// Write an integer to the registers without checking if it is 0 first. - #[inline] - pub fn write_nz(&mut self, reg: NonZeroXRegister, val: XValue) - where - M: backend::ManagerWrite, - { - self.registers.write(reg as usize, val) - } - - /// Reset the integer registers. - pub fn reset(&mut self) - where - M: backend::ManagerWrite, - { - for i in 0..31 { - self.registers.write(i, XValue::default()); - } - } -} - -impl XRegisters { - /// Get the byte offset from a pointer to `XRegisters` to the memory of the value - /// stored in the `reg` in question. - pub(crate) const fn xregister_offset(reg: NonZeroXRegister) -> usize { - std::mem::offset_of!(Self, registers) - + backend::Cells::::region_elem_offset(reg as usize) - } -} - -impl NewState for XRegisters { - fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - XRegisters { - registers: backend::Cells::new(manager), - } - } -} - -impl Clone for XRegisters { - fn clone(&self) -> Self { - Self { - registers: self.registers.clone(), - } - } -} - -/// Register index for integer registers known from the opcode to be `!=x0`. -#[expect(non_camel_case_types, reason = "Consistent with RISC-V spec")] -#[repr(u8)] -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - serde::Serialize, - serde::Deserialize, - strum::EnumIter, -)] -pub enum NonZeroXRegister { - // This enum represents XRegisters known from the opcode to be `!=x0`, hence omitting - // x0 from the list. - // The `usize` representation of these constructors shall be used as an - // index into the 31-element array holding the XRegisters. - x1 = 0, - x2, - x3, - x4, - x5, - x6, - x7, - x8, - x9, - x10, - x11, - x12, - x13, - x14, - x15, - x16, - x17, - x18, - x19, - x20, - x21, - x22, - x23, - x24, - x25, - x26, - x27, - x28, - x29, - x30, - x31, -} - -impl fmt::Display for NonZeroXRegister { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", XRegister::from(*self)) - } -} - -impl NonZeroXRegister { - /// Convert an `XRegister` to a `NonZeroXRegister`, provided `r != x0`. - /// - /// # Panics - /// Panics if `r == x0`. - pub const fn assert_from(r: XRegister) -> Self { - match r { - x0 => panic!("x0 is not a non-zero register"), - // SAFETY: Excluding x0, XRegister is a - // direct map to NonZeroXRegister, so safe to convert. - r => unsafe { std::mem::transmute::(r) }, - } - } -} - -/// ABI register names for NonZeroXRegister types used in backend tests. -pub mod nz { - use super::NonZeroXRegister; - - pub const ra: NonZeroXRegister = NonZeroXRegister::x1; - pub const sp: NonZeroXRegister = NonZeroXRegister::x2; - pub const gp: NonZeroXRegister = NonZeroXRegister::x3; - pub const tp: NonZeroXRegister = NonZeroXRegister::x4; - pub const t0: NonZeroXRegister = NonZeroXRegister::x5; - pub const t1: NonZeroXRegister = NonZeroXRegister::x6; - pub const t2: NonZeroXRegister = NonZeroXRegister::x7; - pub const s0: NonZeroXRegister = NonZeroXRegister::x8; - pub const fp: NonZeroXRegister = NonZeroXRegister::x8; - pub const s1: NonZeroXRegister = NonZeroXRegister::x9; - pub const a0: NonZeroXRegister = NonZeroXRegister::x10; - pub const a1: NonZeroXRegister = NonZeroXRegister::x11; - pub const a2: NonZeroXRegister = NonZeroXRegister::x12; - pub const a3: NonZeroXRegister = NonZeroXRegister::x13; - pub const a4: NonZeroXRegister = NonZeroXRegister::x14; - pub const a5: NonZeroXRegister = NonZeroXRegister::x15; - pub const a6: NonZeroXRegister = NonZeroXRegister::x16; - pub const a7: NonZeroXRegister = NonZeroXRegister::x17; - pub const s2: NonZeroXRegister = NonZeroXRegister::x18; - pub const s3: NonZeroXRegister = NonZeroXRegister::x19; - pub const s4: NonZeroXRegister = NonZeroXRegister::x20; - pub const s5: NonZeroXRegister = NonZeroXRegister::x21; - pub const s6: NonZeroXRegister = NonZeroXRegister::x22; - pub const s7: NonZeroXRegister = NonZeroXRegister::x23; - pub const s8: NonZeroXRegister = NonZeroXRegister::x24; - pub const s9: NonZeroXRegister = NonZeroXRegister::x25; - pub const s10: NonZeroXRegister = NonZeroXRegister::x26; - pub const s11: NonZeroXRegister = NonZeroXRegister::x27; - pub const t3: NonZeroXRegister = NonZeroXRegister::x28; - pub const t4: NonZeroXRegister = NonZeroXRegister::x29; - pub const t5: NonZeroXRegister = NonZeroXRegister::x30; - pub const t6: NonZeroXRegister = NonZeroXRegister::x31; -} - -/// Floating-point number register index -#[expect(non_camel_case_types, reason = "Consistent with RISC-V spec")] -#[repr(u8)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - strum::EnumIter, - Hash, - serde::Serialize, - serde::Deserialize, -)] -pub enum FRegister { - f0 = 0, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - f22, - f23, - f24, - f25, - f26, - f27, - f28, - f29, - f30, - f31, -} - -#[inline(always)] -pub const fn parse_fregister(r: u5) -> FRegister { - let r = r.value(); - - // SAFETY: the possible values of u5 are known to correspond to - // the possible values of the underlying representation of FRegister. - unsafe { std::mem::transmute(r) } -} - -// We want those constructors at the module top-level. -pub use FRegister::*; - -// ABI register names -pub const ft0: FRegister = f0; -pub const ft1: FRegister = f1; -pub const ft2: FRegister = f2; -pub const ft3: FRegister = f3; -pub const ft4: FRegister = f4; -pub const ft5: FRegister = f5; -pub const ft6: FRegister = f6; -pub const ft7: FRegister = f7; -pub const fs0: FRegister = f8; -pub const fs1: FRegister = f9; -pub const fa0: FRegister = f10; -pub const fa1: FRegister = f11; -pub const fa2: FRegister = f12; -pub const fa3: FRegister = f13; -pub const fa4: FRegister = f14; -pub const fa5: FRegister = f15; -pub const fa6: FRegister = f16; -pub const fa7: FRegister = f17; -pub const fs2: FRegister = f18; -pub const fs3: FRegister = f19; -pub const fs4: FRegister = f20; -pub const fs5: FRegister = f21; -pub const fs6: FRegister = f22; -pub const fs7: FRegister = f23; -pub const fs8: FRegister = f24; -pub const fs9: FRegister = f25; -pub const fs10: FRegister = f26; -pub const fs11: FRegister = f27; -pub const ft8: FRegister = f28; -pub const ft9: FRegister = f29; -pub const ft10: FRegister = f30; -pub const ft11: FRegister = f31; - -impl fmt::Display for FRegister { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let name = match *self { - f0 => "ft0", - f1 => "ft1", - f2 => "ft2", - f3 => "ft3", - f4 => "ft4", - f5 => "ft5", - f6 => "ft6", - f7 => "ft7", - f8 => "fs0", - f9 => "fs1", - f10 => "fa0", - f11 => "fa1", - f12 => "fa2", - f13 => "fa3", - f14 => "fa4", - f15 => "fa5", - f16 => "fa6", - f17 => "fa7", - f18 => "fs2", - f19 => "fs3", - f20 => "fs4", - f21 => "fs5", - f22 => "fs6", - f23 => "fs7", - f24 => "fs8", - f25 => "fs9", - f26 => "fs10", - f27 => "fs11", - f28 => "ft8", - f29 => "ft9", - f30 => "ft10", - f31 => "ft11", - }; - f.write_str(name) - } -} - -/// Floating-point number register value -#[repr(transparent)] -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Default, - Debug, - derive_more::From, - derive_more::Into, - serde::Serialize, - serde::Deserialize, -)] -pub struct FValue(u64); - -impl ConstDefault for FValue { - const DEFAULT: Self = Self(0); -} - -impl backend::Elem for FValue { - #[inline(always)] - fn store(&mut self, source: &Self) { - *self = *source; - } - - #[inline(always)] - fn to_stored_in_place(&mut self) {} - - #[inline(always)] - fn from_stored_in_place(&mut self) {} - - #[inline(always)] - fn from_stored(source: &Self) -> Self { - *source - } -} - -/// Layout for [FRegisters] -pub type FRegistersLayout = backend::Array; - -/// Floating-point number registers -pub struct FRegisters { - registers: backend::Cells, -} - -impl FRegisters { - /// Allocate a new floating-point registers state. - pub fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - Self { - registers: backend::Cells::new(manager), - } - } - - /// Bind the floating-point register space to the allocated space. - pub fn bind(space: backend::AllocatedOf) -> Self { - FRegisters { registers: space } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf { - self.registers.struct_ref::() - } - - /// Reset the floating-point registers. - pub fn reset(&mut self) - where - M: backend::ManagerWrite, - { - for i in 0..32 { - self.registers.write(i, FValue::default()); - } - } - - /// Read a floating-point number from the registers. - #[inline(always)] - pub fn read(&self, reg: FRegister) -> FValue - where - M: backend::ManagerRead, - { - self.registers.read(reg as usize) - } - - /// Write a floating-point number to the registers. - #[inline(always)] - pub fn write(&mut self, reg: FRegister, val: FValue) - where - M: backend::ManagerWrite, - { - self.registers.write(reg as usize, val) - } -} - -impl Clone for FRegisters { - fn clone(&self) -> Self { - Self { - registers: self.registers.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use arbitrary_int::Number; - use strum::IntoEnumIterator; - - use super::*; - use crate::backend_test; - use crate::state_backend::ManagerRead; - - backend_test!(test_zero, F, { - let mut registers = XRegisters::new(&mut F::manager()); - - // x0 should always read 0. - assert_eq!(registers.read(x0), 0); - - // Writing anything to x0 must not have an effect. - registers.write(x0, 1337); - assert_eq!(registers.read(x0), 0); - }); - - const NONZERO_REGISTERS: [XRegister; 31] = [ - x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, - x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, - ]; - - backend_test!(test_arbitrary_register, F, { - let mut registers = XRegisters::new(&mut F::manager()); - - // Initialise the registers with something. - for reg in NONZERO_REGISTERS { - let value = rand::random(); - registers.write(reg, value); - assert_eq!(registers.read(reg), value); - } - - // Check state transitions work as expected. - for reg in NONZERO_REGISTERS { - let value = rand::random(); - let expected = { - let mut snapshot = NONZERO_REGISTERS.map(|r| registers.read(r)); - snapshot[reg as usize] = value; - snapshot - }; - - registers.write(reg, value); - - let after = NONZERO_REGISTERS.map(|r| registers.read(r)); - assert_eq!(after, expected); - } - }); - - backend_test!(test_try_read_u32, F, { - let mut registers = XRegisters::new(&mut F::manager()); - - // Reading an integer that is too large should fail - registers.write(x1, 1 << 32); - assert!(registers.try_read::(x1).is_err()); - - // Reading an integer that is negative should fail - registers.write(x1, -1i64 as u64); - assert!(registers.try_read::(x1).is_err()); - - registers.write(x1, 42); - assert_eq!(registers.try_read::(x1), Ok(42)); - }); - - #[test] - fn parse_xregister_zero_to_x0() { - assert_eq!(0_u8, (XRegister::x0 as u8).wrapping_add(1)); - - assert_eq!(parse_xregister(u5::new(0)), XRegister::x0); - } - - #[test] - fn parse_xregister_nonzero() { - for (idx, xreg) in NONZERO_REGISTERS.iter().enumerate() { - assert_eq!(*xreg as usize, idx); - - assert_eq!(parse_xregister(u5::new((idx + 1) as u8)), *xreg); - } - } - - #[test] - fn parse_fregister_all() { - for (idx, freg) in FRegister::iter().enumerate() { - assert_eq!(freg as usize, idx); - - assert_eq!(parse_fregister(u5::new(idx as u8)), freg); - } - } - - #[test] - fn fregister_bounds() { - assert_eq!(FRegister::f0 as usize, u5::new(0).value() as usize); - assert_eq!(FRegister::f31 as usize, u5::MAX.value() as usize); - } - - #[test] - fn test_nonzeroxregister_to_xregister_conversion() { - let nzreg = NonZeroXRegister::iter().collect::>(); - let reg = XRegister::iter().filter(|r| r != &x0).collect::>(); - - assert_eq!(nzreg.len(), reg.len()); - for i in 0..nzreg.len() { - assert_eq!(nzreg[i] as u8, reg[i] as u8) - } - } - - #[test] - fn test_xregister_offsets() { - let registers = XRegisters::new(&mut Owned); - let registers_ptr = (®isters) as *const XRegisters; - - for reg in NonZeroXRegister::iter() { - let offset = XRegisters::::xregister_offset(reg); - let val: &XValue = Owned::region_ref(registers.registers.region_ref(), reg as usize); - - // Safety: both pointers are valid - let offset_refs = unsafe { (val as *const XValue).byte_offset_from(registers_ptr) }; - - assert_eq!( - offset_refs, offset as isize, - "Calculated offset to elem not equal to offset from references for {reg:?}" - ); - } - } -} diff --git a/src/riscv/lib/src/machine_state/reservation_set.rs b/src/riscv/lib/src/machine_state/reservation_set.rs deleted file mode 100644 index 37ef68f8171dd94d300ae649d8cb7d36662a6735..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/machine_state/reservation_set.rs +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Reservation set for Load-Reserved/Store-Conditional instructions -//! in the RISC-V A extension -//! -//! Section 8.2 - Unprivileged spec - -use crate::machine_state::backend; -/// Executing a LR.x instructions registers a reservation set on the address -/// from which data was loaded. The success of a SC.x instruction is conditional -/// on there being a valid reservation which includes the word or doubleword -/// being stored. Every SC.x, wether successful or not, invalidates the hart's -/// reservation set. -/// -/// "The invalidation of a hart’s reservation when it executes an LR or SC implies -/// that a hart can only hold one reservation at a time, and that an SC can only -/// pair with the most recent LR, and LR with the next following SC, in program -/// order." -use crate::machine_state::backend::Cell; -use crate::state::NewState; - -pub struct ReservationSet { - start_addr: Cell, -} - -/// Layout for [ReservationSet] -pub type ReservationSetLayout = backend::Atom; - -/// The size of the reservation set is 8 bytes in order to accommodate -/// LR.D/SC.D instructions which work on doubles. -/// -/// "An implementation can register an arbitrarily large reservation set on -/// each LR, provided the reservation set includes all bytes of the addressed -/// data word or doubleword. [...] The Unix platform is expected to require of -/// main memory that the reservation set be of fixed size, contiguous, naturally -/// aligned, and no greater than the virtual memory page size." -const SIZE: u64 = 8; - -const UNSET_VALUE: u64 = u64::MAX; - -const fn align_address(address: u64, align: u64) -> u64 { - let offset = address.rem_euclid(align); - if offset > 0 { - return address + align - offset; - } - address -} - -impl ReservationSet { - /// Bind the reservation set cell to the given allocated space - pub fn bind(space: backend::AllocatedOf) -> Self { - Self { start_addr: space } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: backend::FnManager>>( - &'a self, - ) -> backend::AllocatedOf { - self.start_addr.struct_ref::() - } - - /// Unset any reservation - pub fn reset(&mut self) - where - M: backend::ManagerWrite, - { - self.write(UNSET_VALUE); - } - - #[inline(always)] - fn write(&mut self, addr: u64) - where - M: backend::ManagerWrite, - { - self.start_addr.write(addr) - } - - #[inline(always)] - fn read(&self) -> u64 - where - M: backend::ManagerRead, - { - self.start_addr.read() - } - - /// Set the reservation set as `addr` aligned to the nearest double - #[inline] - pub fn set(&mut self, addr: u64) - where - M: backend::ManagerWrite, - { - self.write(align_address(addr, SIZE)) - } - - /// Check wether the `addr` is within the reservation set - pub fn test_and_unset(&mut self, addr: u64) -> bool - where - M: backend::ManagerReadWrite, - { - let start_addr = self.read(); - // Regardless of success or failure, executing an SC.x instruction - // invalidates any reservation held by this hart. - self.reset(); - start_addr != UNSET_VALUE && start_addr == align_address(addr, SIZE) - } -} - -impl NewState for ReservationSet { - fn new(manager: &mut M) -> Self - where - M: backend::ManagerAlloc, - { - ReservationSet { - start_addr: Cell::new(manager), - } - } -} - -impl Clone for ReservationSet { - fn clone(&self) -> Self { - Self { - start_addr: self.start_addr.clone(), - } - } -} diff --git a/src/riscv/lib/src/parser.rs b/src/riscv/lib/src/parser.rs deleted file mode 100644 index 5856f8e028cfbccd58c30524f08e78cd41c6480e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/parser.rs +++ /dev/null @@ -1,1721 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -#![allow(clippy::reversed_empty_ranges)] - -pub mod instruction; - -use core::ops::Range; -use std::fmt; -use std::fmt::Display; -use std::fmt::Formatter; - -use arbitrary_int::u3; -use arbitrary_int::u5; -use instruction::*; - -use crate::bits::u16; -use crate::machine_state::csregisters::CSRegister; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::parse_fregister; -use crate::machine_state::registers::parse_xregister; -use crate::machine_state::registers::x0; - -/// Given an instruction encoded as a little-endian `u32`, extract `n` bits -/// starting at `pos`. -#[inline(always)] -const fn bits(bytes: u32, pos: usize, n: usize) -> u32 { - (bytes >> pos) & (!0 >> (32 - n)) -} - -#[inline(always)] -const fn bit(bytes: u32, pos: usize) -> bool { - bytes & (1 << pos) != 0 -} - -#[inline(always)] -const fn opcode(instr: u32) -> u32 { - bits(instr, 0, 7) -} - -#[inline(always)] -const fn funct3(instr: u32) -> u32 { - bits(instr, 12, 3) -} - -#[inline(always)] -const fn funct5(instr: u32) -> u32 { - bits(instr, 27, 5) -} - -#[inline(always)] -const fn funct7(instr: u32) -> u32 { - bits(instr, 25, 7) -} - -#[inline(always)] -const fn rd(instr: u32) -> XRegister { - parse_xregister(u5::extract_u32(instr, 7)) -} - -#[inline(always)] -const fn rd_f(instr: u32) -> FRegister { - parse_fregister(u5::extract_u32(instr, 7)) -} - -#[inline(always)] -const fn rs1_bits(instr: u32) -> u32 { - bits(instr, 15, 5) -} - -#[inline(always)] -const fn rs1_bits_u5(instr: u32) -> u5 { - u5::extract_u32(instr, 15) -} - -#[inline(always)] -const fn rs1(instr: u32) -> XRegister { - parse_xregister(rs1_bits_u5(instr)) -} - -#[inline(always)] -const fn rs1_f(instr: u32) -> FRegister { - parse_fregister(rs1_bits_u5(instr)) -} - -#[inline(always)] -const fn rs2_bits(instr: u32) -> u32 { - bits(instr, 20, 5) -} - -#[inline(always)] -const fn rs2_bits_u5(instr: u32) -> u5 { - u5::extract_u32(instr, 20) -} - -#[inline(always)] -const fn rs2(instr: u32) -> XRegister { - parse_xregister(rs2_bits_u5(instr)) -} - -#[inline(always)] -const fn rs2_f(instr: u32) -> FRegister { - parse_fregister(rs2_bits_u5(instr)) -} - -#[inline(always)] -const fn rs3_f(instr: u32) -> FRegister { - parse_fregister(u5::extract_u32(instr, 27)) -} - -#[inline(always)] -const fn imm_11_6(instr: u32) -> u32 { - bits(instr, 26, 6) << 1 -} - -#[inline(always)] -const fn fm(instr: u32) -> u32 { - bits(instr, 28, 4) -} - -/// Floating-point format field encoding -#[inline(always)] -const fn fmt(instr: u32) -> u32 { - bits(instr, 25, 2) -} - -/// Floating-point width field encoding -#[inline(always)] -const fn width(instr: u32) -> u32 { - bits(instr, 12, 3) -} - -const fn csr(instr: u32) -> Option { - CSRegister::try_parse(bits(instr, 20, 12)) -} - -// Immediates are produced by extracting the relevant bits according to the -// instruction format (c.f. Section 2.3), then shifting them into place. -// The sign bit is always bit 31 of the instruction. Sign extension is -// performed by first casting each segment to i32, then casting the produced -// immediate to i64. - -const fn i_imm(instr: u32) -> i64 { - // instr[31:20] - (((instr & 0b1111_1111_1111_0000_0000_0000_0000_0000) as i32) >> 20) as i64 -} - -const fn shift_imm(instr: u32) -> i64 { - let imm = i_imm(instr); - imm & SHIFT_BITMASK -} - -const fn s_imm(instr: u32) -> i64 { - // instr[31:25] | instr[11:7] - let instr_31_25 = (instr & 0b1111_1110_0000_0000_0000_0000_0000_0000) as i32; - let instr_11_7 = (instr & 0b0000_0000_0000_0000_0000_1111_1000_0000) as i32; - ((instr_31_25 >> 20) | (instr_11_7 >> 7)) as i64 -} - -const fn b_imm(instr: u32) -> i64 { - // instr[31] | instr[7] | instr[30:25] | instr[11:8] | 0 - let instr_31 = (instr & 0b1000_0000_0000_0000_0000_0000_0000_0000) as i32; - let instr_7 = (instr & 0b0000_0000_0000_0000_0000_0000_1000_0000) as i32; - let instr_30_25 = (instr & 0b0111_1110_0000_0000_0000_0000_0000_0000) as i32; - let instr_11_8 = (instr & 0b0000_0000_0000_0000_0000_1111_0000_0000) as i32; - ((instr_31 >> 19) | (instr_7 << 4) | (instr_30_25 >> 20) | (instr_11_8 >> 7)) as i64 -} - -const fn u_imm(instr: u32) -> i64 { - // instr[31:12] | 0000_0000_0000 - ((instr & 0b1111_1111_1111_1111_1111_0000_0000_0000) as i32) as i64 -} - -const fn j_imm(instr: u32) -> i64 { - // instr[31] | instr[19:12] | instr[20] | instr[30:21] | 0 - let instr_31 = (instr & 0b1000_0000_0000_0000_0000_0000_0000_0000) as i32; - let instr_19_12 = (instr & 0b0000_0000_0000_1111_1111_0000_0000_0000) as i32; - let instr_20 = (instr & 0b0000_0000_0001_0000_0000_0000_0000_0000) as i32; - let instr_30_21 = (instr & 0b0111_1111_1110_0000_0000_0000_0000_0000) as i32; - ((instr_31 >> 11) | instr_19_12 | (instr_20 >> 9) | (instr_30_21 >> 20)) as i64 -} - -macro_rules! r_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::RTypeArgs { - rd: rd($instr), - rs1: rs1($instr), - rs2: rs2($instr), - }) - }; - - ($enum_variant:ident, $instr:expr, $rd:expr) => { - $enum_variant(instruction::NonZeroRdRTypeArgs { - rd: $rd, - rs1: rs1($instr), - rs2: rs2($instr), - }) - }; -} - -macro_rules! i_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::ITypeArgs { - rd: rd($instr), - rs1: rs1($instr), - imm: i_imm($instr), - }) - }; - - ($enum_variant:ident, $instr:expr, $rd:expr) => { - $enum_variant(instruction::NonZeroRdITypeArgs { - rd: $rd, - rs1: rs1($instr), - imm: i_imm($instr), - }) - }; -} - -macro_rules! s_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::SBTypeArgs { - rs1: rs1($instr), - rs2: rs2($instr), - imm: s_imm($instr), - }) - }; -} - -macro_rules! b_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::SBTypeArgs { - rs1: rs1($instr), - rs2: rs2($instr), - imm: b_imm($instr), - }) - }; -} - -macro_rules! u_instr { - ($enum_variant:ident, $instr:expr, $rd:expr) => { - $enum_variant(instruction::NonZeroRdUJTypeArgs { - rd: $rd, - imm: u_imm($instr), - }) - }; -} - -macro_rules! j_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::UJTypeArgs { - rd: rd($instr), - imm: j_imm($instr), - }) - }; -} - -macro_rules! fl_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::FLoadArgs { - rd: rd_f($instr), - rs1: rs1($instr), - imm: i_imm($instr), - }) - }; -} - -macro_rules! fs_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::FStoreArgs { - rs1: rs1($instr), - rs2: rs2_f($instr), - imm: s_imm($instr), - }) - }; -} - -macro_rules! f_cmp_instr { - ($enum_variant:ident, $instr:expr, $rs2_bits:expr) => { - $enum_variant(instruction::FCmpArgs { - rd: rd($instr), - rs1: rs1_f($instr), - rs2: parse_fregister($rs2_bits), - }) - }; -} - -macro_rules! f_r_instr { - ($enum_variant:ident, $instr:expr, $rs2_bits:expr) => { - $enum_variant(instruction::FRArgs { - rd: rd_f($instr), - rs1: rs1_f($instr), - rs2: parse_fregister($rs2_bits), - }) - }; -} - -macro_rules! f_r_rm_1_instr { - ($enum_variant:ident, $instr:expr, $rm:expr) => { - if let Some(rounding) = InstrRoundingMode::from_rm($rm) { - $enum_variant(instruction::FR1ArgWithRounding { - rd: rd_f($instr), - rs1: rs1_f($instr), - rm: rounding, - }) - } else { - Unknown { instr: $instr } - } - }; -} - -macro_rules! f_r_fmt_int_instr { - ($enum_variant:ident, $instr:expr, $rm:expr) => { - if let Some(rounding) = InstrRoundingMode::from_rm($rm) { - $enum_variant(instruction::XRegToFRegArgsWithRounding { - rd: rd_f($instr), - rs1: rs1($instr), - rm: rounding, - }) - } else { - Unknown { instr: $instr } - } - }; -} - -macro_rules! f_r_int_fmt_instr { - ($enum_variant:ident, $instr:expr, $rm:expr) => { - if let Some(rounding) = InstrRoundingMode::from_rm($rm) { - $enum_variant(instruction::FRegToXRegArgsWithRounding { - rd: rd($instr), - rs1: rs1_f($instr), - rm: rounding, - }) - } else { - Unknown { instr: $instr } - } - }; -} - -macro_rules! f_r_rm_2_instr { - ($enum_variant:ident, $instr:expr, $rs2_bits:expr, $rm:expr) => {{ - if let Some(rounding) = InstrRoundingMode::from_rm($rm) { - $enum_variant(instruction::FR2ArgsWithRounding { - rd: rd_f($instr), - rs1: rs1_f($instr), - rs2: parse_fregister($rs2_bits), - rm: rounding, - }) - } else { - Unknown { instr: $instr } - } - }}; -} - -macro_rules! f_r_rm_3_instr { - ($enum_variant:ident, $instr:expr) => { - if let Some(rounding) = InstrRoundingMode::from_rm(funct3($instr)) { - $enum_variant(instruction::FR3ArgsWithRounding { - rd: rd_f($instr), - rs1: rs1_f($instr), - rs2: rs2_f($instr), - rs3: rs3_f($instr), - rm: rounding, - }) - } else { - Unknown { instr: $instr } - } - }; -} - -macro_rules! fence_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::FenceArgs { - pred: FenceSet { - i: bit($instr, 27), - o: bit($instr, 26), - r: bit($instr, 25), - w: bit($instr, 24), - }, - succ: FenceSet { - i: bit($instr, 23), - o: bit($instr, 22), - r: bit($instr, 21), - w: bit($instr, 20), - }, - }) - }; -} - -macro_rules! amo_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(instruction::AmoArgs { - rd: rd($instr), - rs1: rs1($instr), - rs2: rs2($instr), - aq: bit($instr, 26), - rl: bit($instr, 25), - }) - }; -} - -macro_rules! csr_instr { - ($enum_variant:ident, $instr:expr) => { - match csr($instr) { - Some(csr) => $enum_variant(instruction::CsrArgs { - rd: rd($instr), - rs1: rs1($instr), - csr, - }), - None => Unknown { instr: $instr }, - } - }; -} - -macro_rules! csri_instr { - ($enum_variant:ident, $instr:expr) => { - match csr($instr) { - Some(csr) => $enum_variant(instruction::CsriArgs { - rd: rd($instr), - imm: bits($instr, 15, 5) as i64, - csr, - }), - None => Unknown { instr: $instr }, - } - }; -} - -const OP_ARITH: u32 = 0b011_0011; -const OP_ARITH_W: u32 = 0b011_1011; -const OP_ARITH_I: u32 = 0b001_0011; -const OP_LOAD: u32 = 0b000_0011; -const OP_ARITH_IW: u32 = 0b001_1011; -const OP_SYNCH: u32 = 0b000_1111; -const OP_SYS: u32 = 0b111_0011; -const OP_STORE: u32 = 0b010_0011; -const OP_BRANCH: u32 = 0b110_0011; -const OP_LUI: u32 = 0b011_0111; -const OP_AUIPC: u32 = 0b001_0111; -const OP_JAL: u32 = 0b110_1111; -const OP_JALR: u32 = 0b110_0111; -const OP_FP: u32 = 0b1010011; -const OP_FP_LOAD: u32 = 0b000_0111; -const OP_FMADD: u32 = 0b100_0011; -const OP_FMSUB: u32 = 0b100_0111; -const OP_FNMSUB: u32 = 0b100_1011; -const OP_FNMADD: u32 = 0b100_1111; -const OP_FP_STORE: u32 = 0b010_0111; -const OP_AMO: u32 = 0b010_1111; - -const F3_0: u32 = 0b000; -const F3_1: u32 = 0b001; -const F3_2: u32 = 0b010; -const F3_3: u32 = 0b011; -const F3_4: u32 = 0b100; -const F3_5: u32 = 0b101; -const F3_6: u32 = 0b110; -const F3_7: u32 = 0b111; - -const F5_0: u32 = 0b0_0000; -const F5_1: u32 = 0b0_0001; -const F5_2: u32 = 0b0_0010; -const F5_3: u32 = 0b0_0011; -const F5_4: u32 = 0b0_0100; -const F5_5: u32 = 0b0_0101; -const F5_8: u32 = 0b0_1000; -const F5_11: u32 = 0b0_1011; -const F5_12: u32 = 0b0_1100; -const F5_16: u32 = 0b1_0000; -const F5_20: u32 = 0b1_0100; -const F5_24: u32 = 0b1_1000; -const F5_26: u32 = 0b1_1010; -const F5_28: u32 = 0b1_1100; -const F5_30: u32 = 0b1_1110; - -const F7_0: u32 = 0b0; -const F7_1: u32 = 0b1; -const F7_9: u32 = 0b1001; -const F7_8: u32 = 0b000_1000; -const F7_20: u32 = 0b10_0000; -const F7_24: u32 = 0b001_1000; -const F7_56: u32 = 0b011_1000; - -const FMT_S: u32 = 0b0; -const FMT_D: u32 = 0b01; - -const WIDTH_W: u32 = 0b010; -const WIDTH_D: u32 = 0b011; - -const RM_0: u32 = 0b000; -const RM_1: u32 = 0b001; -const RM_2: u32 = 0b010; -const RM_EQ: u32 = RM_2; -const RM_LT: u32 = RM_1; -const RM_LE: u32 = RM_0; -const RM_MIN: u32 = RM_0; -const RM_MAX: u32 = RM_1; - -const RS1_0: u32 = 0b0; -const RS2_0: u32 = 0b0; -const RS2_1: u32 = 0b1; -const RS2_2: u32 = 0b10; -const RS2_5: u32 = 0b101; - -const RS2_0_U5: u5 = u5::new(0b0); -const RS2_1_U5: u5 = u5::new(0b1); -const RS2_2_U5: u5 = u5::new(0b10); -const RS2_3_U5: u5 = u5::new(0b11); - -const FM_0: u32 = 0b0; -const FM_8: u32 = 0b1000; - -pub(crate) const SHIFT_BITMASK: i64 = 0b11_1111; - -/// Parse an uncompressed instruction from a u32. -#[inline] -pub const fn parse_uncompressed_instruction(instr: u32) -> Instr { - use InstrCacheable::*; - use InstrUncacheable::*; - // RV64I (Chapter 5.4) and RV64C (Chapter 16.7) describe the code points associated - // with HINT instructions. We do not implement any HINT logic, but decode all these - // as `Hint` or `HintCompresssed` opcodes, which we translate to NOPs in `machine_state`. - use XRegisterParsed::*; - let i = match opcode(instr) { - // R-type instructions - OP_ARITH => match funct3(instr) { - F3_0 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Add, instr, rd), - (F7_1, _) => r_instr!(Mul, instr), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => r_instr!(Sub, instr, rd), - _ => Unknown { instr }, - }, - F3_4 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Xor, instr, rd), - (F7_1, _) => r_instr!(Div, instr), - _ => Unknown { instr }, - }, - F3_6 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Or, instr, rd), - (F7_1, _) => r_instr!(Rem, instr), - _ => Unknown { instr }, - }, - F3_7 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(And, instr, rd), - (F7_1, _) => r_instr!(Remu, instr), - _ => Unknown { instr }, - }, - F3_1 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Sll, instr, rd), - (F7_1, _) => r_instr!(Mulh, instr), - _ => Unknown { instr }, - }, - F3_5 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Srl, instr, rd), - (F7_1, _) => r_instr!(Divu, instr), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => r_instr!(Sra, instr, rd), - _ => Unknown { instr }, - }, - - F3_2 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Slt, instr, rd), - (F7_1, _) => r_instr!(Mulhsu, instr), - _ => Unknown { instr }, - }, - - F3_3 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Sltu, instr, rd), - (F7_1, _) => r_instr!(Mulhu, instr), - _ => Unknown { instr }, - }, - - _ => Unknown { instr }, - }, - OP_ARITH_W => match funct3(instr) { - F3_0 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Addw, instr, rd), - (F7_1, _) => r_instr!(Mulw, instr), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => r_instr!(Subw, instr, rd), - _ => Unknown { instr }, - }, - F3_1 => match split_x0(rd(instr)) { - X0 => Hint { instr }, - NonZero(rd) => r_instr!(Sllw, instr, rd), - }, - F3_4 => match funct7(instr) { - F7_1 => r_instr!(Divw, instr), - _ => Unknown { instr }, - }, - F3_5 => match (funct7(instr), split_x0(rd(instr))) { - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => r_instr!(Srlw, instr, rd), - (F7_1, NonZero(_)) => r_instr!(Divuw, instr), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => r_instr!(Sraw, instr, rd), - _ => Unknown { instr }, - }, - - F3_6 => match funct7(instr) { - F7_1 => r_instr!(Remw, instr), - _ => Unknown { instr }, - }, - F3_7 => match funct7(instr) { - F7_1 => r_instr!(Remuw, instr), - _ => Unknown { instr }, - }, - - _ => Unknown { instr }, - }, - - // I-type instructions - OP_ARITH_I => match (funct3(instr), split_x0(rd(instr))) { - (F3_0, X0) => match (split_x0(rs1(instr)), i_imm(instr)) { - (X0, _) | (_, 0) => Hint { instr }, - (rs1, imm) => Addi(instruction::SplitITypeArgs { rd: X0, rs1, imm }), - }, - (F3_0, rd) => Addi(instruction::SplitITypeArgs { - rd, - rs1: split_x0(rs1(instr)), - imm: i_imm(instr), - }), - (F3_4, X0) => Hint { instr }, - (F3_4, NonZero(rd)) => i_instr!(Xori, instr, rd), - (F3_6, X0) => Hint { instr }, - (F3_6, NonZero(rd)) => i_instr!(Ori, instr, rd), - (F3_7, X0) => Hint { instr }, - (F3_7, NonZero(rd)) => i_instr!(Andi, instr, rd), - (F3_1, rd) => match (imm_11_6(instr), rd) { - // imm[0:5] -> shift amount - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => i_instr!(Slli, instr, rd), - _ => Unknown { instr }, - }, - (F3_5, rd) => match (imm_11_6(instr), rd) { - // imm[6:11] -> type of shift, imm[0:5] -> shift amount - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => i_instr!(Srli, instr, rd), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => InstrCacheable::Srai(NonZeroRdITypeArgs { - rd, - rs1: rs1(instr), - imm: shift_imm(instr), - }), - _ => Unknown { instr }, - }, - (F3_2, X0) => Hint { instr }, - (F3_2, NonZero(rd)) => i_instr!(Slti, instr, rd), - (F3_3, X0) => Hint { instr }, - (F3_3, NonZero(rd)) => i_instr!(Sltiu, instr, rd), - _ => Unknown { instr }, - }, - OP_LOAD => match funct3(instr) { - F3_0 => i_instr!(Lb, instr), - F3_1 => i_instr!(Lh, instr), - F3_2 => i_instr!(Lw, instr), - F3_3 => i_instr!(Ld, instr), - F3_4 => i_instr!(Lbu, instr), - F3_5 => i_instr!(Lhu, instr), - F3_6 => i_instr!(Lwu, instr), - _ => Unknown { instr }, - }, - OP_ARITH_IW => match (funct3(instr), split_x0(rd(instr))) { - (F3_0, X0) => Hint { instr }, - (F3_0, NonZero(rd)) => i_instr!(Addiw, instr, rd), - (F3_1, rd) => match (imm_11_6(instr), rd) { - // imm[0:4] -> shift amount - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => i_instr!(Slliw, instr, rd), - _ => Unknown { instr }, - }, - (F3_5, rd) => match (imm_11_6(instr), rd) { - // imm[6:11] -> type of shift, imm[0:4] -> shift amount - (F7_0, X0) => Hint { instr }, - (F7_0, NonZero(rd)) => i_instr!(Srliw, instr, rd), - (F7_20, X0) => Hint { instr }, - (F7_20, NonZero(rd)) => i_instr!(Sraiw, instr, rd), - _ => Unknown { instr }, - }, - _ => Unknown { instr }, - }, - OP_SYNCH => match funct3(instr) { - F3_0 => match fm(instr) { - FM_0 => match (bits(instr, 20, 4), bits(instr, 24, 4)) { - (0, _) => Hint { instr }, - (_, 0) => Hint { instr }, - (_, _) => return Instr::Uncacheable(fence_instr!(Fence, instr)), - }, - FM_8 => return Instr::Uncacheable(fence_instr!(FenceTso, instr)), - _ => Unknown { instr }, - }, - F3_1 => return Instr::Uncacheable(InstrUncacheable::FenceI), - _ => Unknown { instr }, - }, - OP_SYS => match funct3(instr) { - F3_0 => match funct7(instr) { - F7_0 => match (rs1_bits(instr), rs2_bits(instr)) { - (RS1_0, RS2_0) => Ecall, - (RS1_0, RS2_1) => return Instr::Uncacheable(Ebreak), - _ => Unknown { instr }, - }, - F7_9 => { - return Instr::Uncacheable(SFenceVma { - vaddr: rs1(instr), - asid: rs2(instr), - }); - } - F7_8 => match (rs1_bits(instr), rs2_bits(instr)) { - (RS1_0, RS2_2) => return Instr::Uncacheable(Sret), - (RS1_0, RS2_5) => return Instr::Cacheable(Wfi), - _ => Unknown { instr }, - }, - F7_24 => match (rs1_bits(instr), rs2_bits(instr)) { - (RS1_0, RS2_2) => return Instr::Uncacheable(Mret), - _ => Unknown { instr }, - }, - F7_56 => match (rs1_bits(instr), rs2_bits(instr)) { - (RS1_0, RS2_2) => return Instr::Uncacheable(Mnret), - _ => Unknown { instr }, - }, - _ => Unknown { instr }, - }, - F3_1 => csr_instr!(Csrrw, instr), - F3_2 => csr_instr!(Csrrs, instr), - F3_3 => csr_instr!(Csrrc, instr), - F3_5 => csri_instr!(Csrrwi, instr), - F3_6 => csri_instr!(Csrrsi, instr), - F3_7 => csri_instr!(Csrrci, instr), - _ => Unknown { instr }, - }, - OP_AMO => match funct3(instr) { - F3_2 => match funct5(instr) { - F5_2 => match rs2_bits(instr) { - RS2_0 => amo_instr!(Lrw, instr), - _ => Unknown { instr }, - }, - F5_3 => amo_instr!(Scw, instr), - F5_1 => amo_instr!(Amoswapw, instr), - F5_0 => amo_instr!(Amoaddw, instr), - F5_4 => amo_instr!(Amoxorw, instr), - F5_12 => amo_instr!(Amoandw, instr), - F5_8 => amo_instr!(Amoorw, instr), - F5_16 => amo_instr!(Amominw, instr), - F5_20 => amo_instr!(Amomaxw, instr), - F5_24 => amo_instr!(Amominuw, instr), - F5_28 => amo_instr!(Amomaxuw, instr), - _ => Unknown { instr }, - }, - F3_3 => match funct5(instr) { - F5_2 => match rs2_bits(instr) { - RS2_0 => amo_instr!(Lrd, instr), - _ => Unknown { instr }, - }, - F5_3 => amo_instr!(Scd, instr), - F5_1 => amo_instr!(Amoswapd, instr), - F5_0 => amo_instr!(Amoaddd, instr), - F5_4 => amo_instr!(Amoxord, instr), - F5_12 => amo_instr!(Amoandd, instr), - F5_8 => amo_instr!(Amoord, instr), - F5_16 => amo_instr!(Amomind, instr), - F5_20 => amo_instr!(Amomaxd, instr), - F5_24 => amo_instr!(Amominud, instr), - F5_28 => amo_instr!(Amomaxud, instr), - _ => Unknown { instr }, - }, - _ => Unknown { instr }, - }, - - // S-type instructions - OP_STORE => match funct3(instr) { - F3_0 => s_instr!(Sb, instr), - F3_1 => s_instr!(Sh, instr), - F3_2 => s_instr!(Sw, instr), - F3_3 => s_instr!(Sd, instr), - _ => Unknown { instr }, - }, - - // B-type instructions - OP_BRANCH => match funct3(instr) { - F3_0 => b_instr!(Beq, instr), - F3_1 => b_instr!(Bne, instr), - F3_4 => b_instr!(Blt, instr), - F3_5 => b_instr!(Bge, instr), - F3_6 => b_instr!(Bltu, instr), - F3_7 => b_instr!(Bgeu, instr), - _ => Unknown { instr }, - }, - - // U-type instructions - OP_LUI => match split_x0(rd(instr)) { - X0 => Hint { instr }, - NonZero(rd) => u_instr!(Lui, instr, rd), - }, - OP_AUIPC => match split_x0(rd(instr)) { - X0 => Hint { instr }, - NonZero(rd) => u_instr!(Auipc, instr, rd), - }, - - // F/D-type instructions - OP_FP => match fmt(instr) { - FMT_S => match (funct5(instr), funct3(instr), rs2_bits_u5(instr)) { - (F5_0, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fadds, instr, rs2_bits, rounding) - } - (F5_1, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fsubs, instr, rs2_bits, rounding) - } - (F5_2, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fmuls, instr, rs2_bits, rounding) - } - (F5_3, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fdivs, instr, rs2_bits, rounding) - } - (F5_4, RM_0, rs2_bits) => f_r_instr!(Fsgnjs, instr, rs2_bits), - (F5_4, RM_1, rs2_bits) => f_r_instr!(Fsgnjns, instr, rs2_bits), - (F5_4, RM_2, rs2_bits) => f_r_instr!(Fsgnjxs, instr, rs2_bits), - (F5_5, RM_MIN, rs2_bits) => f_r_instr!(Fmins, instr, rs2_bits), - (F5_5, RM_MAX, rs2_bits) => f_r_instr!(Fmaxs, instr, rs2_bits), - (F5_8, rounding, RS2_1_U5) => f_r_rm_1_instr!(Fcvtsd, instr, rounding), - (F5_11, rounding, RS2_0_U5) => f_r_rm_1_instr!(Fsqrts, instr, rounding), - (F5_20, RM_EQ, rs2_bits) => f_cmp_instr!(Feqs, instr, rs2_bits), - (F5_20, RM_LE, rs2_bits) => f_cmp_instr!(Fles, instr, rs2_bits), - (F5_20, RM_LT, rs2_bits) => f_cmp_instr!(Flts, instr, rs2_bits), - (F5_24, rounding, RS2_0_U5) => f_r_int_fmt_instr!(Fcvtws, instr, rounding), - (F5_24, rounding, RS2_1_U5) => f_r_int_fmt_instr!(Fcvtwus, instr, rounding), - (F5_24, rounding, RS2_2_U5) => f_r_int_fmt_instr!(Fcvtls, instr, rounding), - (F5_24, rounding, RS2_3_U5) => f_r_int_fmt_instr!(Fcvtlus, instr, rounding), - (F5_26, rounding, RS2_0_U5) => f_r_fmt_int_instr!(Fcvtsw, instr, rounding), - (F5_26, rounding, RS2_1_U5) => f_r_fmt_int_instr!(Fcvtswu, instr, rounding), - (F5_26, rounding, RS2_2_U5) => f_r_fmt_int_instr!(Fcvtsl, instr, rounding), - (F5_26, rounding, RS2_3_U5) => f_r_fmt_int_instr!(Fcvtslu, instr, rounding), - (F5_28, RM_0, RS2_0_U5) => FmvXW(FRegToXRegArgs { - rd: rd(instr), - rs1: rs1_f(instr), - }), - (F5_28, RM_1, RS2_0_U5) => FclassS(FRegToXRegArgs { - rd: rd(instr), - rs1: rs1_f(instr), - }), - (F5_30, RM_0, RS2_0_U5) => FmvWX(XRegToFRegArgs { - rd: rd_f(instr), - rs1: rs1(instr), - }), - _ => Unknown { instr }, - }, - FMT_D => match (funct5(instr), funct3(instr), rs2_bits_u5(instr)) { - (F5_0, rounding, rs2_bits) => { - f_r_rm_2_instr!(Faddd, instr, rs2_bits, rounding) - } - (F5_1, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fsubd, instr, rs2_bits, rounding) - } - (F5_2, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fmuld, instr, rs2_bits, rounding) - } - (F5_3, rounding, rs2_bits) => { - f_r_rm_2_instr!(Fdivd, instr, rs2_bits, rounding) - } - (F5_4, RM_0, rs2_bits) => f_r_instr!(Fsgnjd, instr, rs2_bits), - (F5_4, RM_1, rs2_bits) => f_r_instr!(Fsgnjnd, instr, rs2_bits), - (F5_4, RM_2, rs2_bits) => f_r_instr!(Fsgnjxd, instr, rs2_bits), - (F5_5, RM_MIN, rs2_bits) => f_r_instr!(Fmind, instr, rs2_bits), - (F5_5, RM_MAX, rs2_bits) => f_r_instr!(Fmaxd, instr, rs2_bits), - (F5_8, rounding, RS2_0_U5) => f_r_rm_1_instr!(Fcvtds, instr, rounding), - (F5_11, rounding, RS2_0_U5) => f_r_rm_1_instr!(Fsqrtd, instr, rounding), - (F5_20, RM_EQ, rs2_bits) => f_cmp_instr!(Feqd, instr, rs2_bits), - (F5_20, RM_LE, rs2_bits) => f_cmp_instr!(Fled, instr, rs2_bits), - (F5_20, RM_LT, rs2_bits) => f_cmp_instr!(Fltd, instr, rs2_bits), - (F5_24, rounding, RS2_0_U5) => f_r_int_fmt_instr!(Fcvtwd, instr, rounding), - (F5_24, rounding, RS2_1_U5) => f_r_int_fmt_instr!(Fcvtwud, instr, rounding), - (F5_24, rounding, RS2_2_U5) => f_r_int_fmt_instr!(Fcvtld, instr, rounding), - (F5_24, rounding, RS2_3_U5) => f_r_int_fmt_instr!(Fcvtlud, instr, rounding), - (F5_26, rounding, RS2_0_U5) => f_r_fmt_int_instr!(Fcvtdw, instr, rounding), - (F5_26, rounding, RS2_1_U5) => f_r_fmt_int_instr!(Fcvtdwu, instr, rounding), - (F5_26, rounding, RS2_2_U5) => f_r_fmt_int_instr!(Fcvtdl, instr, rounding), - (F5_26, rounding, RS2_3_U5) => f_r_fmt_int_instr!(Fcvtdlu, instr, rounding), - (F5_28, RM_0, RS2_0_U5) => FmvXD(FRegToXRegArgs { - rd: rd(instr), - rs1: rs1_f(instr), - }), - (F5_28, RM_1, RS2_0_U5) => FclassD(FRegToXRegArgs { - rd: rd(instr), - rs1: rs1_f(instr), - }), - (F5_30, RM_0, RS2_0_U5) => FmvDX(XRegToFRegArgs { - rd: rd_f(instr), - rs1: rs1(instr), - }), - _ => Unknown { instr }, - }, - _ => Unknown { instr }, - }, - // F/D fused multiply add instructions - OP_FMADD => match fmt(instr) { - FMT_S => f_r_rm_3_instr!(Fmadds, instr), - FMT_D => f_r_rm_3_instr!(Fmaddd, instr), - _ => Unknown { instr }, - }, - OP_FMSUB => match fmt(instr) { - FMT_S => f_r_rm_3_instr!(Fmsubs, instr), - FMT_D => f_r_rm_3_instr!(Fmsubd, instr), - _ => Unknown { instr }, - }, - OP_FNMSUB => match fmt(instr) { - FMT_S => f_r_rm_3_instr!(Fnmsubs, instr), - FMT_D => f_r_rm_3_instr!(Fnmsubd, instr), - _ => Unknown { instr }, - }, - OP_FNMADD => match fmt(instr) { - FMT_S => f_r_rm_3_instr!(Fnmadds, instr), - FMT_D => f_r_rm_3_instr!(Fnmaddd, instr), - _ => Unknown { instr }, - }, - - // F/D-type load - OP_FP_LOAD => match width(instr) { - WIDTH_W => fl_instr!(Flw, instr), - WIDTH_D => fl_instr!(Fld, instr), - _ => Unknown { instr }, - }, - OP_FP_STORE => match width(instr) { - WIDTH_W => fs_instr!(Fsw, instr), - WIDTH_D => fs_instr!(Fsd, instr), - _ => Unknown { instr }, - }, - // Jump instructions - OP_JAL => j_instr!(Jal, instr), - OP_JALR => match funct3(instr) { - F3_0 => i_instr!(Jalr, instr), - _ => Unknown { instr }, - }, - _ => Unknown { instr }, - }; - - Instr::Cacheable(i) -} - -const NUM_COMPRESSED_INSTRUCTIONS: usize = (u16::MAX as usize) + 1; - -static COMPRESSED_JUMP_TABLE: [Instr; NUM_COMPRESSED_INSTRUCTIONS] = { - let mut table = - [Instr::Cacheable(InstrCacheable::Unknown { instr: 0 }); NUM_COMPRESSED_INSTRUCTIONS]; - let mut i = 0; - - while i < u16::MAX { - table[i as usize] = parse_compressed_instruction_inner(i); - i += 1; - } - table[i as usize] = parse_compressed_instruction_inner(i); - - table -}; - -macro_rules! cr_instr { - ($enum_variant:ident, $instr:expr) => { - $enum_variant(CRTypeArgs { - rd_rs1: c_rs1p($instr), - rs2: c_rdp_rs2p($instr), - }) - }; -} - -macro_rules! cc_instr { - ($enum_variant:ident, $imm:expr, $instr:expr) => { - $enum_variant(CIBTypeArgs { - rd_rs1: c_rs1p($instr), - imm: $imm, - }) - }; -} - -#[inline(always)] -const fn c_bits(bytes: u16, pos: usize, n: usize) -> u16 { - (bytes >> pos) & (!0 >> (16 - n)) -} - -#[inline(always)] -const fn c_rd_rs1(instr: u16) -> XRegister { - parse_xregister(u5::extract_u16(instr, 7)) -} - -#[inline(always)] -const fn c_f_rd_rs1(instr: u16) -> FRegister { - parse_fregister(u5::extract_u16(instr, 7)) -} - -#[inline(always)] -const fn c_rs2(instr: u16) -> XRegister { - parse_xregister(u5::extract_u16(instr, 2)) -} - -#[inline(always)] -const fn c_f_rs2(instr: u16) -> FRegister { - parse_fregister(u5::extract_u16(instr, 2)) -} - -/// Encodings for the most used registers for certain compressed instructions -/// See U:C-16.2 -#[inline(always)] -const fn c_reg_prime(instr: u16, pos: usize) -> u5 { - const EIGHT: u5 = u5::new(8); - - u3::extract_u16(instr, pos).widen::<5>().wrapping_add(EIGHT) -} - -#[inline(always)] -const fn c_rs1p(instr: u16) -> XRegister { - parse_xregister(c_reg_prime(instr, 7)) -} - -#[inline(always)] -const fn c_rs1p_nz(instr: u16) -> NonZeroXRegister { - // This cannot panic as parsing can only return a register from x8 to x15, - // which are all non-zero. - NonZeroXRegister::assert_from(parse_xregister(c_reg_prime(instr, 7))) -} - -#[inline(always)] -const fn c_rdp_rs2p(instr: u16) -> XRegister { - parse_xregister(c_reg_prime(instr, 2)) -} - -#[inline(always)] -const fn c_rdp_rs2p_nz(instr: u16) -> NonZeroXRegister { - // This cannot panic as parsing can only return a register from x8 to x15, - // which are all non-zero. - NonZeroXRegister::assert_from(parse_xregister(c_reg_prime(instr, 2))) -} - -#[inline(always)] -const fn c_f_rdp_rs2p(instr: u16) -> FRegister { - parse_fregister(c_reg_prime(instr, 2)) -} - -#[inline(always)] -const fn c_opcode(instr: u16) -> u16 { - c_bits(instr, 0, 2) -} - -#[inline(always)] -const fn c_funct3(instr: u16) -> u16 { - c_bits(instr, 13, 3) -} - -#[inline(always)] -const fn c_q1_10(instr: u16) -> u16 { - c_bits(instr, 10, 2) -} - -#[inline(always)] -const fn c_q1_5(instr: u16) -> u16 { - c_bits(instr, 5, 2) -} - -const fn sign_extend_u16(value: u16, size: usize) -> i64 { - assert!(size < 16); - let shift = 16 - size; - (((value as i16) << shift) >> shift) as i64 -} - -const fn clw_imm(instr: u16) -> i64 { - // instr[5] | instr[12:10] | instr[6] | 00 - let res = (u16::bits_subset(instr, 5, 5) << 6) - | (u16::bits_subset(instr, 12, 10) << 3) - | (u16::bits_subset(instr, 6, 6) << 2); - res as i64 -} - -const fn cld_imm(instr: u16) -> i64 { - // instr[6:5] | instr[12:10] | 000 - let res = (u16::bits_subset(instr, 6, 5) << 6) | (u16::bits_subset(instr, 12, 10) << 3); - res as i64 -} - -const fn ci_imm(instr: u16) -> i64 { - // instr[12] | instr[6:2] - let res = (u16::bits_subset(instr, 12, 12) << 5) | u16::bits_subset(instr, 6, 2); - sign_extend_u16(res, 6) -} - -const fn cslli_imm(instr: u16) -> i64 { - // instr[12] | instr[6:2] - let res = (u16::bits_subset(instr, 12, 12) << 5) | u16::bits_subset(instr, 6, 2); - res as i64 -} - -const fn ci_addi16sp_imm(instr: u16) -> i64 { - // instr[12] | instr[4:3] | instr[5] | instr[2] | instr[6] | 0000 - let res = (u16::bits_subset(instr, 12, 12) << 9) - | (u16::bits_subset(instr, 4, 3) << 7) - | (u16::bits_subset(instr, 5, 5) << 6) - | (u16::bits_subset(instr, 2, 2) << 5) - | (u16::bits_subset(instr, 6, 6) << 4); - sign_extend_u16(res, 10) -} - -const fn ci_lwsp_imm(instr: u16) -> i64 { - // instr[3:2] | instr[12] | instr[6:4] | 00 - let res = (u16::bits_subset(instr, 3, 2) << 6) - | (u16::bits_subset(instr, 12, 12) << 5) - | (u16::bits_subset(instr, 6, 4) << 2); - res as i64 -} - -const fn ci_ldsp_imm(instr: u16) -> i64 { - // instr[4:2] | instr[12] | instr[6:5] | 000 - let res = (u16::bits_subset(instr, 4, 2) << 6) - | (u16::bits_subset(instr, 12, 12) << 5) - | (u16::bits_subset(instr, 6, 5) << 3); - res as i64 -} - -const fn css_swsp_imm(instr: u16) -> i64 { - // instr[8:7] | instr[12:9] | 00 - let res = (u16::bits_subset(instr, 8, 7) << 6) | (u16::bits_subset(instr, 12, 9) << 2); - res as i64 -} - -const fn css_sdsp_imm(instr: u16) -> i64 { - // instr[9:7] | instr[12:10] | 000 - let res = (u16::bits_subset(instr, 9, 7) << 6) | (u16::bits_subset(instr, 12, 10) << 3); - res as i64 -} - -const fn ciw_imm(instr: u16) -> i64 { - // instr[10:7] | instr[12:11] | instr[5] | instr[6] | 00 - let res = (u16::bits_subset(instr, 10, 7) << 6) - | (u16::bits_subset(instr, 12, 11) << 4) - | (u16::bits_subset(instr, 5, 5) << 3) - | (u16::bits_subset(instr, 6, 6) << 2); - res as i64 -} - -const fn cb_imm(instr: u16) -> i64 { - // instr[12] | instr[6:5] | instr[2] | instr[11:10] | instr[4:3] | 0 - let res = (u16::bits_subset(instr, 12, 12) << 8) - | (u16::bits_subset(instr, 6, 5) << 6) - | (u16::bits_subset(instr, 2, 2) << 5) - | (u16::bits_subset(instr, 11, 10) << 3) - | (u16::bits_subset(instr, 4, 3) << 1); - sign_extend_u16(res, 9) -} - -const fn cb_shamt_imm(instr: u16) -> i64 { - // instr[12] | instr[6:2] - let res = (u16::bits_subset(instr, 12, 12) << 5) | u16::bits_subset(instr, 6, 2); - res as i64 -} - -const fn cb_andi_imm(instr: u16) -> i64 { - // instr[12] | instr[6:2] - let res = (u16::bits_subset(instr, 12, 12) << 5) | u16::bits_subset(instr, 6, 2); - sign_extend_u16(res, 6) -} - -const fn cj_imm(instr: u16) -> i64 { - // instr[12] | instr[8] | instr[10:9] | instr[6] | instr[7] | instr[2] | instr[11] | instr[5:3] | 0 - let res = (u16::bits_subset(instr, 12, 12) << 11) - | (u16::bits_subset(instr, 8, 8) << 10) - | (u16::bits_subset(instr, 10, 9) << 8) - | (u16::bits_subset(instr, 6, 6) << 7) - | (u16::bits_subset(instr, 7, 7) << 6) - | (u16::bits_subset(instr, 2, 2) << 5) - | (u16::bits_subset(instr, 11, 11) << 4) - | (u16::bits_subset(instr, 5, 3) << 1); - sign_extend_u16(res, 12) -} - -const OP_C0: u16 = 0b00; -const OP_C1: u16 = 0b01; -const OP_C2: u16 = 0b10; - -const C_F3_0: u16 = 0b000; -const C_F3_1: u16 = 0b001; -const C_F3_2: u16 = 0b010; -const C_F3_3: u16 = 0b011; -const C_F3_4: u16 = 0b100; -const C_F3_5: u16 = 0b101; -const C_F3_6: u16 = 0b110; -const C_F3_7: u16 = 0b111; - -const C_Q1_0: u16 = 0b00; -const C_Q1_1: u16 = 0b01; -const C_Q1_2: u16 = 0b10; -const C_Q1_3: u16 = 0b11; - -/// Wrapper for splitting x0 from other XRegisters that can -/// be represented by [`NonZeroXRegister`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub(crate) enum XRegisterParsed { - X0, - NonZero(NonZeroXRegister), -} - -/// Convert [`XRegister`] to [`NonZeroXRegister`] when `r != x0`. -pub(crate) const fn split_x0(r: XRegister) -> XRegisterParsed { - match r { - x0 => XRegisterParsed::X0, - r => XRegisterParsed::NonZero(NonZeroXRegister::assert_from(r)), - } -} - -impl Display for XRegisterParsed { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - XRegisterParsed::X0 => write!(f, "zero"), - XRegisterParsed::NonZero(r) => write!(f, "{}", r), - } - } -} - -#[inline] -const fn parse_compressed_instruction_inner(instr: u16) -> Instr { - use InstrCacheable::*; - use XRegisterParsed::*; - let i = match c_opcode(instr) { - OP_C0 => match c_funct3(instr) { - C_F3_1 => CFld(FLoadArgs { - rd: c_f_rdp_rs2p(instr), - rs1: c_rs1p(instr), - imm: cld_imm(instr), - }), - C_F3_0 => match ciw_imm(instr) { - 0 => UnknownCompressed { instr }, - imm => CAddi4spn(CIBTypeArgs { - rd_rs1: c_rdp_rs2p(instr), - imm, - }), - }, - C_F3_2 => CLw(NonZeroITypeArgs { - rd: c_rdp_rs2p_nz(instr), - rs1: c_rs1p_nz(instr), - imm: clw_imm(instr), - }), - C_F3_3 => CLd(NonZeroITypeArgs { - rd: c_rdp_rs2p_nz(instr), - rs1: c_rs1p_nz(instr), - imm: cld_imm(instr), - }), - C_F3_5 => CFsd(FStoreArgs { - rs1: c_rs1p(instr), - rs2: c_f_rdp_rs2p(instr), - imm: cld_imm(instr), - }), - C_F3_6 => CSw(NonZeroSBTypeArgs { - rs1: c_rs1p_nz(instr), - rs2: c_rdp_rs2p_nz(instr), - imm: clw_imm(instr), - }), - C_F3_7 => CSd(NonZeroSBTypeArgs { - rs1: c_rs1p_nz(instr), - rs2: c_rdp_rs2p_nz(instr), - imm: cld_imm(instr), - }), - _ => UnknownCompressed { instr }, - }, - OP_C1 => match c_funct3(instr) { - C_F3_0 => match (ci_imm(instr), split_x0(c_rd_rs1(instr))) { - // "C.ADDI is only valid when rd != x0 and nzimm != 0. - // The code points with rd == x0 and nzimm == 0 encode the C.NOP instruction; - // the remaining code points with nzimm != 0 encode HINTs." - (0, X0) => CNop, - (0, _) | (_, X0) => HintCompressed { instr }, - (imm, NonZero(rd_rs1)) => CAddi(CIBNZTypeArgs { rd_rs1, imm }), - }, - C_F3_1 => match (ci_imm(instr), split_x0(c_rd_rs1(instr))) { - (_, X0) => UnknownCompressed { instr }, - (imm, NonZero(rd_rs1)) => CAddiw(CIBNZTypeArgs { rd_rs1, imm }), - }, - C_F3_2 => match split_x0(c_rd_rs1(instr)) { - X0 => HintCompressed { instr }, - NonZero(rd_rs1) => CLi(CIBNZTypeArgs { - rd_rs1, - imm: ci_imm(instr), - }), - }, - C_F3_3 => { - if u16::bits_subset(instr, 6, 2) == 0 && !u16::bit(instr, 12) { - return Instr::Cacheable(UnknownCompressed { instr }); - }; - match (split_x0(c_rd_rs1(instr)), ci_imm(instr)) { - (NonZero(NonZeroXRegister::x2), _) => CAddi16sp(CJTypeArgs { - imm: ci_addi16sp_imm(instr), - }), - (_, 0) => UnknownCompressed { instr }, - (X0, _) => HintCompressed { instr }, - (NonZero(rd_rs1), _) => CLui(CIBNZTypeArgs { - rd_rs1, - imm: ci_imm(instr) << 12, - }), - } - } - // RV64C declares that `shamt == 0` here is for HINTS. - C_F3_4 => match (c_q1_10(instr), cb_shamt_imm(instr)) { - (C_Q1_0, 0) => HintCompressed { instr }, - (C_Q1_0, imm) => cc_instr!(CSrli, imm, instr), - (C_Q1_1, 0) => HintCompressed { instr }, - (C_Q1_1, imm) => cc_instr!(CSrai, imm, instr), - (C_Q1_2, _) => cc_instr!(CAndi, cb_andi_imm(instr), instr), - (C_Q1_3, _) => match (u16::bit(instr, 12), c_q1_5(instr)) { - (false, C_Q1_0) => cr_instr!(CSub, instr), - (false, C_Q1_1) => cr_instr!(CXor, instr), - (false, C_Q1_2) => cr_instr!(COr, instr), - (false, C_Q1_3) => cr_instr!(CAnd, instr), - (true, C_Q1_0) => cr_instr!(CSubw, instr), - (true, C_Q1_1) => cr_instr!(CAddw, instr), - _ => UnknownCompressed { instr }, - }, - _ => UnknownCompressed { instr }, - }, - C_F3_5 => CJ(CJTypeArgs { imm: cj_imm(instr) }), - - C_F3_6 => cc_instr!(CBeqz, cb_imm(instr), instr), - C_F3_7 => cc_instr!(CBnez, cb_imm(instr), instr), - _ => UnknownCompressed { instr }, - }, - OP_C2 => match c_funct3(instr) { - C_F3_0 => match (cslli_imm(instr), split_x0(c_rd_rs1(instr))) { - (_, X0) | (0, _) => HintCompressed { instr }, - (imm, NonZero(rd_rs1)) => CSlli(CIBNZTypeArgs { rd_rs1, imm }), - }, - C_F3_1 => CFldsp(CIBDTypeArgs { - rd_rs1: c_f_rd_rs1(instr), - imm: ci_ldsp_imm(instr), - }), - C_F3_2 => match split_x0(c_rd_rs1(instr)) { - X0 => UnknownCompressed { instr }, - NonZero(rd_rs1) => CLwsp(CIBNZTypeArgs { - rd_rs1, - imm: ci_lwsp_imm(instr), - }), - }, - C_F3_3 => match split_x0(c_rd_rs1(instr)) { - X0 => UnknownCompressed { instr }, - NonZero(rd_rs1) => CLdsp(CIBNZTypeArgs { - rd_rs1, - imm: ci_ldsp_imm(instr), - }), - }, - - C_F3_4 => match ( - u16::bit(instr, 12), - split_x0(c_rd_rs1(instr)), - split_x0(c_rs2(instr)), - ) { - (true, X0, X0) => return Instr::Uncacheable(InstrUncacheable::CEbreak), - (_, X0, X0) => UnknownCompressed { instr }, - (_, X0, NonZero(_)) => HintCompressed { instr }, - (true, NonZero(rs1), X0) => CJalr(CRJTypeArgs { rs1 }), - (true, NonZero(rd_rs1), NonZero(rs2)) => CAdd(CNZRTypeArgs { rd_rs1, rs2 }), - (false, NonZero(rs1), X0) => CJr(CRJTypeArgs { rs1 }), - (false, NonZero(rd_rs1), NonZero(rs2)) => CMv(CNZRTypeArgs { rd_rs1, rs2 }), - }, - C_F3_5 => CFsdsp(CSSDTypeArgs { - rs2: c_f_rs2(instr), - imm: css_sdsp_imm(instr), - }), - C_F3_6 => CSwsp(CSSTypeArgs { - rs2: c_rs2(instr), - imm: css_swsp_imm(instr), - }), - C_F3_7 => CSdsp(CSSTypeArgs { - rs2: c_rs2(instr), - imm: css_sdsp_imm(instr), - }), - _ => UnknownCompressed { instr }, - }, - _ => UnknownCompressed { instr }, - }; - - Instr::Cacheable(i) -} - -/// Parse a compressed instruction from a u16. -#[inline(always)] -pub fn parse_compressed_instruction(bytes: u16) -> Instr { - COMPRESSED_JUMP_TABLE[bytes as usize] -} - -/// Attempt to parse `bytes` into an instruction. If `bytes` encodes a 2-byte -/// compressed instruction, parse it immediately. If it encodes a 4-byte -/// uncompressed instruction, request 2 extra bytes via `more`. -#[inline(always)] -pub fn parse(bytes: u16, more: impl FnOnce() -> Result) -> Result { - if bytes & 0b11 != 0b11 { - Ok(parse_compressed_instruction(bytes)) - } else { - let upper = more()?; - let combined = ((upper as u32) << 16) | (bytes as u32); - Ok(parse_uncompressed_instruction(combined)) - } -} - -/// Whether the given bytes correspond to a compressed instruction. -#[inline] -pub const fn is_compressed(bytes: u16) -> bool { - bytes & 0b11 != 0b11 -} - -pub fn u16_iter_from_u8_iter(mut iter: impl Iterator) -> impl Iterator { - std::iter::from_fn(move || { - let lower = iter.next()?; - iter.next().map(|upper| u16::from_le_bytes([lower, upper])) - }) -} - -pub fn instr_iter_from_u16_iter( - mut iter: impl Iterator, -) -> impl Iterator { - std::iter::from_fn(move || parse(iter.next()?, || iter.next().ok_or(())).ok()) -} - -pub fn parse_block(bytes: &[u8]) -> Vec { - let iter = bytes.iter().copied(); - instr_iter_from_u16_iter(u16_iter_from_u8_iter(iter)).collect() -} - -pub fn parse_segment(contents: &[u8], range: Range) -> Vec { - parse_block(&contents[range]) -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use super::XRegisterParsed::*; - use super::instruction::CsrArgs; - use super::instruction::Instr; - use super::instruction::InstrCacheable::*; - use super::instruction::NonZeroRdITypeArgs; - use super::instruction::SBTypeArgs; - use super::instruction::UJTypeArgs; - use super::parse_block; - use crate::machine_state::csregisters::CSRegister::mcause; - use crate::machine_state::registers::NonZeroXRegister; - use crate::machine_state::registers::XRegister::*; - use crate::parser::NonZeroRdUJTypeArgs; - use crate::parser::SplitITypeArgs; - use crate::parser::instruction::CIBNZTypeArgs; - use crate::parser::instruction::CIBTypeArgs; - use crate::parser::instruction::InstrUncacheable; - use crate::parser::parse_compressed_instruction; - use crate::parser::parse_compressed_instruction_inner; - - // rv64ui-p-addiw - // 0000000080000000 <_start>: - // 80000000: 0500006f jal zero,80000050 - - // 0000000080000004 : - // 80000004: 34202f73 csrrs t5,mcause,zero - // 80000008: 00800f93 addi t6,zero,8 - #[test] - fn test_1() { - let bytes: [u8; 12] = [ - 0x6f, 0x0, 0x0, 0x5, 0x73, 0x2f, 0x20, 0x34, 0x93, 0xf, 0x80, 0x0, - ]; - - let expected = [ - Instr::Cacheable(Jal(UJTypeArgs { rd: x0, imm: 0x50 })), - Instr::Cacheable(Csrrs(CsrArgs { - rd: x30, - rs1: x0, - csr: mcause, - })), - Instr::Cacheable(Addi(SplitITypeArgs { - rd: NonZero(NonZeroXRegister::x31), - rs1: X0, - imm: 0x8, - })), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // rv64uc-p-rvc - // 0000000080002190 : - // 80002190: 01500193 addi gp,zero,21 - // 80002194: 6405 c.lui s0,0x1 - // 80002196: 2344041b addiw s0,s0,564 # 1234 <_start-0x7fffedcc> - // 8000219a: 0412 c.slli s0,0x4 - // 8000219c: 000123b7 lui t2,0x12 - // 800021a0: 3403839b addiw t2,t2,832 # 12340 <_start-0x7ffedcc0> - // 800021a4: 12741063 bne s0,t2,800022c4 - #[test] - fn test_2() { - let bytes: [u8; 24] = [ - 0x93, 0x1, 0x50, 0x1, 0x5, 0x64, 0x1b, 0x4, 0x44, 0x23, 0x12, 0x4, 0xb7, 0x23, 0x1, - 0x0, 0x9b, 0x83, 0x3, 0x34, 0x63, 0x10, 0x74, 0x12, - ]; - let expected = [ - Instr::Cacheable(Addi(SplitITypeArgs { - rd: NonZero(NonZeroXRegister::x3), - rs1: X0, - imm: 21, - })), - Instr::Cacheable(CLui(CIBNZTypeArgs { - rd_rs1: NonZeroXRegister::x8, - imm: 0x1 << 12, - })), - Instr::Cacheable(Addiw(NonZeroRdITypeArgs { - rd: NonZeroXRegister::x8, - rs1: x8, - imm: 564, - })), - Instr::Cacheable(CSlli(CIBNZTypeArgs { - rd_rs1: NonZeroXRegister::x8, - imm: 4, - })), - Instr::Cacheable(Lui(NonZeroRdUJTypeArgs { - rd: NonZeroXRegister::x7, - imm: 0x12 << 12, - })), - Instr::Cacheable(Addiw(NonZeroRdITypeArgs { - rd: NonZeroXRegister::x7, - rs1: x7, - imm: 832, - })), - Instr::Cacheable(Bne(SBTypeArgs { - rs1: x8, - rs2: x7, - imm: 288, - })), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // Misaligned buffer, last byte ignored - #[test] - fn test_3() { - let bytes: [u8; 5] = [0x1, 0x5, 0x64, 0x1b, 0x4]; - let expected = [ - Instr::Cacheable(HintCompressed { - instr: u16::from_le_bytes([0x1, 0x5]), - }), - Instr::Cacheable(CAddi4spn(CIBTypeArgs { - rd_rs1: x9, - imm: 444, - })), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // Expected uncompressed instruction, only 2 bytes left - #[test] - fn test_4() { - let bytes: [u8; 6] = [0x6f, 0x0, 0x0, 0x5, 0x73, 0x2f]; - let expected = [Instr::Cacheable(Jal(UJTypeArgs { rd: x0, imm: 0x50 }))]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // A valid `slli x10,x10,0x1f` instruction, followed by an invalid one, - // in which one of the upper 6 bits (bit 29) has been set to 1. - #[test] - fn test_5() { - let bytes: [u8; 8] = [0x13, 0x15, 0xf5, 0x01, 0x13, 0x15, 0xf5, 0x21]; - let expected = [ - Instr::Cacheable(Slli(NonZeroRdITypeArgs { - rd: NonZeroXRegister::x10, - rs1: x10, - imm: 31, - })), - Instr::Cacheable(Unknown { - instr: u32::from_le_bytes([0x13, 0x15, 0xf5, 0x21]), - }), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // Instructions not covered in integration tests - #[test] - fn test_6() { - let bytes: [u8; 4] = [0x73, 0x00, 0x20, 0x70]; - let expected = [Instr::Uncacheable(InstrUncacheable::Mnret)]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected) - } - - // Ensure the u16 jump table is initialised correctly. - #[test] - fn parser_compressed() { - for i in 0..=u16::MAX { - assert_eq!( - parse_compressed_instruction(i), - parse_compressed_instruction_inner(i) - ); - } - } - - // Ensure parsing correctly is 1:2:1. - #[test] - fn test_unparse_compressed() { - let mut reverse = HashMap::new(); - - for i in 0..=u16::MAX { - reverse.insert(parse_compressed_instruction(i), i); - } - - for i in 0..=u16::MAX { - assert_eq!( - Some(&i), - reverse.get(&parse_compressed_instruction(i)), - "Failed {i} {}", - parse_compressed_instruction(i) - ); - } - } - - // Check a HINT encoding for each uncompressed opcode. - #[test] - fn test_uncompressed_hints() { - let bytes: [u8; 120] = [ - 0x37, 0x80, 0x33, 0x33, // LUI X0, 3355520 - 0x17, 0x80, 0x33, 0x33, // AUIPC X0, 3355520 - 0x13, 0x00, 0xA0, 0x36, // ADDI, X0, X0, 874 - 0x13, 0xB0, 0xAC, 0x36, // ORI, X0, X25, 874 - 0x13, 0xC0, 0xAC, 0x36, // XORI, X0, X25, 874 - 0x1B, 0x80, 0x19, 0x00, // ADDIW X0, X11, 1 - 0x33, 0x80, 0x31, 0x00, // ADD X0, X3, X3 - 0x33, 0x80, 0x5A, 0x41, // SUB X0, X21, X21 - 0x33, 0xB0, 0x31, 0x00, // AND X0, X3, X3 - 0x33, 0xC0, 0x31, 0x00, // OR X0, X3, X3 - 0x33, 0xD0, 0x31, 0x00, // XOR X0, X3, X3 - 0x33, 0xE0, 0x31, 0x00, // SLL X0, X3, X3 - 0x33, 0xF0, 0x31, 0x00, // SRL X0, X3, X3 - 0x33, 0xD0, 0x5A, 0x41, // SRA X0, X21, X21 - 0x37, 0x00, 0xC6, 0x00, // ADDW X0, X6, X6 - 0x37, 0x00, 0xC6, 0x40, // SUBW X0, X6, X6 - 0x37, 0x10, 0xC6, 0x00, // SLLW X0, X6, X6 - 0x37, 0x20, 0xC6, 0x00, // SRLW X0, X6, X6 - 0x37, 0x10, 0xC6, 0x40, // SRAW X0, X6, X6 - 0x0F, 0x00, 0x00, 0x0F, // FENCE F, 0 - 0x13, 0x20, 0xA0, 0x36, // SLTI, X0, X0, 874 - 0x13, 0x30, 0xA0, 0x36, // SLTIU, X0, X0, 874 - 0x13, 0x10, 0x44, 0x01, // SLLI X0, X8, 333 - 0x13, 0x50, 0x44, 0x01, // SRLI X0, X8, 333 - 0x13, 0x50, 0x44, 0x41, // SRAI X0, X8, 333 - 0x1B, 0x90, 0x19, 0x00, // SLLIW X0, X11, 1 - 0x1B, 0xD0, 0x19, 0x00, // SRLIW X0, X11, 1 - 0x1B, 0xD0, 0x19, 0x40, // SRAIW X0, X11, 1 - 0x33, 0x90, 0x31, 0x00, // SLT X0, X3, X3 - 0x33, 0xA0, 0x31, 0x00, // SLTU X0, X3, X3 - ]; - let expected = [ - Instr::Cacheable(Hint { instr: 0x33338037 }), - Instr::Cacheable(Hint { instr: 0x33338017 }), - Instr::Cacheable(Hint { instr: 0x36A00013 }), - Instr::Cacheable(Hint { instr: 0x36ACB013 }), - Instr::Cacheable(Hint { instr: 0x36ACC013 }), - Instr::Cacheable(Hint { instr: 0x0019801B }), - Instr::Cacheable(Hint { instr: 0x00318033 }), - Instr::Cacheable(Hint { instr: 0x415A8033 }), - Instr::Cacheable(Hint { instr: 0x0031B033 }), - Instr::Cacheable(Hint { instr: 0x0031C033 }), - Instr::Cacheable(Hint { instr: 0x0031D033 }), - Instr::Cacheable(Hint { instr: 0x0031E033 }), - Instr::Cacheable(Hint { instr: 0x0031F033 }), - Instr::Cacheable(Hint { instr: 0x415AD033 }), - Instr::Cacheable(Hint { instr: 0x00C60037 }), - Instr::Cacheable(Hint { instr: 0x40C60037 }), - Instr::Cacheable(Hint { instr: 0x00C61037 }), - Instr::Cacheable(Hint { instr: 0x00C62037 }), - Instr::Cacheable(Hint { instr: 0x40C61037 }), - Instr::Cacheable(Hint { instr: 0x0F00000F }), - Instr::Cacheable(Hint { instr: 0x36A02013 }), - Instr::Cacheable(Hint { instr: 0x36A03013 }), - Instr::Cacheable(Hint { instr: 0x01441013 }), - Instr::Cacheable(Hint { instr: 0x01445013 }), - Instr::Cacheable(Hint { instr: 0x41445013 }), - Instr::Cacheable(Hint { instr: 0x0019901B }), - Instr::Cacheable(Hint { instr: 0x0019D01B }), - Instr::Cacheable(Hint { instr: 0x4019D01B }), - Instr::Cacheable(Hint { instr: 0x00319033 }), - Instr::Cacheable(Hint { instr: 0x0031A033 }), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected); - } - - // Check a HINT encoding for each compressed opcode. - #[test] - fn test_compressed_hints() { - let bytes: [u8; 16] = [ - 0x01, 0x02, // C.ADDI x4, 0 - 0x01, 0x40, // C.LI x0, 0 - 0x51, 0x60, // C.LUI X0, 20 - 0x06, 0x80, // C.MV x0, x1 - 0x06, 0x90, // C.ADD x0, x1 - 0x02, 0x00, // C.SLLI x0 0 - 0x01, 0x81, // C.SRLI x6 0 - 0x01, 0x87, // C.SRAI x6 0 - ]; - let expected = [ - Instr::Cacheable(HintCompressed { instr: 0x0201 }), - Instr::Cacheable(HintCompressed { instr: 0x4001 }), - Instr::Cacheable(HintCompressed { instr: 0x6051 }), - Instr::Cacheable(HintCompressed { instr: 0x8006 }), - Instr::Cacheable(HintCompressed { instr: 0x9006 }), - Instr::Cacheable(HintCompressed { instr: 0x0002 }), - Instr::Cacheable(HintCompressed { instr: 0x8101 }), - Instr::Cacheable(HintCompressed { instr: 0x8701 }), - ]; - let instructions = parse_block(&bytes); - assert_eq!(instructions, expected); - } -} diff --git a/src/riscv/lib/src/parser/instruction.rs b/src/riscv/lib/src/parser/instruction.rs deleted file mode 100644 index 48102790890b70b9912f0eb79e83bc3e06f72415..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/parser/instruction.rs +++ /dev/null @@ -1,1500 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fmt; - -use enum_tag::EnumTag; -use serde::Deserialize; -use serde::Serialize; - -use super::XRegisterParsed; -use crate::default::ConstDefault; -use crate::interpreter::float::RoundingMode; -use crate::machine_state::csregisters::CSRegister; -use crate::machine_state::registers::FRegister; -use crate::machine_state::registers::NonZeroXRegister; -use crate::machine_state::registers::XRegister; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct RTypeArgs { - pub rd: XRegister, - pub rs1: XRegister, - pub rs2: XRegister, -} - -/// Intermediate representation of Args for R-type instructions with guaranteed `rd` != `x0`. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct NonZeroRdRTypeArgs { - pub rd: NonZeroXRegister, - pub rs1: XRegister, - pub rs2: XRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct ITypeArgs { - pub rd: XRegister, - pub rs1: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct NonZeroITypeArgs { - pub rd: NonZeroXRegister, - pub rs1: NonZeroXRegister, - pub imm: i64, -} - -/// Intermediate representation of Args for I-type instructions with parsed split of registers. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct SplitITypeArgs { - pub(crate) rd: XRegisterParsed, - pub(crate) rs1: XRegisterParsed, - pub imm: i64, -} -/// Intermediate representation of Args for I-type instructions with guaranteed `rd` != `x0`. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct NonZeroRdITypeArgs { - pub rd: NonZeroXRegister, - pub rs1: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct SBTypeArgs { - pub rs1: XRegister, - pub rs2: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct NonZeroSBTypeArgs { - pub rs1: NonZeroXRegister, - pub rs2: NonZeroXRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct UJTypeArgs { - pub rd: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct NonZeroRdUJTypeArgs { - pub rd: NonZeroXRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CsrArgs { - pub rd: XRegister, - pub rs1: XRegister, - pub csr: CSRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CsriArgs { - pub rd: XRegister, - pub imm: i64, - pub csr: CSRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct FenceSet { - pub i: bool, - pub o: bool, - pub r: bool, - pub w: bool, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct FenceArgs { - pub pred: FenceSet, - pub succ: FenceSet, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FRegToXRegArgs { - pub rd: XRegister, - pub rs1: FRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct XRegToFRegArgs { - pub rd: FRegister, - pub rs1: XRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct XRegToFRegArgsWithRounding { - pub rd: FRegister, - pub rs1: XRegister, - pub rm: InstrRoundingMode, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FRegToXRegArgsWithRounding { - pub rd: XRegister, - pub rs1: FRegister, - pub rm: InstrRoundingMode, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FCmpArgs { - pub rs1: FRegister, - pub rs2: FRegister, - pub rd: XRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FRArgs { - pub rs1: FRegister, - pub rs2: FRegister, - pub rd: FRegister, -} - -/// There are 6 supported rounding modes that an instruction may use. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub enum InstrRoundingMode { - Dynamic, - Static(RoundingMode), -} - -impl InstrRoundingMode { - /// Read the parsing mode from the byte given - pub const fn from_rm(rm: u32) -> Option { - if rm == 0b111 { - Some(Self::Dynamic) - } else { - match RoundingMode::from_csrrepr(rm as u64) { - Ok(rm) => Some(Self::Static(rm)), - _ => None, - } - } - } -} - -/// Floating-point R-type instruction, containing -/// rounding mode, and one input argument. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FR1ArgWithRounding { - pub rs1: FRegister, - pub rm: InstrRoundingMode, - pub rd: FRegister, -} - -/// Floating-point R-type instruction, containing -/// rounding mode, and two input arguments. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FR2ArgsWithRounding { - pub rs1: FRegister, - pub rs2: FRegister, - pub rm: InstrRoundingMode, - pub rd: FRegister, -} - -/// Floating-point R-type instruction, containing -/// rounding mode, and three input arguments. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FR3ArgsWithRounding { - pub rs1: FRegister, - pub rs2: FRegister, - pub rs3: FRegister, - pub rm: InstrRoundingMode, - pub rd: FRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FStoreArgs { - pub rs1: XRegister, - pub rs2: FRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct FLoadArgs { - pub rs1: XRegister, - pub rd: FRegister, - pub imm: i64, -} - -// R-type instructions with 2 additional bits which specify memory ordering -// constraints as viewed by other RISC-V harts -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct AmoArgs { - pub rd: XRegister, - pub rs1: XRegister, - pub rs2: XRegister, - pub aq: bool, - pub rl: bool, -} - -// Compressed instruction types - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CRTypeArgs { - pub rd_rs1: XRegister, - pub rs2: XRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CNZRTypeArgs { - pub rd_rs1: NonZeroXRegister, - pub rs2: NonZeroXRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CRJTypeArgs { - pub rs1: NonZeroXRegister, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CJTypeArgs { - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CIBTypeArgs { - pub rd_rs1: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CIBNZTypeArgs { - pub rd_rs1: NonZeroXRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CIBDTypeArgs { - pub rd_rs1: FRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CSSTypeArgs { - pub rs2: XRegister, - pub imm: i64, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, serde::Serialize, serde::Deserialize)] -pub struct CSSDTypeArgs { - pub rs2: FRegister, - pub imm: i64, -} - -/// RISC-V parsed instructions. Along with legal instructions, potentially -/// illegal instructions are parsed as `Unknown` or `UnknownCompressed`. -/// These instructions are successfully parsed, but must not be interpreted. -#[derive( - Debug, PartialEq, Eq, Clone, Copy, EnumTag, Hash, serde::Serialize, serde::Deserialize, -)] -pub enum InstrCacheable { - // RV64I R-type instructions - /// `ADD` - Perform `val(rs1) + val(rs2)` and store the result in `rd` - Add(NonZeroRdRTypeArgs), - /// `SUB` - Perform `val(rs1) - val(rs2)` and store the result in `rd` - Sub(NonZeroRdRTypeArgs), - /// `XOR` - Saves in `rd` the bitwise XOR between the value in `rs1` and `rs2`. - Xor(NonZeroRdRTypeArgs), - /// `OR` R-type instruction - Saves in `rd` the bitwise OR between - /// the value in `rs1` and `rs2`. - Or(NonZeroRdRTypeArgs), - /// `AND` - Saves in `rd` the bitwise AND between the value in `rs1` and `rs2` - And(NonZeroRdRTypeArgs), - /// `SLL` - Shift left logically bits in rs1 by `shift_amount = val(rs2)[5:0]` - /// saving the result in rd. (zeros are shifted in the lower bits) - Sll(NonZeroRdRTypeArgs), - /// `SRL` - Shift right logically bits in rs1 by `shift_amount = val(rs2)[5:0]` - /// saving the result in rd (zeros are shifted in the upper bits) - Srl(NonZeroRdRTypeArgs), - /// `SRA` - Shift right arithmeticallly bits in rs1 by `shift_amount = val(rs2)[5:0]` - /// saving the result in rd (sign-bits are shifted in the upper bits) - Sra(NonZeroRdRTypeArgs), - Slt(NonZeroRdRTypeArgs), - Sltu(NonZeroRdRTypeArgs), - Addw(NonZeroRdRTypeArgs), - /// `SUBW` - Perform `val(rs1) - val(rs2)` but only on lowest 32 bits - /// and store the sign-extended result in `rd` - Subw(NonZeroRdRTypeArgs), - /// `SLLW` - Shift left logically only lowest 32 bits in rs1 - /// by shift_amount = val(rs2)\[4:0\] saving the result in rd - /// (zeros are shifted in the lower bits) - Sllw(NonZeroRdRTypeArgs), - /// `SRLW` - Shift right logically only the lowest 32 bits in rs1 - /// by shift_amount = val(rs2)\[4:0\] saving the result in rd - /// (zeros are shifted in the upper bits) - Srlw(NonZeroRdRTypeArgs), - /// `SRAW` - Shift right arithmeticallly only the lowest 32 bits bits in rs1 - /// by shift_amount = val(rs1)\[4:0\] saving the result in rd - /// (sign-bits are shifted in the upper bits) - Sraw(NonZeroRdRTypeArgs), - - // RV64I I-type instructions - /// `ADDI` - Add `imm` to val(rs1) and store the result in `rd`. - Addi(SplitITypeArgs), - Addiw(NonZeroRdITypeArgs), - /// `XORI` - Saves in `rd` the bitwise XOR between the value in `rs1` and `imm` - Xori(NonZeroRdITypeArgs), - /// `ORI` - Saves in `rd` the bitwise OR between the value in `rs1` and `imm` - Ori(NonZeroRdITypeArgs), - /// `ANDI` - Saves in `rd` the bitwise AND between the value in `rs1` and `imm` - Andi(NonZeroRdITypeArgs), - /// `SLLI` - Shift left logically (zeros are shifted in the lower bits) - /// - /// NOTE: RV64I makes the shift amount (shamt) be 6 bits wide for SLLI - Slli(NonZeroRdITypeArgs), - /// `SRLI`- Shift right logically (zeros are shifted in the upper bits) - /// - /// NOTE: RV64I makes the shift amount (shamt) be 6 bits wide for SRLI - Srli(NonZeroRdITypeArgs), - /// `SRAI` - Shift right arithmetically (sign-bits are shifted in the upper bits) - /// - /// NOTE: RV64I makes the shift amount (shamt) be 6 bits wide for SRAI - Srai(NonZeroRdITypeArgs), - /// `SLLIW` - Shift left logically only on lower 32 bits - /// (zeros are shifted in the lower bits) - Slliw(NonZeroRdITypeArgs), - /// `SRLIW` - Shift right logically only on lower 32 bits - /// (zeros are shifted in the upper bits) - Srliw(NonZeroRdITypeArgs), - /// `SRAIW` - Shift right arithmetically only on lower 32 bits - /// (sign-bits are shifted in the upper bits) - Sraiw(NonZeroRdITypeArgs), - Slti(NonZeroRdITypeArgs), - Sltiu(NonZeroRdITypeArgs), - /// `LB` - Loads a single byte from the address given by: `val(rs1) + imm` - /// and sign-extending the result. - Lb(ITypeArgs), - /// `LH` - Loads a half-word (2 bytes) starting from address - /// given by: `val(rs1) + imm` and sign-extending the result. - Lh(ITypeArgs), - /// `LW` - Loads a word (4 bytes) starting from address given by: val(rs1) + imm - /// NOTE: For RV64I the value is sign-extended to 64 bits. - Lw(ITypeArgs), - /// `LBU` - Loads a single byte from the address given by: `val(rs1) + imm`, - /// zero-extending the result. - Lbu(ITypeArgs), - /// `LHU` - Loads a half-word (2 bytes) starting from address given by: `val(rs1) + imm`, - /// zero-extending the result. - Lhu(ITypeArgs), - /// `LWU` - Loads a word (4 bytes) starting from address given by: `val(rs1) + imm`, - /// zero-extending the result. - Lwu(ITypeArgs), - /// `LD` - Loads a double-word (8 bytes) starting - /// from address given by: `val(rs1) + imm`. - Ld(ITypeArgs), - - // RV64I S-type instructions - /// `SB` - Stores a byte (lowest 1 byte from rs2) to the address starting at: `val(rs1) + imm` - Sb(SBTypeArgs), - /// `SH` - Stores a half-word (lowest 2 bytes from rs2) to the address - /// starting at: `val(rs1) + imm`. - Sh(SBTypeArgs), - /// `SW` - Stores a word (lowest 4 bytes from rs2) to the address - /// starting at: `val(rs1) + imm`. - Sw(SBTypeArgs), - /// `SD` - Stores a double-word (8 bytes from rs2) to the address - /// starting at: `val(rs1) + imm`. - Sd(SBTypeArgs), - - // RV64I B-type instructions - /// `BEQ` - Sets the target address if registers contain the same value, - /// otherwise proceeds to the next instruction address. - Beq(SBTypeArgs), - /// `BNE` - Sets the target address if registers contain different values, - /// otherwise proceeds to the next instruction address. - Bne(SBTypeArgs), - /// `BLT` - Sets branching address if `val(rs1) < val(rs2)` in signed comparison, - /// otherwise proceeds to the next instruction address. - Blt(SBTypeArgs), - /// `BGE` - Sets branching address if `val(rs1) >= val(rs2)` in signed comparison, - /// otherwise proceeds to the next instruction address - Bge(SBTypeArgs), - /// `BLTU` - Sets branching address if `val(rs1) < val(rs2)` in unsigned comparison, - /// otherwise proceeds to the next instruction address. - Bltu(SBTypeArgs), - /// `BGEU` - Sets branching address if `val(rs1) >= val(rs2)` in unsigned comparison, - /// otherwise proceeds to the next instruction address - Bgeu(SBTypeArgs), - - // RV64I U-type instructions - /// `LUI` U-type instruction - /// - /// Set the upper 20 bits of the `rd` register with the `U-type` formatted immediate `imm`. - /// - /// Being a `U-type` operation, the immediate is correctly formatted - /// (lower 12 bits cleared and the value is sign-extended). - Lui(NonZeroRdUJTypeArgs), - /// `AUIPC` - Add the `U-type` formatted immediate `imm` to the current program counter - /// and store the result in `rd`. - /// - /// Being a `U-type` operation, the immediate is correctly formatted - /// (lower 12 bits cleared and the value is sign-extended). - Auipc(NonZeroRdUJTypeArgs), - - // RV64I jump instructions - /// `JAL` (note: uncompressed variant) - Instruction mis-aligned will - /// never be thrown because we allow C extension - /// - /// Always returns the target address (current program counter + imm) - Jal(UJTypeArgs), - /// `JALR` (note: uncompressed variant) - Instruction mis-aligned will - /// never be thrown because we allow C extension - /// - /// Always returns the target address (val(rs1) + imm) - Jalr(ITypeArgs), - - // RV64A R-type atomic instructions - Lrw(AmoArgs), - Scw(AmoArgs), - Amoswapw(AmoArgs), - Amoaddw(AmoArgs), - Amoxorw(AmoArgs), - Amoandw(AmoArgs), - Amoorw(AmoArgs), - Amominw(AmoArgs), - Amomaxw(AmoArgs), - Amominuw(AmoArgs), - Amomaxuw(AmoArgs), - Lrd(AmoArgs), - Scd(AmoArgs), - Amoswapd(AmoArgs), - ///`AMOADD.D` - Loads in rd the value from the address in rs1 and stores the result of - /// adding it to val(rs2) back to the address in rs1. - /// - /// The `aq` and `rl` bits specify additional memory constraints in - /// multi-hart environments so they are currently ignored. - Amoaddd(AmoArgs), - Amoxord(AmoArgs), - Amoandd(AmoArgs), - Amoord(AmoArgs), - Amomind(AmoArgs), - Amomaxd(AmoArgs), - Amominud(AmoArgs), - Amomaxud(AmoArgs), - - // RV64M division instructions - Rem(RTypeArgs), - Remu(RTypeArgs), - Remw(RTypeArgs), - Remuw(RTypeArgs), - /// `DIV` - signed integer division `⌊ val(rs1) / val(rs2) ⌋`, storing the result in `rd`. - /// - /// If `val(rs2) == 0`, the result is `-1`. - /// If `val(rs2) == -1` and `val(rs1) == i64::MIN`, the result is `i64::MIN`. - /// - /// All values are _signed integers_. - Div(RTypeArgs), - Divu(RTypeArgs), - Divw(RTypeArgs), - Divuw(RTypeArgs), - /// `MUL` - Perform bitwise-multiplication of `val(rs1)` with `val(rs2)`. - Mul(RTypeArgs), - /// Multiply val(rs1) with val(rs2) and store the upper 64 bits of the result - /// in register `rd`. - Mulh(RTypeArgs), - /// Multiply val(rs1) with val(rs2) and store the upper 64 bits of the result - /// in register `rd`. val(rs1) is treated as a _signed integer_, while val(rs2) - /// is treated as an _unsigned integer_. - Mulhsu(RTypeArgs), - /// Multiply val(rs1) with val(rs2) and store the upper 64 bits of the result - /// in register `rd`. Both val(rs1) and val(rs2) are treated as - /// _unsigned integers_. - Mulhu(RTypeArgs), - /// `MULW` - Multiply the lower 32 bits of `val(rs1)` with the lower 32 bits of `val(rs2)` - /// and store the sign-extended result in `rd`. - Mulw(RTypeArgs), - - // RV64F instructions - FclassS(FRegToXRegArgs), - Feqs(FCmpArgs), - Fles(FCmpArgs), - Flts(FCmpArgs), - Fadds(FR2ArgsWithRounding), - Fsubs(FR2ArgsWithRounding), - Fmuls(FR2ArgsWithRounding), - Fdivs(FR2ArgsWithRounding), - Fsqrts(FR1ArgWithRounding), - Fmins(FRArgs), - Fmaxs(FRArgs), - Fmadds(FR3ArgsWithRounding), - Fmsubs(FR3ArgsWithRounding), - Fnmsubs(FR3ArgsWithRounding), - Fnmadds(FR3ArgsWithRounding), - Flw(FLoadArgs), - Fsw(FStoreArgs), - Fcvtsw(XRegToFRegArgsWithRounding), - Fcvtswu(XRegToFRegArgsWithRounding), - Fcvtsl(XRegToFRegArgsWithRounding), - Fcvtslu(XRegToFRegArgsWithRounding), - Fcvtws(FRegToXRegArgsWithRounding), - Fcvtwus(FRegToXRegArgsWithRounding), - Fcvtls(FRegToXRegArgsWithRounding), - Fcvtlus(FRegToXRegArgsWithRounding), - Fsgnjs(FRArgs), - Fsgnjns(FRArgs), - Fsgnjxs(FRArgs), - FmvXW(FRegToXRegArgs), - FmvWX(XRegToFRegArgs), - - // RV64D instructions - FclassD(FRegToXRegArgs), - Feqd(FCmpArgs), - Fled(FCmpArgs), - Fltd(FCmpArgs), - Faddd(FR2ArgsWithRounding), - Fsubd(FR2ArgsWithRounding), - Fmuld(FR2ArgsWithRounding), - Fdivd(FR2ArgsWithRounding), - Fsqrtd(FR1ArgWithRounding), - Fmind(FRArgs), - Fmaxd(FRArgs), - Fmaddd(FR3ArgsWithRounding), - Fmsubd(FR3ArgsWithRounding), - Fnmsubd(FR3ArgsWithRounding), - Fnmaddd(FR3ArgsWithRounding), - Fld(FLoadArgs), - Fsd(FStoreArgs), - Fcvtdw(XRegToFRegArgsWithRounding), - Fcvtdwu(XRegToFRegArgsWithRounding), - Fcvtdl(XRegToFRegArgsWithRounding), - Fcvtdlu(XRegToFRegArgsWithRounding), - Fcvtds(FR1ArgWithRounding), - Fcvtsd(FR1ArgWithRounding), - Fcvtwd(FRegToXRegArgsWithRounding), - Fcvtwud(FRegToXRegArgsWithRounding), - Fcvtld(FRegToXRegArgsWithRounding), - Fcvtlud(FRegToXRegArgsWithRounding), - Fsgnjd(FRArgs), - Fsgnjnd(FRArgs), - Fsgnjxd(FRArgs), - FmvXD(FRegToXRegArgs), - FmvDX(XRegToFRegArgs), - - // Zicsr instructions - Csrrw(CsrArgs), - Csrrs(CsrArgs), - Csrrc(CsrArgs), - Csrrwi(CsriArgs), - Csrrsi(CsriArgs), - Csrrci(CsriArgs), - - // RV32C compressed instructions - /// `C.LW` - Loads a 32-bit value from memory into register `rd`. It computes - /// an effective address by adding the immediate to the base address - /// in register `rs1`. - /// - /// The immediate is obtained by zero-extending and scaling by 4 the - /// offset encoded in the instruction (see U:C-16.3). - CLw(NonZeroITypeArgs), - /// `C.LWSP` - Loads a 32-bit value from memory into register `rd`. It computes - /// an effective address by adding the immediate to the stack pointer. - /// - /// The immediate is obtained by zero-extending and scaling by 4 the - /// offset encoded in the instruction (see U:C-16.3). - CLwsp(CIBNZTypeArgs), - /// `C.SW` - Stores a 32-bit value in register `rs2` to memory. It computes - /// an effective address by adding the immediate to the base address - /// in register `rs1`. - /// - /// The immediate is obtained by zero-extending and scaling by 4 the - /// offset encoded in the instruction (see U:C-16.3). - CSw(NonZeroSBTypeArgs), - /// `C.SWSP` - Stores a 32-bit value in register `rs2` to memory. It computes - /// an effective address by adding the immediate to the stack pointer. - /// - /// The immediate is obtained by zero-extending and scaling by 4 the - /// offset encoded in the instruction (see U:C-16.3). - CSwsp(CSSTypeArgs), - /// `C.J` - Performs an unconditional control transfer. The immediate is added to - /// the pc to form the jump target address. - CJ(CJTypeArgs), - /// `C.JR` - Performs an unconditional control transfer to the address in register `rs1`. - CJr(CRJTypeArgs), - /// `C.JALR` - Performs the same operation as `C.JR`, but additionally writes the - /// address of the instruction following the jump (pc+2) to the link register (`x1`). - CJalr(CRJTypeArgs), - /// `C.BEQZ` - Performs a conditional ( `val(rs1) == 0` ) control transfer. - /// The offset is sign-extended and added to the pc to form the branch - /// target address. - CBeqz(CIBTypeArgs), - /// `C.BNEZ` - Performs a conditional ( `val(rs1) != 0`) control transfer. - /// The offset is sign-extended and added to the pc to form the branch - /// target address. - CBnez(CIBTypeArgs), - /// `C.LI` - Loads the sign-extended 6-bit immediate into register `rd_rs1`. - CLi(CIBNZTypeArgs), - /// `C.LUI` CI-type compressed instruction - /// - /// Loads the non-zero 6-bit immediate into bits 17–12 of the - /// register `rd_rs1`, clears the bottom 12 bits, and sign-extends bit 17 - /// into all higher bits of `rd_rs1`. - CLui(CIBNZTypeArgs), - /// `C.ADDI` - Adds the non-zero sign-extended 6-bit `imm` - /// to the value in `rd_rs1` then writes the result to `rd_rs1`. - CAddi(CIBNZTypeArgs), - /// `C.ADDI16SP` - Adds the non-zero immediate to the value in the stack pointer. - /// The immediate is obtained by sign-extending and scaling by 16 the value - /// encoded in the instruction (see U:C-16.5). - CAddi16sp(CJTypeArgs), - /// `C.ADDI4SPN`- Adds the non-zero immediate to the stack pointer and writes the result - /// to `rd`. The immediate is obtained by zero-extending and scaling by 4 the value - /// encoded in the instruction (see U:C-16.5). - CAddi4spn(CIBTypeArgs), - /// `C.SLLI` - Performs a logical left shift of the value in register `rd_rs1` - /// by `imm` bits (referred to as `shamt` or `shift-amount`), then writes the result - /// back to `rd_rs1`. - CSlli(CIBNZTypeArgs), - /// `C.SRLI` - Performs a logical right shift of the value in register `rd_rs1` - /// by `imm` bits (referred to as `shamt` or `shift-amount`), then writes the result - /// back to `rd_rs1`. - CSrli(CIBTypeArgs), - /// `C.SRAI` - Performs an arithmetic right shift of the value in register `rd_rs1` - /// by `imm` bits (referred to as `shamt` or `shift-amount`), then writes the result - /// back to `rd_rs1`. - CSrai(CIBTypeArgs), - /// `C.ANDI` - Computes the bitwise AND of the value in register `rd_rs1` and - /// the sign-extended 6-bit immediate, then writes the result back to `rd_rs1`. - CAndi(CIBTypeArgs), - /// `C.MV` - Copies the value in register `rs2` into register `rd_rs1`. - CMv(CNZRTypeArgs), - /// `C.ADD` - Adds the values in registers `rd_rs1` and `rs2` and writes the result - /// back to register `rd_rs1`. - CAdd(CNZRTypeArgs), - /// `C.AND` - Computes the bitwise AND of the values in registers `rd_rs1` and `rs2`, - /// then writes the result back to register rd `rd_rs1`. - CAnd(CRTypeArgs), - /// `C.OR` - Computes the bitwise OR of the values in registers `rd_rs1` and `rs2`, - /// then writes the result back to register `rd_rs1`. - COr(CRTypeArgs), - /// `C.XOR` - Computes the bitwise XOR of the values in registers `rd_rs1` and `rs2`, - /// then writes the result back to register rd `rd_rs1`. - CXor(CRTypeArgs), - /// `C.SUB` - Subtracts the value in register `rs2` from the value in register `rd_rs1`, - /// then writes the result to register `rd_rs1`. - CSub(CRTypeArgs), - CAddw(CRTypeArgs), - /// `C.SUBW` - Subtracts the value in register `rs2` from the value in register `rd_rs1`, - /// then sign-extends the lower 32 bits of the difference and writes the result to - /// register `rd_rs1`. - CSubw(CRTypeArgs), - /// `C.NOP` - Does not change any user-visible state, except for advancing the pc and - /// incrementing any applicable performance counters. Equivalent to `NOP`. - CNop, - - // RV64C compressed instructions - /// `C.LD` - Loads a 64-bit value from memory into register `rd`. - /// It computes an effective address by adding the immediate to the base address - /// in register `rs1`. - /// - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - CLd(NonZeroITypeArgs), - /// `C.LDSP` - Loads a 64-bit value from memory into register `rd`. It computes - /// an effective address by adding the immediate to the stack pointer. - /// - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - CLdsp(CIBNZTypeArgs), - /// `C.SD` - Stores a 64-bit value in register `rs2` to memory. It computes - /// an effective address by adding the immediate to the base address in register `rs1`. - /// - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - CSd(NonZeroSBTypeArgs), - /// `C.SDSP` - Stores a 64-bit value in register `rs2` to memory. It computes - /// an effective address by adding the immediate to the stack pointer. - /// - /// The immediate is obtained by zero-extending and scaling by 8 the - /// offset encoded in the instruction (see U:C-16.3). - CSdsp(CSSTypeArgs), - CAddiw(CIBNZTypeArgs), - - // RV64DC compressed instructions - CFld(FLoadArgs), - CFldsp(CIBDTypeArgs), - CFsd(FStoreArgs), - CFsdsp(CSSDTypeArgs), - - Unknown { - instr: u32, - }, - UnknownCompressed { - instr: u16, - }, - - Hint { - instr: u32, - }, - HintCompressed { - instr: u16, - }, - - // Interrupt-Management - Wfi, - - Ecall, -} - -impl ConstDefault for InstrCacheable { - const DEFAULT: Self = Self::Unknown { instr: 0 }; -} - -/// Uncacheable instructions are those that may result in a -/// breaking of the normal flow of execution. -/// -/// Namely, that may happen due: -/// - interrupt control flow -/// - cache invalidation -/// - altering the mapping of virtual to physical memory -/// -/// Any of these can result in breaking the 'default flow of execution', -/// invalidating the assumptions that are required for the [`BlockCache`] to -/// function. -/// -/// [`BlockCache`]: crate::machine_state::block_cache::BlockCache -#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumTag, Hash)] -pub enum InstrUncacheable { - Fence(FenceArgs), - FenceTso(FenceArgs), - Ebreak, - - // RV32C compressed instructions - CEbreak, - - // Zifencei instructions - FenceI, - - // Privileged instructions - // Trap-Return - Mret, - Sret, - Mnret, - // Supervisor Memory-Management - SFenceVma { asid: XRegister, vaddr: XRegister }, -} - -/// RISC-V parsed instructions. -/// -/// Along with legal instructions, potentially -/// illegal instructions are parsed as `Instr::Cacheable::(InstrCacheable::Unknown)` -/// or `Instr::Cacheable::(InstrCacheable::UnknownCompressed)`. -/// These instructions are successfully parsed, but must not be interpreted. -/// -/// Any `Instr::Cacheable` may be written to & fetched from the Instruction Cache. -#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumTag, Hash)] -pub enum Instr { - Cacheable(InstrCacheable), - Uncacheable(InstrUncacheable), -} - -/// RISC-V instruction width. -/// -/// This is either 4 bytes, in the case of an uncompressed instruction, -/// or 2 bytes, in the case of a compressed instruction. -#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Hash)] -pub enum InstrWidth { - Compressed = 2, - Uncompressed = 4, -} - -impl InstrCacheable { - /// Return the width of the instruction in bytes. - #[inline(always)] - pub const fn width(&self) -> InstrWidth { - use InstrCacheable::*; - match self { - // 4 bytes instructions - Add(_) - | Sub(_) - | Xor(_) - | Or(_) - | And(_) - | Sll(_) - | Srl(_) - | Sra(_) - | Slt(_) - | Sltu(_) - | Addw(_) - | Subw(_) - | Sllw(_) - | Srlw(_) - | Sraw(_) - | Addi(_) - | Addiw(_) - | Xori(_) - | Ori(_) - | Andi(_) - | Slli(_) - | Srli(_) - | Srai(_) - | Slliw(_) - | Srliw(_) - | Sraiw(_) - | Slti(_) - | Sltiu(_) - | Lb(_) - | Lh(_) - | Lw(_) - | Lbu(_) - | Lhu(_) - | Lwu(_) - | Ld(_) - | Sb(_) - | Sh(_) - | Sw(_) - | Sd(_) - | Beq(_) - | Bne(_) - | Blt(_) - | Bge(_) - | Bltu(_) - | Bgeu(_) - | Lui(_) - | Auipc(_) - | Jal(_) - | Jalr(_) - | Lrw(_) - | Scw(_) - | Amoswapw(_) - | Amoaddw(_) - | Amoxorw(_) - | Amoandw(_) - | Amoorw(_) - | Amominw(_) - | Amomaxw(_) - | Amominuw(_) - | Amomaxuw(_) - | Lrd(_) - | Scd(_) - | Amoswapd(_) - | Amoaddd(_) - | Amoxord(_) - | Amoandd(_) - | Amoord(_) - | Amomind(_) - | Amomaxd(_) - | Amominud(_) - | Amomaxud(_) - | Rem(_) - | Remu(_) - | Remw(_) - | Remuw(_) - | Div(_) - | Divu(_) - | Divw(_) - | Divuw(_) - | Mul(_) - | Mulh(_) - | Mulhsu(_) - | Mulhu(_) - | Mulw(_) - | FmvXW(_) - | FmvWX(_) - | Fcvtsw(_) - | Fcvtswu(_) - | Fcvtsl(_) - | Fcvtslu(_) - | Fcvtws(_) - | Fcvtwus(_) - | Fcvtls(_) - | Fcvtlus(_) - | Fsgnjs(_) - | Fsgnjns(_) - | Fsgnjxs(_) - | FclassS(_) - | Feqs(_) - | Fles(_) - | Flts(_) - | Fadds(_) - | Fsubs(_) - | Fmuls(_) - | Fdivs(_) - | Fsqrts(_) - | Fmins(_) - | Fmaxs(_) - | Fmadds(_) - | Fmsubs(_) - | Fnmsubs(_) - | Fnmadds(_) - | Flw(_) - | Fsw(_) - | FmvXD(_) - | FmvDX(_) - | Fcvtdw(_) - | Fcvtdwu(_) - | Fcvtdl(_) - | Fcvtdlu(_) - | Fcvtds(_) - | Fcvtsd(_) - | Fcvtwd(_) - | Fcvtwud(_) - | Fcvtld(_) - | Fcvtlud(_) - | Fsgnjd(_) - | Fsgnjnd(_) - | Fsgnjxd(_) - | FclassD(_) - | Feqd(_) - | Fled(_) - | Fltd(_) - | Faddd(_) - | Fsubd(_) - | Fmuld(_) - | Fdivd(_) - | Fsqrtd(_) - | Fmind(_) - | Fmaxd(_) - | Fmaddd(_) - | Fmsubd(_) - | Fnmsubd(_) - | Fnmaddd(_) - | Fld(_) - | Fsd(_) - | Csrrw(_) - | Csrrs(_) - | Csrrc(_) - | Csrrwi(_) - | Csrrsi(_) - | Csrrci(_) - | Unknown { instr: _ } - | Wfi - | Ecall - | Hint { instr: _ } => InstrWidth::Uncompressed, - - // 2 bytes instructions (compressed instructions) - CLw(_) - | CLwsp(_) - | CSw(_) - | CSwsp(_) - | CJ(_) - | CJr(_) - | CJalr(_) - | CBeqz(_) - | CBnez(_) - | CLi(_) - | CLui(_) - | CAddi(_) - | CAddi16sp(_) - | CAddi4spn(_) - | CSlli(_) - | CSrli(_) - | CSrai(_) - | CAndi(_) - | CMv(_) - | CAdd(_) - | CAnd(_) - | COr(_) - | CXor(_) - | CSub(_) - | CAddw(_) - | CSubw(_) - | CNop - | CLd(_) - | CLdsp(_) - | CSd(_) - | CSdsp(_) - | CAddiw(_) - | CFld(_) - | CFldsp(_) - | CFsd(_) - | CFsdsp(_) - | UnknownCompressed { instr: _ } - | HintCompressed { instr: _ } => InstrWidth::Compressed, - } - } -} - -impl InstrUncacheable { - /// Return the width of the instruction in bytes. - #[inline(always)] - pub const fn width(&self) -> InstrWidth { - use InstrUncacheable::*; - match self { - FenceI | Fence(_) | FenceTso(_) | Ebreak | Mret | Sret | Mnret | SFenceVma { .. } => { - InstrWidth::Uncompressed - } - - CEbreak => InstrWidth::Compressed, - } - } -} - -impl Instr { - /// Return the width of the instruction in bytes. - #[inline(always)] - pub const fn width(&self) -> InstrWidth { - use Instr::*; - match self { - Cacheable(c) => c.width(), - Uncacheable(u) => u.width(), - } - } -} - -macro_rules! r_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},{}", $op, $args.rd, $args.rs1, $args.rs2) - }; -} - -macro_rules! r2_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.rd, $args.rs1) - }; -} - -macro_rules! r4_instr { - ($f:expr, $op:expr, $args:expr) => { - write!( - $f, - "{} {},{},{},{}", - $op, $args.rd, $args.rs1, $args.rs2, $args.rs3 - ) - }; -} - -macro_rules! i_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},{}", $op, $args.rd, $args.rs1, $args.imm) - }; -} - -macro_rules! i_instr_hex { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},0x{:x}", $op, $args.rd, $args.rs1, $args.imm) - }; -} - -macro_rules! i_instr_load { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}({})", $op, $args.rd, $args.imm, $args.rs1) - }; -} - -macro_rules! j_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},0x{:x}", $op, $args.rd, $args.imm) - }; -} - -macro_rules! s_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}({})", $op, $args.rs2, $args.imm, $args.rs1) - }; -} - -macro_rules! b_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},{}", $op, $args.rs1, $args.rs2, $args.imm) - }; -} - -macro_rules! u_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.rd, $args.imm) - }; -} - -macro_rules! f_s1_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.rd, $args.rs1) - }; -} - -macro_rules! f_r1_rm_instr { - ($f:expr, $op:expr, $args:expr) => { - match $args.rm { - InstrRoundingMode::Dynamic => f_s1_instr!($f, $op, $args), - InstrRoundingMode::Static(rm) => { - write!($f, "{} {},{},{}", $op, $args.rd, $args.rs1, rm) - } - } - }; -} - -macro_rules! fence_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.pred, $args.succ) - }; -} - -macro_rules! amo_instr { - ($f:expr, $op:expr, $args:expr) => {{ - let mut bits = String::new(); - if $args.aq || $args.rl { - bits.push('.') - }; - if $args.aq { - bits.push_str("aq") - }; - if $args.rl { - bits.push_str("rl") - }; - write!( - $f, - "{}{} {},{},({})", - $op, bits, $args.rd, $args.rs2, $args.rs1 - ) - }}; -} - -macro_rules! cr_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.rd_rs1, $args.rs2) - }; -} - -macro_rules! ci_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}", $op, $args.rd_rs1, $args.imm) - }; -} - -macro_rules! ci_instr_hex { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},0x{:x}", $op, $args.rd_rs1, $args.imm) - }; -} -macro_rules! c_instr_sp { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}(sp)", $op, $args.rd_rs1, $args.imm) - }; -} - -macro_rules! cs_instr_sp { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{}(sp)", $op, $args.rs2, $args.imm) - }; -} - -macro_rules! csr_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},{}", $op, $args.rd, $args.csr, $args.rs1) - }; -} - -macro_rules! csri_instr { - ($f:expr, $op:expr, $args:expr) => { - write!($f, "{} {},{},{}", $op, $args.rd, $args.csr, $args.imm) - }; -} - -impl fmt::Display for FenceSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut out = String::new(); - if self.i { - out.push('i') - }; - if self.o { - out.push('o') - }; - if self.r { - out.push('r') - }; - if self.w { - out.push('w') - }; - if out.is_empty() { - write!(f, "unknown") - } else { - write!(f, "{}", out) - } - } -} - -/// An objdump-style prettyprinter for parsed instructions, used in testing -/// the parser against objdump. -impl fmt::Display for InstrCacheable { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InstrCacheable::*; - match self { - // RV64I R-type instructions - Add(args) => r_instr!(f, "add", args), - Sub(args) => r_instr!(f, "sub", args), - Xor(args) => r_instr!(f, "xor", args), - Or(args) => r_instr!(f, "or", args), - And(args) => r_instr!(f, "and", args), - Sll(args) => r_instr!(f, "sll", args), - Srl(args) => r_instr!(f, "srl", args), - Sra(args) => r_instr!(f, "sra", args), - Slt(args) => r_instr!(f, "slt", args), - Sltu(args) => r_instr!(f, "sltu", args), - Addw(args) => r_instr!(f, "addw", args), - Subw(args) => r_instr!(f, "subw", args), - Sllw(args) => r_instr!(f, "sllw", args), - Srlw(args) => r_instr!(f, "srlw", args), - Sraw(args) => r_instr!(f, "sraw", args), - - // RV64I I-type instructions - Addi(args) => i_instr!(f, "addi", args), - Addiw(args) => i_instr!(f, "addiw", args), - Xori(args) => i_instr!(f, "xori", args), - Ori(args) => i_instr!(f, "ori", args), - Andi(args) => i_instr!(f, "andi", args), - Slli(args) => i_instr_hex!(f, "slli", args), - Srli(args) => i_instr_hex!(f, "srli", args), - // For consistency with objdump, only the shift amount is printed - Srai(args) => { - i_instr_hex!(f, "srai", NonZeroRdITypeArgs { - imm: args.imm & !(1 << 10), - ..*args - }) - } - Slliw(args) => i_instr_hex!(f, "slliw", args), - Srliw(args) => i_instr_hex!(f, "srliw", args), - Sraiw(args) => { - i_instr_hex!(f, "sraiw", NonZeroRdITypeArgs { - imm: args.imm & !(1 << 10), - ..*args - }) - } - Slti(args) => i_instr!(f, "slti", args), - Sltiu(args) => i_instr!(f, "sltiu", args), - Lb(args) => i_instr_load!(f, "lb", args), - Lh(args) => i_instr_load!(f, "lh", args), - Lw(args) => i_instr_load!(f, "lw", args), - Lbu(args) => i_instr_load!(f, "lbu", args), - Lhu(args) => i_instr_load!(f, "lhu", args), - Lwu(args) => i_instr_load!(f, "lwu", args), - Ld(args) => i_instr_load!(f, "ld", args), - - // RV64I S-type instructions - Sb(args) => s_instr!(f, "sb", args), - Sh(args) => s_instr!(f, "sh", args), - Sw(args) => s_instr!(f, "sw", args), - Sd(args) => s_instr!(f, "sd", args), - - // RV64I B-type instructions - Beq(args) => b_instr!(f, "beq", args), - Bne(args) => b_instr!(f, "bne", args), - Blt(args) => b_instr!(f, "blt", args), - Bge(args) => b_instr!(f, "bge", args), - Bltu(args) => b_instr!(f, "bltu", args), - Bgeu(args) => b_instr!(f, "bgeu", args), - - // RV64I U-type instructions - // For consistency with objdump, upper immediates are shifted down - Lui(args) => j_instr!(f, "lui", NonZeroRdUJTypeArgs { - rd: args.rd, - imm: (args.imm >> 12) & ((0b1 << 20) - 1), - }), - Auipc(args) => j_instr!(f, "auipc", NonZeroRdUJTypeArgs { - rd: args.rd, - imm: (args.imm >> 12) & ((0b1 << 20) - 1), - }), - - // RV64I jump instructions - Jal(args) => u_instr!(f, "jal", args), - Jalr(args) => i_instr_load!(f, "jalr", args), - - // RV64A instructions - Lrw(args) => amo_instr!(f, "lr.w", args), - Scw(args) => amo_instr!(f, "sc.w", args), - Amoswapw(args) => amo_instr!(f, "amoswap.w", args), - Amoaddw(args) => amo_instr!(f, "amoadd.w", args), - Amoxorw(args) => amo_instr!(f, "amoxor.w", args), - Amoandw(args) => amo_instr!(f, "amoand.w", args), - Amoorw(args) => amo_instr!(f, "amoor.w", args), - Amominw(args) => amo_instr!(f, "amomin.w", args), - Amomaxw(args) => amo_instr!(f, "amomax.w", args), - Amominuw(args) => amo_instr!(f, "amominu.w", args), - Amomaxuw(args) => amo_instr!(f, "amomaxu.w", args), - Lrd(args) => amo_instr!(f, "lr.d", args), - Scd(args) => amo_instr!(f, "sc.d", args), - Amoswapd(args) => amo_instr!(f, "amoswap.d", args), - Amoaddd(args) => amo_instr!(f, "amoadd.d", args), - Amoxord(args) => amo_instr!(f, "amoxor.d", args), - Amoandd(args) => amo_instr!(f, "amoand.d", args), - Amoord(args) => amo_instr!(f, "amoor.d", args), - Amomind(args) => amo_instr!(f, "amomin.d", args), - Amomaxd(args) => amo_instr!(f, "amomax.d", args), - Amominud(args) => amo_instr!(f, "amominu.d", args), - Amomaxud(args) => amo_instr!(f, "amomaxu.d", args), - - // RV64M multiplication and division instructions - Rem(args) => r_instr!(f, "rem", args), - Remu(args) => r_instr!(f, "remu", args), - Remw(args) => r_instr!(f, "remw", args), - Remuw(args) => r_instr!(f, "remuw", args), - Div(args) => r_instr!(f, "div", args), - Divu(args) => r_instr!(f, "divu", args), - Divw(args) => r_instr!(f, "divw", args), - Divuw(args) => r_instr!(f, "divuw", args), - Mul(args) => r_instr!(f, "mul", args), - Mulh(args) => r_instr!(f, "mulh", args), - Mulhsu(args) => r_instr!(f, "mulhsu", args), - Mulhu(args) => r_instr!(f, "mulhu", args), - Mulw(args) => r_instr!(f, "mulw", args), - - // RV64F instructions - FclassS(args) => f_s1_instr!(f, "fclass.s", args), - Feqs(args) => r_instr!(f, "feq.s", args), - Fles(args) => r_instr!(f, "fle.s", args), - Flts(args) => r_instr!(f, "flt.s", args), - Fadds(args) => r_instr!(f, "fadd.s", args), - Fsubs(args) => r_instr!(f, "fsub.s", args), - Fmuls(args) => r_instr!(f, "fmul.s", args), - Fdivs(args) => r_instr!(f, "fdiv.s", args), - Fsqrts(args) => r2_instr!(f, "fsqrt.s", args), - Fmins(args) => r_instr!(f, "fmin.s", args), - Fmaxs(args) => r_instr!(f, "fmax.s", args), - Fmadds(args) => r4_instr!(f, "fmadd.s", args), - Fmsubs(args) => r4_instr!(f, "fmsub.s", args), - Fnmsubs(args) => r4_instr!(f, "fnmsub.s", args), - Fnmadds(args) => r4_instr!(f, "fnmadd.s", args), - Flw(args) => i_instr_load!(f, "flw", args), - Fsw(args) => s_instr!(f, "fsw", args), - Fcvtsw(args) => f_s1_instr!(f, "fcvt.s.w", args), - Fcvtswu(args) => f_s1_instr!(f, "fcvt.s.wu", args), - Fcvtsl(args) => f_s1_instr!(f, "fcvt.s.l", args), - Fcvtslu(args) => f_s1_instr!(f, "fcvt.s.lu", args), - Fcvtws(args) => f_r1_rm_instr!(f, "fcvt.w.s", args), - Fcvtwus(args) => f_r1_rm_instr!(f, "fcvt.wu.s", args), - Fcvtls(args) => f_r1_rm_instr!(f, "fcvt.l.s", args), - Fcvtlus(args) => f_r1_rm_instr!(f, "fcvt.lu.s", args), - Fsgnjs(args) => r_instr!(f, "fsgnj.s", args), - Fsgnjns(args) => r_instr!(f, "fsgnjn.s", args), - Fsgnjxs(args) => r_instr!(f, "fsgnjx.s", args), - FmvXW(args) => f_s1_instr!(f, "fmv.x.w", args), - FmvWX(args) => f_s1_instr!(f, "fmv.w.x", args), - - // RV64D instructions - FclassD(args) => f_s1_instr!(f, "fclass.d", args), - Feqd(args) => r_instr!(f, "feq.d", args), - Fled(args) => r_instr!(f, "fle.d", args), - Fltd(args) => r_instr!(f, "flt.d", args), - Faddd(args) => r_instr!(f, "fadd.d", args), - Fsubd(args) => r_instr!(f, "fsub.d", args), - Fmuld(args) => r_instr!(f, "fmul.d", args), - Fdivd(args) => r_instr!(f, "fdiv.d", args), - Fsqrtd(args) => r2_instr!(f, "fsqrt.d", args), - Fmind(args) => r_instr!(f, "fmin.d", args), - Fmaxd(args) => r_instr!(f, "fmax.d", args), - Fmaddd(args) => r4_instr!(f, "fmadd.d", args), - Fmsubd(args) => r4_instr!(f, "fmsub.d", args), - Fnmsubd(args) => r4_instr!(f, "fnmsub.d", args), - Fnmaddd(args) => r4_instr!(f, "fnmadd.d", args), - Fld(args) => i_instr_load!(f, "fld", args), - Fsd(args) => s_instr!(f, "fsd", args), - Fcvtdw(args) => f_s1_instr!(f, "fcvt.d.w", args), - Fcvtdwu(args) => f_s1_instr!(f, "fcvt.d.wu", args), - Fcvtdl(args) => f_r1_rm_instr!(f, "fcvt.d.l", args), - Fcvtdlu(args) => f_r1_rm_instr!(f, "fcvt.d.lu", args), - Fcvtds(args) => r2_instr!(f, "fcvt.d.s", args), - Fcvtsd(args) => r2_instr!(f, "fcvt.s.d", args), - Fcvtwd(args) => f_r1_rm_instr!(f, "fcvt.w.d", args), - Fcvtwud(args) => f_r1_rm_instr!(f, "fcvt.wu.d", args), - Fcvtld(args) => f_r1_rm_instr!(f, "fcvt.l.d", args), - Fcvtlud(args) => f_r1_rm_instr!(f, "fcvt.lu.d", args), - Fsgnjd(args) => r_instr!(f, "fsgnj.d", args), - Fsgnjnd(args) => r_instr!(f, "fsgnjn.d", args), - Fsgnjxd(args) => r_instr!(f, "fsgnjx.d", args), - FmvXD(args) => f_s1_instr!(f, "fmv.x.d", args), - FmvDX(args) => f_s1_instr!(f, "fmv.d.x", args), - - // Zicsr instructions - Csrrw(args) => csr_instr!(f, "csrrw", args), - Csrrs(args) => csr_instr!(f, "csrrs", args), - Csrrc(args) => csr_instr!(f, "csrrc", args), - Csrrwi(args) => csri_instr!(f, "csrrwi", args), - Csrrsi(args) => csri_instr!(f, "csrrsi", args), - Csrrci(args) => csri_instr!(f, "csrrci", args), - - // RV32C compressed instructions - CLw(args) => i_instr_load!(f, "c.lw", args), - CLwsp(args) => c_instr_sp!(f, "c.lwsp", args), - CSw(args) => s_instr!(f, "c.sw", args), - CSwsp(args) => cs_instr_sp!(f, "c.swsp", args), - CJ(args) => write!(f, "c.j {}", args.imm), - CJr(args) => write!(f, "c.jr {}", args.rs1), - CJalr(args) => write!(f, "c.jalr {}", args.rs1), - CBeqz(args) => ci_instr!(f, "c.beqz", args), - CBnez(args) => ci_instr!(f, "c.bnez", args), - CLi(args) => ci_instr!(f, "c.li", args), - CLui(args) => write!( - f, - "c.lui {},0x{:x}", - args.rd_rs1, - // For consistency with objdump, upper immediates are shifted down - (args.imm >> 12) & ((0b1 << 20) - 1) - ), - CAddi(args) => ci_instr!(f, "c.addi", args), - CAddi16sp(args) => write!(f, "c.addi16sp sp,{}", args.imm), - CAddi4spn(args) => write!(f, "c.addi4spn {},sp,{}", args.rd_rs1, args.imm), - CSlli(args) => ci_instr_hex!(f, "c.slli", args), - CSrli(args) => ci_instr_hex!(f, "c.srli", args), - CSrai(args) => ci_instr_hex!(f, "c.srai", args), - CAndi(args) => ci_instr!(f, "c.andi", args), - CMv(args) => cr_instr!(f, "c.mv", args), - CAdd(args) => cr_instr!(f, "c.add", args), - CAnd(args) => cr_instr!(f, "c.and", args), - COr(args) => cr_instr!(f, "c.or", args), - CXor(args) => cr_instr!(f, "c.xor", args), - CSub(args) => cr_instr!(f, "c.sub", args), - CNop => write!(f, "c.nop"), - - // RV64C compressed instructions - CLd(args) => i_instr_load!(f, "c.ld", args), - CLdsp(args) => c_instr_sp!(f, "c.ldsp", args), - CSd(args) => s_instr!(f, "c.sd", args), - CSdsp(args) => cs_instr_sp!(f, "c.sdsp", args), - CAddiw(args) => ci_instr!(f, "c.addiw", args), - CAddw(args) => cr_instr!(f, "c.addw", args), - CSubw(args) => cr_instr!(f, "c.subw", args), - - // RV64DC compressed instructions - CFld(args) => write!(f, "c.fld {},{}({})", args.rd, args.imm, args.rs1), - CFldsp(args) => c_instr_sp!(f, "c.fldsp", args), - CFsd(args) => write!(f, "c.fsd {},{}({})", args.rs2, args.imm, args.rs1), - CFsdsp(args) => cs_instr_sp!(f, "c.fsdsp", args), - - Unknown { instr } => write!(f, "unknown {:x}", instr), - UnknownCompressed { instr } => write!(f, "unknown.c {:x}", instr), - - Hint { instr } => write!(f, "hint {:x}", instr), - HintCompressed { instr } => write!(f, "hint.c {:x}", instr), - // Interrupt-management - Wfi => write!(f, "wfi"), - Ecall => write!(f, "ecall"), - } - } -} - -impl fmt::Display for InstrUncacheable { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use InstrUncacheable::*; - match self { - Fence(args) => fence_instr!(f, "fence", args), - FenceTso(args) => fence_instr!(f, "fence.tso", args), - - Ebreak => write!(f, "ebreak"), - - // Privileged instructions - // Trap-Return - Mret => write!(f, "mret"), - Sret => write!(f, "sret"), - Mnret => write!(f, "mnret"), - // Supervisor Memory-Management - SFenceVma { asid, vaddr } => write!(f, "sfence.vma {vaddr},{asid}"), - - // Zifencei instructions - FenceI => write!(f, "fence.i"), - - // Compressed - CEbreak => write!(f, "c.ebreak"), - } - } -} - -impl fmt::Display for Instr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Cacheable(i) => i.fmt(f), - Self::Uncacheable(i) => i.fmt(f), - } - } -} diff --git a/src/riscv/lib/src/program.rs b/src/riscv/lib/src/program.rs deleted file mode 100644 index 43d6cc16ccc0c5a07e4dc684cce738c322ea833f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/program.rs +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::marker::PhantomData; - -use crate::kernel_loader; -use crate::machine_state::memory; -use crate::parser::parse_block; - -/// RISC-V program -pub struct Program<'a, MC> { - _pd: PhantomData, - - /// Address of the program's entrypoint - pub entrypoint: memory::Address, - - /// Segments to be written to the main memory - // Note: `[u8]` owned is `Vec`. The segment is either re-using an - // existing slice (e.g. from an open ELF file), or a vector in case the - // bytes were dynamically created (e.g. chunks for zero-ed memory). - // Invariant: `segments[index]` corresponds to an array - // representing bytes at `index..index+length` and - // all the arrays are non-overlapping - pub segments: BTreeMap>, - - /// Raw program headers - pub program_headers: Option>, -} - -impl kernel_loader::Memory for Program<'_, MC> { - fn write_bytes( - &mut self, - mut paddr: u64, - mut bytes: &[u8], - ) -> Result<(), kernel_loader::Error> { - if bytes.is_empty() { - return Ok(()); - } - - // If there is a chunk before [paddr] then we need to check if that - // chunk potentially overlaps with the location we want to write. - if let Some((&prev_paddr, prev_bytes)) = self.segments.range_mut(..paddr).last() { - if (paddr as usize) < (prev_paddr as usize) + prev_bytes.len() { - // If there is an overlap, we simply shrink the existing chunk. - // This eliminates the overlap on the left side given we would - // have overridden that section anyway. - prev_bytes.to_mut().resize((paddr - prev_paddr) as usize, 0); - } - } - - // If there is a chunk at or after [paddr] then we need to check if that - // chunk potentially overlaps with the location we want to write. - let paddr_after = paddr + bytes.len() as u64; - let mut new_segments = BTreeMap::new(); - for (&next_paddr, _) in self.segments.range(paddr..paddr_after) { - let nonoverlapping_len = next_paddr - paddr; - let (nonoverlapping, overlapping) = bytes.split_at(nonoverlapping_len as usize); - - if !nonoverlapping.is_empty() { - new_segments.insert(paddr, Cow::Owned(nonoverlapping.to_owned())); - } - - // Continue with the new overlapping chunk that starts exactly at - // the boundary of the next chunk. - paddr = next_paddr; - bytes = overlapping; - } - - // Commit new entries - self.segments.append(&mut new_segments); - - if bytes.is_empty() { - return Ok(()); - } - - // After this point we don't need to dissect `bytes` any further. - // This means there either is or isn't a chunk to be written to at the - // exact address `paddr` and no overlaps would occur. - - if let Some(chunk) = self.segments.get_mut(&paddr) { - if chunk.len() >= bytes.len() { - // There is a chunk at this exact address, and it has space to - // be written to. - chunk.to_mut()[..bytes.len()].copy_from_slice(bytes); - return Ok(()); - } - - // We don't need an else case here because the code below deals with - // overriding the entire chunk already. - } - - self.segments.insert(paddr, Cow::Owned(bytes.to_owned())); - - Ok(()) - } -} - -impl<'a, MC: memory::MemoryConfig> Program<'a, MC> { - /// Parse the given ELF executable and convert it into our program - /// representation. The main memory configuration `MC` is used to compute the - /// correct addresses - pub fn from_elf(elf: &'a [u8]) -> Result { - let start_if_reloc = memory::FIRST_ADDRESS; - - let mut myself = Self { - _pd: PhantomData, - entrypoint: start_if_reloc, - segments: BTreeMap::new(), - program_headers: None, - }; - - let load_result = kernel_loader::load_elf(&mut myself, start_if_reloc, elf)?; - myself.entrypoint = load_result.entry; - myself.program_headers = Some(load_result.program_headers); - - Ok(myself) - } - - pub fn parsed(&self) -> BTreeMap { - let mut parsed = BTreeMap::new(); - for segment in &self.segments { - let mut address = *segment.0; - let instructions = parse_block(segment.1); - for instr in instructions { - parsed.insert(address, instr.to_string()); - address += instr.width() as u64; - } - } - parsed - } -} - -#[cfg(test)] -mod tests { - use std::cell::RefCell; - use std::collections::BTreeMap; - use std::fs; - use std::io::Cursor; - use std::marker::PhantomData; - - use crate::kernel_loader; - use crate::kernel_loader::Memory; - use crate::machine_state::memory; - use crate::machine_state::memory::M1M; - use crate::program::Program; - - #[test] - fn test_impl_memory_program() { - let mut program: Program<'_, M1M> = Program { - _pd: PhantomData, - entrypoint: 0, - segments: BTreeMap::new(), - program_headers: None, - }; - let mut buffer = Cursor::new(vec![0; 2048]); - - let test = RefCell::new(move |addr, data: Vec| { - program.write_bytes(addr, data.as_slice()).unwrap(); - buffer.write_bytes(addr, data.as_slice()).unwrap(); - - for (&addr, segment) in &program.segments { - let addr = addr as usize; - assert_eq!( - &buffer.get_ref()[addr..addr + segment.len()], - segment.as_ref() - ); - } - }); - - proptest::proptest!(|(addr in 0..1024u64, len in 0..1024usize, data: [u8; 1024])| { - test.borrow_mut()(addr, data[..len].to_vec()); - }); - } - - #[test] - fn test_program_from_elf() { - const PATH: &str = "../riscv-dummy.elf"; - let contents = - fs::read(PATH).expect("Failed read dummy RISC-V kernel (try: make -C src/riscv build)"); - - let mut buffer = Cursor::new(Vec::new()); - kernel_loader::load_elf(&mut buffer, memory::FIRST_ADDRESS, &contents).unwrap(); - let buffer = buffer.into_inner(); - - let program = Program::::from_elf(&contents).unwrap(); - for (addr, segment) in program.segments { - let addr = addr as usize; - assert_eq!(&buffer[addr..addr + segment.len()], segment.as_ref()); - } - } -} diff --git a/src/riscv/lib/src/pvm.rs b/src/riscv/lib/src/pvm.rs deleted file mode 100644 index 08eedb9160be67f18980a193201371c051c13522..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm.rs +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -mod common; -pub(crate) mod linux; -pub mod node_pvm; -mod reveals; -mod sbi; - -pub use common::*; diff --git a/src/riscv/lib/src/pvm/common.rs b/src/riscv/lib/src/pvm/common.rs deleted file mode 100644 index a988e757b42d304dc9ed67232a4981c7810643e8..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/common.rs +++ /dev/null @@ -1,850 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::convert::Infallible; -use std::fmt; -use std::io::Write; -use std::io::stdout; -use std::ops::Bound; - -use tezos_smart_rollup_constants::riscv::SbiError; - -use super::linux; -use super::reveals::RevealRequest; -use super::reveals::RevealRequestLayout; -use crate::default::ConstDefault; -use crate::instruction_context::ICB; -use crate::machine_state; -use crate::machine_state::CacheLayouts; -use crate::machine_state::block_cache::block; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::csregisters::CSRegister; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::a0; -use crate::pvm::sbi; -use crate::range_utils::less_than_bound; -use crate::state::NewState; -use crate::state_backend; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::CommitmentLayout; -use crate::state_backend::FnManagerIdent; -use crate::state_backend::ManagerBase; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::ProofGen; -use crate::state_backend::proof_backend::ProofWrapper; -use crate::state_backend::proof_backend::proof::MerkleProof; -use crate::state_backend::proof_backend::proof::Proof; -use crate::state_backend::verify_backend::Verifier; -use crate::storage::Hash; -use crate::storage::HashError; -use crate::struct_layout; -use crate::traps::EnvironException; - -/// PVM configuration -pub struct PvmHooks<'a> { - pub putchar_hook: Box, -} - -impl<'a> PvmHooks<'a> { - /// Create a new configuration. - pub fn new(putchar: F) -> Self { - Self { - putchar_hook: Box::new(putchar), - } - } -} - -impl PvmHooks<'static> { - /// Hook that does nothing. - pub fn none() -> Self { - Self { - putchar_hook: Box::new(|_| {}), - } - } -} - -/// The default PVM configuration prints all debug information from the kernel -/// to the standard output. -impl Default for PvmHooks<'_> { - fn default() -> Self { - fn putchar(char: u8) { - stdout().lock().write_all(&[char]).unwrap(); - } - - Self::new(putchar) - } -} - -/// Type of input that can be passed to the PVM -pub enum PvmInput<'a> { - InboxMessage { - inbox_level: u32, - message_counter: u64, - payload: &'a [u8], - }, - Reveal(&'a [u8]), -} - -struct_layout! { - pub struct PvmLayout { - machine_state: machine_state::MachineStateLayout, - reveal_request: RevealRequestLayout, - system_state: linux::SupervisorStateLayout, - version: Atom, - tick: Atom, - message_counter: Atom, - level: Atom, - level_is_set: Atom, - status: Atom, - } -} - -/// PVM status -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - serde::Serialize, - serde::Deserialize, - strum::EnumCount, -)] -#[repr(u8)] -pub enum PvmStatus { - Evaluating, - WaitingForInput, - WaitingForReveal, -} - -impl ConstDefault for PvmStatus { - const DEFAULT: Self = Self::Evaluating; -} - -impl fmt::Display for PvmStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let status = match self { - PvmStatus::Evaluating => "Evaluating", - PvmStatus::WaitingForInput => "Waiting for input message", - PvmStatus::WaitingForReveal => "Waiting for reveal", - }; - f.write_str(status) - } -} - -/// Value for the initial version -const INITIAL_VERSION: u64 = 0; - -/// Proof generator for the PVM. -/// -/// Uses the interpreted block backend by default. -pub(crate) type PvmProofGen<'a, MC, CL, M> = Pvm< - MC, - CL, - block::Interpreted>>, - ProofGen>, ->; - -/// Proof-generating virtual machine -pub struct Pvm, M: ManagerBase> { - pub(crate) machine_state: machine_state::MachineState, - reveal_request: RevealRequest, - pub(super) system_state: linux::SupervisorState, - version: Cell, - pub(crate) tick: Cell, - pub(crate) message_counter: Cell, - pub(crate) level: Cell, - pub(crate) level_is_set: Cell, - status: Cell, -} - -impl< - MC: MemoryConfig, - CL: machine_state::CacheLayouts, - B: block::Block, - M: state_backend::ManagerBase, -> Pvm -{ - /// Allocate a new PVM. - pub fn new(manager: &mut M, block_builder: B::BlockBuilder) -> Self - where - M: state_backend::ManagerAlloc, - { - Self { - machine_state: machine_state::MachineState::new(manager, block_builder), - reveal_request: RevealRequest::new(manager), - system_state: linux::SupervisorState::new(manager), - version: Cell::new_with(manager, INITIAL_VERSION), - status: Cell::new(manager), - tick: Cell::new(manager), - message_counter: Cell::new(manager), - level: Cell::new(manager), - level_is_set: Cell::new(manager), - } - } - - /// Bind the block cache to the given allocated state and the given [block builder]. - /// - /// [block builder]: block::Block::BlockBuilder - pub(crate) fn bind( - space: state_backend::AllocatedOf, M>, - block_builder: B::BlockBuilder, - ) -> Self - where - M::ManagerRoot: state_backend::ManagerReadWrite, - { - Self { - machine_state: machine_state::MachineState::bind(space.machine_state, block_builder), - reveal_request: RevealRequest::bind(space.reveal_request), - system_state: linux::SupervisorState::bind(space.system_state), - version: space.version, - tick: space.tick, - message_counter: space.message_counter, - level: space.level, - level_is_set: space.level_is_set, - status: space.status, - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub(crate) fn struct_ref<'a, F: state_backend::FnManager>>( - &'a self, - ) -> state_backend::AllocatedOf, F::Output> { - PvmLayoutF { - machine_state: self.machine_state.struct_ref::(), - reveal_request: self.reveal_request.struct_ref::(), - system_state: self.system_state.struct_ref::(), - version: self.version.struct_ref::(), - tick: self.tick.struct_ref::(), - message_counter: self.message_counter.struct_ref::(), - level: self.level.struct_ref::(), - level_is_set: self.level_is_set.struct_ref::(), - status: self.status.struct_ref::(), - } - } - - /// Generate a proof-generating version of this PVM. - pub(crate) fn start_proof(&self) -> PvmProofGen<'_, MC, CL, M> - where - M: state_backend::ManagerRead, - { - let space = self.struct_ref::(); - Pvm::bind(space, block::InterpretedBlockBuilder) - } - - /// Reset the PVM. - pub fn reset(&mut self) - where - M: state_backend::ManagerReadWrite, - { - self.machine_state.reset(); - self.version.write(INITIAL_VERSION); - self.tick.write(0); - self.message_counter.write(0); - self.level.write(0); - self.level_is_set.write(false); - self.status.write(PvmStatus::DEFAULT); - } - - /// Used for testing, corrupt the state so the following proofs will be incorrect. - pub fn insert_failure(&mut self) - where - M: state_backend::ManagerReadWrite, - { - // We want to just slightly modify the state without interfering with normal execution. - // dscratch1 is a debug register, any reasonable (test) kernel shouldn't interact with it. - let csregs = &mut self.machine_state.core.hart.csregisters; - let dscratch1 = csregs.read::(CSRegister::dscratch1); - csregs.write(CSRegister::dscratch1, dscratch1 + 1); - } - - /// Handle an exception using the defined Execution Environment. - // The conditional compilation below causes some warnings. - fn handle_exception(&mut self, hooks: &mut PvmHooks<'_>, _exception: EnvironException) -> bool - where - M: state_backend::ManagerReadWrite, - { - handle_system_call( - &mut self.machine_state.core, - &mut self.system_state, - &mut self.status, - &mut self.reveal_request, - hooks, - ) - } - - /// Perform one evaluation step. - pub(crate) fn eval_one(&mut self, hooks: &mut PvmHooks<'_>) - where - M: state_backend::ManagerReadWrite, - { - // When the status is WaitingForReveal during evaluation, we know that - // nothing has been returned by the rollup node and the reveal request - // is invalid. - if let PvmStatus::WaitingForReveal = self.status.read() { - return self.provide_reveal_error_response(); - } - - if let Err(exc) = self.machine_state.step() { - self.handle_exception(hooks, exc); - } - self.tick.write(self.tick.read().wrapping_add(1u64)); - } - - /// Perform a range of evaluation steps. Returns the actual number of steps - /// performed. - /// - /// If an environment trap is raised, handle it and - /// return the number of retired instructions until the raised trap - /// - /// NOTE: instructions which raise exceptions / are interrupted are NOT retired - /// See section 3.3.1 for context on retired instructions. - /// e.g: a load instruction raises an exception but the first instruction - /// of the trap handler will be executed and retired, - /// so in the end the load instruction which does not bubble it's exception up to - /// the execution environment will still retire an instruction, just not itself. - /// (a possible case: the privilege mode access violation is treated in EE, - /// but a page fault is not) - // The conditional compilation below causes some warnings. - pub(crate) fn eval_max(&mut self, hooks: &mut PvmHooks<'_>, step_bounds: Bound) -> usize - where - M: state_backend::ManagerReadWrite, - { - // Do nothing if step_bounds is less than 1 - if !less_than_bound(0, step_bounds) { - return 0; - } - - // When the status is WaitingForReveal during evaluation, we know that - // nothing has been returned by the rollup node and the reveal request - // is invalid. - if let PvmStatus::WaitingForReveal = self.status.read() { - self.provide_reveal_error_response(); - return 1; - } - - let steps = self - .machine_state - .step_max_handle::(step_bounds, |machine_state, _exception| { - Ok(handle_system_call( - &mut machine_state.core, - &mut self.system_state, - &mut self.status, - &mut self.reveal_request, - hooks, - )) - }) - .steps; - self.tick.write(self.tick.read().wrapping_add(steps as u64)); - steps - } - - /// Provide input. Returns `false` if the machine state is not expecting input. - pub(crate) fn provide_input(&mut self, input: PvmInput) -> bool - where - M: state_backend::ManagerReadWrite, - { - // TODO RV-615: Remove `as u32` conversion - match input { - PvmInput::InboxMessage { - inbox_level, - message_counter, - payload, - } => self.provide_inbox_message(inbox_level, message_counter as u32, payload), - PvmInput::Reveal(reveal_data) => self.provide_reveal_response(reveal_data), - } - } - - /// Provide an inbox message. Returns `false` if the machine state is not - /// expecting a message. - pub(crate) fn provide_inbox_message(&mut self, level: u32, counter: u32, payload: &[u8]) -> bool - where - M: state_backend::ManagerReadWrite, - { - if !sbi::provide_input( - &mut self.status, - &mut self.machine_state.core, - level, - counter, - payload, - ) { - return false; - } - self.tick.write(self.tick.read().wrapping_add(1u64)); - self.message_counter.write(counter as u64); - self.level_is_set.write(true); - self.level.write(level); - true - } - - /// Provide reveal data in response to a reveal request. - /// Returns `false` if the machine is not expecting a reveal. - pub(crate) fn provide_reveal_response(&mut self, reveal_data: &[u8]) -> bool - where - M: state_backend::ManagerReadWrite, - { - if !sbi::provide_reveal_response( - &mut self.status, - &mut self.machine_state.core, - reveal_data, - ) { - return false; - } - self.tick.write(self.tick.read().wrapping_add(1u64)); - true - } - - /// Get the reveal request in the machine state. - pub(crate) fn reveal_request(&self) -> Vec - where - M: state_backend::ManagerRead, - { - self.reveal_request.to_vec() - } - - /// Provide a reveal error response to the PVM - pub fn provide_reveal_error_response(&mut self) - where - M: state_backend::ManagerReadWrite, - { - self.machine_state - .core - .xregister_write(a0, SbiError::InvalidParam as u64); - self.tick.write(self.tick.read().wrapping_add(1u64)); - self.status.write(PvmStatus::Evaluating); - } - - /// Get the current machine status. - pub fn status(&self) -> PvmStatus - where - M: state_backend::ManagerRead, - { - self.status.read() - } -} - -impl> Pvm { - pub(crate) fn empty(block_builder: B::BlockBuilder) -> Self { - Self::new(&mut Owned, block_builder) - } - - pub(crate) fn hash(&self) -> Result { - let refs = self.struct_ref::(); - PvmLayout::::state_hash(refs) - } -} - -impl<'a, MC: MemoryConfig, CL: CacheLayouts, B: Block>>> - Pvm>> -{ - /// Produce a proof. - pub(crate) fn to_proof(&self) -> Result { - let refs = self.struct_ref::(); - let merkle_proof = PvmLayout::::to_merkle_tree(refs)?.to_merkle_proof()?; - - let refs = self.struct_ref::(); - let final_hash = PvmLayout::::state_hash(refs)?; - let proof = Proof::new(merkle_proof, final_hash); - - Ok(proof) - } -} - -impl> Pvm { - /// Construct a PVM state from a Merkle proof. - pub fn from_proof(proof: &MerkleProof, block_builder: B::BlockBuilder) -> Option { - let space = - as ProofLayout>::from_proof(ProofTree::Present(proof)).ok()?; - Some(Self::bind(space, block_builder)) - } -} - -impl< - MC: MemoryConfig, - CL: CacheLayouts, - B: block::Block + Clone, - M: state_backend::ManagerClone, -> Clone for Pvm -{ - fn clone(&self) -> Self { - Self { - machine_state: self.machine_state.clone(), - reveal_request: self.reveal_request.clone(), - system_state: self.system_state.clone(), - version: self.version.clone(), - tick: self.tick.clone(), - message_counter: self.message_counter.clone(), - level: self.level.clone(), - level_is_set: self.level_is_set.clone(), - status: self.status.clone(), - } - } -} - -fn handle_system_call( - core: &mut machine_state::MachineCoreState, - system_state: &mut linux::SupervisorState, - status: &mut Cell, - reveal_request: &mut RevealRequest, - hooks: &mut PvmHooks, -) -> bool -where - MC: MemoryConfig, - M: state_backend::ManagerReadWrite, -{ - system_state.handle_system_call(core, hooks, |core| { - sbi::handle_tezos(core, status, reveal_request); - status.read() == PvmStatus::Evaluating - }) -} - -#[cfg(test)] -mod tests { - use std::mem; - - use proptest::proptest; - use rand::Fill; - use rand::thread_rng; - use tezos_smart_rollup_constants::riscv::REVEAL_REQUEST_MAX_SIZE; - use tezos_smart_rollup_constants::riscv::SBI_FIRMWARE_TEZOS; - use tezos_smart_rollup_constants::riscv::SBI_TEZOS_INBOX_NEXT; - use tezos_smart_rollup_constants::riscv::SBI_TEZOS_REVEAL; - - use super::*; - use crate::backend_test; - use crate::machine_state::TestCacheLayouts; - use crate::machine_state::block_cache::block::InterpretedBlockBuilder; - use crate::machine_state::memory; - use crate::machine_state::memory::M1M; - use crate::machine_state::memory::Memory; - use crate::machine_state::registers::a0; - use crate::machine_state::registers::a1; - use crate::machine_state::registers::a2; - use crate::machine_state::registers::a3; - use crate::machine_state::registers::a6; - use crate::machine_state::registers::a7; - use crate::pvm::common::tests::memory::Address; - use crate::pvm::linux; - use crate::state_backend::owned_backend::Owned; - use crate::state_backend::test_helpers::TestBackendFactory; - - #[test] - fn test_read_input() { - type MC = M1M; - type B = block::Interpreted; - - // Setup PVM - let mut pvm = Pvm::::new(&mut Owned, InterpretedBlockBuilder); - pvm.reset(); - pvm.machine_state - .core - .main_memory - .set_all_readable_writeable(); - - let level_addr = memory::FIRST_ADDRESS; - let counter_addr = level_addr + 4; - let buffer_addr = counter_addr + 4; - - const BUFFER_LEN: usize = 1024; - - // Configure machine for 'sbi_tezos_inbox_next' - pvm.machine_state - .core - .hart - .xregisters - .write(a0, buffer_addr); - pvm.machine_state - .core - .hart - .xregisters - .write(a1, BUFFER_LEN as u64); - pvm.machine_state.core.hart.xregisters.write(a2, level_addr); - pvm.machine_state - .core - .hart - .xregisters - .write(a3, counter_addr); - pvm.machine_state - .core - .hart - .xregisters - .write(a7, SBI_FIRMWARE_TEZOS); - pvm.machine_state - .core - .hart - .xregisters - .write(a6, SBI_TEZOS_INBOX_NEXT); - - // Should be in evaluating mode - assert_eq!(pvm.status(), PvmStatus::Evaluating); - - // Handle the ECALL successfully - let outcome = pvm.handle_exception(&mut Default::default(), EnvironException::EnvCall); - assert!(!outcome); - - // After the ECALL we should be waiting for input - assert_eq!(pvm.status(), PvmStatus::WaitingForInput); - - // Respond to the request for input - let level = rand::random(); - let counter = rand::random(); - let mut payload = [0u8; BUFFER_LEN + 10]; - payload.try_fill(&mut thread_rng()).unwrap(); - assert!(pvm.provide_inbox_message(level, counter, &payload)); - - // The status should switch from WaitingForInput to Evaluating - assert_eq!(pvm.status(), PvmStatus::Evaluating); - - // Returned data is as expected - assert_eq!( - pvm.machine_state.core.hart.xregisters.read(a0) as usize, - BUFFER_LEN - ); - assert_eq!( - pvm.machine_state.core.main_memory.read(level_addr), - Ok(level) - ); - assert_eq!( - pvm.machine_state.core.main_memory.read(counter_addr), - Ok(counter) - ); - - // Payload in memory should be as expected - for (offset, &byte) in payload[..BUFFER_LEN].iter().enumerate() { - let addr = buffer_addr + offset as u64; - let byte_written: u8 = pvm.machine_state.core.main_memory.read(addr).unwrap(); - assert_eq!( - byte, byte_written, - "Byte at {addr:x} (offset {offset}) is not the same" - ); - } - - // Data after the buffer should be untouched - assert!( - (BUFFER_LEN..4096) - .map(|offset| { - let addr = buffer_addr + offset as u64; - pvm.machine_state.core.main_memory.read(addr).unwrap() - }) - .all(|b: u8| b == 0) - ); - } - - #[test] - fn test_write_debug() { - const WRITTEN_SIZE: usize = 100; - proptest!(|( - address in 0u64 as Address..(1024 * 1024 - WRITTEN_SIZE) as Address, - written: [u8; WRITTEN_SIZE], - )|{ - type MC = M1M; - type B = block::Interpreted; - - let mut buffer = Vec::new(); - let mut hooks = PvmHooks::new(|c| buffer.push(c)); - - // Setup PVM - let mut pvm = Pvm::::new(&mut Owned, InterpretedBlockBuilder); - pvm.reset(); - pvm.machine_state - .core - .main_memory - .set_all_readable_writeable(); - - // Write characters - pvm.machine_state - .core - .main_memory - .write_all(address, &written) - .unwrap(); - - // Write the `write` system call number for `ecall` - pvm.machine_state.core.hart.xregisters.write(a7, linux::WRITE); - - // Write `stdout` as the file descriptor parameter - pvm.machine_state.core.hart.xregisters.write(a0, 1); - - // Write the address for the string to be read from - pvm.machine_state - .core - .hart - .xregisters - .write(a1, address); - - // Write the length of the string - pvm.machine_state - .core - .hart - .xregisters - .write(a2, written.len() as u64); - - pvm.handle_exception(&mut hooks, EnvironException::EnvCall); - - // Drop `hooks` to regain access to the mutable references it kept - mem::drop(hooks); - - // Compare what characters have been passed to the hook versus what we - // intended to write - assert_eq!(written.to_vec(), buffer); - }); - } - - backend_test!(test_reveal, F, { - type MC = M1M; - type B = block::Interpreted::Manager>; - - // Setup PVM - let mut pvm = - Pvm::, _>::new(&mut F::manager(), InterpretedBlockBuilder); - pvm.reset(); - pvm.machine_state - .core - .main_memory - .set_all_readable_writeable(); - - let input_address = memory::FIRST_ADDRESS; - let buffer = [1u8, 2, 3, 4]; - let output_address = input_address + buffer.len() as u64; - let xregisters = &mut pvm.machine_state.core.hart.xregisters; - - // Configure machine for 'sbi_tezos_reveal' - xregisters.write(a7, SBI_FIRMWARE_TEZOS); - - xregisters.write(a6, SBI_TEZOS_REVEAL); - - xregisters.write(a0, input_address); - - xregisters.write(a1, buffer.len() as u64); - - xregisters.write(a2, output_address); - - xregisters.write(a3, REVEAL_REQUEST_MAX_SIZE as u64); - - pvm.machine_state - .core - .main_memory - .write_all(input_address, &buffer) - .unwrap(); - - assert_eq!(pvm.status(), PvmStatus::Evaluating); - - // Handle the ECALL successfully - let outcome = pvm.handle_exception(&mut Default::default(), EnvironException::EnvCall); - assert!(!outcome); - - // After the ECALL we should be waiting for reveal - assert_eq!(pvm.status(), PvmStatus::WaitingForReveal); - - // After ECALL the reveal_request field should be set correctly - assert_eq!(pvm.reveal_request.to_vec(), buffer); - - const REVEAL_DATA_SIZE: usize = 1000; - let reveal_data = [1u8; REVEAL_DATA_SIZE]; - - // Handle Reveal successfully - let outcome = pvm.provide_reveal_response(&reveal_data); - assert!(outcome, "Failed to provide reveal data to the PVM"); - - // After the reveal the size of the data revealed should be written to a0 - assert_eq!( - pvm.machine_state.core.hart.xregisters.read(a0) as usize, - REVEAL_DATA_SIZE - ); - - let mut reveal_result_buffer = [0u8; REVEAL_DATA_SIZE]; - - pvm.machine_state - .core - .main_memory - .read_all(output_address, &mut reveal_result_buffer) - .unwrap(); - - // Reveal data returned correctly - assert_eq!(reveal_result_buffer, reveal_data); - }); - - backend_test!(test_reveal_insufficient_buffer_size, F, { - type MC = M1M; - type B = block::Interpreted::Manager>; - - // Setup PVM - let mut pvm = - Pvm::, _>::new(&mut F::manager(), InterpretedBlockBuilder); - pvm.reset(); - pvm.machine_state - .core - .main_memory - .set_all_readable_writeable(); - - const OUTPUT_BUFFER_SIZE: usize = 10; - let input_address = memory::FIRST_ADDRESS; - let buffer = [1u8, 2, 3, 4]; - let output_address = input_address + buffer.len() as u64; - - let xregisters = &mut pvm.machine_state.core.hart.xregisters; - - // Configure machine for 'sbi_tezos_reveal' - xregisters.write(a7, SBI_FIRMWARE_TEZOS); - - xregisters.write(a6, SBI_TEZOS_REVEAL); - - xregisters.write(a0, input_address); - - xregisters.write(a1, buffer.len() as u64); - - xregisters.write(a2, output_address); - - xregisters.write(a3, OUTPUT_BUFFER_SIZE as u64); - - pvm.machine_state - .core - .main_memory - .write_all(input_address, &buffer) - .unwrap(); - - assert_eq!(pvm.status(), PvmStatus::Evaluating); - - // Handle the ECALL successfully - let outcome = pvm.handle_exception(&mut Default::default(), EnvironException::EnvCall); - assert!(!outcome); - - // After the ECALL we should be waiting for reveal - assert_eq!(pvm.status(), PvmStatus::WaitingForReveal); - - // After ECALL the reveal_request field should be set correctly - assert_eq!(pvm.reveal_request.to_vec(), buffer); - - const REVEAL_DATA_SIZE: usize = 1000; - let reveal_data = [1u8; REVEAL_DATA_SIZE]; - - // Handle Reveal successfully - let outcome = pvm.provide_reveal_response(&reveal_data); - assert!(outcome, "Failed to provide reveal data to the PVM"); - - // After the reveal the size of the data revealed should be written to a0 - assert_eq!( - pvm.machine_state.core.hart.xregisters.read(a0) as usize, - OUTPUT_BUFFER_SIZE - ); - - let mut reveal_result_buffer = [0u8; OUTPUT_BUFFER_SIZE]; - - pvm.machine_state - .core - .main_memory - .read_all(output_address, &mut reveal_result_buffer) - .unwrap(); - - // Reveal data returned correctly - assert_eq!(reveal_result_buffer, reveal_data[..OUTPUT_BUFFER_SIZE]); - }); -} diff --git a/src/riscv/lib/src/pvm/linux.rs b/src/riscv/lib/src/pvm/linux.rs deleted file mode 100644 index 9bae1136a5acec989dd0fd3f4c09367b033322f0..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux.rs +++ /dev/null @@ -1,1426 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -mod addr; -pub mod error; -mod fds; -mod fs; -mod memory; -mod parameters; -mod rng; - -use std::convert::Infallible; -use std::ffi::CStr; -use std::ops::Range; - -use tezos_smart_rollup_constants::riscv::SBI_FIRMWARE_TEZOS; - -use self::addr::VirtAddr; -use self::error::Error; -use self::memory::STACK_SIZE; -use super::Pvm; -use super::PvmHooks; -use crate::machine_state::CacheLayouts; -use crate::machine_state::MachineCoreState; -use crate::machine_state::MachineError; -use crate::machine_state::MachineState; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::memory::Address; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::memory::PAGE_SIZE; -use crate::machine_state::memory::Permissions; -use crate::machine_state::registers; -use crate::program::Program; -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; -use crate::state_backend::Ref; -use crate::struct_layout; - -/// Thread identifier for the main thread -const MAIN_THREAD_ID: u64 = 1; - -/// System call number for `getcwd` on RISC-V -const GETCWD: u64 = 17; - -/// System call number for `openat` on RISC-V -const OPENAT: u64 = 56; - -/// System call number for `write` on RISC-V -pub(crate) const WRITE: u64 = 64; - -/// System call number for `writev` on RISC-V -const WRITEV: u64 = 66; - -/// System call number for `ppoll` on RISC-V -const PPOLL: u64 = 73; - -/// System call number for `readlinkat` on RISC-V -const READLINKAT: u64 = 78; - -/// System call number for `exit` on RISC-V -const EXIT: u64 = 93; - -/// System call number for `exit_group` on RISC-V -const EXITGROUP: u64 = 94; - -/// System call number for `set_tid_address` on RISC-V -const SET_TID_ADDRESS: u64 = 96; - -/// System call number for `tkill` on RISC-V -const TKILL: u64 = 130; - -/// System call number for `sigaltstack` on RISC-V -const SIGALTSTACK: u64 = 132; - -/// System call number for `rt_sigaction` on RISC-V -const RT_SIGACTION: u64 = 134; - -/// System call number for `rt_sigprocmask` on RISC-V -const RT_SIGPROCMASK: u64 = 135; - -/// System call number for `brk` on RISC-V -const BRK: u64 = 214; - -/// System call number for `munmap` on RISC-V -const MUNMAP: u64 = 215; - -/// System call number for `mmap` on RISC-V -const MMAP: u64 = 222; - -/// System call number for `mprotect` on RISC-V -const MPROTECT: u64 = 226; - -/// System call number for `madvise` on RISC-V -const MADVISE: u64 = 233; - -/// System call number for `getrandom` on RISC-V -const GETRANDOM: u64 = 278; - -/// System call number for `clock_gettime` on RISC-V -const CLOCK_GETTIME: u64 = 113; - -/// System call number for `gettimeofday` on RISC-V -const GETTIMEOFDAY: u64 = 169; - -/// Key into the auxiliary vector which informs supervised processes of auxiliary information -#[derive(Clone, Copy)] -#[repr(u64)] -enum AuxVectorKey { - /// [AT_PAGESZ](https://github.com/torvalds/linux/blob/bb066fe812d6fb3a9d01c073d9f1e2fd5a63403b/include/uapi/linux/auxvec.h#L15) - PageSize = 6, - - /// [AT_PHNUM](https://github.com/torvalds/linux/blob/bb066fe812d6fb3a9d01c073d9f1e2fd5a63403b/include/uapi/linux/auxvec.h#L14) - NumProgramHeaders = 5, - - /// [AT_PHENT](https://github.com/torvalds/linux/blob/bb066fe812d6fb3a9d01c073d9f1e2fd5a63403b/include/uapi/linux/auxvec.h#L13) - ProgramHeaderSize = 4, - - /// [AT_PHDR](https://github.com/torvalds/linux/blob/bb066fe812d6fb3a9d01c073d9f1e2fd5a63403b/include/uapi/linux/auxvec.h#L12) - ProgramHeadersPtr = 3, -} - -impl, M: ManagerBase> - MachineState -{ - /// Add data to the stack, returning the updated stack pointer. - fn push_stack(&mut self, align: u64, data: impl AsRef<[u8]>) -> Result - where - M: ManagerReadWrite, - { - let data = data.as_ref(); - - let stack_ptr = self.core.hart.xregisters.read(registers::sp); - let stack_ptr = stack_ptr - .saturating_sub(stack_ptr % align) - .saturating_sub(data.len() as u64); - - self.core.hart.xregisters.write(registers::sp, stack_ptr); - self.core.main_memory.write_all(stack_ptr, data)?; - - Ok(stack_ptr) - } - - /// Initialise the stack for a Linux program. Preparing the stack is a major part of Linux's - /// process initialisation. Musl programs extract valuable information from the stack such as - /// the program name, command-line arguments, environment variables and other auxiliary - /// information. - fn init_linux_stack( - &mut self, - args: &[&CStr], - env: &[&CStr], - auxv: &[(AuxVectorKey, u64)], - ) -> Result<(), MachineError> - where - M: ManagerReadWrite, - { - // First we push all constants so that they are at the top of the stack - let arg_ptrs = args - .iter() - .map(|arg| self.push_stack(1, arg.to_bytes_with_nul())) - .collect::, _>>()?; - let env_ptrs = env - .iter() - .map(|arg| self.push_stack(1, arg.to_bytes_with_nul())) - .collect::, _>>()?; - - // auxv[n] = [null, null] - self.push_stack(8, 0u64.to_le_bytes())?; - self.push_stack(8, 0u64.to_le_bytes())?; - - // auxv[..] = [key, value] - for (key, value) in auxv.iter() { - self.push_stack(8, value.to_le_bytes())?; - self.push_stack(8, (*key as u64).to_le_bytes())?; - } - - // envp[n] = null - self.push_stack(8, 0u64.to_le_bytes())?; - - for &env_ptr in env_ptrs.iter().rev() { - // envp[i] - self.push_stack(8, env_ptr.to_le_bytes())?; - } - - // argv[n] = null - self.push_stack(8, 0u64.to_le_bytes())?; - - for &arg_ptr in arg_ptrs.iter().rev() { - // argv[i] - self.push_stack(8, arg_ptr.to_le_bytes())?; - } - - // argc - self.push_stack(8, (arg_ptrs.len() as u64).to_le_bytes())?; - - Ok(()) - } -} - -impl Pvm -where - MC: MemoryConfig, - CL: CacheLayouts, - B: Block, - M: ManagerBase, -{ - /// Load the program into memory and set the PC to its entrypoint. - fn load_program(&mut self, program: &Program) -> Result<(), MachineError> - where - M: ManagerReadWrite, - { - // Reset hart state & set pc to entrypoint - self.machine_state.core.hart.reset(program.entrypoint); - - let program_start = program.segments.keys().min().copied().unwrap_or(0); - let program_end = program - .segments - .iter() - .map(|(addr, data)| addr.saturating_add(data.len() as u64)) - .max() - .unwrap_or(0); - let program_length = program_end.saturating_sub(program_start) as usize; - - // Allow the program to be written to main memory - self.machine_state.core.main_memory.protect_pages( - program_start, - program_length, - Permissions::Write, - )?; - - // Write program to main memory - for (&addr, data) in program.segments.iter() { - self.machine_state.core.main_memory.write_all(addr, data)?; - } - - // Remove access to the program that has just been placed into memory - self.machine_state.core.main_memory.protect_pages( - program_start, - program_length, - Permissions::None, - )?; - - // Configure memory permissions using the ELF program headers, if present - if let Some(program_headers) = &program.program_headers { - for mem_perms in program_headers.permissions.iter() { - self.machine_state.core.main_memory.protect_pages( - mem_perms.start_address, - mem_perms.length as usize, - mem_perms.permissions, - )?; - } - } - - // Other parts of the supervisor make use of program start and end to properly divide the - // memory. These addresses need to be properly aligned. - let program_start = VirtAddr::new(program_start).align_down(PAGE_SIZE); - let program_end = VirtAddr::new(program_end) - .align_up(PAGE_SIZE) - .ok_or(MachineError::MemoryTooSmall)?; - self.system_state.program.write(program_start..program_end); - - Ok(()) - } - - /// Configure the stack for a new process. - fn prepare_stack(&mut self) -> Result<(), MachineError> - where - M: ManagerReadWrite, - { - let stack_top = VirtAddr::new(MC::TOTAL_BYTES as u64); - - // We must fit at least one guard page between the program break and the stack - let guarded_stack_space = stack_top - self.system_state.program.end; - if guarded_stack_space < PAGE_SIZE.get() as i64 { - return Err(MachineError::MemoryTooSmall); - } - - let unaligned_stack_space = STACK_SIZE.min(guarded_stack_space as u64 - PAGE_SIZE.get()); - let stack_bottom = (stack_top - unaligned_stack_space) - .align_up(PAGE_SIZE) - .ok_or(MachineError::MemoryTooSmall)?; - - // If the stack top wasn't aligned, the stack bottom may be higher than the stack top after - // aligning it upwards - if stack_top < stack_bottom { - return Err(MachineError::MemoryTooSmall); - } - - // At this point we know that `stack_top` >= `stack_bottom` - let stack_space = (stack_top - stack_bottom) as usize; - - // Guard the stack with a guard page. This prevents stack overflows spilling into the heap - // or even worse, the program's .bss or .data area. - let stack_guard = stack_bottom - PAGE_SIZE.get(); - self.machine_state.core.main_memory.protect_pages( - stack_guard.to_machine_address(), - PAGE_SIZE.get() as usize, - Permissions::None, - )?; - - // Make sure the stack region is readable and writable - self.machine_state.core.main_memory.protect_pages( - stack_bottom.to_machine_address(), - stack_space, - Permissions::ReadWrite, - )?; - - self.machine_state - .core - .hart - .xregisters - .write(registers::sp, stack_top.to_machine_address()); - - // Remember the stack guard for later use - self.system_state - .stack_guard - .write(stack_guard..stack_bottom); - - Ok(()) - } - - /// Install a Linux program and configure the Hart to start it. - pub fn setup_linux_process(&mut self, program: &Program) -> Result<(), MachineError> - where - M: ManagerReadWrite, - { - self.load_program(program)?; - - // The stack needs to be prepared before we can push anything to it - self.prepare_stack()?; - - // Auxiliary values vector - let mut auxv = vec![(AuxVectorKey::PageSize, PAGE_SIZE.get())]; - - // If program headers are available, then we should inform the supervised process of them - if let Some(prog_headers) = &program.program_headers { - // Program headers are an array of a C struct. The struct for 64-bit ELF requires 8 - // byte alignment. - let prog_headers_ptr = self.machine_state.push_stack(8, prog_headers.contents)?; - - auxv.push((AuxVectorKey::NumProgramHeaders, prog_headers.num_entries)); - auxv.push((AuxVectorKey::ProgramHeaderSize, prog_headers.entry_size)); - auxv.push((AuxVectorKey::ProgramHeadersPtr, prog_headers_ptr)); - } - - self.machine_state.init_linux_stack( - &[c"tezos-smart-rollup"], - &[c"RUST_BACKTRACE=full"], - &auxv, - )?; - - // Setup heap addresses - let program_end = self.system_state.program.end; - let heap_start = program_end - .align_up(PAGE_SIZE) - .ok_or(MachineError::MemoryTooSmall)?; - - self.system_state - .heap - .write(heap_start..self.system_state.stack_guard.start); - - // Mark all memory as allocated. This also has the benefit of initialising the buddy memory - // manager properly. - self.machine_state - .core - .main_memory - .allocate_pages(Some(0), MC::TOTAL_BYTES, true)?; - - // Make sure only the heap can be used for allocation by the user kernel. - self.machine_state.core.main_memory.deallocate_pages( - self.system_state.heap.start.to_machine_address(), - (self.system_state.heap.end - self.system_state.heap.start) as usize, - )?; - - Ok(()) - } - - /// Check if the supervised process has requested an exit. - /// - /// # Safety - /// - /// This function should only be used as an optimization to terminate the PVM stepper early. - /// Do not use this result in production PVM contexts as it may cause state divergence. - /// The return value depends on internal state that is not tracked by the PVM state, - /// which means different PVM implementations could return different values for the - /// same input state. - pub(crate) unsafe fn has_exited(&self) -> Option { - if self.system_state.exited { - return Some(self.system_state.exit_code); - } - - None - } -} - -struct_layout! { - pub struct SupervisorStateLayout { - tid_address: Atom, - program: Atom>, - heap: Atom>, - stack_guard: Atom>, - } -} - -/// Linux supervisor state -pub struct SupervisorState { - /// Thread lock address - tid_address: Cell, - - /// Has the process exited? - exited: bool, - - /// Exit code for when the process exited - exit_code: u64, - - /// Program in memory - program: Cell, M>, - - /// Heap memory - heap: Cell, M>, - - /// Stack guard - stack_guard: Cell, M>, -} - -impl SupervisorState { - /// Allocate a new supervisor state. - pub fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - SupervisorState { - tid_address: Cell::new(manager), - exited: false, - exit_code: 0, - program: Cell::new(manager), - heap: Cell::new(manager), - stack_guard: Cell::new(manager), - } - } - - /// Bind the given allocated regions to the supervisor state. - pub fn bind(space: AllocatedOf) -> Self { - SupervisorState { - tid_address: space.tid_address, - exited: false, - exit_code: 0, - program: space.program, - stack_guard: space.stack_guard, - heap: space.heap, - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf { - SupervisorStateLayoutF { - tid_address: self.tid_address.struct_ref::(), - program: self.program.struct_ref::(), - stack_guard: self.stack_guard.struct_ref::(), - heap: self.heap.struct_ref::(), - } - } - - /// Handle a Linux system call. - pub fn handle_system_call( - &mut self, - core: &mut MachineCoreState, - hooks: &mut PvmHooks, - on_tezos: impl FnOnce(&mut MachineCoreState) -> bool, - ) -> bool - where - MC: MemoryConfig, - M: ManagerReadWrite, - { - // We need to jump to the next instruction. The ECall instruction which triggered this - // function is 4 byte wide. - let pc = core.hart.pc.read().saturating_add(4); - core.hart.pc.write(pc); - - /// Read an argument from a register and interpret it as a system call argument. - /// If that fails, log the failure. - macro_rules! read_arg { - ($system_call:ident, $reg:ident) => { - core.hart - .xregisters - .try_read(registers::$reg) - .inspect_err(|err| { - crate::log::warning! { - argument = stringify!($reg), - value = core.hart.xregisters.read(registers::$reg), - error = format!("{err:?}"), - pc, - "{} failed", - stringify!($system_call), - } - })? - }; - } - - /// Log the system call name and its arguments. - macro_rules! trace_call { - ($system_call:ident $(, $arg:expr)*) => { - crate::log::trace! { - "{}{:?}", - stringify!($system_call), - ($(&$arg),*) - } - }; - } - - /// Check the result of a system call. If it failed, log the error. - macro_rules! check_result { - ($system_call:ident, $result:expr) => {{ - let parameters::SystemCallResultExecution { - result, - control_flow, - } = $result - .inspect(|res| { - crate::log::trace! { - result = format!("{res:?}"), - "{} succeeded", - stringify!($system_call), - } - }) - .inspect_err(|err| { - crate::log::warning! { - error = format!("{err:?}"), - pc, - "{} failed", - stringify!($system_call), - } - })? - .into(); - core.hart.xregisters.write(registers::a0, result); - control_flow - }}; - } - - // `dispatch0!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch0 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - trace_call!($system_call); - let result = self.[]($($arg)*); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch1!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch1 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - trace_call!($system_call, arg1); - let result = self.[]($($arg,)* arg1); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch2!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch2 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - trace_call!($system_call, arg1, arg2); - let result = self.[]($($arg,)* arg1, arg2); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch3!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch3 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - let arg3 = read_arg!($system_call, a2); - trace_call!($system_call, arg1, arg2, arg3); - let result = self.[]($($arg,)* arg1, arg2, arg3); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch4!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch4 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - let arg3 = read_arg!($system_call, a2); - let arg4 = read_arg!($system_call, a3); - trace_call!($system_call, arg1, arg2, arg3, arg4); - let result = self.[]($($arg,)* arg1, arg2, arg3, arg4); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch5!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - #[expect( - unused_macros, - reason = "There is no system call handler with 5 parameters yet" - )] - macro_rules! dispatch5 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - let arg3 = read_arg!($system_call, a2); - let arg4 = read_arg!($system_call, a3); - let arg5 = read_arg!($system_call, a4); - trace_call!($system_call, arg1, arg2, arg3, arg4, arg5); - let result = self.[]($($arg,)* arg1, arg2, arg3, arg4, arg5); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch6!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - macro_rules! dispatch6 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - let arg3 = read_arg!($system_call, a2); - let arg4 = read_arg!($system_call, a3); - let arg5 = read_arg!($system_call, a4); - let arg6 = read_arg!($system_call, a5); - trace_call!($system_call, arg1, arg2, arg3, arg4, arg5, arg6); - let result = self.[]($($arg,)* arg1, arg2, arg3, arg4, arg5, arg6); - check_result!($system_call, result) - } - } - }}; - } - - // `dispatch7!(system_call_no [, optional_arguments_passed_to_handler])` - // Converts the system call name to the handler - #[expect( - unused_macros, - reason = "There is no system call handler with 7 parameters yet" - )] - macro_rules! dispatch7 { - ($system_call:ty$(, $arg:ident)*) => {{ - try_blocks::try_block! { - paste::paste! { - let arg1 = read_arg!($system_call, a0); - let arg2 = read_arg!($system_call, a1); - let arg3 = read_arg!($system_call, a2); - let arg4 = read_arg!($system_call, a3); - let arg5 = read_arg!($system_call, a4); - let arg6 = read_arg!($system_call, a5); - let arg7 = read_arg!($system_call, a6); - trace_call!($system_call, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - let result = self.[]($($arg,)* arg1, arg2, arg3, arg4, arg5, arg6, arg7); - check_result!($system_call, result) - } - } - }}; - } - - // Programs targeting a Linux kernel pass the system call number in register a7 - let system_call_no = core.hart.xregisters.read(registers::a7); - - let result = match system_call_no { - GETCWD => dispatch2!(getcwd, core), - OPENAT => dispatch0!(openat), - WRITE => dispatch3!(write, core, hooks), - WRITEV => dispatch3!(writev, core, hooks), - PPOLL => dispatch2!(ppoll, core), - READLINKAT => dispatch0!(readlinkat), - EXIT | EXITGROUP => dispatch1!(exit), - SET_TID_ADDRESS => dispatch1!(set_tid_address, core), - TKILL => dispatch2!(tkill), - SIGALTSTACK => dispatch2!(sigaltstack, core), - RT_SIGACTION => dispatch4!(rt_sigaction, core), - RT_SIGPROCMASK => dispatch4!(rt_sigprocmask, core), - BRK => dispatch0!(brk), - MMAP => dispatch6!(mmap, core), - MPROTECT => dispatch3!(mprotect, core), - MUNMAP => dispatch2!(munmap, core), - MADVISE => dispatch0!(madvise), - GETRANDOM => dispatch2!(getrandom, core), - CLOCK_GETTIME => dispatch2!(clock_gettime, core), - GETTIMEOFDAY => dispatch2!(gettimeofday, core), - SBI_FIRMWARE_TEZOS => return on_tezos(core), - _ => Err(Error::NoSystemCall), - }; - - match result { - Err(Error::NoSystemCall) => { - core.hart - .xregisters - .write_system_call_error(Error::NoSystemCall); - false - } - - Err(error) => { - core.hart.xregisters.write_system_call_error(error); - true - } - - Ok(continue_eval) => continue_eval, - } - } - - /// Handle `set_tid_address` system call. - /// - /// See: - fn handle_set_tid_address( - &mut self, - _: &mut MachineCoreState, - tid_address: VirtAddr, - ) -> Result - where - M: ManagerRead + ManagerWrite, - { - // NOTE: `set_tid_address` is mostly important for when a thread terminates. As we don't - // really support threading yet, we only save the address and do nothing else. - // In the future, when we add threading, this system call needs to be implemented to - // support informing other (waiting) threads of termination. - - self.tid_address.write(tid_address); - // The caller expects the Thread ID to be returned - Ok(MAIN_THREAD_ID) - } - - fn handle_exit( - &mut self, - status: parameters::ExitStatus, - ) -> Result - where - M: ManagerReadWrite, - { - self.exit_code = status.exit_code(); - self.exited = true; - - Ok(parameters::SystemCallResultExecution { - result: status.exit_code(), - control_flow: false, - }) - } - - /// Handle `sigaltstack` system call. The new signal stack configuration is discarded. If the - /// old signal stack configuration is requested, it will be zeroed out. - fn handle_sigaltstack( - &mut self, - core: &mut MachineCoreState, - _: u64, - old: parameters::SignalAction, - ) -> Result - where - M: ManagerReadWrite, - { - /// `sizeof(struct sigaltstack)` on the Kernel side - const SIZE_SIGALTSTACK: usize = 24; - - if let Some(old) = old.address() { - core.main_memory.write(old, [0u8; SIZE_SIGALTSTACK])?; - } - - // Return 0 as an indicator of success - Ok(0) - } - - /// Handle `rt_sigaction` system call. This does nothing effectively. It does not support - /// retrieving the previous handler for a signal - it just zeroes out the memory. - /// - /// See: - fn handle_rt_sigaction( - &mut self, - core: &mut MachineCoreState, - _: u64, - _: u64, - old: parameters::SignalAction, - _: parameters::SigsetTSizeEightBytes, - ) -> Result - where - M: ManagerReadWrite, - { - /// `sizeof(struct sigaction)` on the Kernel side - const SIZE_SIGACTION: usize = 32; - - if let Some(old) = old.address() { - // As we don't store the previous signal handler, we just zero out the memory - core.main_memory.write(old, [0u8; SIZE_SIGACTION])?; - } - - // Return 0 as an indicator of success - Ok(0) - } - - /// Handle `rt_sigprocmask` system call. This does nothing effectively. If the previous mask is - /// requested, it will simply be zeroed out. - fn handle_rt_sigprocmask( - &mut self, - core: &mut MachineCoreState, - _: u64, - _: u64, - old: parameters::SignalAction, - _: parameters::SigsetTSizeEightBytes, - ) -> Result - where - M: ManagerReadWrite, - { - if let Some(old) = old.address() { - // As we don't store the previous mask, we just zero out the memory - core.main_memory - .write(old, [0u8; parameters::SIGSET_SIZE as usize])?; - } - - // Return 0 as an indicator of success - Ok(0) - } - - /// Handle `tkill` system call. As there is only one thread at the moment, this system call - /// will return an error if the thread ID is not the main thread ID. - fn handle_tkill( - &mut self, - _: parameters::MainThreadId, - signal: parameters::Signal, - ) -> Result - where - M: ManagerReadWrite, - { - // Indicate that we have exited - self.exited = true; - self.exit_code = signal.exit_code(); - - // Return 0 as an indicator of success, even if this might not actually be used - Ok(parameters::SystemCallResultExecution { - result: 0, - control_flow: false, - }) - } - - /// Handle `clock_gettime` system call. Fills the timespec structure with zeros. - /// - /// See: - fn handle_clock_gettime( - &mut self, - core: &mut MachineCoreState, - _clockid: u64, - tp: u64, - ) -> Result - where - M: ManagerReadWrite, - { - // Size of struct timespec (8 bytes for tv_sec + 8 bytes for tv_nsec) - const TIMESPEC_SIZE: usize = 16; - - // Write zeros to the timespec structure - if tp != 0 { - core.main_memory.write(tp, [0u8; TIMESPEC_SIZE])?; - } else { - return Err(Error::InvalidArgument); - } - - // Return 0 as an indicator of success - Ok(0) - } - - /// Handle `gettimeofday` system call. Fills the timeval and timezone structures with zeros. - /// - /// See: - fn handle_gettimeofday( - &mut self, - core: &mut MachineCoreState, - tv: u64, - tz: u64, - ) -> Result - where - M: ManagerReadWrite, - { - // Size of struct timeval (8 bytes for tv_sec + 8 bytes for tv_usec) - const TIMEVAL_SIZE: usize = 16; - - // Size of struct timezone (4 bytes for tz_minuteswest + 4 bytes for tz_dsttime) - const TIMEZONE_SIZE: usize = 8; - - // Write zeros to the timeval structure if it's not NULL - if tv != 0 { - core.main_memory.write(tv, [0u8; TIMEVAL_SIZE])?; - } - - // Write zeros to the timezone structure if it's not NULL - if tz != 0 { - core.main_memory.write(tz, [0u8; TIMEZONE_SIZE])?; - } - - // Return 0 as an indicator of success - Ok(0) - } -} - -impl Clone for SupervisorState { - fn clone(&self) -> Self { - Self { - tid_address: self.tid_address.clone(), - exited: self.exited, - exit_code: self.exit_code, - program: self.program.clone(), - stack_guard: self.stack_guard.clone(), - heap: self.heap.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use std::array; - use std::num::NonZeroU64; - - use rand::Rng; - - use super::parameters::AddressHint; - use super::parameters::Backend; - use super::parameters::Flags; - use super::parameters::Visibility; - use super::*; - use crate::backend_test; - use crate::machine_state::memory::M4K; - use crate::pvm::linux::error::Error; - use crate::pvm::linux::parameters::NoFileDescriptor; - use crate::pvm::linux::parameters::Zero; - - /// Default handler for the `on_tezos` parameter of [`SupervisorState::handle_system_call`] - fn default_on_tezos_handler(core: &mut MachineCoreState) -> bool - where - MC: MemoryConfig, - M: ManagerWrite, - { - core.hart - .xregisters - .write_system_call_error(Error::NoSystemCall); - true - } - - // Check that the `set_tid_address` system call is working correctly. - backend_test!(set_tid_address, F, { - type MemLayout = M4K; - const MEM_BYTES: usize = MemLayout::TOTAL_BYTES; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - let mut supervisor_state = SupervisorState::new(&mut manager); - - machine_state - .hart - .xregisters - .write(registers::a7, SET_TID_ADDRESS); - - let tid_address = rand::thread_rng().gen_range(0..MEM_BYTES as Address); - machine_state - .hart - .xregisters - .write(registers::a0, tid_address); - - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - - assert_eq!(supervisor_state.tid_address.read(), tid_address); - }); - - // Check `ppoll` system call the way it is used in Musl and Rust's initialisation code. - backend_test!(ppoll_init_fds, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - machine_state.reset(); - - // Make sure everything is readable and writable. Otherwise, we'd get access faults. - machine_state - .main_memory - .protect_pages(0, MemLayout::TOTAL_BYTES, Permissions::ReadWrite) - .unwrap(); - - for fd in [0i32, 1, 2] { - let mut supervisor_state = SupervisorState::new(&mut manager); - - let base_address = 0x10; - machine_state.main_memory.write(base_address, fd).unwrap(); - machine_state - .main_memory - .write(base_address + 4, -1i16) - .unwrap(); - machine_state - .main_memory - .write(base_address + 6, -1i16) - .unwrap(); - - machine_state - .hart - .xregisters - .write(registers::a0, base_address); - machine_state.hart.xregisters.write(registers::a1, 1); - machine_state.hart.xregisters.write(registers::a2, 0); - machine_state.hart.xregisters.write(registers::a3, 0); - machine_state.hart.xregisters.write(registers::a7, PPOLL); - - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - - let ret = machine_state.hart.xregisters.read(registers::a0); - assert_eq!(ret, 0); - - let revents = machine_state - .main_memory - .read::(base_address + 6) - .unwrap(); - assert_eq!(revents, 0); - } - }); - - // Check that the `rt_sigaction` system call is working correctly for a basic case. - backend_test!(rt_sigaction_no_handler, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - machine_state.reset(); - - // Make sure everything is readable and writable. Otherwise, we'd get access faults. - machine_state - .main_memory - .protect_pages(0, MemLayout::TOTAL_BYTES, Permissions::ReadWrite) - .unwrap(); - - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, RT_SIGACTION); - - // Signal is SIGPIPE - machine_state - .hart - .xregisters - .write(registers::a0, 13i32 as u64); - - // New handler is located at this address - machine_state.hart.xregisters.write(registers::a1, 0x20); - - // Old handler will be written to this address - machine_state.hart.xregisters.write(registers::a2, 0x40); - machine_state - .main_memory - .write(0x40, array::from_fn::(|i| i as u8)) - .unwrap(); - - // Size of sigset_t - machine_state.hart.xregisters.write(registers::a3, 8); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - - // Check if the location where the old handler was is now zeroed out - let old_action = machine_state.main_memory.read::<[u8; 32]>(0x40).unwrap(); - assert_eq!(old_action, [0u8; 32]); - }); - - // Check that the `sigaltstack` system call can accept 0 for the `old` parameter. - backend_test!(sigaltstack_zero_parameter, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, SIGALTSTACK); - - // Zero old signal - machine_state.hart.xregisters.write(registers::a0, 0u64); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - }); - - // Check that the `rt_sigaction system call can accept 0 for the `old` parameter. - backend_test!(rt_sigaction_zero_parameter, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, RT_SIGACTION); - - machine_state.hart.xregisters.write(registers::a0, 0u64); - - machine_state.hart.xregisters.write(registers::a1, 0u64); - - // Zero old signal - machine_state.hart.xregisters.write(registers::a2, 0u64); - - // Size of sigset_t - machine_state.hart.xregisters.write(registers::a3, 8u64); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - }); - - // Check that the `rt_sigprocmask system call can accept 0 for the `old` parameter. - backend_test!(rt_sigprocmask_zero_parameter, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, RT_SIGPROCMASK); - - machine_state.hart.xregisters.write(registers::a0, 0u64); - - machine_state.hart.xregisters.write(registers::a1, 0u64); - - // Zero old signal - machine_state.hart.xregisters.write(registers::a2, 0u64); - - // Size of sigset_t - machine_state.hart.xregisters.write(registers::a3, 8u64); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - }); - - // Check that the `clock_gettime` system call fills the timespec with zeros. - backend_test!(clock_gettime_fills_with_zeros, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - machine_state.reset(); - - // Make sure everything is readable and writable. Otherwise, we'd get access faults. - machine_state - .main_memory - .protect_pages(0, MemLayout::TOTAL_BYTES, Permissions::ReadWrite) - .unwrap(); - - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, CLOCK_GETTIME); - - // Any clock ID (we ignore it anyway) - machine_state.hart.xregisters.write(registers::a0, 1u64); - - // Timespec pointer (must be non-zero) - let timespec_ptr = 0x100; - - // Fill the timespec struct with non-zero values to verify they are zeroed - machine_state - .main_memory - .write(timespec_ptr, [0xFF; 16]) - .unwrap(); - - machine_state - .hart - .xregisters - .write(registers::a1, timespec_ptr); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - - // Verify that a0 contains 0 (success) - let ret = machine_state.hart.xregisters.read(registers::a0); - assert_eq!(ret, 0); - - // Verify that the timespec is zeroed out - let timespec = machine_state - .main_memory - .read::<[u8; 16]>(timespec_ptr) - .unwrap(); - assert_eq!(timespec, [0u8; 16]); - }); - - // Check that the `gettimeofday` system call fills the timeval and timezone with zeros. - backend_test!(gettimeofday_fills_with_zeros, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - machine_state.reset(); - - // Make sure everything is readable and writable. Otherwise, we'd get access faults. - machine_state - .main_memory - .protect_pages(0, MemLayout::TOTAL_BYTES, Permissions::ReadWrite) - .unwrap(); - - let mut supervisor_state = SupervisorState::new(&mut manager); - - // System call number - machine_state - .hart - .xregisters - .write(registers::a7, GETTIMEOFDAY); - - // Timeval pointer - let timeval_ptr = 0x100; - - // Fill the timeval struct with non-zero values to verify they are zeroed - machine_state - .main_memory - .write(timeval_ptr, [0xFF; 16]) - .unwrap(); - - machine_state - .hart - .xregisters - .write(registers::a0, timeval_ptr); - - // Timezone pointer - let timezone_ptr = 0x200; - - // Fill the timezone struct with non-zero values to verify they are zeroed - machine_state - .main_memory - .write(timezone_ptr, [0xFF; 8]) - .unwrap(); - - machine_state - .hart - .xregisters - .write(registers::a1, timezone_ptr); - - // Perform the system call - let result = supervisor_state.handle_system_call( - &mut machine_state, - &mut PvmHooks::default(), - default_on_tezos_handler, - ); - assert!(result); - - // Verify that a0 contains 0 (success) - let ret = machine_state.hart.xregisters.read(registers::a0); - assert_eq!(ret, 0); - - // Verify that the timeval is zeroed out - let timeval = machine_state - .main_memory - .read::<[u8; 16]>(timeval_ptr) - .unwrap(); - assert_eq!(timeval, [0u8; 16]); - - // Verify that the timezone is zeroed out - let timezone = machine_state - .main_memory - .read::<[u8; 8]>(timezone_ptr) - .unwrap(); - assert_eq!(timezone, [0u8; 8]); - }); - - // Check that `mmap` returns `Error::NoMemory` when allocate_and_protect_pages fails. - backend_test!(mmap_returns_enomem_when_allocation_fails, F, { - type MemLayout = M4K; - - let mut manager = F::manager(); - let mut machine_state = MachineCoreState::::new(&mut manager); - machine_state.reset(); - - // Allocate all memory to ensure subsequent allocations will fail - machine_state - .main_memory - .allocate_and_protect_pages( - Some(0), - MemLayout::TOTAL_BYTES, - Permissions::ReadWrite, - true, - ) - .unwrap(); - - let mut supervisor_state = SupervisorState::new(&mut manager); - - // Set up necessary registers for mmap - machine_state - .hart - .xregisters - .write(registers::a4, -1i64 as u64); // NoFileDescriptor - machine_state.hart.xregisters.write(registers::a5, 0); // Zero offset - - // Case 1: Test with AddressHint::Hint - { - let addr = 0; // will be ignored with AddressHint::Hint - let length = NonZeroU64::new(PAGE_SIZE.get() * 4).unwrap(); // Arbitrary non-zero size - let perms = Permissions::ReadWrite; - let flags = Flags { - addr_hint: AddressHint::Hint, - visibility: Visibility::Private, - backend: Backend::None, - }; - - // Call the function under test - let result = supervisor_state.handle_mmap( - &mut machine_state, - addr.into(), - length, - perms, - flags, - NoFileDescriptor, - Zero, - ); - - // Verify we get the expected error - assert_eq!(result, Err(Error::NoMemory)); - } - - // Case 2: Test with AddressHint::Fixed - { - let addr = PAGE_SIZE.get() * 10; // Some arbitrary page-aligned address - let length = NonZeroU64::new(PAGE_SIZE.get() * 4).unwrap(); - let perms = Permissions::ReadWrite; - let flags = Flags { - addr_hint: AddressHint::Fixed { - allow_replace: false, - }, - visibility: Visibility::Private, - backend: Backend::None, - }; - - // Call the function under test - let result = supervisor_state.handle_mmap( - &mut machine_state, - VirtAddr::new(addr), - length, - perms, - flags, - NoFileDescriptor, - Zero, - ); - - // Verify we get the expected error - assert_eq!(result, Err(Error::NoMemory)); - } - }); -} diff --git a/src/riscv/lib/src/pvm/linux/addr.rs b/src/riscv/lib/src/pvm/linux/addr.rs deleted file mode 100644 index c3d2705b6a9983f644290a1e7fdef2ba35f83608..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/addr.rs +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fmt; -use std::num::NonZeroU64; -use std::ops::Add; -use std::ops::Deref; -use std::ops::Sub; - -use crate::default::ConstDefault; -use crate::machine_state; - -/// Virtual address -#[derive( - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - serde::Serialize, - serde::Deserialize, - derive_more::From, -)] -#[repr(transparent)] -pub struct VirtAddr(u64); - -impl VirtAddr { - /// Create a new virtual address. - pub const fn new(addr: u64) -> Self { - Self(addr) - } - - /// Return an aligned address that is equal or lower than the current address. - pub const fn align_down(self, align: NonZeroU64) -> Self { - let overhang = self.0.rem_euclid(align.get()); - VirtAddr(self.0.saturating_sub(overhang)) - } - - /// Return an aligned address that is equal or higher than the current address. - pub const fn align_up(self, align: NonZeroU64) -> Option { - let overhang = self.0.rem_euclid(align.get()); - - if overhang == 0 { - return Some(self); - } - - let underhang = align.get().saturating_sub(overhang); - let Some(new) = self.0.checked_add(underhang) else { - // Can't use ? operator in constant expression - return None; - }; - - Some(VirtAddr(new)) - } - - /// Is the address a multiple of the given alignment? - pub const fn is_aligned(self, align: NonZeroU64) -> bool { - self.0.rem_euclid(align.get()) == 0 - } - - /// Convert the virtual address to the machine state's memory address representation. - pub fn to_machine_address(self) -> machine_state::memory::Address { - self.0 - } -} - -impl ConstDefault for VirtAddr { - const DEFAULT: Self = VirtAddr(u64::MAX); -} - -impl fmt::Debug for VirtAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#x}", self.0) - } -} - -impl fmt::Display for VirtAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#x}", self.0) - } -} - -impl Add for VirtAddr { - type Output = Self; - - fn add(self, rhs: u64) -> Self::Output { - VirtAddr(self.0.wrapping_add(rhs)) - } -} - -impl Sub for VirtAddr { - type Output = i64; - - fn sub(self, rhs: Self) -> Self::Output { - (self.0 as i64).wrapping_sub(rhs.0 as i64) - } -} - -impl Sub for VirtAddr { - type Output = Self; - - fn sub(self, rhs: u64) -> Self::Output { - VirtAddr(self.0.wrapping_sub(rhs)) - } -} - -impl PartialEq for VirtAddr { - fn eq(&self, other: &u64) -> bool { - self.0.eq(other) - } -} - -impl PartialOrd for VirtAddr { - fn partial_cmp(&self, other: &u64) -> Option { - self.0.partial_cmp(other) - } -} - -/// Page-aligned value -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct PageAligned(T); - -impl Deref for PageAligned { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TryFrom for PageAligned { - type Error = super::Error; - - fn try_from(value: u64) -> Result { - let addr = VirtAddr::new(value); - - if !addr.is_aligned(super::PAGE_SIZE) { - return Err(super::Error::InvalidArgument); - } - - Ok(PageAligned(addr)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_align_up() { - proptest::proptest!(|(org_addr: u64, align: NonZeroU64)| { - let org_addr = VirtAddr::new(org_addr); - let addr = org_addr.align_up(align); - - match addr { - Some(addr) => { - assert!(addr.is_aligned(align)); - assert!(addr >= org_addr.0); - } - - None => { - assert!(org_addr.to_machine_address() >= u64::MAX - align.get()); - } - } - }); - } - - #[test] - fn test_align_down() { - proptest::proptest!(|(org_addr: u64, align: NonZeroU64)| { - let org_addr = VirtAddr::new(org_addr); - let addr = org_addr.align_down(align); - - assert!(addr.is_aligned(align)); - assert!(addr <= org_addr.0); - }); - } -} diff --git a/src/riscv/lib/src/pvm/linux/error.rs b/src/riscv/lib/src/pvm/linux/error.rs deleted file mode 100644 index a41169907b544800b3f47ca32c3fb47974d52f0f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/error.rs +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::convert::Infallible; -use std::num::TryFromIntError; - -use arbitrary_int::TryNewError; - -use crate::machine_state::memory::BadMemoryAccess; -use crate::machine_state::memory::MemoryGovernanceError; -use crate::machine_state::registers::XRegisters; -use crate::machine_state::registers::XValue; -use crate::machine_state::registers::a0; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerWrite; - -/// Linux system call error codes -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[repr(i32)] -pub enum Error { - /// Process or thread not found - /// - /// See [`ESRCH`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L7) - Search = 3, - - /// File descriptor is bad - /// - /// See [`EBADF`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L13) - BadFileDescriptor = 9, - - /// Access denied - /// - /// See [`EACCESS`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L17) - Access = 13, - - /// Out of memory - /// - /// See [`ENOMEM`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L16) - NoMemory = 12, - - /// Fault during memory access - /// - /// See [`EFAULT`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L18) - Fault = 14, - - /// Invalid argument - /// - /// See [`EINVAL`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L26) - InvalidArgument = 22, - - /// Out of range - /// - /// See [`ERANGE`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/include/uapi/asm-generic/errno-base.h#L38) - Range = 34, - - /// System call is not supported - /// - /// See [`ENOSYS`](https://github.com/torvalds/linux/blob/0ad2507d5d93f39619fc42372c347d6006b64319/tools/include/uapi/asm-generic/errno.h#L18) - NoSystemCall = 38, -} - -impl Error { - /// Turn into an error code that can be returned via an integer register. - pub fn into_xvalue(self) -> XValue { - // The discriminant matches the error code - let error_code = -(self as i32); - error_code as u64 - } -} - -impl From for Error { - fn from(infallible: Infallible) -> Self { - match infallible {} - } -} - -impl From for Error { - fn from(BadMemoryAccess: BadMemoryAccess) -> Self { - Self::Fault - } -} - -impl From for Error { - fn from(MemoryGovernanceError: MemoryGovernanceError) -> Self { - Self::NoMemory - } -} - -impl From for Error { - fn from(_: TryFromIntError) -> Self { - Error::InvalidArgument - } -} - -impl From for Error { - fn from(_: TryNewError) -> Self { - Error::InvalidArgument - } -} - -impl XRegisters { - /// Write the error result of a system call to the return value register. - pub fn write_system_call_error(&mut self, error: Error) - where - M: ManagerWrite, - { - self.write(a0, error.into_xvalue()); - } -} diff --git a/src/riscv/lib/src/pvm/linux/fds.rs b/src/riscv/lib/src/pvm/linux/fds.rs deleted file mode 100644 index 1cd8417ccc05d63726c59ac30c820b8440093a21..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/fds.rs +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech fds -// -// SPDX-License-Identifier: MIT - -//! Implementations of system calls related to file descriptors - -use super::SupervisorState; -use super::error::Error; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::memory::PAGE_SIZE; -use crate::pvm::PvmHooks; -use crate::pvm::linux::VirtAddr; -use crate::pvm::linux::parameters; -use crate::pvm::linux::parameters::FileDescriptorWriteable; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; - -impl SupervisorState { - /// Write to a file descriptor. - fn write_to_fd( - &self, - core: &mut MachineCoreState, - hooks: &mut PvmHooks, - fd: parameters::FileDescriptorWriteable, - addr: VirtAddr, - length: u64, - ) -> Result - where - M: ManagerRead, - { - // Limit how much data we can write to prevent proof-size explosion - let length = length.min(PAGE_SIZE.get()); - - // TODO: RV-487: Memory mappings are not yet protected. We assume the kernel knows what - // it's doing for now. - let mut data = vec![0u8; length as usize]; - core.main_memory - .read_all(addr.to_machine_address(), &mut data)?; - - match fd { - FileDescriptorWriteable::StandardOutput | FileDescriptorWriteable::StandardError => { - for &byte in data.as_slice() { - (hooks.putchar_hook)(byte); - } - } - }; - - // Returning a positive value indicates success - Ok(length) - } - - /// Handle `write` system call. Currently only supports writing to standard output and standard - /// error. - /// - /// See - pub(super) fn handle_write( - &mut self, - core: &mut MachineCoreState, - hooks: &mut PvmHooks, - fd: parameters::FileDescriptorWriteable, - addr: VirtAddr, - length: u64, - ) -> Result - where - M: ManagerReadWrite, - { - // `write` takes an unsigned int as the first parameter, which is then converted to a FileDescriptorWriteable - self.write_to_fd(core, hooks, fd, addr, length) - } - - /// Handle `writev` system call. Writes only to the first entry of the `iovec` array which has - /// data to be written. Otherwise it works the same as `write`. - /// - /// See - pub(super) fn handle_writev( - &mut self, - core: &mut MachineCoreState, - hooks: &mut PvmHooks, - fd: parameters::FileDescriptorWriteable, - iovec: VirtAddr, - len: u64, - ) -> Result - where - M: ManagerReadWrite, - { - // `writev` takes an unsigned long as the first parameter - - if len < 1 { - return Ok(0); - } - - // `iovec` is a `struct iovec[]`. - // - // ``` - // struct iovec { - // void* base; - // size_t len; - // }; - // ``` - - /// `sizeof(struct iovec)` - const SIZE_IOVEC: u64 = 16; - - /// `offsetof(struct iovec, base)` - const OFFSET_BASE: u64 = 0; - - /// `offsetof(struct iovec, len)` - const OFFSET_LEN: u64 = 8; - - let (addr, length) = 'search: { - // Limit the number of entries through which we iterate to prevent proof-size explosion - let max_entries = len.min(PAGE_SIZE.get() / SIZE_IOVEC); - - for item in 0..max_entries { - let struct_addr = iovec + SIZE_IOVEC.wrapping_mul(item); - let struct_addr_base = struct_addr + OFFSET_BASE; - let struct_addr_len = struct_addr + OFFSET_LEN; - - // TODO: RV-487: Memory mappings are not yet protected. We assume the kernel knows - // what it's doing for now. - let addr = core - .main_memory - .read(struct_addr_base.to_machine_address())?; - - // TODO: RV-487: Memory mappings are not yet protected. We assume the kernel knows - // what it's doing for now. - let length = core - .main_memory - .read(struct_addr_len.to_machine_address())?; - - if length > 0 { - // Once we found a non-zero data segment to write, we break out of the loop - break 'search (VirtAddr::new(addr), length); - } - } - - // We haven't found any data to write - return Ok(0); - }; - - self.write_to_fd(core, hooks, fd, addr, length) - } - - /// Handle `ppoll` system call in a way that only satisfies the usage by Musl's and the Rust - /// standard library's initialisation code. It supports only very basic functionality. For - /// example, the `timeout` parameter is ignored entirely. - /// - /// See: - pub(super) fn handle_ppoll( - &mut self, - core: &mut MachineCoreState, - fd_ptrs: u64, - num_fds: parameters::FileDescriptorCount, - ) -> Result - where - M: ManagerReadWrite, - { - // The file descriptors are passed as `struct pollfd[]`. - // - // ``` - // struct pollfd { - // int fd; /* file descriptor */ - // short events; /* requested events */ - // short revents; /* returned events */ - // }; - // ``` - - /// sizeof(struct pollfd) - const SIZE_POLLFD: u64 = 8; - - /// offsetof(struct pollfd, fd) - const OFFSET_FD: u64 = 0; - - /// offsetof(struct pollfd, revents) - const OFFSET_REVENTS: u64 = 6; - - let Ok(fds) = (0..num_fds.count()) - .map(|i| { - core.main_memory.read::( - i.wrapping_mul(SIZE_POLLFD) - .wrapping_add(OFFSET_FD) - .wrapping_add(fd_ptrs), - ) - }) - .collect::, _>>() - else { - return Err(Error::Fault); - }; - - // Only support the initial ppoll that Musl and Rust's init code issue - if !fds.iter().all(|fd| [0, 1, 2].contains(fd)) { - return Err(Error::NoSystemCall); - } - - for i in 0..num_fds.count() { - let revents_ptr = i - .wrapping_mul(SIZE_POLLFD) - .wrapping_add(OFFSET_REVENTS) - .wrapping_add(fd_ptrs); - - core.main_memory - .write_all(revents_ptr, &0u16.to_le_bytes())?; - } - - // Indicate success by returning 0 - Ok(0) - } -} diff --git a/src/riscv/lib/src/pvm/linux/fs.rs b/src/riscv/lib/src/pvm/linux/fs.rs deleted file mode 100644 index 6b15fe68afc5a6517c5a45987291190f22e47710..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/fs.rs +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Implementations of system calls related to the file system - -use super::SupervisorState; -use super::error::Error; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerReadWrite; - -impl SupervisorState { - /// Handle the `openat` system call. All access to the file system is denied. - /// - /// See: - pub(super) fn handle_openat(&mut self) -> Result - where - M: ManagerReadWrite, - { - Err(Error::Access) - } - - /// Handle the `readlinkat` system call. All access to the file system is denied. - /// - /// See: - pub(super) fn handle_readlinkat(&mut self) -> Result - where - M: ManagerReadWrite, - { - Err(Error::Access) - } - - // Handle the `getcwd` system call. This is a simple implementation that returns the root - // directory `/`. - // - // See: - pub(super) fn handle_getcwd( - &mut self, - core: &mut MachineCoreState, - buffer: u64, - length: u64, - ) -> Result - where - M: ManagerReadWrite, - { - const CWD: &[u8] = c"/".to_bytes_with_nul(); - - if length == 0 && buffer != 0 { - return Err(Error::InvalidArgument); - } - - if (length as usize) < CWD.len() { - return Err(Error::Range); - } - - // TODO: RV-487: Memory mappings are not yet protected. We assume the kernel knows what - // it's doing for now. - core.main_memory.write_all(buffer, CWD)?; - - // Return the buffer address as an indicator of success - Ok(buffer) - } -} diff --git a/src/riscv/lib/src/pvm/linux/memory.rs b/src/riscv/lib/src/pvm/linux/memory.rs deleted file mode 100644 index 33243f893c0edd730106d63377dd55ec565a3c46..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/memory.rs +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Linux-style memory management -//! -//! # Layout -//! -//! For a memory config `MC`, the layout consists of the following areas: -//! -//! - `0..program_start` is inaccessible -//! - `program_start..program_end` is the program code and data area -//! - `program_end..heap_start` is the area available for the program break -//! - `heap_start..stack_guard_start` is the heap area -//! - `stack_guard_start..stack_guard_start+PAGE_SIZE` is the stack guard page -//! - `stack_guard_start+PAGE_SIZE..MC::TOTAL_BYTES` is the stack area - -use std::num::NonZeroU64; - -use super::SupervisorState; -use super::addr::PageAligned; -use super::addr::VirtAddr; -use super::error::Error; -use super::parameters::AddressHint; -use super::parameters::Backend; -use super::parameters::Flags; -use super::parameters::NoFileDescriptor; -use super::parameters::Visibility; -use super::parameters::Zero; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::memory::PAGE_SIZE; -use crate::machine_state::memory::Permissions; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerReadWrite; - -/// Number of pages that make up the stack -const STACK_PAGES: u64 = 0x2000; - -/// Maximum stack size in bytes -pub const STACK_SIZE: u64 = PAGE_SIZE.get() * STACK_PAGES; - -impl SupervisorState { - /// Handle `brk` system call. - /// - /// We do not allow moving the program break. This system call can only be used to query the - /// position of the program break. - /// - /// What does this mean for the user kernel? Musl's mallocng doesn't strictly need `brk` to - /// work. If it detects that the program break can't be moved it will default to `mmap` to - /// allocate smaller areas and allocator metadata. - /// - /// See: - pub(super) fn handle_brk(&self) -> Result - where - M: ManagerReadWrite, - { - // The program break may not be moved - Ok(self.program.end.to_machine_address()) - } - - /// Handle `madvise` system call. - /// - /// See: - pub(super) fn handle_madvise(&mut self) -> Result { - // We don't make use of advice yet. We just return 0 to indicate success. - Ok(0) - } - - /// Handle `mprotect` system call. - /// - /// See: - pub(super) fn handle_mprotect( - &mut self, - core: &mut MachineCoreState, - addr: PageAligned, - length: u64, - perms: Permissions, - ) -> Result - where - MC: MemoryConfig, - M: ManagerReadWrite, - { - core.main_memory - .protect_pages(addr.to_machine_address(), length as usize, perms)?; - - // Return 0 to indicate success. - Ok(0) - } - - /// Handle `mmap` system call. - /// - /// See: - #[expect( - clippy::too_many_arguments, - reason = "The system call dispatch mechanism needs these arguments to exist, they can't be on a nested structure" - )] - pub(super) fn handle_mmap( - &mut self, - core: &mut MachineCoreState, - addr: VirtAddr, - length: NonZeroU64, - perms: Permissions, - flags: Flags, - _fd: NoFileDescriptor, - _offset: Zero, - ) -> Result - where - MC: MemoryConfig, - M: ManagerReadWrite, - { - // We don't allow shared mappings - match flags.visibility { - Visibility::Private => {} - Visibility::Shared => return Err(Error::NoSystemCall), - } - - // We don't support file descriptors yet - match flags.backend { - Backend::None => {} - Backend::File => return Err(Error::NoSystemCall), - } - - let res_addr: VirtAddr = match flags.addr_hint { - AddressHint::Hint => core.main_memory.allocate_and_protect_pages( - None, - length.get() as usize, - perms, - false, - )?, - - AddressHint::Fixed { allow_replace } => { - if !addr.is_aligned(PAGE_SIZE) { - return Err(Error::InvalidArgument); - } - - core.main_memory.allocate_and_protect_pages( - Some(addr.to_machine_address()), - length.get() as usize, - perms, - allow_replace, - )? - } - } - .into(); - - Ok(res_addr.to_machine_address()) - } - - /// Handle `munmap` system call. - /// - /// See: - pub(super) fn handle_munmap( - &mut self, - core: &mut MachineCoreState, - addr: u64, - length: u64, - ) -> Result - where - MC: MemoryConfig, - M: ManagerReadWrite, - { - core.main_memory - .deallocate_and_protect_pages(addr, length as usize) - .map_err(|_| Error::InvalidArgument)?; - - // TODO: RV-534: Mapped memory is never freed up to be re-allocated - - Ok(0) - } -} diff --git a/src/riscv/lib/src/pvm/linux/parameters.rs b/src/riscv/lib/src/pvm/linux/parameters.rs deleted file mode 100644 index 379fd0f4d859f2c22da62ee53a1a09dbb41b074b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/parameters.rs +++ /dev/null @@ -1,346 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use core::num::NonZeroU64; -use std::fmt; - -use arbitrary_int::u7; - -use super::MAIN_THREAD_ID; -use super::error::Error; - -/// Size of the `sigset_t` type in bytes -/// -/// As we're building a 64-bit system, the sigset should be 64-bit wide as well. -pub const SIGSET_SIZE: u64 = 8; - -/// A type coupling the result of the system call with how the program should continue. -#[derive(Debug, Clone, Copy)] -pub struct SystemCallResultExecution { - pub result: u64, - pub control_flow: bool, -} - -impl> From for SystemCallResultExecution { - fn from(value: T) -> Self { - // The default action is to continue execution after the system call. In cases where the - // execution should halt, this should be specified. - SystemCallResultExecution { - result: value.into(), - control_flow: true, - } - } -} - -/// The status of the program upon exit. While the C standard specifies that this should be equal -/// to EXIT_SUCCESS or EXIT_FAILURE, this is rarely enforced, and can be any int - where `0` -/// indicates success. -#[derive(Debug, Clone, Copy)] -pub struct ExitStatus(u64); - -impl From for u64 { - fn from(value: ExitStatus) -> Self { - value.0 - } -} - -impl From for ExitStatus { - fn from(value: u64) -> Self { - ExitStatus(value) - } -} - -impl ExitStatus { - /// Extract the exit code from the status stored in this type - pub fn exit_code(&self) -> u64 { - self.0 - } -} - -/// Known to be a valid thread ID. As we currently only support single-thread execution, this will -/// be the main thread. -#[derive(Debug, Clone, Copy)] -pub struct MainThreadId; - -impl TryFrom for MainThreadId { - type Error = Error; - - fn try_from(value: u64) -> Result { - // We only support exiting the main thread - if value != MAIN_THREAD_ID { - return Err(Error::Search); - } - Ok(MainThreadId) - } -} - -/// A signal passed to a thread, see `tkill(2)` -#[derive(Debug, Clone, Copy)] -pub struct Signal(u7); - -impl TryFrom for Signal { - type Error = Error; - - fn try_from(value: u64) -> Result { - Ok(Signal(u7::try_new(value.try_into()?)?)) - } -} - -impl Signal { - /// Extract the exit code from the signal stored in this type - pub fn exit_code(&self) -> u64 { - // Setting bit 2^7 of the exit code indicates that the process was killed by a signal - const EXIT_BY_SIGNAL: u8 = 1 << 7; - - (EXIT_BY_SIGNAL | self.0.value()) as u64 - } -} - -/// An address of a signal action in the VM memory -#[derive(Clone, Copy)] -pub struct SignalAction(pub Option); - -impl fmt::Debug for SignalAction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#x}", self.0.map(|nz| nz.get()).unwrap_or(0)) - } -} - -impl From for SignalAction { - fn from(value: u64) -> Self { - SignalAction(NonZeroU64::new(value)) - } -} - -impl SignalAction { - /// Extract the address of the signal action in the VM memory - pub fn address(&self) -> Option { - self.0.map(|nz| nz.get()) - } -} - -/// A valid size of `sigset_t` -#[derive(Clone, Copy, Debug)] -pub struct SigsetTSizeEightBytes; - -impl TryFrom for SigsetTSizeEightBytes { - type Error = Error; - - fn try_from(value: u64) -> Result { - // As we're implementing a 64-bit system, the size of `sigset_t` must be 8 bytes. - // This is an assumption which is used in the remainder of the function body. - match value { - SIGSET_SIZE => Ok(SigsetTSizeEightBytes), - _ => Err(Error::InvalidArgument), - } - } -} - -/// A valid file descriptor, see write(2) -#[derive(Clone, Copy, Debug)] -pub enum FileDescriptorWriteable { - StandardOutput, - StandardError, -} - -impl TryFrom for FileDescriptorWriteable { - type Error = Error; - - fn try_from(value: u64) -> Result { - // Check if the file descriptor is valid and can be written to. - // In our case, it's just standard output (1) and standard error (2). - match value { - 1 => Ok(FileDescriptorWriteable::StandardOutput), - 2 => Ok(FileDescriptorWriteable::StandardError), - _ => Err(Error::BadFileDescriptor), - } - } -} - -/// The number (count) of file descriptors -#[derive(Clone, Copy)] -pub struct FileDescriptorCount(u64); - -impl FileDescriptorCount { - /// Extract the file descriptor count as a [`u64`]. - pub fn count(&self) -> u64 { - self.0 - } -} - -impl fmt::Debug for FileDescriptorCount { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// Hard limit on the number of file descriptors that a system call can work with -/// -/// We also use this constant to implictly limit how much memory can be associated with a system -/// call. For example, `ppoll` takes a pointer to an array of `struct pollfd`. If we don't limit -/// the length of that array, then we might read an arbitrary amount of memory. This impacts the -/// proof size dramatically as everything read would also be in the proof. -const RLIMIT_NOFILE: u64 = 512; - -impl TryFrom for FileDescriptorCount { - type Error = Error; - - fn try_from(value: u64) -> Result { - // Enforce a limit on the number of file descriptors to prevent proof-size explosion. - // This is akin to enforcing RLIMIT_NOFILE in a real system. - if value > RLIMIT_NOFILE { - return Err(Error::InvalidArgument); - } - Ok(FileDescriptorCount(value)) - } -} - -/// Definitely zero -#[derive(Debug, Clone, Copy)] -pub struct Zero; - -impl TryFrom for Zero { - type Error = Error; - - fn try_from(value: u64) -> Result { - if value != 0 { - return Err(Error::InvalidArgument); - } - - Ok(Zero) - } -} - -/// Special file descriptor meaning no file descriptor -#[derive(Debug, Clone, Copy)] -pub struct NoFileDescriptor; - -impl TryFrom for NoFileDescriptor { - type Error = Error; - - fn try_from(value: u64) -> Result { - if value != -1i64 as u64 { - return Err(Error::BadFileDescriptor); - } - - Ok(NoFileDescriptor) - } -} - -/// Visibility of a memory mapping -#[derive(Debug, Clone, Copy)] -pub enum Visibility { - /// Only visible to the current task - Private, - - // Shared with other tasks - Shared, -} - -/// Backing of a memory mapping -#[derive(Debug, Clone, Copy)] -pub enum Backend { - /// Just memory - None, - - /// File-backend memory - File, -} - -/// Hint on how to interpret the address argument when memory mapping -#[derive(Debug, Clone, Copy)] -pub enum AddressHint { - /// May ignore the address hint - Hint, - - /// Fixed address - Fixed { allow_replace: bool }, -} - -/// Memory mapping request flags -#[derive(Debug, Clone)] -pub struct Flags { - /// Visibility of the memory mapping - pub visibility: Visibility, - - /// Memory backing - pub backend: Backend, - - /// How to interpret the address hint - pub addr_hint: AddressHint, -} - -impl TryFrom for Flags { - type Error = Error; - - fn try_from(mut flags: u64) -> Result { - // Check if a bit is set, and clear it if it is - let mut probe_and_clear = |mask: u64| { - let r = flags & mask == mask; - flags &= !mask; - r - }; - - let visibility = { - const MAP_SHARED: u64 = 0x1; - const MAP_PRIVATE: u64 = 0x2; - - let shared = probe_and_clear(MAP_SHARED); - - // `MAP_SHARED_VALIDATE` translates to `MAP_PRIVATE | MAP_SHARED` for some reason. We - // must make sure that private is requested only when `MAP_SHARED` is not set. - let private = probe_and_clear(MAP_PRIVATE) && !shared; - - if private { - Visibility::Private - } else if shared { - Visibility::Shared - } else { - return Err(Error::InvalidArgument); - } - }; - - let backend = { - const MAP_ANON: u64 = 0x20; - - if probe_and_clear(MAP_ANON) { - Backend::None - } else { - Backend::File - } - }; - - let addr_hint = { - const MAP_FIXED: u64 = 0x10; - const MAP_FIXED_NOREPLACE: u64 = 0x100000; - - if probe_and_clear(MAP_FIXED) { - AddressHint::Fixed { - allow_replace: !probe_and_clear(MAP_FIXED_NOREPLACE), - } - } else if probe_and_clear(MAP_FIXED_NOREPLACE) { - AddressHint::Fixed { - allow_replace: false, - } - } else { - AddressHint::Hint - } - }; - - // `MAP_NORESERVE` does nothing for us as we have no swap - const MAP_NORESERVE: u64 = 0x4000; - probe_and_clear(MAP_NORESERVE); - - // If there are other bits set, that means we likely don't support them - if flags != 0 { - return Err(Error::InvalidArgument); - } - - Ok(Self { - visibility, - backend, - addr_hint, - }) - } -} diff --git a/src/riscv/lib/src/pvm/linux/rng.rs b/src/riscv/lib/src/pvm/linux/rng.rs deleted file mode 100644 index aeb99982bf234907b9663bf37f6d4f0526dd56ab..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/linux/rng.rs +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::SupervisorState; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::pvm::linux::error::Error; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerReadWrite; - -impl SupervisorState { - /// Handle `getrandom` system call. We don't support non-determinism, so we return a fixed - /// value to the caller. The `flags` argument is ignored. One major difference from the Linux - /// implementation is that we return fewer bytes than requested in more circumstances - most - /// notably we don't support requesting more than 4096 bytes. - /// - /// See: - pub(super) fn handle_getrandom( - &mut self, - core: &mut MachineCoreState, - buffer: u64, - length: u64, - ) -> Result - where - M: ManagerReadWrite, - { - let actual_length = length.min(RANDOM.len() as u64); - let data = &RANDOM[..actual_length as usize]; - - // TODO: RV-487: Memory mappings are not yet protected. We assume the kernel knows what - // it's doing for now. - core.main_memory.write_all(buffer, data)?; - - Ok(actual_length) - } -} - -/// Series of random bytes to return for the `getrandom` system call -const RANDOM: [u8; 4096] = [ - 189, 81, 168, 50, 22, 116, 133, 88, 202, 209, 171, 161, 46, 205, 239, 252, 215, 207, 55, 210, - 79, 132, 90, 72, 165, 127, 60, 180, 236, 218, 59, 166, 113, 177, 132, 13, 50, 245, 239, 189, - 149, 233, 101, 192, 221, 39, 77, 110, 52, 118, 150, 18, 100, 89, 212, 212, 220, 207, 36, 129, - 17, 71, 226, 108, 191, 164, 88, 233, 31, 105, 67, 112, 110, 220, 30, 234, 198, 106, 180, 162, - 150, 123, 207, 22, 151, 163, 63, 47, 234, 201, 26, 225, 201, 235, 22, 244, 55, 245, 62, 247, - 73, 117, 209, 77, 136, 204, 224, 161, 73, 171, 240, 144, 202, 180, 197, 225, 194, 76, 108, 231, - 237, 177, 2, 11, 146, 150, 114, 92, 247, 82, 198, 151, 174, 85, 164, 233, 239, 78, 246, 200, - 105, 211, 86, 218, 112, 22, 36, 206, 81, 50, 64, 225, 203, 250, 2, 4, 141, 26, 39, 91, 80, 150, - 124, 210, 230, 237, 52, 225, 105, 40, 26, 33, 14, 244, 243, 79, 207, 87, 173, 41, 114, 227, - 224, 51, 34, 198, 191, 65, 79, 254, 1, 39, 232, 31, 186, 61, 128, 234, 114, 129, 180, 28, 170, - 183, 252, 52, 81, 23, 254, 182, 36, 248, 227, 53, 71, 183, 38, 51, 169, 131, 96, 225, 247, 87, - 228, 203, 74, 117, 166, 198, 200, 163, 90, 199, 245, 203, 208, 36, 163, 83, 153, 33, 235, 59, - 41, 236, 53, 181, 247, 74, 22, 232, 128, 175, 60, 122, 219, 219, 96, 252, 63, 205, 84, 244, - 194, 227, 161, 178, 210, 147, 194, 127, 233, 247, 186, 191, 81, 238, 108, 104, 63, 92, 118, - 241, 35, 121, 206, 30, 204, 74, 228, 203, 3, 112, 95, 59, 101, 186, 143, 63, 138, 25, 38, 239, - 66, 118, 139, 126, 106, 182, 36, 194, 248, 197, 228, 118, 251, 73, 97, 156, 253, 156, 206, 87, - 6, 45, 9, 207, 200, 81, 99, 85, 215, 11, 75, 107, 204, 166, 21, 97, 140, 178, 166, 142, 184, - 224, 101, 236, 68, 153, 173, 60, 191, 131, 174, 173, 163, 178, 152, 89, 111, 20, 4, 74, 94, - 254, 215, 246, 49, 100, 157, 11, 141, 101, 125, 4, 74, 247, 59, 94, 75, 79, 222, 73, 3, 71, 61, - 184, 85, 193, 182, 162, 58, 101, 146, 34, 250, 67, 102, 128, 160, 204, 42, 163, 28, 53, 77, 97, - 159, 70, 231, 12, 224, 192, 23, 36, 62, 23, 117, 53, 103, 162, 139, 166, 195, 173, 23, 233, 34, - 51, 151, 202, 220, 99, 202, 137, 155, 135, 29, 208, 88, 121, 180, 156, 24, 206, 250, 216, 92, - 185, 111, 249, 144, 252, 116, 48, 226, 60, 131, 175, 128, 158, 229, 5, 113, 49, 221, 138, 132, - 185, 193, 158, 80, 117, 95, 139, 215, 83, 32, 131, 194, 1, 232, 213, 115, 29, 190, 13, 221, 8, - 5, 177, 168, 98, 173, 143, 41, 228, 0, 84, 58, 228, 25, 121, 170, 153, 114, 180, 198, 225, 194, - 255, 49, 5, 156, 140, 199, 3, 167, 112, 126, 231, 53, 198, 42, 81, 131, 125, 159, 101, 87, 144, - 12, 23, 17, 155, 231, 173, 155, 10, 49, 100, 106, 216, 189, 0, 13, 120, 41, 60, 56, 112, 1, - 156, 225, 248, 88, 74, 3, 247, 19, 83, 159, 83, 195, 241, 158, 66, 205, 56, 223, 198, 207, 243, - 188, 56, 43, 217, 20, 115, 124, 223, 71, 50, 145, 214, 204, 253, 104, 230, 134, 76, 198, 41, - 175, 92, 52, 58, 106, 249, 186, 64, 228, 64, 203, 83, 48, 178, 119, 231, 7, 38, 178, 39, 54, - 176, 116, 225, 198, 22, 242, 104, 212, 37, 171, 45, 110, 71, 170, 211, 84, 226, 14, 61, 75, - 247, 232, 118, 170, 2, 151, 133, 231, 183, 27, 205, 212, 72, 186, 49, 234, 248, 27, 134, 6, - 110, 31, 33, 160, 222, 109, 62, 235, 124, 64, 62, 176, 83, 43, 30, 99, 40, 227, 75, 251, 124, - 127, 133, 23, 87, 34, 204, 47, 241, 67, 252, 223, 63, 112, 124, 33, 138, 40, 48, 136, 104, 79, - 62, 197, 191, 9, 193, 140, 168, 97, 69, 131, 180, 137, 199, 148, 211, 154, 170, 62, 186, 223, - 84, 201, 212, 104, 181, 129, 104, 68, 81, 139, 20, 157, 105, 76, 176, 215, 135, 242, 11, 19, - 169, 67, 216, 253, 207, 185, 118, 63, 255, 221, 79, 147, 205, 63, 238, 164, 76, 141, 191, 219, - 186, 152, 239, 139, 253, 180, 164, 244, 173, 78, 24, 80, 136, 146, 148, 25, 168, 25, 159, 71, - 243, 163, 155, 60, 128, 14, 124, 124, 226, 123, 53, 203, 145, 123, 84, 58, 134, 34, 8, 118, 27, - 192, 91, 141, 69, 29, 2, 239, 152, 57, 165, 59, 199, 147, 223, 76, 39, 129, 215, 60, 184, 60, - 47, 129, 115, 69, 66, 92, 93, 192, 193, 207, 143, 238, 218, 229, 160, 252, 107, 80, 7, 89, 87, - 177, 165, 221, 29, 111, 96, 243, 13, 161, 47, 29, 70, 100, 101, 62, 3, 61, 24, 99, 94, 119, - 174, 254, 19, 169, 173, 84, 178, 44, 199, 67, 70, 202, 154, 58, 134, 72, 147, 19, 122, 221, - 206, 8, 48, 123, 197, 153, 170, 75, 34, 17, 81, 61, 153, 58, 247, 6, 206, 162, 225, 25, 26, 2, - 23, 69, 237, 241, 194, 42, 74, 235, 237, 170, 136, 129, 37, 247, 122, 130, 247, 184, 241, 42, - 183, 165, 144, 83, 126, 149, 79, 89, 141, 140, 134, 114, 20, 151, 240, 29, 77, 164, 190, 224, - 57, 181, 138, 3, 253, 133, 49, 67, 85, 214, 71, 19, 65, 12, 49, 226, 6, 4, 12, 177, 189, 134, - 147, 181, 100, 146, 69, 45, 176, 130, 88, 33, 93, 109, 149, 150, 95, 188, 175, 44, 155, 213, - 134, 230, 16, 185, 2, 3, 233, 230, 132, 198, 100, 126, 4, 39, 159, 188, 136, 252, 135, 39, 169, - 88, 228, 67, 169, 50, 198, 171, 73, 227, 78, 105, 158, 192, 198, 241, 130, 180, 184, 251, 233, - 60, 252, 83, 159, 117, 201, 61, 196, 190, 187, 232, 79, 123, 57, 194, 220, 57, 77, 69, 70, 13, - 0, 96, 85, 137, 111, 78, 191, 88, 59, 187, 103, 133, 225, 134, 190, 16, 93, 180, 89, 83, 71, - 77, 229, 111, 136, 239, 112, 74, 155, 115, 188, 36, 190, 136, 76, 243, 213, 248, 19, 118, 21, - 190, 236, 103, 37, 201, 103, 172, 152, 207, 245, 210, 193, 80, 183, 4, 22, 106, 38, 196, 145, - 189, 210, 144, 115, 84, 28, 3, 198, 254, 156, 207, 228, 18, 223, 105, 179, 210, 240, 254, 157, - 242, 74, 171, 118, 158, 107, 29, 190, 26, 212, 140, 33, 123, 247, 144, 117, 170, 26, 207, 249, - 86, 65, 23, 128, 167, 173, 156, 209, 123, 14, 9, 127, 243, 213, 182, 68, 35, 31, 244, 129, 45, - 12, 38, 52, 104, 39, 175, 205, 67, 179, 44, 206, 42, 139, 13, 137, 18, 120, 221, 100, 108, 145, - 165, 55, 255, 42, 140, 96, 242, 247, 7, 249, 40, 135, 35, 48, 31, 27, 145, 144, 213, 197, 133, - 129, 171, 0, 239, 231, 31, 7, 233, 237, 197, 87, 72, 191, 224, 217, 168, 211, 183, 215, 145, - 39, 147, 184, 218, 191, 187, 39, 127, 50, 155, 142, 223, 140, 164, 197, 233, 32, 49, 15, 184, - 130, 232, 59, 196, 210, 52, 9, 120, 32, 145, 87, 173, 36, 235, 227, 203, 38, 37, 209, 191, 116, - 45, 106, 155, 47, 107, 143, 147, 162, 78, 180, 136, 160, 87, 167, 115, 51, 2, 31, 252, 126, - 186, 51, 186, 255, 134, 99, 142, 129, 135, 37, 146, 144, 175, 237, 172, 58, 103, 34, 142, 193, - 238, 5, 140, 170, 3, 23, 173, 210, 25, 131, 241, 166, 247, 44, 230, 101, 41, 168, 251, 61, 119, - 125, 243, 13, 7, 45, 77, 145, 143, 54, 247, 240, 221, 119, 222, 29, 220, 108, 204, 78, 73, 226, - 118, 72, 120, 144, 106, 9, 176, 217, 92, 35, 206, 221, 215, 167, 186, 185, 222, 107, 248, 201, - 206, 102, 158, 222, 78, 69, 115, 53, 77, 25, 155, 103, 92, 137, 25, 185, 48, 51, 171, 42, 127, - 196, 97, 80, 124, 167, 50, 142, 56, 91, 194, 42, 58, 167, 176, 36, 90, 124, 244, 54, 226, 248, - 223, 211, 8, 202, 75, 85, 40, 115, 116, 57, 250, 55, 121, 135, 138, 159, 47, 222, 233, 121, - 154, 82, 240, 246, 82, 115, 98, 203, 127, 198, 153, 226, 91, 141, 138, 227, 58, 135, 191, 69, - 59, 176, 20, 101, 23, 213, 28, 93, 201, 137, 214, 226, 83, 245, 14, 92, 50, 145, 129, 134, 233, - 39, 94, 76, 182, 80, 29, 35, 43, 239, 95, 36, 172, 128, 136, 181, 236, 160, 37, 228, 127, 49, - 246, 231, 210, 159, 111, 200, 146, 4, 70, 83, 109, 73, 139, 133, 85, 184, 162, 161, 167, 139, - 12, 74, 212, 117, 26, 5, 34, 9, 189, 23, 128, 141, 192, 17, 31, 126, 124, 129, 238, 16, 61, 70, - 56, 140, 71, 14, 80, 198, 193, 77, 198, 126, 233, 192, 219, 41, 65, 34, 157, 232, 218, 88, 254, - 64, 232, 219, 75, 229, 244, 249, 125, 148, 13, 79, 248, 133, 214, 23, 5, 24, 191, 195, 41, 247, - 192, 167, 23, 147, 90, 60, 121, 78, 110, 217, 58, 215, 178, 132, 143, 7, 97, 141, 67, 32, 252, - 53, 90, 150, 149, 208, 169, 217, 178, 195, 236, 129, 192, 60, 161, 155, 213, 206, 197, 110, - 224, 14, 110, 233, 24, 154, 93, 161, 255, 147, 14, 192, 86, 200, 26, 205, 135, 168, 205, 5, 2, - 130, 179, 201, 77, 74, 110, 169, 8, 179, 99, 231, 197, 251, 212, 48, 17, 108, 118, 243, 1, 155, - 3, 176, 230, 124, 181, 102, 215, 20, 110, 149, 105, 113, 212, 123, 147, 124, 79, 161, 212, 177, - 107, 115, 11, 56, 86, 40, 237, 191, 81, 222, 153, 67, 191, 12, 232, 63, 29, 204, 53, 5, 18, - 167, 167, 192, 193, 200, 19, 171, 65, 230, 179, 254, 217, 32, 227, 203, 121, 197, 154, 182, - 231, 251, 18, 250, 59, 198, 18, 59, 108, 97, 101, 170, 54, 253, 188, 224, 203, 38, 4, 156, 214, - 39, 160, 178, 92, 248, 199, 82, 195, 95, 240, 100, 41, 230, 110, 60, 34, 97, 201, 227, 82, 197, - 139, 246, 19, 11, 84, 1, 203, 93, 200, 192, 18, 99, 6, 235, 187, 179, 148, 103, 21, 97, 1, 221, - 92, 88, 115, 235, 48, 182, 44, 221, 212, 31, 188, 206, 121, 243, 60, 111, 40, 131, 119, 71, - 238, 197, 124, 233, 109, 2, 2, 254, 209, 236, 37, 69, 34, 161, 127, 164, 255, 137, 24, 186, 49, - 69, 140, 214, 165, 74, 120, 85, 175, 240, 140, 69, 132, 144, 30, 230, 137, 213, 70, 88, 155, - 95, 91, 141, 13, 55, 149, 82, 182, 154, 19, 215, 18, 130, 123, 147, 64, 111, 55, 209, 187, 161, - 145, 43, 150, 226, 34, 237, 86, 39, 57, 110, 112, 154, 8, 81, 101, 20, 9, 32, 243, 205, 22, - 119, 126, 166, 210, 237, 115, 233, 154, 154, 152, 119, 3, 146, 250, 158, 245, 124, 250, 192, - 110, 170, 125, 158, 168, 2, 124, 119, 126, 56, 92, 149, 224, 177, 237, 255, 92, 136, 132, 189, - 198, 141, 96, 249, 125, 37, 50, 125, 154, 127, 139, 223, 135, 18, 228, 45, 254, 203, 202, 59, - 208, 218, 41, 151, 21, 42, 155, 205, 182, 237, 65, 41, 13, 236, 46, 11, 39, 47, 91, 245, 138, - 226, 221, 11, 179, 219, 57, 91, 118, 109, 3, 19, 148, 70, 189, 196, 197, 91, 229, 55, 72, 100, - 39, 114, 91, 220, 245, 28, 144, 105, 8, 86, 156, 146, 88, 219, 113, 243, 19, 238, 206, 44, 49, - 240, 55, 153, 79, 61, 176, 104, 108, 66, 39, 82, 211, 187, 60, 52, 17, 124, 165, 8, 188, 24, 4, - 233, 207, 156, 141, 59, 77, 240, 144, 129, 146, 225, 107, 188, 229, 11, 34, 168, 207, 32, 134, - 153, 66, 65, 222, 124, 231, 45, 193, 235, 85, 2, 229, 39, 146, 162, 30, 52, 119, 63, 237, 185, - 162, 67, 13, 122, 115, 212, 191, 57, 51, 245, 131, 157, 139, 33, 38, 140, 45, 63, 81, 183, 161, - 198, 92, 111, 222, 57, 233, 137, 120, 217, 2, 65, 8, 71, 174, 30, 250, 36, 100, 99, 161, 5, - 121, 207, 253, 79, 220, 233, 72, 81, 35, 129, 59, 193, 210, 212, 220, 185, 200, 234, 0, 107, 5, - 33, 224, 98, 234, 118, 63, 223, 195, 163, 36, 77, 227, 82, 220, 40, 24, 146, 56, 2, 153, 173, - 143, 44, 26, 172, 138, 59, 61, 121, 34, 20, 113, 179, 109, 49, 159, 173, 189, 47, 72, 162, 155, - 29, 125, 27, 51, 118, 142, 42, 197, 71, 192, 108, 6, 0, 133, 79, 101, 23, 112, 22, 225, 62, - 217, 39, 192, 28, 217, 192, 230, 141, 108, 81, 111, 158, 151, 197, 93, 203, 245, 207, 117, 86, - 190, 220, 217, 36, 117, 5, 78, 39, 107, 130, 15, 58, 211, 160, 13, 245, 23, 27, 67, 108, 158, - 32, 177, 49, 41, 161, 247, 69, 30, 99, 65, 41, 247, 69, 14, 105, 222, 102, 102, 35, 13, 118, - 136, 20, 166, 9, 71, 222, 98, 93, 71, 71, 222, 68, 170, 14, 90, 152, 2, 45, 160, 67, 119, 239, - 118, 172, 228, 166, 217, 115, 46, 30, 80, 161, 84, 86, 177, 159, 100, 74, 248, 51, 125, 131, - 253, 19, 230, 56, 78, 173, 226, 128, 135, 42, 164, 186, 236, 126, 159, 177, 195, 79, 94, 16, - 221, 243, 134, 73, 30, 186, 38, 131, 86, 240, 214, 249, 10, 193, 226, 243, 97, 164, 160, 34, - 174, 142, 232, 246, 176, 123, 23, 84, 42, 140, 19, 54, 195, 12, 216, 44, 110, 83, 113, 153, 90, - 75, 128, 57, 6, 233, 79, 126, 8, 36, 227, 110, 35, 224, 230, 178, 154, 127, 205, 38, 110, 3, - 227, 57, 91, 189, 192, 191, 178, 27, 196, 252, 237, 99, 57, 109, 50, 223, 183, 121, 184, 47, - 161, 107, 106, 245, 201, 74, 137, 9, 145, 137, 22, 202, 49, 48, 245, 166, 93, 190, 97, 81, 103, - 35, 96, 38, 67, 91, 135, 212, 240, 249, 192, 182, 240, 119, 193, 49, 73, 21, 140, 116, 142, 34, - 46, 147, 225, 181, 154, 166, 42, 93, 128, 213, 254, 190, 41, 13, 136, 178, 248, 152, 53, 191, - 239, 73, 143, 110, 239, 221, 56, 7, 177, 209, 176, 185, 72, 86, 149, 83, 210, 226, 146, 11, 26, - 22, 108, 167, 232, 95, 40, 165, 228, 210, 238, 13, 244, 23, 128, 155, 178, 24, 96, 160, 203, - 238, 46, 50, 88, 71, 225, 239, 194, 45, 222, 237, 106, 123, 55, 247, 225, 100, 44, 226, 14, - 125, 190, 237, 87, 184, 205, 175, 70, 249, 142, 201, 93, 13, 33, 28, 126, 59, 245, 178, 193, - 206, 196, 78, 143, 240, 83, 217, 118, 255, 164, 81, 143, 194, 210, 16, 49, 236, 65, 85, 178, - 149, 205, 57, 16, 31, 145, 104, 94, 192, 252, 200, 66, 211, 234, 36, 116, 238, 59, 245, 93, - 151, 60, 20, 36, 1, 170, 247, 98, 8, 29, 35, 166, 154, 39, 9, 169, 94, 253, 181, 146, 74, 201, - 33, 167, 41, 3, 211, 82, 17, 252, 93, 68, 225, 18, 157, 52, 62, 125, 142, 197, 223, 121, 58, - 128, 252, 159, 142, 198, 240, 111, 102, 94, 86, 106, 227, 127, 138, 210, 97, 60, 187, 84, 218, - 35, 227, 249, 127, 105, 15, 205, 189, 89, 122, 19, 115, 176, 24, 64, 105, 47, 90, 31, 185, 33, - 16, 85, 18, 73, 44, 2, 78, 62, 170, 76, 212, 208, 36, 207, 3, 43, 35, 116, 206, 10, 162, 91, - 21, 42, 195, 154, 135, 15, 119, 109, 27, 140, 205, 65, 59, 112, 18, 170, 99, 19, 214, 18, 124, - 61, 75, 80, 54, 40, 105, 144, 210, 188, 239, 95, 155, 168, 61, 157, 224, 27, 200, 249, 28, 255, - 30, 68, 247, 58, 26, 186, 59, 146, 211, 21, 149, 143, 86, 135, 55, 147, 80, 52, 17, 1, 193, - 148, 250, 255, 121, 192, 250, 81, 120, 248, 56, 84, 138, 163, 49, 215, 200, 14, 50, 64, 192, - 35, 69, 169, 250, 72, 128, 153, 208, 107, 62, 12, 42, 141, 168, 140, 53, 13, 114, 230, 189, - 141, 40, 3, 206, 207, 83, 110, 44, 185, 62, 191, 54, 181, 114, 251, 13, 75, 65, 188, 44, 66, - 27, 125, 80, 30, 24, 227, 175, 58, 54, 90, 114, 57, 139, 108, 186, 204, 84, 122, 74, 77, 38, - 156, 61, 117, 226, 79, 203, 40, 235, 52, 240, 52, 236, 11, 144, 190, 193, 132, 165, 216, 224, - 224, 81, 114, 184, 114, 183, 39, 8, 168, 115, 158, 40, 76, 223, 165, 182, 101, 57, 161, 72, - 173, 151, 60, 109, 165, 13, 194, 159, 58, 238, 184, 8, 115, 153, 75, 54, 247, 96, 186, 161, 58, - 205, 21, 11, 57, 12, 146, 66, 72, 57, 157, 189, 14, 225, 119, 163, 14, 112, 35, 236, 8, 147, - 229, 131, 117, 32, 1, 149, 91, 84, 94, 236, 99, 52, 170, 160, 212, 198, 185, 111, 79, 110, 168, - 161, 2, 65, 1, 197, 212, 78, 100, 125, 65, 148, 67, 46, 66, 18, 91, 41, 147, 78, 174, 17, 49, - 114, 222, 55, 222, 18, 140, 64, 237, 139, 198, 117, 66, 156, 85, 30, 70, 167, 136, 113, 6, 73, - 220, 23, 136, 119, 255, 234, 205, 83, 183, 46, 206, 104, 12, 204, 61, 0, 8, 72, 57, 38, 31, - 132, 67, 22, 138, 255, 83, 146, 32, 143, 90, 96, 44, 29, 193, 190, 110, 85, 185, 197, 93, 17, - 70, 45, 245, 248, 102, 112, 130, 166, 94, 223, 30, 97, 168, 120, 70, 185, 207, 72, 195, 55, - 214, 166, 76, 219, 103, 110, 8, 19, 166, 58, 225, 218, 180, 206, 163, 48, 152, 225, 247, 155, - 226, 223, 68, 193, 142, 183, 242, 67, 163, 221, 154, 136, 255, 215, 217, 27, 242, 61, 138, 180, - 241, 80, 129, 69, 197, 31, 232, 102, 67, 240, 128, 128, 23, 20, 195, 136, 40, 122, 143, 157, - 114, 217, 86, 95, 116, 133, 59, 8, 16, 128, 193, 251, 66, 218, 24, 85, 58, 189, 104, 129, 30, - 140, 213, 233, 250, 70, 212, 64, 35, 186, 7, 38, 207, 102, 47, 113, 250, 131, 7, 81, 222, 187, - 116, 213, 30, 85, 142, 6, 121, 114, 61, 239, 30, 140, 107, 222, 198, 71, 60, 31, 124, 76, 141, - 43, 34, 157, 148, 23, 171, 246, 119, 169, 175, 62, 127, 234, 128, 126, 206, 133, 168, 84, 25, - 151, 165, 130, 241, 1, 109, 169, 157, 237, 9, 109, 21, 205, 13, 47, 56, 56, 210, 133, 157, 39, - 159, 199, 238, 24, 134, 63, 13, 115, 181, 69, 243, 77, 15, 231, 126, 125, 91, 152, 20, 96, 85, - 65, 165, 33, 172, 217, 58, 146, 168, 64, 103, 194, 65, 9, 163, 76, 141, 0, 30, 178, 245, 107, - 188, 132, 117, 191, 77, 61, 31, 82, 217, 23, 239, 50, 229, 32, 191, 119, 153, 134, 221, 18, 73, - 190, 158, 233, 32, 27, 151, 105, 139, 192, 214, 243, 188, 90, 231, 75, 202, 60, 46, 222, 177, - 157, 217, 71, 222, 7, 96, 212, 95, 145, 177, 255, 12, 92, 95, 174, 129, 39, 201, 238, 173, 30, - 46, 43, 21, 230, 97, 56, 90, 181, 192, 124, 202, 158, 206, 166, 207, 245, 148, 8, 240, 150, 85, - 214, 175, 95, 178, 152, 70, 210, 15, 167, 99, 249, 122, 33, 45, 234, 3, 115, 18, 120, 57, 69, - 128, 237, 100, 89, 78, 180, 58, 143, 227, 78, 136, 95, 218, 77, 161, 98, 176, 252, 59, 235, - 183, 25, 197, 90, 7, 5, 221, 198, 162, 95, 175, 32, 183, 125, 23, 177, 23, 194, 77, 32, 169, - 117, 153, 181, 63, 85, 8, 108, 9, 55, 193, 26, 4, 119, 71, 227, 3, 122, 87, 92, 78, 172, 79, - 27, 55, 187, 130, 173, 178, 234, 111, 205, 16, 163, 144, 34, 242, 240, 100, 155, 117, 182, 177, - 103, 45, 210, 201, 44, 88, 185, 107, 143, 177, 63, 101, 11, 85, 59, 55, 26, 128, 118, 144, 76, - 233, 211, 37, 196, 129, 176, 137, 74, 206, 104, 41, 82, 248, 30, 109, 100, 204, 194, 243, 246, - 142, 87, 215, 93, 149, 215, 205, 156, 170, 63, 175, 19, 26, 160, 224, 175, 223, 45, 137, 133, - 187, 93, 76, 216, 226, 52, 36, 85, 55, 185, 250, 139, 225, 36, 69, 202, 179, 12, 74, 1, 182, - 169, 215, 44, 77, 30, 74, 122, 219, 31, 139, 195, 96, 140, 28, 86, 16, 132, 65, 14, 16, 157, - 193, 162, 138, 87, 203, 107, 46, 237, 197, 126, 220, 202, 105, 26, 123, 183, 163, 156, 163, 84, - 40, 153, 102, 45, 168, 192, 235, 217, 57, 253, 231, 169, 233, 1, 55, 96, 97, 109, 13, 20, 194, - 96, 37, 74, 206, 234, 122, 200, 139, 103, 223, 145, 39, 23, 119, 2, 155, 94, 161, 191, 37, 186, - 9, 223, 204, 65, 189, 207, 53, 94, 209, 180, 140, 22, 8, 119, 124, 239, 128, 157, 46, 140, 238, - 172, 157, 26, 26, 134, 78, 147, 249, 33, 150, 29, 69, 241, 204, 71, 60, 88, 39, 130, 215, 149, - 50, 110, 78, 157, 188, 104, 142, 50, 106, 231, 60, 168, 78, 178, 227, 243, 3, 72, 232, 203, - 206, 110, 242, 252, 72, 116, 2, 98, 169, 240, 138, 41, 73, 124, 209, 67, 244, 45, 143, 84, 26, - 130, 5, 10, 144, 117, 251, 135, 6, 0, 58, 158, 66, 21, 168, 68, 160, 147, 49, 25, 230, 84, 151, - 141, 0, 197, 32, 187, 140, 68, 31, 121, 19, 182, 244, 9, 48, 157, 5, 159, 39, 160, 243, 253, - 224, 132, 105, 4, 145, 248, 178, 170, 184, 92, 145, 91, 248, 247, 155, 169, 156, 244, 90, 203, - 247, 23, 243, 63, 133, 78, 236, 123, 63, 209, 81, 2, 53, 254, 24, 107, 99, 72, 42, 168, 186, - 132, 65, 46, 125, 21, 156, 146, 214, 45, 63, 128, 120, 129, 95, 49, 155, 250, 158, 210, 32, 88, - 53, 174, 147, 97, 6, 33, 146, 142, 237, 197, 125, 59, 164, 249, 38, 93, 93, 132, 118, 97, 170, - 45, 174, 98, 122, 25, 100, 68, 156, 173, 82, 62, 250, 203, 18, 191, 164, 22, 213, 153, 70, 248, - 187, 79, 247, 159, 135, 39, 215, 62, 53, 26, 128, 44, 104, 242, 228, 204, 26, 97, 200, 241, - 133, 148, 247, 68, 42, 44, 148, 129, 68, 107, 42, 4, 41, 83, 255, 111, 203, 68, 206, 224, 11, - 211, 47, 27, 220, 238, 135, 76, 192, 42, 233, 250, 205, 4, 56, 70, 44, 59, 127, 148, 105, 111, - 104, 163, 127, 56, 107, 130, 240, 181, 181, 133, 230, 215, 230, 176, 217, 63, 211, 213, 124, - 122, 3, 73, 172, 113, 28, 150, 167, 222, 184, 121, 188, 237, 221, 224, 187, 235, 1, 228, 144, - 155, 51, 20, 145, 163, 250, 152, 124, 35, 191, 24, 89, 253, 123, 0, 61, 190, 45, 210, 193, 95, - 36, 123, 106, 68, 36, 127, 43, 218, 179, 107, 18, 122, 192, 60, 21, 116, 20, 199, 205, 31, 26, - 74, 202, 116, 151, 252, 131, 214, 109, 75, 97, 124, 51, 149, 37, 128, 218, 26, 114, 129, 144, - 90, 217, 236, 181, 84, 101, 37, 60, 90, 129, 236, 2, 41, 153, 140, 140, 2, 101, 245, 225, 56, - 134, 224, 8, 49, 150, 117, 184, 137, 26, 203, 103, 2, 199, 4, 235, 222, 204, 216, 115, 125, - 110, 102, 205, 82, 130, 173, 180, 178, 92, 156, 92, 3, 206, 54, 71, 20, 62, 51, 42, 132, 88, - 136, 113, 143, 79, 167, 161, 240, 131, 221, 29, 102, 61, 170, 223, 227, 142, 77, 46, 75, 211, - 240, 173, 126, 161, 141, 146, 248, 118, 106, 184, 148, 205, 13, -]; diff --git a/src/riscv/lib/src/pvm/node_pvm.rs b/src/riscv/lib/src/pvm/node_pvm.rs deleted file mode 100644 index bdc102f59422bdcbf937f04991d536711b136445..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/node_pvm.rs +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -use std::fmt; -use std::ops::Bound; -use std::path::Path; - -use thiserror::Error; - -use super::Pvm; -use super::PvmLayout; -use crate::machine_state::TestCacheLayouts; -use crate::machine_state::block_cache::block::Interpreted; -use crate::machine_state::block_cache::block::InterpretedBlockBuilder; -use crate::program::Program; -use crate::pvm::common::PvmHooks; -use crate::pvm::common::PvmInput; -use crate::pvm::common::PvmStatus; -use crate::state::NewState; -use crate::state_backend; -use crate::state_backend::AllocatedOf; -use crate::state_backend::FnManagerIdent; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::proof::Proof; -use crate::state_backend::verify_backend::Verifier; -use crate::storage; -use crate::storage::Hash; -use crate::storage::Repo; - -#[derive(Error, Debug)] -pub enum PvmError { - #[error("Serialization error: {0}")] - SerializationError(String), -} - -type NodePvmMemConfig = crate::machine_state::memory::M64M; - -type NodePvmLayout = PvmLayout; - -type NodePvmState = Pvm, M>; - -pub struct NodePvm { - state: Box>, -} - -impl NodePvm { - pub fn bind(space: AllocatedOf) -> Self - where - M::ManagerRoot: state_backend::ManagerReadWrite, - { - let state = NodePvmState::::bind(space, InterpretedBlockBuilder); - Self { - state: Box::new(state), - } - } - - fn with_backend_mut(&mut self, f: F) -> T - where - F: FnOnce(&mut NodePvmState) -> T, - { - f(&mut self.state) - } - - fn with_backend(&self, f: F) -> T - where - F: FnOnce(&NodePvmState) -> T, - { - f(&self.state) - } - - pub fn get_status(&self) -> PvmStatus - where - M: state_backend::ManagerRead, - { - self.with_backend(|pvm| pvm.status()) - } - - pub fn get_tick(&self) -> u64 - where - M: state_backend::ManagerRead, - { - self.with_backend(|pvm| pvm.tick.read()) - } - - pub fn get_current_level(&self) -> Option - where - M: state_backend::ManagerRead, - { - self.with_backend(|pvm| { - if pvm.level_is_set.read() { - Some(pvm.level.read()) - } else { - None - } - }) - } - - pub fn get_message_counter(&self) -> u64 - where - M: state_backend::ManagerRead, - { - self.with_backend(|pvm| pvm.message_counter.read()) - } - - /// Get the reveal request from the PVM state - pub fn get_reveal_request(&self) -> Vec - where - M: state_backend::ManagerRead, - { - self.with_backend(|pvm| pvm.reveal_request()) - } - - pub fn install_boot_sector(&mut self, kernel: &[u8]) - where - M: state_backend::ManagerReadWrite, - { - self.with_backend_mut(|pvm| { - let program = Program::from_elf(kernel).unwrap(); - pvm.setup_linux_process(&program).unwrap() - }) - } - - pub fn compute_step(&mut self, pvm_hooks: &mut PvmHooks) - where - M: state_backend::ManagerReadWrite, - { - self.with_backend_mut(|pvm| pvm.eval_one(pvm_hooks)) - } - - pub fn compute_step_many(&mut self, pvm_hooks: &mut PvmHooks, max_steps: usize) -> i64 - where - M: state_backend::ManagerReadWrite, - { - self.with_backend_mut(|pvm| pvm.eval_max(pvm_hooks, Bound::Included(max_steps))) as i64 - } - - pub fn set_input(&mut self, input: PvmInput) -> bool - where - M: state_backend::ManagerReadWrite, - { - self.with_backend_mut(|pvm| pvm.provide_input(input)) - } - - /// Only used by the rollup node in "loser mode" in order to test - /// refutation games. - /// - /// Corrupt the state so commitments after this point will conflict with - /// those of an honest operator. - pub fn insert_failure(&mut self) - where - M: state_backend::ManagerReadWrite, - { - self.with_backend_mut(|pvm| { - pvm.insert_failure(); - }) - } -} - -impl NodePvm { - /// Construct an empty PVM state. - pub fn empty() -> Self { - Self::new(&mut Owned) - } - - /// Compute the root hash of the PVM state. - pub fn hash(&self) -> Hash { - self.with_backend(|pvm| pvm.hash().unwrap()) - } - - /// Produce the Merkle proof corresponding to the next step of the PVM. - /// If the next step is an input request, provide the given input. - pub fn produce_proof( - &self, - input: Option, - pvm_hooks: &mut PvmHooks, - ) -> Option { - let mut proof_state = self.state.start_proof(); - - match input { - None => proof_state.eval_one(pvm_hooks), - Some(input) => { - if !proof_state.provide_input(input) { - return None; - } - } - } - - let proof = proof_state.to_proof().ok()?; - Some(proof) - } -} - -impl NodePvm { - /// Verify the proof with the given input. Upon success, return the input - /// request which corresponds to the initial state of the proof. - pub fn verify_proof( - proof: &Proof, - input: Option, - pvm_hooks: &mut PvmHooks, - ) -> Option<()> { - let proof_tree = proof.tree(); - let mut pvm = Pvm::from_proof(proof_tree, InterpretedBlockBuilder).map(|state| Self { - state: Box::new(state), - })?; - - pvm.with_backend_mut(|pvm| { - match input { - None => pvm.eval_one(pvm_hooks), - Some(input) => { - if !pvm.provide_input(input) { - return None; - } - } - }; - - let refs = pvm.struct_ref::(); - let final_hash = - NodePvmLayout::partial_state_hash(refs, ProofTree::Present(proof_tree)).ok()?; - if final_hash != proof.final_state_hash() { - return None; - } - - // TODO: RV-556: Construct and return input request upon successful verification - todo!() - }) - } -} - -impl fmt::Debug for NodePvm { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let refs = self.state.struct_ref::(); - let rendered = if f.alternate() { - serde_json::to_string_pretty(&refs) - } else { - serde_json::to_string(&refs) - } - .expect("Could not serialize PVM state"); - f.write_str(&rendered) - } -} - -impl Clone for NodePvm { - fn clone(&self) -> Self { - Self { - state: self.state.clone(), - } - } -} - -impl PartialEq for NodePvm { - fn eq(&self, other: &Self) -> bool { - self.state.struct_ref::() - == other.state.struct_ref::() - } -} - -impl Eq for NodePvm {} - -impl NewState for NodePvm { - fn new(manager: &mut M) -> Self - where - M: state_backend::ManagerAlloc, - { - Self { - state: Box::new(NodePvmState::::new(manager, InterpretedBlockBuilder)), - } - } -} - -#[derive(Error, Debug)] -pub enum PvmStorageError { - #[error("Storage error: {0}")] - StorageError(#[from] storage::StorageError), - - #[error("Serialization error: {0}")] - PvmError(#[from] PvmError), -} - -pub struct PvmStorage { - repo: Repo, -} - -impl PvmStorage { - /// Load or create new repo at `path`. - pub fn load(path: impl AsRef) -> Result { - let repo = Repo::load(path)?; - Ok(PvmStorage { repo }) - } - - pub fn close(self) { - self.repo.close() - } - - /// Create a new commit for `state` and return the commit id. - pub fn commit(&mut self, state: &NodePvm) -> Result { - Ok(state.with_backend(|pvm| { - let struct_ref = pvm.struct_ref::(); - self.repo.commit_serialised(&struct_ref) - })?) - } - - /// Checkout the PVM state committed under `id`, if the commit exists. - pub fn checkout(&self, id: &Hash) -> Result { - let allocated: AllocatedOf = self.repo.checkout_serialised(id)?; - Ok(NodePvm::bind(allocated)) - } - - /// A snapshot is a new repo to which only `id` has been committed. - pub fn export_snapshot( - &self, - id: &Hash, - path: impl AsRef, - ) -> Result<(), PvmStorageError> { - Ok(self.repo.export_snapshot(id, path)?) - } -} diff --git a/src/riscv/lib/src/pvm/reveals.rs b/src/riscv/lib/src/pvm/reveals.rs deleted file mode 100644 index df882a6efd20fdef8d205fc8e0638701c49c6b7f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/reveals.rs +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use tezos_smart_rollup_constants::riscv::REVEAL_REQUEST_MAX_SIZE; - -use crate::state::NewState; -use crate::state_backend::AllocatedOf; -use crate::state_backend::Atom; -use crate::state_backend::Cell; -use crate::state_backend::DynArray; -use crate::state_backend::DynCells; -use crate::state_backend::FnManager; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerClone; -use crate::state_backend::ManagerRead; -use crate::state_backend::Ref; - -/// Reveal request layout -pub type RevealRequestLayout = (DynArray, Atom); - -/// Request content of reveal -pub struct RevealRequest { - /// Reveal request payload - pub bytes: DynCells, - /// Size of reveal request payload - pub size: Cell, -} - -impl RevealRequest { - /// Bind the reveal request to the given allocated region. - pub fn bind(space: AllocatedOf) -> Self { - Self { - bytes: space.0, - size: space.1, - } - } - - /// Given a manager morphism `f : &M -> N`, return the reveal request layout's - /// allocated structure containing the constituents of `N` that were produced - /// from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf { - (self.bytes.struct_ref::(), self.size.struct_ref::()) - } - - pub fn to_vec(&self) -> Vec - where - M: ManagerRead, - { - use std::cmp::min; - - let size = self.size.read() as usize; - let mut buffer = vec![0u8; min(size, REVEAL_REQUEST_MAX_SIZE)]; - self.bytes.read_all(0, &mut buffer); - buffer - } -} - -impl NewState for RevealRequest { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - bytes: DynCells::new(manager), - size: Cell::new(manager), - } - } -} - -impl Clone for RevealRequest { - fn clone(&self) -> Self { - Self { - bytes: self.bytes.clone(), - size: self.size.clone(), - } - } -} diff --git a/src/riscv/lib/src/pvm/sbi.rs b/src/riscv/lib/src/pvm/sbi.rs deleted file mode 100644 index 32b0aa8819f8b679642c9ffb4a1ade4df0df2699..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/pvm/sbi.rs +++ /dev/null @@ -1,434 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -#![expect( - dead_code, - reason = "Most functions are not used with the supervising PVM" -)] - -use std::cmp::min; - -use ed25519_dalek::Signature; -use ed25519_dalek::Signer; -use ed25519_dalek::SigningKey; -use ed25519_dalek::VerifyingKey; -use tezos_smart_rollup_constants::core::MAX_INPUT_MESSAGE_SIZE; -use tezos_smart_rollup_constants::riscv::REVEAL_DATA_MAX_SIZE; -use tezos_smart_rollup_constants::riscv::REVEAL_REQUEST_MAX_SIZE; -use tezos_smart_rollup_constants::riscv::SBI_CONSOLE_PUTCHAR; -use tezos_smart_rollup_constants::riscv::SBI_DBCN; -use tezos_smart_rollup_constants::riscv::SBI_DBCN_CONSOLE_WRITE_BYTE; -use tezos_smart_rollup_constants::riscv::SBI_FIRMWARE_TEZOS; -use tezos_smart_rollup_constants::riscv::SBI_SHUTDOWN; -use tezos_smart_rollup_constants::riscv::SBI_SRST; -use tezos_smart_rollup_constants::riscv::SBI_SRST_SYSTEM_RESET; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_BLAKE2B_HASH256; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_ED25519_SIGN; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_ED25519_VERIFY; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_INBOX_NEXT; -use tezos_smart_rollup_constants::riscv::SBI_TEZOS_REVEAL; -use tezos_smart_rollup_constants::riscv::SbiError; - -use super::PvmHooks; -use super::PvmStatus; -use super::reveals::RevealRequest; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::XRegisters; -use crate::machine_state::registers::XValue; -use crate::machine_state::registers::a0; -use crate::machine_state::registers::a1; -use crate::machine_state::registers::a2; -use crate::machine_state::registers::a3; -use crate::machine_state::registers::a6; -use crate::machine_state::registers::a7; -use crate::parser::instruction::InstrCacheable; -use crate::state_backend::CellRead; -use crate::state_backend::CellReadWrite; -use crate::state_backend::CellWrite; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ManagerWrite; - -/// Write the SBI error code as the return value. -#[inline] -fn sbi_return_error(xregisters: &mut XRegisters, code: SbiError) { - xregisters.write(a0, code as i64 as u64); -} - -/// Write an arbitrary value as single return value. -#[inline] -fn sbi_return1(xregisters: &mut XRegisters, value: XValue) { - // The SBI caller interprets the return value as a [i64]. We don't want the value to be - // interpreted as negative because that indicates an error. - if (value as i64) < 0 { - return sbi_return_error(xregisters, SbiError::Failed); - } - - xregisters.write(a0, value); -} - -/// Write an `sbiret` return struct. -#[inline] -fn sbi_return_sbiret( - xregisters: &mut XRegisters, - error: Option, - value: XValue, -) { - xregisters.write(a0, error.map(|err| err as i64 as XValue).unwrap_or(0)); - xregisters.write(a1, value); -} - -/// Run the given closure `inner` and write the corresponding SBI results to `machine`. -#[inline] -fn sbi_wrap(machine: &mut MachineCoreState, inner: F) -where - MC: MemoryConfig, - M: ManagerWrite, - F: FnOnce(&mut MachineCoreState) -> Result, -{ - match inner(machine) { - Ok(value) => sbi_return1(&mut machine.hart.xregisters, value), - Err(error) => sbi_return_error(&mut machine.hart.xregisters, error), - } -} - -/// Provide input information to the machine. Returns `false` in case the -/// machine wasn't expecting any input, otherwise returns `true`. -pub fn provide_input( - status: &mut S, - machine: &mut MachineCoreState, - level: u32, - counter: u32, - payload: &[u8], -) -> bool -where - S: CellReadWrite, - MC: MemoryConfig, - M: ManagerReadWrite, -{ - // This method should only do something when we're waiting for input. - match status.read() { - PvmStatus::WaitingForInput => {} - _ => return false, - } - - // We're evaluating again after this. - status.write(PvmStatus::Evaluating); - - sbi_wrap(machine, |machine| { - // These arguments should have been set by the previous SBI call. - let arg_buffer_addr = machine.hart.xregisters.read(a0); - let arg_buffer_size = machine.hart.xregisters.read(a1); - let arg_level_addr = machine.hart.xregisters.read(a2); - let arg_counter_addr = machine.hart.xregisters.read(a3); - - // The SBI caller expects the payload to be returned at [phys_dest_addr] - // with at maximum [max_buffer_size] bytes written. - let max_buffer_size = payload.len().min(arg_buffer_size as usize).min( - // If we were to allow more data to be passed, we could run into problems with proof - // sizes for inputs. - MAX_INPUT_MESSAGE_SIZE, - ); - - machine - .main_memory - .write_all(arg_buffer_addr, &payload[..max_buffer_size])?; - machine.main_memory.write(arg_level_addr, level)?; - machine.main_memory.write(arg_counter_addr, counter)?; - - // At the moment, this case is unlikely to occur because we cap [max_buffer_size] at - // [MAX_INPUT_MESSAGE_SIZE]. - Ok(max_buffer_size as u64) - }); - - true -} - -/// Provide reveal data in response to a reveal request. Returns `false` -/// if the machine is not expecting reveal. -pub fn provide_reveal_response( - status: &mut S, - machine: &mut MachineCoreState, - reveal_data: &[u8], -) -> bool -where - S: CellReadWrite, - MC: MemoryConfig, - M: ManagerReadWrite, -{ - // This method should only do something when we're waiting for reveal. - if status.read() != PvmStatus::WaitingForReveal { - return false; - } - - // We're evaluating again after this. - status.write(PvmStatus::Evaluating); - - sbi_wrap(machine, |machine| { - // These arguments should have been set by the previous SBI call. - let arg_buffer_addr = machine.hart.xregisters.read(a2); - let arg_buffer_size = machine.hart.xregisters.read(a3); - - let memory_write_size = min( - REVEAL_DATA_MAX_SIZE, - min(arg_buffer_size as usize, reveal_data.len()), - ); - - machine - .main_memory - .write_all(arg_buffer_addr, &reveal_data[..memory_write_size])?; - - Ok(memory_write_size as u64) - }); - - true -} - -/// Handle a [SBI_TEZOS_INBOX_NEXT] call. -#[inline] -fn handle_tezos_inbox_next(status: &mut S) -where - S: CellWrite, -{ - // Prepare the EE state for an input tick. - status.write(PvmStatus::WaitingForInput); -} - -/// Produce a Ed25519 signature. -#[inline] -fn handle_tezos_ed25519_sign(machine: &mut MachineCoreState) -> Result -where - MC: MemoryConfig, - M: ManagerReadWrite, -{ - let arg_sk_addr = machine.hart.xregisters.read(a0); - let arg_msg_addr = machine.hart.xregisters.read(a1); - let arg_msg_len = machine.hart.xregisters.read(a2); - let arg_sig_addr = machine.hart.xregisters.read(a3); - - let mut sk_bytes = [0u8; 32]; - machine.main_memory.read_all(arg_sk_addr, &mut sk_bytes)?; - let sk = SigningKey::try_from(sk_bytes.as_slice()).map_err(|_| SbiError::Failed)?; - sk_bytes.fill(0); - - let mut msg_bytes = vec![0; arg_msg_len as usize]; - machine.main_memory.read_all(arg_msg_addr, &mut msg_bytes)?; - - let sig = sk.sign(msg_bytes.as_slice()); - let sig_bytes: [u8; 64] = sig.to_bytes(); - machine.main_memory.write_all(arg_sig_addr, &sig_bytes)?; - - Ok(sig_bytes.len() as u64) -} - -/// Verify a Ed25519 signature. -#[inline] -fn handle_tezos_ed25519_verify( - machine: &mut MachineCoreState, -) -> Result -where - MC: MemoryConfig, - M: ManagerReadWrite, -{ - let arg_pk_addr = machine.hart.xregisters.read(a0); - let arg_sig_addr = machine.hart.xregisters.read(a1); - let arg_msg_addr = machine.hart.xregisters.read(a2); - let arg_msg_len = machine.hart.xregisters.read(a3); - - let mut pk_bytes = [0u8; 32]; - machine.main_memory.read_all(arg_pk_addr, &mut pk_bytes)?; - - let mut sig_bytes = [0u8; 64]; - machine.main_memory.read_all(arg_sig_addr, &mut sig_bytes)?; - - let mut msg_bytes = vec![0u8; arg_msg_len as usize]; - machine.main_memory.read_all(arg_msg_addr, &mut msg_bytes)?; - - let pk = VerifyingKey::try_from(pk_bytes.as_slice()).map_err(|_| SbiError::Failed)?; - let sig = Signature::from_slice(sig_bytes.as_slice()).map_err(|_| SbiError::Failed)?; - let valid = pk.verify_strict(msg_bytes.as_slice(), &sig).is_ok(); - - Ok(valid as u64) -} - -/// Compute a BLAKE2B 256-bit digest. -#[inline] -fn handle_tezos_blake2b_hash256( - machine: &mut MachineCoreState, -) -> Result -where - MC: MemoryConfig, - M: ManagerReadWrite, -{ - let arg_out_addr = machine.hart.xregisters.read(a0); - let arg_msg_addr = machine.hart.xregisters.read(a1); - let arg_msg_len = machine.hart.xregisters.read(a2); - - let mut msg_bytes = vec![0u8; arg_msg_len as usize]; - machine.main_memory.read_all(arg_msg_addr, &mut msg_bytes)?; - - let hash = tezos_crypto_rs::blake2b::digest_256(msg_bytes.as_slice()); - machine - .main_memory - .write_all(arg_out_addr, hash.as_slice())?; - - Ok(hash.len() as u64) -} - -/// Handle a [SBI_TEZOS_REVEAL] call. -#[inline] -fn handle_tezos_reveal( - machine: &mut MachineCoreState, - reveal_request: &mut RevealRequest, - status: &mut S, -) where - S: CellReadWrite, - MC: MemoryConfig, - M: ManagerReadWrite, -{ - let request_address = machine.hart.xregisters.read(a0); - let request_size = machine.hart.xregisters.read(a1); - - let mut buffer = vec![0u8; min(request_size as usize, REVEAL_REQUEST_MAX_SIZE)]; - - if machine - .main_memory - .read_all(request_address, &mut buffer) - .is_err() - { - return sbi_return_error(&mut machine.hart.xregisters, SbiError::InvalidAddress); - } - - // TODO: RV-425 Cross-page memory accesses are not translated correctly - reveal_request.bytes.write_all(0, &buffer); - reveal_request.size.write(request_size); - status.write(PvmStatus::WaitingForReveal); -} - -/// Handle a [SBI_SHUTDOWN] call. -#[inline(always)] -fn handle_legacy_shutdown(xregisters: &mut XRegisters) -where - M: ManagerWrite, -{ - // This call always fails. - handle_not_supported(xregisters); -} - -/// Handle a [SBI_CONSOLE_PUTCHAR] call. -#[inline(always)] -fn handle_legacy_console_putchar(xregisters: &mut XRegisters, hooks: &mut PvmHooks) -where - M: ManagerReadWrite, -{ - let char = xregisters.read(a0) as u8; - (hooks.putchar_hook)(char); - - // This call always succeeds. - sbi_return1(xregisters, 0); -} - -/// Handle a [SBI_DBCN_CONSOLE_WRITE_BYTE] call. -#[inline(always)] -fn handle_debug_console_write_byte(xregisters: &mut XRegisters, hooks: &mut PvmHooks) -where - M: ManagerReadWrite, -{ - let char = xregisters.read(a0) as u8; - (hooks.putchar_hook)(char); - - // This call always succeeds. - sbi_return_sbiret(xregisters, None, 0); -} - -/// Handle a [SBI_SRST_SYSTEM_RESET] call. -#[inline(always)] -fn handle_system_reset(xregisters: &mut XRegisters) -where - M: ManagerWrite, -{ - sbi_return_sbiret(xregisters, Some(SbiError::NotSupported), 0); -} - -/// Handle unsupported SBI calls. -#[inline(always)] -fn handle_not_supported(xregisters: &mut XRegisters) -where - M: ManagerWrite, -{ - // SBI requires us to indicate that we don't support this function by returning - // `ERR_NOT_SUPPORTED`. - sbi_return_error(xregisters, SbiError::NotSupported); -} - -/// Handle a PVM SBI call. Returns `true` if it makes sense to continue evaluation. -#[inline] -pub fn handle_call( - status: &mut S, - reveal_request: &mut RevealRequest, - machine: &mut MachineCoreState, - hooks: &mut PvmHooks, -) -> bool -where - S: CellReadWrite, - MC: MemoryConfig, - M: ManagerReadWrite, -{ - // No matter the outcome, we need to bump the - // program counter because ECALL's don't update it - // to the following instructions. - let pc = machine.hart.pc.read() + InstrCacheable::Ecall.width() as u64; - machine.hart.pc.write(pc); - - // SBI extension is contained in a7. - let sbi_extension = machine.hart.xregisters.read(a7); - match sbi_extension { - SBI_CONSOLE_PUTCHAR => handle_legacy_console_putchar(&mut machine.hart.xregisters, hooks), - SBI_SHUTDOWN => handle_legacy_shutdown(&mut machine.hart.xregisters), - SBI_DBCN => { - let sbi_function = machine.hart.xregisters.read(a6); - match sbi_function { - SBI_DBCN_CONSOLE_WRITE_BYTE => { - handle_debug_console_write_byte(&mut machine.hart.xregisters, hooks) - } - _ => handle_not_supported(&mut machine.hart.xregisters), - } - } - SBI_SRST => { - let sbi_function = machine.hart.xregisters.read(a6); - match sbi_function { - SBI_SRST_SYSTEM_RESET => handle_system_reset(&mut machine.hart.xregisters), - _ => handle_not_supported(&mut machine.hart.xregisters), - } - } - SBI_FIRMWARE_TEZOS => { - handle_tezos(machine, status, reveal_request); - } - _ => handle_not_supported(&mut machine.hart.xregisters), - } - - status.read() == PvmStatus::Evaluating -} - -/// Handle a Tezos SBI call. -pub(super) fn handle_tezos( - machine: &mut MachineCoreState, - status: &mut S, - reveal_request: &mut RevealRequest, -) where - S: CellReadWrite, - MC: MemoryConfig, - M: ManagerReadWrite, -{ - let sbi_function = machine.hart.xregisters.read(a6); - match sbi_function { - SBI_TEZOS_INBOX_NEXT => handle_tezos_inbox_next(status), - SBI_TEZOS_ED25519_SIGN => sbi_wrap(machine, handle_tezos_ed25519_sign), - SBI_TEZOS_ED25519_VERIFY => sbi_wrap(machine, handle_tezos_ed25519_verify), - SBI_TEZOS_BLAKE2B_HASH256 => sbi_wrap(machine, handle_tezos_blake2b_hash256), - SBI_TEZOS_REVEAL => handle_tezos_reveal(machine, reveal_request, status), - _ => handle_not_supported(&mut machine.hart.xregisters), - } -} diff --git a/src/riscv/lib/src/range_utils.rs b/src/riscv/lib/src/range_utils.rs deleted file mode 100644 index 15e0960f7b4f2eba4f3f636cda7789a83d20f4fd..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/range_utils.rs +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::ops::Bound; - -/// Shift the bound towards zero by the given amount without wrapping. -/// Unbounded limits are unchanged. -#[inline(always)] -pub fn bound_saturating_sub(bound: Bound, shift: usize) -> Bound { - match bound { - Bound::Included(x) => Bound::Included(x.saturating_sub(shift)), - Bound::Excluded(x) => Bound::Excluded(x.saturating_sub(shift)), - Bound::Unbounded => Bound::Unbounded, - } -} - -/// Is `num` below the bound? -#[inline(always)] -pub fn less_than_bound(num: usize, bound: Bound) -> bool { - match bound { - Bound::Included(x) => num < x, - Bound::Excluded(x) => num < x.saturating_sub(1), - Bound::Unbounded => true, - } -} - -/// Turn a bound into a fixed number. -/// -/// `Unbounded` is replaced by `usize::MAX`. When using this function additional -/// logic should be supplied to recheck the original bound. -#[inline(always)] -pub fn unwrap_bound(bound: Bound) -> usize { - match bound { - Bound::Included(x) => x, - Bound::Excluded(x) => x.saturating_sub(1), - Bound::Unbounded => usize::MAX, - } -} diff --git a/src/riscv/lib/src/state.rs b/src/riscv/lib/src/state.rs deleted file mode 100644 index 0548aca07278edb27f54136326ced7449c95bc4e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state.rs +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::array; - -use crate::array_utils::boxed_from_fn; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; - -/// Methods for creating a new state without additional information -pub trait NewState { - /// Create a new state. - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc; -} - -impl, const LEN: usize, M: ManagerBase> NewState for [T; LEN] { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - array::from_fn(|_| T::new(manager)) - } -} - -// We cannot compose the implementations of `NewState` for `[T; LEN]` and `Box<_>`. Doing so would -// allocate potentially large arrays on the stack and only then move them to the heap using `Box`. -// This results in potential stack overflow for large arrays. -// -// To avoid this, we implement a combined version `Box<[T; LEN]>` that does not allocate on the -// stack. This makes usage of `NewState::new` safe everywhere as it doesn't sneakily compose -// implementations that would result in stack overflows. -// -// This comes with a small trade-off, we loose out on the `Box<_>` implementation of `NewState`. -// However, this is not a problem since we can always use `Box::new` independently. -impl, const LEN: usize, M: ManagerBase> NewState for Box<[T; LEN]> { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - boxed_from_fn(|| T::new(manager)) - } -} diff --git a/src/riscv/lib/src/state_backend.rs b/src/riscv/lib/src/state_backend.rs deleted file mode 100644 index ae1346c7d7abd412849c66276eea66da1c15306f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend.rs +++ /dev/null @@ -1,526 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Generic state backends -//! -//! # Layouts -//! -//! [Layouts] are structural descriptions of state types. We use types to -//! describe [Layouts] as statically as possible. This shall help us in writing -//! tests that ensure we don't accidentally change the layout of the machine -//! state. -//! -//! ## Example -//! -//! Consider the following state. -//! -//! ``` -//! struct MyState { -//! foo: u64, -//! bar: [u8; 1024], -//! qux: u16, -//! } -//! ``` -//! -//! We can describe the layout of this state using the following type. -//! -//! ``` -//! use octez_riscv::state_backend::{Atom, Array}; -//! -//! type MyStateLayout = ( -//! Atom, -//! Array, -//! Atom, -//! ); -//! ``` -//! -//! # Managers -//! -//! Different backends have different capabilities and they are described as `Manager`. -//! Some of these capabilities are: -//! - [ManagerBase] -//! - [ManagerAlloc] -//! - [ManagerRead] -//! - [ManagerWrite] -//! - [ManagerReadWrite] -//! -//! # Backends -//! -//! Backends are ZST implementing these traits. -//! The main difference between them is the top-level functionality it provides -//! and management of the underlying state memory. -//! -//! These backends can be: -//! -//! - [Owned] -//! Backend which has the full state allocated in memory. It can execute one step -//! or multiple steps at a time faster. -//! - [Verifier] -//! Backend capable of partially allocating a state and verify a given proof. -//! Needs to be light on memory usage since it runs in the protocol. -//! - [ProofGen] -//! Backend capable of generating a proof for running one step. -//! - [Ref] -//! Helper backend to wrap another backend through a reference to it. -//! -//! [Layouts]: layout::Layout -//! [Owned]: owned_backend::Owned -//! [Verifier]: verify_backend::Verifier -//! [ProofGen]: proof_backend::ProofGen - -mod commitment_layout; -mod effects; -mod elems; -pub mod hash; -mod layout; -pub mod owned_backend; -pub mod proof_backend; -mod proof_layout; -mod region; -mod trans; -pub mod verify_backend; - -pub use commitment_layout::*; -pub use effects::*; -pub use elems::*; -pub use layout::*; -pub use proof_layout::*; -pub use region::*; -pub use trans::*; - -/// An enriched value may be stored in a [`ManagerBase::EnrichedCell`]. -/// -/// This allows a value to have an additional, derived, value attached - that may be expensive -/// to derive lazily. -/// -/// This derived value does not form part of any stored state/commitments. -pub trait EnrichedValue { - /// Type of the stored value which we want to enrich - type E: 'static; - - /// Type of the derived value that enriches the stored value - type D; -} - -/// Specifies that there exists a path to derive `V::D` from `&V::E` -pub trait EnrichedValueLinked: EnrichedValue { - /// Construct the derived value from the stored value, maps to - /// the `From` trait by default. - fn derive(v: &Self::E) -> Self::D; -} - -impl EnrichedValueLinked for Value -where - Value: EnrichedValue, - Value::D: for<'a> From<&'a Value::E>, -{ - fn derive(v: &Self::E) -> Self::D { - v.into() - } -} - -/// Manager of the state backend storage -pub trait ManagerBase: Sized { - /// Region that has been allocated in the state storage - type Region; - - /// Dynamic region represents a fixed-sized byte vector that has been allocated in the state storage - type DynRegion; - - /// An [enriched] value may have a derived value attached. - /// - /// [enriched]: EnrichedValue - type EnrichedCell; - - /// The root manager may either be itself, or occassionally the manager that this manager - /// wraps. - /// - /// For example, the [`Ref`] backend is often use to wrap the [`Owned`] backend to gain access - /// to its regions. In this case, the root manager would be the owned backend. - /// - /// [`Owned`]: owned_backend::Owned - type ManagerRoot: ManagerBase; - - /// Upgrade a single-element region to an enriched cell. - fn enrich_cell(cell: Self::Region) -> Self::EnrichedCell; - - /// Obtain a reference to the underlying region of an enriched cell. - fn as_devalued_cell(cell: &Self::EnrichedCell) -> &Self::Region; -} - -/// Manager with allocation capabilities -/// -/// Any `ManagerAlloc` inherently has read & write capabilities, -/// since the manager creates the values on the first allocation. -pub trait ManagerAlloc: 'static + ManagerReadWrite { - /// Allocate a region in the state storage. - fn allocate_region( - &mut self, - init_value: [E; LEN], - ) -> Self::Region; - - /// Allocate a dynamic region in the state storage. - fn allocate_dyn_region(&mut self) -> Self::DynRegion; -} - -/// Manager with read capabilities -pub trait ManagerRead: ManagerBase { - /// Read an element in the region. - fn region_read(region: &Self::Region, index: usize) -> E; - - /// Obtain a reference to an element in the region. - fn region_ref(region: &Self::Region, index: usize) -> &E; - - /// Read all elements in the region. - fn region_read_all(region: &Self::Region) -> Vec; - - /// Read an element in the region. `address` is in bytes. - fn dyn_region_read( - region: &Self::DynRegion, - address: usize, - ) -> E; - - /// Read elements from the region. `address` is in bytes. - fn dyn_region_read_all( - region: &Self::DynRegion, - address: usize, - values: &mut [E], - ); - - /// Read the value contained in the enriched cell. - fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E - where - V: EnrichedValue, - V::E: Copy; - - /// Read the derived value of the enriched cell. - fn enriched_cell_read_derived(cell: &Self::EnrichedCell) -> V::D - where - V: EnrichedValueLinked, - V::D: Copy; - - /// Obtain a reference to the value contained in the enriched cell. - fn enriched_cell_ref_stored(cell: &Self::EnrichedCell) -> &V::E - where - V: EnrichedValue; -} - -/// Manager with write capabilities -pub trait ManagerWrite: ManagerBase { - /// Update an element in the region. - fn region_write( - region: &mut Self::Region, - index: usize, - value: E, - ); - - /// Update all elements in the region. - fn region_write_all(region: &mut Self::Region, value: &[E]); - - /// Update an element in the region. `address` is in bytes. - fn dyn_region_write( - region: &mut Self::DynRegion, - address: usize, - value: E, - ); - - /// Update multiple elements in the region. `address` is in bytes. - fn dyn_region_write_all( - region: &mut Self::DynRegion, - address: usize, - values: &[E], - ); - - /// Update the value contained in an enriched cell. The derived value will be recalculated. - fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) - where - V: EnrichedValueLinked; -} - -/// Manager with capabilities that require both read and write -pub trait ManagerReadWrite: ManagerRead + ManagerWrite { - /// Update the element in the region and return the previous value. - fn region_replace( - region: &mut Self::Region, - index: usize, - value: E, - ) -> E; -} - -/// Manager with the ability to serialise regions -pub trait ManagerSerialise: ManagerRead { - /// Serialise the contents of the region. - fn serialise_region( - region: &Self::Region, - serializer: S, - ) -> Result; - - /// Serialise the contents of the dynamic region. - fn serialise_dyn_region( - region: &Self::DynRegion, - serializer: S, - ) -> Result; -} - -/// Manager with the ability to deserialise regions -pub trait ManagerDeserialise: ManagerBase { - /// Deserialise a region. - fn deserialise_region< - 'de, - E: serde::Deserialize<'de>, - const LEN: usize, - D: serde::Deserializer<'de>, - >( - deserializer: D, - ) -> Result, D::Error>; - - /// Deserialise the dyanmic region. - fn deserialise_dyn_region<'de, const LEN: usize, D: serde::Deserializer<'de>>( - deserializer: D, - ) -> Result, D::Error>; -} - -/// Manager with the ability to clone regions -pub trait ManagerClone: ManagerBase { - /// Clone the region. - fn clone_region( - region: &Self::Region, - ) -> Self::Region; - - /// Clone the dynamic region. - fn clone_dyn_region(region: &Self::DynRegion) -> Self::DynRegion; - - /// Clone the enriched cell. - fn clone_enriched_cell(cell: &Self::EnrichedCell) -> Self::EnrichedCell - where - V: EnrichedValue, - V::E: Clone, - V::D: Clone; -} - -/// Manager wrapper around `M` whose regions are immutable references to regions of `M` -pub struct Ref<'backend, M>(std::marker::PhantomData); - -impl<'backend, M: ManagerBase> ManagerBase for Ref<'backend, M> { - type Region = &'backend M::Region; - - type DynRegion = &'backend M::DynRegion; - - type EnrichedCell = &'backend M::Region; - - type ManagerRoot = M::ManagerRoot; - - fn enrich_cell(cell: Self::Region) -> Self::EnrichedCell { - cell - } - - fn as_devalued_cell(cell: &Self::EnrichedCell) -> &Self::Region { - cell - } -} - -impl ManagerSerialise for Ref<'_, M> { - fn serialise_region( - region: &Self::Region, - serializer: S, - ) -> Result { - M::serialise_region(region, serializer) - } - - fn serialise_dyn_region( - region: &Self::DynRegion, - serializer: S, - ) -> Result { - M::serialise_dyn_region(region, serializer) - } -} - -impl ManagerRead for Ref<'_, M> { - fn region_read(region: &Self::Region, index: usize) -> E { - M::region_read(region, index) - } - - fn region_ref(region: &Self::Region, index: usize) -> &E { - M::region_ref(region, index) - } - - fn region_read_all(region: &Self::Region) -> Vec { - M::region_read_all(region) - } - - fn dyn_region_read( - region: &Self::DynRegion, - address: usize, - ) -> E { - M::dyn_region_read(region, address) - } - - fn dyn_region_read_all( - region: &Self::DynRegion, - address: usize, - values: &mut [E], - ) { - M::dyn_region_read_all(region, address, values) - } - - fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E - where - V: EnrichedValue, - V::E: Copy, - { - M::region_read(cell, 0) - } - - fn enriched_cell_ref_stored(cell: &Self::EnrichedCell) -> &V::E - where - V: EnrichedValue, - { - M::region_ref(cell, 0) - } - - fn enriched_cell_read_derived(cell: &Self::EnrichedCell) -> V::D - where - V: EnrichedValueLinked, - V::D: Copy, - { - V::derive(M::region_ref(cell, 0)) - } -} - -/// Alias for the allocated structure with references to regions of -/// the [Owned] backend -/// -/// [Owned]: owned_backend::Owned -pub type RefOwnedAlloc<'a, L> = AllocatedOf>; - -/// Alias for the allocated structure with references to a proof-generating backend -pub type RefProofGenOwnedAlloc<'a, 'b, L> = - AllocatedOf>>>; - -/// Alias for the allocated structure with references to regions of -/// the [Verifier] backend -/// -/// [Verifier]: verify_backend::Verifier -pub type RefVerifierAlloc<'a, L> = AllocatedOf>; - -#[cfg(test)] -pub(crate) mod test_helpers { - use super::AllocatedOf; - use super::Layout; - use super::ManagerAlloc; - use super::ManagerClone; - use super::ManagerDeserialise; - use super::ManagerReadWrite; - use super::ManagerSerialise; - use crate::jit::state_access::JitStateAccess; - - /// Generate a test against all test backends. - #[macro_export] - macro_rules! backend_test { - ( $(#[$m:meta])* $name:ident, $fac_name:ident, $expr:block ) => { - $(#[$m])* - #[test] - fn $name() { - fn inner<$fac_name: $crate::state_backend::test_helpers::TestBackendFactory>() { - $expr - } - - inner::<$crate::state_backend::owned_backend::test_helpers::OwnedTestBackendFactory>(); - } - }; - } - - /// This lets you construct backends for any layout. - pub trait TestBackendFactory { - /// Manager used in testing - type Manager: ManagerReadWrite - + ManagerSerialise - + ManagerDeserialise - + ManagerClone - + ManagerAlloc - + JitStateAccess; - - /// Construct a manager. - fn manager() -> Self::Manager; - } - - /// Copy the allocated space by serialising and deserialising it. - pub fn copy_via_serde(refs: &AllocatedOf) -> AllocatedOf - where - L: Layout, - N: ManagerSerialise, - AllocatedOf: serde::Serialize, - M: ManagerDeserialise, - AllocatedOf: serde::de::DeserializeOwned, - { - let data = crate::storage::binary::serialise(refs).unwrap(); - crate::storage::binary::deserialise(&data).unwrap() - } - - /// Assert that two values are different. If they differ, offer a command to run that shows the - /// structural differences between the values. - pub fn assert_eq_struct(lhs: &T, rhs: &T) - where - T: serde::Serialize + PartialEq, - { - if lhs != rhs { - let (file_lhs, path_lhs) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); - serde_json::to_writer_pretty(file_lhs, lhs).unwrap(); - eprintln!("Lhs is located at {}", path_lhs.display()); - - let (file_rhs, path_rhs) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); - serde_json::to_writer_pretty(file_rhs, rhs).unwrap(); - eprintln!("Rhs is located at {}", path_rhs.display()); - - eprintln!("Run the following to diff them:"); - eprintln!("jd {} {}", path_lhs.display(), path_rhs.display()); - - panic!("Assertion failed: values are different"); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::backend_test; - use crate::state::NewState; - - backend_test!(test_example, F, { - struct Example { - first: Cell, - second: Cells, - } - - let first_value: u64 = rand::random(); - let second_value: [u32; 4] = rand::random(); - - let mut manager = F::manager(); - let mut instance = Example { - first: Cell::new(&mut manager), - second: Cells::new(&mut manager), - }; - - instance.first.write(first_value); - assert_eq!(instance.first.read(), first_value); - - instance.second.write_all(&second_value); - assert_eq!(instance.second.read_all(), second_value); - - let first_value_read = u64::from_le_bytes( - bincode::serialize(&instance.first.struct_ref::()) - .unwrap() - .try_into() - .unwrap(), - ); - assert_eq!(first_value_read, first_value); - - let second_value_read = unsafe { - let data = bincode::serialize(&instance.second.struct_ref::()).unwrap(); - data.as_ptr().cast::<[u32; 4]>().read().map(u32::from_le) - }; - assert_eq!(second_value_read, second_value); - }); -} diff --git a/src/riscv/lib/src/state_backend/commitment_layout.rs b/src/riscv/lib/src/state_backend/commitment_layout.rs deleted file mode 100644 index 974dc6b7def9f12784ffcbac4413b177c8c2c29a..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/commitment_layout.rs +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use super::AllocatedOf; -use super::Array; -use super::Atom; -use super::DynArray; -use super::Layout; -use super::ManagerSerialise; -use super::Many; -use super::hash; -use super::hash::Hash; -use super::hash::HashError; -use super::hash::HashWriter; -use super::proof_backend::merkle::MERKLE_ARITY; -use super::proof_backend::merkle::MERKLE_LEAF_SIZE; -use super::proof_backend::merkle::chunks_to_writer; -use crate::state_backend::hash::build_custom_merkle_hash; - -/// [`Layouts`] which may be used for commitments -/// -/// [`Layouts`]: crate::state_backend::Layout -pub trait CommitmentLayout: Layout { - /// Compute the root hash of the given state - fn state_hash(state: AllocatedOf) -> Result; -} - -impl CommitmentLayout for Box { - fn state_hash(state: AllocatedOf) -> Result { - T::state_hash(*state) - } -} - -impl CommitmentLayout for Atom -where - T: serde::Serialize + 'static, -{ - fn state_hash(state: AllocatedOf) -> Result { - Hash::blake2b_hash(state) - } -} - -impl CommitmentLayout for Array -where - T: serde::Serialize + Copy + 'static, -{ - fn state_hash(state: AllocatedOf) -> Result { - Hash::blake2b_hash(state) - } -} - -impl CommitmentLayout for DynArray { - fn state_hash(state: AllocatedOf) -> Result { - let mut writer = HashWriter::new(MERKLE_LEAF_SIZE); - chunks_to_writer::(&mut writer, |address| { - state.read::<[u8; MERKLE_LEAF_SIZE.get()]>(address) - })?; - let hashes = writer.finalise()?; - hash::build_custom_merkle_hash(MERKLE_ARITY, hashes) - } -} - -impl CommitmentLayout for (A, B) -where - A: CommitmentLayout, - B: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes = [A::state_hash(state.0)?, B::state_hash(state.1)?]; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for (A, B, C) -where - A: CommitmentLayout, - B: CommitmentLayout, - C: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes = [ - A::state_hash(state.0)?, - B::state_hash(state.1)?, - C::state_hash(state.2)?, - ]; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for (A, B, C, D) -where - A: CommitmentLayout, - B: CommitmentLayout, - C: CommitmentLayout, - D: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes = [ - A::state_hash(state.0)?, - B::state_hash(state.1)?, - C::state_hash(state.2)?, - D::state_hash(state.3)?, - ]; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for (A, B, C, D, E) -where - A: CommitmentLayout, - B: CommitmentLayout, - C: CommitmentLayout, - D: CommitmentLayout, - E: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes = [ - A::state_hash(state.0)?, - B::state_hash(state.1)?, - C::state_hash(state.2)?, - D::state_hash(state.3)?, - E::state_hash(state.4)?, - ]; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for (A, B, C, D, E, F) -where - A: CommitmentLayout, - B: CommitmentLayout, - C: CommitmentLayout, - D: CommitmentLayout, - E: CommitmentLayout, - F: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes = [ - A::state_hash(state.0)?, - B::state_hash(state.1)?, - C::state_hash(state.2)?, - D::state_hash(state.3)?, - E::state_hash(state.4)?, - F::state_hash(state.5)?, - ]; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for [T; LEN] -where - T: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let hashes: Vec = state - .into_iter() - .map(T::state_hash) - .collect::, _>>()?; - Hash::combine(&hashes) - } -} - -impl CommitmentLayout for Many -where - T: CommitmentLayout, -{ - fn state_hash(state: AllocatedOf) -> Result { - let nodes: Vec = state - .into_iter() - .map(T::state_hash) - .collect::, _>>()?; - build_custom_merkle_hash(MERKLE_ARITY, nodes) - } -} diff --git a/src/riscv/lib/src/state_backend/effects.rs b/src/riscv/lib/src/state_backend/effects.rs deleted file mode 100644 index db6ff96b9390afcfc4a74c0a5d82fe9c53b61961..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/effects.rs +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::marker::PhantomData; - -use super::AllocatedOf; -use super::Atom; -use super::Cell; -use super::FnManager; -use super::ManagerAlloc; -use super::ManagerBase; -use super::ManagerClone; -use super::ManagerRead; -use super::ManagerReadWrite; -use super::ManagerWrite; -use super::Ref; -use crate::default::ConstDefault; -use crate::state::NewState; - -/// XXX: Workaround trait for not having enum variants as const-generics -pub trait EffectGetter { - /// Effect type returned - type Effect; - - /// Effect on write/replace operations. - const EFFECT: Option; -} - -pub type EffectCellLayout = Atom; - -pub struct EffectCell { - inner: Cell, - _pd: PhantomData, -} - -impl EffectCell { - pub fn bind(space: AllocatedOf, M>) -> Self { - Self { - inner: space, - _pd: PhantomData, - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>( - &'a self, - ) -> AllocatedOf, F::Output> { - self.inner.struct_ref::() - } -} - -impl EffectCell { - #[inline(always)] - pub fn read(&self) -> T - where - M: ManagerRead, - { - self.inner.read() - } - - #[inline(always)] - #[must_use = "CSR Effect must be handled. Use 'handle_csr_effect()'"] - pub fn write(&mut self, value: T) -> Option - where - M: ManagerWrite, - { - self.inner.write(value); - EG::EFFECT - } - - #[inline(always)] - #[must_use = "CSR Effect must be handled. Use 'handle_csr_effect()'"] - pub fn replace(&mut self, value: T) -> (T, Option) - where - M: ManagerReadWrite, - { - (self.inner.replace(value), EG::EFFECT) - } -} - -impl NewState for EffectCell { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self { - inner: Cell::new(manager), - _pd: PhantomData, - } - } -} - -impl Clone for EffectCell { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - _pd: PhantomData, - } - } -} diff --git a/src/riscv/lib/src/state_backend/elems.rs b/src/riscv/lib/src/state_backend/elems.rs deleted file mode 100644 index ff0998ff885c9ec3751b4a1288784f702721be6e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/elems.rs +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 TriliTech -// -// SPDX-License-Identifier: MIT - -/// Types that can be copied and contain no non-static references -pub trait StaticCopy: Copy + 'static {} - -impl StaticCopy for T {} - -/// Elements that may be stored using a Backend - i.e. implementors of [super::ManagerBase] -pub trait Elem: StaticCopy { - /// Copy from `source` and convert to stored representation. - fn store(&mut self, source: &Self); - - /// Convert to stored representation in place. - fn to_stored_in_place(&mut self); - - /// Convert from stored representation in place. - // The naming of this function trips Clippy. - #[expect( - clippy::wrong_self_convention, - reason = "It's an appropriate name when ignoring Rust's from/to naming convention" - )] - fn from_stored_in_place(&mut self); - - /// Read a value from its stored representation. - fn from_stored(source: &Self) -> Self; -} - -macro_rules! impl_elem_prim { - ( $x:ty ) => { - impl Elem for $x { - #[inline(always)] - fn store(&mut self, source: &Self) { - *self = source.to_le(); - } - - #[inline(always)] - fn to_stored_in_place(&mut self) { - *self = self.to_le(); - } - - #[inline(always)] - fn from_stored_in_place(&mut self) { - *self = Self::from_le(*self); - } - - #[inline(always)] - fn from_stored(source: &Self) -> Self { - Self::from_le(*source) - } - } - }; -} - -impl_elem_prim!(u8); -impl_elem_prim!(i8); -impl_elem_prim!(u16); -impl_elem_prim!(i16); -impl_elem_prim!(u32); -impl_elem_prim!(i32); -impl_elem_prim!(u64); -impl_elem_prim!(i64); -impl_elem_prim!(u128); -impl_elem_prim!(i128); - -impl Elem for [E; LEN] { - #[inline(always)] - fn store(&mut self, source: &Self) { - self.copy_from_slice(source); - - // NOTE: This loop may be eliminated if [to_stored_in_place] is a no-op. - for elem in self { - elem.to_stored_in_place(); - } - } - - #[inline(always)] - fn to_stored_in_place(&mut self) { - // NOTE: This loop may be eliminated if [to_stored_in_place] is a no-op. - for elem in self { - elem.to_stored_in_place(); - } - } - - #[inline(always)] - fn from_stored_in_place(&mut self) { - // NOTE: This loop may be eliminated if [from_stored_in_place] is a no-op. - for elem in self { - elem.from_stored_in_place(); - } - } - - #[inline(always)] - fn from_stored(source: &Self) -> Self { - let mut new = *source; - - // NOTE: This loop may be eliminated if [from_stored_in_place] is a no-op. - for elem in new.iter_mut() { - elem.from_stored_in_place(); - } - - new - } -} diff --git a/src/riscv/lib/src/state_backend/hash.rs b/src/riscv/lib/src/state_backend/hash.rs deleted file mode 100644 index b7447e6381e27049a203820bd5e19fc60416b022..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/hash.rs +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Common type for hashes - -use std::num::NonZeroUsize; - -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum HashError { - #[error("BLAKE2b hashing error")] - HashingError(#[from] tezos_crypto_rs::blake2b::Blake2bError), - - #[error("Invalid digest size")] - InvalidDigestSize, - - #[error("Serialization error: {0}")] - SerializationError(#[from] bincode::Error), - - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - - #[error("The input buffer was expected to be non-empty")] - NonEmptyBufferExpected, -} - -/// Size of digest produced by the underlying hash function -pub const DIGEST_SIZE: usize = 32; - -/// A value of type [struct@Hash] indicates that the enclosed array is a digest -/// produced by a preset hash function, currently BLAKE2b. It can be obtained -/// by either hashing data directly or after hashing by converting from -/// a suitably sized byte slice or vector. -#[derive( - Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, PartialOrd, Ord, -)] -pub struct Hash { - digest: [u8; DIGEST_SIZE], -} - -impl Hash { - /// Hash a slice of bytes - pub fn blake2b_hash_bytes(bytes: &[u8]) -> Result { - tezos_crypto_rs::blake2b::digest_256(bytes).try_into() - } - - /// Get the hash of a value that can be serialised by hashing its serialisation - pub fn blake2b_hash(data: T) -> Result { - Self::blake2b_hash_bytes(&bincode::serialize(&data)?) - } - - /// Combine multiple [`struct@Hash`] values into a single one. - /// - /// The hashes are combined by concatenating them, then hashing the result. - /// Pre-image resistance is not compromised because the concatenation is not - /// ambiguous, with hashes having a fixed size ([`DIGEST_SIZE`]). - pub fn combine(hashes: &[Hash]) -> Result { - let mut input: Vec = Vec::with_capacity(DIGEST_SIZE * hashes.len()); - - hashes - .iter() - .for_each(|h| input.extend_from_slice(h.as_ref())); - - // TODO RV-250: Instead of building the whole input and hashing it, - // we should use incremental hashing, which isn't currently supported - // in `tezos_crypto_rs`. - Hash::blake2b_hash_bytes(&input) - } -} - -impl std::fmt::Display for Hash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - hex::encode(self.digest).fmt(f) - } -} - -impl std::fmt::Debug for Hash { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } -} - -impl TryFrom<&[u8]> for Hash { - type Error = HashError; - - fn try_from(value: &[u8]) -> Result { - let digest: [u8; DIGEST_SIZE] = - value.try_into().map_err(|_| HashError::InvalidDigestSize)?; - Ok(Hash { digest }) - } -} - -impl TryFrom> for Hash { - type Error = HashError; - - fn try_from(value: Vec) -> Result { - Hash::try_from(value.as_ref()) - } -} - -impl From for [u8; DIGEST_SIZE] { - fn from(value: Hash) -> Self { - value.digest - } -} - -impl From<[u8; DIGEST_SIZE]> for Hash { - fn from(digest: [u8; DIGEST_SIZE]) -> Self { - Hash { digest } - } -} - -impl AsRef<[u8]> for Hash { - fn as_ref(&self) -> &[u8] { - &self.digest - } -} - -/// Writer which hashes fixed-sized chunks of data and produces the digests. -pub struct HashWriter { - size: usize, - buffer: Vec, - hashes: Vec, -} - -impl HashWriter { - /// Initialise a new writer with the given `size`. - pub fn new(size: NonZeroUsize) -> Self { - let size = size.get(); - Self { - size, - hashes: Vec::new(), - buffer: Vec::with_capacity(size), - } - } - - /// Finalise the writer by hashing any remaining data and returning the vector - /// of hashes. - pub fn finalise(mut self) -> Result, HashError> { - if !self.buffer.is_empty() { - self.flush_buffer()?; - } - Ok(self.hashes) - } - - /// Hash the contents of the buffer. - fn flush_buffer(&mut self) -> Result<(), HashError> { - let hash = Hash::blake2b_hash_bytes(&self.buffer)?; - self.hashes.push(hash); - self.buffer.clear(); - Ok(()) - } -} - -impl std::io::Write for HashWriter { - fn write(&mut self, mut buf: &[u8]) -> std::io::Result { - let consumed = buf.len(); - - while !buf.is_empty() { - let rem_buffer_len = self.size - self.buffer.len(); - let new_buf_len = std::cmp::min(rem_buffer_len, buf.len()); - - let new_buf = &buf[..new_buf_len]; - buf = &buf[new_buf_len..]; - self.buffer.extend_from_slice(new_buf); - - // If the buffer has been completely filled, flush it. - if rem_buffer_len == new_buf_len { - self.flush_buffer().map_err(std::io::Error::other)?; - } - } - Ok(consumed) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -/// Compute the Merkle hash of a vector of leaf hashes by building a Merkle tree -/// with the given `arity`. The last node in every level might have -/// a smaller arity. -/// -/// # Panics -/// Panics if `arity < 2`. -pub(crate) fn build_custom_merkle_hash( - arity: usize, - mut nodes: Vec, -) -> Result { - assert!(arity >= 2, "Arity must be at least 2"); - - if nodes.is_empty() { - return Err(HashError::NonEmptyBufferExpected); - } - - let mut next_level = Vec::with_capacity(nodes.len().div_ceil(arity)); - - while nodes.len() > 1 { - // Group the nodes into chunks of size `arity` and hash each chunk. - for chunk in nodes.chunks(arity) { - next_level.push(Hash::combine(chunk)?) - } - - std::mem::swap(&mut nodes, &mut next_level); - next_level.truncate(0); - } - - Ok(nodes[0]) -} diff --git a/src/riscv/lib/src/state_backend/layout.rs b/src/riscv/lib/src/state_backend/layout.rs deleted file mode 100644 index 6b10d8735885c57d2d509d0f48786f67f974d8b1..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/layout.rs +++ /dev/null @@ -1,423 +0,0 @@ -// SPDX-FileCopyrightText: 2023 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::marker::PhantomData; - -/// Structural description of a state type -pub trait Layout { - /// Representation of the allocated regions in the state backend - type Allocated; -} - -impl Layout for () { - type Allocated = (); -} - -impl Layout for Box { - type Allocated = Box>; -} - -/// `L::Allocated` -pub type AllocatedOf = ::Allocated; - -/// Layout for a single value -#[repr(transparent)] -pub struct Atom { - _pd: PhantomData, -} - -impl Layout for Atom { - type Allocated = super::Cell; -} - -/// Layout for a fixed number of values -#[repr(transparent)] -pub struct Array { - _pd: PhantomData, -} - -impl Layout for Array { - type Allocated = super::Cells; -} - -/// Layout for a fixed number of bytes, readable as types implementing [`super::elems::Elem`]. -pub struct DynArray {} - -impl Layout for DynArray { - type Allocated = super::DynCells; -} - -/// Usage: Provide a struct with each field holding a layout. -/// -/// ```ignore -/// use octez_riscv::state_backend::*; -/// use octez_riscv::machine_state::csregisters::CSRRepr; -/// use octez_riscv::struct_layout; -/// -/// struct_layout! { -/// pub struct ExampleLayout { -/// satp_ppn: Atom, -/// mode: Atom, -/// cached: Atom, -/// } -/// } -/// ``` -#[macro_export] -macro_rules! struct_layout { - ( - $vis:vis struct $layout_t:ident $(< $($param:ident),+ >)? { - $($field_vis:vis $field_name:ident: $cell_repr:ty),+ - $(,)? - } - ) => { - paste::paste! { - #[derive(serde::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] - $vis struct [<$layout_t F>]< - $( - [<$field_name:camel>] - ),+ - > { - $( - $field_vis $field_name: [<$field_name:camel>] - ),+ - } - - $vis type $layout_t $(< $($param),+ >)? = [<$layout_t F>]< - $( - $cell_repr - ),+ - >; - - impl < - $( - [<$field_name:camel>]: $crate::state_backend::Layout - ),+ - > $crate::state_backend::Layout for [<$layout_t F>]< - $( - [<$field_name:camel>] - ),+ - > { - type Allocated = [<$layout_t F>]< - $( - <[<$field_name:camel>] as $crate::state_backend::Layout>::Allocated - ),+ - >; - - } - - impl < - $( - [<$field_name:camel>]: $crate::state_backend::proof_backend::merkle::AccessInfoAggregatable + serde::Serialize - ),+ - > $crate::state_backend::proof_backend::merkle::AccessInfoAggregatable for [<$layout_t F>]< - $( - [<$field_name:camel>] - ),+ - > { - #[inline] - fn aggregate_access_info(&self) -> bool { - let children = [ - $( - self.$field_name.aggregate_access_info() - ),+ - ]; - children.iter().any(|&x| x) - } - } - - impl < - $( - [<$field_name:camel>]: $crate::state_backend::CommitmentLayout - ),+ - > $crate::state_backend::CommitmentLayout for [<$layout_t F>]< - $( - [<$field_name:camel>] - ),+ - > { - #[inline] - fn state_hash( - state: $crate::state_backend::AllocatedOf - ) -> Result<$crate::storage::Hash, $crate::storage::HashError> { - $crate::storage::Hash::combine(&[ - $( - [<$field_name:camel>]::state_hash(state.$field_name)? - ),+ - ]) - } - } - - impl < - $( - [<$field_name:camel>]: $crate::state_backend::ProofLayout - ),+ - > $crate::state_backend::ProofLayout for [<$layout_t F>]< - $( - [<$field_name:camel>] - ),+ - > { - #[inline] - fn to_merkle_tree( - state: $crate::state_backend::RefProofGenOwnedAlloc, - ) -> Result<$crate::state_backend::proof_backend::merkle::MerkleTree, $crate::storage::HashError> { - $crate::state_backend::proof_backend::merkle::MerkleTree::make_merkle_node( - vec![ - $( - [<$field_name:camel>]::to_merkle_tree(state.$field_name)? - ),+ - ] - ) - } - - #[inline] - fn from_proof( - proof: $crate::state_backend::ProofTree, - ) -> Result, $crate::state_backend::FromProofError> { - let [ $($field_name),+ ] = *proof.into_branches()?; - Ok(Self::Allocated { - $( - $field_name: [<$field_name:camel>]::from_proof($field_name)? - ),+ - }) - } - - #[inline] - fn partial_state_hash( - state: $crate::state_backend::RefVerifierAlloc, - proof: $crate::state_backend::ProofTree, - ) -> Result<$crate::storage::Hash, $crate::state_backend::PartialHashError> { - let (branches, proof_hash) = proof.into_branches_with_hash()?; - let [ $($field_name),+ ] = *branches; - - $( - let $field_name = [<$field_name:camel>]::partial_state_hash(state.$field_name, $field_name); - )+ - - $crate::state_backend::combine_partial_hashes([$($field_name),+], proof_hash) - } - } - } - }; -} - -impl Layout for (A, B) -where - A: Layout, - B: Layout, -{ - type Allocated = (A::Allocated, B::Allocated); -} - -impl Layout for (A, B, C) -where - A: Layout, - B: Layout, - C: Layout, -{ - type Allocated = (A::Allocated, B::Allocated, C::Allocated); -} - -impl Layout for (A, B, C, D) -where - A: Layout, - B: Layout, - C: Layout, - D: Layout, -{ - type Allocated = ( - A::Allocated, - B::Allocated, - C::Allocated, - D::Allocated, - ); -} - -impl Layout for (A, B, C, D, E) -where - A: Layout, - B: Layout, - C: Layout, - D: Layout, - E: Layout, -{ - type Allocated = ( - A::Allocated, - B::Allocated, - C::Allocated, - D::Allocated, - E::Allocated, - ); -} - -impl Layout for (A, B, C, D, E, F) -where - A: Layout, - B: Layout, - C: Layout, - D: Layout, - E: Layout, - F: Layout, -{ - type Allocated = ( - A::Allocated, - B::Allocated, - C::Allocated, - D::Allocated, - E::Allocated, - F::Allocated, - ); -} - -impl Layout for [T; LEN] -where - T: Layout, -{ - type Allocated = [T::Allocated; LEN]; -} - -/// This [`Layout`] is identical to [`[T; LEN]`] but it allows you to choose a very high `LEN`. -pub struct Many(PhantomData<[T; LEN]>); - -impl Layout for Many -where - T: Layout, -{ - type Allocated = Vec>; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::backend_test; - use crate::default::ConstDefault; - use crate::state::NewState; - use crate::state_backend::Cell; - use crate::state_backend::Cells; - use crate::state_backend::CommitmentLayout; - use crate::state_backend::FnManagerIdent; - use crate::state_backend::ProofLayout; - use crate::state_backend::ProofPart; - use crate::state_backend::owned_backend::Owned; - use crate::state_backend::proof_backend::ProofWrapper; - use crate::state_backend::verify_backend::handle_stepper_panics; - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct MyFoo(u64); - - impl ConstDefault for MyFoo { - const DEFAULT: Self = MyFoo(42); - } - - // Test that the Atom layout initialises the underlying Cell correctly. - backend_test!(test_cell_init, F, { - assert_eq!( - Cell::::new(&mut F::manager()).read(), - MyFoo::DEFAULT - ); - }); - - // Test that the Array layout initialises the underlying Cells correctly. - backend_test!(test_cells_init, F, { - assert_eq!( - Cells::::new(&mut F::manager()).read_all(), - [MyFoo::DEFAULT; 1337] - ); - }); - - #[test] - fn test_struct_layout() { - struct_layout! { - pub struct Foo { - bar: Atom, - qux: Array, - } - } - - fn inner(bar: u64, qux: [u8; 64]) { - let mut foo = AllocatedOf:: { - bar: Cell::new(&mut Owned), - qux: Cells::new(&mut Owned), - }; - - foo.bar.write(bar); - foo.qux.write_all(&qux); - - // Obtain the state hash - let refs = FooF { - bar: foo.bar.struct_ref::(), - qux: foo.qux.struct_ref::(), - }; - let hash = Foo::state_hash(refs).unwrap(); - - // Obtain the Merkle tree via the `ProofGen` backend - let mut proof_foo = FooF { - bar: foo.bar.struct_ref::(), - qux: foo.qux.struct_ref::(), - }; - let proof_foo_refs = FooF { - bar: proof_foo.bar.struct_ref::(), - qux: proof_foo.qux.struct_ref::(), - }; - - let tree = Foo::to_merkle_tree(proof_foo_refs).unwrap(); - let tree_root_hash = tree.root_hash(); - assert_eq!(hash, tree_root_hash); - - // Modify the values so they appear in the proof - proof_foo.bar.write(bar.wrapping_add(1)); - proof_foo.qux.write_all(&qux.map(|x| x.wrapping_add(1))); - - // Obtain the Merkle tree, again, to make sure the root hash has not changed - let proof_foo_refs = FooF { - bar: proof_foo.bar.struct_ref::(), - qux: proof_foo.qux.struct_ref::(), - }; - - let tree = Foo::to_merkle_tree(proof_foo_refs).unwrap(); - let tree_root_hash = tree.root_hash(); - assert_eq!(hash, tree_root_hash); - - // Produce a proof - let proof = tree.to_merkle_proof().unwrap(); - let proof_hash = proof.root_hash().unwrap(); - assert_eq!(hash, proof_hash); - - // Apply the same modification on the `Owned` state in order to obtain - // the final state hash - foo.bar.write(bar.wrapping_add(1)); - foo.qux.write_all(&qux.map(|x| x.wrapping_add(1))); - let refs = FooF { - bar: foo.bar.struct_ref::(), - qux: foo.qux.struct_ref::(), - }; - let final_hash = Foo::state_hash(refs).unwrap(); - - // Verify the proof and check the final hash - handle_stepper_panics(|| { - let mut verify_foo = Foo::from_proof(ProofPart::Present(&proof)).unwrap(); - assert_eq!(bar, verify_foo.bar.read()); - assert_eq!(qux, verify_foo.qux.read_all().as_slice()); - - // Apply the same modification on the `Verifier` state and check - // that the final hash is correct - verify_foo.bar.write(bar.wrapping_add(1)); - verify_foo.qux.write_all(&qux.map(|x| x.wrapping_add(1))); - - let verify_foo_refs = FooF { - bar: verify_foo.bar.struct_ref::(), - qux: verify_foo.qux.struct_ref::(), - }; - - let verify_hash = - Foo::partial_state_hash(verify_foo_refs, ProofPart::Present(&proof)).unwrap(); - assert_eq!(verify_hash, final_hash) - }) - .unwrap(); - } - - proptest::proptest!(|(bar: u64, qux: [u8; 64])| { - inner(bar, qux); - }); - } -} diff --git a/src/riscv/lib/src/state_backend/owned_backend.rs b/src/riscv/lib/src/state_backend/owned_backend.rs deleted file mode 100644 index 6d2f1d6260ea53eb5654389bffea325b59bcd75d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/owned_backend.rs +++ /dev/null @@ -1,540 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::array; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::mem::MaybeUninit; - -use serde::ser::SerializeTuple; - -use super::Elem; -use super::EnrichedValue; -use super::EnrichedValueLinked; -use super::ManagerAlloc; -use super::ManagerBase; -use super::ManagerClone; -use super::ManagerDeserialise; -use super::ManagerRead; -use super::ManagerReadWrite; -use super::ManagerSerialise; -use super::ManagerWrite; -use super::StaticCopy; - -/// Manager that allows state binders to own the state storage -#[derive(Clone, Copy, Debug)] -pub struct Owned; - -impl Owned { - /// Get the byte offset from a pointer to `Owned::Region` to the start of the element at `index`. - pub(crate) const fn region_elem_offset(index: usize) -> usize { - assert!(index < LEN, "Out of bounds access for region"); - - index * std::mem::size_of::() - } -} - -impl ManagerBase for Owned { - type Region = [E; LEN]; - - type DynRegion = Box<[u8; LEN]>; - - type EnrichedCell = (V::E, V::D); - - type ManagerRoot = Self; - - fn enrich_cell(cell: Self::Region) -> Self::EnrichedCell { - let [value] = cell; - let derived = V::derive(&value); - (value, derived) - } - - fn as_devalued_cell(cell: &Self::EnrichedCell) -> &Self::Region { - array::from_ref(&cell.0) - } -} - -impl ManagerAlloc for Owned { - fn allocate_region( - &mut self, - value: [E; LEN], - ) -> Self::Region { - value - } - - fn allocate_dyn_region(&mut self) -> Self::DynRegion { - unsafe { - let layout = std::alloc::Layout::new::<[u8; LEN]>() - .align_to(4096) - .unwrap(); - let alloc = std::alloc::alloc_zeroed(layout); - Box::from_raw(alloc.cast()) - } - } -} - -impl ManagerRead for Owned { - fn region_read( - region: &Self::Region, - index: usize, - ) -> E { - region[index] - } - - fn region_ref(region: &Self::Region, index: usize) -> &E { - ®ion[index] - } - - fn region_read_all(region: &Self::Region) -> Vec { - region.to_vec() - } - - fn dyn_region_read( - region: &Self::DynRegion, - address: usize, - ) -> E { - { - assert!(address + mem::size_of::() <= LEN); - - let mut result = unsafe { region.as_ptr().add(address).cast::().read_unaligned() }; - result.from_stored_in_place(); - - result - } - } - - fn dyn_region_read_all( - region: &Self::DynRegion, - address: usize, - values: &mut [E], - ) { - assert!(address + mem::size_of_val(values) <= LEN); - - unsafe { - region - .as_ptr() - .add(address) - .cast::() - .copy_to(values.as_mut_ptr(), values.len()); - } - - for elem in values.iter_mut() { - elem.from_stored_in_place(); - } - } - - fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E - where - V: EnrichedValue, - V::E: Copy, - { - cell.0 - } - - fn enriched_cell_read_derived(cell: &Self::EnrichedCell) -> V::D - where - V: EnrichedValue, - V::D: Copy, - { - cell.1 - } - - fn enriched_cell_ref_stored(cell: &Self::EnrichedCell) -> &V::E - where - V: EnrichedValue, - { - &cell.0 - } -} - -impl ManagerWrite for Owned { - fn region_write( - region: &mut Self::Region, - index: usize, - value: E, - ) { - region[index] = value; - } - - fn region_write_all( - region: &mut Self::Region, - value: &[E], - ) { - region.copy_from_slice(value) - } - - fn dyn_region_write( - region: &mut Self::DynRegion, - address: usize, - mut value: E, - ) { - assert!(address + mem::size_of_val(&value) <= LEN); - - value.to_stored_in_place(); - - unsafe { - region - .as_mut_ptr() - .add(address) - .cast::() - .write_unaligned(value); - } - } - - fn dyn_region_write_all( - region: &mut Self::DynRegion, - address: usize, - values: &[E], - ) { - assert!(address + mem::size_of_val(values) <= LEN); - - unsafe { - let ptr = region.as_mut_ptr().add(address).cast::(); - - for (i, mut value) in values.iter().copied().enumerate() { - value.to_stored_in_place(); - ptr.add(i).write_unaligned(value) - } - } - } - - fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) - where - V: EnrichedValueLinked, - { - let derived = V::derive(&value); - - cell.0 = value; - cell.1 = derived; - } -} - -impl ManagerReadWrite for Owned { - fn region_replace( - region: &mut Self::Region, - index: usize, - value: E, - ) -> E { - mem::replace(&mut region[index], value) - } -} - -impl ManagerSerialise for Owned { - fn serialise_region( - region: &Self::Region, - serializer: S, - ) -> Result { - // A special encoding for single-element regions helps clean up encoding for serialisation - // formats that contain structures. For example, JSON, where single-element regions would - // be represented as array singletons. - if LEN == 1 { - return region[0].serialize(serializer); - } - - // We're serialising this as a fixed-sized tuple because otherwise `bincode` would prefix - // the length of this array, which is not needed. - let mut serializer = serializer.serialize_tuple(LEN)?; - - for item in region.iter() { - serializer.serialize_element(item)?; - } - - serializer.end() - } - - fn serialise_dyn_region( - region: &Self::DynRegion, - serializer: S, - ) -> Result { - serializer.serialize_bytes(region.as_slice()) - } -} - -impl ManagerDeserialise for Owned { - fn deserialise_region< - 'de, - E: serde::de::Deserialize<'de> + 'static, - const LEN: usize, - D: serde::de::Deserializer<'de>, - >( - deserializer: D, - ) -> Result, D::Error> { - // A special encoding for single-element regions helps clean up encoding for serialisation - // formats that contain structures. For example, JSON, where single-element regions would - // be represented as array singletons. - if LEN == 1 { - let values = unsafe { - let mut values: [MaybeUninit; LEN] = mem::zeroed(); - values[0].write(E::deserialize(deserializer)?); - values.map(|value| value.assume_init()) - }; - return Ok(values); - } - - struct Inner(PhantomData); - - impl<'de, E: serde::Deserialize<'de> + Sized, const LEN: usize> serde::de::Visitor<'de> - for Inner - { - type Value = [E; LEN]; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "{}", std::any::type_name::()) - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut values: [MaybeUninit; LEN] = array::from_fn(|_| MaybeUninit::uninit()); - - for value in values.iter_mut() { - value.write(seq.next_element::()?.ok_or_else(|| { - serde::de::Error::custom(format!( - "Not enough elements to construct {}", - std::any::type_name::() - )) - })?); - } - - // We can't use `std::mem::transmute` here because `[_; LEN]` does not have a fixed - // size according to the compiler. I suspect this because `LEN` is a const generic - // parameter. - Ok(values.map(|value| { - // SAFETY: We've initialised all the elements in the array. - unsafe { value.assume_init() } - })) - } - } - - deserializer.deserialize_tuple(LEN, Inner(PhantomData)) - } - - fn deserialise_dyn_region<'de, const LEN: usize, D: serde::de::Deserializer<'de>>( - deserializer: D, - ) -> Result, D::Error> { - let vec: Vec = serde::Deserialize::deserialize(deserializer)?; - vec.try_into() - .map_err(|_err| serde::de::Error::custom("Dynamic region of mismatching length")) - } -} - -impl ManagerClone for Owned { - fn clone_region( - region: &Self::Region, - ) -> Self::Region { - region.clone() - } - - fn clone_dyn_region(region: &Self::DynRegion) -> Self::DynRegion { - region.clone() - } - - fn clone_enriched_cell(cell: &Self::EnrichedCell) -> Self::EnrichedCell - where - V::E: Clone, - V::D: Clone, - { - cell.clone() - } -} - -#[cfg(test)] -pub(crate) mod test_helpers { - use super::*; - use crate::state_backend::Cell; - use crate::state_backend::Cells; - use crate::state_backend::DynCells; - use crate::state_backend::EnrichedCell; - use crate::state_backend::FnManagerIdent; - use crate::state_backend::Ref; - use crate::state_backend::proof_backend::ProofDynRegion; - use crate::state_backend::proof_backend::ProofGen; - use crate::state_backend::proof_backend::ProofRegion; - use crate::state_backend::test_helpers::TestBackendFactory; - - /// Test backend factory for the owned state manager - pub struct OwnedTestBackendFactory; - - impl TestBackendFactory for OwnedTestBackendFactory { - type Manager = Owned; - - fn manager() -> Self::Manager { - Owned - } - } - - /// Ensure [`Cell`] can be serialised and deserialised in a consistent way. - #[test] - fn cell_serialise() { - proptest::proptest!(|(value: u64)|{ - let region = [value; 1]; - let cell: Cell = Cell::bind(region); - let bytes = bincode::serialize(&cell).unwrap(); - - let cell_after: Cell = bincode::deserialize(&bytes).unwrap(); - assert_eq!(cell.read(), cell_after.read()); - - let bytes_after = bincode::serialize(&cell_after).unwrap(); - assert_eq!(bytes, bytes_after); - - // Serialisation is consistent with that of the `ProofGen` backend. - let proof_cell: Cell>> = - Cell::bind(ProofRegion::bind(®ion)); - let proof_bytes = bincode::serialize(&proof_cell).unwrap(); - assert_eq!(bytes, proof_bytes); - }); - } - - /// Ensure [`Cells`] can be serialised and deserialised in a consistent way. - #[test] - fn cells_serialise() { - proptest::proptest!(|(a: u64, b: u64, c: u64)|{ - let cell: Cells = Cells::bind([a, b, c]); - let bytes = bincode::serialize(&cell).unwrap(); - - let cell_after: Cells = bincode::deserialize(&bytes).unwrap(); - - assert_eq!(cell.read_all(), cell_after.read_all()); - - for i in 0..3 { - assert_eq!(cell.read(i), cell_after.read(i)); - } - - let bytes_after = bincode::serialize(&cell_after).unwrap(); - assert_eq!(bytes, bytes_after); - - // Serialisation is consistent with that of the `ProofGen` backend. - let proof_cells: Cells>> = - Cells::bind(ProofRegion::bind(cell.region_ref())); - let proof_bytes = bincode::serialize(&proof_cells).unwrap(); - assert_eq!(bytes, proof_bytes); - }); - } - - /// Ensure [`DynCells`] can be serialised and deserialised in a consistent way. - #[test] - fn dyn_cells_serialise() { - proptest::proptest!(|(address in (0usize..120), value: u64)|{ - let mut cells: DynCells<128, Owned> = DynCells::bind(Box::new([0u8; 128])); - cells.write(address, value); - let bytes = bincode::serialize(&cells).unwrap(); - - let cells_after: DynCells<128, Owned> = bincode::deserialize(&bytes).unwrap(); - for i in 0..128 { - assert_eq!(cells.read::(i), cells_after.read::(i)); - } - - let bytes_after = bincode::serialize(&cells_after).unwrap(); - assert_eq!(bytes, bytes_after); - - // Serialisation is consistent with that of the `ProofGen` backend. - let proof_cells: DynCells<128, ProofGen>> = - DynCells::bind(ProofDynRegion::bind(cells.region_ref())); - let proof_bytes = bincode::serialize(&proof_cells).unwrap(); - assert_eq!(bytes, proof_bytes); - }); - } - - /// Ensure [`EnrichedCell`] can be serialised and deserialised in a consistent way. - #[test] - fn enriched_cell_serialise() { - pub struct Enriching; - - impl EnrichedValue for Enriching { - type E = u64; - type D = T; - } - - #[derive(Clone, Copy)] - pub struct T(u64); - - impl<'a> From<&'a u64> for T { - fn from(value: &'a u64) -> Self { - T(value.wrapping_add(1)) - } - } - - proptest::proptest!(|(value: u64)| { - let cell = Cell::bind([0u64]); - let mut cell: EnrichedCell = EnrichedCell::bind(cell); - cell.write(value); - - let read_value = cell.read_ref_stored(); - - assert_eq!(value, *read_value); - let bytes = bincode::serialize(&cell).unwrap(); - - let cell_after: EnrichedCell = bincode::deserialize(&bytes).unwrap(); - - assert_eq!(*cell.read_ref_stored(), *cell_after.read_ref_stored()); - - let derived = cell.read_derived(); - let derived_after = cell_after.read_derived(); - - assert_eq!(T::from(read_value).0, derived.0); - assert_eq!(derived.0, derived_after.0); - - // Serialisation is consistent with that of the `ProofGen` backend. - let proof_cell: EnrichedCell> = EnrichedCell::bind(cell.struct_ref::()); - let proof_bytes = bincode::serialize(&proof_cell).unwrap(); - assert_eq!(bytes, proof_bytes); - }); - } - - /// Ensure [`EnrichedCell`] is serialized identically to [`Cell`]. - #[test] - fn enriched_cell_serialise_match_cell() { - pub struct Enriching; - pub struct Fun; - - impl EnrichedValue for Enriching { - type E = u64; - type D = Fun; - } - - impl<'a> From<&'a u64> for Fun { - fn from(_value: &'a u64) -> Self { - Self - } - } - - proptest::proptest!(|(value: u64)| { - let cell = Cell::bind([0u64]); - let mut ecell: EnrichedCell = EnrichedCell::bind(cell); - let mut cell: Cell = Cell::bind([0; 1]); - ecell.write(value); - cell.write(value); - - assert_eq!(value, ecell.read_stored()); - assert_eq!(value, cell.read()); - - let ebytes = bincode::serialize(&ecell).unwrap(); - let cbytes = bincode::serialize(&cell).unwrap(); - - assert_eq!(ebytes, cbytes, "Serializing EnrichedCell and Cell should match"); - }); - } - - /// Ensure that [`Cell`] serialises in a way that represents the underlying element - /// directly instead of wrapping it into an array (as it is an array under the hood). - #[test] - fn cell_direct_serialise() { - let cell: Cell = Cell::bind([42]); - let json_value = serde_json::to_value(cell).unwrap(); - let expected_json_value = serde_json::json!(42); - assert_eq!(json_value, expected_json_value); - } - - /// Check that regions are properly initialised. - #[test] - fn region_init() { - proptest::proptest!(|(init_value: [u64; 17])| { - let region = Owned.allocate_region(init_value); - proptest::prop_assert_eq!(region, init_value); - }); - } -} diff --git a/src/riscv/lib/src/state_backend/proof_backend.rs b/src/riscv/lib/src/state_backend/proof_backend.rs deleted file mode 100644 index 50be5d3dac0eb3508c9920cd15a380cb67d9c78c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend.rs +++ /dev/null @@ -1,729 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Proof-generating backend -//! -//! Generic backend used for PVM proof generation, which wraps a manager and -//! records all state accesses performed during an evaluation step. -//! After evaluation, a [`MerkleTree`] over the PVM state can be obtained, -//! which can be partially blinded to produce a proof as a partial Merkle tree. -//! The structure of the Merkle tree is informed by the layout of the state, -//! which needs to implement [`ProofLayout`]. -//! -//! [`MerkleTree`]: merkle::MerkleTree -//! [`ProofLayout`]: super::ProofLayout - -use std::cell::Cell; -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::mem; -use std::slice; - -use serde::ser::SerializeTuple; - -use super::EnrichedValue; -use super::EnrichedValueLinked; -use super::FnManager; -use super::ManagerBase; -use super::ManagerRead; -use super::ManagerReadWrite; -use super::ManagerSerialise; -use super::ManagerWrite; - -pub mod merkle; -pub mod proof; -pub mod tree; - -/// Proof-generating backend -pub struct ProofGen { - _pd: std::marker::PhantomData, -} - -impl ManagerBase for ProofGen { - type Region = ProofRegion; - - type DynRegion = ProofDynRegion; - - type EnrichedCell = ProofEnrichedCell; - - type ManagerRoot = Self; - - fn enrich_cell( - underlying: Self::Region, - ) -> Self::EnrichedCell { - ProofEnrichedCell { underlying } - } - - fn as_devalued_cell(cell: &Self::EnrichedCell) -> &Self::Region { - &cell.underlying - } -} - -/// Implementation of [`ManagerRead`] which wraps another manager and -/// additionally records read locations. -impl ManagerRead for ProofGen { - fn region_read(region: &Self::Region, index: usize) -> E { - *Self::region_ref(region, index) - } - - fn region_ref(region: &Self::Region, index: usize) -> &E { - region.set_access_info(); - region.unrecorded_ref(index) - } - - fn region_read_all(region: &Self::Region) -> Vec { - (0..LEN).map(|i| Self::region_read(region, i)).collect() - } - - fn dyn_region_read( - region: &Self::DynRegion, - address: usize, - ) -> E { - region.reads.borrow_mut().insert::(address); - region.unrecorded_read(address) - } - - fn dyn_region_read_all( - region: &Self::DynRegion, - address: usize, - values: &mut [E], - ) { - assert!(address + mem::size_of_val(values) <= LEN); - - for (offset, value) in values.iter_mut().enumerate() { - *value = Self::dyn_region_read(region, address + offset * mem::size_of::()); - } - } - - fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E - where - V: EnrichedValue, - V::E: Copy, - { - Self::region_read(&cell.underlying, 0) - } - - fn enriched_cell_read_derived(cell: &Self::EnrichedCell) -> V::D - where - V: EnrichedValueLinked, - V::D: Copy, - { - V::derive(Self::enriched_cell_ref_stored(cell)) - } - - fn enriched_cell_ref_stored(cell: &Self::EnrichedCell) -> &V::E - where - V: EnrichedValue, - { - Self::region_ref(&cell.underlying, 0) - } -} - -/// Implementation of [`ManagerWrite`] which wraps another manager and -/// records written locations but does not write to the wrapped region directly. -impl ManagerWrite for ProofGen { - fn region_write( - region: &mut Self::Region, - index: usize, - value: E, - ) { - region.set_access_info(); - region.writes.insert(index, value); - } - - fn region_write_all( - region: &mut Self::Region, - values: &[E], - ) { - for (i, value) in values.iter().enumerate() { - Self::region_write(region, i, *value); - } - } - - fn dyn_region_write( - region: &mut Self::DynRegion, - address: usize, - mut value: E, - ) { - assert!(address + mem::size_of_val(&value) <= LEN); - - value.to_stored_in_place(); - - // Get a mutable slice of bytes over the value to be written and iterate over it - // in order to record every byte to the write log. The wrapped region is not modified. - let value_bytes = unsafe { - // SAFETY: Obtaining a slice of `mem::size_of::()` bytes from a reference - // to one value of type `E` should be safe, assuming `value` is not the result of - // multiple allocations. - // Cannot use `mem::transmute` because `E` does not have a constant size. - slice::from_raw_parts((&value as *const E) as *const u8, mem::size_of::()) - }; - for (offset, byte) in value_bytes.iter().enumerate() { - region.writes.insert(address + offset, *byte); - } - } - - fn dyn_region_write_all( - region: &mut Self::DynRegion, - address: usize, - values: &[E], - ) { - assert!(address + mem::size_of_val(values) <= LEN); - - for (offset, value) in values.iter().enumerate() { - Self::dyn_region_write(region, address + offset * mem::size_of::(), *value) - } - } - - fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) - where - V: EnrichedValueLinked, - { - Self::region_write(&mut cell.underlying, 0, value); - } -} - -/// Implementation of [`ManagerReadWrite`] which wraps another manager and -/// additionally records read and written locations. -impl ManagerReadWrite for ProofGen { - fn region_replace( - region: &mut Self::Region, - index: usize, - value: E, - ) -> E { - let old = Self::region_read(region, index); - Self::region_write(region, index, value); - old - } -} - -/// Implementation of [`ManagerSerialise`] which wraps another manager and -/// serialises data as recorded by the `ProofGen` backend, reconstructed -/// via variants of [`ManagerRead`] functions which do not record access -/// information. -impl ManagerSerialise for ProofGen { - fn serialise_region( - region: &Self::Region, - serializer: S, - ) -> Result { - if LEN == 1 { - let elem = region.unrecorded_ref(0); - return elem.serialize(serializer); - } - - let mut serializer = serializer.serialize_tuple(LEN)?; - for i in 0..LEN { - let elem = region.unrecorded_ref(i); - serializer.serialize_element(elem)?; - } - serializer.end() - } - - fn serialise_dyn_region( - region: &Self::DynRegion, - serializer: S, - ) -> Result { - let mut values = vec![0u8; LEN]; - region.unrecorded_read_all(0, &mut values); - serializer.serialize_bytes(values.as_slice()) - } -} - -/// Proof region which wraps a region managed by another manager. -/// -/// A [`ManagerBase::Region`] is never split across multiple leaves when Merkleised. -/// An access to any part of the region is thus recorded as an access to the region as a whole. -/// The underlying region is never mutated, but all written values are recorded -/// in order to preserve the integrity of subsequent reads. -pub struct ProofRegion { - source: M::Region, - writes: BTreeMap, - access: Cell, -} - -impl ProofRegion { - /// Bind a pre-existing region. - pub fn bind(source: M::Region) -> Self { - Self { - source, - writes: BTreeMap::new(), - access: Cell::new(false), - } - } - - /// Get a copy of the access log. - pub fn get_access_info(&self) -> bool { - self.access.get() - } - - /// Record that the regions has been accessed - pub fn set_access_info(&self) { - self.access.set(true) - } - - /// Get a reference to the wrapper region. - pub fn inner_region_ref(&self) -> &M::Region { - &self.source - } -} - -impl ProofRegion { - /// Version of [`ManagerRead::region_ref`] which does not record - /// the access as a read. - fn unrecorded_ref(&self, index: usize) -> &E { - self.writes - .get(&index) - .unwrap_or_else(|| M::region_ref(&self.source, index)) - } -} -/// Proof dynamic region which wraps a dynamic region managed by another manager. -/// -/// When Merkleising a [`ManagerBase::DynRegion`], its data can be split into multiple leaves. -/// Accesses are thus recorded for each address. -/// The underlying dynamic region is never mutated, but all written bytes are -/// recorded in order to preserve the integrity of subsequent reads. -pub struct ProofDynRegion { - source: M::DynRegion, - reads: RefCell, - writes: BTreeMap, -} - -impl ProofDynRegion { - /// Bind a pre-existing dynamic region. - pub fn bind(source: M::DynRegion) -> Self { - Self { - source, - reads: RefCell::default(), - writes: BTreeMap::new(), - } - } - - /// Get the set of addresses of the region that were read from. - /// This function is meant to be called once when Merkleising the region. - pub fn get_read(&self) -> DynAccess { - self.reads.take() - } - - /// Get the set of addresses of the region that were written to. - /// This function is meant to be called once when Merkleising the region. - pub fn get_write(&self) -> DynAccess { - let writes: BTreeSet<_> = self.writes.keys().copied().collect(); - DynAccess(writes) - } -} - -impl ProofDynRegion { - /// Read from the wrapped dynamic region. - pub fn inner_dyn_region_read(&self, address: usize) -> E { - M::dyn_region_read(&self.source, address) - } - - /// Version of [`ManagerRead::dyn_region_read`] which does not record - /// the access as a read. - fn unrecorded_read(&self, address: usize) -> E { - let elem_size = mem::size_of::(); - - // Read a value from the wrapped region and convert it to the stored representation. - let mut value: E = M::dyn_region_read(&self.source, address); - value.to_stored_in_place(); - - // Get a mutable slice of bytes over the value and overwrite any byte that has been written - // during the proof step. - let value_bytes: &mut [u8] = unsafe { - // SAFETY: Obtaining a mutable slice of `mem::size_of::()` bytes from a mutable reference - // to one value of type `E` should be safe, assuming `value` is not the result of - // multiple allocations. - // Cannot use `mem::transmute` because `E` does not have a constant size. - slice::from_raw_parts_mut((&mut value as *mut E) as *mut u8, elem_size) - }; - for (i, byte) in self.writes.range(address..address + elem_size) { - value_bytes[*i - address] = *byte; - } - - // Convert back from the stored representation and return. - value.from_stored_in_place(); - value - } - - /// Version of [`ManagerRead::dyn_region_read_all`] which does not record - /// the access as a read. - fn unrecorded_read_all(&self, address: usize, values: &mut [E]) { - assert!(address + mem::size_of_val(values) <= LEN); - - for (offset, value) in values.iter_mut().enumerate() { - *value = self.unrecorded_read(address + offset * mem::size_of::()); - } - } -} - -/// Proof enriched cell which wraps an enriched cell managed by another manager. -/// -/// Similar to [`ManagerBase::Region`], a [`ManagerBase::EnrichedCell`] is never -/// split across multiple leaves when Merkleised. -/// The underlying cell is never mutated, but written values are recorded -/// in order to preserve the integrity of subsequent reads. -pub struct ProofEnrichedCell { - underlying: ProofRegion, -} - -impl ProofEnrichedCell { - /// Bind a pre-existing enriched cell. - pub fn bind(source: M::Region) -> Self { - Self { - underlying: ProofRegion::bind(source), - } - } - - /// Get a copy of the access log. - pub fn get_access_info(&self) -> bool { - self.underlying.access.get() - } -} - -/// A record of accessed addresses in a dynamic region -#[derive(Default)] -pub struct DynAccess(BTreeSet); - -impl DynAccess { - /// Insert all addresses touched while accessing an element of a given size. - pub fn insert(&mut self, address: usize) { - self.0.extend(address..address + mem::size_of::()) - } - - /// Check whether any address within a given range of addresses - /// has been accessed. - pub fn includes_range(&self, r: std::ops::Range) -> bool { - self.0.range(r).next().is_some() - } -} - -/// Natural transformation from a manager `M` to a proof-generating manager `ProofGen` -pub enum ProofWrapper {} - -impl FnManager for ProofWrapper { - type Output = ProofGen; - - fn map_region( - input: ::Region, - ) -> as ManagerBase>::Region { - ProofRegion::bind(input) - } - - fn map_dyn_region( - input: ::DynRegion, - ) -> as ManagerBase>::DynRegion { - ProofDynRegion::bind(input) - } -} - -#[cfg(test)] -mod tests { - use std::collections::VecDeque; - - use proptest::array; - use proptest::prop_assert; - use proptest::prop_assert_eq; - use proptest::proptest; - use tests::merkle::MerkleTree; - - use super::merkle::MERKLE_LEAF_SIZE; - use super::*; - use crate::state_backend::Cells; - use crate::state_backend::CommitmentLayout; - use crate::state_backend::DynArray; - use crate::state_backend::DynCells; - use crate::state_backend::ProofLayout; - use crate::state_backend::Ref; - use crate::state_backend::layout::Array; - use crate::state_backend::owned_backend::Owned; - - const CELLS_SIZE: usize = 32; - - #[test] - fn test_proof_gen_region() { - proptest!(|(value_before: u64, value_after: u64, i in 0..CELLS_SIZE)| { - // A read followed by a write - let cells = [value_before; CELLS_SIZE]; - let region: ProofRegion> = ProofRegion::bind(&cells); - let mut region: Cells>> = Cells::bind(region); - - prop_assert!(!region.region_ref().get_access_info()); - let value = region.read(i); - prop_assert_eq!(value, value_before); - prop_assert!(region.region_ref().get_access_info()); - region.write(i, value_after); - prop_assert!(region.region_ref().get_access_info()); - - // A write followed by a read - let cells = [value_before; CELLS_SIZE]; - let region: ProofRegion> = ProofRegion::bind(&cells); - let mut region: Cells>> = Cells::bind(region); - prop_assert!(!region.region_ref().get_access_info()); - region.write(i, value_after); - prop_assert!(region.region_ref().get_access_info()); - let value = region.read(i); - prop_assert_eq!(value, value_after); - prop_assert!(region.region_ref().get_access_info()); - - // Replace - let cells = [value_before; CELLS_SIZE]; - let region: ProofRegion> = ProofRegion::bind(&cells); - let mut region: Cells>> = Cells::bind(region); - prop_assert!(!region.region_ref().get_access_info()); - let value = region.replace(i, value_after); - prop_assert_eq!(value, value_before); - prop_assert!(region.region_ref().get_access_info()); - - let data_before = [value_before; CELLS_SIZE]; - let data_after = [value_after; CELLS_SIZE]; - - // A read_all followed by a write_all - let cells = data_before; - let region: ProofRegion> = ProofRegion::bind(&cells); - let mut region: Cells>> = Cells::bind(region); - prop_assert!(!region.region_ref().get_access_info()); - let values = region.read_all(); - prop_assert_eq!(values.as_slice(), data_before); - prop_assert!(region.region_ref().get_access_info()); - region.write_all(&data_after); - prop_assert!(region.region_ref().get_access_info()); - - // A write_all followed by a read_all - let cells = data_before; - let region: ProofRegion> = ProofRegion::bind(&cells); - let mut region: Cells>> = Cells::bind(region); - prop_assert!(!region.region_ref().get_access_info()); - region.write_all(&data_after); - prop_assert!(region.region_ref().get_access_info()); - let values = region.read_all(); - prop_assert_eq!(values.as_slice(), data_after); - prop_assert!(region.region_ref().get_access_info()); - - // Check correct Merkleisation - let cells = [value_before; CELLS_SIZE]; - let cells_owned: Cells> = Cells::bind(&cells); - let initial_root_hash = - as CommitmentLayout>::state_hash(cells_owned).unwrap(); - - let mut proof_region: ProofRegion> = - ProofRegion::bind(&cells); - ProofGen::>::region_write(&mut proof_region, i, value_after); - let proof_cells: Cells>>> = - Cells::bind(&proof_region); - - let merkle_tree = - as ProofLayout>::to_merkle_tree(proof_cells).unwrap(); - merkle_tree.check_root_hash(); - match merkle_tree { - MerkleTree::Leaf(hash, access_info, _) => { - prop_assert_eq!(hash, initial_root_hash); - prop_assert!(access_info); - } - _ => panic!("Expected Merkle tree to contain a single written leaf"), - } - }); - } - - const LEAVES: usize = 8; - const DYN_REGION_SIZE: usize = MERKLE_LEAF_SIZE.get() * LEAVES; - const ELEM_SIZE: usize = mem::size_of::(); - - #[test] - fn test_proof_gen_dyn_region() { - if ELEM_SIZE > MERKLE_LEAF_SIZE.get() { - unreachable!( - "This test assumes that a single element does not span more than 2 leaves" - ); - } - let address_range = 0..DYN_REGION_SIZE - ELEM_SIZE; - - // Check that writing to an address in the proof region makes subsequent reads return - // the overwritten value. - proptest!(|(byte_before: u8, - bytes_after: [u8; ELEM_SIZE], - write_address in &address_range)| { - let cells = Box::new([byte_before; DYN_REGION_SIZE]); - let dyn_region: ProofDynRegion = ProofDynRegion::bind(cells); - let mut dyn_cells: DynCells> = - DynCells::bind(dyn_region); - - // Perform static memory accesses - let value_before = u64::from_le_bytes([byte_before; ELEM_SIZE]); - let value_after = u64::from_le_bytes(bytes_after); - - let value: u64 = dyn_cells.read(write_address); - assert_eq!(value, value_before); - dyn_cells.write(write_address, value_after); - let value: u64 = dyn_cells.read(write_address); - assert_eq!(value, value_after); - - let cells = Box::new([byte_before; DYN_REGION_SIZE]); - let dyn_region: ProofDynRegion = ProofDynRegion::bind(cells); - let mut dyn_cells: DynCells> = - DynCells::bind(dyn_region); - - // Perform dynamic memory accesses as `u16` - let value_before = [u16::from_le_bytes([byte_before; 2]); ELEM_SIZE / 2]; - let value_after = [ - u16::from_le_bytes([bytes_after[0], bytes_after[1]]), - u16::from_le_bytes([bytes_after[2], bytes_after[3]]), - u16::from_le_bytes([bytes_after[4], bytes_after[5]]), - u16::from_le_bytes([bytes_after[6], bytes_after[7]]), - ]; - - let mut value = [0u16; ELEM_SIZE / 2]; - dyn_cells.read_all(write_address, &mut value); - assert_eq!(value, value_before); - dyn_cells.write_all(write_address, &value_after); - dyn_cells.read_all(write_address, &mut value); - assert_eq!(value, value_after); - - let cells = Box::new([byte_before; DYN_REGION_SIZE]); - let dyn_region: ProofDynRegion = ProofDynRegion::bind(cells); - let mut dyn_cells: DynCells> = - DynCells::bind(dyn_region); - - // Perform dynamic memory accesses as bytes - let value_before = [byte_before; ELEM_SIZE]; - - let mut value = [0u8; ELEM_SIZE]; - dyn_cells.read_all(write_address, &mut value); - assert_eq!(value, value_before); - dyn_cells.write_all(write_address, &bytes_after); - dyn_cells.read_all(write_address, &mut value); - assert_eq!(value, bytes_after); - }); - - // Check correct Merkleisation of a dynamic region which was read from and written to - proptest!(|(byte_before: u8, - bytes_after: [u8; ELEM_SIZE], - reads in array::uniform2(&address_range), - writes in array::uniform2(&address_range))| { - let dyn_array = Box::new([byte_before; DYN_REGION_SIZE]); - let owned_dyn_cells: DynCells> = - DynCells::bind(&dyn_array); - let initial_root_hash = - as CommitmentLayout>::state_hash(owned_dyn_cells) - .unwrap(); - - let mut proof_dyn_region: ProofDynRegion> = - ProofDynRegion::bind(&dyn_array); - - // Perform memory accesses - let value_before = [byte_before; ELEM_SIZE]; - reads.iter().for_each(|i| { - let mut value = [0u8; ELEM_SIZE]; - ProofGen::>::dyn_region_read_all(&proof_dyn_region, *i, &mut value); - assert_eq!(value, value_before) - }); - writes.iter().for_each(|i| { - ProofGen::>::dyn_region_write_all( - &mut proof_dyn_region, - *i, - &bytes_after, - ); - }); - - // Build the Merkle tree and check that it has the root hash of the - // initial wrapped region. - let proof_dyn_cells: DynCells>>> = - DynCells::bind(&proof_dyn_region); - let merkle_tree = - as ProofLayout>::to_merkle_tree(proof_dyn_cells).unwrap(); - merkle_tree.check_root_hash(); - prop_assert_eq!(merkle_tree.root_hash(), initial_root_hash); - - // Compute expected access info for each leaf, assuming that an access - // cannot span more than 2 leaves. - let expected_leaves = |accesses: &[usize]| { - let mut leaves = BTreeSet::::new(); - for i in accesses { - leaves.insert(*i / MERKLE_LEAF_SIZE); - leaves.insert((i + ELEM_SIZE - 1) / MERKLE_LEAF_SIZE); - } - leaves - }; - let read_leaves = expected_leaves(&reads); - let written_leaves = expected_leaves(&writes); - - // Traverse the generated Merkle tree and check each leaf's access log - let mut queue = VecDeque::with_capacity(LEAVES + 1); - queue.push_back(merkle_tree); - let mut leaf: usize = 0; - while let Some(node) = queue.pop_front() { - match node { - MerkleTree::Node(_, children) => queue.extend(children), - MerkleTree::Leaf(_, access_info, _) => { - assert_eq!( - access_info, - read_leaves.contains(&leaf) || - written_leaves.contains(&leaf) - ); - leaf += 1; - } - } - } - }); - } - - #[test] - fn test_proof_gen_enriched_cell() { - pub struct Enriching; - - impl EnrichedValue for Enriching { - type E = u64; - type D = T; - } - - #[derive(Clone, Copy, Debug, PartialEq)] - pub struct T(u64); - - impl<'a> From<&'a u64> for T { - fn from(value: &'a u64) -> Self { - T(value.wrapping_add(1)) - } - } - - proptest!(|(value_before: u64, value_after: u64)| { - // A read followed by a write - let value = [value_before]; - let mut proof_cell: ProofEnrichedCell> = ProofEnrichedCell::bind(&value); - prop_assert!(!proof_cell.get_access_info()); - let value = ProofGen::>::enriched_cell_read_stored(&proof_cell); - prop_assert_eq!(value, value_before); - let derived = ProofGen::>::enriched_cell_read_derived(&proof_cell); - prop_assert_eq!(derived, T::from(&value_before)); - prop_assert!(proof_cell.get_access_info()); - ProofGen::>::enriched_cell_write(&mut proof_cell, value_after); - prop_assert!(proof_cell.get_access_info()); - - // A write followed by a read - let value = [value_before]; - let mut proof_cell: ProofEnrichedCell> = ProofEnrichedCell::bind(&value); - prop_assert!(!proof_cell.get_access_info()); - ProofGen::>::enriched_cell_write(&mut proof_cell, value_after); - prop_assert!(proof_cell.get_access_info()); - let value = ProofGen::>::enriched_cell_read_stored(&proof_cell); - prop_assert_eq!(value, value_after); - let derived = ProofGen::>::enriched_cell_read_derived(&proof_cell); - prop_assert_eq!(derived, T::from(&value_after)); - prop_assert!(proof_cell.get_access_info()); - }); - } - - #[test] - fn test_proof_gen_region_replace() { - let region: ProofRegion = ProofRegion::bind([0u64; 1]); - let mut cells: Cells> = Cells::bind(region); - - cells.write(0, 13); - - let old = cells.replace(0, 37); - assert_eq!(old, 13); - - let value = cells.read(0); - assert_eq!(value, 37); - } -} diff --git a/src/riscv/lib/src/state_backend/proof_backend/merkle.rs b/src/riscv/lib/src/state_backend/proof_backend/merkle.rs deleted file mode 100644 index 917a9ca4f6197572182751f59797f33785327f71..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend/merkle.rs +++ /dev/null @@ -1,707 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Merkle trees used for proof generation by the PVM - -use std::convert::Infallible; -use std::num::NonZeroUsize; - -use super::DynAccess; -use super::proof::MerkleProof; -use super::proof::MerkleProofLeaf; -use crate::state_backend::hash::Hash; -use crate::state_backend::hash::HashError; -use crate::state_backend::proof_backend::tree::ModifyResult; -use crate::state_backend::proof_backend::tree::impl_modify_map_collect; - -// TODO RV-322: Choose optimal Merkleisation parameters for main memory. -/// Size of the Merkle leaf used for Merkleising [`DynArrays`]. -/// -/// [`DynArrays`]: [`crate::state_backend::layout::DynArray`] -pub const MERKLE_LEAF_SIZE: NonZeroUsize = NonZeroUsize::new(4096).unwrap(); - -// TODO RV-322: Choose optimal Merkleisation parameters for main memory. -/// Arity of the Merkle tree used for Merkleising [`DynArrays`]. -/// -/// [`DynArrays`]: [`crate::state_backend::layout::DynArray`] -pub const MERKLE_ARITY: usize = 4; - -/// A variable-width Merkle tree with access metadata for leaves. -/// -/// Values of this type are produced by the proof-generating backend to capture -/// a snapshot of the machine state along with access information for leaves -/// which hold data that was used in a particular evaluation step. -#[derive(Debug, Clone)] -pub enum MerkleTree { - Leaf(Hash, bool, Vec), - Node(Hash, Vec), -} - -impl MerkleTree { - /// Get the root hash of a Merkle tree - pub fn root_hash(&self) -> Hash { - match self { - Self::Node(hash, _) => *hash, - Self::Leaf(hash, _, _) => *hash, - } - } - - /// Make a Merkle tree consisting of a single leaf; representing - /// the given data and its access pattern. - pub fn make_merkle_leaf(data: Vec, access_info: bool) -> Result { - let hash = Hash::blake2b_hash_bytes(&data)?; - Ok(MerkleTree::Leaf(hash, access_info, data)) - } - - /// Make a Merkle tree consisting of a node which combines the given - /// child trees. - pub fn make_merkle_node(children: Vec) -> Result { - let children_hashes: Vec = children.iter().map(|t| t.root_hash()).collect(); - let node_hash = Hash::combine(children_hashes.as_slice())?; - Ok(MerkleTree::Node(node_hash, children)) - } - - /// Compress into a [`CompressedMerkleTree`]. - /// - /// To fit a proof in a manager operation, the Merkle tree it contains - /// is compressed by folding all fully blindable subtrees (those in which - /// no leaf was accessed) into blinded leaves. - fn compress(self) -> Result { - use CompressedMerkleTree::Leaf as CompresedLeaf; - use CompressedMerkleTree::Node as CompresedNode; - - impl_modify_map_collect( - self, - |subtree| match subtree { - MerkleTree::Leaf(hash, access_info, data) => { - Ok(ModifyResult::LeafStop((hash, access_info, data))) - } - MerkleTree::Node(hash, children) => Ok(ModifyResult::NodeContinue(hash, children)), - }, - |(hash, access, data)| Ok((hash, CompressedAccessInfo::from_access_info(access, data))), - |hash, compact_children| { - let (hashes, compressions) = compact_children - .iter() - .map(|child| { - use CompressedAccessInfo::*; - match child { - CompresedLeaf(hash, access_info) => (hash, match access_info { - NoAccess => Some(access_info.clone()), - ReadWrite(_) => None, - }), - CompresedNode(hash, _) => (hash, None), - } - }) - .unzip::<_, _, Vec, Vec<_>>(); - - // Obtain the compression type of all children - // if all children have been compressed successfully to the same type of leaf. - let compression = compressions - .into_iter() - .reduce(|a, b| (a == b).then_some(a?)) - .flatten(); - - let compressed_tree = match compression { - Some(access) => CompresedLeaf(Hash::combine(hashes.as_slice())?, access), - None => CompresedNode(hash, compact_children), - }; - - Ok(compressed_tree) - }, - ) - } - - /// Produce a minimal Merkle proof on the compressed representation of the - /// given tree. - pub fn to_merkle_proof(self) -> Result { - Ok(self.compress()?.into()) - } -} - -/// Intermediary representation obtained when compressing a [`MerkleTree`]. -/// -/// For the compressed tree, we only care about the data in the non-blinded leaves. -#[derive(Debug, Clone, PartialEq)] -enum CompressedMerkleTree { - Leaf(Hash, CompressedAccessInfo), - Node(Hash, Vec), -} - -impl From for MerkleProof { - fn from(value: CompressedMerkleTree) -> Self { - // Explicitly stating error type to enforce the infallible error type - let res: Result<_, Infallible> = impl_modify_map_collect( - value, - |subtree| match subtree { - CompressedMerkleTree::Leaf(hash, access) => { - Ok(ModifyResult::LeafStop((hash, access))) - } - CompressedMerkleTree::Node(_, children) => { - Ok(ModifyResult::NodeContinue((), children)) - } - }, - |(hash, access)| { - use CompressedAccessInfo::*; - Ok(match access { - NoAccess => MerkleProofLeaf::Blind(hash), - ReadWrite(data) => MerkleProofLeaf::Read(data), - }) - }, - |(), children| Ok(MerkleProof::Node(children)), - ); - - // No panic: error type is infallible - res.unwrap() - } -} - -/// Used in [`impl_modify_map_collect`] -impl From<(Hash, CompressedAccessInfo)> for CompressedMerkleTree { - fn from(data: (Hash, CompressedAccessInfo)) -> CompressedMerkleTree { - CompressedMerkleTree::Leaf(data.0, data.1) - } -} - -/// Type of access associated with leaves in a [`CompressedMerkleTree`]. -/// -/// If a subtree only contains leaves which have not been accessed, it can be -/// compressed into a blinded leaf. Leaves which have been accessed also hold -/// the leaf data. -#[derive(Debug, Clone, PartialEq)] -enum CompressedAccessInfo { - /// A leaf which has not been accessed - NoAccess, - /// A leaf which has been accessed - ReadWrite(Vec), -} - -impl CompressedAccessInfo { - fn from_access_info(access_info: bool, data: Vec) -> Self { - if access_info { - Self::ReadWrite(data) - } else { - Self::NoAccess - } - } -} - -pub trait AccessInfoAggregatable { - /// Aggregate the access information of the Merkle tree described by - /// the layout of the given data, without constructing the tree. - /// - /// Used in implementations of `to_merkle_tree` in which certain leaves can - /// combine data corresponding to multiple layout elements. - fn aggregate_access_info(&self) -> bool; -} - -impl AccessInfoAggregatable for () { - fn aggregate_access_info(&self) -> bool { - false - } -} - -/// Helper function which allows iterating over chunks of a dynamic array -/// and writing them to a writer. The last chunk may be smaller than the -/// Merkle leaf size. The implementations of [`CommitmentLayout`] and -/// [`ProofLayout`] for both use it, ensuring consistency between the two. -/// -/// [`CommitmentLayout`]: crate::state_backend::commitment_layout::CommitmentLayout -/// [`ProofLayout`]: crate::state_backend::proof_layout::ProofLayout -pub(crate) fn chunks_to_writer< - const LEN: usize, - T: std::io::Write, - F: Fn(usize) -> [u8; MERKLE_LEAF_SIZE.get()], ->( - writer: &mut T, - read: F, -) -> Result<(), std::io::Error> { - let merkle_leaf_size = MERKLE_LEAF_SIZE.get(); - assert!(LEN >= merkle_leaf_size); - - let mut address = 0; - - while address + merkle_leaf_size <= LEN { - writer.write_all(read(address).as_slice())?; - address += merkle_leaf_size; - } - - // When the last chunk is smaller than `MERKLE_LEAF_SIZE`, - // read the last `MERKLE_LEAF_SIZE` bytes and pass a subslice containing - // only the bytes not previously read to the writer. - if address != LEN { - address += merkle_leaf_size; - let buffer = read(LEN.saturating_sub(merkle_leaf_size)); - writer.write_all(&buffer[address.saturating_sub(LEN)..])?; - }; - - Ok(()) -} - -/// Writer which splits data in fixed-sized chunks and produces a [`MerkleTree`] -/// with a given arity in which each leaf represents a chunk. -pub struct MerkleWriter { - leaf_size: usize, - arity: usize, - read_log: DynAccess, - write_log: DynAccess, - buffer: Vec, - leaves: Vec, -} - -impl MerkleWriter { - /// Initialise a new writer with a leaf size and arity for the Merkle tree, - /// the access logs of the underlying data, and the expected number of leaves. - /// - /// # Panics - /// Panics if `arity < 2`. - pub fn new( - leaf_size: std::num::NonZeroUsize, - arity: usize, - read_log: DynAccess, - write_log: DynAccess, - expected_leaves: usize, - ) -> Self { - assert!(arity >= 2, "Arity must be at least 2"); - - let leaf_size = leaf_size.get(); - Self { - leaf_size, - arity, - read_log, - write_log, - buffer: Vec::with_capacity(leaf_size), - leaves: Vec::with_capacity(expected_leaves), - } - } - - /// Commit the leaf corresponding to the contents of the buffer before - /// clearing it. - fn flush_buffer(&mut self) -> Result<(), HashError> { - let pos = self.leaves.len() * self.leaf_size; - let range = pos..pos + self.leaf_size; - - // Determine whether addresses in the range of the current buffer - // have been accessed. - let read = self.read_log.includes_range(range.clone()); - let write = self.write_log.includes_range(range); - let access_info = read || write; - - self.leaves.push(MerkleTree::make_merkle_leaf( - self.buffer.clone(), - access_info, - )?); - self.buffer.clear(); - - Ok(()) - } - - /// Finalise the writer by generating the Merkle tree with the configured - /// arity from the stored leaves. The last node in every level might have - /// a smaller arity. - pub fn finalise(mut self) -> Result { - if !self.buffer.is_empty() { - self.flush_buffer()?; - } - - build_custom_merkle_tree(self.arity, self.leaves) - } -} - -impl std::io::Write for MerkleWriter { - fn write(&mut self, mut buf: &[u8]) -> std::io::Result { - let consumed = buf.len(); - - while !buf.is_empty() { - let rem_buffer_len = self.leaf_size - self.buffer.len(); - let new_buf_len = std::cmp::min(rem_buffer_len, buf.len()); - - let new_buf = &buf[..new_buf_len]; - buf = &buf[new_buf_len..]; - self.buffer.extend_from_slice(new_buf); - - // If the buffer has been completely filled, flush it. - if rem_buffer_len == new_buf_len { - self.flush_buffer().map_err(std::io::Error::other)?; - } - } - Ok(consumed) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -impl MerkleTree { - /// Check the validity of the Merkle root by recomputing all hashes - #[cfg(test)] - pub fn check_root_hash(&self) -> bool { - let mut deque = std::collections::VecDeque::new(); - deque.push_back(self); - - while let Some(node) = deque.pop_front() { - let is_valid_hash = match node { - Self::Leaf(hash, _, data) => { - Hash::blake2b_hash_bytes(data).is_ok_and(|h| h == *hash) - } - Self::Node(hash, children) => { - let children_hashes: Vec = children - .iter() - .map(|child| { - deque.push_back(child); - child.root_hash() - }) - .collect(); - - Hash::combine(&children_hashes).is_ok_and(|h| h == *hash) - } - }; - if !is_valid_hash { - return false; - } - } - true - } -} - -/// Build a Merkle tree whose leaves are the elements of `nodes` and in which -/// each node has the given `arity`. -pub(crate) fn build_custom_merkle_tree( - arity: usize, - mut nodes: Vec, -) -> Result { - if nodes.is_empty() { - return Err(HashError::NonEmptyBufferExpected); - } - - let mut next_level = Vec::with_capacity(nodes.len().div_ceil(arity)); - - while nodes.len() > 1 { - for chunk in nodes.chunks(arity) { - next_level.push(MerkleTree::make_merkle_node(chunk.to_vec())?) - } - - std::mem::swap(&mut nodes, &mut next_level); - next_level.truncate(0); - } - - Ok(nodes.pop().unwrap_or_else(|| { - unreachable!( - "After the loop, `nodes` could only have 0 or 1 elements. It had \ - more than 1 element at the beginning of the last iteration of the \ - loop and exactly one element was pushed to it because `nodes.chunks` \ - could not have resulted in 0 chunks for a non-empty vector." - ) - })) -} - -#[cfg(test)] -mod tests { - use std::io::Cursor; - - use proptest::prelude::*; - - use super::CompressedAccessInfo; - use super::CompressedMerkleTree; - use super::MERKLE_LEAF_SIZE; - use super::MerkleTree; - use super::chunks_to_writer; - use crate::state_backend::hash::Hash; - use crate::state_backend::hash::HashError; - use crate::state_backend::proof_backend::proof::MerkleProof; - use crate::state_backend::proof_backend::proof::MerkleProofLeaf; - - impl CompressedMerkleTree { - /// Get the root hash of a compressed Merkle tree - fn root_hash(&self) -> Hash { - match self { - Self::Node(hash, _) => *hash, - Self::Leaf(hash, _) => *hash, - } - } - - /// Check the validity of the Merkle root by recomputing all hashes - fn check_root_hash(&self) -> bool { - let mut deque = std::collections::VecDeque::new(); - deque.push_back(self); - - while let Some(node) = deque.pop_front() { - let is_valid_hash = match node { - Self::Leaf(hash, access_info) => match access_info { - CompressedAccessInfo::NoAccess => true, - CompressedAccessInfo::ReadWrite(data) => { - Hash::blake2b_hash_bytes(data).is_ok_and(|h| h == *hash) - } - }, - Self::Node(hash, children) => { - let children_hashes: Vec = children - .iter() - .map(|child| { - deque.push_back(child); - child.root_hash() - }) - .collect(); - - Hash::combine(&children_hashes).is_ok_and(|h| h == *hash) - } - }; - if !is_valid_hash { - return false; - } - } - true - } - } - - fn m_l(data: &[u8], access: bool) -> Result { - let hash = Hash::blake2b_hash_bytes(data)?; - Ok(MerkleTree::Leaf(hash, access, data.to_vec())) - } - - fn m_t(left: MerkleTree, right: MerkleTree) -> Result { - MerkleTree::make_merkle_node(vec![left, right]) - } - - #[test] - fn test_compression() { - let test = |l: Vec>| -> Result<_, HashError> { - // The LHS leaf will be blinded - let single_leaves_t = m_t(m_l(&l[0], false)?, m_l(&l[1], true)?)?; - - // The whole subtree will be blinded and compressed - let no_access_t = m_t( - m_l(&l[2], false)?, - m_t(m_l(&l[3], false)?, m_l(&l[4], false)?)?, - )?; - - // No leaf will be blinded, the tree will not be compressed - let read_write_3_t = m_t( - m_t(m_l(&l[5], true)?, m_l(&l[6], true)?)?, - m_l(&l[7], true)?, - )?; - - // No leaf will be blinded, the tree will not be compressed - let read_write_4_t = m_t( - m_t(m_l(&l[8], true)?, m_l(&l[9], true)?)?, - m_t(m_l(&l[10], true)?, m_l(&l[11], true)?)?, - )?; - - let combine_isolated_t = m_t(m_t(no_access_t, read_write_3_t)?, read_write_4_t)?; - - let mix_t = m_t( - // The whole subtree will be compressed - m_t( - m_l(&l[12], false)?, - m_t( - m_l(&l[13], false)?, - m_t(m_l(&l[14], false)?, m_l(&l[15], false)?)?, - )?, - )?, - m_t( - m_t( - m_l(&l[16], true)?, - // Only the non-accessed leaves will be compressed - m_t(m_l(&l[17], false)?, m_l(&l[18], false)?)?, - )?, - m_l(&l[19], true)?, - )?, - )?; - - let merkle_tree = m_t(single_leaves_t, m_t(combine_isolated_t, mix_t)?)?; - - let merkle_proof_leaf = - |data: &Vec, access: bool| -> Result { - let hash = Hash::blake2b_hash_bytes(data)?; - Ok(MerkleProof::Leaf(if access { - MerkleProofLeaf::Read(data.clone()) - } else { - MerkleProofLeaf::Blind(hash) - })) - }; - - let proof_single_leaves = MerkleProof::Node(vec![ - merkle_proof_leaf(&l[0], false)?, - merkle_proof_leaf(&l[1], true)?, - ]); - - // The structure of the original subtree is compressed into a single leaf. - let proof_no_access = MerkleProof::Leaf(MerkleProofLeaf::Blind(Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[2])?, - Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[3])?, - Hash::blake2b_hash_bytes(&l[4])?, - ])?, - ])?)); - - let proof_read_write_3 = MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - merkle_proof_leaf(&l[5], true)?, - merkle_proof_leaf(&l[6], true)?, - ]), - merkle_proof_leaf(&l[7], true)?, - ]); - - let proof_read_write_4 = MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - merkle_proof_leaf(&l[8], true)?, - merkle_proof_leaf(&l[9], true)?, - ]), - MerkleProof::Node(vec![ - merkle_proof_leaf(&l[10], true)?, - merkle_proof_leaf(&l[11], true)?, - ]), - ]); - - let proof_combine_isolated = MerkleProof::Node(vec![ - MerkleProof::Node(vec![proof_no_access, proof_read_write_3]), - proof_read_write_4, - ]); - - let proof_mix = MerkleProof::Node(vec![ - // The structure of the original subtree is compressed into a single leaf. - MerkleProof::Leaf(MerkleProofLeaf::Blind(Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[12])?, - Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[13])?, - Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[14])?, - Hash::blake2b_hash_bytes(&l[15])?, - ])?, - ])?, - ])?)), - MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - merkle_proof_leaf(&l[16], true)?, - MerkleProof::Leaf(MerkleProofLeaf::Blind(Hash::combine(&[ - Hash::blake2b_hash_bytes(&l[17])?, - Hash::blake2b_hash_bytes(&l[18])?, - ])?)), - ]), - merkle_proof_leaf(&l[19], true)?, - ]), - ]); - - let proof = MerkleProof::Node(vec![ - proof_single_leaves, - MerkleProof::Node(vec![proof_combine_isolated, proof_mix]), - ]); - - let merkle_tree_root_hash = merkle_tree.root_hash(); - - let compressed_merkle_tree = merkle_tree.clone().compress()?; - assert!(compressed_merkle_tree.check_root_hash()); - assert_eq!(compressed_merkle_tree.root_hash(), merkle_tree_root_hash); - - let compressed_merkle_proof: MerkleProof = compressed_merkle_tree.into(); - assert_eq!(compressed_merkle_proof, proof); - - assert_eq!(merkle_tree.to_merkle_proof().unwrap(), proof); - assert_eq!( - compressed_merkle_proof.root_hash().unwrap(), - merkle_tree_root_hash - ); - - Ok(()) - }; - - // this whole proptest macro delegates to a pure rust function in order to have easy access to formatting - proptest!(|(l in prop::collection::vec( - prop::collection::vec(0u8..255, 0..100), - 20 - ))| { - test(l).expect("Unexpected Hashing error"); - }); - } - - #[test] - fn transform_compressed_merkle_tree_to_proof() { - use CompressedAccessInfo::*; - - let check = |merkle_tree, merkle_proof| { - let proof_from_merkle = MerkleProof::from(merkle_tree); - assert_eq!(proof_from_merkle, merkle_proof); - }; - - let gen_hash_data = || { - let data = rand::random::<[u8; 12]>().to_vec(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - let (data, hash) = gen_hash_data(); - - // Check leaves - check( - CompressedMerkleTree::Leaf(hash, NoAccess), - MerkleProof::Leaf(MerkleProofLeaf::Blind(hash)), - ); - check( - CompressedMerkleTree::Leaf(hash, ReadWrite(data.clone())), - MerkleProof::Leaf(MerkleProofLeaf::Read(data.clone())), - ); - - // Check nodes - let [d0, d1, d2, d3, d4, d5, d6, d7, d8] = [0; 9].map(|_| gen_hash_data()); - let l0 = MerkleProof::Leaf(MerkleProofLeaf::Read(d0.0.clone())); - let l1 = MerkleProof::Leaf(MerkleProofLeaf::Read(d1.0.clone())); - let l2 = MerkleProof::Leaf(MerkleProofLeaf::Read(d2.0.clone())); - let l3 = MerkleProof::Leaf(MerkleProofLeaf::Blind(d3.1)); - let l4 = MerkleProof::Leaf(MerkleProofLeaf::Blind(d4.1)); - let l5 = MerkleProof::Leaf(MerkleProofLeaf::Blind(d5.1)); - - let n6 = MerkleProof::Node(vec![l0, l1, l3]); - let n7 = MerkleProof::Node(vec![l4, l2, l5]); - let root = MerkleProof::Node(vec![n6, n7]); - - let t0 = CompressedMerkleTree::Leaf(d0.1, ReadWrite(d0.0)); - let t1 = CompressedMerkleTree::Leaf(d1.1, ReadWrite(d1.0)); - let t2 = CompressedMerkleTree::Leaf(d2.1, ReadWrite(d2.0)); - let t3 = CompressedMerkleTree::Leaf(d3.1, NoAccess); - let t4 = CompressedMerkleTree::Leaf(d4.1, NoAccess); - let t5 = CompressedMerkleTree::Leaf(d5.1, NoAccess); - - let t6 = CompressedMerkleTree::Node(d6.1, vec![t0, t1, t3]); - let t7 = CompressedMerkleTree::Node(d7.1, vec![t4, t2, t5]); - let t_root = CompressedMerkleTree::Node(d8.1, vec![t6, t7]); - - check(t_root, root); - } - - const LENS: [usize; 4] = [0, 1, 535, 4095]; - const _: () = { - if MERKLE_LEAF_SIZE.get() != 4096 { - panic!( - "Test values in `LENS` assume a specific MERKLE_LEAF_SIZE, change them accordingly" - ); - } - }; - - // Test `chunks_to_writer` with a variety of lengths - macro_rules! generate_test_chunks_to_writer { - ( $name:ident, $i:expr ) => { - proptest! { - #[test] - fn $name( - data in proptest::collection::vec(any::(), 4 * MERKLE_LEAF_SIZE.get() + LENS[$i]) - ) { - const LEN: usize = 4 * MERKLE_LEAF_SIZE.get() + LENS[$i]; - - let read = |pos: usize| { - assert!(pos + MERKLE_LEAF_SIZE.get() <= LEN); - data[pos..pos + MERKLE_LEAF_SIZE.get()].try_into().unwrap() - }; - - let mut writer = Cursor::new(Vec::new()); - chunks_to_writer::(&mut writer, read).unwrap(); - assert_eq!(writer.into_inner(), data); - } - } - } - } - - generate_test_chunks_to_writer!(test_chunks_to_writer_0, 0); - generate_test_chunks_to_writer!(test_chunks_to_writer_1, 1); - generate_test_chunks_to_writer!(test_chunks_to_writer_2, 2); - generate_test_chunks_to_writer!(test_chunks_to_writer_3, 3); -} diff --git a/src/riscv/lib/src/state_backend/proof_backend/proof.rs b/src/riscv/lib/src/state_backend/proof_backend/proof.rs deleted file mode 100644 index 6d5883dab40884fb538020b55af8c6fe1ce9f7a9..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend/proof.rs +++ /dev/null @@ -1,857 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! ## Module for handling proofs -//! -//! - Serialise & Deserialise a [`Proof`] -//! -//! Structure of serialisation: -//! * Final hash state -//! * Tags which dictate the shape of the proof (a partial Merkle tree) -//! * Leaf contents -//! -//! - Convert [`super::merkle::MerkleTree`] to [`MerkleProof`] - -use itertools::Itertools; - -use super::tree::ModifyResult; -use super::tree::Tree; -use super::tree::impl_modify_map_collect; -use crate::state_backend::hash::Hash; -use crate::storage::DIGEST_SIZE; -use crate::storage::HashError; - -pub mod deserialise_owned; -pub mod deserialiser; - -/// Structure of a proof transitioning from state A to state B. -/// -/// The proof needs to be able to: -/// - Contain enough information to be able to run a single step on A -/// - Obtain the hash of the state after the step -#[derive(Clone, Debug, PartialEq)] -pub struct Proof { - /// State of the final state B - final_state_hash: Hash, - /// Partial Merkle tree representation of the initial state - partial_tree: MerkleProof, -} - -impl Proof { - /// Create a proof from a partial Merkle tree and the final state hash. - /// The initial state hash is the root hash of the partial Merkle tree so it is not included separately. - pub fn new(proof: MerkleProof, final_state_hash: Hash) -> Self { - Self { - final_state_hash, - partial_tree: proof, - } - } - - /// Get the proof tree. - pub fn tree(&self) -> &MerkleProof { - &self.partial_tree - } - - /// Get the initial state hash of the proof. - pub fn initial_state_hash(&self) -> Hash { - self.partial_tree - .root_hash() - .expect("Computing the root hash of the Merkle proof should not fail") - } - - /// Get the final state hash of the proof. - pub fn final_state_hash(&self) -> Hash { - self.final_state_hash - } -} - -/// Tag of a node -const TAG_NODE: u8 = 0b00; -/// Tag of a blind leaf -const TAG_BLIND: u8 = 0b10; -/// Tag of a read leaf -const TAG_READ: u8 = 0b11; -/// Bitmask for tags -const TAG_MASK: u8 = 0b11; - -/// Merkle proof tree structure. -/// -/// Leaves can be read and/or written to. -/// If a read was done, the content will be stored in the proof. -/// If a write was done, the written content is not necessary since the semantics of running the step will -/// deduce the written contents. -/// -/// A proof will have the shape of a subtree of a [`MerkleTree`]. -/// The structure of the full [`MerkleTree`] is known statically (since it represents the whole state of the PVM) -/// so the number of children of a node and the sizes of the leaves -/// do not need to be stored in either the proof or its encoding. -/// -/// [`MerkleTree`]: super::merkle::MerkleTree -pub type MerkleProof = Tree; - -/// Type used to describe the leaves of a [`MerkleProof`]. -/// For more details see the documentation of [`MerkleProof`]. -#[derive(Clone, Debug, PartialEq)] -pub enum MerkleProofLeaf { - /// A leaf that is not read. It may be written. - /// Contains the hash of the contents from initial state. - /// - /// Note: a blinded leaf can correspond to a blinded subtree - /// in a [`super::merkle::MerkleTree`] due to compression. - Blind(Hash), - /// A leaf that is read. It may also be written. - /// Contains the read data from the initial state. - /// The initial hash can be deduced based on the read data. - Read(Vec), -} - -impl MerkleProof { - /// Return a 2-bit tag for the variant of the node in the proof. - pub fn to_raw_tag(&self) -> Tag { - match self { - MerkleProof::Node(_) => Tag::Node, - MerkleProof::Leaf(MerkleProofLeaf::Blind(_)) => Tag::Leaf(LeafTag::Blind), - MerkleProof::Leaf(MerkleProofLeaf::Read(_)) => Tag::Leaf(LeafTag::Read), - } - } - - /// Compute the root hash of the Merkle proof. - pub fn root_hash(&self) -> Result { - impl_modify_map_collect( - self, - |subtree| { - Ok(match subtree { - Tree::Node(vec) => ModifyResult::NodeContinue((), vec.iter().collect()), - Tree::Leaf(data) => ModifyResult::LeafStop(data), - }) - }, - |leaf| match leaf { - MerkleProofLeaf::Blind(hash) => Ok(*hash), - MerkleProofLeaf::Read(data) => Hash::blake2b_hash_bytes(data.as_slice()), - }, - |(), leaves| Hash::combine(&leaves), - ) - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Tag { - Node, - Leaf(LeafTag), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LeafTag { - Blind, - Read, -} - -impl From<&MerkleProof> for Tag { - fn from(value: &MerkleProof) -> Self { - match value { - MerkleProof::Node(_) => Tag::Node, - MerkleProof::Leaf(MerkleProofLeaf::Blind(_)) => Tag::Leaf(LeafTag::Blind), - MerkleProof::Leaf(MerkleProofLeaf::Read(_)) => Tag::Leaf(LeafTag::Read), - } - } -} - -impl TryFrom for Tag { - type Error = DeserialiseError; - - fn try_from(value: u8) -> Result { - match value { - TAG_NODE => Ok(Self::Node), - TAG_BLIND => Ok(Self::Leaf(LeafTag::Blind)), - TAG_READ => Ok(Self::Leaf(LeafTag::Read)), - _ => Err(DeserialiseError::InvalidTag), - } - } -} - -impl From for u8 { - fn from(value: Tag) -> Self { - match value { - Tag::Node => TAG_NODE, - Tag::Leaf(leaf_tag) => match leaf_tag { - LeafTag::Blind => TAG_BLIND, - LeafTag::Read => TAG_READ, - }, - } - } -} - -impl From for Tag { - fn from(value: LeafTag) -> Self { - Tag::Leaf(value) - } -} - -fn serialise_raw_tags(raw_tags: impl Iterator) -> Vec { - // Tag serialisation to bytes depends on the number of bits required to hold a raw tag - // Here, a raw tag is 2 bits wide, hence we use 8 / 2 = 4 chunks - raw_tags - .chunks(4) - .into_iter() - .map(|chunk| { - chunk.zip([6, 4, 2, 0]).fold(0, |acc: u8, (tag, offset)| { - let bits = u8::from(tag) << offset; - acc | bits - }) - }) - .collect() -} - -fn iter_raw_tags(proof: &MerkleProof) -> impl Iterator + '_ { - proof.subtree_iterator().map(|subtree| subtree.to_raw_tag()) -} - -fn serialise_proof_values(proof: &MerkleProof) -> impl Iterator + '_ { - proof - .subtree_iterator() - .flat_map(move |subtree| match subtree { - MerkleProof::Leaf(MerkleProofLeaf::Read(vec)) => vec, - MerkleProof::Leaf(MerkleProofLeaf::Blind(hash)) => hash.as_ref(), - MerkleProof::Node(_) => &[], - }) - .copied() -} - -/// Serialise a Merkle proof to an array of bytes. -/// -/// In the encoding, lengths are not necessary, but tags are, -/// since the tags depend on runtime information and events -pub fn serialise_proof(proof: &Proof) -> impl Iterator + '_ { - // Here we collect the `iter_raw_tags` iterator to be able to chunkify it and transform it - // by compressing the tags to a byte-array, fully utilising the bytes capacity. - let final_hash_encoding = proof.final_state_hash.as_ref().iter().copied(); - let tags_encoding = serialise_raw_tags(iter_raw_tags(&proof.partial_tree)).into_iter(); - let nodes_encoding = serialise_proof_values(&proof.partial_tree); - - final_hash_encoding - .chain(tags_encoding) - .chain(nodes_encoding) -} - -#[derive(Debug, PartialEq)] -pub enum DeserialiseError { - ExpectedLeaf, - InvalidTag, - NotEnoughBytes, - TooManyBytes, -} - -/// Tree structure specifying the shape of a Merkle tree. -/// -/// Leaves contain a [`usize`] representing the amount of bytes -/// held in the Merkle tree leaf as raw data. -pub type Shape = Tree; - -impl Shape { - /// Get total amount of bytes in the whole [`Shape`] held in leaves. - fn total_leaf_bytes(&self) -> usize { - match self { - Tree::Node(vec) => vec.iter().map(Shape::total_leaf_bytes).sum(), - Tree::Leaf(size) => *size, - } - } -} - -fn deserialise_final_hash_encoding( - bytes: &mut impl Iterator, -) -> Result { - let mut digest = [0; DIGEST_SIZE]; - for b in digest.iter_mut() { - match bytes.next() { - None => return Err(DeserialiseError::NotEnoughBytes), - Some(byte) => *b = byte, - } - } - Ok(Hash::from(digest)) -} - -fn deserialise_merkle_proof_tags( - bytes: &mut impl Iterator, - full_shape: Shape, -) -> Result, DeserialiseError> { - let mut buffered_raw_tags = vec![]; - let mut get_next_tag = || -> Result { - if buffered_raw_tags.is_empty() { - let byte = bytes.next().ok_or(DeserialiseError::NotEnoughBytes)?; - buffered_raw_tags = [0, 2, 4, 6] - .map(|offset| (byte >> offset) & TAG_MASK) - .to_vec(); - } - - debug_assert!(!buffered_raw_tags.is_empty()); - - let raw_tag = buffered_raw_tags.pop().unwrap(); - Tag::try_from(raw_tag) - }; - - full_shape.modify_shape(&mut |subtree| { - let tag = get_next_tag()?; - - match tag { - Tag::Leaf(leaf_tag) => match subtree { - Tree::Node(_) => Ok(ModifyResult::LeafStop(( - subtree.total_leaf_bytes(), - leaf_tag, - ))), - Tree::Leaf(size) => Ok(ModifyResult::LeafStop((size, leaf_tag))), - }, - Tag::Node => match subtree { - Tree::Node(children) => Ok(ModifyResult::NodeContinue((), children)), - Tree::Leaf(_) => Err(DeserialiseError::ExpectedLeaf), - }, - } - }) -} - -fn deserialise_merkle_proof_values( - bytes: &mut impl Iterator, - shape: Tree<(usize, LeafTag)>, -) -> Result { - shape.map(&mut |(size, tag)| { - Ok(match tag { - LeafTag::Blind => MerkleProofLeaf::Blind(deserialise_final_hash_encoding(bytes)?), - LeafTag::Read => { - let mut data = Vec::with_capacity(size); - for _ in 0..size { - data.push(bytes.next().ok_or(DeserialiseError::NotEnoughBytes)?); - } - MerkleProofLeaf::Read(data) - } - }) - }) -} - -fn deserialise_merkle_proof( - bytes: &mut impl Iterator, - full_shape: Shape, -) -> Result { - let proof_shape = deserialise_merkle_proof_tags(bytes, full_shape)?; - deserialise_merkle_proof_values(bytes, proof_shape) -} - -/// Given a [`Iterator`], parse the contents into a [`Proof`]. -/// -/// A [`Shape`] of the full Merkle tree has to be provided to account for the proof being -/// only a subtree of the actual full Merkle tree. -/// Knowing the initial shape statically helps improve size of the encoding by -/// not needing to know arity of different subtrees / different leaf sizes. -/// -/// Ideally, the shape should be a compile time type/argument. -/// However, for ease of implementation currently it is given in the form of a [`Shape`] argument -pub fn deserialise_proof( - mut bytes: impl Iterator, - full_merkle_tree_shape: Shape, -) -> Result { - let final_hash = deserialise_final_hash_encoding(&mut bytes)?; - let merkle_proof = deserialise_merkle_proof(&mut bytes, full_merkle_tree_shape)?; - - // we expect the iterator to be fully consumed when deserialising a proof - if bytes.next().is_some() { - return Err(DeserialiseError::TooManyBytes); - } - - Ok(Proof::new(merkle_proof, final_hash)) -} - -#[cfg(test)] -mod tests { - use proptest::proptest; - use rand::Fill; - use rand::thread_rng; - - use super::DeserialiseError; - use super::MerkleProof; - use super::MerkleProofLeaf; - use super::Shape; - use super::TAG_BLIND; - use super::TAG_NODE; - use super::TAG_READ; - use super::serialise_proof; - use crate::state_backend::proof_backend::proof::Proof; - use crate::state_backend::proof_backend::proof::deserialise_proof; - use crate::storage::DIGEST_SIZE; - use crate::storage::Hash; - - /// Utility struct that computes the bounds of a [`MerkleProof`] serialisation - /// based on total number of nodes in the tree and total size of raw data in the leafs. - struct SerialisationBound { - nodes_count: u64, - content_size: u64, - } - - impl SerialisationBound { - /// Compute equivalent bound of a node having the children given from the argument. - fn node_combine(children: Vec) -> SerialisationBound { - children - .into_iter() - // The base fold value has node count 1 to account for the node combining the children - .fold( - SerialisationBound { - nodes_count: 1, - content_size: 0, - }, - |a, b| SerialisationBound { - nodes_count: a.nodes_count + b.nodes_count, - content_size: a.content_size + b.content_size, - }, - ) - } - - fn expected_serialisation_length(&self) -> usize { - let hashes_size = 2 * DIGEST_SIZE as u64; - // div_ceil - let tags_size = self.nodes_count / 4 + (self.nodes_count % 4 != 0) as u64; - (hashes_size + tags_size + self.content_size) as usize - } - - /// Generate the serialisation bound for a [`MerkleProof`] leaf. - fn from_merkle_leaf(leaf: &MerkleProof) -> Self { - match leaf { - MerkleProof::Node(_) => panic!("Expected a Merkle proof leaf"), - MerkleProof::Leaf(MerkleProofLeaf::Blind(hash)) => Self { - nodes_count: 1, - content_size: hash.as_ref().len() as u64, - }, - MerkleProof::Leaf(MerkleProofLeaf::Read(data)) => Self { - nodes_count: 1, - content_size: data.len() as u64, - }, - } - } - } - - fn generate_rand_leaf() -> MerkleProof { - let is_leaf_read = rand::random::(); - let length: usize = rand::random::() % 100 + 1; - - let mut raw_array = vec![0; length]; - raw_array.try_fill(&mut rand::thread_rng()).unwrap(); - let blind_hash: Hash = Hash::blake2b_hash_bytes(&raw_array).unwrap(); - - match is_leaf_read { - true => MerkleProof::Leaf(MerkleProofLeaf::Read(raw_array)), - false => MerkleProof::Leaf(MerkleProofLeaf::Blind(blind_hash)), - } - } - - fn check_serialisation(tree: MerkleProof, tree_correct_bytes: &[u8]) { - let final_state_hash = Hash::blake2b_hash_bytes(&rand::random::<[u8; 10]>()).unwrap(); - let proof = Proof::new(tree, final_state_hash); - - let ser_bytes: Vec = serialise_proof(&proof).collect(); - assert_eq!( - ser_bytes.as_slice(), - &[final_state_hash.as_ref(), tree_correct_bytes].concat() - ); - } - - #[test] - fn serialise_leaf_trees() { - // Check serialisation of all leaf variants - - let raw_array: [u8; 10] = rand::random(); - - let rleaf = MerkleProof::Leaf(MerkleProofLeaf::Read(raw_array.to_vec())); - check_serialisation(rleaf, &[&[TAG_READ << 6], raw_array.as_slice()].concat()); - - let hash = Hash::blake2b_hash_bytes(&raw_array).unwrap(); - check_serialisation( - MerkleProof::Leaf(MerkleProofLeaf::Blind(hash)), - &[&[TAG_BLIND << 6], hash.as_ref()].concat(), - ); - } - - #[test] - fn serialise_1_level() { - // Check serialisation of a node containing some leaves. - - // To allow more readable arrays of bit manipulated values - #![allow(clippy::identity_op)] - - let h1 = Hash::blake2b_hash_bytes(&[1, 2, 3]).unwrap(); - let h2 = Hash::blake2b_hash_bytes(&[20, 30, 1, 5, 6]).unwrap(); - - let n1 = MerkleProof::Leaf(MerkleProofLeaf::Read(vec![12, 15, 30, 40])); - let n2 = MerkleProof::Leaf(MerkleProofLeaf::Blind(h1)); - let n3 = MerkleProof::Leaf(MerkleProofLeaf::Blind(h2)); - let n4 = MerkleProof::Leaf(MerkleProofLeaf::Read(vec![123, 234, 42, 1, 2, 3])); - - let root = MerkleProof::Node(vec![n1.clone()]); - check_serialisation(root, &[(TAG_NODE << 6) | (TAG_READ << 4), 12, 15, 30, 40]); - - let root = MerkleProof::Node(vec![n1.clone(), n2.clone()]); - check_serialisation( - root, - &[ - [(TAG_NODE << 6) | (TAG_READ << 4) | (TAG_BLIND << 2)].as_ref(), - &[12, 15, 30, 40], - h1.as_ref(), - ] - .concat(), - ); - - let root = MerkleProof::Node(vec![n1.clone(), n2.clone(), n3.clone()]); - check_serialisation( - root, - &[ - [(TAG_NODE << 6) | (TAG_READ << 4) | (TAG_BLIND << 2) | TAG_BLIND].as_ref(), - &[12, 15, 30, 40], - h1.as_ref(), - h2.as_ref(), - ] - .concat(), - ); - - let root = MerkleProof::Node(vec![n1.clone(), n2.clone(), n4.clone(), n3.clone()]); - check_serialisation( - root, - &[ - [ - (TAG_NODE << 6) | (TAG_READ << 4) | (TAG_BLIND << 2) | TAG_READ, - TAG_BLIND << 6, - ] - .as_ref(), - &[12, 15, 30, 40], - h1.as_ref(), - &[123, 234, 42, 1, 2, 3], - h2.as_ref(), - ] - .concat(), - ) - } - - fn check_bounds(tree: MerkleProof, bound: &SerialisationBound) { - let final_state_hash = Hash::blake2b_hash_bytes(&rand::random::<[u8; 10]>()).unwrap(); - let proof = Proof::new(tree, final_state_hash); - - let serialisation: Vec<_> = super::serialise_proof(&proof).collect(); - assert!(serialisation.len() <= bound.expected_serialisation_length()); - } - - #[test] - fn bounds_1_level() { - // Check size of the serialisations for a node having a number of leaves. - - for i in 1..20 { - let children: Vec<_> = (0..i).map(|_| generate_rand_leaf()).collect(); - let bounds = children - .iter() - .map(SerialisationBound::from_merkle_leaf) - .collect(); - - let root = MerkleProof::Node(children); - let bound = SerialisationBound::node_combine(bounds); - - check_bounds(root, &bound); - } - } - - #[test] - fn bounds_n_levels() { - // Check size of serialisation of a randomly generated Merkle tree. - - // Starting from an array of leaves, combine a randomly generated number of consecutive nodes - // into a node for the level above, This will continue until only a node is left, which is the root. - - proptest!(|(total_length in 100..300)| { - let mut nodes: Vec<_> = (0..total_length).map(|_| { - let leaf = generate_rand_leaf(); - let bound = SerialisationBound::from_merkle_leaf(&leaf); - (leaf, bound) - }).collect(); - - while nodes.len() > 1 { - let mut new_nodes = vec![]; - let mut iter = nodes.into_iter(); - - let mut nothing_taken = false; - while !nothing_taken { - // wanted number of children of a node is between 2 and 10. - // (Note, if only a node is left, then there will be only one child) - let nr_children = rand::random::() % 8 + 2; - - let mut new_children = vec![]; - let mut new_bounds = vec![]; - for _ in 0..nr_children { - if let Some((child, bound)) = iter.next() { - new_children.push(child); - new_bounds.push(bound); - } - } - - nothing_taken = new_bounds.is_empty(); - - if !nothing_taken { - let node = MerkleProof::Node(new_children); - let bound = SerialisationBound::node_combine(new_bounds); - - new_nodes.push((node, bound)); - } - } - - nodes = new_nodes; - } - - let (root, bound) = &nodes[0]; - check_bounds(root.clone(), bound); - }); - } - - fn check_deserialisation(raw_bytes: &[u8], full_shape: Shape, expected_proof: Proof) { - let proof = deserialise_proof(raw_bytes.iter().copied(), full_shape).unwrap(); - assert_eq!(proof, expected_proof); - } - - fn check_bad_deserialisation( - raw_bytes: &[u8], - full_shape: Shape, - expected_error: DeserialiseError, - ) { - let proof = deserialise_proof(raw_bytes.iter().copied(), full_shape); - assert_eq!(proof, Err(expected_error)); - } - - #[test] - fn deserialise_leaf_trees() { - // Serialisation represents a leaf - // Original shape can be a leaf or a tree with multiple children. - - let gen_hash_data = |length| { - let mut data = vec![0; length]; - data.try_fill(&mut thread_rng()).unwrap(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - // Blind leaf, given shape is exact / bigger - let final_hash_bytes = rand::random::<[u8; DIGEST_SIZE]>(); - let (data, hash) = gen_hash_data(12); - let bytes = [final_hash_bytes.as_ref(), &[TAG_BLIND << 6], hash.as_ref()].concat(); - // When a leaf / subtree is blinded, the size is disregarded - let shape = Shape::Leaf(40); - let bigger_shape = Shape::Node(vec![ - Shape::Leaf(30), - Shape::Node(vec![Shape::Leaf(1), Shape::Leaf(10)]), - ]); - let exp_proof = Proof::new( - MerkleProof::Leaf(MerkleProofLeaf::Blind(hash)), - final_hash_bytes.into(), - ); - - check_deserialisation(&bytes, shape, exp_proof.clone()); - check_deserialisation(&bytes, bigger_shape, exp_proof); - - // Read leaf - let bytes = [final_hash_bytes.as_ref(), &[TAG_READ << 6], &data].concat(); - let shape = Shape::Leaf(12); - let bigger_shape = Shape::Node(vec![ - Shape::Leaf(5), - Shape::Node(vec![Shape::Leaf(4), Shape::Leaf(3)]), - ]); - let exp_proof = Proof::new( - MerkleProof::Leaf(MerkleProofLeaf::Read(data.clone())), - final_hash_bytes.into(), - ); - - check_deserialisation(&bytes, shape, exp_proof.clone()); - check_deserialisation(&bytes, bigger_shape, exp_proof); - } - - #[test] - fn deserialise_trees_exact_shape() { - // Serialisation represents (((B),(R-40)), ((R-32),(B),(R-16)), (B)) - // 1 2 3 4 5 6 - // Traversal will be: root (12) 1 2 (345) 3 4 5 6 - // Originial shapes - - // For readability (tag << 0 operations) - #![allow(clippy::identity_op)] - - let gen_hash_data = |length| { - let mut data = vec![0; length]; - data.try_fill(&mut thread_rng()).unwrap(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - let (_, fh) = gen_hash_data(32); - let [(_, h0), (d1, _), (d2, _), (_, h3), (d4, _), (_, h5)] = - [16, 40, 32, 100, 16, 200].map(gen_hash_data); - let tag_bytes = [ - (TAG_NODE << 6) | (TAG_NODE << 4) | (TAG_BLIND << 2) | (TAG_READ << 0), - (TAG_NODE << 6) | (TAG_READ << 4) | (TAG_BLIND << 2) | (TAG_READ << 0), - TAG_BLIND << 6, - ]; - let val_bytes = [h0.as_ref(), &d1, &d2, h3.as_ref(), &d4, h5.as_ref()].concat(); - - let n1 = Shape::Node(vec![Shape::Leaf(32), Shape::Leaf(40)]); - let n2 = Shape::Node(vec![Shape::Leaf(32), Shape::Leaf(10), Shape::Leaf(16)]); - let n3 = Shape::Leaf(100); - let root = Shape::Node(vec![n1, n2, n3]); - - let merkle_proof = MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Blind(h0)), - MerkleProof::Leaf(MerkleProofLeaf::Read(d1)), - ]), - MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Read(d2)), - MerkleProof::Leaf(MerkleProofLeaf::Blind(h3)), - MerkleProof::Leaf(MerkleProofLeaf::Read(d4)), - ]), - MerkleProof::Leaf(MerkleProofLeaf::Blind(h5)), - ]); - - let raw_bytes = [fh.as_ref(), &tag_bytes, &val_bytes].concat(); - check_deserialisation(&raw_bytes, root.clone(), Proof::new(merkle_proof, fh)); - - // check not enough bytes errors are generated - // values cutoff - check_bad_deserialisation( - &raw_bytes[..100], - root.clone(), - DeserialiseError::NotEnoughBytes, - ); - // tags cutoff - check_bad_deserialisation( - &raw_bytes[..33], - root.clone(), - DeserialiseError::NotEnoughBytes, - ); - // hash cutoff - check_bad_deserialisation( - &raw_bytes[..20], - root.clone(), - DeserialiseError::NotEnoughBytes, - ); - } - - #[test] - fn deserialise_trees_bigger_shape() { - // Serialisation represents ( ( ((R-20),(B)),(B),(R-16) ), ( (R-30),(B) ) ) - // 1 2 3 4 5 6 - // Traversal will be: root (1234) (12) 1 2 3 4 (56) 5 6 - // Originial shapes - - // For readability (tag << 0 operations) - #![allow(clippy::identity_op)] - - let gen_hash_data = |length| { - let mut data = vec![0; length]; - data.try_fill(&mut thread_rng()).unwrap(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - let (_, fh) = gen_hash_data(32); - let [(d0, _), (_, h1), (_, h2), (d3, _), (d4, _), (_, h5)] = - [20, 100, 100, 16, 30, 200].map(gen_hash_data); - let tag_bytes = [ - (TAG_NODE << 6) | (TAG_NODE << 4) | (TAG_NODE << 2) | (TAG_READ << 0), - (TAG_BLIND << 6) | (TAG_BLIND << 4) | (TAG_READ << 2) | (TAG_NODE << 0), - (TAG_READ << 6) | (TAG_BLIND << 4), - ]; - let val_bytes = [&d0, h1.as_ref(), h2.as_ref(), &d3, &d4, h5.as_ref()].concat(); - - let blind_subtree_1 = Shape::Node(vec![ - Shape::Node(vec![Shape::Leaf(200), Shape::Leaf(300)]), - Shape::Leaf(400), - Shape::Node(vec![Shape::Leaf(50), Shape::Leaf(50), Shape::Leaf(50)]), - ]); - let blind_subtree_2 = Shape::Node(vec![ - Shape::Node(vec![Shape::Leaf(200), Shape::Leaf(300)]), - Shape::Leaf(400), - Shape::Node(vec![ - Shape::Leaf(50), - Shape::Node(vec![Shape::Leaf(50), Shape::Leaf(400)]), - Shape::Leaf(50), - ]), - ]); - let subtree_size_16 = Shape::Node(vec![ - Shape::Leaf(10), - Shape::Node(vec![Shape::Leaf(2), Shape::Leaf(4)]), - ]); - - let n1 = Shape::Node(vec![Shape::Leaf(20), blind_subtree_1]); - let n11 = Shape::Node(vec![n1, blind_subtree_2, subtree_size_16]); - let n12 = Shape::Node(vec![Shape::Leaf(30), Shape::Leaf(100)]); - let root = Shape::Node(vec![n11, n12]); - - let merkle_proof = MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Read(d0.clone())), - MerkleProof::Leaf(MerkleProofLeaf::Blind(h1)), - ]), - MerkleProof::Leaf(MerkleProofLeaf::Blind(h2)), - MerkleProof::Leaf(MerkleProofLeaf::Read(d3.clone())), - ]), - MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Read(d4.clone())), - MerkleProof::Leaf(MerkleProofLeaf::Blind(h5)), - ]), - ]); - - let raw_bytes = [fh.as_ref(), &tag_bytes, &val_bytes].concat(); - check_deserialisation(&raw_bytes, root.clone(), Proof::new(merkle_proof, fh)); - - // ExpectedLeaf tag error - let tag_bytes_bad = [ - (TAG_NODE << 6) | (TAG_NODE << 4) | (TAG_NODE << 2) | (TAG_READ << 0), - (TAG_BLIND << 6) | (TAG_NODE << 4) | (TAG_READ << 2) | (TAG_NODE << 0), - (TAG_READ << 6) | (TAG_BLIND << 4), - ]; - let raw_bytes = [fh.as_ref(), &tag_bytes_bad, &val_bytes].concat(); - check_bad_deserialisation(&raw_bytes, root.clone(), DeserialiseError::ExpectedLeaf); - } - - #[test] - fn deserialise_too_many_bytes() { - let gen_hash_data = || { - let data = rand::random::<[u8; 12]>().to_vec(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - let (data, _hash) = gen_hash_data(); - let final_hash_bytes = rand::random::<[u8; DIGEST_SIZE]>(); - - let shape = Shape::Leaf(12); - - let extra_bytes = [120, 200, 50, 10]; - let bytes = [ - final_hash_bytes.as_ref(), - &[TAG_READ << 6], - &data, - &extra_bytes, - ] - .concat(); - - check_bad_deserialisation(&bytes, shape, DeserialiseError::TooManyBytes); - } - - #[test] - fn deserialise_invalid_tag() { - let gen_hash_data = || { - let data = rand::random::<[u8; 12]>().to_vec(); - let hash = Hash::blake2b_hash_bytes(&data).unwrap(); - (data, hash) - }; - - let (_data, hash) = gen_hash_data(); - let final_hash_bytes = rand::random::<[u8; DIGEST_SIZE]>(); - - let shape = Shape::Leaf(12); - - let bytes = [final_hash_bytes.as_ref(), &[0b01 << 6], hash.as_ref()].concat(); - - check_bad_deserialisation(&bytes, shape, DeserialiseError::InvalidTag); - } -} diff --git a/src/riscv/lib/src/state_backend/proof_backend/proof/deserialise_owned.rs b/src/riscv/lib/src/state_backend/proof_backend/proof/deserialise_owned.rs deleted file mode 100644 index 445992d1f8b8d5c3a793dc3789124ce60fd4838e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend/proof/deserialise_owned.rs +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::collections::VecDeque; -use std::marker::PhantomData; - -use serde::de::DeserializeOwned; - -use super::deserialiser::DeserError; -use super::deserialiser::Deserialiser; -use super::deserialiser::DeserialiserNode; -use super::deserialiser::Partial; -use super::deserialiser::Result; -use super::deserialiser::Suspended; -use crate::state_backend::FromProofError; -use crate::state_backend::ProofPart; -use crate::state_backend::ProofTree; -use crate::state_backend::proof_backend::proof::MerkleProofLeaf; -use crate::state_backend::proof_backend::tree::Tree; -use crate::storage::binary; - -/// Deserialiser for [`Deserialiser`] which owns the data. -pub struct ProofTreeDeserialiser<'t>(ProofTree<'t>); - -impl<'t> Deserialiser for ProofTreeDeserialiser<'t> { - type Suspended = OwnedParserComb<'t, R>; - - type DeserialiserNode = OwnedBranchComb<'t, R, Self>; - - fn into_leaf_raw(self) -> Result>>> { - self.deserialise_as_leaf()? - .map_present_fallible(|data| { - let data_len = data.len(); - let bytes: Box<[u8; LEN]> = - data.try_into() - .map_err(|_| DeserError::UnexpectedLeafSize { - expected: LEN, - got: data_len, - })?; - Ok(bytes) - }) - .map(OwnedParserComb::new) - } - - fn into_leaf(self) -> Result>> { - self.deserialise_as_leaf()? - .map_present_fallible(|data| Ok(binary::deserialise::(data.as_ref())?)) - .map(OwnedParserComb::new) - } - - fn into_node(self) -> Result>> { - let branches = self.deserialise_as_node()?; - Ok(OwnedBranchComb::new(branches)) - } -} - -impl<'t> From> for ProofTreeDeserialiser<'t> { - fn from(proof: ProofTree<'t>) -> Self { - ProofTreeDeserialiser(proof) - } -} - -impl ProofTreeDeserialiser<'_> { - /// Deserialise the proof as a leaf. - pub fn deserialise_as_leaf(self) -> Result>> { - match self.0 { - ProofPart::Absent => Ok(Partial::Absent), - ProofPart::Present(Tree::Node(_)) => Err(FromProofError::UnexpectedNode), - ProofPart::Present(Tree::Leaf(MerkleProofLeaf::Blind(hash))) => { - Ok(Partial::Blinded(*hash)) - } - ProofPart::Present(Tree::Leaf(MerkleProofLeaf::Read(items))) => { - Ok(Partial::Present(items.clone())) - } - } - } - - /// Deserialise the proof as a node. - pub fn deserialise_as_node(self) -> Result>> { - match self.0 { - ProofPart::Absent => Ok(Partial::Absent), - ProofPart::Present(Tree::Leaf(MerkleProofLeaf::Blind(hash))) => { - Ok(Partial::Blinded(*hash)) - } - ProofPart::Present(Tree::Leaf(MerkleProofLeaf::Read(_))) => { - Err(FromProofError::UnexpectedLeaf) - } - ProofPart::Present(Tree::Node(trees)) => Ok(Partial::Present( - trees - .iter() - .map(ProofPart::Present) - .map(ProofTreeDeserialiser) - .collect(), - )), - } - } -} - -/// Suspended computation combinator for [`ProofTreeDeserialiser`] deserialiser. -pub struct OwnedParserComb<'t, R> { - result: R, - _pd: PhantomData)>, -} - -impl OwnedParserComb<'_, R> { - fn new(result: R) -> Self { - Self { - result, - _pd: PhantomData, - } - } -} - -/// Branch deserialiser combinator for [`ProofTreeDeserialiser`] deserialiser. -pub struct OwnedBranchComb<'p, R, B> { - f: OwnedParserComb<'p, R>, - node_data: Partial>, -} - -impl OwnedBranchComb<'_, Partial<()>, B> { - /// Create a new [`OwnedBranchComb`] with the given branches, - /// preserving the absent/blind/present information from the given [`Partial`]. - fn new(branches: Partial>) -> Self { - // Similar to `map_present` but for `&Partial`. - // This is done to preserve absent/blind/present information from node until calling `done()`. - // See test_blind_node_parsing for an example. - let f_comb = match &branches { - Partial::Absent => Partial::Absent, - Partial::Blinded(hash) => Partial::Blinded(*hash), - Partial::Present(_) => Partial::Present(()), - }; - - Self { - f: OwnedParserComb::new(f_comb), - node_data: branches.map_present(VecDeque::from), - } - } -} - -impl<'t, R> DeserialiserNode for OwnedBranchComb<'t, R, ProofTreeDeserialiser<'t>> { - type Parent = ProofTreeDeserialiser<'t>; - - fn next_branch( - mut self, - branch_deserialiser: impl FnOnce( - Self::Parent, - ) - -> Result<::Suspended>, - ) -> Result<::DeserialiserNode<(R, T)>> - where - R: 'static, - T: 'static, - { - let next_branch = match self.node_data { - // If the node is absent or blinded, the branch to be deserialised as a tree is absent. - Partial::Absent | Partial::Blinded(_) => ProofTreeDeserialiser(ProofTree::Absent), - Partial::Present(ref mut branches) => { - branches - .pop_front() - .ok_or(DeserError::BadNumberOfBranches { - expected: 1, - got: 0, - })? - } - }; - let br_comb = branch_deserialiser(next_branch)?; - - Ok(OwnedBranchComb { - f: self.f.zip(br_comb), - node_data: self.node_data, - }) - } - - fn map( - self, - f: impl FnOnce(R) -> T + 'static, - ) -> ::DeserialiserNode - where - T: 'static, - R: 'static, - { - OwnedBranchComb { - f: self.f.map(f), - node_data: self.node_data, - } - } - - fn done(self) -> Result<::Suspended> { - if let Partial::Present(branches) = self.node_data { - if !branches.is_empty() { - let length = branches.len(); - return Err(DeserError::BadNumberOfBranches { - expected: 0, - got: length, - }); - } - } - - Ok(self.f) - } -} - -impl<'t, R> Suspended for OwnedParserComb<'t, R> { - type Output = R; - - type Parent = ProofTreeDeserialiser<'t>; - - fn map( - self, - f: impl FnOnce(Self::Output) -> T + 'static, - ) -> ::Suspended - where - Self::Output: 'static, - { - OwnedParserComb::new(f(self.result)) - } - - fn zip( - self, - other: ::Suspended, - ) -> ::Suspended<(Self::Output, T)> - where - Self::Output: 'static, - T: 'static, - { - OwnedParserComb::new((self.result, other.result)) - } -} - -impl OwnedParserComb<'_, R> { - pub fn into_result(self) -> R { - self.result - } -} diff --git a/src/riscv/lib/src/state_backend/proof_backend/proof/deserialiser.rs b/src/riscv/lib/src/state_backend/proof_backend/proof/deserialiser.rs deleted file mode 100644 index 97edadc0c1785b9929190717711e15ca2a27cb04..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend/proof/deserialiser.rs +++ /dev/null @@ -1,349 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Module for defining traits which facilitate desserialising a tree structure. -//! More precisely, our usecase if for deserialising a Merkle tree either from a [`ProofTree`] or -//! from the raw bytes of a serialisation. -//! -//! Due to the nature of the deserialisation, the structure of the tree is not known until part of the -//! deserialisation is already parsed and the shape is known. This introduces the need for the -//! [`Suspended`] trait which abstracts over a computation to be obtained after parsing just enough from -//! the serialisation to deduce the shape of the tree. -//! -//! [`ProofTree`]: crate::state_backend::ProofTree - -use serde::de::DeserializeOwned; - -use crate::state_backend::FromProofError; -use crate::state_backend::hash::Hash; - -/// Error used when deserialising using [`Deserialiser`] methods -pub type DeserError = FromProofError; - -/// Result type used when deserialising using [`Deserialiser`] methods. -pub type Result = std::result::Result; - -/// Possible outcomes when parsing a node or a leaf from a Merkle proof -/// where the leaf is assumed to have type `T`. -pub enum Partial { - /// The leaf / node is altogether absent from the proof. - Absent, - /// A blinded subtree and its [`struct@Hash`] is provided. - Blinded(Hash), - /// Data successfully parsed and its type is `T`. - Present(T), -} - -impl Partial { - /// Map the present result of a [`Partial`] into [`Partial`]. - pub fn map_present(self, f: impl FnOnce(T) -> R) -> Partial { - match self { - Partial::Absent => Partial::Absent, - Partial::Blinded(hash) => Partial::Blinded(hash), - Partial::Present(data) => Partial::Present(f(data)), - } - } - - /// Same as [`Partial::map_present`] but can fail. - pub fn map_present_fallible( - self, - f: impl FnOnce(T) -> Result, - ) -> Result, E> { - match self { - Partial::Absent => Ok(Partial::Absent), - Partial::Blinded(hash) => Ok(Partial::Blinded(hash)), - Partial::Present(data) => Ok(Partial::Present(f(data)?)), - } - } -} - -/// The main trait used for deserialising a proof. -/// -/// Having an object of this trait is equivalent to having a proof and being able to deserialise it. -/// -/// A proof can be interpreted in 3 cases: -/// 1. [`Deserialiser::into_leaf_raw`] The proof is a leaf and raw bytes are obtained. -/// 2. [`Deserialiser::into_leaf`] The proof is a leaf and the type `T` is parsed. -/// 3. [`Deserialiser::into_node`] The proof is a node in the tree. -pub trait Deserialiser { - /// After deserialising a proof, a [`Suspended`] computation is obtained. - type Suspended: Suspended; - - /// In case the proof is a node, [`Deserialiser::DeserialiserNode`] is the deserialiser for the branch case. - type DeserialiserNode: DeserialiserNode; - - /// It is expected for the proof to be a leaf. Obtain the raw bytes from that leaf. - fn into_leaf_raw(self) -> Result>>>; - - /// It is expected for the proof to be a leaf. Parse the raw bytes of that leaf into a type `T`. - fn into_leaf(self) -> Result>>; - - /// It is expected for the proof to be a node. Obtain the deserialiser for the branch case. - fn into_node(self) -> Result>>; -} - -/// The trait used for deserialising a proof's node. -/// Having an object of this trait is equivalent to knowing the current proof is a node. -/// Deserialisers for each of its branches are expected to be provided to continue the deserialisation. -pub trait DeserialiserNode { - type Parent: Deserialiser; - - /// The next branch of the current node is deserialised using the provided deserialiser `br_deser`. - fn next_branch( - self, - branch_deserialiser: impl FnOnce( - Self::Parent, - ) - -> Result<::Suspended>, - ) -> Result<::DeserialiserNode<(R, T)>> - where - R: 'static, - T: 'static; - - /// Helper for mapping the current result into a new type. - fn map( - self, - f: impl FnOnce(R) -> T + 'static, - ) -> ::DeserialiserNode - where - T: 'static, - R: 'static; - - /// Signal the end of deserialisation of the node's branches. - /// Call this method after all calls to [`DeserialiserNode::next_branch`] have been made. - fn done(self) -> Result<::Suspended>; -} - -/// The trait represents a computation function obtained after deserialising a proof. -pub trait Suspended { - /// End result of the computation. - type Output; - - type Parent: Deserialiser; - - /// Helper to map the current result into a new type. - fn map( - self, - f: impl FnOnce(Self::Output) -> T + 'static, - ) -> ::Suspended - where - Self::Output: 'static; - - /// Helper to zip the current result with another result. - fn zip( - self, - other: ::Suspended, - ) -> ::Suspended<(Self::Output, T)> - where - Self::Output: 'static, - T: 'static; -} -/// Helper trait for transforming `Self`` to a suspended computation from a given serialiser. -pub trait FromProof { - type Output: Sized; - - fn from_proof(de: D) -> Result>; -} - -#[cfg(test)] -mod tests { - - use super::Deserialiser; - use super::DeserialiserNode; - use super::Partial; - use super::Result; - use super::Suspended; - use crate::state_backend::ProofTree; - use crate::state_backend::proof_backend::proof::MerkleProof; - use crate::state_backend::proof_backend::proof::MerkleProofLeaf; - use crate::state_backend::proof_backend::proof::deserialise_owned::ProofTreeDeserialiser; - use crate::state_backend::proof_backend::proof::deserialiser::DeserError; - use crate::storage::Hash; - - fn create_computation( - proof: D, - ) -> Result<::Suspended> { - // The tree structure: - // Node (root) - // ├── Leaf (type: Hash) - // └── Node - // └── Leaf (type: i32) - - // Computation: return the value of the nested leaf - - let ctx = proof.into_node()?; - let r = ctx - .next_branch(|br_proof| br_proof.into_leaf::())? - .map(|(_node_parse, br)| br) - .next_branch(|br_proof| { - br_proof - .into_node()? - .next_branch(|pr| pr.into_leaf::())? - .map(|(_node_parse, br)| br) - .done() - })? - .done()?; - - Ok(r.map(|(_left, right)| match right { - Partial::Absent => 0, - // This blinded hash can be of the nested leaf or the root - Partial::Blinded(_hash) => -1, - Partial::Present(nr) => nr, - })) - } - - fn create_computation_2( - proof: D, - ) -> Result<::Suspended> { - // The tree structure - // Node (root) - // ├── Leaf 1 (type: i32) - // ├── Leaf 2 (type: i32) - // ├── Leaf 3 (type: i32) - // └── Leaf 4 (type: i32) - - // Computation: sum the non-blinded leaves - - let mut ctx = proof - .into_node()? - .map(|data| data.map_present(|_| Vec::::new())); - - for _ in 0..4 { - ctx = ctx - .next_branch(|br_proof| br_proof.into_leaf::())? - .map(|(acc, val)| { - acc.map_present(|mut acc| { - if let Partial::Present(val) = val { - acc.push(val); - } - acc - }) - }) - } - - Ok(ctx.done()?.map(|data| match data { - Partial::Absent => 0, - Partial::Blinded(_hash) => -1, - Partial::Present(data) => data.into_iter().sum(), - })) - } - - #[test] - fn test_absent_computation() { - // Root is absent already - let proof: ProofTreeDeserialiser = ProofTree::Absent.into(); - let comp_fn = create_computation(proof).unwrap(); - assert_eq!(comp_fn.into_result(), 0); - - // We expect to get the Absent case since the father of the nested node is blinded - let merkle_proof = MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Read( - // Note, this is a Read leaf, not a blinded one - Hash::blake2b_hash_bytes(&[0, 1, 2]) - .unwrap() - .as_ref() - .to_vec(), - )), - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[3, 4, 5]).unwrap(), - )), - ]); - let proof: ProofTreeDeserialiser = ProofTree::Present(&merkle_proof).into(); - let comp_fn = create_computation(proof).unwrap(); - assert_eq!(comp_fn.into_result(), 0); - } - - #[test] - fn test_blind_computation() { - // The nested leaf is blinded - let absent_shape = MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[0, 1, 2]).unwrap(), - )), - MerkleProof::Node(vec![MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[0, 1, 2]).unwrap(), - ))]), - ]); - let comp_fn = - create_computation::(ProofTree::Present(&absent_shape).into()); - - let res = comp_fn.unwrap().into_result(); - - assert_eq!(res, -1); - - // For computation_2, the provided merkle proof will resolve as blinded - // since root is blinded - let merkle_proof = MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[6, 7, 8]).unwrap(), - )); - let proof: ProofTreeDeserialiser = ProofTree::Present(&merkle_proof).into(); - let comp_fn = create_computation_2(proof).unwrap(); - assert_eq!(comp_fn.into_result(), -1); - } - - #[test] - fn test_bad_structure() { - let bad_shape_1 = MerkleProof::Node(vec![]); - let bad_shape_2 = MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[0, 1, 2]).unwrap(), - )), - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[0, 1, 2]).unwrap(), - )), - MerkleProof::Node(vec![]), - MerkleProof::Node(vec![]), - MerkleProof::Node(vec![]), - ]); - let bad_shape_3 = MerkleProof::Node(vec![ - MerkleProof::Node(vec![]), - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[0, 1, 2]).unwrap(), - )), - ]); - // Tree is missing branches - let comp_fn = - create_computation::(ProofTree::Present(&bad_shape_1).into()); - assert!(comp_fn.is_err_and(|e| matches!(e, DeserError::BadNumberOfBranches { .. }))); - let comp_fn = - create_computation::(ProofTree::Present(&bad_shape_2).into()); - // First 2 children of root are ok in shape (blinded) but the total number of children does not correspond - // Ideally, we would like to have expected: 2, got: 5, but the implemenetation for `ProofTreeDeserialiser` - // does not track this information (the original number of chilren) - assert!(comp_fn.is_err_and(|e| { - println!("{e:?}"); - matches!(e, DeserError::BadNumberOfBranches { - expected: 0, - got: 3 - }) - })); - let comp_fn = - create_computation::(ProofTree::Present(&bad_shape_3).into()); - // The first child is a node, but is expected to be a leaf - assert!(comp_fn.is_err_and(|e| matches!(e, DeserError::UnexpectedNode))); - } - - #[test] - fn test_absent_node_parsing() {} - - #[test] - fn test_valid_computation() { - let merkleproof = MerkleProof::Node(vec![ - MerkleProof::Leaf(MerkleProofLeaf::Read( - 0x140A_0000_i32.to_le_bytes().to_vec(), - )), - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[3, 4, 5]).unwrap(), - )), - MerkleProof::Leaf(MerkleProofLeaf::Read(0xC0005_i32.to_le_bytes().to_vec())), - MerkleProof::Leaf(MerkleProofLeaf::Blind( - Hash::blake2b_hash_bytes(&[9, 10, 11]).unwrap(), - )), - ]); - - let proof: ProofTreeDeserialiser = ProofTree::Present(&merkleproof).into(); - let comp_fn = create_computation_2(proof).unwrap(); - assert_eq!(comp_fn.into_result(), 0x140A_0000 + 0xC0005); - } -} diff --git a/src/riscv/lib/src/state_backend/proof_backend/tree.rs b/src/riscv/lib/src/state_backend/proof_backend/tree.rs deleted file mode 100644 index c69c0e4e447ed975a363d3f4e4c11784e4614a71..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_backend/tree.rs +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Module for tree utils like types, traversals. -//! -//! All the traversals implemented in this module should be the same to maintain consistency, -//! which is required for serialisation / deserialisation - -use std::convert::Infallible; - -/// Generic tree structure used to model the [`super::proof::MerkleProof`], -/// as well as the full & partial shapes of a [`super::merkle::MerkleTree`]. -#[derive(Clone, Debug, PartialEq)] -pub enum Tree { - Node(Vec), - Leaf(A), -} - -/// Used in [`impl_modify_map_collect`] -impl From for Tree { - fn from(value: D) -> Self { - Tree::Leaf(value) - } -} - -impl Tree { - /// Iterates over all subtrees in a [`Tree`] in a pre-order DFS traversal. - pub fn subtree_iterator(&self) -> impl Iterator> { - let mut stack = vec![self]; - - std::iter::from_fn(move || { - let subtree = stack.pop()?; - - if let Tree::Node(children) = subtree { - stack.extend(children.iter().rev()); - } - - Some(subtree) - }) - } - - /// Modify the shape of a given [`Tree`]. - /// - /// Provide a `modify` function which is called for every subtree in a pre-order DFS traversal. - /// The function returns a [`ModifyResult::LeafStop`] if the subtree will become a leaf - /// or a [`ModifyResult::NodeContinue`] if the subtree will become a non-leaf node - /// and will be further traversed by this algorithm. - pub fn modify_shape) -> Result, B>, E>>( - self, - modify: &mut F, - ) -> Result, E> { - // map & collect are "default" - impl_modify_map_collect( - self, - modify, - |data| Ok(data), - |(), children| Ok(Tree::Node(children)), - ) - } - - /// Perform a shape-preserving map operation over a [`Tree`]. - /// - /// Note: The traversal order should correspond with the one in [`Tree::modify_shape`] and [`Tree::subtree_iterator`]. - pub fn map Result>(self, map: &mut F) -> Result, E> { - // modify & collect are "default" - impl_modify_map_collect( - self, - |subtree| match subtree { - Tree::Node(vec) => Ok(ModifyResult::NodeContinue((), vec)), - Tree::Leaf(data) => Ok(ModifyResult::LeafStop(data)), - }, - map, - |(), children| Ok(Tree::Node(children)), - ) - } - - /// Borrows each leaf in the tree. - pub fn each_ref(&self) -> Tree<&A> { - impl_modify_map_collect::<_, _, _, Infallible, _, _, _, _, _>( - self, - |subtree| match subtree { - Tree::Node(vec) => Ok(ModifyResult::NodeContinue( - (), - // Obtain references to each sub tree. - vec.iter().collect::>(), - )), - Tree::Leaf(data) => Ok(ModifyResult::LeafStop(data)), - }, - Ok, - |(), children| Ok(Tree::Node(children)), - ) - .unwrap() - } -} - -/// Intermediary either-like type for implementing [`impl_modify_map_collect`] -#[derive(Clone)] -pub enum ModifyResult { - /// Current subtree should be replaced with a node containing the given children, - /// and an auxiliary data for extra context if needed. - /// Traversal should continue recursively. - NodeContinue(D, Vec), - /// Current subtree is replaced by a leaf containing the given data. - LeafStop(L), -} - -/// Perform generic modify_map_collect -/// -/// This is done in 3 steps while traversing the tree in a pre-order DFS traversal: -/// 1. Apply `modify` on current subtree: This operation changes the structure of the current -/// subtree before traversing its children. -/// 2. When encountering leaves, `map` is called to transform a leaf from `A` to `B` type. -/// This is done on children of subtrees which have been traversed after `modify` was called. -/// 3. After modifying & mapping the children of a node, the `collect` method gathers the newly -/// modified & mapped subtrees to create the new subtree. -pub fn impl_modify_map_collect< - InterimLeafData, // InterimLeafData -> FinalLeafData when applying `map` - FinalLeafData, // [FinalLeafData] -> FinalLeafData when applying `collect` - AuxTreeData, // Type of auxiliary data held for a subtree - Err, // Error type when applying `modify` / `map` / `collect` - InputTree, - OutputTree: From, - TreeModifier: FnMut(InputTree) -> Result, Err>, - LeafMapper: FnMut(InterimLeafData) -> Result, - Collector: FnMut(AuxTreeData, Vec) -> Result, ->( - root: InputTree, - mut modify: TreeModifier, - mut map: LeafMapper, - mut collect: Collector, -) -> Result { - enum ProcessEvents { - Node(ProcessEvent), - Collect(CollectAuxTreeData, usize), - } - - let mut process = vec![ProcessEvents::Node(root)]; - let mut done: Vec = vec![]; - - while let Some(event) = process.pop() { - match event { - ProcessEvents::Node(subtree) => match modify(subtree)? { - ModifyResult::LeafStop(data) => { - // Instead of pushing a single leaf process on the Process-queue, - // map the data and append it directly to the Done-queue - done.push(OutputTree::from(map(data)?)); - } - ModifyResult::NodeContinue(node_data, children) => { - // the only case where we push further process events in the process queue - // We have to first push a collect event to know how many children should be collected when forming back the current subtree - process.push(ProcessEvents::Collect(node_data, children.len())); - - process.extend( - children - .into_iter() - .rev() - .map(|child| ProcessEvents::Node(child)), - ); - } - }, - ProcessEvents::Collect(node_data, count) => { - // We need to reconstruct a subtree which is made of `count` children - // No panic: We are guaranted count < done.len() since every Collect(size) - // corresponds to size nodes pushed to Done-queue - let children = done.split_off(done.len() - count); - done.push(collect(node_data, children)?); - } - } - } - - // No Panic: We only add a single node as root at the beginning of the algorithm - // which corresponds to this last node in the Done-queue - let new_root = done.pop().unwrap(); - - debug_assert!(done.is_empty()); - - Ok(new_root) -} diff --git a/src/riscv/lib/src/state_backend/proof_layout.rs b/src/riscv/lib/src/state_backend/proof_layout.rs deleted file mode 100644 index dc3513ac516dd257e5f807cf468e33a9bbb7b28c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/proof_layout.rs +++ /dev/null @@ -1,990 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::collections::VecDeque; - -use super::Array; -use super::Atom; -use super::DynArray; -use super::Layout; -use super::Many; -use super::Ref; -use super::RefProofGenOwnedAlloc; -use super::RefVerifierAlloc; -use super::hash::Hash; -use super::hash::HashError; -use super::owned_backend::Owned; -use super::proof_backend::merkle::MERKLE_ARITY; -use super::proof_backend::merkle::MERKLE_LEAF_SIZE; -use super::proof_backend::merkle::MerkleTree; -use super::proof_backend::merkle::MerkleWriter; -use super::proof_backend::merkle::build_custom_merkle_tree; -use super::proof_backend::merkle::chunks_to_writer; -use super::proof_backend::proof::MerkleProof; -use super::proof_backend::proof::MerkleProofLeaf; -use super::proof_backend::tree::Tree; -use super::verify_backend::PartialState; -use super::verify_backend::{self}; -use crate::array_utils::boxed_array; -use crate::state_backend::verify_backend::PageId; -use crate::storage::binary; - -/// Errors that may occur when parsing a Merkle proof -#[derive(Debug, thiserror::Error)] -pub enum FromProofError { - #[error("Error during hashing: {0}")] - Hash(#[from] HashError), - - #[error("Error during deserialisation: {0}")] - Deserialise(#[from] bincode::Error), - - #[error("Encountered an invalid hash in a blinded node or leaf")] - InvalidHash, - - #[error("Encountered a node with a bad number of branches: expected {expected}, got {got}")] - BadNumberOfBranches { expected: usize, got: usize }, - - #[error("Expected a leaf of size {expected}, got {got}")] - UnexpectedLeafSize { expected: usize, got: usize }, - - #[error("Encountered a leaf where a node was expected")] - UnexpectedLeaf, - - #[error("Encountered a node where a leaf was expected")] - UnexpectedNode, -} - -type Result = std::result::Result; - -/// Errors that may occur when hashing a [`verify_backend::Verifier`] state -#[derive(Debug, thiserror::Error)] -pub enum PartialHashError { - #[error("Error during hashing: {0}")] - Hash(#[from] HashError), - - #[error("Error from proof: {0}")] - FromProof(#[from] FromProofError), - - /// Indicates that a hash could not be computed due to absent data, - /// but from which it is possible to recover if the level at which - /// it was raised is part of a blinded subtree and its hash is present - /// in the proof. - #[error("Potentially recoverable error")] - PotentiallyRecoverable, - - /// Indicates that a hash could not be computed because the data being - /// hashed is only partially available. - #[error("Fatal error")] - Fatal, -} - -/// Common result type for parsing a Merkle proof -pub type FromProofResult = Result<::Allocated>; - -/// Part of a tree that may be absent -pub enum ProofPart<'a, T: ?Sized> { - /// This part of the tree is absent. - Absent, - - /// There is a proof for this part of the tree. - Present(&'a T), -} - -impl Clone for ProofPart<'_, T> { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for ProofPart<'_, T> {} - -/// Part of a Merkle proof tree -pub type ProofTree<'a> = ProofPart<'a, MerkleProof>; - -impl<'a> ProofTree<'a> { - /// Interpret this part of the Merkle proof as a node with `LEN` branches. - pub fn into_branches(self) -> Result> { - let ProofTree::Present(proof) = self else { - // The requested branches are not represented in the Merkle proof at all, not even - // through a blinded node. - return Ok(boxed_array![ProofTree::Absent; LEN]); - }; - - match proof { - Tree::Node(branches) => { - let branches: &[MerkleProof; LEN] = - branches.as_slice().try_into().map_err(|_| { - FromProofError::BadNumberOfBranches { - got: branches.len(), - expected: LEN, - } - })?; - Ok(branches - .iter() - .map(ProofTree::Present) - .collect::>() - .try_into() - .map_err(|_| { - unreachable!( - "Converting a vector to an array of the same size always succeeds" - ) - }) - .unwrap()) - } - - Tree::Leaf(leaf) => match leaf { - MerkleProofLeaf::Blind(_hash) => Ok(boxed_array![ProofTree::Absent; LEN]), - _ => Err(FromProofError::UnexpectedLeaf)?, - }, - } - } - - /// Interpret this part of the Merkle proof as a leaf. - pub fn into_leaf(self) -> Result> { - if let ProofTree::Present(proof) = self { - match proof { - Tree::Node(_) => Err(FromProofError::UnexpectedNode), - Tree::Leaf(leaf) => match leaf { - MerkleProofLeaf::Blind(_) => Ok(ProofPart::Absent), - MerkleProofLeaf::Read(data) => Ok(ProofPart::Present(data.as_slice())), - }, - } - } else { - Ok(ProofPart::Absent) - } - } - - /// For the purpose of computing the final hash of a `Verifier` state, - /// interpret this part of a Merkle proof as a leaf and return its hash if - /// it is a blinded leaf or hash the data if it is present. - pub(crate) fn partial_hash_leaf(self) -> Result { - let ProofTree::Present(proof) = self else { - return Err(PartialHashError::PotentiallyRecoverable); - }; - - let Tree::Leaf(leaf) = proof else { - return Err(FromProofError::UnexpectedNode.into()); - }; - - let hash = match leaf { - MerkleProofLeaf::Blind(hash) => *hash, - MerkleProofLeaf::Read(data) => Hash::blake2b_hash_bytes(data)?, - }; - - Ok(hash) - } - - /// For the purpose of computing the final hash of a `Verifier` state, - /// if present, try to interpret this part of a Merkle proof as: - /// - a node with `LEN` branches, in which case return the proof branches - /// and no proof hash - /// - a blinded leaf which corresponds to a node with `LEN` children, - /// in which case return absent branches and the proof hash - /// - /// If the proof tree is absent, return absent branches and no proof hash. - pub fn into_branches_with_hash( - self, - ) -> Result<(Box<[ProofTree<'a>; LEN]>, Option), PartialHashError> { - let ProofTree::Present(proof) = self else { - return Ok((boxed_array![ProofTree::Absent; LEN], None)); - }; - - match proof { - Tree::Node(branches) if branches.len() != LEN => Err(PartialHashError::FromProof( - FromProofError::BadNumberOfBranches { - got: branches.len(), - expected: LEN, - }, - )), - Tree::Node(branches) => Ok(( - branches - .iter() - .map(ProofTree::Present) - .collect::>() - .into_boxed_slice() - .try_into() - .map_err(|_| PartialHashError::Fatal)?, - None, - )), - Tree::Leaf(leaf) => match leaf { - MerkleProofLeaf::Blind(hash) => { - Ok((boxed_array![ProofTree::Absent; LEN], Some(*hash))) - } - _ => Err(FromProofError::UnexpectedLeaf)?, - }, - } - } -} - -/// [`Layouts`] which may be used in a Merkle proof -/// -/// [`Layouts`]: crate::state_backend::Layout -pub trait ProofLayout: Layout { - /// Obtain the complete Merkle tree which captures an execution trace - /// using the proof-generating backend. - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result; - - /// Parse a Merkle proof into the allocated form of this layout. - fn from_proof(proof: ProofTree) -> FromProofResult; - - /// Compute the state hash of a partial `Verifier` state using its - /// corresponding proof tree where data is missing. - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result; -} - -impl ProofLayout for Box { - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - T::to_merkle_tree(*state) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - T::from_proof(proof).map(Box::new) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - T::partial_state_hash(*state, proof) - } -} - -impl ProofLayout for Atom -where - T: serde::Serialize + serde::de::DeserializeOwned + 'static, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - // The Merkle leaf must hold the serialisation of the initial state. - // Directly serialising the `ProofGen` state would produce the serialisation - // of the final state. Therefore, we rebind and serialise the wrapped `Owned` state. - let region = state.into_region(); - let access_info = region.get_access_info(); - let cell = super::Cell::>::bind(region.inner_region_ref()); - let serialised = binary::serialise(&cell)?; - MerkleTree::make_merkle_leaf(serialised, access_info) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - >::from_proof(proof).map(super::Cell::from) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let region = state.into_region(); - match region.get_partial_region() { - PartialState::Complete(region) => Ok(Hash::blake2b_hash(region)?), - PartialState::Absent => proof.partial_hash_leaf(), - PartialState::Incomplete => Err(PartialHashError::Fatal), - } - } -} - -impl ProofLayout for Array -where - T: serde::Serialize + serde::de::DeserializeOwned + 'static, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - // RV-282: Break down into multiple leaves if the size of the `Cells` - // is too large for a proof. - // - // The Merkle leaf must hold the serialisation of the initial state. - // Directly serialising the `ProofGen` state would produce the serialisation - // of the final state. Therefore, we rebind and serialise the wrapped `Owned` state. - let region = state.into_region(); - let access_info = region.get_access_info(); - let cells = super::Cells::>::bind(region.inner_region_ref()); - let serialised = binary::serialise(&cells)?; - MerkleTree::make_merkle_leaf(serialised, access_info) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let region = if let ProofTree::Present(proof) = proof { - let leaf = match proof { - Tree::Node(_) => Err(FromProofError::UnexpectedNode)?, - Tree::Leaf(leaf) => leaf, - }; - - match leaf { - MerkleProofLeaf::Blind(_) => verify_backend::Region::Absent, - MerkleProofLeaf::Read(data) => { - let cells: super::Cells = binary::deserialise(data)?; - let arr: Box<[Option; LEN]> = Box::new(cells.into_region().map(Some)); - verify_backend::Region::Partial(arr) - } - } - } else { - verify_backend::Region::Absent - }; - - Ok(super::Cells::bind(region)) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let region = state.into_region(); - match region.get_partial_region() { - PartialState::Complete(region) => Ok(Hash::blake2b_hash(region)?), - PartialState::Absent => proof.partial_hash_leaf(), - PartialState::Incomplete => Err(PartialHashError::Fatal), - } - } -} - -impl ProofLayout for DynArray { - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let region = state.region_ref(); - let mut writer = MerkleWriter::new( - MERKLE_LEAF_SIZE, - MERKLE_ARITY, - region.get_read(), - region.get_write(), - LEN.div_ceil(MERKLE_ARITY), - ); - let read = - |address| -> [u8; MERKLE_LEAF_SIZE.get()] { region.inner_dyn_region_read(address) }; - chunks_to_writer::(&mut writer, read)?; - writer.finalise() - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let mut pipeline = vec![(0usize, LEN, proof)]; - let mut pages = Vec::new(); - - while let Some((start, length, tree)) = pipeline.pop() { - if length <= MERKLE_LEAF_SIZE.get() { - // Must be a leaf. - let super::ProofPart::Present(data) = tree.into_leaf()? else { - // No point in doing anything if the leaf isn't present. - continue; - }; - - let start = verify_backend::PageId::from_address(start); - - // TODO RV-463: Leaves smaller than `MERKLE_LEAF_SIZE` should also be accepted. - let data: Box<[u8; MERKLE_LEAF_SIZE.get()]> = - Box::new(data.to_vec().try_into().map_err(|_| { - FromProofError::UnexpectedLeafSize { - got: data.len(), - expected: MERKLE_LEAF_SIZE.get(), - } - })?); - pages.push((start, data)); - } else { - // Expecting a branching point. - // TODO RV-463: Nodes with fewer than `MERKLE_ARITY` children should also be accepted. - let branches = tree.into_branches::<{ MERKLE_ARITY }>()?; - - push_work_items_for_branches( - start, - length, - branches.as_slice(), - |branch_start, branch_length, branch| { - pipeline.push((branch_start, branch_length, branch)); - }, - ); - } - } - - let region = verify_backend::DynRegion::from_pages(pages); - let data = super::DynCells::bind(region); - Ok(data) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - enum Event<'a> { - Span(usize, usize, ProofTree<'a>), - Node(Option), - } - - let mut queue = VecDeque::new(); - queue.push_back(Event::Span(0usize, LEN, proof)); - - let mut hashes: Vec> = Vec::new(); - - while let Some(event) = queue.pop_front() { - match event { - Event::Span(start, length, tree) => { - if length <= MERKLE_LEAF_SIZE.get() { - // TODO RV-463: Leaves smaller than `MERKLE_LEAF_SIZE` should also be accepted. - // The span's size if that of a leaf, obtain its hash if possible and push - // the result to the `hashes` stack. - match state - .region_ref() - .get_partial_page(PageId::from_address(start)) - { - PartialState::Absent => hashes.push(tree.partial_hash_leaf()), - PartialState::Complete(data) => { - hashes.push(Ok(Hash::blake2b_hash_bytes(data)?)) - } - PartialState::Incomplete => { - return Err(PartialHashError::Fatal); - } - }; - } else { - // TODO RV-463: Nodes with fewer than `MERKLE_ARITY` children should also be accepted. - // The span's size is that of a node, produce `Event::Span` work items for each of its - // children and add them to the work queue, followed by an `Event::Node`. - let (branches, proof_hash) = - tree.into_branches_with_hash::<{ MERKLE_ARITY }>()?; - - push_work_items_for_branches( - start, - length, - branches.as_ref(), - |branch_start, branch_length, branch| { - queue.push_back(Event::Span(branch_start, branch_length, branch)); - }, - ); - - queue.push_back(Event::Node(proof_hash)); - } - } - Event::Node(proof_hash) => { - if hashes.is_empty() { - // The hashes which need to be combined have not yet been computed because - // their processing resulted in more `Event::Span` items. Push to the back - // of the work queue. - queue.push_back(Event::Node(proof_hash)); - continue; - } - if hashes.len() < MERKLE_ARITY { - return Err(PartialHashError::Fatal); - }; - - // Take `MERKLE_ARITY` children hashes, compute their parent's hash, and - // push it to the `hashes` stack. - let node_hashes: Vec<_> = hashes.drain(hashes.len() - MERKLE_ARITY..).collect(); - hashes.push(combine_partial_hashes(node_hashes, proof_hash)) - } - } - } - - if hashes.len() == 1 { - hashes.pop().unwrap() - } else { - Err(PartialHashError::Fatal) - } - } -} - -impl ProofLayout for (A, B) -where - A: ProofLayout, - B: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = vec![A::to_merkle_tree(state.0)?, B::to_merkle_tree(state.1)?]; - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let [left, right] = *proof.into_branches()?; - Ok((A::from_proof(left)?, B::from_proof(right)?)) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::<2>()?; - - let hashes = [ - A::partial_state_hash(state.0, branches[0]), - B::partial_state_hash(state.1, branches[1]), - ]; - - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for (A, B, C) -where - A: ProofLayout, - B: ProofLayout, - C: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = vec![ - A::to_merkle_tree(state.0)?, - B::to_merkle_tree(state.1)?, - C::to_merkle_tree(state.2)?, - ]; - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let [a, b, c] = *proof.into_branches()?; - Ok((A::from_proof(a)?, B::from_proof(b)?, C::from_proof(c)?)) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::<3>()?; - - let hashes = [ - A::partial_state_hash(state.0, branches[0]), - B::partial_state_hash(state.1, branches[1]), - C::partial_state_hash(state.2, branches[2]), - ]; - - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for (A, B, C, D) -where - A: ProofLayout, - B: ProofLayout, - C: ProofLayout, - D: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = vec![ - A::to_merkle_tree(state.0)?, - B::to_merkle_tree(state.1)?, - C::to_merkle_tree(state.2)?, - D::to_merkle_tree(state.3)?, - ]; - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let [a, b, c, d] = *proof.into_branches()?; - Ok(( - A::from_proof(a)?, - B::from_proof(b)?, - C::from_proof(c)?, - D::from_proof(d)?, - )) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::<4>()?; - - let hashes = [ - A::partial_state_hash(state.0, branches[0]), - B::partial_state_hash(state.1, branches[1]), - C::partial_state_hash(state.2, branches[2]), - D::partial_state_hash(state.3, branches[3]), - ]; - - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for (A, B, C, D, E) -where - A: ProofLayout, - B: ProofLayout, - C: ProofLayout, - D: ProofLayout, - E: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = vec![ - A::to_merkle_tree(state.0)?, - B::to_merkle_tree(state.1)?, - C::to_merkle_tree(state.2)?, - D::to_merkle_tree(state.3)?, - E::to_merkle_tree(state.4)?, - ]; - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let [a, b, c, d, e] = *proof.into_branches()?; - Ok(( - A::from_proof(a)?, - B::from_proof(b)?, - C::from_proof(c)?, - D::from_proof(d)?, - E::from_proof(e)?, - )) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::<5>()?; - - let hashes = [ - A::partial_state_hash(state.0, branches[0]), - B::partial_state_hash(state.1, branches[1]), - C::partial_state_hash(state.2, branches[2]), - D::partial_state_hash(state.3, branches[3]), - E::partial_state_hash(state.4, branches[4]), - ]; - - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for (A, B, C, D, E, F) -where - A: ProofLayout, - B: ProofLayout, - C: ProofLayout, - D: ProofLayout, - E: ProofLayout, - F: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = vec![ - A::to_merkle_tree(state.0)?, - B::to_merkle_tree(state.1)?, - C::to_merkle_tree(state.2)?, - D::to_merkle_tree(state.3)?, - E::to_merkle_tree(state.4)?, - F::to_merkle_tree(state.5)?, - ]; - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let [a, b, c, d, e, f] = *proof.into_branches()?; - Ok(( - A::from_proof(a)?, - B::from_proof(b)?, - C::from_proof(c)?, - D::from_proof(d)?, - E::from_proof(e)?, - F::from_proof(f)?, - )) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::<6>()?; - - let hashes = [ - A::partial_state_hash(state.0, branches[0]), - B::partial_state_hash(state.1, branches[1]), - C::partial_state_hash(state.2, branches[2]), - D::partial_state_hash(state.3, branches[3]), - E::partial_state_hash(state.4, branches[4]), - F::partial_state_hash(state.5, branches[5]), - ]; - - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for [T; LEN] -where - T: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let children = state - .into_iter() - .map(T::to_merkle_tree) - .collect::, _>>()?; - - MerkleTree::make_merkle_node(children) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - proof - .into_branches::()? - .into_iter() - .map(T::from_proof) - .collect::, _>>()? - .try_into() - .map_err(|_| { - // We can't use `expected` because the error can't be displayed. - unreachable!() - }) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - let (branches, proof_hash) = proof.into_branches_with_hash::()?; - let hashes = state - .into_iter() - .zip(branches.iter()) - .map(|(state, proof)| T::partial_state_hash(state, *proof)) - .collect::>>(); - combine_partial_hashes(hashes, proof_hash) - } -} - -impl ProofLayout for Many -where - T: ProofLayout, -{ - fn to_merkle_tree(state: RefProofGenOwnedAlloc) -> Result { - let leaves = state - .into_iter() - .map(T::to_merkle_tree) - .collect::, _>>()?; - - build_custom_merkle_tree(MERKLE_ARITY, leaves) - } - - fn from_proof(proof: ProofTree) -> FromProofResult { - let mut pipeline = vec![(0usize, LEN, proof)]; - - let mut data = Vec::with_capacity(LEN); - for _ in 0..LEN { - data.push(T::from_proof(ProofTree::Absent)?); - } - - while let Some((start, length, tree)) = pipeline.pop() { - if length == 1 { - data[start] = T::from_proof(tree)?; - } else { - // Expecting a branching point. - // TODO RV-463: Nodes with fewer than `MERKLE_ARITY` children should also be accepted. - let branches = tree.into_branches::<{ MERKLE_ARITY }>()?; - - push_work_items_for_branches( - start, - length, - branches.as_slice(), - |branch_start, branch_length, branch| { - pipeline.push((branch_start, branch_length, branch)); - }, - ); - } - } - Ok(data) - } - - fn partial_state_hash( - state: RefVerifierAlloc, - proof: ProofTree, - ) -> Result { - enum Event<'a> { - Span(usize, usize, ProofTree<'a>), - Node(Option), - } - - // `T::partial_state_hash` needs to take ownership of the elements of `state`. - // Given that `T` is not `Copy`, in order to take ownership of arbitrary elements - // of `state` we'd first need to duplicate it and wrap each element in a type - // which supports taking ownership. - // However, in practice, we compute the hash of each element sequentially, meaning - // that we can simply iterate over the state directly when calling `T::partial_state_hash`. - let mut state = state.into_iter(); - let mut next_vec_index = 0; - - let mut queue = VecDeque::new(); - queue.push_back(Event::Span(0usize, LEN, proof)); - - let mut hashes: Vec> = Vec::new(); - - while let Some(event) = queue.pop_front() { - match event { - Event::Span(start, length, tree) => { - if length == 1 { - // Check that iterating over the state is equivalent to calling `state[start]` - debug_assert_eq!(start, next_vec_index); - next_vec_index += 1; - hashes.push(T::partial_state_hash( - state.next().ok_or(PartialHashError::Fatal)?, - tree, - )) - } else { - // TODO RV-463: Nodes with fewer than `MERKLE_ARITY` children should also be accepted. - // The span's size is that of a node, produce `Event::Span` work items for each of its - // children and add them to the work queue, followed by an `Event::Node`. - let (branches, proof_hash) = - tree.into_branches_with_hash::<{ MERKLE_ARITY }>()?; - - push_work_items_for_branches( - start, - length, - branches.as_ref(), - |branch_start, branch_length, branch| { - queue.push_back(Event::Span(branch_start, branch_length, branch)); - }, - ); - - queue.push_back(Event::Node(proof_hash)); - } - } - Event::Node(proof_hash) => { - if hashes.is_empty() { - // The hashes which need to be combined have not yet been computed because - // their processing resulted in more `Event::Span` items. Push to the back - // of the work queue. - queue.push_back(Event::Node(proof_hash)); - continue; - } - if hashes.len() < MERKLE_ARITY { - return Err(PartialHashError::Fatal); - }; - - // Take `MERKLE_ARITY` children hashes, compute their parent's hash, and - // push it to the `hashes` stack. - let node_hashes: Vec<_> = hashes.drain(hashes.len() - MERKLE_ARITY..).collect(); - hashes.push(combine_partial_hashes(node_hashes, proof_hash)) - } - } - } - - // Check that we iterated over all the elements of the state - debug_assert_eq!(next_vec_index, LEN); - - if hashes.len() == 1 { - hashes.pop().unwrap() - } else { - Err(PartialHashError::Fatal) - } - } -} - -/// Attempt to compute the partial hash of a node from its children's partial -/// hashes if they are present. If none of the children hashes can be computed -/// due to absent data, this node is either a blinded leaf in the proof, in which -/// case its hash can be recovered from the proof, or it is part of a blinded -/// subtree whose hash cannot be computed as this point. -pub fn combine_partial_hashes( - hash_results: impl AsRef<[Result]>, - proof_hash: Option, -) -> Result { - let hash_results = hash_results.as_ref(); - if hash_results.is_empty() { - return Ok(Hash::combine(&[])?); - } - - // If the first result is a hash, all results need to be a hash in order to - // compute the combined hash. If the first result is a potentially - // recoverable error, all results need to to be potentially recoverable - // errors in order to fall back on the proof hash. Anything else is a fatal error. - let expect_ok = match hash_results[0] { - Ok(_) => true, - Err(PartialHashError::PotentiallyRecoverable) => false, - _ => return Err(PartialHashError::Fatal), - }; - - let mut hashes = Vec::with_capacity(hash_results.len()); - let hash_results_len = hash_results.len(); - for r in hash_results { - match r { - Ok(hash) if expect_ok => hashes.push(*hash), - Err(PartialHashError::PotentiallyRecoverable) if !expect_ok => (), - _ => return Err(PartialHashError::Fatal), - } - } - - if expect_ok { - debug_assert_eq!(hashes.len(), hash_results_len); - return Ok(Hash::combine(hashes.as_slice())?); - }; - - proof_hash.ok_or(PartialHashError::PotentiallyRecoverable) -} - -fn push_work_items_for_branches<'a>( - mut branch_start: usize, - mut length_left: usize, - branches: &'_ [ProofTree<'a>], - mut push: impl FnMut(usize, usize, ProofTree<'a>), -) { - let branch_max_length = length_left.div_ceil(MERKLE_ARITY); - - for branch in branches.iter() { - let this_branch_length = branch_max_length.min(length_left); - - if this_branch_length > 0 { - push(branch_start, this_branch_length, *branch); - } - - branch_start = branch_start.saturating_add(this_branch_length); - length_left = length_left.saturating_sub(this_branch_length); - } -} - -#[cfg(test)] -mod tests { - use proptest::prop_assert; - use proptest::prop_assert_eq; - use proptest::proptest; - use tests::verify_backend::handle_stepper_panics; - - use super::*; - use crate::state_backend::Cells; - use crate::state_backend::FnManagerIdent; - use crate::state_backend::ManagerWrite; - use crate::state_backend::proof_backend::ProofGen; - use crate::state_backend::proof_backend::ProofRegion; - - const CELLS_SIZE: usize = 32; - - // When producing a proof from a `ProofGen` state, values written during - // the execution of the tick being proven should not be blinded, whereas - // values which were not accessed should be blinded. When a proof contains - // blinded values, it should be possible to compute the final hash of the - // `Verifier` state constructed from this proof. - #[test] - fn test_proof_blinding() { - type TestLayout = (Array, Array); - - proptest!(|(value_before: u64, value_after: u64, i in 0..CELLS_SIZE)| { - // Bind `ProofGen` cells and write at one address - let cells1 = [value_before; CELLS_SIZE]; - let mut proof_region1: ProofRegion> = - ProofRegion::bind(&cells1); - ProofGen::>::region_write(&mut proof_region1, i, value_after); - let proof_cells1: Cells>>> = - Cells::bind(&proof_region1); - - // Bind `ProofGen` cells and do not access them - let cells2 = [value_before; CELLS_SIZE]; - let proof_region2: ProofRegion> = - ProofRegion::bind(&cells2); - let proof_cells2: Cells>>> = - Cells::bind(&proof_region2); - - let proof_state = (proof_cells1, proof_cells2); - - let merkle_proof = ::to_merkle_tree(proof_state) - .unwrap() - .to_merkle_proof() - .unwrap(); - let proof_tree = ProofTree::Present(&merkle_proof); - - let verifier_state = ::from_proof(proof_tree).unwrap(); - - // The first component of the state was present in the proof, can be - // fully read, and contains the initial state. - prop_assert_eq!(verifier_state.0.read_all(), vec![value_before; CELLS_SIZE]); - - // The second component of the state is fully blinded: no values can - // be read from the array. - for i in 0..CELLS_SIZE { - prop_assert!(handle_stepper_panics(|| verifier_state.1.read(i)).is_err()); - }; - - let ref_verifier_state = ( - verifier_state.0.struct_ref::(), - verifier_state.1.struct_ref::(), - ); - prop_assert!( - ::partial_state_hash(ref_verifier_state, proof_tree).is_ok() - ); - }) - } -} diff --git a/src/riscv/lib/src/state_backend/region.rs b/src/riscv/lib/src/state_backend/region.rs deleted file mode 100644 index 047ac48a9ccbb2055fa4fd973b760ab611c71330..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/region.rs +++ /dev/null @@ -1,896 +0,0 @@ -// SPDX-FileCopyrightText: 2023 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::ops::Deref; - -use super::Elem; -use super::EnrichedValue; -use super::EnrichedValueLinked; -use super::FnManager; -use super::ManagerAlloc; -use super::ManagerBase; -use super::ManagerClone; -use super::ManagerDeserialise; -use super::ManagerRead; -use super::ManagerReadWrite; -use super::ManagerSerialise; -use super::ManagerWrite; -use super::Ref; -use super::owned_backend::Owned; -use super::proof_backend::ProofGen; -use super::proof_backend::merkle::AccessInfoAggregatable; -use crate::default::ConstDefault; -use crate::state::NewState; - -/// Link a stored value directly with a derived value - -/// that would either be expensive to compute each time, or cannot -/// itself be stored. -/// -/// Only the value of `V::E` forms part of the 'state' for the purposes of commitments etc. -pub struct EnrichedCell { - cell: M::EnrichedCell, -} - -impl EnrichedCell { - /// Allocate a new enriched cell with the given value. - pub fn new_with(manager: &mut M, value: V::E) -> Self - where - M: ManagerAlloc, - V: EnrichedValueLinked, - { - let region = manager.allocate_region([value]); - let cell = M::enrich_cell(region); - Self { cell } - } - - /// Bind this state to the enriched cell. - pub fn bind(cell: Cell) -> Self - where - V: EnrichedValueLinked, - { - let region = cell.into_region(); - let cell = M::enrich_cell(region); - Self { cell } - } - - /// Obtain a reference to the underlying cell. - pub fn cell_ref(&self) -> &M::EnrichedCell { - &self.cell - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>(&'a self) -> Cell { - let cell = self.cell_ref(); - let region = M::as_devalued_cell(cell); - let region = F::map_region(region); - Cell::bind(region) - } - - /// Write the value back to the enriched cell. - /// - /// Reading the new value will produce the new derived value also. - pub fn write(&mut self, value: V::E) - where - M: ManagerWrite, - V: EnrichedValueLinked, - { - M::enriched_cell_write(&mut self.cell, value) - } - - /// Read the stored value from the enriched cell. - pub fn read_stored(&self) -> V::E - where - M: ManagerRead, - V::E: Copy, - { - M::enriched_cell_read_stored(&self.cell) - } - - /// Read the derived value from the enriched cell. - pub fn read_derived(&self) -> V::D - where - M: ManagerRead, - V: EnrichedValueLinked, - V::D: Copy, - { - M::enriched_cell_read_derived(&self.cell) - } - - /// Obtain a reference to the value contained within the cell. - pub fn read_ref_stored(&self) -> &V::E - where - M: ManagerRead, - { - M::enriched_cell_ref_stored(&self.cell) - } -} - -impl NewState for EnrichedCell -where - V: EnrichedValueLinked, - V::E: ConstDefault, -{ - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self::new_with(manager, ::DEFAULT) - } -} - -impl Clone for EnrichedCell -where - V::E: Clone, - V::D: Clone, -{ - fn clone(&self) -> Self { - Self { - cell: M::clone_enriched_cell(&self.cell), - } - } -} - -impl PartialEq for EnrichedCell -where - V: EnrichedValueLinked, - V::E: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - M::enriched_cell_ref_stored(&self.cell) == M::enriched_cell_ref_stored(&other.cell) - } -} - -/// Single element of type `E` -#[repr(transparent)] -pub struct Cell { - region: Cells, -} - -impl Cell { - /// Allocate a new cell with the given value. - pub fn new_with(manager: &mut M, value: E) -> Self - where - M: ManagerAlloc, - { - let region = manager.allocate_region([value]); - Self { - region: Cells::bind(region), - } - } - - /// Bind this state to the single element region. - pub const fn bind(region: M::Region) -> Self { - Self { - region: Cells::bind(region), - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>(&'a self) -> Cell { - Cell { - region: self.region.struct_ref::(), - } - } - - /// Obtain the underlying region. - pub fn into_region(self) -> M::Region { - self.region.into_region() - } - - /// Read the value managed by the cell. - #[inline(always)] - pub fn read(&self) -> E - where - E: Copy, - M: ManagerRead, - { - self.region.read(0) - } - - /// Write the value managed by the cell. - #[inline(always)] - pub fn write(&mut self, value: E) - where - M: ManagerWrite, - { - self.region.write(0, value) - } - - /// Replace the value managed by the cell, returning the old value. - #[inline(always)] - pub fn replace(&mut self, value: E) -> E - where - E: Copy, - M: ManagerReadWrite, - { - self.region.replace(0, value) - } -} - -impl NewState for Cell { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self::new_with(manager, E::DEFAULT) - } -} - -impl From> for Cell { - fn from(region: Cells) -> Self { - Self { region } - } -} - -impl serde::Serialize for Cell { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.region.serialize(serializer) - } -} - -impl<'de, E: serde::Deserialize<'de>, M: ManagerDeserialise> serde::Deserialize<'de> - for Cell -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let region = Cells::deserialize(deserializer)?; - Ok(Self { region }) - } -} - -impl, B, M: ManagerRead, N: ManagerRead> PartialEq> for Cell { - fn eq(&self, other: &Cell) -> bool { - self.as_ref() == other.as_ref() - } -} - -impl AccessInfoAggregatable - for Cell>> -{ - fn aggregate_access_info(&self) -> bool { - self.region.region.get_access_info() - } -} - -impl Eq for Cell {} - -impl Clone for Cell { - fn clone(&self) -> Self { - Self { - region: self.region.clone(), - } - } -} - -impl AsRef for Cell { - #[inline] - fn as_ref(&self) -> &E { - M::region_ref(&self.region.region, 0) - } -} - -impl Deref for Cell { - type Target = E; - - #[inline] - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -/// A cell that support reading only. -pub trait CellBase { - /// Element type managed by the cell. - type Value; -} - -impl CellBase for Cell { - type Value = E; -} - -impl CellBase for &E { - type Value = E::Value; -} - -impl CellBase for &mut E { - type Value = E::Value; -} - -/// A cell that support reading only. -pub trait CellRead: CellBase { - /// Read the value managed by the cell. - fn read(&self) -> Self::Value; -} - -impl CellRead for Cell { - #[inline(always)] - fn read(&self) -> E { - Cell::read(self) - } -} - -impl CellRead for &E { - #[inline(always)] - fn read(&self) -> Self::Value { - E::read(self) - } -} - -impl CellRead for &mut E { - #[inline(always)] - fn read(&self) -> Self::Value { - E::read(self) - } -} - -/// A cell that support writing. -pub trait CellWrite: CellBase { - /// Write the value managed by the cell. - fn write(&mut self, value: Self::Value); -} - -impl CellWrite for Cell { - #[inline(always)] - fn write(&mut self, value: E) { - Cell::write(self, value) - } -} - -impl CellWrite for &mut E { - #[inline(always)] - fn write(&mut self, value: Self::Value) { - E::write(self, value) - } -} - -/// A cell that support reading and writing. -pub trait CellReadWrite: CellRead + CellWrite { - /// Replace the value managed by the cell, returning the old value. - fn replace(&mut self, value: Self::Value) -> Self::Value; -} - -impl CellReadWrite for Cell { - #[inline(always)] - fn replace(&mut self, value: E) -> E { - Cell::replace(self, value) - } -} - -impl CellReadWrite for &mut E { - #[inline(always)] - fn replace(&mut self, value: Self::Value) -> Self::Value { - E::replace(self, value) - } -} - -/// Multiple elements of type `E` -#[repr(transparent)] -pub struct Cells { - region: M::Region, -} - -impl Cells { - /// Allocate new cells with the given values. - pub fn new_with(manager: &mut M, values: [E; LEN]) -> Self - where - M: ManagerAlloc, - { - let region = manager.allocate_region(values); - Self { region } - } - - /// Bind this state to the given region. - pub const fn bind(region: M::Region) -> Self { - Self { region } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>(&'a self) -> Cells { - Cells { - region: F::map_region(&self.region), - } - } - - /// Obtain a reference to the underlying region. - pub fn region_ref(&self) -> &M::Region { - &self.region - } - - /// Obtain the underlying region. - pub fn into_region(self) -> M::Region { - self.region - } - - /// Read an element in the region. - #[inline] - pub fn read(&self, index: usize) -> E - where - E: Copy, - M: ManagerRead, - { - M::region_read(&self.region, index) - } - - /// Read all elements in the region. - #[inline] - pub fn read_all(&self) -> Vec - where - E: Copy, - M: ManagerRead, - { - M::region_read_all(&self.region) - } - - /// Update an element in the region. - #[inline] - pub fn write(&mut self, index: usize, value: E) - where - M: ManagerWrite, - { - M::region_write(&mut self.region, index, value) - } - - /// Update all elements in the region. - #[inline] - pub fn write_all(&mut self, value: &[E]) - where - E: Copy, - M: ManagerWrite, - { - M::region_write_all(&mut self.region, value) - } - - /// Update the element in the region and return the previous value. - #[inline] - pub fn replace(&mut self, index: usize, value: E) -> E - where - E: Copy, - M: ManagerReadWrite, - { - M::region_replace(&mut self.region, index, value) - } -} - -impl Cells { - /// Obtain the byte offset from a pointer to `Cells` to the memory of the elem at - /// `index`. - pub(crate) const fn region_elem_offset(index: usize) -> usize { - std::mem::offset_of!(Self, region) + Owned::region_elem_offset::(index) - } -} - -impl NewState for Cells { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - Self::new_with(manager, [E::DEFAULT; LEN]) - } -} - -impl serde::Serialize - for Cells -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - M::serialise_region(&self.region, serializer) - } -} - -impl serde::Serialize for EnrichedCell -where - V::E: serde::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let cell = self.cell_ref(); - let region = M::as_devalued_cell(cell); - M::serialise_region(region, serializer) - } -} - -impl AccessInfoAggregatable - for EnrichedCell>> -where - V::E: serde::Serialize, -{ - fn aggregate_access_info(&self) -> bool { - self.cell.get_access_info() - } -} - -impl<'de, E: serde::Deserialize<'de>, const LEN: usize, M: ManagerDeserialise> - serde::Deserialize<'de> for Cells -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let region = M::deserialise_region(deserializer)?; - Ok(Self { region }) - } -} - -impl<'de, V, M: ManagerDeserialise> serde::Deserialize<'de> for EnrichedCell -where - V: EnrichedValueLinked, - V::E: serde::Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let region = M::deserialise_region(deserializer)?; - let cell = M::enrich_cell(region); - Ok(Self { cell }) - } -} - -impl + Copy, B: Copy, const LEN: usize, M: ManagerRead, N: ManagerRead> - PartialEq> for Cells -{ - fn eq(&self, other: &Cells) -> bool { - (0..LEN).all(|i| self.read(i) == other.read(i)) - } -} - -impl AccessInfoAggregatable - for Cells>> -{ - fn aggregate_access_info(&self) -> bool { - self.region.get_access_info() - } -} - -impl Clone for Cells { - fn clone(&self) -> Self { - Self { - region: M::clone_region(&self.region), - } - } -} - -/// Multiple elements of an unspecified type -pub struct DynCells { - region: M::DynRegion, -} - -impl DynCells { - /// Bind this state to the given dynamic region. - pub fn bind(region: M::DynRegion) -> Self { - Self { region } - } - - /// Obtain a reference to the underlying dynamic region. - pub fn region_ref(&self) -> &M::DynRegion { - &self.region - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref<'a, F: FnManager>>(&'a self) -> DynCells { - DynCells { - region: F::map_dyn_region(&self.region), - } - } - - /// Read an element in the region. `address` is in bytes. - #[inline] - pub fn read(&self, address: usize) -> E - where - M: ManagerRead, - { - M::dyn_region_read(&self.region, address) - } - - /// Read elements from the region. `address` is in bytes. - #[inline] - pub fn read_all(&self, address: usize, values: &mut [E]) - where - M: ManagerRead, - { - M::dyn_region_read_all(&self.region, address, values) - } - - /// Update an element in the region. `address` is in bytes. - #[inline] - pub fn write(&mut self, address: usize, value: E) - where - M: ManagerWrite, - { - M::dyn_region_write(&mut self.region, address, value) - } - - /// Update multiple elements in the region. `address` is in bytes. - #[inline] - pub fn write_all(&mut self, address: usize, values: &[E]) - where - M: ManagerWrite, - { - M::dyn_region_write_all(&mut self.region, address, values) - } -} - -impl NewState for DynCells { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - let region = manager.allocate_dyn_region(); - Self { region } - } -} - -impl serde::Serialize for DynCells { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - M::serialise_dyn_region(&self.region, serializer) - } -} - -impl<'de, const LEN: usize, M: ManagerDeserialise> serde::Deserialize<'de> for DynCells { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let region = M::deserialise_dyn_region(deserializer)?; - Ok(DynCells { region }) - } -} - -impl PartialEq> - for DynCells -{ - fn eq(&self, other: &DynCells) -> bool { - for i in 0..LEN { - if self.read::(i) != other.read::(i) { - return false; - } - } - true - } -} - -impl Eq for DynCells {} - -impl Clone for DynCells { - fn clone(&self) -> Self { - Self { - region: M::clone_dyn_region(&self.region), - } - } -} - -#[cfg(test)] -pub(crate) mod tests { - use serde::ser::SerializeTuple; - - use crate::backend_test; - use crate::default::ConstDefault; - use crate::state::NewState; - use crate::state_backend::Cell; - use crate::state_backend::Cells; - use crate::state_backend::DynCells; - use crate::state_backend::Elem; - use crate::state_backend::FnManagerIdent; - - /// Dummy type that helps us implement custom normalisation via [Elem] - #[repr(C, packed)] - #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Default)] - struct Flipper { - a: u8, - b: u8, - } - - impl ConstDefault for Flipper { - const DEFAULT: Self = Self { a: 0, b: 0 }; - } - - impl serde::Serialize for Flipper { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut serializer = serializer.serialize_tuple(2)?; - serializer.serialize_element(&self.b)?; - serializer.serialize_element(&self.a)?; - serializer.end() - } - } - - impl Elem for Flipper { - fn store(&mut self, source: &Self) { - self.a = source.b; - self.b = source.a; - } - - fn to_stored_in_place(&mut self) { - std::mem::swap(&mut self.a, &mut self.b); - } - - fn from_stored_in_place(&mut self) { - std::mem::swap(&mut self.b, &mut self.a); - } - - fn from_stored(source: &Self) -> Self { - Self { - a: source.b, - b: source.a, - } - } - } - - const FLIPPER_SIZE: usize = std::mem::size_of::(); - - backend_test!(test_region_overlap, F, { - const LEN: usize = 64; - - let mut manager = F::manager(); - let mut array1: Cells = Cells::new(&mut manager); - let mut array2: Cells = Cells::new(&mut manager); - - // Allocate two consecutive arrays - // let mut array1 = manager.allocate_region(array1_place); - let mut array1_mirror = [0; LEN]; - - for (i, item) in array1_mirror.iter_mut().enumerate() { - // Ensure the array is zero-initialised. - assert_eq!(array1.read(i), 0); - - // Then write something random in it. - let value = rand::random(); - array1.write(i, value); - assert_eq!(array1.read(i), value); - - // Retain the value for later. - *item = value; - } - - let array1_vec = array1.read_all(); - assert_eq!(array1_vec, array1_mirror); - - for i in 0..LEN { - // Check the array is zero-initialised and that the first array - // did not mess with the second array. - assert_eq!(array2.read(i), 0); - - // Write a random value to it. - let value = rand::random(); - array2.write(i, value); - assert_eq!(array2.read(i), value); - } - - for (i, item) in array1_mirror.into_iter().enumerate() { - // Ensure that writing to the second array didn't mess with the - // first array. - assert_eq!(item, array1.read(i)); - } - }); - - backend_test!(test_cell_overlap, F, { - let mut manager = F::manager(); - let mut cell1: Cell<[u64; 4], _> = Cell::new(&mut manager); - let mut cell2: Cell<[u64; 4], _> = Cell::new(&mut manager); - - // Cell should be zero-initialised. - assert_eq!(cell1.read(), [0; 4]); - assert_eq!(cell2.read(), [0; 4]); - - // Write something to cell 1 and check. - let cell1_value: [u64; 4] = rand::random(); - cell1.write(cell1_value); - assert_eq!(cell1.read(), cell1_value); - - // Second cell should still be zero-initialised - assert_eq!(cell2.read(), [0; 4]); - - // Write something to cell 2 and check. - let cell2_value: [u64; 4] = rand::random(); - cell2.write(cell2_value); - assert_eq!(cell2.read(), cell2_value); - - // Cell 1 should not have its value changed. - assert_eq!(cell1.read(), cell1_value); - }); - - backend_test!( - #[should_panic] - test_dynregion_oob_2, - F, - { - const LEN: usize = 8; - - let mut manager = F::manager(); - let mut state = DynCells::::new(&mut manager); - - // This should panic because we are trying to write an element at the address which - // corresponds to the end of the buffer. - state.write(LEN * FLIPPER_SIZE, Flipper { a: 1, b: 2 }); - } - ); - - backend_test!(test_dynregion_stored_format, F, { - // Writing to one item of the region must convert to stored format. - let mut manager = F::manager(); - let mut region = DynCells::<1024, _>::new(&mut manager); - - region.write(0, Flipper { a: 13, b: 37 }); - assert_eq!(region.read::(0), Flipper { a: 13, b: 37 }); - - let buffer = region.read::<[u8; 2]>(0); - assert_eq!(buffer, [37, 13]); - - // Writing to the entire region must convert properly to stored format. - region.write_all::(0, &[ - Flipper { a: 11, b: 22 }, - Flipper { a: 13, b: 24 }, - Flipper { a: 15, b: 26 }, - Flipper { a: 17, b: 28 }, - ]); - - let mut buff = [Flipper::default(); 4]; - region.read_all::(0, &mut buff); - assert_eq!(buff, [ - Flipper { a: 11, b: 22 }, - Flipper { a: 13, b: 24 }, - Flipper { a: 15, b: 26 }, - Flipper { a: 17, b: 28 }, - ]); - - let buffer = region.read::<[u8; 8]>(0); - assert_eq!(buffer, [22, 11, 24, 13, 26, 15, 28, 17]); - }); - - backend_test!(test_region_stored_format, F, { - // Writing to one item of the region must convert to stored format. - let mut manager = F::manager(); - let mut region = Cells::::new(&mut manager); - - region.write(0, Flipper { a: 13, b: 37 }); - assert_eq!(region.read(0), Flipper { a: 13, b: 37 }); - - let buffer = bincode::serialize(®ion.struct_ref::()).unwrap(); - assert_eq!(buffer[..2], [37, 13]); - - // Replacing a value in the region must convert to and from stored format. - let old = region.replace(0, Flipper { a: 26, b: 74 }); - assert_eq!(old, Flipper { a: 13, b: 37 }); - - let buffer = bincode::serialize(®ion.struct_ref::()).unwrap(); - assert_eq!(buffer[..2], [74, 26]); - - // Writing to the entire region must convert properly to stored format. - region.write_all(&[ - Flipper { a: 11, b: 22 }, - Flipper { a: 13, b: 24 }, - Flipper { a: 15, b: 26 }, - Flipper { a: 17, b: 28 }, - ]); - - assert_eq!(region.read_all(), [ - Flipper { a: 11, b: 22 }, - Flipper { a: 13, b: 24 }, - Flipper { a: 15, b: 26 }, - Flipper { a: 17, b: 28 }, - ]); - - let buffer = bincode::serialize(®ion.struct_ref::()).unwrap(); - assert_eq!(buffer[..8], [22, 11, 24, 13, 26, 15, 28, 17]); - }); -} diff --git a/src/riscv/lib/src/state_backend/trans.rs b/src/riscv/lib/src/state_backend/trans.rs deleted file mode 100644 index 72e809f31a15aad1d017ad7a6b479356c6e6054c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/trans.rs +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use super::ManagerBase; - -/// Transformation from a manager `I` to another manager -pub trait FnManager { - /// Resulting manager type - type Output: ManagerBase; - - /// Transform the region of manager `I` to one of manager `O`. - fn map_region( - input: I::Region, - ) -> ::Region; - - /// Transform the dynamic region of manager `I` to one of manager `O`. - fn map_dyn_region( - input: I::DynRegion, - ) -> ::DynRegion; -} - -/// Identity transformation for [`FnManager`] -pub enum FnManagerIdent {} - -impl FnManager for FnManagerIdent { - type Output = M; - - fn map_region(input: M::Region) -> M::Region { - input - } - - fn map_dyn_region(input: M::DynRegion) -> M::DynRegion { - input - } -} diff --git a/src/riscv/lib/src/state_backend/verify_backend.rs b/src/riscv/lib/src/state_backend/verify_backend.rs deleted file mode 100644 index b1c129f4999be3155293d199fd024bc3634f39ca..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/state_backend/verify_backend.rs +++ /dev/null @@ -1,902 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::array; -use std::collections::BTreeMap; -use std::mem; -use std::mem::MaybeUninit; -use std::ops::Index; -use std::panic::resume_unwind; -use std::slice; - -use range_collections::RangeSet2; -use serde::ser::SerializeTuple; - -use super::Cell; -use super::EnrichedValue; -use super::ManagerBase; -use super::ManagerClone; -use super::ManagerRead; -use super::ManagerReadWrite; -use super::ManagerWrite; -use super::PartialHashError; -use super::Ref; -use crate::state_backend::hash::Hash; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::merkle::MERKLE_LEAF_SIZE; - -/// Panic payload that is raised when a value isn't present in a part of the Verifier backend. -#[derive(Copy, Clone, Debug, Eq, PartialEq, derive_more::Display, thiserror::Error)] -pub struct NotFound; - -/// Raise a [`NotFound`] panic. -fn not_found() -> ! { - // We use [`resume_unwind`] over [`panic_any`] to avoid calling the panic hook. - // XXX: This fails without a message when there is no matching [`handle_stepper_panics`] wrapper. - resume_unwind(Box::new(NotFound)) -} - -/// Catch errors that the verifier backend may raise during the invocation of `f` and return them -/// as [`Err`]. -pub(crate) fn handle_stepper_panics R + std::panic::UnwindSafe>( - f: F, -) -> Result { - match std::panic::catch_unwind(f) { - Ok(res) => Ok(res), - Err(err) => match err.downcast::() { - Ok(not_found) => Err(ProofVerificationFailure::AbsentDataAccess(*not_found)), - Err(other) => Err(ProofVerificationFailure::StepperPanic(other)), - }, - } -} - -/// Error during proof verification -#[derive(Debug, thiserror::Error)] -pub enum ProofVerificationFailure { - #[error("Unexpected proof shape")] - UnexpectedProofShape, - - #[error("Stepper error")] - StepperError, - - #[error("Stepper panic")] - StepperPanic(Box), - - #[error("Attempted to access absent data")] - AbsentDataAccess(#[from] NotFound), - - #[error("Error computing final state hash: {0}")] - PartialHashError(#[from] PartialHashError), - - #[error("Final state hash mismatch (expected {expected}, computed {computed})")] - FinalHashMismatch { expected: Hash, computed: Hash }, -} - -/// Proof verification backend -pub struct Verifier; - -impl ManagerBase for Verifier { - type Region = Region; - - type DynRegion = DynRegion<{ MERKLE_LEAF_SIZE.get() }, LEN>; - - type EnrichedCell = EnrichedCell; - - type ManagerRoot = Self; - - fn enrich_cell( - underlying: Self::Region, - ) -> Self::EnrichedCell { - EnrichedCell { underlying } - } - - fn as_devalued_cell(cell: &Self::EnrichedCell) -> &Self::Region { - &cell.underlying - } -} - -impl ManagerRead for Verifier { - fn region_read(region: &Self::Region, index: usize) -> E { - region[index] - } - - fn region_ref(region: &Self::Region, index: usize) -> &E { - ®ion[index] - } - - fn region_read_all(region: &Self::Region) -> Vec { - (0..LEN).map(|index| region[index]).collect() - } - - fn dyn_region_read( - region: &Self::DynRegion, - address: usize, - ) -> E { - let mut value = MaybeUninit::::uninit(); - - // SAFETY: `raw_data` points to a byte slice which has same size as `E`. - let raw_data = unsafe { - slice::from_raw_parts_mut(value.as_mut_ptr().cast::(), mem::size_of::()) - }; - - region.read_bytes(address, raw_data); - - // SAFETY: `read_bytes` fully populates the contents of `values`. Additionally, `E: Elem` - // lets us know that any byte combination is valid. - let mut value = unsafe { value.assume_init() }; - - value.from_stored_in_place(); - - value - } - - fn dyn_region_read_all( - region: &Self::DynRegion, - address: usize, - values: &mut [E], - ) { - // SAFETY: `E: Elem` tells us that values of that type would be arranged contiguously in - // addition to values of `E` being valid for any raw byte combination. - // Hence, obtain a slice that points to the same underlying memory as `values` and populate - // it with the raw bytes. - let raw_values = unsafe { - let data = values.as_mut_ptr().cast::(); - let len = mem::size_of_val(values); - slice::from_raw_parts_mut(data, len) - }; - - // `read_bytes` fills the entire slice. - region.read_bytes(address, raw_values); - - for value in values { - value.from_stored_in_place(); - } - } - - fn enriched_cell_read_stored(cell: &Self::EnrichedCell) -> V::E - where - V: EnrichedValue, - V::E: Copy, - { - *Self::enriched_cell_ref_stored(cell) - } - - fn enriched_cell_read_derived(cell: &Self::EnrichedCell) -> V::D - where - V: super::EnrichedValueLinked, - V::D: Copy, - { - let stored = Self::enriched_cell_ref_stored(cell); - V::derive(stored) - } - - fn enriched_cell_ref_stored(cell: &Self::EnrichedCell) -> &V::E - where - V: EnrichedValue, - { - Self::region_ref(&cell.underlying, 0) - } -} - -impl ManagerWrite for Verifier { - fn region_write( - region: &mut Self::Region, - index: usize, - value: E, - ) { - match region { - Region::Absent => { - // We can't uses `[None; LEN]` because `E: Copy` is not given. - let mut data = Box::new(array::from_fn(|_| None)); - - data[index] = Some(value); - - *region = Region::Partial(data); - } - - Region::Partial(data) => { - data[index] = Some(value); - } - } - } - - fn region_write_all( - region: &mut Self::Region, - values: &[E], - ) { - for (i, &value) in values.iter().enumerate() { - Self::region_write(region, i, value); - } - } - - fn dyn_region_write( - region: &mut Self::DynRegion, - address: usize, - mut value: E, - ) { - value.to_stored_in_place(); - - let raw_data = unsafe { - let raw_ptr = (&value as *const E).cast::(); - let len = mem::size_of::(); - slice::from_raw_parts(raw_ptr, len) - }; - - region.write_bytes(address, raw_data); - } - - fn dyn_region_write_all( - region: &mut Self::DynRegion, - base_address: usize, - values: &[E], - ) { - for (i, &value) in values.iter().enumerate() { - let address = base_address + i * mem::size_of::(); - Self::dyn_region_write(region, address, value); - } - } - - fn enriched_cell_write(cell: &mut Self::EnrichedCell, value: V::E) - where - V: super::EnrichedValueLinked, - { - Self::region_write(&mut cell.underlying, 0, value); - } -} - -impl ManagerReadWrite for Verifier { - fn region_replace( - region: &mut Self::Region, - index: usize, - value: E, - ) -> E { - let old = Self::region_read(region, index); - Self::region_write(region, index, value); - old - } -} - -impl ManagerClone for Verifier { - fn clone_region( - region: &Self::Region, - ) -> Self::Region { - region.clone() - } - - fn clone_dyn_region(region: &Self::DynRegion) -> Self::DynRegion { - region.clone() - } - - fn clone_enriched_cell(cell: &Self::EnrichedCell) -> Self::EnrichedCell - where - V: EnrichedValue, - V::E: Clone, - V::D: Clone, - { - cell.clone() - } -} - -/// Verifier region -#[derive(Clone)] -pub enum Region { - // We maintain a separate [`Absent`] variant in order to save space for regions that aren't - // accessed at all. - Absent, - Partial( - // This needs to be boxed to prevent inflating the size of this type for absent regions. - Box<[Option; LEN]>, - ), -} - -/// Represents either a present and complete region of the `Verifier` state -/// or specifies whether it is only partially present or completely absent. -pub enum PartialState { - /// A region is fully present - Complete(T), - /// A region is absent - Absent, - /// A region is only partially present - Incomplete, -} - -/// Reference to a complete region of the Verifier backend -pub struct CompleteRegionRef<'a, E, const LEN: usize> { - region: &'a [Option; LEN], -} - -impl serde::Serialize for CompleteRegionRef<'_, E, LEN> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - // Replicate [`::serialise_region`] - - // A special encoding for single-element regions helps clean up encoding for serialisation - // formats that contain structures. For example, JSON, where single-element regions would - // be represented as array singletons. - if LEN == 1 { - return self - .region - .first() - .and_then(Option::as_ref) - .ok_or_else(|| ::custom("Region is not complete"))? - .serialize(serializer); - } - - // We're serialising this as a fixed-sized tuple because otherwise `bincode` would prefix - // the length of this array, which is not needed. - let mut serializer = serializer.serialize_tuple(LEN)?; - - for item in self.region.iter() { - serializer.serialize_element(item.as_ref().ok_or_else(|| { - ::custom("Region is not complete") - })?)?; - } - - serializer.end() - } -} - -impl Region { - /// Get the contents of the region if it is fully present or its status otherwise. - pub fn get_partial_region(&self) -> PartialState> { - let region = match self { - Region::Absent => return PartialState::Absent, - Region::Partial(region) => region, - }; - - for value in region.iter() { - if value.is_none() { - return PartialState::Incomplete; - } - } - - PartialState::Complete(CompleteRegionRef { - region: region.as_ref(), - }) - } -} - -impl Index for Region { - type Output = E; - - fn index(&self, index: usize) -> &Self::Output { - match self { - Region::Absent => not_found(), - Region::Partial(region) => match region.get(index).and_then(Option::as_ref) { - Some(value) => value, - None => not_found(), - }, - } - } -} - -/// Page identifier -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct PageId(usize); - -impl PageId { - const LEAF_SIZE: usize = { - if LEAF_SIZE.count_ones() != 1 { - panic!("LEAF_SIZE must be a power of 2"); - } - - LEAF_SIZE - }; - - const LEAF_MASK: usize = !(Self::LEAF_SIZE - 1); - - /// Construct a page idetifier from an address. - pub fn from_address(address: usize) -> Self { - PageId(address & Self::LEAF_MASK) - } - - /// Calculate the offset of an address relative to the start of the identified page. - pub fn offset(&self, address: usize) -> Option { - address.checked_sub(self.0) - } -} - -/// Page of a dynamic region where sub-ranges may not be present -#[derive(Clone, Debug)] -pub struct Page { - data: Box<[u8; LEAF_SIZE]>, - available: RangeSet2, -} - -impl Page { - /// Construct a page where the entire data is present. - fn from_full(data: Box<[u8; LEAF_SIZE]>) -> Self { - let available = RangeSet2::from(0..LEAF_SIZE); - Page { data, available } - } - - /// Read a sub-range of the page. Only returns `Some` if the entire range is present. - fn get(&self, start: usize, len: usize) -> Option<&[u8]> { - if len > LEAF_SIZE.saturating_sub(start) { - return None; - } - - let range = start..start.saturating_add(len); - - // Superset means that `self.available` fully covers `range`. In other words, everything in - // `range` is also in `self.available`. - if !self.available.is_superset(&RangeSet2::::from(range)) { - return None; - } - - Some(&self.data[start..][..len]) - } - - /// Write to a range in the page. This makes that range available to subsequent reads. - fn put(&mut self, start: usize, data: &[u8]) -> bool { - if data.len() > LEAF_SIZE.saturating_sub(start) { - return false; - } - - self.available.union_with(&RangeSet2::::from( - start..data.len().saturating_add(start), - )); - - self.data[start..][..data.len()].copy_from_slice(data); - - true - } - - /// Returns true if every byte of the page is available. - fn is_fully_available(&self) -> bool { - self.available.boundaries() == [0, LEAF_SIZE] - } -} - -impl Default for Page { - fn default() -> Self { - Page { - data: Box::new([0; LEAF_SIZE]), - available: RangeSet2::empty(), - } - } -} - -/// Verifier dynamic region -#[derive(Clone, Debug)] -pub struct DynRegion { - pages: BTreeMap, Page>, -} - -impl DynRegion { - const SANE: bool = { - if LEN.rem_euclid(LEAF_SIZE) != 0 { - panic!("LEN must be a multiple of LEAF_SIZE") - } - - true - }; - - /// Construct a verifier dynamic region using the given known pages. - pub fn from_pages( - pages: impl IntoIterator, Box<[u8; LEAF_SIZE]>)>, - ) -> Self { - if !Self::SANE { - unreachable!() - } - - let pages = pages - .into_iter() - .map(|(id, data)| (id, Page::from_full(data))) - .collect(); - - DynRegion { pages } - } - - /// Read bytes from the dynamic region. - pub fn read_bytes(&self, mut address: usize, mut buffer: &mut [u8]) { - if buffer.is_empty() { - return; - } - - if buffer.len() > LEN.saturating_sub(address) { - not_found() - } - - while !buffer.is_empty() { - let page_index = PageId::from_address(address); - - let Some(page) = self.pages.get(&page_index) else { - not_found() - }; - - let Some(offset) = page_index.offset(address) else { - not_found() - }; - - let chunk_length = buffer.len().min(LEAF_SIZE.saturating_sub(offset)); - - let dst = &mut buffer[..chunk_length]; - let Some(src) = page.get(offset, chunk_length) else { - not_found() - }; - dst.copy_from_slice(src); - - address = address.saturating_add(chunk_length); - buffer = &mut buffer[chunk_length..]; - } - } - - /// Write bytes to the dynamic region. - pub fn write_bytes(&mut self, mut address: usize, mut buffer: &[u8]) { - if buffer.is_empty() { - return; - } - - if buffer.len() > LEN.saturating_sub(address) { - not_found() - } - - while !buffer.is_empty() { - let page_index = PageId::from_address(address); - let page = self.pages.entry(page_index).or_default(); - - let Some(offset) = page_index.offset(address) else { - not_found() - }; - - let chunk_length = buffer.len().min(LEAF_SIZE.saturating_sub(offset)); - - let src = &buffer[..chunk_length]; - if !page.put(offset, src) { - not_found() - }; - - address = address.saturating_add(chunk_length); - buffer = &buffer[chunk_length..]; - } - } - - /// Get the contents of a page if it is fully present or its status otherwise. - pub fn get_partial_page(&self, id: PageId) -> PartialState<&[u8; LEAF_SIZE]> { - match self.pages.get(&id) { - Some(page) if page.is_fully_available() => PartialState::Complete(page.data.as_ref()), - Some(_) => PartialState::Incomplete, - None => PartialState::Absent, - } - } -} - -impl Default for DynRegion { - fn default() -> Self { - DynRegion { - pages: BTreeMap::new(), - } - } -} - -/// Verifier enriched cell -pub struct EnrichedCell { - underlying: Region, -} - -impl Clone for EnrichedCell -where - V::E: Clone, -{ - fn clone(&self) -> Self { - Self { - underlying: self.underlying.clone(), - } - } -} - -impl Cell { - /// Construct an absent verifier cell. - pub const fn absent() -> Self { - Cell::bind(Region::Absent) - } - - /// Construct a verifier cell with a value. - pub fn from_owned(cell: Cell) -> Self { - let values = Box::new(cell.into_region().map(Some)); - let region = Region::Partial(values); - Cell::bind(region) - } -} - -impl TryFrom>> for Cell { - type Error = PartialHashError; - - fn try_from(cell: Cell>) -> Result { - match cell.into_region() { - Region::Absent => Err(PartialHashError::PotentiallyRecoverable), - Region::Partial(value) => match value.as_ref() { - [Some(v)] => Ok(Cell::bind([v.clone()])), - [None] => Err(PartialHashError::PotentiallyRecoverable), - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::state_backend; - use crate::state_backend::Cells; - use crate::state_backend::DynCells; - use crate::state_backend::EnrichedValueLinked; - - /// Ensures that page indices are properly calculated. - #[test] - fn page_index() { - let page0 = [ - PageId::<4>::from_address(0), - PageId::<4>::from_address(1), - PageId::<4>::from_address(2), - PageId::<4>::from_address(3), - ]; - - let page4 = [ - PageId::<4>::from_address(4), - PageId::<4>::from_address(5), - PageId::<4>::from_address(6), - PageId::<4>::from_address(7), - ]; - - let page8 = [ - PageId::<4>::from_address(8), - PageId::<4>::from_address(9), - PageId::<4>::from_address(10), - PageId::<4>::from_address(11), - ]; - - page0.into_iter().fold((), |_, item| { - assert_eq!(item, page0[0]); - assert!(item < page4[0]); - assert!(item < page8[0]); - }); - - page4.into_iter().fold((), |_, item| { - assert!(item > page0[0]); - assert_eq!(item, page4[0]); - assert!(item < page8[0]); - }); - - page8.into_iter().fold((), |_, item| { - assert!(item > page0[0]); - assert!(item > page4[0]); - assert_eq!(item, page8[0]); - }); - } - - /// Proptest value for a partial region - type PartialRegionArb = Box<[Option; LEN]>; - - /// Proptest value for a region - type RegionArb = Option>; - - /// Construct [`Cells`] from a proptest value. - fn arb_to_cells(region: RegionArb) -> Cells { - let region = match region { - Some(data) => Region::Partial(data), - None => Region::Absent, - }; - - Cells::bind(region) - } - - /// Check functionality of a region that is partially present. - #[test] - fn region_present() { - proptest::proptest!(|(reg: PartialRegionArb)| { - let mut cells: Cells<_, 32, Verifier> = arb_to_cells(Some(reg.clone())); - - for i in 0..32 { - let value = handle_stepper_panics(|| cells.read(i)).ok(); - proptest::prop_assert_eq!(value, reg[i]); - - let new_value = rand::random(); - cells.write(i, new_value); - - let read_value = cells.read(i); - proptest::prop_assert_eq!(read_value, new_value); - } - }); - } - - /// Check functionality of a region that is absent. - #[test] - fn region_absent() { - let cells: Cells = arb_to_cells(None); - - for i in 0..32 { - let value = handle_stepper_panics(|| cells.read(i)).ok(); - assert_eq!(value, None); - } - } - - /// Construct a [`state_backend::EnrichedCell`] from a proptest value. - fn arb_to_enriched_cell( - value: Option, - ) -> state_backend::EnrichedCell { - let cell = match value { - Some(value) => Region::Partial(Box::new([Some(value)])), - None => Region::Absent, - }; - let cell = Cell::bind(cell); - state_backend::EnrichedCell::bind(cell) - } - - /// Check the functionality of an enriched cell whose value may or may not be present. - #[test] - fn enriched_cell() { - struct Ident; - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - struct Derived(u64); - - impl From<&'_ u64> for Derived { - fn from(value: &u64) -> Self { - Derived(*value) - } - } - - impl EnrichedValue for Ident { - type E = u64; - - type D = Derived; - } - - proptest::proptest!(|(initial: Option)| { - let mut cell = arb_to_enriched_cell::(initial); - - let stored = handle_stepper_panics(|| cell.read_stored()).ok(); - proptest::prop_assert_eq!(stored, initial); - - let derived = handle_stepper_panics(|| cell.read_derived()).ok(); - let expected_derived = initial.as_ref().map(::derive); - proptest::prop_assert_eq!(derived, expected_derived); - - let new_value = rand::random(); - cell.write(new_value); - - let read_value = cell.read_stored(); - proptest::prop_assert_eq!(read_value, new_value); - - let new_derived = ::derive(&new_value); - let read_derived = cell.read_derived(); - proptest::prop_assert_eq!(read_derived, new_derived); - }); - } - - macro_rules! assert_eq_found { - ( $left:expr, $right:expr ) => { - assert!(handle_stepper_panics(|| { $left }).is_ok_and(|v| v == $right)) - }; - } - - macro_rules! assert_not_found { - ( $body:expr ) => { - assert!( - handle_stepper_panics(|| { $body }) - .is_err_and(|e| matches!(e, ProofVerificationFailure::AbsentDataAccess(_))) - ) - }; - } - - /// Check the read functionality of a region that has no gaps between its pages. - #[test] - fn dyn_region_continuous() { - const LEAF_SIZE: usize = MERKLE_LEAF_SIZE.get(); - - let mut dyn_region = DynRegion::default(); - dyn_region.write_bytes( - 0, - [1, 3, 3, 7] - .into_iter() - .cycle() - .take(LEAF_SIZE) - .collect::>() - .as_slice(), - ); - dyn_region.write_bytes( - LEAF_SIZE, - [11, 14, 14, 15] - .into_iter() - .cycle() - .take(LEAF_SIZE) - .collect::>() - .as_slice(), - ); - - let mut dyn_cells: DynCells<{ 3 * LEAF_SIZE }, Verifier> = DynCells::bind(dyn_region); - - // Read things that are contained in the first leaf. - assert_eq_found!(dyn_cells.read::<[u8; 4]>(0), [1, 3, 3, 7]); - assert_eq_found!(dyn_cells.read::<[u8; 4]>(1), [3, 3, 7, 1]); - assert_eq_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE - 4), [1, 3, 3, 7]); - - // Read things that span the first and second leaf. - assert_eq_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE - 2), [3, 7, 11, 14]); - - // Read things that are contained in the second leaf. - assert_eq_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE), [11, 14, 14, 15]); - assert_eq_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE + 1), [14, 14, 15, 11]); - - // Read more than is available. - assert_not_found!(dyn_cells.read::<[u8; LEAF_SIZE * 3 + 1]>(0)); - - // Read at an offset that is out of bounds. - assert_not_found!(dyn_cells.read::(LEAF_SIZE * 2)); - - // Write to an index that is out of bounds. - assert_not_found!(dyn_cells.clone().write(LEAF_SIZE * 3, 0u8)); - - // Add more to the third leaf. - let dyn_cells = handle_stepper_panics(move || { - dyn_cells.write(LEAF_SIZE * 2, [255u8, 0]); - dyn_cells - }) - .unwrap(); - assert_eq_found!(dyn_cells.read::<[u8; 6]>(LEAF_SIZE * 2 - 4), [ - 11, 14, 14, 15, 255, 0 - ]); - assert_eq_found!(dyn_cells.read::<[u8; 2]>(LEAF_SIZE * 2), [255, 0]); - assert_not_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE * 2)); - assert_not_found!(dyn_cells.read::<[u8; 2]>(LEAF_SIZE * 2 + 2)); - - // Read at an offset that is out of bounds. - assert_not_found!(dyn_cells.read::(LEAF_SIZE * 3)); - } - - /// Check the functionality of a region that has gaps between its pages. - #[test] - fn dyn_region_gaps() { - const LEAF_SIZE: usize = MERKLE_LEAF_SIZE.get(); - - let mut dyn_region = DynRegion::default(); - dyn_region.write_bytes( - 0, - [7, 3, 3] - .into_iter() - .cycle() - .take(LEAF_SIZE) - .collect::>() - .as_slice(), - ); - dyn_region.write_bytes( - LEAF_SIZE * 2, - [42, 41] - .into_iter() - .cycle() - .take(LEAF_SIZE) - .collect::>() - .as_slice(), - ); - - let mut dyn_cells: DynCells<{ 3 * LEAF_SIZE }, Verifier> = DynCells::bind(dyn_region); - - assert_eq_found!(dyn_cells.read::<[u8; 3]>(0), [7, 3, 3]); - assert_eq_found!(dyn_cells.read::<[u8; 2]>(1), [3, 3]); - assert_eq_found!(dyn_cells.read::<[u8; 1]>(LEAF_SIZE * 2), [42]); - assert_eq_found!(dyn_cells.read::<[u8; 1]>(LEAF_SIZE * 2 + 1), [41]); - - // Read a range that covers a gap. - assert_not_found!(dyn_cells.read::<[u8; LEAF_SIZE + 4]>(LEAF_SIZE - 2)); - assert_not_found!(dyn_cells.read::<[u8; LEAF_SIZE]>(LEAF_SIZE)); - - // Write within the gap. - let dyn_cells = handle_stepper_panics(move || { - dyn_cells.write(LEAF_SIZE - 1, [1u8, 1, 3]); - dyn_cells - }) - .unwrap(); - - assert_eq_found!(dyn_cells.read::<[u8; 3]>(LEAF_SIZE - 1), [1, 1, 3]); - assert_eq_found!(dyn_cells.read::<[u8; 2]>(LEAF_SIZE), [1, 3]); - assert_eq_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE - 2), [3, 1, 1, 3]); - - assert_not_found!(dyn_cells.read::<[u8; 6]>(LEAF_SIZE - 1)); - assert_not_found!(dyn_cells.read::<[u8; 4]>(LEAF_SIZE)); - } -} diff --git a/src/riscv/lib/src/stepper.rs b/src/riscv/lib/src/stepper.rs deleted file mode 100644 index c08a91f8b48b9343c1a36452ebd0ad411eb7169e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper.rs +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::ops::AddAssign; -use std::ops::Bound; - -use crate::machine_state::CacheLayouts; -use crate::machine_state::MachineCoreState; -use crate::machine_state::memory::MemoryConfig; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerRead; - -pub mod pvm; -#[cfg(test)] -mod test; - -/// Status of a stepper -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum StepperStatus { - /// Stepper is still running. - Running { steps: usize }, - - /// Stepper exited. - Exited { - steps: usize, - success: bool, - status: String, - }, - - /// Stepper errored. - Errored { - steps: usize, - cause: String, - message: String, - }, -} - -impl StepperStatus { - /// Get the number of steps taken so far. - pub fn steps(&self) -> usize { - *match self { - StepperStatus::Running { steps } => steps, - StepperStatus::Exited { steps, .. } => steps, - StepperStatus::Errored { steps, .. } => steps, - } - } -} - -impl Default for StepperStatus { - fn default() -> Self { - Self::Running { steps: 0 } - } -} - -impl AddAssign for StepperStatus { - fn add_assign(&mut self, mut rhs: Self) { - match self { - StepperStatus::Running { steps: prev_steps } => match &mut rhs { - StepperStatus::Running { steps } - | StepperStatus::Exited { steps, .. } - | StepperStatus::Errored { steps, .. } => { - *steps = steps.saturating_add(*prev_steps); - } - }, - - StepperStatus::Exited { .. } | StepperStatus::Errored { .. } => return, - } - - *self = rhs; - } -} - -/// Result after performing a number of steps -pub trait StepResult: Default { - /// Retrieve the status of the stepper - fn to_stepper_status(&self) -> StepperStatus; -} - -impl StepResult for StepperStatus { - #[inline(always)] - fn to_stepper_status(&self) -> StepperStatus { - self.clone() - } -} - -/// Interface for a debuggable stepper -pub trait Stepper { - /// Memory config of the underlying machine state - type MemoryConfig: MemoryConfig; - - /// Layout of the instruction cache - type CacheLayouts: CacheLayouts; - - /// State backend with which the stepper was instantiated - type Manager: ManagerBase + ManagerRead; - - /// Obtain a reference to the underlying machine state. - fn machine_state(&self) -> &MachineCoreState; - - /// Result of one or more steps - type StepResult: StepResult; - - /// Run as many steps as possible but not more than `max`. - fn step_max(&mut self, max: Bound) -> Self::StepResult; -} diff --git a/src/riscv/lib/src/stepper/pvm.rs b/src/riscv/lib/src/stepper/pvm.rs deleted file mode 100644 index 3786f1be4c416d70483d12dcdd199dbfd01fdfac..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper/pvm.rs +++ /dev/null @@ -1,402 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -mod reveals; - -use std::ops::Bound; -use std::path::Path; - -use reveals::RevealRequestResponseMap; -use serde::Serialize; -use serde::de::DeserializeOwned; -use tezos_smart_rollup_utils::inbox::Inbox; - -use super::Stepper; -use super::StepperStatus; -use crate::kernel_loader; -use crate::machine_state::CacheLayouts; -use crate::machine_state::DefaultCacheLayouts; -use crate::machine_state::MachineCoreState; -use crate::machine_state::MachineError; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::block_cache::block::Interpreted; -use crate::machine_state::block_cache::block::InterpretedBlockBuilder; -use crate::machine_state::memory::M1G; -use crate::machine_state::memory::MemoryConfig; -use crate::program::Program; -use crate::pvm::Pvm; -use crate::pvm::PvmHooks; -use crate::pvm::PvmLayout; -use crate::pvm::PvmStatus; -use crate::range_utils::bound_saturating_sub; -use crate::state_backend::AllocatedOf; -use crate::state_backend::FnManagerIdent; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerReadWrite; -use crate::state_backend::ProofLayout; -use crate::state_backend::ProofTree; -use crate::state_backend::Ref; -use crate::state_backend::hash::Hash; -use crate::state_backend::owned_backend::Owned; -use crate::state_backend::proof_backend::ProofGen; -use crate::state_backend::proof_backend::proof::Proof; -use crate::state_backend::verify_backend::ProofVerificationFailure; -use crate::state_backend::verify_backend::Verifier; -use crate::state_backend::verify_backend::handle_stepper_panics; -use crate::storage::binary; - -/// Error during PVM stepping -#[derive(Debug, derive_more::From, thiserror::Error, derive_more::Display)] -pub enum PvmStepperError { - /// Errors related to the machine state - MachineError(MachineError), - - /// Errors arising from loading the kernel - KernelError(kernel_loader::Error), -} - -/// Wrapper over a PVM that lets you step through it -pub struct PvmStepper< - 'hooks, - MC: MemoryConfig = M1G, - CL: CacheLayouts = DefaultCacheLayouts, - M: ManagerBase = Owned, - B: Block = Interpreted, -> { - pvm: Pvm, - hooks: PvmHooks<'hooks>, - inbox: Inbox, - rollup_address: [u8; 20], - origination_level: u32, - reveal_request_response_map: RevealRequestResponseMap, -} - -impl<'hooks, MC: MemoryConfig, B: Block, CL: CacheLayouts> - PvmStepper<'hooks, MC, CL, Owned, B> -{ - /// Create a new PVM stepper. - #[expect( - clippy::too_many_arguments, - reason = "Not worth refactoring this constructor function" - )] - pub fn new( - program: &[u8], - initrd: Option<&[u8]>, - inbox: Inbox, - hooks: PvmHooks<'hooks>, - rollup_address: [u8; 20], - origination_level: u32, - preimages_dir: Option>, - block_builder: B::BlockBuilder, - ) -> Result { - let mut pvm = Pvm::empty(block_builder); - - let program = Program::::from_elf(program)?; - - pvm.setup_linux_process(&program)?; - assert!(initrd.is_none(), "initrd is not supported"); - - let reveal_request_response_map = - RevealRequestResponseMap::new(rollup_address, origination_level, preimages_dir); - - Ok(Self { - pvm, - hooks, - inbox, - rollup_address, - origination_level, - reveal_request_response_map, - }) - } - - /// Obtain the root hash for the PVM state. - pub fn hash(&self) -> Hash { - self.pvm.hash().unwrap() - } -} - -impl PvmStepper<'_, MC, CL, Owned> { - /// Produce the Merkle proof for evaluating one step on the given PVM state. - /// The given stepper takes one step. - pub fn produce_proof(&mut self) -> Option { - // Step using the proof mode stepper in order to obtain the proof - let mut proof_stepper = self.start_proof_mode(); - - proof_stepper.try_step().then_some(())?; - - let proof = proof_stepper.pvm.to_proof().ok()?; - Some(proof) - } -} - -impl, M: ManagerReadWrite> - PvmStepper<'_, MC, CL, M, B> -{ - /// Non-continuing variant of [`Stepper::step_max`] - fn step_max_once(&mut self, steps: Bound) -> StepperStatus { - // SAFETY: We're in a stepper context where divergence (e.g. early exit) is allowed. - unsafe { - if let Some(exit_code) = self.pvm.has_exited() { - return StepperStatus::Exited { - steps: 0, - success: exit_code == 0, - status: format!("Exited with code {}", exit_code), - }; - } - } - - match self.pvm.status() { - PvmStatus::Evaluating => { - let steps = self.pvm.eval_max(&mut self.hooks, steps); - StepperStatus::Running { steps } - } - - PvmStatus::WaitingForInput => match self.inbox.next() { - Some((level, counter, payload)) => { - let success = - self.pvm - .provide_inbox_message(level, counter, payload.as_slice()); - - if success { - StepperStatus::Running { steps: 1 } - } else { - StepperStatus::Errored { - steps: 0, - cause: "PVM was waiting for input".to_owned(), - message: "Providing input did not succeed".to_owned(), - } - } - } - - None => StepperStatus::Exited { - steps: 0, - success: true, - status: "Inbox has been drained".to_owned(), - }, - }, - - PvmStatus::WaitingForReveal => { - let reveal_request = self.pvm.reveal_request(); - - let Some(reveal_response) = self - .reveal_request_response_map - .get_response(reveal_request.as_slice()) - else { - // TODO: RV-573: Handle incorrectly encoded request/ Unavailable data differently in the sandbox. - // When the PVM sends an incorrectly encoded reveal request, the stepper should return an error. - // When the PVM sends a request for unavailable data, the stepper should exit. - self.pvm.provide_reveal_error_response(); - - return StepperStatus::Running { steps: 1 }; - }; - - let success = self.pvm.provide_reveal_response(&reveal_response); - if success { - StepperStatus::Running { steps: 1 } - } else { - StepperStatus::Errored { - steps: 0, - cause: "PVM was waiting for reveal response".to_owned(), - message: "Providing reveal response did not succeed".to_owned(), - } - } - } - } - } - - /// Try to take one step and return true if the PVM is not in an errored state. - fn try_step(&mut self) -> bool { - match self.step_max_once(Bound::Included(1)) { - // We don't include errors in this case because errors count as 0 steps. That means if - // we receive a [`StepperStatus::Errored`] with `steps: 1` then it tried to run 2 steps - // but failed at the second. - StepperStatus::Running { steps: 1 } | StepperStatus::Exited { steps: 1, .. } => true, - _ => false, - } - } - - /// Given a manager morphism `f : &M -> N`, return the layout's allocated structure containing - /// the constituents of `N` that were produced from the constituents of `&M`. - pub fn struct_ref(&self) -> AllocatedOf, Ref<'_, M>> { - self.pvm.struct_ref::() - } - - /// Re-bind the PVM type by serialising and deserialising its state in order to eliminate - /// ephemeral state that doesn't make it into the serialised output. - /// - /// We additionally pass a new instance of the [`BlockBuilder`], to be used in the deserialised - /// pvm. - /// - /// [`BlockBuilder`]: Block::BlockBuilder - pub fn rebind_via_serde(&mut self, block_builder: B::BlockBuilder) - where - for<'a> AllocatedOf, Ref<'a, M>>: Serialize, - AllocatedOf, M>: DeserializeOwned, - { - let space = { - let refs = self.pvm.struct_ref::(); - let data = binary::serialise(&refs).unwrap(); - binary::deserialise(&data).unwrap() - }; - - self.pvm = Pvm::bind(space, block_builder); - } -} - -impl<'hooks, MC: MemoryConfig, CL: CacheLayouts, M: ManagerReadWrite> - PvmStepper<'hooks, MC, CL, M> -{ - /// Create a new stepper in which the existing PVM is managed by - /// the proof-generating backend. - pub fn start_proof_mode<'a>(&'a self) -> PvmStepper<'hooks, MC, CL, ProofGen>> { - PvmStepper::<'hooks, MC, CL, ProofGen>> { - pvm: self.pvm.start_proof(), - rollup_address: self.rollup_address, - origination_level: self.origination_level, - - // The inbox needs to be cloned because we should not mutate it through the new stepper - // instance. - inbox: self.inbox.clone(), - - // We don't want to re-use the same hooks to avoid polluting logs with refutation game - // output. Instead we use hooks that don't do anything. - hooks: PvmHooks::none(), - - // TODO: RV-462 Pvm stepper uses response map less expensive to clone - reveal_request_response_map: self.reveal_request_response_map.clone(), - } - } - - /// Verify a Merkle proof. The [`PvmStepper`] is used for inbox information. - pub fn verify_proof(&self, proof: Proof) -> Result<(), ProofVerificationFailure> { - let proof_tree = ProofTree::Present(proof.tree()); - let space = PvmLayout::::from_proof(proof_tree) - .map_err(|_| ProofVerificationFailure::UnexpectedProofShape)?; - - let pvm = Pvm::, Verifier>::bind(space, InterpretedBlockBuilder); - let stepper = PvmStepper { - pvm, - rollup_address: self.rollup_address, - origination_level: self.origination_level, - - // The inbox needs to be cloned because we should not mutate it through the new stepper - // instance. - inbox: self.inbox.clone(), - - // We don't want to re-use the same hooks to avoid polluting logs with refutation game - // output. Instead we use hooks that don't do anything. - hooks: PvmHooks::none(), - - // TODO: RV-462 Pvm stepper uses response map less expensive to clone - reveal_request_response_map: self.reveal_request_response_map.clone(), - }; - - let stepper = stepper.try_step_partial()?; - - let refs = stepper.pvm.struct_ref::(); - let final_hash = PvmLayout::::partial_state_hash(refs, proof_tree)?; - if final_hash != proof.final_state_hash() { - return Err(ProofVerificationFailure::FinalHashMismatch { - expected: proof.final_state_hash(), - computed: final_hash, - }); - } - - Ok(()) - } -} - -impl PvmStepper<'_, MC, CL, M> { - /// Perform one evaluation step. - pub fn eval_one(&mut self) { - self.pvm.eval_one(&mut self.hooks) - } -} - -impl> - PvmStepper<'_, MC, CL, Verifier, B> -{ - /// Try to take one step. Stepping with the [`Verifier`] backend may panic - /// when attempting to access absent data. Return [`NotFound`] panics, which - /// are expected in the case of verifying an invalid proof, as - /// [`ProofVerificationFailure::AbsentDataAccess`] and all other panics - /// as [`ProofVerificationFailure::StepperPanic`]. - /// - /// [`NotFound`]: crate::state_backend::verify_backend::NotFound - fn try_step_partial(self) -> Result { - // Wrapping the stepper in a Mutex, which implements poisoning, in order to pass it - // across the unwind boundary. - let mutex = std::sync::Mutex::new(self); - handle_stepper_panics(move || { - { - let mut stepper = mutex.lock().unwrap(); - if !stepper.try_step() { - return Err(ProofVerificationFailure::StepperError); - }; - } - // Since all panics were handled and returned as errors, at this point - // the mutex cannot be poisoned. - Ok(mutex.into_inner().unwrap()) - })? - } -} - -impl, CL: CacheLayouts> Stepper - for PvmStepper<'_, MC, CL, Owned, B> -{ - type MemoryConfig = MC; - - type CacheLayouts = CL; - - type Manager = Owned; - - fn machine_state(&self) -> &MachineCoreState { - &self.pvm.machine_state.core - } - - type StepResult = StepperStatus; - - fn step_max(&mut self, mut step_bounds: Bound) -> Self::StepResult { - let mut total_steps = 0usize; - - loop { - match self.step_max_once(step_bounds) { - StepperStatus::Running { steps } => { - total_steps = total_steps.saturating_add(steps); - step_bounds = bound_saturating_sub(step_bounds, steps); - - if steps < 1 { - // Break if no progress has been made. - break StepperStatus::Running { steps: total_steps }; - } - } - - StepperStatus::Exited { - steps, - success, - status, - } => { - break StepperStatus::Exited { - steps: total_steps.saturating_add(steps), - success, - status, - }; - } - - StepperStatus::Errored { - steps, - cause, - message, - } => { - break StepperStatus::Errored { - steps: total_steps.saturating_add(steps), - cause, - message, - }; - } - } - } - } -} diff --git a/src/riscv/lib/src/stepper/pvm/reveals.rs b/src/riscv/lib/src/stepper/pvm/reveals.rs deleted file mode 100644 index 1c5be932ef39d1f46b4abdca580f5614cdf8a6da..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper/pvm/reveals.rs +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: 2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::collections::HashMap; -use std::fs; -use std::path::Path; - -use tezos_crypto_rs::blake2b::digest_256; -use tezos_smart_rollup_constants::core::METADATA_LENGTH; -use tezos_smart_rollup_constants::core::PREIMAGE_HASH_SIZE; -use tezos_smart_rollup_constants::core::ROLLUP_ADDRESS_LENGTH; - -/// Data structure that maps reveal request to reveal response -#[derive(Clone)] -pub struct RevealRequestResponseMap { - map: HashMap, Box<[u8]>>, - preimages_dir: Option>, -} - -impl RevealRequestResponseMap { - /// Construct the mapping from reveal request to reveal response - pub fn new( - rollup_address: [u8; 20], - origination_level: u32, - preimages_dir: Option>, - ) -> Self { - let mut reveal_request_response_map = Self { - map: HashMap::new(), - preimages_dir, - }; - - // Entry for responding to reveal_metadata request - reveal_request_response_map.add_metadata(rollup_address, origination_level); - - reveal_request_response_map - } - - /// Construct an entry of RevealRequestResponseMap with static response - pub fn add_static(&mut self, request: impl Into>, response: impl Into>) { - let response_value: Box<[u8]> = response.into(); - self.map.insert(request.into(), response_value); - } - - /// Get response function for the given request - pub fn get_response(&self, request: &[u8]) -> Option> { - self.map - .get(request) - .cloned() - .or_else(|| self.load_response_from_disk(request)) - } - - fn load_response_from_disk(&self, request: &[u8]) -> Option> { - let preimages_dir = self.preimages_dir.as_ref()?; - - // Expecting the first byte to be the tag for preimage hash - if request.first() != Some(&0) { - return None; - } - - let preimage_hash = &request[1..]; - let hex_name = hex::encode(preimage_hash); - let file_path = preimages_dir.join(hex_name); - - if !file_path.is_file() { - return None; - } - - let content = fs::read(file_path).ok()?; - if check_preimage_hash(&content, preimage_hash) { - Some(content.into_boxed_slice()) - } else { - None - } - } - - fn add_metadata( - &mut self, - rollup_address: [u8; ROLLUP_ADDRESS_LENGTH], - origination_level: u32, - ) { - let mut metadata_response_buffer = [0u8; METADATA_LENGTH]; - metadata_response_buffer[..ROLLUP_ADDRESS_LENGTH].copy_from_slice(&rollup_address); - metadata_response_buffer[ROLLUP_ADDRESS_LENGTH..] - .copy_from_slice(&origination_level.to_be_bytes()); - self.add_static([1u8], metadata_response_buffer); - } -} - -fn check_preimage_hash(preimage: &[u8], preimage_hash: &[u8]) -> bool { - // Only blake2b (`preimage_hash[0] == 0`) is supported for now - if preimage_hash.len() != PREIMAGE_HASH_SIZE || preimage_hash[0] != 0 { - return false; - } - - let computed_hash = digest_256(preimage); - preimage_hash[1..] == computed_hash -} diff --git a/src/riscv/lib/src/stepper/test.rs b/src/riscv/lib/src/stepper/test.rs deleted file mode 100644 index 4cc6b3c1b396ead9dd431e528d770b5a747d49f6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper/test.rs +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -mod interpreter; -mod posix; - -use std::collections::BTreeMap; -use std::ops::Bound; - -use derive_more::Error; -use derive_more::From; -use posix::PosixState; - -use super::StepResult; -use super::Stepper; -use super::StepperStatus; -use crate::kernel_loader; -use crate::machine_state::CacheLayouts; -use crate::machine_state::MachineCoreState; -use crate::machine_state::MachineError; -use crate::machine_state::MachineState; -use crate::machine_state::StepManyResult; -use crate::machine_state::TestCacheLayouts; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::block_cache::block::Interpreted; -use crate::machine_state::memory::M1G; -use crate::machine_state::memory::Memory; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::memory::Permissions; -use crate::program::Program; -use crate::state::NewState; -use crate::state_backend::owned_backend::Owned; -use crate::traps::EnvironException; - -#[derive(Clone, Debug)] -pub enum TestStepperResult { - /// Execution has not finished. Returns the number of steps executed. - Running { steps: usize }, - /// Program exited. Returns exit code and number of steps executed. - Exit { code: usize, steps: usize }, - /// Execution finished because an unhandled environment exception has been thrown. - /// Returns exception and number of steps executed. - Exception { - cause: EnvironException, - steps: usize, - message: Option, - }, -} - -impl Default for TestStepperResult { - fn default() -> Self { - Self::Running { steps: 0 } - } -} - -impl StepResult for TestStepperResult { - fn to_stepper_status(&self) -> StepperStatus { - match self { - Running { steps } => StepperStatus::Running { steps: *steps }, - Exit { code, steps } => StepperStatus::Exited { - steps: *steps, - success: *code == 0, - status: format!("code {code}"), - }, - Exception { - cause, - steps, - message, - } => StepperStatus::Errored { - steps: *steps, - cause: format!("{cause:?}"), - message: message.as_deref().unwrap_or("").to_owned(), - }, - } - } -} - -use TestStepperResult::*; - -#[derive(Debug, From, Error, derive_more::Display)] -pub enum TestStepperError { - KernelLoadingError(kernel_loader::Error), - MachineError(MachineError), -} - -pub struct TestStepper< - MC: MemoryConfig = M1G, - CL: CacheLayouts = TestCacheLayouts, - B: Block = Interpreted, -> { - machine_state: MachineState, - posix_state: PosixState, -} - -impl> TestStepper { - /// Initialise an interpreter with a given `program`. - /// An initial ramdisk can also optionally be passed. - #[inline] - pub fn new( - program: &[u8], - initrd: Option<&[u8]>, - block_builder: B::BlockBuilder, - ) -> Result { - Ok(Self::new_with_parsed_program(program, initrd, block_builder)?.0) - } - - /// Consumes the stepper, returning the [`BlockBuilder`] used internally. - /// - /// This allows the block builder to be re-used with a second stepper. - /// - /// [`BlockBuilder`]: Block::BlockBuilder - pub fn recover_builder(self) -> B::BlockBuilder { - self.machine_state.block_cache.block_builder - } - - /// Initialise an interpreter with a given `program`. - /// An initial ramdisk can also optionally be passed. Returns both the interpreter - /// and the fully parsed program. - #[inline] - pub fn new_with_parsed_program( - program: &[u8], - initrd: Option<&[u8]>, - block_builder: B::BlockBuilder, - ) -> Result<(Self, BTreeMap), TestStepperError> { - let mut stepper = Self { - posix_state: PosixState::new(&mut Owned), - machine_state: MachineState::new(&mut Owned, block_builder), - }; - - // The interpreter needs a program to run. - let elf_program = Program::::from_elf(program)?; - - stepper - .machine_state - .core - .main_memory - .protect_pages(0, MC::TOTAL_BYTES, Permissions::ReadWriteExec) - .unwrap(); - - stepper.machine_state.setup_boot(&elf_program, initrd)?; - - Ok((stepper, elf_program.parsed())) - } - - fn handle_step_result( - &mut self, - result: StepManyResult<(EnvironException, String)>, - ) -> TestStepperResult { - match result.error { - // An error was encountered in the evaluation function. - Some((cause, error)) => TestStepperResult::Exception { - cause, - steps: result.steps, - message: Some(error), - }, - - // Evaluation function returned without error. - None => { - // Check if the machine has exited. - if let Some(code) = self.posix_state.exit_code() { - Exit { - code: code as usize, - steps: result.steps, - } - } else { - Running { - steps: result.steps, - } - } - } - } - } -} - -impl> Stepper for TestStepper { - type MemoryConfig = MC; - - type CacheLayouts = TestCacheLayouts; - - type Manager = Owned; - - #[inline(always)] - fn machine_state(&self) -> &MachineCoreState { - &self.machine_state.core - } - - type StepResult = TestStepperResult; - - fn step_max(&mut self, steps: Bound) -> Self::StepResult { - let result = self - .machine_state - .step_max_handle(steps, |machine_state, exc| { - self.posix_state - .handle_call(machine_state) - .map_err(|message| (exc, message)) - }); - self.handle_step_result(result) - } -} diff --git a/src/riscv/lib/src/stepper/test/interpreter.rs b/src/riscv/lib/src/stepper/test/interpreter.rs deleted file mode 100644 index 169864950f48f9e64af5a1ce2807be90da7a6066..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper/test/interpreter.rs +++ /dev/null @@ -1,247 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fs; -use std::io::Write; -use std::ops::Bound; - -use goldenfile::Mint; -use paste::paste; - -use crate::jit::JIT; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::block_cache::block::Interpreted; -use crate::machine_state::block_cache::block::Jitted; -use crate::machine_state::memory::M1M; -use crate::machine_state::registers::XRegister; -use crate::machine_state::registers::XValue; -use crate::state_backend::ManagerRead; -use crate::state_backend::owned_backend::Owned; -use crate::stepper::Stepper; -use crate::stepper::test::TestStepper; -use crate::stepper::test::TestStepperResult::*; - -const TESTS_DIR: &str = "../assets/generated"; -const GOLDEN_DIR: &str = "tests/expected"; -const MAX_STEPS: usize = 1_000_000; - -fn check_register_values(stepper: &S, check_xregs: &[(XRegister, XValue)]) -where - S::Manager: ManagerRead, -{ - let failure = check_xregs - .iter() - .filter_map(|(xreg, xval)| { - let res = stepper.machine_state().hart.xregisters.read(*xreg); - (res != *xval).then(|| format!("\n- check {xreg} == {xval} | got {res}")) - }) - .collect::(); - - if !failure.is_empty() { - panic!("XRegisters conditions not satisfied: {failure}"); - }; -} - -fn run_test_with_check>( - path: &str, - check_xregs: &[(XRegister, u64)], - block_builder: B::BlockBuilder, -) -> B::BlockBuilder { - // Create a Mint instance: when it goes out of scope (at the end of interpret_test_with_check), - // all golden files will be compared to the checked-in versions. - let mut mint = Mint::new(GOLDEN_DIR); - let mut golden = mint.new_goldenfile(format!("{path}.out")).unwrap(); - - let contents = fs::read(format!("{TESTS_DIR}/{path}")).expect("Failed to read binary"); - - let mut interpreter: TestStepper = - TestStepper::new(&contents, None, block_builder).expect("Boot failed"); - - let res = interpreter.step_max(Bound::Included(MAX_STEPS)); - // Record the result to compare to the expected result - writeln!(golden, "{res:?}").unwrap(); - match res { - Exit { code: 0, .. } => check_register_values(&interpreter, check_xregs), - Exit { code, steps } => { - panic!("Failed at test case: {} - Steps done: {}", code >> 1, steps) - } - Running { .. } => panic!("Timeout"), - Exception { - cause, - message, - steps, - } => match message { - Some(message) => panic!( - "Unexpected exception after {steps} steps: {message} (caused by {:?})", - cause - ), - None => panic!("Unexpected exception after {steps} steps: {:?}", cause), - }, - }; - - interpreter.recover_builder() -} - -fn interpret_test_with_check(path: &str, check_xregs: &[(XRegister, u64)]) { - let block_builder = Default::default(); - run_test_with_check::>(path, check_xregs, block_builder); -} - -/// For the JIT, we run it twice - the first run to build up the blocks, and the -/// second to run with these blocks already compiled (so that we actually use them). -fn inline_jit_test_with_check(path: &str, check_xregs: &[(XRegister, u64)]) { - type BlockImpl = Jitted, M1M, Owned>; - - let block_builder = Default::default(); - let block_builder = run_test_with_check::(path, check_xregs, block_builder); - - run_test_with_check::(path, check_xregs, block_builder); -} - -macro_rules! test_case { - // Run the test cases without specifying xregisters to check - ($(#[$m:meta],)* $name: ident, $path: expr) => { - test_case!($(#[$m],)* $name, $path, &[]); - }; - - // Run the test cases, check xregisters - ($(#[$m:meta],)* $name: ident, $path: expr, $xchecks: expr) => { - paste! { - #[test] - $(#[$m])* - fn [< $name _interpreted >]() { - interpret_test_with_check($path, $xchecks) - } - - #[test] - $(#[$m])* - fn [< $name _inline_jit >]() { - inline_jit_test_with_check($path, $xchecks) - } - } - }; -} - -// RV64-UA -test_case!(test_suite_rv64ua_p_amoadd_d, "rv64ua-p-amoadd_d"); -test_case!(test_suite_rv64ua_p_amoadd_w, "rv64ua-p-amoadd_w"); -test_case!(test_suite_rv64ua_p_amoand_d, "rv64ua-p-amoand_d"); -test_case!(test_suite_rv64ua_p_amoand_w, "rv64ua-p-amoand_w"); -test_case!(test_suite_rv64ua_p_amomax_d, "rv64ua-p-amomax_d"); -test_case!(test_suite_rv64ua_p_amomax_w, "rv64ua-p-amomax_w"); -test_case!(test_suite_rv64ua_p_amomaxu_d, "rv64ua-p-amomaxu_d"); -test_case!(test_suite_rv64ua_p_amomaxu_w, "rv64ua-p-amomaxu_w"); -test_case!(test_suite_rv64ua_p_amomin_d, "rv64ua-p-amomin_d"); -test_case!(test_suite_rv64ua_p_amomin_w, "rv64ua-p-amomin_w"); -test_case!(test_suite_rv64ua_p_amominu_d, "rv64ua-p-amominu_d"); -test_case!(test_suite_rv64ua_p_amominu_w, "rv64ua-p-amominu_w"); -test_case!(test_suite_rv64ua_p_amoor_d, "rv64ua-p-amoor_d"); -test_case!(test_suite_rv64ua_p_amoor_w, "rv64ua-p-amoor_w"); -test_case!(test_suite_rv64ua_p_amoswap_d, "rv64ua-p-amoswap_d"); -test_case!(test_suite_rv64ua_p_amoswap_w, "rv64ua-p-amoswap_w"); -test_case!(test_suite_rv64ua_p_amoxor_d, "rv64ua-p-amoxor_d"); -test_case!(test_suite_rv64ua_p_amoxor_w, "rv64ua-p-amoxor_w"); -test_case!(test_suite_rv64ua_p_lrsc, "rv64ua-p-lrsc"); - -// RV64-UC -test_case!(test_suite_rv64uc_p_rvc, "rv64uc-p-rvc"); - -// RV64-UD -test_case!(test_suite_rv64ud_p_fadd, "rv64ud-p-fadd"); -test_case!(test_suite_rv64ud_p_fclass, "rv64ud-p-fclass"); -test_case!(test_suite_rv64ud_p_fcmp, "rv64ud-p-fcmp"); -test_case!(test_suite_rv64ud_p_fcvt, "rv64ud-p-fcvt"); -test_case!(test_suite_rv64ud_p_fcvt_w, "rv64ud-p-fcvt_w"); -test_case!(test_suite_rv64ud_p_fdiv, "rv64ud-p-fdiv"); -test_case!(test_suite_rv64ud_p_fmadd, "rv64ud-p-fmadd"); -test_case!(test_suite_rv64ud_p_fmin, "rv64ud-p-fmin"); -test_case!(test_suite_rv64ud_p_ldst, "rv64ud-p-ldst"); -test_case!(test_suite_rv64ud_p_move, "rv64ud-p-move"); -test_case!(test_suite_rv64ud_p_recoding, "rv64ud-p-recoding"); -test_case!(test_suite_rv64ud_p_structural, "rv64ud-p-structural"); - -// RV64-UF -test_case!(test_suite_rv64uf_p_fadd, "rv64uf-p-fadd"); -test_case!(test_suite_rv64uf_p_fclass, "rv64uf-p-fclass"); -test_case!(test_suite_rv64uf_p_fcmp, "rv64uf-p-fcmp"); -test_case!(test_suite_rv64uf_p_fcvt, "rv64uf-p-fcvt"); -test_case!(test_suite_rv64uf_p_fcvt_w, "rv64uf-p-fcvt_w"); -test_case!(test_suite_rv64uf_p_fdiv, "rv64uf-p-fdiv"); -test_case!(test_suite_rv64uf_p_fmadd, "rv64uf-p-fmadd"); -test_case!(test_suite_rv64uf_p_fmin, "rv64uf-p-fmin"); -test_case!(test_suite_rv64uf_p_ldst, "rv64uf-p-ldst"); -test_case!(test_suite_rv64uf_p_move, "rv64uf-p-move"); -test_case!(test_suite_rv64uf_p_recoding, "rv64uf-p-recoding"); - -// RV64-UI -test_case!(test_suite_rv64ui_p_add, "rv64ui-p-add"); -test_case!(test_suite_rv64ui_p_addi, "rv64ui-p-addi"); -test_case!(test_suite_rv64ui_p_addiw, "rv64ui-p-addiw"); -test_case!(test_suite_rv64ui_p_addw, "rv64ui-p-addw"); -test_case!(test_suite_rv64ui_p_and, "rv64ui-p-and"); -test_case!(test_suite_rv64ui_p_andi, "rv64ui-p-andi"); -test_case!(test_suite_rv64ui_p_auipc, "rv64ui-p-auipc"); -test_case!(test_suite_rv64ui_p_beq, "rv64ui-p-beq"); -test_case!(test_suite_rv64ui_p_bge, "rv64ui-p-bge"); -test_case!(test_suite_rv64ui_p_bgeu, "rv64ui-p-bgeu"); -test_case!(test_suite_rv64ui_p_blt, "rv64ui-p-blt"); -test_case!(test_suite_rv64ui_p_bltu, "rv64ui-p-bltu"); -test_case!(test_suite_rv64ui_p_bne, "rv64ui-p-bne"); -test_case!(test_suite_rv64ui_p_fence_i, "rv64ui-p-fence_i"); -test_case!(test_suite_rv64ui_p_jal, "rv64ui-p-jal"); -test_case!(test_suite_rv64ui_p_jalr, "rv64ui-p-jalr"); -test_case!(test_suite_rv64ui_p_lb, "rv64ui-p-lb"); -test_case!(test_suite_rv64ui_p_lbu, "rv64ui-p-lbu"); -test_case!(test_suite_rv64ui_p_ld, "rv64ui-p-ld"); -test_case!(test_suite_rv64ui_p_ld_st, "rv64ui-p-ld_st"); -test_case!(test_suite_rv64ui_p_lh, "rv64ui-p-lh"); -test_case!(test_suite_rv64ui_p_lhu, "rv64ui-p-lhu"); -test_case!(test_suite_rv64ui_p_lui, "rv64ui-p-lui"); -test_case!(test_suite_rv64ui_p_lw, "rv64ui-p-lw"); -test_case!(test_suite_rv64ui_p_lwu, "rv64ui-p-lwu"); -test_case!(test_suite_rv64ui_p_ma_data, "rv64ui-p-ma_data"); -test_case!(test_suite_rv64ui_p_or, "rv64ui-p-or"); -test_case!(test_suite_rv64ui_p_ori, "rv64ui-p-ori"); -test_case!(test_suite_rv64ui_p_sb, "rv64ui-p-sb"); -test_case!(test_suite_rv64ui_p_sd, "rv64ui-p-sd"); -test_case!(test_suite_rv64ui_p_sh, "rv64ui-p-sh"); -test_case!(test_suite_rv64ui_p_simple, "rv64ui-p-simple"); -test_case!(test_suite_rv64ui_p_sll, "rv64ui-p-sll"); -test_case!(test_suite_rv64ui_p_slli, "rv64ui-p-slli"); -test_case!(test_suite_rv64ui_p_slliw, "rv64ui-p-slliw"); -test_case!(test_suite_rv64ui_p_sllw, "rv64ui-p-sllw"); -test_case!(test_suite_rv64ui_p_slt, "rv64ui-p-slt"); -test_case!(test_suite_rv64ui_p_slti, "rv64ui-p-slti"); -test_case!(test_suite_rv64ui_p_sltiu, "rv64ui-p-sltiu"); -test_case!(test_suite_rv64ui_p_sltu, "rv64ui-p-sltu"); -test_case!(test_suite_rv64ui_p_sra, "rv64ui-p-sra"); -test_case!(test_suite_rv64ui_p_srai, "rv64ui-p-srai"); -test_case!(test_suite_rv64ui_p_sraiw, "rv64ui-p-sraiw"); -test_case!(test_suite_rv64ui_p_sraw, "rv64ui-p-sraw"); -test_case!(test_suite_rv64ui_p_srl, "rv64ui-p-srl"); -test_case!(test_suite_rv64ui_p_srli, "rv64ui-p-srli"); -test_case!(test_suite_rv64ui_p_srliw, "rv64ui-p-srliw"); -test_case!(test_suite_rv64ui_p_srlw, "rv64ui-p-srlw"); -test_case!(test_suite_rv64ui_p_st_ld, "rv64ui-p-st_ld"); -test_case!(test_suite_rv64ui_p_sub, "rv64ui-p-sub"); -test_case!(test_suite_rv64ui_p_subw, "rv64ui-p-subw"); -test_case!(test_suite_rv64ui_p_sw, "rv64ui-p-sw"); -test_case!(test_suite_rv64ui_p_xor, "rv64ui-p-xor"); -test_case!(test_suite_rv64ui_p_xori, "rv64ui-p-xori"); - -// RV64-UM -test_case!(test_suite_rv64um_p_div, "rv64um-p-div"); -test_case!(test_suite_rv64um_p_divu, "rv64um-p-divu"); -test_case!(test_suite_rv64um_p_divuw, "rv64um-p-divuw"); -test_case!(test_suite_rv64um_p_divw, "rv64um-p-divw"); -test_case!(test_suite_rv64um_p_mul, "rv64um-p-mul"); -test_case!(test_suite_rv64um_p_mulh, "rv64um-p-mulh"); -test_case!(test_suite_rv64um_p_mulhsu, "rv64um-p-mulhsu"); -test_case!(test_suite_rv64um_p_mulhu, "rv64um-p-mulhu"); -test_case!(test_suite_rv64um_p_mulw, "rv64um-p-mulw"); -test_case!(test_suite_rv64um_p_rem, "rv64um-p-rem"); -test_case!(test_suite_rv64um_p_remu, "rv64um-p-remu"); -test_case!(test_suite_rv64um_p_remuw, "rv64um-p-remuw"); -test_case!(test_suite_rv64um_p_remw, "rv64um-p-remw"); diff --git a/src/riscv/lib/src/stepper/test/posix.rs b/src/riscv/lib/src/stepper/test/posix.rs deleted file mode 100644 index e3636a8400c4ce5e62efa24afec6f21faf91493c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/stepper/test/posix.rs +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use crate::machine_state::CacheLayouts; -use crate::machine_state::MachineState; -use crate::machine_state::block_cache::block::Block; -use crate::machine_state::memory::MemoryConfig; -use crate::machine_state::registers::a0; -use crate::machine_state::registers::a7; -use crate::state::NewState; -use crate::state_backend::Cell; -use crate::state_backend::ManagerAlloc; -use crate::state_backend::ManagerBase; -use crate::state_backend::ManagerRead; -use crate::state_backend::ManagerReadWrite; - -/// Posix execution environment state -pub struct PosixState { - code: Cell, - exited: Cell, -} - -impl PosixState { - /// If an exit has been requested, return the exit code. - pub fn exit_code(&self) -> Option - where - M: ManagerRead, - { - self.exited().then(|| self.code.read()) - } - - /// Has an exit been requested? - pub fn exited(&self) -> bool - where - M: ManagerRead, - { - self.exited.read() > 0 - } - - /// Handle a POSIX system call. Returns `Ok(true)` if it makes sense to continue execution. - pub fn handle_call>( - &mut self, - machine: &mut MachineState, - ) -> Result - where - M: ManagerReadWrite, - { - if self.exited() { - // Can't exit twice - return Err("Machine has already exited".to_owned()); - } - - let mut handle_exit = |code| { - let exited = self.exited.read(); - self.exited.write(exited.saturating_add(1)); - self.code.write(code); - - Ok(false) - }; - - // Successful physical memory tests set - // a7 = 93 & a0 = 0 - // Successful virtual memory tests set - // a7 = 0 (a7 never gets set) & a0 = 1 - // Failed physical memory tests set - // a7 = 93 & a0 = 1 | (TEST_FAILED << 1) - // Failed virtual memory tests set - // a7 = 0 (a7 never gets set) & a0 = 1 | (TEST_FAILED << 1) - let a7_val = machine.core.hart.xregisters.read(a7); - let a0_val = machine.core.hart.xregisters.read(a0); - match (a7_val, a0_val) { - // Exit (test pass, physical | virtual) - (93, 0) | (0, 1) => handle_exit(0), - - // Exit (test fail, physical | virtual) - (93, code) | (0, code) => handle_exit(code), - - // Unimplemented - _ => Err(format!("Unknown system call number {a7_val}")), - } - } -} - -impl NewState for PosixState { - fn new(manager: &mut M) -> Self - where - M: ManagerAlloc, - { - PosixState { - code: Cell::new(manager), - exited: Cell::new(manager), - } - } -} diff --git a/src/riscv/lib/src/storage.rs b/src/riscv/lib/src/storage.rs deleted file mode 100644 index 22a8a1625e536d3d7d401d3fd4739bd661ab0ab8..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/storage.rs +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -pub(crate) mod binary; -mod chunked_io; - -use std::io; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; - -use thiserror::Error; - -pub use crate::state_backend::hash::DIGEST_SIZE; -pub use crate::state_backend::hash::Hash; -pub use crate::state_backend::hash::HashError; - -const CHUNK_SIZE: usize = 4096; - -#[derive(Error, Debug)] -pub enum StorageError { - #[error("IO error: {0}")] - IoError(#[from] io::Error), - - #[error("Serialization error: {0}")] - CommitSerializationError(#[from] bincode::Error), - - #[error("Invalid repo")] - InvalidRepo, - - #[error("Hashing error")] - HashError(#[from] HashError), - - #[error("Data for hash {0} not found")] - NotFound(String), - - #[error("Commited chunk {0} not found")] - ChunkNotFound(String), -} - -#[derive(Debug, PartialEq)] -pub struct Store { - path: Box, -} - -impl Store { - /// Initialise a store. Either create a new directory if `path` does not - /// exist or initialise in an existing directory. - /// Throws `StorageError::InvalidRepo` if `path` is a file. - pub fn init(path: impl AsRef) -> Result { - let path = path.as_ref().to_path_buf(); - if !path.exists() { - std::fs::create_dir(&path)?; - } else if path.metadata()?.is_file() { - return Err(StorageError::InvalidRepo); - } - Ok(Store { - path: path.into_boxed_path(), - }) - } - - fn file_name_of_hash(hash: &Hash) -> String { - hex::encode(hash) - } - - fn path_of_hash(&self, hash: &Hash) -> PathBuf { - self.path.join(Self::file_name_of_hash(hash)) - } - - fn write_data_if_new(&self, file_name: PathBuf, data: &[u8]) -> Result<(), StorageError> { - match std::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(file_name) - { - Ok(mut f) => f.write_all(data)?, - Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => (), - Err(e) => return Err(StorageError::IoError(e)), - } - Ok(()) - } - - /// Store data and return its hash. The data is written to disk only if - /// previously unseen. - pub fn store(&self, data: &[u8]) -> Result { - let hash = Hash::blake2b_hash_bytes(data)?; - let file_name = self.path_of_hash(&hash); - self.write_data_if_new(file_name, data)?; - Ok(hash) - } - - /// Load data corresponding to `hash`, if found. - pub fn load(&self, hash: &Hash) -> Result, StorageError> { - let file_name = self.path_of_hash(hash); - std::fs::read(file_name).map_err(|e| { - if e.kind() == io::ErrorKind::NotFound { - StorageError::NotFound(hex::encode(hash)) - } else { - StorageError::IoError(e) - } - }) - } - - /// Copy the data corresponding to `hash` to `path`. - pub fn copy(&self, hash: &Hash, path: impl AsRef) -> Result<(), StorageError> { - let source_path = self.path_of_hash(hash); - let target_path = path.as_ref().join(Self::file_name_of_hash(hash)); - std::fs::copy(source_path, target_path)?; - Ok(()) - } -} - -#[derive(Debug, PartialEq)] -pub struct Repo { - backend: Store, -} - -impl Repo { - /// Load or create new repo at `path`. - pub fn load(path: impl AsRef) -> Result { - Ok(Repo { - backend: Store::init(path)?, - }) - } - - pub fn close(self) {} - - /// Create a new commit for `bytes` and return the commit id. - pub fn commit(&mut self, bytes: &[u8]) -> Result { - let mut commit = Vec::with_capacity(bytes.len().div_ceil(CHUNK_SIZE) * DIGEST_SIZE); - - for chunk in bytes.chunks(CHUNK_SIZE) { - let chunk_hash = self.backend.store(chunk)?; - commit.push(chunk_hash); - } - - // A commit contains the list of all chunks needed to reconstruct `data`. - let commit_bytes = binary::serialise(&commit)?; - self.backend.store(&commit_bytes) - } - - /// Commit something serialisable and return the commit ID. - pub fn commit_serialised( - &mut self, - subject: &impl serde::Serialize, - ) -> Result { - let chunk_hashes = { - let mut writer = chunked_io::ChunkWriter::new(&mut self.backend); - binary::serialise_into(subject, &mut writer)?; - writer.finalise()? - }; - - // A commit contains the list of all chunks needed to reconstruct the underlying data. - let commit_bytes = binary::serialise(&chunk_hashes)?; - self.backend.store(&commit_bytes) - } - - /// Checkout the bytes committed under `id`, if the commit exists. - pub fn checkout(&self, id: &Hash) -> Result, StorageError> { - let bytes = self.backend.load(id)?; - let commit: Vec = binary::deserialise(&bytes)?; - let mut bytes = Vec::new(); - for hash in commit { - let mut chunk = self.backend.load(&hash).map_err(|e| { - if let StorageError::NotFound(hash) = e { - StorageError::ChunkNotFound(hash) - } else { - e - } - })?; - bytes.append(&mut chunk); - } - Ok(bytes) - } - - /// Checkout something deserialisable from the store. - pub fn checkout_serialised( - &self, - id: &Hash, - ) -> Result { - let reader = chunked_io::ChunkedReader::new(&self.backend, id)?; - Ok(binary::deserialise_from(reader)?) - } - - /// A snapshot is a new repo to which only `id` has been committed. - pub fn export_snapshot(&self, id: &Hash, path: impl AsRef) -> Result<(), StorageError> { - // Only export a snapshot to a new or empty directory - let path = path.as_ref(); - if !path.exists() || path.read_dir()?.next().is_none() { - std::fs::create_dir_all(path)?; - } else { - return Err(StorageError::InvalidRepo); - }; - let bytes = self.backend.load(id)?; - let commit: Vec = binary::deserialise(&bytes)?; - for chunk in commit { - self.backend.copy(&chunk, path)?; - } - self.backend.copy(id, path)?; - Ok(()) - } -} diff --git a/src/riscv/lib/src/storage/binary.rs b/src/riscv/lib/src/storage/binary.rs deleted file mode 100644 index 00a7935f7586cd472b1c55c1bf7bd42c6ed554ef..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/storage/binary.rs +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -use std::io::Read; -use std::io::Write; - -use bincode::DefaultOptions; -use bincode::Options; - -/// Constructs the default options for bincode serialisation and deserialisation. -#[inline(always)] -pub(crate) fn bincode_default() -> impl Options { - DefaultOptions::new() - .with_no_limit() - .with_little_endian() - .with_fixint_encoding() - .reject_trailing_bytes() -} - -/// Deserialise a slice of bytes into a value of type `T`. -pub(crate) fn deserialise<'de, T: serde::Deserialize<'de>>(data: &'de [u8]) -> bincode::Result { - bincode_default().deserialize(data) -} - -/// Deserialise a value of type `T` from a byte source. -pub(crate) fn deserialise_from( - source: R, -) -> bincode::Result { - bincode_default().deserialize_from(source) -} - -/// Serialize `T` into a vector of bytes. -pub fn serialise(value: &T) -> bincode::Result> { - bincode_default().serialize(value) -} - -/// Serialize `T` into a sink. -pub(crate) fn serialise_into( - value: &T, - sink: W, -) -> bincode::Result<()> { - bincode_default().serialize_into(sink, value) -} diff --git a/src/riscv/lib/src/storage/chunked_io.rs b/src/riscv/lib/src/storage/chunked_io.rs deleted file mode 100644 index aba0c61faeba5a08895ae2bf7e6d5b039e68d5d5..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/storage/chunked_io.rs +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Trilitech -// -// SPDX-License-Identifier: MIT - -use std::cmp; -use std::collections::VecDeque; -use std::io; -use std::io::Cursor; - -use super::CHUNK_SIZE; -use super::Hash; -use super::StorageError; -use super::Store; -use super::binary; - -/// Simple writer that stores data in chunks of size [`CHUNK_SIZE`] -pub struct ChunkWriter<'a> { - store: &'a mut Store, - hashes: Vec, - buffer: Vec, -} - -impl<'a> ChunkWriter<'a> { - /// Create a new writer that writes the chunks to the given [`Store`]. - pub fn new(store: &'a mut Store) -> Self { - Self { - store, - hashes: Vec::new(), - buffer: Vec::with_capacity(CHUNK_SIZE), - } - } - - /// Finalise the writer by flushing any remaining chunks-in-progress to the store and returning - /// the list of identifiers for each chunk that was written. - pub fn finalise(mut self) -> Result, StorageError> { - if !self.buffer.is_empty() { - self.flush_buffer()?; - } - - Ok(self.hashes) - } - - /// Write a chunk to the store. - fn flush_buffer(&mut self) -> Result<(), StorageError> { - let chunk_hash = self.store.store(self.buffer.as_slice())?; - self.hashes.push(chunk_hash); - self.buffer.clear(); - Ok(()) - } -} - -impl io::Write for ChunkWriter<'_> { - fn write(&mut self, mut data: &[u8]) -> io::Result { - let ret = data.len(); - - while !data.is_empty() { - let rem_buffer_len = CHUNK_SIZE - self.buffer.len(); - let new_data_len = cmp::min(rem_buffer_len, data.len()); - - // Take the data from the input. - let new_data = &data[..new_data_len]; - data = &data[new_data_len..]; - self.buffer.extend_from_slice(new_data); - - // If the buffer has been completely filled, flush it. - if rem_buffer_len == new_data_len { - self.flush_buffer().map_err(io::Error::other)?; - } - } - - Ok(ret) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Just like [`ChunkWriter`], but for reading. -pub struct ChunkedReader<'a> { - store: &'a Store, - hashes: VecDeque, - buffer: Cursor>, -} - -impl<'a> ChunkedReader<'a> { - /// Create a new reader that pulls the chunks from the given [`Store`]. - pub fn new(store: &'a Store, hash: &Hash) -> Result { - let raw_hashes = store.load(hash)?; - let hashes = binary::deserialise(raw_hashes.as_slice())?; - Ok(Self { - store, - hashes, - buffer: Cursor::new(Vec::with_capacity(CHUNK_SIZE)), - }) - } - - /// Start reading the next chunk. - fn next_chunk(&mut self) -> Result<(), StorageError> { - let Some(hash) = self.hashes.pop_front() else { - return Ok(()); - }; - - self.buffer = Cursor::new(self.store.load(&hash)?); - - Ok(()) - } -} - -impl io::Read for ChunkedReader<'_> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - if self.buffer.position() as usize >= self.buffer.get_ref().len() { - self.next_chunk().map_err(io::Error::other)?; - } - - self.buffer.read(buf) - } -} diff --git a/src/riscv/lib/src/traps.rs b/src/riscv/lib/src/traps.rs deleted file mode 100644 index ba1773ef156be037691db6c2ea1b7e6ce47f8a61..0000000000000000000000000000000000000000 --- a/src/riscv/lib/src/traps.rs +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -//! Traps doc -//! There are 4 types of traps, depending on where they are handled and visibility to the hart. -//! ### Contained: -//! A trap which is handled by the normal procedure of -//! trap handling without interacting with the execution environment. -//! (Software knows a trap is taken e.g. U -> M/S, S -> M/S, M -> M) -//! -//! ### Requested: -//! A trap requested by the software to the execution environment. -//! so the software is aware of traps like U/S/M -> EE -> M/S -//! -//! ### Invisible: -//! A trap is handled by the execution environment without software being aware of this. -//! -//! ### Fatal: -//! A trap which causes the execution environment to halt the machine. -//! - -use std::fmt::Formatter; - -use tezos_smart_rollup_constants::riscv::SbiError; - -use crate::machine_state::csregisters::CSRRepr; -use crate::machine_state::memory::Address; - -/// RISC-V Exceptions (also known as synchronous exceptions) -#[derive(PartialEq, Eq, thiserror::Error, strum::Display, Debug, Clone, Copy)] -pub enum EnvironException { - EnvCall, -} - -impl EnvironException { - /// Convert from environment exception. - pub fn as_exception(&self) -> Exception { - // This is not implemeted as a `From<_>` implementation to not make `?` - // syntax harder which unfortunately tries to convert between `Exception` and - // `EnvironException` when "circular" implementations exist. These cycles - // can only be broken with explicit type annotations which is annoying. - - match self { - Self::EnvCall => Exception::EnvCall, - } - } -} - -impl TryFrom<&Exception> for EnvironException { - type Error = &'static str; - - fn try_from(value: &Exception) -> Result { - match value { - Exception::EnvCall => Ok(EnvironException::EnvCall), - Exception::Breakpoint - | Exception::IllegalInstruction - | Exception::InstructionAccessFault(_) - | Exception::LoadAccessFault(_) - | Exception::StoreAMOAccessFault(_) - | Exception::InstructionPageFault(_) - | Exception::LoadPageFault(_) - | Exception::StoreAMOPageFault(_) => { - Err("Execution environment supports only ecall exceptions") - } - } - } -} - -/// RISC-V Exceptions (also known as synchronous exceptions) -#[derive(PartialEq, Eq, thiserror::Error, strum::Display, Clone, Copy)] -pub enum Exception { - /// `InstructionAccessFault(addr)` where `addr` is the faulting instruction address - InstructionAccessFault(Address), - IllegalInstruction, - Breakpoint, - /// `LoadAccessFault(addr)` where `addr` is the faulting load address - LoadAccessFault(Address), - /// `StoreAccessFault(addr)` where `addr` is the faulting store address - StoreAMOAccessFault(Address), - EnvCall, - InstructionPageFault(Address), - LoadPageFault(Address), - StoreAMOPageFault(Address), -} - -impl core::fmt::Debug for Exception { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - match self { - Self::InstructionPageFault(adr) => write!(f, "InstructionPageFault({adr:#X})"), - Self::LoadPageFault(adr) => write!(f, "LoadPageFault({adr:#X})"), - Self::StoreAMOPageFault(adr) => write!(f, "StoreAMOPageFault({adr:#X})"), - Self::LoadAccessFault(adr) => write!(f, "LoadAccessFault({adr:#X})"), - other => write!(f, "{other}"), - } - } -} - -impl From for SbiError { - fn from(value: Exception) -> Self { - match value { - Exception::InstructionAccessFault(_) - | Exception::InstructionPageFault(_) - | Exception::LoadAccessFault(_) - | Exception::LoadPageFault(_) - | Exception::StoreAMOAccessFault(_) - | Exception::StoreAMOPageFault(_) => SbiError::InvalidAddress, - Exception::IllegalInstruction | Exception::Breakpoint | Exception::EnvCall => { - SbiError::Failed - } - } - } -} - -/// RISC-V Interrupts (also known as asynchronous exceptions) -#[derive(PartialEq, Eq, thiserror::Error, strum::Display, Debug, Copy, Clone)] -pub enum Interrupt { - SupervisorSoftware, - MachineSoftware, - SupervisorTimer, - MachineTimer, - SupervisorExternal, - MachineExternal, -} - -impl Interrupt { - pub const SUPERVISOR_SOFTWARE_EXCEPTION_CODE: CSRRepr = 1; - pub const MACHINE_SOFTWARE_EXCEPTION_CODE: CSRRepr = 3; - pub const SUPERVISOR_TIMER_EXCEPTION_CODE: CSRRepr = 5; - pub const MACHINE_TIMER_EXCEPTION_CODE: CSRRepr = 7; - pub const SUPERVISOR_EXTERNAL_EXCEPTION_CODE: CSRRepr = 9; - pub const MACHINE_EXTERNAL_EXCEPTION_CODE: CSRRepr = 11; - - /// Bitmask of all supervisor interrupts - pub const SUPERVISOR_BIT_MASK: CSRRepr = (1 - << Interrupt::SupervisorSoftware.exception_code_const()) - | (1 << Interrupt::SupervisorTimer.exception_code_const()) - | (1 << Interrupt::SupervisorExternal.exception_code_const()); - - /// Bitmask of all machine interrupts - pub const MACHINE_BIT_MASK: CSRRepr = (1 << Interrupt::MachineSoftware.exception_code_const()) - | (1 << Interrupt::MachineTimer.exception_code_const()) - | (1 << Interrupt::MachineExternal.exception_code_const()); - - /// Exception code of interrupts - pub const fn exception_code_const(&self) -> CSRRepr { - match self { - Interrupt::SupervisorSoftware => Self::SUPERVISOR_SOFTWARE_EXCEPTION_CODE, - Interrupt::MachineSoftware => Self::MACHINE_SOFTWARE_EXCEPTION_CODE, - Interrupt::SupervisorTimer => Self::SUPERVISOR_TIMER_EXCEPTION_CODE, - Interrupt::MachineTimer => Self::MACHINE_TIMER_EXCEPTION_CODE, - Interrupt::SupervisorExternal => Self::SUPERVISOR_EXTERNAL_EXCEPTION_CODE, - Interrupt::MachineExternal => Self::MACHINE_EXTERNAL_EXCEPTION_CODE, - } - } -} - -/// Flavour of the trap cause -pub enum TrapKind { - Interrupt, - Exception, -} - -/// Common trait for [`Exception`] & [`Interrupt`] traps used in the context of trap handling -pub trait TrapContext { - /// Trap values to be stored in `xtval` registers when taking a trap. - /// See sections 3.1.16 & 5.1.9 - fn xtval(&self) -> CSRRepr; - - /// Code of trap (exception / interrupt) also known as cause, given by tables 3.6 & 5.2 - /// NOTE: This value does NOT include the interrupt bit - fn exception_code(&self) -> CSRRepr; - - /// xcause value a.k.a. what should be written to `xcause` register. - /// NOTE: this values DOES include the interrupt bit - fn xcause(&self) -> CSRRepr; - - /// Computes the address pc should be set when entering the trap - fn trap_handler_address(&self, xtvec_val: CSRRepr) -> Address; - - /// Obtain the kind that would cause this trap. - fn kind() -> TrapKind; -} - -impl TrapContext for Exception { - fn exception_code(&self) -> CSRRepr { - match self { - Exception::InstructionAccessFault(_) => 1, - Exception::IllegalInstruction => 2, - Exception::Breakpoint => 3, - Exception::LoadAccessFault(_) => 5, - Exception::StoreAMOAccessFault(_) => 7, - Exception::EnvCall => 8, - Exception::InstructionPageFault(_) => 12, - Exception::LoadPageFault(_) => 13, - Exception::StoreAMOPageFault(_) => 15, - } - } - - fn xcause(&self) -> CSRRepr { - self.exception_code() - } - - fn xtval(&self) -> CSRRepr { - match self { - Exception::IllegalInstruction | Exception::Breakpoint | Exception::EnvCall => 0, - Exception::InstructionAccessFault(addr) => *addr, - Exception::LoadAccessFault(addr) => *addr, - Exception::StoreAMOAccessFault(addr) => *addr, - Exception::InstructionPageFault(addr) => *addr, - Exception::LoadPageFault(addr) => *addr, - Exception::StoreAMOPageFault(addr) => *addr, - } - } - - fn trap_handler_address(&self, xtvec_val: CSRRepr) -> Address { - // MODE = xtvec[1:0] - // BASE[xLEN-1:2] = xtvec[xLEN-1:2] - xtvec_val & !0b11 - } - - fn kind() -> TrapKind { - TrapKind::Exception - } -} - -impl TrapContext for Interrupt { - fn xtval(&self) -> CSRRepr { - 0 - } - - fn exception_code(&self) -> CSRRepr { - self.exception_code_const() - } - - fn xcause(&self) -> CSRRepr { - let interrupt_bit = 1 << (CSRRepr::BITS - 1); - interrupt_bit | self.exception_code() - } - - fn trap_handler_address(&self, xtvec_val: CSRRepr) -> Address { - // MODE = xtvec[1:0] - // BASE[xLEN-1:2] = xtvec[xLEN-1:2] - let xtvec_mode = xtvec_val & 0b11; - let xtvec_base = xtvec_val & !0b11; - let handler_offset = match xtvec_mode { - // Vectored mode - 1 => 4 * self.exception_code(), - // Direct or Reserved mode - _ => 0, - }; - - xtvec_base + handler_offset - } - - fn kind() -> TrapKind { - TrapKind::Interrupt - } -} diff --git a/src/riscv/lib/tests/common/mod.rs b/src/riscv/lib/tests/common/mod.rs deleted file mode 100644 index 0c7f0410981dd507d89a69caef7f8a0181d734a5..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/common/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fs; - -use octez_riscv::machine_state::CacheLayouts; -use octez_riscv::machine_state::block_cache::block::InterpretedBlockBuilder; -use octez_riscv::machine_state::memory::M64M; -use octez_riscv::pvm::PvmHooks; -use octez_riscv::stepper::pvm::PvmStepper; -use rand::Rng; -use rand::seq::SliceRandom; -use tezos_smart_rollup_utils::inbox::InboxBuilder; - -pub fn make_stepper_factory() -> impl Fn() -> PvmStepper<'static, M64M, CL> { - let program = fs::read("../assets/jstz").unwrap(); - let initrd = None::>; - - let mut inbox = InboxBuilder::new(); - inbox - .load_from_file("../assets/regression-inbox.json") - .unwrap(); - let inbox = inbox.build(); - - let address = [0; 20]; - - move || { - let hooks = PvmHooks::none(); - let block_builder = InterpretedBlockBuilder; - - PvmStepper::<'_, M64M, CL>::new( - &program, - initrd.as_deref(), - inbox.clone(), - hooks, - address, - 1, - None, - block_builder, - ) - .unwrap() - } -} - -pub fn dissect_steps(mut max_steps: usize, min_step_size: usize) -> Vec { - let mut rng = rand::thread_rng(); - let mut steps: Vec = std::iter::from_fn(|| { - if max_steps == 0 { - return None; - } - - let steps = max_steps.div_euclid(2).max(min_step_size + 1); - let steps = rng.gen_range(min_step_size..=steps); - - max_steps = max_steps.saturating_sub(steps); - - Some(steps) - }) - .collect(); - steps.shuffle(&mut rng); - steps -} diff --git a/src/riscv/lib/tests/expected/dummy-linux-musl-volatile/log b/src/riscv/lib/tests/expected/dummy-linux-musl-volatile/log deleted file mode 100644 index d1963c9cb2c4c9d51879295b8a11591128c20768..0000000000000000000000000000000000000000 Binary files a/src/riscv/lib/tests/expected/dummy-linux-musl-volatile/log and /dev/null differ diff --git a/src/riscv/lib/tests/expected/dummy-linux-musl/log b/src/riscv/lib/tests/expected/dummy-linux-musl/log deleted file mode 100644 index d1963c9cb2c4c9d51879295b8a11591128c20768..0000000000000000000000000000000000000000 Binary files a/src/riscv/lib/tests/expected/dummy-linux-musl/log and /dev/null differ diff --git a/src/riscv/lib/tests/expected/dummy-volatile/log b/src/riscv/lib/tests/expected/dummy-volatile/log deleted file mode 100644 index d1963c9cb2c4c9d51879295b8a11591128c20768..0000000000000000000000000000000000000000 Binary files a/src/riscv/lib/tests/expected/dummy-volatile/log and /dev/null differ diff --git a/src/riscv/lib/tests/expected/dummy/log b/src/riscv/lib/tests/expected/dummy/log deleted file mode 100644 index d1963c9cb2c4c9d51879295b8a11591128c20768..0000000000000000000000000000000000000000 Binary files a/src/riscv/lib/tests/expected/dummy/log and /dev/null differ diff --git a/src/riscv/lib/tests/expected/dummy/result b/src/riscv/lib/tests/expected/dummy/result deleted file mode 100644 index 9d26436fb69b71631f70c6446eeed70ea5486008..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy/result +++ /dev/null @@ -1,5 +0,0 @@ -Exited { - steps: 392185, - success: true, - status: "Inbox has been drained", -} diff --git a/src/riscv/lib/tests/expected/dummy/state_hash_final b/src/riscv/lib/tests/expected/dummy/state_hash_final deleted file mode 100644 index 1e8bae81b16ee08045a996c6c045161338c03593..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy/state_hash_final +++ /dev/null @@ -1 +0,0 @@ -e73d708917db6d0127da35bd720739ac642467d4ec11d24d9bd99f29a6bd9cfc diff --git a/src/riscv/lib/tests/expected/dummy/state_hash_initial b/src/riscv/lib/tests/expected/dummy/state_hash_initial deleted file mode 100644 index f2c85d09de35a91a7538385e38613a9e96424c5d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy/state_hash_initial +++ /dev/null @@ -1 +0,0 @@ -56b4cf1536b0ca0c1880991c463065e02b50ca4a3118c98a4aa47bedbaae8d9e diff --git a/src/riscv/lib/tests/expected/dummy_volatile/log b/src/riscv/lib/tests/expected/dummy_volatile/log deleted file mode 100644 index f32b1024685aa58b0775fe2d376d6be26cfe5523..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy_volatile/log +++ /dev/null @@ -1,114 +0,0 @@ -[src/arch/riscv64/kernel/core_local.rs:38] CPU_ONLINE.load(Ordering::Relaxed) = 0 -[0][WARN] Unable to read entropy! Fallback to a naive implementation! -Hello World -I am sr1UNDWPUYVeomgG15wn5jSw689EJ4RNnVQa -RollupMetadata { - raw_rollup_address: [ - 244, - 228, - 124, - 179, - 196, - 58, - 104, - 176, - 212, - 142, - 48, - 148, - 9, - 44, - 164, - 45, - 113, - 58, - 221, - 181, - ], - origination_level: 1, -} -[09, 90, A8, 2F, DD, B2, 8D, E6, 07, 33, 28, 86, 5C, EF, 23, A4, D5, 2A, CC, 6C, D4, 17, D8, AB, 39, 66, 69, D6, 3C, 3B, A8, BD] -Signature is [133, 153, 89, 165, 225, 52, 223, 180, 255, 131, 0, 136, 151, 153, 173, 101, 89, 156, 116, 182, 204, 196, 243, 216, 218, 77, 17, 96, 8, 132, 254, 190, 220, 2, 63, 202, 122, 112, 169, 61, 119, 166, 30, 78, 142, 64, 30, 40, 210, 60, 25, 219, 193, 87, 197, 19, 194, 101, 0, 232, 241, 244, 56, 2] -Reveal metadata... -Reveal metadata succeeded, result: -Rollup address: RollupMetadata { raw_rollup_address: [244, 228, 124, 179, 196, 58, 104, 176, 212, 142, 48, 148, 9, 44, 164, 45, 113, 58, 221, 181], origination_level: 1 } -Reveal preimage... -Preimage: "cafebabe" -Invalid reveal request... -Reveals Done -Internal( - StartOfLevel, -) -Internal(InfoPerLevel) -External( - [ - 1, - 2, - 3, - 4, - ], -) -External( - [ - 1, - 4, - 3, - 2, - ], -) -Internal( - EndOfLevel, -) -Internal( - StartOfLevel, -) -Internal(InfoPerLevel) -External( - [ - 1, - 1, - ], -) -Internal( - EndOfLevel, -) -Internal( - StartOfLevel, -) -Internal(InfoPerLevel) -External( - [ - 1, - 2, - ], -) -Internal( - EndOfLevel, -) -Internal( - StartOfLevel, -) -Internal(InfoPerLevel) -Internal( - Transfer( - Transfer { - payload: MichelsonUnit, - sender: ContractKt1Hash( - "KT1EfTusMLoeCAAGd9MZJn5yKzFr6kJU5U91", - ), - source: Ed25519( - ContractTz1Hash( - "tz1dJ21ejKD17t7HKcKkTPuwQphgcSiehTYi", - ), - ), - destination: SmartRollupAddress { - hash: SmartRollupHash( - "sr1UNDWPUYVeomgG15wn5jSw689EJ4RNnVQa", - ), - }, - }, - ), -) -Internal( - EndOfLevel, -) diff --git a/src/riscv/lib/tests/expected/dummy_volatile/result b/src/riscv/lib/tests/expected/dummy_volatile/result deleted file mode 100644 index d2ebf17a6b310084882a10976f844232ba3a1ac6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy_volatile/result +++ /dev/null @@ -1,5 +0,0 @@ -Exited { - steps: 8853105, - success: true, - status: "Inbox has been drained", -} diff --git a/src/riscv/lib/tests/expected/dummy_volatile/state_hash_final b/src/riscv/lib/tests/expected/dummy_volatile/state_hash_final deleted file mode 100644 index d8972743c43946789352629b89b11b97e2d1d4ef..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy_volatile/state_hash_final +++ /dev/null @@ -1 +0,0 @@ -f4c8460dc3e462afd3696aa6766b769d4bb94bd5426ebbbb70d5bc0f223904dd diff --git a/src/riscv/lib/tests/expected/dummy_volatile/state_hash_initial b/src/riscv/lib/tests/expected/dummy_volatile/state_hash_initial deleted file mode 100644 index 9abe2a9741e8c830ad0ff1fe1bad020eef1b3a42..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/dummy_volatile/state_hash_initial +++ /dev/null @@ -1 +0,0 @@ -e982d9104ac858caa5587b030d85b02e4e24ab1e30963be559a28d5b96158a74 diff --git a/src/riscv/lib/tests/expected/jstz/log b/src/riscv/lib/tests/expected/jstz/log deleted file mode 100644 index 2f2f5b52bbd67823a952bb4ef1de8e057b7cefb3..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/jstz/log +++ /dev/null @@ -1,158 +0,0 @@ -Internal message: start of level -Internal message: level info (block predecessor: BLkJzts53kqEzvDmomAEAmnPuiZjYeLpNZemPbnGJpndkJePE7L, predecessor_timestamp: 1970-01-01T00:00:00Z -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtiFeCFJWwMPy4koWc1wVwes2YvKqAmqK89iwWZutXcnyzUQvZ2jVh3cyJ8vaiyL3YwiruGmHhT4t66xHpLyyY2waXkVpTU"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(0), content: DeployFunction(DeployFunction { function_code: ParsedCode("function o(e){return typeof e==\"string\"}function a(e,r){return Array.isArray(r)&&r.reduce((n,t)=>n&&e(t),!0)}function i(e){return typeof e==\"number\"&&Number.isInteger(e)}function k(e){let r=e;try{return o(r.to)&&i(r.token_id)&&Number.isInteger(r.amount)}catch{return!1}}function w(e){let r=e;try{return o(r.from)&&a(k,r.transfers)}catch{return!1}}function A(e){let r=e;try{return(r.operation===\"add_operator\"||r.operation===\"remove_operator\")&&o(r.owner)&&o(r.operator)&&i(r.token_id)}catch{return!1}}function p(e){let r=e;try{return o(r.owner)&&i(r.token_id)}catch{return!1}}function _(e){let r=e;try{return a(p,r.requests)}catch{return!1}}function q(e){let r=e;try{return p(r.request)&&Number.isInteger(r.balance)}catch{return!1}}function g(e){let r=e;try{return i(r.token_id)&&o(r.owner)&&Number.isInteger(r.amount)}catch{return!1}}function d(e){return`token/${e}`}function R(e){let r=d(e);if(Kv.get(r))throw\"FA2_TOKEN_ID_EXISTS\";Kv.set(d(e),!0)}function O(e){if(!Kv.get(d(e)))throw\"FA2_TOKEN_UNDEFINED\"}function l(e,r){return`balance/${e}/${r}`}function m(e,r){return Kv.get(l(e,r))||0}function h(e,r,n){if(n<0)throw\"FA2_INSUFFICIENT_BALANCE\";Kv.set(l(e,r),n)}function u(e,r,n){let t=m(e,r);h(e,r,t+n)}function y(e,r,n,t){u(e,n,-t),u(r,n,t)}function f(e,r,n){return`owner/${e}/${r}/${n}`}function I(e,r,n){Kv.set(f(e,r,n),!0)}function b(e,r,n){Kv.delete(f(e,r,n))}function T(e,r,n){if(!(e===r||Kv.get(f(e,r,n))))throw\"FA2_NOT_OPERATOR\"}function B(e,r){if(e!==r)throw console.log(`${e} !== ${r}`),\"FA2_NOT_OWNER\"}function N(e,r,n){O(n.token_id),T(e,r,n.token_id),y(e,n.to,n.token_id,n.amount)}function x(e,r){r.forEach(n=>n.transfers.forEach(t=>N(n.from,e,t)))}function v(e,r){switch(r.operation){case\"add_operator\":B(r.owner,e),I(r.owner,r.operator,r.token_id);break;case\"remove_operator\":T(r.owner,e,r.token_id),b(r.owner,r.operator,r.token_id)}}function E(e){let r=m(e.owner,e.token_id);return console.log(`${e.owner} has ${r} of token ${e.token_id}`),{request:e,balance:r}}function S(e){return e.requests.map(E)}function K(e){R(e.token_id),u(e.owner,e.token_id,e.amount)}async function U(e){let r=new URL(e.url),n=r.pathname;try{switch(n){case\"/ping\":return console.log(\"Hello from runner smart function \\u{1F44B}\"),new Response(\"Pong\");case\"/balance_of\":if(e.method===\"GET\"){let s={requests:JSON.parse(atob(r.searchParams.get(\"requests\")))};if(_(s)){let c=S(s);return Response.json(c)}else return console.error(\"Invalid parameters\",s),Response.error()}else{let s=\"/balance_of is a GET request\";return console.error(s),new Response(s,{status:500})}case\"/transfer\":if(e.method===\"POST\"){let s=await e.json();return a(w,s)?(x(e.headers.get(\"Referer\"),s),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/transfer is a POST request\";return console.error(s),new Response(s,{status:500})}case\"/mint_new\":if(e.method===\"POST\"){let s=await e.json();return a(g,s)?(s.forEach(K),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/mint_new is a POST request\";return console.error(s),new Response(s,{status:500})}case\"/update_operators\":if(e.method===\"PUT\"){let s=await e.json();return a(A,s)?(s.forEach(c=>v(e.headers.get(\"Referer\"),c)),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/update_operators is a PUT request\";return console.error(s),new Response(s,{status:500})}default:let t=`Unrecognised entrypoint ${n}`;return console.error(t),new Response(t,{status:404})}}catch(t){throw console.error(t),t}}var F=U;export{F as default,_ as isBalanceOf,p as isBalanceRequest,q as isBalanceResponse,g as isMintNew,i as isTokenId,k as isTransfer,w as isTransfers,A as isUpdateOperator};\n"), account_credit: 0 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtiFeCFJWwMPy4koWc1wVwes2YvKqAmqK89iwWZutXcnyzUQvZ2jVh3cyJ8vaiyL3YwiruGmHhT4t66xHpLyyY2waXkVpTU"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(0), content: DeployFunction(DeployFunction { function_code: ParsedCode("function o(e){return typeof e==\"string\"}function a(e,r){return Array.isArray(r)&&r.reduce((n,t)=>n&&e(t),!0)}function i(e){return typeof e==\"number\"&&Number.isInteger(e)}function k(e){let r=e;try{return o(r.to)&&i(r.token_id)&&Number.isInteger(r.amount)}catch{return!1}}function w(e){let r=e;try{return o(r.from)&&a(k,r.transfers)}catch{return!1}}function A(e){let r=e;try{return(r.operation===\"add_operator\"||r.operation===\"remove_operator\")&&o(r.owner)&&o(r.operator)&&i(r.token_id)}catch{return!1}}function p(e){let r=e;try{return o(r.owner)&&i(r.token_id)}catch{return!1}}function _(e){let r=e;try{return a(p,r.requests)}catch{return!1}}function q(e){let r=e;try{return p(r.request)&&Number.isInteger(r.balance)}catch{return!1}}function g(e){let r=e;try{return i(r.token_id)&&o(r.owner)&&Number.isInteger(r.amount)}catch{return!1}}function d(e){return`token/${e}`}function R(e){let r=d(e);if(Kv.get(r))throw\"FA2_TOKEN_ID_EXISTS\";Kv.set(d(e),!0)}function O(e){if(!Kv.get(d(e)))throw\"FA2_TOKEN_UNDEFINED\"}function l(e,r){return`balance/${e}/${r}`}function m(e,r){return Kv.get(l(e,r))||0}function h(e,r,n){if(n<0)throw\"FA2_INSUFFICIENT_BALANCE\";Kv.set(l(e,r),n)}function u(e,r,n){let t=m(e,r);h(e,r,t+n)}function y(e,r,n,t){u(e,n,-t),u(r,n,t)}function f(e,r,n){return`owner/${e}/${r}/${n}`}function I(e,r,n){Kv.set(f(e,r,n),!0)}function b(e,r,n){Kv.delete(f(e,r,n))}function T(e,r,n){if(!(e===r||Kv.get(f(e,r,n))))throw\"FA2_NOT_OPERATOR\"}function B(e,r){if(e!==r)throw console.log(`${e} !== ${r}`),\"FA2_NOT_OWNER\"}function N(e,r,n){O(n.token_id),T(e,r,n.token_id),y(e,n.to,n.token_id,n.amount)}function x(e,r){r.forEach(n=>n.transfers.forEach(t=>N(n.from,e,t)))}function v(e,r){switch(r.operation){case\"add_operator\":B(r.owner,e),I(r.owner,r.operator,r.token_id);break;case\"remove_operator\":T(r.owner,e,r.token_id),b(r.owner,r.operator,r.token_id)}}function E(e){let r=m(e.owner,e.token_id);return console.log(`${e.owner} has ${r} of token ${e.token_id}`),{request:e,balance:r}}function S(e){return e.requests.map(E)}function K(e){R(e.token_id),u(e.owner,e.token_id,e.amount)}async function U(e){let r=new URL(e.url),n=r.pathname;try{switch(n){case\"/ping\":return console.log(\"Hello from runner smart function \\u{1F44B}\"),new Response(\"Pong\");case\"/balance_of\":if(e.method===\"GET\"){let s={requests:JSON.parse(atob(r.searchParams.get(\"requests\")))};if(_(s)){let c=S(s);return Response.json(c)}else return console.error(\"Invalid parameters\",s),Response.error()}else{let s=\"/balance_of is a GET request\";return console.error(s),new Response(s,{status:500})}case\"/transfer\":if(e.method===\"POST\"){let s=await e.json();return a(w,s)?(x(e.headers.get(\"Referer\"),s),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/transfer is a POST request\";return console.error(s),new Response(s,{status:500})}case\"/mint_new\":if(e.method===\"POST\"){let s=await e.json();return a(g,s)?(s.forEach(K),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/mint_new is a POST request\";return console.error(s),new Response(s,{status:500})}case\"/update_operators\":if(e.method===\"PUT\"){let s=await e.json();return a(A,s)?(s.forEach(c=>v(e.headers.get(\"Referer\"),c)),new Response(\"Success!\")):(console.error(\"Invalid parameters\",JSON.stringify(s)),Response.error())}else{let s=\"/update_operators is a PUT request\";return console.error(s),new Response(s,{status:500})}default:let t=`Unrecognised entrypoint ${n}`;return console.error(t),new Response(t,{status:404})}}catch(t){throw console.error(t),t}}var F=U;export{F as default,_ as isBalanceOf,p as isBalanceRequest,q as isBalanceResponse,g as isMintNew,i as isTokenId,k as isTransfer,w as isTransfers,A as isUpdateOperator};\n"), account_credit: 0 }) } } -[📜] Smart function deployed: KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib -Receipt: Receipt { hash: Blake2b([33, 70, 27, 168, 129, 21, 184, 237, 27, 213, 97, 217, 149, 7, 101, 172, 248, 89, 172, 128, 198, 35, 169, 30, 122, 202, 143, 59, 243, 112, 155, 245]), result: Success(DeployFunction(DeployFunctionReceipt { address: SmartFunctionHash(ContractKt1Hash("KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib")) })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtqqDrqRr2t9eyMVCfbdB9hxFma4nTnBQKQoyTjT2RrfGPV7uxdJ3u1MXLDZ9UuCw59KwiQG8TGeSzbKttDGjRpS1kZWVoe"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/mint_new, method: POST, headers: {}, body: Some([91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtqqDrqRr2t9eyMVCfbdB9hxFma4nTnBQKQoyTjT2RrfGPV7uxdJ3u1MXLDZ9UuCw59KwiQG8TGeSzbKttDGjRpS1kZWVoe"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/mint_new, method: POST, headers: {}, body: Some([91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 54, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"0dc4220b83f999c38df618ed6f87f3e8cf51915bbba7bfb5848452834baca82b"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"0dc4220b83f999c38df618ed6f87f3e8cf51915bbba7bfb5848452834baca82b"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD8C7F8)) (in 1357 instructions) -Receipt: Receipt { hash: Blake2b([13, 196, 34, 11, 131, 249, 153, 195, 141, 246, 24, 237, 111, 135, 243, 232, 207, 81, 145, 91, 187, 167, 191, 181, 132, 132, 82, 131, 75, 172, 168, 43]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigttPDCTmpwM8kRjPFbRMBrBu7wWhYygcH8CoEnbGGrKTRqSKkbSyYLS9u3A43Emiwk2u331k7Px9o4wgYkm9j479qyPdRRF"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigttPDCTmpwM8kRjPFbRMBrBu7wWhYygcH8CoEnbGGrKTRqSKkbSyYLS9u3A43Emiwk2u331k7Px9o4wgYkm9j479qyPdRRF"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bc152a5102ba0db996fd06417f4d5174cc2fe6acf683fa06e80fae9d5fb20917"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bc152a5102ba0db996fd06417f4d5174cc2fe6acf683fa06e80fae9d5fb20917"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD5A4D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([188, 21, 42, 81, 2, 186, 13, 185, 150, 253, 6, 65, 127, 77, 81, 116, 204, 47, 230, 172, 246, 131, 250, 6, 232, 15, 174, 157, 95, 178, 9, 23]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtowrhvLc7sShtfNFHW7Jviq3iz9p8MoTs1TiNwMZ8hk6wFgPbV9rtN2BrVtG3FY6VbYtRRGZAFXhjxYVZwUhGG8mZj9iPK"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtowrhvLc7sShtfNFHW7Jviq3iz9p8MoTs1TiNwMZ8hk6wFgPbV9rtN2BrVtG3FY6VbYtRRGZAFXhjxYVZwUhGG8mZj9iPK"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"cf78809d33a2782d9a8d1659393c5f9cc33cb05198d306d5a56fe8a199a99931"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"cf78809d33a2782d9a8d1659393c5f9cc33cb05198d306d5a56fe8a199a99931"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD3E4E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([207, 120, 128, 157, 51, 162, 120, 45, 154, 141, 22, 89, 57, 60, 95, 156, 195, 60, 176, 81, 152, 211, 6, 213, 165, 111, 232, 161, 153, 169, 153, 49]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtyMmGsMBRx7T8BmDRgbYYnzuWkRpapJ6HjTybUbArGu95uKufZA3Buq7ATxRwNp3e4rePcU3SZ62UTtHAMWh5699knkjTB"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtyMmGsMBRx7T8BmDRgbYYnzuWkRpapJ6HjTybUbArGu95uKufZA3Buq7ATxRwNp3e4rePcU3SZ62UTtHAMWh5699knkjTB"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"109de28dd6774a7dbc2ed40f418fb8c6b4f086c1b610c67e68d5ef779cda4cbf"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"109de28dd6774a7dbc2ed40f418fb8c6b4f086c1b610c67e68d5ef779cda4cbf"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD214F8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([16, 157, 226, 141, 214, 119, 74, 125, 188, 46, 212, 15, 65, 143, 184, 198, 180, 240, 134, 193, 182, 16, 198, 126, 104, 213, 239, 119, 156, 218, 76, 191]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu3ZnjyctZajBUSmuHNrnmQi1baoWfoJnLxbvyXAAdA9e4AVdCTiVtkqpH4mmKqDcneByyWMtkchAfYkNJszVojomumiziU"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu3ZnjyctZajBUSmuHNrnmQi1baoWfoJnLxbvyXAAdA9e4AVdCTiVtkqpH4mmKqDcneByyWMtkchAfYkNJszVojomumiziU"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"80bfb5a5179a1c6ec3522ee50ecc2b2919524205f53a970335a12ec13fb8442c"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"80bfb5a5179a1c6ec3522ee50ecc2b2919524205f53a970335a12ec13fb8442c"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD214E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([128, 191, 181, 165, 23, 154, 28, 110, 195, 82, 46, 229, 14, 204, 43, 41, 25, 82, 66, 5, 245, 58, 151, 3, 53, 161, 46, 193, 63, 184, 68, 44]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtzBtrQhgjBMue6eJYHgcN2yohkEq8nua2b7esNGRuedhdTqQq35sUdkGu4JEsYRKmuNGPgo5swFMPWoRtSU3A3mSEjzUee"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtzBtrQhgjBMue6eJYHgcN2yohkEq8nua2b7esNGRuedhdTqQq35sUdkGu4JEsYRKmuNGPgo5swFMPWoRtSU3A3mSEjzUee"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"74c6fb939edfa7bcadafc4ce36fa082d5e739add2c2cef454540de42aca56922"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"74c6fb939edfa7bcadafc4ce36fa082d5e739add2c2cef454540de42aca56922"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD204D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([116, 198, 251, 147, 158, 223, 167, 188, 173, 175, 196, 206, 54, 250, 8, 45, 94, 115, 154, 221, 44, 44, 239, 69, 69, 64, 222, 66, 172, 165, 105, 34]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtd2D4p3Z1BQxHKCjr2yhGrjRygzUgB2bgDBCBqcZ5xcZ4U8dZwVVKuERFmNWpXjABA5MCtXN3EkpgXzmx645FBf8hvdB53"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtd2D4p3Z1BQxHKCjr2yhGrjRygzUgB2bgDBCBqcZ5xcZ4U8dZwVVKuERFmNWpXjABA5MCtXN3EkpgXzmx645FBf8hvdB53"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"263cd0282355b48093a2f3e4f607105e3160cf915632a2c0de3152c923cc609e"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"263cd0282355b48093a2f3e4f607105e3160cf915632a2c0de3152c923cc609e"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD214D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([38, 60, 208, 40, 35, 85, 180, 128, 147, 162, 243, 228, 246, 7, 16, 94, 49, 96, 207, 145, 86, 50, 162, 192, 222, 49, 82, 201, 35, 204, 96, 158]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtcR63Xg8X6R1tbVQBgjD2ganZQt1Ug3n73USaZTUCQn5hvaKGsLfGoqJdaESEtPsMzJrGKd9VMwR8DuyhDQFReAaTqcrzh"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtcR63Xg8X6R1tbVQBgjD2ganZQt1Ug3n73USaZTUCQn5hvaKGsLfGoqJdaESEtPsMzJrGKd9VMwR8DuyhDQFReAaTqcrzh"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"87c920e097612d4a0573e7751ec3c5e9526c16ee3704a9a4683a08e3887ed3bc"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"87c920e097612d4a0573e7751ec3c5e9526c16ee3704a9a4683a08e3887ed3bc"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD204E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([135, 201, 32, 224, 151, 97, 45, 74, 5, 115, 231, 117, 30, 195, 197, 233, 82, 108, 22, 238, 55, 4, 169, 164, 104, 58, 8, 227, 136, 126, 211, 188]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtcZMFoL5VpwaGctWnaS6fnnEMJczmwevuMNrxVqSdJMcdUe5MozD6EWb2mzy9M5YYhgYCN8AwwtS5NnBDw8wiqR3743Hqo"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtcZMFoL5VpwaGctWnaS6fnnEMJczmwevuMNrxVqSdJMcdUe5MozD6EWb2mzy9M5YYhgYCN8AwwtS5NnBDw8wiqR3743Hqo"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(0), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"a48a708a37a1a477cf195b82874e684ff287301ff6a99979c363a9aa5e15cc20"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"a48a708a37a1a477cf195b82874e684ff287301ff6a99979c363a9aa5e15cc20"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1E4E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([164, 138, 112, 138, 55, 161, 164, 119, 207, 25, 91, 130, 135, 78, 104, 79, 242, 135, 48, 31, 246, 169, 153, 121, 195, 99, 169, 170, 94, 21, 204, 32]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigteUJD9ByMsRCdkC9GErHgVKm9x3s351DdXzQH3oEBmvrgaZtoj335XnSF8LPqsAFkii44ToC2Z9iE5uto4ZvhTQzWQwagn"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigteUJD9ByMsRCdkC9GErHgVKm9x3s351DdXzQH3oEBmvrgaZtoj335XnSF8LPqsAFkii44ToC2Z9iE5uto4ZvhTQzWQwagn"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"5b183bb52c27d925d67fbe7fd11215e59d00719687e0bbc030b7b1bd9e4a9ccc"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"5b183bb52c27d925d67fbe7fd11215e59d00719687e0bbc030b7b1bd9e4a9ccc"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1D4E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([91, 24, 59, 181, 44, 39, 217, 37, 214, 127, 190, 127, 209, 18, 21, 229, 157, 0, 113, 150, 135, 224, 187, 192, 48, 183, 177, 189, 158, 74, 156, 204]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtyixenMcK9Cm2KQQCWib8wFXusUBSWbNyo1PTKLW2hXY58T32w98qsTNNigifkVFrQTYoAqqBZgHRhu7FpxHnLXmSoWG2L"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtyixenMcK9Cm2KQQCWib8wFXusUBSWbNyo1PTKLW2hXY58T32w98qsTNNigifkVFrQTYoAqqBZgHRhu7FpxHnLXmSoWG2L"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"d6270c1b04a181f1efc0e3ea98f5b48f2b3f220f5e3b2f9c89dc2bce29802754"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"d6270c1b04a181f1efc0e3ea98f5b48f2b3f220f5e3b2f9c89dc2bce29802754"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1D4D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([214, 39, 12, 27, 4, 161, 129, 241, 239, 192, 227, 234, 152, 245, 180, 143, 43, 63, 34, 15, 94, 59, 47, 156, 137, 220, 43, 206, 41, 128, 39, 84]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu2PW9gsanzZR7YDXmwSctmt2uomofzZk8w5L7F4PPGj5FuvKruH3MNjWRAZAonwQZR567ih2CrJUMZa5CmeRMoNpRvuemK"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu2PW9gsanzZR7YDXmwSctmt2uomofzZk8w5L7F4PPGj5FuvKruH3MNjWRAZAonwQZR567ih2CrJUMZa5CmeRMoNpRvuemK"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"c5f6bc13dc40ff3b0cdafcc63b31b5dcc5479d9f7eed91461bed71fb4fb51042"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"c5f6bc13dc40ff3b0cdafcc63b31b5dcc5479d9f7eed91461bed71fb4fb51042"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1E4D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([197, 246, 188, 19, 220, 64, 255, 59, 12, 218, 252, 198, 59, 49, 181, 220, 197, 71, 157, 159, 126, 237, 145, 70, 27, 237, 113, 251, 79, 181, 16, 66]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu55mKfRhbmxa1hfUEKwehGnu4tAFoDuxXjkcpTrAcLZYL4SrircgjdvqTrGWDpHDPLKVrDVp7ZuPH9W9frSBk1cws4Y3yQ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu55mKfRhbmxa1hfUEKwehGnu4tAFoDuxXjkcpTrAcLZYL4SrircgjdvqTrGWDpHDPLKVrDVp7ZuPH9W9frSBk1cws4Y3yQ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"fe6a3352150f0b091268508513098a5d3fdb18533a875703ef6ab46ae0bbe73a"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"fe6a3352150f0b091268508513098a5d3fdb18533a875703ef6ab46ae0bbe73a"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1D4D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([254, 106, 51, 82, 21, 15, 11, 9, 18, 104, 80, 133, 19, 9, 138, 93, 63, 219, 24, 83, 58, 135, 87, 3, 239, 106, 180, 106, 224, 187, 231, 58]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtqQKMjU28tddKRKyqvs59RaXRFPphTDdAviA8JjcX872JZXfrMbLPF52x4xLFREsdVnkKg5LnycJDPBdySqfY7NsDBvnE5"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtqQKMjU28tddKRKyqvs59RaXRFPphTDdAviA8JjcX872JZXfrMbLPF52x4xLFREsdVnkKg5LnycJDPBdySqfY7NsDBvnE5"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 52, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8abf5b21979dfc12f26ffff7157c76ceda2f6e3f7d8795b0d7b9692400557a48"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8abf5b21979dfc12f26ffff7157c76ceda2f6e3f7d8795b0d7b9692400557a48"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1C4D8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([138, 191, 91, 33, 151, 157, 252, 18, 242, 111, 255, 247, 21, 124, 118, 206, 218, 47, 110, 63, 125, 135, 149, 176, 215, 185, 105, 36, 0, 85, 122, 72]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtv99TkbfsX2m1wBm3dWd52tNyV1GrYce7HcM3sgPxQiAv4iToabMczKyG92ycTvq24wNACRKEVNBKQxrKtTi6Mqtvjkrng"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtv99TkbfsX2m1wBm3dWd52tNyV1GrYce7HcM3sgPxQiAv4iToabMczKyG92ycTvq24wNACRKEVNBKQxrKtTi6Mqtvjkrng"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ad08cd9c8ce362dc17fae763c640fb60cc8402d164100152367fcce9c36ba0ee"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ad08cd9c8ce362dc17fae763c640fb60cc8402d164100152367fcce9c36ba0ee"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1C4E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([173, 8, 205, 156, 140, 227, 98, 220, 23, 250, 231, 99, 198, 64, 251, 96, 204, 132, 2, 209, 100, 16, 1, 82, 54, 127, 204, 233, 195, 107, 160, 238]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu5SALg8YtV1AiqNpJsf4CdwgiyED5EVWAgjnP8S8u6QJpQDHjHQhd3W3pXoD7keHdjEdQyvfsSrRhirZY2ktLXy8xt6V3s"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(4), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigu5SALg8YtV1AiqNpJsf4CdwgiyED5EVWAgjnP8S8u6QJpQDHjHQhd3W3pXoD7keHdjEdQyvfsSrRhirZY2ktLXy8xt6V3s"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(4), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 50, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"c5f9802fa19680971cb0cf933ea4eba43cdf18085de7f74a16e347e5b828eae4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"c5f9802fa19680971cb0cf933ea4eba43cdf18085de7f74a16e347e5b828eae4"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1A4E8)) (in 595 instructions) -Receipt: Receipt { hash: Blake2b([197, 249, 128, 47, 161, 150, 128, 151, 28, 176, 207, 147, 62, 164, 235, 164, 60, 223, 24, 8, 93, 231, 247, 74, 22, 227, 71, 229, 184, 40, 234, 228]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtfhYHpKKk7y2CN8pBKzuPgF6RAb44wagjKAgobnZMmrnR7DRTSm3PvXQTZ1wry3nd6bC7JnqYSvnibtyMDWgJw5WAif7RR"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(5), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxZUFrU1d5ZHgyRkVCM0RSY2llc2c4UmNLZFlveEFyOTV4In0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFlQWtTV3lkeDJGRUIzRFJjaWVzZzhSY0tkWW94QXI5NXgifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWVBa1NXeWR4MkZFQjNEUmNpZXNnOFJjS2RZb3hBcjk1eCJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxZUFrU1d5ZHgyRkVCM0RSY2llc2c4UmNLZFlveEFyOTV4In0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFlQWtTV3lkeDJGRUIzRFJjaWVzZzhSY0tkWW94QXI5NXgifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkvSu9MfpfuwUzdCcKrsyd2RSdLmS9EbdVYyqoMiuDhL872yWG12"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtfhYHpKKk7y2CN8pBKzuPgF6RAb44wagjKAgobnZMmrnR7DRTSm3PvXQTZ1wry3nd6bC7JnqYSvnibtyMDWgJw5WAif7RR"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x"))), nonce: Nonce(5), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxZUFrU1d5ZHgyRkVCM0RSY2llc2c4UmNLZFlveEFyOTV4In0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFlQWtTV3lkeDJGRUIzRFJjaWVzZzhSY0tkWW94QXI5NXgifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWVBa1NXeWR4MkZFQjNEUmNpZXNnOFJjS2RZb3hBcjk1eCJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxZUFrU1d5ZHgyRkVCM0RSY2llc2c4UmNLZFlveEFyOTV4In0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFlQWtTV3lkeDJGRUIzRFJjaWVzZzhSY0tkWW94QXI5NXgifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1","level":"LOG","text":"tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x has 2 of token 0"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1","level":"LOG","text":"tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x has 1 of token 1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1","level":"LOG","text":"tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x has 1 of token 2"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1","level":"LOG","text":"tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x has 1 of token 3"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1","level":"LOG","text":"tz1eAkSWydx2FEB3DRciesg8RcKdYoxAr95x has 0 of token 4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"552fad22a8d7375307128a8c20ca664ca328eb2913d6b5777cff5e4c3c64fcf1"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD19CA8)) (in 882 instructions) -Receipt: Receipt { hash: Blake2b([85, 47, 173, 34, 168, 215, 55, 83, 7, 18, 138, 140, 32, 202, 102, 76, 163, 40, 235, 41, 19, 214, 181, 119, 124, 255, 94, 76, 60, 100, 252, 241]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtgyPaZBZdNFiJisbC3spY1rboAWFiBz25icnf982ou1zg51dBXHx4FvSFC52kqmm86ivu1bs9jz3GhsEbRbaLPsnUr4e7z"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxaFB5aDZ4a2FqYUxiVEtqSHZLczgza2VpTGhhMkxGOHhRIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFoUHloNnhrYWphTGJUS2pIdktzODNrZWlMaGEyTEY4eFEifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWhQeWg2eGthamFMYlRLakh2S3M4M2tlaUxoYTJMRjh4USJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxaFB5aDZ4a2FqYUxiVEtqSHZLczgza2VpTGhhMkxGOHhRIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFoUHloNnhrYWphTGJUS2pIdktzODNrZWlMaGEyTEY4eFEifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkuM5nNSxca5UbGmvgiMQZugUrWtyguYmNraQBp3KJZPoT4xhvSn"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtgyPaZBZdNFiJisbC3spY1rboAWFiBz25icnf982ou1zg51dBXHx4FvSFC52kqmm86ivu1bs9jz3GhsEbRbaLPsnUr4e7z"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxaFB5aDZ4a2FqYUxiVEtqSHZLczgza2VpTGhhMkxGOHhRIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFoUHloNnhrYWphTGJUS2pIdktzODNrZWlMaGEyTEY4eFEifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWhQeWg2eGthamFMYlRLakh2S3M4M2tlaUxoYTJMRjh4USJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxaFB5aDZ4a2FqYUxiVEtqSHZLczgza2VpTGhhMkxGOHhRIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFoUHloNnhrYWphTGJUS2pIdktzODNrZWlMaGEyTEY4eFEifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f","level":"LOG","text":"tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ has 1 of token 0"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f","level":"LOG","text":"tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ has 2 of token 1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f","level":"LOG","text":"tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ has 1 of token 2"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f","level":"LOG","text":"tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ has 2 of token 3"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f","level":"LOG","text":"tz1hPyh6xkajaLbTKjHvKs83keiLha2LF8xQ has 0 of token 4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"8a43658bdc7f83c0cb9aba1ce0ae37c9c6be64b11f504cfcc36595c5d7de347f"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD17CA8)) (in 882 instructions) -Receipt: Receipt { hash: Blake2b([138, 67, 101, 139, 220, 127, 131, 192, 203, 154, 186, 28, 224, 174, 55, 201, 198, 190, 100, 177, 31, 80, 76, 252, 195, 101, 149, 197, 215, 222, 52, 127]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigthNHN52ivrP7bRaye7PaQBXhD1mzg6Dgf22ndEw7UYeyCj25HPWsJRoYT8ZTzoeMQnvrzUb3uAbH1aeRYcGtzeCSUM8nyF"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxUm5hb0FvZGV0eFNNR3VSdUJRUnVxQnA5TnNuZHhnNDJEIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFSbmFvQW9kZXR4U01HdVJ1QlFSdXFCcDlOc25keGc0MkQifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MVJuYW9Bb2RldHhTTUd1UnVCUVJ1cUJwOU5zbmR4ZzQyRCJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxUm5hb0FvZGV0eFNNR3VSdUJRUnVxQnA5TnNuZHhnNDJEIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFSbmFvQW9kZXR4U01HdVJ1QlFSdXFCcDlOc25keGc0MkQifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkutk3LEV8RKTCFHEpjzs2EYt5m6stcVMUgrA9ccYA3w6jP8nMKw"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigthNHN52ivrP7bRaye7PaQBXhD1mzg6Dgf22ndEw7UYeyCj25HPWsJRoYT8ZTzoeMQnvrzUb3uAbH1aeRYcGtzeCSUM8nyF"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxUm5hb0FvZGV0eFNNR3VSdUJRUnVxQnA5TnNuZHhnNDJEIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFSbmFvQW9kZXR4U01HdVJ1QlFSdXFCcDlOc25keGc0MkQifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MVJuYW9Bb2RldHhTTUd1UnVCUVJ1cUJwOU5zbmR4ZzQyRCJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxUm5hb0FvZGV0eFNNR3VSdUJRUnVxQnA5TnNuZHhnNDJEIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFSbmFvQW9kZXR4U01HdVJ1QlFSdXFCcDlOc25keGc0MkQifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467","level":"LOG","text":"tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D has 1 of token 0"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467","level":"LOG","text":"tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D has 1 of token 1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467","level":"LOG","text":"tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D has 2 of token 2"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467","level":"LOG","text":"tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D has 0 of token 3"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467","level":"LOG","text":"tz1RnaoAodetxSMGuRuBQRuqBp9Nsndxg42D has 0 of token 4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"ba15d4ea94f4bfb623741f6f5f3dd0be811dc2b4289137b40adc34933fbd1467"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD16CA8)) (in 883 instructions) -Receipt: Receipt { hash: Blake2b([186, 21, 212, 234, 148, 244, 191, 182, 35, 116, 31, 111, 95, 61, 208, 190, 129, 29, 194, 180, 40, 145, 55, 180, 10, 220, 52, 147, 63, 189, 20, 103]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 82, 110, 97, 111, 65, 111, 100, 101, 116, 120, 83, 77, 71, 117, 82, 117, 66, 81, 82, 117, 113, 66, 112, 57, 78, 115, 110, 100, 120, 103, 52, 50, 68, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtrsyuZ8ACfsafUuwVWXLqu2rLncq5yvxwrZ8rsKkXDB2njetjaZ6uKaRaY5DW3hvCGvtw8nKFkSxLX2LfvkZYDuHrPfdEZ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(4), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxYzR2cGR2SHJzTkRCQlBzamFLRWR6Q0pkcUFkS29qUEpZIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFjNHZwZHZIcnNOREJCUHNqYUtFZHpDSmRxQWRLb2pQSlkifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWM0dnBkdkhyc05EQkJQc2phS0VkekNKZHFBZEtvalBKWSJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxYzR2cGR2SHJzTkRCQlBzamFLRWR6Q0pkcUFkS29qUEpZIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFjNHZwZHZIcnNOREJCUHNqYUtFZHpDSmRxQWRLb2pQSlkifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpkv9xKk71uUQJxGTjF2ioTmxHkFXRbtB8uVPU3cbcBZ31JDQbtMh"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtrsyuZ8ACfsafUuwVWXLqu2rLncq5yvxwrZ8rsKkXDB2njetjaZ6uKaRaY5DW3hvCGvtw8nKFkSxLX2LfvkZYDuHrPfdEZ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY"))), nonce: Nonce(4), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxYzR2cGR2SHJzTkRCQlBzamFLRWR6Q0pkcUFkS29qUEpZIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFjNHZwZHZIcnNOREJCUHNqYUtFZHpDSmRxQWRLb2pQSlkifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWM0dnBkdkhyc05EQkJQc2phS0VkekNKZHFBZEtvalBKWSJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxYzR2cGR2SHJzTkRCQlBzamFLRWR6Q0pkcUFkS29qUEpZIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFjNHZwZHZIcnNOREJCUHNqYUtFZHpDSmRxQWRLb2pQSlkifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f","level":"LOG","text":"tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY has 1 of token 0"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f","level":"LOG","text":"tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY has 1 of token 1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f","level":"LOG","text":"tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY has 1 of token 2"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f","level":"LOG","text":"tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY has 2 of token 3"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f","level":"LOG","text":"tz1c4vpdvHrsNDBBPsjaKEdzCJdqAdKojPJY has 0 of token 4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"bd943b7902edcab976f3c49ac3103d5df049ed6b28b77e68ce329eec376a075f"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD15CA8)) (in 882 instructions) -Receipt: Receipt { hash: Blake2b([189, 148, 59, 121, 2, 237, 202, 185, 118, 243, 196, 154, 195, 16, 61, 93, 240, 73, 237, 107, 40, 183, 126, 104, 206, 50, 158, 236, 55, 106, 7, 95]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 99, 52, 118, 112, 100, 118, 72, 114, 115, 78, 68, 66, 66, 80, 115, 106, 97, 75, 69, 100, 122, 67, 74, 100, 113, 65, 100, 75, 111, 106, 80, 74, 89, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } -External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtajDQBUs2o4w1fSxSfGqKZTF7dKuYt6JBVPYEg1BNkFT455Ba6tqfsQ5FAYMFWL2NikYQFDSe17k3YKqPikpYY2ZtthFtZ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxaUJjVVo1OWdTUFdYb2ZUOEIxaGdVeFVIU0tyQ3FRaWlWIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFpQmNVWjU5Z1NQV1hvZlQ4QjFoZ1V4VUhTS3JDcVFpaVYifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWlCY1VaNTlnU1BXWG9mVDhCMWhnVXhVSFNLckNxUWlpViJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxaUJjVVo1OWdTUFdYb2ZUOEIxaGdVeFVIU0tyQ3FRaWlWIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFpQmNVWjU5Z1NQV1hvZlQ4QjFoZ1V4VUhTS3JDcVFpaVYifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("edpktmjxdfuC1aXAcw8zn1QTFPdDmaJKdRPvxtyWZBMadiFrAap6Re"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtajDQBUs2o4w1fSxSfGqKZTF7dKuYt6JBVPYEg1BNkFT455Ba6tqfsQ5FAYMFWL2NikYQFDSe17k3YKqPikpYY2ZtthFtZ"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV"))), nonce: Nonce(3), content: RunFunction(RunFunction { uri: tezos://KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib/balance_of?requests=W3sidG9rZW5faWQiOjAsIm93bmVyIjoidHoxaUJjVVo1OWdTUFdYb2ZUOEIxaGdVeFVIU0tyQ3FRaWlWIn0seyJ0b2tlbl9pZCI6MSwib3duZXIiOiJ0ejFpQmNVWjU5Z1NQV1hvZlQ4QjFoZ1V4VUhTS3JDcVFpaVYifSx7InRva2VuX2lkIjoyLCJvd25lciI6InR6MWlCY1VaNTlnU1BXWG9mVDhCMWhnVXhVSFNLckNxUWlpViJ9LHsidG9rZW5faWQiOjMsIm93bmVyIjoidHoxaUJjVVo1OWdTUFdYb2ZUOEIxaGdVeFVIU0tyQ3FRaWlWIn0seyJ0b2tlbl9pZCI6NCwib3duZXIiOiJ0ejFpQmNVWjU5Z1NQV1hvZlQ4QjFoZ1V4VUhTS3JDcVFpaVYifV0=, method: GET, headers: {}, body: None, gas_limit: 100000 }) } } -[JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff","level":"LOG","text":"tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV has 1 of token 0"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff","level":"LOG","text":"tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV has 1 of token 1"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff","level":"LOG","text":"tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV has 1 of token 2"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff","level":"LOG","text":"tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV has 1 of token 3"} -[JSTZ:SMART_FUNCTION:LOG] {"address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff","level":"LOG","text":"tz1iBcUZ59gSPWXofT8B1hgUxUHSKrCqQiiV has 6 of token 4"} -[JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"KT19D3pSMDc2Cc5uacktqfHHUpHHveSqTwib","request_id":"48c883ca2a31e37e1b0f8bc698e2d8a1122759a9ebcd0d40392f0480b40f50ff"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD14CA8)) (in 881 instructions) -Receipt: Receipt { hash: Blake2b([72, 200, 131, 202, 42, 49, 227, 126, 27, 15, 139, 198, 152, 226, 216, 161, 18, 39, 89, 169, 235, 205, 13, 64, 57, 47, 4, 128, 180, 15, 80, 255]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 50, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 51, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 54, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 105, 66, 99, 85, 90, 53, 57, 103, 83, 80, 87, 88, 111, 102, 84, 56, 66, 49, 104, 103, 85, 120, 85, 72, 83, 75, 114, 67, 113, 81, 105, 105, 86, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 52, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } -Internal message: end of level diff --git a/src/riscv/lib/tests/expected/jstz/proof_initial b/src/riscv/lib/tests/expected/jstz/proof_initial index 3a2f47f7000469b6c91ebe15d0c874e5bcc1fd62..91b393dbd2acaefdf0813570a43c1d2549d341c1 100644 --- a/src/riscv/lib/tests/expected/jstz/proof_initial +++ b/src/riscv/lib/tests/expected/jstz/proof_initial @@ -1 +1 @@ -5229c2cc2c221289d8b5ff90ea3ee425dde9928bee100443c2ae0cd1379ad04f00eb8002a28baaaaaaaa800a8a2eaaaaaaa8fcb8a8828a2a3f3aaaaaaaaa88eaaaaaabab0000000000000000d8fdff0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b69702a889248a4d6620475a105dccd5e0d4230aca8a492aaf6510e55d55b0265f2268196bd53063902e3bd59019dc182e308117688aa8e901e1c6afa51fa70569a030000000000e2d93df6a2e919e879551686bc301480fc50c54dc949b14b916d5834113bb0613e8adfb8922e389026900d8f88c206d542742576db4ce817b835d751f034cf8332239dd891f22c29012ed11f0cf9110110d4fe21c214490a69809303ff4230b62f9b425f38132e4f56907f5add90aa69bcd19d2e37886b3b744e00db93ab833520ed526542a97f034aed7f1af2f541039532fa3487483695a2837a8fa0fb0c67910407d79b43f915899b7236990cc4699a07e1f03dd1d63c4465b3458a96b29c02eb1170f67971d9fcd30a9afa14664cbe828cec832f9a7652874d36dc116e3d2388b7050040af2605106398c6022f27b51875fb9305450005462fa0c502aa841305200613061008854697406b00e7802085aa852685e35db0f8e2604264a2640561828017c593001305a56e97d5bf00138645339305400297000000e7800045397106fc22f826f4800019c90c6110652330050013b515007d15b374c50011a0814417f5bf001305654197606b00e780001c10610c652334c4fc2338b4fc854614e104e51dc6233cc4fc2330b4fe6312d604a1c17d552fb5a5028545631bb502130504fe0f00300297706700e78020460da017f5bf001305653c97606b00e780001797f56800938505ef97f06800e780c0f2e2704274a27421618280aa84130584fc97f06600e780800a268597406a00e780609d411106e422e0000897a06900e780802e7d567e1605066316c500a26002644101828017d6bf001306267a97000000e780e00c011106ec22e800103a88b6872330b4fe2334c4fe97d5bf0013866579930504fe930684fe328797000000e780e04e5d7186e4a2e026fc4af880082e966367b6062a89086193141500637a9604a14563fb950463cc040401c9833589002334b4fc233ca4fc05452338a4fc130504fb8545930684fc268697a06b00e78040b6033504fb0ded033584fb2334a90023309900a6600664e274427961618280b284a145e3e9c5faa144e3d804fa014517e6bf001306663797000000e7802001033584fb833504fce5b7411106e422e0000811e5328597a06b00e78020a397000000e7808000411106e422e000082a862e85b28597400300e7800036597186f4a2f08018930604f0c8ea17f5bf000335453497c26b00e782620217e5bf001305c54597c26b00e782a2ff97000000e780401c597186f4a2f08018930604f0c8ea17f5bf000335e53097c26b00e78202ff17e5bf001305654597c26b00e78242fc97000000e780e018597186f4a2f08018930604f0c8ea17f5bf000335852d97c26b00e782a2fb17e5bf001305054597c26b00e782e2f897000000e7808015597186f4a2f08018930604f0c8ea17f5bf000335252a97c26b00e78242f817e5bf001305a54497c26b00e78282f597000000e7802012411106e422e0000897406c00e78080561d7186eca2e880109705c00003b6a5a1aa851305f4fe233ca4fc2330c4fe17f5bf001305c5932334a4fa05452338a4fa233404fc130684fd233cc4fa2330a4fc130584fa97000000e780600c1d7186eca2e880109705c00003b665beaa851305f4fe233ca4fc2330c4fe17f5bf00130505902334a4fa05452338a4fa233404fc130684fd233cc4fa2330a4fc130584fa97000000e780a007411106e422e000082a8617559400130525fe9305b00297000000e780400d1d7186eca2e880102330a4fa2334b4fa130504fa2330a4fe17556c001305254e2334a4fe17956c00130585982338a4fa0545233ca4fa233804fc930504fe2330b4fc2334a4fc130504fbb28597000000e7808000797106f422f00018233ca4fc2330b4fe05452314a4fe130584fd97b06800e780601e1d7186eca2e88010147518712338d4fc2334e4fc146d18691c6508612330d4fc233ce4fa2338f4fa2334a4fa130584fa233ca4fc2330c4fe230404fea304b4fe130584fd97b06800e780a019000097000000e78060115d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc130504fbb28597000000e78060f55d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc17f5bf0013062580130504fb814597000000e78060f35d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc17e5bf001306857d130504fb854597000000e78040ef597186f4a2f08018930604f0c8ea1705c0000335c593ccee930584f9eceae8ee930504f9ecf2e8f617e5bf001305257bc8f20945c8f6233004fc930504fdccfac8fe130504fab28597000000e78020e8411106e422e00008175594001305e5e39305600297000000e78080f1411106e422e0000817559400130585e49305400297000000e780e0f3011106ec22e80010b2872330a4fe2334b4fe17e5bf001306057617e5bf0013088518930504fe930684fe0145328797000000e780e003011106ec22e80010b2872330a4fe2334b4fe17e5bf001306a57417f5bf001308a58e930504fe930684fe0145328797000000e7808000517186f5a2f1a6edcae98019c2842338b4f0930504f090ed94f198f509c98545631fb500175594001305a5dc29a0175594001305e5db2338a4f2094501a817559400130525db2338a4f21d458c63233ca4f295ed130504f3130604f028fa17556c001305e52128fe130504f148e217556c001305c50748e6930504f24cea48ee17e5bf001305656c48fa0d45b9a8130504f413060003130904f4be8597506b00e780400d130504f3130604f028fa17556c009705c00083b5c5811305e51c28fe233024f94ce6130504f148ea17556c001305e50148ee930504f24cf248f617e5bf001305856948fa1145130604f048fe233804fc930504f76ce268e6130504fba68597000000e78040cd197186fca2f800012330a4f82334b4f82338c4f8233cd4f8130504f82338a4fc17556c0013052515233ca4fc130504f92330a4fe17556c001305c5fa2334a4fe17e5bf001305c5682330a4fa09452334a4fa233004fc930504fd2338b4fa233ca4fa130504faba8597000000e78040c6397106fc22f8800017e5bf001305457e2330a4fc05452334a4fc233004fe21452338a4fc233c04fc17e5bf009305457d130504fc97000000e78080c2411106e422e0000897506c00e780c00d411106e422e0000897506c00e780c012411106e422e0000897506c00e780c017397106fc22f88000aa8517e5bf001305e5792330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78020bc597186f4a2f080182338a4f817f5bf000335e562233cb4f8930584f92338b4fc233ca4fc930504f92330b4fe2334a4fe17e5bf00130525752330a4fa0d452334a4fa233004fc130504fd2338a4fa0945233ca4fa130504fab28597000000e78000b6397106fc22f88000aa8517e5bf00130565772330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e780a0b2411106e422e0000897306c00e78060cb397106fc22f88000aa8517e5bf00130585312330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78040ae397106fc22f88000aa8517f5bf001305a5992330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e780e0aa397106fc22f88000aa8517f5bf00130545972330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78080a7397106fc22f88000aa8517f5bf001305e5942330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78020a4397106fc22f88000aa8517f5bf00130585922330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e780c0a001112ae406ec97f06a00e780404c97f06a00e780e04b17f3bf00033363140293226597706b00e78020359701c000938121000a8593050000137101ff09a00c4113068500814717f7bf000337e7e597f6bf0083b6e60817f5bf000335e5c617f36a00670003309717c0009387678203c7070029eb05472380e70097f7bf0083b7a7cd85c7411106e417f5bf000335c554829797f7bf0083b7272695c3a2601705a600130565ad4101828797f7bf0083b7a72489cb1705a600130505ac8287a2604101828082809707c0009387777c03c7070015e305472380e70097f7bf0083b7a7cf91cb9705c0009385057b1705a600130585a882878280411106e4086197700000e780200d0145a26041018280411106e4086197700000e780c00b0145a26041018280797106f422f026ec2a840a8597106c00e780004022e81795ae009304a5530a850c08268697f06b00e780001b130514002ae80a850c08268697f06b00e780c019130524002ae80a850c08268697f06b00e7808018130534002ae80a850c08268697f06b00e7804017130544002ae80a850c08268697f06b00e7800016130554002ae80a850c08268697f06b00e780c014130564002ae80a850c08268697f06b00e7808013130574002ae80a850c08268697f06b00e7804012130584002ae80a850c08268697f06b00e7800011130594002ae80a850c08268697f06b00e780c00f1305a4002ae80a850c08268697f06b00e780800e1305b4002ae80a850c08268697f06b00e780400d1305c4002ae80a850c08268697f06b00e780000c1305d4002ae80a850c08268697f06b00e780c00a1305e4002ae80a850c08268697f06b00e78080091305f4002ae80a850c08268697f06b00e7804008130504012ae80a850c08268697f06b00e7800007130514012ae80a850c08268697f06b00e780c005130524012ae80a850c08268697f06b00e7808004130534012ae80a850c08268697f06b00e7804003130544012ae80a850c08268697f06b00e7800002130554012ae80a850c08268697f06b00e780c000130564012ae80a850c08268697f06b00e78080ff130574012ae80a850c08268697f06b00e78040fe130584012ae80a850c08268697f06b00e78000fd130594012ae80a850c08268697f06b00e780c0fb1305a4012ae80a850c08268697f06b00e78080fa1305b4012ae80a850c08268697f06b00e78040f91305c4012ae80a850c08268697f06b00e78000f81305d4012ae80a850c08268697f06b00e780c0f61305e4012ae80a850c08268697f06b00e78080f57d0422e80a850c08268697f06b00e78060f40a8597f06b00e780c00aa2700274e26445618280130101d52334112a2330812a233c9128233821292334312923304129233c5127233861272334712723308127be8b03c487073a89b68ab2892e8a2a8b19c405456316a40003c59b0789a881a817f5bf00033525e483b40b080340050013050002a14597300300e780a0716301055a2a8c886018619064946893858401a813029703350120fe755e76be76233cac002338bc002334cc002330dc0083b58b0823008110a300a11062e6986103b60b0983b68b0993850b0aa813029703350120aaea7e75de753e7683b68b0aaae6aee232fe986203b60b0b83b68b0b93850b0ca813029703350120fe755e76aae83e75aee4b2e092652afc326503968b0c2ee4b6652ae8727596662ef45667a2752aec36f03af8231cc1048dcd027597801900e780800811466396c50283451500034605008346250003053500a205d18dc2066205558d4d8db775747a9b85a536630cb5047d557e155d0505052334ab000d452330ab00280097c00000e78040c65e8597d00000e780a0c58330812a0334012a833481290339012983398128033a0128833a8127033b0127833b8126033c01261301012b8280a66581e50345810041cd83568105066541667d366385c60289ce63feb6003306d500030606001307f0fb6357c744b68539a03de591a163010544639fd54213b61500b1462e966384d504a546639ad50497756c009385e52b97406b00e780007221e1d0c13012a266b277e7bd48addb7e25887acab5449ba9f8e07fb92ed143b9c949877700b92b6728e61b5fb314c15e4aa2ff96479e3a3e2397d5f4b179719f70db3d9f25582c92babd6fd673d6fa3b0433c701a81fa00c5785d735f7c9f8615a12fc4117722df00ba7b53a9b7f401badd664f7f63355c0b0b69974002c531c4b6ebc50daa47b925306306b31a5a58e50b70ffddc1034e9c04877585d712c9c1706f643e33d4b336a99d81080e6e51c80062905f839a573a3fdc0d313b3745224bdb143082c9355c9a171bb54f4520d7a024cdb4deca87a6c4c25ecd8b54e3db2c1a4f09564e3d1f4702aaf51c18aec0b04269cf9d6a23bd5876b293124dcf28279b22f92adf5d6a27764cb8790529a2a035fc9b42a99ef38df03c9c166d7d95da7cf3c7cd4d1447817071bbba69f1d81651a1e1b36bc0de2fc9470f538f617de9cbf2fd074d893300c0f7d5f61549c10482ceabef55929be8dde942f43d53406b07e3b455f52171380c55b89429133915bb9640e5c68016153a37d8ee1ad6475c46c786db12c1b61391fc454f6b2c487597e1dda893d7cb171aaea9693e64968436c786db12c1b61391fc454f6b2c487597e1dda893d7cb171aaea9693e6496843742ca332ef4385149e318a4720da5203d7d192a2620f3557c48b65a684c62d239415f01cf3b97fa49cefaaf6b1e74d300d692d8173e2f8493af2c334aa5b959908ef22ec0eb7f6755282161680d4b43953b6c1993ca086812162b48bcd6c9e1d5ff902bf6eb001ac1bb8c30d6e059fc7d2c7aaebfc102d8d859627cb998eeaace4ec48e707e44970933489a369cd77b5c50ea60d28654a2fb5cc013ef77456ebe4ec48e707e44970933489a369cd77b5c50ea60d28654a2fb5cc013ef77456eb458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e73029458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e73029ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e2501ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e7302977579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52077579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52077579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52095d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c895d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c895d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c8b28c2a69e62ee31790849a5e1dd6cf0e5f4e25b7b767d5931afc57b1939377a2dd5631eeef495151858305221df29ecc9193dbe3110c3d1c63eb3a66a3c27c981aea7d480b8532bcd6786ad931543fe4ba6b2ac1d6ce96c2460dd9919343886d04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfb04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfb04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfb18b05bc4a83f148aa136c869b7722eff84dbd7029c203b6a6057b3c11b6fee96ffffffffffffffffffffffffffffffff0000000081e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c0003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac0e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac0e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac0da4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b083513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1d83513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1da41ab30e29a3d698801226eed76777e26c203978679d50fa3b3c5c14f1f9cb3aa41ab30e29a3d698801226eed76777e26c203978679d50fa3b3c5c14f1f9cb3a266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11ffffffffffffffff00000000000000000002000000000000000200000000000000020000000000000000000000000000000000000000000000000000000000010000004d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d0266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11ffffffffffffffff11da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be903170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314a5ac2cbc0a933d1c29880cd57f014963936d0f33680ea9d64ec2d6b508cd5460266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f1183513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1dbf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9abbf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9abbf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9abda4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b0da4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b068105910d731c1ec23d4306791d3e1486c4f8c2ed5ff76f20028ca93b79c8ab6607743f82fac0d8695c9260211f7cbe2df7cf107ea9c5408745914a1460510db81e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c000000000000000081e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c11da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be903170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c11131400000000 +e0e093e89898256d62704ff091ca7365d8d5ce452125eb6d23c5a62286b520aa00eb8002a0eaaaaaaaaa800a83aaaaaaaaa8fcb88880a8fceaaaaaaaaa23aaaaaaaaabab0000000000000000d8fdff0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b69702a889248a4d6620475a105dccd5e0d4230aca8a492aaf6510e55d55b0265f2268196bd53063902e3bd59019dc182e308117688aa8e901e1c6afa51fa70860a030000000000e2d93df6a2e919e879551686bc301480fc50c54dc949b14b916d5834113bb0613e8adfb8922e389026900d8f88c206d542742576db4ce817b835d751f034cf837ceca86bfbb03ff564bc8ecbfd9b91889a5f178fa6d571b3d2fabc44182ac94663b5093b9506db7404bd590160c5c2eaddd46946b89cc26dc19648afbd9cb5ea7d15b374c50011a0814417e5bc001305654797206800e780403710610c652334c4fc2338b4fc854614e104e51dc6233cc4fc2330b4fe6312d604a1c17d552fb5a5028545631bb502130504fe0f00300297206400e780a07a0da017e5bc001305654297206800e780403297b565009385e58a97b06500e780a08ee2704274a27421618280aa84130584fc97a06300e7804060268597006700e780a0b8411106e422e0000897606600e780c0537d567e1605066316c500a26002644101828017c6bc001306267d97000000e780201d797106f422f026ec4ae84ee4001898663289ae89aa8419cb8c6a85c188624e86ca8697300300e780a01a05a86306090217f5bc000335a5b639a0630f090017f5bc000335c5b5034005004a85ce8597300300e780e01611a04e859335150011c1aa8923b4340123b824018ce0a2700274e2644269a26945618280011106ec22e800103a88b6872330b4fe2334c4fe97d5bc0013864539930504fe930684fe328797000000e78080565d7186e4a2e026fc4af88008b384c50063e7b4062a8908619315150063fa9504a14563fb950463cc040401c9833589002334b4fc233ca4fc05452338a4fc130504fb8545930684fc268697000000e7800005033504fb0ded033584fb2334a90023309900a6600664e274427961618280ae84a145e3e995faa144e3d804fa014517d6bc001306a63797000000e780a009033584fb833504fce5b7797106f422f026ec4ae84ee4001898663289ae89aa8415cb8c6a9dcd88624e86ca8697300300e78080069335150011c1aa8923b4340123b824018ce0a2700274e2644269a269456182806309090217f5bc000335a5a039a06302090217f5bc000335c59f034005004a85ce8597300300e780e0009335150045fd65bf4e8593b51900e38809fa6db7411106e422e0000811e5328597606800e78040b997000000e7808000411106e422e000082a862e85b28597300300e78080fe597186f4a2f08018930604f0c8ea17e5bc000335056b97826800e782820f17d5bc001305853d97826800e782c20c97000000e780401c597186f4a2f08018930604f0c8ea17e5bc000335a56797826800e782220c17d5bc001305253d97826800e782620997000000e780e018597186f4a2f08018930604f0c8ea17e5bc000335456497826800e782c20817d5bc001305c53c97826800e782020697000000e7808015597186f4a2f08018930604f0c8ea17e5bc000335e56097826800e782620517d5bc001305653c97826800e782a20297000000e7802012411106e422e0000897106900e780c0ad1d7186eca2e8801097e5bc0003b6e54daa851305f4fe233ca4fc2330c4fe17e5bc001305858b2334a4fa05452338a4fa233404fc130684fd233cc4fa2330a4fc130584fa97000000e780600c1d7186eca2e8801097f5bc0003b625aeaa851305f4fe233ca4fc2330c4fe17e5bc001305c5872334a4fa05452338a4fa233404fc130684fd233cc4fa2330a4fc130584fa97000000e780a007411106e422e000082a86177591001305e53f9305b00297000000e780400d1d7186eca2e880102330a4fa2334b4fa130504fa2330a4fe171569001305e55a2334a4fe17856900130545262338a4fa0545233ca4fa233804fc930504fe2330b4fc2334a4fc130504fbb28597000000e7808000797106f422f00018233ca4fc2330b4fe05452314a4fe130584fd97606500e780e0781d7186eca2e88010147518712338d4fc2334e4fc146d18691c6508612330d4fc233ce4fa2338f4fa2334a4fa130584fa233ca4fc2330c4fe230404fea304b4fe130584fd97606500e7802074000097000000e78060115d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc130504fbb28597000000e78060f55d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc17d5bc001306e577130504fb814597000000e78060f35d7186e4a2e080082330a4fe2334b4fe130504fe2338a4fa0545233ca4fa233804fc21452330a4fc233404fc17d5bc0013064575130504fb854597000000e78040ef597186f4a2f08018930604f0c8ea17e5bc00033505f2ccee930584f9eceae8ee930504f9ecf2e8f617d5bc001305e572c8f20945c8f6233004fc930504fdccfac8fe130504fab28597000000e78020e8411106e422e0000817759100130525269305600297000000e78080f1411106e422e00008177591001305c5269305400297000000e780e0f3011106ec22e800103a88b6872330b4fe2334c4fe97d5bc001386a56d930504fe930684fe328797000000e7806003011106ec22e800103a88b6872330b4fe2334c4fe97d5bc001386c56c930504fe930684fe328797000000e7808000517186f5a2f1a6edcae98019c2842338b4f0930504f090ed94f198f509c98545631fb500177591001305e51f29a0177591001305251f2338a4f2094501a8177591001305651e2338a4f21d458c63233ca4f295ed130504f3130604f028fa171569001305a52f28fe130504f148e2171569001305452d48e6930504f24cea48ee17d5bc001305256548fa0d45b9a8130504f413060003130904f4be8597106800e7804019130504f3130604f028fa1715690097e5bc0083b585131305a52a28fe233024f94ce6130504f148ea171569001305652748ee930504f24cf248f617d5bc001305456248fa1145130604f048fe233804fc930504f76ce268e6130504fba68597000000e78040ce197186fca2f800012330a4f82334b4f82338c4f8233cd4f8130504f82338a4fc171569001305e522233ca4fc130504f92330a4fe17156900130545202334a4fe17d5bc00130585612330a4fa09452334a4fa233004fc930504fd2338b4fa233ca4fa130504faba8597000000e78040c7397106fc22f8800017d5bc00130505772330a4fc05452334a4fc233004fe21452338a4fc233c04fc17d5bc0093050576130504fc97000000e78080c3411106e422e0000897106900e780801b411106e422e0000897106900e7808020411106e422e0000897106900e7808025397106fc22f88000aa8517d5bc001305a5722330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78020bd411106e422e00008aa862e85b68597106900e780c026397106fc22f88000aa8517d5bc001305e5712330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78060b8411106e422e0000897f06800e780e01e397106fc22f88000aa8517d5bc001305052f2330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78000b4397106fc22f88000aa8517e5bc00130525942330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e780a0b0397106fc22f88000aa8517e5bc001305c5912330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78040ad397106fc22f88000aa8517e5bc001305658f2330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e780e0a9397106fc22f88000aa8517e5bc001305058d2330a4fc05452334a4fc233004fe21452338a4fc233c04fc130504fc97000000e78080a601112ae406ec97b06700e780005d97b06700e780a05c17e3bc00033363160293226597306800e780e0479701bd009381218e0a8593050000137101ff09a00c4113068500814717e7bc00033767e297e6bc0083b6660c17e5bc000335e5c017b367006700c3409707bd009387673203c7070029eb05472380e70097e7bc0083b7a7cc85c7411106e417e5bc000335c551829797e7bc0083b7271f95c3a2601785a200130565504101828797e7bc0083b7a71d89cb1785a2001305054f8287a2604101828082809707bd009387772c03c7070015e305472380e70097e7bc0083b7a7c891cb9705bd009385052b1785a2001305854b828782800861171365006700e3bc197186fca2f8a6f4caf0ceecd2e8d6e4dae05efc62f866f46af06eec1061ae8d01450296630a05180c6d638f051497f5bc0003b6c5fd83b70d0003b70d0183b68d00b305c70281cf0dc7060783d706008906be957917b385c5026dfb21a811cb83c706008506be957d17b385c5026dfb11a08145014c17f6bc008337c6f913d6650283388500ea054d8ee591b3fac800033a0500370501011b0515101316050232953388a502130d0afe3705fffe9b0cf5ef13950c0297f5bc0003bb45f6aa9c173569009309250a33055a0183451500034605008346250003473500a205d18dc2066207d98ed58d034655008346450003476500034575002206558e42076205598d518d02153364b500334504019345f5ff6695fd8d33f9a500630c09043ee866e4c28c330520413375a9003305650369914e95034505000d815695b37b150113955b00b305ad406e85ee84d68dc68a97b00100e78000b015e91305f9ff337925016688a26cc267d688ee8aa68de31a09fa13151400b375f4006d8d11e5210ce29ab3fa1a0189b7014501a83305704116055295833585ff0545e6704674a6740679e669466aa66a066be27b427ca27c027de26d0961828017a5aa001305251397e0ffffe780e0640d7186eea2eaa6e6cae24efe8c613284aa8401458295630105162a890861631c0512146018648335890008687d562330c900638b051497e5bc0003b445d212940c60639205121064fd550ce0630c06141c6193058900281897600200e78040bb6275fd59fe19938579026317b5000860050508e0a90975a02675867566662af12eed32e94665a6650666e2762ae52ee1b2fcb6f885094ef50545aae182e582e982ed8c18aefd97050000938585692ee297a5aa00938525062ee62aeaac192eee2af202f688112c0297506800e7802048881197706800e78000274a76aa79ea7632e44e666e6736e82aec32f43af82ef0881897000000e780a0590860a265050508e02ef54265e2650276a2762af92efdb2e1b6e54275fd55fe15ad05aae96381b90a4e65ae650e6688f88cf490f06a75ca752a768336090088ec8ce890e485062330d90023b03401f6605664b6641669f2793561828017a5aa001305c50597f0ffffe780e05217a5aa001305c50197f0ffffe780e051a9a817a5aa001305a5fa97e0ffffe780604c17456900130515e397a5aa001386a500fd4597f0ffffe780805a3da0174569001305a5d997a5aa001386e5fbf94597f0ffffe780c05809a817a5aa00130525f697e0ffffe780e0470000aa84281197a00000e780209919a897f0ffffe7808075aa84281197a00000e780c097881897000000e780604931a097f0ffffe7808073aa840860050508e026858335090085052330b90097f06600e780c0cd8335090085052330b90097f06600e780a0cc130101c2233c113c2338813c2334913c2330213d233c313b2338413b1061ae84014502962a8471c928001306801da68597106800e780a09708605de57d5508e0130984004a8597506000e780804c9309010188131306001dce8597106800e780009517e5bc000335a5ca034005001305001da14597200300e780a02b49cd2a8a1306001dce8597106800e7804092046c0864639ba40017a5aa009305e5e64a85975060005c4ba80ed8c1a9eec5df91932461ce21820ff5ee628e1c09ae46dd735ecae77c1f78743ba4ab5583acfb9d60eaee01d9eae0303774673dc37fd00831984e0e5e436b50adbb9f5a898c2f68c46dab6b04b6d8b55cb08d608d984067735f16e0983ab799cc9164fcadeb7eb407f96d8f540361f7f195d6d0d8b399041174f35a9cc9d3d8512158c755c20bf8ee89a8bd0e3e2637b02cdcbd70910ae53f1a09c4bbcd329fd994cc635377cc147805db1af887aacc00eb5b1e4621d6a7197ee309b0bd7b3bf3a965363b6f9c2651b4bded744a752b2d0296ecb4e18449ca39540501345bd8322607912117ff4d69bd5bdae6e6628967f569524b3a16cb2cc33e2835df5bf8c97b593f8e4e7795736e59bc2e6752b3f83bcf32e06a9bde38ef794d3a8c4171beba2dcfe9dfdd2645a4cf202a27d19336ba0ab77c14c113b3aa4ae3891a74559adb814cb1cef00976cdb607187aea551b14bcdc02b2139089ced6887ed288187e1a47b8f58d48db3fdf663a533d985e51570ece46ddd535b60a400bbf3832c13546661ede4aa3a4f5204b84db008beeed61abecae65b8ee955e5ebc8086effffcf373cb9ecccf70af53955835cf823c914cb63dc640cb3e454d74a8d2ea2e39015c6eb5ba3364fd09f50d7cc6adba64abf39485628e4aea485d5a13fb6c786db12c1b61391fc454f6b2c487597e1dda893d7cb171aaea9693e64968436c786db12c1b61391fc454f6b2c487597e1dda893d7cb171aaea9693e64968434ccaa912f1167d26ed52f38375bcb0dcf2043ebb29b8beb6f581b4de111961afb82d81e8990851ccabe8da352047e467ffa5465a799ae7fea231dde90b8ca5f6e9f3c4d02cc14e3cdf49c1cb1bf931d290617a222bbba0585e8f3d9f35ef4e075ff902bf6eb001ac1bb8c30d6e059fc7d2c7aaebfc102d8d859627cb998eeaace4ec48e707e44970933489a369cd77b5c50ea60d28654a2fb5cc013ef77456ebe4ec48e707e44970933489a369cd77b5c50ea60d28654a2fb5cc013ef77456eb01ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e73029458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e73029458ee9bd46bf3111d0edf0a494ad5aee584044345fe5146c0fd4f358a2e7302977579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52077579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52077579987e453af97fc70f435361e1862cfdddf81f40308c77e9e57b7ddada52095d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c895d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c895d1d984fe6573984e80d53c99933f5e83b2514c2eab082b45d784e2d79c98c8b28c2a69e62ee31790849a5e1dd6cf0e5f4e25b7b767d5931afc57b1939377a206de67afd16f44502004ecda7c1b0623eeae10cdf5bce2461ae0c796306e38551aea7d480b8532bcd6786ad931543fe4ba6b2ac1d6ce96c2460dd9919343886d04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfb04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfb04d03bf6fcc64b8e63e864341851f0c8e67d3f75d6054986c9cdc3f4df60ccfbc5c5ae64c9b6b51bef70a375e055cd0d5452a81f2c47aa6734d039f0b9cf980effffffffffffffffffffffffffffffff0000000081e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c0003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac0da4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b0bf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9ab266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11ffffffffffffffff00000000000000000002000000000000000200000000000000020000000000000000000000000000000000000000000000000000000000010000004d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d04d16cb560e453983ff2c613b2ca6c1c474260a36bd1e9eeaa312d7e968d1c4d0266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11ffffffffffffffff11da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be903170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314a5ac2cbc0a933d1c29880cd57f014963936d0f33680ea9d64ec2d6b508cd5460266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11266d584055150a62d85d14cf42666e575f054bcc3db5b061c3c04eb62c5c6f11a41ab30e29a3d698801226eed76777e26c203978679d50fa3b3c5c14f1f9cb3aa41ab30e29a3d698801226eed76777e26c203978679d50fa3b3c5c14f1f9cb3a83513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1d83513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1d83513bf2e7653b1b4b324ed86c7c4e06ac2b664dc98c75915e4147eb66632c1dbf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9abbf7fe71daa3926d1a19a989536e921e1e8345f57fcfc67f47d04b78f9435d9abda4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b0da4f0ead8caeb6633d454311ffde297daeb02aa4b11a2c6906133d0b44a790b0e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac0e224c09497778e85239c09724a01626ac6c9d45c538e7dba1d76b79e28678ac068105910d731c1ec23d4306791d3e1486c4f8c2ed5ff76f20028ca93b79c8ab62234fcb093ac8fa7bba5e1e4056374c5e943bcd4558aa865d04010fd5673308381e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c000000000000000081e47a19e6b29b0a65b9591762ce5143ed30d0261e5d24a3201752506b20f15c11da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be903170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c11131400000000 diff --git a/src/riscv/lib/tests/expected/jstz/result b/src/riscv/lib/tests/expected/jstz/result deleted file mode 100644 index 8fce915a982d3bcc24f59fdb1a484c062fae6285..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/jstz/result +++ /dev/null @@ -1,5 +0,0 @@ -Exited { - steps: 479879394, - success: true, - status: "Inbox has been drained", -} diff --git a/src/riscv/lib/tests/expected/jstz/state_hash_final b/src/riscv/lib/tests/expected/jstz/state_hash_final deleted file mode 100644 index e7d5522a545262da3b7516c0f0b04d0ea947cf90..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/jstz/state_hash_final +++ /dev/null @@ -1 +0,0 @@ -e50ec06a70137ed9e07a2eb1051d1f843f1e19228d0588ec125441efe837f6a6 diff --git a/src/riscv/lib/tests/expected/jstz/state_hash_initial b/src/riscv/lib/tests/expected/jstz/state_hash_initial deleted file mode 100644 index e32c4051f23c294363a3b6279d18aed102cbcd8e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/jstz/state_hash_initial +++ /dev/null @@ -1 +0,0 @@ -31964c4906e829a3c0871a241bedbf43dd96f2212df1d116e6f07c1f0ba3fd79 diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoadd_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amoadd_d.out deleted file mode 100644 index e0bd46965f1e72843f5e2d6961a0d83822e7f526..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoadd_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 69 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoadd_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amoadd_w.out deleted file mode 100644 index 14c9fa6cca753b6de3427940dd749102468ab4af..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoadd_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 66 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoand_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amoand_d.out deleted file mode 100644 index 14c9fa6cca753b6de3427940dd749102468ab4af..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoand_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 66 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoand_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amoand_w.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoand_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomax_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amomax_d.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomax_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomax_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amomax_w.out deleted file mode 100644 index ed112e26e2fd651d32bcc51f2ded097a3263f1ce..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomax_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 79 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_d.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_w.out deleted file mode 100644 index ed112e26e2fd651d32bcc51f2ded097a3263f1ce..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomaxu_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 79 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomin_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amomin_d.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomin_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amomin_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amomin_w.out deleted file mode 100644 index ed112e26e2fd651d32bcc51f2ded097a3263f1ce..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amomin_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 79 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amominu_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amominu_d.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amominu_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amominu_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amominu_w.out deleted file mode 100644 index ed112e26e2fd651d32bcc51f2ded097a3263f1ce..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amominu_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 79 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoor_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amoor_d.out deleted file mode 100644 index 5b3768a1f3ca740b3ffbb4f2e8fd5471fe59bffd..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoor_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 64 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoor_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amoor_w.out deleted file mode 100644 index 5b3768a1f3ca740b3ffbb4f2e8fd5471fe59bffd..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoor_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 64 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoswap_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amoswap_d.out deleted file mode 100644 index 14c9fa6cca753b6de3427940dd749102468ab4af..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoswap_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 66 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoswap_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amoswap_w.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoswap_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoxor_d.out b/src/riscv/lib/tests/expected/rv64ua-p-amoxor_d.out deleted file mode 100644 index 9aa40264222251312713da8fd21284a75739485f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoxor_d.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 67 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-amoxor_w.out b/src/riscv/lib/tests/expected/rv64ua-p-amoxor_w.out deleted file mode 100644 index e0bd46965f1e72843f5e2d6961a0d83822e7f526..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-amoxor_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 69 } diff --git a/src/riscv/lib/tests/expected/rv64ua-p-lrsc.out b/src/riscv/lib/tests/expected/rv64ua-p-lrsc.out deleted file mode 100644 index c2188c609927e7fc4494d973892b2600d92885df..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ua-p-lrsc.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 6243 } diff --git a/src/riscv/lib/tests/expected/rv64uc-p-rvc.out b/src/riscv/lib/tests/expected/rv64uc-p-rvc.out deleted file mode 100644 index 60df7d1611f392e04c98c5b2daeb465f659c182f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uc-p-rvc.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 260 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fadd.out b/src/riscv/lib/tests/expected/rv64ud-p-fadd.out deleted file mode 100644 index 7a77021edd6472031a24c9dcc608df317f23d276..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fadd.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 172 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fclass.out b/src/riscv/lib/tests/expected/rv64ud-p-fclass.out deleted file mode 100644 index 1686284ac22264959679d2f3c634c83b8538fb8e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fclass.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 115 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fcmp.out b/src/riscv/lib/tests/expected/rv64ud-p-fcmp.out deleted file mode 100644 index 1f08e5e485903272f71c830235c1576123dbc117..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fcmp.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 222 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fcvt.out b/src/riscv/lib/tests/expected/rv64ud-p-fcvt.out deleted file mode 100644 index 0646d5df46816b2c9be8d0912404362bfe25b126..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fcvt.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 154 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fcvt_w.out b/src/riscv/lib/tests/expected/rv64ud-p-fcvt_w.out deleted file mode 100644 index 9e709a3b6e61dbce2dbb9dd9227f262cac39806b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fcvt_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 572 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fdiv.out b/src/riscv/lib/tests/expected/rv64ud-p-fdiv.out deleted file mode 100644 index 5ad39f5623eab7a3f8410c8f58ced72620cf7e0f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fdiv.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 146 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fmadd.out b/src/riscv/lib/tests/expected/rv64ud-p-fmadd.out deleted file mode 100644 index 5f9b2b1c2fdff3bf45462f46acaa95c4d1e22291..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fmadd.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 198 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-fmin.out b/src/riscv/lib/tests/expected/rv64ud-p-fmin.out deleted file mode 100644 index eae35d8b003728fd3a20ba36c3dd8077c980a8a7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-fmin.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 276 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-ldst.out b/src/riscv/lib/tests/expected/rv64ud-p-ldst.out deleted file mode 100644 index d0314f8e89243a95ec539a7dd1e51b7e6754a69e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-ldst.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 87 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-move.out b/src/riscv/lib/tests/expected/rv64ud-p-move.out deleted file mode 100644 index 878fd6d66f2ff7a5bed3dc7a8394fd1de94f6339..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-move.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 992 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-recoding.out b/src/riscv/lib/tests/expected/rv64ud-p-recoding.out deleted file mode 100644 index 1fe4d241f4a1326890d1ffe2bd534223839841f7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-recoding.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 100 } diff --git a/src/riscv/lib/tests/expected/rv64ud-p-structural.out b/src/riscv/lib/tests/expected/rv64ud-p-structural.out deleted file mode 100644 index 8bb1754b7ddf5d1779e4b3ce0f3fbedab5bee024..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ud-p-structural.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 165 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fadd.out b/src/riscv/lib/tests/expected/rv64uf-p-fadd.out deleted file mode 100644 index 7a77021edd6472031a24c9dcc608df317f23d276..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fadd.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 172 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fclass.out b/src/riscv/lib/tests/expected/rv64uf-p-fclass.out deleted file mode 100644 index 199b100437694e39128f8d2f965f56f8322d3acc..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fclass.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 109 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fcmp.out b/src/riscv/lib/tests/expected/rv64uf-p-fcmp.out deleted file mode 100644 index 1f08e5e485903272f71c830235c1576123dbc117..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fcmp.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 222 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fcvt.out b/src/riscv/lib/tests/expected/rv64uf-p-fcvt.out deleted file mode 100644 index 04a0329862f5ee4a974a4adc8b7cfca221da9760..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fcvt.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 114 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fcvt_w.out b/src/riscv/lib/tests/expected/rv64uf-p-fcvt_w.out deleted file mode 100644 index e9600dd4a70568f43b1592a90a362c3484b35a8b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fcvt_w.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 512 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fdiv.out b/src/riscv/lib/tests/expected/rv64uf-p-fdiv.out deleted file mode 100644 index 359b0facf1f4de19057819d8a34b8ad518f4cd8d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fdiv.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 133 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fmadd.out b/src/riscv/lib/tests/expected/rv64uf-p-fmadd.out deleted file mode 100644 index 5f9b2b1c2fdff3bf45462f46acaa95c4d1e22291..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fmadd.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 198 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-fmin.out b/src/riscv/lib/tests/expected/rv64uf-p-fmin.out deleted file mode 100644 index eae35d8b003728fd3a20ba36c3dd8077c980a8a7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-fmin.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 276 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-ldst.out b/src/riscv/lib/tests/expected/rv64uf-p-ldst.out deleted file mode 100644 index 6007f024734dde4736102a4b1346ed383205a3a4..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-ldst.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 68 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-move.out b/src/riscv/lib/tests/expected/rv64uf-p-move.out deleted file mode 100644 index b2e77ff7fb980ffbe88964a66af890c6f8a6f141..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-move.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 217 } diff --git a/src/riscv/lib/tests/expected/rv64uf-p-recoding.out b/src/riscv/lib/tests/expected/rv64uf-p-recoding.out deleted file mode 100644 index c16aa7fb974cd2c59b7bac223c23e9a30c157b9e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64uf-p-recoding.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 75 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-add.out b/src/riscv/lib/tests/expected/rv64ui-p-add.out deleted file mode 100644 index ee4c77ad1aa1f510e20e1658b074592db8b23e4d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-add.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 470 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-addi.out b/src/riscv/lib/tests/expected/rv64ui-p-addi.out deleted file mode 100644 index e272760cfa166d0c3eea5b5703c4a364d7d24bab..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-addi.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 245 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-addiw.out b/src/riscv/lib/tests/expected/rv64ui-p-addiw.out deleted file mode 100644 index 88721718e0897e58be36db3568d4b6221aba856e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-addiw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 242 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-addw.out b/src/riscv/lib/tests/expected/rv64ui-p-addw.out deleted file mode 100644 index 18b53a620f329e1578ee821ac27b6c8bbed436d4..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-addw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 465 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-and.out b/src/riscv/lib/tests/expected/rv64ui-p-and.out deleted file mode 100644 index 6acea77d6b2f7f6c7169554a7eeeb214846fc619..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-and.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 545 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-andi.out b/src/riscv/lib/tests/expected/rv64ui-p-andi.out deleted file mode 100644 index 532c74dd23e79ce6d3836d4c2d4301d2f5858f9b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-andi.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 216 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-auipc.out b/src/riscv/lib/tests/expected/rv64ui-p-auipc.out deleted file mode 100644 index 17fb5ba6304a6a3f71b4df3690d32cc579eb2252..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-auipc.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 59 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-beq.out b/src/riscv/lib/tests/expected/rv64ui-p-beq.out deleted file mode 100644 index 88e196142008e61b2e935e212883aed5277c4376..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-beq.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 291 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-bge.out b/src/riscv/lib/tests/expected/rv64ui-p-bge.out deleted file mode 100644 index e2db32f9e01fa568859ca6ed99b77ded69ed1747..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-bge.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 346 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-bgeu.out b/src/riscv/lib/tests/expected/rv64ui-p-bgeu.out deleted file mode 100644 index 78dde9194c8bf0dd8e3e3d451f4d98e54d74f97e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-bgeu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 436 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-blt.out b/src/riscv/lib/tests/expected/rv64ui-p-blt.out deleted file mode 100644 index 73fcb4105771e085354ee95a1a979ca62d60d618..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-blt.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 329 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-bltu.out b/src/riscv/lib/tests/expected/rv64ui-p-bltu.out deleted file mode 100644 index 7cc3d90f17e73e9e41e32501f4353ad2a0790548..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-bltu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 415 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-bne.out b/src/riscv/lib/tests/expected/rv64ui-p-bne.out deleted file mode 100644 index 88e196142008e61b2e935e212883aed5277c4376..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-bne.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 291 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-fence_i.out b/src/riscv/lib/tests/expected/rv64ui-p-fence_i.out deleted file mode 100644 index e96473de4eae60aabd5ef8f12efb7430dc1b44ec..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-fence_i.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 303 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-jal.out b/src/riscv/lib/tests/expected/rv64ui-p-jal.out deleted file mode 100644 index 339f85f5db8e86c422fd940a0e3c6032e559b5da..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-jal.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 55 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-jalr.out b/src/riscv/lib/tests/expected/rv64ui-p-jalr.out deleted file mode 100644 index 1686284ac22264959679d2f3c634c83b8538fb8e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-jalr.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 115 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lb.out b/src/riscv/lib/tests/expected/rv64ui-p-lb.out deleted file mode 100644 index fd03decdd4d0f75005905e83b3d68e28c09b49c3..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lb.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 253 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lbu.out b/src/riscv/lib/tests/expected/rv64ui-p-lbu.out deleted file mode 100644 index fd03decdd4d0f75005905e83b3d68e28c09b49c3..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lbu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 253 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-ld.out b/src/riscv/lib/tests/expected/rv64ui-p-ld.out deleted file mode 100644 index 3427d15d9159f28d56d25ccb537191026a42f704..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-ld.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 435 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-ld_st.out b/src/riscv/lib/tests/expected/rv64ui-p-ld_st.out deleted file mode 100644 index 13f12b7c130d973e9d0a54e72eeb27dc6f1eff58..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-ld_st.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 1415 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lh.out b/src/riscv/lib/tests/expected/rv64ui-p-lh.out deleted file mode 100644 index 58b43a76e7ad811aa568050ace1d8de40cc88318..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lh.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 269 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lhu.out b/src/riscv/lib/tests/expected/rv64ui-p-lhu.out deleted file mode 100644 index ffae2a9ab3981d8eac87b5b58e183c35e2032be9..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lhu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 278 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lui.out b/src/riscv/lib/tests/expected/rv64ui-p-lui.out deleted file mode 100644 index ee879fd6511cd896fa1f6f42363e38863d61f16b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lui.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 65 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lw.out b/src/riscv/lib/tests/expected/rv64ui-p-lw.out deleted file mode 100644 index cbf23d02713073322c6216d4cdd017f6d6b6704e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 283 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-lwu.out b/src/riscv/lib/tests/expected/rv64ui-p-lwu.out deleted file mode 100644 index c74687999f639631ab88893e6adcc829b0dda863..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-lwu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 317 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-ma_data.out b/src/riscv/lib/tests/expected/rv64ui-p-ma_data.out deleted file mode 100644 index b2418d8d24288b161bee08a765e07398961cf53c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-ma_data.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 1776 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-or.out b/src/riscv/lib/tests/expected/rv64ui-p-or.out deleted file mode 100644 index 09b5e5d724d83cf7441ce38a1d3ffd4b81aea05b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-or.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 578 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-ori.out b/src/riscv/lib/tests/expected/rv64ui-p-ori.out deleted file mode 100644 index 7728c2f767b927a3901002867b7760b230a36b17..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-ori.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 209 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sb.out b/src/riscv/lib/tests/expected/rv64ui-p-sb.out deleted file mode 100644 index f76631fbc22bb200ed5b1f3aefc5094c2bedf28f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sb.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 454 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sd.out b/src/riscv/lib/tests/expected/rv64ui-p-sd.out deleted file mode 100644 index 754610dec4e19d275c15446203a102ac2d1c6438..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sd.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 626 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sh.out b/src/riscv/lib/tests/expected/rv64ui-p-sh.out deleted file mode 100644 index 8a1af290e3de4e825267d4b8c7f5e0a1c4ca308d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sh.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 507 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-simple.out b/src/riscv/lib/tests/expected/rv64ui-p-simple.out deleted file mode 100644 index c1453135c5c98fb11b27544a8a23c9a275983e7c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-simple.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 41 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sll.out b/src/riscv/lib/tests/expected/rv64ui-p-sll.out deleted file mode 100644 index 5152955cc49b35b5a32051b59e6c43108e5c5372..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sll.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 540 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-slli.out b/src/riscv/lib/tests/expected/rv64ui-p-slli.out deleted file mode 100644 index 59a3e4dc10ce3f8f9f5d5d306760a3199175763d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-slli.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 270 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-slliw.out b/src/riscv/lib/tests/expected/rv64ui-p-slliw.out deleted file mode 100644 index b32b6396cff30c03db6a4671eb004d10ae9e1cbd..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-slliw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 277 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sllw.out b/src/riscv/lib/tests/expected/rv64ui-p-sllw.out deleted file mode 100644 index 5152955cc49b35b5a32051b59e6c43108e5c5372..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sllw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 540 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-slt.out b/src/riscv/lib/tests/expected/rv64ui-p-slt.out deleted file mode 100644 index 4504f8e4934780fb986efa85e2b9d7bc720bb253..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-slt.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 459 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-slti.out b/src/riscv/lib/tests/expected/rv64ui-p-slti.out deleted file mode 100644 index fbcc716191b8dc751945ff5e13a9d7ddb4893fe4..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-slti.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 237 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sltiu.out b/src/riscv/lib/tests/expected/rv64ui-p-sltiu.out deleted file mode 100644 index fbcc716191b8dc751945ff5e13a9d7ddb4893fe4..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sltiu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 237 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sltu.out b/src/riscv/lib/tests/expected/rv64ui-p-sltu.out deleted file mode 100644 index e75d2f5ca82e0925011538c08925fa67dbae5a87..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sltu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 476 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sra.out b/src/riscv/lib/tests/expected/rv64ui-p-sra.out deleted file mode 100644 index e9600dd4a70568f43b1592a90a362c3484b35a8b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sra.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 512 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-srai.out b/src/riscv/lib/tests/expected/rv64ui-p-srai.out deleted file mode 100644 index 920edf08562e4257b96281625a28ab64bcfd119c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-srai.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 258 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sraiw.out b/src/riscv/lib/tests/expected/rv64ui-p-sraiw.out deleted file mode 100644 index 9569717a83b272a66d5ad86db9564311031aa97b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sraiw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 304 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sraw.out b/src/riscv/lib/tests/expected/rv64ui-p-sraw.out deleted file mode 100644 index 198d4ce021ca6eab4b13b9553c31a6816d59df5d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sraw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 552 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-srl.out b/src/riscv/lib/tests/expected/rv64ui-p-srl.out deleted file mode 100644 index 6b5b7638e9d92217b33865a437cbe35947687e1c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-srl.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 554 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-srli.out b/src/riscv/lib/tests/expected/rv64ui-p-srli.out deleted file mode 100644 index cb821222b7552988f835b42af271afa1aaf21b7f..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-srli.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 279 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-srliw.out b/src/riscv/lib/tests/expected/rv64ui-p-srliw.out deleted file mode 100644 index a0ead3c462f13e73494bd4d06c36a0633a84d49c..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-srliw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 286 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-srlw.out b/src/riscv/lib/tests/expected/rv64ui-p-srlw.out deleted file mode 100644 index 8e8d0c13b5dc01ac221a0ffc274afd482525db4e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-srlw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 546 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-st_ld.out b/src/riscv/lib/tests/expected/rv64ui-p-st_ld.out deleted file mode 100644 index c1c2f59d4b58ba2abfe34cbdef60fa5376b459be..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-st_ld.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 725 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sub.out b/src/riscv/lib/tests/expected/rv64ui-p-sub.out deleted file mode 100644 index 4687104f8875891153bb8272f43a0ad86ba7a1d9..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sub.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 461 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-subw.out b/src/riscv/lib/tests/expected/rv64ui-p-subw.out deleted file mode 100644 index f3f9b0d4b5f3908f9c6e8b41a4ec3e19c595b87e..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-subw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 457 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-sw.out b/src/riscv/lib/tests/expected/rv64ui-p-sw.out deleted file mode 100644 index 8a9e7003d522b83371fb79dd3ba58de2575d2a70..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-sw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 514 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-xor.out b/src/riscv/lib/tests/expected/rv64ui-p-xor.out deleted file mode 100644 index 8a47e387e6bd99f5a0043b38f46bd321951caac6..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-xor.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 573 } diff --git a/src/riscv/lib/tests/expected/rv64ui-p-xori.out b/src/riscv/lib/tests/expected/rv64ui-p-xori.out deleted file mode 100644 index 49fdadabf7f6ca18c0d2b27d15b7d93ad912d4a0..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64ui-p-xori.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 207 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-div.out b/src/riscv/lib/tests/expected/rv64um-p-div.out deleted file mode 100644 index 5b9af06b979082ee2d7af6f314f5906205e4cd3d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-div.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 101 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-divu.out b/src/riscv/lib/tests/expected/rv64um-p-divu.out deleted file mode 100644 index 0e8556886f4ea3490eb5d79f272f93ad841c9723..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-divu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 107 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-divuw.out b/src/riscv/lib/tests/expected/rv64um-p-divuw.out deleted file mode 100644 index 7602ac0a9b1292378477befc8d3adf2b50472d78..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-divuw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 99 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-divw.out b/src/riscv/lib/tests/expected/rv64um-p-divw.out deleted file mode 100644 index 006111598d5326b0e2bdaf0131e239467cc30a4d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-divw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 96 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-mul.out b/src/riscv/lib/tests/expected/rv64um-p-mul.out deleted file mode 100644 index 380cf09be8b6681e0fd9d7c869b57ef958104edb..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-mul.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 460 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-mulh.out b/src/riscv/lib/tests/expected/rv64um-p-mulh.out deleted file mode 100644 index a2e01e1b54b13d549fbc222b1a508ab571064504..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-mulh.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 468 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-mulhsu.out b/src/riscv/lib/tests/expected/rv64um-p-mulhsu.out deleted file mode 100644 index a2e01e1b54b13d549fbc222b1a508ab571064504..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-mulhsu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 468 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-mulhu.out b/src/riscv/lib/tests/expected/rv64um-p-mulhu.out deleted file mode 100644 index 5ba410fc1650027982dce3558611613b559d4b2b..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-mulhu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 500 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-mulw.out b/src/riscv/lib/tests/expected/rv64um-p-mulw.out deleted file mode 100644 index e74d40418b0d991dcbc2f98aef591084be351a8a..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-mulw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 399 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-rem.out b/src/riscv/lib/tests/expected/rv64um-p-rem.out deleted file mode 100644 index 1fe4d241f4a1326890d1ffe2bd534223839841f7..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-rem.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 100 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-remu.out b/src/riscv/lib/tests/expected/rv64um-p-remu.out deleted file mode 100644 index 5b9af06b979082ee2d7af6f314f5906205e4cd3d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-remu.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 101 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-remuw.out b/src/riscv/lib/tests/expected/rv64um-p-remuw.out deleted file mode 100644 index 006111598d5326b0e2bdaf0131e239467cc30a4d..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-remuw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 96 } diff --git a/src/riscv/lib/tests/expected/rv64um-p-remw.out b/src/riscv/lib/tests/expected/rv64um-p-remw.out deleted file mode 100644 index 21bd55695db60149ddafc7b9a05be9fde2fd1960..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/expected/rv64um-p-remw.out +++ /dev/null @@ -1 +0,0 @@ -Exit { code: 0, steps: 102 } diff --git a/src/riscv/lib/tests/test_determinism.rs b/src/riscv/lib/tests/test_determinism.rs deleted file mode 100644 index 3b53c7340e7f2df17a7021203535564a178087ec..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/test_determinism.rs +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -mod common; - -use std::ops::Bound; - -use common::*; -use octez_riscv::machine_state::DefaultCacheLayouts; -use octez_riscv::machine_state::block_cache::block::InterpretedBlockBuilder; -use octez_riscv::machine_state::memory::M64M; -use octez_riscv::pvm::PvmLayout; -use octez_riscv::state_backend::RefOwnedAlloc; -use octez_riscv::state_backend::hash; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; -use octez_riscv::stepper::pvm::PvmStepper; - -#[test] -#[ignore] -fn test_jstz_determinism() { - let make_stepper = make_stepper_factory(); - - let mut base_stepper = make_stepper(); - let base_result = base_stepper.step_max(Bound::Unbounded); - assert!(matches!(base_result, StepperStatus::Exited { .. })); - - let steps = base_result.steps(); - let base_hash = base_stepper.hash(); - - // If we re-do it with identical number of steps, that should work exactly the same. - let mut stepper = make_stepper(); - let result = stepper.step_max(Bound::Included(steps)); - assert_eq!(base_result, result); - assert_eq!(base_hash, stepper.hash()); - - eprintln!("Final hash is {base_hash}"); - eprintln!("Final result is {base_result:?}"); - - let base_refs = base_stepper.struct_ref(); - - // Create multiple series of bisections that we will evaluate. - let ladder = dissect_steps(steps, 0); - run_steps_ladder(&make_stepper, &ladder, &base_refs, base_hash); -} - -fn run_steps_ladder( - make_stepper: F, - ladder: &[usize], - expected_refs: &RefOwnedAlloc>, - expected_hash: hash::Hash, -) where - F: Fn() -> PvmStepper<'static, M64M, DefaultCacheLayouts>, -{ - let expected_steps = ladder.iter().sum::(); - let mut stepper_lhs = make_stepper(); - let mut stepper_rhs = make_stepper(); - - assert_eq_struct(&stepper_lhs.struct_ref(), &stepper_rhs.struct_ref()); - - let mut steps_done = 0; - for &steps in ladder { - eprintln!("> Running {} steps ...", steps); - let result_lhs = stepper_lhs.step_max(Bound::Included(steps)); - let result_rhs = stepper_rhs.step_max(Bound::Included(steps)); - steps_done += steps; - - eprintln!( - "> Done {:.2}%", - (steps_done as f64 / expected_steps as f64) * 100.0 - ); - - assert_eq!(result_lhs, result_rhs); - assert_eq!( - result_lhs.steps(), - steps, - "Expected {} steps to be run, but got {}", - steps, - result_lhs.steps() - ); - assert_eq_struct(&stepper_lhs.struct_ref(), &stepper_rhs.struct_ref()); - - let block_builder = InterpretedBlockBuilder; - stepper_lhs.rebind_via_serde(block_builder); - } - - assert_eq_struct_wrapper(stepper_lhs.struct_ref(), expected_refs); - assert_eq!(stepper_lhs.hash(), expected_hash); - assert_eq!(stepper_rhs.hash(), expected_hash); -} - -fn assert_eq_struct(lhs: &A, rhs: &B) -where - A: serde::Serialize + PartialEq, - B: serde::Serialize, -{ - if lhs != rhs { - eprintln!("> State mismatch, generating diff ..."); - - let (file_lhs, path_lhs) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); - serde_json::to_writer(file_lhs, lhs).unwrap(); - eprintln!("Lhs is located at {}", path_lhs.display()); - - let (file_rhs, path_rhs) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); - serde_json::to_writer(file_rhs, rhs).unwrap(); - eprintln!("Rhs is located at {}", path_rhs.display()); - - eprintln!("Run the following to diff them:"); - eprintln!("jd {} {}", path_lhs.display(), path_rhs.display()); - - panic!("Assertion failed: values are different"); - } -} - -fn assert_eq_struct_wrapper<'a, 'regions1, 'regions2>( - refs: RefOwnedAlloc<'regions1, PvmLayout>, - expected: &'a RefOwnedAlloc<'regions2, PvmLayout>, -) { - // SAFETY: Rust does not allow us to compare two references with different lifetimes. - // Theoretically this should be possible and safe thanks to `PartialEq`. However, Rust's - // subtyping rules seem to influence trait-implementation selection for our `AllocatedOf<...>` - // in a way that make it more human-friendly but ultimately ends up selecting `A: PartialEq` - // (where `A` is our `AllocatedOf`-struct) such that left-hand and right-hand side operands of - // the `==` operator need to be identical in type. This also means lifetimes are forcibly - // unified. We can work around this by transmuting the references to the same lifetime. This is - // safe because lifetimes are not violated as dictated by the interface of this function. - let refs: RefOwnedAlloc<'regions2, PvmLayout> = - unsafe { std::mem::transmute(refs) }; - assert_eq_struct(&refs, expected); -} diff --git a/src/riscv/lib/tests/test_proofs.rs b/src/riscv/lib/tests/test_proofs.rs deleted file mode 100644 index fa13d942b315931df4c6d5cfd2f5df6b6ae9dc75..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/test_proofs.rs +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-FileCopyrightText: 2024-2025 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -mod common; - -use std::io::Write; -use std::ops::Bound; -use std::time::Instant; - -use common::*; -use octez_riscv::machine_state::CacheLayouts; -use octez_riscv::machine_state::DefaultCacheLayouts; -use octez_riscv::machine_state::TestCacheLayouts; -use octez_riscv::machine_state::memory::M64M; -use octez_riscv::machine_state::memory::MemoryConfig; -use octez_riscv::state_backend::hash; -use octez_riscv::state_backend::proof_backend::proof::Proof; -use octez_riscv::state_backend::proof_backend::proof::serialise_proof; -use octez_riscv::state_backend::verify_backend::ProofVerificationFailure; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; -use octez_riscv::stepper::pvm::PvmStepper; -use rand::Rng; - -#[test] -#[ignore] -fn test_jstz_proofs_one_step() { - test_jstz_proofs(false) -} - -#[test] -#[ignore] -fn test_jstz_proofs_full() { - test_jstz_proofs(true) -} - -#[test] -fn test_jstz_initial_proof_regression() { - // Configuring the stepper with `TestCachelayouts` to match the node PVM - // and make the test run faster. - let make_stepper = make_stepper_factory::(); - let mut stepper = make_stepper(); - - eprintln!("> Producing proof ..."); - let proof = stepper.produce_proof().unwrap(); - let proof_serialisation: Vec = serialise_proof(&proof).collect(); - - // This file is also used in the tests for the OCaml `lib_riscv` library - let mut mint = goldenfile::Mint::new("tests/expected/jstz"); - let mut proof_capture = mint.new_goldenfile("proof_initial").unwrap(); - - let proof_bytes = hex::encode(proof_serialisation); - writeln!(proof_capture, "{proof_bytes}").unwrap(); -} - -fn test_jstz_proofs(full: bool) { - let make_stepper = make_stepper_factory::(); - - let mut base_stepper = make_stepper(); - let base_result = base_stepper.step_max(Bound::Unbounded); - assert!(matches!(base_result, StepperStatus::Exited { .. })); - - let steps = base_result.steps(); - let base_hash = base_stepper.hash(); - - if full { - // For each step `s`, the stepper will initially step `s-1` steps, then - // produce a proof of the `s` step. The minimum step size thus needs to be 1. - let ladder = dissect_steps(steps, 1); - run_steps_ladder(&make_stepper, &ladder, Some(base_hash)); - } else { - // Run a number of steps `s` and produce a proof - let mut rng = rand::thread_rng(); - let step = [rng.gen_range(1..steps)]; - run_steps_ladder(&make_stepper, &step, None) - } -} - -fn run_steps_ladder(make_stepper: F, ladder: &[usize], expected_hash: Option) -where - F: Fn() -> PvmStepper<'static, M64M, DefaultCacheLayouts>, -{ - let expected_steps = ladder.iter().sum::(); - let mut stepper = make_stepper(); - - let mut steps_done = 0; - for &steps in ladder { - // Run one step short of `steps`, then produce a proof of the following step. - let steps = steps.checked_sub(1).expect("minimum step size is 1"); - eprintln!("> Running {} steps ...", steps); - let result = stepper.step_max(Bound::Included(steps)); - steps_done += steps; - - if steps_done != expected_steps { - assert!(matches!(result, StepperStatus::Running { .. })); - - eprintln!("> Producing proof ..."); - let start = Instant::now(); - let proof = stepper.produce_proof().unwrap(); - let time = start.elapsed(); - let serialisation: Vec = serialise_proof(&proof).collect(); - eprintln!( - "> Proof of size {} KiB produced in {:?}", - serialisation.len() / 1024, - time - ); - - eprintln!("> Checking initial proof hash ..."); - let initial_state_hash = proof.initial_state_hash(); - assert_eq!(initial_state_hash, stepper.hash()); - - let final_state_hash = proof.final_state_hash(); - - eprintln!("> Attempting to verify basic invalid proofs ..."); - basic_invalid_proofs_are_rejected(&stepper, &proof, initial_state_hash); - - eprintln!("> Verifying proof ..."); - assert!(stepper.verify_proof(proof).is_ok()); - - // Run one final step, which is the step proven by `proof`, and check that its - // state hash matches the final state hash of `proof`. - eprintln!("> Running 1 step ..."); - stepper.eval_one(); - steps_done += 1; - - eprintln!("> Checking final proof hash ..."); - assert_eq!(final_state_hash, stepper.hash()); - } else { - // Can't generate a proof on the next step if execution has ended - assert!(matches!(result, StepperStatus::Exited { .. })); - assert!(ladder.is_empty()) - }; - - eprintln!( - "> Done {:.2}%", - (steps_done as f64 / expected_steps as f64) * 100.0 - ); - } - - if let Some(hash) = expected_hash { - assert_eq!(stepper.hash(), hash) - } -} - -fn basic_invalid_proofs_are_rejected( - stepper: &PvmStepper<'static, MC, CL>, - proof: &Proof, - state_hash: hash::Hash, -) { - // A fully blinded proof could only be valid if every single leaf - // in the state is written to and proof compression were to optimise - // for this case. - let fully_blinded_proof = proof_helpers::fully_blinded(state_hash); - assert!( - stepper - .verify_proof(fully_blinded_proof) - .is_err_and(|e| matches!(e, ProofVerificationFailure::AbsentDataAccess(_))) - ); - - let empty_proof = proof_helpers::empty(state_hash); - assert!( - stepper - .verify_proof(empty_proof) - .is_err_and(|e| matches!(e, ProofVerificationFailure::UnexpectedProofShape)) - ); - - let invalid_final_hash_proof = proof_helpers::with_final_hash(proof, state_hash); - assert!( - stepper - .verify_proof(invalid_final_hash_proof) - .is_err_and( - |e| matches!(e, ProofVerificationFailure::FinalHashMismatch { - expected: _, - computed: _ - }) - ) - ); -} - -mod proof_helpers { - use octez_riscv::state_backend::hash::Hash; - use octez_riscv::state_backend::proof_backend::proof::MerkleProofLeaf; - use octez_riscv::state_backend::proof_backend::proof::Proof; - use octez_riscv::state_backend::proof_backend::tree::Tree; - - pub fn fully_blinded(hash: Hash) -> Proof { - Proof::new(Tree::Leaf(MerkleProofLeaf::Blind(hash)), hash) - } - - pub(crate) fn empty(hash: Hash) -> Proof { - Proof::new(Tree::Node(Vec::new()), hash) - } - - pub(crate) fn with_final_hash(proof: &Proof, hash: Hash) -> Proof { - Proof::new(proof.tree().clone(), hash) - } -} diff --git a/src/riscv/lib/tests/test_pvm_storage.rs b/src/riscv/lib/tests/test_pvm_storage.rs deleted file mode 100644 index 880401315bbaeb45cf5b17c224c2a9bda19ab9a2..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/test_pvm_storage.rs +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use octez_riscv::pvm::node_pvm::NodePvm; -use octez_riscv::pvm::node_pvm::PvmStorage; -use octez_riscv::storage::Repo; -use octez_riscv::storage::StorageError; -use proptest::prelude::*; -use proptest::strategy::ValueTree; -use proptest::test_runner::TestRunner; - -#[test] -fn test_repo() { - let mut runner = TestRunner::default(); - - let tmp_dir = tempfile::tempdir().unwrap(); - let mut test_data = Vec::new(); - - // Create a new repo, commit 5 times and check that all 5 commits can - // be checked out - let mut repo = Repo::load(tmp_dir.path()).unwrap(); - for _ in 0..5 { - let data = prop::collection::vec(any::(), 0..100) - .new_tree(&mut runner) - .unwrap() - .current(); - let commit_id = repo.commit(&data).unwrap(); - test_data.push((commit_id, data)); - } - for (commit_id, bytes) in test_data.iter() { - let checked_out_data = repo.checkout(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - repo.close(); - - // Reload the repo and check that all previous commits can be checked out - let mut repo = Repo::load(tmp_dir.path()).unwrap(); - for (commit_id, bytes) in test_data.iter() { - let checked_out_data = repo.checkout(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - - // Make 5 additional commits and check that all 10 commits can be checked out - for _ in 0..5 { - let data = prop::collection::vec(any::(), 0..100) - .new_tree(&mut runner) - .unwrap() - .current(); - let commit_id = repo.commit(&data).unwrap(); - test_data.push((commit_id, data)); - } - for (commit_id, bytes) in test_data.iter() { - let checked_out_data = repo.checkout(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - - // Check that an unknown commit returns a `NotFound` error - assert!(matches!( - repo.checkout(&[0; 32].into()), - Err(StorageError::NotFound(_)) - )); - - // Check that exporting a snapshot creates a new repo which contains - // the requested commit - let snapshot_dir = tempfile::tempdir().unwrap(); - let (export_hash, export_data) = &test_data[0]; - repo.export_snapshot(export_hash, &snapshot_dir).unwrap(); - let snapshot_repo = Repo::load(tmp_dir.path()).unwrap(); - let checked_out_data = snapshot_repo.checkout(export_hash).unwrap(); - assert_eq!(checked_out_data, *export_data); - - tmp_dir.close().unwrap(); - snapshot_dir.close().unwrap() -} - -#[test] -fn test_repo_serialised() { - let mut runner = TestRunner::default(); - - let tmp_dir = tempfile::tempdir().unwrap(); - let mut test_data = Vec::new(); - - // Create a new repo, commit 5 times and check that all 5 commits can - // be checked out - let mut repo = Repo::load(tmp_dir.path()).unwrap(); - for _ in 0..5 { - let data = prop::collection::vec(any::(), 0..100) - .new_tree(&mut runner) - .unwrap() - .current(); - let commit_id = repo.commit_serialised(&data).unwrap(); - test_data.push((commit_id, data)); - } - for (commit_id, bytes) in test_data.iter() { - let checked_out_data: Vec = repo.checkout_serialised(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - repo.close(); - - // Reload the repo and check that all previous commits can be checked out - let mut repo = Repo::load(tmp_dir.path()).unwrap(); - for (commit_id, bytes) in test_data.iter() { - let checked_out_data: Vec = repo.checkout_serialised(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - - // Make 5 additional commits and check that all 10 commits can be checked out - for _ in 0..5 { - let data = prop::collection::vec(any::(), 0..100) - .new_tree(&mut runner) - .unwrap() - .current(); - let commit_id = repo.commit_serialised(&data).unwrap(); - test_data.push((commit_id, data)); - } - for (commit_id, bytes) in test_data.iter() { - let checked_out_data: Vec = repo.checkout_serialised(commit_id).unwrap(); - assert_eq!(checked_out_data, *bytes); - } - - // Check that an unknown commit returns a `NotFound` error - assert!(matches!( - repo.checkout_serialised::>(&[0; 32].into()), - Err(StorageError::NotFound(_)) - )); - - // Check that exporting a snapshot creates a new repo which contains - // the requested commit - let snapshot_dir = tempfile::tempdir().unwrap(); - let (export_hash, export_data) = &test_data[0]; - repo.export_snapshot(export_hash, &snapshot_dir).unwrap(); - let snapshot_repo = Repo::load(tmp_dir.path()).unwrap(); - let checked_out_data: Vec = snapshot_repo.checkout_serialised(export_hash).unwrap(); - assert_eq!(checked_out_data, *export_data); - - tmp_dir.close().unwrap(); - snapshot_dir.close().unwrap() -} - -// Mirrors `src/lib_riscv/pvm/test/test_storage.ml` -#[test] -fn test_pvm_storage() { - let tmp_dir = tempfile::tempdir().unwrap(); - let empty = NodePvm::empty(); - let mut repo = PvmStorage::load(tmp_dir.path()).unwrap(); - let id = repo.commit(&empty).unwrap(); - let checked_out_empty = repo.checkout(&id).unwrap(); - assert_eq!(empty, checked_out_empty); - let id2 = repo.commit(&empty).unwrap(); - assert_eq!(id, id2); - repo.close() -} diff --git a/src/riscv/lib/tests/test_regression.rs b/src/riscv/lib/tests/test_regression.rs deleted file mode 100644 index 1f83c1693f654e8b0109b2034bb084b9ffb68a67..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/test_regression.rs +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::fs; -use std::io::Write; -use std::ops::Bound; -use std::path::Path; -use std::path::PathBuf; - -use octez_riscv::machine_state::DefaultCacheLayouts; -use octez_riscv::machine_state::block_cache::block::Block; -use octez_riscv::machine_state::block_cache::block::Interpreted; -use octez_riscv::machine_state::block_cache::block::InterpretedBlockBuilder; -use octez_riscv::machine_state::block_cache::block::Jitted; -use octez_riscv::machine_state::block_cache::block::OutlineCompiler; -use octez_riscv::machine_state::memory::M64M; -use octez_riscv::pvm::PvmHooks; -use octez_riscv::state_backend::owned_backend::Owned; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; -use octez_riscv::stepper::pvm::PvmStepper; -use tezos_smart_rollup_utils::inbox::InboxBuilder; - -fn capture_debug_log(mint: &mut goldenfile::Mint) -> PvmHooks<'_> { - let mut log_capture = mint.new_goldenfile("log").unwrap(); - let hooks = PvmHooks::new(move |c| log_capture.write_all(&[c]).unwrap()); - hooks -} - -#[test] -fn regression_frozen_jstz() { - test_regression( - "tests/expected/jstz", - "../assets/jstz", - "../assets/regression-inbox.json", - true, - ) -} - -#[test] -fn regression_frozen_dummy_kernel() { - test_regression( - "tests/expected/dummy", - "../assets/riscv-dummy.elf", - "../assets/dummy-kernel-inbox.json", - true, - ) -} - -#[test] -fn regression_dummy_kernel() { - test_regression( - "tests/expected/dummy-volatile", - "../riscv-dummy.elf", - "../assets/dummy-kernel-inbox.json", - false, - ) -} - -fn test_regression( - golden_dir: impl AsRef, - kernel_path: impl AsRef, - inbox_path: impl AsRef, - capture_volatile_properties: bool, -) { - test_regression_for_block::>( - InterpretedBlockBuilder, - &golden_dir, - &kernel_path, - &inbox_path, - capture_volatile_properties, - ); - - // This needs to run *after* the previous *interpreted* test. Otherwise, we run into trouble when - // checking and updating the golden files. - test_regression_for_block::>( - ( - OutlineCompiler::::default(), - InterpretedBlockBuilder, - ), - &golden_dir, - &kernel_path, - &inbox_path, - capture_volatile_properties, - ); -} - -fn test_regression_for_block>( - block_builder: B::BlockBuilder, - golden_dir: impl AsRef, - kernel_path: impl AsRef, - inbox_path: impl AsRef, - capture_volatile_properties: bool, -) { - let mut mint = goldenfile::Mint::new(golden_dir); - - let (result, initial_hash, final_hash) = { - // We need to read the kernel in any case - let program = fs::read(kernel_path) - .expect("Failed to read kernel from disk. Try running `make build`."); - let initrd = None::>; - - let inbox = { - let mut inbox = InboxBuilder::new(); - inbox.load_from_file(inbox_path).unwrap(); - inbox.build() - }; - - let hooks = capture_debug_log(&mut mint); - - const ROLLUP_ADDRESS: [u8; 20] = [ - 244, 228, 124, 179, 196, 58, 104, 176, 212, 142, 48, 148, 9, 44, 164, 45, 113, 58, 221, - 181, - ]; - const ORIGINATION_LEVEL: u32 = 1; - - let mut stepper = PvmStepper::<'_, M64M, DefaultCacheLayouts, Owned, B>::new( - &program, - initrd.as_deref(), - inbox, - hooks, - ROLLUP_ADDRESS, - ORIGINATION_LEVEL, - Some(PathBuf::from("../assets/preimages").into_boxed_path()), - block_builder, - ) - .unwrap(); - - let initial_hash = stepper.hash(); - - let result = stepper.step_max(Bound::Unbounded); - let final_hash = stepper.hash(); - - (result, initial_hash, final_hash) - }; - - assert!( - matches!(result, StepperStatus::Exited { .. }), - "Unexpected result: {result:?}" - ); - - if capture_volatile_properties { - let mut initial_hash_capture = mint.new_goldenfile("state_hash_initial").unwrap(); - writeln!(initial_hash_capture, "{initial_hash:?}").unwrap(); - - let mut result_capture = mint.new_goldenfile("result").unwrap(); - writeln!(result_capture, "{result:#?}").unwrap(); - - let mut final_hash_capture = mint.new_goldenfile("state_hash_final").unwrap(); - writeln!(final_hash_capture, "{final_hash:?}").unwrap(); - } -} diff --git a/src/riscv/lib/tests/test_suite_parser.rs b/src/riscv/lib/tests/test_suite_parser.rs deleted file mode 100644 index 9f2089e61d9bc6ec3573098a32ebb4bb2c88a0bf..0000000000000000000000000000000000000000 --- a/src/riscv/lib/tests/test_suite_parser.rs +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use core::panic; -use std::fs::DirEntry; -use std::process::Command; - -use lazy_static::lazy_static; -use octez_riscv::parser::instruction::Instr; -use octez_riscv::parser::parse_block; - -fn compute_offset(src: &str, dest: &str) -> i64 { - i64::from_str_radix(dest, 16).unwrap() - i64::from_str_radix(src, 16).unwrap() -} - -/// For most instructions, the objdump output can be directly compared with -/// the pretty-printed parsed instructions. For branching instructions, objdump -/// prints the computed branching address, rather than the offset, which is what -/// these instructions actually encode in their immediate field. We also replace CSR numeric -/// identifiers with their symbolic name equivalent. -fn transform_objdump_instr<'a>(address: &'a str, instr: &'a str, args: &'a str) -> String { - let op = instr.trim(); - let args = args.split(' ').next().unwrap().trim(); - match op { - "beq" | "bne" | "blt" | "bge" | "bltu" | "bgeu" => { - let mut args = args.split(','); - let rs1 = args.next().unwrap(); - let rs2 = args.next().unwrap(); - let branch_address = args.next().unwrap(); - let offset = compute_offset(address, branch_address); - format!("{} {},{},{}", op, rs1, rs2, offset) - } - "jal" => { - let mut args = args.split(','); - let rd = args.next().unwrap(); - let branch_address = args.next().unwrap(); - let offset = compute_offset(address, branch_address); - format!("{} {},{}", op, rd, offset) - } - "lr.w" | "lr.w.aq" | "lr.w.rl" | "lr.w.aqrl" | "lr.d" | "lr.d.aq" | "lr.d.rl" - | "lr.d.aqrl" => { - let args = args.replace(",(", ",zero,("); - format!("{} {}", op, args) - } - "c.j" => { - let offset = compute_offset(address, args); - format!("{} {}", op, offset) - } - "c.addi" => { - let mut args = args.split(','); - let rd_rs1 = args.next().unwrap(); - let imm = args.next().unwrap(); - match (rd_rs1, imm) { - // objdump seems to treat `c.nop` as a pseudoinstruction, but, - // unlike `nop`, the spec defines it as an instruction - ("zero", "0") => "c.nop".to_string(), - _ => format!("{} {},{}", op, rd_rs1, imm), - } - } - "c.beqz" | "c.bnez" => { - let mut args = args.split(','); - let rs1 = args.next().unwrap(); - let branch_address = args.next().unwrap(); - let offset = compute_offset(address, branch_address); - format!("{} {},{}", op, rs1, offset) - } - instr if instr.starts_with("csr") => { - let mut args = args.split(',').collect::>(); - - let name_replacement = try_blocks::try_block! { - let &second_arg = args.get(1)?; - - // Translate numeric CSR names to symbolic ones - match second_arg { - "0x740" => "mnscratch", - "0x741" => "mnepc", - "0x742" => "mncause", - "0x744" => "mnstatus", - _ => None?, - } - }; - - // Replace the second argument with the symbolic name if one was found - if let Some(name) = name_replacement { - args[1] = name; - } - - let args = args.join(","); - format!("{} {}", op, args) - } - _ => { - if args.is_empty() { - op.to_string() - } else { - format!("{} {}", op, args) - } - } - } -} - -lazy_static! { - static ref OBJDUMP_EXE: &'static str = { - // Iterate through the known executable names to find a suitable objdump. - for exe in ["riscv64-unknown-linux-musl-objdump", "riscv64-unknown-linux-gnu-objdump", "riscv64-linux-gnu-objdump", "riscv64-unknown-elf-objdump", "riscv64-elf-objdump", "objdump"] { - let Some(output) = Command::new(exe).arg("--version").output().ok() else { - continue; - }; - - let stdout = - std::str::from_utf8(output.stdout.as_slice()).expect("Invalid UTF-8 encoding"); - - if stdout.contains("GNU objdump") { - return exe; - } - } - - panic!("Could not find a GNU-style objdump executable") - }; -} - -const OBJDUMP_OPTS: [&str; 3] = ["--disassemble", "-M", "no-aliases"]; - -/// Disassemble a RISC-V binary using objdump and return a vector of tuples -/// consisting of the address of the instruction, the parsed instruction, and -/// the objdump output for the instruction. When `disassembled` is true, -/// `file_path` is expected to point to a file containing the output of -/// a previous objdump run. -fn objdump(file_path: &str, disassembled: bool) -> Vec<(String, Instr, String)> { - let contents = if disassembled { - std::fs::read_to_string(file_path).expect("Failed to read file") - } else { - let output = Command::new(*OBJDUMP_EXE) - .args(OBJDUMP_OPTS) - .arg(file_path) - .output() - .expect("Failed to run objdump"); - std::str::from_utf8(&output.stdout).unwrap().to_string() - }; - - let mut instructions = Vec::new(); - - for line in contents.lines().skip(4) { - let Some((address, rest)) = line.split_once(':') else { - continue; - }; - let Some((encoded, raw_objdump_instr)) = rest.trim().split_once(' ') else { - continue; - }; - let raw_objdump_instr = raw_objdump_instr.trim(); - let (instr, args) = raw_objdump_instr - .split_once('\t') - .unwrap_or((raw_objdump_instr, "")); - let address = address.trim(); - let parsed_instr = parse_encoded(encoded); - let objdump_instr = transform_objdump_instr(address, instr, args); - instructions.push((address.to_string(), parsed_instr, objdump_instr)) - } - assert!( - !instructions.is_empty(), - "Could not extract any instructions from {}", - file_path - ); - instructions -} - -/// Parse one encoded instruction -fn parse_encoded(encoded: &str) -> Instr { - let mut bytes = hex::decode(encoded).unwrap(); - bytes.reverse(); - parse_block(&bytes)[0] -} - -/// List of uncompressed instructions that have a HINT encoding associated. Compressed instructions -/// are not included as there are no HINT encodings for them in the objdump. -const HINT_INSTR_LIST: [&str; 35] = [ - "lui", "auipc", "addi", "andi", "ori", "xori", "add", "sub", "and", "or", "xor", "sll", "srl", - "sra", "slti", "sltiu", "slli", "srli", "srai", "slt", "sltu", "addiw", "addw", "subw", "sllw", - "srlw", "sraw", "slti", "sltiu", "slli", "srli", "srai", "slliw", "srliw", "sraiw", -]; - -fn check_hint_instr(objdump_instr: String) { - // objdump will decode a HINT instruction as ` zero, ...`. In order to check that it is - // a valid HINT, we ensure the opcode is one containing HINTs and that the first argument (rd) - // is zero. - let mut opcode_rd = objdump_instr.split(',').next().unwrap().split(' '); - let opcode = opcode_rd.next().unwrap(); - let rd = opcode_rd.next().unwrap(); - if HINT_INSTR_LIST.contains(&opcode) && rd == "zero" { - return; - } - panic!("Instruction incorrectly parsed as HINT: {}", objdump_instr); -} - -fn check_instructions(fname: &str, instructions: Vec<(String, Instr, String)>) { - use octez_riscv::parser::instruction::InstrCacheable; - for (address, parsed_instr, objdump_instr) in instructions { - if let Instr::Cacheable(InstrCacheable::Hint { instr: _ }) = parsed_instr { - check_hint_instr(objdump_instr); - continue; - } - let printed_instr = parsed_instr.to_string(); - if objdump_instr.starts_with('.') || objdump_instr == "unimp" || objdump_instr == "c.unimp" - { - continue; - } - assert_eq!( - printed_instr, objdump_instr, - "{} at address {}", - fname, address - ); - } -} - -fn should_skip_rv_test(file: &DirEntry) -> bool { - let name = file.file_name(); - let name_str = name.to_str().unwrap(); - - // Conditions to exclude the test - let not_rv = !name_str.starts_with("rv"); - let dir = file.path().is_dir(); - // Skip zfh tests: extension not implemented - let zfh = name_str.starts_with("rv64uzfh"); - not_rv || dir || zfh -} - -#[test] -fn parser_riscv_test_suite() { - let tests_dir = "../assets/generated"; - - for f in std::fs::read_dir(tests_dir).unwrap() { - let file = f.unwrap(); - - if should_skip_rv_test(&file) { - // Skip directories, zfh tests, extraneous files - continue; - } - let path = file.path(); - let fname = path.to_string_lossy(); - let instructions = objdump(&fname, false); - check_instructions(&fname, instructions) - } -} - -#[ignore] -#[test] -fn parser_riscv_jstz() { - // Test currently disabled in CI because running objdump on the jstz kernel - // is slow (~15 min) and the resulting file is very large (~110MB). - // To run locally, generate a dump on the compiled jstz kernel: - // `objdump -d -M no-aliases jstz/target/riscv64gc-unknown-linux-musl/release/jstz > tests/jstz_objdump` - let fname = "../tests/jstz_objdump"; - let instructions = objdump(fname, true); - check_instructions(fname, instructions) -} diff --git a/src/riscv/sandbox/Cargo.toml b/src/riscv/sandbox/Cargo.toml deleted file mode 100644 index 9d74729b00b35c4c3a52032e275ec640a16d5fad..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "riscv-sandbox" -version = "0.0.0" -edition = "2024" - -[[bin]] -name = "riscv-sandbox" -path = "src/main.rs" - -[lints] -workspace = true - -[dependencies] -cfg-if.workspace = true -clap.workspace = true -comfy-table.workspace = true -enum-tag.workspace = true -gdbstub.workspace = true -gdbstub_arch.workspace = true -itertools.workspace = true -meansd.workspace = true -numfmt.workspace = true -quanta.workspace = true -serde.workspace = true -serde_json.workspace = true -tezos-smart-rollup-encoding.workspace = true -tezos-smart-rollup.workspace = true - -[dependencies.octez-riscv] -path = "../lib" - -[dependencies.tracing-subscriber] -workspace = true -optional = true - -[features] -default = [] -disable-jit = [] -inline-jit = [] -metrics = ["octez-riscv/metrics"] -log = ["octez-riscv/log", "dep:tracing-subscriber"] diff --git a/src/riscv/sandbox/src/cli.rs b/src/riscv/sandbox/src/cli.rs deleted file mode 100644 index b7da60d629feb315cae97bfb7ef846d0d7cf2a42..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/cli.rs +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::path::Path; - -use clap::Parser; -use clap::Subcommand; -use clap::ValueEnum; - -#[derive(Debug, Clone, Subcommand)] -pub enum Mode { - /// Run a program using the RISC-V interpreter - Run(RunOptions), - /// Benchmark a program - Bench(BenchOptions), - /// Launch a gdb server, for debugging the given program. - #[clap(alias = "debug")] - GdbServer(GdbServerOptions), -} - -#[derive(Clone, ValueEnum, Debug)] -pub enum ExitMode { - User, - Supervisor, - Machine, -} - -#[derive(Debug, Clone, Parser)] -pub struct Cli { - /// Configure the log level - #[cfg(feature = "log")] - #[arg(long, default_value = "warn")] - pub log_level: octez_riscv::log::tracing_internal::Level, - - /// Log to a JSON file - #[cfg(feature = "log")] - #[arg(long)] - pub log_json_file: Option>, - - #[command(subcommand)] - pub command: Mode, -} - -#[derive(Debug, Clone, Parser)] -pub struct RunOptions { - #[command(flatten)] - pub common: CommonOptions, - - /// Path to the input ELF executable - #[arg(long, short)] - pub input: Box, - - /// Path to the initrd - #[arg(long)] - pub initrd: Option>, - - /// Print the number of steps taken by `run`. - #[arg(long, default_value_t = false)] - pub print_steps: bool, - - /// Options for controlling the output of recorded metrics. - #[cfg(feature = "metrics")] - #[command(flatten)] - pub metrics: MetricsOptions, -} - -#[derive(Debug, Clone, Parser)] -pub struct GdbServerOptions { - /// Path to the input ELF executable - #[arg(long, short)] - pub input: Box, - - /// Path to the initrd - #[arg(long)] - pub initrd: Option>, - - /// Port to listen on for gdb client (on `localhost:`). - #[arg(long, short)] - pub port: u16, - - #[command(flatten)] - pub preimage: PreimageOptions, - - #[command(flatten)] - pub inbox: InboxOptions, -} - -#[derive(Clone, ValueEnum, Debug)] -pub enum BenchMode { - Simple, - Fine, -} - -#[derive(Debug, Clone, Parser)] -pub struct BenchOptions { - #[command(subcommand)] - pub bench_command: BenchSubcommand, -} - -#[derive(Debug, Clone, Subcommand)] -pub enum BenchSubcommand { - /// Runs a benchmark (alias for default bench command) - Run(BenchRunOptions), - /// Loads and compares runs from given json files - Compare(BenchCompareOptions), - /// Runs a parser benchmark over the given RISC-V binaries - Parser(BenchParserRunOptions), -} - -#[derive(Debug, Clone, Parser)] -pub struct BenchRunOptions { - /// Type of benchmark - #[arg(long)] - pub mode: BenchMode, - - /// How often to repeat the benchmark run - #[arg(long = "iter", default_value_t = 1)] - pub repeat: usize, - - /// Name of the file to save the benchmark stats - #[arg( - long, - default_value = "benchmark.json", - value_parser = validate_output, - )] - pub output: String, - - /// Pretty print the benchmark results in a table - #[arg(long)] - pub pretty: bool, - - #[command(flatten)] - pub common: CommonOptions, - - /// Paths to the input ELF executable - #[arg(long, short, num_args=1..)] - pub inputs: Vec>, - - /// Path to the initrd - #[arg(long)] - pub initrd: Option>, - - #[command(flatten)] - pub sort_args: TableSortArgs, -} - -#[derive(Debug, Clone, Parser)] -pub struct BenchParserRunOptions { - /// How often to repeat the benchmark run - #[arg(long = "iter", default_value_t = 1)] - pub repeat: usize, - - /// Paths to the input ELF executable - #[arg(num_args=1..)] - pub inputs: Vec>, -} - -/// Validator for `--output ` option -pub fn validate_output(output: &str) -> Result { - if output.is_empty() { - Err("Output filename can not be empty") - } else { - Ok(output.to_string()) - } -} - -#[derive(Debug, Clone, ValueEnum)] -pub enum SortRuns { - /// Keep the input order for runs - Input, - /// Order runs alphabetically - Alphabetic, - /// Order instructions / s speed - Speed, - /// Order by total count of the instruction - Total, -} - -#[derive(Debug, Clone, ValueEnum)] -pub enum SortInstr { - /// Order instruction groups by the maximum average - MaxAvg, - /// Order sum of total count of instructions in the whole group - TotalCount, - /// Order instruction groups alphabetically by instruction name - Alphabetic, -} - -#[derive(Debug, Clone, Parser)] -pub struct BenchCompareOptions { - /// List of .json files for comparison - #[arg(long, num_args = 1..)] - comp_file: Vec>, - - #[command(flatten)] - pub sort_args: TableSortArgs, -} - -impl BenchCompareOptions { - pub fn comparison_files(&self) -> Result, Box> { - let x = self - .comp_file - .iter() - .map(|p| { - if let Some(p) = p.to_str() { - Ok(p.to_string()) - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "Invalid path to benchmark file", - ))) - } - }) - // If any of the items is `Err`, it will fail with `Err` - .collect::, _>>()?; - - Ok(x) - } -} - -#[derive(Debug, Clone, Parser)] -pub struct TableSortArgs { - /// Order of benchmarks in the context of a single instruction - #[arg(long, value_enum, default_value_t = SortRuns::Input)] - pub sort_runs: SortRuns, - - /// Order of instruction data groups - #[arg(long, value_enum, default_value_t = SortInstr::MaxAvg)] - pub sort_instr: SortInstr, -} - -#[derive(Debug, Clone, Parser)] -pub struct PreimageOptions { - /// Directory containing preimage files for reveal requests - #[arg(long)] - pub preimages_dir: Option>, -} - -#[derive(Debug, Clone, Parser)] -pub struct CommonOptions { - #[arg(short = 'm', long, value_enum, default_value_t = ExitMode::User)] - pub posix_exit_mode: ExitMode, - - #[arg(long)] - pub max_steps: Option, - - #[command(flatten)] - pub inbox: InboxOptions, - - #[arg(long, default_value_t = false)] - pub timings: bool, - - #[command(flatten)] - pub preimage: PreimageOptions, -} - -#[derive(Debug, Clone, Parser)] -pub struct InboxOptions { - /// Keep going after the inbox has been drained. - #[arg(short, long)] - pub keep_going: bool, - - /// Rollup address - #[arg(short, long, default_value = "sr1UNDWPUYVeomgG15wn5jSw689EJ4RNnVQa")] - pub address: String, - - /// Rollup origination level - #[arg(short = 'l', long, default_value_t = 0)] - pub origination_level: u32, - - /// Path to the file containing inbox messages - #[arg(long = "inbox-file")] - pub file: Option>, -} - -#[cfg(feature = "metrics")] -#[derive(Debug, Clone, Parser)] -pub struct MetricsOptions { - /// File to write the recorded block metrics. - #[arg(long)] - pub block_metrics_file: Box, - - #[arg(long)] - /// Whether to include instructions that are supported by the JIT. - pub exclude_supported_instructions: bool, -} - -/// Parse the command-line arguments. -pub fn parse() -> Cli { - Cli::parse() -} diff --git a/src/riscv/sandbox/src/commands.rs b/src/riscv/sandbox/src/commands.rs deleted file mode 100644 index ebe2005b976bbe8ce44c09ddbe7f1cf3137806c2..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands.rs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -pub mod bench; -mod gdb; -pub mod run; - -pub use bench::bench; -pub use gdb::gdb_server; -pub use run::run; diff --git a/src/riscv/sandbox/src/commands/bench.rs b/src/riscv/sandbox/src/commands/bench.rs deleted file mode 100644 index d68baec7988a32353ab255a93bd894f31f4d8f8f..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench.rs +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::fs; -use std::fs::File; - -use crate::cli; -use crate::cli::BenchOptions; -use crate::cli::BenchRunOptions; -pub(crate) use crate::commands::bench::stats::BenchStats; -pub(crate) use crate::commands::bench::stats::NamedStats; -use crate::table; - -pub mod commands; -mod data; -mod stats; - -fn load_from_file(file: &str) -> Result> { - let reader = fs::File::open(file)?; - let stats: BenchStats = serde_json::from_reader(reader)?; - Ok(stats) -} - -fn save_to_file(stats: &BenchStats, opts: &BenchRunOptions) -> Result<(), Box> { - let mut file = File::create(&opts.output)?; - serde_json::to_writer(&mut file, &stats)?; - Ok(()) -} - -fn show_results(stats: &BenchStats, opts: &BenchRunOptions) { - match opts.pretty { - false => println!("{stats}"), - true => { - let table = - table::table_from_stats(&opts.sort_args, [(stats, &opts.output)].as_slice()); - println!("{table}") - } - } -} - -pub fn bench(opts: BenchOptions) -> Result<(), Box> { - use commands::compare; - use commands::parser; - use commands::run; - match opts.bench_command { - cli::BenchSubcommand::Run(bench_run_opts) => run::run(bench_run_opts), - cli::BenchSubcommand::Compare(bench_run_opts) => compare::compare(bench_run_opts), - cli::BenchSubcommand::Parser(parser_run_opts) => parser::bench(parser_run_opts), - } -} diff --git a/src/riscv/sandbox/src/commands/bench/commands.rs b/src/riscv/sandbox/src/commands/bench/commands.rs deleted file mode 100644 index 715606fd5c5da60fd9456d5ddea49092a1086b94..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/commands.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -pub mod compare; -pub mod parser; -pub mod run; diff --git a/src/riscv/sandbox/src/commands/bench/commands/compare.rs b/src/riscv/sandbox/src/commands/bench/commands/compare.rs deleted file mode 100644 index cad4b0f4f7462231638f528d61508f2a55712395..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/commands/compare.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::error::Error; - -use itertools::Itertools; - -use crate::cli::BenchCompareOptions; -use crate::commands::bench::load_from_file; -use crate::table; - -pub fn compare(opts: BenchCompareOptions) -> Result<(), Box> { - let files = opts.comparison_files()?; - - let data = files - .into_iter() - .map(|f| load_from_file(&f).map(|s| (s, f))) - .collect::, _>>()?; - - let d = data.iter().map(|(d, f)| (d, f)).collect_vec(); - let t = table::table_from_stats(&opts.sort_args, d.as_slice()); - println!("{t}"); - Ok(()) -} diff --git a/src/riscv/sandbox/src/commands/bench/commands/parser.rs b/src/riscv/sandbox/src/commands/bench/commands/parser.rs deleted file mode 100644 index 622686eeb70699ce4a721c8c4eb7228cc685b785..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/commands/parser.rs +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::ops::Div; - -use octez_riscv::parser::parse_block; - -use crate::cli::BenchParserRunOptions; - -pub fn bench(opts: BenchParserRunOptions) -> Result<(), Box> { - let runs = opts.repeat; - let files = opts.inputs; - - let mut formatter = numfmt::Formatter::new() - .precision(numfmt::Precision::Decimals(2)) - .separator(',') - .unwrap(); - - for f in files.iter() { - let kernel = std::fs::read(f)?; - - let average = (0..runs) - .map(|_| do_run(&kernel)) - .sum::() - .div(runs as u32); - - let num_instr = parse_block(&kernel).len(); - - let speed = num_instr as f64 / average.as_secs_f64(); - - let speed = formatter.fmt2(speed); - - println!( - r#"{} -- Bytes parsed: {} -- Average: {average:.2?} -- Throughput: {speed} Instructions/s -- Runs: {runs}"#, - f.display(), - kernel.len() - ); - } - - Ok(()) -} - -fn do_run(bytes: &[u8]) -> std::time::Duration { - let now = quanta::Instant::now(); - let _ = parse_block(bytes); - now.elapsed() -} diff --git a/src/riscv/sandbox/src/commands/bench/commands/run.rs b/src/riscv/sandbox/src/commands/bench/commands/run.rs deleted file mode 100644 index 722595d62c8841f20571ef8242e99bfd7f69eb50..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/commands/run.rs +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::collections::HashSet; -use std::error::Error; -use std::fs; -use std::ops::Bound; -use std::path::Path; -use std::path::PathBuf; - -use enum_tag::EnumTag; -use octez_riscv::machine_state::memory::Address; -use octez_riscv::machine_state::memory::Memory; -use octez_riscv::parser::instruction::Instr; -use octez_riscv::parser::parse; -use octez_riscv::state_backend::ManagerRead; -use octez_riscv::stepper::StepResult; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; - -use crate::cli::BenchMode; -use crate::cli::BenchRunOptions; -use crate::commands::bench::BenchStats; -use crate::commands::bench::data::BenchData; -use crate::commands::bench::data::FineBenchData; -use crate::commands::bench::data::InstrGetError; -use crate::commands::bench::data::InstrType; -use crate::commands::bench::data::SimpleBenchData; -use crate::commands::bench::save_to_file; -use crate::commands::bench::show_results; -use crate::commands::run::BlockImpl; -use crate::commands::run::make_pvm_stepper; -use crate::format_status; - -/// Helper function to look in the [`Stepper`] to peek for the current [`Instr`] -/// Assumes the program counter will be a multiple of 2. -fn get_current_instr(stepper: &S) -> Result -where - S::Manager: ManagerRead, -{ - let machine_state = stepper.machine_state(); - let get_half_instr = |raw_pc: Address| -> Result { - machine_state - .main_memory - .read(raw_pc) - .or(Err(InstrGetError::Parse)) - }; - let pc = machine_state.hart.pc.read(); - let first = get_half_instr(pc)?; - let second = || get_half_instr(pc + 2); - parse(first, second) -} - -/// Composes "in time" two [`StepperStatus`] one after another, -/// to obtain the equivalent final [`StepperStatus`] -fn compose(current_state: StepperStatus, following_result: StepperStatus) -> StepperStatus { - use StepperStatus::*; - match current_state { - Exited { .. } => current_state, - Errored { .. } => current_state, - Running { steps: prev_steps } => match following_result { - Exited { - steps, - status, - success, - } => Exited { - success, - status, - steps: prev_steps + steps, - }, - Errored { - cause, - steps, - message, - } => Errored { - cause, - message, - steps: prev_steps + steps, - }, - Running { steps } => Running { - steps: prev_steps + steps, - }, - }, - } -} - -fn bench_fine(interpreter: &mut S, opts: &BenchRunOptions) -> BenchData { - let mut run_res = StepperStatus::default(); - let mut bench_data = FineBenchData::new(); - let bench_start = quanta::Instant::now(); - - for _step in 0..opts.common.max_steps.unwrap_or(usize::MAX) { - let instr = match get_current_instr(interpreter) { - Ok(instr) => InstrType::Instr(instr.tag()), - Err(err) => InstrType::FetchErr(err), - }; - - let start = quanta::Instant::now(); - let step_res = interpreter.step_max(Bound::Included(1)); - let step_duration = start.elapsed(); - - bench_data.add_instr(instr, step_duration); - - run_res = compose(run_res.to_stepper_status(), step_res.to_stepper_status()); - match run_res { - StepperStatus::Exited { .. } => break, - StepperStatus::Errored { .. } => break, - StepperStatus::Running { .. } => (), - } - } - let bench_duration = bench_start.elapsed(); - - BenchData::from_fine(bench_data, bench_duration, run_res) -} - -/// A single run of the given `interpreter`. -/// Provides basic benchmark data and interpreter result. -fn bench_simple(interpreter: &mut S, opts: &BenchRunOptions) -> BenchData { - let start = quanta::Instant::now(); - let step_bound = opts - .common - .max_steps - .map(Bound::Included) - .unwrap_or(Bound::Unbounded); - let res = interpreter.step_max(step_bound); - let duration = start.elapsed(); - - use StepperStatus::*; - let steps = match res.to_stepper_status() { - Exited { steps, .. } => steps, - Running { steps } => steps, - Errored { steps, .. } => steps, - }; - let data = SimpleBenchData::new(duration, steps); - - BenchData::from_simple(data, res.to_stepper_status()) -} - -fn bench_iteration(path: &Path, opts: &BenchRunOptions) -> Result> { - let program = std::fs::read(path)?; - let initrd = opts.initrd.as_ref().map(fs::read).transpose()?; - - let mut stepper = make_pvm_stepper::( - program.as_slice(), - initrd.as_deref(), - &opts.common, - Default::default(), - )?; - - Ok(match opts.mode { - BenchMode::Simple => bench_simple(&mut stepper, opts), - BenchMode::Fine => bench_fine(&mut stepper, opts), - }) -} - -fn transform_folders(inputs: &[Box]) -> Result, Box> { - let mut binary_paths = vec![]; - for path in inputs { - if fs::metadata(path)?.is_dir() { - for sub_path in fs::read_dir(path)? { - let sub_path = sub_path?.path(); - if fs::metadata(&sub_path)?.is_file() { - binary_paths.push(sub_path) - } - } - } else { - binary_paths.push(path.to_path_buf()) - } - } - Ok(binary_paths) -} - -pub fn run(opts: BenchRunOptions) -> Result<(), Box> { - let paths = transform_folders(&opts.inputs)?; - let mut stats = paths - .into_iter() - .filter_map(|path| run_binary(&path, &opts).ok()) - .reduce(|acc, e| e.combine(acc)) - .ok_or("Could not combine benchmark results".to_string())?; - stats.normalize_instr_data(); - save_to_file(&stats, &opts)?; - show_results(&stats, &opts); - Ok(()) -} - -fn run_binary(path: &Path, opts: &BenchRunOptions) -> Result> { - let get_warning_from_iteration = |iteration: &BenchData| match &iteration.run_result { - StepperStatus::Exited { success: true, .. } => None, - result => Some(format_status(result)), - }; - - let mut warnings = HashSet::::new(); - let mut consider_iteration = |iteration: &BenchData| { - if let Some(w) = get_warning_from_iteration(iteration) { - warnings.insert(w); - } - }; - - let stats = match opts.repeat { - 0 | 1 => { - let iteration = bench_iteration(path, opts)?; - consider_iteration(&iteration); - BenchStats::from_data(iteration)? - } - iterations => { - let mut data_list = vec![]; - for _ in 0..iterations { - let iteration = bench_iteration(path, opts)?; - consider_iteration(&iteration); - data_list.push(iteration); - } - BenchStats::from_data_list(data_list)? - } - }; - if !warnings.is_empty() { - eprintln!("Warning: Binary {:?} exited with:", path); - for w in warnings { - eprintln!(" - {}", w); - } - } - Ok(stats) -} diff --git a/src/riscv/sandbox/src/commands/bench/data.rs b/src/riscv/sandbox/src/commands/bench/data.rs deleted file mode 100644 index 0a47bdc701e05d8339cb8cdf449710addd52a6c1..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/data.rs +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use core::fmt; -use std::collections::HashMap; -use std::fmt::Debug; -use std::fmt::Display; -use std::time::Duration; - -use enum_tag::EnumTag; -use octez_riscv::parser::instruction::Instr; -use octez_riscv::stepper::StepperStatus; - -pub(super) type InstructionData = HashMap>; - -/// Underlying data for a benchmark run. -pub(super) struct BenchData { - pub(super) duration: Duration, - pub(super) steps: usize, - pub(super) instr_count: Option, - pub(super) run_result: StepperStatus, -} - -impl BenchData { - pub fn from_simple(data: SimpleBenchData, run_result: StepperStatus) -> Self { - BenchData { - duration: data.duration, - steps: data.steps, - instr_count: None, - run_result, - } - } - - /// [`FineBenchData`] contains only the instruction-level data. - /// The `total_duration` length should be given to be sanity checked against - /// the sum of all instruction durations. - pub fn from_fine(data: FineBenchData, duration: Duration, run_result: StepperStatus) -> Self { - let steps = data.instr_list.values().map(|d| d.len()).sum(); - - BenchData { - duration, - steps, - instr_count: Some(data.instr_list), - run_result, - } - } -} - -pub(super) struct SimpleBenchData { - duration: Duration, - steps: usize, -} - -impl SimpleBenchData { - pub fn new(duration: Duration, steps: usize) -> Self { - SimpleBenchData { duration, steps } - } -} - -impl Display for SimpleBenchData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let f_duration = format!("Bench Duration: {:?}", self.duration); - let f_steps = format!("Steps: {}", self.steps); - write!(f, "Simple bench data:\n {}\n {}", f_duration, f_steps) - } -} - -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub enum InstrGetError { - Parse, -} - -type InstrTag = ::Tag; - -/// Instruction classification type for grouping instruction for further stats. -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub enum InstrType { - Instr(InstrTag), - FetchErr(InstrGetError), -} - -impl fmt::Display for InstrType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InstrType::FetchErr(err) => write!(f, "FetchErr: {err:?}"), - InstrType::Instr(tag) => write!(f, "Instruction: {tag:?}"), - } - } -} - -/// Holds the instruction-level data of a benchmark run. -pub(super) struct FineBenchData { - instr_list: HashMap>, -} - -impl FineBenchData { - pub fn new() -> Self { - FineBenchData { - instr_list: HashMap::new(), - } - } - - pub fn add_instr(&mut self, instr: InstrType, duration: Duration) { - self.instr_list.entry(instr).or_default().push(duration); - } -} diff --git a/src/riscv/sandbox/src/commands/bench/stats.rs b/src/riscv/sandbox/src/commands/bench/stats.rs deleted file mode 100644 index 7de1ed3d4c192fa2ea0c8c166c6dbc527f8891c3..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/bench/stats.rs +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use core::fmt; -use std::time::Duration; - -use itertools::Itertools; -use meansd::MeanSD; -use serde::Deserialize; -use serde::Serialize; - -use super::data::BenchData; -use super::data::InstructionData; -use crate::format_status; -use crate::table::utils::thousand_format; - -/// Serializable data for instruction-level statistics -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct NamedStats { - pub name: String, - pub count: usize, - pub total: Duration, - pub average: Duration, - pub median: Option, - pub stddev: Option, -} - -impl NamedStats { - /// Returns [`None`] if either the array is empty or `count` overflows [`u32`] - pub fn from_sorted_times(times: &Vec, name: String) -> Result { - let err = format!("Could not generate {name} stats from array:\n{times:?}\nIs it empty?"); - let count = times.len(); - let total: Duration = times.iter().sum(); - let average = total - .checked_div(count.try_into().map_err(|_| &err)?) - .ok_or(&err)?; - let median = *times.get(count / 2).ok_or(&err)?; - let mut sd = MeanSD::default(); - times.iter().for_each(|t| sd.update(t.as_nanos() as f64)); - let stddev = Duration::from_nanos(sd.sstdev().round() as u64); - - Ok(Self { - name, - count, - total, - average, - median: Some(median), - stddev: Some(stddev), - }) - } - - /// Used to combine statistics from two related statistics measuring the same metric. - /// Median and Stddev can not be trivially calculated by just knowing what is in [`NamedStats`] - pub fn aggregate(&mut self, other: &NamedStats, name: &str) -> Self { - Self { - name: name.to_string(), - average: (self.total + other.total).div_f64((self.count + other.count) as f64), - count: self.count + other.count, - total: self.total + other.total, - median: None, - stddev: None, - } - } -} - -impl fmt::Display for NamedStats { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let tag_f = format!("Name: {:?}", self.name); - let cnt_f = format!("Count: {:?}", self.count); - let total_f = format!("Total: {:?}", self.total); - let avg_f = format!("Avg: {:?}", self.average); - let med_f = format!("Median: {:?}", self.median); - let stdev_f = format!("Stddev: {:?}", self.stddev); - writeln!( - f, - "{tag_f}\n{cnt_f}\n{total_f}\n{avg_f}\n{med_f}\n{stdev_f}" - ) - } -} - -/// Serializable stats for a benchmark run. -#[derive(Serialize, Deserialize)] -pub struct BenchStats { - pub bench_duration_stats: NamedStats, - pub total_steps: usize, - pub instr_stats: Option>, - pub run_result: String, -} - -impl BenchStats { - fn compute_instr_stats( - instr_data: impl Iterator, - ) -> Result>, String> { - let mut instr_stats: Vec = instr_data - .into_iter() - .flatten() - .into_group_map() - .into_iter() - .map(|(tag, times)| { - let mut times: Vec = times.into_iter().flatten().collect(); - times.sort(); - NamedStats::from_sorted_times(×, format!("{tag}")) - }) - // If one element is [Err], then collect will return [Err] - .collect::>()?; - - let instr_stats = if instr_stats.is_empty() { - None - } else { - instr_stats.sort_by(|a, b| b.count.cmp(&a.count)); - Some(instr_stats) - }; - - Ok(instr_stats) - } - - /// Fails with [`Err`] if for an instruction the corresponding - /// instruction's [`NamedStats`] can not be created. - pub(super) fn from_data(data: BenchData) -> Result { - let instr_stats = Self::compute_instr_stats(data.instr_count.into_iter())?; - let bench_duration_stats = - NamedStats::from_sorted_times(&vec![data.duration], "Bench duration".into())?; - - Ok(BenchStats { - bench_duration_stats, - total_steps: data.steps, - instr_stats, - run_result: format_status(&data.run_result), - }) - } - - /// Fails with [`Err`] if for an instruction the corresponding - /// instruction's [`NamedStats`] can not be created. - pub(super) fn from_data_list(data: Vec) -> Result { - let bench_times = data.iter().map(|bench| bench.duration).collect::>(); - let bench_duration_stats = - NamedStats::from_sorted_times(&bench_times, "Interpreter time".into())?; - let total_steps = data.iter().map(|i| i.steps).sum(); - - let it = data.into_iter().filter_map(|i| i.instr_count); - let instr_stats = Self::compute_instr_stats(it)?; - - Ok(BenchStats { - bench_duration_stats, - total_steps, - instr_stats, - run_result: "Multiple iterations results".to_string(), - }) - } - - /// If multiple binaries have been executed, this function - /// combines the stats for the same instruction coming from different binaries. - pub fn normalize_instr_data(&mut self) { - self.instr_stats = self - .instr_stats - .iter() - .flatten() - .into_group_map_by(|el| &el.name) - .into_values() - .map(|val_list| { - val_list - .iter() - .map(|&e| e.clone()) - .reduce(|mut acc, el| acc.aggregate(&el, &el.name)) - }) - .collect::>>(); - } - - /// Function used to combine [`BenchStats`] in case multiple binaries are executed. - pub fn combine(mut self, other: BenchStats) -> Self { - Self { - bench_duration_stats: self - .bench_duration_stats - .aggregate(&other.bench_duration_stats, "mixed runs"), - instr_stats: match self.instr_stats { - None => other.instr_stats, - Some(mut stats) => Some({ - stats.append(&mut other.instr_stats.unwrap_or_default()); - stats - }), - }, - run_result: "Mixed runs".to_string(), - total_steps: self.total_steps + other.total_steps, - } - } -} - -impl BenchStats { - pub fn instruction_duration(&self) -> Duration { - // The time taken for instructions to run, either the whole bench duration, - // or the added instruction times to account for overhead - match &self.instr_stats { - Some(counts) if !counts.is_empty() => counts.iter().map(|i| i.total).sum::(), - _ => self.bench_duration_stats.total, - } - } -} - -impl fmt::Display for BenchStats { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let instr_duration = self.instruction_duration(); - let instr_speed = - thousand_format(self.total_steps as f64 / instr_duration.as_secs_f64(), 2); - - writeln!(f, "Outcome: {}", self.run_result)?; - writeln!(f, "Total steps: {}", self.total_steps)?; - writeln!(f, "Speed: {} instr / s", instr_speed)?; - writeln!(f)?; - writeln!(f, " Bench stats:\n{}", self.bench_duration_stats)?; - - let stats = match &self.instr_stats { - None => "Not captured".into(), - Some(counts) => { - let intro = format!("Distinct instructions: {}", counts.len()); - - let bench_overhead = self.bench_duration_stats.total - instr_duration; - let per_instr_overhead = bench_overhead.div_f64(self.total_steps as f64); - let instr_duration_f = format!("Instr Duration: {instr_duration:?}"); - let overhead_f = format!( - "Overhead: {bench_overhead:?} (total) / {per_instr_overhead:?} (per instr.)" - ); - - let instr_data = counts - .iter() - .map(|stat| format!("{stat}---------------------\n")) - .fold("".to_string(), |a, b| a + &b); - let summary = format!("{intro}\n{instr_duration_f}\n{overhead_f}\n"); - let instr_data = format!(" Individual instructions:\n{instr_data}"); - format!("{summary}\n{instr_data}") - } - }; - write!(f, " Instruction statistics:\n{}", stats) - } -} diff --git a/src/riscv/sandbox/src/commands/gdb.rs b/src/riscv/sandbox/src/commands/gdb.rs deleted file mode 100644 index 4e3e479cfbc59e4d8f8880d57fce3e593de4172b..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/gdb.rs +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -//! Initial support for debugging RISC-V programs using gdb. -//! -//! This is achieved by implementing 'just enough' of the gdb server protocol to be useful. - -use std::collections::HashSet; -use std::error::Error; -use std::fs; -use std::io; -use std::marker::PhantomData; -use std::net::TcpListener; -use std::net::TcpStream; -use std::ops::Bound; - -use gdbstub::arch::Arch; -use gdbstub::common::Signal; -use gdbstub::conn::Connection; -use gdbstub::conn::ConnectionExt; -use gdbstub::stub::GdbStub; -use gdbstub::stub::SingleThreadStopReason; -use gdbstub::stub::run_blocking; -use gdbstub::target::Target; -use gdbstub::target::TargetError; -use gdbstub::target::TargetResult; -use gdbstub::target::ext::base::BaseOps; -use gdbstub::target::ext::base::singlethread::SingleThreadBase; -use gdbstub::target::ext::base::singlethread::SingleThreadResume; -use gdbstub::target::ext::base::singlethread::SingleThreadResumeOps; -use gdbstub::target::ext::base::singlethread::SingleThreadSingleStep; -use gdbstub::target::ext::base::singlethread::SingleThreadSingleStepOps; -use gdbstub::target::ext::breakpoints::Breakpoints; -use gdbstub::target::ext::breakpoints::BreakpointsOps; -use gdbstub::target::ext::breakpoints::SwBreakpoint; -use gdbstub::target::ext::breakpoints::SwBreakpointOps; -use gdbstub::target::ext::exec_file::ExecFile; -use gdbstub_arch::riscv::reg::RiscvCoreRegs; -use octez_riscv::machine_state::block_cache::block::InterpretedBlockBuilder; -use octez_riscv::machine_state::memory::BadMemoryAccess; -use octez_riscv::machine_state::memory::M1G; -use octez_riscv::machine_state::memory::Memory; -use octez_riscv::pvm::PvmHooks; -use octez_riscv::state_backend::FnManagerIdent; -use octez_riscv::stepper::StepResult; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; -use octez_riscv::stepper::pvm::PvmStepper; -use tezos_smart_rollup::utils::inbox::InboxBuilder; -use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; - -use crate::cli::GdbServerOptions; - -/// Run a gdb server that can be used for debugging RISC-V programs. -pub fn gdb_server(opts: GdbServerOptions) -> Result<(), Box> { - let fname = fs::canonicalize(opts.input).map_err(|_| "Invalid program path")?; - let fname = fname - .to_str() - .ok_or("File name cannot be converted to string")?; - - let program = fs::read(fname)?; - - let initrd = opts.initrd.as_ref().map(fs::read).transpose()?; - - let mut inbox = InboxBuilder::new(); - if let Some(inbox_file) = opts.inbox.file { - inbox.load_from_file(inbox_file)?; - } - let inbox = inbox.build(); - - let rollup_address = SmartRollupAddress::from_b58check(opts.inbox.address.as_str())?; - - let mut stepper = PvmStepper::::new( - program.as_slice(), - initrd.as_deref(), - inbox, - PvmHooks::default(), - rollup_address.into_hash().as_ref().try_into()?, - opts.inbox.origination_level, - opts.preimage.preimages_dir, - InterpretedBlockBuilder, - )?; - - // loop - let mut target = RiscvGdb { - fname, - stepper: &mut stepper, - breakpoints: HashSet::new(), - single_step: false, - }; - - let connection = wait_for_gdb_connection(opts.port)?; - let connection: Box> = Box::new(connection); - let debugger = GdbStub::new(connection); - - let disconnect = debugger.run_blocking::>(&mut target)?; - - eprintln!("{:?}", disconnect); - Ok(()) -} - -/// Blocks until a GDB client connects via TCP. -/// i.e: Running `target remote localhost:` from the GDB prompt. -fn wait_for_gdb_connection(port: u16) -> io::Result { - let sockaddr = format!("localhost:{}", port); - eprintln!("Waiting for a GDB connection on {:?}...", sockaddr); - let sock = TcpListener::bind(sockaddr)?; - let (stream, addr) = sock.accept()?; - - eprintln!("Debugger connected from {}", addr); - Ok(stream) -} - -struct RiscvGdb<'a, S: Stepper> { - fname: &'a str, - stepper: &'a mut S, - breakpoints: HashSet, - single_step: bool, -} - -impl<'a, S: Stepper> RiscvGdb<'a, S> { - fn run_and_check_for_incoming_data( - &mut self, - conn: &mut as run_blocking::BlockingEventLoop>::Connection, - ) -> RiscvGdbEvent { - let mut poll_incoming_data = || { - // borrow the connection from gdbstub to check for pending input - conn.peek().map(|b| b.is_some()).unwrap_or(true) - }; - - if self.single_step { - self.single_step = false; - return RiscvGdbEvent::DoneStep; - } - - loop { - match self - .stepper - .step_max(Bound::Included(1)) - .to_stepper_status() - { - StepperStatus::Running { .. } => { - if poll_incoming_data() { - return RiscvGdbEvent::IncomingData; - } - - if self.at_breakpoint() { - return RiscvGdbEvent::BreakpointHit; - } - } - _ => continue, - } - } - } - - fn at_breakpoint(&self) -> bool { - let pc = self.stepper.machine_state().hart.pc.read(); - self.breakpoints.contains(&pc) - } -} - -enum RiscvGdbEvent { - IncomingData, - BreakpointHit, - DoneStep, -} - -impl Target for RiscvGdb<'_, S> { - type Error = String; - type Arch = gdbstub_arch::riscv::Riscv64; - - #[inline(always)] - fn base_ops(&mut self) -> BaseOps { - BaseOps::SingleThread(self) - } - - // opt-in to support for setting/removing breakpoints - #[inline(always)] - fn support_breakpoints(&mut self) -> Option> { - Some(self) - } - - #[inline(always)] - fn support_exec_file( - &mut self, - ) -> Option> { - Some(self) - } -} - -impl SingleThreadBase for RiscvGdb<'_, S> { - fn read_registers(&mut self, regs: &mut RiscvCoreRegs) -> TargetResult<(), Self> { - let state = self.stepper.machine_state(); - regs.pc = state.hart.pc.read(); - - let array = state.hart.xregisters.struct_ref::(); - for i in 0..31 { - // first is x0 == zero, only x1..=x31 are set - regs.x[i + 1] = array.read(i); - } - Ok(()) - } - - fn write_registers( - &mut self, - _regs: &::Registers, - ) -> TargetResult<(), Self> { - unimplemented!() - } - - fn read_addrs(&mut self, start_addr: u64, data: &mut [u8]) -> TargetResult { - const DEFAULT: u8 = 14; - - let state = self.stepper.machine_state(); - - match state.main_memory.read_all(start_addr, data) { - Ok(()) => Ok(data.len()), - Err(BadMemoryAccess) => Err(TargetError::Errno(DEFAULT)), - } - } - - fn write_addrs(&mut self, _start_addr: u64, _data: &[u8]) -> TargetResult<(), Self> { - unimplemented!() - } - - #[inline(always)] - fn support_resume(&mut self) -> Option> { - Some(self) - } -} - -impl SingleThreadResume for RiscvGdb<'_, S> { - fn resume(&mut self, _signal: Option) -> Result<(), Self::Error> { - Ok(()) - } - - #[inline(always)] - fn support_single_step(&mut self) -> Option> { - Some(self) - } -} - -impl SingleThreadSingleStep for RiscvGdb<'_, S> { - fn step(&mut self, signal: Option) -> Result<(), Self::Error> { - self.single_step = true; - - let None = signal else { return Ok(()) }; - - // handle status - let _status = self - .stepper - .step_max(Bound::Included(1)) - .to_stepper_status(); - - Ok(()) - } -} - -impl Breakpoints for RiscvGdb<'_, S> { - // there are several kinds of breakpoints - this target uses software breakpoints - // We implement support for Software Breakpoints, in a similar fashion to - // the debugger. - #[inline(always)] - fn support_sw_breakpoint(&mut self) -> Option> { - Some(self) - } -} - -impl SwBreakpoint for RiscvGdb<'_, S> { - fn add_sw_breakpoint( - &mut self, - addr: u64, - _kind: ::BreakpointKind, - ) -> TargetResult { - self.breakpoints.insert(addr); - - Ok(true) - } - - fn remove_sw_breakpoint( - &mut self, - addr: u64, - _kind: ::BreakpointKind, - ) -> TargetResult { - Ok(self.breakpoints.remove(&addr)) - } -} - -struct RiscvEventLoop<'a, S: Stepper>(PhantomData<&'a S>); - -impl<'a, S: Stepper> run_blocking::BlockingEventLoop for RiscvEventLoop<'a, S> { - type Target = RiscvGdb<'a, S>; - type Connection = Box>; - - // or MultiThreadStopReason on multi threaded targets - type StopReason = SingleThreadStopReason; - - // Invoked immediately after the target's `resume` method has been - // called. The implementation should block until either the target - // reports a stop reason, or if new data was sent over the connection. - fn wait_for_stop_reason( - target: &mut Self::Target, - conn: &mut Self::Connection, - ) -> Result< - run_blocking::Event>, - run_blocking::WaitForStopReasonError< - ::Error, - ::Error, - >, - > { - // the specific mechanism to "select" between incoming data and target - // events will depend on your project's architecture. - // - // some examples of how you might implement this method include: `epoll`, - // `select!` across multiple event channels, periodic polling, etc... - // - // in this example, lets assume the target has a magic method that handles - // this for us. - let event = match target.run_and_check_for_incoming_data(conn) { - RiscvGdbEvent::IncomingData => { - let byte = conn - .read() // method provided by the `ConnectionExt` trait - .map_err(run_blocking::WaitForStopReasonError::Connection)?; - - run_blocking::Event::IncomingData(byte) - } - RiscvGdbEvent::BreakpointHit => { - run_blocking::Event::TargetStopped(gdbstub::stub::BaseStopReason::SwBreak(())) - } - RiscvGdbEvent::DoneStep => { - run_blocking::Event::TargetStopped(gdbstub::stub::BaseStopReason::DoneStep) - } - }; - - Ok(event) - } - - // Invoked when the GDB client sends a Ctrl-C interrupt. - fn on_interrupt( - _target: &mut Self::Target, - ) -> Result>, ::Error> { - // notify the target that a ctrl-c interrupt has occurred. - // target.stop_in_response_to_ctrl_c_interrupt()?; - - // a pretty typical stop reason in response to a Ctrl-C interrupt is to - // report a "Signal::SIGINT". - Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT))) - } -} - -impl ExecFile for RiscvGdb<'_, S> { - // Return the executable file name to the GDB client. This allows it to fetch the file itself, - // if it wasn't specified in the client already. - fn get_exec_file( - &self, - _pid: Option, - offset: u64, - _length: usize, - buf: &mut [u8], - ) -> TargetResult { - let offset = offset as usize; - let bytes = self.fname.as_bytes(); - - if offset >= bytes.len() { - return Ok(0); - } - - let read = usize::min(bytes.len() - offset, buf.len()); - let slice = &bytes[offset..offset + read]; - - buf[..read].copy_from_slice(slice); - - Ok(read) - } -} diff --git a/src/riscv/sandbox/src/commands/run.rs b/src/riscv/sandbox/src/commands/run.rs deleted file mode 100644 index 7370e8874b5cae384294ad6c0248800185019c6c..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/commands/run.rs +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// SPDX-FileCopyrightText: 2024-2025 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::error::Error; -use std::fs; -use std::io::Write; -use std::ops::Bound; - -use octez_riscv::machine_state::DefaultCacheLayouts; -use octez_riscv::machine_state::block_cache::block; -use octez_riscv::machine_state::block_cache::block::Block; -use octez_riscv::machine_state::memory::M1G; -use octez_riscv::pvm::PvmHooks; -use octez_riscv::state_backend::owned_backend::Owned; -use octez_riscv::stepper::StepResult; -use octez_riscv::stepper::Stepper; -use octez_riscv::stepper::StepperStatus; -use octez_riscv::stepper::pvm::PvmStepper; -use tezos_smart_rollup::utils::console::Console; -use tezos_smart_rollup::utils::inbox::InboxBuilder; -use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; - -use crate::cli::CommonOptions; -use crate::cli::RunOptions; - -cfg_if::cfg_if! { - if #[cfg(feature = "disable-jit")] { - /// Inner execution strategy for blocks. - type BlockImplInner = block::Interpreted; - } else if #[cfg(feature = "inline-jit")] { - /// Inner execution strategy for blocks. - type BlockImplInner = block::Jitted, M1G, Owned>; - } else { - /// Inner execution strategy for blocks. - type BlockImplInner = block::Jitted< - octez_riscv::machine_state::block_cache::block::OutlineCompiler, - M1G, - Owned, - >; - } -} - -/// Executor of blocks -#[cfg(not(feature = "metrics"))] -pub type BlockImpl = BlockImplInner; - -/// Executor of blocks -#[cfg(feature = "metrics")] -pub type BlockImpl = octez_riscv::machine_state::block_cache::metrics::BlockMetrics; - -pub fn run(opts: RunOptions) -> Result<(), Box> { - let program = fs::read(&opts.input)?; - let initrd = opts.initrd.as_ref().map(fs::read).transpose()?; - - let stepper = make_pvm_stepper::( - program.as_slice(), - initrd.as_deref(), - &opts.common, - Default::default(), - )?; - - let steps = run_stepper(stepper, opts.common.max_steps)?; - - if opts.print_steps { - println!("Run consumed {steps} steps."); - } - - #[cfg(feature = "metrics")] - octez_riscv::dump_block_metrics!( - file = &opts.metrics.block_metrics_file, - exclude_supported_instructions = opts.metrics.exclude_supported_instructions - )?; - - Ok(()) -} - -pub(crate) fn make_pvm_stepper>( - program: &[u8], - initrd: Option<&[u8]>, - common: &CommonOptions, - block_builder: B::BlockBuilder, -) -> Result< - PvmStepper<'static, M1G, DefaultCacheLayouts, Owned, B>, - std::boxed::Box, -> { - let mut inbox = InboxBuilder::new(); - if let Some(inbox_file) = &common.inbox.file { - inbox.load_from_file(inbox_file)?; - } - - let rollup_address = SmartRollupAddress::from_b58check(common.inbox.address.as_str())?; - - let mut console = if common.timings { - Console::with_timings() - } else { - Console::new() - }; - - let hooks = PvmHooks::new(move |c| { - let _written = console.write(&[c]).unwrap(); - }); - - let stepper = PvmStepper::<'_, M1G, DefaultCacheLayouts, Owned, B>::new( - program, - initrd, - inbox.build(), - hooks, - rollup_address.into_hash().as_ref().try_into().unwrap(), - common.inbox.origination_level, - common.preimage.preimages_dir.clone(), - block_builder, - )?; - - Ok(stepper) -} - -fn run_stepper( - mut stepper: impl Stepper, - max_steps: Option, -) -> Result> { - let max_steps = match max_steps { - Some(max_steps) => Bound::Included(max_steps), - None => Bound::Unbounded, - }; - - let result = stepper.step_max(max_steps); - - match result.to_stepper_status() { - StepperStatus::Exited { - success: true, - steps, - .. - } => Ok(steps), - result => Err(format!("{result:?}").into()), - } -} diff --git a/src/riscv/sandbox/src/main.rs b/src/riscv/sandbox/src/main.rs deleted file mode 100644 index 01932e719091dc7d951e5e16cd3d95272fa51f4c..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/main.rs +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: 2023 TriliTech -// SPDX-FileCopyrightText: 2024 Nomadic Labs -// -// SPDX-License-Identifier: MIT - -mod cli; -mod commands; -mod table; - -use std::error::Error; - -use octez_riscv::stepper::StepperStatus; - -use self::commands::bench; -use self::commands::gdb_server; -use self::commands::run; - -fn format_status(result: &StepperStatus) -> String { - use StepperStatus::*; - match result { - Exited { - success: true, - status, - .. - } => format!("Ok (status = {status})"), - Exited { status, .. } => format!("Exit with exit code = {}", status), - Running { .. } => "Timeout".to_string(), - Errored { cause, message, .. } => format!("{message}\nCaused by: {cause}"), - } -} - -fn main() -> Result<(), Box> { - let cli = cli::parse(); - - #[cfg(feature = "log")] - { - use octez_riscv::log::tracing_internal as tracing; - - let subscriber_builder = tracing_subscriber::fmt() - .without_time() - .with_max_level(cli.log_level); - - match cli.log_json_file { - Some(log_json_file) => { - let file = std::fs::File::create(log_json_file)?; - let subscriber = subscriber_builder.json().with_writer(file).finish(); - tracing::subscriber::set_global_default(subscriber)?; - } - - None => { - let subscriber = subscriber_builder.with_writer(std::io::stderr).finish(); - tracing::subscriber::set_global_default(subscriber)?; - } - } - } - - match cli.command { - cli::Mode::Run(opts) => run(opts), - cli::Mode::Bench(opts) => bench(opts), - cli::Mode::GdbServer(opts) => gdb_server(opts), - } -} diff --git a/src/riscv/sandbox/src/table.rs b/src/riscv/sandbox/src/table.rs deleted file mode 100644 index 395e0dd1276cd0fce9bae2a587699129e25205b5..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/table.rs +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use comfy_table::Attribute; -use comfy_table::Cell; -use comfy_table::CellAlignment; -use comfy_table::Color; -use comfy_table::ContentArrangement; -use comfy_table::Table; -use comfy_table::modifiers::UTF8_ROUND_CORNERS; -use comfy_table::presets::UTF8_FULL; -use itertools::Itertools; - -use self::utils::NamedBenchInstrStats; -use self::utils::prepend_cell; -use self::utils::tableify_bench_stats; -use self::utils::thousand_format; -use self::utils::vec_cell_factory_ref; -use crate::cli::SortInstr; -use crate::cli::SortRuns; -use crate::cli::TableSortArgs; -use crate::commands::bench::BenchStats; -use crate::commands::bench::NamedStats; -use crate::table::utils::format_opt_duration; - -pub mod utils; - -fn row_header_table(table: &mut Table, filename: Vec<&str>) { - let cells = vec_cell_factory_ref(&filename, |f| { - Cell::new(f) - .add_attribute(Attribute::Bold) - .fg(Color::DarkCyan) - .set_alignment(CellAlignment::Center) - }); - table.set_header(prepend_cell( - Cell::new("Benchmark name") - .fg(Color::DarkGreen) - .add_attribute(Attribute::Bold), - cells, - )); -} - -fn section_summary(table: &mut Table, data: &[(&BenchStats, &String)]) { - row_header_table(table, vec!["Outcome", "Total steps", "Instruction / s"]); - for &(stats, name) in data { - let steps = stats.total_steps; - let instr_duration = stats.instruction_duration(); - let speed = thousand_format(steps as f64 / instr_duration.as_secs_f64(), 2); - - let name = Cell::new(name); - let outcome = Cell::new(&stats.run_result); - let total_steps = - Cell::new(thousand_format(stats.total_steps, 0)).set_alignment(CellAlignment::Right); - let speed = Cell::new(speed).set_alignment(CellAlignment::Right); - table.add_row(vec![name, outcome, total_steps, speed]); - } -} - -fn content_stats_line_opt(name: &str, stats: Option<&NamedStats>) -> Vec { - match stats { - Some(s) => content_stats_line(name, s), - None => vec![ - Cell::new(name), - Cell::new("---").set_alignment(CellAlignment::Center), - Cell::new("---").set_alignment(CellAlignment::Center), - Cell::new("---").set_alignment(CellAlignment::Center), - Cell::new("---").set_alignment(CellAlignment::Center), - ], - } -} - -fn content_stats_line(name: &str, stats: &NamedStats) -> Vec { - vec![ - Cell::new(name), - Cell::new(thousand_format(stats.count, 0)).set_alignment(CellAlignment::Right), - Cell::new(format!("{:#?}", stats.total)).set_alignment(CellAlignment::Right), - Cell::new(format!("{:#?}", stats.average)).set_alignment(CellAlignment::Right), - Cell::new(format!("±{}", format_opt_duration(&stats.stddev))) - .set_alignment(CellAlignment::Right), - Cell::new(format_opt_duration(&stats.median)).set_alignment(CellAlignment::Right), - ] -} - -fn row_header_stats(name_cell: Cell) -> Vec { - let prop_cells = vec_cell_factory_ref(&["Count", "Total", "Avg", "Stddev", "Median"], |f| { - Cell::new(f) - .add_attribute(Attribute::Bold) - .fg(Color::DarkCyan) - .set_alignment(CellAlignment::Center) - }); - prepend_cell(name_cell, prop_cells) -} - -fn section_interpreter_stats(table: &mut Table, data: &[(&BenchStats, &String)]) { - table.add_row(Vec::::new()); - table.add_row(row_header_stats( - Cell::new("Interpreter stats").fg(Color::DarkGreen), - )); - for &(stats, name) in data { - table.add_row(content_stats_line(name, &stats.bench_duration_stats)); - } -} - -fn section_instruction_stats(table: &mut Table, data: &[&[&NamedBenchInstrStats]]) { - table.add_row(Vec::::new()); - - table.add_row(vec![ - Cell::new("Distinct Instructions").fg(Color::DarkGreen), - ]); - - for instr_data in data { - // instruction name + props - let name = match instr_data.iter().filter_map(|item| item.1).next() { - Some(s) => &s.name, - _ => "No instruction data", - }; - table.add_row(row_header_stats( - Cell::new(name) - .add_attribute(Attribute::Bold) - .fg(Color::DarkBlue), - )); - - // content - for instr_stat in instr_data.iter() { - table.add_row(content_stats_line_opt(instr_stat.0, instr_stat.1)); - } - } -} - -/// Sort the instruction groups. -fn sort_instruction_groups<'a, 'b, 'c>( - ordering: &SortInstr, - data: &'c [Vec>], -) -> Vec<&'c Vec>> { - // Obtain an array of references to the lines that will be sorted. - let mut data = data.iter().collect_vec(); - - let instr_name = |instr_data: &Vec>| match instr_data.iter().next() - { - None => "No instruction data", - Some(el) => el.0, - }; - - let max_avg = |instr_data: &Vec| { - instr_data.iter().map(|el| el.1.map(|e| e.average)).max() - }; - - let total_count = |instr_data: &Vec| { - instr_data - .iter() - .map(|el| match el.1 { - None => 0, - Some(stats) => stats.count, - }) - .sum::() - }; - - data.sort_by(|&a, &b| match ordering { - SortInstr::Alphabetic => instr_name(a).cmp(instr_name(b)), - SortInstr::MaxAvg => max_avg(a).cmp(&max_avg(b)).reverse(), - SortInstr::TotalCount => total_count(a).cmp(&total_count(b)).reverse(), - }); - - data -} - -fn sort_runs_per_instr<'a, 'b, 'c>( - ordering: &SortRuns, - data: &[&'c Vec>], -) -> Vec>> { - let bench_name = |stats: &NamedBenchInstrStats<'a, 'b>| stats.0; - - let average = |stats: &NamedBenchInstrStats| stats.1.map(|s| s.average); - - let total = |stats: &NamedBenchInstrStats| stats.1.map(|s| s.total); - - data.iter() - .map(|group| { - let mut group = group.iter().collect_vec(); - group.sort_by(|&a, &b| match ordering { - SortRuns::Alphabetic => bench_name(a).cmp(bench_name(b)), - SortRuns::Input => std::cmp::Ordering::Equal, - SortRuns::Speed => average(a).cmp(&average(b)).reverse(), - SortRuns::Total => total(a).cmp(&total(b)).reverse(), - }); - group - }) - .collect_vec() -} - -pub fn table_from_stats(sort_opts: &TableSortArgs, data: &[(&BenchStats, &String)]) -> Table { - let by_instr_data = tableify_bench_stats(data); - let sorted_data = sort_instruction_groups(&sort_opts.sort_instr, &by_instr_data); - let sorted_data = sort_runs_per_instr(&sort_opts.sort_runs, &sorted_data); - - let mut table = Table::new(); - table - .load_preset(UTF8_FULL) - .apply_modifier(UTF8_ROUND_CORNERS) - .set_content_arrangement(ContentArrangement::Dynamic); - - section_summary(&mut table, data); - section_interpreter_stats(&mut table, data); - section_instruction_stats( - &mut table, - &sorted_data.iter().map(|line| line.as_slice()).collect_vec(), - ); - - table -} diff --git a/src/riscv/sandbox/src/table/utils.rs b/src/riscv/sandbox/src/table/utils.rs deleted file mode 100644 index f8bd56aa7bac3cdfab92cf0579412ce0d701c854..0000000000000000000000000000000000000000 --- a/src/riscv/sandbox/src/table/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-FileCopyrightText: 2024 TriliTech -// -// SPDX-License-Identifier: MIT - -use std::collections::BTreeSet; -use std::collections::HashMap; -use std::time::Duration; - -use comfy_table::Cell; -use itertools::Itertools; -use numfmt::Formatter; -use numfmt::Numeric; - -use crate::commands::bench::BenchStats; -use crate::commands::bench::NamedStats; - -/// Produce a string by formatting the number with the given `separator` and `precision` -pub fn thousand_format(content: N, num_decimals: u8) -> String { - let mut fmt = Formatter::new() - .separator(',') - .unwrap() - .precision(numfmt::Precision::Decimals(num_decimals)); - fmt.fmt2(content).to_string() -} - -/// Build a [`Vec`] from a `&[&T]` and a factory function -pub fn vec_cell_factory_ref Cell>(v: &[&T], factory_f: F) -> Vec { - v.iter().map(|el| factory_f(el)).collect() -} - -/// Prepend a [`Cell`] to a [`Vec`]. -pub fn prepend_cell(first_cell: Cell, mut rest_of_cells: Vec) -> Vec { - rest_of_cells.insert(0, first_cell); - rest_of_cells -} - -/// Type holding `(Name of benchmark, Option)` -pub type NamedBenchInstrStats<'a, 'b> = (&'a str, Option<&'b NamedStats>); - -/// Return an array `r[i][j]` = the stats for the i-th instruction and j-th benchmark & benchmark_name -pub fn tableify_bench_stats<'a, 'b>( - data: &[(&'a BenchStats, &'b String)], -) -> Vec>> { - // Collect all the instructions sorted by name - let instr_names = data - .iter() - .flat_map(|s| s.0.instr_stats.iter().flatten().map(|i_s| &i_s.name)) - .collect::>(); - - // Transform each BenchStats instruction data into a (BenchName, HashMap by instruction name) - // Note: BenchStats with None on instruction-level data will just be an empty hashmap - let stats_by_instr: Vec<(&String, HashMap<_, _>)> = data - .iter() - .map(|s| { - ( - s.1, - s.0.instr_stats - .iter() - .flatten() - .map(|i_s| (&i_s.name, i_s)) - .collect(), - ) - }) - .collect(); - - // For each instruction name, get the corresponding NamedStats or None for that benchmark - instr_names - .into_iter() - .map(|instr_name| { - stats_by_instr - .iter() - .map(|(bench_name, map)| (bench_name.as_str(), map.get(instr_name).copied())) - .collect() - }) - .collect_vec() -} - -pub fn format_opt_duration(d: &Option) -> String { - match d { - None => "---".to_string(), - Some(d) => format!("{d:#?}"), - } -} diff --git a/src/riscv/scripts/benchmark.sh b/src/riscv/scripts/benchmark.sh deleted file mode 100755 index d37682c0486ae75358f7e8ad44f39ff63b45fbaa..0000000000000000000000000000000000000000 --- a/src/riscv/scripts/benchmark.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2024 TriliTech -# -# SPDX-License-Identifier: MIT - -# Build & run jstz on two different commits, -# where the first commit is the "base / master" commit hash -# and the second commit is the current change / MR tested - -set -e - -# Iterations for RISCV and for native are different because usually for native you would want way more runs -USAGE="Usage: -t -i -n -c [ -b : default master ]" - -RISCV_IT="" -NATIVE_IT="" -TX="" -BASE_COMM="master" -CHANGE_COMM="" -NATIVE="" - -while getopts "t:i:n:c:b" OPTION; do - case "$OPTION" in - t) - TX="$OPTARG" - ;; - i) - RISCV_IT="$OPTARG" - ;; - n) - NATIVE_IT="$OPTARG" - ;; - c) - CHANGE_COMM="$OPTARG" - ;; - b) - BASE_COMM="$OPTARG" - ;; - *) - echo "$USAGE" - exit 1 - ;; - esac -done - -if [ -z "$TX" ] || [ -z "$RISCV_IT" ] || [ -z "$NATIVE_IT" ] || [ -z "$CHANGE_COMM" ]; then - echo "$USAGE" - exit 1 -fi - -NATIVE=$(make --silent -C jstz print-native-target | grep -wv make) - -CURR=$(pwd) -RISCV_DIR=$(dirname "$0")/.. -cd "$RISCV_DIR" - -git checkout "$CHANGE_COMM" --quiet - -echo "[INFO]: Run on change commit ($CHANGE_COMM) on ($NATIVE)" -"$RISCV_DIR/scripts/jstz-bench.sh" -t "$TX" -i "$NATIVE_IT" -s -n 2>&1 | tee "CHANGE_NATIVE_$CHANGE_COMM.run" - -echo "[INFO]: Run on change commit ($CHANGE_COMM) on (RISCV)" -"$RISCV_DIR/scripts/jstz-bench.sh" -t "$TX" -i "$RISCV_IT" -s 2>&1 | tee "CHANGE_RISCV_$CHANGE_COMM.run" - -git checkout "$BASE_COMM" --quiet - -echo "[INFO]: Run on base commit ($BASE_COMM) on ($NATIVE)" -"$RISCV_DIR/scripts/jstz-bench.sh" -t "$TX" -i "$NATIVE_IT" -s -n 2>&1 | tee "BASE_NATIVE_$BASE_COMM.run" - -echo "[INFO]: Run on base commit ($BASE_COMM) on (RISCV)" -"$RISCV_DIR/scripts/jstz-bench.sh" -t "$TX" -i "$RISCV_IT" -s 2>&1 | tee "BASE_RISCV_$BASE_COMM.run" - -cd "$CURR" diff --git a/src/riscv/scripts/get-fa2.sh b/src/riscv/scripts/get-fa2.sh deleted file mode 100755 index cda60b851cb8f4cb0f064d96cff53ca1d3f2dd65..0000000000000000000000000000000000000000 --- a/src/riscv/scripts/get-fa2.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2024 TriliTech -# -# SPDX-License-Identifier: MIT - -# Build and install the fa2 example contract from jstz - -set -e - -CURR=$(pwd) - -JSTZ_COMMIT=20ba89084d233c9f180d7bb86bd33af2ef25dc7f -JSTZ_DIR=$(mktemp -d) - -JSTZ_WORKSPACE=$(git rev-parse --show-toplevel)/src/riscv/jstz - -git clone https://github.com/trilitech/jstz.git "${JSTZ_DIR}" --depth 1 -cd "${JSTZ_DIR}" - -git fetch && git checkout $JSTZ_COMMIT -cd examples/fa2 - -npm install && npm run build - -cp dist/index.js "${JSTZ_WORKSPACE}"/fa2.js - -cd "${CURR}" diff --git a/src/riscv/scripts/isa-suite-coverage.sh b/src/riscv/scripts/isa-suite-coverage.sh deleted file mode 100755 index e6d8c50cc0507153974b68fef6a7db86851c331b..0000000000000000000000000000000000000000 --- a/src/riscv/scripts/isa-suite-coverage.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2025 TriliTech -# -# SPDX-License-Identifier: MIT - -# Runs the ISA test suite, gathering code-coverage metrics - -set -e - -# Use nightly for access to llvm-tools-preview -export RUST_TOOLCHAIN="nightly-2025-01-30" - -USAGE="[ -d: install deps ]" - -while getopts "dh" OPTION; do - case "$OPTION" in - d) - rustup component add llvm-tools-preview - cargo install grcov --locked --force --version "0.8.20" - exit 0 - ;; - h) - echo "$USAGE" - echo "Running without any options will generate code coverage" - exit 0 - ;; - *) - echo "unrecognized option, try with -h for help" - exit 1 - ;; - esac -done - -rm -rf target/coverage - -LLVM_PROFILE_FILE="$(pwd)/target/coverage/cargo-test-%p-%m.profraw" -export LLVM_PROFILE_FILE - -CARGO_INCREMENTAL=0 \ - RUSTFLAGS='-Cinstrument-coverage' \ - cargo test --workspace -- test_suite - -grcov \ - target/coverage \ - --binary-path ./target/debug/deps/ \ - -s . \ - --output-type html,cobertura \ - --branch \ - --ignore-not-existing \ - --ignore '../*' \ - --ignore "/*" \ - --keep-only 'lib/src*' \ - -o target/coverage - -echo "Coverage files located in ./target/coverage" - -xmllint --xpath "concat('Coverage: ', 100 * string(//coverage/@line-rate), '%')" target/coverage/cobertura.xml diff --git a/src/riscv/scripts/jstz-bench.sh b/src/riscv/scripts/jstz-bench.sh deleted file mode 100755 index 6d492d2f04d84d3b0a37b83756b11eba321f78ed..0000000000000000000000000000000000000000 --- a/src/riscv/scripts/jstz-bench.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2024-2025 TriliTech -# -# SPDX-License-Identifier: MIT - -# Build and run the jstz TPS benchmark with the specified number of transfers - -set -e - -USAGE="Usage: -t [ -s: static inbox ] [ -p: profile with samply ] [ -n: run natively ] [ -i : number of runs ] [ -j : disable jit / use inline jit ] [ -m : enable metrics ]" -DEFAULT_ROLLUP_ADDRESS="sr163Lv22CdE8QagCwf48PWDTquk6isQwv57" - -ITERATIONS="1" -TX="" -STATIC_INBOX="" -SANDBOX_BIN="riscv-sandbox" -SANDBOX_ENABLE_FEATURES=() -PROFILING_WRAPPER="" -SAMPLY_OUT="riscv-sandbox-profile.json" -METRICS="" -METRICS_ARGS=() -NATIVE="" -JSTZ_SANDBOX_PARAMS=("--input" "jstz/target/riscv64gc-unknown-linux-musl/release/jstz") - -CURR=$(pwd) -RISCV_DIR=$(dirname "$0")/.. -cd "$RISCV_DIR" - -while getopts "i:t:m:spnj:" OPTION; do - case "$OPTION" in - i) - ITERATIONS="$OPTARG" - ;; - t) - TX="$OPTARG" - ;; - s) - STATIC_INBOX="y" - ;; - p) - SANDBOX_BIN="riscv-sandbox.prof" - PROFILING_WRAPPER="samply record -s -o $SAMPLY_OUT" - ;; - n) - NATIVE=$(make --silent -C jstz print-native-target | grep -wv make) - ;; - j) - case "$OPTARG" in - i*) - SANDBOX_ENABLE_FEATURES+=("inline-jit") - ;; - d*) - SANDBOX_ENABLE_FEATURES+=("disable-jit") - ;; - *) - echo "-j " - exit 1 - ;; - esac - ;; - m) - SANDBOX_ENABLE_FEATURES+=("metrics") - METRICS="y" - - case "$OPTARG" in - all) ;; - jit-unsupported) - METRICS_ARGS+=("--exclude-supported-instructions") - ;; - *) - echo "$USAGE" - exit 1 - ;; - esac - ;; - *) - echo "$USAGE" - exit 1 - ;; - esac -done - -if [ -z "$TX" ]; then - echo "$USAGE" - exit 1 -fi - -if [ -n "$NATIVE" ] && [ -z "$STATIC_INBOX" ]; then - echo "Native compilation without static inbox unsupported" - echo "$USAGE" - exit 1 -fi - -echo "[INFO]: building sandbox" -make "SANDBOX_ENABLE_FEATURES=${SANDBOX_ENABLE_FEATURES[*]}" "$SANDBOX_BIN" &> /dev/null -echo "[INFO]: building bench tool" -make -C jstz inbox-bench &> /dev/null - -DATA_DIR=${DATA_DIR:=$(mktemp -d)} - -echo "[INFO]: generating $TX transfers" -INBOX_FILE="${DATA_DIR}/inbox.json" -RUN_INBOX="$INBOX_FILE" -./jstz/inbox-bench generate --inbox-file "$INBOX_FILE" --transfers "$TX" - -log_file_args=() - -BLOCK_METRICS_FILE="${DATA_DIR}/block-metrics.out" -if [ -n "$METRICS" ]; then - METRICS_ARGS+=("--block-metrics-file" "${BLOCK_METRICS_FILE}") -fi - -########## -# RISC-V # -########## -build_jstz_riscv() { - if [ "$STATIC_INBOX" = "y" ]; then - INBOX_FILE="$INBOX_FILE" make -C jstz build-kernel-static &> /dev/null - RUN_INBOX="$DATA_DIR"/empty.json - echo "[]" > "$RUN_INBOX" - else - make -C jstz build-kernel &> /dev/null - fi -} - -run_jstz_riscv() { - LOG="$DATA_DIR/log.$1.log" - $PROFILING_WRAPPER "./$SANDBOX_BIN" run \ - "${JSTZ_SANDBOX_PARAMS[@]}" \ - --inbox-file "$RUN_INBOX" \ - --address "$DEFAULT_ROLLUP_ADDRESS" \ - "${METRICS_ARGS[@]}" \ - --timings > "$LOG" - log_file_args+=("--log-file=$LOG") -} - -########## -# Native # -########## -build_jstz_native() { - INBOX_FILE=$INBOX_FILE make -C jstz build-kernel-native &> /dev/null -} - -run_jstz_native() { - LOG="$DATA_DIR/log.$1.log" - $PROFILING_WRAPPER ./jstz/target/"$NATIVE"/release/jstz \ - --timings > "$LOG" 2> /dev/null - log_file_args+=("--log-file=$LOG") -} - -######### -# Build # -######### -echo "[INFO]: building jstz" - -if [ -z "$NATIVE" ]; then - build_jstz_riscv - echo "[INFO]: running $TX transfers (riscv) " -else - build_jstz_native - echo "[INFO]: running $TX transfers ($NATIVE) " -fi - -################# -# Run & Collect # -################# -run_jstz() { - echo -ne "\r\033[2K[INFO]: Run $1 / $ITERATIONS" - if [ -z "$NATIVE" ]; then - run_jstz_riscv "$1" - else - run_jstz_native "$1" - fi - - if [ -n "$PROFILING_WRAPPER" ]; then - echo -e "\n[INFO]: Samply data saved to: $SAMPLY_OUT" - fi -} - -collect() { - echo -e "\033[1m" - ./jstz/inbox-bench results --inbox-file "$INBOX_FILE" "${log_file_args[@]}" --expected-transfers "$TX" - echo -e "\033[0m" -} - -for i in $(seq "$ITERATIONS"); do - run_jstz "$i" -done - -collect - -# This loads the profile of the last run -if [ -n "$PROFILING_WRAPPER" ]; then - echo "[INFO]: collecting results" - samply load $SAMPLY_OUT -fi - -if [ -n "${METRICS}" ]; then - echo "Block metrics at ${BLOCK_METRICS_FILE}" -fi - -cd "$CURR" diff --git a/src/riscv/scripts/test-status.sh b/src/riscv/scripts/test-status.sh deleted file mode 100755 index 5a51ae6d6d614df97df2ddd4fd433756679a9481..0000000000000000000000000000000000000000 --- a/src/riscv/scripts/test-status.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -# SPDX-FileCopyrightText: 2024 TriliTech -# -# SPDX-License-Identifier: MIT - -# Find all the non-enabled tests, and explain which instructions are missing - -set -e - -top_level=$(git rev-parse --show-toplevel) -cd "${top_level}"/src/riscv - -# Collect passing/ignored tests -echo "[INFO]: examining tests" - -declare -a ignored -declare -a succeed - -while read -r t; do - status=$(echo "$t" | awk '{print $4}') - name=$(echo "$t" | awk '{print $2}' | sed 's/test_suite_//' | sed 's/_/-/g') - - if [ "$status" = "ok" ]; then - succeed+=("$name") - elif [ "$status" = "ignored" ]; then - ignored+=("$name") - fi -done < <(cargo test -- test_suite_rv64 2> /dev/null | grep 'test_suite_rv64') - -# Collect all 'passing' instructions -echo "[INFO]: collecting tested instructions" - -generated_path=${top_level}/src/riscv/assets/generated - -declare -A working - -function instrs() { - file="$1" - - if [ ! -f "$1" ]; then - # shellcheck disable=SC2001 - file=$(echo "$1" | sed 's/\(.*\)-/\1_/') - fi - - riscv64-unknown-linux-gnu-objdump -d -M no-aliases "$file" | - awk '{print $3}' | sort | uniq -} - -for t in "${succeed[@]}"; do - while read -r instr; do - if [ -n "$instr" ]; then - working["$instr"]="" - fi - done < <(instrs "$generated_path/$t") -done - -echo "[INFO]: found ${#working[@]} tested instructions" - -# Examing ignored test requirements -echo "[INFO]: examing ignored tests" - -for t in "${ignored[@]}"; do - echo -en "$(basename "$t")\t\t" - - while read -r instr; do - if [ -z "$instr" ]; then - continue - fi - - if ! [[ -v working["$instr"] ]]; then - echo -n "${instr}," - fi - done < <(instrs "$generated_path/$t") - echo "" -done diff --git a/src/rust_deps/Cargo.lock b/src/rust_deps/Cargo.lock index caca4c10c263af66f7cf4aa4da153fd263190f4b..d592196f5dd25565c7c119c55b58db5153774c74 100644 --- a/src/rust_deps/Cargo.lock +++ b/src/rust_deps/Cargo.lock @@ -2555,6 +2555,7 @@ dependencies = [ [[package]] name = "octez-riscv" version = "0.0.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "arbitrary-int", "bincode", @@ -3802,6 +3803,7 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "tezos-smart-rollup-build-utils" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "rustc_version", ] @@ -3809,10 +3811,12 @@ dependencies = [ [[package]] name = "tezos-smart-rollup-constants" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" [[package]] name = "tezos-smart-rollup-core" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "tezos-smart-rollup-build-utils", "tezos-smart-rollup-constants", @@ -3821,6 +3825,7 @@ dependencies = [ [[package]] name = "tezos-smart-rollup-encoding" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "hex", "nom 7.1.3", @@ -3839,6 +3844,7 @@ dependencies = [ [[package]] name = "tezos-smart-rollup-host" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "tezos-smart-rollup-build-utils", "tezos-smart-rollup-core", @@ -3850,6 +3856,7 @@ dependencies = [ [[package]] name = "tezos-smart-rollup-mock" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "hex", "tezos-smart-rollup-core", @@ -3862,6 +3869,7 @@ dependencies = [ [[package]] name = "tezos-smart-rollup-utils" version = "0.2.2" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "clap 4.5.21", "hex", @@ -3878,6 +3886,7 @@ dependencies = [ [[package]] name = "tezos_crypto_rs" version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "anyhow", "bs58 0.5.1", @@ -3902,6 +3911,7 @@ dependencies = [ [[package]] name = "tezos_data_encoding" version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "bit-vec", "bitvec", @@ -3918,6 +3928,7 @@ dependencies = [ [[package]] name = "tezos_data_encoding_derive" version = "0.6.0" +source = "git+https://github.com/tezos/riscv-pvm.git#314c54e7a340c4a29af3317573272cbf834053bd" dependencies = [ "lazy_static", "once_cell", diff --git a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - RPC API should work and be stable.out b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - RPC API should work and be stable.out index 8279e42affdf33aa890052b46a89c585757ef4d6..62465dccb12f3e0db9469684ff6fd5eccc91264c 100644 Binary files a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - RPC API should work and be stable.out and b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - RPC API should work and be stable.out differ diff --git a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with jstz kernel.out b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with jstz kernel.out index 09f95a23522d66bf86b80df72ae813f7bc8bad21..d47a35e2d5e2047de79e511eeaf554afb52d95c0 100644 --- a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with jstz kernel.out +++ b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with jstz kernel.out @@ -1,5 +1,5 @@ -./octez-client --wait none originate smart rollup rollup from bootstrap1 of kind riscv of type string with kernel kernel:src/riscv/assets/jstz:0ee345890aa9a3e992bb8118800b282b125d28ce196558f33f344f302bcb80d7 --burn-cap 9999999 +./octez-client --wait none originate smart rollup rollup from bootstrap1 of kind riscv of type string with kernel kernel:src/riscv/assets/jstz:afe9ed859f505d6f4a510b9c40014bbf4da13f1563f2e3f47f6f3e8aa7800f10 --burn-cap 9999999 Node is bootstrapped. Estimated gas: 1933.435 units (will add 100 for safety) Estimated storage: 6552 bytes added (will add 20 for safety) @@ -22,7 +22,7 @@ This sequence of operations was run: Smart rollup origination: Kind: riscv Parameter type: string - Kernel Blake2B hash: 'be29f83af72354c390ebede744c30a988c0a8eee9e1cb1eb9d67ab06d81564f8' + Kernel Blake2B hash: '61b30ff4bf971dcd662625ebabfd5031e891df3190350320f9a276962e5db4ad' This smart rollup origination was successfully applied Consumed gas: 1933.402 Storage size: 6552 bytes @@ -38,7 +38,7 @@ GET http://[HOST]:[PORT]/global/block/head/state_hash "[SC_ROLLUP_PVM_STATE_HASH]" -./octez-client --wait none send smart rollup message 'hex:[ "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000004fd8b650f46128bf5c7edfe3c72de5ae420da3b4a78843eca103e14514efc6c337819ebef88b499d613371d6d3e6bb081745562b87172fe41105cd759eb75100000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395000000000000000000000000b80e00000000000066756e6374696f6e206f2865297b72657475726e20747970656f6620653d3d22737472696e67227d66756e6374696f6e206128652c72297b72657475726e2041727261792e697341727261792872292626722e72656475636528286e2c74293d3e6e2626652874292c2130297d66756e6374696f6e20692865297b72657475726e20747970656f6620653d3d226e756d6265722226264e756d6265722e6973496e74656765722865297d66756e6374696f6e206b2865297b6c657420723d653b7472797b72657475726e206f28722e746f2926266928722e746f6b656e5f69642926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20772865297b6c657420723d653b7472797b72657475726e206f28722e66726f6d29262661286b2c722e7472616e7366657273297d63617463687b72657475726e21317d7d66756e6374696f6e20412865297b6c657420723d653b7472797b72657475726e28722e6f7065726174696f6e3d3d3d226164645f6f70657261746f72227c7c722e6f7065726174696f6e3d3d3d2272656d6f76655f6f70657261746f72222926266f28722e6f776e65722926266f28722e6f70657261746f722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e20702865297b6c657420723d653b7472797b72657475726e206f28722e6f776e65722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e205f2865297b6c657420723d653b7472797b72657475726e206128702c722e7265717565737473297d63617463687b72657475726e21317d7d66756e6374696f6e20712865297b6c657420723d653b7472797b72657475726e207028722e726571756573742926264e756d6265722e6973496e746567657228722e62616c616e6365297d63617463687b72657475726e21317d7d66756e6374696f6e20672865297b6c657420723d653b7472797b72657475726e206928722e746f6b656e5f69642926266f28722e6f776e65722926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20642865297b72657475726e60746f6b656e2f247b657d607d66756e6374696f6e20522865297b6c657420723d642865293b6966284b762e676574287229297468726f77224641325f544f4b454e5f49445f455849535453223b4b762e73657428642865292c2130297d66756e6374696f6e204f2865297b696628214b762e676574286428652929297468726f77224641325f544f4b454e5f554e444546494e4544227d66756e6374696f6e206c28652c72297b72657475726e6062616c616e63652f247b657d2f247b727d607d66756e6374696f6e206d28652c72297b72657475726e204b762e676574286c28652c7229297c7c307d66756e6374696f6e206828652c722c6e297b6966286e3c30297468726f77224641325f494e53554646494349454e545f42414c414e4345223b4b762e736574286c28652c72292c6e297d66756e6374696f6e207528652c722c6e297b6c657420743d6d28652c72293b6828652c722c742b6e297d66756e6374696f6e207928652c722c6e2c74297b7528652c6e2c2d74292c7528722c6e2c74297d66756e6374696f6e206628652c722c6e297b72657475726e606f776e65722f247b657d2f247b727d2f247b6e7d607d66756e6374696f6e204928652c722c6e297b4b762e736574286628652c722c6e292c2130297d66756e6374696f6e206228652c722c6e297b4b762e64656c657465286628652c722c6e29297d66756e6374696f6e205428652c722c6e297b6966282128653d3d3d727c7c4b762e676574286628652c722c6e292929297468726f77224641325f4e4f545f4f50455241544f52227d66756e6374696f6e204228652c72297b69662865213d3d72297468726f7720636f6e736f6c652e6c6f672860247b657d20213d3d20247b727d60292c224641325f4e4f545f4f574e4552227d66756e6374696f6e204e28652c722c6e297b4f286e2e746f6b656e5f6964292c5428652c722c6e2e746f6b656e5f6964292c7928652c6e2e746f2c6e2e746f6b656e5f69642c6e2e616d6f756e74297d66756e6374696f6e207828652c72297b722e666f7245616368286e3d3e6e2e7472616e73666572732e666f724561636828743d3e4e286e2e66726f6d2c652c742929297d66756e6374696f6e207628652c72297b73776974636828722e6f7065726174696f6e297b63617365226164645f6f70657261746f72223a4228722e6f776e65722c65292c4928722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964293b627265616b3b636173652272656d6f76655f6f70657261746f72223a5428722e6f776e65722c652c722e746f6b656e5f6964292c6228722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964297d7d66756e6374696f6e20452865297b6c657420723d6d28652e6f776e65722c652e746f6b656e5f6964293b72657475726e20636f6e736f6c652e6c6f672860247b652e6f776e65727d2068617320247b727d206f6620746f6b656e20247b652e746f6b656e5f69647d60292c7b726571756573743a652c62616c616e63653a727d7d66756e6374696f6e20532865297b72657475726e20652e72657175657374732e6d61702845297d66756e6374696f6e204b2865297b5228652e746f6b656e5f6964292c7528652e6f776e65722c652e746f6b656e5f69642c652e616d6f756e74297d6173796e632066756e6374696f6e20552865297b6c657420723d6e65772055524c28652e75726c292c6e3d722e706174686e616d653b7472797b737769746368286e297b63617365222f70696e67223a72657475726e20636f6e736f6c652e6c6f67282248656c6c6f2066726f6d2072756e6e657220736d6172742066756e6374696f6e205c757b31463434427d22292c6e657720526573706f6e73652822506f6e6722293b63617365222f62616c616e63655f6f66223a696628652e6d6574686f643d3d3d2247455422297b6c657420733d7b72657175657374733a4a534f4e2e70617273652861746f6228722e736561726368506172616d732e67657428227265717565737473222929297d3b6966285f287329297b6c657420633d532873293b72657475726e20526573706f6e73652e6a736f6e2863297d656c73652072657475726e20636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c73292c526573706f6e73652e6572726f7228297d656c73657b6c657420733d222f62616c616e63655f6f662069732061204745542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7472616e73666572223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128772c73293f287828652e686561646572732e67657428225265666572657222292c73292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7472616e73666572206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f6d696e745f6e6577223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128672c73293f28732e666f7245616368284b292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f6d696e745f6e6577206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7570646174655f6f70657261746f7273223a696628652e6d6574686f643d3d3d2250555422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128412c73293f28732e666f724561636828633d3e7628652e686561646572732e67657428225265666572657222292c6329292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7570646174655f6f70657261746f72732069732061205055542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d64656661756c743a6c657420743d60556e7265636f676e6973656420656e747279706f696e7420247b6e7d603b72657475726e20636f6e736f6c652e6572726f722874292c6e657720526573706f6e736528742c7b7374617475733a3430347d297d7d63617463682874297b7468726f7720636f6e736f6c652e6572726f722874292c747d7d76617220463d553b6578706f72747b462061732064656661756c742c5f20617320697342616c616e63654f662c7020617320697342616c616e6365526571756573742c7120617320697342616c616e6365526573706f6e73652c672061732069734d696e744e65772c69206173206973546f6b656e49642c6b2061732069735472616e736665722c772061732069735472616e73666572732c412061732069735570646174654f70657261746f727d3b0a0000000000000000" ]' from bootstrap2 +./octez-client --wait none send smart rollup message 'hex:[ "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000004fd8b650f46128bf5c7edfe3c72de5ae420da3b4a78843eca103e14514efc6c337819ebef88b499d613371d6d3e6bb081745562b87172fe41105cd759eb75100000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395000000000000000000000000b80e00000000000066756e6374696f6e206f2865297b72657475726e20747970656f6620653d3d22737472696e67227d66756e6374696f6e206128652c72297b72657475726e2041727261792e697341727261792872292626722e72656475636528286e2c74293d3e6e2626652874292c2130297d66756e6374696f6e20692865297b72657475726e20747970656f6620653d3d226e756d6265722226264e756d6265722e6973496e74656765722865297d66756e6374696f6e206b2865297b6c657420723d653b7472797b72657475726e206f28722e746f2926266928722e746f6b656e5f69642926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20772865297b6c657420723d653b7472797b72657475726e206f28722e66726f6d29262661286b2c722e7472616e7366657273297d63617463687b72657475726e21317d7d66756e6374696f6e20412865297b6c657420723d653b7472797b72657475726e28722e6f7065726174696f6e3d3d3d226164645f6f70657261746f72227c7c722e6f7065726174696f6e3d3d3d2272656d6f76655f6f70657261746f72222926266f28722e6f776e65722926266f28722e6f70657261746f722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e20702865297b6c657420723d653b7472797b72657475726e206f28722e6f776e65722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e205f2865297b6c657420723d653b7472797b72657475726e206128702c722e7265717565737473297d63617463687b72657475726e21317d7d66756e6374696f6e20712865297b6c657420723d653b7472797b72657475726e207028722e726571756573742926264e756d6265722e6973496e746567657228722e62616c616e6365297d63617463687b72657475726e21317d7d66756e6374696f6e20672865297b6c657420723d653b7472797b72657475726e206928722e746f6b656e5f69642926266f28722e6f776e65722926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20642865297b72657475726e60746f6b656e2f247b657d607d66756e6374696f6e20522865297b6c657420723d642865293b6966284b762e676574287229297468726f77224641325f544f4b454e5f49445f455849535453223b4b762e73657428642865292c2130297d66756e6374696f6e204f2865297b696628214b762e676574286428652929297468726f77224641325f544f4b454e5f554e444546494e4544227d66756e6374696f6e206c28652c72297b72657475726e6062616c616e63652f247b657d2f247b727d607d66756e6374696f6e206d28652c72297b72657475726e204b762e676574286c28652c7229297c7c307d66756e6374696f6e206828652c722c6e297b6966286e3c30297468726f77224641325f494e53554646494349454e545f42414c414e4345223b4b762e736574286c28652c72292c6e297d66756e6374696f6e207528652c722c6e297b6c657420743d6d28652c72293b6828652c722c742b6e297d66756e6374696f6e207928652c722c6e2c74297b7528652c6e2c2d74292c7528722c6e2c74297d66756e6374696f6e206628652c722c6e297b72657475726e606f776e65722f247b657d2f247b727d2f247b6e7d607d66756e6374696f6e204928652c722c6e297b4b762e736574286628652c722c6e292c2130297d66756e6374696f6e206228652c722c6e297b4b762e64656c657465286628652c722c6e29297d66756e6374696f6e205428652c722c6e297b6966282128653d3d3d727c7c4b762e676574286628652c722c6e292929297468726f77224641325f4e4f545f4f50455241544f52227d66756e6374696f6e204228652c72297b69662865213d3d72297468726f7720636f6e736f6c652e6c6f672860247b657d20213d3d20247b727d60292c224641325f4e4f545f4f574e4552227d66756e6374696f6e204e28652c722c6e297b4f286e2e746f6b656e5f6964292c5428652c722c6e2e746f6b656e5f6964292c7928652c6e2e746f2c6e2e746f6b656e5f69642c6e2e616d6f756e74297d66756e6374696f6e207828652c72297b722e666f7245616368286e3d3e6e2e7472616e73666572732e666f724561636828743d3e4e286e2e66726f6d2c652c742929297d66756e6374696f6e207628652c72297b73776974636828722e6f7065726174696f6e297b63617365226164645f6f70657261746f72223a4228722e6f776e65722c65292c4928722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964293b627265616b3b636173652272656d6f76655f6f70657261746f72223a5428722e6f776e65722c652c722e746f6b656e5f6964292c6228722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964297d7d66756e6374696f6e20452865297b6c657420723d6d28652e6f776e65722c652e746f6b656e5f6964293b72657475726e20636f6e736f6c652e6c6f672860247b652e6f776e65727d2068617320247b727d206f6620746f6b656e20247b652e746f6b656e5f69647d60292c7b726571756573743a652c62616c616e63653a727d7d66756e6374696f6e20532865297b72657475726e20652e72657175657374732e6d61702845297d66756e6374696f6e204b2865297b5228652e746f6b656e5f6964292c7528652e6f776e65722c652e746f6b656e5f69642c652e616d6f756e74297d6173796e632066756e6374696f6e20552865297b6c657420723d6e65772055524c28652e75726c292c6e3d722e706174686e616d653b7472797b737769746368286e297b63617365222f70696e67223a72657475726e20636f6e736f6c652e6c6f67282248656c6c6f2066726f6d2072756e6e657220736d6172742066756e6374696f6e205c757b31463434427d22292c6e657720526573706f6e73652822506f6e6722293b63617365222f62616c616e63655f6f66223a696628652e6d6574686f643d3d3d2247455422297b6c657420733d7b72657175657374733a4a534f4e2e70617273652861746f6228722e736561726368506172616d732e67657428227265717565737473222929297d3b6966285f287329297b6c657420633d532873293b72657475726e20526573706f6e73652e6a736f6e2863297d656c73652072657475726e20636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c73292c526573706f6e73652e6572726f7228297d656c73657b6c657420733d222f62616c616e63655f6f662069732061204745542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7472616e73666572223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128772c73293f287828652e686561646572732e67657428225265666572657222292c73292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7472616e73666572206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f6d696e745f6e6577223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128672c73293f28732e666f7245616368284b292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f6d696e745f6e6577206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7570646174655f6f70657261746f7273223a696628652e6d6574686f643d3d3d2250555422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128412c73293f28732e666f724561636828633d3e7628652e686561646572732e67657428225265666572657222292c6329292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7570646174655f6f70657261746f72732069732061205055542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d64656661756c743a6c657420743d60556e7265636f676e6973656420656e747279706f696e7420247b6e7d603b72657475726e20636f6e736f6c652e6572726f722874292c6e657720526573706f6e736528742c7b7374617475733a3430347d297d7d63617463682874297b7468726f7720636f6e736f6c652e6572726f722874292c747d7d76617220463d553b6578706f72747b462061732064656661756c742c5f20617320697342616c616e63654f662c7020617320697342616c616e6365526571756573742c7120617320697342616c616e6365526573706f6e73652c672061732069734d696e744e65772c69206173206973546f6b656e49642c6b2061732069735472616e736665722c772061732069735472616e73666572732c412061732069735570646174654f70657261746f727d3b0a0000000000000000" ]' from bootstrap2 Node is bootstrapped. Estimated gas: 321.990 units (will add 100 for safety) Estimated storage: no bytes added @@ -84,7 +84,7 @@ GET http://[HOST]:[PORT]/global/block/head/state_hash "[SC_ROLLUP_PVM_STATE_HASH]" -./octez-client --wait none send smart rollup message 'hex:[ "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000227e53c7d8cf2817f4e12c58fd326c2dba91d2e798e46021f6d18ecd8b760692e04a218ded8909bf028ad9ea19249c9f74b1c7052353d5e20393afb38ca29108000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f6d696e745f6e65770400000000000000504f535400000000000000000193000000000000005b7b22746f6b656e5f6964223a302c226f776e6572223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c22616d6f756e74223a337d2c7b22746f6b656e5f6964223a312c226f776e6572223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c22616d6f756e74223a337d5da086010000000000" ]' from bootstrap2 +./octez-client --wait none send smart rollup message 'hex:[ "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000227e53c7d8cf2817f4e12c58fd326c2dba91d2e798e46021f6d18ecd8b760692e04a218ded8909bf028ad9ea19249c9f74b1c7052353d5e20393afb38ca29108000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f6d696e745f6e65770400000000000000504f535400000000000000000193000000000000005b7b22746f6b656e5f6964223a302c226f776e6572223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c22616d6f756e74223a337d2c7b22746f6b656e5f6964223a312c226f776e6572223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c22616d6f756e74223a337d5da086010000000000" ]' from bootstrap2 Node is bootstrapped. Estimated gas: 186.623 units (will add 100 for safety) Estimated storage: no bytes added @@ -117,7 +117,7 @@ External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519 External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("[PUBLIC_KEY]"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtcKVzbaGrjgEG5sZfTS7xAgRBjM5qwsQiWFMrTkzE1LCwgFNA5easvZRrp5aS92KJxTGL4shPNicpX42sur2RypukqMoxX"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("[PUBLIC_KEY_HASH]"))), nonce: Nonce(1), content: RunFunction(RunFunction { uri: tezos://[CONTRACT_HASH]/mint_new, method: POST, headers: {}, body: Some([91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 125, 44, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 44, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 51, 125, 93]), gas_limit: 100000 }) } } [JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"[CONTRACT_HASH]","request_id":"5be47d4bba79d676bee73ee20265975a0d0911ce0e9514973427f7b8c52ce00c"} [JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"[CONTRACT_HASH]","request_id":"5be47d4bba79d676bee73ee20265975a0d0911ce0e9514973427f7b8c52ce00c"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD8C4D8)) (in 598 instructions) +🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD534D8)) (in 598 instructions) Receipt: Receipt { hash: Blake2b([91, 228, 125, 75, 186, 121, 214, 118, 190, 231, 62, 226, 2, 101, 151, 90, 13, 9, 17, 206, 14, 149, 20, 151, 52, 39, 247, 184, 197, 44, 224, 12]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } Internal message: end of level @@ -131,7 +131,7 @@ GET http://[HOST]:[PORT]/global/block/head/state_hash "[SC_ROLLUP_PVM_STATE_HASH]" -./octez-client --wait none send smart rollup message 'hex:[ "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000af97f9f696782f8d65159b3e2d4c40a12af76a1f3fd7791693627b9a5754f3324d38912c4a55e8706b75d0445599bf65f35f97021ba8b23ea3507ba257e1700e000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a312c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" ]' from bootstrap2 +./octez-client --wait none send smart rollup message 'hex:[ "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000af97f9f696782f8d65159b3e2d4c40a12af76a1f3fd7791693627b9a5754f3324d38912c4a55e8706b75d0445599bf65f35f97021ba8b23ea3507ba257e1700e000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a312c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" ]' from bootstrap2 Node is bootstrapped. Estimated gas: 186.088 units (will add 100 for safety) Estimated storage: no bytes added @@ -164,7 +164,7 @@ External message: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519 External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd25519("[PUBLIC_KEY]"))), signature: Ed25519(Ed25519(Ed25519Signature("edsigtvnBAzRBZxFTzUkq6JyLkV1eqygpWszgapJP2pz98dH1rMa91Er56iigHtSr42xEetkHhhdLQatXpZ1sJoA1W2nFBqXt8Z"))), inner: Operation { source: Tz1(Tz1(ContractTz1Hash("[PUBLIC_KEY_HASH]"))), nonce: Nonce(2), content: RunFunction(RunFunction { uri: tezos://[CONTRACT_HASH]/transfer, method: POST, headers: {}, body: Some([91, 123, 34, 102, 114, 111, 109, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 114, 97, 110, 115, 102, 101, 114, 115, 34, 58, 91, 123, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 49, 44, 34, 116, 111, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 125, 93, 125, 93]), gas_limit: 100000 }) } } [JSTZ:SMART_FUNCTION:REQUEST_START] {"type":"Start","address":"[CONTRACT_HASH]","request_id":"11f887e7159544e30a833ac993cc05e8c2cf2adb2a7a7c74a856735acfeb210d"} [JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"[CONTRACT_HASH]","request_id":"11f887e7159544e30a833ac993cc05e8c2cf2adb2a7a7c74a856735acfeb210d"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD664D8)) (in 595 instructions) +🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD2D4D8)) (in 595 instructions) Receipt: Receipt { hash: Blake2b([17, 248, 135, 231, 21, 149, 68, 227, 10, 131, 58, 201, 147, 204, 5, 232, 194, 207, 42, 219, 42, 122, 124, 116, 168, 86, 115, 90, 207, 235, 33, 13]), result: Success(RunFunction(RunFunctionReceipt { body: Some([83, 117, 99, 99, 101, 115, 115, 33]), status_code: 200, headers: {"content-type": "text/plain;charset=UTF-8"} })) } Internal message: end of level @@ -178,7 +178,7 @@ GET http://[HOST]:[PORT]/global/block/head/state_hash "[SC_ROLLUP_PVM_STATE_HASH]" -./octez-client --wait none send smart rollup message 'hex:[ "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000065348f3eabd9ee6bceb2251ddf1f20b2ac8257398f7dc51d1839e25aabb2f7ab262d84c54b55eb9e93edca1a41b8c457d1add6cfb2085bc465dd9c6cdffb307000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395030000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e5867696656303d0300000000000000474554000000000000000000a086010000000000" ]' from bootstrap2 +./octez-client --wait none send smart rollup message 'hex:[ "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000065348f3eabd9ee6bceb2251ddf1f20b2ac8257398f7dc51d1839e25aabb2f7ab262d84c54b55eb9e93edca1a41b8c457d1add6cfb2085bc465dd9c6cdffb307000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395030000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e5867696656303d0300000000000000474554000000000000000000a086010000000000" ]' from bootstrap2 Node is bootstrapped. Estimated gas: 187.541 units (will add 100 for safety) Estimated storage: no bytes added @@ -213,7 +213,7 @@ External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd255 [JSTZ:SMART_FUNCTION:LOG] {"address":"[CONTRACT_HASH]","request_id":"897d44fa06cf4d675da0734235e4400329ba0cc5cdac0a75a302783ef89d6422","level":"LOG","text":"[PUBLIC_KEY_HASH] has 2 of token 0"} [JSTZ:SMART_FUNCTION:LOG] {"address":"[CONTRACT_HASH]","request_id":"897d44fa06cf4d675da0734235e4400329ba0cc5cdac0a75a302783ef89d6422","level":"LOG","text":"[PUBLIC_KEY_HASH] has 0 of token 1"} [JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"[CONTRACT_HASH]","request_id":"897d44fa06cf4d675da0734235e4400329ba0cc5cdac0a75a302783ef89d6422"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD53B38)) (in 435 instructions) +🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD1AB38)) (in 435 instructions) Receipt: Receipt { hash: Blake2b([137, 125, 68, 250, 6, 207, 77, 103, 93, 160, 115, 66, 53, 228, 64, 3, 41, 186, 12, 197, 205, 172, 10, 117, 163, 2, 120, 62, 248, 157, 100, 34]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 50, 46, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 48, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 101, 65, 107, 83, 87, 121, 100, 120, 50, 70, 69, 66, 51, 68, 82, 99, 105, 101, 115, 103, 56, 82, 99, 75, 100, 89, 111, 120, 65, 114, 57, 53, 120, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } Internal message: end of level @@ -227,7 +227,7 @@ GET http://[HOST]:[PORT]/global/block/head/state_hash "[SC_ROLLUP_PVM_STATE_HASH]" -./octez-client --wait none send smart rollup message 'hex:[ "00b0253ef541d35f2597e3fff3ddd09091ff2df2c00000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000a2e897572537ed28fab5f10c6f377233302255aa73ae2beedbf472e59610febf991384c128c486fcb0c4772d00c32b870f2437c2ff95bbe6315d47d1f2bf000b000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040000000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d6147457954455934654645696656303d0300000000000000474554000000000000000000a086010000000000" ]' from bootstrap2 +./octez-client --wait none send smart rollup message 'hex:[ "000a82a7c758c07af445d3bbdecc99ca51a01894150000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000a2e897572537ed28fab5f10c6f377233302255aa73ae2beedbf472e59610febf991384c128c486fcb0c4772d00c32b870f2437c2ff95bbe6315d47d1f2bf000b000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040000000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d6147457954455934654645696656303d0300000000000000474554000000000000000000a086010000000000" ]' from bootstrap2 Node is bootstrapped. Estimated gas: 187.541 units (will add 100 for safety) Estimated storage: no bytes added @@ -262,7 +262,7 @@ External operation: SignedOperation { public_key: Ed25519(Ed25519(PublicKeyEd255 [JSTZ:SMART_FUNCTION:LOG] {"address":"[CONTRACT_HASH]","request_id":"f476459bf09e3b78c602823f1821cbc88e99c9472f9b300858e218edf42d6577","level":"LOG","text":"[PUBLIC_KEY_HASH] has 1 of token 0"} [JSTZ:SMART_FUNCTION:LOG] {"address":"[CONTRACT_HASH]","request_id":"f476459bf09e3b78c602823f1821cbc88e99c9472f9b300858e218edf42d6577","level":"LOG","text":"[PUBLIC_KEY_HASH] has 3 of token 1"} [JSTZ:SMART_FUNCTION:REQUEST_END] {"type":"End","address":"[CONTRACT_HASH]","request_id":"f476459bf09e3b78c602823f1821cbc88e99c9472f9b300858e218edf42d6577"} -🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD3EB28)) (in 434 instructions) +🚀 Smart function executed successfully with value: Ok(Object(("jstz_api::http::response::Response") 0xD05B28)) (in 434 instructions) Receipt: Receipt { hash: Blake2b([244, 118, 69, 155, 240, 158, 59, 120, 198, 2, 130, 63, 24, 33, 203, 200, 142, 153, 201, 71, 47, 155, 48, 8, 88, 226, 24, 237, 244, 45, 101, 119]), result: Success(RunFunction(RunFunctionReceipt { body: Some([91, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 49, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 48, 125, 125, 44, 123, 34, 98, 97, 108, 97, 110, 99, 101, 34, 58, 51, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 111, 119, 110, 101, 114, 34, 58, 34, 116, 122, 49, 104, 80, 121, 104, 54, 120, 107, 97, 106, 97, 76, 98, 84, 75, 106, 72, 118, 75, 115, 56, 51, 107, 101, 105, 76, 104, 97, 50, 76, 70, 56, 120, 81, 34, 44, 34, 116, 111, 107, 101, 110, 95, 105, 100, 34, 58, 49, 125, 125, 93]), status_code: 200, headers: {"content-type": "application/json"} })) } Internal message: end of level diff --git a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with messages (external).out b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with messages (external).out index a3689410ddd1d7f2c9cdf167452b94f073120a33..fdd8b466a247f2b3c4eeb9108625429d5b5e6d0f 100644 Binary files a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with messages (external).out and b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - node advances PVM state with messages (external).out differ diff --git a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - origination of a SCORU executes without error.out b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - origination of a SCORU executes without error.out index afc16a93794ec6304a33164ab7d3a6290fcbfe05..e687e1376c8cd6cdb503beae39e5c1279cace594 100644 --- a/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - origination of a SCORU executes without error.out +++ b/tezt/tests/expected/sc_rollup.ml/Alpha- riscv - origination of a SCORU executes without error.out @@ -1,5 +1,5 @@ -./octez-client --wait none originate smart rollup rollup from bootstrap1 of kind riscv of type string with kernel kernel:src/riscv/assets/riscv-dummy.elf:c0047e2a561e90aa2e1ae691c9e12798d4c44cccf991f1505a7da59b74f37e83 --burn-cap 9999999 +./octez-client --wait none originate smart rollup rollup from bootstrap1 of kind riscv of type string with kernel kernel:src/riscv/assets/riscv-dummy.elf:3be75e2383b68ba09e834d80b1bcd4710649fdebce130bfeb784719821453249 --burn-cap 9999999 Node is bootstrapped. Estimated gas: 1933.839 units (will add 100 for safety) Estimated storage: 6552 bytes added (will add 20 for safety) @@ -22,7 +22,7 @@ This sequence of operations was run: Smart rollup origination: Kind: riscv Parameter type: string - Kernel Blake2B hash: '960f98f44a4beb8c1cbc661bbed4eb66cab506ea6685e33be37e97ad4e02564b' + Kernel Blake2B hash: 'd88099ee9b91fcad2e0d7d3c62346149f07634ad53772034ee5be4c3f9b7cae7' This smart rollup origination was successfully applied Consumed gas: 1933.806 Storage size: 6552 bytes diff --git a/tezt/tests/riscv-tests/jstz-inbox.json b/tezt/tests/riscv-tests/jstz-inbox.json index 2f313c8111ebbe14aedd7ef3034650dadc9f6d0e..a6225a7b029ae200b117eb865ada730e097595d3 100644 --- a/tezt/tests/riscv-tests/jstz-inbox.json +++ b/tezt/tests/riscv-tests/jstz-inbox.json @@ -1,19 +1,19 @@ [ [ { - "external": "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000004fd8b650f46128bf5c7edfe3c72de5ae420da3b4a78843eca103e14514efc6c337819ebef88b499d613371d6d3e6bb081745562b87172fe41105cd759eb75100000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395000000000000000000000000b80e00000000000066756e6374696f6e206f2865297b72657475726e20747970656f6620653d3d22737472696e67227d66756e6374696f6e206128652c72297b72657475726e2041727261792e697341727261792872292626722e72656475636528286e2c74293d3e6e2626652874292c2130297d66756e6374696f6e20692865297b72657475726e20747970656f6620653d3d226e756d6265722226264e756d6265722e6973496e74656765722865297d66756e6374696f6e206b2865297b6c657420723d653b7472797b72657475726e206f28722e746f2926266928722e746f6b656e5f69642926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20772865297b6c657420723d653b7472797b72657475726e206f28722e66726f6d29262661286b2c722e7472616e7366657273297d63617463687b72657475726e21317d7d66756e6374696f6e20412865297b6c657420723d653b7472797b72657475726e28722e6f7065726174696f6e3d3d3d226164645f6f70657261746f72227c7c722e6f7065726174696f6e3d3d3d2272656d6f76655f6f70657261746f72222926266f28722e6f776e65722926266f28722e6f70657261746f722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e20702865297b6c657420723d653b7472797b72657475726e206f28722e6f776e65722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e205f2865297b6c657420723d653b7472797b72657475726e206128702c722e7265717565737473297d63617463687b72657475726e21317d7d66756e6374696f6e20712865297b6c657420723d653b7472797b72657475726e207028722e726571756573742926264e756d6265722e6973496e746567657228722e62616c616e6365297d63617463687b72657475726e21317d7d66756e6374696f6e20672865297b6c657420723d653b7472797b72657475726e206928722e746f6b656e5f69642926266f28722e6f776e65722926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20642865297b72657475726e60746f6b656e2f247b657d607d66756e6374696f6e20522865297b6c657420723d642865293b6966284b762e676574287229297468726f77224641325f544f4b454e5f49445f455849535453223b4b762e73657428642865292c2130297d66756e6374696f6e204f2865297b696628214b762e676574286428652929297468726f77224641325f544f4b454e5f554e444546494e4544227d66756e6374696f6e206c28652c72297b72657475726e6062616c616e63652f247b657d2f247b727d607d66756e6374696f6e206d28652c72297b72657475726e204b762e676574286c28652c7229297c7c307d66756e6374696f6e206828652c722c6e297b6966286e3c30297468726f77224641325f494e53554646494349454e545f42414c414e4345223b4b762e736574286c28652c72292c6e297d66756e6374696f6e207528652c722c6e297b6c657420743d6d28652c72293b6828652c722c742b6e297d66756e6374696f6e207928652c722c6e2c74297b7528652c6e2c2d74292c7528722c6e2c74297d66756e6374696f6e206628652c722c6e297b72657475726e606f776e65722f247b657d2f247b727d2f247b6e7d607d66756e6374696f6e204928652c722c6e297b4b762e736574286628652c722c6e292c2130297d66756e6374696f6e206228652c722c6e297b4b762e64656c657465286628652c722c6e29297d66756e6374696f6e205428652c722c6e297b6966282128653d3d3d727c7c4b762e676574286628652c722c6e292929297468726f77224641325f4e4f545f4f50455241544f52227d66756e6374696f6e204228652c72297b69662865213d3d72297468726f7720636f6e736f6c652e6c6f672860247b657d20213d3d20247b727d60292c224641325f4e4f545f4f574e4552227d66756e6374696f6e204e28652c722c6e297b4f286e2e746f6b656e5f6964292c5428652c722c6e2e746f6b656e5f6964292c7928652c6e2e746f2c6e2e746f6b656e5f69642c6e2e616d6f756e74297d66756e6374696f6e207828652c72297b722e666f7245616368286e3d3e6e2e7472616e73666572732e666f724561636828743d3e4e286e2e66726f6d2c652c742929297d66756e6374696f6e207628652c72297b73776974636828722e6f7065726174696f6e297b63617365226164645f6f70657261746f72223a4228722e6f776e65722c65292c4928722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964293b627265616b3b636173652272656d6f76655f6f70657261746f72223a5428722e6f776e65722c652c722e746f6b656e5f6964292c6228722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964297d7d66756e6374696f6e20452865297b6c657420723d6d28652e6f776e65722c652e746f6b656e5f6964293b72657475726e20636f6e736f6c652e6c6f672860247b652e6f776e65727d2068617320247b727d206f6620746f6b656e20247b652e746f6b656e5f69647d60292c7b726571756573743a652c62616c616e63653a727d7d66756e6374696f6e20532865297b72657475726e20652e72657175657374732e6d61702845297d66756e6374696f6e204b2865297b5228652e746f6b656e5f6964292c7528652e6f776e65722c652e746f6b656e5f69642c652e616d6f756e74297d6173796e632066756e6374696f6e20552865297b6c657420723d6e65772055524c28652e75726c292c6e3d722e706174686e616d653b7472797b737769746368286e297b63617365222f70696e67223a72657475726e20636f6e736f6c652e6c6f67282248656c6c6f2066726f6d2072756e6e657220736d6172742066756e6374696f6e205c757b31463434427d22292c6e657720526573706f6e73652822506f6e6722293b63617365222f62616c616e63655f6f66223a696628652e6d6574686f643d3d3d2247455422297b6c657420733d7b72657175657374733a4a534f4e2e70617273652861746f6228722e736561726368506172616d732e67657428227265717565737473222929297d3b6966285f287329297b6c657420633d532873293b72657475726e20526573706f6e73652e6a736f6e2863297d656c73652072657475726e20636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c73292c526573706f6e73652e6572726f7228297d656c73657b6c657420733d222f62616c616e63655f6f662069732061204745542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7472616e73666572223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128772c73293f287828652e686561646572732e67657428225265666572657222292c73292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7472616e73666572206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f6d696e745f6e6577223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128672c73293f28732e666f7245616368284b292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f6d696e745f6e6577206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7570646174655f6f70657261746f7273223a696628652e6d6574686f643d3d3d2250555422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128412c73293f28732e666f724561636828633d3e7628652e686561646572732e67657428225265666572657222292c6329292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7570646174655f6f70657261746f72732069732061205055542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d64656661756c743a6c657420743d60556e7265636f676e6973656420656e747279706f696e7420247b6e7d603b72657475726e20636f6e736f6c652e6572726f722874292c6e657720526573706f6e736528742c7b7374617475733a3430347d297d7d63617463682874297b7468726f7720636f6e736f6c652e6572726f722874292c747d7d76617220463d553b6578706f72747b462061732064656661756c742c5f20617320697342616c616e63654f662c7020617320697342616c616e6365526571756573742c7120617320697342616c616e6365526573706f6e73652c672061732069734d696e744e65772c69206173206973546f6b656e49642c6b2061732069735472616e736665722c772061732069735472616e73666572732c412061732069735570646174654f70657261746f727d3b0a0000000000000000" + "external": "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a0000000040000000000000004fd8b650f46128bf5c7edfe3c72de5ae420da3b4a78843eca103e14514efc6c337819ebef88b499d613371d6d3e6bb081745562b87172fe41105cd759eb75100000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395000000000000000000000000b80e00000000000066756e6374696f6e206f2865297b72657475726e20747970656f6620653d3d22737472696e67227d66756e6374696f6e206128652c72297b72657475726e2041727261792e697341727261792872292626722e72656475636528286e2c74293d3e6e2626652874292c2130297d66756e6374696f6e20692865297b72657475726e20747970656f6620653d3d226e756d6265722226264e756d6265722e6973496e74656765722865297d66756e6374696f6e206b2865297b6c657420723d653b7472797b72657475726e206f28722e746f2926266928722e746f6b656e5f69642926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20772865297b6c657420723d653b7472797b72657475726e206f28722e66726f6d29262661286b2c722e7472616e7366657273297d63617463687b72657475726e21317d7d66756e6374696f6e20412865297b6c657420723d653b7472797b72657475726e28722e6f7065726174696f6e3d3d3d226164645f6f70657261746f72227c7c722e6f7065726174696f6e3d3d3d2272656d6f76655f6f70657261746f72222926266f28722e6f776e65722926266f28722e6f70657261746f722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e20702865297b6c657420723d653b7472797b72657475726e206f28722e6f776e65722926266928722e746f6b656e5f6964297d63617463687b72657475726e21317d7d66756e6374696f6e205f2865297b6c657420723d653b7472797b72657475726e206128702c722e7265717565737473297d63617463687b72657475726e21317d7d66756e6374696f6e20712865297b6c657420723d653b7472797b72657475726e207028722e726571756573742926264e756d6265722e6973496e746567657228722e62616c616e6365297d63617463687b72657475726e21317d7d66756e6374696f6e20672865297b6c657420723d653b7472797b72657475726e206928722e746f6b656e5f69642926266f28722e6f776e65722926264e756d6265722e6973496e746567657228722e616d6f756e74297d63617463687b72657475726e21317d7d66756e6374696f6e20642865297b72657475726e60746f6b656e2f247b657d607d66756e6374696f6e20522865297b6c657420723d642865293b6966284b762e676574287229297468726f77224641325f544f4b454e5f49445f455849535453223b4b762e73657428642865292c2130297d66756e6374696f6e204f2865297b696628214b762e676574286428652929297468726f77224641325f544f4b454e5f554e444546494e4544227d66756e6374696f6e206c28652c72297b72657475726e6062616c616e63652f247b657d2f247b727d607d66756e6374696f6e206d28652c72297b72657475726e204b762e676574286c28652c7229297c7c307d66756e6374696f6e206828652c722c6e297b6966286e3c30297468726f77224641325f494e53554646494349454e545f42414c414e4345223b4b762e736574286c28652c72292c6e297d66756e6374696f6e207528652c722c6e297b6c657420743d6d28652c72293b6828652c722c742b6e297d66756e6374696f6e207928652c722c6e2c74297b7528652c6e2c2d74292c7528722c6e2c74297d66756e6374696f6e206628652c722c6e297b72657475726e606f776e65722f247b657d2f247b727d2f247b6e7d607d66756e6374696f6e204928652c722c6e297b4b762e736574286628652c722c6e292c2130297d66756e6374696f6e206228652c722c6e297b4b762e64656c657465286628652c722c6e29297d66756e6374696f6e205428652c722c6e297b6966282128653d3d3d727c7c4b762e676574286628652c722c6e292929297468726f77224641325f4e4f545f4f50455241544f52227d66756e6374696f6e204228652c72297b69662865213d3d72297468726f7720636f6e736f6c652e6c6f672860247b657d20213d3d20247b727d60292c224641325f4e4f545f4f574e4552227d66756e6374696f6e204e28652c722c6e297b4f286e2e746f6b656e5f6964292c5428652c722c6e2e746f6b656e5f6964292c7928652c6e2e746f2c6e2e746f6b656e5f69642c6e2e616d6f756e74297d66756e6374696f6e207828652c72297b722e666f7245616368286e3d3e6e2e7472616e73666572732e666f724561636828743d3e4e286e2e66726f6d2c652c742929297d66756e6374696f6e207628652c72297b73776974636828722e6f7065726174696f6e297b63617365226164645f6f70657261746f72223a4228722e6f776e65722c65292c4928722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964293b627265616b3b636173652272656d6f76655f6f70657261746f72223a5428722e6f776e65722c652c722e746f6b656e5f6964292c6228722e6f776e65722c722e6f70657261746f722c722e746f6b656e5f6964297d7d66756e6374696f6e20452865297b6c657420723d6d28652e6f776e65722c652e746f6b656e5f6964293b72657475726e20636f6e736f6c652e6c6f672860247b652e6f776e65727d2068617320247b727d206f6620746f6b656e20247b652e746f6b656e5f69647d60292c7b726571756573743a652c62616c616e63653a727d7d66756e6374696f6e20532865297b72657475726e20652e72657175657374732e6d61702845297d66756e6374696f6e204b2865297b5228652e746f6b656e5f6964292c7528652e6f776e65722c652e746f6b656e5f69642c652e616d6f756e74297d6173796e632066756e6374696f6e20552865297b6c657420723d6e65772055524c28652e75726c292c6e3d722e706174686e616d653b7472797b737769746368286e297b63617365222f70696e67223a72657475726e20636f6e736f6c652e6c6f67282248656c6c6f2066726f6d2072756e6e657220736d6172742066756e6374696f6e205c757b31463434427d22292c6e657720526573706f6e73652822506f6e6722293b63617365222f62616c616e63655f6f66223a696628652e6d6574686f643d3d3d2247455422297b6c657420733d7b72657175657374733a4a534f4e2e70617273652861746f6228722e736561726368506172616d732e67657428227265717565737473222929297d3b6966285f287329297b6c657420633d532873293b72657475726e20526573706f6e73652e6a736f6e2863297d656c73652072657475726e20636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c73292c526573706f6e73652e6572726f7228297d656c73657b6c657420733d222f62616c616e63655f6f662069732061204745542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7472616e73666572223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128772c73293f287828652e686561646572732e67657428225265666572657222292c73292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7472616e73666572206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f6d696e745f6e6577223a696628652e6d6574686f643d3d3d22504f535422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128672c73293f28732e666f7245616368284b292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f6d696e745f6e6577206973206120504f53542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d63617365222f7570646174655f6f70657261746f7273223a696628652e6d6574686f643d3d3d2250555422297b6c657420733d617761697420652e6a736f6e28293b72657475726e206128412c73293f28732e666f724561636828633d3e7628652e686561646572732e67657428225265666572657222292c6329292c6e657720526573706f6e7365282253756363657373212229293a28636f6e736f6c652e6572726f722822496e76616c696420706172616d6574657273222c4a534f4e2e737472696e67696679287329292c526573706f6e73652e6572726f722829297d656c73657b6c657420733d222f7570646174655f6f70657261746f72732069732061205055542072657175657374223b72657475726e20636f6e736f6c652e6572726f722873292c6e657720526573706f6e736528732c7b7374617475733a3530307d297d64656661756c743a6c657420743d60556e7265636f676e6973656420656e747279706f696e7420247b6e7d603b72657475726e20636f6e736f6c652e6572726f722874292c6e657720526573706f6e736528742c7b7374617475733a3430347d297d7d63617463682874297b7468726f7720636f6e736f6c652e6572726f722874292c747d7d76617220463d553b6578706f72747b462061732064656661756c742c5f20617320697342616c616e63654f662c7020617320697342616c616e6365526571756573742c7120617320697342616c616e6365526573706f6e73652c672061732069734d696e744e65772c69206173206973546f6b656e49642c6b2061732069735472616e736665722c772061732069735472616e73666572732c412061732069735570646174654f70657261746f727d3b0a0000000000000000" }, { - "external": "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000227e53c7d8cf2817f4e12c58fd326c2dba91d2e798e46021f6d18ecd8b760692e04a218ded8909bf028ad9ea19249c9f74b1c7052353d5e20393afb38ca29108000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f6d696e745f6e65770400000000000000504f535400000000000000000193000000000000005b7b22746f6b656e5f6964223a302c226f776e6572223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c22616d6f756e74223a337d2c7b22746f6b656e5f6964223a312c226f776e6572223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c22616d6f756e74223a337d5da086010000000000" + "external": "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000227e53c7d8cf2817f4e12c58fd326c2dba91d2e798e46021f6d18ecd8b760692e04a218ded8909bf028ad9ea19249c9f74b1c7052353d5e20393afb38ca29108000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395010000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f6d696e745f6e65770400000000000000504f535400000000000000000193000000000000005b7b22746f6b656e5f6964223a302c226f776e6572223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c22616d6f756e74223a337d2c7b22746f6b656e5f6964223a312c226f776e6572223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851222c22616d6f756e74223a337d5da086010000000000" }, { - "external": "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000af97f9f696782f8d65159b3e2d4c40a12af76a1f3fd7791693627b9a5754f3324d38912c4a55e8706b75d0445599bf65f35f97021ba8b23ea3507ba257e1700e000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a312c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" + "external": "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000af97f9f696782f8d65159b3e2d4c40a12af76a1f3fd7791693627b9a5754f3324d38912c4a55e8706b75d0445599bf65f35f97021ba8b23ea3507ba257e1700e000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395020000000000000001000000350000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f7472616e736665720400000000000000504f535400000000000000000185000000000000005b7b2266726f6d223a22747a3165416b53577964783246454233445263696573673852634b64596f784172393578222c227472616e7366657273223a5b7b22746f6b656e5f6964223a302c22616d6f756e74223a312c22746f223a22747a316850796836786b616a614c62544b6a48764b7338336b65694c6861324c46387851227d5d7d5da086010000000000" }, { - "external": "00b0253ef541d35f2597e3fff3ddd09091ff2df2c0000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000065348f3eabd9ee6bceb2251ddf1f20b2ac8257398f7dc51d1839e25aabb2f7ab262d84c54b55eb9e93edca1a41b8c457d1add6cfb2085bc465dd9c6cdffb307000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395030000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e5867696656303d0300000000000000474554000000000000000000a086010000000000" + "external": "000a82a7c758c07af445d3bbdecc99ca51a0189415000000002000000000000000edadfe6cfe7b716a47cb43d7901504fefef6e3c2d4c84decaa7ec5d8c404800a000000004000000000000000065348f3eabd9ee6bceb2251ddf1f20b2ac8257398f7dc51d1839e25aabb2f7ab262d84c54b55eb9e93edca1a41b8c457d1add6cfb2085bc465dd9c6cdffb307000000001400000000000000cb4197b79836b135dc5bf07449b0d058fb84a395030000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f785a554672553164355a486779526b56434d30525359326c6c63326334556d4e4c5a466c76654546794f545634496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466c5157745456336c6b65444a475255497a52464a6a6157567a5a7a68535930746b57573934515849354e5867696656303d0300000000000000474554000000000000000000a086010000000000" }, { - "external": "00b0253ef541d35f2597e3fff3ddd09091ff2df2c00000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000a2e897572537ed28fab5f10c6f377233302255aa73ae2beedbf472e59610febf991384c128c486fcb0c4772d00c32b870f2437c2ff95bbe6315d47d1f2bf000b000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040000000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d6147457954455934654645696656303d0300000000000000474554000000000000000000a086010000000000" + "external": "000a82a7c758c07af445d3bbdecc99ca51a01894150000000020000000000000005cc6bbdb7c8217a70e79f54c50ae51fb73cef19d18d7bc66c87135b1fcf073a9000000004000000000000000a2e897572537ed28fab5f10c6f377233302255aa73ae2beedbf472e59610febf991384c128c486fcb0c4772d00c32b870f2437c2ff95bbe6315d47d1f2bf000b000000001400000000000000eeaa7074a27463d707bf4b28e70460c70d3b7040000000000000000001000000e90000000000000074657a6f733a2f2f4b543139443370534d4463324363357561636b74716648485570484876655371547769622f62616c616e63655f6f663f72657175657374733d57337369644739725a573566615751694f6a4173496d3933626d5679496a6f6964486f786146423561445a3461324671595578695645747153485a4c637a677a61325670544768684d6b78474f486852496e307365794a306232746c626c39705a4349364d537769623364755a5849694f694a30656a466f55486c6f4e6e68725957706854474a5553327049646b747a4f444e725a576c4d6147457954455934654645696656303d0300000000000000474554000000000000000000a086010000000000" } ] ] \ No newline at end of file