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