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
  • mem.gnogno
    1package riscv23import "gno.land/p/moul/x/vm/vmkit/v0"45// Memory is the guest's flat address space, with a snapshot that carries only6// the pages it actually touched.7//8

    Functions

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

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

    // The shape is the one the bf ladder argued for. Running memory is a flat
    9// []byte because a load or a store happens on a large fraction of instructions
    10// and an avl lookup per access would dominate the interpreter the way
    11// vmkit.Meter.Charge did there. Snapshot cost is solved separately, by a dirty
    12// bitmap: a program that writes one page pauses in one page, not in a megabyte,
    13// which is the same trick that lets a bf hello world pause in 43 bytes.
    14//
    15// That answers the open question on the design issue ("memory size versus
    16// snapshot cost") without paying for it on every instruction.
    17const (
    18 // PageSize is the snapshot granularity, not an MMU page: there is no
    19 // address translation here, only bookkeeping about what changed.
    20 PageSize = 4096
    21
    22 // MemSize is the address space. 1 MiB is far more than a no_std guest
    23 // needs and small enough that the flat allocation is not the cost.
    24 MemSize = 1 << 20
    25
    26 pageCount = MemSize / PageSize
    27)
    28
    29// Memory is a flat address space plus the record of which pages were written.
    30type Memory struct {
    31 b []byte
    32 dirty []bool
    33}
    34
    35// NewMemory returns a zeroed address space.
    36func NewMemory() *Memory {
    37 return &Memory{b: make([]byte, MemSize), dirty: make([]bool, pageCount)}
    38}
    39
    40// Size returns the address space size.
    41func (m *Memory) Size() int { return len(m.b) }
    42
    43// DirtyPages reports how many pages the guest has written, which is what a
    44// snapshot costs.
    45func (m *Memory) DirtyPages() int {
    46 n := 0
    47 for _, d := range m.dirty {
    48 if d {
    49 n++
    50 }
    51 }
    52 return n
    53}
    54
    55// inRange reports whether [addr, addr+size) is inside the address space.
    56//
    57// Out of range is a trap rather than a wrap. RISC-V leaves the behaviour of an
    58// access outside physical memory to the platform, and a chain has to pick the
    59// one that cannot differ between nodes: refusing is deterministic, wrapping
    60// invites a guest to alias two addresses and get different answers from
    61// different memory sizes.
    62func (m *Memory) inRange(addr uint32, size int) bool {
    63 end := uint64(addr) + uint64(size)
    64 return end <= uint64(len(m.b))
    65}
    66
    67// Load8, Load16 and Load32 read little-endian, which is what RV32 is.
    68func (m *Memory) Load8(addr uint32) (uint8, bool) {
    69 if !m.inRange(addr, 1) {
    70 return 0, false
    71 }
    72 return m.b[addr], true
    73}
    74
    75func (m *Memory) Load16(addr uint32) (uint16, bool) {
    76 if !m.inRange(addr, 2) {
    77 return 0, false
    78 }
    79 return uint16(m.b[addr]) | uint16(m.b[addr+1])<<8, true
    80}
    81
    82func (m *Memory) Load32(addr uint32) (uint32, bool) {
    83 if !m.inRange(addr, 4) {
    84 return 0, false
    85 }
    86 return uint32(m.b[addr]) | uint32(m.b[addr+1])<<8 |
    87 uint32(m.b[addr+2])<<16 | uint32(m.b[addr+3])<<24, true
    88}
    89
    90func (m *Memory) Store8(addr uint32, v uint8) bool {
    91 if !m.inRange(addr, 1) {
    92 return false
    93 }
    94 m.b[addr] = v
    95 m.dirty[addr/PageSize] = true
    96 return true
    97}
    98
    99func (m *Memory) Store16(addr uint32, v uint16) bool {
    100 if !m.inRange(addr, 2) {
    101 return false
    102 }
    103 m.b[addr] = uint8(v)
    104 m.b[addr+1] = uint8(v >> 8)
    105 m.dirty[addr/PageSize] = true
    106 m.dirty[(addr+1)/PageSize] = true
    107 return true
    108}
    109
    110func (m *Memory) Store32(addr uint32, v uint32) bool {
    111 if !m.inRange(addr, 4) {
    112 return false
    113 }
    114 m.b[addr] = uint8(v)
    115 m.b[addr+1] = uint8(v >> 8)
    116 m.b[addr+2] = uint8(v >> 16)
    117 m.b[addr+3] = uint8(v >> 24)
    118 m.dirty[addr/PageSize] = true
    119 m.dirty[(addr+3)/PageSize] = true
    120 return true
    121}
    122
    123// WriteImage places a program image at addr and marks the pages it covers, so
    124// a snapshot taken before the first store still carries the program.
    125func (m *Memory) WriteImage(addr uint32, img []byte) bool {
    126 if !m.inRange(addr, len(img)) {
    127 return false
    128 }
    129 copy(m.b[addr:], img)
    130 for i := 0; i < len(img); i++ {
    131 m.dirty[(uint64(addr)+uint64(i))/PageSize] = true
    132 }
    133 return true
    134}
    135
    136// Snapshot writes only the dirty pages, each prefixed by its index.
    137func (m *Memory) Snapshot(w *vmkit.Writer) {
    138 w.Uint32(uint32(m.DirtyPages()))
    139 for i, d := range m.dirty {
    140 if !d {
    141 continue
    142 }
    143 w.Uint32(uint32(i))
    144 w.Bytes(m.b[i*PageSize : (i+1)*PageSize])
    145 }
    146}
    147
    148// Restore reads a snapshot written by Snapshot into a zeroed address space.
    149func (m *Memory) Restore(r *vmkit.Reader) error {
    150 m.b = make([]byte, MemSize)
    151 m.dirty = make([]bool, pageCount)
    152 n := int(r.Uint32())
    153 if n < 0 || n > pageCount {
    154 return vmkit.ErrBadSnapshot
    155 }
    156 for i := 0; i < n; i++ {
    157 idx := int(r.Uint32())
    158 page := r.Bytes()
    159 if err := r.Err(); err != nil {
    160 return err
    161 }
    162 if idx < 0 || idx >= pageCount || len(page) != PageSize {
    163 return vmkit.ErrBadSnapshot
    164 }
    165 copy(m.b[idx*PageSize:], page)
    166 m.dirty[idx] = true
    167 }
    168 return r.Err()
    169}
    170