compiler: move global handling from ir to compiler package
This is part of a larger rafactor that tries to shrink the ir package and in general tries to shrink the amount of state that is kept around in the compiler. The end goal is being able to compile packages independent of each other, linking them together in a later stage. Along the way, it cleans up lots of old cruft that has accumulated over the months. This refactor also results in globals being loaded lazily. This may be a problem for some specific programs but will probably change back in a commit in the near future.
Этот коммит содержится в:
родитель
6b5b4a681d
коммит
b0e767c4a7
3 изменённых файлов: 115 добавлений и 113 удалений
|
@ -3,6 +3,7 @@ package compiler
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
|
@ -66,6 +67,7 @@ type Compiler struct {
|
|||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||
ir *ir.Program
|
||||
diagnostics []error
|
||||
astComments map[string]*ast.CommentGroup
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
|
@ -275,20 +277,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||
|
||||
var frames []*Frame
|
||||
|
||||
// Declare all globals.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType := c.getLLVMType(typ)
|
||||
global := c.mod.NamedGlobal(g.LinkName())
|
||||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, llvmType, g.LinkName())
|
||||
}
|
||||
g.LLVMGlobal = global
|
||||
if !g.IsExtern() {
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetInitializer(c.getZeroValue(llvmType))
|
||||
}
|
||||
}
|
||||
c.loadASTComments(lprogram)
|
||||
|
||||
// Declare all functions.
|
||||
for _, f := range c.ir.Functions {
|
||||
|
@ -1357,9 +1346,9 @@ func (c *Compiler) getValue(frame *Frame, expr ssa.Value) llvm.Value {
|
|||
}
|
||||
return c.createFuncValue(fn.LLVMFn, llvm.Undef(c.i8ptrType), fn.Signature)
|
||||
case *ssa.Global:
|
||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
||||
value := c.getGlobal(expr)
|
||||
if value.IsNil() {
|
||||
c.addError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName())
|
||||
c.addError(expr.Pos(), "global not found: "+expr.RelString(nil))
|
||||
return llvm.Undef(c.getLLVMType(expr.Type()))
|
||||
}
|
||||
return value
|
||||
|
@ -2511,8 +2500,8 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
|
|||
// var C.add unsafe.Pointer
|
||||
// Instead of a load from the global, create a bitcast of the
|
||||
// function pointer itself.
|
||||
global := c.ir.GetGlobal(unop.X.(*ssa.Global))
|
||||
name := global.LinkName()[:len(global.LinkName())-len("$funcaddr")]
|
||||
globalName := c.getGlobalInfo(unop.X.(*ssa.Global)).linkName
|
||||
name := globalName[:len(globalName)-len("$funcaddr")]
|
||||
fn := c.mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
return llvm.Value{}, c.makeError(unop.Pos(), "cgo function not found: "+name)
|
||||
|
|
107
compiler/symbol.go
Обычный файл
107
compiler/symbol.go
Обычный файл
|
@ -0,0 +1,107 @@
|
|||
package compiler
|
||||
|
||||
// This file manages symbols, that is, functions and globals. It reads their
|
||||
// pragmas, determines the link name, etc.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// globalInfo contains some information about a specific global. By default,
|
||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||
// some symbols this is different (due to //go:extern for example).
|
||||
type globalInfo struct {
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||
func (c *Compiler) loadASTComments(lprogram *loader.Program) {
|
||||
c.astComments = map[string]*ast.CommentGroup{}
|
||||
for _, pkgInfo := range lprogram.Sorted() {
|
||||
for _, file := range pkgInfo.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
||||
c.astComments[id] = decl.Doc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getGlobal returns a LLVM IR global value for a Go SSA global. It is added to
|
||||
// the LLVM IR if it has not been added already.
|
||||
func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
||||
info := c.getGlobalInfo(g)
|
||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||
if llvmGlobal.IsNil() {
|
||||
llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem())
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
if !info.extern {
|
||||
llvmGlobal.SetInitializer(c.getZeroValue(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
}
|
||||
return llvmGlobal
|
||||
}
|
||||
|
||||
// getGlobalInfo returns some information about a specific global.
|
||||
func (c *Compiler) getGlobalInfo(g *ssa.Global) globalInfo {
|
||||
info := globalInfo{}
|
||||
if strings.HasPrefix(g.Name(), "C.") {
|
||||
// Created by CGo: such a name cannot be created by regular C code.
|
||||
info.linkName = g.Name()[2:]
|
||||
info.extern = true
|
||||
} else {
|
||||
// Pick the default linkName.
|
||||
info.linkName = g.RelString(nil)
|
||||
// Check for //go: pragmas, which may change the link name (among
|
||||
// others).
|
||||
doc := c.astComments[info.linkName]
|
||||
if doc != nil {
|
||||
info.parsePragmas(doc)
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// Parse //go: pragma comments from the source. In particular, it parses the
|
||||
// //go:extern pragma on globals.
|
||||
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
for _, comment := range doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
switch parts[0] {
|
||||
case "//go:extern":
|
||||
info.extern = true
|
||||
if len(parts) == 2 {
|
||||
info.linkName = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
ir/ir.go
96
ir/ir.go
|
@ -2,7 +2,6 @@ package ir
|
|||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -23,9 +22,6 @@ type Program struct {
|
|||
mainPkg *ssa.Package
|
||||
Functions []*Function
|
||||
functionMap map[*ssa.Function]*Function
|
||||
Globals []*Global
|
||||
globalMap map[*ssa.Global]*Global
|
||||
comments map[string]*ast.CommentGroup
|
||||
}
|
||||
|
||||
// Function or method.
|
||||
|
@ -40,15 +36,6 @@ type Function struct {
|
|||
inline InlineType // go:inline
|
||||
}
|
||||
|
||||
// Global variable, possibly constant.
|
||||
type Global struct {
|
||||
*ssa.Global
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
// it implements another interface).
|
||||
type Interface struct {
|
||||
|
@ -73,32 +60,6 @@ const (
|
|||
|
||||
// Create and initialize a new *Program from a *ssa.Program.
|
||||
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
comments := map[string]*ast.CommentGroup{}
|
||||
for _, pkgInfo := range lprogram.Sorted() {
|
||||
for _, file := range pkgInfo.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.TYPE, token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
||||
comments[id] = decl.Doc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program := lprogram.LoadSSA()
|
||||
program.Build()
|
||||
|
||||
|
@ -170,8 +131,6 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
|||
LoaderProgram: lprogram,
|
||||
mainPkg: mainPkg,
|
||||
functionMap: make(map[*ssa.Function]*Function),
|
||||
globalMap: make(map[*ssa.Global]*Global),
|
||||
comments: comments,
|
||||
}
|
||||
|
||||
for _, pkg := range packageList {
|
||||
|
@ -204,13 +163,7 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
g := &Global{program: p, Global: member}
|
||||
doc := p.comments[g.RelString(nil)]
|
||||
if doc != nil {
|
||||
g.parsePragmas(doc)
|
||||
}
|
||||
p.Globals = append(p.Globals, g)
|
||||
p.globalMap[member] = g
|
||||
// Ignore. Globals are not handled here.
|
||||
case *ssa.NamedConst:
|
||||
// Ignore: these are already resolved.
|
||||
default:
|
||||
|
@ -244,10 +197,6 @@ func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
|
|||
return p.functionMap[ssaFn]
|
||||
}
|
||||
|
||||
func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global {
|
||||
return p.globalMap[ssaGlobal]
|
||||
}
|
||||
|
||||
func (p *Program) MainPkg() *ssa.Package {
|
||||
return p.mainPkg
|
||||
}
|
||||
|
@ -370,49 +319,6 @@ func (f *Function) CName() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Parse //go: pragma comments from the source.
|
||||
func (g *Global) parsePragmas(doc *ast.CommentGroup) {
|
||||
for _, comment := range doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(comment.Text)
|
||||
switch parts[0] {
|
||||
case "//go:extern":
|
||||
g.extern = true
|
||||
if len(parts) == 2 {
|
||||
g.linkName = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the link name for this global.
|
||||
func (g *Global) LinkName() string {
|
||||
if g.linkName != "" {
|
||||
return g.linkName
|
||||
}
|
||||
if name := g.CName(); name != "" {
|
||||
return name
|
||||
}
|
||||
return g.RelString(nil)
|
||||
}
|
||||
|
||||
func (g *Global) IsExtern() bool {
|
||||
return g.extern || g.CName() != ""
|
||||
}
|
||||
|
||||
// Return the name of the C global if this is a CGo wrapper. Otherwise, return a
|
||||
// zero-length string.
|
||||
func (g *Global) CName() string {
|
||||
name := g.Name()
|
||||
if strings.HasPrefix(name, "C.") {
|
||||
// created by ../loader/cgo.go
|
||||
return name[2:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче