Skip to content

Commit 7ebee2e

Browse files
authored
examples: add a brainfuck->wasm compiler example (#19492)
1 parent 8d98a21 commit 7ebee2e

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

examples/wasm_codegen/bf_compiler.v

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import os
2+
import wasm
3+
4+
const runtime_page = 1024 * 64 // 64 KiBs
5+
6+
fn generate_ciovec(mut start wasm.Function, sp wasm.LocalIndex) {
7+
// construct struct __wasi_ciovec_t
8+
//
9+
// field, `const uint8_t *buf`
10+
start.i32_const(runtime_page)
11+
start.local_get(sp)
12+
start.store(.i32_t, 2, 0)
13+
// field, `__wasi_size_t buf_len`
14+
start.i32_const(runtime_page)
15+
start.i32_const(1) // len
16+
start.store(.i32_t, 2, 4)
17+
}
18+
19+
fn generate_code(mut start wasm.Function, bf_expr string) {
20+
// locals are initialised to zero, by spec
21+
sp := start.new_local_named(.i32_t, 'sp')
22+
23+
mut loop_labels := []wasm.LabelIndex{}
24+
mut block_labels := []wasm.LabelIndex{}
25+
26+
// our page, the second one
27+
28+
for ch in bf_expr {
29+
match ch {
30+
`>` {
31+
start.local_get(sp)
32+
start.i32_const(1)
33+
start.add(.i32_t)
34+
start.local_set(sp)
35+
}
36+
`<` {
37+
start.local_get(sp)
38+
start.i32_const(1)
39+
start.sub(.i32_t)
40+
start.local_set(sp)
41+
}
42+
`+` {
43+
start.local_get(sp)
44+
{
45+
start.local_get(sp)
46+
start.load8(.i32_t, false, 0, 0)
47+
start.i32_const(1)
48+
start.add(.i32_t)
49+
}
50+
start.store8(.i32_t, 0, 0)
51+
}
52+
`-` {
53+
start.local_get(sp)
54+
{
55+
start.local_get(sp)
56+
start.load8(.i32_t, false, 0, 0)
57+
start.i32_const(1)
58+
start.sub(.i32_t)
59+
}
60+
start.store8(.i32_t, 0, 0)
61+
}
62+
`.` {
63+
generate_ciovec(mut start, sp)
64+
65+
start.i32_const(1) // stdout
66+
start.i32_const(runtime_page) // *iovs
67+
start.i32_const(1) // iovs_len
68+
start.i32_const(runtime_page + 1024) // *nwritten
69+
start.call_import('wasi_unstable', 'fd_write')
70+
start.drop() // ignore errno
71+
}
72+
`,` {
73+
generate_ciovec(mut start, sp)
74+
75+
start.i32_const(0) // stdin
76+
start.i32_const(runtime_page) // *iovs
77+
start.i32_const(1) // iovs_len
78+
start.i32_const(runtime_page + 1024) // *nwritten
79+
start.call_import('wasi_unstable', 'fd_read')
80+
start.drop() // ignore errno
81+
}
82+
`[` {
83+
block_lbl := start.c_block([], [])
84+
loop_lbl := start.c_loop([], [])
85+
{
86+
start.local_get(sp)
87+
start.load8(.i32_t, false, 0, 0)
88+
start.eqz(.i32_t)
89+
start.c_br_if(block_lbl)
90+
}
91+
loop_labels << loop_lbl
92+
block_labels << block_lbl
93+
}
94+
`]` {
95+
loop_lbl := loop_labels.pop()
96+
start.c_br(loop_lbl) // jump back to top
97+
start.c_end(loop_lbl)
98+
start.c_end(block_labels.pop())
99+
}
100+
else {}
101+
}
102+
}
103+
}
104+
105+
@[noreturn]
106+
fn usage() {
107+
eprintln('Usage: bf <expr> <outfile>')
108+
exit(1)
109+
}
110+
111+
fn main() {
112+
bf_expr := os.args[1] or { usage() }
113+
114+
outfile := os.args[2] or { usage() }
115+
116+
mut m := wasm.Module{}
117+
m.enable_debug('wasm bf')
118+
m.new_function_import('wasi_unstable', 'fd_write', [.i32_t, .i32_t, .i32_t, .i32_t],
119+
[.i32_t])
120+
m.new_function_import('wasi_unstable', 'fd_read', [.i32_t, .i32_t, .i32_t, .i32_t],
121+
[.i32_t])
122+
m.assign_memory('memory', true, 2, none)
123+
124+
mut start := m.new_function('_start', [], [])
125+
{
126+
generate_code(mut start, bf_expr)
127+
}
128+
m.commit(start, true)
129+
130+
bytes := m.compile()
131+
os.write_file_array(outfile, bytes)!
132+
}

0 commit comments

Comments
 (0)