compiler: add basic debug info
Only implements debugging with source lines and stacktraces (without parameters), which already is a huge improvement over no debug information at all. This should be extended to arguments and local variables (of the correct DWARF type), but this is more work.
Этот коммит содержится в:
родитель
a7fcef62e0
коммит
efdc2b8672
1 изменённых файлов: 114 добавлений и 0 удалений
114
compiler.go
114
compiler.go
|
@ -8,6 +8,7 @@ import (
|
|||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -29,10 +30,15 @@ func init() {
|
|||
|
||||
type Compiler struct {
|
||||
dumpSSA bool
|
||||
debug bool
|
||||
triple string
|
||||
mod llvm.Module
|
||||
ctx llvm.Context
|
||||
builder llvm.Builder
|
||||
dibuilder *llvm.DIBuilder
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[string]llvm.Metadata
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
|
@ -62,6 +68,7 @@ type Frame struct {
|
|||
cleanupBlock llvm.BasicBlock
|
||||
suspendBlock llvm.BasicBlock
|
||||
deferred []*Defer
|
||||
difunc llvm.Metadata
|
||||
}
|
||||
|
||||
type Defer struct {
|
||||
|
@ -79,7 +86,10 @@ var cgoWrapperError = errors.New("tinygo internal: cgo wrapper")
|
|||
func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
||||
c := &Compiler{
|
||||
dumpSSA: dumpSSA,
|
||||
debug: false, // TODO: make configurable
|
||||
triple: triple,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[string]llvm.Metadata),
|
||||
}
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(triple)
|
||||
|
@ -92,6 +102,7 @@ func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
|||
c.mod = llvm.NewModule(pkgName)
|
||||
c.ctx = c.mod.Context()
|
||||
c.builder = c.ctx.NewBuilder()
|
||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||
|
||||
// Depends on platform (32bit or 64bit), but fix it here for now.
|
||||
c.intType = llvm.Int32Type()
|
||||
|
@ -218,6 +229,15 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
|
||||
c.ir.AnalyseGoCalls() // check whether we need a scheduler
|
||||
|
||||
// Initialize debug information.
|
||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: llvm.DW_LANG_Go,
|
||||
File: mainPath,
|
||||
Dir: "",
|
||||
Producer: "TinyGo",
|
||||
Optimized: true,
|
||||
})
|
||||
|
||||
var frames []*Frame
|
||||
|
||||
// Declare all named struct types.
|
||||
|
@ -423,6 +443,16 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
|
||||
c.mod.NamedGlobal("runtime.firstInterfaceNum").SetInitializer(llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.FirstDynamicType()), false))
|
||||
|
||||
// see: https://reviews.llvm.org/D18355
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.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(), // DWARF version
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -621,6 +651,47 @@ func isPointer(typ types.Type) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Get the DWARF type for this Go type.
|
||||
func (c *Compiler) getDIType(typ types.Type) (llvm.Metadata, error) {
|
||||
name := typ.String()
|
||||
if dityp, ok := c.ditypes[name]; ok {
|
||||
return dityp, nil
|
||||
} else {
|
||||
llvmType, err := c.getLLVMType(typ)
|
||||
if err != nil {
|
||||
return llvm.Metadata{}, err
|
||||
}
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
var encoding llvm.DwarfTypeEncoding
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
if typ.Info()&types.IsBoolean != 0 {
|
||||
encoding = llvm.DW_ATE_boolean
|
||||
} else if typ.Info()&types.IsFloat != 0 {
|
||||
encoding = llvm.DW_ATE_float
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
encoding = llvm.DW_ATE_complex_float
|
||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||
encoding = llvm.DW_ATE_unsigned
|
||||
} else if typ.Info()&types.IsInteger != 0 {
|
||||
encoding = llvm.DW_ATE_signed
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
encoding = llvm.DW_ATE_address
|
||||
}
|
||||
case *types.Pointer:
|
||||
encoding = llvm.DW_ATE_address
|
||||
}
|
||||
// TODO: other types
|
||||
return c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: name,
|
||||
SizeInBits: sizeInBytes * 8,
|
||||
Encoding: encoding,
|
||||
}), nil
|
||||
c.ditypes[name] = dityp
|
||||
return dityp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get all methods of a type: both value receivers and pointer receivers.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
var methods []*types.Selection
|
||||
|
@ -713,6 +784,44 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
if frame.fn.llvmFn.IsNil() {
|
||||
frame.fn.llvmFn = llvm.AddFunction(c.mod, name, fnType)
|
||||
}
|
||||
|
||||
if c.debug && f.fn.Syntax() != nil && len(f.fn.Blocks) != 0 {
|
||||
// Create debug info file if needed.
|
||||
pos := c.ir.program.Fset.Position(f.fn.Syntax().Pos())
|
||||
if _, ok := c.difiles[pos.Filename]; !ok {
|
||||
dir, file := filepath.Split(pos.Filename)
|
||||
c.difiles[pos.Filename] = c.dibuilder.CreateFile(file, dir[:len(dir)-1])
|
||||
}
|
||||
|
||||
// Debug info for this function.
|
||||
diparams := make([]llvm.Metadata, 0, len(f.fn.Params))
|
||||
for _, param := range f.fn.Params {
|
||||
ditype, err := c.getDIType(param.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diparams = append(diparams, ditype)
|
||||
}
|
||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: c.difiles[pos.Filename],
|
||||
Parameters: diparams,
|
||||
Flags: 0, // ?
|
||||
})
|
||||
frame.difunc = c.dibuilder.CreateFunction(c.difiles[pos.Filename], llvm.DIFunction{
|
||||
Name: f.fn.RelString(nil),
|
||||
LinkageName: f.LinkName(),
|
||||
File: c.difiles[pos.Filename],
|
||||
Line: pos.Line,
|
||||
Type: diFuncType,
|
||||
LocalToUnit: true,
|
||||
IsDefinition: true,
|
||||
ScopeLine: 0,
|
||||
Flags: llvm.FlagPrototyped,
|
||||
Optimized: true,
|
||||
})
|
||||
frame.fn.llvmFn.SetSubprogram(frame.difunc)
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
|
@ -1136,6 +1245,11 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
}
|
||||
|
||||
func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
if c.debug {
|
||||
pos := c.ir.program.Fset.Position(instr.Pos())
|
||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
switch instr := instr.(type) {
|
||||
case ssa.Value:
|
||||
value, err := c.parseExpr(frame, instr)
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче