Implement //go:linkname pragma
Этот коммит содержится в:
родитель
906e061e37
коммит
771f23e320
3 изменённых файлов: 46 добавлений и 1 удалений
|
@ -13,6 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aykevl/llvm/bindings/go/llvm"
|
"github.com/aykevl/llvm/bindings/go/llvm"
|
||||||
|
"go/parser"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"golang.org/x/tools/go/ssa/ssautil"
|
"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
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: append([]string{"tgo"}, buildTags...),
|
BuildTags: append([]string{"tgo"}, buildTags...),
|
||||||
},
|
},
|
||||||
|
ParserMode: parser.ParseComments,
|
||||||
AllowErrors: true,
|
AllowErrors: true,
|
||||||
}
|
}
|
||||||
config.Import("runtime")
|
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()
|
program.Build()
|
||||||
c.ir = NewProgram(program, mainPath)
|
c.ir = NewProgram(program, mainPath)
|
||||||
|
|
||||||
|
@ -1067,6 +1069,8 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
}
|
}
|
||||||
frame.locals[instr] = value
|
frame.locals[instr] = value
|
||||||
return err
|
return err
|
||||||
|
case *ssa.DebugRef:
|
||||||
|
return nil // ignore
|
||||||
case *ssa.Go:
|
case *ssa.Go:
|
||||||
if instr.Common().Method != nil {
|
if instr.Common().Method != nil {
|
||||||
return errors.New("todo: go on method receiver")
|
return errors.New("todo: go on method receiver")
|
||||||
|
|
|
@ -128,6 +128,8 @@ func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter
|
||||||
default:
|
default:
|
||||||
return i, errors.New("todo: init: unknown convert: " + instr.String())
|
return i, errors.New("todo: init: unknown convert: " + instr.String())
|
||||||
}
|
}
|
||||||
|
case *ssa.DebugRef:
|
||||||
|
// ignore
|
||||||
case *ssa.FieldAddr:
|
case *ssa.FieldAddr:
|
||||||
x, err := p.getValue(instr.X, locals)
|
x, err := p.getValue(instr.X, locals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -273,6 +275,7 @@ func canInterpret(callee *ssa.Function) bool {
|
||||||
// above.
|
// above.
|
||||||
case *ssa.Alloc:
|
case *ssa.Alloc:
|
||||||
case *ssa.Convert:
|
case *ssa.Convert:
|
||||||
|
case *ssa.DebugRef:
|
||||||
case *ssa.FieldAddr:
|
case *ssa.FieldAddr:
|
||||||
case *ssa.IndexAddr:
|
case *ssa.IndexAddr:
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
|
|
38
ir.go
38
ir.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/ast"
|
||||||
"go/types"
|
"go/types"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -33,6 +34,7 @@ type Program struct {
|
||||||
type Function struct {
|
type Function struct {
|
||||||
fn *ssa.Function
|
fn *ssa.Function
|
||||||
llvmFn llvm.Value
|
llvmFn llvm.Value
|
||||||
|
linkName string
|
||||||
blocking bool
|
blocking bool
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
parents []*Function // calculated by AnalyseCallgraph
|
parents []*Function // calculated by AnalyseCallgraph
|
||||||
|
@ -117,6 +119,39 @@ func (p *Program) addFunction(ssaFn *ssa.Function) {
|
||||||
f := &Function{fn: ssaFn}
|
f := &Function{fn: ssaFn}
|
||||||
p.Functions = append(p.Functions, f)
|
p.Functions = append(p.Functions, f)
|
||||||
p.functionMap[ssaFn] = 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 {
|
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.
|
// Return the link name for this function.
|
||||||
func (f *Function) LinkName() string {
|
func (f *Function) LinkName() string {
|
||||||
|
if f.linkName != "" {
|
||||||
|
return f.linkName
|
||||||
|
}
|
||||||
if f.fn.Signature.Recv() != nil {
|
if f.fn.Signature.Recv() != nil {
|
||||||
// Method on a defined type (which may be a pointer).
|
// Method on a defined type (which may be a pointer).
|
||||||
return f.fn.RelString(nil)
|
return f.fn.RelString(nil)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче