mirror of
https://github.com/goplus/llgo.git
synced 2025-09-26 19:51:21 +08:00
574 lines
13 KiB
Go
574 lines
13 KiB
Go
/*
|
|
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package ssa
|
|
|
|
import (
|
|
"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 {
|
|
named, ptr := recvNamed(recv.Type())
|
|
var tName string
|
|
if org {
|
|
tName = named.Obj().Name()
|
|
} else {
|
|
tName = abi.NamedName(named)
|
|
}
|
|
if ptr {
|
|
tName = "(*" + tName + ")"
|
|
}
|
|
return PathOf(pkg) + "." + tName + "." + name
|
|
}
|
|
ret := FullName(pkg, name)
|
|
return ret
|
|
}
|
|
|
|
func recvNamed(t types.Type) (typ *types.Named, ptr bool) {
|
|
if tp, ok := t.(*types.Pointer); ok {
|
|
t = tp.Elem()
|
|
ptr = true
|
|
}
|
|
if _, ok := t.(*types.Alias); ok {
|
|
t = types.Unalias(t)
|
|
}
|
|
typ, _ = t.(*types.Named)
|
|
return
|
|
}
|
|
|
|
func TypeArgs(typeArgs []types.Type) string {
|
|
return abi.TypeArgs(typeArgs)
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|