From a331d897593e34f427be6e54959fb75cf85ba457 Mon Sep 17 00:00:00 2001 From: Marcos Pernambuco Motta <1091485+mpernambuco@users.noreply.github> Date: Sat, 11 Apr 2026 20:01:55 -0300 Subject: [PATCH] feat: improve cpp to solidity conversion --- .github/workflows/test.yml | 9 +- Makefile | 45 +++- README.md | 178 +++++++++------ helper_scripts/generate_SendCmioResponse.sh | 54 ----- helper_scripts/generate_UArchReset.sh | 42 ---- helper_scripts/generate_UArchSolidity.lua | 204 +++++++++++++++++ helper_scripts/generate_UArchStep.sh | 64 ------ .../test_generate_UArchSolidity.lua | 207 ++++++++++++++++++ src/AccessLogs.sol | 2 - src/EmulatorCompat.sol | 36 ++- src/EmulatorConstants.sol | 2 +- src/MetaStep.sol | 8 +- src/SendCmioResponse.sol | 36 +-- src/UArchReset.sol | 9 +- src/UArchStep.sol | 44 ++-- templates/SendCmioResponse.sol.template | 32 --- templates/UArchReset.sol.template | 29 --- templates/UArchStep.sol.template | 29 --- test/AccessLogs.t.sol | 26 +-- test/Memory.t.sol | 14 +- test/SendCmioResponse.t.sol | 9 +- test/UArchReset.t.sol | 5 +- 22 files changed, 654 insertions(+), 430 deletions(-) delete mode 100755 helper_scripts/generate_SendCmioResponse.sh delete mode 100755 helper_scripts/generate_UArchReset.sh create mode 100644 helper_scripts/generate_UArchSolidity.lua delete mode 100755 helper_scripts/generate_UArchStep.sh create mode 100644 helper_scripts/test_generate_UArchSolidity.lua delete mode 100644 templates/SendCmioResponse.sol.template delete mode 100644 templates/UArchReset.sol.template delete mode 100644 templates/UArchStep.sol.template diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0fd7143..bc40eb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,13 +8,16 @@ jobs: with: submodules: recursive - - name: Install gpp - run: sudo apt-get install -y gpp + - name: Install gpp and lua + run: sudo apt-get install -y gpp lua5.4 liblua5.4-dev luarocks + + - name: Install LPEG + run: sudo luarocks --lua-version=5.4 install lpeg - uses: ./.github/actions/setup - name: Run all tests - run: make test-all + run: eval "$(luarocks --lua-version=5.4 path)" && make test-all coverage-mock: runs-on: ubuntu-24.04 diff --git a/Makefile b/Makefile index df7b10e..f1520e9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ EMULATOR_DIR ?= ../emulator +LUA ?= lua5.4 TEST_DIR := test DOWNLOADDIR := downloads SRC_DIR := src @@ -36,18 +37,30 @@ help: @echo ' generate-prod - generate production library code' @echo ' generate-replay - generate replay tests' @echo ' pretest - download necessary files for tests' + @echo ' local-dep - use test data from local emulator build' @echo ' submodules - initialize and update git submodules' @echo ' fmt - format solidity sources with forge fmt' @echo ' test-all - test all' + @echo ' test-transpiler - test the C++ to Solidity transpiler' @echo ' test-mock - test binary files with mock library' @echo ' test-prod - test production code' @echo ' test-replay - test log files' + @echo ' env-check - check that all required tools are installed' @echo 'Coverage targets:' @echo ' coverage-mock - generate coverage info for mock library tests' @echo ' coverage-prod - generate coverage info for production code tests' @echo ' coverage-report - aggregate coverage info and generate html report' +env-check: + @echo "Checking development environment..." + @$(LUA) -v 2>&1 || { echo "MISSING: lua5.4 -- see README.md"; exit 1; } + @$(LUA) -e 'require("lpeg")' 2>/dev/null || { echo "MISSING: lpeg -- see README.md"; exit 1; } + @forge --version 2>&1 || { echo "MISSING: foundry -- see README.md"; exit 1; } + @gpp --version >/dev/null 2>&1 || { echo "MISSING: gpp -- see README.md"; exit 1; } + @$(SED) --version >/dev/null 2>&1 || { echo "MISSING: GNU sed (set SED=gsed on macOS) -- see README.md"; exit 1; } + @echo "All tools found." + all: build test-all build: generate-step generate-reset generate-send-cmio-response generate-constants generate-prod @@ -58,7 +71,10 @@ clean: rm -rf $(DEPDIRS) $(DOWNLOADDIR) forge clean -test-all: +test-transpiler: + $(LUA) helper_scripts/test_generate_UArchSolidity.lua + +test-all: test-transpiler $(MAKE) test-mock $(MAKE) test-replay $(MAKE) test-prod @@ -112,14 +128,19 @@ generate-replay: generate-constants: $(EMULATOR_DIR) EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_EmulatorConstants.sh -generate-step: $(EMULATOR_DIR)/src/uarch-step.h $(EMULATOR_DIR)/src/uarch-step.cpp - EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_UArchStep.sh +GEN_SOL = $(LUA) helper_scripts/generate_UArchSolidity.lua + +generate-step: $(EMULATOR_DIR)/src/uarch-step.hpp $(EMULATOR_DIR)/src/uarch-step.cpp + $(GEN_SOL) $(EMULATOR_DIR)/src/uarch-step.cpp src/UArchStep.sol UArchStep step + forge fmt src/UArchStep.sol generate-reset: $(EMULATOR_DIR)/src/uarch-reset-state.cpp - EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_UArchReset.sh + $(GEN_SOL) $(EMULATOR_DIR)/src/uarch-reset-state.cpp src/UArchReset.sol UArchReset reset + forge fmt src/UArchReset.sol -generate-send-cmio-response: $(EMULATOR_DIR)/src/uarch-reset-state.cpp - EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_SendCmioResponse.sh +generate-send-cmio-response: $(EMULATOR_DIR)/src/send-cmio-response.cpp + $(GEN_SOL) $(EMULATOR_DIR)/src/send-cmio-response.cpp src/SendCmioResponse.sol SendCmioResponse sendCmioResponse + forge fmt src/SendCmioResponse.sol fmt: forge fmt src test @@ -130,6 +151,16 @@ pretest: dep dep: $(DEPDIRS) +EMULATOR_TESTS_DIR = $(EMULATOR_DIR)/tests +EMULATOR_UARCH_BIN_DIR = $(EMULATOR_TESTS_DIR)/build/uarch +EMULATOR_UARCH_LOG_DIR = $(EMULATOR_TESTS_DIR)/build/uarch-riscv-tests-json-logs + +local-dep: $(EMULATOR_UARCH_BIN_DIR) $(EMULATOR_UARCH_LOG_DIR) + rm -rf $(TESTS_DATA_DIR) $(LOG_TEST_DIR) + mkdir -p $(TESTS_DATA_DIR) $(LOG_TEST_DIR) + cp $(EMULATOR_UARCH_BIN_DIR)/*.bin $(TESTS_DATA_DIR)/ + cp $(EMULATOR_UARCH_LOG_DIR)/*.json $(LOG_TEST_DIR)/ + $(DOWNLOADDIR): @mkdir -p $(DOWNLOADDIR) @wget -nc $(TESTS_DATA_DOWNLOAD_URL) -P $(DOWNLOADDIR) @@ -154,4 +185,4 @@ $(LOG_TEST_DIR): | download submodules: git submodule update --init --recursive -.PHONY: help all build clean checksum-download shasum-download fmt generate-mock generate-prod generate-replay generate-step download pretest dep submodules test-all test-mock test-prod test-replay generate-constants generate-reset generate-send-cmio-response coverage-mock coverage-prod coverage-report +.PHONY: help all build clean checksum-download shasum-download fmt generate-mock generate-prod generate-replay generate-step download pretest dep local-dep submodules test-all test-mock test-prod test-replay test-transpiler env-check generate-constants generate-reset generate-send-cmio-response coverage-mock coverage-prod coverage-report diff --git a/README.md b/README.md index 27becbb..603ec58 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,152 @@ # Cartesi RISC-V Solidity Emulator -The Cartesi RISC-V Solidity Emulator is the on-chain host implementation of the Cartesi Machine Specification. The libraries and contracts are written in Solidity, and the testing scripts are written in Solidity (with the help of [Foundry](https://github.com/foundry-rs/foundry)). **_Do not recommend running `forge` command directly, as there are many scripts and templates working together. Please use `make` instead._** +On-chain Solidity implementation of the [Cartesi Machine](https://github.com/cartesi/machine-emulator) state transition function. The core libraries (`UArchStep`, `UArchReset`, `SendCmioResponse`) are transpiled directly from the emulator's C++ sources to guarantee bit-exact equivalence. -For Cartesi's design to work, this implementation must have the exact transition function as the off-chain [Cartesi RISC-V Emulator](https://github.com/cartesi/machine-emulator), meaning that if given the same initial state (s[i]) both implementation's step functions should reach a bit by bit consistent state s[i + 1]. +> Use `make` for all build and test operations -- code generation must run before compilation. -Since the cost of storing a full Cartesi Machine state within the blockchain is prohibitive, all machine states are represented in the blockchain as cryptographic hashes. The contents of those states and memory represented by those hashes are only known off-chain. +## Quick start -Cartesi uses Merkle tree operations and properties to ensure that the blockchain has the ability to correctly verify a state transition without having full state-access. However, the RISC-V Solidity emulator abstracts these operations away and acts as if it knows the full contents of a machine state - it uses the Memory Manager interface to fetch or write any necessary words to memory. +Set up your environment, then build and test: -## AccessLogs + export EMULATOR_DIR=../machine-emulator # path to machine-emulator checkout + export SED=gsed # macOS only (brew install gnu-sed) -The `AccessLogs.Context` struct is consumed by the RISC-V Solidity emulator as if the entire state content was available - since the off and on-chain emulators match down to the order in which accesses are logged. When a dispute arises, Alice packs her off-chain state access log referent to the disagreement step in an struct `AccessLogs.Context`, which will guide the execution of a `step` (i.e state transition function). - -The `AccessLogs` library implements the RISC-V Solidity emulator all necessary read and write operations. + make submodules + make build + make test-all -It also makes sure that all accesses performed by the `step` function match the ones provided by Alice and are consistent with the Merkle proofs provided by her. If that is not the case, Alice loses the dispute. +## Prerequisites -## Step function + make env-check # verify everything is installed -`MetaStep.step` is the top-level state transition function, it is meant to take the machine from state s[i] to state[i + 1], using the `AccessLogs` as an assistant. It receives a `counter` and an `AccessLogs.Context` - the latter populated with the access log generated by the emulator off-chain - and returns a `UArchStepStatus` signaling the result of its execution. Internally it runs one micro-step via `UArchStep.step` and, every `1 << LOG2_CYCLES_TO_RESET` cycles, also runs `UArchReset.reset` to reset the micro-architecture state. +- [Foundry](https://book.getfoundry.sh/) 1.4.3 +- GNU Make >= 3.81 +- Lua 5.4, LuaRocks, LPEG +- GNU sed (gsed on macOS) +- GPP >= 2.27 -During a `MetaStep.step` execution, every necessary read or write (be it to memory, registers etc) is processed and verified by the `AccessLogs`. +
+macOS (Homebrew) -## Execute Instruction + brew install lua luarocks gpp gnu-sed + luarocks install lpeg + curl -L https://foundry.paradigm.xyz | bash + foundryup -i v1.4.3 -The `UArchStep` library implements a single micro-architecture instruction step (`UArchStep.step`), and is invoked by `MetaStep.step`. It consists of the machine instruction logic, such as decoding, executing, opcode matching and etc. The Solidity implementation is converted from the Cpp implementation directly through a translator script. This is to assure that the implementations in two languages are identical. Yet the low level differences in two languages are wrapped in the Compatibility Layer. +Add to your shell profile: -## Compatibility Layer + eval "$(luarocks path)" -The `EmulatorCompat` contract is taking care of all the differences in the two implementations. Ranging from programming languages (Cpp versus Solidity) to architectural differences (RISC-V versus EVM). +
-## Getting Started +
+macOS (MacPorts) -Run `make help` for a list of target options. Here are some of them: + sudo port install lua54 luarocks gpp gsed + luarocks install lpeg + curl -L https://foundry.paradigm.xyz | bash + foundryup -i v1.4.3 -``` -Cleaning targets: - clean - clean the cache artifacts and generated files -Generic targets: -* all - build solidity code. To build from a clean clone, run: make submodules all - build - build solidity code - generate-step - generate solidity-step code from cpp - generate-reset - generate solidity-reset code from cpp - generate-send-cmio-response - generate solidity-send-cmio-response code from cpp - generate-constants - generate solidity-constants code by querying the cartesi machine - generate-mock - generate mock library code - generate-prod - generate production library code - generate-replay - generate replay tests - pretest - download necessary files for tests - submodules - initialize and update git submodules - fmt - format solidity sources with forge fmt - test-all - test all - test-mock - test binary files with mock library - test-prod - test production code - test-replay - test log files -Coverage targets: - coverage-mock - generate coverage info for mock library tests - coverage-prod - generate coverage info for production code tests - coverage-report - aggregate coverage info and generate html report -``` +Add to your shell profile: -### Prerequisite + eval "$(luarocks path)" + export SED=gsed -- Build [Cartesi docker image](https://github.com/cartesi/machine-emulator#getting-started) +
-### Requirements +
+Linux (Ubuntu/Debian) -- Foundry 1.4.3 -- GNU Make >= 3.81 -- GNU sed >= 4.9 -- GPP >= 2.27 + sudo apt install lua5.4 liblua5.4-dev luarocks gpp + sudo luarocks install lpeg + curl -L https://foundry.paradigm.xyz | bash + foundryup -i v1.4.3 -Different version of tools maybe working but is not guaranteed. +Add to your shell profile: -### Install + eval "$(luarocks path)" -Install dependencies and build: +
- make submodules all +## Testing -### Run tests + make test-transpiler # transpiler unit tests (fast) + make test-mock # rv64i instruction tests against mock AccessLogs + make test-prod # production AccessLogs integration tests + make test-replay # replay 56 emulator step logs with full Merkle verification (~1 min) + make test-all # all of the above -There are two types of tests that can be run on the Solidity Emulator. +### Using local emulator test data -1. Load binary rv64i test programs in Solidity and verify the execution result -2. Collect step logs and proofs of a test program and replay them in Solidity, verify all accesses and proofs. +By default, test data (binaries and JSON step logs) is downloaded from a +GitHub release. When developing against a local emulator build, generate and +use local test data instead: -Run all tests: + # one-time: generate step logs in the emulator repo + make -C $EMULATOR_DIR/tests test-generate-uarch-logs + # use them here + make local-dep make test-all -(**_target test-replay is very time consuming_**) +This ensures the constants and test data come from the same emulator build. -## Contributing +### Coverage + + make coverage-mock + make coverage-prod + make coverage-report # html report in coverage/ + +## Code generation + +The build step runs these automatically; you only need them when +iterating on a specific piece: + + make generate-step # uarch-step.cpp -> UArchStep.sol + make generate-reset # uarch-reset-state.cpp -> UArchReset.sol + make generate-send-cmio-response # send-cmio-response.cpp -> SendCmioResponse.sol + make generate-constants # EmulatorConstants.sol (from emulator Lua bindings) + make generate-mock # mock AccessLogs library + make generate-prod # production AccessLogs library + +## Architecture + +This implementation must produce the exact same state transition as the +off-chain emulator: given initial state s[i], both must reach s[i+1] bit for +bit. Machine states live on-chain as Merkle tree root hashes; their contents +are only known off-chain. Merkle proofs let the blockchain verify individual +transitions without storing the full state. -Thank you for your interest in Cartesi! Head over to our [Contributing Guidelines](CONTRIBUTING.md) for instructions on how to sign our Contributors Agreement and get started with Cartesi! +### Components + +**MetaStep** -- Entry point. Runs one micro-step via `UArchStep.step` and +periodically resets the micro-architecture via `UArchReset.reset`. + +**UArchStep** -- One micro-architecture instruction step (transpiled from +`uarch-step.cpp`). Decodes and executes RISC-V opcodes. + +**UArchReset** -- Resets micro-architecture state (transpiled from +`uarch-reset-state.cpp`). + +**SendCmioResponse** -- Handles CMIO yield responses (transpiled from +`send-cmio-response.cpp`). + +**AccessLogs** -- Provides Merkle-verified read/write access to machine state. +During a dispute, the claimant supplies an access log with sibling hashes; +every read and write is checked against the Merkle root. Inconsistencies mean +the claimant loses. + +**EmulatorCompat** -- Bridges C++/Solidity differences (endianness, type +widths, calling conventions). + +**EmulatorConstants** -- Addresses, sizes, and hashes auto-generated from the +emulator's Lua bindings. + +## Contributing -Please note we have a [Code of Conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. +See [Contributing Guidelines](CONTRIBUTING.md) and our +[Code of Conduct](CODE_OF_CONDUCT.md). ## License -The machine-solidity-step repository and all contributions are licensed under -[APACHE 2.0](https://www.apache.org/licenses/LICENSE-2.0). Please review our [LICENSE](LICENSE) file. +[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). See [LICENSE](LICENSE). diff --git a/helper_scripts/generate_SendCmioResponse.sh b/helper_scripts/generate_SendCmioResponse.sh deleted file mode 100755 index 4fe7081..0000000 --- a/helper_scripts/generate_SendCmioResponse.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -set -e - -SED=${SED:-"sed"} -EMULATOR_DIR=${EMULATOR_DIR:-"../emulator"} -CPP_RESET_PATH=${EMULATOR_DIR}"/src/send-cmio-response.cpp" - -TEMPLATE_FILE="./templates/SendCmioResponse.sol.template" -TARGET_FILE="src/SendCmioResponse.sol" -COMPAT_FILE="src/EmulatorCompat.sol" -CONSTANTS_FILE="src/EmulatorConstants.sol" -KEYWORD_START="START OF AUTO-GENERATED CODE" -KEYWORD_END="END OF AUTO-GENERATED CODE" - -# get function names from EmulatorCompat.sol -COMPAT_FNS=`cat $COMPAT_FILE | grep -o "function [^(]*(" | $SED "s/function//g" | $SED "s/(//g"` -COMPAT_FNS=`echo $COMPAT_FNS | $SED -E "s/( |\n)/|/g"` - -# get constant names from EmulatorConstants.sol -CONSTANTS=`cat $CONSTANTS_FILE | grep -E -o 'constant\s+[^ ]*' | $SED -E "s/constant//g; s/ //g" | tr '\n' '|' | sed "s/.$//"` - -# grab head and tail of the template -start=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_START" -n | grep -Eo "[0-9]*"` -end=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_END" -n | grep -Eo "[0-9]*"` -total=`wc -l "$TEMPLATE_FILE" | grep -Eo "[0-9]*"` -let last=total-end+1 - -h=`head -n $start $TEMPLATE_FILE` -t=`tail -n -$last $TEMPLATE_FILE` - -cpp_src=`cat "$CPP_RESET_PATH"` -pattern="namespace cartesi \{(.*)\}" -[[ $cpp_src =~ $pattern ]] - -# replace cpp specific syntaxes with solidity ones -cpp_src=`echo "${BASH_REMATCH[1]}" \ - | $SED "/Explicit instantiatio/d" \ - | $SED "/template/d" \ - | $SED "/ uint32 length);/d" \ - | $SED "s/machine_merkle_tree::get_log2_word_size()/TREE_LOG2_WORD_SIZE/g" \ - | $SED -E "s/($COMPAT_FNS)/EmulatorCompat.\1/g" \ - | $SED "s/writeMemoryWithPadding(a, AR_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size);/a.writeRegion(Memory.regionFromPhysicalAddress(AR_CMIO_RX_BUFFER_START.toPhysicalAddress(),Memory.alignedSizeFromLog2(uint8(writeLengthLog2Size - HASH_TREE_LOG2_WORD_SIZE))),dataHash);"/g \ - | $SED -E "s/($CONSTANTS)([^a-zA-Z])/EmulatorConstants.\1\2/g" \ - | $SED "s/void send_cmio_response(STATE_ACCESS a, uint16 reason, bytes data, uint32 dataLength) {/function sendCmioResponse(AccessLogs.Context memory a, uint16 reason, bytes32 dataHash, uint32 dataLength) internal pure {/" \ - | $SED "s/const uint64/uint64/g" \ - | $SED "s/const uint32/uint32/g" \ - | $SED "/^$/N;/^\n$/D" - ` - -# compose the solidity file from all components -echo -e "$h" "\n\n$h_src" > $TARGET_FILE -echo "$cpp_src" >> $TARGET_FILE -echo -e "\n$t" >> $TARGET_FILE diff --git a/helper_scripts/generate_UArchReset.sh b/helper_scripts/generate_UArchReset.sh deleted file mode 100755 index bf3f136..0000000 --- a/helper_scripts/generate_UArchReset.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -set -e - -SED=${SED:-"sed"} -EMULATOR_DIR=${EMULATOR_DIR:-"../emulator"} -CPP_RESET_PATH=${EMULATOR_DIR}"/src/uarch-reset-state.cpp" - -TEMPLATE_FILE="./templates/UArchReset.sol.template" -TARGET_FILE="src/UArchReset.sol" -COMPAT_FILE="src/EmulatorCompat.sol" -KEYWORD_START="START OF AUTO-GENERATED CODE" -KEYWORD_END="END OF AUTO-GENERATED CODE" - -# get function names from EmulatorCompat.sol -COMPAT_FNS=`cat $COMPAT_FILE | grep -o "function [^(]*(" | $SED "s/function//g" | $SED "s/(//g"` -COMPAT_FNS=`echo $COMPAT_FNS | $SED -E "s/( |\n)/|/g"` - -# grab head and tail of the template -start=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_START" -n | grep -Eo "[0-9]*"` -end=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_END" -n | grep -Eo "[0-9]*"` -total=`wc -l "$TEMPLATE_FILE" | grep -Eo "[0-9]*"` -let last=total-end+1 - -h=`head -n $start $TEMPLATE_FILE` -t=`tail -n -$last $TEMPLATE_FILE` - -cpp_src=`cat "$CPP_RESET_PATH"` -pattern="namespace cartesi \{(.*)\}" -[[ $cpp_src =~ $pattern ]] - -# replace cpp specific syntaxes with solidity ones -cpp_src=`echo "${BASH_REMATCH[1]}" \ - | $SED "/Explicit instantiatio/d" \ - | $SED "/template/d" \ - | $SED -E "s/($COMPAT_FNS)/EmulatorCompat.\1/g" \ - | $SED "s/void uarch_reset_state(UarchState &a) {/function reset(AccessLogs.Context memory a) internal pure {/"` - -# compose the solidity file from all components -echo -e "$h" "\n\n$h_src" > $TARGET_FILE -echo "$cpp_src" >> $TARGET_FILE -echo -e "\n$t" >> $TARGET_FILE diff --git a/helper_scripts/generate_UArchSolidity.lua b/helper_scripts/generate_UArchSolidity.lua new file mode 100644 index 0000000..a310e48 --- /dev/null +++ b/helper_scripts/generate_UArchSolidity.lua @@ -0,0 +1,204 @@ +#!/usr/bin/env lua5.4 +-- Transpiles the emulator's uarch C++ files to Solidity. Not a general-purpose C++ parser. +-- The patterns here match the specific conventions in those files; if the C++ changes style, +-- update the transpiler to match rather than making it handle all possible C++. + +local lpeg = require("lpeg") +local P, R, S, V, C, Cs = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs + +local M = {} + +M.LICENSE_BANNER = [=[// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//]=] + +local ws = S(" \t") +local nl = P("\n") +local ident = (R("az","AZ") + P("_")) * (R("az","AZ","09") + P("_"))^0 + +local function extract_namespace_body(src) + local pattern = (1 - P("namespace cartesi"))^0 + * P("namespace cartesi") * ws^0 + * P{ "{" * C(((1 - S("{}")) + V(1))^0) * "}" } + return assert(pattern:match(src), "namespace cartesi not found") +end + +local function strip_cpp_only(src) + local rest_of_line = (1 - nl)^0 * nl + local until_semicolon = (1 - P(";"))^0 * P(";") * ws^0 * nl^-1 + local pattern = Cs(( + (ws^0 * P("template") * ws^0 * P("<") * (1 - P(">"))^0 * P(">") * ws^0) / "" + + (ws^0 * P("template") * until_semicolon) / "" + + (ws^0 * P("[[maybe_unused]]") * until_semicolon) / "" + + (ws^0 * P("(void)") * S(" \t\n")^1 * P("note") * until_semicolon) / "" + + (ws^0 * P("// Explicit instantiat") * rest_of_line) / "" + + (P("constexpr") * ws^1) / "" + + 1 + )^0) + return pattern:match(src) +end + +local function convert_fn_signatures(src, entrypoint) + local static_inline = P("static") * ws^1 * P("inline") * ws^1 + local leading_ws = C(ws^0) + local return_type = C(ident) + local func_name = C(ident) + local param_list = P("(") * C((1 - P(")"))^0) * P(")") + local open_brace = P("{") + local fn_sig = leading_ws * static_inline^-1 + * return_type * ws^1 * func_name + * ws^0 * param_list + * ws^0 * open_brace + local rewrite = fn_sig / function(indent, ret_type, name, params) + params = params:gsub("const%s+UarchState%s+a", "AccessLogs.Context memory a") + params = params:gsub("UarchState%s+&a", "AccessLogs.Context memory a") + params = params:gsub("STATE_ACCESS%s+a", "AccessLogs.Context memory a") + params = params:gsub("bytes%s+data", "bytes32 dataHash") + params = params:gsub("const%s+unsigned%s+char%s+%%*data", "bytes32 dataHash") + local fn_renames = { + uarch_step = "step", + send_cmio_response = "sendCmioResponse", + uarch_reset_state = "reset", + } + name = fn_renames[name] or name + local visibility = name == entrypoint and "internal" or "private" + local ret = ret_type ~= "void" and (" returns (" .. ret_type .. ")") or "" + return indent .. "function " .. name .. "(" .. params .. ") " .. visibility .. " pure" .. ret .. " {" + end + return Cs((rewrite + 1)^0):match(src) +end + +local function transform_code(src, fn) + local strings = P('"') * (P('\\') * 1 + (1 - P('"')))^0 * P('"') + local comments = P("//") * (1 - nl)^0 + local not_string_or_comment = (1 - strings - comments)^1 + local code = C(not_string_or_comment) / fn + return Cs((strings + comments + code)^0):match(src) +end + +local function cpp_to_solidity_syntax(src) + return transform_code(src, function(code) + code = code:gsub("const%s+(u?int%d+)", "%1") + code = code:gsub("::", ".") + code = code:gsub("UINT64_MAX", "type(uint64).max") + return code + end) +end + +local function prefix_names(src, names, prefix) + return transform_code(src, function(code) + for name in pairs(names) do + code = code:gsub("%f[%w]" .. name .. "%f[%W]", prefix .. name) + end + return code + end) +end + +local function extract_names(src, pattern) + local t = {} + for name in src:gmatch(pattern) do t[name] = true end + return t +end + +local script_dir = debug.getinfo(1, "S").source:match("^@(.*/)") or "./" +local src_dir = script_dir .. "../src/" + +local function read_file(path) + local f = assert(io.open(path, "r")) + return f:read("a") +end + +function M.transpile(cpp_src, h_src, lib_name, entrypoint) + local body = extract_namespace_body(cpp_src) + body = strip_cpp_only(body) + body = convert_fn_signatures(body, entrypoint) + body = cpp_to_solidity_syntax(body) + + -- prefix compat functions and constants with their Solidity library name + local compat_fn_names = extract_names(read_file(src_dir .. "EmulatorCompat.sol"), "function%s+([%w_]+)%s*%(") + body = prefix_names(body, compat_fn_names, "EmulatorCompat.") + local constants_names = extract_names(read_file(src_dir .. "EmulatorConstants.sol"), "constant%s+([%w_]+)") + body = prefix_names(body, constants_names, "EmulatorConstants.") + -- on-chain only the data hash is available, not the raw bytes + body = transform_code(body, function(code) + return code:gsub("%f[%w]data%f[%W]", "dataHash") + end) + + -- extract enums from companion header + local enums = "" + if h_src then + for ename, ebody in h_src:gmatch("enum class (%w+)%s*:%s*[%w_]+%s*{([^}]*)}") do + enums = enums .. "enum " .. ename .. " {" .. ebody .. "}\n" + end + end + + -- wrap in Solidity library + return M.LICENSE_BANNER .. "\n\n" + .. "/// @dev This file is generated from C++ by generate_UArchSolidity.lua\n\n" + .. "pragma solidity ^0.8.30;\n\n" + .. "import \"./EmulatorCompat.sol\";\n\n" + .. "library " .. lib_name .. " {\n" + .. enums .. body .. "\n" + .. "}\n" +end + +local function help() + io.stderr:write(string.format([=[ +Usage: + + %s + +Transpile a C++ uarch source file into a Solidity library. + +Arguments: + + input-cpp C++ source file to transpile (e.g. uarch-step.cpp). + If a companion .hpp or .h exists, enums are extracted from it. + output-sol Output .sol file path (e.g. src/UArchStep.sol) + library-name Solidity library name (e.g. UArchStep) + entry-function The library's public entry point (e.g. step). + Gets "internal" visibility; all others get "private". + +]=], arg[0])) + os.exit() +end + +local function main() + if not arg[1] or arg[1] == "-h" or arg[1] == "--help" then help() end + local input_cpp = arg[1] + local output_sol = assert(arg[2], "missing output-sol") + local library_name = assert(arg[3], "missing library-name") + local entry_function = assert(arg[4], "missing entry-function") + + local input_cpp_src = read_file(input_cpp) + local input_h_src + for _, ext in ipairs({".hpp", ".h"}) do + local h_path = input_cpp:gsub("%.cpp$", ext) + local ok, src = pcall(read_file, h_path) + if ok then input_h_src = src; break end + end + + local result = M.transpile(input_cpp_src, input_h_src, library_name, entry_function) + + local f = assert(io.open(output_sol, "w")) + f:write(result) + io.stderr:write("Generated " .. output_sol .. "\n") +end + +if arg and arg[0] and arg[0]:match("/generate_UArchSolidity%.lua$") then + main() +end + +return M diff --git a/helper_scripts/generate_UArchStep.sh b/helper_scripts/generate_UArchStep.sh deleted file mode 100755 index fa1ebf2..0000000 --- a/helper_scripts/generate_UArchStep.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -set -e - -SED=${SED:-"sed"} -EMULATOR_DIR=${EMULATOR_DIR:-"../emulator"} -CPP_STEP_PATH=${EMULATOR_DIR}"/src/uarch-step.cpp" -CPP_STEP_H_PATH=${EMULATOR_DIR}"/src/uarch-step.h" - -TEMPLATE_FILE="./templates/UArchStep.sol.template" -TARGET_FILE="src/UArchStep.sol" -COMPAT_FILE="src/EmulatorCompat.sol" -CONSTANTS_FILE="src/EmulatorConstants.sol" -KEYWORD_START="START OF AUTO-GENERATED CODE" -KEYWORD_END="END OF AUTO-GENERATED CODE" - -# function with to be internal -INTERNAL_FN="step" - -# grab head and tail of the template -start=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_START" -n | grep -Eo "[0-9]*"` -end=`cat "$TEMPLATE_FILE" | grep "$KEYWORD_END" -n | grep -Eo "[0-9]*"` -total=`wc -l "$TEMPLATE_FILE" | grep -Eo "[0-9]*"` -let last=total-end+1 - -h=`head -n $start $TEMPLATE_FILE` -t=`tail -n -$last $TEMPLATE_FILE` - -h_src=`cat "$CPP_STEP_H_PATH"` -pattern="enum class (.*) : int \{(.*)\};" -[[ $h_src =~ $pattern ]] -# retrieve enum type from cpp header -h_src=`echo "enum ${BASH_REMATCH[1]} {${BASH_REMATCH[2]}}"` - -# get function names from EmulatorCompat.sol -COMPAT_FNS=`cat $COMPAT_FILE | grep -o "function [^(]*(" | $SED "s/function//g" | $SED "s/(//g"` -COMPAT_FNS=`echo $COMPAT_FNS | $SED -E "s/( |\n)/|/g"` - -# get constant names from EmulatorConstants.sol -CONSTANTS=`cat $CONSTANTS_FILE | grep -E -o 'constant\s+[^ ]*' | $SED -E "s/constant//g; s/ //g" | tr '\n' '|' | sed "s/.$//"` - -cpp_src=`cat "$CPP_STEP_PATH"` -pattern="namespace cartesi \{(.*)\}" -[[ $cpp_src =~ $pattern ]] -# replace cpp specific syntaxes with solidity ones -cpp_src=`echo "${BASH_REMATCH[1]}" \ - | $SED "/template/d" \ - | $SED "/[[maybe_unused]]/d" \ - | $SED "/(void) note/d" \ - | $SED "s/constexpr//g" \ - | $SED "s/const UarchState a/AccessLogs.Context memory a/g" \ - | $SED "s/::/./g" \ - | $SED "s/UINT64_MAX/type(uint64).max/g" \ - | $SED -E "s/UArchStepStatus uarch_step/static inline UArchStepStatus step/g" \ - | $SED -E "s/static inline (\w+) ($INTERNAL_FN)\(([^\n]*)\) \{/function \2\(\3\) internal pure returns \(\1\)\{/g" \ - | $SED -E "s/static inline (\w+) (\w+)\(([^\n]*)\) \{/function \2\(\3\) private pure returns \(\1\)\{/g" \ - | $SED -E "s/($COMPAT_FNS)/EmulatorCompat.\1/g" \ - | $SED -E "s/([^a-zA-Z])($CONSTANTS)([^a-zA-Z])/EmulatorConstants.\1\2\3/g" \ - | $SED "s/ returns (void)//g"` - -# compose the solidity file from all components -echo -e "$h" "\n\n$h_src" > $TARGET_FILE -echo "$cpp_src" >> $TARGET_FILE -echo -e "\n$t" >> $TARGET_FILE diff --git a/helper_scripts/test_generate_UArchSolidity.lua b/helper_scripts/test_generate_UArchSolidity.lua new file mode 100644 index 0000000..d315e77 --- /dev/null +++ b/helper_scripts/test_generate_UArchSolidity.lua @@ -0,0 +1,207 @@ +#!/usr/bin/env lua5.4 + +package.path = "helper_scripts/?.lua;" .. package.path +local gen = require("generate_UArchSolidity") + +local pass, fail = 0, 0 + +local function test(name, fn) + local ok, err = pcall(fn) + if ok then + pass = pass + 1 + else + fail = fail + 1 + io.stderr:write("FAIL: " .. name .. "\n " .. tostring(err) .. "\n") + end +end + +local function normalize(s) + -- Remove comments and extra whitespace for easier comparison + s = s:gsub("//[^\n]*", "") + s = s:gsub("%s+", " ") + s = s:gsub("^ ", ""):gsub(" $", "") + return s +end + +local function assert_transpiles(input_cpp, input_h, lib, entry, expected_sol) + local result = gen.transpile(input_cpp, input_h, lib, entry) + local actual = normalize(result:sub(#gen.LICENSE_BANNER + 1)) + local expected = normalize(expected_sol) + assert(actual == expected, + "\nexpected:\n" .. expected .. "\n\ngot:\n" .. actual) +end + + +test("uarch-step", function() + local input_cpp = [==[ + // Copyright Cartesi and individual authors (see AUTHORS) + // SPDX-License-Identifier: LGPL-3.0-or-later + + #include "uarch-step.h" + #include "uarch-record-state-access.h" // IWYU pragma: keep + #include "uarch-solidity-compat.h" + + // NOLINTBEGIN(google-readability-casting,misc-const-correctness) + namespace cartesi { + + template < typename UarchState > + static inline uint64 readUint64(const UarchState a, uint64 paddr) { + return readWord(a, paddr); + } + + template + static inline void executeLUI(const UarchState a, uint32 insn, uint64 pc) { + [[maybe_unused]] auto note = dumpInsn(a, pc, insn, + "lui"); + (void) + note; + constexpr uint32 mask = 0xfffff000; + const uint64 imm = int32ToUint64(int32(insn) & int32(mask)); + uint8 rd = operandRd(insn); + } + + template + UArchStepStatus uarch_step(const UarchState a) { + uint64 cycle = readCycle(a); + if (cycle >= UINT64_MAX) { + return UArchStepStatus::CycleOverflow; + } + } + + // Explicit instantiation for uarch_state_access + template UArchStepStatus uarch_step(const uarch_state_access a); + // Explicit instantiation for uarch_record_state_access + template UArchStepStatus uarch_step(const uarch_record_state_access a); + } + // NOLINTEND(google-readability-casting,misc-const-correctness) + ]==] + local input_h = "enum class UArchStepStatus : int { Success, CycleOverflow, UArchHalted };" + + local expected_sol = [=[ + pragma solidity ^0.8.30; + import "./EmulatorCompat.sol"; + + library UArchStep { + enum UArchStepStatus { Success, CycleOverflow, UArchHalted } + + function readUint64(AccessLogs.Context memory a, uint64 paddr) private pure returns (uint64) { + return EmulatorCompat.readWord(a, paddr); + } + + function executeLUI(AccessLogs.Context memory a, uint32 insn, uint64 pc) private pure { + uint32 mask = 0xfffff000; + uint64 imm = EmulatorCompat.int32ToUint64(int32(insn) & int32(mask)); + uint8 rd = operandRd(insn); + } + + function step(AccessLogs.Context memory a) internal pure returns (UArchStepStatus) { + uint64 cycle = EmulatorCompat.readCycle(a); + if (cycle >= type(uint64).max) { + return UArchStepStatus.CycleOverflow; + } + } + } + ]=] + + assert_transpiles(input_cpp, input_h, "UArchStep", "step", expected_sol) +end) + + +test("uarch-reset-state", function() + local input_cpp = [==[ + // Copyright Cartesi and individual authors (see AUTHORS) + // SPDX-License-Identifier: LGPL-3.0-or-later + + #include "uarch-reset-state.h" + #include "uarch-record-state-access.h" // IWYU pragma: keep + #include "uarch-solidity-compat.h" + + namespace cartesi { + + template + void uarch_reset_state(UarchState &a) { + resetState(a); + } + + // edge cases: no params, empty body + static inline void noop() {} + + // Explicit instantiation for uarch_state_access + template void uarch_reset_state(uarch_state_access &a); + // Explicit instantiation for uarch_record_state_access + template void uarch_reset_state(uarch_record_state_access &a); + } + // NOLINTEND(google-readability-casting,misc-const-correctness) + ]==] + + local expected_sol = [=[ + pragma solidity ^0.8.30; + import "./EmulatorCompat.sol"; + + library UArchReset { + function reset(AccessLogs.Context memory a) internal pure { + EmulatorCompat.resetState(a); + } + + function noop() private pure {} + } + ]=] + + assert_transpiles(input_cpp, nil, "UArchReset", "reset", expected_sol) +end) + + +test("send-cmio-response", function() + local input_cpp = [==[ + // Copyright Cartesi and individual authors (see AUTHORS) + // SPDX-License-Identifier: LGPL-3.0-or-later + + #include "send-cmio-response.h" + #include "uarch-solidity-compat.h" + + // NOLINTBEGIN(google-readability-casting,misc-const-correctness) + namespace cartesi { + + template + void send_cmio_response(STATE_ACCESS a, uint16 reason, bytes data, uint32 dataLength) { + if (dataLength > 0) { + uint32 writeLengthLog2Size = uint32Log2(dataLength); + writeMemoryWithPadding(a, AR_CMIO_RX_BUFFER_START, data, dataLength, writeLengthLog2Size); + } + throwRuntimeError(a, "CMIO response data is too large"); + writeHtifFromhost(a, 0); + writeIflagsY(a, 0); + } + + // Explicit instantiation for state_access + template void send_cmio_response(state_access a, uint16_t reason, const unsigned char *data, uint32 length); + // Explicit instantiation for record_state_access + template void send_cmio_response(record_send_cmio_state_access a, uint16_t reason, const unsigned char *data, + uint32 length); + } + // NOLINTEND(google-readability-casting,misc-const-correctness) + ]==] + + local expected_sol = [=[ + pragma solidity ^0.8.30; + import "./EmulatorCompat.sol"; + + library SendCmioResponse { + function sendCmioResponse(AccessLogs.Context memory a, uint16 reason, bytes32 dataHash, uint32 dataLength) internal pure { + if (dataLength > 0) { + uint32 writeLengthLog2Size = EmulatorCompat.uint32Log2(dataLength); + EmulatorCompat.writeMemoryWithPadding(a, EmulatorConstants.AR_CMIO_RX_BUFFER_START, dataHash, dataLength, writeLengthLog2Size); + } + EmulatorCompat.throwRuntimeError(a, "CMIO response data is too large"); + EmulatorCompat.writeHtifFromhost(a, 0); + EmulatorCompat.writeIflagsY(a, 0); + } + } + ]=] + + assert_transpiles(input_cpp, nil, "SendCmioResponse", "sendCmioResponse", expected_sol) +end) + + +print(string.format("\n%d passed, %d failed", pass, fail)) +if fail > 0 then os.exit(1) end diff --git a/src/AccessLogs.sol b/src/AccessLogs.sol index 95279bf..9db330d 100644 --- a/src/AccessLogs.sol +++ b/src/AccessLogs.sol @@ -117,7 +117,6 @@ library AccessLogs { // // Read methods // - function readWord( AccessLogs.Context memory a, Memory.PhysicalAddress readAddress @@ -142,7 +141,6 @@ library AccessLogs { // // Write methods // - function writeWord( AccessLogs.Context memory a, Memory.PhysicalAddress writeAddress, diff --git a/src/EmulatorCompat.sol b/src/EmulatorCompat.sol index 59bb62d..2853cae 100644 --- a/src/EmulatorCompat.sol +++ b/src/EmulatorCompat.sol @@ -76,9 +76,8 @@ library EmulatorCompat { pure returns (uint64) { - return a.readWord( - EmulatorConstants.UARCH_PC_ADDRESS.toPhysicalAddress() - ); + return + a.readWord(EmulatorConstants.UARCH_PC_ADDRESS.toPhysicalAddress()); } function readWord(AccessLogs.Context memory a, uint64 paddr) @@ -101,7 +100,10 @@ library EmulatorCompat { return a.readWord(paddr.toPhysicalAddress()); } - function writeCycle(AccessLogs.Context memory a, uint64 val) internal pure { + function writeCycle(AccessLogs.Context memory a, uint64 val) + internal + pure + { a.writeWord( EmulatorConstants.UARCH_CYCLE_ADDRESS.toPhysicalAddress(), val ); @@ -158,9 +160,8 @@ library EmulatorCompat { pure returns (bool) { - uint64 iflags_y = a.readWord( - EmulatorConstants.IFLAGS_Y_ADDRESS.toPhysicalAddress() - ); + uint64 iflags_y = + a.readWord(EmulatorConstants.IFLAGS_Y_ADDRESS.toPhysicalAddress()); if (iflags_y == 0) { return false; } @@ -347,6 +348,27 @@ library EmulatorCompat { ); } + function writeMemoryWithPadding( + AccessLogs.Context memory a, + uint64 paddr, + bytes32 dataHash, + uint32 dataLength, + uint32 writeLengthLog2Size + ) internal pure { + a.writeRegion( + Memory.regionFromPhysicalAddress( + paddr.toPhysicalAddress(), + Memory.alignedSizeFromLog2( + uint8( + writeLengthLog2Size + - EmulatorConstants.HASH_TREE_LOG2_WORD_SIZE + ) + ) + ), + dataHash + ); + } + function uint32Log2(uint32 value) internal pure returns (uint32) { require(value > 0, "EmulatorCompat: log2(0) is undefined"); return 31 - clz(value); diff --git a/src/EmulatorConstants.sol b/src/EmulatorConstants.sol index 92832af..3d9f8aa 100644 --- a/src/EmulatorConstants.sol +++ b/src/EmulatorConstants.sol @@ -25,7 +25,7 @@ library EmulatorConstants { // START OF AUTO-GENERATED CODE bytes32 constant UARCH_PRISTINE_STATE_HASH = - 0xa2f4f0018081d795e47c7feae9300055e8551eda5bd6473e54ca80ece64ea620; + 0x3cf87e9cab4091ffefa4ff42b149e765fbc3fd6c21fcaf32b49e7f068047ad7e; uint64 constant UARCH_CYCLE_ADDRESS = 0x400008; uint64 constant UARCH_CYCLE_MAX = 0x100000; uint64 constant UARCH_HALT_FLAG_ADDRESS = 0x400000; diff --git a/src/MetaStep.sol b/src/MetaStep.sol index 6438af9..8fbf821 100644 --- a/src/MetaStep.sol +++ b/src/MetaStep.sol @@ -25,6 +25,8 @@ import "./UArchReset.sol"; library MetaStep { using AccessLogs for AccessLogs.Context; + uint64 constant LOG2_CYCLES_TO_RESET = 10; + /// @notice Run meta-step function step(uint256 counter, AccessLogs.Context memory accessLogs) internal @@ -35,11 +37,9 @@ library MetaStep { bytes32 machineState = accessLogs.currentRootHash; if ( - counter - == (counter >> EmulatorConstants.LOG2_CYCLES_TO_RESET) - << EmulatorConstants.LOG2_CYCLES_TO_RESET + counter == (counter >> LOG2_CYCLES_TO_RESET) << LOG2_CYCLES_TO_RESET ) { - // if counter is a multiple of (1 << EmulatorConstants.LOG2_CYCLES_TO_RESET), run uarch reset + // if counter is a multiple of (1 << LOG2_CYCLES_TO_RESET), run uarch reset UArchReset.reset(accessLogs); machineState = accessLogs.currentRootHash; } diff --git a/src/SendCmioResponse.sol b/src/SendCmioResponse.sol index fdf57bb..4d3ee53 100644 --- a/src/SendCmioResponse.sol +++ b/src/SendCmioResponse.sol @@ -14,21 +14,13 @@ // limitations under the License. // -/// @title SendCmioResponse -/// @notice Sends a CMIO response -//:#include macro.pp -/// DEV_COMMENT(templates/SendCmioResponse.sol.template) +/// @dev This file is generated from C++ by generate_UArchSolidity.lua pragma solidity ^0.8.30; import "./EmulatorCompat.sol"; library SendCmioResponse { - using Memory for uint64; - using AccessLogs for AccessLogs.Context; - - // START OF AUTO-GENERATED CODE - function sendCmioResponse( AccessLogs.Context memory a, uint16 reason, @@ -61,30 +53,22 @@ library SendCmioResponse { a, "CMIO response data is too large" ); } - a.writeRegion( - Memory.regionFromPhysicalAddress( - EmulatorConstants.AR_CMIO_RX_BUFFER_START - .toPhysicalAddress(), - Memory.alignedSizeFromLog2( - uint8( - writeLengthLog2Size - - EmulatorConstants.HASH_TREE_LOG2_WORD_SIZE - ) - ) - ), - dataHash + EmulatorCompat.writeMemoryWithPadding( + a, + EmulatorConstants.AR_CMIO_RX_BUFFER_START, + dataHash, + dataLength, + writeLengthLog2Size ); } // Write data length and reason to fromhost uint64 mask16 = EmulatorCompat.uint64ShiftLeft(1, 16) - 1; uint64 mask32 = EmulatorCompat.uint64ShiftLeft(1, 32) - 1; - uint64 yieldData = - EmulatorCompat.uint64ShiftLeft((uint64(reason) & mask16), 32) - | (uint64(dataLength) & mask32); + uint64 yieldData = EmulatorCompat.uint64ShiftLeft( + (uint64(reason) & mask16), 32 + ) | (uint64(dataLength) & mask32); EmulatorCompat.writeHtifFromhost(a, yieldData); // Reset iflags.Y EmulatorCompat.writeIflagsY(a, 0); } - - // END OF AUTO-GENERATED CODE } diff --git a/src/UArchReset.sol b/src/UArchReset.sol index 21bd8e7..79652db 100644 --- a/src/UArchReset.sol +++ b/src/UArchReset.sol @@ -14,21 +14,14 @@ // limitations under the License. // -/// @title UArchReset -/// @notice Reset microarchitecture to pristine state -//:#include macro.pp -/// DEV_COMMENT(templates/UArchReset.sol.template) +/// @dev This file is generated from C++ by generate_UArchSolidity.lua pragma solidity ^0.8.30; import "./EmulatorCompat.sol"; library UArchReset { - // START OF AUTO-GENERATED CODE - function reset(AccessLogs.Context memory a) internal pure { EmulatorCompat.resetState(a); } - - // END OF AUTO-GENERATED CODE } diff --git a/src/UArchStep.sol b/src/UArchStep.sol index c3c23b3..bef86a9 100644 --- a/src/UArchStep.sol +++ b/src/UArchStep.sol @@ -14,22 +14,18 @@ // limitations under the License. // -/// @title UArchStep -/// @notice State transition function that takes the machine from micro-state s[i] to s[i + 1] -//:#include macro.pp -/// DEV_COMMENT(templates/UArchStep.sol.template) +/// @dev This file is generated from C++ by generate_UArchSolidity.lua pragma solidity ^0.8.30; import "./EmulatorCompat.sol"; library UArchStep { - // START OF AUTO-GENERATED CODE - enum UArchStepStatus { Success, // one micro instruction was executed successfully CycleOverflow, // already at fixed point: uarch cycle has reached its maximum value UArchHalted // already at fixed point: microarchitecture is halted + } // Memory read/write access @@ -258,9 +254,9 @@ library UArchStep { EmulatorCompat.uint32ShiftLeft( uint32(EmulatorCompat.int32ShiftRight(int32(insn), 25)), 5 ) - | EmulatorCompat.uint32ShiftRight( - EmulatorCompat.uint32ShiftLeft(insn, 20), 27 - ) + | EmulatorCompat.uint32ShiftRight( + EmulatorCompat.uint32ShiftLeft(insn, 20), 27 + ) ); } @@ -1122,9 +1118,8 @@ library UArchStep { returns (bool) { uint32 mask = (7 << 12) | 0x7f; - return - (insn & mask) - == (EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode); + return (insn & mask) + == (EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode); } /// \brief Returns true if the opcode, funct3 and funct7 fields of an instruction match the provided arguments @@ -1136,8 +1131,10 @@ library UArchStep { ) private pure returns (bool) { uint32 mask = (0x7f << 25) | (7 << 12) | 0x7f; return ((insn & mask)) - == (EmulatorCompat.uint32ShiftLeft(funct7, 25) - | EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode); + == ( + EmulatorCompat.uint32ShiftLeft(funct7, 25) + | EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode + ); } /// \brief Returns true if the opcode, funct3 and 6 most significant bits of funct7 fields of an instruction match the @@ -1150,11 +1147,14 @@ library UArchStep { ) private pure returns (bool) { uint32 mask = (0x3f << 26) | (7 << 12) | 0x7f; return ((insn & mask)) - == (EmulatorCompat.uint32ShiftLeft(funct7Sr1, 26) - | EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode); + == ( + EmulatorCompat.uint32ShiftLeft(funct7Sr1, 26) + | EmulatorCompat.uint32ShiftLeft(funct3, 12) | opcode + ); } // Decode and execute one instruction + function executeInsn(AccessLogs.Context memory a, uint32 insn, uint64 pc) private pure @@ -1323,7 +1323,7 @@ library UArchStep { pure returns (UArchStepStatus) { - // This must be the first read in order to match the first log access in machine.verify_step_uarch + // This must be the first read in order to match the first log access in machine::verify_step_uarch uint64 cycle = EmulatorCompat.readCycle(a); // do not advance if cycle will overflow if (cycle >= EmulatorConstants.UARCH_CYCLE_MAX) { @@ -1341,14 +1341,4 @@ library UArchStep { EmulatorCompat.writeCycle(a, cycle); return UArchStepStatus.Success; } - - // Explicit instantiation for uarch_state_access - - // Explicit instantiation for uarch_record_state_access - - // Explicit instantiation for uarch_replay_state_access - - // Explicit instantiation for collect_uarch_cycle_hashes_state_access - - // END OF AUTO-GENERATED CODE } diff --git a/templates/SendCmioResponse.sol.template b/templates/SendCmioResponse.sol.template deleted file mode 100644 index 95ab9f9..0000000 --- a/templates/SendCmioResponse.sol.template +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/// @title SendCmioResponse -/// @notice Sends a CMIO response -//:#include macro.pp -/// DEV_COMMENT(templates/SendCmioResponse.sol.template) - -pragma solidity ^0.8.30; - -import "./EmulatorCompat.sol"; - -library SendCmioResponse { - using Memory for uint64; - using AccessLogs for AccessLogs.Context; - - // START OF AUTO-GENERATED CODE - // END OF AUTO-GENERATED CODE -} diff --git a/templates/UArchReset.sol.template b/templates/UArchReset.sol.template deleted file mode 100644 index bf739c8..0000000 --- a/templates/UArchReset.sol.template +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/// @title UArchReset -/// @notice Reset microarchitecture to pristine state -//:#include macro.pp -/// DEV_COMMENT(templates/UArchReset.sol.template) - -pragma solidity ^0.8.30; - -import "./EmulatorCompat.sol"; - -library UArchReset { - // START OF AUTO-GENERATED CODE - // END OF AUTO-GENERATED CODE -} diff --git a/templates/UArchStep.sol.template b/templates/UArchStep.sol.template deleted file mode 100644 index 088975a..0000000 --- a/templates/UArchStep.sol.template +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -/// @title UArchStep -/// @notice State transition function that takes the machine from micro-state s[i] to s[i + 1] -//:#include macro.pp -/// DEV_COMMENT(templates/UArchStep.sol.template) - -pragma solidity ^0.8.30; - -import "./EmulatorCompat.sol"; - -library UArchStep { - // START OF AUTO-GENERATED CODE - // END OF AUTO-GENERATED CODE -} diff --git a/test/AccessLogs.t.sol b/test/AccessLogs.t.sol index 1889d12..5d0300b 100644 --- a/test/AccessLogs.t.sol +++ b/test/AccessLogs.t.sol @@ -120,12 +120,11 @@ contract AccessLogsTest is Test { function testWriteWordBadRegion() public { uint64 wordWritten = 3; - (Buffer.Context memory buffer, bytes32 rootHash) = - makeWriteBuffer( - initialReadLeaf, - /* withReadValueMismatch= */ - false - ); + (Buffer.Context memory buffer, bytes32 rootHash) = makeWriteBuffer( + initialReadLeaf, + /* withReadValueMismatch= */ + false + ); AccessLogs.Context memory accessLogs = AccessLogs.Context(rootHash, buffer); vm.expectRevert("Write word root doesn't match"); @@ -176,11 +175,11 @@ contract AccessLogsTest is Test { return leaf; } - function patchLeaf( - bytes32 currentLeaf, - bytes8 newWord, - uint64 wordPosition - ) public view returns (bytes32) { + function patchLeaf(bytes32 currentLeaf, bytes8 newWord, uint64 wordPosition) + public + view + returns (bytes32) + { uint64 leafPosition = wordPosition & ~uint64(31); uint64 offset = position - leafPosition; @@ -198,9 +197,8 @@ contract AccessLogsTest is Test { view returns (Buffer.Context memory, bytes32) { - Buffer.Context memory buffer = Buffer.Context( - new bytes((59 << Memory.LOG2_LEAF) + 32 + 32), 0 - ); + Buffer.Context memory buffer = + Buffer.Context(new bytes((59 << Memory.LOG2_LEAF) + 32 + 32), 0); bytes32 readData = patchLeaf(initialReadLeaf, readWord, position); // write leaf data, leaf hash and sibling hashes diff --git a/test/Memory.t.sol b/test/Memory.t.sol index 620f2bf..bd658c3 100644 --- a/test/Memory.t.sol +++ b/test/Memory.t.sol @@ -29,21 +29,21 @@ contract MemoryTest is Test { function testStrideAlignment() public { for (uint128 paddr = 8; paddr <= (1 << 63); paddr *= 2) { for (uint8 l = 0; ((1 << l) <= (paddr >> Memory.LOG2_LEAF)); ++l) { - uint64(paddr).toPhysicalAddress() - .strideFromPhysicalAddress(Memory.alignedSizeFromLog2(l)); + uint64(paddr).toPhysicalAddress().strideFromPhysicalAddress( + Memory.alignedSizeFromLog2(l) + ); // address has to be aligned with 8-byte word vm.expectRevert(); - uint64(paddr - 1).toPhysicalAddress() - .strideFromPhysicalAddress(Memory.alignedSizeFromLog2(l)); + uint64(paddr - 1).toPhysicalAddress().strideFromPhysicalAddress( + Memory.alignedSizeFromLog2(l) + ); if ((1 << l) == (paddr >> Memory.LOG2_LEAF)) { // address has to be aligned with stride size vm.expectRevert(); uint64(paddr + paddr / 2).toPhysicalAddress() - .strideFromPhysicalAddress( - Memory.alignedSizeFromLog2(l) - ); + .strideFromPhysicalAddress(Memory.alignedSizeFromLog2(l)); } } } diff --git a/test/SendCmioResponse.t.sol b/test/SendCmioResponse.t.sol index 14de898..ccd218c 100644 --- a/test/SendCmioResponse.t.sol +++ b/test/SendCmioResponse.t.sol @@ -63,9 +63,7 @@ contract SendCmioResponse_Test is AccessLogJsonParse { for (uint256 i = 0; i < catalog.length; i++) { if ( keccak256(abi.encodePacked(catalog[i].logFilename)) - != keccak256( - abi.encodePacked("send-cmio-response-steps.json") - ) + != keccak256(abi.encodePacked("send-cmio-response-steps.json")) ) { continue; } @@ -73,9 +71,8 @@ contract SendCmioResponse_Test is AccessLogJsonParse { string memory rj = loadJsonLog(resetLog); - bytes32 initialRootHash = vm.parseBytes32( - string.concat("0x", catalog[i].initialRootHash) - ); + bytes32 initialRootHash = + vm.parseBytes32(string.concat("0x", catalog[i].initialRootHash)); bytes32 finalRootHash = vm.parseBytes32(string.concat("0x", catalog[i].finalRootHash)); diff --git a/test/UArchReset.t.sol b/test/UArchReset.t.sol index 49f9056..deb8cd3 100644 --- a/test/UArchReset.t.sol +++ b/test/UArchReset.t.sol @@ -70,9 +70,8 @@ contract UArchReset_Test is AccessLogJsonParse { string memory rj = loadJsonLog(resetLog); - bytes32 initialRootHash = vm.parseBytes32( - string.concat("0x", catalog[i].initialRootHash) - ); + bytes32 initialRootHash = + vm.parseBytes32(string.concat("0x", catalog[i].initialRootHash)); bytes32 finalRootHash = vm.parseBytes32(string.concat("0x", catalog[i].finalRootHash));