github.com/goplus/reflectx@v1.2.2/cmd/icall_gen/icall_regabi.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 ) 12 13 func writeRegAbi(filename string, pkgName string, size int) error { 14 dir, f := filepath.Split(filename) 15 if dir != "" { 16 err := os.MkdirAll(dir, 0755) 17 if err != nil { 18 return fmt.Errorf("make dir %v error: %v", dir, err) 19 } 20 } 21 gofile := filepath.Join(dir, strings.Replace(f, ".go", "_regabi.go", 1)) 22 var buf bytes.Buffer 23 r := strings.NewReplacer("$pkgname", pkgName, "$max_size", strconv.Itoa(size)) 24 buf.WriteString(r.Replace(icall_regabi)) 25 26 var ar []string 27 for i := 0; i < size; i++ { 28 buf.WriteString(fmt.Sprintf("func f%v()\n", i)) 29 ar = append(ar, fmt.Sprintf("f%v", i)) 30 } 31 buf.WriteString(fmt.Sprintf(` 32 var ( 33 icall_fn = []func(){%v} 34 ) 35 `, strings.Join(ar, ","))) 36 37 err := ioutil.WriteFile(gofile, buf.Bytes(), 0644) 38 if err != nil { 39 return err 40 } 41 42 asm_amd64_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_amd64.s", 1)) 43 asm_arm64_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_arm64.s", 1)) 44 asm_ppc64x_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_ppc64x.s", 1)) 45 asm_riscv64_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_riscv64.s", 1)) 46 asm_go121_amd64_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_go121_amd64.s", 1)) 47 asm_go121_arm64_file := filepath.Join(dir, strings.Replace(f, ".go", "_regabi_go121_arm64.s", 1)) 48 fnWrite := func(filename string, tmpl string, size int) error { 49 var buf bytes.Buffer 50 buf.WriteString(tmpl) 51 for i := 0; i < size; i++ { 52 buf.WriteString(fmt.Sprintf("MAKE_FUNC_FN(·f%v,%v)\n", i, i)) 53 } 54 return ioutil.WriteFile(filename, buf.Bytes(), 0644) 55 } 56 err = fnWrite(asm_amd64_file, regabi_amd64, size) 57 if err != nil { 58 return err 59 } 60 err = fnWrite(asm_arm64_file, regabi_arm64, size) 61 if err != nil { 62 return err 63 } 64 err = fnWrite(asm_go121_amd64_file, regabi_go121_amd64, size) 65 if err != nil { 66 return err 67 } 68 err = fnWrite(asm_go121_arm64_file, regabi_go121_arm64, size) 69 if err != nil { 70 return err 71 } 72 err = fnWrite(asm_ppc64x_file, regabi_ppc64x, size) 73 if err != nil { 74 return err 75 } 76 err = fnWrite(asm_riscv64_file, regabi_riscv64, size) 77 if err != nil { 78 return err 79 } 80 return nil 81 } 82 83 var icall_regabi = `//go:build ((go1.17 && goexperiment.regabireflect) || (go1.19 && goexperiment.regabiargs) || (go1.18 && amd64) || (go1.19 && arm64) || (go1.19 && ppc64) || (go1.19 && ppc64le) || (go1.20 && riscv64)) && (!js || (js && wasm)) 84 // +build go1.17,goexperiment.regabireflect go1.19,goexperiment.regabiargs go1.18,amd64 go1.19,arm64 go1.19,ppc64 go1.19,ppc64le go1.20,riscv64 85 // +build !js js,wasm 86 87 package $pkgname 88 89 import ( 90 "reflect" 91 "unsafe" 92 93 "github.com/goplus/reflectx/abi" 94 ) 95 96 const capacity = $max_size 97 98 type methodUsed struct { 99 fun reflect.Value 100 ptr unsafe.Pointer 101 } 102 103 type provider struct { 104 used map[int]*methodUsed 105 } 106 107 //go:linkname callReflect reflect.callReflect 108 func callReflect(ctxt unsafe.Pointer, frame unsafe.Pointer, retValid *bool, r unsafe.Pointer) 109 110 //go:linkname moveMakeFuncArgPtrs reflect.moveMakeFuncArgPtrs 111 func moveMakeFuncArgPtrs(ctx unsafe.Pointer, r unsafe.Pointer) 112 113 func i_x(c unsafe.Pointer, frame unsafe.Pointer, retValid *bool, r unsafe.Pointer, index int) { 114 ptr := mp.used[index].ptr 115 moveMakeFuncArgPtrs(ptr, r) 116 callReflect(ptr, frame, retValid, r) 117 } 118 119 func spillArgs() 120 func unspillArgs() 121 122 func (p *provider) Insert(info *abi.MethodInfo) (unsafe.Pointer, int) { 123 var index = -1 124 for i := 0; i < capacity; i++ { 125 if _, ok := p.used[i]; !ok { 126 index = i 127 break 128 } 129 } 130 if index == -1 { 131 return nil, -1 132 } 133 var fn reflect.Value 134 if (!info.Pointer && !info.OnePtr) || info.Indirect { 135 ftyp := info.Func.Type() 136 numIn := ftyp.NumIn() 137 numOut := ftyp.NumOut() 138 in := make([]reflect.Type, numIn, numIn) 139 out := make([]reflect.Type, numOut, numOut) 140 in[0] = reflect.PtrTo(info.Type) 141 for i := 1; i < numIn; i++ { 142 in[i] = ftyp.In(i) 143 } 144 for i := 0; i < numOut; i++ { 145 out[i] = ftyp.Out(i) 146 } 147 ftyp = reflect.FuncOf(in, out, info.Variadic) 148 if info.Variadic { 149 fn = reflect.MakeFunc(ftyp, func(args []reflect.Value) []reflect.Value { 150 args[0] = args[0].Elem() 151 return info.Func.CallSlice(args) 152 }) 153 } else { 154 fn = reflect.MakeFunc(ftyp, func(args []reflect.Value) []reflect.Value { 155 args[0] = args[0].Elem() 156 return info.Func.Call(args) 157 }) 158 } 159 } else { 160 fn = info.Func 161 } 162 p.used[index] = &methodUsed{ 163 fun: fn, 164 ptr: (*struct{ typ, ptr unsafe.Pointer })(unsafe.Pointer(&fn)).ptr, 165 } 166 icall := icall_fn[index] 167 return unsafe.Pointer(reflect.ValueOf(icall).Pointer()), index 168 } 169 170 func (p *provider) Remove(indexs []int) { 171 for _, n := range indexs { 172 delete(p.used, n) 173 } 174 } 175 176 func (p *provider) Available() int { 177 return capacity - len(p.used) 178 } 179 180 func (p *provider) Used() int { 181 return len(p.used) 182 } 183 184 func (p *provider) Cap() int { 185 return capacity 186 } 187 188 func (p *provider) Clear() { 189 p.used = make(map[int]*methodUsed) 190 } 191 192 var ( 193 mp = &provider{ 194 used: make(map[int]*methodUsed), 195 } 196 ) 197 198 func init() { 199 abi.AddMethodProvider(mp) 200 } 201 202 ` 203 204 var regabi_go121_amd64 = `//go:build go1.21 205 // +build go1.21 206 207 // Copyright 2012 The Go Authors. All rights reserved. 208 // Use of this source code is governed by a BSD-style 209 // license that can be found in the LICENSE file. 210 211 #include "textflag.h" 212 #include "funcdata.h" 213 #include "go_asm.h" 214 215 // The frames of each of the two functions below contain two locals, at offsets 216 // that are known to the runtime. 217 // 218 // The first local is a bool called retValid with a whole pointer-word reserved 219 // for it on the stack. The purpose of this word is so that the runtime knows 220 // whether the stack-allocated return space contains valid values for stack 221 // scanning. 222 // 223 // The second local is an abi.RegArgs value whose offset is also known to the 224 // runtime, so that a stack map for it can be constructed, since it contains 225 // pointers visible to the GC. 226 #define LOCAL_RETVALID 32 227 #define LOCAL_REGARGS 40 228 229 // makeFuncStub is the code half of the function returned by MakeFunc. 230 // See the comment on the declaration of makeFuncStub in makefunc.go 231 // for more details. 232 // No arg size here; runtime pulls arg map out of the func value. 233 // This frame contains two locals. See the comment above LOCAL_RETVALID. 234 // amd64 argframe+8(FP) offset to func from method 235 #define MAKE_FUNC_FN(NAME,INDEX) \ 236 TEXT NAME(SB),(NOSPLIT|WRAPPER),$312 \ 237 NO_LOCAL_POINTERS \ 238 LEAQ LOCAL_REGARGS(SP), R12 \ 239 CALL runtime·spillArgs(SB) \ 240 MOVQ 24(SP), DX \ 241 MOVQ DX, 0(SP) \ 242 LEAQ argframe+16(FP), CX \ 243 MOVQ CX, 8(SP) \ 244 MOVB $0, LOCAL_RETVALID(SP) \ 245 LEAQ LOCAL_RETVALID(SP), AX \ 246 MOVQ AX, 16(SP) \ 247 LEAQ LOCAL_REGARGS(SP), AX \ 248 MOVQ AX, 24(SP) \ 249 MOVQ $INDEX, AX \ 250 MOVQ AX, 32(SP) \ 251 CALL ·i_x(SB) \ 252 LEAQ LOCAL_REGARGS(SP), R12 \ 253 CALL runtime·unspillArgs(SB) \ 254 RET 255 256 ` 257 258 var regabi_go121_arm64 = `//go:build go1.21 259 // +build go1.21 260 261 // Copyright 2012 The Go Authors. All rights reserved. 262 // Use of this source code is governed by a BSD-style 263 // license that can be found in the LICENSE file. 264 265 #include "textflag.h" 266 #include "funcdata.h" 267 268 // The frames of each of the two functions below contain two locals, at offsets 269 // that are known to the runtime. 270 // 271 // The first local is a bool called retValid with a whole pointer-word reserved 272 // for it on the stack. The purpose of this word is so that the runtime knows 273 // whether the stack-allocated return space contains valid values for stack 274 // scanning. 275 // 276 // The second local is an abi.RegArgs value whose offset is also known to the 277 // runtime, so that a stack map for it can be constructed, since it contains 278 // pointers visible to the GC. 279 #define LOCAL_RETVALID 40 280 #define LOCAL_REGARGS 48 281 282 // The frame size of the functions below is 283 // 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432. 284 285 // makeFuncStub is the code half of the function returned by MakeFunc. 286 // See the comment on the declaration of makeFuncStub in makefunc.go 287 // for more details. 288 // No arg size here, runtime pulls arg map out of the func value. 289 #define MAKE_FUNC_FN(NAME,INDEX) \ 290 TEXT NAME(SB),(NOSPLIT|WRAPPER),$432 \ 291 NO_LOCAL_POINTERS \ 292 ADD $LOCAL_REGARGS, RSP, R20 \ 293 CALL runtime·spillArgs(SB) \ 294 MOVD 32(RSP), R26 \ 295 MOVD R26, 16(RSP) \ 296 MOVD $argframe+0(FP), R3 \ 297 MOVD R3, 16(RSP) \ 298 MOVB $0, LOCAL_RETVALID(RSP) \ 299 ADD $LOCAL_RETVALID, RSP, R3 \ 300 MOVD R3, 24(RSP) \ 301 ADD $LOCAL_REGARGS, RSP, R3 \ 302 MOVD R3, 32(RSP) \ 303 MOVD $INDEX, R3 \ 304 MOVD R3, 40(RSP) \ 305 CALL ·i_x(SB) \ 306 ADD $LOCAL_REGARGS, RSP, R20 \ 307 CALL runtime·unspillArgs(SB) \ 308 RET 309 310 ` 311 312 var regabi_amd64 = `//go:build (go1.17 && goexperiment.regabireflect) || (go1.18 && !go1.21) 313 // +build go1.17,goexperiment.regabireflect go1.18,!go1.21 314 315 // Copyright 2012 The Go Authors. All rights reserved. 316 // Use of this source code is governed by a BSD-style 317 // license that can be found in the LICENSE file. 318 319 #include "textflag.h" 320 #include "funcdata.h" 321 #include "go_asm.h" 322 323 // The frames of each of the two functions below contain two locals, at offsets 324 // that are known to the runtime. 325 // 326 // The first local is a bool called retValid with a whole pointer-word reserved 327 // for it on the stack. The purpose of this word is so that the runtime knows 328 // whether the stack-allocated return space contains valid values for stack 329 // scanning. 330 // 331 // The second local is an abi.RegArgs value whose offset is also known to the 332 // runtime, so that a stack map for it can be constructed, since it contains 333 // pointers visible to the GC. 334 #define LOCAL_RETVALID 32 335 #define LOCAL_REGARGS 40 336 337 TEXT ·spillArgs(SB),NOSPLIT,$0-0 338 MOVQ AX, 0(R12) 339 MOVQ BX, 8(R12) 340 MOVQ CX, 16(R12) 341 MOVQ DI, 24(R12) 342 MOVQ SI, 32(R12) 343 MOVQ R8, 40(R12) 344 MOVQ R9, 48(R12) 345 MOVQ R10, 56(R12) 346 MOVQ R11, 64(R12) 347 MOVQ X0, 72(R12) 348 MOVQ X1, 80(R12) 349 MOVQ X2, 88(R12) 350 MOVQ X3, 96(R12) 351 MOVQ X4, 104(R12) 352 MOVQ X5, 112(R12) 353 MOVQ X6, 120(R12) 354 MOVQ X7, 128(R12) 355 MOVQ X8, 136(R12) 356 MOVQ X9, 144(R12) 357 MOVQ X10, 152(R12) 358 MOVQ X11, 160(R12) 359 MOVQ X12, 168(R12) 360 MOVQ X13, 176(R12) 361 MOVQ X14, 184(R12) 362 RET 363 364 // unspillArgs loads args into registers from a *internal/abi.RegArgs in R12. 365 TEXT ·unspillArgs(SB),NOSPLIT,$0-0 366 MOVQ 0(R12), AX 367 MOVQ 8(R12), BX 368 MOVQ 16(R12), CX 369 MOVQ 24(R12), DI 370 MOVQ 32(R12), SI 371 MOVQ 40(R12), R8 372 MOVQ 48(R12), R9 373 MOVQ 56(R12), R10 374 MOVQ 64(R12), R11 375 MOVQ 72(R12), X0 376 MOVQ 80(R12), X1 377 MOVQ 88(R12), X2 378 MOVQ 96(R12), X3 379 MOVQ 104(R12), X4 380 MOVQ 112(R12), X5 381 MOVQ 120(R12), X6 382 MOVQ 128(R12), X7 383 MOVQ 136(R12), X8 384 MOVQ 144(R12), X9 385 MOVQ 152(R12), X10 386 MOVQ 160(R12), X11 387 MOVQ 168(R12), X12 388 MOVQ 176(R12), X13 389 MOVQ 184(R12), X14 390 RET 391 392 // makeFuncStub is the code half of the function returned by MakeFunc. 393 // See the comment on the declaration of makeFuncStub in makefunc.go 394 // for more details. 395 // No arg size here; runtime pulls arg map out of the func value. 396 // This frame contains two locals. See the comment above LOCAL_RETVALID. 397 #define MAKE_FUNC_FN(NAME,INDEX) \ 398 TEXT NAME(SB),(NOSPLIT|WRAPPER),$312 \ 399 NO_LOCAL_POINTERS \ 400 LEAQ LOCAL_REGARGS(SP), R12 \ 401 CALL ·spillArgs(SB) \ 402 MOVQ 24(SP), DX \ 403 MOVQ DX, 0(SP) \ 404 LEAQ argframe+8(FP), CX \ 405 MOVQ CX, 8(SP) \ 406 MOVB $0, LOCAL_RETVALID(SP) \ 407 LEAQ LOCAL_RETVALID(SP), AX \ 408 MOVQ AX, 16(SP) \ 409 LEAQ LOCAL_REGARGS(SP), AX \ 410 MOVQ AX, 24(SP) \ 411 MOVQ $INDEX, AX \ 412 MOVQ AX, 32(SP) \ 413 CALL ·i_x(SB) \ 414 LEAQ LOCAL_REGARGS(SP), R12 \ 415 CALL ·unspillArgs(SB) \ 416 RET 417 418 ` 419 420 var regabi_arm64 = `//go:build (go1.18 && goexperiment.regabireflect) || (go1.19 && !go1.21) 421 // +build go1.18,goexperiment.regabireflect go1.19,!go1.21 422 423 // Copyright 2012 The Go Authors. All rights reserved. 424 // Use of this source code is governed by a BSD-style 425 // license that can be found in the LICENSE file. 426 427 #include "textflag.h" 428 #include "funcdata.h" 429 430 // The frames of each of the two functions below contain two locals, at offsets 431 // that are known to the runtime. 432 // 433 // The first local is a bool called retValid with a whole pointer-word reserved 434 // for it on the stack. The purpose of this word is so that the runtime knows 435 // whether the stack-allocated return space contains valid values for stack 436 // scanning. 437 // 438 // The second local is an abi.RegArgs value whose offset is also known to the 439 // runtime, so that a stack map for it can be constructed, since it contains 440 // pointers visible to the GC. 441 #define LOCAL_RETVALID 40 442 #define LOCAL_REGARGS 48 443 444 // The frame size of the functions below is 445 // 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432. 446 447 // makeFuncStub is the code half of the function returned by MakeFunc. 448 // See the comment on the declaration of makeFuncStub in makefunc.go 449 // for more details. 450 // No arg size here, runtime pulls arg map out of the func value. 451 #define MAKE_FUNC_FN(NAME,INDEX) \ 452 TEXT NAME(SB),(NOSPLIT|WRAPPER),$432 \ 453 NO_LOCAL_POINTERS \ 454 ADD $LOCAL_REGARGS, RSP, R20 \ 455 CALL runtime·spillArgs(SB) \ 456 MOVD 32(RSP), R26 \ 457 MOVD R26, 8(RSP) \ 458 MOVD $argframe+0(FP), R3 \ 459 MOVD R3, 16(RSP) \ 460 MOVB $0, LOCAL_RETVALID(RSP) \ 461 ADD $LOCAL_RETVALID, RSP, R3 \ 462 MOVD R3, 24(RSP) \ 463 ADD $LOCAL_REGARGS, RSP, R3 \ 464 MOVD R3, 32(RSP) \ 465 MOVD $INDEX, R3 \ 466 MOVD R3, 40(RSP) \ 467 CALL ·i_x(SB) \ 468 ADD $LOCAL_REGARGS, RSP, R20 \ 469 CALL runtime·unspillArgs(SB) \ 470 RET 471 472 ` 473 474 var regabi_ppc64x = `//go:build ((go1.18 && goexperiment.regabireflect) || go1.19) && (ppc64 || ppc64le) 475 // +build go1.18,goexperiment.regabireflect go1.19 476 // +build ppc64 ppc64le 477 478 // Copyright 2012 The Go Authors. All rights reserved. 479 // Use of this source code is governed by a BSD-style 480 // license that can be found in the LICENSE file. 481 482 #include "textflag.h" 483 #include "funcdata.h" 484 #include "asm_ppc64x.h" 485 486 // The frames of each of the two functions below contain two locals, at offsets 487 // that are known to the runtime. 488 // 489 // The first local is a bool called retValid with a whole pointer-word reserved 490 // for it on the stack. The purpose of this word is so that the runtime knows 491 // whether the stack-allocated return space contains valid values for stack 492 // scanning. 493 // 494 // The second local is an abi.RegArgs value whose offset is also known to the 495 // runtime, so that a stack map for it can be constructed, since it contains 496 // pointers visible to the GC. 497 498 #define LOCAL_RETVALID 32+FIXED_FRAME 499 #define LOCAL_REGARGS 40+FIXED_FRAME 500 501 // The frame size of the functions below is 502 // 32 (args of callReflect) + 8 (bool + padding) + 296 (abi.RegArgs) = 336. 503 504 // makeFuncStub is the code half of the function returned by MakeFunc. 505 // See the comment on the declaration of makeFuncStub in makefunc.go 506 // for more details. 507 // No arg size here, runtime pulls arg map out of the func value. 508 #define MAKE_FUNC_FN(NAME,INDEX) \ 509 TEXT NAME(SB),(NOSPLIT|WRAPPER),$336 \ 510 NO_LOCAL_POINTERS \ 511 ADD $LOCAL_REGARGS, R1, R20 \ 512 CALL runtime·spillArgs(SB) \ 513 MOVD FIXED_FRAME+32(R1), R11 \ 514 MOVD R11, FIXED_FRAME+0(R1) \ 515 MOVD $argframe+0(FP), R3 \ 516 MOVD R3, FIXED_FRAME+8(R1) \ 517 ADD $LOCAL_RETVALID, R1, R3 \ 518 MOVB R0, (R3) \ 519 MOVD R3, FIXED_FRAME+16(R1) \ 520 ADD $LOCAL_REGARGS, R1, R3 \ 521 MOVD R3, FIXED_FRAME+24(R1) \ 522 MOVD $INDEX, R3 \ 523 MOVD R3, FIXED_FRAME+32(R1) \ 524 BL ·i_x(SB) \ 525 ADD $LOCAL_REGARGS, R1, R20 \ 526 CALL runtime·unspillArgs(SB) \ 527 RET 528 529 ` 530 531 var regabi_riscv64 = `//go:build (go1.19 && goexperiment.regabiargs) || go1.20 532 // +build go1.19,goexperiment.regabiargs go1.20 533 534 // Copyright 2019 The Go Authors. All rights reserved. 535 // Use of this source code is governed by a BSD-style 536 // license that can be found in the LICENSE file. 537 538 #include "textflag.h" 539 #include "funcdata.h" 540 541 // The frames of each of the two functions below contain two locals, at offsets 542 // that are known to the runtime. 543 // 544 // The first local is a bool called retValid with a whole pointer-word reserved 545 // for it on the stack. The purpose of this word is so that the runtime knows 546 // whether the stack-allocated return space contains valid values for stack 547 // scanning. 548 // 549 // The second local is an abi.RegArgs value whose offset is also known to the 550 // runtime, so that a stack map for it can be constructed, since it contains 551 // pointers visible to the GC. 552 #define LOCAL_RETVALID 40 553 #define LOCAL_REGARGS 48 554 555 // The frame size of the functions below is 556 // 32 (args of callReflect/callMethod) + (8 bool with padding) + 392 (abi.RegArgs) = 432. 557 558 // makeFuncStub is the code half of the function returned by MakeFunc. 559 // See the comment on the declaration of makeFuncStub in makefunc.go 560 // for more details. 561 // No arg size here, runtime pulls arg map out of the func value. 562 #define MAKE_FUNC_FN(NAME,INDEX) \ 563 TEXT NAME(SB),(NOSPLIT|WRAPPER),$432 \ 564 NO_LOCAL_POINTERS \ 565 ADD $LOCAL_REGARGS, SP, X25 \ 566 CALL runtime·spillArgs(SB) \ 567 MOV 32(SP), CTXT \ 568 MOV CTXT, 8(SP) \ 569 MOV $argframe+0(FP), T0 \ 570 MOV T0, 16(SP) \ 571 MOV ZERO, LOCAL_RETVALID(SP) \ 572 ADD $LOCAL_RETVALID, SP, T1 \ 573 MOV T1, 24(SP) \ 574 ADD $LOCAL_REGARGS, SP, T1 \ 575 MOV T1, 32(SP) \ 576 MOV $INDEX, T1 \ 577 MOV T1, 40(SP) \ 578 CALL ·i_x(SB) \ 579 ADD $LOCAL_REGARGS, SP, X25 \ 580 CALL runtime·unspillArgs(SB) \ 581 RET 582 583 `