Source file src/pkg/cmd/internal/obj/wasm/wasmobj.go
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 "io"
15 "math"
16 )
17
18 var Register = map[string]int16{
19 "SP": REG_SP,
20 "CTXT": REG_CTXT,
21 "g": REG_g,
22 "RET0": REG_RET0,
23 "RET1": REG_RET1,
24 "RET2": REG_RET2,
25 "RET3": REG_RET3,
26 "PAUSE": REG_PAUSE,
27
28 "R0": REG_R0,
29 "R1": REG_R1,
30 "R2": REG_R2,
31 "R3": REG_R3,
32 "R4": REG_R4,
33 "R5": REG_R5,
34 "R6": REG_R6,
35 "R7": REG_R7,
36 "R8": REG_R8,
37 "R9": REG_R9,
38 "R10": REG_R10,
39 "R11": REG_R11,
40 "R12": REG_R12,
41 "R13": REG_R13,
42 "R14": REG_R14,
43 "R15": REG_R15,
44
45 "F0": REG_F0,
46 "F1": REG_F1,
47 "F2": REG_F2,
48 "F3": REG_F3,
49 "F4": REG_F4,
50 "F5": REG_F5,
51 "F6": REG_F6,
52 "F7": REG_F7,
53 "F8": REG_F8,
54 "F9": REG_F9,
55 "F10": REG_F10,
56 "F11": REG_F11,
57 "F12": REG_F12,
58 "F13": REG_F13,
59 "F14": REG_F14,
60 "F15": REG_F15,
61
62 "PC_B": REG_PC_B,
63 }
64
65 var registerNames []string
66
67 func init() {
68 obj.RegisterRegister(MINREG, MAXREG, rconv)
69 obj.RegisterOpcode(obj.ABaseWasm, Anames)
70
71 registerNames = make([]string, MAXREG-MINREG)
72 for name, reg := range Register {
73 registerNames[reg-MINREG] = name
74 }
75 }
76
77 func rconv(r int) string {
78 return registerNames[r-MINREG]
79 }
80
81 var unaryDst = map[obj.As]bool{
82 ASet: true,
83 ATee: true,
84 ACall: true,
85 ACallIndirect: true,
86 ACallImport: true,
87 ABr: true,
88 ABrIf: true,
89 ABrTable: true,
90 AI32Store: true,
91 AI64Store: true,
92 AF32Store: true,
93 AF64Store: true,
94 AI32Store8: true,
95 AI32Store16: true,
96 AI64Store8: true,
97 AI64Store16: true,
98 AI64Store32: true,
99 ACALLNORESUME: true,
100 }
101
102 var Linkwasm = obj.LinkArch{
103 Arch: sys.ArchWasm,
104 Init: instinit,
105 Preprocess: preprocess,
106 Assemble: assemble,
107 UnaryDst: unaryDst,
108 }
109
110 var (
111 morestack *obj.LSym
112 morestackNoCtxt *obj.LSym
113 gcWriteBarrier *obj.LSym
114 sigpanic *obj.LSym
115 deferreturn *obj.LSym
116 jmpdefer *obj.LSym
117 )
118
119 const (
120
121 WasmImport = 1 << 0
122 )
123
124 func instinit(ctxt *obj.Link) {
125 morestack = ctxt.Lookup("runtime.morestack")
126 morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
127 gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
128 sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
129 deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal)
130
131
132
133
134 jmpdefer = ctxt.LookupABI(`"".jmpdefer`, obj.ABIInternal)
135 }
136
137 func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
138 appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog {
139 if p.As != obj.ANOP {
140 p2 := obj.Appendp(p, newprog)
141 p2.Pc = p.Pc
142 p = p2
143 }
144 p.As = as
145 switch len(args) {
146 case 0:
147 p.From = obj.Addr{}
148 p.To = obj.Addr{}
149 case 1:
150 if unaryDst[as] {
151 p.From = obj.Addr{}
152 p.To = args[0]
153 } else {
154 p.From = args[0]
155 p.To = obj.Addr{}
156 }
157 case 2:
158 p.From = args[0]
159 p.To = args[1]
160 default:
161 panic("bad args")
162 }
163 return p
164 }
165
166 framesize := s.Func.Text.To.Offset
167 if framesize < 0 {
168 panic("bad framesize")
169 }
170 s.Func.Args = s.Func.Text.To.Val.(int32)
171 s.Func.Locals = int32(framesize)
172
173 if s.Func.Text.From.Sym.Wrapper() {
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 gpanic := obj.Addr{
195 Type: obj.TYPE_MEM,
196 Reg: REGG,
197 Offset: 4 * 8,
198 }
199
200 panicargp := obj.Addr{
201 Type: obj.TYPE_MEM,
202 Reg: REG_R0,
203 Offset: 0,
204 }
205
206 p := s.Func.Text
207 p = appendp(p, AMOVD, gpanic, regAddr(REG_R0))
208
209 p = appendp(p, AGet, regAddr(REG_R0))
210 p = appendp(p, AI64Eqz)
211 p = appendp(p, ANot)
212 p = appendp(p, AIf)
213
214 p = appendp(p, AGet, regAddr(REG_SP))
215 p = appendp(p, AI64ExtendI32U)
216 p = appendp(p, AI64Const, constAddr(framesize+8))
217 p = appendp(p, AI64Add)
218 p = appendp(p, AI64Load, panicargp)
219
220 p = appendp(p, AI64Eq)
221 p = appendp(p, AIf)
222 p = appendp(p, AMOVD, regAddr(REG_SP), panicargp)
223 p = appendp(p, AEnd)
224
225 p = appendp(p, AEnd)
226 }
227
228 if framesize > 0 {
229 p := s.Func.Text
230 p = appendp(p, AGet, regAddr(REG_SP))
231 p = appendp(p, AI32Const, constAddr(framesize))
232 p = appendp(p, AI32Sub)
233 p = appendp(p, ASet, regAddr(REG_SP))
234 p.Spadj = int32(framesize)
235 }
236
237
238
239 numResumePoints := 0
240 explicitBlockDepth := 0
241 pc := int64(0)
242 var tableIdxs []uint64
243 tablePC := int64(0)
244 base := ctxt.PosTable.Pos(s.Func.Text.Pos).Base()
245 for p := s.Func.Text; p != nil; p = p.Link {
246 prevBase := base
247 base = ctxt.PosTable.Pos(p.Pos).Base()
248 switch p.As {
249 case ABlock, ALoop, AIf:
250 explicitBlockDepth++
251
252 case AEnd:
253 if explicitBlockDepth == 0 {
254 panic("End without block")
255 }
256 explicitBlockDepth--
257
258 case ARESUMEPOINT:
259 if explicitBlockDepth != 0 {
260 panic("RESUME can only be used on toplevel")
261 }
262 p.As = AEnd
263 for tablePC <= pc {
264 tableIdxs = append(tableIdxs, uint64(numResumePoints))
265 tablePC++
266 }
267 numResumePoints++
268 pc++
269
270 case obj.ACALL:
271 if explicitBlockDepth != 0 {
272 panic("CALL can only be used on toplevel, try CALLNORESUME instead")
273 }
274 appendp(p, ARESUMEPOINT)
275 }
276
277 p.Pc = pc
278
279
280
281
282
283 if p.As == ACALLNORESUME || p.As == obj.ANOP || p.As == ANop || p.Spadj != 0 || base != prevBase {
284 pc++
285 if p.To.Sym == sigpanic {
286
287
288
289
290 pc++
291 }
292 }
293 }
294 tableIdxs = append(tableIdxs, uint64(numResumePoints))
295 s.Size = pc + 1
296
297 if !s.Func.Text.From.Sym.NoSplit() {
298 p := s.Func.Text
299
300 if framesize <= objabi.StackSmall {
301
302
303
304
305
306
307
308 p = appendp(p, AGet, regAddr(REG_SP))
309 p = appendp(p, AGet, regAddr(REGG))
310 p = appendp(p, AI32WrapI64)
311 p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize)))
312 p = appendp(p, AI32LeU)
313 } else {
314
315
316
317
318
319
320
321
322
323
324 p = appendp(p, AGet, regAddr(REG_SP))
325 p = appendp(p, AGet, regAddr(REGG))
326 p = appendp(p, AI32WrapI64)
327 p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize)))
328 p = appendp(p, AI32Const, constAddr(int64(framesize)-objabi.StackSmall))
329 p = appendp(p, AI32Add)
330 p = appendp(p, AI32LeU)
331 }
332
333
334 p = appendp(p, AIf)
335 p = appendp(p, obj.ACALL, constAddr(0))
336 if s.Func.Text.From.Sym.NeedCtxt() {
337 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
338 } else {
339 p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt}
340 }
341 p = appendp(p, AEnd)
342 }
343
344
345
346 var entryPointLoopBranches []*obj.Prog
347 var unwindExitBranches []*obj.Prog
348 currentDepth := 0
349 for p := s.Func.Text; p != nil; p = p.Link {
350 switch p.As {
351 case ABlock, ALoop, AIf:
352 currentDepth++
353 case AEnd:
354 currentDepth--
355 }
356
357 switch p.As {
358 case obj.AJMP:
359 jmp := *p
360 p.As = obj.ANOP
361
362 if jmp.To.Type == obj.TYPE_BRANCH {
363
364 p = appendp(p, AI32Const, constAddr(jmp.To.Val.(*obj.Prog).Pc))
365 p = appendp(p, ASet, regAddr(REG_PC_B))
366 p = appendp(p, ABr)
367 entryPointLoopBranches = append(entryPointLoopBranches, p)
368 break
369 }
370
371
372 switch jmp.To.Type {
373 case obj.TYPE_MEM:
374 if !notUsePC_B[jmp.To.Sym.Name] {
375
376 p = appendp(p, AI32Const, constAddr(0))
377 }
378 p = appendp(p, ACall, jmp.To)
379
380 case obj.TYPE_NONE:
381
382 p = appendp(p, AI32WrapI64)
383 p = appendp(p, AI32Const, constAddr(16))
384 p = appendp(p, AI32ShrU)
385
386
387
388
389
390 p = appendp(p, ASet, regAddr(REG_PC_B))
391 p = appendp(p, AI32Const, constAddr(0))
392 p = appendp(p, AGet, regAddr(REG_PC_B))
393
394 p = appendp(p, ACallIndirect)
395
396 default:
397 panic("bad target for JMP")
398 }
399
400 p = appendp(p, AReturn)
401
402 case obj.ACALL, ACALLNORESUME:
403 call := *p
404 p.As = obj.ANOP
405
406 pcAfterCall := call.Link.Pc
407 if call.To.Sym == sigpanic {
408 pcAfterCall--
409 }
410
411
412
413 if call.To.Sym == deferreturn {
414 p = appendp(p, ALoop)
415 }
416
417
418 p = appendp(p, AGet, regAddr(REG_SP))
419 p = appendp(p, AI32Const, constAddr(8))
420 p = appendp(p, AI32Sub)
421 p = appendp(p, ASet, regAddr(REG_SP))
422
423
424 p = appendp(p, AGet, regAddr(REG_SP))
425 p = appendp(p, AI64Const, obj.Addr{
426 Type: obj.TYPE_ADDR,
427 Name: obj.NAME_EXTERN,
428 Sym: s,
429 Offset: pcAfterCall,
430 })
431 p = appendp(p, AI64Store, constAddr(0))
432
433
434 switch call.To.Type {
435 case obj.TYPE_MEM:
436 if !notUsePC_B[call.To.Sym.Name] {
437
438 p = appendp(p, AI32Const, constAddr(0))
439 }
440 p = appendp(p, ACall, call.To)
441
442 case obj.TYPE_NONE:
443
444 p = appendp(p, AI32WrapI64)
445 p = appendp(p, AI32Const, constAddr(16))
446 p = appendp(p, AI32ShrU)
447
448
449
450
451
452 p = appendp(p, ASet, regAddr(REG_PC_B))
453 p = appendp(p, AI32Const, constAddr(0))
454 p = appendp(p, AGet, regAddr(REG_PC_B))
455
456 p = appendp(p, ACallIndirect)
457
458 default:
459 panic("bad target for CALL")
460 }
461
462
463 if call.To.Sym == gcWriteBarrier {
464 break
465 }
466
467
468
469
470
471 if call.To.Sym == jmpdefer {
472 p = appendp(p, AReturn)
473 break
474 }
475
476
477 if call.As == ACALLNORESUME && call.To.Sym != sigpanic {
478
479 p = appendp(p, AIf)
480 p = appendp(p, obj.AUNDEF)
481 p = appendp(p, AEnd)
482 } else {
483
484 p = appendp(p, ABrIf)
485 unwindExitBranches = append(unwindExitBranches, p)
486 }
487
488
489 if call.To.Sym == deferreturn {
490
491 p = appendp(p, AGet, regAddr(REG_SP))
492 p = appendp(p, AI32Const, constAddr(8))
493 p = appendp(p, AI32Sub)
494 p = appendp(p, AI32Load16U, constAddr(0))
495 p = appendp(p, ATee, regAddr(REG_PC_B))
496
497 p = appendp(p, AI32Const, constAddr(call.Pc))
498 p = appendp(p, AI32Eq)
499 p = appendp(p, ABrIf, constAddr(0))
500 p = appendp(p, AEnd)
501 }
502
503 case obj.ARET, ARETUNWIND:
504 ret := *p
505 p.As = obj.ANOP
506
507 if framesize > 0 {
508
509 p = appendp(p, AGet, regAddr(REG_SP))
510 p = appendp(p, AI32Const, constAddr(framesize))
511 p = appendp(p, AI32Add)
512 p = appendp(p, ASet, regAddr(REG_SP))
513
514
515 }
516
517 if ret.To.Type == obj.TYPE_MEM {
518
519 p = appendp(p, AI32Const, constAddr(0))
520
521
522 p = appendp(p, ACall, ret.To)
523 p = appendp(p, AReturn)
524 break
525 }
526
527
528 p = appendp(p, AGet, regAddr(REG_SP))
529 p = appendp(p, AI32Const, constAddr(8))
530 p = appendp(p, AI32Add)
531 p = appendp(p, ASet, regAddr(REG_SP))
532
533 if ret.As == ARETUNWIND {
534
535 p = appendp(p, AI32Const, constAddr(1))
536 p = appendp(p, AReturn)
537 break
538 }
539
540
541 p = appendp(p, AI32Const, constAddr(0))
542 p = appendp(p, AReturn)
543 }
544 }
545
546 for p := s.Func.Text; p != nil; p = p.Link {
547 switch p.From.Name {
548 case obj.NAME_AUTO:
549 p.From.Offset += int64(framesize)
550 case obj.NAME_PARAM:
551 p.From.Reg = REG_SP
552 p.From.Offset += int64(framesize) + 8
553 }
554
555 switch p.To.Name {
556 case obj.NAME_AUTO:
557 p.To.Offset += int64(framesize)
558 case obj.NAME_PARAM:
559 p.To.Reg = REG_SP
560 p.To.Offset += int64(framesize) + 8
561 }
562
563 switch p.As {
564 case AGet:
565 if p.From.Type == obj.TYPE_ADDR {
566 get := *p
567 p.As = obj.ANOP
568
569 switch get.From.Name {
570 case obj.NAME_EXTERN:
571 p = appendp(p, AI64Const, get.From)
572 case obj.NAME_AUTO, obj.NAME_PARAM:
573 p = appendp(p, AGet, regAddr(get.From.Reg))
574 if get.From.Reg == REG_SP {
575 p = appendp(p, AI64ExtendI32U)
576 }
577 if get.From.Offset != 0 {
578 p = appendp(p, AI64Const, constAddr(get.From.Offset))
579 p = appendp(p, AI64Add)
580 }
581 default:
582 panic("bad Get: invalid name")
583 }
584 }
585
586 case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
587 if p.From.Type == obj.TYPE_MEM {
588 as := p.As
589 from := p.From
590
591 p.As = AGet
592 p.From = regAddr(from.Reg)
593
594 if from.Reg != REG_SP {
595 p = appendp(p, AI32WrapI64)
596 }
597
598 p = appendp(p, as, constAddr(from.Offset))
599 }
600
601 case AMOVB, AMOVH, AMOVW, AMOVD:
602 mov := *p
603 p.As = obj.ANOP
604
605 var loadAs obj.As
606 var storeAs obj.As
607 switch mov.As {
608 case AMOVB:
609 loadAs = AI64Load8U
610 storeAs = AI64Store8
611 case AMOVH:
612 loadAs = AI64Load16U
613 storeAs = AI64Store16
614 case AMOVW:
615 loadAs = AI64Load32U
616 storeAs = AI64Store32
617 case AMOVD:
618 loadAs = AI64Load
619 storeAs = AI64Store
620 }
621
622 appendValue := func() {
623 switch mov.From.Type {
624 case obj.TYPE_CONST:
625 p = appendp(p, AI64Const, constAddr(mov.From.Offset))
626
627 case obj.TYPE_ADDR:
628 switch mov.From.Name {
629 case obj.NAME_NONE, obj.NAME_PARAM, obj.NAME_AUTO:
630 p = appendp(p, AGet, regAddr(mov.From.Reg))
631 if mov.From.Reg == REG_SP {
632 p = appendp(p, AI64ExtendI32U)
633 }
634 p = appendp(p, AI64Const, constAddr(mov.From.Offset))
635 p = appendp(p, AI64Add)
636 case obj.NAME_EXTERN:
637 p = appendp(p, AI64Const, mov.From)
638 default:
639 panic("bad name for MOV")
640 }
641
642 case obj.TYPE_REG:
643 p = appendp(p, AGet, mov.From)
644 if mov.From.Reg == REG_SP {
645 p = appendp(p, AI64ExtendI32U)
646 }
647
648 case obj.TYPE_MEM:
649 p = appendp(p, AGet, regAddr(mov.From.Reg))
650 if mov.From.Reg != REG_SP {
651 p = appendp(p, AI32WrapI64)
652 }
653 p = appendp(p, loadAs, constAddr(mov.From.Offset))
654
655 default:
656 panic("bad MOV type")
657 }
658 }
659
660 switch mov.To.Type {
661 case obj.TYPE_REG:
662 appendValue()
663 if mov.To.Reg == REG_SP {
664 p = appendp(p, AI32WrapI64)
665 }
666 p = appendp(p, ASet, mov.To)
667
668 case obj.TYPE_MEM:
669 switch mov.To.Name {
670 case obj.NAME_NONE, obj.NAME_PARAM:
671 p = appendp(p, AGet, regAddr(mov.To.Reg))
672 if mov.To.Reg != REG_SP {
673 p = appendp(p, AI32WrapI64)
674 }
675 case obj.NAME_EXTERN:
676 p = appendp(p, AI32Const, obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: mov.To.Sym})
677 default:
678 panic("bad MOV name")
679 }
680 appendValue()
681 p = appendp(p, storeAs, constAddr(mov.To.Offset))
682
683 default:
684 panic("bad MOV type")
685 }
686
687 case ACallImport:
688 p.As = obj.ANOP
689 p = appendp(p, AGet, regAddr(REG_SP))
690 p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: s})
691 p.Mark = WasmImport
692 }
693 }
694
695 {
696 p := s.Func.Text
697 if len(unwindExitBranches) > 0 {
698 p = appendp(p, ABlock)
699 for _, b := range unwindExitBranches {
700 b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
701 }
702 }
703 if len(entryPointLoopBranches) > 0 {
704 p = appendp(p, ALoop)
705 for _, b := range entryPointLoopBranches {
706 b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
707 }
708 }
709 if numResumePoints > 0 {
710
711 for i := 0; i < numResumePoints+1; i++ {
712 p = appendp(p, ABlock)
713 }
714 p = appendp(p, AGet, regAddr(REG_PC_B))
715 p = appendp(p, ABrTable, obj.Addr{Val: tableIdxs})
716 p = appendp(p, AEnd)
717 }
718 for p.Link != nil {
719 p = p.Link
720 }
721 if len(entryPointLoopBranches) > 0 {
722 p = appendp(p, AEnd)
723 }
724 p = appendp(p, obj.AUNDEF)
725 if len(unwindExitBranches) > 0 {
726 p = appendp(p, AEnd)
727 p = appendp(p, AI32Const, constAddr(1))
728 }
729 }
730
731 currentDepth = 0
732 blockDepths := make(map[*obj.Prog]int)
733 for p := s.Func.Text; p != nil; p = p.Link {
734 switch p.As {
735 case ABlock, ALoop, AIf:
736 currentDepth++
737 blockDepths[p] = currentDepth
738 case AEnd:
739 currentDepth--
740 }
741
742 switch p.As {
743 case ABr, ABrIf:
744 if p.To.Type == obj.TYPE_BRANCH {
745 blockDepth, ok := blockDepths[p.To.Val.(*obj.Prog)]
746 if !ok {
747 panic("label not at block")
748 }
749 p.To = constAddr(int64(currentDepth - blockDepth))
750 }
751 }
752 }
753 }
754
755 func constAddr(value int64) obj.Addr {
756 return obj.Addr{Type: obj.TYPE_CONST, Offset: value}
757 }
758
759 func regAddr(reg int16) obj.Addr {
760 return obj.Addr{Type: obj.TYPE_REG, Reg: reg}
761 }
762
763
764
765 func countRegisters(s *obj.LSym) (numI, numF int16) {
766 for p := s.Func.Text; p != nil; p = p.Link {
767 var reg int16
768 switch p.As {
769 case AGet:
770 reg = p.From.Reg
771 case ASet:
772 reg = p.To.Reg
773 case ATee:
774 reg = p.To.Reg
775 default:
776 continue
777 }
778 if reg >= REG_R0 && reg <= REG_R15 {
779 if n := reg - REG_R0 + 1; numI < n {
780 numI = n
781 }
782 } else if reg >= REG_F0 && reg <= REG_F15 {
783 if n := reg - REG_F0 + 1; numF < n {
784 numF = n
785 }
786 }
787 }
788 return
789 }
790
791
792
793 var notUsePC_B = map[string]bool{
794 "_rt0_wasm_js": true,
795 "wasm_export_run": true,
796 "wasm_export_resume": true,
797 "wasm_export_getsp": true,
798 "wasm_pc_f_loop": true,
799 "runtime.wasmMove": true,
800 "runtime.wasmZero": true,
801 "runtime.wasmDiv": true,
802 "runtime.wasmTruncS": true,
803 "runtime.wasmTruncU": true,
804 "runtime.gcWriteBarrier": true,
805 "cmpbody": true,
806 "memeqbody": true,
807 "memcmp": true,
808 "memchr": true,
809 }
810
811 func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
812 w := new(bytes.Buffer)
813
814 hasLocalSP := false
815 hasPC_B := false
816 var r0, f0 int16
817
818
819
820 switch s.Name {
821 case "_rt0_wasm_js", "wasm_export_run", "wasm_export_resume", "wasm_export_getsp", "wasm_pc_f_loop",
822 "runtime.wasmMove", "runtime.wasmZero", "runtime.wasmDiv", "runtime.wasmTruncS", "runtime.wasmTruncU", "memeqbody":
823 writeUleb128(w, 0)
824 case "memchr", "memcmp":
825 writeUleb128(w, 1)
826 writeUleb128(w, 2)
827 w.WriteByte(0x7F)
828 case "cmpbody":
829 writeUleb128(w, 1)
830 writeUleb128(w, 2)
831 w.WriteByte(0x7E)
832 case "runtime.gcWriteBarrier":
833 writeUleb128(w, 1)
834 writeUleb128(w, 4)
835 w.WriteByte(0x7E)
836 default:
837
838 hasLocalSP = true
839 hasPC_B = true
840 numI, numF := countRegisters(s)
841 r0 = 2
842 f0 = 2 + numI
843
844 numTypes := 1
845 if numI > 0 {
846 numTypes++
847 }
848 if numF > 0 {
849 numTypes++
850 }
851
852 writeUleb128(w, uint64(numTypes))
853 writeUleb128(w, 1)
854 w.WriteByte(0x7F)
855 if numI > 0 {
856 writeUleb128(w, uint64(numI))
857 w.WriteByte(0x7E)
858 }
859 if numF > 0 {
860 writeUleb128(w, uint64(numF))
861 w.WriteByte(0x7C)
862 }
863 }
864
865 if hasLocalSP {
866
867 updateLocalSP(w)
868 }
869
870 for p := s.Func.Text; p != nil; p = p.Link {
871 switch p.As {
872 case AGet:
873 if p.From.Type != obj.TYPE_REG {
874 panic("bad Get: argument is not a register")
875 }
876 reg := p.From.Reg
877 switch {
878 case reg == REG_SP && hasLocalSP:
879 w.WriteByte(0x20)
880 writeUleb128(w, 1)
881 case reg >= REG_SP && reg <= REG_PAUSE:
882 w.WriteByte(0x23)
883 writeUleb128(w, uint64(reg-REG_SP))
884 case reg == REG_PC_B:
885 if !hasPC_B {
886 panic(fmt.Sprintf("PC_B is not used in %s", s.Name))
887 }
888 w.WriteByte(0x20)
889 writeUleb128(w, 0)
890 case reg >= REG_R0 && reg <= REG_R15:
891 w.WriteByte(0x20)
892 writeUleb128(w, uint64(r0+(reg-REG_R0)))
893 case reg >= REG_F0 && reg <= REG_F15:
894 w.WriteByte(0x20)
895 writeUleb128(w, uint64(f0+(reg-REG_F0)))
896 default:
897 panic("bad Get: invalid register")
898 }
899 continue
900
901 case ASet:
902 if p.To.Type != obj.TYPE_REG {
903 panic("bad Set: argument is not a register")
904 }
905 reg := p.To.Reg
906 switch {
907 case reg >= REG_SP && reg <= REG_PAUSE:
908 if reg == REG_SP && hasLocalSP {
909 w.WriteByte(0x22)
910 writeUleb128(w, 1)
911 }
912 w.WriteByte(0x24)
913 writeUleb128(w, uint64(reg-REG_SP))
914 case reg >= REG_R0 && reg <= REG_PC_B:
915 if p.Link.As == AGet && p.Link.From.Reg == reg {
916 w.WriteByte(0x22)
917 p = p.Link
918 } else {
919 w.WriteByte(0x21)
920 }
921 if reg == REG_PC_B {
922 if !hasPC_B {
923 panic(fmt.Sprintf("PC_B is not used in %s", s.Name))
924 }
925 writeUleb128(w, 0)
926 } else if reg <= REG_R15 {
927 writeUleb128(w, uint64(r0+(reg-REG_R0)))
928 } else {
929 writeUleb128(w, uint64(f0+(reg-REG_F0)))
930 }
931 default:
932 panic("bad Set: invalid register")
933 }
934 continue
935
936 case ATee:
937 if p.To.Type != obj.TYPE_REG {
938 panic("bad Tee: argument is not a register")
939 }
940 reg := p.To.Reg
941 switch {
942 case reg == REG_PC_B:
943 if !hasPC_B {
944 panic(fmt.Sprintf("PC_B is not used in %s", s.Name))
945 }
946 w.WriteByte(0x22)
947 writeUleb128(w, 0)
948 case reg >= REG_R0 && reg <= REG_R15:
949 w.WriteByte(0x22)
950 writeUleb128(w, uint64(r0+(reg-REG_R0)))
951 case reg >= REG_F0 && reg <= REG_F15:
952 w.WriteByte(0x22)
953 writeUleb128(w, uint64(f0+(reg-REG_F0)))
954 default:
955 panic("bad Tee: invalid register")
956 }
957 continue
958
959 case ANot:
960 w.WriteByte(0x45)
961 continue
962
963 case obj.AUNDEF:
964 w.WriteByte(0x00)
965 continue
966
967 case obj.ANOP, obj.ATEXT, obj.AFUNCDATA, obj.APCDATA:
968
969 continue
970 }
971
972 switch {
973 case p.As < AUnreachable:
974 panic(fmt.Sprintf("unexpected assembler op: %s", p.As))
975 case p.As < AEnd:
976 w.WriteByte(byte(p.As - AUnreachable + 0x00))
977 case p.As < ADrop:
978 w.WriteByte(byte(p.As - AEnd + 0x0B))
979 case p.As < AI32Load:
980 w.WriteByte(byte(p.As - ADrop + 0x1A))
981 case p.As < AI32TruncSatF32S:
982 w.WriteByte(byte(p.As - AI32Load + 0x28))
983 case p.As < ALast:
984 w.WriteByte(0xFC)
985 w.WriteByte(byte(p.As - AI32TruncSatF32S + 0x00))
986 default:
987 panic(fmt.Sprintf("unexpected assembler op: %s", p.As))
988 }
989
990 switch p.As {
991 case ABlock, ALoop, AIf:
992 if p.From.Offset != 0 {
993
994 w.WriteByte(0x80 - byte(p.From.Offset))
995 continue
996 }
997 w.WriteByte(0x40)
998
999 case ABr, ABrIf:
1000 if p.To.Type != obj.TYPE_CONST {
1001 panic("bad Br/BrIf")
1002 }
1003 writeUleb128(w, uint64(p.To.Offset))
1004
1005 case ABrTable:
1006 idxs := p.To.Val.([]uint64)
1007 writeUleb128(w, uint64(len(idxs)-1))
1008 for _, idx := range idxs {
1009 writeUleb128(w, idx)
1010 }
1011
1012 case ACall:
1013 switch p.To.Type {
1014 case obj.TYPE_CONST:
1015 writeUleb128(w, uint64(p.To.Offset))
1016
1017 case obj.TYPE_MEM:
1018 if p.To.Name != obj.NAME_EXTERN && p.To.Name != obj.NAME_STATIC {
1019 fmt.Println(p.To)
1020 panic("bad name for Call")
1021 }
1022 r := obj.Addrel(s)
1023 r.Off = int32(w.Len())
1024 r.Type = objabi.R_CALL
1025 if p.Mark&WasmImport != 0 {
1026 r.Type = objabi.R_WASMIMPORT
1027 }
1028 r.Sym = p.To.Sym
1029 if hasLocalSP {
1030
1031 updateLocalSP(w)
1032 }
1033
1034 default:
1035 panic("bad type for Call")
1036 }
1037
1038 case ACallIndirect:
1039 writeUleb128(w, uint64(p.To.Offset))
1040 w.WriteByte(0x00)
1041 if hasLocalSP {
1042
1043 updateLocalSP(w)
1044 }
1045
1046 case AI32Const, AI64Const:
1047 if p.From.Name == obj.NAME_EXTERN {
1048 r := obj.Addrel(s)
1049 r.Off = int32(w.Len())
1050 r.Type = objabi.R_ADDR
1051 r.Sym = p.From.Sym
1052 r.Add = p.From.Offset
1053 break
1054 }
1055 writeSleb128(w, p.From.Offset)
1056
1057 case AF64Const:
1058 b := make([]byte, 8)
1059 binary.LittleEndian.PutUint64(b, math.Float64bits(p.From.Val.(float64)))
1060 w.Write(b)
1061
1062 case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
1063 if p.From.Offset < 0 {
1064 panic("negative offset for *Load")
1065 }
1066 if p.From.Type != obj.TYPE_CONST {
1067 panic("bad type for *Load")
1068 }
1069 if p.From.Offset > math.MaxUint32 {
1070 ctxt.Diag("bad offset in %v", p)
1071 }
1072 writeUleb128(w, align(p.As))
1073 writeUleb128(w, uint64(p.From.Offset))
1074
1075 case AI32Store, AI64Store, AF32Store, AF64Store, AI32Store8, AI32Store16, AI64Store8, AI64Store16, AI64Store32:
1076 if p.To.Offset < 0 {
1077 panic("negative offset")
1078 }
1079 if p.From.Offset > math.MaxUint32 {
1080 ctxt.Diag("bad offset in %v", p)
1081 }
1082 writeUleb128(w, align(p.As))
1083 writeUleb128(w, uint64(p.To.Offset))
1084
1085 case ACurrentMemory, AGrowMemory:
1086 w.WriteByte(0x00)
1087
1088 }
1089 }
1090
1091 w.WriteByte(0x0b)
1092
1093 s.P = w.Bytes()
1094 }
1095
1096 func updateLocalSP(w *bytes.Buffer) {
1097 w.WriteByte(0x23)
1098 writeUleb128(w, 0)
1099 w.WriteByte(0x21)
1100 writeUleb128(w, 1)
1101 }
1102
1103 func align(as obj.As) uint64 {
1104 switch as {
1105 case AI32Load8S, AI32Load8U, AI64Load8S, AI64Load8U, AI32Store8, AI64Store8:
1106 return 0
1107 case AI32Load16S, AI32Load16U, AI64Load16S, AI64Load16U, AI32Store16, AI64Store16:
1108 return 1
1109 case AI32Load, AF32Load, AI64Load32S, AI64Load32U, AI32Store, AF32Store, AI64Store32:
1110 return 2
1111 case AI64Load, AF64Load, AI64Store, AF64Store:
1112 return 3
1113 default:
1114 panic("align: bad op")
1115 }
1116 }
1117
1118 func writeUleb128(w io.ByteWriter, v uint64) {
1119 more := true
1120 for more {
1121 c := uint8(v & 0x7f)
1122 v >>= 7
1123 more = v != 0
1124 if more {
1125 c |= 0x80
1126 }
1127 w.WriteByte(c)
1128 }
1129 }
1130
1131 func writeSleb128(w io.ByteWriter, v int64) {
1132 more := true
1133 for more {
1134 c := uint8(v & 0x7f)
1135 s := uint8(v & 0x40)
1136 v >>= 7
1137 more = !((v == 0 && s == 0) || (v == -1 && s != 0))
1138 if more {
1139 c |= 0x80
1140 }
1141 w.WriteByte(c)
1142 }
1143 }
1144
View as plain text