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/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -29,10 +30,15 @@ func init() {
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
dumpSSA bool
|
dumpSSA bool
|
||||||
|
debug bool
|
||||||
triple string
|
triple string
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
ctx llvm.Context
|
ctx llvm.Context
|
||||||
builder llvm.Builder
|
builder llvm.Builder
|
||||||
|
dibuilder *llvm.DIBuilder
|
||||||
|
cu 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
|
||||||
|
@ -62,6 +68,7 @@ type Frame struct {
|
||||||
cleanupBlock llvm.BasicBlock
|
cleanupBlock llvm.BasicBlock
|
||||||
suspendBlock llvm.BasicBlock
|
suspendBlock llvm.BasicBlock
|
||||||
deferred []*Defer
|
deferred []*Defer
|
||||||
|
difunc llvm.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
type Defer struct {
|
type Defer struct {
|
||||||
|
@ -79,7 +86,10 @@ var cgoWrapperError = errors.New("tinygo internal: cgo wrapper")
|
||||||
func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
dumpSSA: dumpSSA,
|
dumpSSA: dumpSSA,
|
||||||
|
debug: false, // TODO: make configurable
|
||||||
triple: triple,
|
triple: triple,
|
||||||
|
difiles: make(map[string]llvm.Metadata),
|
||||||
|
ditypes: make(map[string]llvm.Metadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := llvm.GetTargetFromTriple(triple)
|
target, err := llvm.GetTargetFromTriple(triple)
|
||||||
|
@ -92,6 +102,7 @@ func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
||||||
c.mod = llvm.NewModule(pkgName)
|
c.mod = llvm.NewModule(pkgName)
|
||||||
c.ctx = c.mod.Context()
|
c.ctx = c.mod.Context()
|
||||||
c.builder = c.ctx.NewBuilder()
|
c.builder = c.ctx.NewBuilder()
|
||||||
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
|
|
||||||
// Depends on platform (32bit or 64bit), but fix it here for now.
|
// Depends on platform (32bit or 64bit), but fix it here for now.
|
||||||
c.intType = llvm.Int32Type()
|
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.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
|
||||||
c.ir.AnalyseGoCalls() // check whether we need a scheduler
|
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
|
var frames []*Frame
|
||||||
|
|
||||||
// Declare all named struct types.
|
// 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))
|
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
|
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.
|
// Get all methods of a type: both value receivers and pointer receivers.
|
||||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||||
var methods []*types.Selection
|
var methods []*types.Selection
|
||||||
|
@ -713,6 +784,44 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
||||||
if frame.fn.llvmFn.IsNil() {
|
if frame.fn.llvmFn.IsNil() {
|
||||||
frame.fn.llvmFn = llvm.AddFunction(c.mod, name, fnType)
|
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
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1136,6 +1245,11 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) 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) {
|
switch instr := instr.(type) {
|
||||||
case ssa.Value:
|
case ssa.Value:
|
||||||
value, err := c.parseExpr(frame, instr)
|
value, err := c.parseExpr(frame, instr)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче