Does Golang inline functions ?

Felipe Dutra Tine e Silva
3 min readJan 11, 2017

--

I’m wondering if the compiler of golang is smart enough to inline functions ?

To check it I will write a small go program :

package mainimport "fmt"func main() {x := 40
y := 2
res := add(x, y)fmt.Println(res)}func add(x int, y int) int {
return x + y
}

So you have understood the scope, it’s a simple immutable add. And we expect to see 42 in result.

What is inline function ?

Inline function is when the compiler is smart enough, on some cases, to substitute the body of the inline function by inserting the function code at the address of each function call.

It means that in the assembler code generated, the compiler will generate the code like if it was :

func main() {x := 40
y := 2
//inline function add replace by the body of the function
res := x+y
fmt.Println(res)}

otherwise, if the compiler don’t optimize the code generate I should see in the assembly code , a CALL to the add function, something like :

401032: e8 89 00 00 00        callq  4010c0 <main.add>

The inline function has is pro and cons, I let you see on internet, if you want to know more.

Dissasemblying go code

To disassembly a code there are many ways, depending of your os.

Using linux, I would use in an exe that I don’t know if it is go or not objdump:

$ odbjdump -d [program_name]

But in go we can use go tools for that, and the result is much readable :

$ go tool objdump [program_name]

Anyway before begin I want to know, what the builder of go tells me about , what is the optimization the compiler will do in my small program.

For that I use go build, with the argument [-gcflags -m]

$ go build --help
[......]
-gcflags 'arg list'
arguments to pass on each go tool compile invocation.

and for the arg list of gcflags :

$ go build -gcflags --help
[.......]
-m print optimization decisions

So :

$ go build -gcflags -m main.go
# command-line-arguments
./main.go:16: can inline add
./main.go:10: inlining call to add
./main.go:12: res escapes to heap
./main.go:12: main ... argument does not escape

As you can see the build (with gcflags -m options) tell me that the compiler has inline the add function.

Let’s check that.

First I need to install my program :

$ go install github.com/tkanos/my-small-app

then on $GOPATH/bin/

$ go tool objdump my-small-app
[.........]
0x401000 64488b0c25f8ffffff FS MOVQ FS:0xfffffff8, CX
0x401009 483b6110 CMPQ 0x10(CX), SP
0x40100d 0f868a000000 JBE 0x40109d
0x401013 4883ec50 SUBQ $0x50, SP
0x401017 48896c2448 MOVQ BP, 0x48(SP)
0x40101c 488d6c2448 LEAQ 0x48(SP), BP
0x401021 48c74424302a000000 MOVQ $0x2a, 0x30(SP)
0x40102a 48c744243800000000 MOVQ $0x0, 0x38(SP)
0x401033 48c744244000000000 MOVQ $0x0, 0x40(SP)
0x40103c 488d055d900800 LEAQ 0x8905d(IP), AX
0x401043 48890424 MOVQ AX, 0(SP)
0x401047 488d442430 LEAQ 0x30(SP), AX
0x40104c 4889442408 MOVQ AX, 0x8(SP)
0x401051 48c744241000000000 MOVQ $0x0, 0x10(SP)
0x40105a e8819d0000 CALL runtime.convT2E(SB)
0x40105f 488b442418 MOVQ 0x18(SP), AX
0x401064 488b4c2420 MOVQ 0x20(SP), CX
0x401069 4889442438 MOVQ AX, 0x38(SP)
0x40106e 48894c2440 MOVQ CX, 0x40(SP)
0x401073 488d442438 LEAQ 0x38(SP), AX
0x401078 48890424 MOVQ AX, 0(SP)
0x40107c 48c744240801000000 MOVQ $0x1, 0x8(SP)
0x401085 48c744241001000000 MOVQ $0x1, 0x10(SP)
0x40108e e8bd430500 CALL fmt.Println(SB)
0x401093 488b6c2448 MOVQ 0x48(SP), BP
0x401098 4883c450 ADDQ $0x50, SP
0x40109c c3 RET
0x40109d e86ede0400 CALL runtime.morestack_noctxt(SB)
0x4010a2 e959ffffff JMP main.main(SB)

As you can see, there is no CALL except the ones use by fmt.Println , and we also see:

0x401021 48c74424302a000000 MOVQ $0x2a, 0x30(SP)

0x2a means 42. So the compiler was smart enough to inline function, and to do the addition.

Goals

So go compiler inline code when it find it’s necessary. It’s important if you want to optimize your code because inline function avoid to waste time on creating the frame stack and keeping the state of the current function. (If I have time I will try to write a post about that)

Anyway , to write better code, I would like to know what is the algorithm used by go compiler, if I find the answer I will write a new post on medium. :D

--

--