3

A while ago someone asked a question about how Golang actually swaps variables in statements like a, b = b, a.

To answer, I got out my Golang compiler, put on my thinking cap and crafted an answer to said question. SO questions are supposed to be self-contained, so here's my answer truncated for brevity:

To figure out how the compiler makes native code, we need to look at the assembly code it generates, which is turned into machine code by the linker.

I wrote a little Go program to help with this:

package main     
import "fmt"

func main() { fmt.Println(myfunction()) }
func myfunction() []int {
  a, b := 10, 5
  b, a = a, b
  return []int{a, b}
}

Using go tool compile -S > swap.s, I found these four lines, which correspond to the first two lines of myfunction in the Go code: (note this is for my 64-bit machine; the output will differ on other architechtures like 32-bit)

0x0028 00040 (swap.go:10) MOVQ    $10, CX         ; var a = 10
0x002f 00047 (swap.go:10) MOVQ    $5, AX          ; var b = 5
0x0036 00054 (swap.go:11) MOVQ    CX, "".b+16(SP) ; copy a to *b+16
0x003b 00059 (swap.go:11) MOVQ    AX, "".a+24(SP) ; copy b to *a+24 

Looking at the Golang docs on asm, we can see the assembler uses indirection to juggle the values.

When the program runs, the CPU is smart enough to see what's happening and use a register to avoid overwriting the existing value.

Here's the full disassembly, if you're interested.

My comment, informed by my meager knowledge of (Intel) x86 Assembly, garnered 6 votes and my answer got an accept and 3 votes.

What are the four lines of Golang assembly actually doing? Was my answer correct?

I ask because the docs I linked to aren't very (at all) exhaustive.

2
  • 1
    I'm not sure what that weird-ass syntax is about, but after disassembling it becomes mov rcx,10 \ mov rax,5 \ mov [rsp+16],rcx \ mov [rsp+24],rax (which is almost reasonable for 64bit code, but it wastes useless REX.W prefixes on mov-immediates with a tiny immediate) Commented Mar 11, 2016 at 23:47
  • 1
    @harold It's go asm, not x86 Commented Mar 11, 2016 at 23:56

2 Answers 2

3
a, b := 10, 5
b, a = a, b

0x0028 00040 (swap.go:10) MOVQ    $10, CX         ; CX = 10
0x002f 00047 (swap.go:10) MOVQ    $5, AX          ; AX = 5
0x0036 00054 (swap.go:11) MOVQ    CX, "".b+16(SP) ; b = CX or *(SP+16) = CX
0x003b 00059 (swap.go:11) MOVQ    AX, "".a+24(SP) ; a = AX or *(SP+24) = CX 

CX, AX, and SP are registers. a and b are variables on the stack at SP+24 and SP+16 respectively.

Sign up to request clarification or add additional context in comments.

Comments

3

It's loading the constants 10 and 5 into CPU registers and then storing registers into the stack locations reserved for the variables a and b.

It's the equivalent to:

 CX := 10
 AX := 5
 b := CX
 a := AX

Note a half decent optimizing compiler should optimize this into code that stores the constants directly into the locations on the stack:

 b := 10
 a := 5

Or better yet eliminates the variables entirely:

 return []int{5, 10}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.