From 94eb1900880da3eff0d6b6f48059c460a8d11e6f Mon Sep 17 00:00:00 2001 From: Tei Im Date: Fri, 22 Mar 2024 12:28:10 +0900 Subject: [PATCH 1/3] Add rvsol-lint CI --- .github/workflows/ci.yaml | 11 +++++++++++ rvsol/Makefile | 8 ++++++++ rvsol/foundry.toml | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3f9e29c1..f35cd5aa 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,6 +43,17 @@ jobs: - name: Run foundry tests run: forge test -vvv --ffi working-directory: rvsol + rvsol-lint: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Run lint + run: make lint-check + working-directory: rvsol # go-lint: # runs-on: ubuntu-latest diff --git a/rvsol/Makefile b/rvsol/Makefile index 51d8b650..a9b107eb 100644 --- a/rvsol/Makefile +++ b/rvsol/Makefile @@ -9,3 +9,11 @@ clean: test: forge test -vvv --ffi .PHONY: test + +lint-fix: + forge fmt +.PHONY: lint-fix + +lint-check: + forge fmt && git diff --exit-code +.PHONY: lint-check diff --git a/rvsol/foundry.toml b/rvsol/foundry.toml index c347e965..80c0c198 100644 --- a/rvsol/foundry.toml +++ b/rvsol/foundry.toml @@ -30,4 +30,10 @@ fs_permissions = [ { access='read', path='./' } ] +[fmt] +line_length=120 +multiline_func_header='all' +bracket_spacing=true +wrap_comments=true + # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file From dc4c392974827b958728f488a0a652eae6115089 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Fri, 22 Mar 2024 12:29:32 +0900 Subject: [PATCH 2/3] Fix contracts lint --- rvsol/src/RISCV.sol | 1029 +++++++++++++++++++++++-------------- rvsol/test/CommonTest.sol | 11 +- rvsol/test/RISCV.t.sol | 48 +- 3 files changed, 691 insertions(+), 397 deletions(-) diff --git a/rvsol/src/RISCV.sol b/rvsol/src/RISCV.sol index f2e30f15..8619a417 100644 --- a/rvsol/src/RISCV.sol +++ b/rvsol/src/RISCV.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; -import {IPreimageOracle} from "@optimism/src/cannon/interfaces/IPreimageOracle.sol"; +import { IPreimageOracle } from "@optimism/src/cannon/interfaces/IPreimageOracle.sol"; contract RISCV { - IPreimageOracle public preimageOracle; constructor(IPreimageOracle _preimageOracle) { @@ -19,14 +18,16 @@ contract RISCV { revert(0, 0x20) } - function preimageOraclePos() -> out { // slot of preimageOraclePos field + function preimageOraclePos() -> out { + // slot of preimageOraclePos field out := 0 } // // Yul64 - functions to do 64 bit math - see yul64.go // - function u64Mask() -> out { // max uint64 + function u64Mask() -> out { + // max uint64 out := shr(192, not(0)) // 256-64 = 192 } @@ -62,34 +63,32 @@ contract RISCV { out := signExtend64(and64(v, u32Mask()), toU64(31)) } - function u64Mod() -> out { // 1 << 64 + function u64Mod() -> out { + // 1 << 64 out := shl(toU256(64), toU256(1)) } - function u64TopBit() -> out { // 1 << 63 + function u64TopBit() -> out { + // 1 << 63 out := shl(toU256(63), toU256(1)) } function signExtend64(v, bit) -> out { switch and(v, shl(bit, 1)) case 0 { - // fill with zeroes, by masking + // fill with zeroes, by masking out := U64(and(U256(v), shr(sub(toU256(63), bit), U256(u64Mask())))) } default { - // fill with ones, by or-ing + // fill with ones, by or-ing out := U64(or(U256(v), shl(bit, shr(bit, U256(u64Mask()))))) } } function signExtend64To256(v) -> out { switch and(U256(v), u64TopBit()) - case 0 { - out := v - } - default { - out := or(shl(toU256(64), not(0)), v) - } + case 0 { out := v } + default { out := or(shl(toU256(64), not(0)), v) } } function add64(x, y) -> out { @@ -108,7 +107,8 @@ contract RISCV { out := u256ToU64(div(U256(x), U256(y))) } - function sdiv64(x, y) -> out { // note: signed overflow semantics are the same between Go and EVM assembly + function sdiv64(x, y) -> out { + // note: signed overflow semantics are the same between Go and EVM assembly out := u256ToU64(sdiv(signExtend64To256(x), signExtend64To256(y))) } @@ -225,9 +225,7 @@ contract RISCV { x := shr(1, x) n := add(n, 1) } - if gt(x, 0) { - n := add(n, 1) - } + if gt(x, 0) { n := add(n, 1) } } function endianSwap(x) -> out { @@ -237,32 +235,73 @@ contract RISCV { } } - // // State layout // - function stateSizeMemRoot() -> out { out := 32 } - function stateSizePreimageKey() -> out { out := 32 } - function stateSizePreimageOffset() -> out { out := 8 } - function stateSizePC() -> out { out := 8 } - function stateSizeExitCode() -> out { out := 1 } - function stateSizeExited() -> out { out := 1 } - function stateSizeStep() -> out { out := 8 } - function stateSizeHeap() -> out { out := 8 } - function stateSizeLoadReservation() -> out { out := 8 } - function stateSizeRegisters() -> out { out := mul(8, 32) } - - function stateOffsetMemRoot() -> out { out := 0 } - function stateOffsetPreimageKey() -> out { out := add(stateOffsetMemRoot(), stateSizeMemRoot()) } - function stateOffsetPreimageOffset() -> out { out := add(stateOffsetPreimageKey(), stateSizePreimageKey()) } - function stateOffsetPC() -> out { out := add(stateOffsetPreimageOffset(), stateSizePreimageOffset()) } - function stateOffsetExitCode() -> out { out := add(stateOffsetPC(), stateSizePC()) } - function stateOffsetExited() -> out { out := add(stateOffsetExitCode(), stateSizeExitCode()) } - function stateOffsetStep() -> out { out := add(stateOffsetExited(), stateSizeExited()) } - function stateOffsetHeap() -> out { out := add(stateOffsetStep(), stateSizeStep()) } - function stateOffsetLoadReservation() -> out { out := add(stateOffsetHeap(), stateSizeHeap()) } - function stateOffsetRegisters() -> out { out := add(stateOffsetLoadReservation(), stateSizeLoadReservation()) } - function stateSize() -> out { out := add(stateOffsetRegisters(), stateSizeRegisters()) } + function stateSizeMemRoot() -> out { + out := 32 + } + function stateSizePreimageKey() -> out { + out := 32 + } + function stateSizePreimageOffset() -> out { + out := 8 + } + function stateSizePC() -> out { + out := 8 + } + function stateSizeExitCode() -> out { + out := 1 + } + function stateSizeExited() -> out { + out := 1 + } + function stateSizeStep() -> out { + out := 8 + } + function stateSizeHeap() -> out { + out := 8 + } + function stateSizeLoadReservation() -> out { + out := 8 + } + function stateSizeRegisters() -> out { + out := mul(8, 32) + } + + function stateOffsetMemRoot() -> out { + out := 0 + } + function stateOffsetPreimageKey() -> out { + out := add(stateOffsetMemRoot(), stateSizeMemRoot()) + } + function stateOffsetPreimageOffset() -> out { + out := add(stateOffsetPreimageKey(), stateSizePreimageKey()) + } + function stateOffsetPC() -> out { + out := add(stateOffsetPreimageOffset(), stateSizePreimageOffset()) + } + function stateOffsetExitCode() -> out { + out := add(stateOffsetPC(), stateSizePC()) + } + function stateOffsetExited() -> out { + out := add(stateOffsetExitCode(), stateSizeExitCode()) + } + function stateOffsetStep() -> out { + out := add(stateOffsetExited(), stateSizeExited()) + } + function stateOffsetHeap() -> out { + out := add(stateOffsetStep(), stateSizeStep()) + } + function stateOffsetLoadReservation() -> out { + out := add(stateOffsetHeap(), stateSizeHeap()) + } + function stateOffsetRegisters() -> out { + out := add(stateOffsetLoadReservation(), stateSizeLoadReservation()) + } + function stateSize() -> out { + out := add(stateOffsetRegisters(), stateSizeRegisters()) + } // // Initial EVM memory / calldata checks @@ -279,7 +318,8 @@ contract RISCV { // user-provided state size must match expected state size revert(0, 0) } - function paddedLen(v) -> out { // padded to multiple of 32 bytes + function paddedLen(v) -> out { + // padded to multiple of 32 bytes let padding := mod(sub(32, mod(v, 32)), 32) out := add(v, padding) } @@ -287,19 +327,20 @@ contract RISCV { // 132+stateSize+padding+32 = expected proof offset revert(0, 0) } - function proofContentOffset() -> out { // since we can't reference proof.offset in functions, blame Yul + function proofContentOffset() -> out { + // since we can't reference proof.offset in functions, blame Yul // 132+362+(32-362%32)+32=548 out := 548 } - if iszero(eq(proof.offset, proofContentOffset())) { - revert(0, 0) - } + if iszero(eq(proof.offset, proofContentOffset())) { revert(0, 0) } // TODO: validate abi offset values? // // State loading // - function memStateOffset() -> out { out := 0x80 } + function memStateOffset() -> out { + out := 0x80 + } // copy the state calldata into memory, so we can mutate it mstore(0x40, add(memStateOffset(), stateSize())) // alloc, update free mem pointer calldatacopy(memStateOffset(), stateData.offset, stateSize()) // same format in memory as in calldata @@ -389,20 +430,19 @@ contract RISCV { } function getRegister(reg) -> out { - if gt64(reg, toU64(31)) { - revertWithCode(0xbad4e9) // cannot load invalid register - } + if gt64(reg, toU64(31)) { revertWithCode(0xbad4e9) } // cannot load invalid register + let offset := add64(toU64(stateOffsetRegisters()), mul64(reg, toU64(8))) out := readState(offset, 8) } function setRegister(reg, v) { - if iszero64(reg) { // reg 0 must stay 0 + if iszero64(reg) { + // reg 0 must stay 0 // v is a HINT, but no hints are specified by standard spec, or used by us. leave } - if gt64(reg, toU64(31)) { - revertWithCode(0xbad4e9) // unknown register - } + if gt64(reg, toU64(31)) { revertWithCode(0xbad4e9) } // unknown register + let offset := add64(toU64(stateOffsetRegisters()), mul64(reg, toU64(8))) writeState(offset, 8, v) } @@ -414,16 +454,13 @@ contract RISCV { switch getExited() case 1 { switch getExitCode() - case 0 { - status := 0 // VMStatusValid - } case 1 { - status := 1 // VMStatusInvalid - } default { - status := 2 // VMStatusPanic - } - } default { - status := 3 // VMStatusUnfinished + case 0 { status := 0 } + // VMStatusValid + case 1 { status := 1 } + // VMStatusInvalid + default { status := 2 } // VMStatusPanic } + default { status := 3 } // VMStatusUnfinished } function computeStateHash() -> out { @@ -442,28 +479,28 @@ contract RISCV { } function parseImmTypeS(instr) -> out { - out := signExtend64( - or64( - shl64(toU64(5), shr64(toU64(25), instr)), - and64(shr64(toU64(7), instr), toU64(0x1F)) - ), - toU64(11)) + out := + signExtend64( + or64(shl64(toU64(5), shr64(toU64(25), instr)), and64(shr64(toU64(7), instr), toU64(0x1F))), + toU64(11) + ) } function parseImmTypeB(instr) -> out { - out := signExtend64( - or64( + out := + signExtend64( or64( - shl64(toU64(1), and64(shr64(toU64(8), instr), toU64(0xF))), - shl64(toU64(5), and64(shr64(toU64(25), instr), toU64(0x3F))) + or64( + shl64(toU64(1), and64(shr64(toU64(8), instr), toU64(0xF))), + shl64(toU64(5), and64(shr64(toU64(25), instr), toU64(0x3F))) + ), + or64( + shl64(toU64(11), and64(shr64(toU64(7), instr), toU64(1))), + shl64(toU64(12), shr64(toU64(31), instr)) + ) ), - or64( - shl64(toU64(11), and64(shr64(toU64(7), instr), toU64(1))), - shl64(toU64(12), shr64(toU64(31), instr)) - ) - ), - toU64(12) - ) + toU64(12) + ) } function parseImmTypeU(instr) -> out { @@ -471,19 +508,20 @@ contract RISCV { } function parseImmTypeJ(instr) -> out { - out := signExtend64( - or64( + out := + signExtend64( or64( - and64(shr64(toU64(21), instr), shortToU64(0x3FF)), // 10 bits for index 0:9 - shl64(toU64(10), and64(shr64(toU64(20), instr), toU64(1))) // 1 bit for index 10 + or64( + and64(shr64(toU64(21), instr), shortToU64(0x3FF)), // 10 bits for index 0:9 + shl64(toU64(10), and64(shr64(toU64(20), instr), toU64(1))) // 1 bit for index 10 + ), + or64( + shl64(toU64(11), and64(shr64(toU64(12), instr), toU64(0xFF))), // 8 bits for index 11:18 + shl64(toU64(19), shr64(toU64(31), instr)) // 1 bit for index 19 + ) ), - or64( - shl64(toU64(11), and64(shr64(toU64(12), instr), toU64(0xFF))), // 8 bits for index 11:18 - shl64(toU64(19), shr64(toU64(31), instr)) // 1 bit for index 19 - ) - ), - toU64(19) - ) + toU64(19) + ) } function parseOpcode(instr) -> out { @@ -530,7 +568,8 @@ contract RISCV { } function getMemoryB32(addr, proofIndex) -> out { - if and64(addr, toU64(31)) { // quick addr alignment check + if and64(addr, toU64(31)) { + // quick addr alignment check revertWithCode(0xbad10ad0) // addr not aligned with 32 bytes } let offset := proofOffset(proofIndex) @@ -538,19 +577,18 @@ contract RISCV { offset := add64(offset, toU64(32)) let path := shr64(toU64(5), addr) // 32 bytes of memory per leaf - let node := leaf // starting from the leaf node, work back up by combining with siblings, to reconstruct the root + let node := leaf // starting from the leaf node, work back up by combining with siblings, to reconstruct + // the root for { let i := 0 } lt(i, sub(64, 5)) { i := add(i, 1) } { let sibling := calldataload(offset) offset := add64(offset, toU64(32)) switch and64(shr64(toU64(i), path), toU64(1)) - case 0 { - node := hashPair(node, sibling) - } case 1 { - node := hashPair(sibling, node) - } + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } } let memRoot := getMemRoot() - if iszero(eq(b32asBEWord(node), b32asBEWord(memRoot))) { // verify the root matches + if iszero(eq(b32asBEWord(node), b32asBEWord(memRoot))) { + // verify the root matches revertWithCode(0xbadf00d1) // bad memory proof } out := leaf @@ -559,33 +597,28 @@ contract RISCV { // warning: setMemoryB32 does not verify the proof, // it assumes the same memory proof has been verified with getMemoryB32 function setMemoryB32(addr, v, proofIndex) { - if and64(addr, toU64(31)) { - revertWithCode(0xbad10ad0) // addr not aligned with 32 bytes - } + if and64(addr, toU64(31)) { revertWithCode(0xbad10ad0) } // addr not aligned with 32 bytes + let offset := proofOffset(proofIndex) let leaf := v offset := add64(offset, toU64(32)) let path := shr64(toU64(5), addr) // 32 bytes of memory per leaf - let node := leaf // starting from the leaf node, work back up by combining with siblings, to reconstruct the root + let node := leaf // starting from the leaf node, work back up by combining with siblings, to reconstruct + // the root for { let i := 0 } lt(i, sub(64, 5)) { i := add(i, 1) } { let sibling := calldataload(offset) offset := add64(offset, toU64(32)) switch and64(shr64(toU64(i), path), toU64(1)) - case 0 { - node := hashPair(node, sibling) - } case 1 { - node := hashPair(sibling, node) - } + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } } setMemRoot(node) // store new memRoot } // load unaligned, optionally signed, little-endian, integer of 1 ... 8 bytes from memory function loadMem(addr, size, signed, proofIndexL, proofIndexR) -> out { - if gt(size, 8) { - revertWithCode(0xbad512e0) // cannot load more than 8 bytes - } + if gt(size, 8) { revertWithCode(0xbad512e0) } // cannot load more than 8 bytes // load/verify left part let leftAddr := and64(addr, not64(toU64(31))) let left := b32asBEWord(getMemoryB32(leftAddr, proofIndexL)) @@ -597,9 +630,8 @@ contract RISCV { let rightShamt := toU64(0) if iszero64(eq64(leftAddr, rightAddr)) { // if unaligned, use second proof for the right part - if eq(proofIndexR, 0xff) { - revertWithCode(0xbad22220) // unexpected need for right-side proof in loadMem - } + if eq(proofIndexR, 0xff) { revertWithCode(0xbad22220) } // unexpected need for right-side proof in + // loadMem // load/verify right part right := b32asBEWord(getMemoryB32(rightAddr, proofIndexR)) // left content is aligned to right of 32 bytes @@ -613,7 +645,8 @@ contract RISCV { right := shr(u64ToU256(shl64(toU64(3), rightShamt)), right) // loop: for { let i := 0 } lt(i, size) { i := add(i, 1) } { - // translate to reverse byte lookup, since we are reading little-endian memory, and need the highest byte first. + // translate to reverse byte lookup, since we are reading little-endian memory, and need the highest + // byte first. // effAddr := (addr + size - 1 - i) &^ 31 let effAddr := and64(sub64(sub64(add64(addr, size), toU64(1)), toU64(i)), not64(toU64(31))) // take a byte from either left or right, depending on the effective address @@ -622,7 +655,8 @@ contract RISCV { case 1 { b := and(left, toU256(0xff)) left := shr(toU256(8), left) - } case 0 { + } + case 0 { b := and(right, toU256(0xff)) right := shr(toU256(8), right) } @@ -636,7 +670,8 @@ contract RISCV { } } - // Splits the value into a left and a right part, each with a mask (identify data) and a patch (diff content). + // Splits the value into a left and a right part, each with a mask (identify data) and a patch (diff + // content). function leftAndRight(alignment, size, value) -> leftMask, rightMask, leftPatch, rightPatch { let start := alignment let end := add64(alignment, size) @@ -647,17 +682,20 @@ contract RISCV { case 1 { leftPatch := shl(8, leftPatch) leftMask := shl(8, leftMask) - } case 0 { + } + case 0 { rightPatch := shl(8, rightPatch) rightMask := shl(8, rightMask) } - if and64(eq64(lt64(index, start), toU64(0)), lt64(index, end)) { // if alignment <= i < alignment+size + if and64(eq64(lt64(index, start), toU64(0)), lt64(index, end)) { + // if alignment <= i < alignment+size let b := and(shr(u64ToU256(shl64(toU64(3), sub64(index, alignment))), value), toU256(0xff)) switch leftSide case 1 { leftPatch := or(leftPatch, b) leftMask := or(leftMask, toU256(0xff)) - } case 0 { + } + case 0 { rightPatch := or(rightPatch, b) rightMask := or(rightMask, toU256(0xff)) } @@ -666,9 +704,7 @@ contract RISCV { } function storeMemUnaligned(addr, size, value, proofIndexL, proofIndexR) { - if gt(size, 32) { - revertWithCode(0xbad512e1) // cannot store more than 32 bytes - } + if gt(size, 32) { revertWithCode(0xbad512e1) } // cannot store more than 32 bytes let leftAddr := and64(addr, not64(toU64(31))) let rightAddr := and64(add64(addr, sub64(size, toU64(1))), not64(toU64(31))) @@ -683,12 +719,9 @@ contract RISCV { setMemoryB32(leftAddr, beWordAsB32(left), proofIndexL) // if aligned: nothing more to do here - if eq64(leftAddr, rightAddr) { - leave - } - if eq(proofIndexR, 0xff) { - revertWithCode(0xbad22221) // unexpected need for right-side proof in storeMem - } + if eq64(leftAddr, rightAddr) { leave } + if eq(proofIndexR, 0xff) { revertWithCode(0xbad22221) } // unexpected need for right-side proof in + // storeMem // load the right base (with updated mem root) let right := b32asBEWord(getMemoryB32(rightAddr, proofIndexR)) // apply the right patch @@ -716,13 +749,17 @@ contract RISCV { out := readCSR(num) switch mode case 1 { // ?01 = CSRRW(I) - } case 2 { // ?10 = CSRRS(I) + } + case 2 { + // ?10 = CSRRS(I) v := or64(out, v) - } case 3 { // ?11 = CSRRC(I) + } + case 3 { + // ?11 = CSRRC(I) v := and64(out, not64(v)) - } default { - revertWithCode(0xbadc0de0) // unknown CSR mode } + default { revertWithCode(0xbadc0de0) } // unknown CSR mode + writeCSR(num, v) } @@ -733,9 +770,7 @@ contract RISCV { // adjust count down, so we only have to read a single 32 byte leaf of memory let alignment := and64(addr, toU64(31)) let maxData := sub64(toU64(32), alignment) - if gt64(count, maxData) { - count := maxData - } + if gt64(count, maxData) { count := maxData } let dat := b32asBEWord(getMemoryB32(sub64(addr, alignment), 1)) // shift out leading bits @@ -761,12 +796,14 @@ contract RISCV { function readPreimagePart(key, offset) -> dat, datlen { let addr := sload(preimageOraclePos()) // calling Oracle.readPreimage(bytes32,uint256) let memPtr := mload(0x40) // get pointer to free memory for preimage interactions - mstore(memPtr, shl(224, 0xe03110e1)) // (32-4)*8=224: right-pad the function selector, and then store it as prefix + mstore(memPtr, shl(224, 0xe03110e1)) // (32-4)*8=224: right-pad the function selector, and then store it + // as prefix mstore(add(memPtr, 0x04), key) mstore(add(memPtr, 0x24), offset) let cgas := 100000 // TODO change call gas let res := call(cgas, addr, 0, memPtr, 0x44, 0x00, 0x40) // output into scratch space - if res { // 1 on success + if res { + // 1 on success dat := mload(0x00) datlen := mload(0x20) leave @@ -793,28 +830,26 @@ contract RISCV { let offset := getPreimageOffset() // If the preimage key is a local key, localize it in the context of the caller. let preImageKeyPrefix := shr(248, preImageKey) // 256-8=248 - if eq(preImageKeyPrefix, 1) { - preImageKey := localize(preImageKey, localContext_) - } + if eq(preImageKeyPrefix, 1) { preImageKey := localize(preImageKey, localContext_) } // make call to pre-image oracle contract let pdatB32, pdatlen := readPreimagePart(preImageKey, offset) - if iszero64(pdatlen) { // EOF + if iszero64(pdatlen) { + // EOF out := toU64(0) leave } - let alignment := and64(addr, toU64(31)) // how many bytes addr is offset from being left-aligned + let alignment := and64(addr, toU64(31)) // how many bytes addr is offset from being left-aligned let maxData := sub64(toU64(32), alignment) // higher alignment leaves less room for data this step - if gt64(count, maxData) { - count := maxData - } - if gt64(count, pdatlen) { // cannot read more than pdatlen + if gt64(count, maxData) { count := maxData } + if gt64(count, pdatlen) { + // cannot read more than pdatlen count := pdatlen } - let bits := shl64(toU64(3), sub64(toU64(32), count)) // 32-count, in bits + let bits := shl64(toU64(3), sub64(toU64(32), count)) // 32-count, in bits let mask := not(sub(shl(u64ToU256(bits), toU256(1)), toU256(1))) // left-aligned mask for count bytes let alignmentBits := u64ToU256(shl64(toU64(3), alignment)) - mask := shr(alignmentBits, mask) // mask of count bytes, shifted by alignment + mask := shr(alignmentBits, mask) // mask of count bytes, shifted by alignment let pdat := shr(alignmentBits, b32asBEWord(pdatB32)) // pdat, shifted by alignment // update pre-image reader with updated offset @@ -823,7 +858,7 @@ contract RISCV { let node := getMemoryB32(sub64(addr, alignment), 1) let dat := and(b32asBEWord(node), not(mask)) // keep old bytes outside of mask - dat := or(dat, and(pdat, mask)) // fill with bytes from pdat + dat := or(dat, and(pdat, mask)) // fill with bytes from pdat setMemoryB32(sub64(addr, alignment), beWordAsB32(dat), 1) out := count } @@ -834,29 +869,38 @@ contract RISCV { function sysCall(localContext_) { let a7 := getRegister(toU64(17)) switch a7 - case 93 { // exit the calling thread. No multi-thread support yet, so just exit. + case 93 { + // exit the calling thread. No multi-thread support yet, so just exit. let a0 := getRegister(toU64(10)) setExitCode(and(a0, 0xff)) setExited() // program stops here, no need to change registers. - } case 94 { // exit-group + } + case 94 { + // exit-group let a0 := getRegister(toU64(10)) setExitCode(and(a0, 0xff)) setExited() - } case 214 { // brk - // Go sys_linux_riscv64 runtime will only ever call brk(NULL), i.e. first argument (register a0) set to 0. + } + case 214 { + // brk + // Go sys_linux_riscv64 runtime will only ever call brk(NULL), i.e. first argument (register a0) set + // to 0. // brk(0) changes nothing about the memory, and returns the current page break let v := shl64(toU64(30), toU64(1)) // set program break at 1 GiB setRegister(toU64(10), v) setRegister(toU64(11), toU64(0)) // no error - } case 222 { // mmap + } + case 222 { + // mmap // A0 = addr (hint) let addr := getRegister(toU64(10)) // A1 = n (length) let length := getRegister(toU64(11)) // A2 = prot (memory protection type, can ignore) - // A3 = flags (shared with other process and or written back to file, can ignore) // TODO maybe assert the MAP_ANONYMOUS flag is set + // A3 = flags (shared with other process and or written back to file, can ignore) // TODO maybe + // assert the MAP_ANONYMOUS flag is set // A4 = fd (file descriptor, can ignore because we support anon memory only) // A5 = offset (offset in file, we don't support any non-anon memory, so we can ignore this) @@ -866,9 +910,7 @@ contract RISCV { // No hint, allocate it ourselves, by as much as the requested length. // Increase the length to align it with desired page size if necessary. let align := and64(length, shortToU64(4095)) - if align { - length := add64(length, sub64(shortToU64(4096), align)) - } + if align { length := add64(length, sub64(shortToU64(4096), align)) } let prevHeap := getHeap() setRegister(toU64(10), prevHeap) setHeap(add64(prevHeap, length)) // increment heap with length @@ -877,173 +919,256 @@ contract RISCV { // allow hinted memory address (leave it in A0 as return argument) } setRegister(toU64(11), toU64(0)) // no error - } case 63 { // read - let fd := getRegister(toU64(10)) // A0 = fd - let addr := getRegister(toU64(11)) // A1 = *buf addr + } + case 63 { + // read + let fd := getRegister(toU64(10)) // A0 = fd + let addr := getRegister(toU64(11)) // A1 = *buf addr let count := getRegister(toU64(12)) // A2 = count let n := 0 let errCode := 0 switch fd - case 0 { // stdin + case 0 { + // stdin n := toU64(0) // never read anything from stdin errCode := toU64(0) - } case 3 { // hint-read + } + case 3 { + // hint-read // say we read it all, to continue execution after reading the hint-write ack response n := count errCode := toU64(0) - } case 5 { // preimage read + } + case 5 { + // preimage read n := readPreimageValue(addr, count, localContext_) errCode := toU64(0) - } default { - n := u64Mask() // -1 (reading error) + } + default { + n := u64Mask() // -1 (reading error) errCode := toU64(0x4d) // EBADF } setRegister(toU64(10), n) setRegister(toU64(11), errCode) - } case 64 { // write - let fd := getRegister(toU64(10)) // A0 = fd - let addr := getRegister(toU64(11)) // A1 = *buf addr + } + case 64 { + // write + let fd := getRegister(toU64(10)) // A0 = fd + let addr := getRegister(toU64(11)) // A1 = *buf addr let count := getRegister(toU64(12)) // A2 = count let n := 0 let errCode := 0 switch fd - case 1 { // stdout + case 1 { + // stdout n := count // write completes fully in single instruction step errCode := toU64(0) - } case 2 { // stderr + } + case 2 { + // stderr n := count // write completes fully in single instruction step errCode := toU64(0) - } case 4 { // hint-write + } + case 4 { + // hint-write n := count errCode := toU64(0) - } case 6 { // pre-image key-write + } + case 6 { + // pre-image key-write n := writePreimageKey(addr, count) errCode := toU64(0) // no error - } default { // any other file, including (3) hint read (5) preimage read - n := u64Mask() // -1 (writing error) + } + default { + // any other file, including (3) hint read (5) preimage read + n := u64Mask() // -1 (writing error) errCode := toU64(0x4d) // EBADF } setRegister(toU64(10), n) setRegister(toU64(11), errCode) - } case 25 { // fcntl - file descriptor manipulation / info lookup - let fd := getRegister(toU64(10)) // A0 = fd + } + case 25 { + // fcntl - file descriptor manipulation / info lookup + let fd := getRegister(toU64(10)) // A0 = fd let cmd := getRegister(toU64(11)) // A1 = cmd let out := 0 let errCode := 0 switch cmd - case 0x3 { // F_GETFL: get file descriptor flags + case 0x3 { + // F_GETFL: get file descriptor flags switch fd - case 0 { // stdin + case 0 { + // stdin out := toU64(0) // O_RDONLY - } case 1 { // stdout + } + case 1 { + // stdout out := toU64(1) // O_WRONLY - } case 2 { // stderr + } + case 2 { + // stderr out := toU64(1) // O_WRONLY - } case 3 { // hint-read + } + case 3 { + // hint-read out := toU64(0) // O_RDONLY - } case 4 { // hint-write + } + case 4 { + // hint-write out := toU64(1) // O_WRONLY - } case 5 { // pre-image read + } + case 5 { + // pre-image read out := toU64(0) // O_RDONLY - } case 6 { // pre-image write + } + case 6 { + // pre-image write out := toU64(1) // O_WRONLY - } default { + } + default { out := u64Mask() errCode := toU64(0x4d) // EBADF } - } default { // no other commands: don't allow changing flags, duplicating FDs, etc. + } + default { + // no other commands: don't allow changing flags, duplicating FDs, etc. out := u64Mask() errCode := toU64(0x16) // EINVAL (cmd not recognized by this kernel) } setRegister(toU64(10), out) setRegister(toU64(11), errCode) // EBADF - } case 56 { // openat - the Go linux runtime will try to open optional /sys/kernel files for performance hints + } + case 56 { + // openat - the Go linux runtime will try to open optional /sys/kernel files for performance hints setRegister(toU64(10), u64Mask()) setRegister(toU64(11), toU64(0xd)) // EACCES - no access allowed - } case 123 { // sched_getaffinity - hardcode to indicate affinity with any cpu-set mask + } + case 123 { + // sched_getaffinity - hardcode to indicate affinity with any cpu-set mask setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 124 { // sched_yield - nothing to yield, synchronous execution only, for now + } + case 124 { + // sched_yield - nothing to yield, synchronous execution only, for now setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 113 { // clock_gettime + } + case 113 { + // clock_gettime let addr := getRegister(toU64(11)) // addr of timespec struct // write 1337s + 42ns as time storeMemUnaligned(addr, toU64(8), shortToU256(1337), 1, 0xff) storeMemUnaligned(add64(addr, toU64(8)), toU64(8), toU256(42), 2, 0xff) setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 135 { // rt_sigprocmask - ignore any sigset changes + } + case 135 { + // rt_sigprocmask - ignore any sigset changes setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 132 { // sigaltstack - ignore any hints of an alternative signal receiving stack addr + } + case 132 { + // sigaltstack - ignore any hints of an alternative signal receiving stack addr setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 178 { // gettid - hardcode to 0 + } + case 178 { + // gettid - hardcode to 0 setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 134 { // rt_sigaction - no-op, we never send signals, and thus need no sig handler info + } + case 134 { + // rt_sigaction - no-op, we never send signals, and thus need no sig handler info setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 220 { // clone - not supported + } + case 220 { + // clone - not supported setRegister(toU64(10), toU64(1)) setRegister(toU64(11), toU64(0)) - } case 163 { // getrlimit + } + case 163 { + // getrlimit let res := getRegister(toU64(10)) let addr := getRegister(toU64(11)) switch res - case 0x7 { // RLIMIT_NOFILE + case 0x7 { + // RLIMIT_NOFILE // first 8 bytes: soft limit. 1024 file handles max open // second 8 bytes: hard limit - storeMemUnaligned(addr, toU64(16), or(shortToU256(1024), shl(toU256(64), shortToU256(1024))), 1, 2) + storeMemUnaligned( + addr, toU64(16), or(shortToU256(1024), shl(toU256(64), shortToU256(1024))), 1, 2 + ) setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } default { - revertWithCode(0xf0012) // unrecognized resource limit lookup } - } case 233 { // madvise - ignored + default { revertWithCode(0xf0012) } // unrecognized resource limit lookup + } + case 233 { + // madvise - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 20 { // epoll_create1 - ignored + } + case 20 { + // epoll_create1 - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 21 { // epoll_ctl - ignored + } + case 21 { + // epoll_ctl - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 59 { // pipe2 - ignored + } + case 59 { + // pipe2 - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 78 { // readlinkat - ignored + } + case 78 { + // readlinkat - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 79 { // newfstatat - ignored + } + case 79 { + // newfstatat - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 160 { // newuname - ignored + } + case 160 { + // newuname - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 215 { // munmap - ignored + } + case 215 { + // munmap - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 278 { // getrandom - ignored + } + case 278 { + // getrandom - ignored setRegister(toU64(10), toU64(0)) setRegister(toU64(11), toU64(0)) - } case 261 { // prlimit64 -- unsupported, we have getrlimit, is prlimit64 even called? + } + case 261 { + // prlimit64 -- unsupported, we have getrlimit, is prlimit64 even called? revertWithCode(0xf001ca11) // unsupported system call - } case 422 { // futex - not supported, for now + } + case 422 { + // futex - not supported, for now revertWithCode(0xf001ca11) // unsupported system call - } case 101 { // nanosleep - not supported, for now + } + case 101 { + // nanosleep - not supported, for now revertWithCode(0xf001ca11) // unsupported system call - } default { - revertWithCode(0xf001ca11) // unrecognized system call } + default { revertWithCode(0xf001ca11) } // unrecognized system call } // // Instruction execution // - - if getExited() { // early exit if we can + if getExited() { + // early exit if we can mstore(0, computeStateHash()) return(0, 0x20) } @@ -1061,17 +1186,20 @@ contract RISCV { let funct7 := parseFunct7(instr) switch opcode - case 0x03 { // 000_0011: memory loading + case 0x03 { + // 000_0011: memory loading // LB, LH, LW, LD, LBU, LHU, LWU let imm := parseImmTypeI(instr) - let signed := iszero64(and64(funct3, toU64(4))) // 4 = 100 -> bitflag + let signed := iszero64(and64(funct3, toU64(4))) // 4 = 100 -> bitflag let size := shl64(and64(funct3, toU64(3)), toU64(1)) // 3 = 11 -> 1, 2, 4, 8 bytes size let rs1Value := getRegister(rs1) let memIndex := add64(rs1Value, signExtend64(imm, toU64(11))) let rdValue := loadMem(memIndex, size, signed, 1, 2) setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x23 { // 010_0011: memory storing + } + case 0x23 { + // 010_0011: memory storing // SB, SH, SW, SD let imm := parseImmTypeS(instr) let size := shl64(funct3, toU64(1)) @@ -1080,264 +1208,377 @@ contract RISCV { let memIndex := add64(rs1Value, signExtend64(imm, toU64(11))) storeMem(memIndex, size, value, 1, 2) setPC(add64(_pc, toU64(4))) - } case 0x63 { // 110_0011: branching + } + case 0x63 { + // 110_0011: branching let rs1Value := getRegister(rs1) let rs2Value := getRegister(rs2) let branchHit := toU64(0) switch funct3 - case 0 { // 000 = BEQ + case 0 { + // 000 = BEQ branchHit := eq64(rs1Value, rs2Value) - } case 1 { // 001 = BNE + } + case 1 { + // 001 = BNE branchHit := and64(not64(eq64(rs1Value, rs2Value)), toU64(1)) - } case 4 { // 100 = BLT + } + case 4 { + // 100 = BLT branchHit := slt64(rs1Value, rs2Value) - } case 5 { // 101 = BGE + } + case 5 { + // 101 = BGE branchHit := and64(not64(slt64(rs1Value, rs2Value)), toU64(1)) - } case 6 { // 110 = BLTU + } + case 6 { + // 110 = BLTU branchHit := lt64(rs1Value, rs2Value) - } case 7 { // 111 := BGEU + } + case 7 { + // 111 := BGEU branchHit := and64(not64(lt64(rs1Value, rs2Value)), toU64(1)) } switch branchHit - case 0 { - _pc := add64(_pc, toU64(4)) - } default { + case 0 { _pc := add64(_pc, toU64(4)) } + default { let imm := parseImmTypeB(instr) // imm12 is a signed offset, in multiples of 2 bytes. - // So it's really 13 bits with a hardcoded 0 bit. + // So it's really 13 bits with a hardcoded 0 bit. _pc := add64(_pc, imm) } // not like the other opcodes: nothing to write to rd register, and PC has already changed setPC(_pc) - } case 0x13 { // 001_0011: immediate arithmetic and logic - let rs1Value := getRegister(rs1) + } + case 0x13 { + // 001_0011: immediate arithmetic and logic + let rs1Value := getRegister(rs1) let imm := parseImmTypeI(instr) let rdValue := 0 switch funct3 - case 0 { // 000 = ADDI + case 0 { + // 000 = ADDI rdValue := add64(rs1Value, imm) - } case 1 { // 001 = SLLI + } + case 1 { + // 001 = SLLI rdValue := shl64(and64(imm, toU64(0x3F)), rs1Value) // lower 6 bits in 64 bit mode - } case 2 { // 010 = SLTI + } + case 2 { + // 010 = SLTI rdValue := slt64(rs1Value, imm) - } case 3 { // 011 = SLTIU + } + case 3 { + // 011 = SLTIU rdValue := lt64(rs1Value, imm) - } case 4 { // 100 = XORI + } + case 4 { + // 100 = XORI rdValue := xor64(rs1Value, imm) - } case 5 { // 101 = SR~ - switch shr64(toU64(6), imm) // in rv64i the top 6 bits select the shift type - case 0x00 { // 000000 = SRLI + } + case 5 { + // 101 = SR~ + switch shr64(toU64(6), imm) + // in rv64i the top 6 bits select the shift type + case 0x00 { + // 000000 = SRLI rdValue := shr64(and64(imm, toU64(0x3F)), rs1Value) // lower 6 bits in 64 bit mode - } case 0x10 { // 010000 = SRAI + } + case 0x10 { + // 010000 = SRAI rdValue := sar64(and64(imm, toU64(0x3F)), rs1Value) // lower 6 bits in 64 bit mode } - } case 6 { // 110 = ORI + } + case 6 { + // 110 = ORI rdValue := or64(rs1Value, imm) - } case 7 { // 111 = ANDI + } + case 7 { + // 111 = ANDI rdValue := and64(rs1Value, imm) } setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x1B { // 001_1011: immediate arithmetic and logic signed 32 bit - let rs1Value := getRegister(rs1) + } + case 0x1B { + // 001_1011: immediate arithmetic and logic signed 32 bit + let rs1Value := getRegister(rs1) let imm := parseImmTypeI(instr) let rdValue := 0 switch funct3 - case 0 { // 000 = ADDIW + case 0 { + // 000 = ADDIW rdValue := mask32Signed64(add64(rs1Value, imm)) - } case 1 { // 001 = SLLIW + } + case 1 { + // 001 = SLLIW rdValue := mask32Signed64(shl64(and64(imm, toU64(0x1F)), rs1Value)) - } case 5 { // 101 = SR~ + } + case 5 { + // 101 = SR~ let shamt := and64(imm, toU64(0x1F)) - switch shr64(toU64(5), imm) // top 7 bits select the shift type - case 0x00 { // 0000000 = SRLIW + switch shr64(toU64(5), imm) + // top 7 bits select the shift type + case 0x00 { + // 0000000 = SRLIW rdValue := signExtend64(shr64(shamt, and64(rs1Value, u32Mask())), toU64(31)) - } case 0x20 { // 0100000 = SRAIW + } + case 0x20 { + // 0100000 = SRAIW rdValue := signExtend64(shr64(shamt, and64(rs1Value, u32Mask())), sub64(toU64(31), shamt)) } } setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x33 { // 011_0011: register arithmetic and logic - let rs1Value := getRegister(rs1) - let rs2Value := getRegister(rs2) + } + case 0x33 { + // 011_0011: register arithmetic and logic + let rs1Value := getRegister(rs1) + let rs2Value := getRegister(rs2) let rdValue := 0 switch funct7 - case 1 { // RV M extension + case 1 { + // RV M extension switch funct3 - case 0 { // 000 = MUL: signed x signed + case 0 { + // 000 = MUL: signed x signed rdValue := mul64(rs1Value, rs2Value) - } case 1 { // 001 = MULH: upper bits of signed x signed - rdValue := u256ToU64(shr(toU256(64), mul(signExtend64To256(rs1Value), signExtend64To256(rs2Value)))) - } case 2 { // 010 = MULHSU: upper bits of signed x unsigned + } + case 1 { + // 001 = MULH: upper bits of signed x signed + rdValue := + u256ToU64(shr(toU256(64), mul(signExtend64To256(rs1Value), signExtend64To256(rs2Value)))) + } + case 2 { + // 010 = MULHSU: upper bits of signed x unsigned rdValue := u256ToU64(shr(toU256(64), mul(signExtend64To256(rs1Value), u64ToU256(rs2Value)))) - } case 3 { // 011 = MULHU: upper bits of unsigned x unsigned + } + case 3 { + // 011 = MULHU: upper bits of unsigned x unsigned rdValue := u256ToU64(shr(toU256(64), mul(u64ToU256(rs1Value), u64ToU256(rs2Value)))) - } case 4 { // 100 = DIV + } + case 4 { + // 100 = DIV switch rs2Value - case 0 { - rdValue := u64Mask() - } default { - rdValue := sdiv64(rs1Value, rs2Value) - } - } case 5 { // 101 = DIVU + case 0 { rdValue := u64Mask() } + default { rdValue := sdiv64(rs1Value, rs2Value) } + } + case 5 { + // 101 = DIVU switch rs2Value - case 0 { - rdValue := u64Mask() - } default { - rdValue := div64(rs1Value, rs2Value) - } - } case 6 { // 110 = REM + case 0 { rdValue := u64Mask() } + default { rdValue := div64(rs1Value, rs2Value) } + } + case 6 { + // 110 = REM switch rs2Value - case 0 { - rdValue := rs1Value - } default { - rdValue := smod64(rs1Value, rs2Value) - } - } case 7 { // 111 = REMU + case 0 { rdValue := rs1Value } + default { rdValue := smod64(rs1Value, rs2Value) } + } + case 7 { + // 111 = REMU switch rs2Value - case 0 { - rdValue := rs1Value - } default { - rdValue := mod64(rs1Value, rs2Value) - } + case 0 { rdValue := rs1Value } + default { rdValue := mod64(rs1Value, rs2Value) } } - } default { + } + default { switch funct3 - case 0 { // 000 = ADD/SUB + case 0 { + // 000 = ADD/SUB switch funct7 - case 0x00 { // 0000000 = ADD + case 0x00 { + // 0000000 = ADD rdValue := add64(rs1Value, rs2Value) - } case 0x20 { // 0100000 = SUB + } + case 0x20 { + // 0100000 = SUB rdValue := sub64(rs1Value, rs2Value) } - } case 1 { // 001 = SLL - rdValue := shl64(and64(rs2Value, toU64(0x3F)), rs1Value) // only the low 6 bits are consider in RV6VI - } case 2 { // 010 = SLT + } + case 1 { + // 001 = SLL + rdValue := shl64(and64(rs2Value, toU64(0x3F)), rs1Value) // only the low 6 bits are consider in + // RV6VI + } + case 2 { + // 010 = SLT rdValue := slt64(rs1Value, rs2Value) - } case 3 { // 011 = SLTU + } + case 3 { + // 011 = SLTU rdValue := lt64(rs1Value, rs2Value) - } case 4 { // 100 = XOR + } + case 4 { + // 100 = XOR rdValue := xor64(rs1Value, rs2Value) - } case 5 { // 101 = SR~ + } + case 5 { + // 101 = SR~ switch funct7 - case 0x00 { // 0000000 = SRL + case 0x00 { + // 0000000 = SRL rdValue := shr64(and64(rs2Value, toU64(0x3F)), rs1Value) // logical: fill with zeroes - } case 0x20 { // 0100000 = SRA + } + case 0x20 { + // 0100000 = SRA rdValue := sar64(and64(rs2Value, toU64(0x3F)), rs1Value) // arithmetic: sign bit is extended } - } case 6 { // 110 = OR + } + case 6 { + // 110 = OR rdValue := or64(rs1Value, rs2Value) - } case 7 { // 111 = AND + } + case 7 { + // 111 = AND rdValue := and64(rs1Value, rs2Value) } } setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x3B { // 011_1011: register arithmetic and logic in 32 bits + } + case 0x3B { + // 011_1011: register arithmetic and logic in 32 bits let rs1Value := getRegister(rs1) let rs2Value := getRegister(rs2) let rdValue := 0 switch funct7 - case 1 { // RV M extension + case 1 { + // RV M extension switch funct3 - case 0 { // 000 = MULW + case 0 { + // 000 = MULW rdValue := mask32Signed64(mul64(and64(rs1Value, u32Mask()), and64(rs2Value, u32Mask()))) - } case 4 { // 100 = DIVW + } + case 4 { + // 100 = DIVW switch rs2Value - case 0 { - rdValue := u64Mask() - } default { + case 0 { rdValue := u64Mask() } + default { rdValue := mask32Signed64(sdiv64(mask32Signed64(rs1Value), mask32Signed64(rs2Value))) } - } case 5 { // 101 = DIVUW + } + case 5 { + // 101 = DIVUW switch rs2Value - case 0 { - rdValue := u64Mask() - } default { + case 0 { rdValue := u64Mask() } + default { rdValue := mask32Signed64(div64(and64(rs1Value, u32Mask()), and64(rs2Value, u32Mask()))) } - } case 6 { // 110 = REMW + } + case 6 { + // 110 = REMW switch rs2Value - case 0 { - rdValue := mask32Signed64(rs1Value) - } default { + case 0 { rdValue := mask32Signed64(rs1Value) } + default { rdValue := mask32Signed64(smod64(mask32Signed64(rs1Value), mask32Signed64(rs2Value))) } - } case 7 { // 111 = REMUW + } + case 7 { + // 111 = REMUW switch rs2Value - case 0 { - rdValue := mask32Signed64(rs1Value) - } default { + case 0 { rdValue := mask32Signed64(rs1Value) } + default { rdValue := mask32Signed64(mod64(and64(rs1Value, u32Mask()), and64(rs2Value, u32Mask()))) } } - } default { + } + default { switch funct3 - case 0 { // 000 = ADDW/SUBW + case 0 { + // 000 = ADDW/SUBW switch funct7 - case 0x00 { // 0000000 = ADDW + case 0x00 { + // 0000000 = ADDW rdValue := mask32Signed64(add64(and64(rs1Value, u32Mask()), and64(rs2Value, u32Mask()))) - } case 0x20 { // 0100000 = SUBW + } + case 0x20 { + // 0100000 = SUBW rdValue := mask32Signed64(sub64(and64(rs1Value, u32Mask()), and64(rs2Value, u32Mask()))) } - } case 1 { // 001 = SLLW + } + case 1 { + // 001 = SLLW rdValue := mask32Signed64(shl64(and64(rs2Value, toU64(0x1F)), rs1Value)) - } case 5 { // 101 = SR~ + } + case 5 { + // 101 = SR~ let shamt := and64(rs2Value, toU64(0x1F)) switch funct7 - case 0x00 { // 0000000 = SRLW + case 0x00 { + // 0000000 = SRLW rdValue := signExtend64(shr64(shamt, and64(rs1Value, u32Mask())), toU64(31)) - } case 0x20 { // 0100000 = SRAW + } + case 0x20 { + // 0100000 = SRAW rdValue := signExtend64(shr64(shamt, and64(rs1Value, u32Mask())), sub64(toU64(31), shamt)) } } } setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x37 { // 011_0111: LUI = Load upper immediate + } + case 0x37 { + // 011_0111: LUI = Load upper immediate let imm := parseImmTypeU(instr) let rdValue := shl64(toU64(12), imm) setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x17 { // 001_0111: AUIPC = Add upper immediate to PC + } + case 0x17 { + // 001_0111: AUIPC = Add upper immediate to PC let imm := parseImmTypeU(instr) let rdValue := add64(_pc, signExtend64(shl64(toU64(12), imm), toU64(31))) setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) - } case 0x6F { // 110_1111: JAL = Jump and link + } + case 0x6F { + // 110_1111: JAL = Jump and link let imm := parseImmTypeJ(instr) let rdValue := add64(_pc, toU64(4)) setRegister(rd, rdValue) - setPC(add64(_pc, signExtend64(shl64(toU64(1), imm), toU64(20)))) // signed offset in multiples of 2 bytes (last bit is there, but ignored) - } case 0x67 { // 110_0111: JALR = Jump and link register - let rs1Value := getRegister(rs1) + setPC(add64(_pc, signExtend64(shl64(toU64(1), imm), toU64(20)))) // signed offset in multiples of 2 + // bytes (last bit is there, but ignored) + } + case 0x67 { + // 110_0111: JALR = Jump and link register + let rs1Value := getRegister(rs1) let imm := parseImmTypeI(instr) let rdValue := add64(_pc, toU64(4)) setRegister(rd, rdValue) - setPC(and64(add64(rs1Value, signExtend64(imm, toU64(11))), xor64(u64Mask(), toU64(1)))) // least significant bit is set to 0 - } case 0x73 { // 111_0011: environment things + setPC(and64(add64(rs1Value, signExtend64(imm, toU64(11))), xor64(u64Mask(), toU64(1)))) // least + // significant bit is set to 0 + } + case 0x73 { + // 111_0011: environment things switch funct3 - case 0 { // 000 = ECALL/EBREAK - switch shr64(toU64(20), instr) // I-type, top 12 bits - case 0 { // imm12 = 000000000000 ECALL + case 0 { + // 000 = ECALL/EBREAK + switch shr64(toU64(20), instr) + // I-type, top 12 bits + case 0 { + // imm12 = 000000000000 ECALL sysCall(localContext) setPC(add64(_pc, toU64(4))) - } default { // imm12 = 000000000001 EBREAK + } + default { + // imm12 = 000000000001 EBREAK setPC(add64(_pc, toU64(4))) // ignore breakpoint } - } default { // CSR instructions + } + default { + // CSR instructions let imm := parseCSRR(instr) let value := rs1 - if iszero64(and64(funct3, toU64(4))) { - value := getRegister(rs1) - } + if iszero64(and64(funct3, toU64(4))) { value := getRegister(rs1) } let mode := and64(funct3, toU64(3)) let rdValue := updateCSR(imm, value, mode) setRegister(rd, rdValue) setPC(add64(_pc, toU64(4))) } - } case 0x2F { // 010_1111: RV{32,64}A and RV{32,64}A atomic operations extension + } + case 0x2F { + // 010_1111: RV{32,64}A and RV{32,64}A atomic operations extension // acquire and release bits: // aq := and64(shr64(toU64(1), funct7), toU64(1)) // rl := and64(funct7, toU64(1)) @@ -1350,19 +1591,21 @@ contract RISCV { // 0b010 == RV32A W variants // 0b011 == RV64A D variants let size := shl64(funct3, toU64(1)) - if or(lt64(size, toU64(4)), gt64(size, toU64(8))) { - revertWithCode(0xbada70) // bad AMO size - } + if or(lt64(size, toU64(4)), gt64(size, toU64(8))) { revertWithCode(0xbada70) } // bad AMO size + let addr := getRegister(rs1) // TODO check if addr is aligned let op := shr64(toU64(2), funct7) switch op - case 0x2 { // 00010 = LR = Load Reserved + case 0x2 { + // 00010 = LR = Load Reserved let v := loadMem(addr, size, true, 1, 2) setRegister(rd, v) setLoadReservation(addr) - } case 0x3 { // 00011 = SC = Store Conditional + } + case 0x3 { + // 00011 = SC = Store Conditional let rdValue := toU64(1) if eq64(addr, getLoadReservation()) { let rs2Value := getRegister(rs2) @@ -1371,62 +1614,78 @@ contract RISCV { } setRegister(rd, rdValue) setLoadReservation(toU64(0)) - } default { // AMO: Atomic Memory Operation + } + default { + // AMO: Atomic Memory Operation let rs2Value := getRegister(rs2) - if eq64(size, toU64(4)) { - rs2Value := mask32Signed64(rs2Value) - } + if eq64(size, toU64(4)) { rs2Value := mask32Signed64(rs2Value) } let value := rs2Value let v := loadMem(addr, size, true, 1, 2) let rdValue := v switch op - case 0x0 { // 00000 = AMOADD = add + case 0x0 { + // 00000 = AMOADD = add v := add64(v, value) - } case 0x1 { // 00001 = AMOSWAP + } + case 0x1 { + // 00001 = AMOSWAP v := value - } case 0x4 { // 00100 = AMOXOR = xor + } + case 0x4 { + // 00100 = AMOXOR = xor v := xor64(v, value) - } case 0x8 { // 01000 = AMOOR = or + } + case 0x8 { + // 01000 = AMOOR = or v := or64(v, value) - } case 0xc { // 01100 = AMOAND = and + } + case 0xc { + // 01100 = AMOAND = and v := and64(v, value) - } case 0x10 { // 10000 = AMOMIN = min signed - if slt64(value, v) { - v := value - } - } case 0x14 { // 10100 = AMOMAX = max signed - if sgt64(value, v) { - v := value - } - } case 0x18 { // 11000 = AMOMINU = min unsigned - if lt64(value, v) { - v := value - } - } case 0x1c { // 11100 = AMOMAXU = max unsigned - if gt64(value, v) { - v := value - } - } default { - revertWithCode(0xf001a70) // unknown atomic operation } + case 0x10 { + // 10000 = AMOMIN = min signed + if slt64(value, v) { v := value } + } + case 0x14 { + // 10100 = AMOMAX = max signed + if sgt64(value, v) { v := value } + } + case 0x18 { + // 11000 = AMOMINU = min unsigned + if lt64(value, v) { v := value } + } + case 0x1c { + // 11100 = AMOMAXU = max unsigned + if gt64(value, v) { v := value } + } + default { revertWithCode(0xf001a70) } // unknown atomic operation + storeMem(addr, size, v, 1, 3) // after overwriting 1, proof 2 is no longer valid setRegister(rd, rdValue) } setPC(add64(_pc, toU64(4))) - } case 0x0F { // 000_1111: fence + } + case 0x0F { + // 000_1111: fence // Used to impose additional ordering constraints; flushing the mem operation pipeline. // This VM doesn't have a pipeline, nor additional harts, so this is a no-op. // FENCE / FENCE.TSO / FENCE.I all no-op: there's nothing to synchronize. setPC(add64(_pc, toU64(4))) - } case 0x07 { // FLW/FLD: floating point load word/double - setPC(add64(_pc, toU64(4))) // no-op this. - } case 0x27 { // FSW/FSD: floating point store word/double - setPC(add64(_pc, toU64(4))) // no-op this. - } case 0x53 { // FADD etc. no-op is enough to pass Go runtime check - setPC(add64(_pc, toU64(4))) // no-op this. - } default { - revertWithCode(0xf001c0de) // unknown instruction opcode } + case 0x07 { + // FLW/FLD: floating point load word/double + setPC(add64(_pc, toU64(4))) // no-op this. + } + case 0x27 { + // FSW/FSD: floating point store word/double + setPC(add64(_pc, toU64(4))) // no-op this. + } + case 0x53 { + // FADD etc. no-op is enough to pass Go runtime check + setPC(add64(_pc, toU64(4))) // no-op this. + } + default { revertWithCode(0xf001c0de) } // unknown instruction opcode mstore(0, computeStateHash()) return(0, 0x20) diff --git a/rvsol/test/CommonTest.sol b/rvsol/test/CommonTest.sol index d5cf1a4b..f0498e1c 100644 --- a/rvsol/test/CommonTest.sol +++ b/rvsol/test/CommonTest.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; +import { Test } from "forge-std/Test.sol"; +import { Vm } from "forge-std/Vm.sol"; /// @title FFIInterface /// @notice This contract is set into state using `etch` and therefore must not have constructor logic. @@ -23,7 +23,12 @@ contract FFIInterface { return (memRoot, proof); } - function getAsteriscMemoryProof(uint64 pc, uint32 insn, uint64 memAddr, bytes32 memVal) + function getAsteriscMemoryProof( + uint64 pc, + uint32 insn, + uint64 memAddr, + bytes32 memVal + ) external returns (bytes32, bytes memory) { diff --git a/rvsol/test/RISCV.t.sol b/rvsol/test/RISCV.t.sol index 9076533b..3385de7d 100644 --- a/rvsol/test/RISCV.t.sol +++ b/rvsol/test/RISCV.t.sol @@ -1,9 +1,9 @@ pragma solidity 0.8.15; -import {Test} from "forge-std/Test.sol"; -import {RISCV} from "src/RISCV.sol"; -import {PreimageOracle} from "@optimism/src/cannon/PreimageOracle.sol"; -import {CommonTest} from "./CommonTest.sol"; +import { Test } from "forge-std/Test.sol"; +import { RISCV } from "src/RISCV.sol"; +import { PreimageOracle } from "@optimism/src/cannon/PreimageOracle.sol"; +import { CommonTest } from "./CommonTest.sol"; // FIXME: somehow this import gives a multiple declaration error // This import is for the VMStatus // import "@optimism/src/libraries/DisputeTypes.sol"; @@ -2407,7 +2407,12 @@ contract RISCV_Test is CommonTest { } } - function constructRISCVState(uint64 pc, uint32 insn, uint64 addr, bytes32 val) + function constructRISCVState( + uint64 pc, + uint32 insn, + uint64 addr, + bytes32 val + ) internal returns (State memory state, bytes memory proof) { @@ -2420,7 +2425,14 @@ contract RISCV_Test is CommonTest { state.pc = pc; } - function encodeRType(uint8 opcode, uint8 rd, uint8 funct3, uint8 rs1, uint8 rs2, uint8 funct7) + function encodeRType( + uint8 opcode, + uint8 rd, + uint8 funct3, + uint8 rs1, + uint8 rs2, + uint8 funct7 + ) internal pure returns (uint32 insn) @@ -2435,7 +2447,13 @@ contract RISCV_Test is CommonTest { insn |= uint32(opcode & 0x7F); } - function encodeIType(uint8 opcode, uint8 rd, uint8 funct3, uint8 rs1, uint16 imm) + function encodeIType( + uint8 opcode, + uint8 rd, + uint8 funct3, + uint8 rs1, + uint16 imm + ) internal pure returns (uint32 insn) @@ -2449,7 +2467,13 @@ contract RISCV_Test is CommonTest { insn |= uint32(opcode & 0x7F); } - function encodeSType(uint8 opcode, uint8 funct3, uint8 rs1, uint8 rs2, uint16 imm) + function encodeSType( + uint8 opcode, + uint8 funct3, + uint8 rs1, + uint8 rs2, + uint16 imm + ) internal pure returns (uint32 insn) @@ -2464,7 +2488,13 @@ contract RISCV_Test is CommonTest { insn |= uint32(opcode & 0x7F); } - function encodeBType(uint8 opcode, uint8 funct3, uint8 rs1, uint8 rs2, uint16 imm) + function encodeBType( + uint8 opcode, + uint8 funct3, + uint8 rs1, + uint8 rs2, + uint16 imm + ) internal pure returns (uint32 insn) From b479d5802d4165366f2e8f5882a9e0157b0843f2 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Fri, 22 Mar 2024 12:32:18 +0900 Subject: [PATCH 3/3] Add license identifier --- rvsol/test/RISCV.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/rvsol/test/RISCV.t.sol b/rvsol/test/RISCV.t.sol index 3385de7d..c9bb44d6 100644 --- a/rvsol/test/RISCV.t.sol +++ b/rvsol/test/RISCV.t.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol";