438 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
//===- debug.go - debug info builder --------------------------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// 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) {
 | 
						|
	if !pos.IsValid() {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	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),
 | 
						|
			AlignInBits: uint64(d.sizes.Alignof(t) * 8),
 | 
						|
			Encoding:    llvm.DW_ATE_unsigned,
 | 
						|
		})
 | 
						|
	default:
 | 
						|
		bt := llvm.DIBasicType{
 | 
						|
			Name:        t.String(),
 | 
						|
			SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
 | 
						|
			AlignInBits: uint64(d.sizes.Alignof(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: uint64(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:  uint64(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: uint64(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: uint64(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,
 | 
						|
	})
 | 
						|
}
 |