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.
Этот коммит содержится в:
родитель
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
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче