Легко читаемый сборник Golang?

Мне интересно изучить сборку сборки x86 стандартного компилятора Go, чтобы проверить, действительно ли мой код преобразован в разумно эффективный код сборки; надеюсь, путем профилирования и изучения сборки, я мог бы получить представление о том, где/как я должен переписать код Go для максимальной производительности. Но когда я исследую код с помощью флага -S, Go выплевывает беспорядок! Я бы хотел две вещи:

  • Есть ли способ заставить компилятор Go скомпилировать вывод сборки в файл, а не просто распечатать его на терминале?

  • Кроме того, есть ли способ заставить компилятор Go разделить код сборки на отдельные функции с метками? Я знаю, что некоторые функции могут быть встроены и, следовательно, не отображаются в коде сборки. То, что я вижу, - это просто однородный блок сборки, который почти невозможно понять.

Ответ 1

  • Вы можете перенаправить вывод в файл следующим образом:

    go tool 6g -S file.go > file.s
    
  • Вы можете отключить оптимизацию с помощью -N:

    go tool 6g -S -N file.go
    

В качестве альтернативы вы можете использовать gccgo:

gccgo -S -O0 -masm=intel test.go

который будет генерировать test.s. Вы можете играть с -O0/1/2/3, чтобы увидеть различные оптимизации.

Ответ 2

Я не рекомендую использовать вывод -S, поскольку компоновщик Go может изменить то, что записывается в код объекта довольно много. Это дает вам некоторое представление о том, что происходит.

Выход ассемблера Go также не является стандартным.

Когда я хочу это сделать, я всегда использую objdump, который даст вам хороший стандартный выход ассемблера.

Например, для x86/amd64

objdump -d executable > disassembly

И для ARM (чтобы имена регистров были такими же, как использование Go)

objdump -M reg-names-raw -d executable > disassembly

Ответ 3

Запустите go tool objdump в результирующем исполняемом файле.

Чтобы ограничить вывод интересными функциями, используйте его опцию -s.

Ответ 4

У меня были проблемы с другими ответами, поскольку собранная сборка предоставила гораздо больше информации, чем я хотел, и все еще не хватает деталей. Позвольте мне объяснить: он предоставил сборку для всех библиотек, импортированных путем внутреннего использования, и не предоставил строки, где был мой код (мой код был в нижней части файла)

Вот что я нашел из официальных документов:

$ GOOS=linux GOARCH=amd64 go tool compile -S x.go # or: go build -gcflags -S x.go

Файл:

package main

func main() {
    println(3)
}

Производит:

--- prog list "main" ---
0000 (x.go:3) TEXT    main+0(SB),$8-0
0001 (x.go:3) FUNCDATA $0,gcargs·0+0(SB)
0002 (x.go:3) FUNCDATA $1,gclocals·0+0(SB)
0003 (x.go:4) MOVQ    $3,(SP)
0004 (x.go:4) PCDATA  $0,$8
0005 (x.go:4) CALL    ,runtime.printint+0(SB)
0006 (x.go:4) PCDATA  $0,$-1
0007 (x.go:4) PCDATA  $0,$0
0008 (x.go:4) CALL    ,runtime.printnl+0(SB)
0009 (x.go:4) PCDATA  $0,$-1
0010 (x.go:5) RET     ,

Так что я сделал в основном:

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

и он получил результат, который я хотел!

Ответ 5

Чтобы выгрузить вывод в файл:

go tool objdump EXECUTABLE_FILE > ASSEMBLY_FILE

Если вы хотите включить исходный код Go (при условии, что у вас есть рабочая настройка golang, и вы сами создали исполняемый файл):

go tool objdump -S EXECUTABLE_FILE

Чтобы сделать вывод еще проще, я использую небольшую хакерскую оболочку, которая производит следующее (в двух словах она раскрашивает инструкции, которые изменяют поток управления -blue для переходов, зеленый для вызова/возврата, красный для ловушек, фиолетовый для padding- и добавляет новые строки после безусловных переходов потока управления):

example output of go-objdump wrapper

Если вы используете обертку выше, вы, скорее всего, захотите использовать переключатель -R если конвейер будет less (или добавив его в среду, например, в .bashrc: export LESS="$LESS -R"):

go-objdump EXECUTABLE_FILE | less -R

Кроме того, есть godbolt.org, который, вероятно, самый читаемый вывод и позволяет легко переключаться между компиляторами (gc, gccgo) и версиями.