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