]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/mkpreempt.go
0bfbd379e03e3bca157609be72c0b259482656fa
[gostls13.git] / src / runtime / mkpreempt.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build ignore
6
7 // mkpreempt generates the asyncPreempt functions for each
8 // architecture.
9 package main
10
11 import (
12         "flag"
13         "fmt"
14         "io"
15         "log"
16         "os"
17         "strings"
18 )
19
20 // Copied from cmd/compile/internal/ssa/gen/*Ops.go
21
22 var regNames386 = []string{
23         "AX",
24         "CX",
25         "DX",
26         "BX",
27         "SP",
28         "BP",
29         "SI",
30         "DI",
31         "X0",
32         "X1",
33         "X2",
34         "X3",
35         "X4",
36         "X5",
37         "X6",
38         "X7",
39 }
40
41 var regNamesAMD64 = []string{
42         "AX",
43         "CX",
44         "DX",
45         "BX",
46         "SP",
47         "BP",
48         "SI",
49         "DI",
50         "R8",
51         "R9",
52         "R10",
53         "R11",
54         "R12",
55         "R13",
56         "R14",
57         "R15",
58         "X0",
59         "X1",
60         "X2",
61         "X3",
62         "X4",
63         "X5",
64         "X6",
65         "X7",
66         "X8",
67         "X9",
68         "X10",
69         "X11",
70         "X12",
71         "X13",
72         "X14",
73         "X15",
74 }
75
76 var out io.Writer
77
78 var arches = map[string]func(){
79         "386":     gen386,
80         "amd64":   genAMD64,
81         "arm":     genARM,
82         "arm64":   genARM64,
83         "loong64": genLoong64,
84         "mips64x": func() { genMIPS(true) },
85         "mipsx":   func() { genMIPS(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                 out = os.Stdout
97                 for _, arch := range flag.Args() {
98                         gen, ok := arches[arch]
99                         if !ok {
100                                 log.Fatalf("unknown arch %s", arch)
101                         }
102                         header(arch)
103                         gen()
104                 }
105                 return
106         }
107
108         for arch, gen := range arches {
109                 f, err := os.Create(fmt.Sprintf("preempt_%s.s", arch))
110                 if err != nil {
111                         log.Fatal(err)
112                 }
113                 out = f
114                 header(arch)
115                 gen()
116                 if err := f.Close(); err != nil {
117                         log.Fatal(err)
118                 }
119         }
120 }
121
122 func header(arch string) {
123         fmt.Fprintf(out, "// Code generated by mkpreempt.go; DO NOT EDIT.\n\n")
124         if beLe[arch] {
125                 base := arch[:len(arch)-1]
126                 fmt.Fprintf(out, "//go:build %s || %sle\n\n", base, base)
127         }
128         fmt.Fprintf(out, "#include \"go_asm.h\"\n")
129         if arch == "amd64" {
130                 fmt.Fprintf(out, "#include \"asm_amd64.h\"\n")
131         }
132         fmt.Fprintf(out, "#include \"textflag.h\"\n\n")
133         fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n")
134 }
135
136 func p(f string, args ...any) {
137         fmted := fmt.Sprintf(f, args...)
138         fmt.Fprintf(out, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t"))
139 }
140
141 func label(l string) {
142         fmt.Fprintf(out, "%s\n", l)
143 }
144
145 type layout struct {
146         stack int
147         regs  []regPos
148         sp    string // stack pointer register
149 }
150
151 type regPos struct {
152         pos int
153
154         saveOp    string
155         restoreOp string
156         reg       string
157
158         // If this register requires special save and restore, these
159         // give those operations with a %d placeholder for the stack
160         // offset.
161         save, restore string
162 }
163
164 func (l *layout) add(op, reg string, size int) {
165         l.regs = append(l.regs, regPos{saveOp: op, restoreOp: op, reg: reg, pos: l.stack})
166         l.stack += size
167 }
168
169 func (l *layout) add2(sop, rop, reg string, size int) {
170         l.regs = append(l.regs, regPos{saveOp: sop, restoreOp: rop, reg: reg, pos: l.stack})
171         l.stack += size
172 }
173
174 func (l *layout) addSpecial(save, restore string, size int) {
175         l.regs = append(l.regs, regPos{save: save, restore: restore, pos: l.stack})
176         l.stack += size
177 }
178
179 func (l *layout) save() {
180         for _, reg := range l.regs {
181                 if reg.save != "" {
182                         p(reg.save, reg.pos)
183                 } else {
184                         p("%s %s, %d(%s)", reg.saveOp, reg.reg, reg.pos, l.sp)
185                 }
186         }
187 }
188
189 func (l *layout) restore() {
190         for i := len(l.regs) - 1; i >= 0; i-- {
191                 reg := l.regs[i]
192                 if reg.restore != "" {
193                         p(reg.restore, reg.pos)
194                 } else {
195                         p("%s %d(%s), %s", reg.restoreOp, reg.pos, l.sp, reg.reg)
196                 }
197         }
198 }
199
200 func gen386() {
201         p("PUSHFL")
202         // Save general purpose registers.
203         var l = layout{sp: "SP"}
204         for _, reg := range regNames386 {
205                 if reg == "SP" || strings.HasPrefix(reg, "X") {
206                         continue
207                 }
208                 l.add("MOVL", reg, 4)
209         }
210
211         softfloat := "GO386_softfloat"
212
213         // Save SSE state only if supported.
214         lSSE := layout{stack: l.stack, sp: "SP"}
215         for i := 0; i < 8; i++ {
216                 lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
217         }
218
219         p("ADJSP $%d", lSSE.stack)
220         p("NOP SP")
221         l.save()
222         p("#ifndef %s", softfloat)
223         lSSE.save()
224         p("#endif")
225         p("CALL ·asyncPreempt2(SB)")
226         p("#ifndef %s", softfloat)
227         lSSE.restore()
228         p("#endif")
229         l.restore()
230         p("ADJSP $%d", -lSSE.stack)
231
232         p("POPFL")
233         p("RET")
234 }
235
236 func genAMD64() {
237         // Assign stack offsets.
238         var l = layout{sp: "SP"}
239         for _, reg := range regNamesAMD64 {
240                 if reg == "SP" || reg == "BP" {
241                         continue
242                 }
243                 if !strings.HasPrefix(reg, "X") {
244                         l.add("MOVQ", reg, 8)
245                 }
246         }
247         lSSE := layout{stack: l.stack, sp: "SP"}
248         for _, reg := range regNamesAMD64 {
249                 if strings.HasPrefix(reg, "X") {
250                         lSSE.add("MOVUPS", reg, 16)
251                 }
252         }
253
254         // TODO: MXCSR register?
255
256         p("PUSHQ BP")
257         p("MOVQ SP, BP")
258         p("// Save flags before clobbering them")
259         p("PUSHFQ")
260         p("// obj doesn't understand ADD/SUB on SP, but does understand ADJSP")
261         p("ADJSP $%d", lSSE.stack)
262         p("// But vet doesn't know ADJSP, so suppress vet stack checking")
263         p("NOP SP")
264
265         l.save()
266
267         // Apparently, the signal handling code path in darwin kernel leaves
268         // the upper bits of Y registers in a dirty state, which causes
269         // many SSE operations (128-bit and narrower) become much slower.
270         // Clear the upper bits to get to a clean state. See issue #37174.
271         // It is safe here as Go code don't use the upper bits of Y registers.
272         p("#ifdef GOOS_darwin")
273         p("#ifndef hasAVX")
274         p("CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0")
275         p("JE 2(PC)")
276         p("#endif")
277         p("VZEROUPPER")
278         p("#endif")
279
280         lSSE.save()
281         p("CALL ·asyncPreempt2(SB)")
282         lSSE.restore()
283         l.restore()
284         p("ADJSP $%d", -lSSE.stack)
285         p("POPFQ")
286         p("POPQ BP")
287         p("RET")
288 }
289
290 func genARM() {
291         // Add integer registers R0-R12.
292         // R13 (SP), R14 (LR), R15 (PC) are special and not saved here.
293         var l = layout{sp: "R13", stack: 4} // add LR slot
294         for i := 0; i <= 12; i++ {
295                 reg := fmt.Sprintf("R%d", i)
296                 if i == 10 {
297                         continue // R10 is g register, no need to save/restore
298                 }
299                 l.add("MOVW", reg, 4)
300         }
301         // Add flag register.
302         l.addSpecial(
303                 "MOVW CPSR, R0\nMOVW R0, %d(R13)",
304                 "MOVW %d(R13), R0\nMOVW R0, CPSR",
305                 4)
306
307         // Add floating point registers F0-F15 and flag register.
308         var lfp = layout{stack: l.stack, sp: "R13"}
309         lfp.addSpecial(
310                 "MOVW FPCR, R0\nMOVW R0, %d(R13)",
311                 "MOVW %d(R13), R0\nMOVW R0, FPCR",
312                 4)
313         for i := 0; i <= 15; i++ {
314                 reg := fmt.Sprintf("F%d", i)
315                 lfp.add("MOVD", reg, 8)
316         }
317
318         p("MOVW.W R14, -%d(R13)", lfp.stack) // allocate frame, save LR
319         l.save()
320         p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // test goarm, and skip FP registers if goarm=5.
321         lfp.save()
322         label("nofp:")
323         p("CALL ·asyncPreempt2(SB)")
324         p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // test goarm, and skip FP registers if goarm=5.
325         lfp.restore()
326         label("nofp2:")
327         l.restore()
328
329         p("MOVW %d(R13), R14", lfp.stack)     // sigctxt.pushCall pushes LR on stack, restore it
330         p("MOVW.P %d(R13), R15", lfp.stack+4) // load PC, pop frame (including the space pushed by sigctxt.pushCall)
331         p("UNDEF")                            // shouldn't get here
332 }
333
334 func genARM64() {
335         // Add integer registers R0-R26
336         // R27 (REGTMP), R28 (g), R29 (FP), R30 (LR), R31 (SP) are special
337         // and not saved here.
338         var l = layout{sp: "RSP", stack: 8} // add slot to save PC of interrupted instruction
339         for i := 0; i < 26; i += 2 {
340                 if i == 18 {
341                         i--
342                         continue // R18 is not used, skip
343                 }
344                 reg := fmt.Sprintf("(R%d, R%d)", i, i+1)
345                 l.add2("STP", "LDP", reg, 16)
346         }
347         // Add flag registers.
348         l.addSpecial(
349                 "MOVD NZCV, R0\nMOVD R0, %d(RSP)",
350                 "MOVD %d(RSP), R0\nMOVD R0, NZCV",
351                 8)
352         l.addSpecial(
353                 "MOVD FPSR, R0\nMOVD R0, %d(RSP)",
354                 "MOVD %d(RSP), R0\nMOVD R0, FPSR",
355                 8)
356         // TODO: FPCR? I don't think we'll change it, so no need to save.
357         // Add floating point registers F0-F31.
358         for i := 0; i < 31; i += 2 {
359                 reg := fmt.Sprintf("(F%d, F%d)", i, i+1)
360                 l.add2("FSTPD", "FLDPD", reg, 16)
361         }
362         if l.stack%16 != 0 {
363                 l.stack += 8 // SP needs 16-byte alignment
364         }
365
366         // allocate frame, save PC of interrupted instruction (in LR)
367         p("MOVD R30, %d(RSP)", -l.stack)
368         p("SUB $%d, RSP", l.stack)
369         p("MOVD R29, -8(RSP)") // save frame pointer (only used on Linux)
370         p("SUB $8, RSP, R29")  // set up new frame pointer
371         // On iOS, save the LR again after decrementing SP. We run the
372         // signal handler on the G stack (as it doesn't support sigaltstack),
373         // so any writes below SP may be clobbered.
374         p("#ifdef GOOS_ios")
375         p("MOVD R30, (RSP)")
376         p("#endif")
377
378         l.save()
379         p("CALL ·asyncPreempt2(SB)")
380         l.restore()
381
382         p("MOVD %d(RSP), R30", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
383         p("MOVD -8(RSP), R29")          // restore frame pointer
384         p("MOVD (RSP), R27")            // load PC to REGTMP
385         p("ADD $%d, RSP", l.stack+16)   // pop frame (including the space pushed by sigctxt.pushCall)
386         p("JMP (R27)")
387 }
388
389 func genMIPS(_64bit bool) {
390         mov := "MOVW"
391         movf := "MOVF"
392         add := "ADD"
393         sub := "SUB"
394         r28 := "R28"
395         regsize := 4
396         softfloat := "GOMIPS_softfloat"
397         if _64bit {
398                 mov = "MOVV"
399                 movf = "MOVD"
400                 add = "ADDV"
401                 sub = "SUBV"
402                 r28 = "RSB"
403                 regsize = 8
404                 softfloat = "GOMIPS64_softfloat"
405         }
406
407         // Add integer registers R1-R22, R24-R25, R28
408         // R0 (zero), R23 (REGTMP), R29 (SP), R30 (g), R31 (LR) are special,
409         // and not saved here. R26 and R27 are reserved by kernel and not used.
410         var l = layout{sp: "R29", stack: regsize} // add slot to save PC of interrupted instruction (in LR)
411         for i := 1; i <= 25; i++ {
412                 if i == 23 {
413                         continue // R23 is REGTMP
414                 }
415                 reg := fmt.Sprintf("R%d", i)
416                 l.add(mov, reg, regsize)
417         }
418         l.add(mov, r28, regsize)
419         l.addSpecial(
420                 mov+" HI, R1\n"+mov+" R1, %d(R29)",
421                 mov+" %d(R29), R1\n"+mov+" R1, HI",
422                 regsize)
423         l.addSpecial(
424                 mov+" LO, R1\n"+mov+" R1, %d(R29)",
425                 mov+" %d(R29), R1\n"+mov+" R1, LO",
426                 regsize)
427
428         // Add floating point control/status register FCR31 (FCR0-FCR30 are irrelevant)
429         var lfp = layout{sp: "R29", stack: l.stack}
430         lfp.addSpecial(
431                 mov+" FCR31, R1\n"+mov+" R1, %d(R29)",
432                 mov+" %d(R29), R1\n"+mov+" R1, FCR31",
433                 regsize)
434         // Add floating point registers F0-F31.
435         for i := 0; i <= 31; i++ {
436                 reg := fmt.Sprintf("F%d", i)
437                 lfp.add(movf, reg, regsize)
438         }
439
440         // allocate frame, save PC of interrupted instruction (in LR)
441         p(mov+" R31, -%d(R29)", lfp.stack)
442         p(sub+" $%d, R29", lfp.stack)
443
444         l.save()
445         p("#ifndef %s", softfloat)
446         lfp.save()
447         p("#endif")
448         p("CALL ·asyncPreempt2(SB)")
449         p("#ifndef %s", softfloat)
450         lfp.restore()
451         p("#endif")
452         l.restore()
453
454         p(mov+" %d(R29), R31", lfp.stack)     // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
455         p(mov + " (R29), R23")                // load PC to REGTMP
456         p(add+" $%d, R29", lfp.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall)
457         p("JMP (R23)")
458 }
459
460 func genLoong64() {
461         mov := "MOVV"
462         movf := "MOVD"
463         add := "ADDV"
464         sub := "SUBV"
465         regsize := 8
466
467         // Add integer registers r4-r21 r23-r29 r31
468         // R0 (zero), R30 (REGTMP), R2 (tp), R3 (SP), R22 (g), R1 (LR) are special,
469         var l = layout{sp: "R3", stack: regsize} // add slot to save PC of interrupted instruction (in LR)
470         for i := 4; i <= 31; i++ {
471                 if i == 22 || i == 30 {
472                         continue
473                 }
474                 reg := fmt.Sprintf("R%d", i)
475                 l.add(mov, reg, regsize)
476         }
477
478         // Add floating point registers F0-F31.
479         for i := 0; i <= 31; i++ {
480                 reg := fmt.Sprintf("F%d", i)
481                 l.add(movf, reg, regsize)
482         }
483
484         // save/restore FCC0
485         l.addSpecial(
486                 mov+" FCC0, R4\n"+mov+" R4, %d(R3)",
487                 mov+" %d(R3), R4\n"+mov+" R4, FCC0",
488                 regsize)
489
490         // allocate frame, save PC of interrupted instruction (in LR)
491         p(mov+" R1, -%d(R3)", l.stack)
492         p(sub+" $%d, R3", l.stack)
493
494         l.save()
495         p("CALL ·asyncPreempt2(SB)")
496         l.restore()
497
498         p(mov+" %d(R3), R1", l.stack)      // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
499         p(mov + " (R3), R30")              // load PC to REGTMP
500         p(add+" $%d, R3", l.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall)
501         p("JMP (R30)")
502 }
503
504 func genPPC64() {
505         // Add integer registers R3-R29
506         // R0 (zero), R1 (SP), R30 (g) are special and not saved here.
507         // R2 (TOC pointer in PIC mode), R12 (function entry address in PIC mode) have been saved in sigctxt.pushCall.
508         // R31 (REGTMP) will be saved manually.
509         var l = layout{sp: "R1", stack: 32 + 8} // MinFrameSize on PPC64, plus one word for saving R31
510         for i := 3; i <= 29; i++ {
511                 if i == 12 || i == 13 {
512                         // R12 has been saved in sigctxt.pushCall.
513                         // R13 is TLS pointer, not used by Go code. we must NOT
514                         // restore it, otherwise if we parked and resumed on a
515                         // different thread we'll mess up TLS addresses.
516                         continue
517                 }
518                 reg := fmt.Sprintf("R%d", i)
519                 l.add("MOVD", reg, 8)
520         }
521         l.addSpecial(
522                 "MOVW CR, R31\nMOVW R31, %d(R1)",
523                 "MOVW %d(R1), R31\nMOVFL R31, $0xff", // this is MOVW R31, CR
524                 8)                                    // CR is 4-byte wide, but just keep the alignment
525         l.addSpecial(
526                 "MOVD XER, R31\nMOVD R31, %d(R1)",
527                 "MOVD %d(R1), R31\nMOVD R31, XER",
528                 8)
529         // Add floating point registers F0-F31.
530         for i := 0; i <= 31; i++ {
531                 reg := fmt.Sprintf("F%d", i)
532                 l.add("FMOVD", reg, 8)
533         }
534         // Add floating point control/status register FPSCR.
535         l.addSpecial(
536                 "MOVFL FPSCR, F0\nFMOVD F0, %d(R1)",
537                 "FMOVD %d(R1), F0\nMOVFL F0, FPSCR",
538                 8)
539
540         p("MOVD R31, -%d(R1)", l.stack-32) // save R31 first, we'll use R31 for saving LR
541         p("MOVD LR, R31")
542         p("MOVDU R31, -%d(R1)", l.stack) // allocate frame, save PC of interrupted instruction (in LR)
543
544         l.save()
545         p("CALL ·asyncPreempt2(SB)")
546         l.restore()
547
548         p("MOVD %d(R1), R31", l.stack) // sigctxt.pushCall has pushed LR, R2, R12 (at interrupt) on stack, restore them
549         p("MOVD R31, LR")
550         p("MOVD %d(R1), R2", l.stack+8)
551         p("MOVD %d(R1), R12", l.stack+16)
552         p("MOVD (R1), R31") // load PC to CTR
553         p("MOVD R31, CTR")
554         p("MOVD 32(R1), R31")        // restore R31
555         p("ADD $%d, R1", l.stack+32) // pop frame (including the space pushed by sigctxt.pushCall)
556         p("JMP (CTR)")
557 }
558
559 func genRISCV64() {
560         // X0 (zero), X1 (LR), X2 (SP), X3 (GP), X4 (TP), X27 (g), X31 (TMP) are special.
561         var l = layout{sp: "X2", stack: 8}
562
563         // Add integer registers (X5-X26, X28-30).
564         for i := 5; i < 31; i++ {
565                 if i == 27 {
566                         continue
567                 }
568                 reg := fmt.Sprintf("X%d", i)
569                 l.add("MOV", reg, 8)
570         }
571
572         // Add floating point registers (F0-F31).
573         for i := 0; i <= 31; i++ {
574                 reg := fmt.Sprintf("F%d", i)
575                 l.add("MOVD", reg, 8)
576         }
577
578         p("MOV X1, -%d(X2)", l.stack)
579         p("ADD $-%d, X2", l.stack)
580         l.save()
581         p("CALL ·asyncPreempt2(SB)")
582         l.restore()
583         p("MOV %d(X2), X1", l.stack)
584         p("MOV (X2), X31")
585         p("ADD $%d, X2", l.stack+8)
586         p("JMP (X31)")
587 }
588
589 func genS390X() {
590         // Add integer registers R0-R12
591         // R13 (g), R14 (LR), R15 (SP) are special, and not saved here.
592         // Saving R10 (REGTMP) is not necessary, but it is saved anyway.
593         var l = layout{sp: "R15", stack: 16} // add slot to save PC of interrupted instruction and flags
594         l.addSpecial(
595                 "STMG R0, R12, %d(R15)",
596                 "LMG %d(R15), R0, R12",
597                 13*8)
598         // Add floating point registers F0-F31.
599         for i := 0; i <= 15; i++ {
600                 reg := fmt.Sprintf("F%d", i)
601                 l.add("FMOVD", reg, 8)
602         }
603
604         // allocate frame, save PC of interrupted instruction (in LR) and flags (condition code)
605         p("IPM R10") // save flags upfront, as ADD will clobber flags
606         p("MOVD R14, -%d(R15)", l.stack)
607         p("ADD $-%d, R15", l.stack)
608         p("MOVW R10, 8(R15)") // save flags
609
610         l.save()
611         p("CALL ·asyncPreempt2(SB)")
612         l.restore()
613
614         p("MOVD %d(R15), R14", l.stack)    // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
615         p("ADD $%d, R15", l.stack+8)       // pop frame (including the space pushed by sigctxt.pushCall)
616         p("MOVWZ -%d(R15), R10", l.stack)  // load flags to REGTMP
617         p("TMLH R10, $(3<<12)")            // restore flags
618         p("MOVD -%d(R15), R10", l.stack+8) // load PC to REGTMP
619         p("JMP (R10)")
620 }
621
622 func genWasm() {
623         p("// No async preemption on wasm")
624         p("UNDEF")
625 }
626
627 func notImplemented() {
628         p("// Not implemented yet")
629         p("JMP ·abort(SB)")
630 }