рдХреНрдпрд╛ рдЖрдк рдХреЛ рдкрддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рдЖрдк рдХреЛрдбрд╛рдВрддрд░рдХ рд╕реЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХреА рдЬрд░реВрд░рдд рд╣реИ

рдЖрдк 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) // => 3, 11
}

// 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


// file jit.go
package main

import (
    "log"
    "reflect"
    "syscall"
    "unsafe"
)

func main() {
    a := funcAddr(goFunc)

    code := []byte{
        // MOVQ addr(goFunc), AX
        0xb8, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
        // CALL AX
        0xff, 0xd0,
        // RET
        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{
    // MOVQ funcAddr(goFunc), CX
    0x48, 0xc7, 0xc1, byte(a), byte(a >> 8), byte(a >> 16), byte(a >> 24),
    // MOVQ funcAddr(calljit), DI
    0x48, 0xc7, 0xc7, byte(j), byte(j >> 8), byte(j >> 16), byte(j >> 24),
    // LEAQ 6(PC), SI
    0x48, 0x8d, 0x35, (4 + 2), 0, 0, 0,
    // MOVQ SI, (SP)
    0x48, 0x89, 0x34, 0x24,
    // JMP DI
    0xff, 0xe7,

    // ADDQ $framesize, SP
    0x48, 0x83, 0xc4, (8 + 8),
    // RET
    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, , .


:


  1. .
  2. .

calling convention ABI0, ABIInternal.


Go -S, , ABIInternal , ABI0:



ABIInternal , ABI1, . ABIInternal calling convention .


, .


, Go . , .





Hub-


, . , , , . .


All Articles