Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}