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