Deep analysis of Golang multiple value return and closure implementation _golang

Source: Internet
Author: User
Tags anonymous closure subq

First, the preface

Golang has a lot of novel characteristics, do not know the use of the time, have not thought about how these features are achieved? Of course you might say, do not understand these features do not seem to affect their use of Golang, you said that there is a reason, but, more understanding of the bottom of the implementation of the principle of the use of Golang when the vision is completely different, similar to the implementation of the HTTP, then to use the HTTP framework, It's not the same as not looking at the HTTP framework, of course, if you are an IT enthusiast, Curiosity will naturally lead you to learn.

Second, this article mainly on the analysis of two points:

1, Golang The realization of multiple value return;

2, Golang closure of the implementation of the package;

Implementation of Golang Multi-value return

When we were learning C + +, a lot of people should have known the C/s + + function call procedure, the parameters are passed registers di and Si (suppose to two parameters) pass to the called function, the returned result of the called function can only be returned by the EAX register to the calling function, so the C + + function can only return a value, So is it possible to imagine that Golang multivalued returns can be achieved through multiple registers, just as with multiple registers to pass the argument?

This is also a method, but Golang is not adopted; My understanding is that the introduction of multiple registers to store return values can cause multiple registers to be renewed, which undoubtedly adds complexity; So, Golang's Abi is very different from C + +;

In the analysis of Golang from the assembly point of view before the return, you need to familiarize yourself with the Golang assembly code of some of the conventions, Golang official website has instructions, here is an emphasis on four symbols, note that the register here is a pseudo register:

1.FP Stack Bottom register, pointing to the top of a function stack;

2.PC program counter, pointing to the next execution instruction;

3.SB pointer to static data, global symbol;

4.SP stack top register;

The most important thing here is that the FP and SP,FP registers are used primarily for parameters and for storing return values, and the implementation of Golang function calls is largely dependent on both registers, where the results are first given,

+-----------+---\
| return value 2 | \
+-----------+  \
| return value 1 |  \
+---------+-+  
| parameter 2 |  These are in the calling function
+-----------+  
| parameter 1 | /
+-----------+  /
| return address |/
+-----------+--\/-----fp value
| local variable | |
called number stack zhen
|< c18/>| /
+-----------+--/+---SP value

This is the Golang function stack, which is also said that function arguments are fp+offset implemented by, and that multiple return values are fp+offset stored in the stack frame of the calling function.

The following is an example to analyze

Package main

Import "FMT"

func Test (i, J int) (int, int) {
a:=i+ J
b:=i-j return
 a,b
}

func m Ain () {
a,b:= Test (2,1)
 FMT. Println (A, b)
}

This example is very simple, mainly to illustrate the process of Golang multivalued return; We compile the program with the following command

go tool compile -S test.go > test.s

Then, you can open the TEST.S to see the assembly code for this applet. First, let's look at the assembly code for the test function.

"". Test t=1size=32value=0args=0x20locals=0x0
0x000000000 (test.go:5) TEXT "". Test (SB), $0-32//stack size is 32 bytes
0x000000000 (test.go:5) NOP
0x000000000 (test.go:5) NOP 0x000000000
(test.go:5) movq "". I+8 (FP), cx//take the first parameter I
0x000500005 (test.go:5) movq "". J+16 (FP), ax//take the second parameter J
0x000a00010 (test.go:5) funcdata$0, Gclocals a8eabfc4a4514ed6b3b0c61e9680e440 (SB)
0x000a00010 (test.go:5) funcdata$1, Gclocals 33CDECCCCEBE80329F1FDBEE7F5874CB (SB)
0x000a00010 (test.go:6) movqcx,bx//will I into BX
0x000d00013 (test.go:6) Addqax,cx//i+j into CX
0x001000016 (test.go:7) subqax,bx//i-j into BX
 //return results to call function stack frame
0x001300019 (test.go : 8) Movqcx, "". ~r2+24 (FP)
 //Returns the result to the call function stack frame
0x001800024 (test.go:8) movqbx, "". ~R3+32 (FP)
0x001d00029 ( Test.go:8) RET

It can be seen from this assembly code that, test inside the function, the first argument is taken by Fp+8, the fp+16 second argument is taken, and then the first value returned is saved, fp+24 and the second value is saved, which is fp+32 exactly the same as what I said above; Golang function call procedure , is fp+offset to implement the parameters and return values, unlike C + + is through registers to implement the parameters and return values;

However, there is a problem, my variables are int type, why the allocation of all 8 bytes, this remains to be verified.

Originally wanted to check the stack frame of the main function to verify the previous conclusion, but Golang to the small function automatically into the inline function, so you can compile your own to see that the main function inside is not called the test function, but the test function of the assembly code directly copied into the main function execution.

Four, Golang closure of the implementation

Before there is to see the implementation of the function of the next c++11 lambda , in fact, the realization of the principle is the imitation function; when the compiler compiles lambda a function, it generates an anonymous functor class, and then executes the lambda function, and then invokes the compiled anonymous functor class Overload function call method, which is The lambda method defined in the function; In fact, the implementation of the Golang closure is similar to this, and we illustrate by example

Packagemain

Import "FMT"

functest (Aint) func (iint) int{returnfunc
(iint) int{
 a = a + I
Returna
 }
}

Funcmain () {
 f: = Test (1)
 A: = f (2)
 FMT. Println (a)
 B: = f (3)
 FMT. Println (b)
}

The example program is simple, the test function passes in an integer parameter a , returns a function type, the function type passes in an integer parameter and returns an integer value main , function calls test function, returns a closure function.

Look test at the assembly code for the function:

"". Test t=1size=160value=0args=0x10locals=0x20 0x000000000 (test.go:5) TEXT "". Test (SB), $32-16 0x000000000 (test.go:5
) Movq (TLS), CX 0x000900009 (test.go:5) cmpqsp,16 (CX) 0x000d00013 (test.go:5) JLS142 0x000f00015 (test.go:5) subq$32,sp 0x001300019 (Test.go:5) funcdata$0, Gclocals 8edb5632446ada37b0a930d010725cc5 (SB) 0x001300019 (test.go:5) FUNCDATA$1 , Gclocals 008e235a1392cc90d1ed9ad2f7e76d87 (SB) 0x001300019 (test.go:5) Leaq type.int (SB), BX 0x001a00026 (Test.go:5) MOVQBX, (SP) 0x001e00030 (test.go:5) pcdata$0,$0//Generate an int object, that is, a 0x001e00030 (test.go:5) callruntime.newobject (SB)//8 ( SP) is the address of a generated a, put in the ax 0x002300035 (test.go:5) MOVQ8 (SP), and AX//place A's address in the sp+24 location 0x002800040 (test.go:5) Movqax, "". &a+24 ( SP)//Remove the first argument passed in by the main function, that is, a 0x002d00045 (test.go:5) movq "". A+40 (FP), BP//Put A in (AX)-pointed memory, that is, the newly generated int object 0x003200050 (  Test.go:5) MOVQBP, (AX) 0x003500053 (test.go:6) leaq type.struct {F uintptr; a *int} (SB), BX 0x003c00060 (test.go:6) MOVQBX, (SP) 0x004000064 (test.go:6) pcdata$0,$1 0x004000064 (test.go:6) callruntime.newObject (SB)//8 (SP) This is the generated struct object address 0x004500069 (test.go:6) MOVQ8 (sp), AX 0x004a00074 (test.go:6) NOP// Test internal anonymous function address is stored in the BP 0x004a00074 (test.go:6) Leaq "". Test.func1 (SB), BP//Place the anonymous function address into (AX) point to the address, that is,//f uintptr (Test.go:6)
MOVQBP, (AX) 0x005400084 (test.go:6) Movqax, "". Autotmp_0001+16 (SP) 0x005900089 (test.go:6) NOP//Save the address of the generated integer object A to BP 0x005900089 (test.go:6) movq "". &a+24 (SP), BP 0x005e00094 (test.go:6) CMPB runtime.writebarrier (SB), $0x006500101 (
TEST.GO:6) jne$0,117//Place a address in the AX point to memory +8,//That is the struct a *int assignment 0x006700103 (test.go:6) movqbp,8 (AX)//The address of the above structure is deposited into the main function stack frame; 0x006b00107 (Test.go:9) Movqax, "". ~r1+48 (FP) 0x007000112 (test.go:9) addq$32,sp (0x007400116) RET

Have seen a sentence before, very vividly described the closure

Class is the behavior of the data, for the closure is the behavior of data;

That is, the closure is contextual, and we take the test example, the test closure function generated by the function, have their own a, this is the context of the a closure of the data, and this a has been accompanied by the closure function, every time the call a will change;

We analyze the above assembly code, look at the closure of the implementation of the principle; In this test example, because a it is the context data of the closure, it must be allocated on the heap, and if allocated on the stack, the a function ends and is a reclaimed; then an anonymous structure is defined:

type.struct{
 F uintptr//This is the closure call function pointer
 A *int//this is the closure context data
}

The object is then generated, and assigning the address of the integer object previously allocated on the heap a to a pointer in the structure, then assigning the function address of the closure call func to the pointer in the structure body, F so that every time a closure function is generated, it is actually generating one of the above structural objects. Each closure object also has its own data a and call function, and F finally returns the address of the structure to the main function;

Look main at the process of the function acquisition closure;

 

Obviously, the main function call test function Gets the address of the closure object, finds the closure function through the closure object address, executes the closure function, and passes the address of the closure object into the function, which is the same as C + +, in order to modify the member variable a ;

Finally, look at test the internal anonymous function (closure function implementation):

"". test.func1t=1size=32value=0args=0x10 locals=0x0
0x000000000 (test.go:6) TEXT "". Test.func1 (SB), $0-16
0x000000000 (test.go:6) NOP
0x000000000 (test.go:6) NOP 0x000000000
(test.go:6) Funcdata $, gclocals 23E8278E2B69A3A75FA59B23C49ED6AD (SB)
0x000000000 (test.go:6) Funcdata $, gclocals 33CDECCCCEBE80329F1FDBEE7F5874CB (SB)
//DX is the address of the closure object, +8 is the address of a
0x000000000 (test.go:6) MOVQ8 (DX), AX
// Ax is the address of a, (ax) is the value of a
0x000400004 (test.go:7) movq (ax), BP
//I deposit parameter I into R8
0x000700007 (test.go:7) movq "". i+ 8 (FP), the
value of R8//a+i is deposited in the BP
0x000c00012 (test.go:7) addq R8, BP
//Will a+i be deposited in a address
0x000f00015 (test.go:7) MOVQ BP, (AX)
///Address the latest data in the A to BP
0x001200018 (test.go:8) movq (ax), BP
//Put a latest value as the return value into the
main function stack 0x001500021 (test.go:8) movq BP, "". ~r1+16 (FP)
0x001a00026 (test.go:8) RET

The call procedure for the closure function:

1, through the closure object address to obtain the closure context data a address;

2, and then through the address of a to obtain the value of a, and with the parameter I added;

3, the A+i as the latest value into the address of A;

4, return a latest value to the main function;

V. Summary

This article simply analyzes the implementation of Golang multiple value return and closure from the assembly point of view;

The multiple value returns are mainly obtained by the FP register +offset and stored in the return value.

Closures are implemented primarily by building structures that include closure functions and closure context data at compile time;

The above is the entire content of this article, I hope to learn or only with Golang can have certain help, if there is doubt you can message exchange.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.