Files
llgo/ssa/decl.go
2024-05-24 02:42:10 +08:00

418 lines
11 KiB
Go

/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ssa
import (
"go/types"
"log"
"sort"
"strconv"
"strings"
"github.com/goplus/llvm"
)
// -----------------------------------------------------------------------------
const (
ClosureCtx = "__llgo_ctx"
ClosureStub = "__llgo_stub."
)
// -----------------------------------------------------------------------------
const (
NameValist = "__llgo_va_list"
)
func VArg() *types.Var {
return types.NewParam(0, nil, NameValist, types.NewSlice(tyAny))
}
// -----------------------------------------------------------------------------
type aNamedConst struct {
}
// A NamedConst is a Member of a Package representing a package-level
// named constant.
//
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
// identifier.
//
// NB: a NamedConst is not a Value; it contains a constant Value, which
// it augments with the name and position of its 'const' declaration.
type NamedConst = *aNamedConst
/*
// NewConst creates a new named constant.
func (p Package) NewConst(name string, val constant.Value) NamedConst {
return &aNamedConst{}
}
*/
// -----------------------------------------------------------------------------
type aGlobal struct {
Expr
array bool
}
// A Global is a named Value holding the address of a package-level
// variable.
type Global = *aGlobal
// NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type, bg Background) Global {
if v, ok := p.vars[name]; ok {
return v
}
t := p.Prog.Type(typ, bg)
return p.doNewVar(name, t)
}
// NewVarFrom creates a new global variable.
func (p Package) NewVarFrom(name string, t Type) Global {
if v, ok := p.vars[name]; ok {
return v
}
return p.doNewVar(name, t)
}
func (p Package) doNewVar(name string, t Type) Global {
var gbl llvm.Value
var array bool
if t.kind == vkPtr && p.Prog.Elem(t).kind == vkArray { // TODO(xsw): check this code
typ := p.Prog.Elem(t).ll
gbl = llvm.AddGlobal(p.mod, typ, name)
gbl.SetInitializer(llvm.Undef(typ))
array = true
} else {
gbl = llvm.AddGlobal(p.mod, t.ll, name)
}
ret := &aGlobal{Expr{gbl, t}, array}
p.vars[name] = ret
return ret
}
// VarOf returns a global variable by name.
func (p Package) VarOf(name string) Global {
return p.vars[name]
}
// Init initializes the global variable with the given value.
func (g Global) Init(v Expr) {
if g.array && v.kind == vkPtr { // TODO(xsw): check this code
return
}
g.impl.SetInitializer(v.impl)
}
// -----------------------------------------------------------------------------
// Function represents the parameters, results, and code of a function
// or method.
//
// If Blocks is nil, this indicates an external function for which no
// Go source code is available. In this case, FreeVars, Locals, and
// Params are nil too. Clients performing whole-program analysis must
// handle external functions specially.
//
// Blocks contains the function's control-flow graph (CFG).
// Blocks[0] is the function entry point; block order is not otherwise
// semantically significant, though it may affect the readability of
// the disassembly.
// To iterate over the blocks in dominance order, use DomPreorder().
//
// Recover is an optional second entry point to which control resumes
// after a recovered panic. The Recover block may contain only a return
// statement, preceded by a load of the function's named return
// parameters, if any.
//
// A nested function (Parent()!=nil) that refers to one or more
// lexically enclosing local variables ("free variables") has FreeVars.
// Such functions cannot be called directly but require a
// value created by MakeClosure which, via its Bindings, supplies
// values for these parameters.
//
// If the function is a method (Signature.Recv() != nil) then the first
// element of Params is the receiver parameter.
//
// A Go package may declare many functions called "init".
// For each one, Object().Name() returns "init" but Name() returns
// "init#1", etc, in declaration order.
//
// Pos() returns the declaring ast.FuncLit.Type.Func or the position
// of the ast.FuncDecl.Name, if the function was explicit in the
// source. Synthetic wrappers, for which Synthetic != "", may share
// the same position as the function they wrap.
// Syntax.Pos() always returns the position of the declaring "func" token.
//
// Type() returns the function's Signature.
//
// A generic function is a function or method that has uninstantiated type
// parameters (TypeParams() != nil). Consider a hypothetical generic
// method, (*Map[K,V]).Get. It may be instantiated with all ground
// (non-parameterized) types as (*Map[string,int]).Get or with
// parameterized types as (*Map[string,U]).Get, where U is a type parameter.
// In both instantiations, Origin() refers to the instantiated generic
// method, (*Map[K,V]).Get, TypeParams() refers to the parameters [K,V] of
// the generic method. TypeArgs() refers to [string,U] or [string,int],
// respectively, and is nil in the generic method.
type aFunction struct {
Expr
Pkg Package
Prog Program
blks []BasicBlock
params []Type
freeVars Expr
base int // base = 1 if hasFreeVars; base = 0 otherwise
hasVArg bool
}
// Function represents a function or method.
type Function = *aFunction
// NewFunc creates a new function.
func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function {
return p.NewFuncEx(name, sig, bg, false)
}
// NewFuncEx creates a new function.
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function {
if v, ok := p.fns[name]; ok {
return v
}
t := p.Prog.FuncDecl(sig, bg)
if debugInstr {
log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars)
}
fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p, p.Prog, hasFreeVars)
p.fns[name] = ret
return ret
}
// FuncOf returns a function by name.
func (p Package) FuncOf(name string) Function {
return p.fns[name]
}
func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function {
params, hasVArg := newParams(t, prog)
base := 0
if hasFreeVars {
base = 1
}
return &aFunction{Expr{fn, t}, pkg, prog, nil, params, Expr{}, base, hasVArg}
}
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
sig := fn.raw.Type.(*types.Signature)
in := sig.Params()
if n := in.Len(); n > 0 {
if hasVArg = sig.Variadic(); hasVArg {
n--
}
params = make([]Type, n)
for i := 0; i < n; i++ {
params[i] = prog.rawType(in.At(i).Type())
}
}
return
}
/*
// Name returns the function's name.
func (p Function) Name() string {
return p.impl.Name()
}
*/
// Params returns the function's ith parameter.
func (p Function) Param(i int) Expr {
i += p.base // skip if hasFreeVars
return Expr{p.impl.Param(i), p.params[i]}
}
func (p Function) closureCtx(b Builder) Expr {
if p.freeVars.IsNil() {
if p.base == 0 {
panic("ssa: function has no free variables")
}
ptr := Expr{p.impl.Param(0), p.params[0]}
p.freeVars = b.Load(ptr)
}
return p.freeVars
}
// FreeVar returns the function's ith free variable.
func (p Function) FreeVar(b Builder, i int) Expr {
ctx := p.closureCtx(b)
return b.getField(ctx, i)
}
// NewBuilder creates a new Builder for the function.
func (p Function) NewBuilder() Builder {
prog := p.Prog
b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize()
return &aBuilder{b, p, p.Pkg, prog}
}
// HasBody reports whether the function has a body.
func (p Function) HasBody() bool {
return len(p.blks) > 0
}
// MakeBody creates nblk basic blocks for the function, and creates
// a new Builder associated to #0 block.
func (p Function) MakeBody(nblk int) Builder {
p.MakeBlocks(nblk)
b := p.NewBuilder()
b.impl.SetInsertPointAtEnd(p.blks[0].impl)
return b
}
// MakeBlocks creates nblk basic blocks for the function.
func (p Function) MakeBlocks(nblk int) []BasicBlock {
n := len(p.blks)
if n == 0 {
p.blks = make([]BasicBlock, 0, nblk)
}
for i := 0; i < nblk; i++ {
p.addBlock(n + i)
}
return p.blks[n:]
}
func (p Function) addBlock(idx int) BasicBlock {
label := "_llgo_" + strconv.Itoa(idx)
blk := llvm.AddBasicBlock(p.impl, label)
ret := &aBasicBlock{blk, p, idx}
p.blks = append(p.blks, ret)
return ret
}
// MakeBlock creates a new basic block for the function.
func (p Function) MakeBlock() BasicBlock {
return p.addBlock(len(p.blks))
}
// Block returns the ith basic block of the function.
func (p Function) Block(idx int) BasicBlock {
return p.blks[idx]
}
// -----------------------------------------------------------------------------
type aPyGlobal struct {
Expr
}
type PyGlobal = *aPyGlobal
// PyNewVar creates a Python variable.
func (b Builder) PyNewVar(modName, name string) PyGlobal {
modPtr := b.Pkg.PyNewModVar(modName, false).Expr
mod := b.Load(modPtr)
return &aPyGlobal{pyVarExpr(mod, name)}
}
func (b Builder) pyLoad(ptr Expr) Expr {
t := ptr.raw.Type.(*pyVarTy)
fn := b.Pkg.pyFunc("PyObject_GetAttrString", b.Prog.tyGetAttrString())
return b.Call(fn, t.mod, b.CStr(t.name))
}
// -----------------------------------------------------------------------------
type aPyObjRef struct {
Expr
Obj Global
}
// PyObjRef represents a python object reference.
type PyObjRef = *aPyObjRef
// PyNewFunc creates a new python function.
func (p Package) PyNewFunc(name string, sig *types.Signature, doInit bool) PyObjRef {
if v, ok := p.pyobjs[name]; ok {
return v
}
prog := p.Prog
obj := p.NewVar(name, prog.PyObjectPtrPtr().RawType(), InC)
if doInit {
prog.NeedPyInit = true
obj.Init(prog.Null(obj.Type))
obj.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
}
ty := &aType{obj.ll, rawType{types.NewPointer(sig)}, vkPyFuncRef}
expr := Expr{obj.impl, ty}
ret := &aPyObjRef{expr, obj}
p.pyobjs[name] = ret
return ret
}
// PyObjOf returns a python object by name.
func (p Package) PyObjOf(name string) PyObjRef {
return p.pyobjs[name]
}
func (p Package) pyHasModSyms() bool {
return len(p.pyobjs) > 0
}
// pyLoadModSyms loads module symbols used in this package.
func (p Package) pyLoadModSyms(b Builder) {
objs := p.pyobjs
names := make([]string, 0, len(objs))
for name := range objs {
names = append(names, name)
}
sort.Strings(names)
mods := make(map[string][]PyObjRef)
modNames := make([]string, 0, 8)
lastMod := ""
for _, name := range names {
modName := modOf(name)
mods[modName] = append(mods[modName], objs[name])
if modName != lastMod {
modNames = append(modNames, modName)
lastMod = modName
}
}
for _, modName := range modNames {
objs := mods[modName]
b.PyLoadModSyms(modName, objs...)
}
}
func modOf(name string) string {
if pos := strings.LastIndexByte(name, '.'); pos > 0 {
return name[:pos]
}
panic("unreachable")
}
// -----------------------------------------------------------------------------