func (tm *TypeMap) interfaceRuntimeType(i *types.Interface) (global, ptr llvm.Value) { rtype := tm.makeRtype(i, reflect.Interface) interfaceType := llvm.ConstNull(tm.runtime.interfaceType.llvm) global, ptr = tm.makeRuntimeTypeGlobal(interfaceType, typeString(i)) tm.types.Set(i, runtimeTypeInfo{global, ptr}) interfaceType = llvm.ConstInsertValue(interfaceType, rtype, []uint32{0}) methodset := tm.MethodSet(i) imethods := make([]llvm.Value, methodset.Len()) for index := 0; index < methodset.Len(); index++ { method := methodset.At(index).Obj() imethod := llvm.ConstNull(tm.runtime.imethod.llvm) name := tm.globalStringPtr(method.Name()) name = llvm.ConstBitCast(name, tm.runtime.imethod.llvm.StructElementTypes()[0]) mtyp := tm.ToRuntime(method.Type()) imethod = llvm.ConstInsertValue(imethod, name, []uint32{0}) if !ast.IsExported(method.Name()) { pkgpath := tm.globalStringPtr(method.Pkg().Path()) pkgpath = llvm.ConstBitCast(pkgpath, tm.runtime.imethod.llvm.StructElementTypes()[1]) imethod = llvm.ConstInsertValue(imethod, pkgpath, []uint32{1}) } imethod = llvm.ConstInsertValue(imethod, mtyp, []uint32{2}) imethods[index] = imethod } imethodsSliceType := tm.runtime.interfaceType.llvm.StructElementTypes()[1] imethodsSlice := tm.makeSlice(imethods, imethodsSliceType) interfaceType = llvm.ConstInsertValue(interfaceType, imethodsSlice, []uint32{1}) global.SetInitializer(interfaceType) return global, ptr }
func (tm *TypeMap) makeRuntimeTypeGlobal(v llvm.Value, name string) (global, ptr llvm.Value) { global = llvm.AddGlobal(tm.module, v.Type(), typeSymbol(name)) global.SetInitializer(v) global.SetLinkage(llvm.LinkOnceAnyLinkage) ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtime.rtype.llvm, 0)) return global, ptr }
func (tm *TypeMap) nameRuntimeType(n *types.Named) (global, ptr llvm.Value) { name := typeString(n) path := "runtime" if pkg := n.Obj().Pkg(); pkg != nil { path = pkg.Path() } if path != tm.pkgpath { // We're not compiling the package from whence the type came, // so we'll just create a pointer to it here. global := llvm.AddGlobal(tm.module, tm.runtime.rtype.llvm, typeSymbol(name)) global.SetInitializer(llvm.ConstNull(tm.runtime.rtype.llvm)) global.SetLinkage(llvm.CommonLinkage) return global, global } // If the underlying type is Basic, then we always create // a new global. Otherwise, we clone the value returned // from toRuntime in case it is cached and reused. underlying := n.Underlying() if basic, ok := underlying.(*types.Basic); ok { global, ptr = tm.basicRuntimeType(basic, true) global.SetName(typeSymbol(name)) } else { global, ptr = tm.toRuntime(underlying) clone := llvm.AddGlobal(tm.module, global.Type().ElementType(), typeSymbol(name)) clone.SetInitializer(global.Initializer()) global = clone ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtime.rtype.llvm, 0)) } global.SetLinkage(llvm.ExternalLinkage) // Locate the rtype. underlyingRuntimeType := global.Initializer() rtype := underlyingRuntimeType if rtype.Type() != tm.runtime.rtype.llvm { rtype = llvm.ConstExtractValue(rtype, []uint32{0}) } // Insert the uncommon type. uncommonTypeInit := tm.uncommonType(n, nil) uncommonType := llvm.AddGlobal(tm.module, uncommonTypeInit.Type(), "") uncommonType.SetInitializer(uncommonTypeInit) rtype = llvm.ConstInsertValue(rtype, uncommonType, []uint32{9}) // Replace the rtype's string representation with the one from // uncommonType. XXX should we have the package name prepended? Probably. namePtr := llvm.ConstExtractValue(uncommonTypeInit, []uint32{0}) rtype = llvm.ConstInsertValue(rtype, namePtr, []uint32{8}) // Update the global's initialiser. Note that we take a copy // of the underlying type; we're not updating a shared type. if underlyingRuntimeType.Type() != tm.runtime.rtype.llvm { underlyingRuntimeType = llvm.ConstInsertValue(underlyingRuntimeType, rtype, []uint32{0}) } else { underlyingRuntimeType = rtype } global.SetInitializer(underlyingRuntimeType) return global, ptr }
func (tm *TypeMap) structRuntimeType(s *types.Struct) (global, ptr llvm.Value) { rtype := tm.makeRtype(s, reflect.Struct) structType := llvm.ConstNull(tm.runtime.structType.llvm) structType = llvm.ConstInsertValue(structType, rtype, []uint32{0}) global, ptr = tm.makeRuntimeTypeGlobal(structType, typeString(s)) tm.types.Set(s, runtimeTypeInfo{global, ptr}) 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 := range structFields { field := fieldVars[i] structField := llvm.ConstNull(tm.runtime.structField.llvm) if !field.Anonymous() { name := tm.globalStringPtr(field.Name()) name = llvm.ConstBitCast(name, tm.runtime.structField.llvm.StructElementTypes()[0]) structField = llvm.ConstInsertValue(structField, name, []uint32{0}) } if !ast.IsExported(field.Name()) { pkgpath := tm.globalStringPtr(field.Pkg().Path()) pkgpath = llvm.ConstBitCast(pkgpath, tm.runtime.structField.llvm.StructElementTypes()[1]) structField = llvm.ConstInsertValue(structField, pkgpath, []uint32{1}) } fieldType := tm.ToRuntime(field.Type()) structField = llvm.ConstInsertValue(structField, fieldType, []uint32{2}) if tag := s.Tag(i); tag != "" { tag := tm.globalStringPtr(tag) tag = llvm.ConstBitCast(tag, tm.runtime.structField.llvm.StructElementTypes()[3]) structField = llvm.ConstInsertValue(structField, tag, []uint32{3}) } offset := llvm.ConstInt(tm.runtime.structField.llvm.StructElementTypes()[4], uint64(offsets[i]), false) structField = llvm.ConstInsertValue(structField, offset, []uint32{4}) structFields[i] = structField } structFieldsSliceType := tm.runtime.structType.llvm.StructElementTypes()[1] structFieldsSlice := tm.makeSlice(structFields, structFieldsSliceType) structType = llvm.ConstInsertValue(structType, structFieldsSlice, []uint32{1}) global.SetInitializer(structType) return global, ptr }
// 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.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.SetInitializer(str) return g }
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.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 (tm *TypeMap) makeRtype(t types.Type, k reflect.Kind) llvm.Value { // Not sure if there's an easier way to do this, but if you just // use ConstStruct, you end up getting a different llvm.Type. typ := llvm.ConstNull(tm.runtime.rtype.llvm) elementTypes := tm.runtime.rtype.llvm.StructElementTypes() // Size. size := llvm.ConstInt(elementTypes[0], uint64(tm.Sizeof(t)), false) typ = llvm.ConstInsertValue(typ, size, []uint32{0}) // TODO hash // TODO padding // Alignment. align := llvm.ConstInt(llvm.Int8Type(), uint64(tm.Alignof(t)), false) typ = llvm.ConstInsertValue(typ, align, []uint32{3}) // var typ = llvm.ConstInsertValue(typ, align, []uint32{4}) // field // Kind. kind := llvm.ConstInt(llvm.Int8Type(), uint64(k), false) typ = llvm.ConstInsertValue(typ, kind, []uint32{5}) // Algorithm table. alg := tm.makeAlgorithmTable(t) algptr := llvm.AddGlobal(tm.module, alg.Type(), "") algptr.SetInitializer(alg) algptr = llvm.ConstBitCast(algptr, elementTypes[6]) typ = llvm.ConstInsertValue(typ, algptr, []uint32{6}) // String representation. stringrep := tm.globalStringPtr(t.String()) typ = llvm.ConstInsertValue(typ, stringrep, []uint32{8}) // TODO gc return typ }
// p != nil iff we're generatig the uncommonType for a pointer type. func (tm *TypeMap) uncommonType(n *types.Named, p *types.Pointer) llvm.Value { uncommonTypeInit := llvm.ConstNull(tm.runtime.uncommonType.llvm) namePtr := tm.globalStringPtr(n.Obj().Name()) uncommonTypeInit = llvm.ConstInsertValue(uncommonTypeInit, namePtr, []uint32{0}) var path string if pkg := n.Obj().Pkg(); pkg != nil { path = pkg.Path() } pkgpathPtr := tm.globalStringPtr(path) uncommonTypeInit = llvm.ConstInsertValue(uncommonTypeInit, pkgpathPtr, []uint32{1}) // If we're dealing with an interface, stop now; // we store interface methods on the interface // type. if _, ok := n.Underlying().(*types.Interface); ok { return uncommonTypeInit } var methodset, pmethodset *types.MethodSet if p != nil { methodset = tm.MethodSet(p) } else { methodset = tm.MethodSet(n) } // 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, methodset.Len()) for i := range methods { sel := methodset.At(i) mname := sel.Obj().Name() mfunc := tm.methodResolver.ResolveMethod(sel) ftyp := mfunc.Type().(*types.Signature) method := llvm.ConstNull(tm.runtime.method.llvm) name := tm.globalStringPtr(mname) name = llvm.ConstBitCast(name, tm.runtime.method.llvm.StructElementTypes()[0]) // name method = llvm.ConstInsertValue(method, name, []uint32{0}) // pkgPath method = llvm.ConstInsertValue(method, pkgpathPtr, []uint32{1}) // mtyp (method type, no receiver) { ftyp := types.NewSignature(nil, nil, ftyp.Params(), ftyp.Results(), ftyp.Variadic()) mtyp := tm.ToRuntime(ftyp) method = llvm.ConstInsertValue(method, mtyp, []uint32{2}) } // typ (function type, with receiver) typ := tm.ToRuntime(ftyp) method = llvm.ConstInsertValue(method, typ, []uint32{3}) // tfn (standard method/function pointer for plain method calls) tfn := llvm.ConstPtrToInt(mfunc.LLVMValue(), tm.target.IntPtrType()) // ifn (single-word receiver function pointer for interface calls) ifn := tfn if p == nil { if tm.Sizeof(n) > int64(tm.target.PointerSize()) { if pmethodset == nil { pmethodset = tm.MethodSet(types.NewPointer(n)) } pmfunc := tm.methodResolver.ResolveMethod(pmethodset.Lookup(sel.Obj().Pkg(), mname)) ifn = llvm.ConstPtrToInt(pmfunc.LLVMValue(), tm.target.IntPtrType()) } else if _, ok := n.Underlying().(*types.Pointer); !ok { // Create a wrapper function that takes an *int8, // and coerces to the receiver type. ifn = tm.interfaceFuncWrapper(mfunc.LLVMValue()) ifn = llvm.ConstPtrToInt(ifn, tm.target.IntPtrType()) } } method = llvm.ConstInsertValue(method, ifn, []uint32{4}) method = llvm.ConstInsertValue(method, tfn, []uint32{5}) methods[i] = method } methodsSliceType := tm.runtime.uncommonType.llvm.StructElementTypes()[2] methodsSlice := tm.makeSlice(methods, methodsSliceType) uncommonTypeInit = llvm.ConstInsertValue(uncommonTypeInit, methodsSlice, []uint32{2}) return uncommonTypeInit }
func (c *compiler) NewConstValue(v exact.Value, typ types.Type) *LLVMValue { switch { case v == nil: llvmtyp := c.types.ToLLVM(typ) return c.NewValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := c.types.ToLLVM(typ) strval := exact.StringVal(v) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { init := llvm.ConstString(strval, false) ptr = llvm.AddGlobal(c.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(c.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return c.NewValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := c.types.ToLLVM(typ) var llvmvalue llvm.Value if isUnsigned(typ) { v, _ := exact.Uint64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, v, false) } else { v, _ := exact.Int64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true) } return c.NewValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } var llvmvalue llvm.Value if exact.BoolVal(v) { llvmvalue = llvm.ConstAllOnes(llvm.Int1Type()) } else { llvmvalue = llvm.ConstNull(llvm.Int1Type()) } return c.NewValue(llvmvalue, typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := c.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return c.NewValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := c.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(llvmtyp, v, false) return c.NewValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := c.types.ToLLVM(typ) floattyp := llvmtyp.StructElementTypes()[0] llvmvalue := llvm.ConstNull(llvmtyp) realv := exact.Real(v) imagv := exact.Imag(v) realfloatval, _ := exact.Float64Val(realv) imagfloatval, _ := exact.Float64Val(imagv) llvmre := llvm.ConstFloat(floattyp, realfloatval) llvmim := llvm.ConstFloat(floattyp, imagfloatval) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1}) return c.NewValue(llvmvalue, typ) } // Special case for string -> [](byte|rune) if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) { if v.Kind() == exact.String { strval := c.NewConstValue(v, types.Typ[types.String]) return strval.Convert(typ).(*LLVMValue) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v)) }
func (fr *frame) value(v ssa.Value) (result *LLVMValue) { switch v := v.(type) { case nil: return nil case *ssa.Function: result, ok := fr.funcvals[v] if ok { return result } // fr.globals[v] has the function in raw pointer form; // we must convert it to <f,ctx> form. If the function // does not have a receiver, then create a wrapper // function that has an additional "context" parameter. f := fr.resolveFunction(v) if v.Signature.Recv() == nil && len(v.FreeVars) == 0 { f = contextFunction(fr.compiler, f) } pair := llvm.ConstNull(fr.llvmtypes.ToLLVM(f.Type())) fnptr := llvm.ConstBitCast(f.LLVMValue(), pair.Type().StructElementTypes()[0]) pair = llvm.ConstInsertValue(pair, fnptr, []uint32{0}) result = fr.NewValue(pair, f.Type()) fr.funcvals[v] = result return result case *ssa.Const: return fr.NewConstValue(v.Value, v.Type()) case *ssa.Global: if g, ok := fr.globals[v]; ok { return g } // Create an external global. Globals for this package are defined // on entry to translatePackage, and have initialisers. llelemtyp := fr.llvmtypes.ToLLVM(deref(v.Type())) llglobal := llvm.AddGlobal(fr.module.Module, llelemtyp, v.String()) global := fr.NewValue(llglobal, v.Type()) fr.globals[v] = global return global } if value, ok := fr.env[v]; ok { return value } // Instructions are not necessarily visited before they are used (e.g. Phi // edges) so we must "backpatch": create a value with the resultant type, // and then replace it when we visit the instruction. if b, ok := fr.backpatch[v]; ok { return b } if fr.backpatch == nil { fr.backpatch = make(map[ssa.Value]*LLVMValue) } // Note: we must not create a constant here (e.g. Undef/ConstNull), as // it is not permissible to replace a constant with a non-constant. // We must create the value in its own standalone basic block, so we can // dispose of it after replacing. currBlock := fr.builder.GetInsertBlock() fr.builder.SetInsertPointAtEnd(llvm.AddBasicBlock(currBlock.Parent(), "")) placeholder := fr.compiler.builder.CreatePHI(fr.llvmtypes.ToLLVM(v.Type()), "") fr.builder.SetInsertPointAtEnd(currBlock) value := fr.NewValue(placeholder, v.Type()) fr.backpatch[v] = value return value }