v2 / vlib / v / gen / wasm / gen.v
1892 lines · 1689 sloc · 44.91 KB · 0dcbb3a0b11aa65558733822a781e5d3a1233776
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 v.ast
7import v.pref
8import v.util
9import v.token
10import v.errors
11import v.gen.wasm.serialise
12import wasm
13import os
14
15@[heap; minify]
16pub struct Gen {
17 out_name string
18 pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
19 files []&ast.File
20mut:
21 file_path string // current ast.File path
22 warnings []errors.Warning
23 errors []errors.Error
24 table &ast.Table = unsafe { nil }
25 enum_vals map[string]Enum
26
27 mod wasm.Module
28 pool serialise.Pool
29 func wasm.Function
30 local_vars []Var
31 global_vars map[string]Global
32 ret_rvars []Var
33 ret ast.Type
34 ret_types []ast.Type
35 ret_br wasm.LabelIndex
36 bp_idx wasm.LocalIndex = -1 // Base pointer temporary's index for function, if needed (-1 for none)
37 sp_global ?wasm.GlobalIndex
38 heap_base ?wasm.GlobalIndex
39 fn_local_idx_end int
40 fn_name string
41 stack_frame int // Size of the current stack frame, if needed
42 is_leaf_function bool = true
43 loop_breakpoint_stack []LoopBreakpoint
44 stack_top int // position in linear memory
45 data_base int // position in linear memory
46 needs_address bool
47 defer_vars []Var
48 is_direct_array_access bool // inside a `[direct_array_access]` function
49}
50
51struct Global {
52mut:
53 init ?ast.Expr
54 v Var
55}
56
57pub struct LoopBreakpoint {
58 c_continue wasm.LabelIndex
59 c_break wasm.LabelIndex
60 name string
61}
62
63@[noreturn]
64pub fn (mut g Gen) v_error(s string, pos token.Pos) {
65 util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s)
66 exit(1)
67 /*
68 if g.pref.output_mode == .stdout {
69 util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s)
70 exit(1)
71 } else {
72 g.errors << errors.Error{
73 file_path: g.file_path
74 pos: pos
75 reporter: .gen
76 message: s
77 }
78 }
79 */
80}
81
82pub fn (mut g Gen) warning(s string, pos token.Pos) {
83 if g.pref.output_mode == .stdout {
84 util.show_compiler_message('warning:', pos: pos, file_path: g.file_path, message: s)
85 } else {
86 g.warnings << errors.Warning{
87 file_path: g.file_path
88 pos: pos
89 reporter: .gen
90 message: s
91 }
92 }
93}
94
95@[noreturn]
96pub fn (mut g Gen) w_error(s string) {
97 if g.pref.is_verbose {
98 print_backtrace()
99 }
100 util.verror('wasm error', s)
101}
102
103pub fn (g &Gen) unpack_type(typ ast.Type) []ast.Type {
104 ts := g.table.sym(typ)
105 return match ts.info {
106 ast.MultiReturn {
107 ts.info.types
108 }
109 else {
110 [typ]
111 }
112 }
113}
114
115pub fn (g &Gen) is_param_type(typ ast.Type) bool {
116 return !typ.is_ptr() && !g.is_pure_type(typ)
117}
118
119pub fn (mut g Gen) dbg_type_name(name string, typ ast.Type) string {
120 return '${name}<${`&`.repeat(typ.nr_muls())}${*g.table.sym(typ)}>'
121}
122
123pub fn unpack_literal_int(typ ast.Type) ast.Type {
124 return if typ == ast.int_literal_type { ast.i64_type } else { typ }
125}
126
127pub fn (g &Gen) get_ns_plus_name(default_name string, attrs []ast.Attr) (string, string) {
128 mut name := default_name
129 mut namespace := 'env'
130
131 if cattr := attrs.find_first('wasm_import_namespace') {
132 namespace = cattr.arg
133 }
134 if cattr := attrs.find_first('wasm_import_name') {
135 name = cattr.arg
136 }
137
138 return namespace, name
139}
140
141pub fn (mut g Gen) fn_external_import(node ast.FnDecl) {
142 if !node.no_body || node.is_method {
143 g.v_error('interop functions cannot have bodies', node.body_pos)
144 }
145 if node.language == .js && g.pref.os == .wasi {
146 g.v_error('javascript interop functions are not allowed in a `wasi` build', node.pos)
147 }
148 if node.return_type.has_option_or_result() {
149 g.v_error('interop functions must not return option or result', node.pos)
150 }
151
152 mut paraml := []wasm.ValType{cap: node.params.len}
153 mut retl := []wasm.ValType{cap: 1}
154 for arg in node.params {
155 if !g.is_pure_type(arg.typ) {
156 g.v_error('interop functions do not support complex arguments', arg.type_pos)
157 }
158 paraml << g.get_wasm_type(arg.typ)
159 }
160
161 is_ret := node.return_type != ast.void_type
162
163 if is_ret && !g.is_pure_type(node.return_type) {
164 g.v_error('interop functions do not support complex returns', node.return_type_pos)
165 }
166 if is_ret {
167 retl << g.get_wasm_type(node.return_type)
168 }
169
170 namespace, name := g.get_ns_plus_name(node.short_name, node.attrs)
171 g.mod.new_function_import(namespace, name, paraml, retl)
172}
173
174pub fn (mut g Gen) fn_decl(node ast.FnDecl) {
175 if node.language in [.js, .wasm] {
176 g.fn_external_import(node)
177 return
178 }
179
180 if node.attrs.contains('flag_enum_fn') {
181 // TODO: remove, when support for fn results is done
182 return
183 }
184
185 name := if node.is_method {
186 '${g.table.get_type_name(node.receiver.typ)}.${node.name}'
187 } else {
188 node.name
189 }
190
191 util.timing_start('${@METHOD}: ${name}')
192 defer {
193 util.timing_measure('${@METHOD}: ${name}')
194 }
195
196 if node.no_body {
197 return
198 }
199 if g.pref.is_verbose {
200 // println(term.green('\n${name}:'))
201 }
202 if node.is_deprecated {
203 g.warning('fn_decl: ${name} is deprecated', node.pos)
204 }
205
206 mut paramdbg := []?string{cap: node.params.len}
207 mut paraml := []wasm.ValType{cap: node.params.len}
208 mut retl := []wasm.ValType{cap: 1}
209
210 // fn ()! | fn () &IError
211 // fn () ?(...) | fn () (..., bool)
212 // fn () !(...) | fn () (..., &IError)
213 //
214 // fn (...) struct | fn (_ &struct, ...)
215 // fn (...) !struct | fn (_ &struct, ...) &IError
216 // fn (...) (...struct) | fn (...&struct, ...)
217
218 g.ret_rvars = []Var{}
219 rt := node.return_type
220 rts := g.table.sym(rt)
221 g.ret = rt
222 match rts.info {
223 ast.MultiReturn {
224 for t in rts.info.types {
225 wtyp := g.get_wasm_type(t)
226 if g.is_param_type(t) {
227 paramdbg << g.dbg_type_name('__rval(${g.ret_rvars.len})', t)
228 paraml << wtyp
229 g.ret_rvars << Var{
230 typ: t
231 idx: g.ret_rvars.len
232 is_address: true
233 }
234 } else {
235 retl << wtyp
236 }
237 g.ret_types << t
238 }
239 if rt.has_flag(.option) {
240 g.v_error('option types are not implemented', node.return_type_pos)
241 retl << .i32_t // bool
242 }
243 }
244 else {
245 if rt.idx() != ast.void_type_idx {
246 wtyp := g.get_wasm_type(rt)
247 if g.is_param_type(rt) {
248 paramdbg << g.dbg_type_name('__rval(0)', rt)
249 paraml << wtyp
250 g.ret_rvars << Var{
251 typ: rt
252 is_address: true
253 }
254 } else {
255 retl << wtyp
256 }
257 g.ret_types << rt
258 } else if rt.has_flag(.option) {
259 g.v_error('returning a void option is forbidden', node.return_type_pos)
260 }
261 }
262 }
263
264 if rt.has_flag(.result) {
265 g.v_error('result types are not implemented', node.return_type_pos)
266 retl << .i32_t // &IError
267 }
268
269 for p in node.params {
270 typ := g.get_wasm_type_int_literal(p.typ)
271 ntyp := unpack_literal_int(p.typ)
272 g.local_vars << Var{
273 name: p.name
274 typ: ntyp
275 idx: g.local_vars.len + g.ret_rvars.len
276 is_address: !g.is_pure_type(p.typ)
277 }
278 paramdbg << g.dbg_type_name(p.name, p.typ)
279 paraml << typ
280 }
281
282 // bottom scope
283
284 g.is_direct_array_access = node.is_direct_arr || g.pref.no_bounds_checking
285 g.fn_local_idx_end = (g.local_vars.len + g.ret_rvars.len)
286 g.fn_name = name
287
288 mut should_export := g.pref.os in [.browser, .wasi] && node.is_pub && node.mod == 'main'
289
290 g.func = g.mod.new_debug_function(name, wasm.FuncType{paraml, retl, none}, paramdbg)
291 func_start := g.func.patch_pos()
292 if node.stmts.len > 0 {
293 g.ret_br = g.func.c_block([], retl)
294 {
295 g.expr_stmts(node.stmts, ast.void_type)
296 }
297 {
298 for idx, defer_stmt in node.defer_stmts {
299 g.get(g.defer_vars[idx])
300 lbl := g.func.c_if([], [])
301 {
302 g.expr_stmts(defer_stmt.stmts, ast.void_type)
303 }
304 g.func.c_end(lbl)
305 }
306 }
307 g.func.c_end(g.ret_br)
308 g.bare_function_frame(func_start)
309 }
310 if cattr := node.attrs.find_first('export') {
311 g.func.export_name(cattr.arg)
312 should_export = true
313 }
314 g.mod.commit(g.func, should_export)
315 g.bare_function_end()
316
317 // printfn is not implemented!
318}
319
320pub fn (mut g Gen) bare_function_frame(func_start wasm.PatchPos) {
321 // Setup stack frame.
322 // If the function does not call other functions,
323 // a leaf function, the omission of setting the
324 // stack pointer is perfectly acceptable.
325 //
326 if g.stack_frame != 0 {
327 prologue := g.func.patch_pos()
328 {
329 g.func.global_get(g.sp())
330 g.func.i32_const(i32(g.stack_frame))
331 g.func.sub(.i32_t)
332 if !g.is_leaf_function {
333 g.func.local_tee(g.bp())
334 g.func.global_set(g.sp())
335 } else {
336 g.func.local_set(g.bp())
337 }
338 }
339 g.func.patch(func_start, prologue)
340 if !g.is_leaf_function {
341 g.func.global_get(g.sp())
342 g.func.i32_const(i32(g.stack_frame))
343 g.func.add(.i32_t)
344 g.func.global_set(g.sp())
345 }
346 }
347}
348
349pub fn (mut g Gen) bare_function_end() {
350 g.local_vars.clear()
351 g.ret_rvars.clear()
352 g.ret_types.clear()
353 g.defer_vars.clear()
354 g.bp_idx = -1
355 g.stack_frame = 0
356 g.is_leaf_function = true
357 g.is_direct_array_access = false
358 assert g.loop_breakpoint_stack.len == 0
359}
360
361pub fn (mut g Gen) literalint(val i64, expected ast.Type) {
362 match g.get_wasm_type(expected) {
363 .i32_t { g.func.i32_const(i32(val)) }
364 .i64_t { g.func.i64_const(val) }
365 .f32_t { g.func.f32_const(f32(val)) }
366 .f64_t { g.func.f64_const(f64(val)) }
367 else { g.w_error('literalint: bad type `${expected}`') }
368 }
369}
370
371pub fn (mut g Gen) literal(val string, expected ast.Type) {
372 match g.get_wasm_type(expected) {
373 .i32_t { g.func.i32_const(i32(val.int())) }
374 .i64_t { g.func.i64_const(val.i64()) }
375 .f32_t { g.func.f32_const(val.f32()) }
376 .f64_t { g.func.f64_const(val.f64()) }
377 else { g.w_error('literal: bad type `${expected}`') }
378 }
379}
380
381pub fn (mut g Gen) cast(typ ast.Type, expected_type ast.Type) {
382 wtyp := g.as_numtype(g.get_wasm_type_int_literal(typ))
383 expected_wtype := g.as_numtype(g.get_wasm_type_int_literal(expected_type))
384
385 g.func.cast(wtyp, typ.is_signed(), expected_wtype)
386}
387
388pub fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
389 if expr is ast.IntegerLiteral {
390 g.literal(expr.val, expected_type)
391 return
392 } else if expr is ast.FloatLiteral {
393 g.literal(expr.val, expected_type)
394 return
395 }
396
397 got_type := ast.mktyp(got_type_raw)
398 got_wtype := g.as_numtype(g.get_wasm_type(got_type))
399 expected_wtype := g.as_numtype(g.get_wasm_type(expected_type))
400
401 g.expr(expr, got_type)
402 g.func.cast(got_wtype, got_type.is_signed(), expected_wtype)
403}
404
405pub fn (mut g Gen) handle_ptr_arithmetic(typ ast.Type) {
406 if typ.is_ptr() {
407 size, _ := g.pool.type_size(typ)
408 g.func.i32_const(i32(size))
409 g.func.mul(.i32_t)
410 }
411}
412
413fn (mut g Gen) handle_string_operation(op token.Kind) {
414 left_tmp := g.func.new_local_named(.i32_t, '__tmp<string>.left')
415 right_tmp := g.func.new_local_named(.i32_t, '__tmp<string>.right')
416 g.func.local_set(right_tmp)
417 g.func.local_set(left_tmp)
418
419 match op {
420 .plus {
421 ret_var := g.new_local('', ast.string_type)
422 g.ref(ret_var)
423 g.func.local_get(left_tmp)
424 g.func.local_get(right_tmp)
425 g.func.call('string.+')
426 g.get(ret_var)
427 }
428 .eq {
429 g.func.local_get(left_tmp)
430 g.func.local_get(right_tmp)
431 g.func.call('string.==')
432 }
433 .ne {
434 g.func.local_get(left_tmp)
435 g.func.local_get(right_tmp)
436 g.func.call('string.==')
437 g.func.eqz(.i32_t)
438 }
439 .lt {
440 g.func.local_get(left_tmp)
441 g.func.local_get(right_tmp)
442 g.func.call('string.<')
443 }
444 .gt {
445 g.func.local_get(right_tmp)
446 g.func.local_get(left_tmp)
447 g.func.call('string.<')
448 }
449 .le {
450 g.func.local_get(right_tmp)
451 g.func.local_get(left_tmp)
452 g.func.call('string.<')
453 g.func.eqz(.i32_t)
454 }
455 .ge {
456 g.func.local_get(left_tmp)
457 g.func.local_get(right_tmp)
458 g.func.call('string.<')
459 g.func.eqz(.i32_t)
460 }
461 else {
462 g.w_error('unsupported string operation: `${op}`')
463 }
464 }
465}
466
467pub fn (mut g Gen) string_inter_literal_expr(node ast.StringInterLiteral, expected ast.Type) {
468 if node.exprs.len == 0 {
469 g.expr(ast.StringLiteral{ val: node.vals[0], pos: node.pos }, expected)
470 return
471 }
472
473 result_var := g.new_local('__str_inter', ast.string_type)
474
475 g.set_with_expr(ast.StringLiteral{ val: node.vals[0], pos: node.pos }, result_var)
476
477 for i, expr in node.exprs {
478 mut expr_to_concat := expr
479 typ := node.expr_types[i]
480
481 if typ != ast.string_type {
482 has_str, _, _ := g.table.sym(typ).str_method_info()
483 if !has_str {
484 g.v_error('cannot interpolate type without .str() method', node.fmt_poss[i])
485 }
486
487 expr_to_concat = ast.CallExpr{
488 name: 'str'
489 left: expr
490 left_type: typ
491 receiver_type: typ
492 return_type: ast.string_type
493 is_method: true
494 is_return_used: true
495 }
496 }
497
498 // result = result + expr_as_string
499 {
500 g.get(result_var)
501 g.expr(expr_to_concat, ast.string_type)
502 g.handle_string_operation(.plus)
503 g.set(result_var)
504 }
505
506 // Concat the next string segment (if not empty)
507 if i + 1 < node.vals.len && node.vals[i + 1].len > 0 {
508 g.get(result_var)
509 g.expr(ast.StringLiteral{ val: node.vals[i + 1], pos: node.pos }, ast.string_type)
510 g.handle_string_operation(.plus)
511 g.set(result_var)
512 }
513 }
514
515 g.get(result_var)
516}
517
518pub fn (mut g Gen) infix_expr(node ast.InfixExpr, expected ast.Type) {
519 if node.op in [.logical_or, .and] {
520 temp := g.func.new_local_named(.i32_t, '__tmp<bool>')
521 {
522 g.expr(node.left, ast.bool_type)
523 g.func.local_set(temp)
524 }
525 g.func.local_get(temp)
526 if node.op == .logical_or {
527 g.func.eqz(.i32_t)
528 }
529
530 blk := g.func.c_if([], [.i32_t])
531 {
532 g.expr(node.right, ast.bool_type)
533 }
534 g.func.c_else(blk)
535 {
536 g.func.local_get(temp)
537 }
538 g.func.c_end(blk)
539 return
540 }
541
542 {
543 g.expr(node.left, node.left_type)
544 }
545 {
546 g.expr_with_cast(node.right, node.right_type, node.left_type)
547 if node.op in [.plus, .minus] && node.left_type.is_ptr() {
548 g.handle_ptr_arithmetic(node.left_type.deref())
549 }
550 }
551 g.infix_from_typ(node.left_type, node.op)
552
553 res_typ := if node.op in [.eq, .ne, .gt, .lt, .ge, .le] { ast.bool_type } else { node.left_type }
554 g.func.cast(g.as_numtype(g.get_wasm_type(res_typ)), res_typ.is_signed(),
555 g.as_numtype(g.get_wasm_type(expected)))
556}
557
558pub fn (mut g Gen) prefix_expr(node ast.PrefixExpr, expected ast.Type) {
559 match node.op {
560 .minus {
561 if node.right_type.is_pure_float() {
562 g.expr(node.right, node.right_type)
563 if node.right_type == ast.f32_type_idx {
564 g.func.neg(.f32_t)
565 } else {
566 g.func.neg(.f64_t)
567 }
568 } else {
569 // -val == 0 - val
570
571 vt := g.get_wasm_type(node.right_type)
572
573 g.literalint(0, node.right_type)
574 g.expr(node.right, node.right_type)
575 g.func.sub(g.as_numtype(vt))
576 }
577 }
578 .not {
579 g.expr(node.right, node.right_type)
580 g.func.eqz(.i32_t) // !expr
581 }
582 .bit_not {
583 // ~val == val ^ -1
584
585 vt := g.get_wasm_type(node.right_type)
586
587 g.expr(node.right, node.right_type)
588 g.literalint(-1, node.right_type)
589 g.func.b_xor(g.as_numtype(vt))
590 }
591 .amp {
592 if v := g.get_var_from_expr(node.right) {
593 if !v.is_address {
594 g.v_error("cannot take the address of a value that doesn't live on the stack",
595 node.pos)
596 }
597 g.ref(v)
598 } else {
599 g.needs_address = true
600 {
601 g.expr(node.right, node.right_type)
602 }
603 g.needs_address = false
604 }
605 }
606 .mul {
607 g.expr(node.right, node.right_type)
608 if g.is_pure_type(expected) && !g.needs_address {
609 // in a RHS context, not lvalue
610 g.load(expected, 0)
611 }
612 }
613 else {
614 // impl deref (.mul), and impl address of (.amp)
615 g.w_error('`${node.op}val` prefix expression not implemented')
616 }
617 }
618}
619
620pub fn (mut g Gen) if_branch(ifexpr ast.IfExpr, expected ast.Type, unpacked_params []wasm.ValType, idx int,
621 existing_rvars []Var) {
622 curr := ifexpr.branches[idx]
623
624 g.expr(curr.cond, ast.bool_type)
625 blk := g.func.c_if([], unpacked_params)
626 {
627 g.rvar_expr_stmts(curr.stmts, expected, existing_rvars)
628 }
629 {
630 if ifexpr.has_else && idx + 2 >= ifexpr.branches.len {
631 g.func.c_else(blk)
632 g.rvar_expr_stmts(ifexpr.branches[idx + 1].stmts, expected, existing_rvars)
633 } else if !(idx + 1 >= ifexpr.branches.len) {
634 g.func.c_else(blk)
635 g.if_branch(ifexpr, expected, unpacked_params, idx + 1, existing_rvars)
636 }
637 }
638 g.func.c_end(blk)
639}
640
641pub fn (mut g Gen) if_expr(ifexpr ast.IfExpr, expected ast.Type, existing_rvars []Var) {
642 if ifexpr.is_comptime {
643 g.comptime_if_expr(ifexpr, expected, existing_rvars)
644 return
645 }
646
647 params := if expected == ast.void_type {
648 []wasm.ValType{}
649 } else if existing_rvars.len == 0 {
650 g.unpack_type(expected).map(g.get_wasm_type(it))
651 } else {
652 g.unpack_type(expected).filter(!g.is_param_type(it)).map(g.get_wasm_type(it))
653 }
654 g.if_branch(ifexpr, expected, params, 0, existing_rvars)
655}
656
657pub fn (mut g Gen) match_expr(node ast.MatchExpr, expected ast.Type, existing_rvars []Var) {
658 results := if expected == ast.void_type {
659 []wasm.ValType{}
660 } else if existing_rvars.len == 0 {
661 g.unpack_type(expected).map(g.get_wasm_type(it))
662 } else {
663 g.unpack_type(expected).filter(!g.is_param_type(it)).map(g.get_wasm_type(it))
664 }
665 g.match_branch(node, expected, results, 0, existing_rvars)
666}
667
668fn (mut g Gen) match_branch(node ast.MatchExpr, expected ast.Type, unpacked_params []wasm.ValType, branch_idx int, existing_rvars []Var) {
669 if branch_idx >= node.branches.len {
670 return
671 }
672
673 branch := node.branches[branch_idx]
674 mut is_last_branch := branch_idx + 1 >= node.branches.len
675 mut has_else := branch.is_else
676
677 if has_else {
678 if branch.stmts.len > 0 {
679 g.rvar_expr_stmts(branch.stmts, expected, existing_rvars)
680 }
681 return
682 }
683
684 if branch.exprs.len > 0 {
685 g.match_branch_exprs(node, expected, unpacked_params, branch_idx, 0, existing_rvars, branch)
686 } else {
687 if branch.stmts.len > 0 {
688 g.rvar_expr_stmts(branch.stmts, expected, existing_rvars)
689 }
690 if !is_last_branch {
691 g.match_branch(node, expected, unpacked_params, branch_idx + 1, existing_rvars)
692 }
693 }
694}
695
696fn (mut g Gen) match_branch_exprs(node ast.MatchExpr, expected ast.Type, unpacked_params []wasm.ValType, branch_idx int, expr_idx int, existing_rvars []Var, branch ast.MatchBranch) {
697 if expr_idx >= branch.exprs.len {
698 return
699 }
700
701 mut is_last_branch := branch_idx + 1 >= node.branches.len
702 mut is_last_expr := expr_idx + 1 >= branch.exprs.len
703
704 expr := branch.exprs[expr_idx]
705
706 if expr is ast.RangeExpr {
707 wasm_type := g.as_numtype(g.get_wasm_type(node.cond_type))
708 is_signed := node.cond_type.is_signed()
709
710 g.expr(node.cond, node.cond_type)
711 g.expr(expr.high, node.cond_type)
712 g.func.le(wasm_type, is_signed)
713 } else {
714 if g.is_param_type(node.cond_type) {
715 // Param types -> strings etc
716 g.expr(node.cond, node.cond_type)
717 g.expr(expr, node.cond_type)
718 g.infix_from_typ(node.cond_type, .eq)
719 } else {
720 // Numeric types -> direct comparison
721 wasm_type := g.as_numtype(g.get_wasm_type(node.cond_type))
722 g.expr(node.cond, node.cond_type)
723 g.expr(expr, node.cond_type)
724 g.func.eq(wasm_type)
725 }
726 }
727
728 blk := g.func.c_if([], unpacked_params)
729 {
730 if branch.stmts.len > 0 {
731 g.rvar_expr_stmts(branch.stmts, expected, existing_rvars)
732 }
733 }
734 {
735 g.func.c_else(blk)
736 if is_last_expr {
737 if !is_last_branch {
738 g.match_branch(node, expected, unpacked_params, branch_idx + 1, existing_rvars)
739 }
740 } else {
741 g.match_branch_exprs(node, expected, unpacked_params, branch_idx, expr_idx + 1,
742 existing_rvars, branch)
743 }
744 }
745 g.func.c_end(blk)
746}
747
748pub fn (mut g Gen) call_expr(node ast.CallExpr, expected ast.Type, existing_rvars []Var) {
749 mut wasm_ns := ?string(none)
750 mut name := node.name
751
752 is_print := name in ['panic', 'println', 'print', 'eprintln', 'eprint']
753
754 if node.is_method {
755 name = '${g.table.get_type_name(node.receiver_type)}.${node.name}'
756 }
757
758 if node.language in [.js, .wasm] {
759 cfn_attrs := unsafe { g.table.fns[node.name].attrs }
760
761 short_name := if node.language == .js {
762 node.name.all_after_last('JS.')
763 } else {
764 node.name.all_after_last('WASM.')
765 }
766
767 // setting a `?string` in a multireturn causes UNDEFINED BEHAVIOR AND STACK CORRUPTION
768 // best to use a workaround till that is fixed
769
770 mut wasm_ns_storage := ''
771 wasm_ns_storage, name = g.get_ns_plus_name(short_name, cfn_attrs)
772 wasm_ns = wasm_ns_storage
773 }
774
775 // callconv: {return structs} {method self} {arguments}
776
777 // {return structs}
778 //
779 mut rvars := existing_rvars.clone()
780 rts := g.unpack_type(node.return_type)
781 if rvars.len == 0 && node.return_type != ast.void_type {
782 for rt in rts {
783 if g.is_param_type(rt) {
784 v := g.new_local('', rt)
785 rvars << v
786 }
787 }
788 }
789 for v in rvars {
790 g.ref(v)
791 }
792
793 // {method self}
794 //
795 if node.is_method {
796 expr := if !node.left_type.is_ptr() && node.receiver_type.is_ptr() { ast.Expr(ast.PrefixExpr{
797 op: .amp
798 right: node.left
799 }) } else { node.left }
800 // hack alert!
801 if node.receiver_type == ast.int_literal_type && expr is ast.IntegerLiteral {
802 g.literal(expr.val, ast.i64_type)
803 } else {
804 g.expr(expr, node.receiver_type)
805 }
806 }
807
808 // {arguments}
809 //
810 for idx, arg in node.args {
811 mut expr := arg.expr
812
813 mut typ := arg.typ
814 if is_print && typ != ast.string_type {
815 has_str, _, _ := g.table.sym(typ).str_method_info()
816 if typ != ast.string_type && !has_str {
817 g.v_error('cannot implicitly convert as argument does not have a .str() function',
818 arg.pos)
819 }
820
821 expr = ast.CallExpr{
822 name: 'str'
823 left: expr
824 left_type: typ
825 receiver_type: typ
826 return_type: ast.string_type
827 is_method: true
828 is_return_used: true
829 }
830 }
831
832 // another hack alert!
833 if node.expected_arg_types[idx] == ast.int_literal_type && mut expr is ast.IntegerLiteral {
834 g.literal(expr.val, ast.i64_type)
835 } else {
836 g.expr(expr, node.expected_arg_types[idx])
837 }
838 }
839
840 if namespace := wasm_ns {
841 // import calls won't touch `__vsp` !
842
843 g.func.call_import(namespace, name)
844 } else {
845 // other calls may...
846 g.is_leaf_function = false
847
848 g.func.call(name)
849 }
850
851 if expected == ast.void_type && node.return_type != ast.void_type {
852 for rt in rts { // order doesn't matter
853 if !g.is_param_type(rt) {
854 g.func.drop()
855 }
856 }
857 } else if rvars.len > 0 && existing_rvars.len == 0 {
858 mut rr_vars := []Var{cap: rts.len}
859 mut r := rvars.len
860
861 for rt in rts.reverse() {
862 if !g.is_param_type(rt) {
863 v := g.new_local('', rt)
864 rr_vars << v
865 g.set(v)
866 } else {
867 r--
868 rr_vars << rvars[r]
869 }
870 }
871
872 for v in rr_vars.reverse() {
873 g.get(v)
874 }
875 }
876 if node.is_noreturn {
877 g.func.unreachable()
878 }
879}
880
881pub fn (mut g Gen) get_field_offset(typ ast.Type, name string) int {
882 ts := g.table.sym(typ)
883 field := ts.find_field(name) or { g.w_error('could not find field `${name}` on init') }
884 si := g.pool.type_struct_info(typ) or { panic('unreachable') }
885 return si.offsets[field.i]
886}
887
888pub fn (mut g Gen) field_offset(typ ast.Type, name string) {
889 offset := g.get_field_offset(typ, name)
890 if offset != 0 {
891 g.func.i32_const(i32(offset))
892 g.func.add(.i32_t)
893 }
894}
895
896pub fn (mut g Gen) load_field(typ ast.Type, ftyp ast.Type, name string) {
897 offset := g.get_field_offset(typ, name)
898 g.load(ftyp, offset)
899}
900
901pub fn (mut g Gen) store_field(typ ast.Type, ftyp ast.Type, name string) {
902 offset := g.get_field_offset(typ, name)
903 g.store(ftyp, offset)
904}
905
906pub fn (mut g Gen) expr(node ast.Expr, expected ast.Type) {
907 match node {
908 ast.ParExpr, ast.UnsafeExpr {
909 g.expr(node.expr, expected)
910 }
911 ast.ArrayInit {
912 v := g.new_local('', node.typ)
913 g.set_with_expr(node, v)
914 g.get(v)
915 }
916 ast.GoExpr {
917 g.w_error('wasm backend does not support threads')
918 }
919 ast.IndexExpr {
920 mut direct_array_access := g.is_direct_array_access || node.is_direct
921 mut tmp_voidptr_var := wasm.LocalIndex(-1)
922
923 // ptr + index * size
924 mut typ := node.left_type
925 ts := g.table.sym(typ)
926
927 g.expr(node.left, node.left_type)
928 if node.left_type == ast.string_type {
929 if !direct_array_access {
930 tmp_voidptr_var = g.func.new_local_named(.i32_t, '__tmp<voidptr>')
931 g.func.local_tee(tmp_voidptr_var)
932 }
933
934 // be pedantic...
935 g.load_field(ast.string_type, ast.voidptr_type, 'str')
936 typ = ast.u8_type
937 } else if typ.is_ptr() {
938 typ = typ.deref()
939 direct_array_access = true
940 } else {
941 match ts.info {
942 ast.Array {
943 g.w_error('wasm backend does not support dynamic arrays')
944 }
945 ast.ArrayFixed {
946 typ = ts.info.elem_type
947 if node.index.is_pure_literal() {
948 // checker would have gotten this by now
949 direct_array_access = true
950 }
951 }
952 else {
953 g.w_error('ast.IndexExpr: unreachable')
954 }
955 }
956 }
957
958 size, _ := g.pool.type_size(typ)
959
960 old_needs_address := g.needs_address
961 g.needs_address = false
962 g.expr(node.index, ast.int_type)
963 g.needs_address = old_needs_address
964
965 if !direct_array_access {
966 g.is_leaf_function = false // calls panic()
967
968 idx_temp := g.func.new_local_named(.i32_t, '__tmp<int>')
969 g.func.local_tee(idx_temp)
970
971 // .len
972 if node.left_type == ast.string_type {
973 g.func.local_get(tmp_voidptr_var)
974 g.load_field(ast.string_type, ast.int_type, 'len')
975 } else if ts.info is ast.ArrayFixed {
976 g.func.i32_const(i32(ts.info.size))
977 } else {
978 panic('unreachable')
979 }
980
981 g.func.ge(.i32_t, false)
982 // is_signed: false, negative numbers will be reinterpreted as > 2^31 and will also trigger false
983 blk := g.func.c_if([], [])
984 {
985 g.expr(ast.StringLiteral{ val: '${g.file_pos(node.pos)}: ${ast.Expr(node)}' },
986 ast.string_type)
987 g.func.call('eprintln')
988 g.expr(ast.StringLiteral{ val: 'index out of range' }, ast.string_type)
989 g.func.call('panic')
990 }
991 g.func.c_end(blk)
992
993 g.func.local_get(idx_temp)
994 }
995
996 if size > 1 {
997 g.literalint(size, ast.int_type)
998 g.func.mul(.i32_t)
999 }
1000
1001 g.func.add(.i32_t)
1002
1003 if !g.is_pure_type(typ) {
1004 return
1005 }
1006
1007 if !g.needs_address {
1008 // ptr
1009 g.load(typ, 0)
1010 }
1011 g.cast(typ, expected)
1012 }
1013 ast.StructInit {
1014 v := g.new_local('', node.typ)
1015 g.set_with_expr(node, v)
1016 g.get(v)
1017 }
1018 ast.SelectorExpr {
1019 if v := g.get_var_from_expr(node) {
1020 if g.needs_address {
1021 if !v.is_address {
1022 g.v_error("cannot take the address of a value that doesn't live on the stack. this is a current limitation.",
1023 node.pos)
1024 }
1025 g.ref(v)
1026 } else {
1027 g.get(v)
1028 }
1029 } else {
1030 g.needs_address = true
1031 {
1032 g.expr(node.expr, node.typ)
1033 }
1034 g.needs_address = false
1035 g.field_offset(node.expr_type, node.field_name)
1036 if g.is_pure_type(node.typ) && !g.needs_address {
1037 // expected to be a pointer
1038 g.load(node.typ, 0)
1039 }
1040 }
1041 g.cast(node.typ, expected)
1042 }
1043 ast.MatchExpr {
1044 g.match_expr(node, expected, [])
1045 }
1046 ast.EnumVal {
1047 type_name := g.table.get_type_name(node.typ)
1048 ts_type := (g.table.sym(node.typ).info as ast.Enum).typ
1049 g.literalint(g.enum_vals[type_name].fields[node.val], ts_type)
1050 }
1051 ast.OffsetOf {
1052 sym := g.table.sym(node.struct_type)
1053 if sym.kind != .struct {
1054 g.v_error('__offsetof expects a struct Type as first argument', node.pos)
1055 }
1056 off := g.get_field_offset(node.struct_type, node.field)
1057 g.literalint(off, ast.u32_type)
1058 }
1059 ast.SizeOf {
1060 if !g.table.known_type_idx(node.typ) {
1061 g.v_error('unknown type `${*g.table.sym(node.typ)}`', node.pos)
1062 }
1063 size, _ := g.pool.type_size(node.typ)
1064 g.literalint(size, ast.u32_type)
1065 }
1066 ast.BoolLiteral {
1067 g.func.i32_const(i32(node.val))
1068 }
1069 ast.StringLiteral {
1070 if expected != ast.string_type {
1071 val := serialise.eval_escape_codes(node) or { panic('unreachable') }
1072 str_pos := g.pool.append_string(val)
1073
1074 // c'str'
1075 g.literalint(g.data_base + str_pos, ast.voidptr_type)
1076 return
1077 }
1078
1079 v := g.new_local('', ast.string_type)
1080 g.set_with_expr(node, v)
1081 g.get(v)
1082 }
1083 ast.StringInterLiteral {
1084 g.string_inter_literal_expr(node, expected)
1085 }
1086 ast.InfixExpr {
1087 g.infix_expr(node, expected)
1088 }
1089 ast.PrefixExpr {
1090 g.prefix_expr(node, expected)
1091 }
1092 ast.PostfixExpr {
1093 kind := if node.op == .inc { token.Kind.plus } else { token.Kind.minus }
1094 v := g.get_var_or_make_from_expr(node.expr, node.typ)
1095
1096 g.set_prepare(v)
1097 {
1098 g.get(v)
1099 g.literalint(1, node.typ)
1100 g.handle_ptr_arithmetic(node.typ)
1101 g.infix_from_typ(node.typ, kind)
1102 }
1103 g.set_set(v)
1104 }
1105 ast.CharLiteral {
1106 rns := serialise.eval_escape_codes_raw(node.val) or { panic('unreachable') }.runes()[0]
1107 g.func.i32_const(i32(rns))
1108 }
1109 ast.Ident {
1110 v := g.get_var_from_ident(node)
1111 g.get(v)
1112 g.cast(v.typ, expected)
1113 }
1114 ast.IntegerLiteral, ast.FloatLiteral {
1115 g.literal(node.val, expected)
1116 }
1117 ast.Nil {
1118 g.func.i32_const(0)
1119 }
1120 ast.EmptyExpr {}
1121 ast.IfExpr {
1122 g.if_expr(node, expected, [])
1123 }
1124 ast.CastExpr {
1125 // don't want to handle ast.int_literal_type
1126 if node.expr is ast.IntegerLiteral || node.expr is ast.FloatLiteral {
1127 g.expr(node.expr, node.typ)
1128 return
1129 }
1130
1131 g.expr(node.expr, node.expr_type)
1132
1133 // TODO: unbelievable colossal hack
1134 mut typ := node.expr_type
1135 if node.expr is ast.Ident {
1136 v := g.get_var_from_ident(node.expr)
1137 if g.is_param(v) && node.expr_type == ast.int_literal_type {
1138 typ = ast.i64_type
1139 }
1140 }
1141
1142 g.func.cast(g.as_numtype(g.get_wasm_type(typ)), typ.is_signed(),
1143 g.as_numtype(g.get_wasm_type(node.typ)))
1144 }
1145 ast.CallExpr {
1146 g.call_expr(node, expected, [])
1147 }
1148 else {
1149 g.w_error('wasm.expr(): unhandled node: ' + node.type_name())
1150 }
1151 }
1152}
1153
1154pub fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
1155 if node.is_range {
1156 g.for_in_range(node)
1157 return
1158 }
1159
1160 cond_sym := g.table.sym(node.cond_type)
1161
1162 match cond_sym.kind {
1163 .array_fixed {
1164 g.for_in_array_fixed(node, cond_sym)
1165 }
1166 .string {
1167 g.for_in_string(node)
1168 }
1169 else {
1170 g.w_error('unsupported iter type: ${cond_sym.kind}')
1171 }
1172 }
1173}
1174
1175fn (mut g Gen) for_in_range(node ast.ForInStmt) {
1176 loop_var_type := unpack_literal_int(node.val_type)
1177 block := g.func.c_block([], [])
1178 {
1179 mut loop_var := Var{}
1180 loop_var = g.new_local(node.val_var, loop_var_type)
1181
1182 g.expr(node.cond, loop_var_type)
1183 g.set(loop_var)
1184
1185 loop := g.func.c_loop([], [])
1186 {
1187 g.loop_breakpoint_stack << LoopBreakpoint{
1188 c_continue: loop
1189 c_break: block
1190 name: node.label
1191 }
1192
1193 g.get(loop_var)
1194 g.expr(node.high, loop_var_type)
1195 wtyp := g.as_numtype(g.get_wasm_type(loop_var_type))
1196 g.func.lt(wtyp, loop_var_type.is_signed())
1197 g.func.eqz(.i32_t)
1198 g.func.c_br_if(block)
1199
1200 g.expr_stmts(node.stmts, ast.void_type)
1201
1202 g.set_prepare(loop_var)
1203 {
1204 g.get(loop_var)
1205 g.literalint(1, loop_var_type)
1206 g.func.add(wtyp)
1207 }
1208 g.set(loop_var)
1209
1210 g.func.c_br(loop)
1211 g.loop_breakpoint_stack.pop()
1212 }
1213 g.func.c_end(loop)
1214 }
1215 g.func.c_end(block)
1216}
1217
1218fn (mut g Gen) for_in_array_fixed(node ast.ForInStmt, cond_sym &ast.TypeSymbol) {
1219 info := cond_sym.info as ast.ArrayFixed
1220 array_size := info.size
1221
1222 block := g.func.c_block([], [])
1223 {
1224 idx_var := g.new_local('__idx', ast.int_type)
1225 g.literalint(0, ast.int_type)
1226 g.set(idx_var)
1227
1228 array_base := g.new_local('__array_base', node.cond_type)
1229 g.expr(node.cond, node.cond_type)
1230 g.set(array_base)
1231
1232 loop := g.func.c_loop([], [])
1233 {
1234 g.loop_breakpoint_stack << LoopBreakpoint{
1235 c_continue: loop
1236 c_break: block
1237 name: node.label
1238 }
1239
1240 // if index >= array_size
1241 g.get(idx_var)
1242 g.literalint(array_size, ast.int_type)
1243 g.func.ge(.i32_t, false)
1244 g.func.c_br_if(block)
1245
1246 // _ -> No variable in the loop
1247 if node.val_var != '_' {
1248 element_var := g.new_local(node.val_var, node.val_type)
1249
1250 // array_base + idx * element_size
1251 g.get(array_base)
1252 g.get(idx_var)
1253
1254 elem_size, _ := g.pool.type_size(node.val_type)
1255 if elem_size > 1 {
1256 g.literalint(elem_size, ast.int_type)
1257 g.func.mul(.i32_t)
1258 }
1259 g.func.add(.i32_t)
1260
1261 if g.is_pure_type(node.val_type) {
1262 g.load(node.val_type, 0)
1263 }
1264
1265 g.set(element_var)
1266 }
1267
1268 // Inside loop
1269 g.expr_stmts(node.stmts, ast.void_type)
1270
1271 // idx++
1272 g.set_prepare(idx_var)
1273 {
1274 g.get(idx_var)
1275 g.literalint(1, ast.int_type)
1276 g.func.add(.i32_t)
1277 }
1278 g.set(idx_var)
1279
1280 g.func.c_br(loop)
1281 g.loop_breakpoint_stack.pop()
1282 }
1283 g.func.c_end(loop)
1284 }
1285 g.func.c_end(block)
1286}
1287
1288fn (mut g Gen) for_in_string(node ast.ForInStmt) {
1289 block := g.func.c_block([], [])
1290 {
1291 idx_var := g.new_local('__idx', ast.int_type)
1292 g.literalint(0, ast.int_type)
1293 g.set(idx_var)
1294
1295 // String ptr
1296 string_var := g.new_local('__string', ast.string_type)
1297 g.expr(node.cond, ast.string_type)
1298 g.set(string_var)
1299
1300 len_var := g.new_local('__len', ast.int_type)
1301 g.get(string_var)
1302 g.load_field(ast.string_type, ast.int_type, 'len')
1303 g.set(len_var)
1304
1305 loop := g.func.c_loop([], [])
1306 {
1307 g.loop_breakpoint_stack << LoopBreakpoint{
1308 c_continue: loop
1309 c_break: block
1310 name: node.label
1311 }
1312
1313 // if index >= length
1314 g.get(idx_var)
1315 g.get(len_var)
1316 g.func.ge(.i32_t, false)
1317 g.func.c_br_if(block)
1318
1319 // _ -> No variable in the loop
1320 if node.val_var != '_' {
1321 char_var := g.new_local(node.val_var, node.val_type)
1322
1323 // Use string.at(idx) method to get the byte, don't reinvent the wheel
1324 g.get(string_var)
1325 g.get(idx_var)
1326 g.func.call('string.at')
1327
1328 g.set(char_var)
1329 }
1330
1331 // Inside loop
1332 g.expr_stmts(node.stmts, ast.void_type)
1333
1334 // idx++
1335 g.set_prepare(idx_var)
1336 {
1337 g.get(idx_var)
1338 g.literalint(1, ast.int_type)
1339 g.func.add(.i32_t)
1340 }
1341 g.set(idx_var)
1342
1343 g.func.c_br(loop)
1344 g.loop_breakpoint_stack.pop()
1345 }
1346 g.func.c_end(loop)
1347 }
1348 g.func.c_end(block)
1349}
1350
1351pub fn (g &Gen) file_pos(pos token.Pos) string {
1352 return '${os.to_slash(g.file_path)}:${pos.line_nr + 1}:${pos.col + 1}'
1353}
1354
1355pub fn (mut g Gen) expr_stmt(node ast.Stmt, expected ast.Type) {
1356 match node {
1357 ast.Block {
1358 g.expr_stmts(node.stmts, expected)
1359 }
1360 ast.Return {
1361 if node.exprs.len > 1 {
1362 g.set_with_multi_expr(ast.ConcatExpr{ vals: node.exprs }, g.ret, g.ret_rvars)
1363 } else if node.exprs.len == 1 {
1364 g.set_with_multi_expr(node.exprs[0], g.ret, g.ret_rvars)
1365 }
1366 g.func.c_br(g.ret_br)
1367 }
1368 ast.ExprStmt {
1369 g.expr(node.expr, expected)
1370 }
1371 ast.ForStmt {
1372 block := g.func.c_block([], [])
1373 {
1374 loop := g.func.c_loop([], [])
1375 {
1376 g.loop_breakpoint_stack << LoopBreakpoint{
1377 c_continue: loop
1378 c_break: block
1379 name: node.label
1380 }
1381
1382 if !node.is_inf {
1383 g.expr(node.cond, ast.bool_type)
1384 g.func.eqz(.i32_t)
1385 g.func.c_br_if(block) // !cond, goto end
1386 }
1387 g.expr_stmts(node.stmts, ast.void_type)
1388 g.func.c_br(loop) // goto loop
1389
1390 g.loop_breakpoint_stack.pop()
1391 }
1392 g.func.c_end(loop)
1393 }
1394 g.func.c_end(block)
1395 }
1396 ast.ForCStmt {
1397 block := g.func.c_block([], [])
1398 {
1399 if node.has_init {
1400 g.expr_stmt(node.init, ast.void_type)
1401 }
1402
1403 loop := g.func.c_loop([], [])
1404 {
1405 continue_block := g.func.c_block([], [])
1406 {
1407 g.loop_breakpoint_stack << LoopBreakpoint{
1408 c_continue: continue_block
1409 c_break: block
1410 name: node.label
1411 }
1412
1413 if node.has_cond {
1414 g.expr(node.cond, ast.bool_type)
1415 g.func.eqz(.i32_t)
1416 g.func.c_br_if(block) // !cond, goto end
1417 }
1418
1419 g.expr_stmts(node.stmts, ast.void_type)
1420
1421 g.loop_breakpoint_stack.pop()
1422 }
1423 g.func.c_end(continue_block)
1424
1425 if node.has_inc {
1426 g.expr_stmt(node.inc, ast.void_type)
1427 }
1428
1429 g.func.c_br(loop)
1430 }
1431 g.func.c_end(loop)
1432 }
1433 g.func.c_end(block)
1434 }
1435 ast.ForInStmt {
1436 g.for_in_stmt(node)
1437 }
1438 ast.BranchStmt {
1439 mut bp := g.loop_breakpoint_stack.last()
1440 if node.label != '' {
1441 for i := g.loop_breakpoint_stack.len; i > 0; {
1442 i--
1443 if g.loop_breakpoint_stack[i].name == node.label {
1444 bp = g.loop_breakpoint_stack[i]
1445 }
1446 }
1447 }
1448
1449 if node.kind == .key_break {
1450 g.func.c_br(bp.c_break)
1451 } else {
1452 g.func.c_br(bp.c_continue)
1453 }
1454 }
1455 ast.DeferStmt {
1456 v := g.new_local('__defer(${node.idx_in_fn})', ast.bool_type)
1457 g.func.i32_const(1)
1458 g.set(v)
1459 g.defer_vars << v
1460 }
1461 ast.AssertStmt {
1462 if !node.is_used {
1463 return
1464 }
1465
1466 // calls builtin functions, don't want to corrupt stack frame!
1467 g.is_leaf_function = false
1468
1469 g.expr(node.expr, ast.bool_type)
1470 g.func.eqz(.i32_t) // !expr
1471 lbl := g.func.c_if([], [])
1472 {
1473 // main.main: ${msg}
1474 // V panic: Assertion failed...
1475
1476 mut msg := '${g.file_pos(node.pos)}: fn ${g.fn_name}: ${ast.Stmt(node)}'
1477 if node.extra is ast.StringLiteral {
1478 msg += ", '${node.extra.val}'"
1479 }
1480
1481 g.expr(ast.StringLiteral{ val: msg }, ast.string_type)
1482 g.func.call('eprintln')
1483 g.expr(ast.StringLiteral{ val: 'Assertion failed...' }, ast.string_type)
1484 g.func.call('panic')
1485 }
1486 g.func.c_end(lbl)
1487 }
1488 ast.AssignStmt {
1489 if node.has_cross_var {
1490 g.w_error('complex assign statements are not implemented')
1491 // `a, b = b, a`
1492 } else {
1493 // `a := 1` | `a, b := 1, 2`
1494 // `a, b := foo()`
1495 // `a, b := if cond { 1, 2 } else { 3, 4 }`
1496
1497 is_expr_assign := node.op !in [.decl_assign, .assign]
1498
1499 // similar code from `call_expr()`
1500 // create variables or obtain them for use as rvals
1501 mut rvars := []Var{cap: node.left_types.len}
1502 for idx, rt in node.left_types {
1503 left := node.left[idx]
1504
1505 mut var := Var{}
1506 mut passed := false
1507
1508 if left is ast.Ident {
1509 if left.kind == .blank_ident {
1510 var = g.new_local('_', rt)
1511 passed = true
1512 } else if node.op == .decl_assign {
1513 var = g.new_local(left.name, rt)
1514 passed = true
1515 }
1516 }
1517
1518 if !passed {
1519 if node.op == .assign {
1520 if v := g.get_var_from_expr(left) {
1521 var = v
1522 }
1523 } else if node.op == .plus_assign && g.is_param_type(rt) {
1524 var = g.new_local('', rt)
1525 }
1526 }
1527
1528 if g.is_param_type(rt) {
1529 rvars << var
1530 }
1531 }
1532
1533 mut set := false
1534 if node.right.len == 1 {
1535 right := node.right[0]
1536 match right {
1537 ast.IfExpr {
1538 params :=
1539 node.left_types.filter(!g.is_param_type(it)).map(g.get_wasm_type(it))
1540 g.if_branch(right, right.typ, params, 0, rvars)
1541 set = true
1542 }
1543 ast.CallExpr {
1544 g.call_expr(right, 0, rvars)
1545 set = true
1546 }
1547 else {
1548 // : set = false
1549 // execute below instead
1550 }
1551 }
1552 }
1553
1554 // will never be a multi expr
1555 // assume len == 1 for left and right
1556 if is_expr_assign {
1557 left, right, typ := node.left[0], node.right[0], node.left_types[0]
1558
1559 rop := token.assign_op_to_infix_op(node.op)
1560 lhs := g.get_var_or_make_from_expr(left, typ)
1561
1562 if !g.is_pure_type(lhs.typ) {
1563 // main.struct.+
1564 name := '${g.table.get_type_name(lhs.typ)}.${rop}'
1565 g.ref(lhs)
1566 g.ref(lhs)
1567 g.expr(right, lhs.typ)
1568 g.func.call(name)
1569 } else {
1570 g.set_prepare(lhs)
1571 {
1572 g.get(lhs)
1573 g.expr(right, lhs.typ)
1574 g.infix_from_typ(lhs.typ, rop)
1575 }
1576 g.set_set(lhs)
1577 }
1578
1579 return
1580 }
1581
1582 // prepare variables using expr()
1583 // if is an rvar, set it and ignore following
1584 if !set {
1585 assert node.left.len == node.right.len
1586 mut ridx := 0
1587 for idx, right in node.right {
1588 typ := node.left_types[idx]
1589 if g.is_param_type(typ) {
1590 g.set_with_expr(right, rvars[ridx])
1591 ridx++
1592 } else {
1593 g.expr(right, typ)
1594 }
1595 }
1596 }
1597
1598 for i := node.left.len; i > 0; {
1599 i--
1600 left := node.left[i]
1601 typ := node.left_types[i]
1602
1603 if g.is_param_type(typ) {
1604 // is already set
1605 continue
1606 }
1607
1608 if left is ast.Ident {
1609 // `_ = expr`
1610 if left.kind == .blank_ident {
1611 // expression still may have side effect
1612 g.func.drop()
1613 continue
1614 }
1615 }
1616
1617 v := g.get_var_or_make_from_expr(left, typ)
1618 g.set(v)
1619 }
1620 }
1621 }
1622 ast.AsmStmt {
1623 // assumed expected == void
1624 g.asm_stmt(node)
1625 }
1626 ast.EmptyStmt {
1627 // EmptyStmt nodes are emitted by earlier compiler passes for eliminated statements.
1628 }
1629 else {
1630 g.w_error('wasm.expr_stmt(): unhandled node: ' + node.type_name())
1631 }
1632 }
1633}
1634
1635pub fn (mut g Gen) expr_stmts(stmts []ast.Stmt, expected ast.Type) {
1636 g.rvar_expr_stmts(stmts, expected, [])
1637}
1638
1639pub fn (mut g Gen) rvar_expr_stmts(stmts []ast.Stmt, expected ast.Type, existing_rvars []Var) {
1640 for idx, stmt in stmts {
1641 if idx + 1 >= stmts.len {
1642 if stmt is ast.ExprStmt {
1643 g.set_with_multi_expr(stmt.expr, expected, existing_rvars)
1644 } else {
1645 g.expr_stmt(stmt, expected)
1646 }
1647 } else {
1648 g.expr_stmt(stmt, ast.void_type)
1649 }
1650 }
1651}
1652
1653pub fn (mut g Gen) toplevel_stmt(node ast.Stmt) {
1654 match node {
1655 ast.FnDecl {
1656 g.fn_decl(node)
1657 }
1658 ast.Module {}
1659 ast.GlobalDecl {}
1660 ast.ConstDecl {}
1661 ast.Import {}
1662 ast.StructDecl {}
1663 ast.EnumDecl {}
1664 ast.TypeDecl {}
1665 else {
1666 g.w_error('wasm.toplevel_stmt(): unhandled node: ' + node.type_name())
1667 }
1668 }
1669}
1670
1671pub fn (mut g Gen) toplevel_stmts(stmts []ast.Stmt) {
1672 for stmt in stmts {
1673 g.toplevel_stmt(stmt)
1674 }
1675}
1676
1677struct Enum {
1678mut:
1679 fields map[string]i64
1680}
1681
1682fn (mut g Gen) eval_enum_field_expr(expr ast.Expr) ?i64 {
1683 match expr {
1684 ast.IntegerLiteral {
1685 return expr.val.i64()
1686 }
1687 ast.CharLiteral {
1688 runes := expr.val.runes()
1689 if runes.len == 0 {
1690 return none
1691 }
1692 return i64(runes[0])
1693 }
1694 ast.BoolLiteral {
1695 return if expr.val { i64(1) } else { i64(0) }
1696 }
1697 ast.ParExpr {
1698 return g.eval_enum_field_expr(expr.expr)
1699 }
1700 ast.CastExpr {
1701 return g.eval_enum_field_expr(expr.expr)
1702 }
1703 ast.PrefixExpr {
1704 right := g.eval_enum_field_expr(expr.right)?
1705 match expr.op {
1706 .plus {
1707 return right
1708 }
1709 .minus {
1710 return -right
1711 }
1712 .bit_not {
1713 return ~right
1714 }
1715 else {
1716 return none
1717 }
1718 }
1719 }
1720 ast.InfixExpr {
1721 left := g.eval_enum_field_expr(expr.left)?
1722 right := g.eval_enum_field_expr(expr.right)?
1723 match expr.op {
1724 .plus {
1725 return left + right
1726 }
1727 .minus {
1728 return left - right
1729 }
1730 .mul {
1731 return left * right
1732 }
1733 .div {
1734 if right == 0 {
1735 return none
1736 }
1737 return left / right
1738 }
1739 .mod {
1740 if right == 0 {
1741 return none
1742 }
1743 return left % right
1744 }
1745 .left_shift {
1746 return i64(u64(left) << int(right))
1747 }
1748 .right_shift {
1749 return left >> int(right)
1750 }
1751 .unsigned_right_shift {
1752 return i64(u64(left) >> int(right))
1753 }
1754 .amp {
1755 return left & right
1756 }
1757 .pipe {
1758 return left | right
1759 }
1760 .xor {
1761 return left ^ right
1762 }
1763 else {
1764 return none
1765 }
1766 }
1767 }
1768 ast.Ident {
1769 mut obj := expr.obj
1770 if obj !in [ast.ConstField, ast.GlobalField] {
1771 obj = expr.scope.find(expr.name) or { return none }
1772 }
1773 match mut obj {
1774 ast.ConstField {
1775 return g.eval_enum_field_expr(obj.expr)
1776 }
1777 ast.GlobalField {
1778 return g.eval_enum_field_expr(obj.expr)
1779 }
1780 else {
1781 return none
1782 }
1783 }
1784 }
1785 else {
1786 return none
1787 }
1788 }
1789}
1790
1791pub fn (mut g Gen) calculate_enum_fields() {
1792 // `enum Enum as u64` is supported
1793 for name, decl in g.table.enum_decls {
1794 mut enum_vals := Enum{}
1795 mut value := if decl.is_flag { i64(1) } else { 0 }
1796 for field in decl.fields {
1797 if field.has_expr {
1798 value = g.eval_enum_field_expr(field.expr) or {
1799 g.w_error('wasm: unsupported enum expression for `${name}.${field.name}`')
1800 }
1801 }
1802 enum_vals.fields[field.name] = value
1803 if decl.is_flag {
1804 value <<= 1
1805 } else {
1806 value++
1807 }
1808 }
1809 g.enum_vals[name] = enum_vals
1810 }
1811}
1812
1813pub fn gen(files []&ast.File, mut table ast.Table, out_name string, w_pref &pref.Preferences) {
1814 stack_top := w_pref.wasm_stack_top
1815 mut g := &Gen{
1816 table: table
1817 pref: w_pref
1818 files: files
1819 pool: serialise.new_pool(table, store_relocs: true, null_terminated: false)
1820 stack_top: stack_top
1821 data_base: calc_align(stack_top + 1, 16)
1822 }
1823 g.mod.assign_memory('memory', true, 1, none)
1824
1825 if g.pref.is_debug {
1826 g.mod.enable_debug(none)
1827 }
1828
1829 g.calculate_enum_fields()
1830 for file in g.files {
1831 g.file_path = file.path
1832 if file.errors.len > 0 {
1833 util.verror('wasm error', file.errors[0].str())
1834 }
1835 g.toplevel_stmts(file.stmts)
1836 }
1837 g.housekeeping()
1838
1839 mod := g.mod.compile()
1840
1841 if out_name == '-' {
1842 if os.is_atty(1) == 1 {
1843 eprintln('pretty printing to stdout is not implemented for the time being')
1844 eprintln('raw bytes can mess up your terminal, are you piping into a file?')
1845 exit(1)
1846 } else {
1847 print(mod.bytestr())
1848 }
1849 }
1850
1851 if out_name != '-' {
1852 os.write_file_array(out_name, mod) or { panic(err) }
1853 if g.pref.wasm_validate {
1854 exe := $if windows { 'wasm-validate.exe' } $else { 'wasm-validate' }
1855 if rt := os.find_abs_path_of_executable(exe) {
1856 mut p := os.new_process(rt)
1857 p.set_args([out_name])
1858 p.set_redirect_stdio()
1859 p.run()
1860 err := p.stderr_slurp()
1861 p.wait()
1862 if p.code != 0 {
1863 eprintln(err)
1864 g.w_error('validation failed, this should not happen. report an issue with the above messages, the webassembly generated, and appropriate code.')
1865 }
1866 } else {
1867 g.w_error('${exe} not found! Try installing WABT (WebAssembly Binary Toolkit). Run `./cmd/tools/install_wabt.vsh`, to download a prebuilt executable for your platform.')
1868 }
1869 }
1870 if g.pref.is_prod {
1871 exe := $if windows { 'wasm-opt.exe' } $else { 'wasm-opt' }
1872 if rt := os.find_abs_path_of_executable(exe) {
1873 // -lmu: low memory unused, very important optimisation
1874 res :=
1875 os.execute('${os.quoted_path(rt)} -all -lmu -c -O4 ${os.quoted_path(out_name)} -o ${os.quoted_path(out_name)}')
1876 if res.exit_code != 0 {
1877 eprintln(res.output)
1878 g.w_error('${rt} failed, this should not happen. Report an issue with the above messages, the webassembly generated, and appropriate code.')
1879 }
1880 } else {
1881 g.w_error('${exe} not found! Try installing Binaryen.
1882 | Run `./cmd/tools/install_binaryen.vsh`, to download a prebuilt executable for your platform.
1883 | After that, either copy or symlink thirdparty/binaryen/bin/wasm-opt to a folder on your PATH,
1884 | or add thirdparty/binaryen/bin to your PATH.
1885 | Use `wasm-opt --version` to verify that it can be found.
1886 '.strip_margin())
1887 }
1888 }
1889 } else if g.pref.wasm_validate || g.pref.is_prod {
1890 eprintln('stdout output, cannot validate or optimise wasm')
1891 }
1892}
1893