From 7caf0732fa43e16d077d9a11a0aeaaf58b390ae6 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 4 Nov 2021 03:27:44 +0100 Subject: [PATCH] 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. --- builder/sizes.go | 23 +++++++++++ transform/interface-lowering.go | 71 +++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/builder/sizes.go b/builder/sizes.go index 97879556..f7e72049 100644 --- a/builder/sizes.go +++ b/builder/sizes.go @@ -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 == "" { + // Interface type assert, generated by the interface lowering pass. + packagePath = "Go interface assert" + } else if path == "" { + // Interface method wrapper (switch over all concrete types), + // generated by the interface lowering pass. + packagePath = "Go interface method" + } else if path == "" { + // 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 "". + // 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 diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index ba1a27f0..2bc12ac8 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -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: "", + 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("") + 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("") + 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 +}