Files
llgo/ssa/python.go

620 lines
18 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/token"
"go/types"
"sort"
"strings"
"github.com/goplus/llvm"
)
// -----------------------------------------------------------------------------
// PyObjectPtrPtr returns the **py.Object type.
func (p Program) PyObjectPtrPtr() Type {
if p.pyObjPPtr == nil {
p.pyObjPPtr = p.Pointer(p.PyObjectPtr())
}
return p.pyObjPPtr
}
// PyObjectPtr returns the *py.Object type.
func (p Program) PyObjectPtr() Type {
if p.pyObjPtr == nil {
objPtr := types.NewPointer(p.pyNamed("Object"))
p.pyObjPtr = p.rawType(objPtr)
}
return p.pyObjPtr
}
func (p Program) pyNamed(name string) *types.Named {
// TODO(xsw): does python type need to convert?
t := p.python().Scope().Lookup(name).Type().(*types.Named)
return t
}
func (p Program) python() *types.Package {
if p.py == nil {
p.py = p.pyget()
}
return p.py
}
// SetPython sets the Python package.
// Its type can be *types.Package or func() *types.Package.
func (p Program) SetPython(py any) {
switch v := py.(type) {
case *types.Package:
p.py = v
case func() *types.Package:
p.pyget = v
}
}
// -----------------------------------------------------------------------------
func (p Package) pyFunc(fullName string, sig *types.Signature) Expr {
p.NeedPyInit = true
return p.NewFunc(fullName, sig, InC).Expr
}
func (p Program) paramObjPtr() *types.Var {
if p.paramObjPtr_ == nil {
objPtr := p.PyObjectPtr().raw.Type
p.paramObjPtr_ = types.NewParam(token.NoPos, nil, "", objPtr)
}
return p.paramObjPtr_
}
// func(*char) *Object
func (p Program) tyImportPyModule() *types.Signature {
if p.pyImpTy == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", charPtr))
results := types.NewTuple(p.paramObjPtr())
p.pyImpTy = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyImpTy
}
// func(*Object) *Object
func (p Program) tyCallNoArgs() *types.Signature {
if p.callNoArgs == nil {
params := types.NewTuple(p.paramObjPtr())
p.callNoArgs = types.NewSignatureType(nil, nil, nil, params, params, false)
}
return p.callNoArgs
}
// func(*Object, *Object) *Object
func (p Program) tyCallOneArg() *types.Signature {
if p.callOneArg == nil {
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, paramObjPtr)
results := types.NewTuple(paramObjPtr)
p.callOneArg = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.callOneArg
}
// func(*Object, ...) *Object
func (p Program) tyCallFunctionObjArgs() *types.Signature {
if p.callFOArgs == nil {
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, VArg())
results := types.NewTuple(paramObjPtr)
p.callFOArgs = types.NewSignatureType(nil, nil, nil, params, results, true)
}
return p.callFOArgs
}
/*
// func(*Object, *Object, *Object) *Object
func (p Program) tyCall() *types.Signature {
if p.callArgs == nil {
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, paramObjPtr, paramObjPtr)
results := types.NewTuple(paramObjPtr)
p.callArgs = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.callArgs
}
*/
// func(*Object, uintptr, *Object) cint
func (p Program) tyListSetItem() *types.Signature {
if p.pyListSetI == nil {
paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, paramUintptr, paramObjPtr)
results := types.NewTuple(paramCInt)
p.pyListSetI = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyListSetI
}
// func(uintptr) *Object
func (p Program) tyNewList() *types.Signature {
if p.pyNewList == nil {
paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
params := types.NewTuple(paramUintptr)
results := types.NewTuple(p.paramObjPtr())
p.pyNewList = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyNewList
}
// func(*Object, uintptr, *Object) cint
func (p Program) tyTupleSetItem() *types.Signature {
if p.pyTupleSetI == nil {
paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, paramUintptr, paramObjPtr)
results := types.NewTuple(paramCInt)
p.pyTupleSetI = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyTupleSetI
}
// func(uintptr) *Object
func (p Program) tyNewTuple() *types.Signature {
if p.pyNewTuple == nil {
paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
params := types.NewTuple(paramUintptr)
results := types.NewTuple(p.paramObjPtr())
p.pyNewTuple = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyNewTuple
}
// func(int32) *Object
func (p Program) tyBoolFromLong() *types.Signature {
if p.pyBoolFromInt32 == nil {
paramObjPtr := p.paramObjPtr()
paramFloat := types.NewParam(token.NoPos, nil, "", p.Int32().raw.Type)
params := types.NewTuple(paramFloat)
results := types.NewTuple(paramObjPtr)
p.pyBoolFromInt32 = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyBoolFromInt32
}
// func(float64) *Object
func (p Program) tyFloatFromDouble() *types.Signature {
if p.floatFromDbl == nil {
paramObjPtr := p.paramObjPtr()
paramFloat := types.NewParam(token.NoPos, nil, "", p.Float64().raw.Type)
params := types.NewTuple(paramFloat)
results := types.NewTuple(paramObjPtr)
p.floatFromDbl = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.floatFromDbl
}
// func(int64) *Object
func (p Program) tyLongFromInt64() *types.Signature {
if p.pyLongFromInt64 == nil {
paramObjPtr := p.paramObjPtr()
paramInt := types.NewParam(token.NoPos, nil, "", p.Int64().raw.Type)
params := types.NewTuple(paramInt)
results := types.NewTuple(paramObjPtr)
p.pyLongFromInt64 = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyLongFromInt64
}
// func(uint64) *Object
func (p Program) tyLongFromUint64() *types.Signature {
if p.pyLongFromUint64 == nil {
paramObjPtr := p.paramObjPtr()
paramInt := types.NewParam(token.NoPos, nil, "", p.Uint64().raw.Type)
params := types.NewTuple(paramInt)
results := types.NewTuple(paramObjPtr)
p.pyLongFromUint64 = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyLongFromUint64
}
// func(*Object, ...)
func (p Program) tyLoadPyModSyms() *types.Signature {
if p.loadPyModS == nil {
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, VArg())
p.loadPyModS = types.NewSignatureType(nil, nil, nil, params, nil, true)
}
return p.loadPyModS
}
// func(*char) *Object
func (p Program) tyPyUnicodeFromString() *types.Signature {
if p.pyUniStr == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", charPtr))
results := types.NewTuple(p.paramObjPtr())
p.pyUniStr = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyUniStr
}
// func(*char, int) *Object
func (p Program) tyPyUnicodeFromStringAndSize() *types.Signature {
if p.pyUniFromStrAndSize == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", charPtr), types.NewParam(token.NoPos, nil, "", types.Typ[types.Int]))
results := types.NewTuple(p.paramObjPtr())
p.pyUniFromStrAndSize = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyUniFromStrAndSize
}
// func(float64, float64) *Object
func (p Program) tyPyComplexFromDoubles() *types.Signature {
if p.pyComplexFromDbs == nil {
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.Float64]), types.NewParam(token.NoPos, nil, "", types.Typ[types.Float64]))
results := types.NewTuple(p.paramObjPtr())
p.pyComplexFromDbs = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyComplexFromDbs
}
// func(*char, int) *Object
func (p Program) tyPyByteArrayFromStringAndSize() *types.Signature {
if p.pyBytesFromStrAndSize == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", charPtr), types.NewParam(token.NoPos, nil, "", types.Typ[types.Int]))
results := types.NewTuple(p.paramObjPtr())
p.pyBytesFromStrAndSize = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyBytesFromStrAndSize
}
// func(*Objecg, *char) *Object
func (p Program) tyGetAttrString() *types.Signature {
if p.getAttrStr == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
paramObjPtr := p.paramObjPtr()
params := types.NewTuple(paramObjPtr, types.NewParam(token.NoPos, nil, "", charPtr))
results := types.NewTuple(paramObjPtr)
p.getAttrStr = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.getAttrStr
}
// PyNewModVar creates a new global variable for a Python module.
func (p Package) PyNewModVar(name string, doInit bool) Global {
if v, ok := p.pymods[name]; ok {
return v
}
prog := p.Prog
objPtr := prog.PyObjectPtrPtr().raw.Type
g := p.NewVar(name, objPtr, InC)
if doInit {
g.InitNil()
g.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
}
p.pymods[name] = g
return g
}
// PyImportMod imports a Python module.
func (b Builder) PyImportMod(path string) Expr {
fnImp := b.Pkg.pyFunc("PyImport_ImportModule", b.Prog.tyImportPyModule())
return b.Call(fnImp, b.CStr(path))
}
// PyLoadModSyms loads python objects from specified module.
func (b Builder) PyLoadModSyms(modName string, objs ...PyObjRef) Expr {
pkg := b.Pkg
fnLoad := pkg.pyFunc("llgoLoadPyModSyms", b.Prog.tyLoadPyModSyms())
modPtr := pkg.PyNewModVar(modName, false).Expr
mod := b.Load(modPtr)
args := make([]Expr, 1, len(objs)*2+2)
args[0] = mod
nbase := len(modName) + 1
for _, o := range objs {
fullName := o.impl.Name()
name := fullName[nbase:]
args = append(args, b.CStr(name))
args = append(args, o.Expr)
}
prog := b.Prog
args = append(args, prog.Nil(prog.CStr()))
return b.Call(fnLoad, args...)
}
func (b Builder) pyCall(fn Expr, args []Expr) (ret Expr) {
prog := b.Prog
pkg := b.Pkg
fn = b.Load(fn)
sig := fn.raw.Type.(*types.Signature)
params := sig.Params()
n := params.Len()
switch n {
case 0:
call := pkg.pyFunc("PyObject_CallNoArgs", prog.tyCallNoArgs())
ret = b.Call(call, fn)
case 1:
if !sig.Variadic() {
call := pkg.pyFunc("PyObject_CallOneArg", prog.tyCallOneArg())
return b.Call(call, fn, args[0])
}
fallthrough
default:
call := pkg.pyFunc("PyObject_CallFunctionObjArgs", prog.tyCallFunctionObjArgs())
n = len(args)
callargs := make([]Expr, n+2)
callargs[0] = fn
copy(callargs[1:], args)
callargs[n+1] = prog.Nil(prog.PyObjectPtr())
ret = b.Call(call, callargs...)
}
return
}
// PyNewList(n uintptr) *Object
func (b Builder) PyNewList(n Expr) (ret Expr) {
prog := b.Prog
fn := b.Pkg.pyFunc("PyList_New", prog.tyNewList())
return b.Call(fn, n)
}
// PyListSetItem(list *Object, index uintptr, item *Object) c.Int
func (b Builder) PyListSetItem(list, index, item Expr) (ret Expr) {
prog := b.Prog
fn := b.Pkg.pyFunc("PyList_SetItem", prog.tyListSetItem())
return b.Call(fn, list, index, item)
}
// PyList(args ...Expr) *Object
func (b Builder) PyList(args ...Expr) (ret Expr) {
prog := b.Prog
n := len(args)
uintPtr := prog.Uintptr()
list := b.PyNewList(prog.IntVal(uint64(n), uintPtr))
for i, arg := range args {
b.PyListSetItem(list, prog.IntVal(uint64(i), uintPtr), b.PyVal(arg))
}
return list
}
// PyNewTuple(n int) *Object
func (b Builder) PyNewTuple(n Expr) (ret Expr) {
prog := b.Prog
fn := b.Pkg.pyFunc("PyTuple_New", prog.tyNewTuple())
return b.Call(fn, n)
}
// PyTupleSetItem(list *Object, index uintptr, item *Object) c.Int
func (b Builder) PyTupleSetItem(list, index, item Expr) (ret Expr) {
prog := b.Prog
fn := b.Pkg.pyFunc("PyTuple_SetItem", prog.tyTupleSetItem())
return b.Call(fn, list, index, item)
}
// PyTuple(args ...Expr) *Object
func (b Builder) PyTuple(args ...Expr) (ret Expr) {
prog := b.Prog
n := len(args)
uintPtr := prog.Uintptr()
list := b.PyNewTuple(prog.IntVal(uint64(n), uintPtr))
for i, arg := range args {
b.PyTupleSetItem(list, prog.IntVal(uint64(i), uintPtr), b.PyVal(arg))
}
return list
}
// PyVal(v any) *Object
func (b Builder) PyVal(v Expr) (ret Expr) {
switch t := v.raw.Type.Underlying().(type) {
case *types.Basic:
switch t.Kind() {
case types.Bool:
return b.PyBool(v)
case types.Float32:
typ := b.Prog.Float64()
return b.PyFloat(Expr{castFloat(b, v.impl, typ), typ})
case types.Float64:
return b.PyFloat(v)
case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
typ := b.Prog.Int64().ll
if b.Prog.td.TypeAllocSize(v.ll) < b.Prog.td.TypeAllocSize(typ) {
v.impl = llvm.CreateSExt(b.impl, v.impl, typ)
v.ll = typ
}
return b.PyInt64(v)
case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
typ := b.Prog.Uint64().ll
if b.Prog.td.TypeAllocSize(v.ll) < b.Prog.td.TypeAllocSize(typ) {
v.impl = llvm.CreateZExt(b.impl, v.impl, typ)
v.ll = typ
}
return b.PyUint64(v)
case types.String:
return b.PyStrExpr(v)
case types.Complex64:
return b.PyComplex64(v)
case types.Complex128:
return b.PyComplex128(v)
}
case *types.Slice:
if elem, ok := t.Elem().(*types.Basic); ok && elem.Kind() == types.Byte {
return b.PyByteArray(v)
}
case *types.Pointer:
if v.Type == b.Prog.PyObjectPtr() {
return v
}
}
panic("PyVal: todo " + v.raw.Type.String())
}
// PyBool(bVal bool) *Object
func (b Builder) PyBool(bVal Expr) (ret Expr) {
fn := b.Pkg.pyFunc("PyBool_FromLong", b.Prog.tyBoolFromLong())
typ := b.Prog.Int32()
return b.Call(fn, Expr{castInt(b, bVal.impl, typ), typ})
}
// PyFloat(fltVal float64) *Object
func (b Builder) PyFloat(fltVal Expr) (ret Expr) {
fn := b.Pkg.pyFunc("PyFloat_FromDouble", b.Prog.tyFloatFromDouble())
return b.Call(fn, fltVal)
}
// PyInt64(val int64) *Object
func (b Builder) PyInt64(intVal Expr) (ret Expr) {
fn := b.Pkg.pyFunc("PyLong_FromLongLong", b.Prog.tyLongFromInt64())
return b.Call(fn, intVal)
}
// PyUint64(val uint64) *Object
func (b Builder) PyUint64(uintVal Expr) (ret Expr) {
fn := b.Pkg.pyFunc("PyLong_FromUnsignedLongLong", b.Prog.tyLongFromUint64())
return b.Call(fn, uintVal)
}
// PyStr returns a py-style string constant expression.
func (b Builder) PyStr(v string) Expr {
fn := b.Pkg.pyFunc("PyUnicode_FromString", b.Prog.tyPyUnicodeFromString())
return b.Call(fn, b.CStr(v))
}
// PyStrExpr(str string) *Object
func (b Builder) PyStrExpr(v Expr) Expr {
fn := b.Pkg.pyFunc("PyUnicode_FromStringAndSize", b.Prog.tyPyUnicodeFromStringAndSize())
return b.Call(fn, b.StringData(v), b.StringLen(v))
}
// PyComplex128(val complex128) *Object
func (b Builder) PyComplex128(v Expr) Expr {
fn := b.Pkg.pyFunc("PyComplex_FromDoubles", b.Prog.tyPyComplexFromDoubles())
return b.Call(fn, b.getField(v, 0), b.getField(v, 1))
}
// PyComplex64(val complex64) *Object
func (b Builder) PyComplex64(v Expr) Expr {
fn := b.Pkg.pyFunc("PyComplex_FromDoubles", b.Prog.tyPyComplexFromDoubles())
typ := b.Prog.Float64()
return b.Call(fn, Expr{castFloat(b, b.getField(v, 0).impl, typ), typ}, Expr{castFloat(b, b.getField(v, 1).impl, typ), typ})
}
// PyByteArray(val []byte) *Object
func (b Builder) PyByteArray(v Expr) Expr {
fn := b.Pkg.pyFunc("PyByteArray_FromStringAndSize", b.Prog.tyPyByteArrayFromStringAndSize())
return b.Call(fn, b.SliceData(v), b.SliceLen(v))
}
// -----------------------------------------------------------------------------
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 {
p.NeedPyInit = true
obj.InitNil()
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")
}
// -----------------------------------------------------------------------------