transform: add debug info in interface lowering pass

This is fake debug info. It doesn't point to a source location because
there is no source location. However, it helps to correctly attribute
code size usage to particular packages.

I've also updated builder/sizes.go with some debugging helpers.
Этот коммит содержится в:
Ayke van Laethem 2021-11-04 03:27:44 +01:00 коммит произвёл Ron Evans
родитель edcece33ca
коммит 7caf0732fa
2 изменённых файлов: 91 добавлений и 3 удалений

Просмотреть файл

@ -17,6 +17,9 @@ import (
"github.com/tinygo-org/tinygo/goenv"
)
// Set to true to print extra debug logs.
const sizesDebug = false
// programSize contains size statistics per package of a compiled program.
type programSize struct {
Packages map[string]packageSize
@ -514,6 +517,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
// There is a gap: there is a space between the current and the
// previous line entry.
addSize("(unknown)", line.Address-addr, false)
if sizesDebug {
fmt.Printf("%08x..%08x %4d: unknown (gap)\n", addr, line.Address, line.Address-addr)
}
}
if addr > line.Address+line.Length {
// The current line is already covered by a previous line entry.
@ -536,6 +542,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
if addr < sectionEnd {
// There is a gap at the end of the section.
addSize("(unknown)", sectionEnd-addr, false)
if sizesDebug {
fmt.Printf("%08x..%08x %4d: unknown (end)\n", addr, sectionEnd, sectionEnd-addr)
}
}
}
@ -556,6 +565,20 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
} else if reflectDataRegexp.MatchString(path) {
// Parse symbol names like reflect.structTypesSidetable.
packagePath = "Go reflect data"
} else if path == "<Go interface assert>" {
// Interface type assert, generated by the interface lowering pass.
packagePath = "Go interface assert"
} else if path == "<Go interface method>" {
// Interface method wrapper (switch over all concrete types),
// generated by the interface lowering pass.
packagePath = "Go interface method"
} else if path == "<stdin>" {
// This can happen when the source code (in Go) doesn't have a
// source file and uses "-" as the location. Somewhere this is
// converted to "<stdin>".
// Convert this back to the "-" string. Eventually, this should be
// fixed in the compiler.
packagePath = "-"
} else {
// This is some other path. Not sure what it is, so just emit its directory.
packagePath = filepath.Dir(path) // fallback

Просмотреть файл

@ -87,6 +87,8 @@ type lowerInterfacesPass struct {
mod llvm.Module
config *compileopts.Config
builder llvm.Builder
dibuilder *llvm.DIBuilder
difiles map[string]llvm.Metadata
ctx llvm.Context
uintptrType llvm.Type
types map[string]*typeInfo
@ -109,11 +111,28 @@ func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error {
signatures: make(map[string]*signatureInfo),
interfaces: make(map[string]*interfaceInfo),
}
if config.Debug() {
p.dibuilder = llvm.NewDIBuilder(mod)
defer p.dibuilder.Finalize()
p.difiles = make(map[string]llvm.Metadata)
}
return p.run()
}
// run runs the pass itself.
func (p *lowerInterfacesPass) run() error {
if p.dibuilder != nil {
p.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
File: "<unknown>",
Dir: "",
Producer: "TinyGo",
Optimized: true,
})
}
// Collect all type codes.
for global := p.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if strings.HasPrefix(global.Name(), "reflect/types.type:") {
@ -340,7 +359,6 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
// types by the LLVM simplifycfg pass.
func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf *interfaceInfo) {
// Create the function and function signature.
// TODO: debug info
fn.Param(0).SetName("actualType")
fn.SetLinkage(llvm.InternalLinkage)
fn.SetUnnamedAddr(true)
@ -351,6 +369,26 @@ func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf *
thenBlock := p.ctx.AddBasicBlock(fn, "then")
p.builder.SetInsertPointAtEnd(entry)
if p.dibuilder != nil {
difile := p.getDIFile("<Go interface assert>")
diFuncType := p.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: difile,
})
difunc := p.dibuilder.CreateFunction(difile, llvm.DIFunction{
Name: "(Go interface assert)",
File: difile,
Line: 0,
Type: diFuncType,
LocalToUnit: true,
IsDefinition: true,
ScopeLine: 0,
Flags: llvm.FlagPrototyped,
Optimized: true,
})
fn.SetSubprogram(difunc)
p.builder.SetCurrentDebugLocation(0, 0, difunc, llvm.Metadata{})
}
// Iterate over all possible types. Each iteration creates a new branch
// either to the 'then' block (success) or the .next block, for the next
// check.
@ -390,8 +428,6 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
fn.SetUnnamedAddr(true)
AddStandardAttributes(fn, p.config)
// TODO: debug info
// Collect the params that will be passed to the functions to call.
// These params exclude the receiver (which may actually consist of multiple
// parts).
@ -408,6 +444,26 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
entry := p.ctx.AddBasicBlock(fn, "entry")
p.builder.SetInsertPointAtEnd(entry)
if p.dibuilder != nil {
difile := p.getDIFile("<Go interface method>")
diFuncType := p.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: difile,
})
difunc := p.dibuilder.CreateFunction(difile, llvm.DIFunction{
Name: "(Go interface method)",
File: difile,
Line: 0,
Type: diFuncType,
LocalToUnit: true,
IsDefinition: true,
ScopeLine: 0,
Flags: llvm.FlagPrototyped,
Optimized: true,
})
fn.SetSubprogram(difunc)
p.builder.SetCurrentDebugLocation(0, 0, difunc, llvm.Metadata{})
}
// Define all possible functions that can be called.
for _, typ := range itf.types {
// Create type check (if/else).
@ -467,3 +523,12 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
}, "")
p.builder.CreateUnreachable()
}
func (p *lowerInterfacesPass) getDIFile(file string) llvm.Metadata {
difile, ok := p.difiles[file]
if !ok {
difile = p.dibuilder.CreateFile(file, "")
p.difiles[file] = difile
}
return difile
}