Skip to content

Commit

Permalink
chapter2 > imported everything
Browse files Browse the repository at this point in the history
  • Loading branch information
teh-cmc committed Apr 9, 2018
1 parent 03b52db commit 27fcccf
Show file tree
Hide file tree
Showing 21 changed files with 2,995 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _cgo_export.*
_testmain.go

*.exe
*.bin
*.test


Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.PHONY: toc

toc:
docker run --rm -it -v ${PWD}:/usr/src jorgeandrada/doctoc --github
$(shell tail -n +`grep -n '# \`go-internals\`' README.md | tr ':' ' ' | awk '{print $$1}'` README.md > /tmp/README2.md)
cp /tmp/README2.md README.md
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
## Table of Contents

- [Chapter I: A Primer on Go Assembly](./chapter1_assembly_primer/README.md)
- [Chapter II (soon!): The implementation & cost of Interfaces](./chapter2_interfaces/README.md)
- [Chapter II: Interfaces](./chapter2_interfaces/README.md)
- [Chapter III (soon!): The Garbage Collector](./chapter3_garbage_collector/README.md)

---

Expand Down
2 changes: 0 additions & 2 deletions chapter2_interfaces/.gitignore

This file was deleted.

20 changes: 20 additions & 0 deletions chapter2_interfaces/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
GOOS=linux
GOARCH=amd64

SOURCES := $(wildcard *.go)
OBJECTS = $(SOURCES:.go=.o)
EXECUTABLES = $(OBJECTS:.o=.bin)

.SECONDARY: ${OBJECTS}

all: ${EXECUTABLES}

%.o: %.go
GOOS=${GOOS} GOARCH=${GOARCH} go tool compile $<

%.bin: %.o
GOOS=${GOOS} GOARCH=${GOARCH} go tool link -o $@ $<

clean:
rm -f ${OBJECTS}
rm -f ${EXECUTABLES}
2,249 changes: 2,248 additions & 1 deletion chapter2_interfaces/README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions chapter2_interfaces/compound_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

type Adder interface{ Add(a, b int32) int32 }
type Subber interface{ Sub(a, b int32) int32 }

type Mather interface {
Adder
Subber
}

type Calculator struct{ id int32 }

func (c *Calculator) Add(a, b int32) int32 { return a + b }
func (c *Calculator) Sub(a, b int32) int32 { return a - b }

func main() {
calc := Calculator{id: 6754}
var m Mather = &calc
m.Sub(10, 32)
}
22 changes: 22 additions & 0 deletions chapter2_interfaces/direct_calls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

//go:noinline
func Add(a, b int32) int32 { return a + b }

type Adder struct{ id int32 }

//go:noinline
func (adder *Adder) AddPtr(a, b int32) int32 { return a + b }

//go:noinline
func (adder Adder) AddVal(a, b int32) int32 { return a + b }

func main() {
Add(10, 32) // direct call of top-level function

adder := Adder{id: 6754}
adder.AddPtr(10, 32) // direct call of method with pointer receiver
adder.AddVal(10, 32) // direct call of method with value receiver

(&adder).AddVal(10, 32) // implicit dereferencing
}
36 changes: 36 additions & 0 deletions chapter2_interfaces/dump_sym.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash

BIN="$1"
test "$BIN"
SECTION="$2"
test "$SECTION"
SYM="$3"
test "$SYM"

section_offset=$(
readelf -St -W "$BIN" | \
grep -A 1 "$SECTION" | \
tail -n +2 | \
awk '{print toupper($3)}'
)
section_offset_dec=$(echo "ibase=16;$section_offset" | bc)
echo "$SECTION file-offset: $section_offset_dec"

section_vma=$(
readelf -St -W "$BIN" | \
grep -A 1 "$SECTION" | \
tail -n +2 | \
awk '{print toupper($2)}'
)
section_vma_dec=$(echo "ibase=16;$section_vma" | bc)
echo "$SECTION VMA: $section_vma_dec"

sym_vma=$(objdump -t -j "$SECTION" "$BIN" | grep "$SYM" | awk '{print toupper($1)}')
sym_vma_dec=$(echo "ibase=16;$sym_vma" | bc)
echo "$SYM VMA: $sym_vma_dec"
sym_size=$(objdump -t -j "$SECTION" "$BIN" | grep "$SYM" | awk '{print toupper($5)}')
sym_size_dec=$(echo "ibase=16;$sym_size" | bc)
echo -e "$SYM SIZE: $sym_size_dec\n"

sym_offset=$(( $sym_vma_dec - $section_vma_dec + $section_offset_dec ))
dd if="$BIN" of=/dev/stdout bs=1 count=$sym_size_dec skip="$sym_offset" 2>/dev/null | hexdump
110 changes: 110 additions & 0 deletions chapter2_interfaces/eface_scalar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"testing"
)

func BenchmarkEfaceScalar(b *testing.B) {
var Uint uint32
b.Run("uint32", func(b *testing.B) {
for i := 0; i < b.N; i++ {
// MOVL DX, (AX)
Uint = uint32(i)
}
})
var Eface interface{}
b.Run("eface32", func(b *testing.B) {
for i := 0; i < b.N; i++ {
// MOVL CX, ""..autotmp_3+36(SP)
// LEAQ type.uint32(SB), AX
// MOVQ AX, (SP)
// LEAQ ""..autotmp_3+36(SP), DX
// MOVQ DX, 8(SP)
// CALL runtime.convT2E32(SB)
// MOVQ 24(SP), AX
// MOVQ 16(SP), CX
// MOVQ "".&Eface+48(SP), DX
// MOVQ CX, (DX)
// MOVL runtime.writeBarrier(SB), CX
// LEAQ 8(DX), DI
// TESTL CX, CX
// JNE 148
// MOVQ AX, 8(DX)
// JMP 46
// CALL runtime.gcWriteBarrier(SB)
// JMP 46
Eface = uint32(i)
}
})
b.Run("eface8", func(b *testing.B) {
for i := 0; i < b.N; i++ {
// LEAQ type.uint8(SB), BX
// MOVQ BX, (CX)
// MOVBLZX AL, SI
// LEAQ runtime.staticbytes(SB), R8
// ADDQ R8, SI
// MOVL runtime.writeBarrier(SB), R9
// LEAQ 8(CX), DI
// TESTL R9, R9
// JNE 100
// MOVQ SI, 8(CX)
// JMP 40
// MOVQ AX, R9
// MOVQ SI, AX
// CALL runtime.gcWriteBarrier(SB)
// MOVQ R9, AX
// JMP 40
Eface = uint8(i)
}
})
b.Run("eface-zeroval", func(b *testing.B) {
for i := 0; i < b.N; i++ {
// MOVL $0, ""..autotmp_3+36(SP)
// LEAQ type.uint32(SB), AX
// MOVQ AX, (SP)
// LEAQ ""..autotmp_3+36(SP), CX
// MOVQ CX, 8(SP)
// CALL runtime.convT2E32(SB)
// MOVQ 16(SP), AX
// MOVQ 24(SP), CX
// MOVQ "".&Eface+48(SP), DX
// MOVQ AX, (DX)
// MOVL runtime.writeBarrier(SB), AX
// LEAQ 8(DX), DI
// TESTL AX, AX
// JNE 152
// MOVQ CX, 8(DX)
// JMP 46
// MOVQ CX, AX
// CALL runtime.gcWriteBarrier(SB)
// JMP 46
Eface = uint32(i - i) // outsmart the compiler
}
})
b.Run("eface-static", func(b *testing.B) {
for i := 0; i < b.N; i++ {
// LEAQ type.uint64(SB), BX
// MOVQ BX, (CX)
// MOVL runtime.writeBarrier(SB), SI
// LEAQ 8(CX), DI
// TESTL SI, SI
// JNE 92
// LEAQ "".statictmp_0(SB), SI
// MOVQ SI, 8(CX)
// JMP 40
// MOVQ AX, SI
// LEAQ "".statictmp_0(SB), AX
// CALL runtime.gcWriteBarrier(SB)
// MOVQ SI, AX
// LEAQ "".statictmp_0(SB), SI
// JMP 40
Eface = uint64(42)
}
})
}

func main() {
// So that we can easily compile this and retrieve `main.statictmp_0`
// from the final executable.
BenchmarkEfaceScalar(&testing.B{})
}
82 changes: 82 additions & 0 deletions chapter2_interfaces/eface_to_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

var j uint32
var Eface interface{} // outsmart compiler (avoid static inference)

func assertion() {
i := uint32(42)
Eface = i

// 0x0065 00101 MOVQ "".Eface(SB), AX ;; AX = Eface._type
// 0x006c 00108 MOVQ "".Eface+8(SB), CX ;; CX = Eface.data
// 0x0073 00115 LEAQ type.uint32(SB), DX ;; DX = type.uint32
// 0x007a 00122 CMPQ AX, DX ;; Eface._type == type.uint32 ?
// 0x007d 00125 JNE 162 ;; no? panic our way outta here
// 0x007f 00127 MOVL (CX), AX ;; AX = *Eface.data
// 0x0081 00129 MOVL AX, "".j(SB) ;; j = AX = *Eface.data
// ;; exit
// 0x0087 00135 MOVQ 40(SP), BP
// 0x008c 00140 ADDQ $48, SP
// 0x0090 00144 RET
// ;; panic: interface conversion: <iface> is <have>, not <want>
// 0x00a2 00162 MOVQ AX, (SP) ;; have: Eface._type
// 0x00a6 00166 MOVQ DX, 8(SP) ;; want: type.uint32
// 0x00ab 00171 LEAQ type.interface {}(SB), AX ;; AX = type.interface{} (eface)
// 0x00b2 00178 MOVQ AX, 16(SP) ;; iface: AX
// 0x00b7 00183 CALL runtime.panicdottypeE(SB) ;; func panicdottypeE(have, want, iface *_type)
// 0x00bc 00188 UNDEF
// 0x00be 00190 NOP
j = Eface.(uint32)
}

func typeSwitch() {
i := uint32(42)
Eface = i

// ;; switch v := Eface.(type)
// 0x0065 00101 MOVQ "".Eface(SB), AX ;; AX = Eface._type
// 0x006c 00108 MOVQ "".Eface+8(SB), CX ;; CX = Eface.data
// 0x0073 00115 TESTQ AX, AX ;; Eface._type == nil ?
// 0x0076 00118 JEQ 153 ;; yes? exit the switch
// 0x0078 00120 MOVL 16(AX), DX ;; DX = Eface.type._hash
// ;; case uint32
// 0x007b 00123 CMPL DX, $-800397251 ;; Eface.type._hash == type.uint32.hash ?
// 0x0081 00129 JNE 163 ;; no? go to next case (uint16)
// 0x0083 00131 LEAQ type.uint32(SB), BX ;; BX = type.uint32
// 0x008a 00138 CMPQ BX, AX ;; type.uint32 == Eface._type ? (HASH COLLISION?)
// 0x008d 00141 JNE 206 ;; no? clear BX and go to next case (uint16)
// 0x008f 00143 MOVL (CX), BX ;; BX = *Eface.data
// 0x0091 00145 JNE 163 ;; landsite for indirect jump starting at 0x00d3
// 0x0093 00147 MOVL BX, "".j(SB) ;; j = BX = *Eface.data
// ;; exit
// 0x0099 00153 MOVQ 40(SP), BP
// 0x009e 00158 ADDQ $48, SP
// 0x00a2 00162 RET
// ;; case uint16
// 0x00a3 00163 CMPL DX, $-269349216 ;; Eface.type._hash == type.uint16.hash ?
// 0x00a9 00169 JNE 153 ;; no? exit the switch
// 0x00ab 00171 LEAQ type.uint16(SB), DX ;; DX = type.uint16
// 0x00b2 00178 CMPQ DX, AX ;; type.uint16 == Eface._type ? (HASH COLLISION?)
// 0x00b5 00181 JNE 199 ;; no? clear AX and exit the switch
// 0x00b7 00183 MOVWLZX (CX), AX ;; AX = uint16(*Eface.data)
// 0x00ba 00186 JNE 153 ;; landsite for indirect jump starting at 0x00cc
// 0x00bc 00188 MOVWLZX AX, AX ;; AX = uint16(AX) (redundant)
// 0x00bf 00191 MOVL AX, "".j(SB) ;; j = AX = *Eface.data
// 0x00c5 00197 JMP 153 ;; we're done, exit the switch
// ;; indirect jump table
// 0x00c7 00199 MOVL $0, AX ;; AX = $0
// 0x00cc 00204 JMP 186 ;; indirect jump to 153 (exit)
// 0x00ce 00206 MOVL $0, BX ;; BX = $0
// 0x00d3 00211 JMP 145 ;; indirect jump to 163 (case uint16)
switch v := Eface.(type) {
case uint16:
j = uint32(v)
case uint32:
j = v
}
}

func main() {
assertion()
typeSwitch()
}
49 changes: 49 additions & 0 deletions chapter2_interfaces/eface_to_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import "testing"

var j uint32
var eface interface{} = uint32(42)

func BenchmarkEfaceToType(b *testing.B) {
b.Run("switch-small", func(b *testing.B) {
for i := 0; i < b.N; i++ {
switch v := eface.(type) {
case int8:
j = uint32(v)
case uint32:
j = uint32(v)
case int16:
j = uint32(v)
default:
j = v.(uint32)
}
}
})
b.Run("switch-big", func(b *testing.B) {
for i := 0; i < b.N; i++ {
switch v := eface.(type) {
case int8:
j = uint32(v)
case int16:
j = uint32(v)
case int32:
j = uint32(v)
case uint32:
j = uint32(v)
case int64:
j = uint32(v)
case uint8:
j = uint32(v)
case uint16:
j = uint32(v)
case uint64:
j = uint32(v)
default:
j = v.(uint32)
}
}
})
}

func main() {}
Loading

0 comments on commit 27fcccf

Please sign in to comment.