github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/runtime/mkpreempt.go (about)

     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  // +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  	"mips64x": func() { genMIPS(true) },
    84  	"mipsx":   func() { genMIPS(false) },
    85  	"ppc64x":  genPPC64,
    86  	"riscv64": genRISCV64,
    87  	"s390x":   genS390X,
    88  	"wasm":    genWasm,
    89  }
    90  var beLe = map[string]bool{"mips64x": true, "mipsx": true, "ppc64x": true}
    91  
    92  func main() {
    93  	flag.Parse()
    94  	if flag.NArg() > 0 {
    95  		out = os.Stdout
    96  		for _, arch := range flag.Args() {
    97  			gen, ok := arches[arch]
    98  			if !ok {
    99  				log.Fatalf("unknown arch %s", arch)
   100  			}
   101  			header(arch)
   102  			gen()
   103  		}
   104  		return
   105  	}
   106  
   107  	for arch, gen := range arches {
   108  		f, err := os.Create(fmt.Sprintf("preempt_%s.s", arch))
   109  		if err != nil {
   110  			log.Fatal(err)
   111  		}
   112  		out = f
   113  		header(arch)
   114  		gen()
   115  		if err := f.Close(); err != nil {
   116  			log.Fatal(err)
   117  		}
   118  	}
   119  }
   120  
   121  func header(arch string) {
   122  	fmt.Fprintf(out, "// Code generated by mkpreempt.go; DO NOT EDIT.\n\n")
   123  	if beLe[arch] {
   124  		base := arch[:len(arch)-1]
   125  		fmt.Fprintf(out, "// +build %s %sle\n\n", base, base)
   126  	}
   127  	fmt.Fprintf(out, "#include \"go_asm.h\"\n")
   128  	fmt.Fprintf(out, "#include \"textflag.h\"\n\n")
   129  	fmt.Fprintf(out, "// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally.\n")
   130  	fmt.Fprintf(out, "TEXT ·asyncPreempt<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-0\n")
   131  }
   132  
   133  func p(f string, args ...interface{}) {
   134  	fmted := fmt.Sprintf(f, args...)
   135  	fmt.Fprintf(out, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t"))
   136  }
   137  
   138  func label(l string) {
   139  	fmt.Fprintf(out, "%s\n", l)
   140  }
   141  
   142  type layout struct {
   143  	stack int
   144  	regs  []regPos
   145  	sp    string // stack pointer register
   146  }
   147  
   148  type regPos struct {
   149  	pos int
   150  
   151  	op  string
   152  	reg string
   153  
   154  	// If this register requires special save and restore, these
   155  	// give those operations with a %d placeholder for the stack
   156  	// offset.
   157  	save, restore string
   158  }
   159  
   160  func (l *layout) add(op, reg string, size int) {
   161  	l.regs = append(l.regs, regPos{op: op, reg: reg, pos: l.stack})
   162  	l.stack += size
   163  }
   164  
   165  func (l *layout) addSpecial(save, restore string, size int) {
   166  	l.regs = append(l.regs, regPos{save: save, restore: restore, pos: l.stack})
   167  	l.stack += size
   168  }
   169  
   170  func (l *layout) save() {
   171  	for _, reg := range l.regs {
   172  		if reg.save != "" {
   173  			p(reg.save, reg.pos)
   174  		} else {
   175  			p("%s %s, %d(%s)", reg.op, reg.reg, reg.pos, l.sp)
   176  		}
   177  	}
   178  }
   179  
   180  func (l *layout) restore() {
   181  	for i := len(l.regs) - 1; i >= 0; i-- {
   182  		reg := l.regs[i]
   183  		if reg.restore != "" {
   184  			p(reg.restore, reg.pos)
   185  		} else {
   186  			p("%s %d(%s), %s", reg.op, reg.pos, l.sp, reg.reg)
   187  		}
   188  	}
   189  }
   190  
   191  func gen386() {
   192  	p("PUSHFL")
   193  	// Save general purpose registers.
   194  	var l = layout{sp: "SP"}
   195  	for _, reg := range regNames386 {
   196  		if reg == "SP" || strings.HasPrefix(reg, "X") {
   197  			continue
   198  		}
   199  		l.add("MOVL", reg, 4)
   200  	}
   201  
   202  	// Save SSE state only if supported.
   203  	lSSE := layout{stack: l.stack, sp: "SP"}
   204  	for i := 0; i < 8; i++ {
   205  		lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
   206  	}
   207  
   208  	p("ADJSP $%d", lSSE.stack)
   209  	p("NOP SP")
   210  	l.save()
   211  	p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse")
   212  	lSSE.save()
   213  	label("nosse:")
   214  	p("CALL ·asyncPreempt2(SB)")
   215  	p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2")
   216  	lSSE.restore()
   217  	label("nosse2:")
   218  	l.restore()
   219  	p("ADJSP $%d", -lSSE.stack)
   220  
   221  	p("POPFL")
   222  	p("RET")
   223  }
   224  
   225  func genAMD64() {
   226  	// Assign stack offsets.
   227  	var l = layout{sp: "SP"}
   228  	for _, reg := range regNamesAMD64 {
   229  		if reg == "SP" || reg == "BP" {
   230  			continue
   231  		}
   232  		if strings.HasPrefix(reg, "X") {
   233  			l.add("MOVUPS", reg, 16)
   234  		} else {
   235  			l.add("MOVQ", reg, 8)
   236  		}
   237  	}
   238  
   239  	// TODO: MXCSR register?
   240  
   241  	p("PUSHQ BP")
   242  	p("MOVQ SP, BP")
   243  	p("// Save flags before clobbering them")
   244  	p("PUSHFQ")
   245  	p("// obj doesn't understand ADD/SUB on SP, but does understand ADJSP")
   246  	p("ADJSP $%d", l.stack)
   247  	p("// But vet doesn't know ADJSP, so suppress vet stack checking")
   248  	p("NOP SP")
   249  
   250  	// Apparently, the signal handling code path in darwin kernel leaves
   251  	// the upper bits of Y registers in a dirty state, which causes
   252  	// many SSE operations (128-bit and narrower) become much slower.
   253  	// Clear the upper bits to get to a clean state. See issue #37174.
   254  	// It is safe here as Go code don't use the upper bits of Y registers.
   255  	p("#ifdef GOOS_darwin")
   256  	p("CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0")
   257  	p("JE 2(PC)")
   258  	p("VZEROUPPER")
   259  	p("#endif")
   260  
   261  	l.save()
   262  	p("CALL ·asyncPreempt2(SB)")
   263  	l.restore()
   264  	p("ADJSP $%d", -l.stack)
   265  	p("POPFQ")
   266  	p("POPQ BP")
   267  	p("RET")
   268  }
   269  
   270  func genARM() {
   271  	// Add integer registers R0-R12.
   272  	// R13 (SP), R14 (LR), R15 (PC) are special and not saved here.
   273  	var l = layout{sp: "R13", stack: 4} // add LR slot
   274  	for i := 0; i <= 12; i++ {
   275  		reg := fmt.Sprintf("R%d", i)
   276  		if i == 10 {
   277  			continue // R10 is g register, no need to save/restore
   278  		}
   279  		l.add("MOVW", reg, 4)
   280  	}
   281  	// Add flag register.
   282  	l.addSpecial(
   283  		"MOVW CPSR, R0\nMOVW R0, %d(R13)",
   284  		"MOVW %d(R13), R0\nMOVW R0, CPSR",
   285  		4)
   286  
   287  	// Add floating point registers F0-F15 and flag register.
   288  	var lfp = layout{stack: l.stack, sp: "R13"}
   289  	lfp.addSpecial(
   290  		"MOVW FPCR, R0\nMOVW R0, %d(R13)",
   291  		"MOVW %d(R13), R0\nMOVW R0, FPCR",
   292  		4)
   293  	for i := 0; i <= 15; i++ {
   294  		reg := fmt.Sprintf("F%d", i)
   295  		lfp.add("MOVD", reg, 8)
   296  	}
   297  
   298  	p("MOVW.W R14, -%d(R13)", lfp.stack) // allocate frame, save LR
   299  	l.save()
   300  	p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // test goarm, and skip FP registers if goarm=5.
   301  	lfp.save()
   302  	label("nofp:")
   303  	p("CALL ·asyncPreempt2(SB)")
   304  	p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // test goarm, and skip FP registers if goarm=5.
   305  	lfp.restore()
   306  	label("nofp2:")
   307  	l.restore()
   308  
   309  	p("MOVW %d(R13), R14", lfp.stack)     // sigctxt.pushCall pushes LR on stack, restore it
   310  	p("MOVW.P %d(R13), R15", lfp.stack+4) // load PC, pop frame (including the space pushed by sigctxt.pushCall)
   311  	p("UNDEF")                            // shouldn't get here
   312  }
   313  
   314  func genARM64() {
   315  	// Add integer registers R0-R26
   316  	// R27 (REGTMP), R28 (g), R29 (FP), R30 (LR), R31 (SP) are special
   317  	// and not saved here.
   318  	var l = layout{sp: "RSP", stack: 8} // add slot to save PC of interrupted instruction
   319  	for i := 0; i <= 26; i++ {
   320  		if i == 18 {
   321  			continue // R18 is not used, skip
   322  		}
   323  		reg := fmt.Sprintf("R%d", i)
   324  		l.add("MOVD", reg, 8)
   325  	}
   326  	// Add flag registers.
   327  	l.addSpecial(
   328  		"MOVD NZCV, R0\nMOVD R0, %d(RSP)",
   329  		"MOVD %d(RSP), R0\nMOVD R0, NZCV",
   330  		8)
   331  	l.addSpecial(
   332  		"MOVD FPSR, R0\nMOVD R0, %d(RSP)",
   333  		"MOVD %d(RSP), R0\nMOVD R0, FPSR",
   334  		8)
   335  	// TODO: FPCR? I don't think we'll change it, so no need to save.
   336  	// Add floating point registers F0-F31.
   337  	for i := 0; i <= 31; i++ {
   338  		reg := fmt.Sprintf("F%d", i)
   339  		l.add("FMOVD", reg, 8)
   340  	}
   341  	if l.stack%16 != 0 {
   342  		l.stack += 8 // SP needs 16-byte alignment
   343  	}
   344  
   345  	// allocate frame, save PC of interrupted instruction (in LR)
   346  	p("MOVD R30, %d(RSP)", -l.stack)
   347  	p("SUB $%d, RSP", l.stack)
   348  	p("#ifdef GOOS_linux")
   349  	p("MOVD R29, -8(RSP)") // save frame pointer (only used on Linux)
   350  	p("SUB $8, RSP, R29")  // set up new frame pointer
   351  	p("#endif")
   352  	// On iOS, save the LR again after decrementing SP. We run the
   353  	// signal handler on the G stack (as it doesn't support sigaltstack),
   354  	// so any writes below SP may be clobbered.
   355  	p("#ifdef GOOS_ios")
   356  	p("MOVD R30, (RSP)")
   357  	p("#endif")
   358  
   359  	l.save()
   360  	p("CALL ·asyncPreempt2(SB)")
   361  	l.restore()
   362  
   363  	p("MOVD %d(RSP), R30", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
   364  	p("#ifdef GOOS_linux")
   365  	p("MOVD -8(RSP), R29") // restore frame pointer
   366  	p("#endif")
   367  	p("MOVD (RSP), R27")          // load PC to REGTMP
   368  	p("ADD $%d, RSP", l.stack+16) // pop frame (including the space pushed by sigctxt.pushCall)
   369  	p("JMP (R27)")
   370  }
   371  
   372  func genMIPS(_64bit bool) {
   373  	mov := "MOVW"
   374  	movf := "MOVF"
   375  	add := "ADD"
   376  	sub := "SUB"
   377  	r28 := "R28"
   378  	regsize := 4
   379  	softfloat := "GOMIPS_softfloat"
   380  	if _64bit {
   381  		mov = "MOVV"
   382  		movf = "MOVD"
   383  		add = "ADDV"
   384  		sub = "SUBV"
   385  		r28 = "RSB"
   386  		regsize = 8
   387  		softfloat = "GOMIPS64_softfloat"
   388  	}
   389  
   390  	// Add integer registers R1-R22, R24-R25, R28
   391  	// R0 (zero), R23 (REGTMP), R29 (SP), R30 (g), R31 (LR) are special,
   392  	// and not saved here. R26 and R27 are reserved by kernel and not used.
   393  	var l = layout{sp: "R29", stack: regsize} // add slot to save PC of interrupted instruction (in LR)
   394  	for i := 1; i <= 25; i++ {
   395  		if i == 23 {
   396  			continue // R23 is REGTMP
   397  		}
   398  		reg := fmt.Sprintf("R%d", i)
   399  		l.add(mov, reg, regsize)
   400  	}
   401  	l.add(mov, r28, regsize)
   402  	l.addSpecial(
   403  		mov+" HI, R1\n"+mov+" R1, %d(R29)",
   404  		mov+" %d(R29), R1\n"+mov+" R1, HI",
   405  		regsize)
   406  	l.addSpecial(
   407  		mov+" LO, R1\n"+mov+" R1, %d(R29)",
   408  		mov+" %d(R29), R1\n"+mov+" R1, LO",
   409  		regsize)
   410  
   411  	// Add floating point control/status register FCR31 (FCR0-FCR30 are irrelevant)
   412  	var lfp = layout{sp: "R29", stack: l.stack}
   413  	lfp.addSpecial(
   414  		mov+" FCR31, R1\n"+mov+" R1, %d(R29)",
   415  		mov+" %d(R29), R1\n"+mov+" R1, FCR31",
   416  		regsize)
   417  	// Add floating point registers F0-F31.
   418  	for i := 0; i <= 31; i++ {
   419  		reg := fmt.Sprintf("F%d", i)
   420  		lfp.add(movf, reg, regsize)
   421  	}
   422  
   423  	// allocate frame, save PC of interrupted instruction (in LR)
   424  	p(mov+" R31, -%d(R29)", lfp.stack)
   425  	p(sub+" $%d, R29", lfp.stack)
   426  
   427  	l.save()
   428  	p("#ifndef %s", softfloat)
   429  	lfp.save()
   430  	p("#endif")
   431  	p("CALL ·asyncPreempt2(SB)")
   432  	p("#ifndef %s", softfloat)
   433  	lfp.restore()
   434  	p("#endif")
   435  	l.restore()
   436  
   437  	p(mov+" %d(R29), R31", lfp.stack)     // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
   438  	p(mov + " (R29), R23")                // load PC to REGTMP
   439  	p(add+" $%d, R29", lfp.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall)
   440  	p("JMP (R23)")
   441  }
   442  
   443  func genPPC64() {
   444  	// Add integer registers R3-R29
   445  	// R0 (zero), R1 (SP), R30 (g) are special and not saved here.
   446  	// R2 (TOC pointer in PIC mode), R12 (function entry address in PIC mode) have been saved in sigctxt.pushCall.
   447  	// R31 (REGTMP) will be saved manually.
   448  	var l = layout{sp: "R1", stack: 32 + 8} // MinFrameSize on PPC64, plus one word for saving R31
   449  	for i := 3; i <= 29; i++ {
   450  		if i == 12 || i == 13 {
   451  			// R12 has been saved in sigctxt.pushCall.
   452  			// R13 is TLS pointer, not used by Go code. we must NOT
   453  			// restore it, otherwise if we parked and resumed on a
   454  			// different thread we'll mess up TLS addresses.
   455  			continue
   456  		}
   457  		reg := fmt.Sprintf("R%d", i)
   458  		l.add("MOVD", reg, 8)
   459  	}
   460  	l.addSpecial(
   461  		"MOVW CR, R31\nMOVW R31, %d(R1)",
   462  		"MOVW %d(R1), R31\nMOVFL R31, $0xff", // this is MOVW R31, CR
   463  		8)                                    // CR is 4-byte wide, but just keep the alignment
   464  	l.addSpecial(
   465  		"MOVD XER, R31\nMOVD R31, %d(R1)",
   466  		"MOVD %d(R1), R31\nMOVD R31, XER",
   467  		8)
   468  	// Add floating point registers F0-F31.
   469  	for i := 0; i <= 31; i++ {
   470  		reg := fmt.Sprintf("F%d", i)
   471  		l.add("FMOVD", reg, 8)
   472  	}
   473  	// Add floating point control/status register FPSCR.
   474  	l.addSpecial(
   475  		"MOVFL FPSCR, F0\nFMOVD F0, %d(R1)",
   476  		"FMOVD %d(R1), F0\nMOVFL F0, FPSCR",
   477  		8)
   478  
   479  	p("MOVD R31, -%d(R1)", l.stack-32) // save R31 first, we'll use R31 for saving LR
   480  	p("MOVD LR, R31")
   481  	p("MOVDU R31, -%d(R1)", l.stack) // allocate frame, save PC of interrupted instruction (in LR)
   482  
   483  	l.save()
   484  	p("CALL ·asyncPreempt2(SB)")
   485  	l.restore()
   486  
   487  	p("MOVD %d(R1), R31", l.stack) // sigctxt.pushCall has pushed LR, R2, R12 (at interrupt) on stack, restore them
   488  	p("MOVD R31, LR")
   489  	p("MOVD %d(R1), R2", l.stack+8)
   490  	p("MOVD %d(R1), R12", l.stack+16)
   491  	p("MOVD (R1), R31") // load PC to CTR
   492  	p("MOVD R31, CTR")
   493  	p("MOVD 32(R1), R31")        // restore R31
   494  	p("ADD $%d, R1", l.stack+32) // pop frame (including the space pushed by sigctxt.pushCall)
   495  	p("JMP (CTR)")
   496  }
   497  
   498  func genRISCV64() {
   499  	// X0 (zero), X1 (LR), X2 (SP), X4 (TP), X27 (g), X31 (TMP) are special.
   500  	var l = layout{sp: "X2", stack: 8}
   501  
   502  	// Add integer registers (X3, X5-X26, X28-30).
   503  	for i := 3; i < 31; i++ {
   504  		if i == 4 || i == 27 {
   505  			continue
   506  		}
   507  		reg := fmt.Sprintf("X%d", i)
   508  		l.add("MOV", reg, 8)
   509  	}
   510  
   511  	// Add floating point registers (F0-F31).
   512  	for i := 0; i <= 31; i++ {
   513  		reg := fmt.Sprintf("F%d", i)
   514  		l.add("MOVD", reg, 8)
   515  	}
   516  
   517  	p("MOV X1, -%d(X2)", l.stack)
   518  	p("ADD $-%d, X2", l.stack)
   519  	l.save()
   520  	p("CALL ·asyncPreempt2(SB)")
   521  	l.restore()
   522  	p("MOV %d(X2), X1", l.stack)
   523  	p("MOV (X2), X31")
   524  	p("ADD $%d, X2", l.stack+8)
   525  	p("JMP (X31)")
   526  }
   527  
   528  func genS390X() {
   529  	// Add integer registers R0-R12
   530  	// R13 (g), R14 (LR), R15 (SP) are special, and not saved here.
   531  	// Saving R10 (REGTMP) is not necessary, but it is saved anyway.
   532  	var l = layout{sp: "R15", stack: 16} // add slot to save PC of interrupted instruction and flags
   533  	l.addSpecial(
   534  		"STMG R0, R12, %d(R15)",
   535  		"LMG %d(R15), R0, R12",
   536  		13*8)
   537  	// Add floating point registers F0-F31.
   538  	for i := 0; i <= 15; i++ {
   539  		reg := fmt.Sprintf("F%d", i)
   540  		l.add("FMOVD", reg, 8)
   541  	}
   542  
   543  	// allocate frame, save PC of interrupted instruction (in LR) and flags (condition code)
   544  	p("IPM R10") // save flags upfront, as ADD will clobber flags
   545  	p("MOVD R14, -%d(R15)", l.stack)
   546  	p("ADD $-%d, R15", l.stack)
   547  	p("MOVW R10, 8(R15)") // save flags
   548  
   549  	l.save()
   550  	p("CALL ·asyncPreempt2(SB)")
   551  	l.restore()
   552  
   553  	p("MOVD %d(R15), R14", l.stack)    // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
   554  	p("ADD $%d, R15", l.stack+8)       // pop frame (including the space pushed by sigctxt.pushCall)
   555  	p("MOVWZ -%d(R15), R10", l.stack)  // load flags to REGTMP
   556  	p("TMLH R10, $(3<<12)")            // restore flags
   557  	p("MOVD -%d(R15), R10", l.stack+8) // load PC to REGTMP
   558  	p("JMP (R10)")
   559  }
   560  
   561  func genWasm() {
   562  	p("// No async preemption on wasm")
   563  	p("UNDEF")
   564  }
   565  
   566  func notImplemented() {
   567  	p("// Not implemented yet")
   568  	p("JMP ·abort(SB)")
   569  }