v2 / vlib / v / gen / wasm / mem.v
913 lines · 805 sloc · 19.43 KB · 85768336b5364752a0e5d07e31814d2f95f8e2ba
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 wasm
7import v.ast
8import v.gen.wasm.serialise
9import encoding.binary
10
11pub struct Var {
12 name string
13mut:
14 typ ast.Type
15 idx wasm.LocalIndex
16 is_address bool
17 is_global bool
18 g_idx wasm.GlobalIndex
19 offset int
20}
21
22pub fn (mut g Gen) get_var_from_ident(ident ast.Ident) Var {
23 mut obj := ident.obj
24 if obj !in [ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister] {
25 obj = ident.scope.find(ident.name) or { g.w_error('unknown variable ${ident.name}') }
26 }
27
28 match mut obj {
29 ast.Var {
30 if g.local_vars.len == 0 {
31 g.w_error('get_var_from_ident: g.local_vars.len == 0')
32 }
33 mut c := g.local_vars.len
34 for {
35 c--
36 if g.local_vars[c].name == obj.name {
37 return g.local_vars[c]
38 }
39 if c == 0 {
40 break
41 }
42 }
43 g.w_error('get_var_from_ident: unreachable, variable not found')
44 }
45 ast.ConstField {
46 if gbl := g.global_vars[obj.name] {
47 return gbl.v
48 }
49 gbl := g.new_global(obj.name, obj.typ, obj.expr, false)
50 g.global_vars[obj.name] = gbl
51 return gbl.v
52 }
53 ast.GlobalField {
54 if gbl := g.global_vars[obj.name] {
55 return gbl.v
56 }
57 gbl := g.new_global(obj.name, obj.typ, obj.expr, true)
58 g.global_vars[obj.name] = gbl
59 return gbl.v
60 }
61 else {
62 g.w_error('unsupported variable type type:${obj} name:${ident.name}')
63 }
64 }
65}
66
67pub fn (mut g Gen) get_var_from_expr(node ast.Expr) ?Var {
68 match node {
69 ast.Ident {
70 return g.get_var_from_ident(node)
71 }
72 ast.ParExpr {
73 return g.get_var_from_expr(node.expr)
74 }
75 ast.CastExpr {
76 // For cast expressions like &u32(), we need to look through
77 // the cast to get the underlying variable because WASM don't have
78 // really pointers like in C
79 return g.get_var_from_expr(node.expr)
80 }
81 ast.SelectorExpr {
82 mut addr := g.get_var_from_expr(node.expr) or {
83 // if place {
84 // g.field_offset(node.expr_type, node.field_name)
85 // }
86 return none
87 }
88 addr.is_address = true
89
90 offset := g.get_field_offset(node.expr_type, node.field_name)
91 return g.offset(addr, node.typ, offset)
92 }
93 else {
94 // g.w_error('get_var_from_expr: unexpected `${node.type_name()}`')
95 return none
96 }
97 }
98}
99
100// ONLY call this with the LHS of an assign, an lvalue
101// use get_var_from_expr in all other cases
102pub fn (mut g Gen) get_var_or_make_from_expr(node ast.Expr, typ ast.Type) Var {
103 if v := g.get_var_from_expr(node) {
104 return v
105 }
106
107 mut v := g.new_local('__tmp', ast.voidptr_type)
108 g.needs_address = true
109 {
110 g.set_with_expr(node, v)
111 }
112 g.needs_address = false
113
114 v.typ = typ
115 v.is_address = true
116
117 return v
118}
119
120pub fn (mut g Gen) bp() wasm.LocalIndex {
121 if g.bp_idx == -1 {
122 g.bp_idx = g.func.new_local_named(.i32_t, '__vbp')
123 }
124 return g.bp_idx
125}
126
127pub fn (mut g Gen) sp() wasm.GlobalIndex {
128 if sp := g.sp_global {
129 return sp
130 }
131 // (64KiB - 1KiB) of stack space, grows downwards.
132 // Memory addresses from 0 to 1024 are forbidden.
133 g.sp_global = g.mod.new_global('__vsp', false, .i32_t, true, wasm.constexpr_value(0))
134 return g.sp()
135}
136
137pub fn (mut g Gen) hp() wasm.GlobalIndex {
138 if hp := g.heap_base {
139 return hp
140 }
141 hp := g.mod.new_global('__heap_base', false, .i32_t, false, wasm.constexpr_value(0))
142 g.heap_base = hp
143 return hp
144}
145
146pub fn (mut g Gen) new_local(name string, typ_ ast.Type) Var {
147 mut typ := typ_
148 ts := g.table.sym(typ)
149
150 match ts.info {
151 ast.Enum {
152 typ = ts.info.typ
153 }
154 ast.Alias {
155 typ = ts.info.parent_type
156 }
157 else {}
158 }
159
160 is_address := !g.is_pure_type(typ)
161 wtyp := g.get_wasm_type(typ)
162
163 mut v := Var{
164 name: name
165 typ: typ
166 is_address: is_address
167 }
168
169 if !is_address {
170 v.idx = g.func.new_local_named(wtyp, g.dbg_type_name(name, typ_))
171 g.local_vars << v
172 return v
173 }
174 v.idx = g.bp()
175
176 // allocate memory, then assign an offset
177 //
178 match ts.info {
179 ast.Struct, ast.ArrayFixed {
180 size, align := g.pool.type_size(typ)
181 padding := calc_padding(g.stack_frame, align)
182 address := g.stack_frame
183 g.stack_frame += size + padding
184
185 v.offset = address
186 }
187 else {
188 g.w_error('new_local: type `${*ts}` (${ts.info.type_name()}) is not a supported local type')
189 }
190 }
191
192 g.local_vars << v
193 return v
194}
195
196pub fn (mut g Gen) literal_to_constant_expression(typ_ ast.Type, init ast.Expr) ?wasm.ConstExpression {
197 typ := ast.mktyp(typ_)
198 match init {
199 ast.BoolLiteral {
200 return wasm.constexpr_value(int(init.val))
201 }
202 ast.CharLiteral {
203 return wasm.constexpr_value(int(init.val.runes()[0]))
204 }
205 ast.FloatLiteral {
206 if typ == ast.f32_type {
207 return wasm.constexpr_value(init.val.f32())
208 } else if typ == ast.f64_type {
209 return wasm.constexpr_value(init.val.f64())
210 }
211 }
212 ast.IntegerLiteral {
213 if !typ.is_pure_int() {
214 return none
215 }
216 t := g.get_wasm_type(typ)
217 match t {
218 .i32_t { return wasm.constexpr_value(init.val.int()) }
219 .i64_t { return wasm.constexpr_value(init.val.i64()) }
220 else {}
221 }
222 }
223 ast.Ident {
224 mut obj := init.obj
225 if obj !in [ast.ConstField, ast.GlobalField] {
226 obj = init.scope.find(init.name) or { return none }
227 }
228 match mut obj {
229 ast.ConstField {
230 return g.literal_to_constant_expression(typ, obj.expr)
231 }
232 ast.GlobalField {
233 return g.literal_to_constant_expression(typ, obj.expr)
234 }
235 else {
236 return none
237 }
238 }
239 }
240 else {}
241 }
242
243 return none
244}
245
246pub fn (mut g Gen) new_global(name string, typ_ ast.Type, init ast.Expr, is_global_mut bool) Global {
247 mut typ := ast.mktyp(typ_)
248 ts := g.table.sym(typ)
249
250 match ts.info {
251 ast.Enum {
252 typ = ts.info.typ
253 }
254 ast.Alias {
255 typ = ts.info.parent_type
256 }
257 else {}
258 }
259
260 mut is_mut := false
261 is_address := !g.is_pure_type(typ)
262 mut init_expr := ?ast.Expr(none)
263
264 cexpr := if cexpr_v := g.literal_to_constant_expression(unpack_literal_int(typ), init) {
265 is_mut = is_global_mut
266 cexpr_v
267 } else {
268 // Isn't a literal ...
269 if is_address {
270 // ... allocate memory and append
271 pos, is_init := g.pool.append(init, typ)
272 if !is_init {
273 // ... AND wait for init in `_vinit`
274 init_expr = init
275 }
276 wasm.constexpr_value(g.data_base + pos)
277 } else {
278 // ... wait for init in `_vinit`
279 init_expr = init
280 is_mut = true
281
282 t := g.get_wasm_type(typ)
283 wasm.constexpr_value_zero(t)
284 }
285 }
286
287 mut glbl := Global{
288 init: init_expr
289 v: Var{
290 name: name
291 typ: typ
292 is_address: is_address
293 is_global: true
294 g_idx: g.mod.new_global(g.dbg_type_name(name, typ), false,
295 g.get_wasm_type_int_literal(typ), is_mut, cexpr)
296 }
297 }
298
299 return glbl
300}
301
302// is_pure_type(voidptr) == true
303// is_pure_type(&Struct) == false
304pub fn (g &Gen) is_pure_type(typ ast.Type) bool {
305 if typ.is_pure_int() || typ.is_pure_float() || typ == ast.char_type_idx
306 || typ.is_any_kind_of_pointer() || typ.is_bool() {
307 return true
308 }
309 ts := g.table.sym(typ)
310 match ts.info {
311 ast.Alias {
312 return g.is_pure_type(ts.info.parent_type)
313 }
314 ast.Enum {
315 return g.is_pure_type(ts.info.typ)
316 }
317 else {}
318 }
319
320 return false
321}
322
323pub fn log2(size int) int {
324 return match size {
325 1 { 0 }
326 2 { 1 }
327 4 { 2 }
328 8 { 3 }
329 else { panic('unreachable') }
330 }
331}
332
333pub fn (mut g Gen) load(typ ast.Type, offset int) {
334 size, align := g.pool.type_size(typ)
335 wtyp := g.as_numtype(g.get_wasm_type(typ))
336
337 match size {
338 1 { g.func.load8(wtyp, typ.is_signed(), log2(align), offset) }
339 2 { g.func.load16(wtyp, typ.is_signed(), log2(align), offset) }
340 else { g.func.load(wtyp, log2(align), offset) }
341 }
342}
343
344pub fn (mut g Gen) store(typ ast.Type, offset int) {
345 size, align := g.pool.type_size(typ)
346 wtyp := g.as_numtype(g.get_wasm_type(typ))
347
348 match size {
349 1 { g.func.store8(wtyp, log2(align), offset) }
350 2 { g.func.store16(wtyp, log2(align), offset) }
351 else { g.func.store(wtyp, log2(align), offset) }
352 }
353}
354
355pub fn (mut g Gen) get(v Var) {
356 if v.is_global {
357 g.func.global_get(v.g_idx)
358 } else {
359 g.func.local_get(v.idx)
360 }
361
362 if v.is_address && g.is_pure_type(v.typ) {
363 g.load(v.typ, v.offset)
364 } else if v.is_address && v.offset != 0 {
365 g.func.i32_const(i32(v.offset))
366 g.func.add(.i32_t)
367 }
368}
369
370pub fn (mut g Gen) mov(to Var, v Var) {
371 if !v.is_address || g.is_pure_type(v.typ) {
372 g.get(v)
373 g.cast(v.typ, to.typ)
374 g.set(to)
375 return
376 }
377
378 size, _ := g.pool.type_size(v.typ)
379
380 if size > 16 {
381 g.ref(to)
382 g.ref(v)
383 g.func.i32_const(i32(size))
384 g.func.memory_copy()
385 return
386 }
387
388 mut sz := size
389 mut oz := 0
390 for sz > 0 {
391 g.ref_ignore_offset(to)
392 g.ref_ignore_offset(v)
393 if sz - 8 >= 0 {
394 g.load(ast.u64_type_idx, v.offset + oz)
395 g.store(ast.u64_type_idx, to.offset + oz)
396 sz -= 8
397 oz += 8
398 } else if sz - 4 >= 0 {
399 g.load(ast.u32_type_idx, v.offset + oz)
400 g.store(ast.u32_type_idx, to.offset + oz)
401 sz -= 4
402 oz += 4
403 } else if sz - 2 >= 0 {
404 g.load(ast.u16_type_idx, v.offset + oz)
405 g.store(ast.u16_type_idx, to.offset + oz)
406 sz -= 2
407 oz += 2
408 } else if sz - 1 >= 0 {
409 g.load(ast.u8_type_idx, v.offset + oz)
410 g.store(ast.u8_type_idx, to.offset + oz)
411 sz -= 1
412 oz += 1
413 }
414 }
415}
416
417pub fn (mut g Gen) set_prepare(v Var) {
418 if !v.is_address {
419 return
420 }
421
422 if g.is_pure_type(v.typ) {
423 if v.is_global {
424 g.func.global_get(v.g_idx)
425 } else {
426 g.func.local_get(v.idx)
427 }
428 return
429 }
430}
431
432pub fn (mut g Gen) set_set(v Var) {
433 if !v.is_address {
434 if v.is_global {
435 g.func.global_set(v.g_idx)
436 } else {
437 g.func.local_set(v.idx)
438 }
439 return
440 }
441
442 if g.is_pure_type(v.typ) {
443 g.store(v.typ, v.offset)
444 return
445 }
446
447 from := Var{
448 typ: v.typ
449 idx: g.func.new_local_named(.i32_t, '__tmp<voidptr>')
450 is_address: v.is_address
451 }
452
453 g.func.local_set(from.idx)
454 g.mov(v, from)
455}
456
457// set structures with pointer, memcpy.
458// set pointers with value, get local, store value.
459// set value, set local.
460// -- set works with a single value present on the stack beforehand
461// -- not optimal for copying stack memory or shuffling structs
462// -- use mov instead
463pub fn (mut g Gen) set(v Var) {
464 if !v.is_address {
465 if v.is_global {
466 g.func.global_set(v.g_idx)
467 } else {
468 g.func.local_set(v.idx)
469 }
470 return
471 }
472
473 if g.is_pure_type(v.typ) {
474 l := g.new_local('__tmp', v.typ)
475 g.func.local_set(l.idx)
476
477 if v.is_global {
478 g.func.global_get(v.g_idx)
479 } else {
480 g.func.local_get(v.idx)
481 }
482
483 g.func.local_get(l.idx)
484
485 g.store(v.typ, v.offset)
486 return
487 }
488
489 from := Var{
490 typ: v.typ
491 idx: g.func.new_local_named(.i32_t, '__tmp<voidptr>')
492 is_address: v.is_address
493 }
494
495 g.func.local_set(from.idx)
496 g.mov(v, from)
497}
498
499// to satisfy inline assembly needs
500// never used by actual codegen
501pub fn (mut g Gen) tee(v Var) {
502 assert !v.is_global
503
504 if !v.is_address {
505 g.func.local_tee(v.idx)
506 return
507 }
508
509 if g.is_pure_type(v.typ) {
510 l := g.new_local('__tmp', v.typ)
511 g.func.local_tee(l.idx) // tee here, leave on stack
512
513 g.func.local_get(v.idx)
514 g.func.local_get(l.idx)
515 g.store(v.typ, v.offset)
516 return
517 }
518
519 from := Var{
520 typ: v.typ
521 idx: g.func.new_local_named(.i32_t, '__tmp<voidptr>')
522 is_address: v.is_address
523 }
524
525 g.func.local_tee(from.idx) // tee here, leave on stack
526 g.mov(v, from)
527}
528
529pub fn (mut g Gen) ref(v Var) {
530 g.ref_ignore_offset(v)
531
532 if v.offset != 0 {
533 g.func.i32_const(i32(v.offset))
534 g.func.add(.i32_t)
535 }
536}
537
538pub fn (mut g Gen) ref_ignore_offset(v Var) {
539 if !v.is_address {
540 panic('unreachable')
541 }
542
543 if v.is_global {
544 g.func.global_get(v.g_idx)
545 } else {
546 g.func.local_get(v.idx)
547 }
548}
549
550// creates a new pointer variable with the offset `offset` and type `typ`
551pub fn (mut g Gen) offset(v Var, typ ast.Type, offset int) Var {
552 if !v.is_address {
553 panic('unreachable')
554 }
555
556 nv := Var{
557 ...v
558 typ: typ
559 offset: v.offset + offset
560 }
561
562 return nv
563}
564
565pub fn (mut g Gen) zero_fill(v Var, size int) {
566 assert size > 0
567
568 // TODO: support coalescing `zero_fill` calls together.
569 // maybe with some kind of context?
570 //
571 // ```v
572 // struct AA {
573 // a bool
574 // b int = 20
575 // c int
576 // d int
577 // }
578 // ```
579 //
580 // ```wast
581 // (i32.store8
582 // (local.get $0)
583 // (i32.const 0)
584 // )
585 // (i32.store offset=4
586 // (i32.const 20)
587 // (local.get $0)
588 // ) ;; /- join these together.
589 // (i32.store offset=8 ;;-\
590 // (local.get $0) ;; |
591 // (i32.const 0) ;; |
592 // ) ;; |
593 // (i32.store offset=12 ;; |
594 // (local.get $0) ;; |
595 // (i32.const 0) ;; |
596 // ) ;;-/
597 // ```
598
599 if size > 16 {
600 g.ref(v)
601 g.func.i32_const(0)
602 g.func.i32_const(i32(size))
603 g.func.memory_fill()
604 return
605 }
606
607 mut sz := size
608 mut oz := 0
609 for sz > 0 {
610 g.ref_ignore_offset(v)
611 if sz - 8 >= 0 {
612 g.func.i64_const(0)
613 g.store(ast.u64_type_idx, v.offset + oz)
614 sz -= 8
615 oz += 8
616 } else if sz - 4 >= 0 {
617 g.func.i32_const(0)
618 g.store(ast.u32_type_idx, v.offset + oz)
619 sz -= 4
620 oz += 4
621 } else if sz - 2 >= 0 {
622 g.func.i32_const(0)
623 g.store(ast.u16_type_idx, v.offset + oz)
624 sz -= 2
625 oz += 2
626 } else if sz - 1 >= 0 {
627 g.func.i32_const(0)
628 g.store(ast.u8_type_idx, v.offset + oz)
629 sz -= 1
630 oz += 1
631 }
632 }
633}
634
635pub fn (g &Gen) is_param(v Var) bool {
636 if v.is_global {
637 return false
638 }
639
640 return v.idx < g.fn_local_idx_end
641}
642
643pub fn (mut g Gen) set_with_multi_expr(init ast.Expr, expected ast.Type, existing_rvars []Var) {
644 // misleading name: this doesn't really perform similar to `set_with_expr`
645 match init {
646 ast.ConcatExpr {
647 mut r := 0
648 types := g.unpack_type(expected)
649 for idx, expr in init.vals {
650 typ := types[idx]
651 if g.is_param_type(typ) {
652 if rhs := g.get_var_from_expr(expr) {
653 g.mov(existing_rvars[r], rhs)
654 } else {
655 g.set_with_expr(expr, existing_rvars[r])
656 }
657 r++
658 } else {
659 g.expr(expr, typ)
660 }
661 }
662 }
663 ast.IfExpr {
664 g.if_expr(init, expected, existing_rvars)
665 }
666 ast.MatchExpr {
667 g.match_expr(init, expected, existing_rvars)
668 }
669 ast.CallExpr {
670 g.call_expr(init, expected, existing_rvars)
671 }
672 else {
673 if existing_rvars.len > 1 {
674 g.w_error('wasm.set_with_multi_expr(): (existing_rvars.len > 1) node: ' +
675 init.type_name())
676 }
677
678 if existing_rvars.len == 1 && g.is_param_type(expected) {
679 if rhs := g.get_var_from_expr(init) {
680 g.mov(existing_rvars[0], rhs)
681 } else {
682 g.set_with_expr(init, existing_rvars[0])
683 }
684 } else {
685 g.expr(init, expected)
686 }
687 }
688 }
689}
690
691pub fn (mut g Gen) set_with_expr(init ast.Expr, v Var) {
692 match init {
693 ast.StructInit {
694 size, _ := g.pool.type_size(v.typ)
695 ts := g.table.sym(v.typ)
696 ts_info := ts.info as ast.Struct
697 si := g.pool.type_struct_info(v.typ) or { panic('unreachable') }
698
699 if init.init_fields.len == 0 && !(ts_info.fields.any(it.has_default_expr)) {
700 // Struct definition contains no default initialisers
701 // AND struct init contains no set values.
702 g.zero_fill(v, size)
703 return
704 }
705
706 for i, f in ts_info.fields {
707 field_to_be_set := init.init_fields.map(it.name).contains(f.name)
708
709 if !field_to_be_set {
710 offset := si.offsets[i]
711 offset_var := g.offset(v, f.typ, offset)
712
713 fsize, _ := g.pool.type_size(f.typ)
714
715 if f.has_default_expr {
716 g.set_with_expr(f.default_expr, offset_var)
717 } else {
718 g.zero_fill(offset_var, fsize)
719 }
720 }
721 }
722
723 for f in init.init_fields {
724 field := ts.find_field(f.name) or {
725 g.w_error('could not find field `${f.name}` on init')
726 }
727
728 offset := si.offsets[field.i]
729 offset_var := g.offset(v, f.expected_type, offset)
730
731 g.set_with_expr(f.expr, offset_var)
732 }
733 }
734 ast.StringLiteral {
735 val := serialise.eval_escape_codes(init) or { panic('unreachable') }
736 str_pos := g.pool.append_string(val)
737
738 if v.typ != ast.string_type {
739 // c'str'
740 g.set_prepare(v)
741 {
742 g.literalint(g.data_base + str_pos, ast.voidptr_type)
743 }
744 g.set_set(v)
745 return
746 }
747
748 // init struct
749 // fields: str, len
750 g.ref(v)
751 g.literalint(g.data_base + str_pos, ast.voidptr_type)
752 g.store_field(ast.string_type, ast.voidptr_type, 'str')
753 g.ref(v)
754 g.literalint(val.len, ast.int_type)
755 g.store_field(ast.string_type, ast.int_type, 'len')
756 }
757 ast.CallExpr {
758 // `set_with_expr` is never called with a multireturn call expression
759 is_pt := g.is_param_type(v.typ)
760
761 g.call_expr(init, v.typ, if is_pt { [v] } else { []Var{} })
762 if !is_pt {
763 g.set(v)
764 }
765 }
766 ast.ArrayInit {
767 if !init.is_fixed {
768 g.v_error('wasm backend does not support non fixed arrays yet', init.pos)
769 }
770
771 elm_typ := init.elem_type
772 elm_size, _ := g.pool.type_size(elm_typ)
773
774 if !init.has_val {
775 arr_size, _ := g.pool.type_size(v.typ)
776
777 g.zero_fill(v, arr_size)
778 return
779 }
780
781 mut voff := g.offset(v, elm_typ, 0) // index zero
782
783 for e in init.exprs {
784 g.set_with_expr(e, voff)
785 voff = g.offset(voff, elm_typ, elm_size)
786 }
787 }
788 else {
789 // impl of set but taken out
790
791 if !v.is_address {
792 g.expr(init, v.typ)
793 if v.is_global {
794 g.func.global_set(v.g_idx)
795 } else {
796 g.func.local_set(v.idx)
797 }
798 return
799 }
800
801 if g.is_pure_type(v.typ) {
802 if v.is_global {
803 g.func.global_get(v.g_idx)
804 } else {
805 g.func.local_get(v.idx)
806 }
807 g.expr(init, v.typ)
808 g.store(v.typ, v.offset)
809 return
810 }
811
812 if var := g.get_var_from_expr(init) {
813 g.mov(v, var)
814 return
815 }
816
817 from := Var{
818 typ: v.typ
819 idx: g.func.new_local_named(.i32_t, '__tmp<voidptr>')
820 is_address: v.is_address // true
821 }
822
823 g.expr(init, v.typ)
824 g.func.local_set(from.idx)
825 g.mov(v, from)
826 }
827 }
828}
829
830pub fn calc_padding(value int, alignment int) int {
831 if alignment == 0 {
832 return value
833 }
834 return (alignment - value % alignment) % alignment
835}
836
837pub fn calc_align(value int, alignment int) int {
838 if alignment == 0 {
839 return value
840 }
841 return (value + alignment - 1) / alignment * alignment
842}
843
844pub fn (mut g Gen) make_vinit() {
845 g.func = g.mod.new_function('_vinit', [], [])
846 func_start := g.func.patch_pos()
847 {
848 for mod_name in g.table.modules {
849 if mod_name == 'v.reflection' {
850 g.w_error('the wasm backend does not implement `v.reflection` yet')
851 }
852 init_fn_name := if mod_name != 'builtin' { '${mod_name}.init' } else { 'init' }
853 if _ := g.table.find_fn(init_fn_name) {
854 g.func.call(init_fn_name)
855 }
856 cleanup_fn_name := if mod_name != 'builtin' { '${mod_name}.cleanup' } else { 'cleanup' }
857 if _ := g.table.find_fn(cleanup_fn_name) {
858 g.func.call(cleanup_fn_name)
859 }
860 }
861 for _, gv in g.global_vars {
862 if init := gv.init {
863 g.set_with_expr(init, gv.v)
864 }
865 }
866 g.bare_function_frame(func_start)
867 }
868 g.mod.commit(g.func, false)
869 g.bare_function_end()
870}
871
872pub fn (mut g Gen) housekeeping() {
873 g.make_vinit()
874
875 heap_base := calc_align(g.data_base + g.pool.buf.len, 16) // 16?
876 page_boundary := calc_align(g.data_base + g.pool.buf.len, 64 * 1024)
877 preallocated_pages := page_boundary / (64 * 1024)
878
879 if g.pref.is_verbose {
880 eprintln('housekeeping(): acceptable addresses are > 1024')
881 eprintln('housekeeping(): stack top: ${g.stack_top}, data_base: ${g.data_base} (size: ${g.pool.buf.len}), heap_base: ${heap_base}')
882 eprintln('housekeeping(): preallocated pages: ${preallocated_pages}')
883 }
884
885 if sp := g.sp_global {
886 g.mod.assign_global_init(sp, wasm.constexpr_value(g.stack_top))
887 }
888 if g.sp_global != none || g.pool.buf.len > 0 {
889 g.mod.assign_memory('memory', true, u32(preallocated_pages), none)
890 if g.pool.buf.len > 0 {
891 mut buf := g.pool.buf.clone()
892
893 for reloc in g.pool.relocs {
894 binary.little_endian_put_u32_at(mut buf, u32(g.data_base + reloc.offset), reloc.pos)
895 }
896 g.mod.new_data_segment(none, g.data_base, buf)
897 }
898 }
899 if hp := g.heap_base {
900 g.mod.assign_global_init(hp, wasm.constexpr_value(heap_base))
901 }
902
903 if g.pref.os == .wasi && !g.pref.is_shared {
904 mut fn_start := g.mod.new_function('_start', [], [])
905 {
906 fn_start.call('_vinit')
907 fn_start.call('main.main')
908 }
909 g.mod.commit(fn_start, true)
910 } else {
911 g.mod.assign_start('_vinit')
912 }
913}
914