diff --git a/compiler.go b/compiler.go index f50945f4..dee6c5eb 100644 --- a/compiler.go +++ b/compiler.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/aykevl/llvm/bindings/go/llvm" + "go/parser" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -139,6 +140,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { Compiler: "gc", // must be one of the recognized compilers BuildTags: append([]string{"tgo"}, buildTags...), }, + ParserMode: parser.ParseComments, AllowErrors: true, } config.Import("runtime") @@ -155,7 +157,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error { } } - program := ssautil.CreateProgram(lprogram, ssa.SanityCheckFunctions|ssa.BareInits) + program := ssautil.CreateProgram(lprogram, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug) program.Build() c.ir = NewProgram(program, mainPath) @@ -1067,6 +1069,8 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error { } frame.locals[instr] = value return err + case *ssa.DebugRef: + return nil // ignore case *ssa.Go: if instr.Common().Method != nil { return errors.New("todo: go on method receiver") diff --git a/interpreter.go b/interpreter.go index 6cf34e59..7e642d00 100644 --- a/interpreter.go +++ b/interpreter.go @@ -128,6 +128,8 @@ func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter default: return i, errors.New("todo: init: unknown convert: " + instr.String()) } + case *ssa.DebugRef: + // ignore case *ssa.FieldAddr: x, err := p.getValue(instr.X, locals) if err != nil { @@ -273,6 +275,7 @@ func canInterpret(callee *ssa.Function) bool { // above. case *ssa.Alloc: case *ssa.Convert: + case *ssa.DebugRef: case *ssa.FieldAddr: case *ssa.IndexAddr: case *ssa.MakeInterface: diff --git a/ir.go b/ir.go index 312e305c..2772a13b 100644 --- a/ir.go +++ b/ir.go @@ -1,6 +1,7 @@ package main import ( + "go/ast" "go/types" "sort" "strings" @@ -33,6 +34,7 @@ type Program struct { type Function struct { fn *ssa.Function llvmFn llvm.Value + linkName string blocking bool flag bool // used by dead code elimination parents []*Function // calculated by AnalyseCallgraph @@ -117,6 +119,39 @@ func (p *Program) addFunction(ssaFn *ssa.Function) { f := &Function{fn: ssaFn} p.Functions = append(p.Functions, f) p.functionMap[ssaFn] = f + + // Parse compiler directives in the preceding comments. + if f.fn.Syntax() != nil && f.fn.Syntax().(*ast.FuncDecl).Doc != nil { + for _, comment := range f.fn.Syntax().(*ast.FuncDecl).Doc.List { + if !strings.HasPrefix(comment.Text, "//go:") { + continue + } + parts := strings.Fields(comment.Text) + switch parts[0] { + case "//go:linkname": + if len(parts) != 3 || parts[1] != ssaFn.Name() { + continue + } + // Only enable go:linkname when the package imports "unsafe". + // This is a slightly looser requirement than what gc uses: gc + // requires the file to import "unsafe", not the package as a + // whole. + if hasUnsafeImport(ssaFn.Pkg.Pkg) { + f.linkName = parts[2] + } + } + } + } +} + +// Return true if this package imports "unsafe", false otherwise. +func hasUnsafeImport(pkg *types.Package) bool { + for _, imp := range pkg.Imports() { + if imp == types.Unsafe { + return true + } + } + return false } func (p *Program) GetFunction(ssaFn *ssa.Function) *Function { @@ -129,6 +164,9 @@ func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global { // Return the link name for this function. func (f *Function) LinkName() string { + if f.linkName != "" { + return f.linkName + } if f.fn.Signature.Recv() != nil { // Method on a defined type (which may be a pointer). return f.fn.RelString(nil)