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