Cgo и build дълбини

12.12.2018

Но преди това

Въпроси за мъфини

Въпрос #1

Какво прави go generate ./...? Как?

Въпрос #2

Къде можете да видите документацията на gitlab.com/ivan/libawesome?

Въпрос #3

Кога работи вградения race detector? Как се ползва?

Въпрос #4

Как мога да направя файл в пакет, който се build-ва само в Линукс?

Въпрос #5

Днес ще видите

C? Go? Cgo!

C в .go файлове

package main

/*
#include <stdlib.h>
*/
import "C"

func Random() int {
    return int(C.random())
}

func Seed(i int) {
    C.srandom(C.uint(i))
}

Особености

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"
// #cgo pkg-config: png cairo

Go в .c файлове (1)

package goc

import "C"

//export GreetFromGo
func GreetFromGo(name string) {
    println("Hello from Go, ", name)
}

func main() {
    // Needed by cgo in order to generate a library
}

Go в .c файлове (2)

go build -buildmode=c-archive -o goc.a goc.go
extern void GreetFromGo(GoString p0);

Go в .c файлове (3)

#include "goc.h"
#include <stdio.h>

int main() {
  printf("Hi, I am a C program.\n");
  GoString name = {"Doycho", 4};
  GreetFromGo(name);
  return 0;
}
gcc -pthread -o out goc.c goc.a

Никога не е толкова просто

Стигне ли се до компилиране на C, забравете за лесно:

Но, за това пък, има много от:

Ситният шрифт (2)

Споделяне на памет, алокирана от Go е възможно ако:

runtime проверява за това и ако види нарушение crash-ва програмата.

Още от магиите на "C"

Error handling

n, err := C.sqrt(-1)

Function pointers

Мoля?

package main

// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
//      return f();
// }
//
// int fortytwo()
// {
//      return 42;
// }
import "C"
import "fmt"

func main() {
    f := C.intFunc(C.fortytwo)
    fmt.Println(int(C.bridge_int_func(f)))
    // Output: 42
}

Друг начин за викане на Go от C

package main

func Add(a, b int) int {
    return a + b
}

Друг начин за викане на Go от C (2)

#include <stdio.h>

extern int go_add(int, int) __asm__ ("example.main.Add");

int main() {
  int x = go_add(2, 3);
  printf("Result: %d\n", x);
}

Друг начин за викане на Go от C (3)

all: main

main: foo.o bar.c
    gcc foo.o bar.c -o main

foo.o: foo.go
    gccgo -c foo.go -o foo.o -fgo-prefix=example

clean:
    rm -f main *.o

Указатели без граници

package unsafe

Декларира невинно изглеждащите:

реално тези дефиниции съществуват главно за документация, имплементацията е в компилатора.

Safety third

unsafe.Pointer има четири важни харектеристики

Това е практическо заобикаляне на типовата система в Go.

Unsafe пример:

package main

/*
#include <stdlib.h>
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    cs := C.CString("42")            // alloc on C's heap
    defer C.free(unsafe.Pointer(cs)) // don't leak
    answer := C.atoi(cs)
    fmt.Println(answer)
}

Адресна аритметика:

package main

import "unsafe"
import "fmt"

type slice struct {
    array      unsafe.Pointer
    size, _cap int
}

func main() {
    var p = []string{"Hello", " "}
    p = append(p, "World!")
    var s = (*slice)(unsafe.Pointer(&p))
    var sizeOfString = unsafe.Sizeof("")
    fmt.Printf("size=%d, cap=%d\n", s.size, s._cap)
    for i := 0; s.size > i; i++ {
        fmt.Printf("[%d]: `%s`\n", i,
            *(*string)(unsafe.Pointer(uintptr(s.array) + uintptr(i)*sizeOfString)))
    }
}

Go in Go

С? Защо?

Компилатор

Runtime

Как се махат хиляди редове С?

Също преминали в Go Land:

Go assembler

Go assembler

IBM System/360

1        PRINT NOGEN
2 STOCK1 START 0
3 BEGIN  BALR  11,0
4        USING *,11
5        MVC   NEWOH,OLDOH
6        AP    NEWOH,RECPT
7        AP    NEWOH,ISSUE
8        EOJ
11 OLDOH DC    PL4'9'
12 RECPT DC    PL4'4'
13 ISSUE DC    PL4'6'
14 NEWOH DS    PL4
15       END   BEGIN

Apollo 11 Guidance Computer

# TO ENTER A JOB REQUEST REQUIRING NO VAC AREA:

          COUNT     02/EXEC
                
NOVAC     INHINT
          AD        FAKEPRET     # LOC(MPAC +6) - LOC(QPRET)
          TS        NEWPRIO      # PRIORITY OF NEW JOB + NOVAC C(FIXLOC)

          EXTEND
          INDEX     Q            # Q WILL BE UNDISTURBED THROUGHOUT.
          DCA       0            # 2CADR OF JOB ENTERED.
          DXCH      NEWLOC
          CAF       EXECBANK
          XCH       FBANK
          TS        EXECTEM1
          TCF       NOVAC2       # ENTER EXECUTIVE BANK.

Използван за ходене до луната

Маргарет Хамилтън

PDP-10

TITLE   COUNT
 
A=1                             ;Define a name for an accumulator.

START:  MOVSI A,-100            ;initialize loop counter.
                                ;A contains -100,,0
LOOP:   HRRZM A,TABLE(A)        ;Use right half of A to index.
        AOBJN A,LOOP            ;Add 1 to both halves (-77,,1 -76,,2 etc.)
                                ;Jump if still negative.
        .VALUE                  ;Halt program.

TABLE:  BLOCK 100               ;Assemble space to fill up.

END START                       ;End the assembly.

PDP-11

/ a3 -- pdp-11 assembler pass 1

assem:
        jsr     pc,readop
        jsr     pc,checkeos
        br      ealoop
        tst     ifflg
        beq     3f
        cmp     r4,$200
        blos    assem
        cmpb    (r4),$21   /if
        bne     2f
        inc     ifflg
2:
        cmpb    (r4),$22   /endif
        bne     assem
        dec     ifflg
        br      assem

Motorola 68000

strtolower      public
                link    a6,#0           ;Set up stack frame
                movea   8(a6),a0        ;A0 = src, from stack
                movea   12(a6),a1       ;A1 = dst, from stack
loop            move.b  (a0)+,d0        ;Load D0 from (src)
                cmpi    #'A',d0         ;If D0 < 'A',
                blo     copy            ;skip
                cmpi    #'Z',d0         ;If D0 > 'Z',
                bhi     copy            ;skip
                addi    #'a'-'A',d0     ;D0 = lowercase(D0)
copy            move.b  d0,(a1)+        ;Store D0 to (dst)
                bne     loop            ;Repeat while D0 <> NUL
                unlk    a6              ;Restore stack frame
                rts                     ;Return
                end

CRAY-1

ident slice
         V6        0               ; initialize S
         A4        S0              ; initialize *x
         A5        S1              ; initialize *y
         A3        S2              ; initialize i
loop     S0        A3
         JSZ       exit            ; if S0 == 0 goto exit
         VL        A3              ; set vector length
         V11       ,A4,1           ; load slice of x[i], stride 1
         V12       ,A5,1           ; load slice of y[i], stride 1
         V13       V11 *F V12      ; slice of x[i] * y[i]
         V6        V6 +F V13       ; partial sum
         A14       VL              ; get vector length of this iteration
         A4        A4 + A14        ; *x = *x + VL
         A5        A5 + A14        ; *y = *y + VL
         A3        A3 - A14        ; i = i - VL
         J        loop
 exit

Go assembler

Имат доста обща структура:

subroutine header
label:
    instruction operand...    ; comment
    ...

Нека ги направим еднакви!

Чрез собствен assembler, който след това лесно се превежда до реалния на всяка машина.

Един пример

Нека разгледаме go програмата

package add

func add(a, b int) int {
    return a + b
}

И сега генерирания assembly

32-bit x86 (386)

TEXT add(SB), $0-12
    MOVL    a+4(FP), BX
    ADDL    b+8(FP), BX
    MOVL    BX, 12(FP)
    RET

64-bit x86 (amd64)

TEXT add(SB), $0-24
    MOVQ    b+16(FP), AX
    MOVQ    a+8(FP), CX
    ADDQ    CX, AX
    MOVQ    AX, 24(FP)
    RET

64-bit arm (arm64)

TEXT add(SB), $-8-24
    MOVD    a(FP), R0
    MOVD    b+8(FP), R1
    ADD     R1, R0
    MOVD    R0, 16(FP)
    RET

S390 (s390x)

TEXT add(SB), $0-24
    MOVD    a(FP), R1
    MOVD    b+8(FP), R2
    ADD     R2, R1, R1
    MOVD    R1, 16(FP)
    RET

64-bit Power (ppc64le)

TEXT add(SB), $0-24
    MOVD    a(FP), R2
    MOVD    b+8(FP), R3
    ADD     R3, R2
    MOVD    R2, 16(FP)
    RET

... and so on

На практика са еднакви.

Какво се печели?

Пример?

Повече от командир Pike

Go linker

GODEBUG

Добре, компилира се!

Повече информация

export GODEBUG="name=flag"

Пример:

export GODEBUG="gctrace=2,invalidptr=1"

Позволява:

...

GOGC

GOMAXPROCS

Въпроси?