move out c/cpp/py
This commit is contained in:
333
ssa/abi/abi.go
Normal file
333
ssa/abi/abi.go
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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 abi
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"hash"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
"github.com/goplus/llgo/runtime/abi"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func BasicKind(t *types.Basic) abi.Kind {
|
||||
kind := t.Kind()
|
||||
switch kind {
|
||||
case types.String:
|
||||
return abi.String
|
||||
case types.UnsafePointer:
|
||||
return abi.UnsafePointer
|
||||
}
|
||||
return abi.Kind(kind)
|
||||
}
|
||||
|
||||
func UnderlyingKind(t types.Type) abi.Kind {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return BasicKind(t)
|
||||
case *types.Pointer:
|
||||
return abi.Pointer
|
||||
case *types.Slice:
|
||||
return abi.Slice
|
||||
case *types.Signature:
|
||||
return abi.Func
|
||||
case *types.Interface:
|
||||
return abi.Interface
|
||||
case *types.Struct:
|
||||
return abi.Struct
|
||||
case *types.Map:
|
||||
return abi.Map
|
||||
case *types.Array:
|
||||
return abi.Array
|
||||
case *types.Chan:
|
||||
return abi.Chan
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
func ChanDir(dir types.ChanDir) (abi.ChanDir, string) {
|
||||
switch dir {
|
||||
case types.SendRecv:
|
||||
return abi.BothDir, "chan"
|
||||
case types.SendOnly:
|
||||
return abi.SendDir, "chan->"
|
||||
case types.RecvOnly:
|
||||
return abi.RecvDir, "<-chan"
|
||||
}
|
||||
panic("invlid chan dir")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type DataKind int
|
||||
|
||||
const (
|
||||
Invalid DataKind = iota
|
||||
Indirect // allocate memory for the value
|
||||
Pointer // store a pointer value directly in the interface value
|
||||
Integer // store a integer value directly in the interface value
|
||||
BitCast // store other value (need bitcast) directly in the interface value
|
||||
)
|
||||
|
||||
func DataKindOf(raw types.Type, lvl int, is32Bits bool) (DataKind, types.Type, int) {
|
||||
switch t := raw.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
kind := t.Kind()
|
||||
switch {
|
||||
case types.Bool <= kind && kind <= types.Uintptr:
|
||||
if is32Bits && (kind == types.Int64 || kind == types.Uint64) {
|
||||
return Indirect, raw, lvl
|
||||
}
|
||||
return Integer, raw, lvl
|
||||
case kind == types.Float32:
|
||||
return BitCast, raw, lvl
|
||||
case kind == types.Float64:
|
||||
if is32Bits {
|
||||
return Indirect, raw, lvl
|
||||
}
|
||||
return BitCast, raw, lvl
|
||||
case kind == types.UnsafePointer:
|
||||
return Pointer, raw, lvl
|
||||
}
|
||||
case *types.Pointer, *types.Signature, *types.Map, *types.Chan:
|
||||
return Pointer, raw, lvl
|
||||
case *types.Struct:
|
||||
if t.NumFields() == 1 {
|
||||
return DataKindOf(t.Field(0).Type(), lvl+1, is32Bits)
|
||||
}
|
||||
case *types.Interface, *types.Slice:
|
||||
case *types.Array:
|
||||
if t.Len() == 1 {
|
||||
return DataKindOf(t.Elem(), lvl+1, is32Bits)
|
||||
}
|
||||
default:
|
||||
panic("unkown type")
|
||||
}
|
||||
return Indirect, raw, lvl
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Builder is a helper for constructing ABI types.
|
||||
type Builder struct {
|
||||
buf []byte
|
||||
Pkg string
|
||||
}
|
||||
|
||||
// New creates a new ABI type Builder.
|
||||
func New(pkg string) *Builder {
|
||||
ret := new(Builder)
|
||||
ret.Init(pkg)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *Builder) Init(pkg string) {
|
||||
b.Pkg = pkg
|
||||
b.buf = make([]byte, sha256.Size)
|
||||
}
|
||||
|
||||
// TypeName returns the ABI type name for the specified type.
|
||||
func (b *Builder) TypeName(t types.Type) (ret string, pub bool) {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return BasicName(t), true
|
||||
case *types.Pointer:
|
||||
ret, pub = b.TypeName(t.Elem())
|
||||
return "*" + ret, pub
|
||||
case *types.Struct:
|
||||
return b.StructName(t)
|
||||
case *types.Signature:
|
||||
return b.FuncName(t), true
|
||||
case *types.Slice:
|
||||
ret, pub = b.TypeName(t.Elem())
|
||||
return "[]" + ret, pub
|
||||
case *types.Array:
|
||||
ret, pub = b.TypeName(t.Elem())
|
||||
return fmt.Sprintf("[%v]%s", t.Len(), ret), pub
|
||||
case *types.Named:
|
||||
o := t.Obj()
|
||||
pkg := o.Pkg()
|
||||
return "_llgo_" + FullName(pkg, NamedName(t)), (pkg == nil || o.Exported())
|
||||
case *types.Interface:
|
||||
if t.Empty() {
|
||||
return "_llgo_any", true
|
||||
}
|
||||
return b.InterfaceName(t)
|
||||
case *types.Map:
|
||||
key, pub1 := b.TypeName(t.Key())
|
||||
elem, pub2 := b.TypeName(t.Elem())
|
||||
return fmt.Sprintf("map[%s]%s", key, elem), pub1 && pub2
|
||||
case *types.Chan:
|
||||
elem, pub := b.TypeName(t.Elem())
|
||||
var s string
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
s = "chan"
|
||||
case types.SendOnly:
|
||||
s = "chan<-"
|
||||
case types.RecvOnly:
|
||||
s = "<-chan"
|
||||
}
|
||||
return fmt.Sprintf("%s %s", s, elem), pub
|
||||
case *types.Alias:
|
||||
return b.TypeName(types.Unalias(t))
|
||||
}
|
||||
log.Panicf("todo: %T\n", t)
|
||||
return
|
||||
}
|
||||
|
||||
func NamedName(t *types.Named) string {
|
||||
if targs := t.TypeArgs(); targs != nil {
|
||||
n := targs.Len()
|
||||
infos := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
infos[i] = types.TypeString(targs.At(i), PathOf)
|
||||
}
|
||||
return t.Obj().Name() + "[" + strings.Join(infos, ",") + "]"
|
||||
}
|
||||
return t.Obj().Name()
|
||||
}
|
||||
|
||||
func TypeArgs(typeArgs []types.Type) string {
|
||||
targs := make([]string, len(typeArgs))
|
||||
for i, t := range typeArgs {
|
||||
targs[i] = types.TypeString(t, PathOf)
|
||||
}
|
||||
return "[" + strings.Join(targs, ",") + "]"
|
||||
}
|
||||
|
||||
const (
|
||||
PatchPathPrefix = env.LLGoRuntimePkg + "/internal/lib/"
|
||||
)
|
||||
|
||||
// PathOf returns the package path of the specified package.
|
||||
func PathOf(pkg *types.Package) string {
|
||||
if pkg == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimPrefix(pkg.Path(), PatchPathPrefix)
|
||||
}
|
||||
|
||||
// FullName returns the full name of a package member.
|
||||
func FullName(pkg *types.Package, name string) string {
|
||||
if pkg == nil {
|
||||
return name
|
||||
}
|
||||
return PathOf(pkg) + "." + name
|
||||
}
|
||||
|
||||
// BasicName returns the ABI type name for the specified basic type.
|
||||
func BasicName(t *types.Basic) string {
|
||||
name := t.Name()
|
||||
if name == "byte" {
|
||||
name = "uint8"
|
||||
}
|
||||
return "_llgo_" + name
|
||||
}
|
||||
|
||||
// FuncName returns the ABI type name for the specified function type.
|
||||
func (b *Builder) FuncName(t *types.Signature) string {
|
||||
hash := b.funcHash(t)
|
||||
hashStr := base64.RawURLEncoding.EncodeToString(hash)
|
||||
return "_llgo_func$" + hashStr
|
||||
}
|
||||
|
||||
func (b *Builder) funcHash(t *types.Signature) []byte {
|
||||
h := sha256.New()
|
||||
params, results := t.Params(), t.Results()
|
||||
fmt.Fprintln(h, "func", params.Len(), results.Len(), t.Variadic())
|
||||
b.tuple(h, params)
|
||||
b.tuple(h, results)
|
||||
return h.Sum(b.buf[:0])
|
||||
}
|
||||
|
||||
func (b *Builder) tuple(h hash.Hash, t *types.Tuple) {
|
||||
n := t.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
v := t.At(i)
|
||||
ft, _ := b.TypeName(v.Type())
|
||||
fmt.Fprintln(h, ft)
|
||||
}
|
||||
}
|
||||
|
||||
// InterfaceName returns the ABI type name for the specified interface type.
|
||||
func (b *Builder) InterfaceName(t *types.Interface) (ret string, pub bool) {
|
||||
hash, private := b.interfaceHash(t)
|
||||
hashStr := base64.RawURLEncoding.EncodeToString(hash)
|
||||
if private {
|
||||
return b.Pkg + ".iface$" + hashStr, false
|
||||
}
|
||||
return "_llgo_iface$" + hashStr, true
|
||||
}
|
||||
|
||||
func (b *Builder) interfaceHash(t *types.Interface) (ret []byte, private bool) {
|
||||
h := sha256.New()
|
||||
n := t.NumMethods()
|
||||
fmt.Fprintln(h, "interface", n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
if !m.Exported() {
|
||||
private = true
|
||||
}
|
||||
ft := b.FuncName(m.Type().(*types.Signature))
|
||||
fmt.Fprintln(h, m.Name(), ft)
|
||||
}
|
||||
ret = h.Sum(b.buf[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// StructName returns the ABI type name for the specified struct type.
|
||||
func (b *Builder) StructName(t *types.Struct) (ret string, pub bool) {
|
||||
hash, private := b.structHash(t)
|
||||
hashStr := base64.RawURLEncoding.EncodeToString(hash)
|
||||
if private {
|
||||
return b.Pkg + ".struct$" + hashStr, false
|
||||
}
|
||||
return "_llgo_struct$" + hashStr, false
|
||||
}
|
||||
|
||||
func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) {
|
||||
h := sha256.New()
|
||||
n := t.NumFields()
|
||||
fmt.Fprintln(h, "struct", n)
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if !f.Exported() {
|
||||
private = true
|
||||
}
|
||||
name := f.Name()
|
||||
if f.Embedded() {
|
||||
name = "-"
|
||||
}
|
||||
ft, pub := b.TypeName(f.Type())
|
||||
fmt.Fprintln(h, name, ft)
|
||||
if !pub {
|
||||
private = true
|
||||
}
|
||||
}
|
||||
ret = h.Sum(b.buf[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
547
ssa/abi/map.go
Normal file
547
ssa/abi/map.go
Normal file
@@ -0,0 +1,547 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llgo/runtime/abi"
|
||||
)
|
||||
|
||||
// Builds a type representing a Bucket structure for
|
||||
// the given map type. This type is not visible to users -
|
||||
// we include only enough information to generate a correct GC
|
||||
// program for it.
|
||||
// Make sure this stays in sync with runtime/map.go.
|
||||
//
|
||||
// A "bucket" is a "struct" {
|
||||
// tophash [BUCKETSIZE]uint8
|
||||
// keys [BUCKETSIZE]keyType
|
||||
// elems [BUCKETSIZE]elemType
|
||||
// overflow *bucket
|
||||
// }
|
||||
const (
|
||||
BUCKETSIZE = abi.MapBucketCount
|
||||
MAXKEYSIZE = abi.MapMaxKeyBytes
|
||||
MAXELEMSIZE = abi.MapMaxElemBytes
|
||||
)
|
||||
|
||||
func makefield(name string, t types.Type) *types.Var {
|
||||
return types.NewField(token.NoPos, nil, name, t, false)
|
||||
}
|
||||
|
||||
// MapBucketType makes the map bucket type given the type of the map.
|
||||
func MapBucketType(t *types.Map, sizes types.Sizes) types.Type {
|
||||
keytype := t.Key()
|
||||
elemtype := t.Elem()
|
||||
if sizes.Sizeof(keytype) > MAXKEYSIZE {
|
||||
keytype = types.NewPointer(keytype)
|
||||
}
|
||||
if sizes.Sizeof(elemtype) > MAXELEMSIZE {
|
||||
elemtype = types.NewPointer(elemtype)
|
||||
}
|
||||
|
||||
field := make([]*types.Var, 0, 5)
|
||||
|
||||
// The first field is: uint8 topbits[BUCKETSIZE].
|
||||
arr := types.NewArray(types.Typ[types.Uint8], BUCKETSIZE)
|
||||
field = append(field, makefield("topbits", arr))
|
||||
|
||||
arr = types.NewArray(keytype, BUCKETSIZE)
|
||||
//arr.SetNoalg(true)
|
||||
keys := makefield("keys", arr)
|
||||
field = append(field, keys)
|
||||
|
||||
arr = types.NewArray(elemtype, BUCKETSIZE)
|
||||
//arr.SetNoalg(true)
|
||||
elems := makefield("elems", arr)
|
||||
field = append(field, elems)
|
||||
|
||||
// If keys and elems have no pointers, the map implementation
|
||||
// can keep a list of overflow pointers on the side so that
|
||||
// buckets can be marked as having no pointers.
|
||||
// Arrange for the bucket to have no pointers by changing
|
||||
// the type of the overflow field to uintptr in this case.
|
||||
// See comment on hmap.overflow in runtime/map.go.
|
||||
otyp := types.Typ[types.UnsafePointer]
|
||||
if !HasPtrData(elemtype) && !HasPtrData(keytype) {
|
||||
otyp = types.Typ[types.Uintptr]
|
||||
}
|
||||
overflow := makefield("overflow", otyp)
|
||||
field = append(field, overflow)
|
||||
|
||||
// link up fields
|
||||
bucket := types.NewStruct(field[:], nil)
|
||||
|
||||
// Check invariants that map code depends on.
|
||||
if !types.Comparable(t.Key()) {
|
||||
log.Fatalf("unsupported map key type for %v", t)
|
||||
}
|
||||
if BUCKETSIZE < 8 {
|
||||
log.Fatalf("bucket size %d too small for proper alignment %d", BUCKETSIZE, 8)
|
||||
}
|
||||
if uint8(sizes.Alignof(keytype)) > BUCKETSIZE {
|
||||
log.Fatalf("key align too big for %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(elemtype)) > BUCKETSIZE {
|
||||
log.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", sizes.Alignof(elemtype), t, BUCKETSIZE)
|
||||
}
|
||||
if sizes.Alignof(keytype) > MAXKEYSIZE {
|
||||
log.Fatalf("key size too large for %v", t)
|
||||
}
|
||||
if sizes.Alignof(elemtype) > MAXELEMSIZE {
|
||||
log.Fatalf("elem size too large for %v", t)
|
||||
}
|
||||
if sizes.Alignof(t.Key()) > MAXKEYSIZE && !isPointer(keytype) {
|
||||
log.Fatalf("key indirect incorrect for %v", t)
|
||||
}
|
||||
if sizes.Alignof(t.Elem()) > MAXELEMSIZE && !isPointer(elemtype) {
|
||||
log.Fatalf("elem indirect incorrect for %v", t)
|
||||
}
|
||||
if sizes.Sizeof(keytype)%sizes.Alignof(keytype) != 0 {
|
||||
log.Fatalf("key size not a multiple of key align for %v", t)
|
||||
}
|
||||
if sizes.Sizeof(elemtype)%sizes.Alignof(elemtype) != 0 {
|
||||
log.Fatalf("elem size not a multiple of elem align for %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(keytype)) != 0 {
|
||||
log.Fatalf("bucket align not multiple of key align %v", t)
|
||||
}
|
||||
if uint8(sizes.Alignof(bucket))%uint8(sizes.Alignof(elemtype)) != 0 {
|
||||
log.Fatalf("bucket align not multiple of elem align %v", t)
|
||||
}
|
||||
offs := sizes.Offsetsof(field)
|
||||
if offs[1]%sizes.Alignof(keytype) != 0 {
|
||||
log.Fatalf("bad alignment of keys in bmap for %v", t)
|
||||
}
|
||||
if offs[2]%sizes.Alignof(elemtype) != 0 {
|
||||
log.Fatalf("bad alignment of elems in bmap for %v", t)
|
||||
}
|
||||
|
||||
// // Double-check that overflow field is final memory in struct,
|
||||
// // with no padding at end.
|
||||
// if overflow.Offset != bucket.Size()-int64(types.PtrSize) {
|
||||
// log.Fatalf("bad offset of overflow in bmap for %v, overflow.Offset=%d, bucket.Size()-int64(types.PtrSize)=%d",
|
||||
// t, overflow.Offset, bucket.Size()-int64(types.PtrSize))
|
||||
// }
|
||||
return bucket
|
||||
}
|
||||
|
||||
func isPointer(t types.Type) (ok bool) {
|
||||
_, ok = t.Underlying().(*types.Pointer)
|
||||
return
|
||||
}
|
||||
|
||||
func MapTypeFlags(t *types.Map, sizes types.Sizes) (flags int) {
|
||||
if sizes.Sizeof(t.Key()) > MAXKEYSIZE {
|
||||
flags |= 1 // indirect key
|
||||
}
|
||||
if sizes.Sizeof(t.Elem()) > MAXELEMSIZE {
|
||||
flags |= 2 // indirect value
|
||||
}
|
||||
if IsReflexive(t.Key()) {
|
||||
flags |= 4 // reflexive key
|
||||
}
|
||||
if needkeyupdate(t.Key()) {
|
||||
flags |= 8 // need key update
|
||||
}
|
||||
if hashMightPanic(t.Key()) {
|
||||
flags |= 16 // hash might panic
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go
|
||||
// func MapBucketType(t *types.Type) *types.Type {
|
||||
// if t.MapType().Bucket != nil {
|
||||
// return t.MapType().Bucket
|
||||
// }
|
||||
|
||||
// keytype := t.Key()
|
||||
// elemtype := t.Elem()
|
||||
// types.CalcSize(keytype)
|
||||
// types.CalcSize(elemtype)
|
||||
// if keytype.Size() > MAXKEYSIZE {
|
||||
// keytype = types.NewPtr(keytype)
|
||||
// }
|
||||
// if elemtype.Size() > MAXELEMSIZE {
|
||||
// elemtype = types.NewPtr(elemtype)
|
||||
// }
|
||||
|
||||
// field := make([]*types.Field, 0, 5)
|
||||
|
||||
// // The first field is: uint8 topbits[BUCKETSIZE].
|
||||
// arr := types.NewArray(types.Types[types.TUINT8], BUCKETSIZE)
|
||||
// field = append(field, makefield("topbits", arr))
|
||||
|
||||
// arr = types.NewArray(keytype, BUCKETSIZE)
|
||||
// arr.SetNoalg(true)
|
||||
// keys := makefield("keys", arr)
|
||||
// field = append(field, keys)
|
||||
|
||||
// arr = types.NewArray(elemtype, BUCKETSIZE)
|
||||
// arr.SetNoalg(true)
|
||||
// elems := makefield("elems", arr)
|
||||
// field = append(field, elems)
|
||||
|
||||
// // If keys and elems have no pointers, the map implementation
|
||||
// // can keep a list of overflow pointers on the side so that
|
||||
// // buckets can be marked as having no pointers.
|
||||
// // Arrange for the bucket to have no pointers by changing
|
||||
// // the type of the overflow field to uintptr in this case.
|
||||
// // See comment on hmap.overflow in runtime/map.go.
|
||||
// otyp := types.Types[types.TUNSAFEPTR]
|
||||
// if !elemtype.HasPointers() && !keytype.HasPointers() {
|
||||
// otyp = types.Types[types.TUINTPTR]
|
||||
// }
|
||||
// overflow := makefield("overflow", otyp)
|
||||
// field = append(field, overflow)
|
||||
|
||||
// // link up fields
|
||||
// bucket := types.NewStruct(field[:])
|
||||
// bucket.SetNoalg(true)
|
||||
// types.CalcSize(bucket)
|
||||
|
||||
// // Check invariants that map code depends on.
|
||||
// if !types.IsComparable(t.Key()) {
|
||||
// base.Fatalf("unsupported map key type for %v", t)
|
||||
// }
|
||||
// if BUCKETSIZE < 8 {
|
||||
// base.Fatalf("bucket size %d too small for proper alignment %d", BUCKETSIZE, 8)
|
||||
// }
|
||||
// if uint8(keytype.Alignment()) > BUCKETSIZE {
|
||||
// base.Fatalf("key align too big for %v", t)
|
||||
// }
|
||||
// if uint8(elemtype.Alignment()) > BUCKETSIZE {
|
||||
// base.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", elemtype.Alignment(), t, BUCKETSIZE)
|
||||
// }
|
||||
// if keytype.Size() > MAXKEYSIZE {
|
||||
// base.Fatalf("key size too large for %v", t)
|
||||
// }
|
||||
// if elemtype.Size() > MAXELEMSIZE {
|
||||
// base.Fatalf("elem size too large for %v", t)
|
||||
// }
|
||||
// if t.Key().Size() > MAXKEYSIZE && !keytype.IsPtr() {
|
||||
// base.Fatalf("key indirect incorrect for %v", t)
|
||||
// }
|
||||
// if t.Elem().Size() > MAXELEMSIZE && !elemtype.IsPtr() {
|
||||
// base.Fatalf("elem indirect incorrect for %v", t)
|
||||
// }
|
||||
// if keytype.Size()%keytype.Alignment() != 0 {
|
||||
// base.Fatalf("key size not a multiple of key align for %v", t)
|
||||
// }
|
||||
// if elemtype.Size()%elemtype.Alignment() != 0 {
|
||||
// base.Fatalf("elem size not a multiple of elem align for %v", t)
|
||||
// }
|
||||
// if uint8(bucket.Alignment())%uint8(keytype.Alignment()) != 0 {
|
||||
// base.Fatalf("bucket align not multiple of key align %v", t)
|
||||
// }
|
||||
// if uint8(bucket.Alignment())%uint8(elemtype.Alignment()) != 0 {
|
||||
// base.Fatalf("bucket align not multiple of elem align %v", t)
|
||||
// }
|
||||
// if keys.Offset%keytype.Alignment() != 0 {
|
||||
// base.Fatalf("bad alignment of keys in bmap for %v", t)
|
||||
// }
|
||||
// if elems.Offset%elemtype.Alignment() != 0 {
|
||||
// base.Fatalf("bad alignment of elems in bmap for %v", t)
|
||||
// }
|
||||
|
||||
// // Double-check that overflow field is final memory in struct,
|
||||
// // with no padding at end.
|
||||
// if overflow.Offset != bucket.Size()-int64(types.PtrSize) {
|
||||
// base.Fatalf("bad offset of overflow in bmap for %v, overflow.Offset=%d, bucket.Size()-int64(types.PtrSize)=%d",
|
||||
// t, overflow.Offset, bucket.Size()-int64(types.PtrSize))
|
||||
// }
|
||||
|
||||
// t.MapType().Bucket = bucket
|
||||
|
||||
// bucket.StructType().Map = t
|
||||
// return bucket
|
||||
// }
|
||||
|
||||
// PtrDataSize returns the length in bytes of the prefix of t
|
||||
// containing pointer data. Anything after this offset is scalar data.
|
||||
//
|
||||
// PtrDataSize is only defined for actual Go types. It's an error to
|
||||
// use it on compiler-internal types (e.g., TSSA, TRESULTS).
|
||||
func HasPtrData(t types.Type) bool {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.String:
|
||||
return true
|
||||
case types.UnsafePointer:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case *types.Pointer:
|
||||
return true
|
||||
case *types.Signature, *types.Chan, *types.Map:
|
||||
return true
|
||||
case *types.Interface:
|
||||
return true
|
||||
case *types.Slice:
|
||||
return true
|
||||
case *types.Array:
|
||||
if t.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
return HasPtrData(t.Elem())
|
||||
case *types.Struct:
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
if HasPtrData(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
log.Fatalf("PtrDataSize: unexpected type, %v", t)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/types/type.go
|
||||
// func PtrDataSize(t *Type) int64 {
|
||||
// switch t.Kind() {
|
||||
// case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32,
|
||||
// TUINT32, TINT64, TUINT64, TINT, TUINT,
|
||||
// TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64:
|
||||
// return 0
|
||||
|
||||
// case TPTR:
|
||||
// if t.Elem().NotInHeap() {
|
||||
// return 0
|
||||
// }
|
||||
// return int64(PtrSize)
|
||||
|
||||
// case TUNSAFEPTR, TFUNC, TCHAN, TMAP:
|
||||
// return int64(PtrSize)
|
||||
|
||||
// case TSTRING:
|
||||
// // struct { byte *str; intgo len; }
|
||||
// return int64(PtrSize)
|
||||
|
||||
// case TINTER:
|
||||
// // struct { Itab *tab; void *data; } or
|
||||
// // struct { Type *type; void *data; }
|
||||
// // Note: see comment in typebits.Set
|
||||
// return 2 * int64(PtrSize)
|
||||
|
||||
// case TSLICE:
|
||||
// if t.Elem().NotInHeap() {
|
||||
// return 0
|
||||
// }
|
||||
// // struct { byte *array; uintgo len; uintgo cap; }
|
||||
// return int64(PtrSize)
|
||||
|
||||
// case TARRAY:
|
||||
// if t.NumElem() == 0 {
|
||||
// return 0
|
||||
// }
|
||||
// // t.NumElem() > 0
|
||||
// size := PtrDataSize(t.Elem())
|
||||
// if size == 0 {
|
||||
// return 0
|
||||
// }
|
||||
// return (t.NumElem()-1)*t.Elem().Size() + size
|
||||
|
||||
// case TSTRUCT:
|
||||
// // Find the last field that has pointers, if any.
|
||||
// fs := t.Fields().Slice()
|
||||
// for i := len(fs) - 1; i >= 0; i-- {
|
||||
// if size := PtrDataSize(fs[i].Type); size > 0 {
|
||||
// return fs[i].Offset + size
|
||||
// }
|
||||
// }
|
||||
// return 0
|
||||
|
||||
// case TSSA:
|
||||
// if t != TypeInt128 {
|
||||
// base.Fatalf("PtrDataSize: unexpected ssa type %v", t)
|
||||
// }
|
||||
// return 0
|
||||
|
||||
// default:
|
||||
// base.Fatalf("PtrDataSize: unexpected type, %v", t)
|
||||
// return 0
|
||||
// }
|
||||
// }
|
||||
|
||||
// IsReflexive reports whether t has a reflexive equality operator.
|
||||
// That is, if x==x for all x of type t.
|
||||
func IsReflexive(t types.Type) bool {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Float32, types.Float64, types.Complex64, types.Complex128:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
case *types.Pointer, *types.Chan:
|
||||
return true
|
||||
case *types.Interface:
|
||||
return false
|
||||
case *types.Array:
|
||||
return IsReflexive(t.Elem())
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
if !IsReflexive(t.Field(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
log.Fatalf("bad type for map key: %v", t)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/types/type.go
|
||||
// func IsReflexive(t *Type) bool {
|
||||
// switch t.Kind() {
|
||||
// case TBOOL,
|
||||
// TINT,
|
||||
// TUINT,
|
||||
// TINT8,
|
||||
// TUINT8,
|
||||
// TINT16,
|
||||
// TUINT16,
|
||||
// TINT32,
|
||||
// TUINT32,
|
||||
// TINT64,
|
||||
// TUINT64,
|
||||
// TUINTPTR,
|
||||
// TPTR,
|
||||
// TUNSAFEPTR,
|
||||
// TSTRING,
|
||||
// TCHAN:
|
||||
// return true
|
||||
|
||||
// case TFLOAT32,
|
||||
// TFLOAT64,
|
||||
// TCOMPLEX64,
|
||||
// TCOMPLEX128,
|
||||
// TINTER:
|
||||
// return false
|
||||
|
||||
// case TARRAY:
|
||||
// return IsReflexive(t.Elem())
|
||||
|
||||
// case TSTRUCT:
|
||||
// for _, t1 := range t.Fields().Slice() {
|
||||
// if !IsReflexive(t1.Type) {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// return true
|
||||
|
||||
// default:
|
||||
// base.Fatalf("bad type for map key: %v", t)
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/types/type.go
|
||||
// needkeyupdate reports whether map updates with t as a key
|
||||
// need the key to be updated.
|
||||
func needkeyupdate(t types.Type) bool {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Float32, types.Float64, types.Complex64, types.Complex128:
|
||||
return true // floats and complex can be +0/-0
|
||||
case types.String:
|
||||
return true // strings might have smaller backing stores
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case *types.Interface:
|
||||
return true
|
||||
case *types.Pointer, *types.Chan:
|
||||
return false
|
||||
case *types.Array:
|
||||
return needkeyupdate(t.Elem())
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
if needkeyupdate(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
log.Fatalf("bad type for map key: %v", t)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go
|
||||
// func needkeyupdate(t *types.Type) bool {
|
||||
// switch t.Kind() {
|
||||
// case types.TBOOL, types.TINT, types.TUINT, types.TINT8, types.TUINT8, types.TINT16, types.TUINT16, types.TINT32, types.TUINT32,
|
||||
// types.TINT64, types.TUINT64, types.TUINTPTR, types.TPTR, types.TUNSAFEPTR, types.TCHAN:
|
||||
// return false
|
||||
|
||||
// case types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, // floats and complex can be +0/-0
|
||||
// types.TINTER,
|
||||
// types.TSTRING: // strings might have smaller backing stores
|
||||
// return true
|
||||
|
||||
// case types.TARRAY:
|
||||
// return needkeyupdate(t.Elem())
|
||||
|
||||
// case types.TSTRUCT:
|
||||
// for _, t1 := range t.Fields().Slice() {
|
||||
// if needkeyupdate(t1.Type) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
|
||||
// default:
|
||||
// base.Fatalf("bad type for map key: %v", t)
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
|
||||
// hashMightPanic reports whether the hash of a map key of type t might panic.
|
||||
func hashMightPanic(t types.Type) bool {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Interface:
|
||||
return true
|
||||
case *types.Array:
|
||||
return hashMightPanic(t.Elem())
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
if hashMightPanic(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go
|
||||
// func hashMightPanic(t *types.Type) bool {
|
||||
// switch t.Kind() {
|
||||
// case types.TINTER:
|
||||
// return true
|
||||
|
||||
// case types.TARRAY:
|
||||
// return hashMightPanic(t.Elem())
|
||||
|
||||
// case types.TSTRUCT:
|
||||
// for _, t1 := range t.Fields().Slice() {
|
||||
// if hashMightPanic(t1.Type) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
|
||||
// default:
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
505
ssa/abitype.go
Normal file
505
ssa/abitype.go
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* 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"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/ssa/abi"
|
||||
"github.com/goplus/llvm"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// abiBasic returns the abi type of the specified basic kind.
|
||||
func (b Builder) abiBasic(t *types.Basic) func() Expr {
|
||||
/*
|
||||
TODO(xsw):
|
||||
return b.abiExtern(abi.BasicName(t))
|
||||
*/
|
||||
return func() Expr {
|
||||
kind := int(abi.BasicKind(t))
|
||||
dk, _, _ := abi.DataKindOf(t, 0, b.Prog.is32Bits)
|
||||
switch dk {
|
||||
case abi.Integer, abi.BitCast, abi.Pointer:
|
||||
const kindDirectIface = 1 << 5
|
||||
kind |= kindDirectIface
|
||||
}
|
||||
return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(kind))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (b Builder) abiExtern(name string) Expr {
|
||||
g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr())
|
||||
return b.Load(g.Expr)
|
||||
}
|
||||
*/
|
||||
|
||||
func (b Builder) abiTypeOf(t types.Type) func() Expr {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return b.abiBasic(t)
|
||||
case *types.Pointer:
|
||||
return b.abiPointerOf(t)
|
||||
case *types.Struct:
|
||||
return b.abiStructOf(t)
|
||||
case *types.Named:
|
||||
if _, ok := t.Underlying().(*types.Interface); ok {
|
||||
return b.abiNamedInterfaceOf(t)
|
||||
}
|
||||
return b.abiNamedOf(t)
|
||||
case *types.Interface:
|
||||
return b.abiInterfaceOf(t)
|
||||
case *types.Signature:
|
||||
return b.abiFuncOf(t)
|
||||
case *types.Slice:
|
||||
return b.abiSliceOf(t)
|
||||
case *types.Array:
|
||||
return b.abiArrayOf(t)
|
||||
case *types.Chan:
|
||||
return b.abiChanOf(t)
|
||||
case *types.Map:
|
||||
return b.abiMapOf(t)
|
||||
case *types.Alias:
|
||||
return b.abiTypeOf(types.Unalias(t))
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
func (b Builder) abiTupleOf(t *types.Tuple) func() Expr {
|
||||
n := t.Len()
|
||||
tuple := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tuple[i] = b.abiType(t.At(i).Type())
|
||||
}
|
||||
return func() Expr {
|
||||
prog := b.Prog
|
||||
tSlice := prog.Slice(prog.AbiTypePtr())
|
||||
return b.SliceLit(tSlice, tuple...)
|
||||
}
|
||||
}
|
||||
|
||||
// func Func(in, out []*Type, variadic bool)
|
||||
func (b Builder) abiFuncOf(sig *types.Signature) func() Expr {
|
||||
params := b.abiTupleOf(sig.Params())
|
||||
results := b.abiTupleOf(sig.Results())
|
||||
return func() Expr {
|
||||
prog := b.Prog
|
||||
pkg := b.Pkg
|
||||
fn := pkg.rtFunc("Func")
|
||||
variadic := prog.Val(sig.Variadic())
|
||||
return b.Call(fn, params(), results(), variadic)
|
||||
}
|
||||
}
|
||||
|
||||
// Imethod{name string, typ *FuncType}
|
||||
func (b Builder) abiImethodOf(mName string, typ Expr) Expr {
|
||||
prog := b.Prog
|
||||
name := b.Str(mName)
|
||||
return b.aggregateValue(prog.rtType("Imethod"), name.impl, typ.impl)
|
||||
}
|
||||
|
||||
func (b Builder) abiMethods(t *types.Named) (ret, pret int) {
|
||||
methods := typeutil.IntuitiveMethodSet(t, nil)
|
||||
pret = len(methods)
|
||||
for _, m := range methods {
|
||||
if _, ok := m.Recv().(*types.Pointer); ok {
|
||||
continue
|
||||
}
|
||||
ret++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Method{name string, typ *FuncType, ifn, tfn abi.Text}
|
||||
func (b Builder) abiMethodOf(mPkg *types.Package, mName string, mSig *types.Signature /*, bg Background = InGo */) (mthd, ptrMthd Expr) {
|
||||
prog := b.Prog
|
||||
name := b.Str(mName).impl
|
||||
if !token.IsExported(mName) {
|
||||
name = b.Str(abi.FullName(mPkg, mName)).impl
|
||||
}
|
||||
abiSigGo := types.NewSignatureType(nil, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
|
||||
abiSig := prog.FuncDecl(abiSigGo, InGo).raw.Type
|
||||
abiTyp := b.abiType(abiSig)
|
||||
abiTypImpl := abiTyp.impl
|
||||
|
||||
recv := mSig.Recv()
|
||||
recvType := recv.Type()
|
||||
if _, ok := recvType.(*types.Pointer); ok {
|
||||
ptrMthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, llvm.Value{})
|
||||
return
|
||||
}
|
||||
ptrRecv := types.NewVar(0, nil, "", types.NewPointer(recvType))
|
||||
ptrSig := types.NewSignatureType(ptrRecv, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
|
||||
ptrMthd, ifn := b.abiMthd(mPkg, mName, ptrSig, name, abiTypImpl, llvm.Value{})
|
||||
mthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, ifn)
|
||||
return
|
||||
}
|
||||
|
||||
func (b Builder) abiMthd(mPkg *types.Package, mName string, mSig *types.Signature, name, abiTyp, ifn llvm.Value) (ret Expr, tfn llvm.Value) {
|
||||
fullName := FuncName(mPkg, mName, mSig.Recv(), false)
|
||||
if mSig.TypeParams().Len() > 0 || mSig.RecvTypeParams().Len() > 0 {
|
||||
if !b.Pkg.Prog.FuncCompiled(fullName) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if b.Pkg.fnlink != nil {
|
||||
fullName = b.Pkg.fnlink(fullName)
|
||||
}
|
||||
tfn = b.Pkg.NewFunc(fullName, mSig, InGo).impl // TODO(xsw): use rawType to speed up
|
||||
if ifn.IsNil() {
|
||||
ifn = tfn
|
||||
}
|
||||
ret = b.aggregateValue(b.Prog.rtType("Method"), name, abiTyp, ifn, tfn)
|
||||
return
|
||||
}
|
||||
|
||||
// func Interface(pkgPath, name string, methods []abi.Imethod)
|
||||
func (b Builder) abiInterfaceOf(t *types.Interface) func() Expr {
|
||||
n := t.NumMethods()
|
||||
typs := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
typs[i] = b.abiType(m.Type())
|
||||
}
|
||||
return func() Expr {
|
||||
prog := b.Prog
|
||||
methods := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
mName := m.Name()
|
||||
if !token.IsExported(mName) {
|
||||
mName = abi.FullName(m.Pkg(), mName)
|
||||
}
|
||||
methods[i] = b.abiImethodOf(mName, typs[i])
|
||||
}
|
||||
pkg := b.Pkg
|
||||
fn := pkg.rtFunc("Interface")
|
||||
tSlice := lastParamType(prog, fn)
|
||||
methodSlice := b.SliceLit(tSlice, methods...)
|
||||
return b.Call(fn, b.Str(pkg.Path()), methodSlice)
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiInitNamedInterface(ret Expr, t *types.Interface) func() Expr {
|
||||
n := t.NumMethods()
|
||||
typs := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
typs[i] = b.abiType(m.Type())
|
||||
}
|
||||
return func() Expr {
|
||||
prog := b.Prog
|
||||
methods := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
mName := m.Name()
|
||||
if !token.IsExported(mName) {
|
||||
mName = abi.FullName(m.Pkg(), mName)
|
||||
}
|
||||
methods[i] = b.abiImethodOf(mName, typs[i])
|
||||
}
|
||||
fn := b.Pkg.rtFunc("InitNamedInterface")
|
||||
tSlice := lastParamType(prog, fn)
|
||||
methodSlice := b.SliceLit(tSlice, methods...)
|
||||
return b.Call(fn, ret, methodSlice)
|
||||
}
|
||||
}
|
||||
|
||||
// func NewNamed(kind abi.Kind, methods, ptrMethods int)
|
||||
func (b Builder) abiNamedOf(t *types.Named) func() Expr {
|
||||
expr := b.abiNamed(t)
|
||||
return func() Expr {
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiNamed(t *types.Named) Expr {
|
||||
pkg := b.Pkg
|
||||
tunder := t.Underlying()
|
||||
kind := int(abi.UnderlyingKind(tunder))
|
||||
size := b.sizeof(tunder)
|
||||
numMethods, numPtrMethods := b.abiMethods(t)
|
||||
newNamed := pkg.rtFunc("NewNamed")
|
||||
obj := t.Obj()
|
||||
expr := b.Call(newNamed, b.Str(abi.PathOf(obj.Pkg())), b.Str(abi.NamedName(t)), b.Prog.Val(kind), b.Prog.IntVal(uint64(size), b.Prog.Uintptr()), b.Prog.Val(numMethods), b.Prog.Val(numPtrMethods))
|
||||
return expr
|
||||
}
|
||||
|
||||
func (b Builder) abiNamedInterfaceOf(t *types.Named) func() Expr {
|
||||
obj := t.Obj()
|
||||
fn := b.Pkg.rtFunc("NewNamedInterface")
|
||||
expr := b.Call(fn, b.Str(abi.PathOf(obj.Pkg())), b.Str(obj.Name()))
|
||||
return func() Expr {
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) sizeof(t types.Type) int64 {
|
||||
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
|
||||
return sizes.Sizeof(t)
|
||||
}
|
||||
|
||||
// func InitNamed(ret *Type, pkgPath, name string, underlying *Type, methods, ptrMethods []Method)
|
||||
func (b Builder) abiInitNamed(ret Expr, t *types.Named) func() Expr {
|
||||
under := b.abiType(t.Underlying())
|
||||
return func() Expr {
|
||||
pkg := b.Pkg
|
||||
prog := b.Prog
|
||||
var initNamed = pkg.rtFunc("InitNamed")
|
||||
var tSlice = lastParamType(prog, initNamed)
|
||||
mset := typeutil.IntuitiveMethodSet(t, nil)
|
||||
n := len(mset)
|
||||
var methods, ptrMethods Expr
|
||||
if n == 0 {
|
||||
methods = prog.Zero(tSlice)
|
||||
ptrMethods = methods
|
||||
} else {
|
||||
var mthds []Expr
|
||||
var ptrMthds = make([]Expr, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var mPkg *types.Package
|
||||
var mSig *types.Signature
|
||||
m := mset[i]
|
||||
obj := m.Obj()
|
||||
mName := obj.Name()
|
||||
if token.IsExported(mName) {
|
||||
mPkg = t.Obj().Pkg()
|
||||
mSig = m.Type().(*types.Signature)
|
||||
} else {
|
||||
mPkg = obj.Pkg()
|
||||
mSig = obj.Type().(*types.Signature)
|
||||
}
|
||||
mthd, ptrMthd := b.abiMethodOf(mPkg, mName, mSig)
|
||||
if !mthd.IsNil() {
|
||||
mthds = append(mthds, mthd)
|
||||
}
|
||||
if !ptrMthd.IsNil() {
|
||||
ptrMthds = append(ptrMthds, ptrMthd)
|
||||
}
|
||||
}
|
||||
if len(mthds) > 0 {
|
||||
methods = b.SliceLit(tSlice, mthds...)
|
||||
} else {
|
||||
methods = prog.Zero(tSlice)
|
||||
}
|
||||
if len(ptrMthds) > 0 {
|
||||
ptrMethods = b.SliceLit(tSlice, ptrMthds...)
|
||||
} else {
|
||||
ptrMethods = prog.Zero(tSlice)
|
||||
}
|
||||
}
|
||||
return b.Call(initNamed, ret, under, methods, ptrMethods)
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiPointerOf(t *types.Pointer) func() Expr {
|
||||
elem := b.abiTypeOf(t.Elem())
|
||||
return func() Expr {
|
||||
return b.Call(b.Pkg.rtFunc("PointerTo"), elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiSliceOf(t *types.Slice) func() Expr {
|
||||
elem := b.abiTypeOf(t.Elem())
|
||||
return func() Expr {
|
||||
return b.Call(b.Pkg.rtFunc("SliceOf"), elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiArrayOf(t *types.Array) func() Expr {
|
||||
elem := b.abiTypeOf(t.Elem())
|
||||
return func() Expr {
|
||||
n := b.Prog.IntVal(uint64(t.Len()), b.Prog.Uintptr())
|
||||
return b.Call(b.Pkg.rtFunc("ArrayOf"), n, elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiChanOf(t *types.Chan) func() Expr {
|
||||
elem := b.abiTypeOf(t.Elem())
|
||||
return func() Expr {
|
||||
dir, s := abi.ChanDir(t.Dir())
|
||||
return b.Call(b.Pkg.rtFunc("ChanOf"), b.Prog.IntVal(uint64(dir), b.Prog.Int()), b.Str(s), elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) abiMapOf(t *types.Map) func() Expr {
|
||||
key := b.abiTypeOf(t.Key())
|
||||
elem := b.abiTypeOf(t.Elem())
|
||||
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
|
||||
bucket := b.abiTypeOf(abi.MapBucketType(t, sizes))
|
||||
flags := abi.MapTypeFlags(t, sizes)
|
||||
return func() Expr {
|
||||
return b.Call(b.Pkg.rtFunc("MapOf"), key(), elem(), bucket(), b.Prog.Val(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// func StructField(name string, typ *abi.Type, off uintptr, tag string, embedded bool)
|
||||
// func Struct(pkgPath string, size uintptr, fields []abi.StructField)
|
||||
func (b Builder) abiStructOf(t *types.Struct) func() Expr {
|
||||
n := t.NumFields()
|
||||
typs := make([]func() Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
typs[i] = b.abiTypeOf(f.Type())
|
||||
}
|
||||
return func() Expr {
|
||||
pkg := b.Pkg
|
||||
prog := b.Prog
|
||||
flds := make([]Expr, n)
|
||||
strucAbi := pkg.rtFunc("Struct")
|
||||
sfAbi := pkg.rtFunc("StructField")
|
||||
tStruc := prog.rawType(t)
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
off := uintptr(prog.OffsetOf(tStruc, i))
|
||||
name := b.Str(f.Name())
|
||||
tag := b.Str(t.Tag(i))
|
||||
embedded := prog.Val(f.Embedded())
|
||||
flds[i] = b.Call(sfAbi, name, typs[i](), prog.Val(off), tag, embedded)
|
||||
}
|
||||
pkgPath := b.Str(pkg.Path())
|
||||
tSlice := lastParamType(prog, strucAbi)
|
||||
fldSlice := b.SliceLit(tSlice, flds...)
|
||||
size := prog.IntVal(prog.SizeOf(tStruc), prog.Uintptr())
|
||||
return b.Call(strucAbi, pkgPath, size, fldSlice)
|
||||
}
|
||||
}
|
||||
|
||||
func lastParamType(prog Program, fn Expr) Type {
|
||||
params := fn.raw.Type.(*types.Signature).Params()
|
||||
return prog.rawType(params.At(params.Len() - 1).Type())
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (p Package) patchType(t types.Type) types.Type {
|
||||
switch t := t.(type) {
|
||||
case *types.Pointer:
|
||||
return types.NewPointer(p.patchType(t.Elem()))
|
||||
}
|
||||
return p.patch(t)
|
||||
}
|
||||
|
||||
func (p Package) abiTypeInit(g Global, t types.Type, pub bool) {
|
||||
b := p.afterBuilder()
|
||||
if p.patch != nil {
|
||||
t = p.patchType(t)
|
||||
}
|
||||
tabi := b.abiTypeOf(t)
|
||||
expr := g.Expr
|
||||
var eq Expr
|
||||
var blks []BasicBlock
|
||||
if pub {
|
||||
eq = b.BinOp(token.EQL, b.Load(expr), b.Prog.Nil(expr.Type))
|
||||
blks = b.Func.MakeBlocks(2)
|
||||
b.If(eq, blks[0], blks[1])
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
}
|
||||
vexpr := tabi()
|
||||
prog := p.Prog
|
||||
if kind, _, _ := abi.DataKindOf(t, 0, prog.is32Bits); kind == abi.Pointer {
|
||||
b.InlineCall(b.Pkg.rtFunc("SetDirectIface"), vexpr)
|
||||
}
|
||||
b.Store(expr, vexpr)
|
||||
if pub {
|
||||
b.Jump(blks[1])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
b.blk.last = blks[1].last
|
||||
}
|
||||
|
||||
if t, ok := t.(*types.Named); ok {
|
||||
if iface, ok := t.Underlying().(*types.Interface); ok {
|
||||
tabi = b.abiInitNamedInterface(vexpr, iface)
|
||||
} else {
|
||||
tabi = b.abiInitNamed(vexpr, t)
|
||||
}
|
||||
if pub {
|
||||
blks = b.Func.MakeBlocks(2)
|
||||
b.If(eq, blks[0], blks[1])
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
}
|
||||
tabi()
|
||||
if pub {
|
||||
b.Jump(blks[1])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
b.blk.last = blks[1].last
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// abiType returns the abi type of the specified type.
|
||||
func (b Builder) abiType(t types.Type) Expr {
|
||||
switch t := t.(type) {
|
||||
case *types.Pointer:
|
||||
b.checkAbi(t.Elem())
|
||||
case *types.Array:
|
||||
b.checkAbi(t.Elem())
|
||||
case *types.Map:
|
||||
b.checkAbi(t.Key())
|
||||
b.checkAbi(t.Elem())
|
||||
case *types.Slice:
|
||||
b.checkAbi(t.Elem())
|
||||
case *types.Chan:
|
||||
b.checkAbi(t.Elem())
|
||||
case *types.Struct:
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
b.checkAbi(t.Field(i).Type())
|
||||
}
|
||||
case *types.Interface:
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
b.checkAbi(t.Method(i).Type())
|
||||
}
|
||||
case *types.Signature:
|
||||
for i := 0; i < t.Params().Len(); i++ {
|
||||
b.checkAbi(t.Params().At(i).Type())
|
||||
}
|
||||
for i := 0; i < t.Results().Len(); i++ {
|
||||
b.checkAbi(t.Results().At(i).Type())
|
||||
}
|
||||
}
|
||||
g := b.loadType(t)
|
||||
return b.Load(g.Expr)
|
||||
}
|
||||
|
||||
func (b Builder) checkAbi(t types.Type) {
|
||||
if b.Pkg.chkabi[t] {
|
||||
return
|
||||
}
|
||||
b.abiType(t)
|
||||
}
|
||||
|
||||
func (b Builder) loadType(t types.Type) Global {
|
||||
b.Pkg.chkabi[t] = true
|
||||
pkg := b.Pkg
|
||||
name, pub := pkg.abi.TypeName(t)
|
||||
g := pkg.VarOf(name)
|
||||
if g == nil {
|
||||
prog := b.Prog
|
||||
g = pkg.doNewVar(name, prog.AbiTypePtrPtr())
|
||||
g.InitNil()
|
||||
g.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
|
||||
pkg.abiTypeInit(g, t, pub)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
119
ssa/cl_test.go
Normal file
119
ssa/cl_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* 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_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/cl/cltest"
|
||||
"github.com/goplus/llgo/ssa"
|
||||
"github.com/goplus/llgo/ssa/ssatest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
ssa.SetDebug(ssa.DbgFlagAll)
|
||||
if !testing.Verbose() {
|
||||
log.SetOutput(io.Discard)
|
||||
}
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestFromTestlibgo(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testlibgo")
|
||||
}
|
||||
|
||||
func TestFromTestgo(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testgo")
|
||||
}
|
||||
|
||||
func TestFromTestpy(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testpy")
|
||||
}
|
||||
|
||||
func TestFromTestlibc(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testlibc")
|
||||
}
|
||||
|
||||
func TestFromTestrt(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testrt")
|
||||
}
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testdata")
|
||||
}
|
||||
|
||||
func TestMakeInterface(t *testing.T) {
|
||||
prog := ssatest.NewProgram(t, &ssa.Target{GOARCH: "x86"})
|
||||
pkg := prog.NewPackage("foo", "foo")
|
||||
fn := pkg.NewFunc("main", types.NewSignatureType(nil, nil, nil, nil, nil, false), ssa.InC)
|
||||
b := fn.MakeBody(1)
|
||||
b.MakeInterface(prog.Any(), prog.IntVal(100, prog.Int64()))
|
||||
b.MakeInterface(prog.Any(), prog.FloatVal(100, prog.Float64()))
|
||||
b.DeferData()
|
||||
b.Return()
|
||||
}
|
||||
|
||||
/*
|
||||
func TestCallback(t *testing.T) {
|
||||
ctx := llvm.NewContext()
|
||||
mod := ctx.NewModule("foo/bar")
|
||||
|
||||
tc := llvm.FunctionType(ctx.VoidType(), nil, false)
|
||||
callback := llvm.PointerType(tc, 0)
|
||||
params := []llvm.Type{callback}
|
||||
|
||||
tfn := llvm.FunctionType(ctx.VoidType(), params, false)
|
||||
f := llvm.AddFunction(mod, "fn", tfn)
|
||||
b := ctx.NewBuilder()
|
||||
blk := llvm.AddBasicBlock(f, "")
|
||||
b.SetInsertPointAtEnd(blk)
|
||||
|
||||
arg := f.Param(0)
|
||||
// arg = b.CreateLoad(tc, arg, "")
|
||||
b.CreateCall(tc, arg, nil, "")
|
||||
b.CreateRetVoid()
|
||||
|
||||
expected := `; ModuleID = 'foo/bar'
|
||||
`
|
||||
if v := mod.String(); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestMap(t *testing.T) {
|
||||
var m typeutil.Map
|
||||
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||
m.Set(sig, 1)
|
||||
csig := (*ssa.CFuncPtr)(sig)
|
||||
m.Set(csig, 2)
|
||||
if v := m.At(sig); v.(int) != 1 {
|
||||
t.Fatal("At(sig):", v)
|
||||
}
|
||||
if v := m.At(csig); v.(int) != 2 {
|
||||
t.Fatal("At(csig):", v)
|
||||
}
|
||||
}
|
||||
*/
|
||||
767
ssa/datastruct.go
Normal file
767
ssa/datastruct.go
Normal file
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The FieldAddr instruction yields the address of Field of *struct X.
|
||||
//
|
||||
// The field is identified by its index within the field list of the
|
||||
// struct type of X.
|
||||
//
|
||||
// Dynamically, this instruction panics if X evaluates to a nil
|
||||
// pointer.
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Pointer.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = &t0.name [#1]
|
||||
func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||
}
|
||||
prog := b.Prog
|
||||
tstruc := prog.Elem(x.Type)
|
||||
telem := prog.Field(tstruc, idx)
|
||||
pt := prog.Pointer(telem)
|
||||
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
||||
}
|
||||
|
||||
// The Field instruction yields the value of Field of struct X.
|
||||
func (b Builder) Field(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Field %v, %d\n", x.impl, idx)
|
||||
}
|
||||
return b.getField(x, idx)
|
||||
}
|
||||
|
||||
func (b Builder) getField(x Expr, idx int) Expr {
|
||||
tfld := b.Prog.Field(x.Type, idx)
|
||||
fld := llvm.CreateExtractValue(b.impl, x.impl, idx)
|
||||
return Expr{fld, tfld}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (b Builder) Complex(r, i Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Complex %v, %v\n", r.impl, i.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
var t Type
|
||||
switch kind := r.raw.Type.Underlying().(*types.Basic).Kind(); kind {
|
||||
case types.Float64:
|
||||
t = prog.Complex128()
|
||||
case types.Float32:
|
||||
t = prog.Complex64()
|
||||
}
|
||||
return b.aggregateValue(t, r.impl, i.impl)
|
||||
}
|
||||
|
||||
// MakeString creates a new string from a C string pointer and length.
|
||||
func (b Builder) MakeString(cstr Expr, n ...Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeString %v\n", cstr.impl)
|
||||
}
|
||||
pkg := b.Pkg
|
||||
prog := b.Prog
|
||||
ret.Type = prog.String()
|
||||
if len(n) == 0 {
|
||||
ret.impl = b.Call(pkg.rtFunc("StringFromCStr"), cstr).impl
|
||||
} else {
|
||||
// TODO(xsw): remove Convert
|
||||
ret.impl = b.Call(pkg.rtFunc("StringFrom"), cstr, b.Convert(prog.Int(), n[0])).impl
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// StringData returns the data pointer of a string.
|
||||
func (b Builder) StringData(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("StringData %v\n", x.impl)
|
||||
}
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
|
||||
return Expr{ptr, b.Prog.CStr()}
|
||||
}
|
||||
|
||||
// StringLen returns the length of a string.
|
||||
func (b Builder) StringLen(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("StringLen %v\n", x.impl)
|
||||
}
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
|
||||
return Expr{ptr, b.Prog.Int()}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// SliceData returns the data pointer of a slice.
|
||||
func (b Builder) SliceData(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceData %v\n", x.impl)
|
||||
}
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
|
||||
ty := x.Type.RawType()
|
||||
tySlice := ty.Underlying().(*types.Slice)
|
||||
return Expr{ptr, b.Prog.Pointer(b.Prog.rawType(tySlice.Elem()))}
|
||||
}
|
||||
|
||||
// SliceLen returns the length of a slice.
|
||||
func (b Builder) SliceLen(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceLen %v\n", x.impl)
|
||||
}
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
|
||||
return Expr{ptr, b.Prog.Int()}
|
||||
}
|
||||
|
||||
// SliceCap returns the length of a slice cap.
|
||||
func (b Builder) SliceCap(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("SliceCap %v\n", x.impl)
|
||||
}
|
||||
ptr := llvm.CreateExtractValue(b.impl, x.impl, 2)
|
||||
return Expr{ptr, b.Prog.Int()}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The IndexAddr instruction yields the address of the element at
|
||||
// index `idx` of collection `x`. `idx` is an integer expression.
|
||||
//
|
||||
// The elements of maps and strings are not addressable; use Lookup (map),
|
||||
// Index (string), or MapUpdate instead.
|
||||
//
|
||||
// Dynamically, this instruction panics if `x` evaluates to a nil *array
|
||||
// pointer.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = &t0[t1]
|
||||
func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
telem := prog.Index(x.Type)
|
||||
pt := prog.Pointer(telem)
|
||||
switch t := x.raw.Type.Underlying().(type) {
|
||||
case *types.Slice:
|
||||
ptr := b.SliceData(x)
|
||||
max := b.SliceLen(x)
|
||||
idx = b.checkIndex(idx, max)
|
||||
indices := []llvm.Value{idx.impl}
|
||||
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
|
||||
case *types.Pointer:
|
||||
ar := t.Elem().Underlying().(*types.Array)
|
||||
max := prog.IntVal(uint64(ar.Len()), prog.Int())
|
||||
idx = b.checkIndex(idx, max)
|
||||
}
|
||||
indices := []llvm.Value{idx.impl}
|
||||
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
|
||||
}
|
||||
|
||||
func isConstantInt(x Expr) (v int64, ok bool) {
|
||||
if rv := x.impl.IsAConstantInt(); !rv.IsNil() {
|
||||
v = rv.SExtValue()
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isConstantUint(x Expr) (v uint64, ok bool) {
|
||||
if rv := x.impl.IsAConstantInt(); !rv.IsNil() {
|
||||
v = rv.ZExtValue()
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkRange(idx Expr, max Expr) (checkMin, checkMax bool) {
|
||||
if idx.kind == vkSigned {
|
||||
if v, ok := isConstantInt(idx); ok {
|
||||
if v < 0 {
|
||||
checkMin = true
|
||||
}
|
||||
if m, ok := isConstantInt(max); ok {
|
||||
if v >= m {
|
||||
checkMax = true
|
||||
}
|
||||
} else {
|
||||
checkMax = true
|
||||
}
|
||||
} else {
|
||||
checkMin = true
|
||||
checkMax = true
|
||||
}
|
||||
} else {
|
||||
if v, ok := isConstantUint(idx); ok {
|
||||
if m, ok := isConstantUint(max); ok {
|
||||
if v >= m {
|
||||
checkMax = true
|
||||
}
|
||||
} else {
|
||||
checkMax = true
|
||||
}
|
||||
} else {
|
||||
checkMax = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// check index >= 0 && index < max and size to uint
|
||||
func (b Builder) checkIndex(idx Expr, max Expr) Expr {
|
||||
prog := b.Prog
|
||||
// check range
|
||||
checkMin, checkMax := checkRange(idx, max)
|
||||
// fit size
|
||||
var typ Type
|
||||
if idx.kind == vkSigned {
|
||||
typ = prog.Int()
|
||||
} else {
|
||||
typ = prog.Uint()
|
||||
}
|
||||
if prog.SizeOf(idx.Type) < prog.SizeOf(typ) {
|
||||
idx.Type = typ
|
||||
idx.impl = castUintptr(b, idx.impl, typ)
|
||||
}
|
||||
// check range expr
|
||||
var check Expr
|
||||
if checkMin {
|
||||
zero := llvm.ConstInt(idx.ll, 0, false)
|
||||
check = Expr{llvm.CreateICmp(b.impl, llvm.IntSLT, idx.impl, zero), prog.Bool()}
|
||||
}
|
||||
if checkMax {
|
||||
r := Expr{llvm.CreateICmp(b.impl, llvm.IntSGE, idx.impl, max.impl), prog.Bool()}
|
||||
if check.IsNil() {
|
||||
check = r
|
||||
} else {
|
||||
check = Expr{b.impl.CreateOr(r.impl, check.impl, ""), prog.Bool()}
|
||||
}
|
||||
}
|
||||
if !check.IsNil() {
|
||||
b.InlineCall(b.Pkg.rtFunc("AssertIndexRange"), check)
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
// The Index instruction yields element Index of collection X, an array,
|
||||
// string or type parameter containing an array, a string, a pointer to an,
|
||||
// array or a slice.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = t0[t1]
|
||||
func (b Builder) Index(x, idx Expr, takeAddr func() (addr Expr, zero bool)) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Index %v, %v\n", x.impl, idx.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
var telem Type
|
||||
var ptr Expr
|
||||
var max Expr
|
||||
var zero bool
|
||||
switch t := x.raw.Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if t.Kind() != types.String {
|
||||
panic(fmt.Errorf("invalid operation: cannot index %v", t))
|
||||
}
|
||||
telem = prog.rawType(types.Typ[types.Byte])
|
||||
ptr = b.StringData(x)
|
||||
max = b.StringLen(x)
|
||||
case *types.Array:
|
||||
telem = prog.Index(x.Type)
|
||||
ptr, zero = takeAddr()
|
||||
max = prog.IntVal(uint64(t.Len()), prog.Int())
|
||||
}
|
||||
idx = b.checkIndex(idx, max)
|
||||
if zero {
|
||||
return prog.Zero(telem)
|
||||
}
|
||||
if ptr.IsNil() {
|
||||
if x.impl.IsConstant() {
|
||||
return Expr{llvm.ConstExtractElement(x.impl, idx.impl), telem}
|
||||
}
|
||||
ptr = b.Alloc(x.Type, false)
|
||||
b.impl.CreateStore(x.impl, ptr.impl)
|
||||
}
|
||||
pt := prog.Pointer(telem)
|
||||
indices := []llvm.Value{idx.impl}
|
||||
buf := Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
|
||||
return b.Load(buf)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Slice instruction yields a slice of an existing string, slice
|
||||
// or *array X between optional integer bounds Low and High.
|
||||
//
|
||||
// Dynamically, this instruction panics if X evaluates to a nil *array
|
||||
// pointer.
|
||||
//
|
||||
// Type() returns string if the type of X was string, otherwise a
|
||||
// *types.Slice with the same element type as X.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = slice t0[1:]
|
||||
func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
var nCap Expr
|
||||
var nEltSize Expr
|
||||
var base Expr
|
||||
var lowIsNil = low.IsNil()
|
||||
if lowIsNil {
|
||||
low = prog.IntVal(0, prog.Int())
|
||||
}
|
||||
switch t := x.raw.Type.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if t.Kind() != types.String {
|
||||
panic(fmt.Errorf("invalid operation: cannot slice %v", t))
|
||||
}
|
||||
if high.IsNil() {
|
||||
high = b.StringLen(x)
|
||||
}
|
||||
ret.Type = x.Type
|
||||
ret.impl = b.InlineCall(b.Pkg.rtFunc("StringSlice"), x, low, high).impl
|
||||
return
|
||||
case *types.Slice:
|
||||
nEltSize = SizeOf(prog, prog.Index(x.Type))
|
||||
nCap = b.SliceCap(x)
|
||||
if high.IsNil() {
|
||||
high = b.SliceLen(x)
|
||||
}
|
||||
ret.Type = x.Type
|
||||
base = b.SliceData(x)
|
||||
case *types.Pointer:
|
||||
telem := t.Elem()
|
||||
switch te := telem.Underlying().(type) {
|
||||
case *types.Array:
|
||||
elem := prog.rawType(te.Elem())
|
||||
ret.Type = prog.Slice(elem)
|
||||
nEltSize = SizeOf(prog, elem)
|
||||
nCap = prog.IntVal(uint64(te.Len()), prog.Int())
|
||||
if high.IsNil() {
|
||||
if lowIsNil && max.IsNil() {
|
||||
ret.impl = b.unsafeSlice(x, nCap.impl, nCap.impl).impl
|
||||
return
|
||||
}
|
||||
high = nCap
|
||||
}
|
||||
base = x
|
||||
}
|
||||
}
|
||||
if max.IsNil() {
|
||||
max = nCap
|
||||
}
|
||||
ret.impl = b.InlineCall(b.Pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
|
||||
return
|
||||
}
|
||||
|
||||
// SliceLit creates a new slice with the specified elements.
|
||||
func (b Builder) SliceLit(t Type, elts ...Expr) Expr {
|
||||
prog := b.Prog
|
||||
telem := prog.Index(t)
|
||||
ptr := b.AllocU(telem, int64(len(elts)))
|
||||
for i, elt := range elts {
|
||||
b.Store(b.Advance(ptr, prog.Val(i)), elt)
|
||||
}
|
||||
size := llvm.ConstInt(prog.tyInt(), uint64(len(elts)), false)
|
||||
return b.unsafeSlice(ptr, size, size)
|
||||
}
|
||||
|
||||
// The MakeSlice instruction yields a slice of length Len backed by a
|
||||
// newly allocated array of length Cap.
|
||||
//
|
||||
// Both Len and Cap must be non-nil Values of integer type.
|
||||
//
|
||||
// (Alloc(types.Array) followed by Slice will not suffice because
|
||||
// Alloc can only create arrays of constant length.)
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Slice.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make []string 1:int t0
|
||||
// t1 = make StringSlice 1:int t0
|
||||
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
len = b.fitIntSize(len)
|
||||
cap = b.fitIntSize(cap)
|
||||
telem := prog.Index(t)
|
||||
ret = b.InlineCall(b.Pkg.rtFunc("MakeSlice"), len, cap, prog.IntVal(prog.SizeOf(telem), prog.Int()))
|
||||
ret.Type = t
|
||||
return
|
||||
}
|
||||
|
||||
// fit size to int
|
||||
func (b Builder) fitIntSize(n Expr) Expr {
|
||||
prog := b.Prog
|
||||
typ := prog.Int()
|
||||
if prog.SizeOf(n.Type) != prog.SizeOf(typ) {
|
||||
n.Type = typ
|
||||
n.impl = castInt(b, n.impl, typ)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The MakeMap instruction creates a new hash-table-based map object
|
||||
// and yields a value of kind map.
|
||||
//
|
||||
// t is a (possibly named) *types.Map.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make map[string]int t0
|
||||
// t1 = make StringIntMap t0
|
||||
func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl)
|
||||
}
|
||||
if nReserve.IsNil() {
|
||||
nReserve = b.Prog.Val(0)
|
||||
}
|
||||
typ := b.abiType(t.raw.Type)
|
||||
ret = b.InlineCall(b.Pkg.rtFunc("MakeMap"), typ, nReserve)
|
||||
ret.Type = t
|
||||
return
|
||||
}
|
||||
|
||||
// The Lookup instruction yields element Index of collection map X.
|
||||
// Index is the appropriate key type.
|
||||
//
|
||||
// If CommaOk, the result is a 2-tuple of the value above and a
|
||||
// boolean indicating the result of a map membership test for the key.
|
||||
// The components of the tuple are accessed using Extract.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t2 = t0[t1]
|
||||
// t5 = t3[t4],ok
|
||||
func (b Builder) Lookup(x, key Expr, commaOk bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Lookup %v, %v, %v\n", x.impl, key.impl, commaOk)
|
||||
}
|
||||
prog := b.Prog
|
||||
typ := b.abiType(x.raw.Type)
|
||||
vtyp := prog.Elem(x.Type)
|
||||
ptr := b.mapKeyPtr(key)
|
||||
if commaOk {
|
||||
vals := b.Call(b.Pkg.rtFunc("MapAccess2"), typ, x, ptr)
|
||||
val := b.Load(Expr{b.impl.CreateExtractValue(vals.impl, 0, ""), prog.Pointer(vtyp)})
|
||||
ok := b.impl.CreateExtractValue(vals.impl, 1, "")
|
||||
t := prog.Struct(vtyp, prog.Bool())
|
||||
return b.aggregateValue(t, val.impl, ok)
|
||||
} else {
|
||||
val := b.Call(b.Pkg.rtFunc("MapAccess1"), typ, x, ptr)
|
||||
val.Type = prog.Pointer(vtyp)
|
||||
ret = b.Load(val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// The MapUpdate instruction updates the association of Map[Key] to
|
||||
// Value.
|
||||
//
|
||||
// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
|
||||
// if explicit in the source.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t0[t1] = t2
|
||||
func (b Builder) MapUpdate(m, k, v Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MapUpdate %v[%v] = %v\n", m.impl, k.impl, v.impl)
|
||||
}
|
||||
typ := b.abiType(m.raw.Type)
|
||||
ptr := b.mapKeyPtr(k)
|
||||
ret := b.Call(b.Pkg.rtFunc("MapAssign"), typ, m, ptr)
|
||||
ret.Type = b.Prog.Pointer(v.Type)
|
||||
b.Store(ret, v)
|
||||
}
|
||||
|
||||
// key => unsafe.Pointer
|
||||
func (b Builder) mapKeyPtr(x Expr) Expr {
|
||||
typ := x.Type
|
||||
vtyp := b.Prog.VoidPtr()
|
||||
vptr := b.AllocU(typ)
|
||||
b.Store(vptr, x)
|
||||
return Expr{vptr.impl, vtyp}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Range instruction yields an iterator over the domain and range
|
||||
// of X, which must be a string or map.
|
||||
//
|
||||
// Elements are accessed via Next.
|
||||
//
|
||||
// Type() returns an opaque and degenerate "rangeIter" type.
|
||||
//
|
||||
// Pos() returns the ast.RangeStmt.For.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t0 = range "hello":string
|
||||
func (b Builder) Range(x Expr) Expr {
|
||||
switch x.kind {
|
||||
case vkString:
|
||||
return b.InlineCall(b.Pkg.rtFunc("NewStringIter"), x)
|
||||
case vkMap:
|
||||
typ := b.abiType(x.raw.Type)
|
||||
return b.InlineCall(b.Pkg.rtFunc("NewMapIter"), typ, x)
|
||||
}
|
||||
panic("unsupport range for " + x.raw.Type.String())
|
||||
}
|
||||
|
||||
// The Next instruction reads and advances the (map or string)
|
||||
// iterator Iter and returns a 3-tuple value (ok, k, v). If the
|
||||
// iterator is not exhausted, ok is true and k and v are the next
|
||||
// elements of the domain and range, respectively. Otherwise ok is
|
||||
// false and k and v are undefined.
|
||||
//
|
||||
// Components of the tuple are accessed using Extract.
|
||||
//
|
||||
// The IsString field distinguishes iterators over strings from those
|
||||
// over maps, as the Type() alone is insufficient: consider
|
||||
// map[int]rune.
|
||||
//
|
||||
// Type() returns a *types.Tuple for the triple (ok, k, v).
|
||||
// The types of k and/or v may be types.Invalid.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = next t0
|
||||
func (b Builder) Next(typ Type, iter Expr, isString bool) Expr {
|
||||
if isString {
|
||||
return b.InlineCall(b.Pkg.rtFunc("StringIterNext"), iter)
|
||||
}
|
||||
prog := b.Prog
|
||||
ktyp := prog.Type(typ.raw.Type.Underlying().(*types.Map).Key(), InGo)
|
||||
vtyp := prog.Type(typ.raw.Type.Underlying().(*types.Map).Elem(), InGo)
|
||||
rets := b.InlineCall(b.Pkg.rtFunc("MapIterNext"), iter)
|
||||
ok := b.impl.CreateExtractValue(rets.impl, 0, "")
|
||||
t := prog.Struct(prog.Bool(), ktyp, vtyp)
|
||||
blks := b.Func.MakeBlocks(3)
|
||||
b.If(Expr{ok, prog.Bool()}, blks[0], blks[1])
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
phi := b.Phi(t)
|
||||
phi.AddIncoming(b, blks[:2], func(i int, blk BasicBlock) Expr {
|
||||
b.SetBlockEx(blk, AtEnd, false)
|
||||
if i == 0 {
|
||||
k := b.impl.CreateExtractValue(rets.impl, 1, "")
|
||||
v := b.impl.CreateExtractValue(rets.impl, 2, "")
|
||||
valTrue := aggregateValue(b.impl, t.ll, prog.BoolVal(true).impl,
|
||||
llvm.CreateLoad(b.impl, ktyp.ll, k),
|
||||
llvm.CreateLoad(b.impl, vtyp.ll, v))
|
||||
b.Jump(blks[2])
|
||||
return Expr{valTrue, t}
|
||||
}
|
||||
valFalse := aggregateValue(b.impl, t.ll, prog.BoolVal(false).impl,
|
||||
llvm.ConstNull(ktyp.ll),
|
||||
llvm.ConstNull(vtyp.ll))
|
||||
b.Jump(blks[2])
|
||||
return Expr{valFalse, t}
|
||||
})
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
b.blk.last = blks[2].last
|
||||
return phi.Expr
|
||||
}
|
||||
|
||||
// The MakeChan instruction creates a new channel object and yields a
|
||||
// value of kind chan.
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Chan.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen for the make(chan) that
|
||||
// created it.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t0 = make chan int 0
|
||||
// t0 = make IntChan 0
|
||||
//
|
||||
// type MakeChan struct {
|
||||
// register
|
||||
// Size Value // int; size of buffer; zero => synchronous.
|
||||
// }
|
||||
func (b Builder) MakeChan(t Type, size Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeChan %v, %v\n", t.RawType(), size.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
eltSize := prog.IntVal(prog.SizeOf(prog.Elem(t)), prog.Int())
|
||||
ret.Type = t
|
||||
ret.impl = b.InlineCall(b.Pkg.rtFunc("NewChan"), eltSize, size).impl
|
||||
return
|
||||
}
|
||||
|
||||
// The Send instruction sends X on channel Chan.
|
||||
//
|
||||
// Pos() returns the ast.SendStmt.Arrow, if explicit in the source.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// send t0 <- t1
|
||||
func (b Builder) Send(ch Expr, x Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Send %v, %v\n", ch.impl, x.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
eltSize := prog.IntVal(prog.SizeOf(prog.Elem(ch.Type)), prog.Int())
|
||||
ret = b.InlineCall(b.Pkg.rtFunc("ChanSend"), ch, b.toPtr(x), eltSize)
|
||||
return
|
||||
}
|
||||
|
||||
func (b Builder) toPtr(x Expr) Expr {
|
||||
typ := x.Type
|
||||
vtyp := b.Prog.VoidPtr()
|
||||
vptr := b.Alloc(typ, false)
|
||||
b.Store(vptr, x)
|
||||
return Expr{vptr.impl, vtyp}
|
||||
}
|
||||
|
||||
func (b Builder) Recv(ch Expr, commaOk bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Recv %v, %v\n", ch.impl, commaOk)
|
||||
}
|
||||
prog := b.Prog
|
||||
eltSize := prog.IntVal(prog.SizeOf(prog.Elem(ch.Type)), prog.Int())
|
||||
etyp := prog.Elem(ch.Type)
|
||||
ptr := b.Alloc(etyp, false)
|
||||
ok := b.InlineCall(b.Pkg.rtFunc("ChanRecv"), ch, ptr, eltSize)
|
||||
if commaOk {
|
||||
val := b.Load(ptr)
|
||||
t := prog.Struct(etyp, prog.Bool())
|
||||
return b.aggregateValue(t, val.impl, ok.impl)
|
||||
} else {
|
||||
return b.Load(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
type SelectState struct {
|
||||
Chan Expr // channel to use (for send or receive)
|
||||
Value Expr // value to send (for send)
|
||||
Send bool // direction of case (SendOnly or RecvOnly)
|
||||
}
|
||||
|
||||
// The Select instruction tests whether (or blocks until) one
|
||||
// of the specified sent or received states is entered.
|
||||
//
|
||||
// Let n be the number of States for which Dir==RECV and T_i (0<=i<n)
|
||||
// be the element type of each such state's Chan.
|
||||
// Select returns an n+2-tuple
|
||||
//
|
||||
// (index int, recvOk bool, r_0 T_0, ... r_n-1 T_n-1)
|
||||
//
|
||||
// The tuple's components, described below, must be accessed via the
|
||||
// Extract instruction.
|
||||
//
|
||||
// If Blocking, select waits until exactly one state holds, i.e. a
|
||||
// channel becomes ready for the designated operation of sending or
|
||||
// receiving; select chooses one among the ready states
|
||||
// pseudorandomly, performs the send or receive operation, and sets
|
||||
// 'index' to the index of the chosen channel.
|
||||
//
|
||||
// If !Blocking, select doesn't block if no states hold; instead it
|
||||
// returns immediately with index equal to -1.
|
||||
//
|
||||
// If the chosen channel was used for a receive, the r_i component is
|
||||
// set to the received value, where i is the index of that state among
|
||||
// all n receive states; otherwise r_i has the zero value of type T_i.
|
||||
// Note that the receive index i is not the same as the state
|
||||
// index index.
|
||||
//
|
||||
// The second component of the triple, recvOk, is a boolean whose value
|
||||
// is true iff the selected operation was a receive and the receive
|
||||
// successfully yielded a value.
|
||||
//
|
||||
// Pos() returns the ast.SelectStmt.Select.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t3 = select nonblocking [<-t0, t1<-t2]
|
||||
// t4 = select blocking []
|
||||
func (b Builder) Select(states []*SelectState, blocking bool) (ret Expr) {
|
||||
ops := make([]Expr, len(states))
|
||||
for i, s := range states {
|
||||
ops[i] = b.chanOp(s)
|
||||
}
|
||||
var fn Expr
|
||||
if blocking {
|
||||
fn = b.Pkg.rtFunc("Select")
|
||||
} else {
|
||||
fn = b.Pkg.rtFunc("TrySelect")
|
||||
}
|
||||
prog := b.Prog
|
||||
tSlice := lastParamType(prog, fn)
|
||||
slice := b.SliceLit(tSlice, ops...)
|
||||
ret = b.Call(fn, slice)
|
||||
chosen := b.impl.CreateExtractValue(ret.impl, 0, "")
|
||||
recvOK := b.impl.CreateExtractValue(ret.impl, 1, "")
|
||||
if !blocking {
|
||||
chosen = llvm.CreateSelect(b.impl, recvOK, chosen, prog.Val(-1).impl)
|
||||
}
|
||||
results := []llvm.Value{chosen, recvOK}
|
||||
typs := []Type{prog.Int(), prog.Bool()}
|
||||
for i, s := range states {
|
||||
if !s.Send {
|
||||
etyp := b.Prog.Elem(s.Chan.Type)
|
||||
typs = append(typs, etyp)
|
||||
r := b.Load(Expr{b.impl.CreateExtractValue(ops[i].impl, 1, ""), prog.Pointer(etyp)})
|
||||
results = append(results, r.impl)
|
||||
}
|
||||
}
|
||||
return b.aggregateValue(b.Prog.Struct(typs...), results...)
|
||||
}
|
||||
|
||||
func (b Builder) chanOp(s *SelectState) Expr {
|
||||
prog := b.Prog
|
||||
var val Expr
|
||||
var size Expr
|
||||
if s.Send {
|
||||
val = b.toPtr(s.Value)
|
||||
size = prog.IntVal(prog.SizeOf(s.Value.Type), prog.Int32())
|
||||
} else {
|
||||
etyp := prog.Elem(s.Chan.Type)
|
||||
val = b.Alloc(etyp, false)
|
||||
size = prog.IntVal(prog.SizeOf(etyp), prog.Int32())
|
||||
}
|
||||
send := prog.BoolVal(s.Send)
|
||||
typ := b.Prog.rtType("ChanOp")
|
||||
return b.aggregateValue(typ, s.Chan.impl, val.impl, size.impl, send.impl)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
370
ssa/decl.go
Normal file
370
ssa/decl.go
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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"
|
||||
"strconv"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
NameValist = "__llgo_va_list"
|
||||
)
|
||||
|
||||
func VArg() *types.Var {
|
||||
return types.NewParam(0, nil, NameValist, types.NewSlice(tyAny))
|
||||
}
|
||||
|
||||
func hasNameValist(sig *types.Signature) bool {
|
||||
if sig.Variadic() {
|
||||
if params := sig.Params(); params.At(params.Len()-1).Name() == NameValist {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// NewVarEx creates a new global variable.
|
||||
func (p Package) NewVarEx(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 {
|
||||
typ := p.Prog.Elem(t).ll
|
||||
gbl := llvm.AddGlobal(p.mod, typ, name)
|
||||
alignment := p.Prog.td.ABITypeAlignment(typ)
|
||||
gbl.SetAlignment(alignment)
|
||||
ret := &aGlobal{Expr{gbl, t}}
|
||||
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) {
|
||||
g.impl.SetInitializer(v.impl)
|
||||
}
|
||||
|
||||
func (g Global) InitNil() {
|
||||
g.impl.SetInitializer(llvm.ConstNull(g.impl.GlobalValueType()))
|
||||
}
|
||||
|
||||
func (g Global) ReplaceAllUsesWith(v Expr) {
|
||||
g.impl.ReplaceAllUsesWith(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
|
||||
|
||||
defer_ *aDefer
|
||||
recov BasicBlock
|
||||
|
||||
params []Type
|
||||
freeVars Expr
|
||||
base int // base = 1 if hasFreeVars; base = 0 otherwise
|
||||
hasVArg bool
|
||||
|
||||
diFunc DIFunction
|
||||
}
|
||||
|
||||
// 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, false)
|
||||
}
|
||||
|
||||
// NewFuncEx creates a new function.
|
||||
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool, instantiated 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)
|
||||
if instantiated {
|
||||
fn.SetLinkage(llvm.LinkOnceAnyLinkage)
|
||||
}
|
||||
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: Expr{fn, t},
|
||||
Pkg: pkg,
|
||||
Prog: prog,
|
||||
params: params,
|
||||
base: base,
|
||||
hasVArg: 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 = hasNameValist(sig); 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]}
|
||||
if b.blk.Index() != 0 {
|
||||
blk := b.impl.GetInsertBlock()
|
||||
b.SetBlockEx(p.blks[0], AtStart, false)
|
||||
p.freeVars = b.Load(ptr)
|
||||
b.impl.SetInsertPointAtEnd(blk)
|
||||
} else {
|
||||
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, nil, p, p.Pkg, prog,
|
||||
make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)}
|
||||
}
|
||||
|
||||
// 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.blk = p.blks[0]
|
||||
b.impl.SetInsertPointAtEnd(b.blk.last)
|
||||
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, 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]
|
||||
}
|
||||
|
||||
// SetRecover sets the recover block for the function.
|
||||
func (p Function) SetRecover(blk BasicBlock) {
|
||||
p.recov = blk
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type inlineAttr int
|
||||
|
||||
const (
|
||||
NoInline inlineAttr = iota
|
||||
AlwaysInline
|
||||
InlineHint
|
||||
)
|
||||
|
||||
func (p Function) Inline(inline inlineAttr) {
|
||||
inlineAttrName := map[inlineAttr]string{
|
||||
NoInline: "noinline",
|
||||
AlwaysInline: "alwaysinline",
|
||||
InlineHint: "inlinehint",
|
||||
}[inline]
|
||||
inlineAttr := p.Pkg.mod.Context().CreateEnumAttribute(llvm.AttributeKindID(inlineAttrName), 0)
|
||||
p.impl.AddFunctionAttr(inlineAttr)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
791
ssa/di.go
Normal file
791
ssa/di.go
Normal file
@@ -0,0 +1,791 @@
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
type Positioner interface {
|
||||
Position(pos token.Pos) token.Position
|
||||
}
|
||||
|
||||
type aDIBuilder struct {
|
||||
di *llvm.DIBuilder
|
||||
prog Program
|
||||
types map[Type]DIType
|
||||
positioner Positioner
|
||||
m llvm.Module // Add this field
|
||||
}
|
||||
|
||||
type diBuilder = *aDIBuilder
|
||||
|
||||
func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder {
|
||||
m := pkg.mod
|
||||
ctx := m.Context()
|
||||
|
||||
b := &aDIBuilder{
|
||||
di: llvm.NewDIBuilder(m),
|
||||
prog: prog,
|
||||
types: make(map[*aType]DIType),
|
||||
positioner: positioner,
|
||||
m: m, // Initialize the m field
|
||||
}
|
||||
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 2, "Debug Info Version", 3)
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 7, "Dwarf Version", 4)
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 1, "wchar_size", 4)
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 8, "PIC Level", 2)
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 7, "uwtable", 1)
|
||||
b.addNamedMetadataOperand("llvm.module.flags", 7, "frame-pointer", 1)
|
||||
|
||||
// Add llvm.ident metadata
|
||||
identNode := ctx.MDNode([]llvm.Metadata{
|
||||
ctx.MDString("LLGo Compiler"),
|
||||
})
|
||||
m.AddNamedMetadataOperand("llvm.ident", identNode)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// New method to add named metadata operand
|
||||
func (b diBuilder) addNamedMetadataOperand(name string, intValue int, stringValue string, intValue2 int) {
|
||||
ctx := b.m.Context()
|
||||
b.m.AddNamedMetadataOperand(name,
|
||||
ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(intValue), false).ConstantAsMetadata(),
|
||||
ctx.MDString(stringValue),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(intValue2), false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aCompilationUnit struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type CompilationUnit = *aCompilationUnit
|
||||
|
||||
func (c CompilationUnit) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
|
||||
return &aDIScopeMeta{c.ll}
|
||||
}
|
||||
|
||||
var DWARF_LANG_C llvm.DwarfLang = 0x2
|
||||
var DWARF_LANG_GO llvm.DwarfLang = 0x16
|
||||
|
||||
func (b diBuilder) createCompileUnit(filename, dir string) CompilationUnit {
|
||||
return &aCompilationUnit{ll: b.di.CreateCompileUnit(llvm.DICompileUnit{
|
||||
// TODO(lijie): use C language for now, change after Go plugin of LLDB is ready
|
||||
Language: DWARF_LANG_C - 1,
|
||||
File: filename,
|
||||
Dir: dir,
|
||||
Producer: "LLGo",
|
||||
Optimized: true,
|
||||
RuntimeVersion: 1,
|
||||
})}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIScopeMeta struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIScopeMeta = *aDIScopeMeta
|
||||
|
||||
type DIScope interface {
|
||||
scopeMeta(b diBuilder, pos token.Position) DIScopeMeta
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIFile struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIFile = *aDIFile
|
||||
|
||||
func (b diBuilder) createFile(filename string) DIFile {
|
||||
dir, file := filepath.Split(filename)
|
||||
return &aDIFile{ll: b.di.CreateFile(file, dir)}
|
||||
}
|
||||
|
||||
func (f DIFile) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
|
||||
return &aDIScopeMeta{b.file(pos.Filename).ll}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIType struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIType = *aDIType
|
||||
|
||||
func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
|
||||
var typ llvm.Metadata
|
||||
switch t := ty.RawType().(type) {
|
||||
case *types.Basic:
|
||||
if t.Kind() == types.UnsafePointer {
|
||||
typ = b.di.CreatePointerType(llvm.DIPointerType{
|
||||
Name: name,
|
||||
SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(t) * 8),
|
||||
})
|
||||
return &aDIType{typ}
|
||||
}
|
||||
|
||||
var encoding llvm.DwarfTypeEncoding
|
||||
if t.Info()&types.IsBoolean != 0 {
|
||||
encoding = llvm.DW_ATE_boolean
|
||||
} else if t.Info()&types.IsUnsigned != 0 {
|
||||
encoding = llvm.DW_ATE_unsigned
|
||||
} else if t.Info()&types.IsInteger != 0 {
|
||||
encoding = llvm.DW_ATE_signed
|
||||
} else if t.Info()&types.IsFloat != 0 {
|
||||
encoding = llvm.DW_ATE_float
|
||||
} else if t.Info()&types.IsComplex != 0 {
|
||||
return b.createComplexType(ty)
|
||||
} else if t.Info()&types.IsString != 0 {
|
||||
return b.createStringType()
|
||||
} else {
|
||||
panic(fmt.Errorf("can't create debug info of basic type: %v, %T", ty.RawType(), ty.RawType()))
|
||||
}
|
||||
|
||||
typ = b.di.CreateBasicType(llvm.DIBasicType{
|
||||
Name: name,
|
||||
SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8,
|
||||
Encoding: encoding,
|
||||
})
|
||||
case *types.Pointer:
|
||||
return b.createPointerType(name, b.prog.rawType(t.Elem()), pos)
|
||||
case *types.Named:
|
||||
// Create typedef type for named types
|
||||
return b.createTypedefType(name, ty, pos)
|
||||
case *types.Interface:
|
||||
ty := b.prog.rtType("Iface")
|
||||
return b.createInterfaceType(name, ty)
|
||||
case *types.Slice:
|
||||
ty := b.prog.rtType("Slice")
|
||||
tyElem := b.prog.rawType(t.Elem())
|
||||
return b.createSliceType(name, ty, tyElem)
|
||||
case *types.Struct:
|
||||
return b.createStructType(name, ty, pos)
|
||||
case *types.Signature:
|
||||
tyFn := b.prog.Closure(t)
|
||||
return b.createFuncPtrType(name, tyFn, pos)
|
||||
case *types.Array:
|
||||
return b.createArrayType(ty, t.Len())
|
||||
case *types.Chan:
|
||||
return b.createChanType(name, ty, pos)
|
||||
case *types.Map:
|
||||
ty := b.prog.rtType("Map")
|
||||
return b.createMapType(name, ty, pos)
|
||||
case *types.Tuple:
|
||||
return b.createTupleType(name, ty, pos)
|
||||
default:
|
||||
panic(fmt.Errorf("can't create debug info of type: %v, %T", ty.RawType(), ty.RawType()))
|
||||
}
|
||||
return &aDIType{typ}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIFunction struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIFunction = *aDIFunction
|
||||
|
||||
func (p Function) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
|
||||
return &aDIScopeMeta{p.diFunc.ll}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIGlobalVariableExpression struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIGlobalVariableExpression = *aDIGlobalVariableExpression
|
||||
|
||||
func (b diBuilder) createGlobalVariableExpression(scope DIScope, pos token.Position, name, linkageName string, ty Type, isLocalToUnit bool) DIGlobalVariableExpression {
|
||||
return &aDIGlobalVariableExpression{
|
||||
ll: b.di.CreateGlobalVariableExpression(
|
||||
scope.scopeMeta(b, pos).ll,
|
||||
llvm.DIGlobalVariableExpression{
|
||||
Name: name,
|
||||
LinkageName: linkageName,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
Type: b.diType(ty, pos).ll,
|
||||
LocalToUnit: isLocalToUnit,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType()) * 8),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDILexicalBlock struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DILexicalBlock = *aDILexicalBlock
|
||||
|
||||
func (l *aDILexicalBlock) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
|
||||
return &aDIScopeMeta{l.ll}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIVar struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIVar = *aDIVar
|
||||
|
||||
func (b diBuilder) createParameterVariable(scope DIScope, pos token.Position, name string, argNo int, ty DIType) DIVar {
|
||||
return &aDIVar{
|
||||
ll: b.di.CreateParameterVariable(
|
||||
scope.scopeMeta(b, pos).ll,
|
||||
llvm.DIParameterVariable{
|
||||
Name: name,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
ArgNo: argNo,
|
||||
Type: ty.ll,
|
||||
AlwaysPreserve: true,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (b diBuilder) createAutoVariable(scope DIScope, pos token.Position, name string, ty DIType) DIVar {
|
||||
return &aDIVar{
|
||||
ll: b.di.CreateAutoVariable(
|
||||
scope.scopeMeta(b, pos).ll,
|
||||
llvm.DIAutoVariable{
|
||||
Name: name,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
Type: ty.ll,
|
||||
AlwaysPreserve: true,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (b diBuilder) createTypedefType(name string, ty Type, pos token.Position) DIType {
|
||||
underlyingType := b.diType(b.prog.rawType(ty.RawType().(*types.Named).Underlying()), pos)
|
||||
typ := b.di.CreateTypedef(llvm.DITypedef{
|
||||
Name: name,
|
||||
Type: underlyingType.ll,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType()) * 8),
|
||||
})
|
||||
return &aDIType{typ}
|
||||
}
|
||||
|
||||
func (b diBuilder) createStringType() DIType {
|
||||
ty := b.prog.rtType("String")
|
||||
return b.doCreateStructType("string", ty, token.Position{}, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{
|
||||
b.createMemberType("data", ty, b.prog.CStr(), 0),
|
||||
b.createMemberType("len", ty, b.prog.Uint(), 1),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createArrayType(ty Type, l int64) DIType {
|
||||
tyElem := b.prog.rawType(ty.RawType().(*types.Array).Elem())
|
||||
return &aDIType{ll: b.di.CreateArrayType(llvm.DIArrayType{
|
||||
SizeInBits: b.prog.SizeOf(ty) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType()) * 8),
|
||||
ElementType: b.diType(tyElem, token.Position{}).ll,
|
||||
Subscripts: []llvm.DISubrange{{
|
||||
Count: l,
|
||||
}},
|
||||
})}
|
||||
}
|
||||
|
||||
func (b diBuilder) createSliceType(name string, ty, tyElem Type) DIType {
|
||||
pos := token.Position{}
|
||||
diElemTyPtr := b.prog.Pointer(tyElem)
|
||||
|
||||
return b.doCreateStructType(name, ty, pos, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{
|
||||
b.createMemberTypeEx("data", ty, diElemTyPtr, 0, pos, 0),
|
||||
b.createMemberTypeEx("len", ty, b.prog.Uint(), 1, pos, 0),
|
||||
b.createMemberTypeEx("cap", ty, b.prog.Uint(), 2, pos, 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createInterfaceType(name string, ty Type) DIType {
|
||||
tyRaw := ty.RawType().Underlying()
|
||||
tyIntr := b.prog.rawType(tyRaw)
|
||||
tyType := b.prog.VoidPtr()
|
||||
tyData := b.prog.VoidPtr()
|
||||
|
||||
return b.doCreateStructType(name, tyIntr, token.Position{}, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{
|
||||
b.createMemberType("type", ty, tyType, 0),
|
||||
b.createMemberType("data", ty, tyData, 1),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createMemberType(name string, tyStruct, tyField Type, idxField int) llvm.Metadata {
|
||||
return b.createMemberTypeEx(name, tyStruct, tyField, idxField, token.Position{}, 0)
|
||||
}
|
||||
|
||||
func (b diBuilder) createMemberTypeEx(name string, tyStruct, tyField Type, idxField int, pos token.Position, flags int) llvm.Metadata {
|
||||
return b.di.CreateMemberType(
|
||||
b.diType(tyStruct, pos).ll,
|
||||
llvm.DIMemberType{
|
||||
Name: name,
|
||||
SizeInBits: b.prog.SizeOf(tyField) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(tyField.RawType()) * 8),
|
||||
OffsetInBits: b.prog.OffsetOf(tyStruct, idxField) * 8,
|
||||
Type: b.diType(tyField, pos).ll,
|
||||
Flags: flags,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (b diBuilder) createMapType(name string, tyMap Type, pos token.Position) DIType {
|
||||
// ty := tyMap.RawType().(*types.Map)
|
||||
// tk := b.prog.rawType(ty.Key())
|
||||
// tv := b.prog.rawType(ty.Elem())
|
||||
tyCount := b.prog.Int()
|
||||
return b.doCreateStructType(name, tyMap, pos, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{
|
||||
b.createMemberType("count", tyMap, tyCount, 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createChanType(name string, t Type, pos token.Position) DIType {
|
||||
return b.doCreateStructType(name, t, pos, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createComplexType(t Type) DIType {
|
||||
var tfield Type
|
||||
var tyName string
|
||||
if t.RawType().(*types.Basic).Kind() == types.Complex128 {
|
||||
tfield = b.prog.Float64()
|
||||
tyName = "complex128"
|
||||
} else {
|
||||
tfield = b.prog.Float32()
|
||||
tyName = "complex64"
|
||||
}
|
||||
return b.doCreateStructType(tyName, t, token.Position{}, func(ditStruct DIType) []llvm.Metadata {
|
||||
return []llvm.Metadata{
|
||||
b.createMemberType("real", t, tfield, 0),
|
||||
b.createMemberType("imag", t, tfield, 1),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createPointerType(name string, ty Type, pos token.Position) DIType {
|
||||
ptrType := b.prog.VoidPtr()
|
||||
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
|
||||
Name: name,
|
||||
Pointee: b.diType(ty, pos).ll,
|
||||
SizeInBits: b.prog.SizeOf(ptrType) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(ptrType.RawType())) * 8,
|
||||
})}
|
||||
}
|
||||
|
||||
func (b diBuilder) doCreateStructType(name string, ty Type, pos token.Position, fn func(ty DIType) []llvm.Metadata) (ret DIType) {
|
||||
structType := ty.RawType().Underlying()
|
||||
|
||||
scope := b.file(pos.Filename)
|
||||
ret = &aDIType{b.di.CreateReplaceableCompositeType(
|
||||
scope.ll,
|
||||
llvm.DIReplaceableCompositeType{
|
||||
Tag: dwarf.TagStructType,
|
||||
Name: name,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
SizeInBits: b.prog.SizeOf(ty) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(structType) * 8),
|
||||
},
|
||||
)}
|
||||
b.types[ty] = ret
|
||||
|
||||
fields := fn(ret)
|
||||
|
||||
st := b.di.CreateStructType(
|
||||
scope.ll,
|
||||
llvm.DIStructType{
|
||||
Name: name,
|
||||
File: b.file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
SizeInBits: b.prog.SizeOf(ty) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(structType) * 8),
|
||||
Elements: fields,
|
||||
},
|
||||
)
|
||||
ret.ll.ReplaceAllUsesWith(st)
|
||||
ret.ll = st
|
||||
return
|
||||
}
|
||||
|
||||
func (b diBuilder) createStructType(name string, ty Type, pos token.Position) (ret DIType) {
|
||||
structType := ty.RawType().(*types.Struct)
|
||||
return b.doCreateStructType(name, ty, pos, func(ditStruct DIType) []llvm.Metadata {
|
||||
fields := make([]llvm.Metadata, structType.NumFields())
|
||||
for i := 0; i < structType.NumFields(); i++ {
|
||||
field := structType.Field(i)
|
||||
tyField := b.prog.rawType(field.Type())
|
||||
flags := 0
|
||||
pos := b.positioner.Position(field.Pos())
|
||||
fields[i] = b.createMemberTypeEx(field.Name(), ty, tyField, i, pos, flags)
|
||||
}
|
||||
return fields
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createTupleType(name string, ty Type, pos token.Position) DIType {
|
||||
tupleType := ty.RawType().(*types.Tuple)
|
||||
if tupleType.Len() == 0 {
|
||||
return &aDIType{}
|
||||
}
|
||||
if tupleType.Len() == 1 {
|
||||
t := b.prog.rawType(tupleType.At(0).Type())
|
||||
return b.diType(t, pos)
|
||||
}
|
||||
return b.doCreateStructType(name, ty, pos, func(ditStruct DIType) []llvm.Metadata {
|
||||
fields := make([]llvm.Metadata, ty.RawType().(*types.Tuple).Len())
|
||||
for i := 0; i < ty.RawType().(*types.Tuple).Len(); i++ {
|
||||
field := ty.RawType().(*types.Tuple).At(i)
|
||||
tyField := b.prog.rawType(field.Type())
|
||||
fields[i] = b.createMemberTypeEx(field.Name(), ty, tyField, i, pos, 0)
|
||||
}
|
||||
return fields
|
||||
})
|
||||
}
|
||||
|
||||
func (b diBuilder) createFuncPtrType(name string, ty Type, pos token.Position) DIType {
|
||||
ptr := b.prog.VoidPtr()
|
||||
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
|
||||
Name: name,
|
||||
Pointee: b.diType(ptr, pos).ll,
|
||||
SizeInBits: b.prog.SizeOf(ptr) * 8,
|
||||
AlignInBits: uint32(b.prog.sizes.Alignof(ptr.RawType()) * 8),
|
||||
})}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func (b diBuilder) dbgDeclare(v Expr, dv DIVar, scope DIScope, pos token.Position, expr DIExpression, blk BasicBlock) {
|
||||
loc := llvm.DebugLoc{
|
||||
Line: uint(pos.Line),
|
||||
Col: uint(pos.Column),
|
||||
Scope: scope.scopeMeta(b, pos).ll,
|
||||
}
|
||||
b.di.InsertDeclareAtEnd(
|
||||
v.impl,
|
||||
dv.ll,
|
||||
expr.ll,
|
||||
loc,
|
||||
blk.last,
|
||||
)
|
||||
}
|
||||
|
||||
func (b diBuilder) dbgValue(v Expr, dv DIVar, scope DIScope, pos token.Position, expr DIExpression, blk BasicBlock) {
|
||||
loc := llvm.DebugLoc{
|
||||
Line: uint(pos.Line),
|
||||
Col: uint(pos.Column),
|
||||
Scope: scope.scopeMeta(b, pos).ll,
|
||||
}
|
||||
b.di.InsertValueAtEnd(
|
||||
v.impl,
|
||||
dv.ll,
|
||||
expr.ll,
|
||||
loc,
|
||||
blk.last,
|
||||
)
|
||||
}
|
||||
|
||||
func (b diBuilder) diType(t Type, pos token.Position) DIType {
|
||||
name := t.RawType().String()
|
||||
return b.diTypeEx(name, t, pos)
|
||||
}
|
||||
|
||||
func (b diBuilder) diTypeEx(name string, t Type, pos token.Position) DIType {
|
||||
if ty, ok := b.types[t]; ok {
|
||||
return ty
|
||||
}
|
||||
ty := b.createType(name, t, pos)
|
||||
b.types[t] = ty
|
||||
return ty
|
||||
}
|
||||
|
||||
func (b diBuilder) varParam(scope DIScope, pos token.Position, varName string, vt DIType, argNo int) DIVar {
|
||||
return b.createParameterVariable(
|
||||
scope,
|
||||
pos,
|
||||
varName,
|
||||
argNo,
|
||||
vt,
|
||||
)
|
||||
}
|
||||
|
||||
func (b diBuilder) varAuto(scope DIScope, pos token.Position, varName string, vt DIType) DIVar {
|
||||
return b.createAutoVariable(scope, pos, varName, vt)
|
||||
}
|
||||
|
||||
func (b diBuilder) file(filename string) DIFile {
|
||||
return b.createFile(filename)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type aDIExpression struct {
|
||||
ll llvm.Metadata
|
||||
}
|
||||
|
||||
type DIExpression = *aDIExpression
|
||||
|
||||
func (b diBuilder) createExpression(ops []uint64) DIExpression {
|
||||
return &aDIExpression{b.di.CreateExpression(ops)}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Copy to alloca'd memory to get declareable address.
|
||||
func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, exists bool) {
|
||||
if v, ok := b.dbgVars[v]; ok {
|
||||
return v.ptr, v.val, true
|
||||
}
|
||||
t := v.Type.RawType().Underlying()
|
||||
dbgPtr, dbgVal = b.doConstructDebugAddr(v, t)
|
||||
dbgExpr := dbgExpr{dbgPtr, dbgVal}
|
||||
b.dbgVars[v] = dbgExpr
|
||||
b.dbgVars[dbgVal] = dbgExpr
|
||||
return dbgPtr, dbgVal, false
|
||||
}
|
||||
|
||||
func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr) {
|
||||
var ty Type
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
if t.Info()&types.IsComplex != 0 {
|
||||
if t.Kind() == types.Complex128 {
|
||||
ty = b.Prog.Complex128()
|
||||
} else {
|
||||
ty = b.Prog.Complex64()
|
||||
}
|
||||
} else if t.Info()&types.IsString != 0 {
|
||||
ty = b.Prog.rtType("String")
|
||||
} else {
|
||||
ty = v.Type
|
||||
}
|
||||
case *types.Struct:
|
||||
ty = v.Type
|
||||
case *types.Slice:
|
||||
ty = b.Prog.Type(b.Prog.rtType("Slice").RawType().Underlying(), InGo)
|
||||
case *types.Signature:
|
||||
ty = b.Prog.Closure(t)
|
||||
case *types.Named:
|
||||
ty = b.Prog.Type(t.Underlying(), InGo)
|
||||
case *types.Map:
|
||||
ty = b.Prog.Type(b.Prog.rtType("Map").RawType().Underlying(), InGo)
|
||||
default:
|
||||
ty = v.Type
|
||||
}
|
||||
dbgPtr = b.AllocaT(ty)
|
||||
dbgPtr.Type = b.Prog.Pointer(v.Type)
|
||||
b.Store(dbgPtr, v)
|
||||
dbgVal = b.Load(dbgPtr)
|
||||
return dbgPtr, dbgVal
|
||||
}
|
||||
|
||||
func (b Builder) di() diBuilder {
|
||||
return b.Pkg.di
|
||||
}
|
||||
|
||||
func (b Builder) DIParam(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
|
||||
b.DIValue(variable, v, dv, scope, pos, blk)
|
||||
}
|
||||
|
||||
func (b Builder) DIDeclare(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
|
||||
expr := b.di().createExpression(nil)
|
||||
b.di().dbgDeclare(v, dv, scope, pos, expr, blk)
|
||||
}
|
||||
|
||||
func (b Builder) DIValue(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
|
||||
ty := v.Type.RawType().Underlying()
|
||||
if !needConstructAddr(ty) {
|
||||
expr := b.di().createExpression(nil)
|
||||
b.di().dbgValue(v, dv, scope, pos, expr, blk)
|
||||
} else {
|
||||
dbgPtr, _, _ := b.constructDebugAddr(v)
|
||||
expr := b.di().createExpression([]uint64{opDeref})
|
||||
b.di().dbgValue(dbgPtr, dv, scope, pos, expr, blk)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
opDeref = 0x06
|
||||
)
|
||||
|
||||
func needConstructAddr(t types.Type) bool {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
if t.Info()&types.IsComplex != 0 {
|
||||
return true
|
||||
} else if t.Info()&types.IsString != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case *types.Pointer:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (b Builder) DIVarParam(scope DIScope, pos token.Position, varName string, vt Type, argNo int) DIVar {
|
||||
t := b.di().diType(vt, pos)
|
||||
return b.di().varParam(scope, pos, varName, t, argNo)
|
||||
}
|
||||
|
||||
func (b Builder) DIVarAuto(scope DIScope, pos token.Position, varName string, vt Type) DIVar {
|
||||
t := b.di().diType(vt, pos)
|
||||
return b.di().varAuto(scope, pos, varName, t)
|
||||
}
|
||||
|
||||
// hack for types.Scope
|
||||
type hackScope struct {
|
||||
parent *types.Scope
|
||||
children []*types.Scope
|
||||
number int // parent.children[number-1] is this scope; 0 if there is no parent
|
||||
elems map[string]types.Object // lazily allocated
|
||||
pos, end token.Pos // scope extent; may be invalid
|
||||
comment string // for debugging only
|
||||
isFunc bool // set if this is a function scope (internal use only)
|
||||
}
|
||||
|
||||
func isFunc(scope *types.Scope) bool {
|
||||
hs := (*hackScope)(unsafe.Pointer(scope))
|
||||
return hs.isFunc
|
||||
}
|
||||
|
||||
func (b Builder) DIScope(f Function, scope *types.Scope) DIScope {
|
||||
if cachedScope, ok := b.diScopeCache[scope]; ok {
|
||||
return cachedScope
|
||||
}
|
||||
pos := b.di().positioner.Position(scope.Pos())
|
||||
// skip package and universe scope
|
||||
// if scope.Parent().Parent() == nil {
|
||||
// return b.di().file(pos.Filename)
|
||||
// }
|
||||
|
||||
var result DIScope
|
||||
if isFunc(scope) {
|
||||
// TODO(lijie): should check scope == function scope
|
||||
result = f
|
||||
} else {
|
||||
parentScope := b.DIScope(f, scope.Parent())
|
||||
result = &aDILexicalBlock{b.di().di.CreateLexicalBlock(parentScope.scopeMeta(b.di(), pos).ll, llvm.DILexicalBlock{
|
||||
File: b.di().file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
Column: pos.Column,
|
||||
})}
|
||||
}
|
||||
|
||||
b.diScopeCache[scope] = result
|
||||
return result
|
||||
}
|
||||
|
||||
const (
|
||||
MD_dbg = 0
|
||||
)
|
||||
|
||||
func (b Builder) DIGlobal(v Expr, name string, pos token.Position) {
|
||||
if _, ok := b.Pkg.glbDbgVars[v]; ok {
|
||||
return
|
||||
}
|
||||
gv := b.di().createGlobalVariableExpression(
|
||||
b.Pkg.cu,
|
||||
pos,
|
||||
name,
|
||||
name,
|
||||
v.Type,
|
||||
false,
|
||||
)
|
||||
v.impl.AddMetadata(MD_dbg, gv.ll)
|
||||
b.Pkg.glbDbgVars[v] = true
|
||||
}
|
||||
|
||||
func (b Builder) DISetCurrentDebugLocation(diScope DIScope, pos token.Position) {
|
||||
b.impl.SetCurrentDebugLocation(
|
||||
uint(pos.Line),
|
||||
uint(pos.Column),
|
||||
diScope.scopeMeta(b.di(), pos).ll,
|
||||
llvm.Metadata{},
|
||||
)
|
||||
}
|
||||
|
||||
func (b Builder) DebugFunction(f Function, pos token.Position, bodyPos token.Position) {
|
||||
p := f
|
||||
if p.diFunc == nil {
|
||||
sig := p.Type.raw.Type.(*types.Signature)
|
||||
rt := p.Prog.Type(sig.Results(), InGo)
|
||||
paramTypes := make([]llvm.Metadata, len(p.params)+1)
|
||||
paramTypes[0] = b.di().diType(rt, pos).ll
|
||||
for i, t := range p.params {
|
||||
paramTypes[i+1] = b.di().diType(t, pos).ll
|
||||
}
|
||||
diFuncType := b.di().di.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: b.di().file(pos.Filename).ll,
|
||||
Parameters: paramTypes,
|
||||
})
|
||||
dif := llvm.DIFunction{
|
||||
Type: diFuncType,
|
||||
Name: p.Name(),
|
||||
LinkageName: p.Name(),
|
||||
File: b.di().file(pos.Filename).ll,
|
||||
Line: pos.Line,
|
||||
ScopeLine: bodyPos.Line,
|
||||
IsDefinition: true,
|
||||
LocalToUnit: true,
|
||||
Optimized: true,
|
||||
}
|
||||
p.diFunc = &aDIFunction{
|
||||
b.di().di.CreateFunction(b.di().file(pos.Filename).ll, dif),
|
||||
}
|
||||
p.impl.SetSubprogram(p.diFunc.ll)
|
||||
}
|
||||
b.impl.SetCurrentDebugLocation(
|
||||
uint(bodyPos.Line),
|
||||
uint(bodyPos.Column),
|
||||
p.diFunc.ll,
|
||||
f.impl.InstructionDebugLoc(),
|
||||
)
|
||||
}
|
||||
|
||||
func (b Builder) Param(idx int) Expr {
|
||||
p := b.Func.Param(idx)
|
||||
if v, ok := b.dbgVars[p]; ok {
|
||||
return v.val
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
394
ssa/eh.go
Normal file
394
ssa/eh.go
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
// #include <setjmp.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type sigjmpbuf = C.sigjmp_buf
|
||||
|
||||
// func(env unsafe.Pointer, savemask c.Int) c.Int
|
||||
func (p Program) tySigsetjmp() *types.Signature {
|
||||
if p.sigsetjmpTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
|
||||
params := types.NewTuple(paramPtr, paramCInt)
|
||||
results := types.NewTuple(paramCInt)
|
||||
p.sigsetjmpTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.sigsetjmpTy
|
||||
}
|
||||
|
||||
// func(env unsafe.Pointer, retval c.Int)
|
||||
func (p Program) tySiglongjmp() *types.Signature {
|
||||
if p.sigljmpTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
|
||||
params := types.NewTuple(paramPtr, paramCInt)
|
||||
p.sigljmpTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.sigljmpTy
|
||||
}
|
||||
|
||||
func (b Builder) AllocaSigjmpBuf() Expr {
|
||||
prog := b.Prog
|
||||
n := unsafe.Sizeof(sigjmpbuf{})
|
||||
size := prog.IntVal(uint64(n), prog.Uintptr())
|
||||
return b.Alloca(size)
|
||||
}
|
||||
|
||||
func (b Builder) Sigsetjmp(jb, savemask Expr) Expr {
|
||||
fname := "sigsetjmp"
|
||||
if b.Prog.target.GOOS == "linux" {
|
||||
fname = "__sigsetjmp"
|
||||
}
|
||||
fn := b.Pkg.cFunc(fname, b.Prog.tySigsetjmp())
|
||||
return b.Call(fn, jb, savemask)
|
||||
}
|
||||
|
||||
func (b Builder) Siglongjmp(jb, retval Expr) {
|
||||
fn := b.Pkg.cFunc("siglongjmp", b.Prog.tySiglongjmp()) // TODO(xsw): mark as noreturn
|
||||
b.Call(fn, jb, retval)
|
||||
// b.Unreachable()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
deferKey = "__llgo_defer"
|
||||
)
|
||||
|
||||
func (p Function) deferInitBuilder() (b Builder, next BasicBlock) {
|
||||
b = p.NewBuilder()
|
||||
next = b.setBlockMoveLast(p.blks[0])
|
||||
p.blks[0].last = next.last
|
||||
return
|
||||
}
|
||||
|
||||
type aDefer struct {
|
||||
nextBit int // next defer bit
|
||||
key Expr // pthread TLS key
|
||||
data Expr // pointer to runtime.Defer
|
||||
bitsPtr Expr // pointer to defer bits
|
||||
rethPtr Expr // next block of Rethrow
|
||||
rundPtr Expr // next block of RunDefers
|
||||
argsPtr Expr // func and args links
|
||||
procBlk BasicBlock // deferProc block
|
||||
panicBlk BasicBlock // panic block (runDefers and rethrow)
|
||||
rundsNext []BasicBlock // next blocks of RunDefers
|
||||
stmts []func(bits Expr)
|
||||
}
|
||||
|
||||
func (p Package) keyInit(name string) {
|
||||
keyVar := p.VarOf(name)
|
||||
if keyVar == nil {
|
||||
return
|
||||
}
|
||||
prog := p.Prog
|
||||
keyVar.InitNil()
|
||||
keyVar.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
|
||||
|
||||
b := p.afterBuilder()
|
||||
eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), prog.IntVal(0, prog.CInt()))
|
||||
b.IfThen(eq, func() {
|
||||
b.pthreadKeyCreate(keyVar.Expr, prog.Nil(prog.VoidPtr()))
|
||||
})
|
||||
}
|
||||
|
||||
func (p Package) newKey(name string) Global {
|
||||
return p.NewVarEx(name, p.Prog.CIntPtr())
|
||||
}
|
||||
|
||||
func (b Builder) deferKey() Expr {
|
||||
return b.Load(b.Pkg.newKey(deferKey).Expr)
|
||||
}
|
||||
|
||||
const (
|
||||
// 0: addr sigjmpbuf
|
||||
// 1: bits uintptr
|
||||
// 2: link *Defer
|
||||
// 3: reth voidptr: block address after Rethrow
|
||||
// 4: rund voidptr: block address after RunDefers
|
||||
// 5: func and args links
|
||||
deferSigjmpbuf = iota
|
||||
deferBits
|
||||
deferLink
|
||||
deferRethrow
|
||||
deferRunDefers
|
||||
deferArgs
|
||||
)
|
||||
|
||||
func (b Builder) getDefer(kind DoAction) *aDefer {
|
||||
if b.Func.recov == nil {
|
||||
// b.Func.recov maybe nil in ssa.NaiveForm
|
||||
return nil
|
||||
}
|
||||
self := b.Func
|
||||
if self.defer_ == nil {
|
||||
// TODO(xsw): check if in pkg.init
|
||||
var next, panicBlk BasicBlock
|
||||
if kind != DeferAlways {
|
||||
b, next = self.deferInitBuilder()
|
||||
}
|
||||
|
||||
prog := b.Prog
|
||||
blks := self.MakeBlocks(2)
|
||||
procBlk, rethrowBlk := blks[0], blks[1]
|
||||
|
||||
key := b.deferKey()
|
||||
zero := prog.Val(uintptr(0))
|
||||
link := Expr{b.pthreadGetspecific(key).impl, prog.DeferPtr()}
|
||||
jb := b.AllocaSigjmpBuf()
|
||||
ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl, procBlk.Addr().impl)
|
||||
deferData := Expr{ptr, prog.DeferPtr()}
|
||||
b.pthreadSetspecific(key, deferData)
|
||||
bitsPtr := b.FieldAddr(deferData, deferBits)
|
||||
rethPtr := b.FieldAddr(deferData, deferRethrow)
|
||||
rundPtr := b.FieldAddr(deferData, deferRunDefers)
|
||||
argsPtr := b.FieldAddr(deferData, deferArgs)
|
||||
|
||||
czero := prog.IntVal(0, prog.CInt())
|
||||
retval := b.Sigsetjmp(jb, czero)
|
||||
if kind != DeferAlways {
|
||||
panicBlk = self.MakeBlock()
|
||||
} else {
|
||||
blks = self.MakeBlocks(2)
|
||||
next, panicBlk = blks[0], blks[1]
|
||||
}
|
||||
b.If(b.BinOp(token.EQL, retval, czero), next, panicBlk)
|
||||
|
||||
self.defer_ = &aDefer{
|
||||
key: key,
|
||||
data: deferData,
|
||||
bitsPtr: bitsPtr,
|
||||
rethPtr: rethPtr,
|
||||
rundPtr: rundPtr,
|
||||
argsPtr: argsPtr,
|
||||
procBlk: procBlk,
|
||||
panicBlk: panicBlk,
|
||||
rundsNext: []BasicBlock{rethrowBlk},
|
||||
}
|
||||
|
||||
b.SetBlockEx(rethrowBlk, AtEnd, false) // rethrow
|
||||
b.Call(b.Pkg.rtFunc("Rethrow"), link)
|
||||
b.Jump(self.recov)
|
||||
|
||||
if kind == DeferAlways {
|
||||
b.SetBlockEx(next, AtEnd, false)
|
||||
b.blk.last = next.last
|
||||
}
|
||||
}
|
||||
return self.defer_
|
||||
}
|
||||
|
||||
// DeferData returns the defer data (*runtime.Defer).
|
||||
func (b Builder) DeferData() Expr {
|
||||
key := b.deferKey()
|
||||
return Expr{b.pthreadGetspecific(key).impl, b.Prog.DeferPtr()}
|
||||
}
|
||||
|
||||
// Defer emits a defer instruction.
|
||||
func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
|
||||
if debugInstr {
|
||||
logCall("Defer", fn, args)
|
||||
}
|
||||
var prog Program
|
||||
var nextbit Expr
|
||||
var self = b.getDefer(kind)
|
||||
switch kind {
|
||||
case DeferInCond:
|
||||
prog = b.Prog
|
||||
next := self.nextBit
|
||||
self.nextBit++
|
||||
bits := b.Load(self.bitsPtr)
|
||||
nextbit = prog.Val(uintptr(1 << next))
|
||||
b.Store(self.bitsPtr, b.BinOp(token.OR, bits, nextbit))
|
||||
case DeferAlways:
|
||||
// nothing to do
|
||||
default:
|
||||
panic("todo: DeferInLoop is not supported - " + b.Func.Name())
|
||||
}
|
||||
typ := b.saveDeferArgs(self, fn, args)
|
||||
self.stmts = append(self.stmts, func(bits Expr) {
|
||||
switch kind {
|
||||
case DeferInCond:
|
||||
zero := prog.Val(uintptr(0))
|
||||
has := b.BinOp(token.NEQ, b.BinOp(token.AND, bits, nextbit), zero)
|
||||
b.IfThen(has, func() {
|
||||
b.callDefer(self, typ, fn, args)
|
||||
})
|
||||
case DeferAlways:
|
||||
b.callDefer(self, typ, fn, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
type node struct {
|
||||
prev *node
|
||||
fn func()
|
||||
args ...
|
||||
}
|
||||
// push
|
||||
defer.Args = &node{defer.Args,fn,args...}
|
||||
// pop
|
||||
node := defer.Args
|
||||
defer.Args = node.prev
|
||||
free(node)
|
||||
*/
|
||||
|
||||
func (b Builder) saveDeferArgs(self *aDefer, fn Expr, args []Expr) Type {
|
||||
if fn.kind != vkClosure && len(args) == 0 {
|
||||
return nil
|
||||
}
|
||||
prog := b.Prog
|
||||
offset := 1
|
||||
if fn.kind == vkClosure {
|
||||
offset++
|
||||
}
|
||||
typs := make([]Type, len(args)+offset)
|
||||
flds := make([]llvm.Value, len(args)+offset)
|
||||
typs[0] = prog.VoidPtr()
|
||||
flds[0] = b.Load(self.argsPtr).impl
|
||||
if offset == 2 {
|
||||
typs[1] = fn.Type
|
||||
flds[1] = fn.impl
|
||||
}
|
||||
for i, arg := range args {
|
||||
typs[i+offset] = arg.Type
|
||||
flds[i+offset] = arg.impl
|
||||
}
|
||||
typ := prog.Struct(typs...)
|
||||
ptr := Expr{b.aggregateMalloc(typ, flds...), prog.VoidPtr()}
|
||||
b.Store(self.argsPtr, ptr)
|
||||
return typ
|
||||
}
|
||||
|
||||
func (b Builder) callDefer(self *aDefer, typ Type, fn Expr, args []Expr) {
|
||||
if typ == nil {
|
||||
b.Call(fn, args...)
|
||||
return
|
||||
}
|
||||
prog := b.Prog
|
||||
ptr := b.Load(self.argsPtr)
|
||||
data := b.Load(Expr{ptr.impl, prog.Pointer(typ)})
|
||||
offset := 1
|
||||
b.Store(self.argsPtr, Expr{b.getField(data, 0).impl, prog.VoidPtr()})
|
||||
if fn.kind == vkClosure {
|
||||
fn = b.getField(data, 1)
|
||||
offset++
|
||||
}
|
||||
for i := 0; i < len(args); i++ {
|
||||
args[i] = b.getField(data, i+offset)
|
||||
}
|
||||
b.Call(fn, args...)
|
||||
b.free(ptr)
|
||||
}
|
||||
|
||||
// RunDefers emits instructions to run deferred instructions.
|
||||
func (b Builder) RunDefers() {
|
||||
self := b.getDefer(DeferInCond)
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
blk := b.Func.MakeBlock()
|
||||
self.rundsNext = append(self.rundsNext, blk)
|
||||
|
||||
b.Store(self.rundPtr, blk.Addr())
|
||||
b.Jump(self.procBlk)
|
||||
|
||||
b.SetBlockEx(blk, AtEnd, false)
|
||||
b.blk.last = blk.last
|
||||
}
|
||||
|
||||
func (p Function) endDefer(b Builder) {
|
||||
self := p.defer_
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
nexts := self.rundsNext
|
||||
if len(nexts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
rethrowBlk := nexts[0]
|
||||
procBlk := self.procBlk
|
||||
panicBlk := self.panicBlk
|
||||
rethPtr := self.rethPtr
|
||||
rundPtr := self.rundPtr
|
||||
bitsPtr := self.bitsPtr
|
||||
|
||||
stmts := self.stmts
|
||||
n := len(stmts)
|
||||
rethsNext := make([]BasicBlock, n+1)
|
||||
blks := p.MakeBlocks(n - 1)
|
||||
copy(rethsNext[1:], blks)
|
||||
rethsNext[0] = rethrowBlk
|
||||
rethsNext[n] = procBlk
|
||||
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
rethNext := rethsNext[i]
|
||||
b.SetBlockEx(rethsNext[i+1], AtEnd, true)
|
||||
b.Store(rethPtr, rethNext.Addr())
|
||||
stmts[i](b.Load(bitsPtr))
|
||||
if i != 0 {
|
||||
b.Jump(rethNext)
|
||||
}
|
||||
}
|
||||
link := b.getField(b.Load(self.data), deferLink)
|
||||
b.pthreadSetspecific(self.key, link)
|
||||
b.IndirectJump(b.Load(rundPtr), nexts)
|
||||
|
||||
b.SetBlockEx(panicBlk, AtEnd, false) // panicBlk: exec runDefers and rethrow
|
||||
b.Store(rundPtr, rethrowBlk.Addr())
|
||||
b.IndirectJump(b.Load(rethPtr), rethsNext)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Unreachable emits an unreachable instruction.
|
||||
func (b Builder) Unreachable() {
|
||||
b.impl.CreateUnreachable()
|
||||
}
|
||||
|
||||
// Recover emits a recover instruction.
|
||||
func (b Builder) Recover() Expr {
|
||||
if debugInstr {
|
||||
log.Println("Recover")
|
||||
}
|
||||
// TODO(xsw): recover can't be a function call in Go
|
||||
return b.Call(b.Pkg.rtFunc("Recover"))
|
||||
}
|
||||
|
||||
// Panic emits a panic instruction.
|
||||
func (b Builder) Panic(v Expr) {
|
||||
b.Call(b.Pkg.rtFunc("Panic"), v)
|
||||
b.Unreachable() // TODO: func supports noreturn attribute
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
1401
ssa/expr.go
Normal file
1401
ssa/expr.go
Normal file
File diff suppressed because it is too large
Load Diff
178
ssa/goroutine.go
Normal file
178
ssa/goroutine.go
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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"
|
||||
"strconv"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// func(c.Pointer) c.Pointer
|
||||
func (p Program) tyRoutine() *types.Signature {
|
||||
if p.routineTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramPtr)
|
||||
p.routineTy = types.NewSignatureType(nil, nil, nil, params, params, false)
|
||||
}
|
||||
return p.routineTy
|
||||
}
|
||||
|
||||
func (b Builder) pthreadCreate(pp, attr, routine, arg Expr) Expr {
|
||||
fn := b.Pkg.rtFunc("CreateThread")
|
||||
return b.Call(fn, pp, attr, routine, arg)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Go instruction creates a new goroutine and calls the specified
|
||||
// function within it.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// go println(t0, t1)
|
||||
// go t3()
|
||||
// go invoke t5.Println(...t6)
|
||||
func (b Builder) Go(fn Expr, args ...Expr) {
|
||||
if debugInstr {
|
||||
logCall("Go", fn, args)
|
||||
}
|
||||
|
||||
prog := b.Prog
|
||||
pkg := b.Pkg
|
||||
|
||||
var offset int
|
||||
if fn.kind != vkBuiltin {
|
||||
offset = 1
|
||||
}
|
||||
typs := make([]Type, len(args)+offset)
|
||||
flds := make([]llvm.Value, len(args)+offset)
|
||||
if offset == 1 {
|
||||
typs[0] = fn.Type
|
||||
flds[0] = fn.impl
|
||||
}
|
||||
for i, arg := range args {
|
||||
typs[i+offset] = arg.Type
|
||||
flds[i+offset] = arg.impl
|
||||
}
|
||||
t := prog.Struct(typs...)
|
||||
voidPtr := prog.VoidPtr()
|
||||
data := Expr{b.aggregateMalloc(t, flds...), voidPtr}
|
||||
size := prog.SizeOf(voidPtr)
|
||||
pthd := b.Alloca(prog.IntVal(uint64(size), prog.Uintptr()))
|
||||
b.pthreadCreate(pthd, prog.Nil(voidPtr), pkg.routine(t, fn, len(args)), data)
|
||||
}
|
||||
|
||||
func (p Package) routineName() string {
|
||||
p.iRoutine++
|
||||
return p.Path() + "._llgo_routine$" + strconv.Itoa(p.iRoutine)
|
||||
}
|
||||
|
||||
func (p Package) routine(t Type, fn Expr, n int) Expr {
|
||||
prog := p.Prog
|
||||
routine := p.NewFunc(p.routineName(), prog.tyRoutine(), InC)
|
||||
b := routine.MakeBody(1)
|
||||
param := routine.Param(0)
|
||||
data := Expr{llvm.CreateLoad(b.impl, t.ll, param.impl), t}
|
||||
args := make([]Expr, n)
|
||||
var offset int
|
||||
if fn.kind != vkBuiltin {
|
||||
fn = b.getField(data, 0)
|
||||
offset = 1
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
args[i] = b.getField(data, i+offset)
|
||||
}
|
||||
b.Call(fn, args...)
|
||||
b.free(param)
|
||||
b.Return(prog.Nil(prog.VoidPtr()))
|
||||
return routine.Expr
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// func(c.Pointer)
|
||||
func (p Program) tyDestruct() *types.Signature {
|
||||
if p.destructTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramPtr)
|
||||
p.destructTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.destructTy
|
||||
}
|
||||
|
||||
// func(*c.Int, func(c.Pointer)) c.Int
|
||||
func (p Program) tyPthreadKeyCreate() *types.Signature {
|
||||
if p.createKeyTy == nil {
|
||||
cint := p.CInt()
|
||||
cintPtr := p.Pointer(cint)
|
||||
paramCintPtr := types.NewParam(token.NoPos, nil, "", cintPtr.raw.Type)
|
||||
paramDestruct := types.NewParam(token.NoPos, nil, "", p.tyDestruct())
|
||||
paramCInt := types.NewParam(token.NoPos, nil, "", cint.raw.Type)
|
||||
params := types.NewTuple(paramCintPtr, paramDestruct)
|
||||
results := types.NewTuple(paramCInt)
|
||||
p.createKeyTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.createKeyTy
|
||||
}
|
||||
|
||||
func (b Builder) pthreadKeyCreate(key, destruct Expr) Expr {
|
||||
fn := b.Pkg.cFunc("pthread_key_create", b.Prog.tyPthreadKeyCreate())
|
||||
return b.Call(fn, key, destruct)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// func(c.Int) c.Pointer
|
||||
func (p Program) tyPthreadGetspecific() *types.Signature {
|
||||
if p.getSpecTy == nil {
|
||||
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramCInt)
|
||||
results := types.NewTuple(paramPtr)
|
||||
p.getSpecTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.getSpecTy
|
||||
}
|
||||
|
||||
// func(c.Int, c.Pointer) c.Int
|
||||
func (p Program) tyPthreadSetspecific() *types.Signature {
|
||||
if p.setSpecTy == nil {
|
||||
paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type)
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramCInt, paramPtr)
|
||||
results := types.NewTuple(paramCInt)
|
||||
p.setSpecTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.setSpecTy
|
||||
}
|
||||
|
||||
func (b Builder) pthreadGetspecific(key Expr) Expr {
|
||||
fn := b.Pkg.cFunc("pthread_getspecific", b.Prog.tyPthreadGetspecific())
|
||||
return b.Call(fn, key)
|
||||
}
|
||||
|
||||
func (b Builder) pthreadSetspecific(key, val Expr) Expr {
|
||||
fn := b.Pkg.cFunc("pthread_setspecific", b.Prog.tyPthreadSetspecific())
|
||||
return b.Call(fn, key, val)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
340
ssa/interface.go
Normal file
340
ssa/interface.go
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llgo/ssa/abi"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// unsafeEface(t *abi.Type, data unsafe.Pointer) Eface
|
||||
func (b Builder) unsafeEface(t, data llvm.Value) llvm.Value {
|
||||
return aggregateValue(b.impl, b.Prog.rtEface(), t, data)
|
||||
}
|
||||
|
||||
// unsafeIface(itab *runtime.Itab, data unsafe.Pointer) Eface
|
||||
func (b Builder) unsafeIface(itab, data llvm.Value) llvm.Value {
|
||||
return aggregateValue(b.impl, b.Prog.rtIface(), itab, data)
|
||||
}
|
||||
|
||||
// func NewItab(tintf *InterfaceType, typ *Type) *runtime.Itab
|
||||
func (b Builder) newItab(tintf, typ Expr) Expr {
|
||||
return b.Call(b.Pkg.rtFunc("NewItab"), tintf, typ)
|
||||
}
|
||||
|
||||
func (b Builder) unsafeInterface(rawIntf *types.Interface, t Expr, data llvm.Value) llvm.Value {
|
||||
if rawIntf.Empty() {
|
||||
return b.unsafeEface(t.impl, data)
|
||||
}
|
||||
tintf := b.abiType(rawIntf)
|
||||
itab := b.newItab(tintf, t)
|
||||
return b.unsafeIface(itab.impl, data)
|
||||
}
|
||||
|
||||
func iMethodOf(rawIntf *types.Interface, name string) int {
|
||||
n := rawIntf.NumMethods()
|
||||
for i := 0; i < n; i++ {
|
||||
m := rawIntf.Method(i)
|
||||
if m.Name() == name {
|
||||
// TODO(xsw): check signature
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Imethod returns closure of an interface method.
|
||||
func (b Builder) Imethod(intf Expr, method *types.Func) Expr {
|
||||
prog := b.Prog
|
||||
rawIntf := intf.raw.Type.Underlying().(*types.Interface)
|
||||
tclosure := prog.Type(method.Type(), InGo)
|
||||
i := iMethodOf(rawIntf, method.Name())
|
||||
data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf)
|
||||
impl := intf.impl
|
||||
itab := Expr{b.faceItab(impl), prog.VoidPtrPtr()}
|
||||
pfn := b.Advance(itab, prog.IntVal(uint64(i+3), prog.Int()))
|
||||
fn := b.Load(pfn)
|
||||
ret := b.aggregateValue(tclosure, fn.impl, data.impl)
|
||||
return ret
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// MakeInterface constructs an instance of an interface type from a
|
||||
// value of a concrete type.
|
||||
//
|
||||
// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
|
||||
// of X, and Program.MethodValue(m) to find the implementation of a method.
|
||||
//
|
||||
// To construct the zero value of an interface type T, use:
|
||||
//
|
||||
// NewConst(constant.MakeNil(), T, pos)
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make interface{} <- int (42:int)
|
||||
// t2 = make Stringer <- t0
|
||||
func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
|
||||
rawIntf := tinter.raw.Type.Underlying().(*types.Interface)
|
||||
if debugInstr {
|
||||
log.Printf("MakeInterface %v, %v\n", rawIntf, x.impl)
|
||||
}
|
||||
if x.kind == vkFuncDecl {
|
||||
typ := b.Prog.Type(x.raw.Type, InGo)
|
||||
x = checkExpr(x, typ.raw.Type, b)
|
||||
}
|
||||
prog := b.Prog
|
||||
typ := x.Type
|
||||
tabi := b.abiType(typ.raw.Type)
|
||||
kind, _, lvl := abi.DataKindOf(typ.raw.Type, 0, prog.is32Bits)
|
||||
switch kind {
|
||||
case abi.Indirect:
|
||||
vptr := b.AllocU(typ)
|
||||
b.Store(vptr, x)
|
||||
return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter}
|
||||
}
|
||||
ximpl := x.impl
|
||||
if lvl > 0 {
|
||||
ximpl = extractVal(b.impl, ximpl, lvl)
|
||||
}
|
||||
var u llvm.Value
|
||||
switch kind {
|
||||
case abi.Pointer:
|
||||
return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter}
|
||||
case abi.Integer:
|
||||
tu := prog.Uintptr()
|
||||
u = llvm.CreateIntCast(b.impl, ximpl, tu.ll)
|
||||
case abi.BitCast:
|
||||
tu := prog.Uintptr()
|
||||
if b.Prog.td.TypeAllocSize(typ.ll) < b.Prog.td.TypeAllocSize(tu.ll) {
|
||||
u = llvm.CreateBitCast(b.impl, ximpl, prog.Uint32().ll)
|
||||
} else {
|
||||
u = llvm.CreateBitCast(b.impl, ximpl, tu.ll)
|
||||
}
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr())
|
||||
return Expr{b.unsafeInterface(rawIntf, tabi, data), tinter}
|
||||
}
|
||||
|
||||
func (b Builder) valFromData(typ Type, data llvm.Value) Expr {
|
||||
prog := b.Prog
|
||||
kind, real, lvl := abi.DataKindOf(typ.raw.Type, 0, prog.is32Bits)
|
||||
switch kind {
|
||||
case abi.Indirect:
|
||||
impl := b.impl
|
||||
tll := typ.ll
|
||||
tptr := llvm.PointerType(tll, 0)
|
||||
ptr := llvm.CreatePointerCast(impl, data, tptr)
|
||||
return Expr{llvm.CreateLoad(impl, tll, ptr), typ}
|
||||
}
|
||||
t := typ
|
||||
if lvl > 0 {
|
||||
t = prog.rawType(real)
|
||||
}
|
||||
switch kind {
|
||||
case abi.Pointer:
|
||||
return b.buildVal(typ, data, lvl)
|
||||
case abi.Integer:
|
||||
x := castUintptr(b, data, prog.Uintptr())
|
||||
return b.buildVal(typ, castInt(b, x, t), lvl)
|
||||
case abi.BitCast:
|
||||
x := castUintptr(b, data, prog.Uintptr())
|
||||
if int(prog.SizeOf(t)) != prog.PointerSize() {
|
||||
x = castInt(b, x, prog.Int32())
|
||||
}
|
||||
return b.buildVal(typ, llvm.CreateBitCast(b.impl, x, t.ll), lvl)
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
func extractVal(b llvm.Builder, val llvm.Value, lvl int) llvm.Value {
|
||||
for lvl > 0 {
|
||||
// TODO(xsw): check array support
|
||||
val = llvm.CreateExtractValue(b, val, 0)
|
||||
lvl--
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (b Builder) buildVal(typ Type, val llvm.Value, lvl int) Expr {
|
||||
if lvl == 0 {
|
||||
return Expr{val, typ}
|
||||
}
|
||||
switch t := typ.raw.Type.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
telem := b.Prog.rawType(t.Field(0).Type())
|
||||
elem := b.buildVal(telem, val, lvl-1)
|
||||
return Expr{aggregateValue(b.impl, typ.ll, elem.impl), typ}
|
||||
case *types.Array:
|
||||
telem := b.Prog.rawType(t.Elem())
|
||||
elem := b.buildVal(telem, val, lvl-1)
|
||||
return Expr{llvm.ConstArray(typ.ll, []llvm.Value{elem.impl}), typ}
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// The TypeAssert instruction tests whether interface value X has type
|
||||
// AssertedType.
|
||||
//
|
||||
// If !CommaOk, on success it returns v, the result of the conversion
|
||||
// (defined below); on failure it panics.
|
||||
//
|
||||
// If CommaOk: on success it returns a pair (v, true) where v is the
|
||||
// result of the conversion; on failure it returns (z, false) where z
|
||||
// is AssertedType's zero value. The components of the pair must be
|
||||
// accessed using the Extract instruction.
|
||||
//
|
||||
// If Underlying: tests whether interface value X has the underlying
|
||||
// type AssertedType.
|
||||
//
|
||||
// If AssertedType is a concrete type, TypeAssert checks whether the
|
||||
// dynamic type in interface X is equal to it, and if so, the result
|
||||
// of the conversion is a copy of the value in the interface.
|
||||
//
|
||||
// If AssertedType is an interface, TypeAssert checks whether the
|
||||
// dynamic type of the interface is assignable to it, and if so, the
|
||||
// result of the conversion is a copy of the interface value X.
|
||||
// If AssertedType is a superinterface of X.Type(), the operation will
|
||||
// fail iff the operand is nil. (Contrast with ChangeInterface, which
|
||||
// performs no nil-check.)
|
||||
//
|
||||
// Type() reflects the actual type of the result, possibly a
|
||||
// 2-types.Tuple; AssertedType is the asserted type.
|
||||
//
|
||||
// Depending on the TypeAssert's purpose, Pos may return:
|
||||
// - the ast.CallExpr.Lparen of an explicit T(e) conversion;
|
||||
// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation;
|
||||
// - the ast.CaseClause.Case of a case of a type-switch statement;
|
||||
// - the Ident(m).NamePos of an interface method value i.m
|
||||
// (for which TypeAssert may be used to effect the nil check).
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = typeassert t0.(int)
|
||||
// t3 = typeassert,ok t2.(T)
|
||||
func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk)
|
||||
}
|
||||
tx := b.faceAbiType(x)
|
||||
tabi := b.abiType(assertedTyp.raw.Type)
|
||||
|
||||
var eq Expr
|
||||
var val func() Expr
|
||||
if x.RawType() == assertedTyp.RawType() {
|
||||
eq = b.Const(constant.MakeBool(true), b.Prog.Bool())
|
||||
val = func() Expr { return x }
|
||||
} else {
|
||||
if rawIntf, ok := assertedTyp.raw.Type.Underlying().(*types.Interface); ok {
|
||||
eq = b.InlineCall(b.Pkg.rtFunc("Implements"), tabi, tx)
|
||||
val = func() Expr { return Expr{b.unsafeInterface(rawIntf, tx, b.faceData(x.impl)), assertedTyp} }
|
||||
} else {
|
||||
eq = b.BinOp(token.EQL, tx, tabi)
|
||||
val = func() Expr { return b.valFromData(assertedTyp, b.faceData(x.impl)) }
|
||||
}
|
||||
}
|
||||
|
||||
if commaOk {
|
||||
prog := b.Prog
|
||||
t := prog.Struct(assertedTyp, prog.Bool())
|
||||
blks := b.Func.MakeBlocks(3)
|
||||
b.If(eq, blks[0], blks[1])
|
||||
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
phi := b.Phi(t)
|
||||
phi.AddIncoming(b, blks[:2], func(i int, blk BasicBlock) Expr {
|
||||
b.SetBlockEx(blk, AtEnd, false)
|
||||
if i == 0 {
|
||||
valTrue := aggregateValue(b.impl, t.ll, val().impl, prog.BoolVal(true).impl)
|
||||
b.Jump(blks[2])
|
||||
return Expr{valTrue, t}
|
||||
}
|
||||
zero := prog.Zero(assertedTyp)
|
||||
valFalse := aggregateValue(b.impl, t.ll, zero.impl, prog.BoolVal(false).impl)
|
||||
b.Jump(blks[2])
|
||||
return Expr{valFalse, t}
|
||||
})
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
b.blk.last = blks[2].last
|
||||
return phi.Expr
|
||||
}
|
||||
blks := b.Func.MakeBlocks(2)
|
||||
b.If(eq, blks[0], blks[1])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
b.Panic(b.MakeInterface(b.Prog.Any(), b.Str("type assertion "+x.RawType().String()+" -> "+assertedTyp.RawType().String()+" failed")))
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
b.blk.last = blks[0].last
|
||||
return val()
|
||||
}
|
||||
|
||||
// ChangeInterface constructs a value of one interface type from a
|
||||
// value of another interface type known to be assignable to it.
|
||||
// This operation cannot fail.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
|
||||
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
|
||||
// instruction arose from an explicit e.(T) operation; or token.NoPos
|
||||
// otherwise.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = change interface interface{} <- I (t0)
|
||||
func (b Builder) ChangeInterface(typ Type, x Expr) (ret Expr) {
|
||||
rawIntf := typ.raw.Type.Underlying().(*types.Interface)
|
||||
tabi := b.faceAbiType(x)
|
||||
data := b.faceData(x.impl)
|
||||
return Expr{b.unsafeInterface(rawIntf, tabi, data), typ}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
// InterfaceData returns the data pointer of an interface.
|
||||
func (b Builder) InterfaceData(x Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("InterfaceData %v\n", x.impl)
|
||||
}
|
||||
return Expr{b.faceData(x.impl), b.Prog.VoidPtr()}
|
||||
}
|
||||
*/
|
||||
|
||||
func (b Builder) faceData(x llvm.Value) llvm.Value {
|
||||
return llvm.CreateExtractValue(b.impl, x, 1)
|
||||
}
|
||||
|
||||
func (b Builder) faceItab(x llvm.Value) llvm.Value {
|
||||
return llvm.CreateExtractValue(b.impl, x, 0)
|
||||
}
|
||||
|
||||
func (b Builder) faceAbiType(x Expr) Expr {
|
||||
if x.kind == vkIface {
|
||||
return b.InlineCall(b.Pkg.rtFunc("IfaceType"), x)
|
||||
}
|
||||
typ := llvm.CreateExtractValue(b.impl, x.impl, 0)
|
||||
return Expr{typ, b.Prog.AbiTypePtr()}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
367
ssa/memory.go
Normal file
367
ssa/memory.go
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* 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"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (b Builder) aggregateAllocU(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.allocUninited(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) aggregateAlloca(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.Alloca(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) aggregateMalloc(t Type, flds ...llvm.Value) llvm.Value {
|
||||
prog := b.Prog
|
||||
size := prog.SizeOf(t)
|
||||
ptr := b.malloc(prog.IntVal(size, prog.Uintptr())).impl
|
||||
aggregateInit(b.impl, ptr, t.ll, flds...)
|
||||
return ptr
|
||||
}
|
||||
|
||||
// aggregateValue yields the value of the aggregate X with the fields
|
||||
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
|
||||
return Expr{aggregateValue(b.impl, t.ll, flds...), t}
|
||||
}
|
||||
|
||||
func aggregateValue(b llvm.Builder, tll llvm.Type, flds ...llvm.Value) llvm.Value {
|
||||
agg := llvm.Undef(tll)
|
||||
for i, fld := range flds {
|
||||
agg = b.CreateInsertValue(agg, fld, i, "")
|
||||
}
|
||||
return agg
|
||||
}
|
||||
|
||||
func aggregateInit(b llvm.Builder, ptr llvm.Value, tll llvm.Type, flds ...llvm.Value) {
|
||||
for i, fld := range flds {
|
||||
b.CreateStore(fld, llvm.CreateStructGEP(b, tll, ptr, i))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (b Builder) dupMalloc(v Expr) Expr {
|
||||
prog := b.Prog
|
||||
n := prog.SizeOf(v.Type)
|
||||
tptr := prog.Pointer(v.Type)
|
||||
ptr := b.malloc(prog.Val(uintptr(n))).impl
|
||||
b.Store(Expr{ptr, tptr}, v)
|
||||
return Expr{ptr, tptr}
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The Alloc instruction reserves space for a variable of the given type,
|
||||
// zero-initializes it, and yields its address.
|
||||
//
|
||||
// If heap is false, Alloc zero-initializes the same local variable in
|
||||
// the call frame and returns its address; in this case the Alloc must
|
||||
// be present in Function.Locals. We call this a "local" alloc.
|
||||
//
|
||||
// If heap is true, Alloc allocates a new zero-initialized variable
|
||||
// each time the instruction is executed. We call this a "new" alloc.
|
||||
//
|
||||
// When Alloc is applied to a channel, map or slice type, it returns
|
||||
// the address of an uninitialized (nil) reference of that kind; store
|
||||
// the result of MakeSlice, MakeMap or MakeChan in that location to
|
||||
// instantiate these types.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t0 = local int
|
||||
// t1 = new int
|
||||
func (b Builder) Alloc(elem Type, heap bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloc %v, %v\n", elem.RawType(), heap)
|
||||
}
|
||||
prog := b.Prog
|
||||
pkg := b.Pkg
|
||||
size := SizeOf(prog, elem)
|
||||
if heap {
|
||||
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
|
||||
} else {
|
||||
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
|
||||
ret.impl = b.zeroinit(ret, size).impl
|
||||
}
|
||||
ret.Type = prog.Pointer(elem)
|
||||
return
|
||||
}
|
||||
|
||||
// AllocU allocates uninitialized space for n*sizeof(elem) bytes.
|
||||
func (b Builder) AllocU(elem Type, n ...int64) (ret Expr) {
|
||||
prog := b.Prog
|
||||
size := SizeOf(prog, elem, n...)
|
||||
return Expr{b.allocUninited(size).impl, prog.Pointer(elem)}
|
||||
}
|
||||
|
||||
func (b Builder) allocUninited(size Expr) (ret Expr) {
|
||||
return b.InlineCall(b.Pkg.rtFunc("AllocU"), size)
|
||||
}
|
||||
|
||||
// AllocZ allocates zero initialized space for n bytes.
|
||||
func (b Builder) AllocZ(n Expr) (ret Expr) {
|
||||
return b.InlineCall(b.Pkg.rtFunc("AllocZ"), n)
|
||||
}
|
||||
|
||||
// Alloca allocates uninitialized space for n bytes.
|
||||
func (b Builder) Alloca(n Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloca %v\n", n.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
telem := prog.tyInt8()
|
||||
ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl)
|
||||
ret.Type = prog.VoidPtr()
|
||||
return
|
||||
}
|
||||
|
||||
func (b Builder) AllocaT(t Type) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaT %v\n", t.RawType())
|
||||
}
|
||||
prog := b.Prog
|
||||
ret.impl = llvm.CreateAlloca(b.impl, t.ll)
|
||||
ret.Type = prog.Pointer(t)
|
||||
return
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// AllocaU allocates uninitialized space for n*sizeof(elem) bytes.
|
||||
func (b Builder) AllocaU(elem Type, n ...int64) (ret Expr) {
|
||||
prog := b.Prog
|
||||
size := SizeOf(prog, elem, n...)
|
||||
return Expr{b.Alloca(size).impl, prog.Pointer(elem)}
|
||||
}
|
||||
*/
|
||||
|
||||
// AllocaCStr allocates space for copy it from a Go string.
|
||||
func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaCStr %v\n", gostr.impl)
|
||||
}
|
||||
n := b.StringLen(gostr)
|
||||
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
|
||||
cstr := b.Alloca(n1)
|
||||
return b.InlineCall(b.Pkg.rtFunc("CStrCopy"), cstr, gostr)
|
||||
}
|
||||
|
||||
// func allocaCStrs(strs []string, endWithNil bool) **int8
|
||||
func (b Builder) AllocaCStrs(strs Expr, endWithNil bool) (cstrs Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("AllocaCStrs %v, %v\n", strs.impl, endWithNil)
|
||||
}
|
||||
prog := b.Prog
|
||||
n := b.SliceLen(strs)
|
||||
n1 := n
|
||||
if endWithNil {
|
||||
n1 = b.BinOp(token.ADD, n, prog.Val(1))
|
||||
}
|
||||
tcstr := prog.CStr()
|
||||
cstrs = b.ArrayAlloca(tcstr, n1)
|
||||
b.Times(n, func(i Expr) {
|
||||
pstr := b.IndexAddr(strs, i)
|
||||
s := b.Load(pstr)
|
||||
b.Store(b.Advance(cstrs, i), b.AllocaCStr(s))
|
||||
})
|
||||
if endWithNil {
|
||||
b.Store(b.Advance(cstrs, n), prog.Nil(tcstr))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (p Program) tyMalloc() *types.Signature {
|
||||
if p.mallocTy == nil {
|
||||
paramSize := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramSize)
|
||||
results := types.NewTuple(paramPtr)
|
||||
p.mallocTy = types.NewSignatureType(nil, nil, nil, params, results, false)
|
||||
}
|
||||
return p.mallocTy
|
||||
}
|
||||
|
||||
func (p Program) tyFree() *types.Signature {
|
||||
if p.freeTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
params := types.NewTuple(paramPtr)
|
||||
p.freeTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.freeTy
|
||||
}
|
||||
|
||||
func (p Program) tyMemsetInline() *types.Signature {
|
||||
if p.memsetInlineTy == nil {
|
||||
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
|
||||
paramInt := types.NewParam(token.NoPos, nil, "", p.Byte().raw.Type)
|
||||
paramSize := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type)
|
||||
paramBool := types.NewParam(token.NoPos, nil, "", p.Bool().raw.Type)
|
||||
params := types.NewTuple(paramPtr, paramInt, paramSize, paramBool)
|
||||
p.memsetInlineTy = types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
}
|
||||
return p.memsetInlineTy
|
||||
}
|
||||
|
||||
func (b Builder) malloc(size Expr) Expr {
|
||||
fn := b.Pkg.cFunc("malloc", b.Prog.tyMalloc())
|
||||
return b.Call(fn, size)
|
||||
}
|
||||
|
||||
func (b Builder) free(ptr Expr) Expr {
|
||||
fn := b.Pkg.cFunc("free", b.Prog.tyFree())
|
||||
return b.Call(fn, ptr)
|
||||
}
|
||||
|
||||
// declare void @llvm.memset.inline.p0.p0i8.i32(ptr <dest>, i8 <val>, i32 <len>, i1 <isvolatile>)
|
||||
// declare void @llvm.memset.inline.p0.p0.i64(ptr <dest>, i8 <val>, i64 <len>, i1 <isvolatile>)
|
||||
func (b Builder) memset(ptr, val, len, isvolatile Expr) Expr {
|
||||
fn := b.Pkg.cFunc("llvm.memset", b.Prog.tyMemsetInline())
|
||||
b.Call(fn, ptr, val, len, isvolatile)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (b Builder) zeroinit(ptr, size Expr) Expr {
|
||||
return b.memset(ptr, b.Prog.IntVal(0, b.Prog.Byte()), size, b.Prog.Val(false))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// ArrayAlloca reserves space for an array of n elements of type telem.
|
||||
func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("ArrayAlloca %v, %v\n", telem.raw.Type, n.impl)
|
||||
}
|
||||
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
|
||||
ret.Type = b.Prog.Pointer(telem)
|
||||
return
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// ArrayAlloc allocates zero initialized space for an array of n elements of type telem.
|
||||
func (b Builder) ArrayAlloc(telem Type, n Expr) (ret Expr) {
|
||||
prog := b.Prog
|
||||
elemSize := SizeOf(prog, telem)
|
||||
size := b.BinOp(token.MUL, n, elemSize)
|
||||
ret.impl = b.AllocZ(size).impl
|
||||
ret.Type = prog.Pointer(telem)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// AtomicOp is an atomic operation.
|
||||
type AtomicOp = llvm.AtomicRMWBinOp
|
||||
|
||||
const (
|
||||
OpXchg = llvm.AtomicRMWBinOpXchg
|
||||
OpAdd = llvm.AtomicRMWBinOpAdd
|
||||
OpSub = llvm.AtomicRMWBinOpSub
|
||||
OpAnd = llvm.AtomicRMWBinOpAnd
|
||||
OpNand = llvm.AtomicRMWBinOpNand
|
||||
OpOr = llvm.AtomicRMWBinOpOr
|
||||
OpXor = llvm.AtomicRMWBinOpXor
|
||||
OpMax = llvm.AtomicRMWBinOpMax
|
||||
OpMin = llvm.AtomicRMWBinOpMin
|
||||
OpUMax = llvm.AtomicRMWBinOpUMax
|
||||
OpUMin = llvm.AtomicRMWBinOpUMin
|
||||
)
|
||||
|
||||
// Atomic performs an atomic operation on the memory location pointed to by ptr.
|
||||
func (b Builder) Atomic(op AtomicOp, ptr, val Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Atomic %v, %v, %v\n", op, ptr.impl, val.impl)
|
||||
}
|
||||
t := b.Prog.Elem(ptr.Type)
|
||||
val = b.ChangeType(t, val)
|
||||
ret := b.impl.CreateAtomicRMW(op, ptr.impl, val.impl, llvm.AtomicOrderingSequentiallyConsistent, false)
|
||||
return Expr{ret, t}
|
||||
}
|
||||
|
||||
// AtomicCmpXchg performs an atomic compare-and-swap operation on the memory location pointed to by ptr.
|
||||
func (b Builder) AtomicCmpXchg(ptr, old, new Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("AtomicCmpXchg %v, %v, %v\n", ptr.impl, old.impl, new.impl)
|
||||
}
|
||||
prog := b.Prog
|
||||
t := prog.Elem(ptr.Type)
|
||||
old = b.ChangeType(t, old)
|
||||
new = b.ChangeType(t, new)
|
||||
ret := b.impl.CreateAtomicCmpXchg(
|
||||
ptr.impl, old.impl, new.impl,
|
||||
llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, false)
|
||||
return Expr{ret, prog.Struct(t, prog.Bool())}
|
||||
}
|
||||
|
||||
// Load returns the value at the pointer ptr.
|
||||
func (b Builder) Load(ptr Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Load %v\n", ptr.impl)
|
||||
}
|
||||
if ptr.kind == vkPyVarRef {
|
||||
return b.pyLoad(ptr)
|
||||
}
|
||||
telem := b.Prog.Elem(ptr.Type)
|
||||
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
|
||||
}
|
||||
|
||||
// Store stores val at the pointer ptr.
|
||||
func (b Builder) Store(ptr, val Expr) Expr {
|
||||
raw := ptr.raw.Type
|
||||
if debugInstr {
|
||||
log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
|
||||
}
|
||||
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
|
||||
return Expr{b.impl.CreateStore(val.impl, ptr.impl), b.Prog.Void()}
|
||||
}
|
||||
|
||||
// Advance returns the pointer ptr advanced by offset.
|
||||
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
|
||||
}
|
||||
var elem llvm.Type
|
||||
var prog = b.Prog
|
||||
switch t := ptr.raw.Type.(type) {
|
||||
case *types.Basic: // void
|
||||
elem = prog.tyInt8()
|
||||
default:
|
||||
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
|
||||
}
|
||||
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
|
||||
return Expr{ret, ptr.Type}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
809
ssa/package.go
Normal file
809
ssa/package.go
Normal file
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
"github.com/goplus/llgo/ssa/abi"
|
||||
"github.com/goplus/llvm"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
const (
|
||||
PkgPython = "github.com/goplus/lib/py"
|
||||
PkgRuntime = env.LLGoRuntimePkg + "/internal/runtime"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type dbgFlags = int
|
||||
|
||||
const (
|
||||
DbgFlagInstruction dbgFlags = 1 << iota
|
||||
|
||||
DbgFlagAll = DbgFlagInstruction
|
||||
)
|
||||
|
||||
var (
|
||||
debugInstr bool
|
||||
)
|
||||
|
||||
// SetDebug sets debug flags.
|
||||
func SetDebug(dbgFlags dbgFlags) {
|
||||
debugInstr = (dbgFlags & DbgFlagInstruction) != 0
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// InitFlags is a set of flags for initializing the LLVM library.
|
||||
type InitFlags int
|
||||
|
||||
const (
|
||||
InitNativeTarget InitFlags = 1 << iota
|
||||
InitAllTargets
|
||||
InitAllTargetInfos
|
||||
InitAllTargetMCs
|
||||
|
||||
InitNativeAsmPrinter
|
||||
InitAllAsmPrinters
|
||||
|
||||
InitAllAsmParsers
|
||||
|
||||
InitNative = InitNativeTarget | InitNativeAsmPrinter
|
||||
InitAll = InitAllTargets | InitAllAsmParsers | InitAllAsmPrinters | InitAllTargetInfos | InitAllTargetMCs
|
||||
)
|
||||
|
||||
// Initialize initializes the LLVM library.
|
||||
func Initialize(flags InitFlags) {
|
||||
if flags&InitAllTargetInfos != 0 {
|
||||
llvm.InitializeAllTargetInfos()
|
||||
}
|
||||
if flags&InitAllTargets != 0 {
|
||||
llvm.InitializeAllTargets()
|
||||
}
|
||||
if flags&InitAllTargetMCs != 0 {
|
||||
llvm.InitializeAllTargetMCs()
|
||||
}
|
||||
if flags&InitAllAsmParsers != 0 {
|
||||
llvm.InitializeAllAsmParsers()
|
||||
}
|
||||
if flags&InitAllAsmPrinters != 0 {
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
if flags&InitNativeTarget != 0 {
|
||||
llvm.InitializeNativeTarget()
|
||||
}
|
||||
if flags&InitNativeAsmPrinter != 0 {
|
||||
llvm.InitializeNativeAsmPrinter()
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type aProgram struct {
|
||||
ctx llvm.Context
|
||||
typs typeutil.Map // rawType -> Type
|
||||
sizes types.Sizes // provided by Go compiler
|
||||
gocvt goTypes
|
||||
|
||||
patchType func(types.Type) types.Type
|
||||
|
||||
fnsCompiled map[string]bool
|
||||
|
||||
rt *types.Package
|
||||
rtget func() *types.Package
|
||||
|
||||
py *types.Package
|
||||
pyget func() *types.Package
|
||||
|
||||
target *Target
|
||||
td llvm.TargetData
|
||||
// tm llvm.TargetMachine
|
||||
named map[string]llvm.Type
|
||||
fnnamed map[string]int
|
||||
|
||||
intType llvm.Type
|
||||
int1Type llvm.Type
|
||||
int8Type llvm.Type
|
||||
int16Type llvm.Type
|
||||
int32Type llvm.Type
|
||||
int64Type llvm.Type
|
||||
voidType llvm.Type
|
||||
voidPtrTy llvm.Type
|
||||
|
||||
c64Type llvm.Type
|
||||
c128Type llvm.Type
|
||||
|
||||
rtStringTy llvm.Type
|
||||
rtEfaceTy llvm.Type
|
||||
rtIfaceTy llvm.Type
|
||||
rtSliceTy llvm.Type
|
||||
rtMapTy llvm.Type
|
||||
rtChanTy llvm.Type
|
||||
|
||||
anyTy Type
|
||||
voidTy Type
|
||||
voidPtr Type
|
||||
voidPPtr Type
|
||||
boolTy Type
|
||||
cstrTy Type
|
||||
cintTy Type
|
||||
cintPtr Type
|
||||
stringTy Type
|
||||
uintptrTy Type
|
||||
intTy Type
|
||||
uintTy Type
|
||||
f64Ty Type
|
||||
f32Ty Type
|
||||
c128Ty Type
|
||||
c64Ty Type
|
||||
byteTy Type
|
||||
i32Ty Type
|
||||
u32Ty Type
|
||||
i64Ty Type
|
||||
u64Ty Type
|
||||
|
||||
pyObjPtr Type
|
||||
pyObjPPtr Type
|
||||
|
||||
abiTyPtr Type
|
||||
abiTyPPtr Type
|
||||
deferTy Type
|
||||
deferPtr Type
|
||||
|
||||
pyImpTy *types.Signature
|
||||
pyNewList *types.Signature
|
||||
pyListSetI *types.Signature
|
||||
pyNewTuple *types.Signature
|
||||
pyTupleSetI *types.Signature
|
||||
floatFromDbl *types.Signature
|
||||
callNoArgs *types.Signature
|
||||
callOneArg *types.Signature
|
||||
callFOArgs *types.Signature
|
||||
loadPyModS *types.Signature
|
||||
getAttrStr *types.Signature
|
||||
pyUniStr *types.Signature
|
||||
|
||||
mallocTy *types.Signature
|
||||
freeTy *types.Signature
|
||||
memsetInlineTy *types.Signature
|
||||
|
||||
createKeyTy *types.Signature
|
||||
getSpecTy *types.Signature
|
||||
setSpecTy *types.Signature
|
||||
routineTy *types.Signature
|
||||
destructTy *types.Signature
|
||||
sigsetjmpTy *types.Signature
|
||||
sigljmpTy *types.Signature
|
||||
|
||||
printfTy *types.Signature
|
||||
|
||||
paramObjPtr_ *types.Var
|
||||
linkname map[string]string // pkgPath.nameInPkg => linkname
|
||||
|
||||
ptrSize int
|
||||
|
||||
is32Bits bool
|
||||
}
|
||||
|
||||
// A Program presents a program.
|
||||
type Program = *aProgram
|
||||
|
||||
// NewProgram creates a new program.
|
||||
func NewProgram(target *Target) Program {
|
||||
if target == nil {
|
||||
target = &Target{
|
||||
GOOS: runtime.GOOS,
|
||||
GOARCH: runtime.GOARCH,
|
||||
}
|
||||
}
|
||||
ctx := llvm.NewContext()
|
||||
td := target.targetData() // TODO(xsw): target config
|
||||
fnsCompiled := make(map[string]bool)
|
||||
/*
|
||||
arch := target.GOARCH
|
||||
if arch == "" {
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
sizes := types.SizesFor("gc", arch)
|
||||
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
ctx.Finalize()
|
||||
*/
|
||||
is32Bits := (td.PointerSize() == 4 || target.GOARCH == "x86") // TODO(xsw): remove temp code
|
||||
return &aProgram{
|
||||
ctx: ctx, gocvt: newGoTypes(), fnsCompiled: fnsCompiled,
|
||||
target: target, td: td, is32Bits: is32Bits,
|
||||
ptrSize: td.PointerSize(), named: make(map[string]llvm.Type), fnnamed: make(map[string]int),
|
||||
linkname: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (p Program) SetPatch(patchType func(types.Type) types.Type) {
|
||||
p.patchType = patchType
|
||||
}
|
||||
|
||||
func (p Program) patch(typ types.Type) types.Type {
|
||||
if p.patchType != nil {
|
||||
return p.patchType(typ)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// SetRuntime sets the runtime.
|
||||
// Its type can be *types.Package or func() *types.Package.
|
||||
func (p Program) SetRuntime(runtime any) {
|
||||
switch v := runtime.(type) {
|
||||
case *types.Package:
|
||||
p.rt = v
|
||||
case func() *types.Package:
|
||||
p.rtget = v
|
||||
}
|
||||
}
|
||||
|
||||
func (p Program) SetTypeBackground(fullName string, bg Background) {
|
||||
p.gocvt.typbg.Store(fullName, bg)
|
||||
}
|
||||
|
||||
func (p Program) SetLinkname(name, link string) {
|
||||
p.linkname[name] = link
|
||||
}
|
||||
|
||||
func (p Program) Linkname(name string) (link string, ok bool) {
|
||||
link, ok = p.linkname[name]
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) runtime() *types.Package {
|
||||
if p.rt == nil {
|
||||
p.rt = p.rtget()
|
||||
}
|
||||
return p.rt
|
||||
}
|
||||
|
||||
// check generic function instantiation
|
||||
func (p Program) FuncCompiled(name string) bool {
|
||||
_, ok := p.fnsCompiled[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p Program) SetFuncCompiled(name string) {
|
||||
p.fnsCompiled[name] = true
|
||||
}
|
||||
|
||||
func (p Program) rtNamed(name string) *types.Named {
|
||||
if rt := p.runtime(); rt != nil {
|
||||
if rtScope := rt.Scope(); rtScope != nil {
|
||||
if obj := rtScope.Lookup(name); obj != nil {
|
||||
t := obj.Type()
|
||||
for {
|
||||
if alias, ok := t.(*types.Alias); ok {
|
||||
t = types.Unalias(alias)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
t, _ = p.gocvt.cvtNamed(t.(*types.Named))
|
||||
return t.(*types.Named)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("runtime type (%s) not found, install from pre-built package or set LLGO_ROOT", name))
|
||||
}
|
||||
|
||||
func (p Program) rtType(name string) Type {
|
||||
return p.rawType(p.rtNamed(name))
|
||||
}
|
||||
|
||||
func (p Program) rtEface() llvm.Type {
|
||||
if p.rtEfaceTy.IsNil() {
|
||||
p.rtEfaceTy = p.rtType("Eface").ll
|
||||
}
|
||||
return p.rtEfaceTy
|
||||
}
|
||||
|
||||
func (p Program) rtIface() llvm.Type {
|
||||
if p.rtIfaceTy.IsNil() {
|
||||
p.rtIfaceTy = p.rtType("Iface").ll
|
||||
}
|
||||
return p.rtIfaceTy
|
||||
}
|
||||
|
||||
func (p Program) rtMap() llvm.Type {
|
||||
if p.rtMapTy.IsNil() {
|
||||
p.rtMapTy = p.rtType("Map").ll
|
||||
}
|
||||
return p.rtMapTy
|
||||
}
|
||||
|
||||
func (p Program) rtSlice() llvm.Type {
|
||||
if p.rtSliceTy.IsNil() {
|
||||
p.rtSliceTy = p.rtType("Slice").ll
|
||||
}
|
||||
return p.rtSliceTy
|
||||
}
|
||||
|
||||
func (p Program) rtString() llvm.Type {
|
||||
if p.rtStringTy.IsNil() {
|
||||
p.rtStringTy = p.rtType("String").ll
|
||||
}
|
||||
return p.rtStringTy
|
||||
}
|
||||
|
||||
func (p Program) rtChan() llvm.Type {
|
||||
if p.rtChanTy.IsNil() {
|
||||
p.rtChanTy = p.rtType("Chan").ll
|
||||
}
|
||||
return p.rtChanTy
|
||||
}
|
||||
|
||||
func (p Program) tyComplex64() llvm.Type {
|
||||
if p.c64Type.IsNil() {
|
||||
ctx := p.ctx
|
||||
f32 := ctx.FloatType()
|
||||
p.c64Type = ctx.StructType([]llvm.Type{f32, f32}, false)
|
||||
}
|
||||
return p.c64Type
|
||||
}
|
||||
|
||||
func (p Program) tyComplex128() llvm.Type {
|
||||
if p.c128Type.IsNil() {
|
||||
ctx := p.ctx
|
||||
f64 := ctx.DoubleType()
|
||||
p.c128Type = ctx.StructType([]llvm.Type{f64, f64}, false)
|
||||
}
|
||||
return p.c128Type
|
||||
}
|
||||
|
||||
// NewPackage creates a new package.
|
||||
func (p Program) NewPackage(name, pkgPath string) Package {
|
||||
mod := p.ctx.NewModule(pkgPath)
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// mod.Finalize()
|
||||
gbls := make(map[string]Global)
|
||||
fns := make(map[string]Function)
|
||||
stubs := make(map[string]Function)
|
||||
pyobjs := make(map[string]PyObjRef)
|
||||
pymods := make(map[string]Global)
|
||||
strs := make(map[string]llvm.Value)
|
||||
chkabi := make(map[types.Type]bool)
|
||||
glbDbgVars := make(map[Expr]bool)
|
||||
// Don't need reset p.needPyInit here
|
||||
// p.needPyInit = false
|
||||
ret := &aPackage{
|
||||
mod: mod, vars: gbls, fns: fns, stubs: stubs,
|
||||
pyobjs: pyobjs, pymods: pymods, strs: strs,
|
||||
chkabi: chkabi, Prog: p,
|
||||
di: nil, cu: nil, glbDbgVars: glbDbgVars,
|
||||
}
|
||||
ret.abi.Init(pkgPath)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Struct returns a struct type.
|
||||
func (p Program) Struct(typs ...Type) Type {
|
||||
els := make([]*types.Var, len(typs))
|
||||
for i, t := range typs {
|
||||
els[i] = types.NewParam(token.NoPos, nil, "_llgo_f"+strconv.Itoa(i), t.raw.Type)
|
||||
}
|
||||
return p.rawType(types.NewStruct(els, nil))
|
||||
}
|
||||
|
||||
// Defer returns runtime.Defer type.
|
||||
func (p Program) Defer() Type {
|
||||
if p.deferTy == nil {
|
||||
p.deferTy = p.rtType("Defer")
|
||||
}
|
||||
return p.deferTy
|
||||
}
|
||||
|
||||
// DeferPtr returns *runtime.Defer type.
|
||||
func (p Program) DeferPtr() Type {
|
||||
if p.deferPtr == nil {
|
||||
p.deferPtr = p.Pointer(p.Defer())
|
||||
}
|
||||
return p.deferPtr
|
||||
}
|
||||
|
||||
// AbiTypePtr returns *abi.Type type.
|
||||
func (p Program) AbiTypePtr() Type {
|
||||
if p.abiTyPtr == nil {
|
||||
p.abiTyPtr = p.rawType(types.NewPointer(p.rtNamed("Type")))
|
||||
}
|
||||
return p.abiTyPtr
|
||||
}
|
||||
|
||||
// AbiTypePtrPtr returns **abi.Type type.
|
||||
func (p Program) AbiTypePtrPtr() Type {
|
||||
if p.abiTyPPtr == nil {
|
||||
p.abiTyPPtr = p.Pointer(p.AbiTypePtr())
|
||||
}
|
||||
return p.abiTyPPtr
|
||||
}
|
||||
|
||||
// Void returns void type.
|
||||
func (p Program) Void() Type {
|
||||
if p.voidTy == nil {
|
||||
p.voidTy = &aType{p.tyVoid(), rawType{types.Typ[types.Invalid]}, vkInvalid}
|
||||
}
|
||||
return p.voidTy
|
||||
}
|
||||
|
||||
// VoidPtr returns *void type.
|
||||
func (p Program) VoidPtr() Type {
|
||||
if p.voidPtr == nil {
|
||||
p.voidPtr = p.rawType(types.Typ[types.UnsafePointer])
|
||||
}
|
||||
return p.voidPtr
|
||||
}
|
||||
|
||||
// VoidPtrPtr returns **void type.
|
||||
func (p Program) VoidPtrPtr() Type {
|
||||
if p.voidPPtr == nil {
|
||||
p.voidPPtr = p.rawType(types.NewPointer(types.Typ[types.UnsafePointer]))
|
||||
}
|
||||
return p.voidPPtr
|
||||
}
|
||||
|
||||
// Bool returns bool type.
|
||||
func (p Program) Bool() Type {
|
||||
if p.boolTy == nil {
|
||||
p.boolTy = p.rawType(types.Typ[types.Bool])
|
||||
}
|
||||
return p.boolTy
|
||||
}
|
||||
|
||||
// CStr returns *int8 type.
|
||||
func (p Program) CStr() Type {
|
||||
if p.cstrTy == nil { // *int8
|
||||
p.cstrTy = p.rawType(types.NewPointer(types.Typ[types.Int8]))
|
||||
}
|
||||
return p.cstrTy
|
||||
}
|
||||
|
||||
// String returns string type.
|
||||
func (p Program) String() Type {
|
||||
if p.stringTy == nil {
|
||||
p.stringTy = p.rawType(types.Typ[types.String])
|
||||
}
|
||||
return p.stringTy
|
||||
}
|
||||
|
||||
// Any returns the any (empty interface) type.
|
||||
func (p Program) Any() Type {
|
||||
if p.anyTy == nil {
|
||||
p.anyTy = p.rawType(tyAny)
|
||||
}
|
||||
return p.anyTy
|
||||
}
|
||||
|
||||
/*
|
||||
// Eface returns the empty interface type.
|
||||
// It is equivalent to Any.
|
||||
func (p Program) Eface() Type {
|
||||
return p.Any()
|
||||
}
|
||||
*/
|
||||
|
||||
// CIntPtr returns *c.Int type.
|
||||
func (p Program) CIntPtr() Type {
|
||||
if p.cintPtr == nil {
|
||||
p.cintPtr = p.Pointer(p.CInt())
|
||||
}
|
||||
return p.cintPtr
|
||||
}
|
||||
|
||||
// CInt returns c.Int type.
|
||||
func (p Program) CInt() Type {
|
||||
if p.cintTy == nil { // C.int
|
||||
p.cintTy = p.rawType(types.Typ[types.Int32]) // TODO(xsw): support 64-bit
|
||||
}
|
||||
return p.cintTy
|
||||
}
|
||||
|
||||
// Int returns int type.
|
||||
func (p Program) Int() Type {
|
||||
if p.intTy == nil {
|
||||
p.intTy = p.rawType(types.Typ[types.Int])
|
||||
}
|
||||
return p.intTy
|
||||
}
|
||||
|
||||
// Uint returns uint type.
|
||||
func (p Program) Uint() Type {
|
||||
if p.uintTy == nil {
|
||||
p.uintTy = p.rawType(types.Typ[types.Uint])
|
||||
}
|
||||
return p.uintTy
|
||||
}
|
||||
|
||||
// Uintptr returns uintptr type.
|
||||
func (p Program) Uintptr() Type {
|
||||
if p.uintptrTy == nil {
|
||||
p.uintptrTy = p.rawType(types.Typ[types.Uintptr])
|
||||
}
|
||||
return p.uintptrTy
|
||||
}
|
||||
|
||||
// Float64 returns float64 type.
|
||||
func (p Program) Float64() Type {
|
||||
if p.f64Ty == nil {
|
||||
p.f64Ty = p.rawType(types.Typ[types.Float64])
|
||||
}
|
||||
return p.f64Ty
|
||||
}
|
||||
|
||||
// Float32 returns float32 type.
|
||||
func (p Program) Float32() Type {
|
||||
if p.f32Ty == nil {
|
||||
p.f32Ty = p.rawType(types.Typ[types.Float32])
|
||||
}
|
||||
return p.f32Ty
|
||||
}
|
||||
|
||||
// Complex128 returns complex128 type.
|
||||
func (p Program) Complex128() Type {
|
||||
if p.c128Ty == nil {
|
||||
p.c128Ty = p.rawType(types.Typ[types.Complex128])
|
||||
}
|
||||
return p.c128Ty
|
||||
}
|
||||
|
||||
// Complex64 returns complex64 type.
|
||||
func (p Program) Complex64() Type {
|
||||
if p.c64Ty == nil {
|
||||
p.c64Ty = p.rawType(types.Typ[types.Complex64])
|
||||
}
|
||||
return p.c64Ty
|
||||
}
|
||||
|
||||
// Byte returns byte type.
|
||||
func (p Program) Byte() Type {
|
||||
if p.byteTy == nil {
|
||||
p.byteTy = p.rawType(types.Typ[types.Byte])
|
||||
}
|
||||
return p.byteTy
|
||||
}
|
||||
|
||||
// Int32 returns int32 type.
|
||||
func (p Program) Int32() Type {
|
||||
if p.i32Ty == nil {
|
||||
p.i32Ty = p.rawType(types.Typ[types.Int32])
|
||||
}
|
||||
return p.i32Ty
|
||||
}
|
||||
|
||||
// Uint32 returns uint32 type.
|
||||
func (p Program) Uint32() Type {
|
||||
if p.u32Ty == nil {
|
||||
p.u32Ty = p.rawType(types.Typ[types.Uint32])
|
||||
}
|
||||
return p.u32Ty
|
||||
}
|
||||
|
||||
// Int64 returns int64 type.
|
||||
func (p Program) Int64() Type {
|
||||
if p.i64Ty == nil {
|
||||
p.i64Ty = p.rawType(types.Typ[types.Int64])
|
||||
}
|
||||
return p.i64Ty
|
||||
}
|
||||
|
||||
// Uint64 returns uint64 type.
|
||||
func (p Program) Uint64() Type {
|
||||
if p.u64Ty == nil {
|
||||
p.u64Ty = p.rawType(types.Typ[types.Uint64])
|
||||
}
|
||||
return p.u64Ty
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// A Package is a single analyzed Go package containing Members for
|
||||
// all package-level functions, variables, constants and types it
|
||||
// declares. These may be accessed directly via Members, or via the
|
||||
// type-specific accessor methods Func, Type, Var and Const.
|
||||
//
|
||||
// Members also contains entries for "init" (the synthetic package
|
||||
// initializer) and "init#%d", the nth declared init function,
|
||||
// and unspecified other things too.
|
||||
type aPackage struct {
|
||||
mod llvm.Module
|
||||
abi abi.Builder
|
||||
|
||||
Prog Program
|
||||
|
||||
di diBuilder
|
||||
cu CompilationUnit
|
||||
glbDbgVars map[Expr]bool
|
||||
|
||||
vars map[string]Global
|
||||
fns map[string]Function
|
||||
stubs map[string]Function
|
||||
pyobjs map[string]PyObjRef
|
||||
pymods map[string]Global
|
||||
strs map[string]llvm.Value
|
||||
goStrs map[string]llvm.Value
|
||||
chkabi map[types.Type]bool
|
||||
afterb unsafe.Pointer
|
||||
patch func(types.Type) types.Type
|
||||
fnlink func(string) string
|
||||
|
||||
iRoutine int
|
||||
|
||||
NeedRuntime bool
|
||||
NeedPyInit bool
|
||||
}
|
||||
|
||||
type Package = *aPackage
|
||||
|
||||
func (p Package) rtFunc(fnName string) Expr {
|
||||
p.NeedRuntime = true
|
||||
fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func)
|
||||
name := FullName(fn.Pkg(), fnName)
|
||||
sig := fn.Type().(*types.Signature)
|
||||
return p.NewFunc(name, sig, InGo).Expr
|
||||
}
|
||||
|
||||
func (p Package) cFunc(fullName string, sig *types.Signature) Expr {
|
||||
return p.NewFunc(fullName, sig, InC).Expr
|
||||
}
|
||||
|
||||
const (
|
||||
closureCtx = "__llgo_ctx"
|
||||
closureStub = "__llgo_stub."
|
||||
)
|
||||
|
||||
func (p Package) closureStub(b Builder, t types.Type, v Expr) Expr {
|
||||
name := v.impl.Name()
|
||||
prog := b.Prog
|
||||
nilVal := prog.Nil(prog.VoidPtr()).impl
|
||||
if fn, ok := p.stubs[name]; ok {
|
||||
v = fn.Expr
|
||||
} else {
|
||||
sig := v.raw.Type.(*types.Signature)
|
||||
n := sig.Params().Len()
|
||||
nret := sig.Results().Len()
|
||||
ctx := types.NewParam(token.NoPos, nil, closureCtx, types.Typ[types.UnsafePointer])
|
||||
sig = FuncAddCtx(ctx, sig)
|
||||
fn := p.NewFunc(closureStub+name, sig, InC)
|
||||
fn.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
|
||||
args := make([]Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
args[i] = fn.Param(i + 1)
|
||||
}
|
||||
b := fn.MakeBody(1)
|
||||
call := b.Call(v, args...)
|
||||
call.impl.SetTailCall(true)
|
||||
switch nret {
|
||||
case 0:
|
||||
b.impl.CreateRetVoid()
|
||||
default: // TODO(xsw): support multiple return values
|
||||
b.impl.CreateRet(call.impl)
|
||||
}
|
||||
p.stubs[name] = fn
|
||||
v = fn.Expr
|
||||
}
|
||||
return b.aggregateValue(prog.rawType(t), v.impl, nilVal)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Path returns the package path.
|
||||
func (p Package) Path() string {
|
||||
return p.abi.Pkg
|
||||
}
|
||||
|
||||
// String returns a string representation of the package.
|
||||
func (p Package) String() string {
|
||||
return p.mod.String()
|
||||
}
|
||||
|
||||
// SetPatch sets a patch function.
|
||||
func (p Package) SetPatch(fn func(types.Type) types.Type) {
|
||||
p.patch = fn
|
||||
}
|
||||
|
||||
// SetResolveLinkname sets a function to resolve linkname.
|
||||
func (p Package) SetResolveLinkname(fn func(string) string) {
|
||||
p.fnlink = fn
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func (p Package) afterBuilder() Builder {
|
||||
if p.afterb == nil {
|
||||
fn := p.NewFunc(p.Path()+".init$after", NoArgsNoRet, InC)
|
||||
fnb := fn.MakeBody(1)
|
||||
p.afterb = unsafe.Pointer(fnb)
|
||||
}
|
||||
return Builder(p.afterb)
|
||||
}
|
||||
|
||||
// AfterInit is called after the package is initialized (init all packages that depends on).
|
||||
func (p Package) AfterInit(b Builder, ret BasicBlock) {
|
||||
p.keyInit(deferKey)
|
||||
doAfterb := p.afterb != nil
|
||||
doPyLoadModSyms := p.pyHasModSyms()
|
||||
if doAfterb || doPyLoadModSyms {
|
||||
b.SetBlockEx(ret, afterInit, false)
|
||||
if doAfterb {
|
||||
afterb := Builder(p.afterb)
|
||||
afterb.Return()
|
||||
b.Call(afterb.Func.Expr)
|
||||
}
|
||||
if doPyLoadModSyms {
|
||||
p.pyLoadModSyms(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p Package) InitDebug(name, pkgPath string, positioner Positioner) {
|
||||
p.di = newDIBuilder(p.Prog, p, positioner)
|
||||
p.cu = p.di.createCompileUnit(name, pkgPath)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
type CodeGenFileType = llvm.CodeGenFileType
|
||||
|
||||
const (
|
||||
AssemblyFile = llvm.AssemblyFile
|
||||
ObjectFile = llvm.ObjectFile
|
||||
)
|
||||
|
||||
func (p *Package) CodeGen(ft CodeGenFileType) (ret []byte, err error) {
|
||||
buf, err := p.prog.targetMachine().EmitToMemoryBuffer(p.mod, ft)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret = buf.Bytes()
|
||||
buf.Dispose()
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Package) Bitcode() []byte {
|
||||
buf := llvm.WriteBitcodeToMemoryBuffer(p.mod)
|
||||
ret := buf.Bytes()
|
||||
buf.Dispose()
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Package) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write(p.Bitcode())
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
func (p *Package) WriteFile(file string) (err error) {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
return llvm.WriteBitcodeToFile(p.mod, f)
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
477
ssa/python.go
Normal file
477
ssa/python.go
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* 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(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(*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(*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.(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Float64:
|
||||
return b.PyFloat(v)
|
||||
default:
|
||||
panic("PyVal: todo")
|
||||
}
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
552
ssa/ssa_test.go
Normal file
552
ssa/ssa_test.go
Normal file
@@ -0,0 +1,552 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* 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/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/gogen/packages"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
func TestEndDefer(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("foo", "foo")
|
||||
fn := pkg.NewFunc("main", NoArgsNoRet, InC)
|
||||
b := fn.MakeBody(1)
|
||||
fn.defer_ = &aDefer{}
|
||||
fn.endDefer(b)
|
||||
}
|
||||
|
||||
func TestUnsafeString(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
fset := token.NewFileSet()
|
||||
imp := packages.NewImporter(fset)
|
||||
pkg, _ := imp.Import(PkgRuntime)
|
||||
return pkg
|
||||
})
|
||||
pkg := prog.NewPackage("foo", "foo")
|
||||
b := pkg.NewFunc("main", NoArgsNoRet, InC).MakeBody(1)
|
||||
b.Println(b.BuiltinCall("String", b.CStr("hello"), prog.Val(5)))
|
||||
b.Return()
|
||||
}
|
||||
|
||||
func TestPointerSize(t *testing.T) {
|
||||
expected := unsafe.Sizeof(uintptr(0))
|
||||
if size := NewProgram(nil).PointerSize(); size != int(expected) {
|
||||
t.Fatal("bad PointerSize:", size)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetBlock(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("SetBlock: no error?")
|
||||
}
|
||||
}()
|
||||
fn := &aFunction{}
|
||||
b := &aBuilder{Func: fn}
|
||||
b.SetBlock(&aBasicBlock{})
|
||||
}
|
||||
|
||||
func TestSetBlockEx(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("SetBlockEx: no error?")
|
||||
}
|
||||
}()
|
||||
fn := &aFunction{}
|
||||
b := &aBuilder{Func: fn}
|
||||
b.SetBlockEx(&aBasicBlock{fn: fn}, -1, false)
|
||||
}
|
||||
|
||||
func TestSetPython(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
typ := types.NewPackage("foo", "foo")
|
||||
prog.SetPython(typ)
|
||||
}
|
||||
|
||||
func TestClosureCtx(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("closureCtx: no error?")
|
||||
}
|
||||
}()
|
||||
var f aFunction
|
||||
f.closureCtx(nil)
|
||||
}
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
ctx := llvm.NewContext()
|
||||
llvmIntType(ctx, 4)
|
||||
|
||||
intT := types.NewVar(0, nil, "", types.Typ[types.Int])
|
||||
ret := types.NewTuple(intT, intT)
|
||||
sig := types.NewSignatureType(nil, nil, nil, nil, ret, false)
|
||||
prog := NewProgram(nil)
|
||||
prog.retType(sig)
|
||||
}
|
||||
|
||||
func TestIndexType(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("indexType: no error?")
|
||||
}
|
||||
}()
|
||||
indexType(types.Typ[types.Int])
|
||||
}
|
||||
|
||||
func TestCvtType(t *testing.T) {
|
||||
gt := newGoTypes()
|
||||
params := types.NewTuple(types.NewParam(0, nil, "", NoArgsNoRet))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
ret1 := gt.cvtFunc(sig, nil)
|
||||
if ret1 == sig {
|
||||
t.Fatal("cvtFunc failed")
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("cvtType: no error?")
|
||||
}
|
||||
}()
|
||||
gt.cvtType(nil)
|
||||
}
|
||||
|
||||
func TestUserdefExpr(t *testing.T) {
|
||||
c := &pyVarTy{}
|
||||
b := &builtinTy{}
|
||||
_ = c.String()
|
||||
_ = b.String()
|
||||
test := func(a types.Type) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("TestUserdefExpr: no error?")
|
||||
}
|
||||
}()
|
||||
a.Underlying()
|
||||
}
|
||||
test(c)
|
||||
test(b)
|
||||
}
|
||||
|
||||
func TestAny(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
ret := types.NewPackage("runtime", "runtime")
|
||||
scope := ret.Scope()
|
||||
name := types.NewTypeName(0, ret, "Eface", nil)
|
||||
types.NewNamed(name, types.NewStruct(nil, nil), nil)
|
||||
scope.Insert(name)
|
||||
return ret
|
||||
})
|
||||
prog.Any()
|
||||
}
|
||||
|
||||
func assertPkg(t *testing.T, p Package, expected string) {
|
||||
t.Helper()
|
||||
if v := p.String(); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPyFunc(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
py := types.NewPackage("foo", "foo")
|
||||
o := types.NewTypeName(0, py, "Object", nil)
|
||||
types.NewNamed(o, types.Typ[types.Int], nil)
|
||||
py.Scope().Insert(o)
|
||||
prog.SetPython(py)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
a := pkg.PyNewFunc("a", NoArgsNoRet, false)
|
||||
if pkg.PyNewFunc("a", NoArgsNoRet, false) != a {
|
||||
t.Fatal("NewPyFunc(a) failed")
|
||||
}
|
||||
foo := pkg.PyNewModVar("foo", false)
|
||||
if pkg.PyNewModVar("foo", false) != foo {
|
||||
t.Fatal("NewPyModVar(foo) failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
typ := types.NewPointer(types.Typ[types.Int])
|
||||
a := pkg.NewVar("a", typ, InGo)
|
||||
if pkg.NewVar("a", typ, InGo) != a {
|
||||
t.Fatal("NewVar(a) failed")
|
||||
}
|
||||
pkg.NewVarEx("a", prog.Type(typ, InGo))
|
||||
a.Init(prog.Val(100))
|
||||
b := pkg.NewVar("b", typ, InGo)
|
||||
b.Init(a.Expr)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
@a = global i64 100, align 8
|
||||
@b = global i64 @a, align 8
|
||||
`)
|
||||
}
|
||||
|
||||
func TestConst(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Bool]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, nil, rets, false)
|
||||
b := pkg.NewFunc("fn", sig, InGo).MakeBody(1)
|
||||
b.Return(b.Const(constant.MakeBool(true), prog.Bool()))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i1 @fn() {
|
||||
_llgo_0:
|
||||
ret i1 true
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestStruct(t *testing.T) {
|
||||
empty := types.NewStruct(nil, nil)
|
||||
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
pkg.NewVar("a", types.NewPointer(empty), InGo)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
@a = external global {}, align 1
|
||||
`)
|
||||
if pkg.NeedRuntime {
|
||||
t.Fatal("NeedRuntime?")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamedStruct(t *testing.T) {
|
||||
src := types.NewPackage("bar", "foo/bar")
|
||||
empty := types.NewNamed(types.NewTypeName(0, src, "Empty", nil), types.NewStruct(nil, nil), nil)
|
||||
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
pkg.NewVar("a", types.NewPointer(empty), InGo)
|
||||
if pkg.VarOf("a") == nil {
|
||||
t.Fatal("VarOf failed")
|
||||
}
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
%bar.Empty = type {}
|
||||
|
||||
@a = external global %bar.Empty, align 1
|
||||
`)
|
||||
}
|
||||
|
||||
func TestDeclFunc(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, nil, false)
|
||||
pkg.NewFunc("fn", sig, InGo)
|
||||
if pkg.FuncOf("fn") == nil {
|
||||
t.Fatal("FuncOf failed")
|
||||
}
|
||||
if prog.retType(sig) != prog.Void() {
|
||||
t.Fatal("retType failed")
|
||||
}
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
declare void @fn(i64)
|
||||
`)
|
||||
}
|
||||
|
||||
func TestBasicFunc(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "a", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
pkg.NewFunc("fn", sig, InGo).MakeBody(1).
|
||||
Return(prog.Val(1))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 1
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestFuncParam(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "a", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
fn.MakeBody(1).Return(fn.Param(0))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 %0
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestFuncCall(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "a", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
fn.MakeBody(1).
|
||||
Return(prog.Val(1))
|
||||
|
||||
b := pkg.NewFunc("main", NoArgsNoRet, InGo).MakeBody(1)
|
||||
b.Call(fn.Expr, prog.Val(1), prog.Val(1.2))
|
||||
b.Return()
|
||||
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 1
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
%0 = call i64 @fn(i64 1, double 1.200000e+00)
|
||||
ret void
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestFuncMultiRet(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||
rets := types.NewTuple(
|
||||
types.NewVar(0, nil, "c", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "d", types.Typ[types.Float64]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
a := pkg.NewVar("a", types.NewPointer(types.Typ[types.Int]), InGo)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(1)
|
||||
b.Return(a.Expr, fn.Param(0))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
@a = external global i64, align 8
|
||||
|
||||
define { i64, double } @fn(double %0) {
|
||||
_llgo_0:
|
||||
%1 = insertvalue { i64, double } { ptr @a, double undef }, double %0, 1
|
||||
ret { i64, double } %1
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestJump(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
fn := pkg.NewFunc("loop", NoArgsNoRet, InGo)
|
||||
b := fn.MakeBody(1)
|
||||
b.Jump(fn.Block(0))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define void @loop() {
|
||||
_llgo_0:
|
||||
br label %_llgo_0
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIf(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(3)
|
||||
iftrue := fn.Block(1)
|
||||
iffalse := fn.Block(2)
|
||||
if iftrue.Index() != 1 || iftrue.Parent() != fn {
|
||||
t.Fatal("iftrue")
|
||||
}
|
||||
cond := b.BinOp(token.GTR, fn.Param(0), prog.Val(0))
|
||||
b.If(cond, iftrue, iffalse)
|
||||
b.SetBlock(iftrue).Return(prog.Val(1))
|
||||
b.SetBlock(iffalse).Return(prog.Val(0))
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0) {
|
||||
_llgo_0:
|
||||
%1 = icmp sgt i64 %0, 0
|
||||
br i1 %1, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret i64 1
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret i64 0
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestPrintf(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
pchar := types.NewPointer(types.Typ[types.Int8])
|
||||
params := types.NewTuple(types.NewVar(0, nil, "format", pchar), VArg())
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int32]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, true)
|
||||
pkg.NewFunc("printf", sig, InC)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
declare i32 @printf(ptr, ...)
|
||||
`)
|
||||
}
|
||||
|
||||
func TestBinOp(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "a", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Float64]))
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(1)
|
||||
ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1))
|
||||
b.Return(ret)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
%2 = add i64 %0, 1
|
||||
ret i64 %2
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestUnOp(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "p", types.NewPointer(types.Typ[types.Int])),
|
||||
)
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
b := fn.MakeBody(1)
|
||||
ptr := fn.Param(0)
|
||||
val := b.UnOp(token.MUL, ptr)
|
||||
val2 := b.BinOp(token.XOR, val, prog.Val(1))
|
||||
b.Store(ptr, val2)
|
||||
b.Return(val2)
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = load i64, ptr %0, align 4
|
||||
%2 = xor i64 %1, 1
|
||||
store i64 %2, ptr %0, align 4
|
||||
ret i64 %2
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestBasicType(t *testing.T) {
|
||||
type typeInfo struct {
|
||||
typ Type
|
||||
kind types.BasicKind
|
||||
}
|
||||
prog := NewProgram(nil)
|
||||
infos := []*typeInfo{
|
||||
{prog.Bool(), types.Bool},
|
||||
{prog.Byte(), types.Byte},
|
||||
{prog.Int(), types.Int},
|
||||
{prog.Uint(), types.Uint},
|
||||
{prog.Int32(), types.Int32},
|
||||
{prog.Int64(), types.Int64},
|
||||
{prog.Uint32(), types.Uint32},
|
||||
{prog.Uint64(), types.Uint64},
|
||||
{prog.Uintptr(), types.Uintptr},
|
||||
{prog.VoidPtr(), types.UnsafePointer},
|
||||
}
|
||||
for _, info := range infos {
|
||||
if info.typ.RawType() != types.Typ[info.kind] {
|
||||
t.Fatal("bad type", info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareSelect(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
pkg := prog.NewPackage("bar", "foo/bar")
|
||||
|
||||
params := types.NewTuple(
|
||||
types.NewVar(0, nil, "a", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "b", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "c", types.Typ[types.Int]),
|
||||
)
|
||||
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
|
||||
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
|
||||
fn := pkg.NewFunc("fn", sig, InGo)
|
||||
|
||||
b := fn.MakeBody(1)
|
||||
result := b.compareSelect(token.GTR, fn.Param(0), fn.Param(1), fn.Param(2))
|
||||
b.Return(result)
|
||||
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
define i64 @fn(i64 %0, i64 %1, i64 %2) {
|
||||
_llgo_0:
|
||||
%3 = icmp sgt i64 %0, %1
|
||||
%4 = select i1 %3, i64 %0, i64 %1
|
||||
%5 = icmp sgt i64 %4, %2
|
||||
%6 = select i1 %5, i64 %4, i64 %2
|
||||
ret i64 %6
|
||||
}
|
||||
`)
|
||||
}
|
||||
58
ssa/ssatest/ssautil.go
Normal file
58
ssa/ssatest/ssautil.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 ssatest
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/gogen/packages"
|
||||
"github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
func NewProgram(t *testing.T, target *ssa.Target) ssa.Program {
|
||||
fset := token.NewFileSet()
|
||||
imp := packages.NewImporter(fset)
|
||||
return NewProgramEx(t, target, imp)
|
||||
}
|
||||
|
||||
func NewProgramEx(t *testing.T, target *ssa.Target, imp types.Importer) ssa.Program {
|
||||
prog := ssa.NewProgram(target)
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
rt, err := imp.Import(ssa.PkgRuntime)
|
||||
if err != nil {
|
||||
t.Fatal("load runtime failed:", err)
|
||||
}
|
||||
return rt
|
||||
})
|
||||
prog.SetPython(func() *types.Package {
|
||||
rt, err := imp.Import(ssa.PkgPython)
|
||||
if err != nil {
|
||||
t.Fatal("load python failed:", err)
|
||||
}
|
||||
return rt
|
||||
})
|
||||
return prog
|
||||
}
|
||||
|
||||
func Assert(t *testing.T, p ssa.Package, expected string) {
|
||||
t.Helper()
|
||||
if v := p.String(); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
118
ssa/ssatest/ssautil_test.go
Normal file
118
ssa/ssatest/ssautil_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
package ssatest
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/ssa"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
}
|
||||
|
||||
type mockImporter struct {
|
||||
pkgs map[string]*types.Package
|
||||
}
|
||||
|
||||
func newMockImporter() *mockImporter {
|
||||
return &mockImporter{
|
||||
pkgs: make(map[string]*types.Package),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockImporter) Import(path string) (*types.Package, error) {
|
||||
if pkg, ok := m.pkgs[path]; ok {
|
||||
return pkg, nil
|
||||
}
|
||||
pkg := types.NewPackage(path, path)
|
||||
m.pkgs[path] = pkg
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
func TestNewProgram(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
prog := NewProgram(t, target)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgram returned nil")
|
||||
}
|
||||
|
||||
// Set runtime package
|
||||
rtPkg := types.NewPackage(ssa.PkgRuntime, ssa.PkgRuntime)
|
||||
prog.SetRuntime(rtPkg)
|
||||
|
||||
// Set python package
|
||||
pyPkg := types.NewPackage(ssa.PkgPython, ssa.PkgPython)
|
||||
prog.SetRuntime(pyPkg)
|
||||
}
|
||||
|
||||
func TestNewProgramEx(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
imp := newMockImporter()
|
||||
prog := NewProgramEx(t, target, imp)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgramEx returned nil")
|
||||
}
|
||||
|
||||
// Set runtime package
|
||||
rtPkg := types.NewPackage(ssa.PkgRuntime, ssa.PkgRuntime)
|
||||
prog.SetRuntime(rtPkg)
|
||||
|
||||
// Set python package
|
||||
pyPkg := types.NewPackage(ssa.PkgPython, ssa.PkgPython)
|
||||
prog.SetRuntime(pyPkg)
|
||||
}
|
||||
|
||||
func TestAssert(t *testing.T) {
|
||||
target := &ssa.Target{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
GOARM: "7",
|
||||
}
|
||||
|
||||
prog := NewProgram(t, target)
|
||||
if prog == nil {
|
||||
t.Fatal("NewProgram returned nil")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg ssa.Package
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "test package path",
|
||||
pkg: prog.NewPackage("test", "test/path"),
|
||||
expected: "; ModuleID = 'test/path'\nsource_filename = \"test/path\"\n",
|
||||
},
|
||||
{
|
||||
name: "another package path",
|
||||
pkg: prog.NewPackage("another", "another/path"),
|
||||
expected: "; ModuleID = 'another/path'\nsource_filename = \"another/path\"\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Assert(t, tt.pkg, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
371
ssa/stmt_builder.go
Normal file
371
ssa/stmt_builder.go
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type aBasicBlock struct {
|
||||
first llvm.BasicBlock // a Go SSA basic block may have multiple LLVM basic blocks
|
||||
last llvm.BasicBlock
|
||||
fn Function
|
||||
idx int
|
||||
}
|
||||
|
||||
// BasicBlock represents a basic block in a function.
|
||||
type BasicBlock = *aBasicBlock
|
||||
|
||||
// Parent returns the function to which the basic block belongs.
|
||||
func (p BasicBlock) Parent() Function {
|
||||
return p.fn
|
||||
}
|
||||
|
||||
// Index returns the index of the basic block in the parent function.
|
||||
func (p BasicBlock) Index() int {
|
||||
return p.idx
|
||||
}
|
||||
|
||||
// Addr returns the address of the basic block.
|
||||
func (p BasicBlock) Addr() Expr {
|
||||
fn := p.fn
|
||||
return Expr{llvm.BlockAddress(fn.impl, p.first), fn.Prog.VoidPtr()}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type dbgExpr struct {
|
||||
ptr Expr
|
||||
val Expr
|
||||
}
|
||||
|
||||
type aBuilder struct {
|
||||
impl llvm.Builder
|
||||
blk BasicBlock
|
||||
Func Function
|
||||
Pkg Package
|
||||
Prog Program
|
||||
|
||||
dbgVars map[Expr]dbgExpr // save copied address and values for debug info
|
||||
diScopeCache map[*types.Scope]DIScope // avoid duplicated DILexicalBlock(s)
|
||||
}
|
||||
|
||||
// Builder represents a builder for creating instructions in a function.
|
||||
type Builder = *aBuilder
|
||||
|
||||
// EndBuild ends the build process of a function.
|
||||
func (b Builder) EndBuild() {
|
||||
b.Func.endDefer(b)
|
||||
}
|
||||
|
||||
// Dispose disposes of the builder.
|
||||
func (b Builder) Dispose() {
|
||||
b.impl.Dispose()
|
||||
}
|
||||
|
||||
// SetBlock means SetBlockEx(blk, AtEnd, true).
|
||||
func (b Builder) SetBlock(blk BasicBlock) Builder {
|
||||
if debugInstr {
|
||||
log.Printf("Block _llgo_%v:\n", blk.idx)
|
||||
}
|
||||
b.SetBlockEx(blk, AtEnd, true)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b Builder) setBlockMoveLast(blk BasicBlock) (next BasicBlock) {
|
||||
blkLast := blk.last
|
||||
last := blkLast.LastInstruction()
|
||||
last.RemoveFromParentAsInstruction()
|
||||
|
||||
impl := b.impl
|
||||
|
||||
next = b.Func.MakeBlock()
|
||||
impl.SetInsertPointAtEnd(next.last)
|
||||
impl.Insert(last)
|
||||
|
||||
impl.SetInsertPointAtEnd(blkLast)
|
||||
return
|
||||
}
|
||||
|
||||
type InsertPoint int
|
||||
|
||||
const (
|
||||
AtEnd InsertPoint = iota
|
||||
AtStart
|
||||
BeforeLast
|
||||
afterInit
|
||||
)
|
||||
|
||||
// SetBlockEx sets blk as current basic block and pos as its insert point.
|
||||
func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint, setBlk bool) {
|
||||
if b.Func != blk.fn {
|
||||
panic("mismatched function")
|
||||
}
|
||||
switch pos {
|
||||
case AtEnd:
|
||||
b.impl.SetInsertPointAtEnd(blk.last)
|
||||
case AtStart:
|
||||
b.impl.SetInsertPointBefore(blk.first.FirstInstruction())
|
||||
case BeforeLast:
|
||||
b.impl.SetInsertPointBefore(blk.last.LastInstruction())
|
||||
case afterInit:
|
||||
b.impl.SetInsertPointBefore(instrAfterInit(blk.first))
|
||||
default:
|
||||
panic("SetBlockEx: invalid pos")
|
||||
}
|
||||
if setBlk {
|
||||
b.blk = blk
|
||||
}
|
||||
}
|
||||
|
||||
func instrAfterInit(blk llvm.BasicBlock) llvm.Value {
|
||||
instr := blk.FirstInstruction()
|
||||
for {
|
||||
instr = llvm.NextInstruction(instr)
|
||||
if notInit(instr) {
|
||||
return instr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notInit(instr llvm.Value) bool {
|
||||
switch op := instr.InstructionOpcode(); op {
|
||||
case llvm.Call:
|
||||
if n := instr.OperandsCount(); n == 1 {
|
||||
fn := instr.Operand(0)
|
||||
return !strings.HasSuffix(fn.Name(), ".init")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Return emits a return instruction.
|
||||
func (b Builder) Return(results ...Expr) {
|
||||
if debugInstr {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprint(&b, "Return ")
|
||||
for i, arg := range results {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&b, ", ")
|
||||
}
|
||||
fmt.Fprint(&b, arg.impl)
|
||||
}
|
||||
log.Println(b.String())
|
||||
}
|
||||
switch n := len(results); n {
|
||||
case 0:
|
||||
b.impl.CreateRetVoid()
|
||||
case 1:
|
||||
raw := b.Func.raw.Type.(*types.Signature).Results().At(0).Type()
|
||||
ret := checkExpr(results[0], raw, b)
|
||||
b.impl.CreateRet(ret.impl)
|
||||
default:
|
||||
tret := b.Func.raw.Type.(*types.Signature).Results()
|
||||
n := tret.Len()
|
||||
typs := make([]Type, n)
|
||||
for i := 0; i < n; i++ {
|
||||
typs[i] = b.Prog.Type(tret.At(i).Type(), InC)
|
||||
}
|
||||
typ := b.Prog.Struct(typs...)
|
||||
expr := b.aggregateValue(typ, llvmParams(0, results, tret, b)...)
|
||||
b.impl.CreateRet(expr.impl)
|
||||
}
|
||||
}
|
||||
|
||||
// The Extract instruction yields component Index of Tuple.
|
||||
//
|
||||
// This is used to access the results of instructions with multiple
|
||||
// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
|
||||
// IndexExpr(Map).
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = extract t0 #1
|
||||
func (b Builder) Extract(x Expr, i int) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Extract %v, %d\n", x.impl, i)
|
||||
}
|
||||
return b.getField(x, i)
|
||||
}
|
||||
|
||||
// Jump emits a jump instruction.
|
||||
func (b Builder) Jump(jmpb BasicBlock) {
|
||||
if b.Func != jmpb.fn {
|
||||
panic("mismatched function")
|
||||
}
|
||||
if debugInstr {
|
||||
log.Printf("Jump _llgo_%v\n", jmpb.idx)
|
||||
}
|
||||
b.impl.CreateBr(jmpb.first)
|
||||
}
|
||||
|
||||
// IndirectJump emits an indirect jump instruction.
|
||||
func (b Builder) IndirectJump(addr Expr, dests []BasicBlock) {
|
||||
if debugInstr {
|
||||
log.Printf("IndirectJump %v\n", addr.impl)
|
||||
}
|
||||
ibr := b.impl.CreateIndirectBr(addr.impl, len(dests))
|
||||
for _, dest := range dests {
|
||||
ibr.AddDest(dest.first)
|
||||
}
|
||||
}
|
||||
|
||||
// If emits an if instruction.
|
||||
func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
|
||||
if b.Func != thenb.fn || b.Func != elseb.fn {
|
||||
panic("mismatched function")
|
||||
}
|
||||
if debugInstr {
|
||||
log.Printf("If %v, _llgo_%v, _llgo_%v\n", cond.impl, thenb.idx, elseb.idx)
|
||||
}
|
||||
b.impl.CreateCondBr(cond.impl, thenb.first, elseb.first)
|
||||
}
|
||||
|
||||
// IfThen emits an if-then instruction.
|
||||
func (b Builder) IfThen(cond Expr, then func()) {
|
||||
blks := b.Func.MakeBlocks(2)
|
||||
b.If(cond, blks[0], blks[1])
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
then()
|
||||
b.Jump(blks[1])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
b.blk.last = blks[1].last
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// For emits a for-loop instruction.
|
||||
func (b Builder) For(cond func() Expr, loop func()) {
|
||||
blks := b.Func.MakeBlocks(3)
|
||||
b.Jump(blks[0])
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
b.If(cond(), blks[1], blks[2])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
loop()
|
||||
b.Jump(blks[0])
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
b.blk.last = blks[2].last
|
||||
}
|
||||
*/
|
||||
|
||||
// Times emits a times-loop instruction.
|
||||
func (b Builder) Times(n Expr, loop func(i Expr)) {
|
||||
at := b.blk
|
||||
blks := b.Func.MakeBlocks(3)
|
||||
b.Jump(blks[0])
|
||||
b.SetBlockEx(blks[0], AtEnd, false)
|
||||
typ := n.Type
|
||||
phi := b.Phi(typ)
|
||||
b.If(b.BinOp(token.LSS, phi.Expr, n), blks[1], blks[2])
|
||||
b.SetBlockEx(blks[1], AtEnd, false)
|
||||
loop(phi.Expr)
|
||||
post := b.BinOp(token.ADD, phi.Expr, b.Prog.IntVal(1, typ))
|
||||
b.Jump(blks[0])
|
||||
phi.AddIncoming(b, []BasicBlock{at, blks[1]}, func(i int, blk BasicBlock) Expr {
|
||||
if i == 0 {
|
||||
return b.Prog.IntVal(0, typ)
|
||||
}
|
||||
return post
|
||||
})
|
||||
b.SetBlockEx(blks[2], AtEnd, false)
|
||||
b.blk.last = blks[2].last
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/*
|
||||
type caseStmt struct {
|
||||
v llvm.Value
|
||||
blk llvm.BasicBlock
|
||||
}
|
||||
|
||||
type aSwitch struct {
|
||||
v llvm.Value
|
||||
def llvm.BasicBlock
|
||||
cases []caseStmt
|
||||
}
|
||||
|
||||
// Switch represents a switch statement.
|
||||
type Switch = *aSwitch
|
||||
|
||||
// Case emits a case instruction.
|
||||
func (p Switch) Case(v Expr, blk BasicBlock) {
|
||||
if debugInstr {
|
||||
log.Printf("Case %v, _llgo_%v\n", v.impl, blk.idx)
|
||||
}
|
||||
p.cases = append(p.cases, caseStmt{v.impl, blk.first})
|
||||
}
|
||||
|
||||
// End ends a switch statement.
|
||||
func (p Switch) End(b Builder) {
|
||||
sw := b.impl.CreateSwitch(p.v, p.def, len(p.cases))
|
||||
for _, c := range p.cases {
|
||||
sw.AddCase(c.v, c.blk)
|
||||
}
|
||||
}
|
||||
|
||||
// Switch starts a switch statement.
|
||||
func (b Builder) Switch(v Expr, defb BasicBlock) Switch {
|
||||
if debugInstr {
|
||||
log.Printf("Switch %v, _llgo_%v\n", v.impl, defb.idx)
|
||||
}
|
||||
return &aSwitch{v.impl, defb.first, nil}
|
||||
}
|
||||
*/
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Phi represents a phi node.
|
||||
type Phi struct {
|
||||
Expr
|
||||
}
|
||||
|
||||
// AddIncoming adds incoming values to a phi node.
|
||||
func (p Phi) AddIncoming(b Builder, preds []BasicBlock, f func(i int, blk BasicBlock) Expr) {
|
||||
raw := p.raw.Type
|
||||
bs := llvmPredBlocks(preds)
|
||||
vals := make([]llvm.Value, len(preds))
|
||||
for iblk, blk := range preds {
|
||||
val := f(iblk, blk)
|
||||
vals[iblk] = checkExpr(val, raw, b).impl
|
||||
}
|
||||
p.impl.AddIncoming(vals, bs)
|
||||
}
|
||||
|
||||
func llvmPredBlocks(preds []BasicBlock) []llvm.BasicBlock {
|
||||
ret := make([]llvm.BasicBlock, len(preds))
|
||||
for i, v := range preds {
|
||||
ret[i] = v.last
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Phi returns a phi node.
|
||||
func (b Builder) Phi(t Type) Phi {
|
||||
phi := llvm.CreatePHI(b.impl, t.ll)
|
||||
return Phi{Expr{phi, t}}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
168
ssa/target.go
Normal file
168
ssa/target.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 (
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Target struct {
|
||||
GOOS string
|
||||
GOARCH string
|
||||
GOARM string // "5", "6", "7" (default)
|
||||
}
|
||||
|
||||
func (p *Target) targetData() llvm.TargetData {
|
||||
spec := p.toSpec()
|
||||
if spec.triple == "" {
|
||||
spec.triple = llvm.DefaultTargetTriple()
|
||||
}
|
||||
t, err := llvm.GetTargetFromTriple(spec.triple)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
machine := t.CreateTargetMachine(spec.triple, spec.cpu, spec.features, llvm.CodeGenLevelDefault, llvm.RelocDefault, llvm.CodeModelDefault)
|
||||
return machine.CreateTargetData()
|
||||
}
|
||||
|
||||
/*
|
||||
func (p *Program) targetMachine() llvm.TargetMachine {
|
||||
if p.tm.C == nil {
|
||||
spec := p.target.toSpec()
|
||||
target, err := llvm.GetTargetFromTriple(spec.triple)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.tm = target.CreateTargetMachine(
|
||||
spec.triple,
|
||||
spec.cpu,
|
||||
spec.features,
|
||||
llvm.CodeGenLevelDefault,
|
||||
llvm.RelocDefault,
|
||||
llvm.CodeModelDefault,
|
||||
)
|
||||
}
|
||||
return p.tm
|
||||
}
|
||||
*/
|
||||
|
||||
type targetSpec struct {
|
||||
triple string
|
||||
cpu string
|
||||
features string
|
||||
}
|
||||
|
||||
// TODO config
|
||||
func (p *Target) toSpec() (spec targetSpec) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
func (p *Target) toSpec() (spec targetSpec) {
|
||||
// Configure based on GOOS/GOARCH environment variables (falling back to
|
||||
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
|
||||
var llvmarch string
|
||||
var goarch = p.GOARCH
|
||||
var goos = p.GOOS
|
||||
if goarch == "" {
|
||||
goarch = runtime.GOARCH
|
||||
}
|
||||
if goos == "" {
|
||||
goos = runtime.GOOS
|
||||
}
|
||||
switch goarch {
|
||||
case "386":
|
||||
llvmarch = "i386"
|
||||
case "amd64":
|
||||
llvmarch = "x86_64"
|
||||
case "arm64":
|
||||
llvmarch = "aarch64"
|
||||
case "arm":
|
||||
switch p.GOARM {
|
||||
case "5":
|
||||
llvmarch = "armv5"
|
||||
case "6":
|
||||
llvmarch = "armv6"
|
||||
default:
|
||||
llvmarch = "armv7"
|
||||
}
|
||||
case "wasm":
|
||||
llvmarch = "wasm32"
|
||||
default:
|
||||
llvmarch = goarch
|
||||
}
|
||||
llvmvendor := "unknown"
|
||||
llvmos := goos
|
||||
switch goos {
|
||||
case "darwin":
|
||||
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
|
||||
// to iOS!
|
||||
llvmos = "macosx10.12.0"
|
||||
if llvmarch == "aarch64" {
|
||||
// Looks like Apple prefers to call this architecture ARM64
|
||||
// instead of AArch64.
|
||||
llvmarch = "arm64"
|
||||
llvmos = "macosx11.0.0"
|
||||
}
|
||||
llvmvendor = "apple"
|
||||
case "wasip1":
|
||||
llvmos = "wasi"
|
||||
}
|
||||
// Target triples (which actually have four components, but are called
|
||||
// triples for historical reasons) have the form:
|
||||
// arch-vendor-os-environment
|
||||
spec.triple = llvmarch + "-" + llvmvendor + "-" + llvmos
|
||||
if llvmos == "windows" {
|
||||
spec.triple += "-gnu"
|
||||
} else if goarch == "arm" {
|
||||
spec.triple += "-gnueabihf"
|
||||
}
|
||||
switch goarch {
|
||||
case "386":
|
||||
spec.cpu = "pentium4"
|
||||
spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "amd64":
|
||||
spec.cpu = "x86-64"
|
||||
spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
|
||||
case "arm":
|
||||
spec.cpu = "generic"
|
||||
switch llvmarch {
|
||||
case "armv5":
|
||||
spec.features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv6":
|
||||
spec.features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
case "armv7":
|
||||
spec.features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
|
||||
}
|
||||
case "arm64":
|
||||
spec.cpu = "generic"
|
||||
if goos == "darwin" {
|
||||
spec.features = "+neon"
|
||||
} else { // windows, linux
|
||||
spec.features = "+neon,-fmv"
|
||||
}
|
||||
case "wasm":
|
||||
spec.cpu = "generic"
|
||||
spec.features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
566
ssa/type.go
Normal file
566
ssa/type.go
Normal file
@@ -0,0 +1,566 @@
|
||||
/*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/ssa/abi"
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
var (
|
||||
tyAny = types.NewInterfaceType(nil, nil)
|
||||
|
||||
NoArgsNoRet = types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type valueKind = int
|
||||
|
||||
const (
|
||||
vkInvalid valueKind = iota
|
||||
vkSigned
|
||||
vkUnsigned
|
||||
vkFloat
|
||||
vkComplex
|
||||
vkString
|
||||
vkBool
|
||||
vkPtr
|
||||
vkFuncDecl
|
||||
vkFuncPtr
|
||||
vkClosure
|
||||
vkBuiltin
|
||||
vkPyFuncRef
|
||||
vkPyVarRef
|
||||
vkTuple
|
||||
vkSlice
|
||||
vkArray
|
||||
vkMap
|
||||
vkEface
|
||||
vkIface
|
||||
vkStruct
|
||||
vkChan
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func indexType(t types.Type) types.Type {
|
||||
typ := t
|
||||
retry:
|
||||
switch t := typ.(type) {
|
||||
case *types.Named:
|
||||
typ = t.Underlying()
|
||||
goto retry
|
||||
case *types.Slice:
|
||||
return t.Elem()
|
||||
case *types.Pointer:
|
||||
switch t := t.Elem().Underlying().(type) {
|
||||
case *types.Array:
|
||||
return t.Elem()
|
||||
}
|
||||
case *types.Array:
|
||||
return t.Elem()
|
||||
}
|
||||
panic("index: type doesn't support index - " + t.String())
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type goProgram aProgram
|
||||
|
||||
// Alignof returns the alignment of a variable of type T.
|
||||
// Alignof must implement the alignment guarantees required by the spec.
|
||||
// The result must be >= 1.
|
||||
func (p *goProgram) Alignof(T types.Type) int64 {
|
||||
return p.sizes.Alignof(T)
|
||||
}
|
||||
|
||||
// Offsetsof returns the offsets of the given struct fields, in bytes.
|
||||
// Offsetsof must implement the offset guarantees required by the spec.
|
||||
// A negative entry in the result indicates that the struct is too large.
|
||||
func (p *goProgram) Offsetsof(fields []*types.Var) (ret []int64) {
|
||||
prog := Program(unsafe.Pointer(p))
|
||||
ptrSize := int64(prog.PointerSize())
|
||||
extra := int64(0)
|
||||
ret = p.sizes.Offsetsof(fields)
|
||||
for i, f := range fields {
|
||||
ret[i] += extra
|
||||
extra += p.extraSize(f.Type(), ptrSize)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sizeof returns the size of a variable of type T.
|
||||
// Sizeof must implement the size guarantees required by the spec.
|
||||
// A negative result indicates that T is too large.
|
||||
func (p *goProgram) Sizeof(T types.Type) int64 {
|
||||
prog := Program(unsafe.Pointer(p))
|
||||
ptrSize := int64(prog.PointerSize())
|
||||
baseSize := prog.sizes.Sizeof(T) + p.extraSize(T, ptrSize)
|
||||
switch T.Underlying().(type) {
|
||||
case *types.Struct, *types.Array:
|
||||
return align(baseSize, prog.sizes.Alignof(T))
|
||||
}
|
||||
return baseSize
|
||||
}
|
||||
|
||||
func (p *goProgram) extraSize(typ types.Type, ptrSize int64) (ret int64) {
|
||||
retry:
|
||||
switch t := typ.(type) {
|
||||
case *types.Named:
|
||||
if v, ok := p.gocvt.typbg.Load(namedLinkname(t)); ok && v.(Background) == InC {
|
||||
return 0
|
||||
}
|
||||
typ = t.Underlying()
|
||||
goto retry
|
||||
case *types.Signature:
|
||||
return ptrSize
|
||||
case *types.Struct:
|
||||
n := t.NumFields()
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
ret += p.extraSize(f.Type(), ptrSize)
|
||||
}
|
||||
return
|
||||
case *types.Array:
|
||||
return p.extraSize(t.Elem(), ptrSize) * t.Len()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func align(x, a int64) int64 {
|
||||
return (x + a - 1) &^ (a - 1)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type rawType struct {
|
||||
Type types.Type
|
||||
}
|
||||
|
||||
type aType struct {
|
||||
ll llvm.Type
|
||||
raw rawType
|
||||
kind valueKind // value kind of llvm.Type
|
||||
}
|
||||
|
||||
type Type = *aType
|
||||
|
||||
// RawType returns the raw type.
|
||||
func (t Type) RawType() types.Type {
|
||||
return t.raw.Type
|
||||
}
|
||||
|
||||
// TypeSizes returns the sizes of the types.
|
||||
func (p Program) TypeSizes(sizes types.Sizes) types.Sizes {
|
||||
p.sizes = sizes
|
||||
return (*goProgram)(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
// TODO(xsw):
|
||||
// how to generate platform independent code?
|
||||
func (p Program) SizeOf(typ Type, n ...int64) uint64 {
|
||||
size := p.td.TypeAllocSize(typ.ll)
|
||||
if len(n) != 0 {
|
||||
size *= uint64(n[0])
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// OffsetOf returns the offset of a field in a struct.
|
||||
func (p Program) OffsetOf(typ Type, i int) uint64 {
|
||||
return p.td.ElementOffset(typ.ll, i)
|
||||
}
|
||||
|
||||
// SizeOf returns the size of a type.
|
||||
func SizeOf(prog Program, t Type, n ...int64) Expr {
|
||||
size := prog.SizeOf(t, n...)
|
||||
return prog.IntVal(size, prog.Uintptr())
|
||||
}
|
||||
|
||||
/*
|
||||
func OffsetOf(prog Program, t Type, i int) Expr {
|
||||
offset := prog.OffsetOf(t, i)
|
||||
return prog.IntVal(offset, prog.Uintptr())
|
||||
}
|
||||
*/
|
||||
|
||||
func (p Program) PointerSize() int {
|
||||
return p.ptrSize
|
||||
}
|
||||
|
||||
func (p Program) Slice(typ Type) Type {
|
||||
return p.rawType(types.NewSlice(typ.raw.Type))
|
||||
}
|
||||
|
||||
func (p Program) Pointer(typ Type) Type {
|
||||
return p.rawType(types.NewPointer(typ.raw.Type))
|
||||
}
|
||||
|
||||
func (p Program) Elem(typ Type) Type {
|
||||
elem := typ.raw.Type.Underlying().(interface {
|
||||
Elem() types.Type
|
||||
}).Elem()
|
||||
return p.rawType(elem)
|
||||
}
|
||||
|
||||
func (p Program) Index(typ Type) Type {
|
||||
return p.rawType(indexType(typ.raw.Type))
|
||||
}
|
||||
|
||||
func (p Program) Field(typ Type, i int) Type {
|
||||
var fld *types.Var
|
||||
switch t := typ.raw.Type.Underlying().(type) {
|
||||
case *types.Tuple:
|
||||
fld = t.At(i)
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Complex128:
|
||||
return p.Float64()
|
||||
case types.Complex64:
|
||||
return p.Float32()
|
||||
}
|
||||
panic("Field: basic type doesn't have fields")
|
||||
default:
|
||||
fld = t.(*types.Struct).Field(i)
|
||||
}
|
||||
return p.rawType(fld.Type())
|
||||
}
|
||||
|
||||
func (p Program) rawType(raw types.Type) Type {
|
||||
if v := p.typs.At(raw); v != nil {
|
||||
return v.(Type)
|
||||
}
|
||||
ret := p.toType(raw)
|
||||
p.typs.Set(raw, ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p Program) tyVoidPtr() llvm.Type {
|
||||
if p.voidPtrTy.IsNil() {
|
||||
p.voidPtrTy = llvm.PointerType(p.tyVoid(), 0)
|
||||
}
|
||||
return p.voidPtrTy
|
||||
}
|
||||
|
||||
func (p Program) tyVoid() llvm.Type {
|
||||
if p.voidType.IsNil() {
|
||||
p.voidType = p.ctx.VoidType()
|
||||
}
|
||||
return p.voidType
|
||||
}
|
||||
|
||||
func (p Program) tyInt1() llvm.Type {
|
||||
if p.int1Type.IsNil() {
|
||||
p.int1Type = p.ctx.Int1Type()
|
||||
}
|
||||
return p.int1Type
|
||||
}
|
||||
|
||||
func (p Program) tyInt() llvm.Type {
|
||||
if p.intType.IsNil() {
|
||||
p.intType = llvmIntType(p.ctx, p.td.PointerSize())
|
||||
}
|
||||
return p.intType
|
||||
}
|
||||
|
||||
func llvmIntType(ctx llvm.Context, size int) llvm.Type {
|
||||
if size <= 4 {
|
||||
return ctx.Int32Type()
|
||||
}
|
||||
return ctx.Int64Type()
|
||||
}
|
||||
|
||||
func (p Program) tyInt8() llvm.Type {
|
||||
if p.int8Type.IsNil() {
|
||||
p.int8Type = p.ctx.Int8Type()
|
||||
}
|
||||
return p.int8Type
|
||||
}
|
||||
|
||||
func (p Program) tyInt16() llvm.Type {
|
||||
if p.int16Type.IsNil() {
|
||||
p.int16Type = p.ctx.Int16Type()
|
||||
}
|
||||
return p.int16Type
|
||||
}
|
||||
|
||||
func (p Program) tyInt32() llvm.Type {
|
||||
if p.int32Type.IsNil() {
|
||||
p.int32Type = p.ctx.Int32Type()
|
||||
}
|
||||
return p.int32Type
|
||||
}
|
||||
|
||||
func (p Program) tyInt64() llvm.Type {
|
||||
if p.int64Type.IsNil() {
|
||||
p.int64Type = p.ctx.Int64Type()
|
||||
}
|
||||
return p.int64Type
|
||||
}
|
||||
|
||||
/*
|
||||
func (p Program) toTuple(typ *types.Tuple) Type {
|
||||
return &aType{p.toLLVMTuple(typ), rawType{typ}, vkTuple}
|
||||
}
|
||||
*/
|
||||
|
||||
func (p Program) toType(raw types.Type) Type {
|
||||
typ := rawType{raw}
|
||||
switch t := raw.(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Int:
|
||||
return &aType{p.tyInt(), typ, vkSigned}
|
||||
case types.Uint, types.Uintptr:
|
||||
return &aType{p.tyInt(), typ, vkUnsigned}
|
||||
case types.Bool:
|
||||
return &aType{p.tyInt1(), typ, vkBool}
|
||||
case types.Uint8:
|
||||
return &aType{p.tyInt8(), typ, vkUnsigned}
|
||||
case types.Int8:
|
||||
return &aType{p.tyInt8(), typ, vkSigned}
|
||||
case types.Int16:
|
||||
return &aType{p.tyInt16(), typ, vkSigned}
|
||||
case types.Uint16:
|
||||
return &aType{p.tyInt16(), typ, vkUnsigned}
|
||||
case types.Int32:
|
||||
return &aType{p.tyInt32(), typ, vkSigned}
|
||||
case types.Uint32:
|
||||
return &aType{p.tyInt32(), typ, vkUnsigned}
|
||||
case types.Int64:
|
||||
return &aType{p.tyInt64(), typ, vkSigned}
|
||||
case types.Uint64:
|
||||
return &aType{p.tyInt64(), typ, vkUnsigned}
|
||||
case types.Float32:
|
||||
return &aType{p.ctx.FloatType(), typ, vkFloat}
|
||||
case types.Float64:
|
||||
return &aType{p.ctx.DoubleType(), typ, vkFloat}
|
||||
case types.Complex64:
|
||||
return &aType{p.tyComplex64(), typ, vkComplex}
|
||||
case types.Complex128:
|
||||
return &aType{p.tyComplex128(), typ, vkComplex}
|
||||
case types.String:
|
||||
return &aType{p.rtString(), typ, vkString}
|
||||
case types.UnsafePointer:
|
||||
return &aType{p.tyVoidPtr(), typ, vkPtr}
|
||||
}
|
||||
case *types.Pointer:
|
||||
elem := p.rawType(t.Elem())
|
||||
return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr}
|
||||
case *types.Interface:
|
||||
if t.Empty() {
|
||||
return &aType{p.rtEface(), typ, vkEface}
|
||||
}
|
||||
return &aType{p.rtIface(), typ, vkIface}
|
||||
case *types.Slice:
|
||||
return &aType{p.rtSlice(), typ, vkSlice}
|
||||
case *types.Map:
|
||||
return &aType{llvm.PointerType(p.rtMap(), 0), typ, vkMap}
|
||||
case *types.Struct:
|
||||
ll, kind := p.toLLVMStruct(t)
|
||||
return &aType{ll, typ, kind}
|
||||
case *types.Named:
|
||||
return p.toNamed(t)
|
||||
case *types.Signature: // represents a C function pointer in raw type
|
||||
return &aType{p.toLLVMFuncPtr(t), typ, vkFuncPtr}
|
||||
case *types.Tuple:
|
||||
return &aType{p.toLLVMTuple(t), typ, vkTuple}
|
||||
case *types.Array:
|
||||
elem := p.rawType(t.Elem())
|
||||
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkArray}
|
||||
case *types.Chan:
|
||||
return &aType{llvm.PointerType(p.rtChan(), 0), typ, vkChan}
|
||||
case *types.Alias:
|
||||
return p.toType(types.Unalias(t))
|
||||
case *types.TypeParam:
|
||||
return p.toType(t.Constraint())
|
||||
}
|
||||
panic(fmt.Sprintf("toLLVMType: todo - %T\n", raw))
|
||||
}
|
||||
|
||||
func (p Program) toLLVMNamedStruct(name string, raw *types.Struct) llvm.Type {
|
||||
if typ, ok := p.named[name]; ok {
|
||||
return typ
|
||||
}
|
||||
t := p.ctx.StructCreateNamed(name)
|
||||
p.named[name] = t
|
||||
fields := p.toLLVMFields(raw)
|
||||
t.StructSetBody(fields, false)
|
||||
return t
|
||||
}
|
||||
|
||||
func (p Program) toLLVMStruct(raw *types.Struct) (ret llvm.Type, kind valueKind) {
|
||||
fields := p.toLLVMFields(raw)
|
||||
ret = p.ctx.StructType(fields, false)
|
||||
if isClosure(raw) {
|
||||
kind = vkClosure
|
||||
} else {
|
||||
kind = vkStruct
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isClosure(raw *types.Struct) bool {
|
||||
n := raw.NumFields()
|
||||
if n == 2 {
|
||||
f1, f2 := raw.Field(0), raw.Field(1)
|
||||
if _, ok := f1.Type().(*types.Signature); ok && f1.Name() == "$f" {
|
||||
return f2.Type() == types.Typ[types.UnsafePointer] && f2.Name() == "$data"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p Program) toLLVMFields(raw *types.Struct) (fields []llvm.Type) {
|
||||
n := raw.NumFields()
|
||||
if n > 0 {
|
||||
fields = make([]llvm.Type, n)
|
||||
for i := 0; i < n; i++ {
|
||||
fields[i] = p.rawType(p.patch(raw.Field(i).Type())).ll
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) toLLVMTuple(t *types.Tuple) llvm.Type {
|
||||
return p.ctx.StructType(p.toLLVMTypes(t, t.Len()), false)
|
||||
}
|
||||
|
||||
func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
|
||||
if n > 0 {
|
||||
ret = make([]llvm.Type, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ret[i] = p.rawType(p.patch(t.At(i).Type())).ll
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) toLLVMFunc(sig *types.Signature) llvm.Type {
|
||||
tParams := sig.Params()
|
||||
n := tParams.Len()
|
||||
hasVArg := hasNameValist(sig)
|
||||
if hasVArg {
|
||||
n--
|
||||
}
|
||||
params := p.toLLVMTypes(tParams, n)
|
||||
out := sig.Results()
|
||||
var ret llvm.Type
|
||||
switch nret := out.Len(); nret {
|
||||
case 0:
|
||||
ret = p.tyVoid()
|
||||
case 1:
|
||||
ret = p.rawType(out.At(0).Type()).ll
|
||||
default:
|
||||
ret = p.toLLVMTuple(out)
|
||||
}
|
||||
return llvm.FunctionType(ret, params, hasVArg)
|
||||
}
|
||||
|
||||
func (p Program) toLLVMFuncPtr(sig *types.Signature) llvm.Type {
|
||||
ft := p.toLLVMFunc(sig)
|
||||
return llvm.PointerType(ft, 0)
|
||||
}
|
||||
|
||||
func (p Program) retType(raw *types.Signature) Type {
|
||||
out := raw.Results()
|
||||
switch n := out.Len(); n {
|
||||
case 0:
|
||||
return p.Void()
|
||||
case 1:
|
||||
return p.rawType(out.At(0).Type())
|
||||
default:
|
||||
return &aType{p.toLLVMTuple(out), rawType{out}, vkTuple}
|
||||
}
|
||||
}
|
||||
|
||||
func (p Program) llvmNameOf(named *types.Named) (name string) {
|
||||
name = NameOf(named)
|
||||
if obj := named.Obj(); obj != nil && obj.Parent() != nil && obj.Parent() != obj.Pkg().Scope() {
|
||||
index := p.fnnamed[name]
|
||||
p.fnnamed[name] = index + 1
|
||||
name += fmt.Sprintf("#%v", index)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (p Program) toNamed(raw *types.Named) Type {
|
||||
switch t := raw.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
name := p.llvmNameOf(raw)
|
||||
kind := vkStruct
|
||||
if isClosure(t) {
|
||||
kind = vkClosure
|
||||
}
|
||||
return &aType{p.toLLVMNamedStruct(name, t), rawType{raw}, kind}
|
||||
default:
|
||||
typ := p.rawType(t)
|
||||
return &aType{typ.ll, rawType{raw}, typ.kind}
|
||||
}
|
||||
}
|
||||
|
||||
// NameOf returns the full name of a named type.
|
||||
func NameOf(typ *types.Named) string {
|
||||
return abi.FullName(typ.Obj().Pkg(), abi.NamedName(typ))
|
||||
}
|
||||
|
||||
// FullName returns the full name of a package member.
|
||||
func FullName(pkg *types.Package, name string) string {
|
||||
return abi.FullName(pkg, name)
|
||||
}
|
||||
|
||||
// PathOf returns the package path of the specified package.
|
||||
func PathOf(pkg *types.Package) string {
|
||||
return abi.PathOf(pkg)
|
||||
}
|
||||
|
||||
// FuncName:
|
||||
// - func: pkg.name
|
||||
// - method: pkg.T.name, pkg.(*T).name
|
||||
func FuncName(pkg *types.Package, name string, recv *types.Var, org bool) string {
|
||||
if recv != nil {
|
||||
var tName string
|
||||
t := recv.Type()
|
||||
if org {
|
||||
if tp, ok := t.(*types.Pointer); ok {
|
||||
tName = "(*" + tp.Elem().(*types.Named).Obj().Name() + ")"
|
||||
} else {
|
||||
tName = t.(*types.Named).Obj().Name()
|
||||
}
|
||||
} else {
|
||||
if tp, ok := t.(*types.Pointer); ok {
|
||||
tName = "(*" + abi.NamedName(tp.Elem().(*types.Named)) + ")"
|
||||
} else {
|
||||
tName = abi.NamedName(t.(*types.Named))
|
||||
}
|
||||
}
|
||||
return PathOf(pkg) + "." + tName + "." + name
|
||||
}
|
||||
ret := FullName(pkg, name)
|
||||
return ret
|
||||
}
|
||||
|
||||
func TypeArgs(typeArgs []types.Type) string {
|
||||
return abi.TypeArgs(typeArgs)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
308
ssa/type_cvt.go
Normal file
308
ssa/type_cvt.go
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type goTypes struct {
|
||||
typs map[unsafe.Pointer]unsafe.Pointer
|
||||
typbg sync.Map
|
||||
}
|
||||
|
||||
func newGoTypes() goTypes {
|
||||
typs := make(map[unsafe.Pointer]unsafe.Pointer)
|
||||
return goTypes{typs: typs}
|
||||
}
|
||||
|
||||
type Background int
|
||||
|
||||
const (
|
||||
inUnknown Background = iota
|
||||
InGo
|
||||
InC
|
||||
InPython
|
||||
)
|
||||
|
||||
// Type convert a Go/C type into raw type.
|
||||
// C type = raw type
|
||||
// Go type: convert to raw type (because of closure)
|
||||
func (p Program) Type(typ types.Type, bg Background) Type {
|
||||
if bg == InGo {
|
||||
typ, _ = p.gocvt.cvtType(typ)
|
||||
}
|
||||
return p.rawType(typ)
|
||||
}
|
||||
|
||||
// FuncDecl converts a Go/C function declaration into raw type.
|
||||
func (p Program) FuncDecl(sig *types.Signature, bg Background) Type {
|
||||
recv := sig.Recv()
|
||||
if bg == InGo {
|
||||
sig = p.gocvt.cvtFunc(sig, recv)
|
||||
} else if recv != nil { // even in C, we need to add ctx for method
|
||||
sig = FuncAddCtx(recv, sig)
|
||||
}
|
||||
return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl}
|
||||
}
|
||||
|
||||
// Closure creates a closture type for a function.
|
||||
func (p Program) Closure(sig *types.Signature) Type {
|
||||
closure := p.gocvt.cvtClosure(sig)
|
||||
return p.rawType(closure)
|
||||
}
|
||||
|
||||
func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
|
||||
switch t := typ.(type) {
|
||||
case *types.Basic:
|
||||
case *types.Pointer:
|
||||
if elem, cvt := p.cvtType(t.Elem()); cvt {
|
||||
return types.NewPointer(elem), true
|
||||
}
|
||||
case *types.Interface:
|
||||
return p.cvtInterface(t)
|
||||
case *types.Slice:
|
||||
if elem, cvt := p.cvtType(t.Elem()); cvt {
|
||||
return types.NewSlice(elem), true
|
||||
}
|
||||
case *types.Map:
|
||||
key, cvt1 := p.cvtType(t.Key())
|
||||
elem, cvt2 := p.cvtType(t.Elem())
|
||||
if cvt1 || cvt2 {
|
||||
return types.NewMap(key, elem), true
|
||||
}
|
||||
case *types.Struct:
|
||||
if isClosure(t) {
|
||||
return typ, false
|
||||
}
|
||||
return p.cvtStruct(t)
|
||||
case *types.Named:
|
||||
if v, ok := p.typbg.Load(namedLinkname(t)); ok && v.(Background) == InC {
|
||||
break
|
||||
}
|
||||
return p.cvtNamed(t)
|
||||
case *types.Signature:
|
||||
return p.cvtClosure(t), true
|
||||
case *types.Array:
|
||||
if elem, cvt := p.cvtType(t.Elem()); cvt {
|
||||
return types.NewArray(elem, t.Len()), true
|
||||
}
|
||||
case *types.Chan:
|
||||
if elem, cvt := p.cvtType(t.Elem()); cvt {
|
||||
return types.NewChan(t.Dir(), elem), true
|
||||
}
|
||||
case *types.Tuple:
|
||||
return p.cvtTuple(t)
|
||||
case *types.TypeParam:
|
||||
return typ.Underlying(), false
|
||||
case *types.Alias:
|
||||
return p.cvtType(types.Unalias(t))
|
||||
default:
|
||||
panic(fmt.Sprintf("cvtType: unexpected type - %T", typ))
|
||||
}
|
||||
return typ, false
|
||||
}
|
||||
|
||||
func namedLinkname(t *types.Named) string {
|
||||
obj := t.Obj()
|
||||
if obj.Pkg() != nil {
|
||||
return obj.Pkg().Path() + "." + obj.Name()
|
||||
}
|
||||
return obj.Name()
|
||||
}
|
||||
|
||||
func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
|
||||
if v, ok := p.typs[unsafe.Pointer(t)]; ok {
|
||||
raw = (*types.Named)(v)
|
||||
cvt = t != raw
|
||||
return
|
||||
}
|
||||
n := t.NumMethods()
|
||||
methods := make([]*types.Func, n)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i) // don't need to convert method signature
|
||||
methods[i] = m
|
||||
}
|
||||
named := types.NewNamed(t.Obj(), types.Typ[types.Int], methods)
|
||||
if tp := t.TypeParams(); tp != nil {
|
||||
list := make([]*types.TypeParam, tp.Len())
|
||||
for i := 0; i < tp.Len(); i++ {
|
||||
param := tp.At(i)
|
||||
list[i] = types.NewTypeParam(param.Obj(), param.Constraint())
|
||||
}
|
||||
named.SetTypeParams(list)
|
||||
}
|
||||
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(t)
|
||||
if tund, cvt := p.cvtType(t.Underlying()); cvt {
|
||||
named.SetUnderlying(tund)
|
||||
if typ, ok := Instantiate(named, t); ok {
|
||||
named = typ.(*types.Named)
|
||||
}
|
||||
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(named)
|
||||
return named, true
|
||||
}
|
||||
return t, false
|
||||
}
|
||||
|
||||
func Instantiate(orig types.Type, t *types.Named) (types.Type, bool) {
|
||||
if tp := t.TypeArgs(); tp != nil {
|
||||
targs := make([]types.Type, tp.Len())
|
||||
for i := 0; i < tp.Len(); i++ {
|
||||
targs[i] = tp.At(i)
|
||||
}
|
||||
if typ, err := types.Instantiate(nil, orig, targs, true); err == nil {
|
||||
return typ, true
|
||||
}
|
||||
}
|
||||
return orig, false
|
||||
}
|
||||
|
||||
func (p goTypes) cvtClosure(sig *types.Signature) *types.Struct {
|
||||
raw := p.cvtFunc(sig, nil)
|
||||
flds := []*types.Var{
|
||||
types.NewField(token.NoPos, nil, "$f", raw, false),
|
||||
types.NewField(token.NoPos, nil, "$data", types.Typ[types.UnsafePointer], false),
|
||||
}
|
||||
return types.NewStruct(flds, nil)
|
||||
}
|
||||
|
||||
func (p goTypes) cvtFunc(sig *types.Signature, recv *types.Var) (raw *types.Signature) {
|
||||
if recv != nil {
|
||||
sig = FuncAddCtx(recv, sig)
|
||||
}
|
||||
params, cvt1 := p.cvtTuple(sig.Params())
|
||||
results, cvt2 := p.cvtTuple(sig.Results())
|
||||
if cvt1 || cvt2 {
|
||||
return types.NewSignatureType(nil, nil, nil, params, results, sig.Variadic())
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
func (p goTypes) cvtTuple(t *types.Tuple) (*types.Tuple, bool) {
|
||||
n := t.Len()
|
||||
vars := make([]*types.Var, n)
|
||||
needcvt := false
|
||||
for i := 0; i < n; i++ {
|
||||
v := t.At(i)
|
||||
if t, cvt := p.cvtType(v.Type()); cvt {
|
||||
v = types.NewParam(v.Pos(), v.Pkg(), v.Name(), t)
|
||||
needcvt = true
|
||||
}
|
||||
vars[i] = v
|
||||
}
|
||||
if needcvt {
|
||||
return types.NewTuple(vars...), true
|
||||
}
|
||||
return t, false
|
||||
}
|
||||
|
||||
func (p goTypes) cvtExplicitMethods(typ *types.Interface) ([]*types.Func, bool) {
|
||||
n := typ.NumExplicitMethods()
|
||||
methods := make([]*types.Func, n)
|
||||
needcvt := false
|
||||
for i := 0; i < n; i++ {
|
||||
m := typ.ExplicitMethod(i)
|
||||
sig := m.Type().(*types.Signature)
|
||||
if raw := p.cvtFunc(sig, nil); sig != raw {
|
||||
m = types.NewFunc(m.Pos(), m.Pkg(), m.Name(), raw)
|
||||
needcvt = true
|
||||
}
|
||||
methods[i] = m
|
||||
}
|
||||
return methods, needcvt
|
||||
}
|
||||
|
||||
func (p goTypes) cvtEmbeddedTypes(typ *types.Interface) ([]types.Type, bool) {
|
||||
n := typ.NumEmbeddeds()
|
||||
embeddeds := make([]types.Type, n)
|
||||
needcvt := false
|
||||
for i := 0; i < n; i++ {
|
||||
t := typ.EmbeddedType(i)
|
||||
if raw, cvt := p.cvtType(t); cvt {
|
||||
t = raw
|
||||
needcvt = true
|
||||
}
|
||||
embeddeds[i] = t
|
||||
}
|
||||
return embeddeds, needcvt
|
||||
}
|
||||
|
||||
func (p goTypes) cvtInterface(typ *types.Interface) (raw *types.Interface, cvt bool) {
|
||||
if v, ok := p.typs[unsafe.Pointer(typ)]; ok {
|
||||
raw = (*types.Interface)(v)
|
||||
cvt = typ != raw
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
p.typs[unsafe.Pointer(typ)] = unsafe.Pointer(raw)
|
||||
}()
|
||||
methods, cvt1 := p.cvtExplicitMethods(typ)
|
||||
embeddeds, cvt2 := p.cvtEmbeddedTypes(typ)
|
||||
if cvt1 || cvt2 {
|
||||
return types.NewInterfaceType(methods, embeddeds), true
|
||||
}
|
||||
return typ, false
|
||||
}
|
||||
|
||||
func (p goTypes) cvtStruct(typ *types.Struct) (raw *types.Struct, cvt bool) {
|
||||
if v, ok := p.typs[unsafe.Pointer(typ)]; ok {
|
||||
raw = (*types.Struct)(v)
|
||||
cvt = typ != raw
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
p.typs[unsafe.Pointer(typ)] = unsafe.Pointer(raw)
|
||||
}()
|
||||
n := typ.NumFields()
|
||||
flds := make([]*types.Var, n)
|
||||
needcvt := false
|
||||
for i := 0; i < n; i++ {
|
||||
f := typ.Field(i)
|
||||
if t, cvt := p.cvtType(f.Type()); cvt {
|
||||
f = types.NewField(f.Pos(), f.Pkg(), f.Name(), t, f.Anonymous())
|
||||
needcvt = true
|
||||
}
|
||||
flds[i] = f
|
||||
}
|
||||
if needcvt {
|
||||
return types.NewStruct(flds, nil), true
|
||||
}
|
||||
return typ, false
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// FuncAddCtx adds a ctx to a function signature.
|
||||
func FuncAddCtx(ctx *types.Var, sig *types.Signature) *types.Signature {
|
||||
tParams := sig.Params()
|
||||
nParams := tParams.Len()
|
||||
params := make([]*types.Var, nParams+1)
|
||||
params[0] = ctx
|
||||
for i := 0; i < nParams; i++ {
|
||||
params[i+1] = tParams.At(i)
|
||||
}
|
||||
return types.NewSignatureType(
|
||||
nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user