| //===- typemap.go - type and type descriptor mapping ----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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.AddFunction(tm.module, hashFnName, tm.hashFnType) |
| typeAlgs.hashDescriptor = llvm.AddGlobal(tm.module, tm.funcValType, hashDescriptorName) |
| typeAlgs.equal = llvm.AddFunction(tm.module, equalFnName, tm.equalFnType) |
| 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() |
| } |