//===- 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/gotools/go/ssa" "llvm.org/llgo/third_party/gotools/go/ssa/ssautil" "llvm.org/llgo/third_party/gotools/go/types" "llvm.org/llgo/third_party/gotools/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 funcValType llvm.Type hashFnType, equalFnType llvm.Type algsEmptyInterface, algsInterface, algsFloat, algsComplex, algsString, algsIdentity, algsError algorithms } type algorithms struct { hash, hashDescriptor, equal, equalDescriptor 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) tm.funcValType = tm.ctx.StructCreateNamed("funcVal") tm.funcValType.StructSetBody([]llvm.Type{ llvm.PointerType(llvm.FunctionType(llvm.VoidType(), []llvm.Type{}, false), 0), }, false) 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) typeAlgorithms := [...]struct { Name string *algorithms }{ {"empty_interface", &tm.algsEmptyInterface}, {"interface", &tm.algsInterface}, {"float", &tm.algsFloat}, {"complex", &tm.algsComplex}, {"string", &tm.algsString}, {"identity", &tm.algsIdentity}, {"error", &tm.algsError}, } for _, typeAlgs := range typeAlgorithms { hashFnName := "__go_type_hash_" + typeAlgs.Name hashDescriptorName := hashFnName + "_descriptor" equalFnName := "__go_type_equal_" + typeAlgs.Name equalDescriptorName := equalFnName + "_descriptor" typeAlgs.hash = llvm.AddGlobal(tm.module, tm.hashFnType, hashFnName) typeAlgs.hashDescriptor = llvm.AddGlobal(tm.module, tm.funcValType, hashDescriptorName) typeAlgs.equal = llvm.AddGlobal(tm.module, tm.equalFnType, equalFnName) typeAlgs.equalDescriptor = llvm.AddGlobal(tm.module, tm.funcValType, equalDescriptorName) } 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.funcValType, 0), // hashfn llvm.PointerType(tm.funcValType, 0), // equalfn voidPtrType, // gc stringPtrType, // string llvm.PointerType(tm.uncommonTypeType, 0), // uncommonType commonTypeTypePtr, // ptrToThis }, 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 ManglePackagePath(pkgpath string) string { pkgpath = strings.Replace(pkgpath, "/", "_", -1) pkgpath = strings.Replace(pkgpath, ".", "_", -1) return 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 != "" { nb.WriteString(ManglePackagePath(ti.pkgpath)) 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 != "" { b.WriteString(ManglePackagePath(ti.pkgpath)) 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() } // Synthetic bound and thunk functions are special cases; they can only be // distinguished using private data that is only exposed via String(). if strings.HasSuffix(f.Name(), "$bound") || strings.HasSuffix(f.Name(), "$thunk") { b.WriteString(f.String()) return b.String() } var pkg *types.Package if f.Pkg != nil { pkg = f.Pkg.Object } else if !f.Object().Exported() { pkg = f.Object().Pkg() } if pkg != nil { b.WriteString(ManglePackagePath(pkg.Path())) 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 b.WriteString(ManglePackagePath(g.Pkg.Object.Path())) 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') b.WriteString(ManglePackagePath(ti.pkgpath)) 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 } } } } } 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)) } } func (tm *TypeMap) getStructAlgorithms(st *types.Struct) algorithms { if algs, ok := tm.algs.At(st).(algorithms); ok { return algs } hashes := make([]llvm.Value, st.NumFields()) equals := make([]llvm.Value, st.NumFields()) for i := range hashes { algs := tm.getAlgorithms(st.Field(i).Type()) if algs.hashDescriptor == tm.algsError.hashDescriptor { return algs } hashes[i], equals[i] = algs.hash, algs.equal } i8ptr := llvm.PointerType(tm.ctx.Int8Type(), 0) llsptrty := llvm.PointerType(tm.ToLLVM(st), 0) builder := tm.ctx.NewBuilder() defer builder.Dispose() hashFunctionName := tm.mc.mangleHashFunctionName(st) hash := llvm.AddFunction(tm.module, hashFunctionName, tm.hashFnType) hash.SetLinkage(llvm.LinkOnceODRLinkage) hashDescriptor := tm.createAlgorithmDescriptor(hashFunctionName+"_descriptor", hash) 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) equalFunctionName := tm.mc.mangleEqualFunctionName(st) equal := llvm.AddFunction(tm.module, equalFunctionName, tm.equalFnType) equal.SetLinkage(llvm.LinkOnceODRLinkage) equalDescriptor := tm.createAlgorithmDescriptor(equalFunctionName+"_descriptor", equal) 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) algs := algorithms{ hash: hash, hashDescriptor: hashDescriptor, equal: equal, equalDescriptor: equalDescriptor, } tm.algs.Set(st, algs) return algs } func (tm *TypeMap) getArrayAlgorithms(at *types.Array) algorithms { if algs, ok := tm.algs.At(at).(algorithms); ok { return algs } elemAlgs := tm.getAlgorithms(at.Elem()) if elemAlgs.hashDescriptor == tm.algsError.hashDescriptor { return elemAlgs } 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() hashFunctionName := tm.mc.mangleHashFunctionName(at) hash := llvm.AddFunction(tm.module, hashFunctionName, tm.hashFnType) hash.SetLinkage(llvm.LinkOnceODRLinkage) hashDescriptor := tm.createAlgorithmDescriptor(hashFunctionName+"_descriptor", hash) equalFunctionName := tm.mc.mangleHashFunctionName(at) equal := llvm.AddFunction(tm.module, equalFunctionName, tm.equalFnType) equal.SetLinkage(llvm.LinkOnceODRLinkage) equalDescriptor := tm.createAlgorithmDescriptor(equalFunctionName+"_descriptor", equal) algs := algorithms{ hash: hash, hashDescriptor: hashDescriptor, equal: equal, equalDescriptor: equalDescriptor, } tm.algs.Set(at, algs) 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(elemAlgs.hash, []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) 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(elemAlgs.equal, []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) } return algs } func (tm *TypeMap) createAlgorithmDescriptor(name string, fn llvm.Value) llvm.Value { d := llvm.AddGlobal(tm.module, tm.funcValType, name) d.SetLinkage(llvm.LinkOnceODRLinkage) d.SetGlobalConstant(true) fn = llvm.ConstBitCast(fn, tm.funcValType.StructElementTypes()[0]) init := llvm.ConstNull(tm.funcValType) init = llvm.ConstInsertValue(init, fn, []uint32{0}) d.SetInitializer(init) return d } func (tm *TypeMap) getAlgorithms(t types.Type) algorithms { switch t := t.Underlying().(type) { case *types.Interface: if t.NumMethods() == 0 { return tm.algsEmptyInterface } return tm.algsInterface case *types.Basic: switch t.Kind() { case types.Float32, types.Float64: return tm.algsFloat case types.Complex64, types.Complex128: return tm.algsComplex case types.String: return tm.algsString } return tm.algsIdentity case *types.Signature, *types.Map, *types.Slice: return tm.algsError case *types.Struct: return tm.getStructAlgorithms(t) case *types.Array: return tm.getArrayAlgorithms(t) } return tm.algsIdentity } 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 gccgoRuntimeTypeKindDIRECT_IFACE = (1 << 5) 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 | gccgoRuntimeTypeKindDIRECT_IFACE 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 | gccgoRuntimeTypeKindDIRECT_IFACE 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 [11]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) algs := tm.getAlgorithms(t) vals[5] = algs.hashDescriptor vals[6] = algs.equalDescriptor vals[7] = tm.getGcPointer(t) var b bytes.Buffer tm.writeType(t, &b) vals[8] = tm.globalStringPtr(b.String()) vals[9] = tm.makeUncommonTypePtr(t) switch t.(type) { case *types.Named, *types.Struct: vals[10] = tm.getTypeDescriptorPointer(types.NewPointer(t)) default: vals[10] = llvm.ConstPointerNull(llvm.PointerType(tm.commonTypeType, 0)) } 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() }