v2 / vlib / wasm / instructions.v
1221 lines · 1088 sloc · 31.52 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2023 l-m.dev. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module wasm
5
6import encoding.leb128
7
8fn (mut func Function) u32(v u32) {
9 func.code << leb128.encode_u32(v)
10}
11
12fn (mut func Function) blocktype(typ FuncType) {
13 if typ.parameters.len == 0 {
14 if typ.results.len == 0 {
15 func.code << 0x40 // empty type
16 return
17 } else if typ.results.len == 1 {
18 func.code << u8(typ.results[0]) // encode a single result type
19 return
20 }
21 }
22
23 // encode full type
24 tidx := func.mod.new_functype(typ)
25 func.code << leb128.encode_i32(i32(tidx))
26}
27
28pub type PatchPos = int
29
30// patch_pos returns a `PatchPos` for use with `patch`.
31pub fn (func Function) patch_pos() PatchPos {
32 return func.code.len
33}
34
35// patch "patches" the code generated starting from the last `patch_pos` call in `begin` to `loc`.
36//
37// ```v
38// start := func.patch_pos()
39//
40// ...
41//
42// patch_block := func.patch_pos()
43// {
44// func.i32_const(10)
45// func.local_set(idx)
46// }
47// func.patch(start, patch_block) // will patch code to the `start`.
48// // func.code[patch_block..]
49// ```
50pub fn (mut func Function) patch(loc PatchPos, begin PatchPos) {
51 if loc == begin {
52 return
53 }
54 assert loc < begin
55
56 v := func.code[begin..].clone()
57 func.code.trim(begin)
58 func.code.insert(int(loc), v)
59
60 for mut patch in func.patches {
61 if patch.pos >= begin {
62 patch.pos -= begin - loc
63 } else if patch.pos >= loc {
64 patch.pos += func.code.len - begin
65 }
66 }
67
68 func.patches.sort(a.pos < b.pos)
69
70 /*
71 lenn := begin
72 diff := loc - begin
73
74 for mut patch in func.patches {
75 if patch.pos >= begin {
76 patch.pos += diff
77 }
78 if patch.pos <= lenn {
79 continue
80 }
81 delta := patch.pos - lenn
82 patch.pos = loc + delta
83 }
84
85 func.patches.sort(a.pos < b.pos)*/
86}
87
88// new_local creates a function local and returns it's index.
89// See `local_get`, `local_set`, `local_tee`.
90pub fn (mut func Function) new_local(v ValType) LocalIndex {
91 ret := func.locals.len
92 func.locals << FunctionLocal{
93 typ: v
94 }
95 return ret
96}
97
98// new_local_named creates a function local with a name and returns it's index.
99// The `name` is used in debug information, where applicable.
100// See `local_get`, `local_set`, `local_tee`.
101pub fn (mut func Function) new_local_named(v ValType, name string) LocalIndex {
102 ret := func.locals.len
103 func.locals << FunctionLocal{
104 typ: v
105 name: name
106 }
107 return ret
108}
109
110// i32_const places a constant i32 value on the stack.
111// WebAssembly instruction: `i32.const`.
112pub fn (mut func Function) i32_const(v i32) {
113 func.code << 0x41 // i32.const
114 func.code << leb128.encode_i32(v)
115}
116
117// i64_const places a constant i64 value on the stack.
118// WebAssembly instruction: `i64.const`.
119pub fn (mut func Function) i64_const(v i64) {
120 func.code << 0x42 // i64.const
121 func.code << leb128.encode_i64(v)
122}
123
124// f32_const places a constant f32 value on the stack.
125// WebAssembly instruction: `f32.const`.
126pub fn (mut func Function) f32_const(v f32) {
127 func.code << 0x43 // f32.const
128 push_f32(mut func.code, v)
129}
130
131// f64_const places a constant f64 value on the stack.
132// WebAssembly instruction: `f64.const`.
133pub fn (mut func Function) f64_const(v f64) {
134 func.code << 0x44 // f64.const
135 push_f64(mut func.code, v)
136}
137
138// local_get places the value of the local at the index `local` on the stack.
139// WebAssembly instruction: `local.get`.
140pub fn (mut func Function) local_get(local LocalIndex) {
141 func.code << 0x20 // local.get
142 func.u32(u32(local))
143}
144
145// local_get sets the local at the index `local` to the value on the stack.
146// WebAssembly instruction: `local.set`.
147pub fn (mut func Function) local_set(local LocalIndex) {
148 func.code << 0x21 // local.set
149 func.u32(u32(local))
150}
151
152// local_tee sets the local at the index `local` to the value on the stack, then places it's value on the stack.
153// WebAssembly instruction: `local.tee`.
154pub fn (mut func Function) local_tee(local LocalIndex) {
155 func.code << 0x22 // local.tee
156 func.u32(u32(local))
157}
158
159type GlobalIndices = GlobalImportIndex | GlobalIndex
160
161// global_get places the value of the global at the index `global` on the stack.
162// WebAssembly instruction: `global.get`.
163pub fn (mut func Function) global_get(global GlobalIndices) {
164 func.code << 0x23 // global.get
165 match global {
166 GlobalIndex {
167 func.patches << FunctionGlobalPatch{
168 idx: global
169 pos: func.code.len
170 }
171 }
172 GlobalImportIndex {
173 func.u32(u32(global))
174 }
175 }
176}
177
178// global_set sets the global at the index `global` to the value on the stack.
179// WebAssembly instruction: `global.set`.
180pub fn (mut func Function) global_set(global GlobalIndices) {
181 func.code << 0x24 // global.set
182 match global {
183 GlobalIndex {
184 func.patches << FunctionGlobalPatch{
185 idx: global
186 pos: func.code.len
187 }
188 }
189 GlobalImportIndex {
190 func.u32(u32(global))
191 }
192 }
193}
194
195// drop drops the value on the stack
196// WebAssembly instruction: `drop`.
197pub fn (mut func Function) drop() {
198 func.code << 0x1A
199}
200
201// c_select selects one of its first two operands based on an i32 condition.
202// WebAssembly instruction: `select`.
203pub fn (mut func Function) c_select() {
204 func.code << 0x1B
205}
206
207// add adds two values on the stack with type `typ`.
208// WebAssembly instructions: `i32|i64|f32|f64.add`.
209pub fn (mut func Function) add(typ NumType) {
210 match typ {
211 .i32_t { func.code << 0x6A } // i32.add
212 .i64_t { func.code << 0x7C } // i64.add
213 .f32_t { func.code << 0x92 } // f32.add
214 .f64_t { func.code << 0xA0 } // f64.add
215 }
216}
217
218// sub subtracts two values on the stack with type `typ`.
219// WebAssembly instructions: `i32|i64|f32|f64.sub`.
220pub fn (mut func Function) sub(typ NumType) {
221 match typ {
222 .i32_t { func.code << 0x6B } // i32.sub
223 .i64_t { func.code << 0x7D } // i64.sub
224 .f32_t { func.code << 0x93 } // f32.sub
225 .f64_t { func.code << 0xA1 } // f64.sub
226 }
227}
228
229// mul multiplies two values on the stack with type `typ`.
230// WebAssembly instructions: `i32|i64|f32|f64.mul`.
231pub fn (mut func Function) mul(typ NumType) {
232 match typ {
233 .i32_t { func.code << 0x6C } // i32.mul
234 .i64_t { func.code << 0x7E } // i64.mul
235 .f32_t { func.code << 0x94 } // f32.mul
236 .f64_t { func.code << 0xA2 } // f64.mul
237 }
238}
239
240// div divides two values on the stack with type `typ`, with respect to `is_signed`.
241// WebAssembly instructions: `i32|i64.div_s`, `i32|i64.div_u`, `f32|f64.div`.
242pub fn (mut func Function) div(typ NumType, is_signed bool) {
243 match typ {
244 .i32_t {
245 if is_signed {
246 func.code << 0x6D // i32.div_s
247 } else {
248 func.code << 0x6E // i32.div_u
249 }
250 }
251 .i64_t {
252 if is_signed {
253 func.code << 0x7F // i64.div_s
254 } else {
255 func.code << 0x80 // i64.div_u
256 }
257 }
258 .f32_t {
259 func.code << 0x95 // f32.div
260 }
261 .f64_t {
262 func.code << 0xA3 // f64.div
263 }
264 }
265}
266
267// rem takes the remainder of two values on the stack with type `typ`, with respect to `is_signed`.
268// WebAssembly instructions: `i32|i64.rem_s`, `i32|i64.rem_u`.
269pub fn (mut func Function) rem(typ NumType, is_signed bool) {
270 assert typ in [.i32_t, .i64_t]
271
272 match typ {
273 .i32_t {
274 if is_signed {
275 func.code << 0x6F // i32.rem_s
276 } else {
277 func.code << 0x70 // i32.rem_u
278 }
279 }
280 .i64_t {
281 if is_signed {
282 func.code << 0x81 // i64.rem_s
283 } else {
284 func.code << 0x82 // i64.rem_u
285 }
286 }
287 else {}
288 }
289}
290
291// and takes the bitwise and of two values on the stack with type `typ`.
292// WebAssembly instruction: `i32|i64.and`.
293pub fn (mut func Function) b_and(typ NumType) {
294 assert typ in [.i32_t, .i64_t]
295
296 match typ {
297 .i32_t { func.code << 0x71 } // i32.and
298 .i64_t { func.code << 0x83 } // i64.and
299 else {}
300 }
301}
302
303// or takes the bitwise or of two values on the stack with type `typ`.
304// WebAssembly instruction: `i32|i64.or`.
305pub fn (mut func Function) b_or(typ NumType) {
306 assert typ in [.i32_t, .i64_t]
307
308 match typ {
309 .i32_t { func.code << 0x72 } // i32.or
310 .i64_t { func.code << 0x84 } // i64.or
311 else {}
312 }
313}
314
315// xor takes the bitwise xor of two values on the stack with type `typ`.
316// WebAssembly instruction: `i32|i64.xor`.
317pub fn (mut func Function) b_xor(typ NumType) {
318 assert typ in [.i32_t, .i64_t]
319
320 match typ {
321 .i32_t { func.code << 0x73 } // i32.xor
322 .i64_t { func.code << 0x85 } // i64.xor
323 else {}
324 }
325}
326
327// shl performs bitwise left-shift on a value with type `typ`.
328// WebAssembly instruction: `i32|i64.shl`.
329pub fn (mut func Function) b_shl(typ NumType) {
330 assert typ in [.i32_t, .i64_t]
331
332 match typ {
333 .i32_t { func.code << 0x74 } // i32.shl
334 .i64_t { func.code << 0x86 } // i64.shl
335 else {}
336 }
337}
338
339// shr performs bitwise right-shift on a value with type `typ`, with respect to `is_signed`.
340// WebAssembly instructions: `i32|i64.shr_s`, `i32|i64.shr_u`.
341pub fn (mut func Function) b_shr(typ NumType, is_signed bool) {
342 assert typ in [.i32_t, .i64_t]
343
344 match typ {
345 .i32_t {
346 if is_signed {
347 func.code << 0x75 // i32.shr_s
348 } else {
349 func.code << 0x76 // i32.shr_u
350 }
351 }
352 .i64_t {
353 if is_signed {
354 func.code << 0x87 // i64.shr_s
355 } else {
356 func.code << 0x88 // i64.shr_u
357 }
358 }
359 else {}
360 }
361}
362
363// clz counts the amount of leading zeros in the numbers binary representation.
364// WebAssembly instruction: `i32|i64.clz`.
365pub fn (mut func Function) clz(typ NumType) {
366 assert typ in [.i32_t, .i64_t]
367
368 match typ {
369 .i32_t { func.code << 0x67 } // i32.clz
370 .i64_t { func.code << 0x79 } // i64.clz
371 else {}
372 }
373}
374
375// ctz counts the amount of trailing zeros in the numbers binary representation.
376// WebAssembly instruction: `i32|i64.ctz`.
377pub fn (mut func Function) ctz(typ NumType) {
378 assert typ in [.i32_t, .i64_t]
379
380 match typ {
381 .i32_t { func.code << 0x68 } // i32.ctz
382 .i64_t { func.code << 0x7A } // i64.ctz
383 else {}
384 }
385}
386
387// popcnt counts the amount of 1s in a numbers binary representation.
388// WebAssembly instruction: `i32|i64.popcnt`.
389pub fn (mut func Function) popcnt(typ NumType) {
390 assert typ in [.i32_t, .i64_t]
391
392 match typ {
393 .i32_t { func.code << 0x69 } // i32.popcnt
394 .i64_t { func.code << 0x7B } // i64.popcnt
395 else {}
396 }
397}
398
399// rotl performs bitwise left-rotate on a value with type `typ`.
400// WebAssembly instruction: `i32|i64.rotl`.
401pub fn (mut func Function) rotl(typ NumType) {
402 assert typ in [.i32_t, .i64_t]
403
404 match typ {
405 .i32_t { func.code << 0x77 } // i32.rotl
406 .i64_t { func.code << 0x89 } // i64.rotl
407 else {}
408 }
409}
410
411// rotr performs bitwise right-rotate on a value with type `typ`.
412// WebAssembly instruction: `i32|i64.rotr`.
413pub fn (mut func Function) rotr(typ NumType) {
414 assert typ in [.i32_t, .i64_t]
415
416 match typ {
417 .i32_t { func.code << 0x78 } // i32.rotr
418 .i64_t { func.code << 0xA8 } // i64.rotr
419 else {}
420 }
421}
422
423// abs gets the absolute value of a float with type `typ`.
424// WebAssembly instruction: `f32|f64.abs`.
425pub fn (mut func Function) abs(typ NumType) {
426 assert typ in [.f32_t, .f64_t]
427
428 match typ {
429 .f32_t { func.code << 0x8B } // f32.abs
430 .f64_t { func.code << 0x99 } // f64.abs
431 else {}
432 }
433}
434
435// neg negates the value of a float with type `typ`.
436// WebAssembly instruction: `f32|f64.neg`.
437pub fn (mut func Function) neg(typ NumType) {
438 assert typ in [.f32_t, .f64_t]
439
440 match typ {
441 .f32_t { func.code << 0x8C } // f32.neg
442 .f64_t { func.code << 0x9A } // f64.neg
443 else {}
444 }
445}
446
447// ceil rounds up the value of a float with type `typ` to the nearest integer.
448// WebAssembly instruction: `f32|f64.ceil`.
449pub fn (mut func Function) ceil(typ NumType) {
450 assert typ in [.f32_t, .f64_t]
451
452 match typ {
453 .f32_t { func.code << 0x8D } // f32.ceil
454 .f64_t { func.code << 0x9B } // f64.ceil
455 else {}
456 }
457}
458
459// floor rounds down the value of a float with type `typ` to the nearest integer.
460// WebAssembly instruction: `f32|f64.floor`.
461pub fn (mut func Function) floor(typ NumType) {
462 assert typ in [.f32_t, .f64_t]
463
464 match typ {
465 .f32_t { func.code << 0x8E } // f32.floor
466 .f64_t { func.code << 0x9C } // f64.floor
467 else {}
468 }
469}
470
471// trunc discards the fractional part of the value of a float with type `typ`.
472// WebAssembly instruction: `f32|f64.trunc`.
473pub fn (mut func Function) trunc(typ NumType) {
474 assert typ in [.f32_t, .f64_t]
475
476 match typ {
477 .f32_t { func.code << 0x8F } // f32.trunc
478 .f64_t { func.code << 0x9D } // f64.trunc
479 else {}
480 }
481}
482
483// nearest rounds the value of a float with type `typ` to the nearest integer.
484// WebAssembly instruction: `f32|f64.nearest`.
485pub fn (mut func Function) nearest(typ NumType) {
486 assert typ in [.f32_t, .f64_t]
487
488 match typ {
489 .f32_t { func.code << 0x90 } // f32.nearest
490 .f64_t { func.code << 0x9E } // f64.nearest
491 else {}
492 }
493}
494
495// sqrt performs the square root on the value of a float with type `typ`.
496// WebAssembly instruction: `f32|f64.sqrt`.
497pub fn (mut func Function) sqrt(typ NumType) {
498 assert typ in [.f32_t, .f64_t]
499
500 match typ {
501 .f32_t { func.code << 0x91 } // f32.sqrt
502 .f64_t { func.code << 0x9F } // f64.sqrt
503 else {}
504 }
505}
506
507// min gets the smaller value of two floats with type `typ`.
508// WebAssembly instruction: `f32|f64.min`.
509pub fn (mut func Function) min(typ NumType) {
510 assert typ in [.f32_t, .f64_t]
511
512 match typ {
513 .f32_t { func.code << 0x96 } // f32.min
514 .f64_t { func.code << 0xA4 } // f64.min
515 else {}
516 }
517}
518
519// max gets the higher value of two floats with type `typ`.
520// WebAssembly instruction: `f32|f64.max`.
521pub fn (mut func Function) max(typ NumType) {
522 assert typ in [.f32_t, .f64_t]
523
524 match typ {
525 .f32_t { func.code << 0x97 } // f32.max
526 .f64_t { func.code << 0xA5 } // f64.max
527 else {}
528 }
529}
530
531// copysign copies the sign bit of one float value to another float, both with type `typ`.
532// WebAssembly instruction: `f32|f64.copysign`.
533pub fn (mut func Function) copysign(typ NumType) {
534 assert typ in [.f32_t, .f64_t]
535
536 match typ {
537 .f32_t { func.code << 0x98 } // f32.copysign
538 .f64_t { func.code << 0xA6 } // f64.copysign
539 else {}
540 }
541}
542
543// eqz checks if the value with type `typ` is equal to zero, places an i32 boolean value on the stack.
544// WebAssembly instruction: `i32|i64.eqz`.
545pub fn (mut func Function) eqz(typ NumType) {
546 assert typ in [.i32_t, .i64_t]
547
548 match typ {
549 .i32_t { func.code << 0x45 } // i32.eqz
550 .i64_t { func.code << 0x50 } // i64.eqz
551 else {}
552 }
553}
554
555// eq checks if two values with type `typ` are equal, places an i32 boolean value on the stack.
556// WebAssembly instruction: `i32|i64|f32|f64.eq`.
557pub fn (mut func Function) eq(typ NumType) {
558 match typ {
559 .i32_t { func.code << 0x46 } // i32.eq
560 .i64_t { func.code << 0x51 } // i64.eq
561 .f32_t { func.code << 0x5B } // f32.eq
562 .f64_t { func.code << 0x61 } // f64.eq
563 }
564}
565
566// ne checks if two values with type `typ` are not equal, places an i32 boolean value on the stack.
567// WebAssembly instruction: `i32|i64|f32|f64.ne`.
568pub fn (mut func Function) ne(typ NumType) {
569 match typ {
570 .i32_t { func.code << 0x47 } // i32.ne
571 .i64_t { func.code << 0x52 } // i64.ne
572 .f32_t { func.code << 0x5C } // f32.ne
573 .f64_t { func.code << 0x62 } // f64.ne
574 }
575}
576
577// lt checks if two values with type `typ` with respect to `is_signed` are less than another, places an i32 boolean value on the stack.
578// WebAssembly instructions: `i32|i64.lt_s`, `i32|i64.lt_u`, `f32|f64.lt`.
579pub fn (mut func Function) lt(typ NumType, is_signed bool) {
580 match typ {
581 .i32_t {
582 if is_signed {
583 func.code << 0x48 // i32.lt_s
584 } else {
585 func.code << 0x49 // i32.lt_u
586 }
587 }
588 .i64_t {
589 if is_signed {
590 func.code << 0x53 // i64.lt_s
591 } else {
592 func.code << 0x54 // i64.lt_u
593 }
594 }
595 .f32_t {
596 func.code << 0x5D // f32.lt
597 }
598 .f64_t {
599 func.code << 0x63 // f64.lt
600 }
601 }
602}
603
604// gt checks if two values with type `typ` with respect to `is_signed` are greater than another, places an i32 boolean value on the stack.
605// WebAssembly instructions: `i32|i64.gt_s`, `i32|i64.gt_u`, `f32|f64.gt`.
606pub fn (mut func Function) gt(typ NumType, is_signed bool) {
607 match typ {
608 .i32_t {
609 if is_signed {
610 func.code << 0x4A // i32.gt_s
611 } else {
612 func.code << 0x4B // i32.gt_u
613 }
614 }
615 .i64_t {
616 if is_signed {
617 func.code << 0x55 // i64.gt_s
618 } else {
619 func.code << 0x56 // i64.gt_u
620 }
621 }
622 .f32_t {
623 func.code << 0x5E
624 } // f32.gt
625 .f64_t {
626 func.code << 0x64
627 } // f64.gt
628 }
629}
630
631// le checks if two values with type `typ` with respect to `is_signed` are less than or equal to another, places an i32 boolean value on the stack.
632// WebAssembly instructions: `i32|i64.le_s`, `i32|i64.le_u`, `f32|f64.le`.
633pub fn (mut func Function) le(typ NumType, is_signed bool) {
634 match typ {
635 .i32_t {
636 if is_signed {
637 func.code << 0x4C // i32.le_s
638 } else {
639 func.code << 0x4D // i32.le_u
640 }
641 }
642 .i64_t {
643 if is_signed {
644 func.code << 0x57 // i64.le_s
645 } else {
646 func.code << 0x58 // i64.le_u
647 }
648 }
649 .f32_t {
650 func.code << 0x5F
651 } // f32.le
652 .f64_t {
653 func.code << 0x65
654 } // f64.le
655 }
656}
657
658// ge checks if two values with type `typ` with respect to `is_signed` are greater than or equal to another, places an i32 boolean value on the stack.
659// WebAssembly instructions: `i32|i64.ge_s`, `i32|i64.ge_u`, `f32|f64.ge`.
660pub fn (mut func Function) ge(typ NumType, is_signed bool) {
661 match typ {
662 .i32_t {
663 if is_signed {
664 func.code << 0x4E // i32.ge_s
665 } else {
666 func.code << 0x4F // i32.ge_u
667 }
668 }
669 .i64_t {
670 if is_signed {
671 func.code << 0x59 // i64.ge_s
672 } else {
673 func.code << 0x5A // i64.ge_u
674 }
675 }
676 .f32_t {
677 func.code << 0x60
678 } // f32.ge
679 .f64_t {
680 func.code << 0x66
681 } // f64.ge
682 }
683}
684
685// sign_extend8 extends the value of a 8-bit integer of type `typ`.
686// WebAssembly instruction: `i32|i64.extend8_s`.
687pub fn (mut func Function) sign_extend8(typ ValType) {
688 assert typ in [.i32_t, .i64_t]
689
690 match typ {
691 .i32_t { func.code << 0xC0 } // i32.extend8_s
692 .i64_t { func.code << 0xC2 } // i64.extend8_s
693 else {}
694 }
695}
696
697// sign_extend16 extends the value of a 16-bit integer of type `typ`.
698// WebAssembly instruction: `i32|i64.extend16_s`.
699pub fn (mut func Function) sign_extend16(typ ValType) {
700 assert typ in [.i32_t, .i64_t]
701
702 match typ {
703 .i32_t { func.code << 0xC1 } // i32.extend16_s
704 .i64_t { func.code << 0xC3 } // i64.extend16_s
705 else {}
706 }
707}
708
709// sign_extend32_i64 extends the value of a 32-bit integer of type i64.
710// WebAssembly instruction: `i64.extend32_s`.
711pub fn (mut func Function) sign_extend32() {
712 func.code << 0xC4 // i64.extend32_s
713}
714
715// cast casts a value of type `a` with respect to `is_signed`, to type `b`.
716// A generic utility function over a large amount of WebAssembly instructions.
717// Note: This function uses non-trapping float conversion operators, see `cast_trapping` to use opcodes that cause a runtime exception.
718// WebAssembly instructions:
719// - `i32|i64.trunc_sat_f32_s`, `i32|i64.trunc_sat_f64_s`.
720// - `f32.demote_f64`, `f64.promote_f32`.
721// - `i32.wrap_i64`, `i64.extend_i32_s`, `i64.extend_i32_u`.
722// - `f32|f64.convert_i32_s`, `f32|f64.convert_i32_u`.
723// - `f32|f64.convert_i64_s`, `f32|f64.convert_i64_u`.
724pub fn (mut func Function) cast(a NumType, is_signed bool, b NumType) {
725 if a in [.f32_t, .f64_t] {
726 if a == .f32_t {
727 match b {
728 .i32_t {
729 func.code << 0xFC // sat opcode
730 func.code << 0x00 // i32.trunc_sat_f32_s
731 }
732 .i64_t {
733 func.code << 0xFC // sat opcode
734 func.code << 0x04 // i64.trunc_sat_f32_s
735 }
736 .f64_t {
737 func.code << 0xBB // f64.promote_f32
738 }
739 else {}
740 }
741 } else {
742 match b {
743 .i32_t {
744 func.code << 0xFC // sat opcode
745 func.code << 0x02 // i32.trunc_sat_f64_s
746 }
747 .i64_t {
748 func.code << 0xFC // sat opcode
749 func.code << 0x06 // i64.trunc_sat_f64_s
750 }
751 .f32_t {
752 func.code << 0xB6 // f32.demote_f64
753 }
754 else {}
755 }
756 }
757 return
758 }
759
760 if a == .i64_t && b == .i32_t {
761 func.code << 0xA7 // i32.wrap_i64
762 return
763 }
764
765 if is_signed {
766 match a {
767 .i32_t {
768 match b {
769 .i64_t {
770 func.code << 0xAC // i64.extend_i32_s
771 }
772 .f32_t {
773 func.code << 0xB2 // f32.convert_i32_s
774 }
775 .f64_t {
776 func.code << 0xB7 // f64.convert_i32_s
777 }
778 else {}
779 }
780 }
781 .i64_t {
782 match b {
783 .f32_t {
784 func.code << 0xB4 // f32.convert_i64_s
785 }
786 .f64_t {
787 func.code << 0xB9 // f64.convert_i64_s
788 }
789 else {}
790 }
791 }
792 else {}
793 }
794 } else {
795 match a {
796 .i32_t {
797 match b {
798 .i64_t {
799 func.code << 0xAD // i64.extend_i32_u
800 }
801 .f32_t {
802 func.code << 0xB3 // f32.convert_i32_u
803 }
804 .f64_t {
805 func.code << 0xB8 // f64.convert_i32_u
806 }
807 else {}
808 }
809 }
810 .i64_t {
811 match b {
812 .f32_t {
813 func.code << 0xB5 // f32.convert_i64_u
814 }
815 .f64_t {
816 func.code << 0xBA // f64.convert_i64_u
817 }
818 else {}
819 }
820 }
821 else {}
822 }
823 }
824}
825
826// cast_trapping casts a value of type `a` with respect to `is_signed`, to type `b`.
827// A generic utility function over a large amount of WebAssembly instructions.
828// Note: This function uses trapping float conversion operators, see `cast` to use opcodes that do NOT cause a runtime exception.
829// WebAssembly instructions:
830// - `i32|i64.trunc_f32_s`, `i32|i64.trunc_f64_s`.
831// - See function `cast` for the rest.
832pub fn (mut func Function) cast_trapping(a NumType, is_signed bool, b NumType) {
833 if a in [.f32_t, .f64_t] {
834 if is_signed {
835 if a == .f32_t {
836 match b {
837 .i32_t {
838 func.code << 0xA8 // i32.trunc_f32_s
839 return
840 }
841 .i64_t {
842 func.code << 0xAE // i64.trunc_f32_s
843 return
844 }
845 else {}
846 }
847 } else {
848 match b {
849 .i32_t {
850 func.code << 0xAA // i32.trunc_f64_s
851 return
852 }
853 .i64_t {
854 func.code << 0xB0 // i64.trunc_f64_s
855 return
856 }
857 else {}
858 }
859 }
860 } else {
861 if a == .f32_t {
862 match b {
863 .i32_t {
864 func.code << 0xA9 // i32.trunc_f32_u
865 return
866 }
867 .i64_t {
868 func.code << 0xAF // i64.trunc_f32_u
869 return
870 }
871 else {}
872 }
873 } else {
874 match b {
875 .i32_t {
876 func.code << 0xAB // i32.trunc_f64_u
877 return
878 }
879 .i64_t {
880 func.code << 0xB1 // i64.trunc_f64_u
881 return
882 }
883 else {}
884 }
885 }
886 }
887 }
888
889 func.cast(a, is_signed, b)
890}
891
892// reinterpret returns a value which has the same bit-pattern as its operand value, in its result type.
893// WebAssembly instruction: `f32.reinterpret_i32`, `i32.reinterpret_f32`, `f64.reinterpret_i64`, `i64.reinterpret_f64`.
894pub fn (mut func Function) reinterpret(a NumType) {
895 match a {
896 .f32_t { func.code << 0xBC } // i32.reinterpret_f32
897 .i32_t { func.code << 0xBE } // f32.reinterpret_i32
898 .f64_t { func.code << 0xBD } // i64.reinterpret_f64
899 .i64_t { func.code << 0xBF } // f64.reinterpret_i64
900 }
901}
902
903// unreachable denotes a point in code that should not be reachable, it is an unconditional trap.
904// WebAssembly instruction: `unreachable`.
905pub fn (mut func Function) unreachable() {
906 func.code << 0x00
907}
908
909// nop instruction, does nothing.
910// WebAssembly instruction: `nop`.
911pub fn (mut func Function) nop() {
912 func.code << 0x01
913}
914
915pub type LabelIndex = int
916
917// c_block creates a label that can later be branched out of with `c_br` and `c_br_if`.
918// Blocks are strongly typed, you must supply a list of types for `parameters` and `results`.
919// All blocks must be ended, see the `c_end` function.
920pub fn (mut func Function) c_block(parameters []ValType, results []ValType) LabelIndex {
921 func.label++
922 func.code << 0x02
923 func.blocktype(parameters: parameters, results: results)
924 return func.label
925}
926
927// c_loop creates a label that can later be branched to with `c_br` and `c_br_if`.
928// Loops are strongly typed, you must supply a list of types for `parameters` and `results`.
929// All loops must be ended, see the `c_end` function.
930pub fn (mut func Function) c_loop(parameters []ValType, results []ValType) LabelIndex {
931 func.label++
932 func.code << 0x03
933 func.blocktype(parameters: parameters, results: results)
934 return func.label
935}
936
937// c_if opens an if expression. It executes a statement if the last item on the stack is true.
938// It creates a label that can later be branched out of with `c_br` and `c_br_if`.
939// If expressions are strongly typed, you must supply a list of types for `parameters` and `results`.
940// Call `c_else` to open the else case of an if expression, or close it by calling `c_end_if`.
941// All if expressions must be ended, see the `c_end` function.
942pub fn (mut func Function) c_if(parameters []ValType, results []ValType) LabelIndex {
943 func.label++
944 func.code << 0x04
945 func.blocktype(parameters: parameters, results: results)
946 return func.label
947}
948
949// c_else opens the else case of an if expression, it must be closed by calling `c_end`.
950pub fn (mut func Function) c_else(label LabelIndex) {
951 assert func.label == label, 'c_else: called with an invalid label ${label}'
952 func.code << 0x05
953}
954
955// c_return returns from a function.
956// WebAssembly instruction: `return`.
957pub fn (mut func Function) c_return() {
958 func.code << 0x0F // return
959}
960
961// c_end ends the block, loop or if expression with the label passed in at `label`.
962pub fn (mut func Function) c_end(label LabelIndex) {
963 assert func.label == label, 'c_end: called with an invalid label ${label}'
964 func.label--
965 assert func.label >= 0, 'c_end: negative label index, unbalanced calls'
966 func.code << 0x0B // END expression opcode
967}
968
969// c_br branches to a loop or block with the label passed in at `label`.
970// WebAssembly instruction: `br`.
971pub fn (mut func Function) c_br(label LabelIndex) {
972 v := func.label - label
973 assert v >= 0, 'c_br: malformed label index'
974 func.code << 0x0C // br
975 func.u32(u32(v))
976}
977
978// c_br_if branches to a loop or block with the label passed in at `label`, based on an i32 condition.
979// WebAssembly instruction: `br_if`.
980pub fn (mut func Function) c_br_if(label LabelIndex) {
981 v := func.label - label
982 assert v >= 0, 'c_br_if: malformed label index'
983 func.code << 0x0D // br_if
984 func.u32(u32(v))
985}
986
987// call calls a locally defined function.
988// If this function does not exist when calling `compile` on the module, it will panic.
989// WebAssembly instruction: `call`.
990pub fn (mut func Function) call(name string) {
991 func.code << 0x10 // call
992 func.patches << CallPatch(FunctionCallPatch{
993 name: name
994 pos: func.code.len
995 })
996}
997
998// call calls an imported function.
999// If the imported function does not exist when calling `compile` on the module, it will panic.
1000// WebAssembly instruction: `call`.
1001pub fn (mut func Function) call_import(mod string, name string) {
1002 func.code << 0x10 // call
1003 func.patches << CallPatch(ImportCallPatch{
1004 mod: mod
1005 name: name
1006 pos: func.code.len
1007 })
1008}
1009
1010// load loads a value with type `typ` from memory.
1011// WebAssembly instruction: `i32|i64|f32|f64.load`.
1012pub fn (mut func Function) load(typ NumType, align int, offset int) {
1013 match typ {
1014 .i32_t { func.code << 0x28 } // i32.load
1015 .i64_t { func.code << 0x29 } // i64.load
1016 .f32_t { func.code << 0x2A } // f32.load
1017 .f64_t { func.code << 0x2B } // f64.load
1018 }
1019
1020 func.u32(u32(align))
1021 func.u32(u32(offset))
1022}
1023
1024// load8 loads a 8-bit value with type `typ` with respect to `is_signed` from memory.
1025// WebAssembly instructions: `i32|i64.load8_s`, `i32|i64.load8_u`.
1026pub fn (mut func Function) load8(typ NumType, is_signed bool, align int, offset int) {
1027 assert typ in [.i32_t, .i64_t]
1028
1029 match typ {
1030 .i32_t {
1031 if is_signed {
1032 func.code << 0x2C // i32.load8_s
1033 } else {
1034 func.code << 0x2D // i32.load8_u
1035 }
1036 }
1037 .i64_t {
1038 if is_signed {
1039 func.code << 0x30 // i64.load8_s
1040 } else {
1041 func.code << 0x31 // i64.load8_u
1042 }
1043 }
1044 else {}
1045 }
1046
1047 func.u32(u32(align))
1048 func.u32(u32(offset))
1049}
1050
1051// load16 loads a 16-bit value with type `typ` with respect to `is_signed` from memory.
1052// WebAssembly instructions: `i32|i64.load16_s`, `i32|i64.load16_u`.
1053pub fn (mut func Function) load16(typ NumType, is_signed bool, align int, offset int) {
1054 assert typ in [.i32_t, .i64_t]
1055
1056 match typ {
1057 .i32_t {
1058 if is_signed {
1059 func.code << 0x2E // i32.load16_s
1060 } else {
1061 func.code << 0x2F // i32.load16_u
1062 }
1063 }
1064 .i64_t {
1065 if is_signed {
1066 func.code << 0x32 // i64.load16_s
1067 } else {
1068 func.code << 0x33 // i64.load16_u
1069 }
1070 }
1071 else {}
1072 }
1073
1074 func.u32(u32(align))
1075 func.u32(u32(offset))
1076}
1077
1078// load32_i64 loads a 32-bit value of type i64 with respect to `is_signed` from memory.
1079// WebAssembly instructions: `i64.load32_s`, `i64.load32_u`.
1080pub fn (mut func Function) load32_i64(is_signed bool, align int, offset int) {
1081 if is_signed {
1082 func.code << 0x34 // i64.load32_s
1083 } else {
1084 func.code << 0x35 // i64.load32_u
1085 }
1086 func.u32(u32(align))
1087 func.u32(u32(offset))
1088}
1089
1090// store stores a value with type `typ` into memory.
1091// WebAssembly instruction: `i32|i64|f32|f64.store`.
1092pub fn (mut func Function) store(typ NumType, align int, offset int) {
1093 match typ {
1094 .i32_t { func.code << 0x36 } // i32.store
1095 .i64_t { func.code << 0x37 } // i64.store
1096 .f32_t { func.code << 0x38 } // f32.store
1097 .f64_t { func.code << 0x39 } // f64.store
1098 }
1099
1100 func.u32(u32(align))
1101 func.u32(u32(offset))
1102}
1103
1104// store8 stores a 8-bit value with type `typ` into memory.
1105// WebAssembly instruction: `i32|i64.store8`.
1106pub fn (mut func Function) store8(typ NumType, align int, offset int) {
1107 assert typ in [.i32_t, .i64_t]
1108
1109 match typ {
1110 .i32_t { func.code << 0x3A } // i32.store8
1111 .i64_t { func.code << 0x3C } // i64.store8
1112 else {}
1113 }
1114
1115 func.u32(u32(align))
1116 func.u32(u32(offset))
1117}
1118
1119// store16 stores a 16-bit value with type `typ` into memory.
1120// WebAssembly instruction: `i32|i64.store16`.
1121pub fn (mut func Function) store16(typ NumType, align int, offset int) {
1122 assert typ in [.i32_t, .i64_t]
1123
1124 match typ {
1125 .i32_t { func.code << 0x3B } // i32.store16
1126 .i64_t { func.code << 0x3D } // i64.store16
1127 else {}
1128 }
1129
1130 func.u32(u32(align))
1131 func.u32(u32(offset))
1132}
1133
1134// store16 stores a 32-bit value of type i64 into memory.
1135// WebAssembly instruction: `i64.store32`.
1136pub fn (mut func Function) store32_i64(align int, offset int) {
1137 func.code << 0x3E // i64.store32
1138 func.u32(u32(align))
1139 func.u32(u32(offset))
1140}
1141
1142// memory_size gets the size of the memory instance.
1143// WebAssembly instruction: `memory.size`.
1144pub fn (mut func Function) memory_size() {
1145 func.code << 0x3F
1146 func.code << 0x00
1147}
1148
1149// memory_grow increases the size of the memory instance.
1150// WebAssembly instruction: `memory.grow`.
1151pub fn (mut func Function) memory_grow() {
1152 func.code << 0x40
1153 func.code << 0x00
1154}
1155
1156// memory_init copies from a passive memory segment to the memory instance.
1157// WebAssembly instruction: `memory.init`.
1158pub fn (mut func Function) memory_init(idx DataSegmentIndex) {
1159 func.code << 0xFC
1160 func.code << 0x08
1161 func.u32(u32(idx))
1162 func.code << 0x00
1163}
1164
1165// data_drop prevents further use of a passive memory segment.
1166// WebAssembly instruction: `data.drop`.
1167pub fn (mut func Function) data_drop(idx DataSegmentIndex) {
1168 func.code << 0xFC
1169 func.code << 0x09
1170 func.u32(u32(idx))
1171}
1172
1173// memory_copy copies one region of memory to another.
1174// Similar to `memcpy` and `memmove`, memory regions can overlap.
1175// WebAssembly instruction: `memory.copy`.
1176pub fn (mut func Function) memory_copy() {
1177 func.code << [u8(0xFC), 0x0A, 0x00, 0x00]
1178}
1179
1180// memory_fill sets a memory region to a byte value.
1181// Similar to `memset`.
1182// WebAssembly instruction: `memory.copy`.
1183pub fn (mut func Function) memory_fill() {
1184 func.code << [u8(0xFC), 0x0B, 0x00]
1185}
1186
1187// ref_null places a null reference on the stack.
1188// WebAssembly instruction: `ref.null`.
1189pub fn (mut func Function) ref_null(rt RefType) {
1190 func.code << 0xD0 // ref.null
1191 func.code << u8(rt)
1192}
1193
1194// ref_is_null checks if the reference value on the stack is null, places an i32 boolean value on the stack.
1195// WebAssembly instruction: `ref_is_null`.
1196pub fn (mut func Function) ref_is_null(rt RefType) {
1197 func.code << 0xD1 // ref_is_null
1198}
1199
1200// ref_func places a reference to a function with `name` on the stack.
1201// If this function does not exist when calling `compile` on the module, it will panic.
1202// WebAssembly instruction: `ref.func`.
1203pub fn (mut func Function) ref_func(name string) {
1204 func.code << 0xD2 // ref.func
1205 func.patches << CallPatch(FunctionCallPatch{
1206 name: name
1207 pos: func.code.len
1208 })
1209}
1210
1211// ref_func_import places a reference to an imported function with `name` on the stack.
1212// If the imported function does not exist when calling `compile` on the module, it will panic.
1213// WebAssembly instruction: `ref.func`.
1214pub fn (mut func Function) ref_func_import(mod string, name string) {
1215 func.code << 0xD2 // ref.func
1216 func.patches << CallPatch(ImportCallPatch{
1217 mod: mod
1218 name: name
1219 pos: func.code.len
1220 })
1221}
1222