vxx2 / vlib / v3 / gen / wasm / code.v
155 lines · 122 sloc · 2.8 KB · 836d2b5f835e217a80d9bf688384aa3c8821d4ca
Raw
1module wasm
2
3// code.v emits the WASM instruction stream for a single function body. It is a
4// thin layer over a []u8 buffer with one method per opcode used by the backend,
5// analogous to the arm64 backend's asm.v instruction encoders.
6
7pub struct Code {
8pub mut:
9 bytes []u8
10}
11
12@[inline]
13pub fn (mut c Code) raw(b u8) {
14 c.bytes << b
15}
16
17// ---- constants ----
18
19pub fn (mut c Code) i32_const(v i64) {
20 c.bytes << 0x41
21 // Wrap to a signed 32-bit value so the LEB128 stays in i32 range; e.g.
22 // u32(4000000000) must encode as the 32-bit pattern, not a 5-byte i64.
23 leb_i(mut c.bytes, i64(i32(v)))
24}
25
26pub fn (mut c Code) i64_const(v i64) {
27 c.bytes << 0x42
28 leb_i(mut c.bytes, v)
29}
30
31pub fn (mut c Code) f32_const(v f32) {
32 c.bytes << 0x43
33 bits := u32(f32_bits(v))
34 for i in 0 .. 4 {
35 c.bytes << u8((bits >> (8 * i)) & 0xff)
36 }
37}
38
39pub fn (mut c Code) f64_const(v f64) {
40 c.bytes << 0x44
41 bits := f64_bits(v)
42 for i in 0 .. 8 {
43 c.bytes << u8((bits >> (8 * i)) & 0xff)
44 }
45}
46
47fn f32_bits(v f32) u32 {
48 return unsafe { *(&u32(&v)) }
49}
50
51fn f64_bits(v f64) u64 {
52 return unsafe { *(&u64(&v)) }
53}
54
55// ---- locals / globals ----
56
57pub fn (mut c Code) local_get(idx int) {
58 c.bytes << 0x20
59 leb_u(mut c.bytes, u64(idx))
60}
61
62pub fn (mut c Code) local_set(idx int) {
63 c.bytes << 0x21
64 leb_u(mut c.bytes, u64(idx))
65}
66
67pub fn (mut c Code) local_tee(idx int) {
68 c.bytes << 0x22
69 leb_u(mut c.bytes, u64(idx))
70}
71
72pub fn (mut c Code) global_get(idx int) {
73 c.bytes << 0x23
74 leb_u(mut c.bytes, u64(idx))
75}
76
77pub fn (mut c Code) global_set(idx int) {
78 c.bytes << 0x24
79 leb_u(mut c.bytes, u64(idx))
80}
81
82// ---- memory ----
83
84pub fn (mut c Code) load(op u8, align int, offset int) {
85 c.bytes << op
86 leb_u(mut c.bytes, u64(align))
87 leb_u(mut c.bytes, u64(offset))
88}
89
90pub fn (mut c Code) store(op u8, align int, offset int) {
91 c.bytes << op
92 leb_u(mut c.bytes, u64(align))
93 leb_u(mut c.bytes, u64(offset))
94}
95
96// i32_store / i32_load with natural alignment for a full i32.
97pub fn (mut c Code) i32_store(offset int) {
98 c.store(0x36, 2, offset)
99}
100
101pub fn (mut c Code) i32_store8(offset int) {
102 c.store(0x3a, 0, offset)
103}
104
105pub fn (mut c Code) i32_load(offset int) {
106 c.load(0x28, 2, offset)
107}
108
109// ---- control flow ----
110
111pub fn (mut c Code) block_void() {
112 c.bytes << 0x02
113 c.bytes << 0x40
114}
115
116pub fn (mut c Code) loop_void() {
117 c.bytes << 0x03
118 c.bytes << 0x40
119}
120
121pub fn (mut c Code) if_void() {
122 c.bytes << 0x04
123 c.bytes << 0x40
124}
125
126pub fn (mut c Code) else_() {
127 c.bytes << 0x05
128}
129
130pub fn (mut c Code) end() {
131 c.bytes << 0x0b
132}
133
134pub fn (mut c Code) br(depth int) {
135 c.bytes << 0x0c
136 leb_u(mut c.bytes, u64(depth))
137}
138
139pub fn (mut c Code) br_if(depth int) {
140 c.bytes << 0x0d
141 leb_u(mut c.bytes, u64(depth))
142}
143
144pub fn (mut c Code) ret() {
145 c.bytes << 0x0f
146}
147
148pub fn (mut c Code) call(idx int) {
149 c.bytes << 0x10
150 leb_u(mut c.bytes, u64(idx))
151}
152
153pub fn (mut c Code) drop() {
154 c.bytes << 0x1a
155}
156