teak-llvm/llgo/debug/debug.go
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

432 lines
13 KiB
Go

//===- debug.go - debug info builder --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This package builds LLVM debug info from go/* data structures.
//
//===----------------------------------------------------------------------===//
package debug
import (
"debug/dwarf"
"fmt"
"go/token"
"os"
"strings"
"llvm.org/llgo/third_party/gotools/go/ssa"
"llvm.org/llgo/third_party/gotools/go/types"
"llvm.org/llgo/third_party/gotools/go/types/typeutil"
"llvm.org/llvm/bindings/go/llvm"
)
const (
// non-standard debug metadata tags
tagAutoVariable dwarf.Tag = 0x100
tagArgVariable dwarf.Tag = 0x101
)
type PrefixMap struct {
Source, Replacement string
}
// DIBuilder builds debug metadata for Go programs.
type DIBuilder struct {
// builder is the current builder; there is one per CU.
builder *llvm.DIBuilder
module llvm.Module
files map[*token.File]llvm.Metadata
cu, fn, lb llvm.Metadata
fnFile string
sizes types.Sizes
fset *token.FileSet
prefixMaps []PrefixMap
types typeutil.Map
voidType llvm.Metadata
}
// NewDIBuilder creates a new debug information builder.
func NewDIBuilder(sizes types.Sizes, module llvm.Module, fset *token.FileSet, prefixMaps []PrefixMap) *DIBuilder {
var d DIBuilder
d.module = module
d.files = make(map[*token.File]llvm.Metadata)
d.sizes = sizes
d.fset = fset
d.prefixMaps = prefixMaps
d.builder = llvm.NewDIBuilder(d.module)
d.cu = d.createCompileUnit()
return &d
}
// Destroy destroys the DIBuilder.
func (d *DIBuilder) Destroy() {
d.builder.Destroy()
}
func (d *DIBuilder) scope() llvm.Metadata {
if d.lb.C != nil {
return d.lb
}
if d.fn.C != nil {
return d.fn
}
return d.cu
}
func (d *DIBuilder) remapFilePath(path string) string {
for _, pm := range d.prefixMaps {
if strings.HasPrefix(path, pm.Source) {
return pm.Replacement + path[len(pm.Source):]
}
}
return path
}
func (d *DIBuilder) getFile(file *token.File) llvm.Metadata {
if diFile := d.files[file]; diFile.C != nil {
return diFile
}
diFile := d.builder.CreateFile(d.remapFilePath(file.Name()), "")
d.files[file] = diFile
return diFile
}
// createCompileUnit creates and returns debug metadata for the compile
// unit as a whole, using the first file in the file set as a representative
// (the choice of file is arbitrary).
func (d *DIBuilder) createCompileUnit() llvm.Metadata {
var file *token.File
d.fset.Iterate(func(f *token.File) bool {
file = f
return false
})
dir, err := os.Getwd()
if err != nil {
panic("could not get current directory: " + err.Error())
}
return d.builder.CreateCompileUnit(llvm.DICompileUnit{
Language: llvm.DW_LANG_Go,
File: d.remapFilePath(file.Name()),
Dir: dir,
Producer: "llgo",
})
}
// PushFunction creates debug metadata for the specified function,
// and pushes it onto the scope stack.
func (d *DIBuilder) PushFunction(fnptr llvm.Value, sig *types.Signature, pos token.Pos) {
var diFile llvm.Metadata
var line int
if file := d.fset.File(pos); file != nil {
d.fnFile = file.Name()
diFile = d.getFile(file)
line = file.Line(pos)
}
d.fn = d.builder.CreateFunction(d.scope(), llvm.DIFunction{
Name: fnptr.Name(), // TODO(axw) unmangled name?
LinkageName: fnptr.Name(),
File: diFile,
Line: line,
Type: d.DIType(sig),
IsDefinition: true,
})
fnptr.SetSubprogram(d.fn)
}
// PopFunction pops the previously pushed function off the scope stack.
func (d *DIBuilder) PopFunction() {
d.lb = llvm.Metadata{}
d.fn = llvm.Metadata{}
d.fnFile = ""
}
// Value creates an llvm.dbg.value call for the specified register value.
func (d *DIBuilder) Value(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) {
// TODO(axw)
}
// SetLocation sets the current debug location.
func (d *DIBuilder) SetLocation(b llvm.Builder, pos token.Pos) {
position := d.fset.Position(pos)
d.lb = llvm.Metadata{}
if position.Filename != d.fnFile && position.Filename != "" {
// This can happen rarely, e.g. in init functions.
diFile := d.builder.CreateFile(d.remapFilePath(position.Filename), "")
d.lb = d.builder.CreateLexicalBlockFile(d.scope(), diFile, 0)
}
b.SetCurrentDebugLocation(uint(position.Line), uint(position.Column), d.scope(), llvm.Metadata{})
}
// Finalize must be called after all compilation units are translated,
// generating the final debug metadata for the module.
func (d *DIBuilder) Finalize() {
d.module.AddNamedMetadataOperand(
"llvm.module.flags",
llvm.GlobalContext().MDNode([]llvm.Metadata{
llvm.ConstInt(llvm.Int32Type(), 2, false).ConstantAsMetadata(), // Warn on mismatch
llvm.GlobalContext().MDString("Dwarf Version"),
llvm.ConstInt(llvm.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
d.module.AddNamedMetadataOperand(
"llvm.module.flags",
llvm.GlobalContext().MDNode([]llvm.Metadata{
llvm.ConstInt(llvm.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
llvm.GlobalContext().MDString("Debug Info Version"),
llvm.ConstInt(llvm.Int32Type(), 3, false).ConstantAsMetadata(),
}),
)
d.builder.Finalize()
}
// DIType maps a Go type to DIType debug metadata value.
func (d *DIBuilder) DIType(t types.Type) llvm.Metadata {
return d.typeDebugDescriptor(t, types.TypeString(nil, t))
}
func (d *DIBuilder) typeDebugDescriptor(t types.Type, name string) llvm.Metadata {
// Signature needs to be handled specially, to preprocess
// methods, moving the receiver to the parameter list.
if t, ok := t.(*types.Signature); ok {
return d.descriptorSignature(t, name)
}
if t == nil {
if d.voidType.C == nil {
d.voidType = d.builder.CreateBasicType(llvm.DIBasicType{Name: "void"})
}
return d.voidType
}
if dt, ok := d.types.At(t).(llvm.Metadata); ok {
return dt
}
dt := d.descriptor(t, name)
d.types.Set(t, dt)
return dt
}
func (d *DIBuilder) descriptor(t types.Type, name string) llvm.Metadata {
switch t := t.(type) {
case *types.Basic:
return d.descriptorBasic(t, name)
case *types.Pointer:
return d.descriptorPointer(t)
case *types.Struct:
return d.descriptorStruct(t, name)
case *types.Named:
return d.descriptorNamed(t)
case *types.Array:
return d.descriptorArray(t, name)
case *types.Slice:
return d.descriptorSlice(t, name)
case *types.Map:
return d.descriptorMap(t, name)
case *types.Chan:
return d.descriptorChan(t, name)
case *types.Interface:
return d.descriptorInterface(t, name)
default:
panic(fmt.Sprintf("unhandled type: %T", t))
}
}
func (d *DIBuilder) descriptorBasic(t *types.Basic, name string) llvm.Metadata {
switch t.Kind() {
case types.String:
return d.typeDebugDescriptor(types.NewStruct([]*types.Var{
types.NewVar(0, nil, "ptr", types.NewPointer(types.Typ[types.Uint8])),
types.NewVar(0, nil, "len", types.Typ[types.Int]),
}, nil), name)
case types.UnsafePointer:
return d.builder.CreateBasicType(llvm.DIBasicType{
Name: name,
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
Encoding: llvm.DW_ATE_unsigned,
})
default:
bt := llvm.DIBasicType{
Name: t.String(),
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
}
switch bi := t.Info(); {
case bi&types.IsBoolean != 0:
bt.Encoding = llvm.DW_ATE_boolean
case bi&types.IsUnsigned != 0:
bt.Encoding = llvm.DW_ATE_unsigned
case bi&types.IsInteger != 0:
bt.Encoding = llvm.DW_ATE_signed
case bi&types.IsFloat != 0:
bt.Encoding = llvm.DW_ATE_float
case bi&types.IsComplex != 0:
bt.Encoding = llvm.DW_ATE_imaginary_float
case bi&types.IsUnsigned != 0:
bt.Encoding = llvm.DW_ATE_unsigned
default:
panic(fmt.Sprintf("unhandled: %#v", t))
}
return d.builder.CreateBasicType(bt)
}
}
func (d *DIBuilder) descriptorPointer(t *types.Pointer) llvm.Metadata {
return d.builder.CreatePointerType(llvm.DIPointerType{
Pointee: d.DIType(t.Elem()),
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
AlignInBits: uint32(d.sizes.Alignof(t) * 8),
})
}
func (d *DIBuilder) descriptorStruct(t *types.Struct, name string) llvm.Metadata {
fields := make([]*types.Var, t.NumFields())
for i := range fields {
fields[i] = t.Field(i)
}
offsets := d.sizes.Offsetsof(fields)
members := make([]llvm.Metadata, len(fields))
for i, f := range fields {
// TODO(axw) file/line where member is defined.
t := f.Type()
members[i] = d.builder.CreateMemberType(d.cu, llvm.DIMemberType{
Name: f.Name(),
Type: d.DIType(t),
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
AlignInBits: uint32(d.sizes.Alignof(t) * 8),
OffsetInBits: uint64(offsets[i] * 8),
})
}
// TODO(axw) file/line where struct is defined.
return d.builder.CreateStructType(d.cu, llvm.DIStructType{
Name: name,
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
AlignInBits: uint32(d.sizes.Alignof(t) * 8),
Elements: members,
})
}
func (d *DIBuilder) descriptorNamed(t *types.Named) llvm.Metadata {
var diFile llvm.Metadata
var line int
if file := d.fset.File(t.Obj().Pos()); file != nil {
line = file.Line(t.Obj().Pos())
diFile = d.getFile(file)
}
// Create a placeholder for the named type, to terminate cycles.
name := t.Obj().Name()
placeholder := d.builder.CreateReplaceableCompositeType(d.scope(), llvm.DIReplaceableCompositeType{
Tag: dwarf.TagStructType,
Name: name,
File: diFile,
Line: line,
})
d.types.Set(t, placeholder)
typedef := d.builder.CreateTypedef(llvm.DITypedef{
Type: d.DIType(t.Underlying()),
Name: name,
File: diFile,
Line: line,
})
placeholder.ReplaceAllUsesWith(typedef)
return typedef
}
func (d *DIBuilder) descriptorArray(t *types.Array, name string) llvm.Metadata {
return d.builder.CreateArrayType(llvm.DIArrayType{
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
AlignInBits: uint32(d.sizes.Alignof(t) * 8),
ElementType: d.DIType(t.Elem()),
Subscripts: []llvm.DISubrange{{Count: t.Len()}},
})
}
func (d *DIBuilder) descriptorSlice(t *types.Slice, name string) llvm.Metadata {
sliceStruct := types.NewStruct([]*types.Var{
types.NewVar(0, nil, "ptr", types.NewPointer(t.Elem())),
types.NewVar(0, nil, "len", types.Typ[types.Int]),
types.NewVar(0, nil, "cap", types.Typ[types.Int]),
}, nil)
return d.typeDebugDescriptor(sliceStruct, name)
}
func (d *DIBuilder) descriptorMap(t *types.Map, name string) llvm.Metadata {
// FIXME: This should be DW_TAG_pointer_type to __go_map.
return d.descriptorBasic(types.Typ[types.Uintptr], name)
}
func (d *DIBuilder) descriptorChan(t *types.Chan, name string) llvm.Metadata {
// FIXME: This should be DW_TAG_pointer_type to __go_channel.
return d.descriptorBasic(types.Typ[types.Uintptr], name)
}
func (d *DIBuilder) descriptorInterface(t *types.Interface, name string) llvm.Metadata {
ifaceStruct := types.NewStruct([]*types.Var{
types.NewVar(0, nil, "type", types.NewPointer(types.Typ[types.Uint8])),
types.NewVar(0, nil, "data", types.NewPointer(types.Typ[types.Uint8])),
}, nil)
return d.typeDebugDescriptor(ifaceStruct, name)
}
func (d *DIBuilder) descriptorSignature(t *types.Signature, name string) llvm.Metadata {
// If there's a receiver change the receiver to an
// additional (first) parameter, and take the value of
// the resulting signature instead.
if recv := t.Recv(); recv != nil {
params := t.Params()
paramvars := make([]*types.Var, int(params.Len()+1))
paramvars[0] = recv
for i := 0; i < int(params.Len()); i++ {
paramvars[i+1] = params.At(i)
}
params = types.NewTuple(paramvars...)
t := types.NewSignature(nil, nil, params, t.Results(), t.Variadic())
return d.typeDebugDescriptor(t, name)
}
if dt, ok := d.types.At(t).(llvm.Metadata); ok {
return dt
}
var returnType llvm.Metadata
results := t.Results()
switch n := results.Len(); n {
case 0:
returnType = d.DIType(nil) // void
case 1:
returnType = d.DIType(results.At(0).Type())
default:
fields := make([]*types.Var, results.Len())
for i := range fields {
f := results.At(i)
// Structs may not have multiple fields
// with the same name, excepting "_".
if f.Name() == "" {
f = types.NewVar(f.Pos(), f.Pkg(), "_", f.Type())
}
fields[i] = f
}
returnType = d.typeDebugDescriptor(types.NewStruct(fields, nil), "")
}
var paramTypes []llvm.Metadata
params := t.Params()
if params != nil && params.Len() > 0 {
paramTypes = make([]llvm.Metadata, params.Len()+1)
paramTypes[0] = returnType
for i := range paramTypes[1:] {
paramTypes[i+1] = d.DIType(params.At(i).Type())
}
} else {
paramTypes = []llvm.Metadata{returnType}
}
// TODO(axw) get position of type definition for File field
return d.builder.CreateSubroutineType(llvm.DISubroutineType{
Parameters: paramTypes,
})
}