Where is the func append implementation in Go?

I am very interested in going and trying to read go implementation functions. I found that some of these functions have no implementations.

Such as append or call:

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//  slice = append(slice, elem1, elem2)
//  slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//  slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

// call calls fn with a copy of the n argument bytes pointed at by arg.
// After fn returns, reflectcall copies n-retoffset result bytes
// back into arg+retoffset before returning. If copying result bytes back,
// the caller must pass the argument frame type as argtype, so that
// call can execute appropriate write barriers during the copy.
func call(argtype *rtype, fn, arg unsafe.Pointer, n uint32, retoffset uint32)

It does not seem to call C code because using cgo requires special comments. Where are these function implementations?

+4
source share
3 answers

The code you read and quote is just dummy code to have consistent documentation. Built-in functions are built into the language and, as such, are included in the code processing stage (compiler).

, : lexer 'append(...)' APPEND, APPEND, // , . - APPEND - .

APPEND . :

b := []byte{'a'}
b = append(b, 'b')
println(string(b), cap(b))

:

ab 2

APPEND :

// create new slice object
MOVQ    BX, "".b+120(SP)       // BX contains data addr., write to b.addr
MOVQ    BX, CX                 // store addr. in CX
MOVQ    AX, "".b+128(SP)       // AX contains len(b) == 1, write to b.len
MOVQ    DI, "".b+136(SP)       // DI contains cap(b) == 1, write to b.cap
MOVQ    AX, BX                 // BX now contains len(b)
INCQ    BX                     // BX++
CMPQ    BX, DI                 // compare new length (2) with cap (1)
JHI $1, 225                    // jump to grow code if len > cap
...
LEAQ    (CX)(AX*1), BX         // load address of newly allocated slice entry
MOVB    $98, (BX)              // write 'b' to loaded address

// grow code, call runtime.growslice(t *slicetype, old slice, cap int)
LEAQ    type.[]uint8(SB), BP
MOVQ    BP, (SP)               // load parameters onto stack
MOVQ    CX, 8(SP)
MOVQ    AX, 16(SP)
MOVQ    SI, 24(SP)
MOVQ    BX, 32(SP)
PCDATA  $0, $0
CALL    runtime.growslice(SB)  // call
MOVQ    40(SP), DI
MOVQ    48(SP), R8
MOVQ    56(SP), SI
MOVQ    R8, AX
INCQ    R8
MOVQ    DI, CX
JMP 108                        // jump back, growing done

, CALL APPEND . APPEND . - ( , ..).

+11

Go append Go gc gccgo Go package runtime (, runtime.growslice()) go/src/runtime/slice.go.

,

package main

func main() {
    b := []int{0, 1}
    b = append(b, 2)
}

-:

$ go tool compile -S a.go

"".main t=1 size=192 value=0 args=0x0 locals=0x68
    0x0000 00000 (a.go:3)   TEXT    "".main(SB), $104-0
    0x0000 00000 (a.go:3)   MOVQ    (TLS), CX
    0x0009 00009 (a.go:3)   CMPQ    SP, 16(CX)
    0x000d 00013 (a.go:3)   JLS 167
    0x0013 00019 (a.go:3)   SUBQ    $104, SP
    0x0017 00023 (a.go:3)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0017 00023 (a.go:3)   FUNCDATA    $1, gclocals·790e5cc5051fc0affc980ade09e929ec(SB)
    0x0017 00023 (a.go:4)   LEAQ    "".autotmp_0002+64(SP), BX
    0x001c 00028 (a.go:4)   MOVQ    BX, CX
    0x001f 00031 (a.go:4)   NOP
    0x001f 00031 (a.go:4)   MOVQ    "".statictmp_0000(SB), BP
    0x0026 00038 (a.go:4)   MOVQ    BP, (BX)
    0x0029 00041 (a.go:4)   MOVQ    "".statictmp_0000+8(SB), BP
    0x0030 00048 (a.go:4)   MOVQ    BP, 8(BX)
    0x0034 00052 (a.go:4)   NOP
    0x0034 00052 (a.go:4)   MOVQ    $2, AX
    0x003b 00059 (a.go:4)   MOVQ    $2, DX
    0x0042 00066 (a.go:5)   MOVQ    CX, "".b+80(SP)
    0x0047 00071 (a.go:5)   MOVQ    AX, "".b+88(SP)
    0x004c 00076 (a.go:5)   MOVQ    DX, "".b+96(SP)
    0x0051 00081 (a.go:5)   MOVQ    AX, BX
    0x0054 00084 (a.go:5)   INCQ    BX
    0x0057 00087 (a.go:5)   CMPQ    BX, DX
    0x005a 00090 (a.go:5)   JHI $1, 108
    0x005c 00092 (a.go:5)   LEAQ    (CX)(AX*8), BX
    0x0060 00096 (a.go:5)   MOVQ    $2, (BX)
    0x0067 00103 (a.go:6)   ADDQ    $104, SP
    0x006b 00107 (a.go:6)   RET
    0x006c 00108 (a.go:5)   LEAQ    type.[]int(SB), BP
    0x0073 00115 (a.go:5)   MOVQ    BP, (SP)
    0x0077 00119 (a.go:5)   MOVQ    CX, 8(SP)
    0x007c 00124 (a.go:5)   MOVQ    AX, 16(SP)
    0x0081 00129 (a.go:5)   MOVQ    DX, 24(SP)
    0x0086 00134 (a.go:5)   MOVQ    BX, 32(SP)
    0x008b 00139 (a.go:5)   PCDATA  $0, $0
    0x008b 00139 (a.go:5)   CALL    runtime.growslice(SB)
    0x0090 00144 (a.go:5)   MOVQ    40(SP), CX
    0x0095 00149 (a.go:5)   MOVQ    48(SP), AX
    0x009a 00154 (a.go:5)   MOVQ    56(SP), DX
    0x009f 00159 (a.go:5)   MOVQ    AX, BX
    0x00a2 00162 (a.go:5)   INCQ    BX
    0x00a5 00165 (a.go:5)   JMP 92
    0x00a7 00167 (a.go:3)   CALL    runtime.morestack_noctxt(SB)
    0x00ac 00172 (a.go:3)   JMP 0
+3

, , Go (1.5.1) gc: https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/cmd/compile/internal/gc/walk.go#L2895

// expand append(l1, l2...) to
//   init {
//     s := l1
//     if n := len(l1) + len(l2) - cap(s); n > 0 {
//       s = growslice_n(s, n)
//     }
//     s = s[:len(l1)+len(l2)]
//     memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
//   }
//   s
//
// l2 is allowed to be a string.

growslice_n: https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/slice.go#L36

// growslice_n is a variant of growslice that takes the number of new elements
// instead of the new minimum capacity.
// TODO(rsc): This is used by append(slice, slice...).
// The compiler should change that code to use growslice directly (issue #11419).
func growslice_n(t *slicetype, old slice, n int) slice {
    if n < 1 {
        panic(errorString("growslice: invalid n"))
    }
    return growslice(t, old, old.cap+n)
}

// growslice handles slice growth during append.
// It is passed the slice type, the old slice, and the desired new minimum capacity,
// and it returns a new slice with at least that capacity, with the old data
// copied into it.
func growslice(t *slicetype, old slice, cap int) slice {
    if cap < old.cap || t.elem.size > 0 && uintptr(cap) > _MaxMem/uintptr(t.elem.size) {
        panic(errorString("growslice: cap out of range"))
    }

    if raceenabled {
        callerpc := getcallerpc(unsafe.Pointer(&t))
        racereadrangepc(old.array, uintptr(old.len*int(t.elem.size)), callerpc, funcPC(growslice))
    }

    et := t.elem
    if et.size == 0 {
        // append should not create a slice with nil pointer but non-zero len.
        // We assume that append doesn't need to preserve old.array in this case.
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }

    newcap := old.cap
    if newcap+newcap < cap {
        newcap = cap
    } else {
        for {
            if old.len < 1024 {
                newcap += newcap
            } else {
                newcap += newcap / 4
            }
            if newcap >= cap {
                break
            }
        }
    }

    if uintptr(newcap) >= _MaxMem/uintptr(et.size) {
        panic(errorString("growslice: cap out of range"))
    }
    lenmem := uintptr(old.len) * uintptr(et.size)
    capmem := roundupsize(uintptr(newcap) * uintptr(et.size))
    newcap = int(capmem / uintptr(et.size))
    var p unsafe.Pointer
    if et.kind&kindNoPointers != 0 {
        p = rawmem(capmem)
        memmove(p, old.array, lenmem)
        memclr(add(p, lenmem), capmem-lenmem)
    } else {
        // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
        p = newarray(et, uintptr(newcap))
        if !writeBarrierEnabled {
            memmove(p, old.array, lenmem)
        } else {
            for i := uintptr(0); i < lenmem; i += et.size {
                typedmemmove(et, add(p, i), add(old.array, i))
            }
        }
    }

    return slice{p, old.len, newcap}
}
+1

Source: https://habr.com/ru/post/1613579/


All Articles