forked from OSchip/llvm-project
2034 lines
55 KiB
Go
2034 lines
55 KiB
Go
//===- typemap.go - type and type descriptor mapping ----------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the mapping from go/types types to LLVM types and to
|
|
// type descriptors.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package irgen
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
|
"llvm.org/llgo/third_party/go.tools/go/ssa/ssautil"
|
|
"llvm.org/llgo/third_party/go.tools/go/types"
|
|
"llvm.org/llgo/third_party/go.tools/go/types/typeutil"
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
)
|
|
|
|
type MethodResolver interface {
|
|
ResolveMethod(*types.Selection) *govalue
|
|
}
|
|
|
|
// llvmTypeMap is provides a means of mapping from a types.Map
|
|
// to llgo's corresponding LLVM type representation.
|
|
type llvmTypeMap struct {
|
|
sizes *types.StdSizes
|
|
ctx llvm.Context
|
|
target llvm.TargetData
|
|
inttype llvm.Type
|
|
stringType llvm.Type
|
|
|
|
types typeutil.Map
|
|
}
|
|
|
|
type typeDescInfo struct {
|
|
global llvm.Value
|
|
commonTypePtr llvm.Value
|
|
mapDescPtr llvm.Value
|
|
gc, gcPtr llvm.Value
|
|
|
|
interfaceMethodTables typeutil.Map
|
|
}
|
|
|
|
type TypeMap struct {
|
|
*llvmTypeMap
|
|
mc manglerContext
|
|
|
|
module llvm.Module
|
|
pkgpath string
|
|
types, algs typeutil.Map
|
|
runtime *runtimeInterface
|
|
methodResolver MethodResolver
|
|
types.MethodSetCache
|
|
|
|
commonTypeType, uncommonTypeType, ptrTypeType, funcTypeType, arrayTypeType, sliceTypeType, mapTypeType, chanTypeType, interfaceTypeType, structTypeType llvm.Type
|
|
mapDescType llvm.Type
|
|
|
|
methodType, imethodType, structFieldType llvm.Type
|
|
|
|
typeSliceType, methodSliceType, imethodSliceType, structFieldSliceType llvm.Type
|
|
|
|
hashFnType, equalFnType llvm.Type
|
|
|
|
hashFnEmptyInterface, hashFnInterface, hashFnFloat, hashFnComplex, hashFnString, hashFnIdentity, hashFnError llvm.Value
|
|
equalFnEmptyInterface, equalFnInterface, equalFnFloat, equalFnComplex, equalFnString, equalFnIdentity, equalFnError llvm.Value
|
|
|
|
zeroType llvm.Type
|
|
zeroValue llvm.Value
|
|
}
|
|
|
|
func NewLLVMTypeMap(ctx llvm.Context, target llvm.TargetData) *llvmTypeMap {
|
|
// spec says int is either 32-bit or 64-bit.
|
|
// ABI currently requires sizeof(int) == sizeof(uint) == sizeof(uintptr).
|
|
inttype := ctx.IntType(8 * target.PointerSize())
|
|
|
|
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
|
elements := []llvm.Type{i8ptr, inttype}
|
|
stringType := llvm.StructType(elements, false)
|
|
|
|
return &llvmTypeMap{
|
|
ctx: ctx,
|
|
sizes: &types.StdSizes{
|
|
WordSize: int64(target.PointerSize()),
|
|
MaxAlign: 8,
|
|
},
|
|
target: target,
|
|
inttype: inttype,
|
|
stringType: stringType,
|
|
}
|
|
}
|
|
|
|
func NewTypeMap(pkg *ssa.Package, llvmtm *llvmTypeMap, module llvm.Module, r *runtimeInterface, mr MethodResolver) *TypeMap {
|
|
tm := &TypeMap{
|
|
llvmTypeMap: llvmtm,
|
|
module: module,
|
|
pkgpath: pkg.Object.Path(),
|
|
runtime: r,
|
|
methodResolver: mr,
|
|
}
|
|
|
|
tm.mc.init(pkg.Prog, &tm.MethodSetCache)
|
|
|
|
uintptrType := tm.inttype
|
|
voidPtrType := llvm.PointerType(tm.ctx.Int8Type(), 0)
|
|
boolType := llvm.Int8Type()
|
|
stringPtrType := llvm.PointerType(tm.stringType, 0)
|
|
|
|
// Create runtime algorithm function types.
|
|
params := []llvm.Type{voidPtrType, uintptrType}
|
|
tm.hashFnType = llvm.FunctionType(uintptrType, params, false)
|
|
params = []llvm.Type{voidPtrType, voidPtrType, uintptrType}
|
|
tm.equalFnType = llvm.FunctionType(boolType, params, false)
|
|
|
|
tm.hashFnEmptyInterface = llvm.AddFunction(tm.module, "__go_type_hash_empty_interface", tm.hashFnType)
|
|
tm.hashFnInterface = llvm.AddFunction(tm.module, "__go_type_hash_interface", tm.hashFnType)
|
|
tm.hashFnFloat = llvm.AddFunction(tm.module, "__go_type_hash_float", tm.hashFnType)
|
|
tm.hashFnComplex = llvm.AddFunction(tm.module, "__go_type_hash_complex", tm.hashFnType)
|
|
tm.hashFnString = llvm.AddFunction(tm.module, "__go_type_hash_string", tm.hashFnType)
|
|
tm.hashFnIdentity = llvm.AddFunction(tm.module, "__go_type_hash_identity", tm.hashFnType)
|
|
tm.hashFnError = llvm.AddFunction(tm.module, "__go_type_hash_error", tm.hashFnType)
|
|
|
|
tm.equalFnEmptyInterface = llvm.AddFunction(tm.module, "__go_type_equal_empty_interface", tm.equalFnType)
|
|
tm.equalFnInterface = llvm.AddFunction(tm.module, "__go_type_equal_interface", tm.equalFnType)
|
|
tm.equalFnFloat = llvm.AddFunction(tm.module, "__go_type_equal_float", tm.equalFnType)
|
|
tm.equalFnComplex = llvm.AddFunction(tm.module, "__go_type_equal_complex", tm.equalFnType)
|
|
tm.equalFnString = llvm.AddFunction(tm.module, "__go_type_equal_string", tm.equalFnType)
|
|
tm.equalFnIdentity = llvm.AddFunction(tm.module, "__go_type_equal_identity", tm.equalFnType)
|
|
tm.equalFnError = llvm.AddFunction(tm.module, "__go_type_equal_error", tm.equalFnType)
|
|
|
|
// The body of this type is set in emitTypeDescInitializers once we have scanned
|
|
// every type, as it needs to be as large and well aligned as the
|
|
// largest/most aligned type.
|
|
tm.zeroType = tm.ctx.StructCreateNamed("zero")
|
|
tm.zeroValue = llvm.AddGlobal(tm.module, tm.zeroType, "go$zerovalue")
|
|
tm.zeroValue.SetLinkage(llvm.CommonLinkage)
|
|
tm.zeroValue.SetInitializer(llvm.ConstNull(tm.zeroType))
|
|
|
|
tm.commonTypeType = tm.ctx.StructCreateNamed("commonType")
|
|
commonTypeTypePtr := llvm.PointerType(tm.commonTypeType, 0)
|
|
|
|
tm.methodType = tm.ctx.StructCreateNamed("method")
|
|
tm.methodType.StructSetBody([]llvm.Type{
|
|
stringPtrType, // name
|
|
stringPtrType, // pkgPath
|
|
commonTypeTypePtr, // mtype (without receiver)
|
|
commonTypeTypePtr, // type (with receiver)
|
|
voidPtrType, // function
|
|
}, false)
|
|
|
|
tm.methodSliceType = tm.makeNamedSliceType("methodSlice", tm.methodType)
|
|
|
|
tm.uncommonTypeType = tm.ctx.StructCreateNamed("uncommonType")
|
|
tm.uncommonTypeType.StructSetBody([]llvm.Type{
|
|
stringPtrType, // name
|
|
stringPtrType, // pkgPath
|
|
tm.methodSliceType, // methods
|
|
}, false)
|
|
|
|
tm.commonTypeType.StructSetBody([]llvm.Type{
|
|
tm.ctx.Int8Type(), // Kind
|
|
tm.ctx.Int8Type(), // align
|
|
tm.ctx.Int8Type(), // fieldAlign
|
|
uintptrType, // size
|
|
tm.ctx.Int32Type(), // hash
|
|
llvm.PointerType(tm.hashFnType, 0), // hashfn
|
|
llvm.PointerType(tm.equalFnType, 0), // equalfn
|
|
voidPtrType, // gc
|
|
stringPtrType, // string
|
|
llvm.PointerType(tm.uncommonTypeType, 0), // uncommonType
|
|
commonTypeTypePtr, // ptrToThis
|
|
llvm.PointerType(tm.zeroType, 0), // zero
|
|
}, false)
|
|
|
|
tm.typeSliceType = tm.makeNamedSliceType("typeSlice", commonTypeTypePtr)
|
|
|
|
tm.ptrTypeType = tm.ctx.StructCreateNamed("ptrType")
|
|
tm.ptrTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
commonTypeTypePtr,
|
|
}, false)
|
|
|
|
tm.funcTypeType = tm.ctx.StructCreateNamed("funcType")
|
|
tm.funcTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
tm.ctx.Int8Type(), // dotdotdot
|
|
tm.typeSliceType, // in
|
|
tm.typeSliceType, // out
|
|
}, false)
|
|
|
|
tm.arrayTypeType = tm.ctx.StructCreateNamed("arrayType")
|
|
tm.arrayTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
commonTypeTypePtr, // elem
|
|
commonTypeTypePtr, // slice
|
|
tm.inttype, // len
|
|
}, false)
|
|
|
|
tm.sliceTypeType = tm.ctx.StructCreateNamed("sliceType")
|
|
tm.sliceTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
commonTypeTypePtr, // elem
|
|
}, false)
|
|
|
|
tm.mapTypeType = tm.ctx.StructCreateNamed("mapType")
|
|
tm.mapTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
commonTypeTypePtr, // key
|
|
commonTypeTypePtr, // elem
|
|
}, false)
|
|
|
|
tm.chanTypeType = tm.ctx.StructCreateNamed("chanType")
|
|
tm.chanTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
commonTypeTypePtr, // elem
|
|
tm.inttype, // dir
|
|
}, false)
|
|
|
|
tm.imethodType = tm.ctx.StructCreateNamed("imethod")
|
|
tm.imethodType.StructSetBody([]llvm.Type{
|
|
stringPtrType, // name
|
|
stringPtrType, // pkgPath
|
|
commonTypeTypePtr, // typ
|
|
}, false)
|
|
|
|
tm.imethodSliceType = tm.makeNamedSliceType("imethodSlice", tm.imethodType)
|
|
|
|
tm.interfaceTypeType = tm.ctx.StructCreateNamed("interfaceType")
|
|
tm.interfaceTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
tm.imethodSliceType,
|
|
}, false)
|
|
|
|
tm.structFieldType = tm.ctx.StructCreateNamed("structField")
|
|
tm.structFieldType.StructSetBody([]llvm.Type{
|
|
stringPtrType, // name
|
|
stringPtrType, // pkgPath
|
|
commonTypeTypePtr, // typ
|
|
stringPtrType, // tag
|
|
tm.inttype, // offset
|
|
}, false)
|
|
|
|
tm.structFieldSliceType = tm.makeNamedSliceType("structFieldSlice", tm.structFieldType)
|
|
|
|
tm.structTypeType = tm.ctx.StructCreateNamed("structType")
|
|
tm.structTypeType.StructSetBody([]llvm.Type{
|
|
tm.commonTypeType,
|
|
tm.structFieldSliceType, // fields
|
|
}, false)
|
|
|
|
tm.mapDescType = tm.ctx.StructCreateNamed("mapDesc")
|
|
tm.mapDescType.StructSetBody([]llvm.Type{
|
|
commonTypeTypePtr, // map_descriptor
|
|
tm.inttype, // entry_size
|
|
tm.inttype, // key_offset
|
|
tm.inttype, // value_offset
|
|
}, false)
|
|
|
|
return tm
|
|
}
|
|
|
|
func (tm *llvmTypeMap) ToLLVM(t types.Type) llvm.Type {
|
|
return tm.toLLVM(t, "")
|
|
}
|
|
|
|
func (tm *llvmTypeMap) toLLVM(t types.Type, name string) llvm.Type {
|
|
lt, ok := tm.types.At(t).(llvm.Type)
|
|
if !ok {
|
|
lt = tm.makeLLVMType(t, name)
|
|
if lt.IsNil() {
|
|
panic(fmt.Sprint("Failed to create LLVM type for: ", t))
|
|
}
|
|
tm.types.Set(t, lt)
|
|
}
|
|
return lt
|
|
}
|
|
|
|
func (tm *llvmTypeMap) makeLLVMType(t types.Type, name string) llvm.Type {
|
|
return tm.getBackendType(t).ToLLVM(tm.ctx)
|
|
}
|
|
|
|
func (tm *llvmTypeMap) Offsetsof(fields []*types.Var) []int64 {
|
|
offsets := make([]int64, len(fields))
|
|
var o int64
|
|
for i, f := range fields {
|
|
a := tm.Alignof(f.Type())
|
|
o = align(o, a)
|
|
offsets[i] = o
|
|
o += tm.Sizeof(f.Type())
|
|
}
|
|
return offsets
|
|
}
|
|
|
|
var basicSizes = [...]byte{
|
|
types.Bool: 1,
|
|
types.Int8: 1,
|
|
types.Int16: 2,
|
|
types.Int32: 4,
|
|
types.Int64: 8,
|
|
types.Uint8: 1,
|
|
types.Uint16: 2,
|
|
types.Uint32: 4,
|
|
types.Uint64: 8,
|
|
types.Float32: 4,
|
|
types.Float64: 8,
|
|
types.Complex64: 8,
|
|
types.Complex128: 16,
|
|
}
|
|
|
|
func (tm *llvmTypeMap) Sizeof(T types.Type) int64 {
|
|
switch t := T.Underlying().(type) {
|
|
case *types.Basic:
|
|
k := t.Kind()
|
|
if int(k) < len(basicSizes) {
|
|
if s := basicSizes[k]; s > 0 {
|
|
return int64(s)
|
|
}
|
|
}
|
|
if k == types.String {
|
|
return tm.sizes.WordSize * 2
|
|
}
|
|
case *types.Array:
|
|
a := tm.Alignof(t.Elem())
|
|
z := tm.Sizeof(t.Elem())
|
|
return align(z, a) * t.Len() // may be 0
|
|
case *types.Slice:
|
|
return tm.sizes.WordSize * 3
|
|
case *types.Struct:
|
|
n := t.NumFields()
|
|
if n == 0 {
|
|
return 0
|
|
}
|
|
fields := make([]*types.Var, t.NumFields())
|
|
for i := 0; i != t.NumFields(); i++ {
|
|
fields[i] = t.Field(i)
|
|
}
|
|
offsets := tm.Offsetsof(fields)
|
|
return align(offsets[n-1]+tm.Sizeof(t.Field(n-1).Type()), tm.Alignof(t))
|
|
case *types.Interface:
|
|
return tm.sizes.WordSize * 2
|
|
}
|
|
return tm.sizes.WordSize // catch-all
|
|
}
|
|
|
|
func (tm *llvmTypeMap) Alignof(t types.Type) int64 {
|
|
return tm.sizes.Alignof(t)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func (tm *TypeMap) ToRuntime(t types.Type) llvm.Value {
|
|
return llvm.ConstBitCast(tm.getTypeDescriptorPointer(t), llvm.PointerType(llvm.Int8Type(), 0))
|
|
}
|
|
|
|
type localNamedTypeInfo struct {
|
|
functionName string
|
|
scopeNum int
|
|
}
|
|
|
|
type namedTypeInfo struct {
|
|
pkgname, pkgpath string
|
|
name string
|
|
localNamedTypeInfo
|
|
}
|
|
|
|
type manglerContext struct {
|
|
ti map[*types.Named]localNamedTypeInfo
|
|
msc *types.MethodSetCache
|
|
}
|
|
|
|
// Assembles the method set into the order that gccgo uses (unexported methods first).
|
|
// TODO(pcc): cache this.
|
|
func orderedMethodSet(ms *types.MethodSet) []*types.Selection {
|
|
oms := make([]*types.Selection, ms.Len())
|
|
omsi := 0
|
|
for i := 0; i != ms.Len(); i++ {
|
|
if sel := ms.At(i); !sel.Obj().Exported() {
|
|
oms[omsi] = sel
|
|
omsi++
|
|
}
|
|
}
|
|
for i := 0; i != ms.Len(); i++ {
|
|
if sel := ms.At(i); sel.Obj().Exported() {
|
|
oms[omsi] = sel
|
|
omsi++
|
|
}
|
|
}
|
|
return oms
|
|
}
|
|
|
|
func (ctx *manglerContext) init(prog *ssa.Program, msc *types.MethodSetCache) {
|
|
ctx.msc = msc
|
|
ctx.ti = make(map[*types.Named]localNamedTypeInfo)
|
|
for f, _ := range ssautil.AllFunctions(prog) {
|
|
scopeNum := 0
|
|
var addNamedTypesToMap func(*types.Scope)
|
|
addNamedTypesToMap = func(scope *types.Scope) {
|
|
hasNamedTypes := false
|
|
for _, n := range scope.Names() {
|
|
if tn, ok := scope.Lookup(n).(*types.TypeName); ok {
|
|
hasNamedTypes = true
|
|
ctx.ti[tn.Type().(*types.Named)] = localNamedTypeInfo{f.Name(), scopeNum}
|
|
}
|
|
}
|
|
if hasNamedTypes {
|
|
scopeNum++
|
|
}
|
|
for i := 0; i != scope.NumChildren(); i++ {
|
|
addNamedTypesToMap(scope.Child(i))
|
|
}
|
|
}
|
|
if fobj, ok := f.Object().(*types.Func); ok && fobj.Scope() != nil {
|
|
addNamedTypesToMap(fobj.Scope())
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ctx *manglerContext) getNamedTypeInfo(t types.Type) (nti namedTypeInfo) {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.Byte:
|
|
nti.name = "uint8"
|
|
case types.Rune:
|
|
nti.name = "int32"
|
|
case types.UnsafePointer:
|
|
nti.pkgname = "unsafe"
|
|
nti.pkgpath = "unsafe"
|
|
nti.name = "Pointer"
|
|
default:
|
|
nti.name = t.Name()
|
|
}
|
|
|
|
case *types.Named:
|
|
obj := t.Obj()
|
|
if pkg := obj.Pkg(); pkg != nil {
|
|
nti.pkgname = obj.Pkg().Name()
|
|
nti.pkgpath = obj.Pkg().Path()
|
|
}
|
|
nti.name = obj.Name()
|
|
nti.localNamedTypeInfo = ctx.ti[t]
|
|
|
|
default:
|
|
panic("not a named type")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleSignature(s *types.Signature, recv *types.Var, b *bytes.Buffer) {
|
|
b.WriteRune('F')
|
|
if recv != nil {
|
|
b.WriteRune('m')
|
|
ctx.mangleType(recv.Type(), b)
|
|
}
|
|
|
|
if p := s.Params(); p.Len() != 0 {
|
|
b.WriteRune('p')
|
|
for i := 0; i != p.Len(); i++ {
|
|
ctx.mangleType(p.At(i).Type(), b)
|
|
}
|
|
if s.Variadic() {
|
|
b.WriteRune('V')
|
|
}
|
|
b.WriteRune('e')
|
|
}
|
|
|
|
if r := s.Results(); r.Len() != 0 {
|
|
b.WriteRune('r')
|
|
for i := 0; i != r.Len(); i++ {
|
|
ctx.mangleType(r.At(i).Type(), b)
|
|
}
|
|
b.WriteRune('e')
|
|
}
|
|
|
|
b.WriteRune('e')
|
|
}
|
|
|
|
func (ctx *manglerContext) manglePackagePath(pkgpath string, b *bytes.Buffer) {
|
|
pkgpath = strings.Replace(pkgpath, "/", "_", -1)
|
|
pkgpath = strings.Replace(pkgpath, ".", "_", -1)
|
|
b.WriteString(pkgpath)
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleType(t types.Type, b *bytes.Buffer) {
|
|
switch t := t.(type) {
|
|
case *types.Basic, *types.Named:
|
|
var nb bytes.Buffer
|
|
ti := ctx.getNamedTypeInfo(t)
|
|
if ti.pkgpath != "" {
|
|
ctx.manglePackagePath(ti.pkgpath, &nb)
|
|
nb.WriteRune('.')
|
|
}
|
|
if ti.functionName != "" {
|
|
nb.WriteString(ti.functionName)
|
|
nb.WriteRune('$')
|
|
if ti.scopeNum != 0 {
|
|
nb.WriteString(strconv.Itoa(ti.scopeNum))
|
|
nb.WriteRune('$')
|
|
}
|
|
}
|
|
nb.WriteString(ti.name)
|
|
|
|
b.WriteRune('N')
|
|
b.WriteString(strconv.Itoa(nb.Len()))
|
|
b.WriteRune('_')
|
|
b.WriteString(nb.String())
|
|
|
|
case *types.Pointer:
|
|
b.WriteRune('p')
|
|
ctx.mangleType(t.Elem(), b)
|
|
|
|
case *types.Map:
|
|
b.WriteRune('M')
|
|
ctx.mangleType(t.Key(), b)
|
|
b.WriteString("__")
|
|
ctx.mangleType(t.Elem(), b)
|
|
|
|
case *types.Chan:
|
|
b.WriteRune('C')
|
|
ctx.mangleType(t.Elem(), b)
|
|
switch t.Dir() {
|
|
case types.SendOnly:
|
|
b.WriteRune('s')
|
|
case types.RecvOnly:
|
|
b.WriteRune('r')
|
|
case types.SendRecv:
|
|
b.WriteString("sr")
|
|
}
|
|
b.WriteRune('e')
|
|
|
|
case *types.Signature:
|
|
ctx.mangleSignature(t, t.Recv(), b)
|
|
|
|
case *types.Array:
|
|
b.WriteRune('A')
|
|
ctx.mangleType(t.Elem(), b)
|
|
b.WriteString(strconv.FormatInt(t.Len(), 10))
|
|
b.WriteRune('e')
|
|
|
|
case *types.Slice:
|
|
b.WriteRune('A')
|
|
ctx.mangleType(t.Elem(), b)
|
|
b.WriteRune('e')
|
|
|
|
case *types.Struct:
|
|
b.WriteRune('S')
|
|
for i := 0; i != t.NumFields(); i++ {
|
|
f := t.Field(i)
|
|
if f.Anonymous() {
|
|
b.WriteString("0_")
|
|
} else {
|
|
b.WriteString(strconv.Itoa(len(f.Name())))
|
|
b.WriteRune('_')
|
|
b.WriteString(f.Name())
|
|
}
|
|
ctx.mangleType(f.Type(), b)
|
|
// TODO: tags are mangled here
|
|
}
|
|
b.WriteRune('e')
|
|
|
|
case *types.Interface:
|
|
b.WriteRune('I')
|
|
methodset := ctx.msc.MethodSet(t)
|
|
for _, m := range orderedMethodSet(methodset) {
|
|
method := m.Obj()
|
|
var nb bytes.Buffer
|
|
if !method.Exported() {
|
|
nb.WriteRune('.')
|
|
nb.WriteString(method.Pkg().Path())
|
|
nb.WriteRune('.')
|
|
}
|
|
nb.WriteString(method.Name())
|
|
|
|
b.WriteString(strconv.Itoa(nb.Len()))
|
|
b.WriteRune('_')
|
|
b.WriteString(nb.String())
|
|
|
|
ctx.mangleSignature(method.Type().(*types.Signature), nil, b)
|
|
}
|
|
b.WriteRune('e')
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleTypeDescriptorName(t types.Type, b *bytes.Buffer) {
|
|
switch t := t.(type) {
|
|
case *types.Basic, *types.Named:
|
|
b.WriteString("__go_tdn_")
|
|
ti := ctx.getNamedTypeInfo(t)
|
|
if ti.pkgpath != "" {
|
|
ctx.manglePackagePath(ti.pkgpath, b)
|
|
b.WriteRune('.')
|
|
}
|
|
if ti.functionName != "" {
|
|
b.WriteString(ti.functionName)
|
|
b.WriteRune('.')
|
|
if ti.scopeNum != 0 {
|
|
b.WriteString(strconv.Itoa(ti.scopeNum))
|
|
b.WriteRune('.')
|
|
}
|
|
}
|
|
b.WriteString(ti.name)
|
|
|
|
default:
|
|
b.WriteString("__go_td_")
|
|
ctx.mangleType(t, b)
|
|
}
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleMapDescriptorName(t types.Type, b *bytes.Buffer) {
|
|
b.WriteString("__go_map_")
|
|
ctx.mangleType(t, b)
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleImtName(srctype types.Type, targettype *types.Interface, b *bytes.Buffer) {
|
|
b.WriteString("__go_imt_")
|
|
ctx.mangleType(targettype, b)
|
|
b.WriteString("__")
|
|
ctx.mangleType(srctype, b)
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleHashFunctionName(t types.Type) string {
|
|
var b bytes.Buffer
|
|
b.WriteString("__go_type_hash_")
|
|
ctx.mangleType(t, &b)
|
|
return b.String()
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleEqualFunctionName(t types.Type) string {
|
|
var b bytes.Buffer
|
|
b.WriteString("__go_type_equal_")
|
|
ctx.mangleType(t, &b)
|
|
return b.String()
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleFunctionName(f *ssa.Function) string {
|
|
var b bytes.Buffer
|
|
|
|
if f.Parent() != nil {
|
|
// Anonymous functions are not guaranteed to
|
|
// have unique identifiers at the global scope.
|
|
b.WriteString(ctx.mangleFunctionName(f.Parent()))
|
|
b.WriteRune(':')
|
|
b.WriteString(f.String())
|
|
return b.String()
|
|
}
|
|
|
|
pkg := f.Pkg
|
|
var pkgobj *types.Package
|
|
if pkg != nil {
|
|
pkgobj = pkg.Object
|
|
} else if f.Signature.Recv() != nil {
|
|
pkgobj = f.Signature.Recv().Pkg()
|
|
} else {
|
|
b.WriteString(f.String())
|
|
return b.String()
|
|
}
|
|
|
|
if pkg != nil {
|
|
ctx.manglePackagePath(pkgobj.Path(), &b)
|
|
b.WriteRune('.')
|
|
}
|
|
if f.Signature.Recv() == nil && f.Name() == "init" {
|
|
b.WriteString(".import")
|
|
} else {
|
|
b.WriteString(f.Name())
|
|
}
|
|
if f.Signature.Recv() != nil {
|
|
b.WriteRune('.')
|
|
ctx.mangleType(f.Signature.Recv().Type(), &b)
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
func (ctx *manglerContext) mangleGlobalName(g *ssa.Global) string {
|
|
var b bytes.Buffer
|
|
|
|
ctx.manglePackagePath(g.Pkg.Object.Path(), &b)
|
|
b.WriteRune('.')
|
|
b.WriteString(g.Name())
|
|
|
|
return b.String()
|
|
}
|
|
|
|
const (
|
|
// From gofrontend/types.h
|
|
gccgoTypeClassERROR = iota
|
|
gccgoTypeClassVOID
|
|
gccgoTypeClassBOOLEAN
|
|
gccgoTypeClassINTEGER
|
|
gccgoTypeClassFLOAT
|
|
gccgoTypeClassCOMPLEX
|
|
gccgoTypeClassSTRING
|
|
gccgoTypeClassSINK
|
|
gccgoTypeClassFUNCTION
|
|
gccgoTypeClassPOINTER
|
|
gccgoTypeClassNIL
|
|
gccgoTypeClassCALL_MULTIPLE_RESULT
|
|
gccgoTypeClassSTRUCT
|
|
gccgoTypeClassARRAY
|
|
gccgoTypeClassMAP
|
|
gccgoTypeClassCHANNEL
|
|
gccgoTypeClassINTERFACE
|
|
gccgoTypeClassNAMED
|
|
gccgoTypeClassFORWARD
|
|
)
|
|
|
|
func getStringHash(s string, h uint32) uint32 {
|
|
for _, c := range []byte(s) {
|
|
h ^= uint32(c)
|
|
h += 16777619
|
|
}
|
|
return h
|
|
}
|
|
|
|
func (tm *TypeMap) getTypeHash(t types.Type) uint32 {
|
|
switch t := t.(type) {
|
|
case *types.Basic, *types.Named:
|
|
nti := tm.mc.getNamedTypeInfo(t)
|
|
h := getStringHash(nti.functionName+nti.name+nti.pkgpath, 0)
|
|
h ^= uint32(nti.scopeNum)
|
|
return gccgoTypeClassNAMED + h
|
|
|
|
case *types.Signature:
|
|
var h uint32
|
|
|
|
p := t.Params()
|
|
for i := 0; i != p.Len(); i++ {
|
|
h += tm.getTypeHash(p.At(i).Type()) << uint32(i+1)
|
|
}
|
|
|
|
r := t.Results()
|
|
for i := 0; i != r.Len(); i++ {
|
|
h += tm.getTypeHash(r.At(i).Type()) << uint32(i+2)
|
|
}
|
|
|
|
if t.Variadic() {
|
|
h += 1
|
|
}
|
|
h <<= 4
|
|
return gccgoTypeClassFUNCTION + h
|
|
|
|
case *types.Pointer:
|
|
return gccgoTypeClassPOINTER + (tm.getTypeHash(t.Elem()) << 4)
|
|
|
|
case *types.Struct:
|
|
var h uint32
|
|
for i := 0; i != t.NumFields(); i++ {
|
|
h = (h << 1) + tm.getTypeHash(t.Field(i).Type())
|
|
}
|
|
h <<= 2
|
|
return gccgoTypeClassSTRUCT + h
|
|
|
|
case *types.Array:
|
|
return gccgoTypeClassARRAY + tm.getTypeHash(t.Elem()) + 1
|
|
|
|
case *types.Slice:
|
|
return gccgoTypeClassARRAY + tm.getTypeHash(t.Elem()) + 1
|
|
|
|
case *types.Map:
|
|
return gccgoTypeClassMAP + tm.getTypeHash(t.Key()) + tm.getTypeHash(t.Elem()) + 2
|
|
|
|
case *types.Chan:
|
|
var h uint32
|
|
|
|
switch t.Dir() {
|
|
case types.SendOnly:
|
|
h = 1
|
|
case types.RecvOnly:
|
|
h = 2
|
|
case types.SendRecv:
|
|
h = 3
|
|
}
|
|
|
|
h += tm.getTypeHash(t.Elem()) << 2
|
|
h <<= 3
|
|
return gccgoTypeClassCHANNEL + h
|
|
|
|
case *types.Interface:
|
|
var h uint32
|
|
for _, m := range orderedMethodSet(tm.MethodSet(t)) {
|
|
h = getStringHash(m.Obj().Name(), h)
|
|
h <<= 1
|
|
}
|
|
return gccgoTypeClassINTERFACE + h
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
}
|
|
|
|
func (tm *TypeMap) writeType(typ types.Type, b *bytes.Buffer) {
|
|
switch t := typ.(type) {
|
|
case *types.Basic, *types.Named:
|
|
ti := tm.mc.getNamedTypeInfo(t)
|
|
if ti.pkgpath != "" {
|
|
b.WriteByte('\t')
|
|
tm.mc.manglePackagePath(ti.pkgpath, b)
|
|
b.WriteByte('\t')
|
|
b.WriteString(ti.pkgname)
|
|
b.WriteByte('.')
|
|
}
|
|
if ti.functionName != "" {
|
|
b.WriteByte('\t')
|
|
b.WriteString(ti.functionName)
|
|
b.WriteByte('$')
|
|
if ti.scopeNum != 0 {
|
|
b.WriteString(strconv.Itoa(ti.scopeNum))
|
|
b.WriteByte('$')
|
|
}
|
|
b.WriteByte('\t')
|
|
}
|
|
b.WriteString(ti.name)
|
|
|
|
case *types.Array:
|
|
fmt.Fprintf(b, "[%d]", t.Len())
|
|
tm.writeType(t.Elem(), b)
|
|
|
|
case *types.Slice:
|
|
b.WriteString("[]")
|
|
tm.writeType(t.Elem(), b)
|
|
|
|
case *types.Struct:
|
|
if t.NumFields() == 0 {
|
|
b.WriteString("struct {}")
|
|
return
|
|
}
|
|
b.WriteString("struct { ")
|
|
for i := 0; i != t.NumFields(); i++ {
|
|
f := t.Field(i)
|
|
if i > 0 {
|
|
b.WriteString("; ")
|
|
}
|
|
if !f.Anonymous() {
|
|
b.WriteString(f.Name())
|
|
b.WriteByte(' ')
|
|
}
|
|
tm.writeType(f.Type(), b)
|
|
if tag := t.Tag(i); tag != "" {
|
|
fmt.Fprintf(b, " %q", tag)
|
|
}
|
|
}
|
|
b.WriteString(" }")
|
|
|
|
case *types.Pointer:
|
|
b.WriteByte('*')
|
|
tm.writeType(t.Elem(), b)
|
|
|
|
case *types.Signature:
|
|
b.WriteString("func")
|
|
tm.writeSignature(t, b)
|
|
|
|
case *types.Interface:
|
|
if t.NumMethods() == 0 && t.NumEmbeddeds() == 0 {
|
|
b.WriteString("interface {}")
|
|
return
|
|
}
|
|
// We write the source-level methods and embedded types rather
|
|
// than the actual method set since resolved method signatures
|
|
// may have non-printable cycles if parameters have anonymous
|
|
// interface types that (directly or indirectly) embed the
|
|
// current interface. For instance, consider the result type
|
|
// of m:
|
|
//
|
|
// type T interface{
|
|
// m() interface{ T }
|
|
// }
|
|
//
|
|
b.WriteString("interface { ")
|
|
// print explicit interface methods and embedded types
|
|
for i := 0; i != t.NumMethods(); i++ {
|
|
m := t.Method(i)
|
|
if i > 0 {
|
|
b.WriteString("; ")
|
|
}
|
|
if !m.Exported() {
|
|
b.WriteString(m.Pkg().Path())
|
|
b.WriteByte('.')
|
|
}
|
|
b.WriteString(m.Name())
|
|
tm.writeSignature(m.Type().(*types.Signature), b)
|
|
}
|
|
for i := 0; i != t.NumEmbeddeds(); i++ {
|
|
typ := t.Embedded(i)
|
|
if i > 0 || t.NumMethods() > 0 {
|
|
b.WriteString("; ")
|
|
}
|
|
tm.writeType(typ, b)
|
|
}
|
|
b.WriteString(" }")
|
|
|
|
case *types.Map:
|
|
b.WriteString("map[")
|
|
tm.writeType(t.Key(), b)
|
|
b.WriteByte(']')
|
|
tm.writeType(t.Elem(), b)
|
|
|
|
case *types.Chan:
|
|
var s string
|
|
var parens bool
|
|
switch t.Dir() {
|
|
case types.SendRecv:
|
|
s = "chan "
|
|
// chan (<-chan T) requires parentheses
|
|
if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
|
|
parens = true
|
|
}
|
|
case types.SendOnly:
|
|
s = "chan<- "
|
|
case types.RecvOnly:
|
|
s = "<-chan "
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
b.WriteString(s)
|
|
if parens {
|
|
b.WriteByte('(')
|
|
}
|
|
tm.writeType(t.Elem(), b)
|
|
if parens {
|
|
b.WriteByte(')')
|
|
}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
}
|
|
|
|
func (tm *TypeMap) writeTuple(tup *types.Tuple, variadic bool, b *bytes.Buffer) {
|
|
b.WriteByte('(')
|
|
if tup != nil {
|
|
for i := 0; i != tup.Len(); i++ {
|
|
v := tup.At(i)
|
|
if i > 0 {
|
|
b.WriteString(", ")
|
|
}
|
|
typ := v.Type()
|
|
if variadic && i == tup.Len()-1 {
|
|
b.WriteString("...")
|
|
typ = typ.(*types.Slice).Elem()
|
|
}
|
|
tm.writeType(typ, b)
|
|
}
|
|
}
|
|
b.WriteByte(')')
|
|
}
|
|
|
|
func (tm *TypeMap) writeSignature(sig *types.Signature, b *bytes.Buffer) {
|
|
tm.writeTuple(sig.Params(), sig.Variadic(), b)
|
|
|
|
n := sig.Results().Len()
|
|
if n == 0 {
|
|
// no result
|
|
return
|
|
}
|
|
|
|
b.WriteByte(' ')
|
|
if n == 1 {
|
|
tm.writeType(sig.Results().At(0).Type(), b)
|
|
return
|
|
}
|
|
|
|
// multiple results
|
|
tm.writeTuple(sig.Results(), false, b)
|
|
}
|
|
|
|
func (tm *TypeMap) getTypeDescType(t types.Type) llvm.Type {
|
|
switch t.Underlying().(type) {
|
|
case *types.Basic:
|
|
return tm.commonTypeType
|
|
case *types.Pointer:
|
|
return tm.ptrTypeType
|
|
case *types.Signature:
|
|
return tm.funcTypeType
|
|
case *types.Array:
|
|
return tm.arrayTypeType
|
|
case *types.Slice:
|
|
return tm.sliceTypeType
|
|
case *types.Map:
|
|
return tm.mapTypeType
|
|
case *types.Chan:
|
|
return tm.chanTypeType
|
|
case *types.Struct:
|
|
return tm.structTypeType
|
|
case *types.Interface:
|
|
return tm.interfaceTypeType
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
}
|
|
|
|
func (tm *TypeMap) getNamedTypeLinkage(nt *types.Named) (linkage llvm.Linkage, emit bool) {
|
|
if pkg := nt.Obj().Pkg(); pkg != nil {
|
|
linkage = llvm.ExternalLinkage
|
|
emit = pkg.Path() == tm.pkgpath
|
|
} else {
|
|
linkage = llvm.LinkOnceODRLinkage
|
|
emit = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (tm *TypeMap) getTypeDescLinkage(t types.Type) (linkage llvm.Linkage, emit bool) {
|
|
switch t := t.(type) {
|
|
case *types.Named:
|
|
linkage, emit = tm.getNamedTypeLinkage(t)
|
|
|
|
case *types.Pointer:
|
|
elem := t.Elem()
|
|
if nt, ok := elem.(*types.Named); ok {
|
|
// Thanks to the ptrToThis member, pointers to named types appear
|
|
// in exactly the same objects as the named types themselves, so
|
|
// we can give them the same linkage.
|
|
linkage, emit = tm.getNamedTypeLinkage(nt)
|
|
return
|
|
}
|
|
linkage = llvm.LinkOnceODRLinkage
|
|
emit = true
|
|
|
|
default:
|
|
linkage = llvm.LinkOnceODRLinkage
|
|
emit = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
type typeAndInfo struct {
|
|
typ types.Type
|
|
typeString string
|
|
tdi *typeDescInfo
|
|
}
|
|
|
|
type byTypeName []typeAndInfo
|
|
|
|
func (ts byTypeName) Len() int { return len(ts) }
|
|
func (ts byTypeName) Swap(i, j int) {
|
|
ts[i], ts[j] = ts[j], ts[i]
|
|
}
|
|
func (ts byTypeName) Less(i, j int) bool {
|
|
return ts[i].typeString < ts[j].typeString
|
|
}
|
|
|
|
func (tm *TypeMap) emitTypeDescInitializers() {
|
|
var maxSize, maxAlign int64
|
|
maxAlign = 1
|
|
|
|
for changed := true; changed; {
|
|
changed = false
|
|
|
|
var ts []typeAndInfo
|
|
|
|
tm.types.Iterate(func(key types.Type, value interface{}) {
|
|
tdi := value.(*typeDescInfo)
|
|
if tdi.global.Initializer().C == nil {
|
|
linkage, emit := tm.getTypeDescLinkage(key)
|
|
tdi.global.SetLinkage(linkage)
|
|
tdi.gc.SetLinkage(linkage)
|
|
if emit {
|
|
changed = true
|
|
ts = append(ts, typeAndInfo{key, key.String(), tdi})
|
|
}
|
|
}
|
|
})
|
|
|
|
if changed {
|
|
sort.Sort(byTypeName(ts))
|
|
for _, t := range ts {
|
|
tm.emitTypeDescInitializer(t.typ, t.tdi)
|
|
if size := tm.Sizeof(t.typ); size > maxSize {
|
|
maxSize = size
|
|
}
|
|
if align := tm.Alignof(t.typ); align > maxAlign {
|
|
maxAlign = align
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tm.zeroType.StructSetBody([]llvm.Type{llvm.ArrayType(tm.ctx.Int8Type(), int(maxSize))}, false)
|
|
tm.zeroValue.SetAlignment(int(maxAlign))
|
|
}
|
|
|
|
const (
|
|
// From libgo/runtime/mgc0.h
|
|
gcOpcodeEND = iota
|
|
gcOpcodePTR
|
|
gcOpcodeAPTR
|
|
gcOpcodeARRAY_START
|
|
gcOpcodeARRAY_NEXT
|
|
gcOpcodeCALL
|
|
gcOpcodeCHAN_PTR
|
|
gcOpcodeSTRING
|
|
gcOpcodeEFACE
|
|
gcOpcodeIFACE
|
|
gcOpcodeSLICE
|
|
gcOpcodeREGION
|
|
|
|
gcStackCapacity = 8
|
|
)
|
|
|
|
func (tm *TypeMap) makeGcInst(val int64) llvm.Value {
|
|
c := llvm.ConstInt(tm.inttype, uint64(val), false)
|
|
return llvm.ConstIntToPtr(c, llvm.PointerType(tm.ctx.Int8Type(), 0))
|
|
}
|
|
|
|
func (tm *TypeMap) appendGcInsts(insts []llvm.Value, t types.Type, offset, stackSize int64) []llvm.Value {
|
|
switch u := t.Underlying().(type) {
|
|
case *types.Basic:
|
|
switch u.Kind() {
|
|
case types.String:
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeSTRING), tm.makeGcInst(offset))
|
|
case types.UnsafePointer:
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset))
|
|
}
|
|
case *types.Pointer:
|
|
insts = append(insts, tm.makeGcInst(gcOpcodePTR), tm.makeGcInst(offset),
|
|
tm.getGcPointer(u.Elem()))
|
|
case *types.Signature, *types.Map:
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset))
|
|
case *types.Array:
|
|
if u.Len() == 0 {
|
|
return insts
|
|
} else if stackSize >= gcStackCapacity {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeREGION), tm.makeGcInst(offset),
|
|
tm.makeGcInst(tm.Sizeof(t)), tm.getGcPointer(t))
|
|
} else {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeARRAY_START), tm.makeGcInst(offset),
|
|
tm.makeGcInst(u.Len()), tm.makeGcInst(tm.Sizeof(u.Elem())))
|
|
insts = tm.appendGcInsts(insts, u.Elem(), 0, stackSize+1)
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeARRAY_NEXT))
|
|
}
|
|
case *types.Slice:
|
|
if tm.Sizeof(u.Elem()) == 0 {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset))
|
|
} else {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeSLICE), tm.makeGcInst(offset),
|
|
tm.getGcPointer(u.Elem()))
|
|
}
|
|
case *types.Chan:
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeCHAN_PTR), tm.makeGcInst(offset),
|
|
tm.ToRuntime(t))
|
|
case *types.Struct:
|
|
fields := make([]*types.Var, u.NumFields())
|
|
for i := range fields {
|
|
fields[i] = u.Field(i)
|
|
}
|
|
offsets := tm.Offsetsof(fields)
|
|
|
|
for i, field := range fields {
|
|
insts = tm.appendGcInsts(insts, field.Type(), offset+offsets[i], stackSize)
|
|
}
|
|
case *types.Interface:
|
|
if u.NumMethods() == 0 {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeEFACE), tm.makeGcInst(offset))
|
|
} else {
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeIFACE), tm.makeGcInst(offset))
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
|
|
return insts
|
|
}
|
|
|
|
func (tm *TypeMap) emitTypeDescInitializer(t types.Type, tdi *typeDescInfo) {
|
|
// initialize type descriptor
|
|
tdi.global.SetInitializer(tm.makeTypeDescInitializer(t))
|
|
|
|
// initialize GC program
|
|
insts := []llvm.Value{tm.makeGcInst(tm.Sizeof(t))}
|
|
insts = tm.appendGcInsts(insts, t, 0, 0)
|
|
insts = append(insts, tm.makeGcInst(gcOpcodeEND))
|
|
|
|
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
|
instArray := llvm.ConstArray(i8ptr, insts)
|
|
|
|
newGc := llvm.AddGlobal(tm.module, instArray.Type(), "")
|
|
newGc.SetGlobalConstant(true)
|
|
newGc.SetInitializer(instArray)
|
|
gcName := tdi.gc.Name()
|
|
tdi.gc.SetName("")
|
|
newGc.SetName(gcName)
|
|
newGc.SetLinkage(tdi.gc.Linkage())
|
|
|
|
tdi.gc.ReplaceAllUsesWith(llvm.ConstBitCast(newGc, tdi.gc.Type()))
|
|
tdi.gc.EraseFromParentAsGlobal()
|
|
tdi.gc = llvm.Value{nil}
|
|
tdi.gcPtr = llvm.ConstBitCast(newGc, i8ptr)
|
|
}
|
|
|
|
func (tm *TypeMap) makeTypeDescInitializer(t types.Type) llvm.Value {
|
|
switch u := t.Underlying().(type) {
|
|
case *types.Basic:
|
|
return tm.makeBasicType(t, u)
|
|
case *types.Pointer:
|
|
return tm.makePointerType(t, u)
|
|
case *types.Signature:
|
|
return tm.makeFuncType(t, u)
|
|
case *types.Array:
|
|
return tm.makeArrayType(t, u)
|
|
case *types.Slice:
|
|
return tm.makeSliceType(t, u)
|
|
case *types.Map:
|
|
return tm.makeMapType(t, u)
|
|
case *types.Chan:
|
|
return tm.makeChanType(t, u)
|
|
case *types.Struct:
|
|
return tm.makeStructType(t, u)
|
|
case *types.Interface:
|
|
return tm.makeInterfaceType(t, u)
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type: %#v", t))
|
|
}
|
|
}
|
|
|
|
type algorithmFns struct {
|
|
hash, equal llvm.Value
|
|
}
|
|
|
|
func (tm *TypeMap) getStructAlgorithmFunctions(st *types.Struct) (hash, equal llvm.Value) {
|
|
if algs, ok := tm.algs.At(st).(algorithmFns); ok {
|
|
return algs.hash, algs.equal
|
|
}
|
|
|
|
hashes := make([]llvm.Value, st.NumFields())
|
|
equals := make([]llvm.Value, st.NumFields())
|
|
|
|
for i := range hashes {
|
|
fhash, fequal := tm.getAlgorithmFunctions(st.Field(i).Type())
|
|
if fhash == tm.hashFnError {
|
|
return fhash, fequal
|
|
}
|
|
hashes[i], equals[i] = fhash, fequal
|
|
}
|
|
|
|
i8ptr := llvm.PointerType(tm.ctx.Int8Type(), 0)
|
|
llsptrty := llvm.PointerType(tm.ToLLVM(st), 0)
|
|
|
|
builder := tm.ctx.NewBuilder()
|
|
defer builder.Dispose()
|
|
|
|
hash = llvm.AddFunction(tm.module, tm.mc.mangleHashFunctionName(st), tm.hashFnType)
|
|
hash.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
builder.SetInsertPointAtEnd(llvm.AddBasicBlock(hash, "entry"))
|
|
sptr := builder.CreateBitCast(hash.Param(0), llsptrty, "")
|
|
|
|
hashval := llvm.ConstNull(tm.inttype)
|
|
i33 := llvm.ConstInt(tm.inttype, 33, false)
|
|
|
|
for i, fhash := range hashes {
|
|
fptr := builder.CreateStructGEP(sptr, i, "")
|
|
fptr = builder.CreateBitCast(fptr, i8ptr, "")
|
|
|
|
fsize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(st.Field(i).Type())), false)
|
|
|
|
hashcall := builder.CreateCall(fhash, []llvm.Value{fptr, fsize}, "")
|
|
hashval = builder.CreateMul(hashval, i33, "")
|
|
hashval = builder.CreateAdd(hashval, hashcall, "")
|
|
}
|
|
|
|
builder.CreateRet(hashval)
|
|
|
|
equal = llvm.AddFunction(tm.module, tm.mc.mangleEqualFunctionName(st), tm.equalFnType)
|
|
equal.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
eqentrybb := llvm.AddBasicBlock(equal, "entry")
|
|
eqretzerobb := llvm.AddBasicBlock(equal, "retzero")
|
|
|
|
builder.SetInsertPointAtEnd(eqentrybb)
|
|
s1ptr := builder.CreateBitCast(equal.Param(0), llsptrty, "")
|
|
s2ptr := builder.CreateBitCast(equal.Param(1), llsptrty, "")
|
|
|
|
zerobool := llvm.ConstNull(tm.ctx.Int8Type())
|
|
onebool := llvm.ConstInt(tm.ctx.Int8Type(), 1, false)
|
|
|
|
for i, fequal := range equals {
|
|
f1ptr := builder.CreateStructGEP(s1ptr, i, "")
|
|
f1ptr = builder.CreateBitCast(f1ptr, i8ptr, "")
|
|
f2ptr := builder.CreateStructGEP(s2ptr, i, "")
|
|
f2ptr = builder.CreateBitCast(f2ptr, i8ptr, "")
|
|
|
|
fsize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(st.Field(i).Type())), false)
|
|
|
|
equalcall := builder.CreateCall(fequal, []llvm.Value{f1ptr, f2ptr, fsize}, "")
|
|
equaleqzero := builder.CreateICmp(llvm.IntEQ, equalcall, zerobool, "")
|
|
|
|
contbb := llvm.AddBasicBlock(equal, "cont")
|
|
builder.CreateCondBr(equaleqzero, eqretzerobb, contbb)
|
|
|
|
builder.SetInsertPointAtEnd(contbb)
|
|
}
|
|
|
|
builder.CreateRet(onebool)
|
|
|
|
builder.SetInsertPointAtEnd(eqretzerobb)
|
|
builder.CreateRet(zerobool)
|
|
|
|
tm.algs.Set(st, algorithmFns{hash, equal})
|
|
return
|
|
}
|
|
|
|
func (tm *TypeMap) getArrayAlgorithmFunctions(at *types.Array) (hash, equal llvm.Value) {
|
|
if algs, ok := tm.algs.At(at).(algorithmFns); ok {
|
|
return algs.hash, algs.equal
|
|
}
|
|
|
|
ehash, eequal := tm.getAlgorithmFunctions(at.Elem())
|
|
if ehash == tm.hashFnError {
|
|
return ehash, eequal
|
|
}
|
|
|
|
i8ptr := llvm.PointerType(tm.ctx.Int8Type(), 0)
|
|
llelemty := llvm.PointerType(tm.ToLLVM(at.Elem()), 0)
|
|
|
|
i1 := llvm.ConstInt(tm.inttype, 1, false)
|
|
alen := llvm.ConstInt(tm.inttype, uint64(at.Len()), false)
|
|
esize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(at.Elem())), false)
|
|
|
|
builder := tm.ctx.NewBuilder()
|
|
defer builder.Dispose()
|
|
|
|
hash = llvm.AddFunction(tm.module, tm.mc.mangleHashFunctionName(at), tm.hashFnType)
|
|
hash.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
hashentrybb := llvm.AddBasicBlock(hash, "entry")
|
|
builder.SetInsertPointAtEnd(hashentrybb)
|
|
if at.Len() == 0 {
|
|
builder.CreateRet(llvm.ConstNull(tm.inttype))
|
|
} else {
|
|
i33 := llvm.ConstInt(tm.inttype, 33, false)
|
|
|
|
aptr := builder.CreateBitCast(hash.Param(0), llelemty, "")
|
|
loopbb := llvm.AddBasicBlock(hash, "loop")
|
|
builder.CreateBr(loopbb)
|
|
|
|
exitbb := llvm.AddBasicBlock(hash, "exit")
|
|
|
|
builder.SetInsertPointAtEnd(loopbb)
|
|
indexphi := builder.CreatePHI(tm.inttype, "")
|
|
index := indexphi
|
|
hashvalphi := builder.CreatePHI(tm.inttype, "")
|
|
hashval := hashvalphi
|
|
|
|
eptr := builder.CreateGEP(aptr, []llvm.Value{index}, "")
|
|
eptr = builder.CreateBitCast(eptr, i8ptr, "")
|
|
|
|
hashcall := builder.CreateCall(ehash, []llvm.Value{eptr, esize}, "")
|
|
hashval = builder.CreateMul(hashval, i33, "")
|
|
hashval = builder.CreateAdd(hashval, hashcall, "")
|
|
|
|
index = builder.CreateAdd(index, i1, "")
|
|
|
|
indexphi.AddIncoming(
|
|
[]llvm.Value{llvm.ConstNull(tm.inttype), index},
|
|
[]llvm.BasicBlock{hashentrybb, loopbb},
|
|
)
|
|
hashvalphi.AddIncoming(
|
|
[]llvm.Value{llvm.ConstNull(tm.inttype), hashval},
|
|
[]llvm.BasicBlock{hashentrybb, loopbb},
|
|
)
|
|
|
|
exit := builder.CreateICmp(llvm.IntEQ, index, alen, "")
|
|
builder.CreateCondBr(exit, exitbb, loopbb)
|
|
|
|
builder.SetInsertPointAtEnd(exitbb)
|
|
builder.CreateRet(hashval)
|
|
}
|
|
|
|
zerobool := llvm.ConstNull(tm.ctx.Int8Type())
|
|
onebool := llvm.ConstInt(tm.ctx.Int8Type(), 1, false)
|
|
|
|
equal = llvm.AddFunction(tm.module, tm.mc.mangleEqualFunctionName(at), tm.equalFnType)
|
|
equal.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
eqentrybb := llvm.AddBasicBlock(equal, "entry")
|
|
builder.SetInsertPointAtEnd(eqentrybb)
|
|
if at.Len() == 0 {
|
|
builder.CreateRet(onebool)
|
|
} else {
|
|
a1ptr := builder.CreateBitCast(equal.Param(0), llelemty, "")
|
|
a2ptr := builder.CreateBitCast(equal.Param(1), llelemty, "")
|
|
loopbb := llvm.AddBasicBlock(equal, "loop")
|
|
builder.CreateBr(loopbb)
|
|
|
|
exitbb := llvm.AddBasicBlock(equal, "exit")
|
|
retzerobb := llvm.AddBasicBlock(equal, "retzero")
|
|
|
|
builder.SetInsertPointAtEnd(loopbb)
|
|
indexphi := builder.CreatePHI(tm.inttype, "")
|
|
index := indexphi
|
|
|
|
e1ptr := builder.CreateGEP(a1ptr, []llvm.Value{index}, "")
|
|
e1ptr = builder.CreateBitCast(e1ptr, i8ptr, "")
|
|
e2ptr := builder.CreateGEP(a2ptr, []llvm.Value{index}, "")
|
|
e2ptr = builder.CreateBitCast(e2ptr, i8ptr, "")
|
|
|
|
equalcall := builder.CreateCall(eequal, []llvm.Value{e1ptr, e2ptr, esize}, "")
|
|
equaleqzero := builder.CreateICmp(llvm.IntEQ, equalcall, zerobool, "")
|
|
|
|
contbb := llvm.AddBasicBlock(equal, "cont")
|
|
builder.CreateCondBr(equaleqzero, retzerobb, contbb)
|
|
|
|
builder.SetInsertPointAtEnd(contbb)
|
|
|
|
index = builder.CreateAdd(index, i1, "")
|
|
|
|
indexphi.AddIncoming(
|
|
[]llvm.Value{llvm.ConstNull(tm.inttype), index},
|
|
[]llvm.BasicBlock{eqentrybb, contbb},
|
|
)
|
|
|
|
exit := builder.CreateICmp(llvm.IntEQ, index, alen, "")
|
|
builder.CreateCondBr(exit, exitbb, loopbb)
|
|
|
|
builder.SetInsertPointAtEnd(exitbb)
|
|
builder.CreateRet(onebool)
|
|
|
|
builder.SetInsertPointAtEnd(retzerobb)
|
|
builder.CreateRet(zerobool)
|
|
}
|
|
|
|
tm.algs.Set(at, algorithmFns{hash, equal})
|
|
return
|
|
}
|
|
|
|
func (tm *TypeMap) getAlgorithmFunctions(t types.Type) (hash, equal llvm.Value) {
|
|
switch t := t.Underlying().(type) {
|
|
case *types.Interface:
|
|
if t.NumMethods() == 0 {
|
|
hash = tm.hashFnEmptyInterface
|
|
equal = tm.equalFnEmptyInterface
|
|
} else {
|
|
hash = tm.hashFnInterface
|
|
equal = tm.equalFnInterface
|
|
}
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.Float32, types.Float64:
|
|
hash = tm.hashFnFloat
|
|
equal = tm.equalFnFloat
|
|
case types.Complex64, types.Complex128:
|
|
hash = tm.hashFnComplex
|
|
equal = tm.equalFnComplex
|
|
case types.String:
|
|
hash = tm.hashFnString
|
|
equal = tm.equalFnString
|
|
default:
|
|
hash = tm.hashFnIdentity
|
|
equal = tm.equalFnIdentity
|
|
}
|
|
case *types.Signature, *types.Map, *types.Slice:
|
|
hash = tm.hashFnError
|
|
equal = tm.equalFnError
|
|
case *types.Struct:
|
|
hash, equal = tm.getStructAlgorithmFunctions(t)
|
|
case *types.Array:
|
|
hash, equal = tm.getArrayAlgorithmFunctions(t)
|
|
default:
|
|
hash = tm.hashFnIdentity
|
|
equal = tm.equalFnIdentity
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (tm *TypeMap) getTypeDescInfo(t types.Type) *typeDescInfo {
|
|
if tdi, ok := tm.types.At(t).(*typeDescInfo); ok {
|
|
return tdi
|
|
}
|
|
|
|
var b bytes.Buffer
|
|
tm.mc.mangleTypeDescriptorName(t, &b)
|
|
|
|
global := llvm.AddGlobal(tm.module, tm.getTypeDescType(t), b.String())
|
|
global.SetGlobalConstant(true)
|
|
ptr := llvm.ConstBitCast(global, llvm.PointerType(tm.commonTypeType, 0))
|
|
|
|
gc := llvm.AddGlobal(tm.module, llvm.PointerType(llvm.Int8Type(), 0), b.String()+"$gc")
|
|
gc.SetGlobalConstant(true)
|
|
gcPtr := llvm.ConstBitCast(gc, llvm.PointerType(tm.ctx.Int8Type(), 0))
|
|
|
|
var mapDescPtr llvm.Value
|
|
if m, ok := t.Underlying().(*types.Map); ok {
|
|
var mapb bytes.Buffer
|
|
tm.mc.mangleMapDescriptorName(t, &mapb)
|
|
|
|
mapDescPtr = llvm.AddGlobal(tm.module, tm.mapDescType, mapb.String())
|
|
mapDescPtr.SetGlobalConstant(true)
|
|
mapDescPtr.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
mapDescPtr.SetInitializer(tm.makeMapDesc(ptr, m))
|
|
}
|
|
|
|
tdi := &typeDescInfo{
|
|
global: global,
|
|
commonTypePtr: ptr,
|
|
mapDescPtr: mapDescPtr,
|
|
gc: gc,
|
|
gcPtr: gcPtr,
|
|
}
|
|
tm.types.Set(t, tdi)
|
|
return tdi
|
|
}
|
|
|
|
func (tm *TypeMap) getTypeDescriptorPointer(t types.Type) llvm.Value {
|
|
return tm.getTypeDescInfo(t).commonTypePtr
|
|
}
|
|
|
|
func (tm *TypeMap) getMapDescriptorPointer(t types.Type) llvm.Value {
|
|
return tm.getTypeDescInfo(t).mapDescPtr
|
|
}
|
|
|
|
func (tm *TypeMap) getGcPointer(t types.Type) llvm.Value {
|
|
return tm.getTypeDescInfo(t).gcPtr
|
|
}
|
|
|
|
func (tm *TypeMap) getItabPointer(srctype types.Type, targettype *types.Interface) llvm.Value {
|
|
if targettype.NumMethods() == 0 {
|
|
return tm.ToRuntime(srctype)
|
|
} else {
|
|
return tm.getImtPointer(srctype, targettype)
|
|
}
|
|
}
|
|
|
|
func (tm *TypeMap) getImtPointer(srctype types.Type, targettype *types.Interface) llvm.Value {
|
|
tdi := tm.getTypeDescInfo(srctype)
|
|
|
|
if ptr, ok := tdi.interfaceMethodTables.At(targettype).(llvm.Value); ok {
|
|
return ptr
|
|
}
|
|
|
|
srcms := tm.MethodSet(srctype)
|
|
targetms := tm.MethodSet(targettype)
|
|
|
|
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
|
|
|
elems := make([]llvm.Value, targetms.Len()+1)
|
|
elems[0] = tm.ToRuntime(srctype)
|
|
for i, targetm := range orderedMethodSet(targetms) {
|
|
srcm := srcms.Lookup(targetm.Obj().Pkg(), targetm.Obj().Name())
|
|
|
|
elems[i+1] = tm.methodResolver.ResolveMethod(srcm).value
|
|
}
|
|
imtinit := llvm.ConstArray(i8ptr, elems)
|
|
|
|
var b bytes.Buffer
|
|
tm.mc.mangleImtName(srctype, targettype, &b)
|
|
imt := llvm.AddGlobal(tm.module, imtinit.Type(), b.String())
|
|
imt.SetGlobalConstant(true)
|
|
imt.SetInitializer(imtinit)
|
|
imt.SetLinkage(llvm.LinkOnceODRLinkage)
|
|
|
|
imtptr := llvm.ConstBitCast(imt, i8ptr)
|
|
tdi.interfaceMethodTables.Set(targettype, imtptr)
|
|
return imtptr
|
|
}
|
|
|
|
const (
|
|
// From gofrontend/types.h
|
|
gccgoRuntimeTypeKindBOOL = 1
|
|
gccgoRuntimeTypeKindINT = 2
|
|
gccgoRuntimeTypeKindINT8 = 3
|
|
gccgoRuntimeTypeKindINT16 = 4
|
|
gccgoRuntimeTypeKindINT32 = 5
|
|
gccgoRuntimeTypeKindINT64 = 6
|
|
gccgoRuntimeTypeKindUINT = 7
|
|
gccgoRuntimeTypeKindUINT8 = 8
|
|
gccgoRuntimeTypeKindUINT16 = 9
|
|
gccgoRuntimeTypeKindUINT32 = 10
|
|
gccgoRuntimeTypeKindUINT64 = 11
|
|
gccgoRuntimeTypeKindUINTPTR = 12
|
|
gccgoRuntimeTypeKindFLOAT32 = 13
|
|
gccgoRuntimeTypeKindFLOAT64 = 14
|
|
gccgoRuntimeTypeKindCOMPLEX64 = 15
|
|
gccgoRuntimeTypeKindCOMPLEX128 = 16
|
|
gccgoRuntimeTypeKindARRAY = 17
|
|
gccgoRuntimeTypeKindCHAN = 18
|
|
gccgoRuntimeTypeKindFUNC = 19
|
|
gccgoRuntimeTypeKindINTERFACE = 20
|
|
gccgoRuntimeTypeKindMAP = 21
|
|
gccgoRuntimeTypeKindPTR = 22
|
|
gccgoRuntimeTypeKindSLICE = 23
|
|
gccgoRuntimeTypeKindSTRING = 24
|
|
gccgoRuntimeTypeKindSTRUCT = 25
|
|
gccgoRuntimeTypeKindUNSAFE_POINTER = 26
|
|
gccgoRuntimeTypeKindNO_POINTERS = (1 << 7)
|
|
)
|
|
|
|
func hasPointers(t types.Type) bool {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
return t.Kind() == types.String || t.Kind() == types.UnsafePointer
|
|
|
|
case *types.Signature, *types.Pointer, *types.Slice, *types.Map, *types.Chan, *types.Interface:
|
|
return true
|
|
|
|
case *types.Struct:
|
|
for i := 0; i != t.NumFields(); i++ {
|
|
if hasPointers(t.Field(i).Type()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
|
|
case *types.Named:
|
|
return hasPointers(t.Underlying())
|
|
|
|
case *types.Array:
|
|
return hasPointers(t.Elem())
|
|
|
|
default:
|
|
panic("unrecognized type")
|
|
}
|
|
}
|
|
|
|
func runtimeTypeKind(t types.Type) (k uint8) {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.Bool:
|
|
k = gccgoRuntimeTypeKindBOOL
|
|
case types.Int:
|
|
k = gccgoRuntimeTypeKindINT
|
|
case types.Int8:
|
|
k = gccgoRuntimeTypeKindINT8
|
|
case types.Int16:
|
|
k = gccgoRuntimeTypeKindINT16
|
|
case types.Int32:
|
|
k = gccgoRuntimeTypeKindINT32
|
|
case types.Int64:
|
|
k = gccgoRuntimeTypeKindINT64
|
|
case types.Uint:
|
|
k = gccgoRuntimeTypeKindUINT
|
|
case types.Uint8:
|
|
k = gccgoRuntimeTypeKindUINT8
|
|
case types.Uint16:
|
|
k = gccgoRuntimeTypeKindUINT16
|
|
case types.Uint32:
|
|
k = gccgoRuntimeTypeKindUINT32
|
|
case types.Uint64:
|
|
k = gccgoRuntimeTypeKindUINT64
|
|
case types.Uintptr:
|
|
k = gccgoRuntimeTypeKindUINTPTR
|
|
case types.Float32:
|
|
k = gccgoRuntimeTypeKindFLOAT32
|
|
case types.Float64:
|
|
k = gccgoRuntimeTypeKindFLOAT64
|
|
case types.Complex64:
|
|
k = gccgoRuntimeTypeKindCOMPLEX64
|
|
case types.Complex128:
|
|
k = gccgoRuntimeTypeKindCOMPLEX128
|
|
case types.String:
|
|
k = gccgoRuntimeTypeKindSTRING
|
|
case types.UnsafePointer:
|
|
k = gccgoRuntimeTypeKindUNSAFE_POINTER
|
|
default:
|
|
panic("unrecognized builtin type")
|
|
}
|
|
case *types.Array:
|
|
k = gccgoRuntimeTypeKindARRAY
|
|
case *types.Slice:
|
|
k = gccgoRuntimeTypeKindSLICE
|
|
case *types.Struct:
|
|
k = gccgoRuntimeTypeKindSTRUCT
|
|
case *types.Pointer:
|
|
k = gccgoRuntimeTypeKindPTR
|
|
case *types.Signature:
|
|
k = gccgoRuntimeTypeKindFUNC
|
|
case *types.Interface:
|
|
k = gccgoRuntimeTypeKindINTERFACE
|
|
case *types.Map:
|
|
k = gccgoRuntimeTypeKindMAP
|
|
case *types.Chan:
|
|
k = gccgoRuntimeTypeKindCHAN
|
|
case *types.Named:
|
|
return runtimeTypeKind(t.Underlying())
|
|
default:
|
|
panic("unrecognized type")
|
|
}
|
|
|
|
if !hasPointers(t) {
|
|
k |= gccgoRuntimeTypeKindNO_POINTERS
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (tm *TypeMap) makeCommonType(t types.Type) llvm.Value {
|
|
var vals [12]llvm.Value
|
|
vals[0] = llvm.ConstInt(tm.ctx.Int8Type(), uint64(runtimeTypeKind(t)), false)
|
|
vals[1] = llvm.ConstInt(tm.ctx.Int8Type(), uint64(tm.Alignof(t)), false)
|
|
vals[2] = vals[1]
|
|
vals[3] = llvm.ConstInt(tm.inttype, uint64(tm.Sizeof(t)), false)
|
|
vals[4] = llvm.ConstInt(tm.ctx.Int32Type(), uint64(tm.getTypeHash(t)), false)
|
|
hash, equal := tm.getAlgorithmFunctions(t)
|
|
vals[5] = hash
|
|
vals[6] = equal
|
|
vals[7] = tm.getGcPointer(t)
|
|
var b bytes.Buffer
|
|
tm.writeType(t, &b)
|
|
vals[8] = tm.globalStringPtr(b.String())
|
|
vals[9] = tm.makeUncommonTypePtr(t)
|
|
if _, ok := t.(*types.Named); ok {
|
|
vals[10] = tm.getTypeDescriptorPointer(types.NewPointer(t))
|
|
} else {
|
|
vals[10] = llvm.ConstPointerNull(llvm.PointerType(tm.commonTypeType, 0))
|
|
}
|
|
vals[11] = tm.zeroValue
|
|
|
|
return llvm.ConstNamedStruct(tm.commonTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeBasicType(t types.Type, u *types.Basic) llvm.Value {
|
|
return tm.makeCommonType(t)
|
|
}
|
|
|
|
func (tm *TypeMap) makeArrayType(t types.Type, a *types.Array) llvm.Value {
|
|
var vals [4]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
vals[1] = tm.getTypeDescriptorPointer(a.Elem())
|
|
vals[2] = tm.getTypeDescriptorPointer(types.NewSlice(a.Elem()))
|
|
vals[3] = llvm.ConstInt(tm.inttype, uint64(a.Len()), false)
|
|
|
|
return llvm.ConstNamedStruct(tm.arrayTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeSliceType(t types.Type, s *types.Slice) llvm.Value {
|
|
var vals [2]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
vals[1] = tm.getTypeDescriptorPointer(s.Elem())
|
|
|
|
return llvm.ConstNamedStruct(tm.sliceTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeStructType(t types.Type, s *types.Struct) llvm.Value {
|
|
var vals [2]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
|
|
fieldVars := make([]*types.Var, s.NumFields())
|
|
for i := range fieldVars {
|
|
fieldVars[i] = s.Field(i)
|
|
}
|
|
offsets := tm.Offsetsof(fieldVars)
|
|
structFields := make([]llvm.Value, len(fieldVars))
|
|
for i, field := range fieldVars {
|
|
var sfvals [5]llvm.Value
|
|
if !field.Anonymous() {
|
|
sfvals[0] = tm.globalStringPtr(field.Name())
|
|
} else {
|
|
sfvals[0] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0))
|
|
}
|
|
if !field.Exported() && field.Pkg() != nil {
|
|
sfvals[1] = tm.globalStringPtr(field.Pkg().Path())
|
|
} else {
|
|
sfvals[1] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0))
|
|
}
|
|
sfvals[2] = tm.getTypeDescriptorPointer(field.Type())
|
|
if tag := s.Tag(i); tag != "" {
|
|
sfvals[3] = tm.globalStringPtr(tag)
|
|
} else {
|
|
sfvals[3] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0))
|
|
}
|
|
sfvals[4] = llvm.ConstInt(tm.inttype, uint64(offsets[i]), false)
|
|
|
|
structFields[i] = llvm.ConstNamedStruct(tm.structFieldType, sfvals[:])
|
|
}
|
|
vals[1] = tm.makeSlice(structFields, tm.structFieldSliceType)
|
|
|
|
return llvm.ConstNamedStruct(tm.structTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makePointerType(t types.Type, p *types.Pointer) llvm.Value {
|
|
var vals [2]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
vals[1] = tm.getTypeDescriptorPointer(p.Elem())
|
|
|
|
return llvm.ConstNamedStruct(tm.ptrTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) rtypeSlice(t *types.Tuple) llvm.Value {
|
|
rtypes := make([]llvm.Value, t.Len())
|
|
for i := range rtypes {
|
|
rtypes[i] = tm.getTypeDescriptorPointer(t.At(i).Type())
|
|
}
|
|
return tm.makeSlice(rtypes, tm.typeSliceType)
|
|
}
|
|
|
|
func (tm *TypeMap) makeFuncType(t types.Type, f *types.Signature) llvm.Value {
|
|
var vals [4]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
// dotdotdot
|
|
variadic := 0
|
|
if f.Variadic() {
|
|
variadic = 1
|
|
}
|
|
vals[1] = llvm.ConstInt(llvm.Int8Type(), uint64(variadic), false)
|
|
// in
|
|
vals[2] = tm.rtypeSlice(f.Params())
|
|
// out
|
|
vals[3] = tm.rtypeSlice(f.Results())
|
|
|
|
return llvm.ConstNamedStruct(tm.funcTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeInterfaceType(t types.Type, i *types.Interface) llvm.Value {
|
|
var vals [2]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
|
|
methodset := tm.MethodSet(i)
|
|
imethods := make([]llvm.Value, methodset.Len())
|
|
for index, ms := range orderedMethodSet(methodset) {
|
|
method := ms.Obj()
|
|
var imvals [3]llvm.Value
|
|
imvals[0] = tm.globalStringPtr(method.Name())
|
|
if !method.Exported() && method.Pkg() != nil {
|
|
imvals[1] = tm.globalStringPtr(method.Pkg().Path())
|
|
} else {
|
|
imvals[1] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0))
|
|
}
|
|
mtyp := method.Type().(*types.Signature)
|
|
mftyp := types.NewSignature(nil, nil, mtyp.Params(), mtyp.Results(), mtyp.Variadic())
|
|
imvals[2] = tm.getTypeDescriptorPointer(mftyp)
|
|
|
|
imethods[index] = llvm.ConstNamedStruct(tm.imethodType, imvals[:])
|
|
}
|
|
vals[1] = tm.makeSlice(imethods, tm.imethodSliceType)
|
|
|
|
return llvm.ConstNamedStruct(tm.interfaceTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeMapType(t types.Type, m *types.Map) llvm.Value {
|
|
var vals [3]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
vals[1] = tm.getTypeDescriptorPointer(m.Key())
|
|
vals[2] = tm.getTypeDescriptorPointer(m.Elem())
|
|
|
|
return llvm.ConstNamedStruct(tm.mapTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeMapDesc(ptr llvm.Value, m *types.Map) llvm.Value {
|
|
mapEntryType := structBType{[]backendType{
|
|
tm.getBackendType(types.Typ[types.UnsafePointer]),
|
|
tm.getBackendType(m.Key()),
|
|
tm.getBackendType(m.Elem()),
|
|
}}.ToLLVM(tm.ctx)
|
|
|
|
var vals [4]llvm.Value
|
|
// map_descriptor
|
|
vals[0] = ptr
|
|
// entry_size
|
|
vals[1] = llvm.ConstInt(tm.inttype, tm.target.TypeAllocSize(mapEntryType), false)
|
|
// key_offset
|
|
vals[2] = llvm.ConstInt(tm.inttype, tm.target.ElementOffset(mapEntryType, 1), false)
|
|
// value_offset
|
|
vals[3] = llvm.ConstInt(tm.inttype, tm.target.ElementOffset(mapEntryType, 2), false)
|
|
|
|
return llvm.ConstNamedStruct(tm.mapDescType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeChanType(t types.Type, c *types.Chan) llvm.Value {
|
|
var vals [3]llvm.Value
|
|
vals[0] = tm.makeCommonType(t)
|
|
vals[1] = tm.getTypeDescriptorPointer(c.Elem())
|
|
|
|
// From gofrontend/go/types.cc
|
|
// These bits must match the ones in libgo/runtime/go-type.h.
|
|
var dir int
|
|
switch c.Dir() {
|
|
case types.RecvOnly:
|
|
dir = 1
|
|
case types.SendOnly:
|
|
dir = 2
|
|
case types.SendRecv:
|
|
dir = 3
|
|
}
|
|
vals[2] = llvm.ConstInt(tm.inttype, uint64(dir), false)
|
|
|
|
return llvm.ConstNamedStruct(tm.chanTypeType, vals[:])
|
|
}
|
|
|
|
func (tm *TypeMap) makeUncommonTypePtr(t types.Type) llvm.Value {
|
|
_, isbasic := t.(*types.Basic)
|
|
_, isnamed := t.(*types.Named)
|
|
|
|
var mset types.MethodSet
|
|
// We store interface methods on the interface type.
|
|
if _, ok := t.Underlying().(*types.Interface); !ok {
|
|
mset = *tm.MethodSet(t)
|
|
}
|
|
|
|
if !isbasic && !isnamed && mset.Len() == 0 {
|
|
return llvm.ConstPointerNull(llvm.PointerType(tm.uncommonTypeType, 0))
|
|
}
|
|
|
|
var vals [3]llvm.Value
|
|
|
|
nullStringPtr := llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0))
|
|
vals[0] = nullStringPtr
|
|
vals[1] = nullStringPtr
|
|
|
|
if isbasic || isnamed {
|
|
nti := tm.mc.getNamedTypeInfo(t)
|
|
vals[0] = tm.globalStringPtr(nti.name)
|
|
if nti.pkgpath != "" {
|
|
path := nti.pkgpath
|
|
if nti.functionName != "" {
|
|
path += "." + nti.functionName
|
|
if nti.scopeNum != 0 {
|
|
path += "$" + strconv.Itoa(nti.scopeNum)
|
|
}
|
|
}
|
|
vals[1] = tm.globalStringPtr(path)
|
|
}
|
|
}
|
|
|
|
// Store methods. All methods must be stored, not only exported ones;
|
|
// this is to allow satisfying of interfaces with non-exported methods.
|
|
methods := make([]llvm.Value, mset.Len())
|
|
omset := orderedMethodSet(&mset)
|
|
for i := range methods {
|
|
var mvals [5]llvm.Value
|
|
|
|
sel := omset[i]
|
|
mname := sel.Obj().Name()
|
|
mfunc := tm.methodResolver.ResolveMethod(sel)
|
|
ftyp := mfunc.Type().(*types.Signature)
|
|
|
|
// name
|
|
mvals[0] = tm.globalStringPtr(mname)
|
|
|
|
// pkgPath
|
|
mvals[1] = nullStringPtr
|
|
if pkg := sel.Obj().Pkg(); pkg != nil && !sel.Obj().Exported() {
|
|
mvals[1] = tm.globalStringPtr(pkg.Path())
|
|
}
|
|
|
|
// mtyp (method type, no receiver)
|
|
mftyp := types.NewSignature(nil, nil, ftyp.Params(), ftyp.Results(), ftyp.Variadic())
|
|
mvals[2] = tm.getTypeDescriptorPointer(mftyp)
|
|
|
|
// typ (function type, with receiver)
|
|
recvparam := types.NewParam(0, nil, "", t)
|
|
params := ftyp.Params()
|
|
rfparams := make([]*types.Var, params.Len()+1)
|
|
rfparams[0] = recvparam
|
|
for i := 0; i != ftyp.Params().Len(); i++ {
|
|
rfparams[i+1] = params.At(i)
|
|
}
|
|
rftyp := types.NewSignature(nil, nil, types.NewTuple(rfparams...), ftyp.Results(), ftyp.Variadic())
|
|
mvals[3] = tm.getTypeDescriptorPointer(rftyp)
|
|
|
|
// function
|
|
mvals[4] = mfunc.value
|
|
|
|
methods[i] = llvm.ConstNamedStruct(tm.methodType, mvals[:])
|
|
}
|
|
|
|
vals[2] = tm.makeSlice(methods, tm.methodSliceType)
|
|
|
|
uncommonType := llvm.ConstNamedStruct(tm.uncommonTypeType, vals[:])
|
|
|
|
uncommonTypePtr := llvm.AddGlobal(tm.module, tm.uncommonTypeType, "")
|
|
uncommonTypePtr.SetGlobalConstant(true)
|
|
uncommonTypePtr.SetInitializer(uncommonType)
|
|
uncommonTypePtr.SetLinkage(llvm.InternalLinkage)
|
|
return uncommonTypePtr
|
|
}
|
|
|
|
// globalStringPtr returns a *string with the specified value.
|
|
func (tm *TypeMap) globalStringPtr(value string) llvm.Value {
|
|
strval := llvm.ConstString(value, false)
|
|
strglobal := llvm.AddGlobal(tm.module, strval.Type(), "")
|
|
strglobal.SetGlobalConstant(true)
|
|
strglobal.SetLinkage(llvm.InternalLinkage)
|
|
strglobal.SetInitializer(strval)
|
|
strglobal = llvm.ConstBitCast(strglobal, llvm.PointerType(llvm.Int8Type(), 0))
|
|
strlen := llvm.ConstInt(tm.inttype, uint64(len(value)), false)
|
|
str := llvm.ConstStruct([]llvm.Value{strglobal, strlen}, false)
|
|
g := llvm.AddGlobal(tm.module, str.Type(), "")
|
|
g.SetGlobalConstant(true)
|
|
g.SetLinkage(llvm.InternalLinkage)
|
|
g.SetInitializer(str)
|
|
return g
|
|
}
|
|
|
|
func (tm *TypeMap) makeNamedSliceType(tname string, elemtyp llvm.Type) llvm.Type {
|
|
t := tm.ctx.StructCreateNamed(tname)
|
|
t.StructSetBody([]llvm.Type{
|
|
llvm.PointerType(elemtyp, 0),
|
|
tm.inttype,
|
|
tm.inttype,
|
|
}, false)
|
|
return t
|
|
}
|
|
|
|
func (tm *TypeMap) makeSlice(values []llvm.Value, slicetyp llvm.Type) llvm.Value {
|
|
ptrtyp := slicetyp.StructElementTypes()[0]
|
|
var globalptr llvm.Value
|
|
if len(values) > 0 {
|
|
array := llvm.ConstArray(ptrtyp.ElementType(), values)
|
|
globalptr = llvm.AddGlobal(tm.module, array.Type(), "")
|
|
globalptr.SetGlobalConstant(true)
|
|
globalptr.SetLinkage(llvm.InternalLinkage)
|
|
globalptr.SetInitializer(array)
|
|
globalptr = llvm.ConstBitCast(globalptr, ptrtyp)
|
|
} else {
|
|
globalptr = llvm.ConstNull(ptrtyp)
|
|
}
|
|
len_ := llvm.ConstInt(tm.inttype, uint64(len(values)), false)
|
|
slice := llvm.ConstNull(slicetyp)
|
|
slice = llvm.ConstInsertValue(slice, globalptr, []uint32{0})
|
|
slice = llvm.ConstInsertValue(slice, len_, []uint32{1})
|
|
slice = llvm.ConstInsertValue(slice, len_, []uint32{2})
|
|
return slice
|
|
}
|
|
|
|
func isGlobalObject(obj types.Object) bool {
|
|
pkg := obj.Pkg()
|
|
return pkg == nil || obj.Parent() == pkg.Scope()
|
|
}
|