Browse Source

compiler: add debug info for function arguments

This commit adds debug info to function arguments, so that in many cases
you can see them when compiling with less optimizations enabled.
Unfortunately, due to the way Go SSA works, it is hard to preserve them
in many cases.
Local variables are not yet saved.

Also, change the language type to C, to make sure lldb shows function
arguments. The previous language was Modula 3, apparently due to a
off-by-one error somewhere.
gc-precise
Ayke van Laethem 6 years ago
committed by Ron Evans
parent
commit
371c468e8e
  1. 40
      compiler/calls.go
  2. 215
      compiler/compiler.go
  3. 2
      interp/frame.go
  4. 3
      interp/interp.go
  5. 2
      interp/scan.go

40
compiler/calls.go

@ -55,6 +55,25 @@ func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
} }
} }
// Expand an argument type to a list of offsets from the start of the object.
// Used together with expandFormalParam to get the offset of each value from the
// start of the non-expanded value.
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := c.flattenAggregateTypeOffsets(t)
if len(fields) <= MaxFieldsPerParam {
return fields
} else {
// failed to lower
return []uint64{0}
}
default:
// TODO: split small arrays
return []uint64{0}
}
}
// Equivalent of expandFormalParamType for parameter values. // Equivalent of expandFormalParamType for parameter values.
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value { func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() { switch v.Type().TypeKind() {
@ -92,6 +111,27 @@ func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
} }
} }
// Return the offsets from the start of the object if this object type were
// flattened like in flattenAggregate. Used together with flattenAggregate to
// know the start indices of each value in the non-flattened object.
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]uint64, 0, t.StructElementTypesCount())
for fieldIndex, field := range t.StructElementTypes() {
suboffsets := c.flattenAggregateTypeOffsets(field)
offset := c.targetData.ElementOffset(t, fieldIndex)
for i := range suboffsets {
suboffsets[i] += offset
}
fields = append(fields, suboffsets...)
}
return fields
default:
return []uint64{0}
}
}
// Break down a struct into its elementary types for argument passing. The value // Break down a struct into its elementary types for argument passing. The value
// equivalent of flattenAggregateType // equivalent of flattenAggregateType
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value { func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {

215
compiler/compiler.go

@ -52,7 +52,6 @@ type Compiler struct {
dibuilder *llvm.DIBuilder dibuilder *llvm.DIBuilder
cu llvm.Metadata cu llvm.Metadata
difiles map[string]llvm.Metadata difiles map[string]llvm.Metadata
ditypes map[string]llvm.Metadata
machine llvm.TargetMachine machine llvm.TargetMachine
targetData llvm.TargetData targetData llvm.TargetData
intType llvm.Type intType llvm.Type
@ -96,7 +95,6 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
c := &Compiler{ c := &Compiler{
Config: config, Config: config,
difiles: make(map[string]llvm.Metadata), difiles: make(map[string]llvm.Metadata),
ditypes: make(map[string]llvm.Metadata),
} }
target, err := llvm.GetTargetFromTriple(config.Triple) target, err := llvm.GetTargetFromTriple(config.Triple)
@ -252,7 +250,7 @@ func (c *Compiler) Compile(mainPath string) []error {
// Initialize debug information. // Initialize debug information.
if c.Debug { if c.Debug {
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
Language: llvm.DW_LANG_Go, Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
File: mainPath, File: mainPath,
Dir: "", Dir: "",
Producer: "TinyGo", Producer: "TinyGo",
@ -387,6 +385,13 @@ func (c *Compiler) Compile(mainPath string) []error {
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
}), }),
) )
c.mod.AddNamedMetadataOperand("llvm.module.flags",
c.ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
llvm.GlobalContext().MDString("Dwarf Version"),
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
c.dibuilder.Finalize() c.dibuilder.Finalize()
} }
@ -550,39 +555,166 @@ func isPointer(typ types.Type) bool {
// Get the DWARF type for this Go type. // Get the DWARF type for this Go type.
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata { func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
name := typ.String() llvmType := c.getLLVMType(typ)
if dityp, ok := c.ditypes[name]; ok { sizeInBytes := c.targetData.TypeAllocSize(llvmType)
return dityp switch typ := typ.(type) {
} else { case *types.Array:
llvmType := c.getLLVMType(typ) return c.dibuilder.CreateArrayType(llvm.DIArrayType{
sizeInBytes := c.targetData.TypeAllocSize(llvmType) SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
ElementType: c.getDIType(typ.Elem()),
Subscripts: []llvm.DISubrange{
llvm.DISubrange{
Lo: 0,
Count: typ.Len(),
},
},
})
case *types.Basic:
var encoding llvm.DwarfTypeEncoding var encoding llvm.DwarfTypeEncoding
switch typ := typ.(type) { if typ.Info()&types.IsBoolean != 0 {
case *types.Basic: encoding = llvm.DW_ATE_boolean
if typ.Info()&types.IsBoolean != 0 { } else if typ.Info()&types.IsFloat != 0 {
encoding = llvm.DW_ATE_boolean encoding = llvm.DW_ATE_float
} else if typ.Info()&types.IsFloat != 0 { } else if typ.Info()&types.IsComplex != 0 {
encoding = llvm.DW_ATE_float encoding = llvm.DW_ATE_complex_float
} else if typ.Info()&types.IsComplex != 0 { } else if typ.Info()&types.IsUnsigned != 0 {
encoding = llvm.DW_ATE_complex_float encoding = llvm.DW_ATE_unsigned
} else if typ.Info()&types.IsUnsigned != 0 { } else if typ.Info()&types.IsInteger != 0 {
encoding = llvm.DW_ATE_unsigned encoding = llvm.DW_ATE_signed
} else if typ.Info()&types.IsInteger != 0 { } else if typ.Kind() == types.UnsafePointer {
encoding = llvm.DW_ATE_signed return c.dibuilder.CreatePointerType(llvm.DIPointerType{
} else if typ.Kind() == types.UnsafePointer { Name: "unsafe.Pointer",
encoding = llvm.DW_ATE_address SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
} AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
case *types.Pointer: AddressSpace: 0,
encoding = llvm.DW_ATE_address })
} else if typ.Info()&types.IsString != 0 {
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
Name: "string",
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
Elements: []llvm.Metadata{
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "ptr",
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
OffsetInBits: 0,
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
}),
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "len",
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
Type: c.getDIType(types.Typ[types.Uintptr]),
}),
},
})
} else {
panic("unknown basic type")
} }
// TODO: other types return c.dibuilder.CreateBasicType(llvm.DIBasicType{
dityp = c.dibuilder.CreateBasicType(llvm.DIBasicType{ Name: typ.String(),
Name: name,
SizeInBits: sizeInBytes * 8, SizeInBits: sizeInBytes * 8,
Encoding: encoding, Encoding: encoding,
}) })
c.ditypes[name] = dityp case *types.Chan:
return dityp return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
case *types.Interface:
return c.getDIType(c.ir.Program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
case *types.Map:
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
case *types.Named:
return c.dibuilder.CreateTypedef(llvm.DITypedef{
Type: c.getDIType(typ.Underlying()),
Name: typ.String(),
})
case *types.Pointer:
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
Pointee: c.getDIType(typ.Elem()),
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
AddressSpace: 0,
})
case *types.Signature:
// actually a closure
fields := llvmType.StructElementTypes()
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
Elements: []llvm.Metadata{
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "context",
SizeInBits: c.targetData.TypeAllocSize(fields[1]) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[1])) * 8,
OffsetInBits: 0,
Type: c.getDIType(types.Typ[types.UnsafePointer]),
}),
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "fn",
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
Type: c.getDIType(types.Typ[types.UnsafePointer]),
}),
},
})
case *types.Slice:
fields := llvmType.StructElementTypes()
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
Name: typ.String(),
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
Elements: []llvm.Metadata{
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "ptr",
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
OffsetInBits: 0,
Type: c.getDIType(types.NewPointer(typ.Elem())),
}),
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "len",
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
Type: c.getDIType(types.Typ[types.Uintptr]),
}),
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: "cap",
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
Type: c.getDIType(types.Typ[types.Uintptr]),
}),
},
})
case *types.Struct:
elements := make([]llvm.Metadata, typ.NumFields())
for i := range elements {
field := typ.Field(i)
fieldType := field.Type()
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
// XXX hack to avoid recursive types
fieldType = types.Typ[types.UnsafePointer]
}
llvmField := c.getLLVMType(fieldType)
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
Name: field.Name(),
SizeInBits: c.targetData.TypeAllocSize(llvmField) * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmField)) * 8,
OffsetInBits: c.targetData.ElementOffset(llvmType, i) * 8,
Type: c.getDIType(fieldType),
})
}
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
SizeInBits: sizeInBytes * 8,
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
Elements: elements,
})
default:
panic("unknown type while generating DWARF debug type: " + typ.String())
} }
} }
@ -733,15 +865,30 @@ func (c *Compiler) parseFunc(frame *Frame) {
// Add debug information to this parameter (if available) // Add debug information to this parameter (if available)
if c.Debug && frame.fn.Syntax() != nil { if c.Debug && frame.fn.Syntax() != nil {
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos()) pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{ diType := c.getDIType(param.Type())
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
Name: param.Name(), Name: param.Name(),
File: c.difiles[pos.Filename], File: c.difiles[pos.Filename],
Line: pos.Line, Line: pos.Line,
Type: c.getDIType(param.Type()), Type: diType,
AlwaysPreserve: true, AlwaysPreserve: true,
ArgNo: i + 1, ArgNo: i + 1,
}) })
// TODO: set the value of this parameter. loc := c.builder.GetCurrentDebugLocation()
if len(fields) == 1 {
expr := c.dibuilder.CreateExpression(nil)
c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
} else {
fieldOffsets := c.expandFormalParamOffsets(llvmType)
for i, field := range fields {
expr := c.dibuilder.CreateExpression([]int64{
0x1000, // DW_OP_LLVM_fragment
int64(fieldOffsets[i]) * 8, // offset in bits
int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
})
c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
}
}
} }
} }

2
interp/frame.go

@ -347,6 +347,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)} fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
case callee.Name() == "runtime.nanotime": case callee.Name() == "runtime.nanotime":
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)} fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
case callee.Name() == "llvm.dbg.value":
// do nothing
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic": case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
// This are all print instructions, which necessarily have side // This are all print instructions, which necessarily have side
// effects but no results. // effects but no results.

3
interp/interp.go

@ -18,7 +18,6 @@ type Eval struct {
TargetData llvm.TargetData TargetData llvm.TargetData
Debug bool Debug bool
builder llvm.Builder builder llvm.Builder
dibuilder *llvm.DIBuilder
dirtyGlobals map[llvm.Value]struct{} dirtyGlobals map[llvm.Value]struct{}
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
} }
@ -38,7 +37,6 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
dirtyGlobals: map[llvm.Value]struct{}{}, dirtyGlobals: map[llvm.Value]struct{}{},
} }
e.builder = mod.Context().NewBuilder() e.builder = mod.Context().NewBuilder()
e.dibuilder = llvm.NewDIBuilder(mod)
initAll := mod.NamedFunction(name) initAll := mod.NamedFunction(name)
bb := initAll.EntryBasicBlock() bb := initAll.EntryBasicBlock()
@ -49,7 +47,6 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
e.builder.SetInsertPointBefore(bb.FirstInstruction()) e.builder.SetInsertPointBefore(bb.FirstInstruction())
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy") dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
e.builder.SetInsertPointBefore(dummy) e.builder.SetInsertPointBefore(dummy)
e.builder.SetInstDebugLocation(bb.FirstInstruction())
var initCalls []llvm.Value var initCalls []llvm.Value
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) { for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst == dummy { if inst == dummy {

2
interp/scan.go

@ -35,6 +35,8 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
return &sideEffectResult{severity: sideEffectLimited} return &sideEffectResult{severity: sideEffectLimited}
case "runtime.interfaceImplements": case "runtime.interfaceImplements":
return &sideEffectResult{severity: sideEffectNone} return &sideEffectResult{severity: sideEffectNone}
case "llvm.dbg.value":
return &sideEffectResult{severity: sideEffectNone}
} }
if e.sideEffectFuncs == nil { if e.sideEffectFuncs == nil {
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult) e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)

Loading…
Cancel
Save