github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 }