Node Trees

Now it's time for something completely different and low level. You are free to skip this section if you like, but I bet that you are not reading this book for its easy topics but for the difficult ones, such as node trees!

The go tool 6g -W test.go command does not work on newer Go versions. You should use go tool compile -W test.go instead.

A Go node is a struct with a large number of properties. You will learn more about defining and using Go structures in Chapter 4, The Uses of Composite Types. Everything in a Go program is being parsed and analyzed by the modules of the Go compiler according to the grammar of the Go programming language. The final product of this analysis is a tree that is specific to the provided Go code, and it represents the program in a different way that is suited for the compiler rather than for the developer.

This section will start with the following Go code, which is saved as nodeTree.go. It is presented here as an example of the kind of low-level information that the go tool can provide:

package main

import (
   "fmt"
)

func main() {
   fmt.Println("Hello there!")
}

The Go code of nodeTree.go is pretty easy to understand, so you will not be surprised by its output, which comes next:

$ go run nodeTree.go
Hello there!

Now it's time to see some of the internal workings of Go by executing the following command:

$ go tool compile -W nodeTree.go
before main
.   CALLFUNC l(8) tc(1) STRUCT-(int, error)
.   .   NAME-fmt.Println a(true) l(4) x(0) class(PFUNC) tc(1) used FUNC-func(...interface {}) (int, error)
.   .   DDDARG l(8) esc(no) PTR64-*[1]interface {}
.   CALLFUNC-list
.   .   CONVIFACE l(8) esc(h) tc(1) implicit(true) INTER-interface {}
.   .   .   NAME-main.statictmp_0 a(true) l(8) x(0) class(PEXTERN) f(1) tc(1) used string
    
.   VARKILL l(8) tc(1)
.   .   NAME-main..autotmp_0 a(true) l(8) x(0) class(PAUTO) esc(N) used ARRAY-[1]interface {}
after walk main
.   CALLFUNC-init
.   .   AS l(8) tc(1)
.   .   .   NAME-main..autotmp_0 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) addrtaken assigned used ARRAY-[1]interface {}
    
.   .   AS l(8) tc(1)
.   .   .   NAME-main..autotmp_2 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used PTR64-*[1]interface {}
.   .   .   ADDR l(8) tc(1) PTR64-*[1]interface {}
.   .   .   .   NAME-main..autotmp_0 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) addrtaken assigned used ARRAY-[1]interface {}
    
.   .   BLOCK l(8)
.   .   BLOCK-list
.   .   .   AS l(8) tc(1) hascall
.   .   .   .   INDEX l(8) tc(1) assigned bounded hascall INTER-interface {}
.   .   .   .   .   IND l(8) tc(1) implicit(true) assigned hascall ARRAY-[1]interface {}
.   .   .   .   .   .   NAME-main..autotmp_2 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used PTR64-*[1]interface {}
.   .   .   .   .   LITERAL-0 a(true) l(8) tc(1) int
.   .   .   .   EFACE l(8) tc(1) INTER-interface {}
.   .   .   .   .   ADDR a(true) l(8) tc(1) PTR64-*uint8
.   .   .   .   .   .   NAME-type.string a(true) x(0) class(PEXTERN) tc(1) uint8
.   .   .   .   .   ADDR l(8) tc(1) PTR64-*string
.   .   .   .   .   .   NAME-main.statictmp_0 a(true) l(8) x(0) class(PEXTERN) f(1) tc(1) addrtaken used string
    
.   .   BLOCK l(8)
.   .   BLOCK-list
.   .   .   AS l(8) tc(1) hascall
.   .   .   .   NAME-main..autotmp_1 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used SLICE-[]interface {}
.   .   .   .   SLICEARR l(8) tc(1) hascall SLICE-[]interface {}
.   .   .   .   .   NAME-main..autotmp_2 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used PTR64-*[1]interface {}
.   CALLFUNC l(8) tc(1) hascall STRUCT-(int, error)
.   .   NAME-fmt.Println a(true) l(4) x(0) class(PFUNC) tc(1) used FUNC-func(...interface {}) (int, error)
.   .   DDDARG l(8) esc(no) PTR64-*[1]interface {}
.   CALLFUNC-list
.   .   AS l(8) tc(1)
.   .   .   INDREGSP-SP a(true) l(8) x(0) tc(1) addrtaken main.__ SLICE-[]interface {}
.   .   .   NAME-main..autotmp_1 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used SLICE-[]interface {}
    
.   VARKILL l(8) tc(1)
.   .   NAME-main..autotmp_0 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) addrtaken assigned used ARRAY-[1]interface {}
    
before init
.   IF l(1) tc(1)
.   .   GT l(1) tc(1) bool
.   .   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   .   LITERAL-1 a(true) l(1) tc(1) uint8
.   IF-body
.   .   RETURN l(1) tc(1)
    
.   IF l(1) tc(1)
.   .   EQ l(1) tc(1) bool
.   .   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   .   LITERAL-1 a(true) l(1) tc(1) uint8
.   IF-body
.   .   CALLFUNC l(1) tc(1)
.   .   .   NAME-runtime.throwinit a(true) x(0) class(PFUNC) tc(1) used FUNC-func()
    
.   AS l(1) tc(1)
.   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   LITERAL-1 a(true) l(1) tc(1) uint8
    
.   CALLFUNC l(1) tc(1)
.   .   NAME-fmt.init a(true) l(4) x(0) class(PFUNC) tc(1) used FUNC-func()
    
.   AS l(1) tc(1)
.   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   LITERAL-2 a(true) l(1) tc(1) uint8
    
.   RETURN l(1) tc(1)
after walk init
.   IF l(1) tc(1)
.   .   GT l(1) tc(1) bool
.   .   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   .   LITERAL-1 a(true) l(1) tc(1) uint8
.   IF-body
.   .   RETURN l(1) tc(1)
    
.   IF l(1) tc(1)
.   .   EQ l(1) tc(1) bool
.   .   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   .   LITERAL-1 a(true) l(1) tc(1) uint8
.   IF-body
.   .   CALLFUNC l(1) tc(1) hascall
.   .   .   NAME-runtime.throwinit a(true) x(0) class(PFUNC) tc(1) used FUNC-func()
    
.   AS l(1) tc(1)
.   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   LITERAL-1 a(true) l(1) tc(1) uint8
    
.   CALLFUNC l(1) tc(1) hascall
.   .   NAME-fmt.init a(true) l(4) x(0) class(PFUNC) tc(1) used FUNC-func()
    
.   AS l(1) tc(1)
.   .   NAME-main.initdone  a(true) l(1) x(0) class(PEXTERN) tc(1) assigned used uint8
.   .   LITERAL-2 a(true) l(1) tc(1) uint8
    
.   RETURN l(1) tc(1)

As you can appreciate, the Go compiler and its tools do many things behind the scenes, even for a small program such as nodeTree.go.

The -W parameter tells the go tool compile command to print the debug parse tree after the type checking.

Look at the output of the next two commands:

$ go tool compile -W nodeTree.go | grep before
before main
before init
$ go tool compile -W nodeTree.go | grep after
after walk main
after walk init

As you can see, the before keyword is about the beginning of the execution of a function. If your program had more functions, you would have gotten more output. This is illustrated in the following example:

$ go tool compile -W defer.go | grep before
before d1
before d2
before d3
before main
before d2.func1
before d3.func1
before init
before type..hash.[2]interface {}
before type..eq.[2]interface {}

The preceding example uses the Go code of defer.go, which is much more complicated than nodeTree.go. However, it should be obvious than the init() function is automatically created by Go as it exists in both examples (nodeTree.go and defer.go).

I will now present you with a juicier version of nodeTree.go, named nodeTreeMore.go:

package main

import (
   "fmt"
)

func functionOne(x int) {
   fmt.Println(x)
}

func main() {
   varOne := 1
   varTwo := 2
   fmt.Println("Hello there!")
   functionOne(varOne)
   functionOne(varTwo)
}

The nodeTreeMore.go program has two variables, named varOne and varTwo, and one additional function named functionOne. Searching the output of go tool compile -W for varOne, varTwo, and functionOne reveals the following information:

$ go tool compile -W nodeTreeMore.go  | grep functionOne | uniq
before functionOne
after walk functionOne
.   .   NAME-main.functionOne a(true) l(7) x(0) class(PFUNC) tc(1) used FUNC-func(int)
$ go tool compile -W nodeTreeMore.go  | grep varTwo | uniq
.   .   NAME-main.varTwo a(true) g(2) l(13) x(0) class(PAUTO) f(1) tc(1) used int
.   .   .   NAME-main.varTwo a(true) g(2) l(13) x(0) class(PAUTO) f(1) tc(1) used int
$ go tool compile -W nodeTreeMore.go  | grep varOne | uniq
.   .   NAME-main.varOne a(true) g(1) l(12) x(0) class(PAUTO) f(1) tc(1) used int
.   .   .   NAME-main.varOne a(true) g(1) l(12) x(0) class(PAUTO) f(1) tc(1) used int  

Thus, varOne is represented as NAME-main.varOne while varTwo is indicated by NAME-main.varTwo. The functionOne() function is referenced as NAME-main.functionOne. Consequently, the main() function is referenced as NAME-main.

Now let's examine the following code of the debug parse tree of nodeTreeMore.go:

before functionOne
.   AS l(8) tc(1)
.   .   NAME-main..autotmp_2 a(true) l(8) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   NAME-main.x a(true) g(1) l(7) x(0) class(PPARAM) f(1) tc(1) used int

This data is related to the definition of functionOne(). The l(8) string tells us that the definition of this node can be found in line 8; that is, after reading line 7. The NAME-main..autotmp_2 integer variable is automatically generated by the compiler.

The next part of the debug parse tree output that I will explain below follows:

.   CALLFUNC l(15) tc(1)
.   .   NAME-main.functionOne a(true) l(7) x(0) class(PFUNC) tc(1) used FUNC-func(int)
.   CALLFUNC-list
.   .   NAME-main.varOne a(true) g(1) l(12) x(0) class(PAUTO) f(1) tc(1) used int

The first line says that in line 15 of the program, which is specified by l(15), you will call NAME-main.functionOne, which is defined in line 7 of the program, as specified by l(7), which is a function that requires a single integer parameter, as specified by FUNC-func(int). The function list of parameters, which is specified after CALLFUNC-list, includes the NAME-main.varOne variable that is defined in line 12 of the program as l(12) shows.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.34.117