Skip to content

Commit

Permalink
llgo: Emit more debug metadata. For issue go-llvm#49.
Browse files Browse the repository at this point in the history
  • Loading branch information
quarnster committed Sep 30, 2013
1 parent 6a2b2b0 commit f43ffe4
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 32 deletions.
18 changes: 17 additions & 1 deletion cmd/llgo-build/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
)

// linkdeps links dependencies into the specified output file.
Expand Down Expand Up @@ -68,8 +69,23 @@ func linkdeps(pkg *build.Package, output string) error {

// Finally, link with clang++ to get exception handling.
if !emitllvm || triple == "pnacl" {
input := output
if strings.Contains(triple, "darwin") || strings.Contains(triple, "mac") {
// Not doing this intermediate step will make it invoke "dsymutil"
// which then asserts and kills the build.
// See discussion in issue #49 for more details.
input += ".o"
args := []string{"-g", "-c", "-o", input, output}
cmd := exec.Command(clang, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = runCmd(cmd); err != nil {
return err
}
}

clangxx := clang + "++"
args := []string{"-pthread", "-o", output, output}
args := []string{"-pthread", "-g", "-o", output, input}
if triple == "pnacl" {
args = append(args, "-l", "ppapi")
}
Expand Down
19 changes: 19 additions & 0 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type compiler struct {
// field will have been updated to the true triple used to
// compile PNaCl modules.
pnacl bool

debug_context []llvm.DebugDescriptor
compile_unit *llvm.CompileUnitDescriptor
debug_info *llvm.DebugInfo
}

func (c *compiler) archinfo() (intsize, ptrsize int64) {
Expand Down Expand Up @@ -285,11 +289,25 @@ func (compiler *compiler) Compile(fset *token.FileSet, files []*ast.File, import
compiler.builder = newBuilder(compiler.types)
defer compiler.builder.Dispose()

compiler.debug_info = &llvm.DebugInfo{}
// Compile each file in the package.
for _, file := range files {
compiler.compile_unit = &llvm.CompileUnitDescriptor{
Language: llvm.DW_LANG_Go,
Path: llvm.FileDescriptor(fset.File(file.Pos()).Name()),
Producer: LLGOProducer,
Runtime: LLGORuntimeVersion,
}
compiler.pushDebugContext(&compiler.compile_unit.Path)

for _, decl := range file.Decls {
compiler.VisitDecl(decl)
}
compiler.popDebugContext()
if len(compiler.debug_context) > 0 {
log.Panicln(compiler.debug_context)
}
compiler.module.AddNamedMetadataOperand("llvm.dbg.cu", compiler.debug_info.MDNode(compiler.compile_unit))
}

// Export runtime type information.
Expand Down Expand Up @@ -386,6 +404,7 @@ func (c *compiler) createMainFunction() error {
// function with no result.
runtimeMain := c.NamedFunction("runtime.main", "func(int32, **byte, **byte, *int8) int32")
main := c.NamedFunction("main", "func(int32, **byte, **byte) int32")
c.builder.SetCurrentDebugLocation(c.debug_info.MDNode(nil))
entry := llvm.AddBasicBlock(main, "entry")
c.builder.SetInsertPointAtEnd(entry)
mainMain = c.builder.CreateBitCast(mainMain, runtimeMain.Type().ElementType().ParamTypes()[3], "")
Expand Down
168 changes: 168 additions & 0 deletions debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package llgo

import (
"code.google.com/p/go.tools/go/types"
"github.com/axw/gollvm/llvm"
"go/ast"
"go/token"
)

// llgo constants.
Expand All @@ -23,8 +25,61 @@ var (
Alignment: 32,
TypeEncoding: llvm.DW_ATE_signed,
}
void_debug_type = &llvm.BasicTypeDescriptor{
Name: "void",
Size: 0,
Alignment: 0,
}
)

func (c *compiler) tollvmDebugDescriptor(t types.Type) llvm.DebugDescriptor {
switch t := t.(type) {
case *types.Pointer:
return llvm.NewPointerDerivedType(c.tollvmDebugDescriptor(t.Elem()))
case nil:
return void_debug_type
}
bt := &llvm.BasicTypeDescriptor{
Name: c.types.TypeString(t),
Size: uint64(c.types.Sizeof(t) * 8),
Alignment: uint64(c.types.Alignof(t) * 8),
}
if basic, ok := t.(*types.Basic); ok {
switch bi := basic.Info(); {
case bi&types.IsBoolean != 0:
bt.TypeEncoding = llvm.DW_ATE_boolean
case bi&types.IsUnsigned != 0:
bt.TypeEncoding = llvm.DW_ATE_unsigned
case bi&types.IsInteger != 0:
bt.TypeEncoding = llvm.DW_ATE_signed
case bi&types.IsFloat != 0:
bt.TypeEncoding = llvm.DW_ATE_float
}
}
return bt
}

func (c *compiler) pushDebugContext(d llvm.DebugDescriptor) {
c.debug_context = append(c.debug_context, d)
}

func (c *compiler) popDebugContext() {
c.debug_context = c.debug_context[:len(c.debug_context)-1]
}

func (c *compiler) currentDebugContext() llvm.DebugDescriptor {
return c.debug_context[len(c.debug_context)-1]
}

func (c *compiler) setDebugLine(pos token.Pos) {
file := c.fileset.File(pos)
ld := &llvm.LineDescriptor{
Line: uint32(file.Line(pos)),
Context: c.currentDebugContext(),
}
c.builder.SetCurrentDebugLocation(c.debug_info.MDNode(ld))
}

// Debug intrinsic collectors.
func createGlobalVariableMetadata(global llvm.Value) llvm.DebugDescriptor {
return &llvm.GlobalVariableDescriptor{
Expand All @@ -36,6 +91,119 @@ func createGlobalVariableMetadata(global llvm.Value) llvm.DebugDescriptor {
Value: global}
}

// Creates a new named variable allocated on the stack.
// If value is not nil, its contents is stored in the allocated stack space.
func (c *compiler) newStackVar(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) {

This comment has been minimized.

Copy link
@axw

axw Oct 1, 2013

I don't think this belongs in this file, really. Maybe introduce a new file (var.go?), where we can also do escape analysis later.

This comment has been minimized.

Copy link
@axw

axw Oct 1, 2013

The "argument" parameter feels a bit wrong, as it's particular to function argument variables, and irrelevant to "auto" variables. This makes me think there should be two functions: newStackVar and newArgStackVar (or similar). What do you think?

This comment has been minimized.

Copy link
@quarnster

quarnster Oct 1, 2013

Author Owner

Makes sense from an api perspective, though I'd rather not have the same/very similar code in two places. How do you feel about having newStackVar and newArgStackVar calling into a common newStackVarEx where the Ex version is just the current code?

This comment has been minimized.

Copy link
@axw

axw Oct 1, 2013

Yep, that's what I had in mind, so I'd be happy.

typ := v.Type()

// We need to put alloca instructions in the top block or the values
// displayed when inspecting these variables in a debugger will be
// completely messed up.
curBlock := c.builder.GetInsertBlock()
if p := curBlock.Parent(); !p.IsNil() {
fb := p.FirstBasicBlock()
fi := fb.FirstInstruction()
if !fb.IsNil() && !fi.IsNil() {
c.builder.SetInsertPointBefore(fi)
}
}
old := c.builder.CurrentDebugLocation()
c.builder.SetCurrentDebugLocation(llvm.Value{})
stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name)

// For arguments we want to insert the store instruction
// without debug information to ensure that they are executed
// (and hence have proper values) before the debugger hits the
// first line in a function.
if argument == 0 {
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)
}

if !value.IsNil() {
c.builder.CreateStore(value, stackvalue)
}
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)

ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ))
stackvar = ptrvalue.makePointee()
stackvar.stack = stackf
c.objectdata[v].Value = stackvar

file := c.fileset.File(v.Pos())
tag := llvm.DW_TAG_auto_variable
if argument > 0 {
tag = llvm.DW_TAG_arg_variable
}
ld := llvm.NewLocalVariableDescriptor(tag)
ld.Argument = uint32(argument)
ld.Line = uint32(file.Line(v.Pos()))
ld.Name = name
ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())}
ld.Type = c.tollvmDebugDescriptor(typ)
ld.Context = c.currentDebugContext()
c.builder.InsertDeclare(c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(ld))
return stackvalue, stackvar
}

var uniqueId uint32

func (c *compiler) createBlockMetadata(stmt *ast.BlockStmt) llvm.DebugDescriptor {
uniqueId++
file := c.fileset.File(stmt.Pos())
fd := llvm.FileDescriptor(file.Name())
return &llvm.BlockDescriptor{
File: &fd,
Line: uint32(file.Line(stmt.Pos())),
Context: c.currentDebugContext(),
Id: uniqueId,
}
}

func (c *compiler) createFunctionMetadata(f *ast.FuncDecl, fn *LLVMValue) llvm.DebugDescriptor {
file := c.fileset.File(f.Pos())
fnptr := fn.value
fun := fnptr.IsAFunction()
if fun.IsNil() {
fnptr = llvm.ConstExtractValue(fn.value, []uint32{0})
}
meta := &llvm.SubprogramDescriptor{
Name: fnptr.Name(),
DisplayName: f.Name.Name,
Path: llvm.FileDescriptor(file.Name()),
Line: uint32(file.Line(f.Pos())),
ScopeLine: uint32(file.Line(f.Body.Pos())),
Context: &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())},
Function: fnptr}

var result types.Type
var metaparams []llvm.DebugDescriptor
if ftyp, ok := fn.Type().(*types.Signature); ok {
if recv := ftyp.Recv(); recv != nil {
metaparams = append(metaparams, c.tollvmDebugDescriptor(recv.Type()))
}
if ftyp.Params() != nil {
for i := 0; i < ftyp.Params().Len(); i++ {
p := ftyp.Params().At(i)
metaparams = append(metaparams, c.tollvmDebugDescriptor(p.Type()))
}
}
if ftyp.Results() != nil {
result = ftyp.Results().At(0).Type()
// TODO: what to do with multiple returns?
for i := 1; i < ftyp.Results().Len(); i++ {
p := ftyp.Results().At(i)
metaparams = append(metaparams, c.tollvmDebugDescriptor(p.Type()))
}
}
}

meta.Type = llvm.NewSubroutineCompositeType(c.tollvmDebugDescriptor(result), metaparams)
c.compile_unit.Subprograms = append(c.compile_unit.Subprograms, meta)
return meta
}

func (c *compiler) createMetadata() {
functions := []llvm.DebugDescriptor{}
globals := []llvm.DebugDescriptor{}
Expand Down
23 changes: 10 additions & 13 deletions decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,7 @@ func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.T
name := v.Name()
if !isBlank(name) {
value := llvm_fn.Param(i + paramoffset)
typ := v.Type()
stackvalue := c.builder.CreateAlloca(c.types.ToLLVM(typ), name)
c.builder.CreateStore(value, stackvalue)
ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ))
stackvar := ptrvalue.makePointee()
stackvar.stack = f
c.objectdata[v].Value = stackvar
c.newStackVar(i+1, f, v, value, name)
}
}

Expand All @@ -168,12 +162,7 @@ func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.T
if allocstack {
typ := v.Type()
llvmtyp := c.types.ToLLVM(typ)
stackptr := c.builder.CreateAlloca(llvmtyp, name)
c.builder.CreateStore(llvm.ConstNull(llvmtyp), stackptr)
ptrvalue := c.NewValue(stackptr, types.NewPointer(typ))
stackvar := ptrvalue.makePointee()
stackvar.stack = f
c.objectdata[v].Value = stackvar
c.newStackVar(0, f, v, llvm.ConstNull(llvmtyp), name)
}
}

Expand All @@ -184,6 +173,8 @@ func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.T
c.VisitBlockStmt(body, false)
c.functions.pop()

c.setDebugLine(body.End())

// If the last instruction in the function is not a terminator, then
// we either have unreachable code or a missing optional return statement
// (the latter case is allowable only for functions without results).
Expand Down Expand Up @@ -226,6 +217,11 @@ func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value {
paramVars = append(paramVars, p)
}
}

c.pushDebugContext(c.createFunctionMetadata(f, fn))
defer c.popDebugContext()
c.setDebugLine(f.Pos())

paramVarsTuple := types.NewTuple(paramVars...)
c.buildFunction(fn, nil, paramVarsTuple, ftyp.Results(), f.Body)

Expand Down Expand Up @@ -429,6 +425,7 @@ func (c *compiler) VisitGenDecl(decl *ast.GenDecl) {
}

func (c *compiler) VisitDecl(decl ast.Decl) Value {
c.setDebugLine(decl.Pos())
// This is temporary. We'll return errors later, rather than panicking.
if c.Logger != nil {
c.Logger.Println("Compile declaration:", c.fileset.Position(decl.Pos()))
Expand Down
1 change: 1 addition & 0 deletions expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ func (c *compiler) VisitTypeAssertExpr(expr *ast.TypeAssertExpr) Value {
}

func (c *compiler) VisitExpr(expr ast.Expr) Value {
c.setDebugLine(expr.Pos())
// Before all else, check if we've got a constant expression.
// go/types performs constant folding, and we store the value
// alongside the expression's type.
Expand Down
8 changes: 7 additions & 1 deletion llgo/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,13 @@ func runMainFunction(m *llgo.Module) (output []string, err error) {
}

exepath := filepath.Join(tempdir, "test")
cmd = exec.Command("clang++", "-pthread", "-g", "-o", exepath, bcpath)
args := []string{"-pthread", "-o", exepath, bcpath}
if runtime.GOOS != "darwin" {
// TODO(q): -g breaks badly on my system at the moment, so is not enabled on darwin for now
args = append([]string{"-g"}, args...)
}

cmd = exec.Command("clang++", args...)
data, err = cmd.CombinedOutput()
if err != nil {
output = strings.Split(strings.TrimSpace(string(data)), "\n")
Expand Down
Loading

0 comments on commit f43ffe4

Please sign in to comment.