Source file
src/runtime/mkpreempt.go
1
2
3
4
5
6
7
8
9 package main
10
11 import (
12 "bytes"
13 "flag"
14 "fmt"
15 "go/format"
16 "io"
17 "log"
18 "os"
19 "strings"
20 )
21
22
23
24 var regNames386 = []string{
25 "AX",
26 "CX",
27 "DX",
28 "BX",
29 "SP",
30 "BP",
31 "SI",
32 "DI",
33 "X0",
34 "X1",
35 "X2",
36 "X3",
37 "X4",
38 "X5",
39 "X6",
40 "X7",
41 }
42
43 var regNamesAMD64 = []string{
44 "AX",
45 "CX",
46 "DX",
47 "BX",
48 "SP",
49 "BP",
50 "SI",
51 "DI",
52 "R8",
53 "R9",
54 "R10",
55 "R11",
56 "R12",
57 "R13",
58 "R14",
59 "R15",
60 "X0",
61 "X1",
62 "X2",
63 "X3",
64 "X4",
65 "X5",
66 "X6",
67 "X7",
68 "X8",
69 "X9",
70 "X10",
71 "X11",
72 "X12",
73 "X13",
74 "X14",
75 "X15",
76 }
77
78 var arches = map[string]func(g *gen){
79 "386": gen386,
80 "amd64": genAMD64,
81 "arm": genARM,
82 "arm64": genARM64,
83 "loong64": genLoong64,
84 "mips64x": func(g *gen) { genMIPS(g, true) },
85 "mipsx": func(g *gen) { genMIPS(g, false) },
86 "ppc64x": genPPC64,
87 "riscv64": genRISCV64,
88 "s390x": genS390X,
89 "wasm": genWasm,
90 }
91 var beLe = map[string]bool{"mips64x": true, "mipsx": true, "ppc64x": true}
92
93 func main() {
94 flag.Parse()
95 if flag.NArg() > 0 {
96 for _, arch := range flag.Args() {
97 genFn, ok := arches[arch]
98 if !ok {
99 log.Fatalf("unknown arch %s", arch)
100 }
101 g := gen{os.Stdout, arch}
102 g.asmHeader()
103 genFn(&g)
104 }
105 return
106 }
107
108 for arch, genFn := range arches {
109 f, err := os.Create(fmt.Sprintf("preempt_%s.s", arch))
110 if err != nil {
111 log.Fatal(err)
112 }
113 g := gen{f, arch}
114 g.asmHeader()
115 genFn(&g)
116 if err := f.Close(); err != nil {
117 log.Fatal(err)
118 }
119 }
120 }
121
122 type gen struct {
123 w io.Writer
124 goarch string
125 }
126
127 func (g *gen) commonHeader() {
128 fmt.Fprintf(g.w, "// Code generated by mkpreempt.go; DO NOT EDIT.\n\n")
129 if beLe[g.goarch] {
130 base := g.goarch[:len(g.goarch)-1]
131 fmt.Fprintf(g.w, "//go:build %s || %sle\n\n", base, base)
132 }
133 }
134
135 func (g *gen) asmHeader() {
136 g.commonHeader()
137 fmt.Fprintf(g.w, "#include \"go_asm.h\"\n")
138 if g.goarch == "amd64" {
139 fmt.Fprintf(g.w, "#include \"go_tls.h\"\n")
140 fmt.Fprintf(g.w, "#include \"asm_amd64.h\"\n")
141 }
142 fmt.Fprintf(g.w, "#include \"textflag.h\"\n\n")
143 fmt.Fprintf(g.w, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n")
144 }
145
146 func (g *gen) p(f string, args ...any) {
147 fmted := fmt.Sprintf(f, args...)
148 fmt.Fprintf(g.w, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t"))
149 }
150
151 func (g *gen) label(l string) {
152 fmt.Fprintf(g.w, "%s\n", l)
153 }
154
155
156 func writeXRegs(arch string, l *layout) {
157 var code bytes.Buffer
158 g := gen{&code, arch}
159 g.commonHeader()
160 fmt.Fprintf(g.w, `
161 package runtime
162
163 type xRegs struct {
164 `)
165 pos := 0
166 for _, reg := range l.regs {
167 if reg.pos != pos {
168 log.Fatalf("padding not implemented")
169 }
170 typ := fmt.Sprintf("[%d]byte", reg.size)
171 switch {
172 case reg.size == 4 && reg.pos%4 == 0:
173 typ = "uint32"
174 case reg.size == 8 && reg.pos%8 == 0:
175 typ = "uint64"
176 }
177 fmt.Fprintf(g.w, "\t%s %s\n", reg.reg, typ)
178 pos += reg.size
179 }
180 fmt.Fprintf(g.w, "}\n")
181
182 path := fmt.Sprintf("preempt_%s.go", arch)
183 b, err := format.Source(code.Bytes())
184 if err != nil {
185 log.Fatalf("formatting %s: %s", path, err)
186 }
187 if err := os.WriteFile(path, b, 0666); err != nil {
188 log.Fatal(err)
189 }
190 }
191
192 type layout struct {
193 stack int
194 regs []regPos
195 sp string
196 }
197
198 type regPos struct {
199 pos, size int
200
201 saveOp string
202 restoreOp string
203 reg string
204
205
206
207
208 save, restore string
209 }
210
211 func (l *layout) add(op, reg string, size int) {
212 l.regs = append(l.regs, regPos{saveOp: op, restoreOp: op, reg: reg, pos: l.stack, size: size})
213 l.stack += size
214 }
215
216 func (l *layout) add2(sop, rop, reg string, size int) {
217 l.regs = append(l.regs, regPos{saveOp: sop, restoreOp: rop, reg: reg, pos: l.stack, size: size})
218 l.stack += size
219 }
220
221 func (l *layout) addSpecial(save, restore string, size int) {
222 l.regs = append(l.regs, regPos{save: save, restore: restore, pos: l.stack, size: size})
223 l.stack += size
224 }
225
226 func (l *layout) save(g *gen) {
227 for _, reg := range l.regs {
228 if reg.save != "" {
229 g.p(reg.save, reg.pos)
230 } else {
231 g.p("%s %s, %d(%s)", reg.saveOp, reg.reg, reg.pos, l.sp)
232 }
233 }
234 }
235
236 func (l *layout) restore(g *gen) {
237 for i := len(l.regs) - 1; i >= 0; i-- {
238 reg := l.regs[i]
239 if reg.restore != "" {
240 g.p(reg.restore, reg.pos)
241 } else {
242 g.p("%s %d(%s), %s", reg.restoreOp, reg.pos, l.sp, reg.reg)
243 }
244 }
245 }
246
247 func gen386(g *gen) {
248 p := g.p
249
250 p("PUSHFL")
251
252 var l = layout{sp: "SP"}
253 for _, reg := range regNames386 {
254 if reg == "SP" || strings.HasPrefix(reg, "X") {
255 continue
256 }
257 l.add("MOVL", reg, 4)
258 }
259
260 softfloat := "GO386_softfloat"
261
262
263 lSSE := layout{stack: l.stack, sp: "SP"}
264 for i := 0; i < 8; i++ {
265 lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
266 }
267
268 p("ADJSP $%d", lSSE.stack)
269 p("NOP SP")
270 l.save(g)
271 p("#ifndef %s", softfloat)
272 lSSE.save(g)
273 p("#endif")
274 p("CALL ·asyncPreempt2(SB)")
275 p("#ifndef %s", softfloat)
276 lSSE.restore(g)
277 p("#endif")
278 l.restore(g)
279 p("ADJSP $%d", -lSSE.stack)
280
281 p("POPFL")
282 p("RET")
283 }
284
285 func genAMD64(g *gen) {
286 const xReg = "AX"
287
288 p, label := g.p, g.label
289
290
291 var l = layout{sp: "SP"}
292 for _, reg := range regNamesAMD64 {
293 if reg == "SP" || reg == "BP" {
294 continue
295 }
296 if !strings.HasPrefix(reg, "X") {
297 l.add("MOVQ", reg, 8)
298 }
299 }
300
301 const (
302 numXRegs = 16
303 numZRegs = 16
304 numKRegs = 8
305 )
306 lZRegs := layout{sp: xReg}
307 lXRegs, lYRegs := lZRegs, lZRegs
308 for i := range numZRegs {
309 lZRegs.add("VMOVDQU64", fmt.Sprintf("Z%d", i), 512/8)
310 if i < numXRegs {
311
312 lXRegs.add("MOVUPS", fmt.Sprintf("X%d", i), 128/8)
313 lYRegs.add("VMOVDQU", fmt.Sprintf("Y%d", i), 256/8)
314 }
315 }
316 for i := range numKRegs {
317 lZRegs.add("KMOVQ", fmt.Sprintf("K%d", i), 8)
318 }
319
320
321
322 for i := range lXRegs.regs {
323 lXRegs.regs[i].pos = lZRegs.regs[i].pos
324 lYRegs.regs[i].pos = lZRegs.regs[i].pos
325 }
326 writeXRegs(g.goarch, &lZRegs)
327
328 p("PUSHQ BP")
329 p("MOVQ SP, BP")
330 p("// Save flags before clobbering them")
331 p("PUSHFQ")
332 p("// obj doesn't understand ADD/SUB on SP, but does understand ADJSP")
333 p("ADJSP $%d", l.stack)
334 p("// But vet doesn't know ADJSP, so suppress vet stack checking")
335 p("NOP SP")
336
337 p("// Save GPs")
338 l.save(g)
339
340
341
342
343
344
345
346
347 p("// Save extended register state to p.xRegs.scratch")
348 p("// Don't make assumptions about ABI register state. See mkpreempt.go")
349 p("get_tls(CX)")
350 p("MOVQ g(CX), R14")
351 p("MOVQ g_m(R14), %s", xReg)
352 p("MOVQ m_p(%s), %s", xReg, xReg)
353 p("LEAQ (p_xRegs+xRegPerP_scratch)(%s), %s", xReg, xReg)
354
355
356 p("#ifdef GOEXPERIMENT_simd")
357 p("CMPB internal∕cpu·X86+const_offsetX86HasAVX512(SB), $1")
358 p("JE saveAVX512")
359 p("CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1")
360 p("JE saveAVX2")
361 p("#endif")
362
363
364 label("saveSSE:")
365 lXRegs.save(g)
366 p("JMP preempt")
367
368 label("saveAVX2:")
369 lYRegs.save(g)
370 p("JMP preempt")
371
372 label("saveAVX512:")
373 lZRegs.save(g)
374 p("JMP preempt")
375
376 label("preempt:")
377 p("CALL ·asyncPreempt2(SB)")
378
379 p("// Restore non-GPs from *p.xRegs.cache")
380 p("MOVQ g_m(R14), %s", xReg)
381 p("MOVQ m_p(%s), %s", xReg, xReg)
382 p("MOVQ (p_xRegs+xRegPerP_cache)(%s), %s", xReg, xReg)
383
384 p("#ifdef GOEXPERIMENT_simd")
385 p("CMPB internal∕cpu·X86+const_offsetX86HasAVX512(SB), $1")
386 p("JE restoreAVX512")
387 p("CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1")
388 p("JE restoreAVX2")
389 p("#endif")
390
391 label("restoreSSE:")
392 lXRegs.restore(g)
393 p("JMP restoreGPs")
394
395 label("restoreAVX2:")
396 lYRegs.restore(g)
397 p("JMP restoreGPs")
398
399 label("restoreAVX512:")
400 lZRegs.restore(g)
401 p("JMP restoreGPs")
402
403 label("restoreGPs:")
404 p("// Restore GPs")
405 l.restore(g)
406 p("ADJSP $%d", -l.stack)
407 p("POPFQ")
408 p("POPQ BP")
409 p("RET")
410 }
411
412 func genARM(g *gen) {
413 p := g.p
414
415
416
417 var l = layout{sp: "R13", stack: 4}
418 for i := 0; i <= 12; i++ {
419 reg := fmt.Sprintf("R%d", i)
420 if i == 10 {
421 continue
422 }
423 l.add("MOVW", reg, 4)
424 }
425
426 l.addSpecial(
427 "MOVW CPSR, R0\nMOVW R0, %d(R13)",
428 "MOVW %d(R13), R0\nMOVW R0, CPSR",
429 4)
430
431
432 var lfp = layout{stack: l.stack, sp: "R13"}
433 lfp.addSpecial(
434 "MOVW FPCR, R0\nMOVW R0, %d(R13)",
435 "MOVW %d(R13), R0\nMOVW R0, FPCR",
436 4)
437 for i := 0; i <= 15; i++ {
438 reg := fmt.Sprintf("F%d", i)
439 lfp.add("MOVD", reg, 8)
440 }
441
442 p("MOVW.W R14, -%d(R13)", lfp.stack)
443 l.save(g)
444 p("MOVB ·goarmsoftfp(SB), R0\nCMP $0, R0\nBNE nofp")
445 lfp.save(g)
446 g.label("nofp:")
447 p("CALL ·asyncPreempt2(SB)")
448 p("MOVB ·goarmsoftfp(SB), R0\nCMP $0, R0\nBNE nofp2")
449 lfp.restore(g)
450 g.label("nofp2:")
451 l.restore(g)
452
453 p("MOVW %d(R13), R14", lfp.stack)
454 p("MOVW.P %d(R13), R15", lfp.stack+4)
455 p("UNDEF")
456 }
457
458 func genARM64(g *gen) {
459 p := g.p
460
461
462
463 var l = layout{sp: "RSP", stack: 8}
464 for i := 0; i < 26; i += 2 {
465 if i == 18 {
466 i--
467 continue
468 }
469 reg := fmt.Sprintf("(R%d, R%d)", i, i+1)
470 l.add2("STP", "LDP", reg, 16)
471 }
472
473 l.addSpecial(
474 "MOVD NZCV, R0\nMOVD R0, %d(RSP)",
475 "MOVD %d(RSP), R0\nMOVD R0, NZCV",
476 8)
477 l.addSpecial(
478 "MOVD FPSR, R0\nMOVD R0, %d(RSP)",
479 "MOVD %d(RSP), R0\nMOVD R0, FPSR",
480 8)
481
482
483 for i := 0; i < 31; i += 2 {
484 reg := fmt.Sprintf("(F%d, F%d)", i, i+1)
485 l.add2("FSTPD", "FLDPD", reg, 16)
486 }
487 if l.stack%16 != 0 {
488 l.stack += 8
489 }
490
491
492 p("MOVD R30, %d(RSP)", -l.stack)
493 p("SUB $%d, RSP", l.stack)
494 p("MOVD R29, -8(RSP)")
495 p("SUB $8, RSP, R29")
496
497
498
499 p("#ifdef GOOS_ios")
500 p("MOVD R30, (RSP)")
501 p("#endif")
502
503 l.save(g)
504 p("CALL ·asyncPreempt2(SB)")
505 l.restore(g)
506
507 p("MOVD %d(RSP), R30", l.stack)
508 p("MOVD -8(RSP), R29")
509 p("MOVD (RSP), R27")
510 p("ADD $%d, RSP", l.stack+16)
511 p("RET (R27)")
512 }
513
514 func genMIPS(g *gen, _64bit bool) {
515 p := g.p
516
517 mov := "MOVW"
518 movf := "MOVF"
519 add := "ADD"
520 sub := "SUB"
521 r28 := "R28"
522 regsize := 4
523 softfloat := "GOMIPS_softfloat"
524 if _64bit {
525 mov = "MOVV"
526 movf = "MOVD"
527 add = "ADDV"
528 sub = "SUBV"
529 r28 = "RSB"
530 regsize = 8
531 softfloat = "GOMIPS64_softfloat"
532 }
533
534
535
536
537 var l = layout{sp: "R29", stack: regsize}
538 for i := 1; i <= 25; i++ {
539 if i == 23 {
540 continue
541 }
542 reg := fmt.Sprintf("R%d", i)
543 l.add(mov, reg, regsize)
544 }
545 l.add(mov, r28, regsize)
546 l.addSpecial(
547 mov+" HI, R1\n"+mov+" R1, %d(R29)",
548 mov+" %d(R29), R1\n"+mov+" R1, HI",
549 regsize)
550 l.addSpecial(
551 mov+" LO, R1\n"+mov+" R1, %d(R29)",
552 mov+" %d(R29), R1\n"+mov+" R1, LO",
553 regsize)
554
555
556 var lfp = layout{sp: "R29", stack: l.stack}
557 lfp.addSpecial(
558 mov+" FCR31, R1\n"+mov+" R1, %d(R29)",
559 mov+" %d(R29), R1\n"+mov+" R1, FCR31",
560 regsize)
561
562 for i := 0; i <= 31; i++ {
563 reg := fmt.Sprintf("F%d", i)
564 lfp.add(movf, reg, regsize)
565 }
566
567
568 p(mov+" R31, -%d(R29)", lfp.stack)
569 p(sub+" $%d, R29", lfp.stack)
570
571 l.save(g)
572 p("#ifndef %s", softfloat)
573 lfp.save(g)
574 p("#endif")
575 p("CALL ·asyncPreempt2(SB)")
576 p("#ifndef %s", softfloat)
577 lfp.restore(g)
578 p("#endif")
579 l.restore(g)
580
581 p(mov+" %d(R29), R31", lfp.stack)
582 p(mov + " (R29), R23")
583 p(add+" $%d, R29", lfp.stack+regsize)
584 p("JMP (R23)")
585 }
586
587 func genLoong64(g *gen) {
588 p := g.p
589
590 mov := "MOVV"
591 movf := "MOVD"
592 add := "ADDV"
593 sub := "SUBV"
594 regsize := 8
595
596
597
598 var l = layout{sp: "R3", stack: regsize}
599 for i := 4; i <= 31; i++ {
600 if i == 22 || i == 30 {
601 continue
602 }
603 reg := fmt.Sprintf("R%d", i)
604 l.add(mov, reg, regsize)
605 }
606
607
608 for i := 0; i <= 31; i++ {
609 reg := fmt.Sprintf("F%d", i)
610 l.add(movf, reg, regsize)
611 }
612
613
614 l.addSpecial(
615 mov+" FCC0, R4\n"+mov+" R4, %d(R3)",
616 mov+" %d(R3), R4\n"+mov+" R4, FCC0",
617 regsize)
618
619
620 p(mov+" R1, -%d(R3)", l.stack)
621 p(sub+" $%d, R3", l.stack)
622
623 l.save(g)
624 p("CALL ·asyncPreempt2(SB)")
625 l.restore(g)
626
627 p(mov+" %d(R3), R1", l.stack)
628 p(mov + " (R3), R30")
629 p(add+" $%d, R3", l.stack+regsize)
630 p("JMP (R30)")
631 }
632
633 func genPPC64(g *gen) {
634 p := g.p
635
636
637
638
639
640 var l = layout{sp: "R1", stack: 32 + 8}
641 for i := 3; i <= 29; i++ {
642 if i == 12 || i == 13 {
643
644
645
646
647 continue
648 }
649 reg := fmt.Sprintf("R%d", i)
650 l.add("MOVD", reg, 8)
651 }
652 l.addSpecial(
653 "MOVW CR, R31\nMOVW R31, %d(R1)",
654 "MOVW %d(R1), R31\nMOVFL R31, $0xff",
655 8)
656 l.addSpecial(
657 "MOVD XER, R31\nMOVD R31, %d(R1)",
658 "MOVD %d(R1), R31\nMOVD R31, XER",
659 8)
660
661 for i := 0; i <= 31; i++ {
662 reg := fmt.Sprintf("F%d", i)
663 l.add("FMOVD", reg, 8)
664 }
665
666 l.addSpecial(
667 "MOVFL FPSCR, F0\nFMOVD F0, %d(R1)",
668 "FMOVD %d(R1), F0\nMOVFL F0, FPSCR",
669 8)
670
671 p("MOVD R31, -%d(R1)", l.stack-32)
672 p("MOVD LR, R31")
673 p("MOVDU R31, -%d(R1)", l.stack)
674
675 l.save(g)
676 p("CALL ·asyncPreempt2(SB)")
677 l.restore(g)
678
679 p("MOVD %d(R1), R31", l.stack)
680 p("MOVD R31, LR")
681 p("MOVD %d(R1), R2", l.stack+8)
682 p("MOVD %d(R1), R12", l.stack+16)
683 p("MOVD (R1), R31")
684 p("MOVD R31, CTR")
685 p("MOVD 32(R1), R31")
686 p("ADD $%d, R1", l.stack+32)
687 p("JMP (CTR)")
688 }
689
690 func genRISCV64(g *gen) {
691 p := g.p
692
693
694 var l = layout{sp: "X2", stack: 8}
695
696
697 for i := 5; i < 31; i++ {
698 if i == 27 {
699 continue
700 }
701 reg := fmt.Sprintf("X%d", i)
702 l.add("MOV", reg, 8)
703 }
704
705
706 for i := 0; i <= 31; i++ {
707 reg := fmt.Sprintf("F%d", i)
708 l.add("MOVD", reg, 8)
709 }
710
711 p("MOV X1, -%d(X2)", l.stack)
712 p("SUB $%d, X2", l.stack)
713 l.save(g)
714 p("CALL ·asyncPreempt2(SB)")
715 l.restore(g)
716 p("MOV %d(X2), X1", l.stack)
717 p("MOV (X2), X31")
718 p("ADD $%d, X2", l.stack+8)
719 p("JMP (X31)")
720 }
721
722 func genS390X(g *gen) {
723 p := g.p
724
725
726
727
728 var l = layout{sp: "R15", stack: 16}
729 l.addSpecial(
730 "STMG R0, R12, %d(R15)",
731 "LMG %d(R15), R0, R12",
732 13*8)
733
734 for i := 0; i <= 15; i++ {
735 reg := fmt.Sprintf("F%d", i)
736 l.add("FMOVD", reg, 8)
737 }
738
739
740 p("IPM R10")
741 p("MOVD R14, -%d(R15)", l.stack)
742 p("ADD $-%d, R15", l.stack)
743 p("MOVW R10, 8(R15)")
744
745 l.save(g)
746 p("CALL ·asyncPreempt2(SB)")
747 l.restore(g)
748
749 p("MOVD %d(R15), R14", l.stack)
750 p("ADD $%d, R15", l.stack+8)
751 p("MOVWZ -%d(R15), R10", l.stack)
752 p("TMLH R10, $(3<<12)")
753 p("MOVD -%d(R15), R10", l.stack+8)
754 p("JMP (R10)")
755 }
756
757 func genWasm(g *gen) {
758 p := g.p
759 p("// No async preemption on wasm")
760 p("UNDEF")
761 }
762
763 func notImplemented(g *gen) {
764 p := g.p
765 p("// Not implemented yet")
766 p("JMP ·abort(SB)")
767 }
768
View as plain text