1
2
3
4
5 package wasm
6
7 import (
8 "bytes"
9 "cmd/internal/obj"
10 "cmd/internal/objabi"
11 "cmd/internal/sys"
12 "encoding/binary"
13 "fmt"
14 "internal/abi"
15 "io"
16 "math"
17 )
18
19 var Register = map[string]int16{
20 "SP": REG_SP,
21 "CTXT": REG_CTXT,
22 "g": REG_g,
23 "RET0": REG_RET0,
24 "RET1": REG_RET1,
25 "RET2": REG_RET2,
26 "RET3": REG_RET3,
27 "PAUSE": REG_PAUSE,
28
29 "R0": REG_R0,
30 "R1": REG_R1,
31 "R2": REG_R2,
32 "R3": REG_R3,
33 "R4": REG_R4,
34 "R5": REG_R5,
35 "R6": REG_R6,
36 "R7": REG_R7,
37 "R8": REG_R8,
38 "R9": REG_R9,
39 "R10": REG_R10,
40 "R11": REG_R11,
41 "R12": REG_R12,
42 "R13": REG_R13,
43 "R14": REG_R14,
44 "R15": REG_R15,
45
46 "F0": REG_F0,
47 "F1": REG_F1,
48 "F2": REG_F2,
49 "F3": REG_F3,
50 "F4": REG_F4,
51 "F5": REG_F5,
52 "F6": REG_F6,
53 "F7": REG_F7,
54 "F8": REG_F8,
55 "F9": REG_F9,
56 "F10": REG_F10,
57 "F11": REG_F11,
58 "F12": REG_F12,
59 "F13": REG_F13,
60 "F14": REG_F14,
61 "F15": REG_F15,
62
63 "F16": REG_F16,
64 "F17": REG_F17,
65 "F18": REG_F18,
66 "F19": REG_F19,
67 "F20": REG_F20,
68 "F21": REG_F21,
69 "F22": REG_F22,
70 "F23": REG_F23,
71 "F24": REG_F24,
72 "F25": REG_F25,
73 "F26": REG_F26,
74 "F27": REG_F27,
75 "F28": REG_F28,
76 "F29": REG_F29,
77 "F30": REG_F30,
78 "F31": REG_F31,
79
80 "PC_B": REG_PC_B,
81 }
82
83 var registerNames []string
84
85 func init() {
86 obj.RegisterRegister(MINREG, MAXREG, rconv)
87 obj.RegisterOpcode(obj.ABaseWasm, Anames)
88
89 registerNames = make([]string, MAXREG-MINREG)
90 for name, reg := range Register {
91 registerNames[reg-MINREG] = name
92 }
93 }
94
95 func rconv(r int) string {
96 return registerNames[r-MINREG]
97 }
98
99 var unaryDst = map[obj.As]bool{
100 ASet: true,
101 ATee: true,
102 ACall: true,
103 ACallIndirect: true,
104 ABr: true,
105 ABrIf: true,
106 ABrTable: true,
107 AI32Store: true,
108 AI64Store: true,
109 AF32Store: true,
110 AF64Store: true,
111 AI32Store8: true,
112 AI32Store16: true,
113 AI64Store8: true,
114 AI64Store16: true,
115 AI64Store32: true,
116 ACALLNORESUME: true,
117 }
118
119 var Linkwasm = obj.LinkArch{
120 Arch: sys.ArchWasm,
121 Init: instinit,
122 Preprocess: preprocess,
123 Assemble: assemble,
124 UnaryDst: unaryDst,
125 }
126
127 var (
128 morestack *obj.LSym
129 morestackNoCtxt *obj.LSym
130 sigpanic *obj.LSym
131 wasm_pc_f_loop_export *obj.LSym
132 runtimeNotInitialized *obj.LSym
133 )
134
135 const (
136
137 WasmImport = 1 << 0
138 )
139
140 const (
141
142
143
144
145 GojsModule = "gojs"
146 )
147
148 func instinit(ctxt *obj.Link) {
149 morestack = ctxt.Lookup("runtime.morestack")
150 morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
151 sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
152 wasm_pc_f_loop_export = ctxt.Lookup("wasm_pc_f_loop_export")
153 runtimeNotInitialized = ctxt.Lookup("runtime.notInitialized")
154 }
155
156 func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
157 appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog {
158 if p.As != obj.ANOP {
159 p2 := obj.Appendp(p, newprog)
160 p2.Pc = p.Pc
161 p = p2
162 }
163 p.As = as
164 switch len(args) {
165 case 0:
166 p.From = obj.Addr{}
167 p.To = obj.Addr{}
168 case 1:
169 if unaryDst[as] {
170 p.From = obj.Addr{}
171 p.To = args[0]
172 } else {
173 p.From = args[0]
174 p.To = obj.Addr{}
175 }
176 case 2:
177 p.From = args[0]
178 p.To = args[1]
179 default:
180 panic("bad args")
181 }
182 return p
183 }
184
185 framesize := s.Func().Text.To.Offset
186 if framesize < 0 {
187 panic("bad framesize")
188 }
189 s.Func().Args = s.Func().Text.To.Val.(int32)
190 s.Func().Locals = int32(framesize)
191
192
193
194
195 if s.Func().WasmImport != nil {
196 genWasmImportWrapper(s, appendp)
197
198
199
200
201
202 framesize = 0
203 } else if s.Func().WasmExport != nil {
204 genWasmExportWrapper(s, appendp)
205 } else if s.Func().Text.From.Sym.Wrapper() {
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 gpanic := obj.Addr{
227 Type: obj.TYPE_MEM,
228 Reg: REGG,
229 Offset: 4 * 8,
230 }
231
232 panicargp := obj.Addr{
233 Type: obj.TYPE_MEM,
234 Reg: REG_R0,
235 Offset: 0,
236 }
237
238 p := s.Func().Text
239 p = appendp(p, AMOVD, gpanic, regAddr(REG_R0))
240
241 p = appendp(p, AGet, regAddr(REG_R0))
242 p = appendp(p, AI64Eqz)
243 p = appendp(p, ANot)
244 p = appendp(p, AIf)
245
246 p = appendp(p, AGet, regAddr(REG_SP))
247 p = appendp(p, AI64ExtendI32U)
248 p = appendp(p, AI64Const, constAddr(framesize+8))
249 p = appendp(p, AI64Add)
250 p = appendp(p, AI64Load, panicargp)
251
252 p = appendp(p, AI64Eq)
253 p = appendp(p, AIf)
254 p = appendp(p, AMOVD, regAddr(REG_SP), panicargp)
255 p = appendp(p, AEnd)
256
257 p = appendp(p, AEnd)
258 }
259
260 if framesize > 0 && s.Func().WasmExport == nil {
261 p := s.Func().Text
262 p = appendp(p, AGet, regAddr(REG_SP))
263 p = appendp(p, AI32Const, constAddr(framesize))
264 p = appendp(p, AI32Sub)
265 p = appendp(p, ASet, regAddr(REG_SP))
266 p.Spadj = int32(framesize)
267 }
268
269
270
271 needMoreStack := framesize > 0 && !s.Func().Text.From.Sym.NoSplit()
272
273
274
275
276
277 var pMorestack = s.Func().Text
278 if needMoreStack && ctxt.Flag_maymorestack != "" {
279 p := pMorestack
280
281
282 const tempFrame = 8
283 p = appendp(p, AGet, regAddr(REG_SP))
284 p = appendp(p, AI32Const, constAddr(tempFrame))
285 p = appendp(p, AI32Sub)
286 p = appendp(p, ASet, regAddr(REG_SP))
287 p.Spadj = tempFrame
288 ctxtp := obj.Addr{
289 Type: obj.TYPE_MEM,
290 Reg: REG_SP,
291 Offset: 0,
292 }
293 p = appendp(p, AMOVD, regAddr(REGCTXT), ctxtp)
294
295
296
297
298 p = appendp(p, ACALLNORESUME, constAddr(0))
299
300 sym := ctxt.LookupABI(ctxt.Flag_maymorestack, s.ABI())
301 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
302
303
304 p = appendp(p, AMOVD, ctxtp, regAddr(REGCTXT))
305 p = appendp(p, AGet, regAddr(REG_SP))
306 p = appendp(p, AI32Const, constAddr(tempFrame))
307 p = appendp(p, AI32Add)
308 p = appendp(p, ASet, regAddr(REG_SP))
309 p.Spadj = -tempFrame
310
311
312
313 pMorestack = appendp(p, ARESUMEPOINT)
314 }
315
316
317
318 numResumePoints := 0
319 explicitBlockDepth := 0
320 pc := int64(0)
321 var tableIdxs []uint64
322 tablePC := int64(0)
323 base := ctxt.PosTable.Pos(s.Func().Text.Pos).Base()
324 for p := s.Func().Text; p != nil; p = p.Link {
325 prevBase := base
326 base = ctxt.PosTable.Pos(p.Pos).Base()
327 switch p.As {
328 case ABlock, ALoop, AIf:
329 explicitBlockDepth++
330
331 case AEnd:
332 if explicitBlockDepth == 0 {
333 panic("End without block")
334 }
335 explicitBlockDepth--
336
337 case ARESUMEPOINT:
338 if explicitBlockDepth != 0 {
339 panic("RESUME can only be used on toplevel")
340 }
341 p.As = AEnd
342 for tablePC <= pc {
343 tableIdxs = append(tableIdxs, uint64(numResumePoints))
344 tablePC++
345 }
346 numResumePoints++
347 pc++
348
349 case obj.ACALL:
350 if explicitBlockDepth != 0 {
351 panic("CALL can only be used on toplevel, try CALLNORESUME instead")
352 }
353 appendp(p, ARESUMEPOINT)
354 }
355
356 p.Pc = pc
357
358
359
360
361
362 if p.As == ACALLNORESUME || p.As == obj.ANOP || p.As == ANop || p.Spadj != 0 || base != prevBase {
363 pc++
364 if p.To.Sym == sigpanic {
365
366
367
368
369 pc++
370 }
371 }
372 }
373 tableIdxs = append(tableIdxs, uint64(numResumePoints))
374 s.Size = pc + 1
375 if pc >= 1<<16 {
376 ctxt.Diag("function too big: %s exceeds 65536 blocks", s)
377 }
378
379 if needMoreStack {
380 p := pMorestack
381
382 if framesize <= abi.StackSmall {
383
384
385
386
387
388
389
390 p = appendp(p, AGet, regAddr(REG_SP))
391 p = appendp(p, AGet, regAddr(REGG))
392 p = appendp(p, AI32WrapI64)
393 p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize)))
394 p = appendp(p, AI32LeU)
395 } else {
396
397
398
399
400
401
402
403
404
405
406 p = appendp(p, AGet, regAddr(REG_SP))
407 p = appendp(p, AGet, regAddr(REGG))
408 p = appendp(p, AI32WrapI64)
409 p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize)))
410 p = appendp(p, AI32Const, constAddr(framesize-abi.StackSmall))
411 p = appendp(p, AI32Add)
412 p = appendp(p, AI32LeU)
413 }
414
415
416 p = appendp(p, AIf)
417
418
419
420
421
422
423
424 p = appendp(p, obj.ACALL, constAddr(0))
425 if s.Func().Text.From.Sym.NeedCtxt() {
426 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
427 } else {
428 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt}
429 }
430 p = appendp(p, AEnd)
431 }
432
433
434
435 var entryPointLoopBranches []*obj.Prog
436 var unwindExitBranches []*obj.Prog
437 currentDepth := 0
438 for p := s.Func().Text; p != nil; p = p.Link {
439 switch p.As {
440 case ABlock, ALoop, AIf:
441 currentDepth++
442 case AEnd:
443 currentDepth--
444 }
445
446 switch p.As {
447 case obj.AJMP:
448 jmp := *p
449 p.As = obj.ANOP
450
451 if jmp.To.Type == obj.TYPE_BRANCH {
452
453 p = appendp(p, AI32Const, constAddr(jmp.To.Val.(*obj.Prog).Pc))
454 p = appendp(p, ASet, regAddr(REG_PC_B))
455 p = appendp(p, ABr)
456 entryPointLoopBranches = append(entryPointLoopBranches, p)
457 break
458 }
459
460
461 switch jmp.To.Type {
462 case obj.TYPE_MEM:
463 if !notUsePC_B[jmp.To.Sym.Name] {
464
465 p = appendp(p, AI32Const, constAddr(0))
466 }
467 p = appendp(p, ACall, jmp.To)
468
469 case obj.TYPE_NONE:
470
471 p = appendp(p, AI64Const, constAddr(16))
472 p = appendp(p, AI64ShrU)
473 p = appendp(p, AI32WrapI64)
474
475
476
477
478
479 p = appendp(p, ASet, regAddr(REG_PC_B))
480 p = appendp(p, AI32Const, constAddr(0))
481 p = appendp(p, AGet, regAddr(REG_PC_B))
482
483 p = appendp(p, ACallIndirect)
484
485 default:
486 panic("bad target for JMP")
487 }
488
489 p = appendp(p, AReturn)
490
491 case obj.ACALL, ACALLNORESUME:
492 call := *p
493 p.As = obj.ANOP
494
495 pcAfterCall := call.Link.Pc
496 if call.To.Sym == sigpanic {
497 pcAfterCall--
498 }
499
500
501 p = appendp(p, AGet, regAddr(REG_SP))
502 p = appendp(p, AI32Const, constAddr(8))
503 p = appendp(p, AI32Sub)
504 p = appendp(p, ASet, regAddr(REG_SP))
505
506
507 p = appendp(p, AGet, regAddr(REG_SP))
508 p = appendp(p, AI64Const, obj.Addr{
509 Type: obj.TYPE_ADDR,
510 Name: obj.NAME_EXTERN,
511 Sym: s,
512 Offset: pcAfterCall,
513 })
514 p = appendp(p, AI64Store, constAddr(0))
515
516
517 switch call.To.Type {
518 case obj.TYPE_MEM:
519 if !notUsePC_B[call.To.Sym.Name] {
520
521 p = appendp(p, AI32Const, constAddr(0))
522 }
523 p = appendp(p, ACall, call.To)
524
525 case obj.TYPE_NONE:
526
527 p = appendp(p, AI64Const, constAddr(16))
528 p = appendp(p, AI64ShrU)
529 p = appendp(p, AI32WrapI64)
530
531
532
533
534
535 p = appendp(p, ASet, regAddr(REG_PC_B))
536 p = appendp(p, AI32Const, constAddr(0))
537 p = appendp(p, AGet, regAddr(REG_PC_B))
538
539 p = appendp(p, ACallIndirect)
540
541 default:
542 panic("bad target for CALL")
543 }
544
545
546 if call.As == ACALLNORESUME && call.To.Sym != sigpanic {
547
548 p = appendp(p, AIf)
549 p = appendp(p, obj.AUNDEF)
550 p = appendp(p, AEnd)
551 } else {
552
553 p = appendp(p, ABrIf)
554 unwindExitBranches = append(unwindExitBranches, p)
555 }
556
557 case obj.ARET, ARETUNWIND:
558 ret := *p
559 p.As = obj.ANOP
560
561 if framesize > 0 {
562
563 p = appendp(p, AGet, regAddr(REG_SP))
564 p = appendp(p, AI32Const, constAddr(framesize))
565 p = appendp(p, AI32Add)
566 p = appendp(p, ASet, regAddr(REG_SP))
567
568
569 }
570
571 if ret.To.Type == obj.TYPE_MEM {
572
573 p = appendp(p, AI32Const, constAddr(0))
574
575
576 p = appendp(p, ACall, ret.To)
577 p = appendp(p, AReturn)
578 break
579 }
580
581
582 p = appendp(p, AGet, regAddr(REG_SP))
583 p = appendp(p, AI32Const, constAddr(8))
584 p = appendp(p, AI32Add)
585 p = appendp(p, ASet, regAddr(REG_SP))
586
587 if ret.As == ARETUNWIND {
588
589 p = appendp(p, AI32Const, constAddr(1))
590 p = appendp(p, AReturn)
591 break
592 }
593
594
595 p = appendp(p, AI32Const, constAddr(0))
596 p = appendp(p, AReturn)
597 }
598 }
599
600 for p := s.Func().Text; p != nil; p = p.Link {
601 switch p.From.Name {
602 case obj.NAME_AUTO:
603 p.From.Offset += framesize
604 case obj.NAME_PARAM:
605 p.From.Reg = REG_SP
606 p.From.Offset += framesize + 8
607 }
608
609 switch p.To.Name {
610 case obj.NAME_AUTO:
611 p.To.Offset += framesize
612 case obj.NAME_PARAM:
613 p.To.Reg = REG_SP
614 p.To.Offset += framesize + 8
615 }
616
617 switch p.As {
618 case AGet:
619 if p.From.Type == obj.TYPE_ADDR {
620 get := *p
621 p.As = obj.ANOP
622
623 switch get.From.Name {
624 case obj.NAME_EXTERN:
625 p = appendp(p, AI64Const, get.From)
626 case obj.NAME_AUTO, obj.NAME_PARAM:
627 p = appendp(p, AGet, regAddr(get.From.Reg))
628 if get.From.Reg == REG_SP {
629 p = appendp(p, AI64ExtendI32U)
630 }
631 if get.From.Offset != 0 {
632 p = appendp(p, AI64Const, constAddr(get.From.Offset))
633 p = appendp(p, AI64Add)
634 }
635 default:
636 panic("bad Get: invalid name")
637 }
638 }
639
640 case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
641 if p.From.Type == obj.TYPE_MEM {
642 as := p.As
643 from := p.From
644
645 p.As = AGet
646 p.From = regAddr(from.Reg)
647
648 if from.Reg != REG_SP {
649 p = appendp(p, AI32WrapI64)
650 }
651
652 p = appendp(p, as, constAddr(from.Offset))
653 }
654
655 case AMOVB, AMOVH, AMOVW, AMOVD:
656 mov := *p
657 p.As = obj.ANOP
658
659 var loadAs obj.As
660 var storeAs obj.As
661 switch mov.As {
662 case AMOVB:
663 loadAs = AI64Load8U
664 storeAs = AI64Store8
665 case AMOVH:
666 loadAs = AI64Load16U
667 storeAs = AI64Store16
668 case AMOVW:
669 loadAs = AI64Load32U
670 storeAs = AI64Store32
671 case AMOVD:
672 loadAs = AI64Load
673 storeAs = AI64Store
674 }
675
676 appendValue := func() {
677 switch mov.From.Type {
678 case obj.TYPE_CONST:
679 p = appendp(p, AI64Const, constAddr(mov.From.Offset))
680
681 case obj.TYPE_ADDR:
682 switch mov.From.Name {
683 case obj.NAME_NONE, obj.NAME_PARAM, obj.NAME_AUTO:
684 p = appendp(p, AGet, regAddr(mov.From.Reg))
685 if mov.From.Reg == REG_SP {
686 p = appendp(p, AI64ExtendI32U)
687 }
688 p = appendp(p, AI64Const, constAddr(mov.From.Offset))
689 p = appendp(p, AI64Add)
690 case obj.NAME_EXTERN:
691 p = appendp(p, AI64Const, mov.From)
692 default:
693 panic("bad name for MOV")
694 }
695
696 case obj.TYPE_REG:
697 p = appendp(p, AGet, mov.From)
698 if mov.From.Reg == REG_SP {
699 p = appendp(p, AI64ExtendI32U)
700 }
701
702 case obj.TYPE_MEM:
703 p = appendp(p, AGet, regAddr(mov.From.Reg))
704 if mov.From.Reg != REG_SP {
705 p = appendp(p, AI32WrapI64)
706 }
707 p = appendp(p, loadAs, constAddr(mov.From.Offset))
708
709 default:
710 panic("bad MOV type")
711 }
712 }
713
714 switch mov.To.Type {
715 case obj.TYPE_REG:
716 appendValue()
717 if mov.To.Reg == REG_SP {
718 p = appendp(p, AI32WrapI64)
719 }
720 p = appendp(p, ASet, mov.To)
721
722 case obj.TYPE_MEM:
723 switch mov.To.Name {
724 case obj.NAME_NONE, obj.NAME_PARAM:
725 p = appendp(p, AGet, regAddr(mov.To.Reg))
726 if mov.To.Reg != REG_SP {
727 p = appendp(p, AI32WrapI64)
728 }
729 case obj.NAME_EXTERN:
730 p = appendp(p, AI32Const, obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: mov.To.Sym})
731 default:
732 panic("bad MOV name")
733 }
734 appendValue()
735 p = appendp(p, storeAs, constAddr(mov.To.Offset))
736
737 default:
738 panic("bad MOV type")
739 }
740 }
741 }
742
743 {
744 p := s.Func().Text
745 if len(unwindExitBranches) > 0 {
746 p = appendp(p, ABlock)
747 for _, b := range unwindExitBranches {
748 b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
749 }
750 }
751 if len(entryPointLoopBranches) > 0 {
752 p = appendp(p, ALoop)
753 for _, b := range entryPointLoopBranches {
754 b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
755 }
756 }
757 if numResumePoints > 0 {
758
759 for i := 0; i < numResumePoints+1; i++ {
760 p = appendp(p, ABlock)
761 }
762 p = appendp(p, AGet, regAddr(REG_PC_B))
763 p = appendp(p, ABrTable, obj.Addr{Val: tableIdxs})
764 p = appendp(p, AEnd)
765 }
766 for p.Link != nil {
767 p = p.Link
768 }
769 if len(entryPointLoopBranches) > 0 {
770 p = appendp(p, AEnd)
771 }
772 p = appendp(p, obj.AUNDEF)
773 if len(unwindExitBranches) > 0 {
774 p = appendp(p, AEnd)
775 p = appendp(p, AI32Const, constAddr(1))
776 }
777 }
778
779 currentDepth = 0
780 blockDepths := make(map[*obj.Prog]int)
781 for p := s.Func().Text; p != nil; p = p.Link {
782 switch p.As {
783 case ABlock, ALoop, AIf:
784 currentDepth++
785 blockDepths[p] = currentDepth
786 case AEnd:
787 currentDepth--
788 }
789
790 switch p.As {
791 case ABr, ABrIf:
792 if p.To.Type == obj.TYPE_BRANCH {
793 blockDepth, ok := blockDepths[p.To.Val.(*obj.Prog)]
794 if !ok {
795 panic("label not at block")
796 }
797 p.To = constAddr(int64(currentDepth - blockDepth))
798 }
799 }
800 }
801 }
802
803
804 func genWasmImportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog) {
805 wi := s.Func().WasmImport
806 wi.CreateAuxSym()
807 p := s.Func().Text
808 if p.Link != nil {
809 panic("wrapper functions for WASM imports should not have a body")
810 }
811 to := obj.Addr{
812 Type: obj.TYPE_MEM,
813 Name: obj.NAME_EXTERN,
814 Sym: s,
815 }
816
817
818
819
820
821 if wi.Module == GojsModule {
822
823
824
825 p = appendp(p, AGet, regAddr(REG_SP))
826 p = appendp(p, ACall, to)
827
828 p.Mark = WasmImport
829 } else {
830 if len(wi.Results) > 1 {
831
832
833 panic("invalid results type")
834 }
835 for _, f := range wi.Params {
836
837
838
839 p = appendp(p, AGet, regAddr(REG_SP))
840
841
842
843
844
845
846
847 loadOffset := f.Offset + 8
848
849
850
851 switch f.Type {
852 case obj.WasmI32:
853 p = appendp(p, AI32Load, constAddr(loadOffset))
854 case obj.WasmI64:
855 p = appendp(p, AI64Load, constAddr(loadOffset))
856 case obj.WasmF32:
857 p = appendp(p, AF32Load, constAddr(loadOffset))
858 case obj.WasmF64:
859 p = appendp(p, AF64Load, constAddr(loadOffset))
860 case obj.WasmPtr:
861 p = appendp(p, AI32Load, constAddr(loadOffset))
862 case obj.WasmBool:
863 p = appendp(p, AI32Load8U, constAddr(loadOffset))
864 default:
865 panic("bad param type")
866 }
867 }
868
869
870
871
872 p = appendp(p, ACall, to)
873 p.Mark = WasmImport
874
875 if len(wi.Results) == 1 {
876 f := wi.Results[0]
877
878
879
880 storeOffset := f.Offset + 8
881
882
883
884
885
886
887 switch f.Type {
888 case obj.WasmI32:
889 p = appendp(p, AI64ExtendI32U)
890 p = appendp(p, ASet, regAddr(REG_R0))
891 p = appendp(p, AGet, regAddr(REG_SP))
892 p = appendp(p, AGet, regAddr(REG_R0))
893 p = appendp(p, AI64Store32, constAddr(storeOffset))
894 case obj.WasmI64:
895 p = appendp(p, ASet, regAddr(REG_R0))
896 p = appendp(p, AGet, regAddr(REG_SP))
897 p = appendp(p, AGet, regAddr(REG_R0))
898 p = appendp(p, AI64Store, constAddr(storeOffset))
899 case obj.WasmF32:
900 p = appendp(p, ASet, regAddr(REG_F0))
901 p = appendp(p, AGet, regAddr(REG_SP))
902 p = appendp(p, AGet, regAddr(REG_F0))
903 p = appendp(p, AF32Store, constAddr(storeOffset))
904 case obj.WasmF64:
905 p = appendp(p, ASet, regAddr(REG_F16))
906 p = appendp(p, AGet, regAddr(REG_SP))
907 p = appendp(p, AGet, regAddr(REG_F16))
908 p = appendp(p, AF64Store, constAddr(storeOffset))
909 case obj.WasmPtr:
910 p = appendp(p, AI64ExtendI32U)
911 p = appendp(p, ASet, regAddr(REG_R0))
912 p = appendp(p, AGet, regAddr(REG_SP))
913 p = appendp(p, AGet, regAddr(REG_R0))
914 p = appendp(p, AI64Store, constAddr(storeOffset))
915 case obj.WasmBool:
916 p = appendp(p, AI64ExtendI32U)
917 p = appendp(p, ASet, regAddr(REG_R0))
918 p = appendp(p, AGet, regAddr(REG_SP))
919 p = appendp(p, AGet, regAddr(REG_R0))
920 p = appendp(p, AI64Store8, constAddr(storeOffset))
921 default:
922 panic("bad result type")
923 }
924 }
925 }
926
927 p = appendp(p, obj.ARET)
928 }
929
930
931 func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog) {
932 we := s.Func().WasmExport
933 we.CreateAuxSym()
934 p := s.Func().Text
935 framesize := p.To.Offset
936 for p.Link != nil && p.Link.As == obj.AFUNCDATA {
937 p = p.Link
938 }
939 if p.Link != nil {
940 panic("wrapper functions for WASM export should not have a body")
941 }
942
943
944
945 p = appendp(p, AGet, regAddr(REG_SP))
946 p = appendp(p, AI32Eqz)
947 p = appendp(p, AIf)
948 p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: runtimeNotInitialized})
949 p = appendp(p, AEnd)
950
951
952 if framesize > 0 {
953 p = appendp(p, AGet, regAddr(REG_SP))
954 p = appendp(p, AI32Const, constAddr(framesize))
955 p = appendp(p, AI32Sub)
956 p = appendp(p, ASet, regAddr(REG_SP))
957 p.Spadj = int32(framesize)
958 }
959
960
961 for i, f := range we.Params {
962 p = appendp(p, AGet, regAddr(REG_SP))
963 p = appendp(p, AGet, regAddr(REG_R0+int16(i)))
964 switch f.Type {
965 case obj.WasmI32:
966 p = appendp(p, AI32Store, constAddr(f.Offset))
967 case obj.WasmI64:
968 p = appendp(p, AI64Store, constAddr(f.Offset))
969 case obj.WasmF32:
970 p = appendp(p, AF32Store, constAddr(f.Offset))
971 case obj.WasmF64:
972 p = appendp(p, AF64Store, constAddr(f.Offset))
973 case obj.WasmPtr:
974 p = appendp(p, AI64ExtendI32U)
975 p = appendp(p, AI64Store, constAddr(f.Offset))
976 case obj.WasmBool:
977 p = appendp(p, AI32Store8, constAddr(f.Offset))
978 default:
979 panic("bad param type")
980 }
981 }
982
983
984
985
986
987 p = appendp(p, AGet, regAddr(REG_SP))
988 p = appendp(p, AI32Const, constAddr(8))
989 p = appendp(p, AI32Sub)
990 p = appendp(p, ASet, regAddr(REG_SP))
991
992 p = appendp(p, AGet, regAddr(REG_SP))
993 retAddr := obj.Addr{
994 Type: obj.TYPE_ADDR,
995 Name: obj.NAME_EXTERN,
996 Sym: s,
997 Offset: 1,
998 }
999 if framesize == 0 {
1000
1001 retAddr.Offset = 0
1002 }
1003 p = appendp(p, AI64Const, retAddr)
1004 p = appendp(p, AI64Store, constAddr(0))
1005
1006 p = appendp(p, AI32Const, constAddr(0))
1007 p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: we.WrappedSym})
1008
1009
1010
1011 p = appendp(p, AIf)
1012 p = appendp(p, AI64Const, retAddr)
1013 p = appendp(p, AI64Const, constAddr(16))
1014 p = appendp(p, AI64ShrU)
1015 p = appendp(p, AI32WrapI64)
1016 p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: wasm_pc_f_loop_export})
1017 p = appendp(p, AEnd)
1018
1019
1020 if len(we.Results) > 1 {
1021 panic("invalid results type")
1022 } else if len(we.Results) == 1 {
1023 p = appendp(p, AGet, regAddr(REG_SP))
1024 f := we.Results[0]
1025 switch f.Type {
1026 case obj.WasmI32:
1027 p = appendp(p, AI32Load, constAddr(f.Offset))
1028 case obj.WasmI64:
1029 p = appendp(p, AI64Load, constAddr(f.Offset))
1030 case obj.WasmF32:
1031 p = appendp(p, AF32Load, constAddr(f.Offset))
1032 case obj.WasmF64:
1033 p = appendp(p, AF64Load, constAddr(f.Offset))
1034 case obj.WasmPtr:
1035 p = appendp(p, AI32Load, constAddr(f.Offset))
1036 case obj.WasmBool:
1037 p = appendp(p, AI32Load8U, constAddr(f.Offset))
1038 default:
1039 panic("bad result type")
1040 }
1041 }
1042
1043
1044 if framesize > 0 {
1045
1046 p = appendp(p, AGet, regAddr(REG_SP))
1047 p = appendp(p, AI32Const, constAddr(framesize))
1048 p = appendp(p, AI32Add)
1049 p = appendp(p, ASet, regAddr(REG_SP))
1050 }
1051 p = appendp(p, AReturn)
1052 }
1053
1054 func constAddr(value int64) obj.Addr {
1055 return obj.Addr{Type: obj.TYPE_CONST, Offset: value}
1056 }
1057
1058 func regAddr(reg int16) obj.Addr {
1059 return obj.Addr{Type: obj.TYPE_REG, Reg: reg}
1060 }
1061
1062
1063
1064 var notUsePC_B = map[string]bool{
1065 "_rt0_wasm_js": true,
1066 "_rt0_wasm_wasip1": true,
1067 "_rt0_wasm_wasip1_lib": true,
1068 "wasm_export_run": true,
1069 "wasm_export_resume": true,
1070 "wasm_export_getsp": true,
1071 "wasm_pc_f_loop": true,
1072 "wasm_pc_f_loop_export": true,
1073 "gcWriteBarrier": true,
1074 "runtime.gcWriteBarrier1": true,
1075 "runtime.gcWriteBarrier2": true,
1076 "runtime.gcWriteBarrier3": true,
1077 "runtime.gcWriteBarrier4": true,
1078 "runtime.gcWriteBarrier5": true,
1079 "runtime.gcWriteBarrier6": true,
1080 "runtime.gcWriteBarrier7": true,
1081 "runtime.gcWriteBarrier8": true,
1082 "runtime.notInitialized": true,
1083 "runtime.wasmDiv": true,
1084 "runtime.wasmTruncS": true,
1085 "runtime.wasmTruncU": true,
1086 "cmpbody": true,
1087 "memeqbody": true,
1088 "memcmp": true,
1089 "memchr": true,
1090 }
1091
1092 func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
1093 type regVar struct {
1094 global bool
1095 index uint64
1096 }
1097
1098 type varDecl struct {
1099 count uint64
1100 typ valueType
1101 }
1102
1103 hasLocalSP := false
1104 regVars := [MAXREG - MINREG]*regVar{
1105 REG_SP - MINREG: {true, 0},
1106 REG_CTXT - MINREG: {true, 1},
1107 REG_g - MINREG: {true, 2},
1108 REG_RET0 - MINREG: {true, 3},
1109 REG_RET1 - MINREG: {true, 4},
1110 REG_RET2 - MINREG: {true, 5},
1111 REG_RET3 - MINREG: {true, 6},
1112 REG_PAUSE - MINREG: {true, 7},
1113 }
1114 var varDecls []*varDecl
1115 useAssemblyRegMap := func() {
1116 for i := int16(0); i < 16; i++ {
1117 regVars[REG_R0+i-MINREG] = ®Var{false, uint64(i)}
1118 }
1119 }
1120
1121
1122
1123 switch s.Name {
1124 case "_rt0_wasm_js", "_rt0_wasm_wasip1", "_rt0_wasm_wasip1_lib",
1125 "wasm_export_run", "wasm_export_resume", "wasm_export_getsp",
1126 "wasm_pc_f_loop", "runtime.wasmDiv", "runtime.wasmTruncS", "runtime.wasmTruncU", "memeqbody":
1127 varDecls = []*varDecl{}
1128 useAssemblyRegMap()
1129 case "wasm_pc_f_loop_export":
1130 varDecls = []*varDecl{{count: 2, typ: i32}}
1131 useAssemblyRegMap()
1132 case "memchr", "memcmp":
1133 varDecls = []*varDecl{{count: 2, typ: i32}}
1134 useAssemblyRegMap()
1135 case "cmpbody":
1136 varDecls = []*varDecl{{count: 2, typ: i64}}
1137 useAssemblyRegMap()
1138 case "gcWriteBarrier":
1139 varDecls = []*varDecl{{count: 5, typ: i64}}
1140 useAssemblyRegMap()
1141 case "runtime.gcWriteBarrier1",
1142 "runtime.gcWriteBarrier2",
1143 "runtime.gcWriteBarrier3",
1144 "runtime.gcWriteBarrier4",
1145 "runtime.gcWriteBarrier5",
1146 "runtime.gcWriteBarrier6",
1147 "runtime.gcWriteBarrier7",
1148 "runtime.gcWriteBarrier8",
1149 "runtime.notInitialized":
1150
1151 useAssemblyRegMap()
1152 default:
1153 if s.Func().WasmExport != nil {
1154
1155 useAssemblyRegMap()
1156 break
1157 }
1158
1159
1160 regVars[REG_PC_B-MINREG] = ®Var{false, 0}
1161 hasLocalSP = true
1162
1163 var regUsed [MAXREG - MINREG]bool
1164 for p := s.Func().Text; p != nil; p = p.Link {
1165 if p.From.Reg != 0 {
1166 regUsed[p.From.Reg-MINREG] = true
1167 }
1168 if p.To.Reg != 0 {
1169 regUsed[p.To.Reg-MINREG] = true
1170 }
1171 }
1172
1173 regs := []int16{REG_SP}
1174 for reg := int16(REG_R0); reg <= REG_F31; reg++ {
1175 if regUsed[reg-MINREG] {
1176 regs = append(regs, reg)
1177 }
1178 }
1179
1180 var lastDecl *varDecl
1181 for i, reg := range regs {
1182 t := regType(reg)
1183 if lastDecl == nil || lastDecl.typ != t {
1184 lastDecl = &varDecl{
1185 count: 0,
1186 typ: t,
1187 }
1188 varDecls = append(varDecls, lastDecl)
1189 }
1190 lastDecl.count++
1191 if reg != REG_SP {
1192 regVars[reg-MINREG] = ®Var{false, 1 + uint64(i)}
1193 }
1194 }
1195 }
1196
1197 w := new(bytes.Buffer)
1198
1199 writeUleb128(w, uint64(len(varDecls)))
1200 for _, decl := range varDecls {
1201 writeUleb128(w, decl.count)
1202 w.WriteByte(byte(decl.typ))
1203 }
1204
1205 if hasLocalSP {
1206
1207 updateLocalSP(w)
1208 }
1209
1210 for p := s.Func().Text; p != nil; p = p.Link {
1211 switch p.As {
1212 case AGet:
1213 if p.From.Type != obj.TYPE_REG {
1214 panic("bad Get: argument is not a register")
1215 }
1216 reg := p.From.Reg
1217 v := regVars[reg-MINREG]
1218 if v == nil {
1219 panic("bad Get: invalid register")
1220 }
1221 if reg == REG_SP && hasLocalSP {
1222 writeOpcode(w, ALocalGet)
1223 writeUleb128(w, 1)
1224 continue
1225 }
1226 if v.global {
1227 writeOpcode(w, AGlobalGet)
1228 } else {
1229 writeOpcode(w, ALocalGet)
1230 }
1231 writeUleb128(w, v.index)
1232 continue
1233
1234 case ASet:
1235 if p.To.Type != obj.TYPE_REG {
1236 panic("bad Set: argument is not a register")
1237 }
1238 reg := p.To.Reg
1239 v := regVars[reg-MINREG]
1240 if v == nil {
1241 panic("bad Set: invalid register")
1242 }
1243 if reg == REG_SP && hasLocalSP {
1244 writeOpcode(w, ALocalTee)
1245 writeUleb128(w, 1)
1246 }
1247 if v.global {
1248 writeOpcode(w, AGlobalSet)
1249 } else {
1250 if p.Link.As == AGet && p.Link.From.Reg == reg {
1251 writeOpcode(w, ALocalTee)
1252 p = p.Link
1253 } else {
1254 writeOpcode(w, ALocalSet)
1255 }
1256 }
1257 writeUleb128(w, v.index)
1258 continue
1259
1260 case ATee:
1261 if p.To.Type != obj.TYPE_REG {
1262 panic("bad Tee: argument is not a register")
1263 }
1264 reg := p.To.Reg
1265 v := regVars[reg-MINREG]
1266 if v == nil {
1267 panic("bad Tee: invalid register")
1268 }
1269 writeOpcode(w, ALocalTee)
1270 writeUleb128(w, v.index)
1271 continue
1272
1273 case ANot:
1274 writeOpcode(w, AI32Eqz)
1275 continue
1276
1277 case obj.AUNDEF:
1278 writeOpcode(w, AUnreachable)
1279 continue
1280
1281 case obj.ANOP, obj.ATEXT, obj.AFUNCDATA, obj.APCDATA:
1282
1283 continue
1284 }
1285
1286 writeOpcode(w, p.As)
1287
1288 switch p.As {
1289 case ABlock, ALoop, AIf:
1290 if p.From.Offset != 0 {
1291
1292 w.WriteByte(0x80 - byte(p.From.Offset))
1293 continue
1294 }
1295 w.WriteByte(0x40)
1296
1297 case ABr, ABrIf:
1298 if p.To.Type != obj.TYPE_CONST {
1299 panic("bad Br/BrIf")
1300 }
1301 writeUleb128(w, uint64(p.To.Offset))
1302
1303 case ABrTable:
1304 idxs := p.To.Val.([]uint64)
1305 writeUleb128(w, uint64(len(idxs)-1))
1306 for _, idx := range idxs {
1307 writeUleb128(w, idx)
1308 }
1309
1310 case ACall:
1311 switch p.To.Type {
1312 case obj.TYPE_CONST:
1313 writeUleb128(w, uint64(p.To.Offset))
1314
1315 case obj.TYPE_MEM:
1316 if p.To.Name != obj.NAME_EXTERN && p.To.Name != obj.NAME_STATIC {
1317 fmt.Println(p.To)
1318 panic("bad name for Call")
1319 }
1320 typ := objabi.R_CALL
1321 if p.Mark&WasmImport != 0 {
1322 typ = objabi.R_WASMIMPORT
1323 }
1324 s.AddRel(ctxt, obj.Reloc{
1325 Type: typ,
1326 Off: int32(w.Len()),
1327 Siz: 1,
1328 Sym: p.To.Sym,
1329 })
1330 if hasLocalSP {
1331
1332 updateLocalSP(w)
1333 }
1334
1335 default:
1336 panic("bad type for Call")
1337 }
1338
1339 case ACallIndirect:
1340 writeUleb128(w, uint64(p.To.Offset))
1341 w.WriteByte(0x00)
1342 if hasLocalSP {
1343
1344 updateLocalSP(w)
1345 }
1346
1347 case AI32Const, AI64Const:
1348 if p.From.Name == obj.NAME_EXTERN {
1349 s.AddRel(ctxt, obj.Reloc{
1350 Type: objabi.R_ADDR,
1351 Off: int32(w.Len()),
1352 Siz: 1,
1353 Sym: p.From.Sym,
1354 Add: p.From.Offset,
1355 })
1356 break
1357 }
1358 writeSleb128(w, p.From.Offset)
1359
1360 case AF32Const:
1361 b := make([]byte, 4)
1362 binary.LittleEndian.PutUint32(b, math.Float32bits(float32(p.From.Val.(float64))))
1363 w.Write(b)
1364
1365 case AF64Const:
1366 b := make([]byte, 8)
1367 binary.LittleEndian.PutUint64(b, math.Float64bits(p.From.Val.(float64)))
1368 w.Write(b)
1369
1370 case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
1371 if p.From.Offset < 0 {
1372 panic("negative offset for *Load")
1373 }
1374 if p.From.Type != obj.TYPE_CONST {
1375 panic("bad type for *Load")
1376 }
1377 if p.From.Offset > math.MaxUint32 {
1378 ctxt.Diag("bad offset in %v", p)
1379 }
1380 writeUleb128(w, align(p.As))
1381 writeUleb128(w, uint64(p.From.Offset))
1382
1383 case AI32Store, AI64Store, AF32Store, AF64Store, AI32Store8, AI32Store16, AI64Store8, AI64Store16, AI64Store32:
1384 if p.To.Offset < 0 {
1385 panic("negative offset")
1386 }
1387 if p.From.Offset > math.MaxUint32 {
1388 ctxt.Diag("bad offset in %v", p)
1389 }
1390 writeUleb128(w, align(p.As))
1391 writeUleb128(w, uint64(p.To.Offset))
1392
1393 case ACurrentMemory, AGrowMemory, AMemoryFill:
1394 w.WriteByte(0x00)
1395
1396 case AMemoryCopy:
1397 w.WriteByte(0x00)
1398 w.WriteByte(0x00)
1399
1400 }
1401 }
1402
1403 w.WriteByte(0x0b)
1404
1405 s.P = w.Bytes()
1406 }
1407
1408 func updateLocalSP(w *bytes.Buffer) {
1409 writeOpcode(w, AGlobalGet)
1410 writeUleb128(w, 0)
1411 writeOpcode(w, ALocalSet)
1412 writeUleb128(w, 1)
1413 }
1414
1415 func writeOpcode(w *bytes.Buffer, as obj.As) {
1416 switch {
1417 case as < AUnreachable:
1418 panic(fmt.Sprintf("unexpected assembler op: %s", as))
1419 case as < AEnd:
1420 w.WriteByte(byte(as - AUnreachable + 0x00))
1421 case as < ADrop:
1422 w.WriteByte(byte(as - AEnd + 0x0B))
1423 case as < ALocalGet:
1424 w.WriteByte(byte(as - ADrop + 0x1A))
1425 case as < AI32Load:
1426 w.WriteByte(byte(as - ALocalGet + 0x20))
1427 case as < AI32TruncSatF32S:
1428 w.WriteByte(byte(as - AI32Load + 0x28))
1429 case as < ALast:
1430 w.WriteByte(0xFC)
1431 w.WriteByte(byte(as - AI32TruncSatF32S + 0x00))
1432 default:
1433 panic(fmt.Sprintf("unexpected assembler op: %s", as))
1434 }
1435 }
1436
1437 type valueType byte
1438
1439 const (
1440 i32 valueType = 0x7F
1441 i64 valueType = 0x7E
1442 f32 valueType = 0x7D
1443 f64 valueType = 0x7C
1444 )
1445
1446 func regType(reg int16) valueType {
1447 switch {
1448 case reg == REG_SP:
1449 return i32
1450 case reg >= REG_R0 && reg <= REG_R15:
1451 return i64
1452 case reg >= REG_F0 && reg <= REG_F15:
1453 return f32
1454 case reg >= REG_F16 && reg <= REG_F31:
1455 return f64
1456 default:
1457 panic("invalid register")
1458 }
1459 }
1460
1461 func align(as obj.As) uint64 {
1462 switch as {
1463 case AI32Load8S, AI32Load8U, AI64Load8S, AI64Load8U, AI32Store8, AI64Store8:
1464 return 0
1465 case AI32Load16S, AI32Load16U, AI64Load16S, AI64Load16U, AI32Store16, AI64Store16:
1466 return 1
1467 case AI32Load, AF32Load, AI64Load32S, AI64Load32U, AI32Store, AF32Store, AI64Store32:
1468 return 2
1469 case AI64Load, AF64Load, AI64Store, AF64Store:
1470 return 3
1471 default:
1472 panic("align: bad op")
1473 }
1474 }
1475
1476 func writeUleb128(w io.ByteWriter, v uint64) {
1477 if v < 128 {
1478 w.WriteByte(uint8(v))
1479 return
1480 }
1481 more := true
1482 for more {
1483 c := uint8(v & 0x7f)
1484 v >>= 7
1485 more = v != 0
1486 if more {
1487 c |= 0x80
1488 }
1489 w.WriteByte(c)
1490 }
1491 }
1492
1493 func writeSleb128(w io.ByteWriter, v int64) {
1494 more := true
1495 for more {
1496 c := uint8(v & 0x7f)
1497 s := uint8(v & 0x40)
1498 v >>= 7
1499 more = !((v == 0 && s == 0) || (v == -1 && s != 0))
1500 if more {
1501 c |= 0x80
1502 }
1503 w.WriteByte(c)
1504 }
1505 }
1506
View as plain text