Location>code7788 >text

Go plan9 assembly: memory alignment and recursion

Popularity:78 ℃/2024-09-02 10:27:58

Go plan9 compilation series of articles:

  • Go plan9 compilation: Getting through the application to the ground floor
  • Go plan9 assembly: handwritten assembly
  • Go plan9 assembly: talking through the function stack

0. Preface

In the Go plan9 assembly series of articles, function and function stack calls were introduced. Here we continue to look at the memory alignment and recursive call aspects.

1. Memory alignment

Straight to the example:

type temp struct {
	a bool
	b int16
	c []string
}

func main() {
	var t = temp{a: true, b: 1, c: []string{}}
	((t))
}

Output:

32

Rewrites the temp structure member variable position:

type temp struct {
	a bool
    c []string
	b int16
}

func main() {
	var t = temp{a: true, b: 1, c: []string{}}
	((t))
}

Output:

40

Why does moving down the position of a structure member have an effect on the size of the structure in memory?

The address of the structure member variable in the print example is as follows:

# Example 1
func main() {
var t = temp{a: true, b: 1, c: []string{}}

((), (), ())
("%p %p %p %p\n", &t, &, &, &, &)

((t))
}

# go run
1 2 24
0xc0000a4000 0xc0000a4000 0xc0000a4002 0xc0000a4008
32

# Example 2
func main() {
var t = temp{a: true, b: 1, c: []string{}}

((), (), ())
("%p %p %p %p %p\n", &t, &, &, &, &)

((t))
}

# go run
1 24 2
0xc00006e090 0xc00006e090 0xc00006e098 0xc00006e0b0
40

As you can see, memory alignment is followed when allocating memory for structures. Memory alignment is used to simplify addressing so that the CPU can find the location of a variable at once. Because of memory alignment, the variable a in Example 2 takes up 8 bytes, even though it only takes up 1 byte, which is a memory drain on the code and should be avoided.

2. Recursion

Let's look at an example of recursion:

func main() {
	println(sum(1000))
}

//go:nosplit
func sum(n int) int {
	if n > 0 {
		return n + sum(n-1)
	} else {
		return 0
	}
}

Output:

# go run  
# command-line-arguments
: nosplit stack over 792 byte limit
<1>
    grows 24 bytes, calls <1>
    infinite cycle

Here we are atsum prefix//go:nosplit is to declare that this function is non-stack-splittable. Meaning that when the function stack is full, no new space will be opened (by the memory allocator) for it.

Go allocates 2K of initial stack space for goroutines. If the main stack plus the sum stack of the nosplit exceeds 2K, the stack will explode.

commander-in-chief (military)//go:nosplit Take it out and re-execute it:

func main() {
	println(sum(100000))
}

func sum(n int) int {
	if n > 0 {
		return n + sum(n-1)
	} else {
		return 0
	}
}

Output:

5000050000

in that casesum Is it possible to have infinite recursion? We givesum A large number 10000000000000, then re-execute:

runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200f8398 stack=[0xc0200f8000, 0xc0400f8000]
fatal error: stack overflow

exportsstack overflowIf the stack of the main program is from 0xc0200f8000 to 0xc0400f8000, the recursive stack exceeds the maximum limit of the goroutine's stack.1000000000-byte(exceeded means that the stack of the main stack plus the stack of the sum recursive call exceeds the maximum limit), which is 1G.