|  | //===- 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, | 
|  | Function:     fnptr, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // PopFunction pops the previously pushed function off the scope stack. | 
|  | func (d *DIBuilder) PopFunction() { | 
|  | d.lb = llvm.Metadata{} | 
|  | d.fn = llvm.Metadata{} | 
|  | d.fnFile = "" | 
|  | } | 
|  |  | 
|  | // Declare creates an llvm.dbg.declare call for the specified function | 
|  | // parameter or local variable. | 
|  | func (d *DIBuilder) Declare(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) { | 
|  | tag := tagAutoVariable | 
|  | if paramIndex >= 0 { | 
|  | tag = tagArgVariable | 
|  | } | 
|  | var diFile llvm.Metadata | 
|  | var line int | 
|  | if file := d.fset.File(v.Pos()); file != nil { | 
|  | line = file.Line(v.Pos()) | 
|  | diFile = d.getFile(file) | 
|  | } | 
|  | localVar := d.builder.CreateLocalVariable(d.scope(), llvm.DILocalVariable{ | 
|  | Tag:   tag, | 
|  | Name:  llv.Name(), | 
|  | File:  diFile, | 
|  | Line:  line, | 
|  | ArgNo: paramIndex + 1, | 
|  | Type:  d.DIType(v.Type()), | 
|  | }) | 
|  | expr := d.builder.CreateExpression(nil) | 
|  | d.builder.InsertDeclareAtEnd(llv, localVar, expr, b.GetInsertBlock()) | 
|  | } | 
|  |  | 
|  | // 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(), 2, 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, | 
|  | }) | 
|  | } |