28

I am trying to understand the internals of go. Consider the following code

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

The above code swaps 2 number perfectly and a becomes 5 and b becomes 10. I am not able to understand how this works. Considering in the second line of code, if a is assigned to b first, then b would be 10. Now if we assign b to a then shouldn't a be 10 too.

Please help me understand how this works

Thanks

6
  • 9
    The compiler uses a register as a temporary store, like a temporary variable
    – cat
    Commented Feb 29, 2016 at 18:52
  • 2
    Make this into an executable, then disassemble it
    – cat
    Commented Feb 29, 2016 at 18:53
  • or go tool compile -S file
    – cat
    Commented Feb 29, 2016 at 18:53
  • 1
    I got a .o file with this. Is there a way to disassemble/decompile it Commented Feb 29, 2016 at 19:01
  • For such a simple program, there is no the two variables a and b at all. Every place where references the a and b, it is the constant 10 and 5. Even you plus/multiply something to the a and b, like a += 999; b *= 999, or print them, like fmt.Println(a, b). All are the constants. Nowadays, the compiler is smart enough. The variables are created in the memory only when they are used in certain cases as complicated as they have to be created. Even by then, because there is no code between the definition and the swap, the a and b would be set to 5 and 10 directly.
    – Bruce
    Commented Mar 16, 2022 at 1:24

3 Answers 3

48

TL;DR: The disassembly shows that the CPU must be smart enough to see what's happening and use a register to avoid overwriting the existing value in memory.


This question helped me learn a little more about Golang, so thank you!

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 then CTRL - F'd for myfunction (which was the point of that name: easily searchable), and 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 

Go's disassembly is so helpful to debugging :D

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.

3
  • 2
    Is C, as well, capable of achieving this? With a high-level abstraction as opposed to machine codes, Golang is able to instruct the CPU to read two or more values/variables at one time.
    – Coconut
    Commented Feb 28, 2019 at 9:09
  • 2
    There's no such thing as CPU being smart! CPU has no hint to not overwrite values, and it'll happily do so. The first write will either be eliminated during uop generation, or will be fused into a macro uop. uarch issues aside, what happens here is the compiler generates the code such that the the swapped values are assigned directly to the return tuple. The compiler can do so because the swapping is done within the function. If you perform the swap operation in a separate function, you'll see that the compiler will make use of an additional temporary register.
    – MCG
    Commented Apr 12, 2020 at 17:32
  • What will happen for struct's swap (not simple integers)? Value could not fit to CPU register
    – FallDi
    Commented May 24, 2022 at 11:17
1

It's worth noting that in go, when we use a,b=b,a the Go runtime uses bitwise operation to perform the swapping internally.

When swapping two variable, the Go runtime will internally use bitwise operation XOR to swap the values.

It's faster than the traditional way which uses a temporary variable to store one of the values.

-3

here's my implementation of swapping numbers in Go:

func swap_int(a *int, b *int) {
   var x int
  
   x = *a
   *a = *b
   *b = x
}

func main() {
  var a int = 40
  var b int = 32
  fmt.Println("a = %d, b = %d\n", a, b)
  swap_int(&a, &b)
  fmt.Println("a = %d, b = %d", a, b)
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.