Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

samples.gno

5.61 Kb · 198 lines
  1package riscvdemo
  2
  3import "gno.land/p/moul/x/vm/riscv/v0"
  4
  5// The programs the front page offers, as the machine words a cross compiler
  6// would have emitted. They are assembled outside this repo and pinned here,
  7// and riscvdemo_test.gno runs every one of them to the expected answer, so a
  8// wrong word is a failing test rather than a page that traps when clicked.
  9type sample struct {
 10	slug   string // what UploadSample takes: short, no punctuation, stable
 11	name   string
 12	budget int64 // the instance's total instruction budget, not the slice
 13
 14	// builtin names an image the library ships compiled, instead of the words
 15	// below. Empty means the words are the program.
 16	builtin string
 17	words []uint32
 18	input string
 19	note  string
 20	asm   string
 21}
 22
 23var samples = []sample{
 24	{
 25		slug: "hello",
 26		name: "Hello, gno.land",
 27		words: []uint32{
 28			0x00100513, 0x000015B7, 0x02058593, 0x01000613,
 29			0x04000893, 0x00000073, 0x05D00893, 0x00000073,
 30			0x6C6C6548, 0x67202C6F, 0x6C2E6F6E, 0x0A646E61,
 31		},
 32		budget: 1000,
 33		note: "Eight instructions and a string constant. The write syscall reads " +
 34			"the bytes straight out of the text segment and hands them to Host.Output.",
 35		asm: `addi a0, zero, 1          # fd
 36lui  a1, 0x1000           # buf
 37addi a1, a1, 0x20
 38addi a2, zero, 16         # len
 39addi a7, zero, 64         # write
 40ecall
 41addi a7, zero, 93         # exit
 42ecall
 43.ascii "Hello, gno.land\n"`,
 44	},
 45	{
 46		slug: "sum100",
 47		name: "Sum 1 to 100",
 48		words: []uint32{
 49			0x06400413, 0x00000293, 0x00000313, 0x00130313,
 50			0x006282B3, 0xFE831CE3, 0x05D00893, 0x00000073,
 51		},
 52		budget: 1000,
 53		note: "A real loop, 303 instructions. Writes nothing: the answer is in t0, " +
 54			"and the instance page shows the register file.",
 55		asm: `addi s0, zero, 100
 56addi t0, zero, 0
 57addi t1, zero, 0
 58loop:
 59addi t1, t1, 1
 60add  t0, t0, t1
 61bne  t1, s0, loop
 62addi a7, zero, 93
 63ecall`,
 64	},
 65	{
 66		slug: "heavy",
 67		name: "Heavy loop",
 68		words: []uint32{
 69			0x0000C437, 0x35040413, 0x00000293, 0x00000313,
 70			0x00130313, 0x006282B3, 0x00000393, 0xFE831AE3,
 71			0x05D00893, 0x00000073,
 72		},
 73		budget: 250000,
 74		note: "200,006 instructions, four per iteration. At the largest slice this realm " +
 75			"allows it takes three transactions to finish, which is the thing it exists to show.",
 76		asm: `lui  s0, 0xc000           # s0 = 50000
 77addi s0, s0, 0x350
 78addi t0, zero, 0
 79addi t1, zero, 0
 80loop:
 81addi t1, t1, 1
 82add  t0, t0, t1
 83addi t2, zero, 0
 84bne  t1, s0, loop
 85addi a7, zero, 93
 86ecall`,
 87	},
 88	{
 89		slug: "mext",
 90		name: "Multiply and divide",
 91		words: []uint32{
 92			0x3B9AD2B7, 0xA0728293, 0x00300313, 0x026282B3,
 93			0x00700393, 0x0272D333, 0x05D00893, 0x00000073,
 94		},
 95		budget: 1000,
 96		note: "The M extension. t0 = 1000000007 * 3 truncated to 32 bits, then t1 = t0 / 7. " +
 97			"Division by zero and the one signed overflow case return values here " +
 98			"rather than trapping, which is what the spec says and what a chain needs.",
 99		asm: `lui  t0, 0x3b9ad000       # t0 = 1000000007
100addi t0, t0, -1529
101addi t1, zero, 3
102mul  t0, t0, t1
103addi t2, zero, 7
104divu t1, t0, t2
105addi a7, zero, 93
106ecall`,
107	},
108	{
109		slug:   "clang",
110		name:   "Compiled by clang",
111		input:  "gno.land",
112		budget: 20000,
113		note: "Not written for gno and not written by hand: a freestanding C program, " +
114			"compiled by clang for riscv32im and shipped as the bytes LLVM emitted. It reads " +
115			"the call input, hashes it with FNV-1a and writes eight hex digits back. " +
116			"With the default input it prints fb7ffba0. Source and build command: " +
117			"tools/riscv-guests/fnv.",
118		builtin: "fnv",
119		asm: `long n = syscall3(SYS_READ, 0, (long)in, sizeof(in));
120
121unsigned int h = 2166136261u;
122for (long i = 0; i < n; i++) {
123	h ^= in[i];
124	h *= 16777619u;      /* the reason this is rv32im */
125}
126
127for (int i = 0; i < 8; i++) {
128	unsigned int nib = (h >> (28 - i * 4)) & 0xF;
129	out[i] = nib < 10 ? '0' + nib : 'a' + (nib - 10);
130}
131
132syscall3(SYS_WRITE, 1, (long)out, 8);
133syscall3(SYS_EXIT, 0, 0, 0);`,
134	},
135	{
136		slug:   "ledger",
137		name:   "A token, in Rust",
138		input:  "mint alice 100\nmint bob 50\nsend alice bob 30\n",
139		budget: 20000,
140		note: "no_std Rust, compiled by rustc for riscv32im: mint, send and burn, applied " +
141			"from the call input. It links Rust's real core and compiler_builtins, so the " +
142			"balances being 64-bit means a divide runs __udivdi3 here, because RV32 has no " +
143			"64-bit divide instruction. With the default input it prints alice 70 and bob 80. " +
144			"Source: tools/riscv-guests/ledger.",
145		builtin: "ledger",
146		asm: `match cmd {
147	b"send" => {
148		let (from, rest) = field(rest);
149		let (to, rest) = field(rest);
150		let (amount, _) = field(rest);
151		let n = parse_u32(amount).ok_or("bad amount")?;
152		let fi = led.find(from).ok_or("unknown sender")?;
153		if led.balances[fi] < n {
154			return Err("insufficient balance");
155		}
156		let ti = led.intern(to).ok_or("ledger full or bad name")?;
157		led.balances[fi] -= n;
158		led.balances[ti] += n;
159		Ok(())
160	}
161	_ => Err("unknown command"),
162}`,
163	},
164}
165
166// image is what gets uploaded: either the words assembled here, or a program
167// the library ships because a compiler produced it.
168func (s *sample) image() []byte {
169	switch s.builtin {
170	case "fnv":
171		return riscv.GuestFNV()
172	case "ledger":
173		return riscv.GuestLedger()
174	}
175	return riscv.Image(s.words)
176}
177
178// lang is the fence tag for what the sample shows. The compiled guest shows
179// its C, because showing a disassembly of what LLVM did with it would bury the
180// only interesting fact: nobody wrote the machine code.
181func (s *sample) lang() string {
182	switch s.builtin {
183	case "fnv":
184		return "c"
185	case "ledger":
186		return "rust"
187	}
188	return "asm"
189}
190
191func sampleByName(slug string) *sample {
192	for i := range samples {
193		if samples[i].slug == slug {
194			return &samples[i]
195		}
196	}
197	return nil
198}