рдЖрдк asm рдХреЛрдб рдХреЗ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдмрд╛рд▓реЛрдВ рд╡рд╛рд▓реЗ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рднрд╛рдЧ рдЪреБрдХреЗ рд╣реИрдВред
рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рд╕реБрдЭрд╛рд╡ рдЕрд╕реЗрдВрдмрд▓рд░ рд╕реЗ рдЧреЛ рдореЗрдВ рдХреЙрд▓ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдирд╣реАрдВ рд╣реИред - рдЗрдпрд╛рди рд▓рд╛рдВрд╕ рдЯреЗрд▓рд░
рдЬрдм рддрдХ рдЖрдкрдХрд╛ рдХреЛрдбрд╛рдВрддрд░рдХ рдХреЛрдб рдХреБрдЫ рд╕рд░рд▓ рдХрд░рддрд╛ рд╣реИ, рддрдм рддрдХ рд╕рдм рдХреБрдЫ рдЕрдЪреНрдЫрд╛ рд▓рдЧрддрд╛ рд╣реИред
рдЬреИрд╕реЗ рд╣реА рдЖрдкрдХреЗ рдкрд╛рд╕ рдЧреЛ рдХреЗ рдХреЛрдбрд╛рдВрддрд░рдХ рдХреЛрдб рд╕реЗ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рдХрд╛ рдХрд╛рд░реНрдп рд╣реЛрддрд╛ рд╣реИ, рдкрд╣рд▓реЗ рд╕реБрдЭрд╛рд╡реЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдЬреЛ рдЖрдкрдХреЛ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛: рдРрд╕рд╛ рди рдХрд░реЗрдВред
рд▓реЗрдХрд┐рди рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдпрджрд┐ рдЖрдк рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдЙрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХреГрдкрдпрд╛, рдмрд┐рд▓реНрд▓реА рдХреЗ рдиреАрдЪреЗред

рдЕрдзрд┐рд╡реЗрд╢рди рдмреБрд▓рд╛ рд░рд╣реЗ рд╣реИрдВ
рдпрд╣ рд╕рдм рдЗрд╕ рддрдереНрдп рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдпрд╣ рд╕рдордЭрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рддрд░реНрдХ рдХреИрд╕реЗ рдкрд╛рд░рд┐рдд рдХрд░реЗрдВ рдФрд░ рдЗрд╕рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЛ рдХреИрд╕реЗ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░реЗрдВ ред
рдореЗрд░рд╛ рд╕реБрдЭрд╛рд╡ рд╣реИ рдХрд┐ рдЖрдк рдЕрдкрдиреЗ рдЖрдк рдХреЛ рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдХреА рднрд╛рд╖рд╛ рдореЗрдВ рдЧреЛ рдХрд╛рд░реНрдпреЛрдВ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рдХрд░рд╛рдПрдБ , рдЬрд╣рд╛рдБ рд╣рдореЗрдВ рдЬреЛ рднреА рдЬрд╛рдирдХрд╛рд░реА рдЪрд╛рд╣рд┐рдП рд╡рд╣ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╡рд░реНрдгрд┐рдд рд╣реИред
рдЖрдорддреМрд░ рдкрд░, рдХреЙрд▓рд┐рдВрдЧ рдХрдиреНрд╡реЗрдВрд╢рди рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рд╕реЗ рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдкрд░ рднрд┐рдиреНрди рд╣реЛрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЙрдкрд▓рдмреНрдз рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдХрд╛ рд╕реЗрдЯ рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рд╣рдо рдХреЗрд╡рд▓ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВрдЧреЗ GOARCH=amd64
, рд▓реЗрдХрд┐рди рдЧреЛ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕рдореНрдореЗрд▓рдиреЛрдВ рдореЗрдВ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЕрдВрддрд░ рдирд╣реАрдВ рд╣реИред
рдпрд╣рд╛рдБ рдЧреЛ рдореЗрдВ рд╕рдореНрдореЗрд▓рди рдмреБрд▓рд╛ рд╕рдорд╛рд░реЛрд╣ рдХреА рдХреБрдЫ рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рд╣реИрдВ:
- рд╕рднреА рддрд░реНрдХреЛрдВ рдХреЛ рд╕реНрдЯреИрдХ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдХреНрд▓реЛрдЬрд░ рдореЗрдВ "рд╕рдВрджрд░реНрдн" рдХреЛ рдЫреЛрдбрд╝рдХрд░, рдпрд╣ рд░рдЬрд┐рд╕реНрдЯрд░
DX
(% rdx) рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реБрд▓рдн рд╣реИ ред - ( arguments).
- .
- . , .
, . .

, register-based calling convention. Go.
, :
- , .
0(SP)
, 8(SP)
( 8 ), .n(SP)
, n
тАФ . int64
, 16(SP)
.
.
package main
func asmfunc(x int32) (int32, int32)
func gofunc(a1 int64, a2, a3 int32) (int32, int32) {
return int32(a1) + a2, int32(a1) + a3
}
func main() {
v1, v2 := asmfunc(10)
println(v1, v2)
}
// func asmfunc(x int32) (int32, int32)
TEXT ┬╖asmfunc(SB), 0, $24-12
MOVL x+0(FP), AX
MOVQ $1, 0(SP) // (a1 int64)
MOVL $2, 8(SP) // (a2 int32)
MOVL AX, 12(SP) // (a3 int32)
CALL ┬╖gofunc(SB)
MOVL 16(SP), AX //
MOVL 20(SP), CX //
MOVL AX, ret+8(FP) //
MOVL CX, ret+12(FP) //
RET
$24-16 (locals=24 bytes, args=16 bytes)
0 8 12 16 20 SP
locals=24 [a1:8][a2:4][a3:4][ret:4][ret:4]
(ret asmfunc, gofunc)
0 4 8 12 FP
args=16 [x:4][padding:4][ret:4][ret:4]
(ret main, asmfunc)
, 4 . , , (8 amd64
).
, . тАФ int32
, тАФ int64
, offset 8, 4, reflect.TypeOf(int64(0)).Align()
8.
, FP
go vet
.
stackmap
-.
package foo
import (
"fmt"
"testing"
)
func foo(ptr *object)
type object struct {
x, y, z int64
}
func printPtr(ptr *object) {
fmt.Println(*ptr)
}
func TestFoo(t *testing.T) {
foo(&object{x: 11, y: 22, z: 33})
}
TEXT ┬╖foo(SB), 0, $8-8
MOVQ ptr+0(FP), AX
MOVQ AX, 0(SP)
CALL ┬╖printPtr(SB)
RET
, - stackmap:
=== RUN TestFoo
runtime: frame <censored> untyped locals 0xc00008ff38+0x8
fatal error: missing stackmap
, , GC stackmaps. Go , .
stub ( Go ). , stackmap , .
, stackmap (), NO_LOCAL_POINTERS
( ).
NO_LOCAL_POINTERS
TestFoo
:
#include "funcdata.h"
TEXT ┬╖foo(SB), 0, $8-8
NO_LOCAL_POINTERS
MOVQ ptr+0(FP), AX
MOVQ AX, 0(SP)
CALL ┬╖printPtr(SB)
RET
, .
, ? , , "" , , , , ?
, heap, , . , , . GC "" , , .
, , "" (escapes to heap) escape analysis, , .
NO_LOCAL_POINTERS
: , , , GC . .
Go non-cooperative preemption, , .
Go. , go:nosplit
, , , NO_LOCAL_POINTERS
.
GO_ARGS
, Go prototype, GO_ARGS
.
GO_ARGS
тАФ funcdata.h, NO_LOCAL_POINTERS
. , stackmap Go .
, stackmap . args_stackmap
. : , stackmap.
GO_RESULTS_INITIALIZED
Go , ( ) GO_RESULTS_INITIALIZED
.
:
// func getg() interface{}
TEXT ┬╖getg(SB), NOSPLIT, $32-16
// .
// .
MOVQ $0, ret_type+0(FP)
MOVQ $0, ret_data+8(FP)
GO_RESULTS_INITIALIZED
// ...
RET
, , -.
GitHub.
Go JIT-
Go .
Garbage collector Go , , , , , Go JIT'.
, . , , .
calljit-v1
package main
import (
"log"
"reflect"
"syscall"
"unsafe"
)
func main() {
a := funcAddr(goFunc)
code := []byte{
0xb8, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
0xff, 0xd0,
0xc3,
}
executable, err := mmapExecutable(len(code))
if err != nil {
log.Panicf("mmap: %v", err)
}
copy(executable, code)
calljit(&executable[0])
}
func calljit(code *byte)
func goFunc() {
println("called from JIT")
}
func mmapExecutable(length int) ([]byte, error) {
const prot = syscall.PROT_READ | syscall.PROT_WRITE | syscall.PROT_EXEC
const flags = syscall.MAP_PRIVATE | syscall.MAP_ANON
return mmapLinux(0, uintptr(length), prot, flags, 0, 0)
}
func mmapLinux(addr, length, prot, flags, fd, off uintptr) ([]byte, error) {
ptr, _, err := syscall.Syscall6(
syscall.SYS_MMAP,
addr, length, prot, flags, fd, offset)
if err != 0 {
return nil, err
}
slice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: ptr,
Len: int(length),
Cap: int(length),
}))
return slice, nil
}
func funcAddr(fn interface{}) uintptr {
type emptyInterface struct {
typ uintptr
value *uintptr
}
e := (*emptyInterface)(unsafe.Pointer(&fn))
return *e.value
}
// file jit_amd64.s
TEXT ┬╖calljit(SB), 0, $0-8
MOVQ code+0(FP), AX
JMP AX
, ( ):
$ go build -o jit . && ./jit
called from JIT
goFunc
, JIT-:
func goFunc() {
println("called from JIT")
+ runtime.GC()
}
:
$ go build -o jit . && ./jit
called from JIT
runtime: unexpected return pc for main.goFunc called from 0x7f9465f7c007
stack: frame={sp:0xc00008ced0, fp:0xc00008cef0} stack=[0xc00008c000,0xc00008d000)
000000c00008cdd0: 0000000000000000 00007f94681f7558
000000c00008cde0: 000000c000029270 000000000000000b
... (+ more)
: unexpected return pc for main.goFunc called from 0x7f9465f7c007
, 0x7f9465f7c007
тАФ JIT-. , runtime.
, , FP
BP
return address , .
Go runtime , JIT-, , .
calljit
, , Go . , Go , , Go (calljit
).
#include "funcdata.h"
TEXT ┬╖calljit(SB), 0, $8-8
NO_LOCAL_POINTERS
MOVQ code+0(FP), AX
JMP AX
callgo:
CALL CX
JMP (SP)
:
- 8 , return address JIT .
NO_LOCAL_POINTERS
- , CALL
.
calljit
, , , Go . , CX
, [rsp]
тАФ , .
, callgo
. , . main()
:
a := funcAddr(goFunc)
j := funcAddr(calljit) + 36
code := []byte{
0x48, 0xc7, 0xc1, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
0x48, 0xc7, 0xc7, byte(j), byte(j >> 8), byte(j >> 16), byte(j >> 24),
0x48, 0x8d, 0x35, (4 + 2), 0, 0, 0,
0x48, 0x89, 0x34, 0x24,
0xff, 0xe7,
0x48, 0x83, 0xc4, (8 + 8),
0xc3,
}
CX
, callgo
DI
, SI
, [rsp]
. 4+2
тАФ LEAQ
.
, , ADDQ
. 8 8 BP
.
calljit
:
return address callgo
|
| Go BP
| |
0(SP) 8(SP) 16(SP) 24(SP)
[empty] [prevBP] [retaddr] [arg1:code]
| / | |
| / | calljit (caller frame)
| / |
| / CALL calljit
| /
calljit frame, 16 bytes
runtime.GC()
:
$ go build -o jit . && ./jit
called from JIT
Go . , тАФ , .
, , . Go.
Go Internal ABI
Go Internal ABI тАФ .
Go , , . ABI, , .
:
- .
- .
calling convention ABI0
, ABIInternal
.
Go -S
, , ABIInternal
, ABI0
:

ABIInternal
, ABI1
, . ABIInternal
calling convention .
, .
, Go . , .

Hub-
, . , , , . .