Merge pull request #806 from cpunion/llvm-debug

Debug: fix struct vars debug, params modication, supports expressions, lexical scope/lifecycle
This commit is contained in:
xushiwei
2024-10-29 11:09:00 +08:00
committed by GitHub
19 changed files with 861 additions and 293 deletions

View File

@@ -17,7 +17,6 @@
package ssa
import (
"go/token"
"go/types"
"log"
"strconv"
@@ -281,7 +280,8 @@ func (p Function) NewBuilder() Builder {
b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize()
return &aBuilder{b, nil, p, p.Pkg, prog, make(map[Expr]dbgExpr)}
return &aBuilder{b, nil, p, p.Pkg, prog,
make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)}
}
// HasBody reports whether the function has a body.
@@ -334,33 +334,24 @@ func (p Function) SetRecover(blk BasicBlock) {
p.recov = blk
}
func (p Function) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
if p.diFunc == nil {
paramTypes := make([]llvm.Metadata, len(p.params))
for i, t := range p.params {
paramTypes[i] = b.diType(t, pos).ll
}
diFuncType := b.di.CreateSubroutineType(llvm.DISubroutineType{
File: b.file(pos.Filename).ll,
Parameters: paramTypes,
})
p.diFunc = &aDIFunction{
b.di.CreateFunction(
b.file(pos.Filename).ll,
llvm.DIFunction{
Type: diFuncType,
Name: p.Name(),
LinkageName: p.Name(),
File: b.file(pos.Filename).ll,
Line: pos.Line,
IsDefinition: true,
Optimized: false,
},
),
}
p.impl.SetSubprogram(p.diFunc.ll)
}
return &aDIScopeMeta{p.diFunc.ll}
// -----------------------------------------------------------------------------
type inlineAttr int
const (
NoInline inlineAttr = iota
AlwaysInline
InlineHint
)
func (p Function) Inline(inline inlineAttr) {
inlineAttrName := map[inlineAttr]string{
NoInline: "noinline",
AlwaysInline: "alwaysinline",
InlineHint: "inlinehint",
}[inline]
inlineAttr := p.Pkg.mod.Context().CreateEnumAttribute(llvm.AttributeKindID(inlineAttrName), 0)
p.impl.AddFunctionAttr(inlineAttr)
}
// -----------------------------------------------------------------------------

298
ssa/di.go
View File

@@ -6,6 +6,7 @@ import (
"go/token"
"go/types"
"path/filepath"
"unsafe"
"github.com/goplus/llvm"
)
@@ -19,6 +20,7 @@ type aDIBuilder struct {
prog Program
types map[Type]DIType
positioner Positioner
m llvm.Module // Add this field
}
type diBuilder = *aDIBuilder
@@ -26,20 +28,21 @@ type diBuilder = *aDIBuilder
func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder {
m := pkg.mod
ctx := m.Context()
m.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
ctx.MDString("Debug Info Version"),
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
}),
)
m.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
ctx.MDString("Dwarf Version"),
llvm.ConstInt(ctx.Int32Type(), 5, false).ConstantAsMetadata(),
}),
)
b := &aDIBuilder{
di: llvm.NewDIBuilder(m),
prog: prog,
types: make(map[*aType]DIType),
positioner: positioner,
m: m, // Initialize the m field
}
b.addNamedMetadataOperand("llvm.module.flags", 2, "Debug Info Version", 3)
b.addNamedMetadataOperand("llvm.module.flags", 7, "Dwarf Version", 4)
b.addNamedMetadataOperand("llvm.module.flags", 1, "wchar_size", 4)
b.addNamedMetadataOperand("llvm.module.flags", 8, "PIC Level", 2)
b.addNamedMetadataOperand("llvm.module.flags", 7, "uwtable", 1)
b.addNamedMetadataOperand("llvm.module.flags", 7, "frame-pointer", 1)
// Add llvm.ident metadata
identNode := ctx.MDNode([]llvm.Metadata{
@@ -47,12 +50,19 @@ func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder {
})
m.AddNamedMetadataOperand("llvm.ident", identNode)
return &aDIBuilder{
di: llvm.NewDIBuilder(m),
prog: prog,
types: make(map[*aType]DIType),
positioner: positioner,
}
return b
}
// New method to add named metadata operand
func (b diBuilder) addNamedMetadataOperand(name string, intValue int, stringValue string, intValue2 int) {
ctx := b.m.Context()
b.m.AddNamedMetadataOperand(name,
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), uint64(intValue), false).ConstantAsMetadata(),
ctx.MDString(stringValue),
llvm.ConstInt(ctx.Int32Type(), uint64(intValue2), false).ConstantAsMetadata(),
}),
)
}
// ----------------------------------------------------------------------------
@@ -77,7 +87,7 @@ func (b diBuilder) createCompileUnit(filename, dir string) CompilationUnit {
File: filename,
Dir: dir,
Producer: "LLGo",
Optimized: false,
Optimized: true,
RuntimeVersion: 1,
})}
}
@@ -125,10 +135,9 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Basic:
if t.Kind() == types.UnsafePointer {
typ = b.di.CreatePointerType(llvm.DIPointerType{
Name: name,
SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(t) * 8),
AddressSpace: 0,
Name: name,
SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(t) * 8),
})
return &aDIType{typ}
}
@@ -158,9 +167,8 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Pointer:
return b.createPointerType(name, b.prog.rawType(t.Elem()), pos)
case *types.Named:
ty = b.prog.rawType(t.Underlying())
pos = b.positioner.Position(t.Obj().Pos())
return b.diTypeEx(name, ty, pos)
// Create typedef type for named types
return b.createTypedefType(name, ty, pos)
case *types.Interface:
ty := b.prog.rtType("Iface")
return b.createInterfaceType(name, ty)
@@ -180,6 +188,8 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Map:
ty := b.prog.rtType("Map")
return b.createMapType(name, ty, pos)
case *types.Tuple:
return b.createTupleType(name, ty, pos)
default:
panic(fmt.Errorf("can't create debug info of type: %v, %T", ty.RawType(), ty.RawType()))
}
@@ -194,6 +204,10 @@ type aDIFunction struct {
type DIFunction = *aDIFunction
func (p Function) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
return &aDIScopeMeta{p.diFunc.ll}
}
// ----------------------------------------------------------------------------
type aDIGlobalVariableExpression struct {
@@ -221,16 +235,28 @@ func (b diBuilder) createGlobalVariableExpression(scope DIScope, pos token.Posit
// ----------------------------------------------------------------------------
type aDILexicalBlock struct {
ll llvm.Metadata
}
type DILexicalBlock = *aDILexicalBlock
func (l *aDILexicalBlock) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
return &aDIScopeMeta{l.ll}
}
// ----------------------------------------------------------------------------
type aDIVar struct {
ll llvm.Metadata
}
type DIVar = *aDIVar
func (b diBuilder) createParameterVariable(f Function, pos token.Position, name string, argNo int, ty DIType) DIVar {
func (b diBuilder) createParameterVariable(scope DIScope, pos token.Position, name string, argNo int, ty DIType) DIVar {
return &aDIVar{
ll: b.di.CreateParameterVariable(
f.scopeMeta(b, pos).ll,
scope.scopeMeta(b, pos).ll,
llvm.DIParameterVariable{
Name: name,
File: b.file(pos.Filename).ll,
@@ -258,6 +284,18 @@ func (b diBuilder) createAutoVariable(scope DIScope, pos token.Position, name st
}
}
func (b diBuilder) createTypedefType(name string, ty Type, pos token.Position) DIType {
underlyingType := b.diType(b.prog.rawType(ty.RawType().(*types.Named).Underlying()), pos)
typ := b.di.CreateTypedef(llvm.DITypedef{
Name: name,
Type: underlyingType.ll,
File: b.file(pos.Filename).ll,
Line: pos.Line,
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType()) * 8),
})
return &aDIType{typ}
}
func (b diBuilder) createStringType() DIType {
ty := b.prog.rtType("String")
return b.doCreateStructType("string", ty, token.Position{}, func(ditStruct DIType) []llvm.Metadata {
@@ -362,12 +400,12 @@ func (b diBuilder) createComplexType(t Type) DIType {
}
func (b diBuilder) createPointerType(name string, ty Type, pos token.Position) DIType {
ptrType := b.prog.VoidPtr()
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
Name: name,
Pointee: b.diType(ty, pos).ll,
SizeInBits: b.prog.SizeOf(ty) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType())) * 8,
AddressSpace: 0,
Name: name,
Pointee: b.diType(ty, pos).ll,
SizeInBits: b.prog.SizeOf(ptrType) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(ptrType.RawType())) * 8,
})}
}
@@ -421,6 +459,26 @@ func (b diBuilder) createStructType(name string, ty Type, pos token.Position) (r
})
}
func (b diBuilder) createTupleType(name string, ty Type, pos token.Position) DIType {
tupleType := ty.RawType().(*types.Tuple)
if tupleType.Len() == 0 {
return &aDIType{}
}
if tupleType.Len() == 1 {
t := b.prog.rawType(tupleType.At(0).Type())
return b.diType(t, pos)
}
return b.doCreateStructType(name, ty, pos, func(ditStruct DIType) []llvm.Metadata {
fields := make([]llvm.Metadata, ty.RawType().(*types.Tuple).Len())
for i := 0; i < ty.RawType().(*types.Tuple).Len(); i++ {
field := ty.RawType().(*types.Tuple).At(i)
tyField := b.prog.rawType(field.Type())
fields[i] = b.createMemberTypeEx(field.Name(), ty, tyField, i, pos, 0)
}
return fields
})
}
func (b diBuilder) createFuncPtrType(name string, ty Type, pos token.Position) DIType {
ptr := b.prog.VoidPtr()
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
@@ -477,9 +535,9 @@ func (b diBuilder) diTypeEx(name string, t Type, pos token.Position) DIType {
return ty
}
func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt DIType, argNo int) DIVar {
func (b diBuilder) varParam(scope DIScope, pos token.Position, varName string, vt DIType, argNo int) DIVar {
return b.createParameterVariable(
f,
scope,
pos,
varName,
argNo,
@@ -487,8 +545,8 @@ func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt D
)
}
func (b diBuilder) varAuto(f Function, pos token.Position, varName string, vt DIType) DIVar {
return b.createAutoVariable(f, pos, varName, vt)
func (b diBuilder) varAuto(scope DIScope, pos token.Position, varName string, vt DIType) DIVar {
return b.createAutoVariable(scope, pos, varName, vt)
}
func (b diBuilder) file(filename string) DIFile {
@@ -510,21 +568,21 @@ func (b diBuilder) createExpression(ops []uint64) DIExpression {
// -----------------------------------------------------------------------------
// Copy to alloca'd memory to get declareable address.
func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, deref bool) {
func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, exists bool) {
if v, ok := b.dbgVars[v]; ok {
return v.ptr, v.val, v.deref
return v.ptr, v.val, true
}
t := v.Type.RawType().Underlying()
dbgPtr, dbgVal, deref = b.doConstructDebugAddr(v, t)
b.dbgVars[v] = dbgExpr{dbgPtr, dbgVal, deref}
return dbgPtr, dbgVal, deref
dbgPtr, dbgVal = b.doConstructDebugAddr(v, t)
dbgExpr := dbgExpr{dbgPtr, dbgVal}
b.dbgVars[v] = dbgExpr
b.dbgVars[dbgVal] = dbgExpr
return dbgPtr, dbgVal, false
}
func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr, deref bool) {
func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr) {
var ty Type
switch t := t.(type) {
case *types.Pointer:
return v, v, false
case *types.Basic:
if t.Info()&types.IsComplex != 0 {
if t.Kind() == types.Complex128 {
@@ -554,34 +612,111 @@ func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal
dbgPtr.Type = b.Prog.Pointer(v.Type)
b.Store(dbgPtr, v)
dbgVal = b.Load(dbgPtr)
return dbgPtr, dbgVal, deref
return dbgPtr, dbgVal
}
func (b Builder) di() diBuilder {
return b.Pkg.di
}
func (b Builder) DIDeclare(v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
dbgPtr, _, _ := b.constructDebugAddr(v)
func (b Builder) DIParam(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
b.DIValue(variable, v, dv, scope, pos, blk)
}
func (b Builder) DIDeclare(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
expr := b.di().createExpression(nil)
b.di().dbgDeclare(dbgPtr, dv, scope, pos, expr, blk)
b.di().dbgDeclare(v, dv, scope, pos, expr, blk)
}
func (b Builder) DIValue(v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
expr := b.di().createExpression(nil)
b.di().dbgValue(v, dv, scope, pos, expr, blk)
func (b Builder) DIValue(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
ty := v.Type.RawType().Underlying()
if !needConstructAddr(ty) {
expr := b.di().createExpression(nil)
b.di().dbgValue(v, dv, scope, pos, expr, blk)
} else {
dbgPtr, _, _ := b.constructDebugAddr(v)
expr := b.di().createExpression([]uint64{opDeref})
b.di().dbgValue(dbgPtr, dv, scope, pos, expr, blk)
}
}
func (b Builder) DIVarParam(f Function, pos token.Position, varName string, vt Type, argNo int) DIVar {
const (
opDeref = 0x06
)
func needConstructAddr(t types.Type) bool {
switch t := t.(type) {
case *types.Basic:
if t.Info()&types.IsComplex != 0 {
return true
} else if t.Info()&types.IsString != 0 {
return true
}
return false
case *types.Pointer:
return false
default:
return true
}
}
func (b Builder) DIVarParam(scope DIScope, pos token.Position, varName string, vt Type, argNo int) DIVar {
t := b.di().diType(vt, pos)
return b.di().varParam(f, pos, varName, t, argNo)
return b.di().varParam(scope, pos, varName, t, argNo)
}
func (b Builder) DIVarAuto(f Function, pos token.Position, varName string, vt Type) DIVar {
func (b Builder) DIVarAuto(scope DIScope, pos token.Position, varName string, vt Type) DIVar {
t := b.di().diType(vt, pos)
return b.di().varAuto(f, pos, varName, t)
return b.di().varAuto(scope, pos, varName, t)
}
// hack for types.Scope
type hackScope struct {
parent *types.Scope
children []*types.Scope
number int // parent.children[number-1] is this scope; 0 if there is no parent
elems map[string]types.Object // lazily allocated
pos, end token.Pos // scope extent; may be invalid
comment string // for debugging only
isFunc bool // set if this is a function scope (internal use only)
}
func isFunc(scope *types.Scope) bool {
hs := (*hackScope)(unsafe.Pointer(scope))
return hs.isFunc
}
func (b Builder) DIScope(f Function, scope *types.Scope) DIScope {
if cachedScope, ok := b.diScopeCache[scope]; ok {
return cachedScope
}
pos := b.di().positioner.Position(scope.Pos())
// skip package and universe scope
// if scope.Parent().Parent() == nil {
// return b.di().file(pos.Filename)
// }
var result DIScope
if isFunc(scope) {
// TODO(lijie): should check scope == function scope
result = f
} else {
parentScope := b.DIScope(f, scope.Parent())
result = &aDILexicalBlock{b.di().di.CreateLexicalBlock(parentScope.scopeMeta(b.di(), pos).ll, llvm.DILexicalBlock{
File: b.di().file(pos.Filename).ll,
Line: pos.Line,
Column: pos.Column,
})}
}
b.diScopeCache[scope] = result
return result
}
const (
MD_dbg = 0
)
func (b Builder) DIGlobal(v Expr, name string, pos token.Position) {
if _, ok := b.Pkg.glbDbgVars[v]; ok {
return
@@ -594,22 +729,55 @@ func (b Builder) DIGlobal(v Expr, name string, pos token.Position) {
v.Type,
false,
)
v.impl.AddMetadata(0, gv.ll)
v.impl.AddMetadata(MD_dbg, gv.ll)
b.Pkg.glbDbgVars[v] = true
}
func (b Builder) DISetCurrentDebugLocation(f Function, pos token.Position) {
func (b Builder) DISetCurrentDebugLocation(diScope DIScope, pos token.Position) {
b.impl.SetCurrentDebugLocation(
uint(pos.Line),
uint(pos.Column),
f.scopeMeta(b.di(), pos).ll,
f.impl.InstructionDebugLoc(),
diScope.scopeMeta(b.di(), pos).ll,
llvm.Metadata{},
)
}
func (b Builder) DebugFunction(f Function, pos token.Position) {
// attach debug info to function
f.scopeMeta(b.Pkg.di, pos)
func (b Builder) DebugFunction(f Function, pos token.Position, bodyPos token.Position) {
p := f
if p.diFunc == nil {
sig := p.Type.raw.Type.(*types.Signature)
rt := p.Prog.Type(sig.Results(), InGo)
paramTypes := make([]llvm.Metadata, len(p.params)+1)
paramTypes[0] = b.di().diType(rt, pos).ll
for i, t := range p.params {
paramTypes[i+1] = b.di().diType(t, pos).ll
}
diFuncType := b.di().di.CreateSubroutineType(llvm.DISubroutineType{
File: b.di().file(pos.Filename).ll,
Parameters: paramTypes,
})
dif := llvm.DIFunction{
Type: diFuncType,
Name: p.Name(),
LinkageName: p.Name(),
File: b.di().file(pos.Filename).ll,
Line: pos.Line,
ScopeLine: bodyPos.Line,
IsDefinition: true,
LocalToUnit: true,
Optimized: true,
}
p.diFunc = &aDIFunction{
b.di().di.CreateFunction(b.di().file(pos.Filename).ll, dif),
}
p.impl.SetSubprogram(p.diFunc.ll)
}
b.impl.SetCurrentDebugLocation(
uint(bodyPos.Line),
uint(bodyPos.Column),
p.diFunc.ll,
f.impl.InstructionDebugLoc(),
)
}
func (b Builder) Param(idx int) Expr {

View File

@@ -141,6 +141,10 @@ const (
)
func (b Builder) getDefer(kind DoAction) *aDefer {
if b.Func.recov == nil {
// b.Func.recov maybe nil in ssa.NaiveForm
return nil
}
self := b.Func
if self.defer_ == nil {
// TODO(xsw): check if in pkg.init
@@ -241,6 +245,9 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
// RunDefers emits instructions to run deferred instructions.
func (b Builder) RunDefers() {
self := b.getDefer(DeferInCond)
if self == nil {
return
}
blk := b.Func.MakeBlock()
self.rundsNext = append(self.rundsNext, blk)

View File

@@ -58,9 +58,8 @@ func (p BasicBlock) Addr() Expr {
// -----------------------------------------------------------------------------
type dbgExpr struct {
ptr Expr
val Expr
deref bool
ptr Expr
val Expr
}
type aBuilder struct {
@@ -70,7 +69,8 @@ type aBuilder struct {
Pkg Package
Prog Program
dbgVars map[Expr]dbgExpr
dbgVars map[Expr]dbgExpr // save copied address and values for debug info
diScopeCache map[*types.Scope]DIScope // avoid duplicated DILexicalBlock(s)
}
// Builder represents a builder for creating instructions in a function.

View File

@@ -109,6 +109,8 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewChan(t.Dir(), elem), true
}
case *types.Tuple:
return p.cvtTuple(t)
default:
panic(fmt.Sprintf("cvtType: unexpected type - %T", typ))
}