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