Skip to content
Permalink
Browse files

compiler: add -size flag to replace size utility

The size flag has two modes:
  -size=short: prints data basically equivalent to the `size` program.
  -size=full:  tries to determine sizes per package (not entirely
               accurate).
  • Loading branch information...
aykevl committed Sep 17, 2018
1 parent 8b94fe9 commit 87963d3d5be075ea194ace0922d3658d399eed1a
Showing with 189 additions and 14 deletions.
  1. +2 −7 Makefile
  2. +160 −0 binutils.go
  3. +27 −7 main.go
@@ -9,16 +9,13 @@ TARGET ?= unix

ifeq ($(TARGET),unix)
# Regular *nix system.
SIZE = size

else ifeq ($(TARGET),pca10040)
# PCA10040: nRF52832 development board
SIZE = arm-none-eabi-size
OBJCOPY = arm-none-eabi-objcopy
TGOFLAGS += -target $(TARGET)

else ifeq ($(TARGET),arduino)
SIZE = avr-size
OBJCOPY = avr-objcopy
TGOFLAGS += -target $(TARGET)

@@ -68,13 +65,11 @@ build/tgo: *.go

# Binary that can run on the host.
build/%: src/examples/% src/examples/%/*.go build/tgo src/runtime/*.go
./build/tgo build $(TGOFLAGS) -o $@ $(subst src/,,$<)
@$(SIZE) $@
./build/tgo build $(TGOFLAGS) -size=short -o $@ $(subst src/,,$<)

# ELF file that can run on a microcontroller.
build/%.elf: src/examples/% src/examples/%/*.go build/tgo src/runtime/*.go
./build/tgo build $(TGOFLAGS) -o $@ $(subst src/,,$<)
@$(SIZE) $@
./build/tgo build $(TGOFLAGS) -size=short -o $@ $(subst src/,,$<)

# Convert executable to Intel hex file (for flashing).
build/%.hex: build/%.elf
@@ -0,0 +1,160 @@
package main

import (
"debug/elf"
"sort"
"strings"
)

// Statistics about code size in a program.
type ProgramSize struct {
Packages map[string]*PackageSize
Sum *PackageSize
Code uint64
Data uint64
BSS uint64
}

// Return the list of package names (ProgramSize.Packages) sorted
// alphabetically.
func (ps *ProgramSize) SortedPackageNames() []string {
names := make([]string, 0, len(ps.Packages))
for name := range ps.Packages {
names = append(names, name)
}
sort.Strings(names)
return names
}

// The size of a package, calculated from the linked object file.
type PackageSize struct {
Code uint64
ROData uint64
Data uint64
BSS uint64
}

// Flash usage in regular microcontrollers.
func (ps *PackageSize) Flash() uint64 {
return ps.Code + ps.ROData + ps.Data
}

// Static RAM usage in regular microcontrollers.
func (ps *PackageSize) RAM() uint64 {
return ps.Data + ps.BSS
}

type symbolList []elf.Symbol

func (l symbolList) Len() int {
return len(l)
}

func (l symbolList) Less(i, j int) bool {
bind_i := elf.ST_BIND(l[i].Info)
bind_j := elf.ST_BIND(l[j].Info)
if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK {
// sort weak symbols after non-weak symbols
return true
}
return l[i].Value < l[j].Value
}

func (l symbolList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}

// Calculate program/data size breakdown of each package for a given ELF file.
func Sizes(path string) (*ProgramSize, error) {
file, err := elf.Open(path)
if err != nil {
return nil, err
}
defer file.Close()

var sumCode uint64
var sumData uint64
var sumBSS uint64
for _, section := range file.Sections {
if section.Flags&elf.SHF_ALLOC == 0 {
continue
}
if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS {
continue
}
if section.Type == elf.SHT_NOBITS {
sumBSS += section.Size
} else if section.Flags&elf.SHF_EXECINSTR != 0 {
sumCode += section.Size
} else if section.Flags&elf.SHF_WRITE != 0 {
sumData += section.Size
}
}

allSymbols, err := file.Symbols()
if err != nil {
return nil, err
}
symbols := make([]elf.Symbol, 0, len(allSymbols))
for _, symbol := range allSymbols {
symType := elf.ST_TYPE(symbol.Info)
if symbol.Size == 0 {
continue
}
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
continue
}
if symbol.Section >= elf.SectionIndex(len(file.Sections)) {
continue
}
section := file.Sections[symbol.Section]
if section.Flags&elf.SHF_ALLOC == 0 {
continue
}
symbols = append(symbols, symbol)
}
sort.Sort(symbolList(symbols))

sizes := map[string]*PackageSize{}
var lastSymbolValue uint64
for _, symbol := range symbols {
symType := elf.ST_TYPE(symbol.Info)
//bind := elf.ST_BIND(symbol.Info)
section := file.Sections[symbol.Section]
pkgName := "(bootstrap)"
symName := strings.TrimLeft(symbol.Name, "(*")
dot := strings.IndexByte(symName, '.')
if dot > 0 {
pkgName = symName[:dot]
}
pkgSize := sizes[pkgName]
if pkgSize == nil {
pkgSize = &PackageSize{}
sizes[pkgName] = pkgSize
}
if lastSymbolValue != symbol.Value || lastSymbolValue == 0 {
if symType == elf.STT_FUNC {
pkgSize.Code += symbol.Size
} else if section.Flags&elf.SHF_WRITE != 0 {
if section.Type == elf.SHT_NOBITS {
pkgSize.BSS += symbol.Size
} else {
pkgSize.Data += symbol.Size
}
} else {
pkgSize.ROData += symbol.Size
}
}
lastSymbolValue = symbol.Value
}

sum := &PackageSize{}
for _, pkg := range sizes {
sum.Code += pkg.Code
sum.ROData += pkg.ROData
sum.Data += pkg.Data
sum.BSS += pkg.BSS
}

return &ProgramSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil
}
34 main.go
@@ -16,7 +16,7 @@ import (
)

// Helper function for Compiler object.
func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA bool, action func(string) error) error {
func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA bool, printSizes string, action func(string) error) error {
c, err := NewCompiler(pkgName, spec.Triple, dumpSSA)
if err != nil {
return err
@@ -97,6 +97,25 @@ func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA bool, a
return err
}

if printSizes == "short" || printSizes == "full" {
sizes, err := Sizes(executable)
if err != nil {
return err
}
if printSizes == "short" {
fmt.Printf(" code data bss | flash ram\n")
fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
} else {
fmt.Printf(" code rodata data bss | flash ram | package\n")
for _, name := range sizes.SortedPackageNames() {
pkgSize := sizes.Packages[name]
fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
}
fmt.Printf("%7d %7d %7d %7d | %7d %7d | (sum)\n", sizes.Sum.Code, sizes.Sum.ROData, sizes.Sum.Data, sizes.Sum.BSS, sizes.Sum.Flash(), sizes.Sum.RAM())
fmt.Printf("%7d - %7d %7d | %7d %7d | (all)\n", sizes.Code, sizes.Data, sizes.BSS, sizes.Code+sizes.Data, sizes.Data+sizes.BSS)
}
}

if strings.HasSuffix(outpath, ".hex") {
// Get an Intel .hex file from the .elf file.
tmppath = filepath.Join(dir, "main.hex")
@@ -112,13 +131,13 @@ func Compile(pkgName, outpath string, spec *TargetSpec, printIR, dumpSSA bool, a
}
}

func Build(pkgName, outpath, target string, printIR, dumpSSA bool) error {
func Build(pkgName, outpath, target string, printIR, dumpSSA bool, printSizes string) error {
spec, err := LoadTarget(target)
if err != nil {
return err
}

return Compile(pkgName, outpath, spec, printIR, dumpSSA, func(tmppath string) error {
return Compile(pkgName, outpath, spec, printIR, dumpSSA, printSizes, func(tmppath string) error {
if err := os.Rename(tmppath, outpath); err != nil {
// Moving failed. Do a file copy.
inf, err := os.Open(tmppath)
@@ -146,13 +165,13 @@ func Build(pkgName, outpath, target string, printIR, dumpSSA bool) error {
})
}

func Flash(pkgName, target, port string, printIR, dumpSSA bool) error {
func Flash(pkgName, target, port string, printIR, dumpSSA bool, printSizes string) error {
spec, err := LoadTarget(target)
if err != nil {
return err
}

return Compile(pkgName, ".hex", spec, printIR, dumpSSA, func(tmppath string) error {
return Compile(pkgName, ".hex", spec, printIR, dumpSSA, printSizes, func(tmppath string) error {
// Create the command.
flashCmd := spec.Flasher
parts := strings.Split(flashCmd, " ") // TODO: this should be a real shell split
@@ -216,6 +235,7 @@ func main() {
printIR := flag.Bool("printir", false, "print LLVM IR")
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
target := flag.String("target", llvm.DefaultTargetTriple(), "LLVM target")
printSize := flag.String("size", "", "print sizes (none, short, full)")
port := flag.String("port", "/dev/ttyACM0", "flash port")

if len(os.Args) < 2 {
@@ -241,7 +261,7 @@ func main() {
usage()
os.Exit(1)
}
err := Build(flag.Arg(0), *outpath, *target, *printIR, *dumpSSA)
err := Build(flag.Arg(0), *outpath, *target, *printIR, *dumpSSA, *printSize)
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
@@ -252,7 +272,7 @@ func main() {
usage()
os.Exit(1)
}
err := Flash(flag.Arg(0), *target, *port, *printIR, *dumpSSA)
err := Flash(flag.Arg(0), *target, *port, *printIR, *dumpSSA, *printSize)
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)

0 comments on commit 87963d3

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.