//===- 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()
}
