为什么在make([] int,14)中有一个runtime.morestack但在make([] int,13)中没有?

I've already known that the runtime.morestack will cause goroutine context switch (If the sysmon goroutine has marked it "has to switch").

And when I do some experiment around this, I've found an interesting fact.

Compare the following codes.

func main() {
    _ = make([]int, 13)
}

func main() {
    _ = make([]int, 14)
}

And compile them by running the following command: (Tried in go1.9 and go 1.11)

$ go build -gcflags "-S -l -N" x.go

You may find a major difference that the first outputs contains CALL runtime.morestack_noctxt(SB) while the second doesn't.

I guess it is an optimization, but why?

Finally, I got the answer.

  • Making a slice that less than 65,536 bytes and not escaped from the func will be allocated in the stack, not the heap.

  • StackGuard0 will be higher than the stack's lowest address by at least 128 bytes. (Even after downsizing)

  • make([]int, 13) will allocate 128 bytes memories in total.

sizeof(struct slice) + 13 * 8 = 24 + 104 = 128.

So the answer is clear, this is an optimization for amd64.

For a leaf function, if it used less than 128 bytes memories, the compiler wouldn't generate codes that checking if the stack is overflowed (because there are enough spaces).

Here is the explanation in go/src/runtime/stack.go

The per-goroutine g->stackguard is set to point StackGuard bytes above the bottom of the stack. Each function compares its stack pointer against g->stackguard to check for overflow. To cut one instruction from the check sequence for functions with tiny frames, the stack is allowed to protrude StackSmall bytes below the stack guard. Functions with large frames don't bother with the check and always call morestack. The sequences are (for amd64, others are similar):