PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/vm/riscv/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / vm / riscv
Files
17 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/vm/riscv/v0
gno
0.9

Files (17)

  • README.mdmarkdown
  • gnomod.tomltoml
  • decode.gnogno
  • ecall.gnogno
  • exec.gnogno
  • guests.gnogno
  • mem.gnogno
  • predecode.gnogno
  • snapshot.gnogno
  • bench_test.gnogno
  • clang_test.gnogno
  • conformance_test.gnogno
  • decode_test.gnogno
  • predecode_test.gnogno
  • riscv_test.gnogno
  • rust_test.gnogno
  • switchcost_test.gnogno
  • bench_test.gnogno
    1package riscv23import (4	"testing"56	"gno.land/p/moul/x/vm/vmkit/v0"7	"gno.land/p/nt/uassert/v0"8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    )
    9
    10// The measurement the design issue set a kill criterion against: if an RV32I
    11// instruction costs more than roughly 15k cycles after the bf ladder's tricks,
    12// a RISC-V guest runs ~200k instructions per block, which is a token transfer
    13// and not a program a Rust programmer would write without thinking about it.
    14//
    15// The loop is arithmetic and branches only, no syscalls and no memory beyond
    16// the fetch, so it measures the decode-and-dispatch path rather than the host.
    17
    18// li loads a full 32-bit constant, because addi cannot: its immediate is 12
    19// bits SIGN EXTENDED, so addi(rd, x0, 4000) loads -96 and a loop counting up to
    20// it never terminates. That is not hypothetical, it hung this benchmark for two
    21// days. The +0x800 pre-bias is the standard correction: it cancels the sign
    22// extension addi is about to apply to the low half.
    23func li(rd, v uint32) []uint32 {
    24 return []uint32{
    25 lui(rd, (v+0x800)&0xFFFFF000),
    26 addi(rd, rd, v&0xFFF),
    27 }
    28}
    29
    30func lui(rd, imm uint32) uint32 { return uType(imm, rd, opLUI) }
    31
    32// benchProg counts x6 up to x8 in a 4-instruction loop, then exits.
    33func benchProg(n uint32) []uint32 {
    34 prog := li(8, n) // x8 = n, the loop bound
    35 prog = append(prog,
    36 addi(5, 0, 0), // sum = 0
    37 addi(6, 0, 0), // i = 0
    38 // loop:
    39 add(5, 5, 6), // sum += i
    40 addi(6, 6, 1), // i++
    41 addi(7, 0, 0), // filler, keeps the loop a round 4 instructions
    42 bne(6, 8, 0x1FF4), // -12, while i != x8
    43 )
    44 return append(prog, exitWith()...)
    45}
    46
    47// benchInstrs is how many instructions benchProg(n) executes: 4 setup (li is
    48// two), 4 per iteration including the branch, 2 to exit.
    49func benchInstrs(n int64) int64 { return 4 + 4*n + 2 }
    50
    51// runBench returns the instruction count the machine charged itself. The fuel
    52// cap is ten times what the program should need: a mis-encoded immediate turns
    53// an unmetered run into an infinite loop with no test output, so the budget is
    54// what makes a bad encoding fail in a second instead of hanging the suite.
    55func runBench(t *testing.T, n uint32) int64 {
    56 t.Helper()
    57 m, err := NewMachine(asm(benchProg(n)), entry)
    58 uassert.NoError(t, err)
    59 if m == nil {
    60 return 0
    61 }
    62 used, status := m.Step(vmkit.NewTestHost(), benchInstrs(int64(n))*10)
    63 uassert.Equal(t, "halted", status.String())
    64 return used
    65}
    66
    67// One test per size, so `gno test -print-runtime-metrics` prints one cycle
    68// count each and the slope cancels the fixed per-test overhead. The README
    69// quotes the result.
    70
    71func TestBenchRV32I1000(t *testing.T) {
    72 uassert.Equal(t, benchInstrs(1000), runBench(t, 1000))
    73}
    74
    75func TestBenchRV32I4000(t *testing.T) {
    76 uassert.Equal(t, benchInstrs(4000), runBench(t, 4000))
    77}
    78
    79// li has to survive the sign-extension boundary in both directions, or the two
    80// sizes above are not measuring the loop counts they claim to.
    81func TestLoadImmediateSpansTheSignBoundary(t *testing.T) {
    82 for _, v := range []uint32{0, 1, 0x7FF, 0x800, 1000, 4000, 0x7FFFFFFF, 0xFFFFFFFF} {
    83 prog := append(li(5, v), exitWith()...)
    84 m, err := NewMachine(asm(prog), entry)
    85 uassert.NoError(t, err)
    86 if m == nil {
    87 continue
    88 }
    89 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    90 uassert.Equal(t, "halted", status.String())
    91 uassert.Equal(t, uint64(v), uint64(m.reg[5]))
    92 }
    93}
    94
    95// Predecoding is paid once per load, and a realm loads a machine on every
    96// transaction that resumes one. So the saving on the dispatch loop is only
    97// real if this side of the trade is small against the instructions the slice
    98// then executes. Two sizes again, so the slope is per word.
    99func benchImage(words int) []byte {
    100 img := make([]byte, words*4)
    101 inst := addi(5, 5, 1)
    102 for i := 0; i < words; i++ {
    103 img[i*4] = byte(inst)
    104 img[i*4+1] = byte(inst >> 8)
    105 img[i*4+2] = byte(inst >> 16)
    106 img[i*4+3] = byte(inst >> 24)
    107 }
    108 return img
    109}
    110
    111func TestBenchPredecode1000(t *testing.T) {
    112 c := predecode(entry, benchImage(1000))
    113 uassert.Equal(t, uint64(1000), uint64(c.words))
    114}
    115
    116func TestBenchPredecode4000(t *testing.T) {
    117 c := predecode(entry, benchImage(4000))
    118 uassert.Equal(t, uint64(4000), uint64(c.words))
    119}
    120
    121// The control, so the slope above is predecode and not the loop that builds
    122// the image.
    123func TestBenchImageOnly1000(t *testing.T) {
    124 uassert.Equal(t, 4000, len(benchImage(1000)))
    125}
    126
    127func TestBenchImageOnly4000(t *testing.T) {
    128 uassert.Equal(t, 16000, len(benchImage(4000)))
    129}
    130