// mapLookup implements v[, ok] = m[k] func (c *compiler) mapLookup(m, k *LLVMValue, commaOk bool) *LLVMValue { dyntyp := c.types.ToRuntime(m.Type()) dyntyp = c.builder.CreatePtrToInt(dyntyp, c.target.IntPtrType(), "") stackptr := c.stacksave() llk := k.LLVMValue() pk := c.builder.CreateAlloca(llk.Type(), "") c.builder.CreateStore(llk, pk) elemtyp := m.Type().Underlying().(*types.Map).Elem() pv := c.builder.CreateAlloca(c.types.ToLLVM(elemtyp), "") ok := c.builder.CreateCall( c.runtime.mapaccess.LLVMValue(), []llvm.Value{ dyntyp, m.LLVMValue(), c.builder.CreatePtrToInt(pk, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(pv, c.target.IntPtrType(), ""), }, "", ) v := c.builder.CreateLoad(pv, "") c.stackrestore(stackptr) if !commaOk { return c.NewValue(v, elemtyp) } typ := tupleType(elemtyp, types.Typ[types.Bool]) tuple := llvm.Undef(c.types.ToLLVM(typ)) tuple = c.builder.CreateInsertValue(tuple, v, 0, "") tuple = c.builder.CreateInsertValue(tuple, ok, 1, "") return c.NewValue(tuple, typ) }
// stringIterNext advances the iterator, and returns the tuple (ok, k, v). func (c *compiler) stringIterNext(str *LLVMValue, preds []llvm.BasicBlock) *LLVMValue { // While Range/Next expresses a mutating operation, we represent them using // a Phi node where the first incoming branch (before the loop), and all // others take the previous value plus one. // // See ssa.go for comments on (and assertions of) our assumptions. index := c.builder.CreatePHI(c.types.inttype, "index") strnext := c.runtime.strnext.LLVMValue() args := []llvm.Value{ c.coerceString(str.LLVMValue(), strnext.Type().ElementType().ParamTypes()[0]), index, } result := c.builder.CreateCall(strnext, args, "") nextindex := c.builder.CreateExtractValue(result, 0, "") runeval := c.builder.CreateExtractValue(result, 1, "") values := make([]llvm.Value, len(preds)) values[0] = llvm.ConstNull(index.Type()) for i, _ := range preds[1:] { values[i+1] = nextindex } index.AddIncoming(values, preds) // Create an (ok, index, rune) tuple. ok := c.builder.CreateIsNotNull(nextindex, "") typ := tupleType(types.Typ[types.Bool], types.Typ[types.Int], types.Typ[types.Rune]) tuple := llvm.Undef(c.types.ToLLVM(typ)) tuple = c.builder.CreateInsertValue(tuple, ok, 0, "") tuple = c.builder.CreateInsertValue(tuple, index, 1, "") tuple = c.builder.CreateInsertValue(tuple, runeval, 2, "") return c.NewValue(tuple, typ) }
func (c *compiler) makeInterface(v *LLVMValue, iface types.Type) *LLVMValue { llv := v.LLVMValue() lltyp := llv.Type() i8ptr := llvm.PointerType(llvm.Int8Type(), 0) if lltyp.TypeKind() == llvm.PointerTypeKind { llv = c.builder.CreateBitCast(llv, i8ptr, "") } else { // If the value fits exactly in a pointer, then we can just // bitcast it. Otherwise we need to malloc. if c.target.TypeStoreSize(lltyp) <= uint64(c.target.PointerSize()) { bits := c.target.TypeSizeInBits(lltyp) if bits > 0 { llv = coerce(c.builder, llv, llvm.IntType(int(bits))) llv = c.builder.CreateIntToPtr(llv, i8ptr, "") } else { llv = llvm.ConstNull(i8ptr) } } else { ptr := c.createTypeMalloc(lltyp) c.builder.CreateStore(llv, ptr) llv = c.builder.CreateBitCast(ptr, i8ptr, "") } } value := llvm.Undef(c.types.ToLLVM(iface)) rtype := c.types.ToRuntime(v.Type()) rtype = c.builder.CreateBitCast(rtype, llvm.PointerType(llvm.Int8Type(), 0), "") value = c.builder.CreateInsertValue(value, rtype, 0, "") value = c.builder.CreateInsertValue(value, llv, 1, "") if iface.Underlying().(*types.Interface).NumMethods() > 0 { result := c.NewValue(value, types.NewInterface(nil, nil)) result, _ = result.convertE2I(iface) return result } return c.NewValue(value, iface) }
func (c *compiler) VisitAppend(expr *ast.CallExpr) Value { // TODO handle ellpisis arg s := c.VisitExpr(expr.Args[0]) elem := c.VisitExpr(expr.Args[1]) sliceappend := c.NamedFunction("runtime.sliceappend", "func f(t uintptr, dst, src slice) slice") i8slice := sliceappend.Type().ElementType().ReturnType() i8ptr := c.types.ToLLVM(&types.Pointer{Base: types.Int8}) // Coerce first argument into an []int8. a_ := s.LLVMValue() sliceTyp := a_.Type() a := c.coerceSlice(a_, i8slice) // Construct a fresh []int8 for the temporary slice. b_ := elem.LLVMValue() one := llvm.ConstInt(llvm.Int32Type(), 1, false) mem := c.builder.CreateAlloca(elem.LLVMValue().Type(), "") c.builder.CreateStore(b_, mem) b := llvm.Undef(i8slice) b = c.builder.CreateInsertValue(b, c.builder.CreateBitCast(mem, i8ptr, ""), 0, "") b = c.builder.CreateInsertValue(b, one, 1, "") b = c.builder.CreateInsertValue(b, one, 2, "") // Call runtime function, then coerce the result. runtimeTyp := c.types.ToRuntime(s.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, a, b} result := c.builder.CreateCall(sliceappend, args, "") return c.NewLLVMValue(c.coerceSlice(result, sliceTyp), s.Type()) }
// makeSlice allocates a new slice with the optional length and capacity, // initialising its contents to their zero values. func (c *compiler) makeSlice(elttyp types.Type, length, capacity Value) llvm.Value { var lengthValue llvm.Value if length != nil { lengthValue = length.Convert(types.Int32).LLVMValue() } else { lengthValue = llvm.ConstNull(llvm.Int32Type()) } // TODO check capacity >= length capacityValue := lengthValue if capacity != nil { capacityValue = capacity.Convert(types.Int32).LLVMValue() } llvmelttyp := c.types.ToLLVM(elttyp) mem := c.builder.CreateArrayMalloc(llvmelttyp, capacityValue, "") sizeof := llvm.ConstTrunc(llvm.SizeOf(llvmelttyp), llvm.Int32Type()) size := c.builder.CreateMul(capacityValue, sizeof, "") c.memsetZero(mem, size) slicetyp := types.Slice{Elt: elttyp} struct_ := llvm.Undef(c.types.ToLLVM(&slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, lengthValue, 1, "") struct_ = c.builder.CreateInsertValue(struct_, capacityValue, 2, "") return struct_ }
// makeSlice allocates a new slice with the optional length and capacity, // initialising its contents to their zero values. func (c *compiler) makeSlice(elttyp types.Type, length, capacity Value) llvm.Value { var lengthValue llvm.Value if length != nil { lengthValue = length.Convert(types.Typ[types.Int]).LLVMValue() } else { lengthValue = llvm.ConstNull(c.llvmtypes.inttype) } // TODO check capacity >= length capacityValue := lengthValue if capacity != nil { capacityValue = capacity.Convert(types.Typ[types.Int]).LLVMValue() } eltType := c.types.ToLLVM(elttyp) sizeof := llvm.ConstTruncOrBitCast(llvm.SizeOf(eltType), c.types.inttype) size := c.builder.CreateMul(capacityValue, sizeof, "") mem := c.createMalloc(size) mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "") c.memsetZero(mem, size) slicetyp := types.NewSlice(elttyp) struct_ := llvm.Undef(c.types.ToLLVM(slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, lengthValue, 1, "") struct_ = c.builder.CreateInsertValue(struct_, capacityValue, 2, "") return struct_ }
// interfaceMethod returns a function pointer for the specified // interface and method pair. func (c *compiler) interfaceMethod(iface *LLVMValue, method *types.Func) *LLVMValue { lliface := iface.LLVMValue() llitab := c.builder.CreateExtractValue(lliface, 0, "") llvalue := c.builder.CreateExtractValue(lliface, 1, "") sig := method.Type().(*types.Signature) methodset := c.types.MethodSet(sig.Recv().Type()) // TODO(axw) cache ordered method index var index int for i := 0; i < methodset.Len(); i++ { if methodset.At(i).Obj() == method { index = i break } } llitab = c.builder.CreateBitCast(llitab, llvm.PointerType(c.runtime.itab.llvm, 0), "") llifn := c.builder.CreateGEP(llitab, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), 5, false), // index of itab.fun }, "") _ = index llifn = c.builder.CreateGEP(llifn, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), uint64(index), false), }, "") llifn = c.builder.CreateLoad(llifn, "") // Strip receiver. sig = types.NewSignature(nil, nil, sig.Params(), sig.Results(), sig.Variadic()) llfn := llvm.Undef(c.types.ToLLVM(sig)) llifn = c.builder.CreateIntToPtr(llifn, llfn.Type().StructElementTypes()[0], "") llfn = c.builder.CreateInsertValue(llfn, llifn, 0, "") llfn = c.builder.CreateInsertValue(llfn, llvalue, 1, "") return c.NewValue(llfn, sig) }
func (c *compiler) slice(x, low, high *LLVMValue) *LLVMValue { if low != nil { low = low.Convert(types.Typ[types.Int]).(*LLVMValue) } else { low = c.NewValue(llvm.ConstNull(c.types.inttype), types.Typ[types.Int]) } if high != nil { high = high.Convert(types.Typ[types.Int]).(*LLVMValue) } else { // all bits set is -1 high = c.NewValue(llvm.ConstAllOnes(c.types.inttype), types.Typ[types.Int]) } switch typ := x.Type().Underlying().(type) { case *types.Pointer: // *array sliceslice := c.runtime.sliceslice.LLVMValue() i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := llvm.Undef(i8slice) // temporary slice arraytyp := typ.Elem().Underlying().(*types.Array) arrayptr := x.LLVMValue() arrayptr = c.builder.CreateBitCast(arrayptr, i8slice.StructElementTypes()[0], "") arraylen := llvm.ConstInt(c.llvmtypes.inttype, uint64(arraytyp.Len()), false) sliceValue = c.builder.CreateInsertValue(sliceValue, arrayptr, 0, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 1, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 2, "") sliceTyp := types.NewSlice(arraytyp.Elem()) runtimeTyp := c.types.ToRuntime(sliceTyp) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low.LLVMValue(), high.LLVMValue()} result := c.builder.CreateCall(sliceslice, args, "") llvmSliceTyp := c.types.ToLLVM(sliceTyp) return c.NewValue(c.coerceSlice(result, llvmSliceTyp), sliceTyp) case *types.Slice: sliceslice := c.runtime.sliceslice.LLVMValue() i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := x.LLVMValue() sliceTyp := sliceValue.Type() sliceValue = c.coerceSlice(sliceValue, i8slice) runtimeTyp := c.types.ToRuntime(x.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low.LLVMValue(), high.LLVMValue()} result := c.builder.CreateCall(sliceslice, args, "") return c.NewValue(c.coerceSlice(result, sliceTyp), x.Type()) case *types.Basic: stringslice := c.runtime.stringslice.LLVMValue() llv := x.LLVMValue() args := []llvm.Value{ c.coerceString(llv, stringslice.Type().ElementType().ParamTypes()[0]), low.LLVMValue(), high.LLVMValue(), } result := c.builder.CreateCall(stringslice, args, "") return c.NewValue(c.coerceString(result, llv.Type()), x.Type()) default: panic("unimplemented") } panic("unreachable") }
func (c *compiler) coerceString(v llvm.Value, typ llvm.Type) llvm.Value { result := llvm.Undef(typ) ptr := c.builder.CreateExtractValue(v, 0, "") len := c.builder.CreateExtractValue(v, 1, "") result = c.builder.CreateInsertValue(result, ptr, 0, "") result = c.builder.CreateInsertValue(result, len, 1, "") return result }
func (tm *TypeMap) makeRuntimeTypeGlobal(v llvm.Value) (global, ptr llvm.Value) { // Each runtime type is preceded by an interface{}. initType := llvm.StructType([]llvm.Type{tm.runtimeType, v.Type()}, false) global = llvm.AddGlobal(tm.module, initType, "") ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtimeType, 0)) // interface{} containing v's *commonType representation. runtimeTypeValue := llvm.Undef(tm.runtimeType) zero := llvm.ConstNull(llvm.Int32Type()) one := llvm.ConstInt(llvm.Int32Type(), 1, false) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) if tm.commonTypePtrRuntimeType.IsNil() { // Create a dummy pointer value, which we'll update straight after // defining the runtime type info for commonType. tm.commonTypePtrRuntimeType = llvm.Undef(i8ptr) commonTypePtr := &types.Pointer{Base: tm.commonType} commonTypeGlobal, commonTypeRuntimeType := tm.makeRuntimeType(tm.commonType) tm.types[tm.commonType.String()] = runtimeTypeInfo{commonTypeGlobal, commonTypeRuntimeType} commonTypePtrGlobal, commonTypePtrRuntimeType := tm.makeRuntimeType(commonTypePtr) tm.types[commonTypePtr.String()] = runtimeTypeInfo{commonTypePtrGlobal, commonTypePtrRuntimeType} tm.commonTypePtrRuntimeType = llvm.ConstBitCast(commonTypePtrRuntimeType, i8ptr) if tm.pkgpath == tm.commonType.Package { // Update the interace{} header of the commonType/*commonType // runtime types we just created. for _, g := range [...]llvm.Value{commonTypeGlobal, commonTypePtrGlobal} { init := g.Initializer() typptr := tm.commonTypePtrRuntimeType runtimeTypeValue := llvm.ConstExtractValue(init, []uint32{0}) runtimeTypeValue = llvm.ConstInsertValue(runtimeTypeValue, typptr, []uint32{0}) init = llvm.ConstInsertValue(init, runtimeTypeValue, []uint32{0}) g.SetInitializer(init) } } } commonTypePtr := llvm.ConstGEP(global, []llvm.Value{zero, one}) commonTypePtr = llvm.ConstBitCast(commonTypePtr, i8ptr) runtimeTypeValue = llvm.ConstInsertValue(runtimeTypeValue, tm.commonTypePtrRuntimeType, []uint32{0}) runtimeTypeValue = llvm.ConstInsertValue(runtimeTypeValue, commonTypePtr, []uint32{1}) init := llvm.Undef(initType) init = llvm.ConstInsertValue(init, runtimeTypeValue, []uint32{0}) init = llvm.ConstInsertValue(init, v, []uint32{1}) global.SetInitializer(init) return global, ptr }
func (c *compiler) VisitSliceExpr(expr *ast.SliceExpr) Value { // expr.X, expr.Low, expr.High value := c.VisitExpr(expr.X) var low, high llvm.Value if expr.Low != nil { low = c.VisitExpr(expr.Low).Convert(types.Int32).LLVMValue() } else { low = llvm.ConstNull(llvm.Int32Type()) } if expr.High != nil { high = c.VisitExpr(expr.High).Convert(types.Int32).LLVMValue() } else { high = llvm.ConstAllOnes(llvm.Int32Type()) // -1 } if _, ok := types.Underlying(value.Type()).(*types.Pointer); ok { value = value.(*LLVMValue).makePointee() } switch typ := types.Underlying(value.Type()).(type) { case *types.Array: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int32) slice") i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := llvm.Undef(i8slice) // temporary slice arrayptr := value.(*LLVMValue).pointer.LLVMValue() arrayptr = c.builder.CreateBitCast(arrayptr, i8slice.StructElementTypes()[0], "") arraylen := llvm.ConstInt(llvm.Int32Type(), typ.Len, false) sliceValue = c.builder.CreateInsertValue(sliceValue, arrayptr, 0, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 1, "") sliceValue = c.builder.CreateInsertValue(sliceValue, arraylen, 2, "") sliceTyp := &types.Slice{Elt: typ.Elt} runtimeTyp := c.types.ToRuntime(sliceTyp) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low, high} result := c.builder.CreateCall(sliceslice, args, "") llvmSliceTyp := c.types.ToLLVM(sliceTyp) return c.NewLLVMValue(c.coerceSlice(result, llvmSliceTyp), sliceTyp) case *types.Slice: sliceslice := c.NamedFunction("runtime.sliceslice", "func f(t uintptr, s slice, low, high int32) slice") i8slice := sliceslice.Type().ElementType().ReturnType() sliceValue := value.LLVMValue() sliceTyp := sliceValue.Type() sliceValue = c.coerceSlice(sliceValue, i8slice) runtimeTyp := c.types.ToRuntime(value.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, sliceValue, low, high} result := c.builder.CreateCall(sliceslice, args, "") return c.NewLLVMValue(c.coerceSlice(result, sliceTyp), value.Type()) case *types.Name: // String stringslice := c.NamedFunction("runtime.stringslice", "func f(a string, low, high int32) string") args := []llvm.Value{value.LLVMValue(), low, high} result := c.builder.CreateCall(stringslice, args, "") return c.NewLLVMValue(result, value.Type()) default: panic("unimplemented") } panic("unreachable") }
// coerceSlice takes a slice of one element type and coerces it to a // slice of another. func (c *compiler) coerceSlice(src llvm.Value, dsttyp llvm.Type) llvm.Value { dst := llvm.Undef(dsttyp) srcmem := c.builder.CreateExtractValue(src, 0, "") srclen := c.builder.CreateExtractValue(src, 1, "") srccap := c.builder.CreateExtractValue(src, 2, "") dstmemtyp := dsttyp.StructElementTypes()[0] dstmem := c.builder.CreateBitCast(srcmem, dstmemtyp, "") dst = c.builder.CreateInsertValue(dst, dstmem, 0, "") dst = c.builder.CreateInsertValue(dst, srclen, 1, "") dst = c.builder.CreateInsertValue(dst, srccap, 2, "") return dst }
func (c *compiler) VisitAppend(expr *ast.CallExpr) Value { s := c.VisitExpr(expr.Args[0]) elemtyp := s.Type().Underlying().(*types.Slice).Elem() if len(expr.Args) == 1 { return s } else if expr.Ellipsis.IsValid() { c.convertUntyped(expr.Args[1], s.Type()) } else { for _, arg := range expr.Args[1:] { c.convertUntyped(arg, elemtyp) } } sliceappend := c.NamedFunction("runtime.sliceappend", "func(t uintptr, dst, src slice) slice") i8slice := sliceappend.Type().ElementType().ReturnType() i8ptr := c.types.ToLLVM(types.NewPointer(types.Typ[types.Int8])) // Coerce first argument into an []int8. a_ := s.LLVMValue() sliceTyp := a_.Type() a := c.coerceSlice(a_, i8slice) var b llvm.Value if expr.Ellipsis.IsValid() { // Pass the provided slice straight through. If it's a string, // convert it to a []byte first. elem := c.VisitExpr(expr.Args[1]).Convert(s.Type()) b = c.coerceSlice(elem.LLVMValue(), i8slice) } else { // Construct a fresh []int8 for the temporary slice. n := llvm.ConstInt(c.types.inttype, uint64(len(expr.Args)-1), false) mem := c.builder.CreateArrayAlloca(c.types.ToLLVM(elemtyp), n, "") for i, arg := range expr.Args[1:] { elem := c.VisitExpr(arg).Convert(elemtyp) indices := []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(i), false)} ptr := c.builder.CreateGEP(mem, indices, "") c.builder.CreateStore(elem.LLVMValue(), ptr) } b = llvm.Undef(i8slice) b = c.builder.CreateInsertValue(b, c.builder.CreateBitCast(mem, i8ptr, ""), 0, "") b = c.builder.CreateInsertValue(b, n, 1, "") b = c.builder.CreateInsertValue(b, n, 2, "") } // Call runtime function, then coerce the result. runtimeTyp := c.types.ToRuntime(s.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, c.target.IntPtrType(), "") args := []llvm.Value{runtimeTyp, a, b} result := c.builder.CreateCall(sliceappend, args, "") return c.NewValue(c.coerceSlice(result, sliceTyp), s.Type()) }
// makeSlice allocates a new slice, storing in it the provided elements. func (c *compiler) makeSlice(v []llvm.Value, elttyp types.Type) llvm.Value { n := llvm.ConstInt(llvm.Int32Type(), uint64(len(v)), false) llvmelttyp := c.types.ToLLVM(elttyp) mem := c.builder.CreateArrayMalloc(llvmelttyp, n, "") for i, value := range v { indices := []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(i), false)} ep := c.builder.CreateGEP(mem, indices, "") c.builder.CreateStore(value, ep) } slicetyp := types.Slice{Elt: elttyp} struct_ := llvm.Undef(c.types.ToLLVM(&slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, n, 1, "") struct_ = c.builder.CreateInsertValue(struct_, n, 2, "") return struct_ }
func (v *LLVMValue) convertMethodValue(dsttyp types.Type) *LLVMValue { b := v.compiler.builder dstlt := v.compiler.types.ToLLVM(dsttyp) dstltelems := dstlt.StructElementTypes() srclv := v.LLVMValue() fnptr := b.CreateExtractValue(srclv, 0, "") fnctx := b.CreateExtractValue(srclv, 1, "") // TODO(axw) There's a lot of overlap between this // and the code that converts concrete methods to // interface methods. Refactor. if fnctx.Type().TypeKind() == llvm.PointerTypeKind { fnctx = b.CreateBitCast(fnctx, dstltelems[1], "") } else { c := v.compiler ptrsize := c.target.PointerSize() if c.target.TypeStoreSize(fnctx.Type()) <= uint64(ptrsize) { bits := c.target.TypeSizeInBits(fnctx.Type()) if bits > 0 { fnctx = c.coerce(fnctx, llvm.IntType(int(bits))) fnctx = b.CreateIntToPtr(fnctx, dstltelems[1], "") } else { fnctx = llvm.ConstNull(dstltelems[1]) } } else { ptr := c.createTypeMalloc(fnctx.Type()) b.CreateStore(fnctx, ptr) fnctx = b.CreateBitCast(ptr, dstltelems[1], "") // Switch to the pointer-receiver method. methodset := c.methods(v.Type().(*types.Signature).Recv().Type()) ptrmethod := methodset.lookup(v.method.Name(), true) if f, ok := ptrmethod.(*types.Func); ok { ptrmethod = c.methodfunc(f) } lv := c.Resolve(c.objectdata[ptrmethod].Ident).LLVMValue() fnptr = c.builder.CreateExtractValue(lv, 0, "") } } dstlv := llvm.Undef(dstlt) fnptr = b.CreateBitCast(fnptr, dstltelems[0], "") dstlv = b.CreateInsertValue(dstlv, fnptr, 0, "") dstlv = b.CreateInsertValue(dstlv, fnctx, 1, "") return v.compiler.NewValue(dstlv, dsttyp) }
// makeClosure creates a closure from a function pointer and // a set of bindings. The bindings are addresses of captured // variables. func (c *compiler) makeClosure(fn *LLVMValue, bindings []*LLVMValue) *LLVMValue { types := make([]llvm.Type, len(bindings)) for i, binding := range bindings { types[i] = c.types.ToLLVM(binding.Type()) } block := c.createTypeMalloc(llvm.StructType(types, false)) for i, binding := range bindings { addressPtr := c.builder.CreateStructGEP(block, i, "") c.builder.CreateStore(binding.LLVMValue(), addressPtr) } block = c.builder.CreateBitCast(block, llvm.PointerType(llvm.Int8Type(), 0), "") // fn is a raw function pointer; ToLLVM yields {*fn, *uint8}. closure := llvm.Undef(c.types.ToLLVM(fn.Type())) fnptr := c.builder.CreateBitCast(fn.LLVMValue(), closure.Type().StructElementTypes()[0], "") closure = c.builder.CreateInsertValue(closure, fnptr, 0, "") closure = c.builder.CreateInsertValue(closure, block, 1, "") return c.NewValue(closure, fn.Type()) }
// makeLiteralSlice allocates a new slice, storing in it the provided elements. func (c *compiler) makeLiteralSlice(v []llvm.Value, elttyp types.Type) llvm.Value { n := llvm.ConstInt(c.types.inttype, uint64(len(v)), false) eltType := c.types.ToLLVM(elttyp) arrayType := llvm.ArrayType(eltType, len(v)) mem := c.createMalloc(llvm.SizeOf(arrayType)) mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "") for i, value := range v { indices := []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(i), false)} ep := c.builder.CreateGEP(mem, indices, "") c.builder.CreateStore(value, ep) } slicetyp := types.NewSlice(elttyp) struct_ := llvm.Undef(c.types.ToLLVM(slicetyp)) struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "") struct_ = c.builder.CreateInsertValue(struct_, n, 1, "") struct_ = c.builder.CreateInsertValue(struct_, n, 2, "") return struct_ }
func (c *compiler) VisitAppend(expr *ast.CallExpr) Value { // TODO handle ellpisis arg s := c.VisitExpr(expr.Args[0]) elem := c.VisitExpr(expr.Args[1]) appendName := "runtime.sliceappend" appendFun := c.module.NamedFunction(appendName) uintptrTyp := c.target.IntPtrType() var i8slice llvm.Type if appendFun.IsNil() { i8slice = c.types.ToLLVM(&types.Slice{Elt: types.Int8}) args := []llvm.Type{uintptrTyp, i8slice, i8slice} appendFunTyp := llvm.FunctionType(i8slice, args, false) appendFun = llvm.AddFunction(c.module.Module, appendName, appendFunTyp) } else { i8slice = appendFun.Type().ReturnType() } i8ptr := i8slice.StructElementTypes()[0] // Coerce first argument into an []int8. a_ := s.LLVMValue() sliceTyp := a_.Type() a := c.coerceSlice(a_, i8slice) // Construct a fresh []int8 for the temporary slice. b_ := elem.LLVMValue() one := llvm.ConstInt(llvm.Int32Type(), 1, false) mem := c.builder.CreateAlloca(elem.LLVMValue().Type(), "") c.builder.CreateStore(b_, mem) b := llvm.Undef(i8slice) b = c.builder.CreateInsertValue(b, c.builder.CreateBitCast(mem, i8ptr, ""), 0, "") b = c.builder.CreateInsertValue(b, one, 1, "") b = c.builder.CreateInsertValue(b, one, 2, "") // Call runtime function, then coerce the result. runtimeTyp := c.types.ToRuntime(s.Type()) runtimeTyp = c.builder.CreatePtrToInt(runtimeTyp, uintptrTyp, "") args := []llvm.Value{runtimeTyp, a, b} result := c.builder.CreateCall(appendFun, args, "") return c.NewLLVMValue(c.coerceSlice(result, sliceTyp), s.Type()) }
func (tm *TypeMap) makeRuntimeTypeGlobal(v llvm.Value) (global, ptr llvm.Value) { runtimeTypeValue := llvm.ConstNull(tm.runtimeType) initType := llvm.StructType([]llvm.Type{tm.runtimeType, v.Type()}, false) global = llvm.AddGlobal(tm.module, initType, "") ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtimeType, 0)) // Set ptrToThis in v's commonType. if v.Type() == tm.runtimeCommonType { v = llvm.ConstInsertValue(v, ptr, []uint32{9}) } else { commonType := llvm.ConstExtractValue(v, []uint32{0}) commonType = llvm.ConstInsertValue(commonType, ptr, []uint32{9}) v = llvm.ConstInsertValue(v, commonType, []uint32{0}) } init := llvm.Undef(initType) //runtimeTypeValue = llvm.ConstInsertValue() TODO init = llvm.ConstInsertValue(init, runtimeTypeValue, []uint32{0}) init = llvm.ConstInsertValue(init, v, []uint32{1}) global.SetInitializer(init) return }
// chanRecv implements x[, ok] = <-ch func (c *compiler) chanRecv(ch *LLVMValue, commaOk bool) *LLVMValue { elemtyp := ch.Type().Underlying().(*types.Chan).Elem() stackptr := c.stacksave() ptr := c.builder.CreateAlloca(c.types.ToLLVM(elemtyp), "") chanrecv := c.runtime.chanrecv.LLVMValue() chantyp := c.types.ToRuntime(ch.Type().Underlying()) chantyp = c.builder.CreateBitCast(chantyp, chanrecv.Type().ElementType().ParamTypes()[0], "") ok := c.builder.CreateCall(chanrecv, []llvm.Value{ chantyp, ch.LLVMValue(), c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), ""), boolLLVMValue(false), // nb }, "") elem := c.builder.CreateLoad(ptr, "") c.stackrestore(stackptr) if !commaOk { return c.NewValue(elem, elemtyp) } typ := tupleType(elemtyp, types.Typ[types.Bool]) tuple := llvm.Undef(c.types.ToLLVM(typ)) tuple = c.builder.CreateInsertValue(tuple, elem, 0, "") tuple = c.builder.CreateInsertValue(tuple, ok, 1, "") return c.NewValue(tuple, typ) }
func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value { // Is it a type conversion? if len(expr.Args) == 1 && c.isType(expr.Fun) { typ := c.typeinfo.Types[expr] c.convertUntyped(expr.Args[0], typ) value := c.VisitExpr(expr.Args[0]) return value.Convert(typ) } // Builtin functions. // Builtin function's have a special Type (types.builtin). // // Note: we do not handle unsafe.{Align,Offset,Size}of here, // as they are evaluated during type-checking. if builtin := c.maybeBuiltin(expr.Fun); builtin != nil { switch builtin.Name() { case "close": c.visitClose(expr) return nil case "copy": return c.VisitCopy(expr) case "print": return c.visitPrint(expr) case "println": return c.visitPrintln(expr) case "cap": return c.VisitCap(expr) case "len": return c.VisitLen(expr) case "new": return c.VisitNew(expr) case "make": return c.VisitMake(expr) case "append": return c.VisitAppend(expr) case "delete": m := c.VisitExpr(expr.Args[0]).(*LLVMValue) key := c.VisitExpr(expr.Args[1]) c.mapDelete(m, key) return nil case "panic": var arg Value if len(expr.Args) > 0 { arg = c.VisitExpr(expr.Args[0]) } c.visitPanic(arg) return nil case "recover": return c.visitRecover() case "real": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(0) case "imag": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(1) case "complex": r := c.VisitExpr(expr.Args[0]).LLVMValue() i := c.VisitExpr(expr.Args[1]).LLVMValue() typ := c.typeinfo.Types[expr] cmplx := llvm.Undef(c.types.ToLLVM(typ)) cmplx = c.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = c.builder.CreateInsertValue(cmplx, i, 1, "") return c.NewValue(cmplx, typ) } } // Not a type conversion, so must be a function call. lhs := c.VisitExpr(expr.Fun) fn := lhs.(*LLVMValue) fn_type := fn.Type().Underlying().(*types.Signature) // Evaluate arguments. dotdotdot := expr.Ellipsis.IsValid() argValues := c.evalCallArgs(fn_type, expr.Args, dotdotdot) // Depending on whether the function contains defer statements or not, // we'll generate either a "call" or an "invoke" instruction. var invoke bool if f := c.functions.top(); f != nil && !f.deferblock.IsNil() { invoke = true } return c.createCall(fn, argValues, dotdotdot, invoke) }
func (v *LLVMValue) Convert(dst_typ types.Type) Value { // If it's a stack allocated value, we'll want to compare the // value type, not the pointer type. src_typ := v.typ // Get the underlying type, if any. orig_dst_typ := dst_typ if name, isname := dst_typ.(*types.Name); isname { dst_typ = types.Underlying(name) } // Get the underlying type, if any. if name, isname := src_typ.(*types.Name); isname { src_typ = types.Underlying(name) } // Identical (underlying) types? Just swap in the destination type. if types.Identical(src_typ, dst_typ) { dst_typ = orig_dst_typ // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } // Convert from an interface type. if _, isinterface := src_typ.(*types.Interface); isinterface { if interface_, isinterface := dst_typ.(*types.Interface); isinterface { return v.convertI2I(interface_) } else { return v.convertI2V(dst_typ) } } // Converting to an interface type. if interface_, isinterface := dst_typ.(*types.Interface); isinterface { return v.convertV2I(interface_) } // string -> []byte byteslice := &types.Slice{Elt: types.Byte} if src_typ == types.String && types.Identical(dst_typ, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") struct_ := llvm.Undef(c.types.ToLLVM(byteslice)) struct_ = c.builder.CreateInsertValue(struct_, strdata, 0, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 1, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 2, "") return c.NewLLVMValue(struct_, byteslice) } // []byte -> string if types.Identical(src_typ, byteslice) && dst_typ == types.String { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") struct_ := llvm.Undef(c.types.ToLLVM(types.String)) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewLLVMValue(struct_, types.String) } // TODO other special conversions, e.g. int->string. llvm_type := v.compiler.types.ToLLVM(dst_typ) // Unsafe pointer conversions. if dst_typ == types.UnsafePointer { // X -> unsafe.Pointer if _, isptr := src_typ.(*types.Pointer); isptr { value := v.compiler.builder.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, dst_typ) } else if src_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } } else if src_typ == types.UnsafePointer { // unsafe.Pointer -> X if _, isptr := dst_typ.(*types.Pointer); isptr { value := v.compiler.builder.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewLLVMValue(value, dst_typ) } else if dst_typ == types.Uintptr { return v.compiler.NewLLVMValue(v.LLVMValue(), dst_typ) } } // FIXME select the appropriate cast here, depending on size, type (int/float) // and sign. lv := v.LLVMValue() srcType := lv.Type() switch srcType.TypeKind() { // source type case llvm.IntegerTypeKind: switch llvm_type.TypeKind() { case llvm.IntegerTypeKind: srcBits := srcType.IntTypeWidth() dstBits := llvm_type.IntTypeWidth() delta := srcBits - dstBits switch { case delta < 0: // TODO check if (un)signed, use S/ZExt accordingly. lv = v.compiler.builder.CreateZExt(lv, llvm_type, "") case delta > 0: lv = v.compiler.builder.CreateTrunc(lv, llvm_type, "") } return v.compiler.NewLLVMValue(lv, dst_typ) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = v.compiler.builder.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, dst_typ) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = v.compiler.builder.CreateFPExt(lv, llvm_type, "") return v.compiler.NewLLVMValue(lv, dst_typ) } } //bitcast_value := v.compiler.builder.CreateBitCast(lv, llvm_type, "") /* value_type := value.Type() switch value_type.TypeKind() { case llvm.IntegerTypeKind: switch totype.TypeKind() { case llvm.IntegerTypeKind: //delta := value_type.IntTypeWidth() - totype.IntTypeWidth() //var switch { case delta == 0: return value // TODO handle signed/unsigned (SExt/ZExt) case delta < 0: return c.compiler.builder.CreateZExt(value, totype, "") case delta > 0: return c.compiler.builder.CreateTrunc(value, totype, "") } return LLVMValue{lhs.compiler.builder, value} } } */ panic(fmt.Sprint("unimplemented conversion: ", v.typ, " -> ", orig_dst_typ)) }
func (c *compiler) VisitCallExpr(expr *ast.CallExpr) Value { switch x := (expr.Fun).(type) { case *ast.Ident: switch x.String() { case "copy": // TODO zero := llvm.ConstInt(llvm.Int32Type(), 0, false) return c.NewLLVMValue(zero, types.Int) case "print": return c.VisitPrint(expr, false) case "println": return c.VisitPrint(expr, true) case "cap": return c.VisitCap(expr) case "len": return c.VisitLen(expr) case "new": return c.VisitNew(expr) case "make": return c.VisitMake(expr) case "append": return c.VisitAppend(expr) case "delete": m := c.VisitExpr(expr.Args[0]).(*LLVMValue) key := c.VisitExpr(expr.Args[1]) c.mapDelete(m, key) return nil case "panic": var arg Value if len(expr.Args) > 0 { arg = c.VisitExpr(expr.Args[0]) } c.visitPanic(arg) return nil case "recover": return c.visitRecover() case "real": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(0) case "imag": cmplx := c.VisitExpr(expr.Args[0]).(*LLVMValue) return cmplx.extractComplexComponent(1) case "complex": r := c.VisitExpr(expr.Args[0]).LLVMValue() i := c.VisitExpr(expr.Args[1]).LLVMValue() typ := c.types.expr[expr] cmplx := llvm.Undef(c.types.ToLLVM(typ)) cmplx = c.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = c.builder.CreateInsertValue(cmplx, i, 1, "") return c.NewLLVMValue(cmplx, typ) } case *ast.SelectorExpr: // Handle unsafe functions specially. if pkgobj, ok := x.X.(*ast.Ident); ok && pkgobj.Obj.Data == types.Unsafe.Data { var value int switch x.Sel.Name { case "Alignof": argtype := c.types.expr[expr.Args[0]] value = c.alignofType(argtype) value := c.NewConstValue(token.INT, strconv.Itoa(value)) value.typ = types.Uintptr return value case "Sizeof": argtype := c.types.expr[expr.Args[0]] value = c.sizeofType(argtype) value := c.NewConstValue(token.INT, strconv.Itoa(value)) value.typ = types.Uintptr return value case "Offsetof": // FIXME this should be constant, but I'm lazy, and this ought // to be done when we're doing constant folding anyway. lhs := expr.Args[0].(*ast.SelectorExpr).X baseaddr := c.VisitExpr(lhs).(*LLVMValue).pointer.LLVMValue() addr := c.VisitExpr(expr.Args[0]).(*LLVMValue).pointer.LLVMValue() baseaddr = c.builder.CreatePtrToInt(baseaddr, c.target.IntPtrType(), "") addr = c.builder.CreatePtrToInt(addr, c.target.IntPtrType(), "") diff := c.builder.CreateSub(addr, baseaddr, "") return c.NewLLVMValue(diff, types.Uintptr) } } } // Is it a type conversion? if len(expr.Args) == 1 && isType(expr.Fun) { typ := c.types.expr[expr] value := c.VisitExpr(expr.Args[0]) return value.Convert(typ) } // Not a type conversion, so must be a function call. lhs := c.VisitExpr(expr.Fun) fn := lhs.(*LLVMValue) fn_type := types.Underlying(fn.Type()).(*types.Func) args := make([]llvm.Value, 0) if fn.receiver != nil { // Don't dereference the receiver here. It'll have been worked out in // the selector. receiver := fn.receiver args = append(args, receiver.LLVMValue()) } if nparams := len(fn_type.Params); nparams > 0 { if fn_type.IsVariadic { nparams-- } for i := 0; i < nparams; i++ { value := c.VisitExpr(expr.Args[i]) param_type := fn_type.Params[i].Type.(types.Type) args = append(args, value.Convert(param_type).LLVMValue()) } if fn_type.IsVariadic { param_type := fn_type.Params[nparams].Type.(*types.Slice).Elt varargs := make([]llvm.Value, 0) for i := nparams; i < len(expr.Args); i++ { value := c.VisitExpr(expr.Args[i]) value = value.Convert(param_type) varargs = append(varargs, value.LLVMValue()) } slice_value := c.makeLiteralSlice(varargs, param_type) args = append(args, slice_value) } } var result_type types.Type switch len(fn_type.Results) { case 0: // no-op case 1: result_type = fn_type.Results[0].Type.(types.Type) default: fields := make([]*ast.Object, len(fn_type.Results)) for i, result := range fn_type.Results { fields[i] = result } result_type = &types.Struct{Fields: fields} } // After calling the function, we must bitcast to the computed LLVM // type. This is a no-op, and exists just to satisfy LLVM's type // comparisons. result := c.builder.CreateCall(fn.LLVMValue(), args, "") if len(fn_type.Results) == 1 { result = c.builder.CreateBitCast(result, c.types.ToLLVM(result_type), "") } return c.NewLLVMValue(result, result_type) }
// Create a constructor function which initialises a global. // TODO collapse all global inits into one init function? func (c *compiler) createGlobals(idents []*ast.Ident, values []ast.Expr, pkg string) { globals := make([]*LLVMValue, len(idents)) for i, ident := range idents { if ident.Name != "_" { t := ident.Obj.Type.(types.Type) llvmtyp := c.types.ToLLVM(t) gv := llvm.AddGlobal(c.module.Module, llvmtyp, pkg+"."+ident.Name) g := c.NewLLVMValue(gv, &types.Pointer{Base: t}).makePointee() globals[i] = g ident.Obj.Data = g } } if len(values) == 0 { for _, g := range globals { if g != nil { initializer := llvm.ConstNull(g.pointer.value.Type().ElementType()) g.pointer.value.SetInitializer(initializer) } } return } // FIXME Once we have constant folding, we can check first if the value is // a constant. For now we'll create a function and then erase it if the // computed value is a constant. if block := c.builder.GetInsertBlock(); !block.IsNil() { defer c.builder.SetInsertPointAtEnd(block) } fntype := &types.Func{} llvmfntype := c.types.ToLLVM(fntype).ElementType() fn := llvm.AddFunction(c.module.Module, "", llvmfntype) entry := llvm.AddBasicBlock(fn, "entry") c.builder.SetInsertPointAtEnd(entry) if len(values) == 1 && len(idents) > 1 { // Compound values are always non-constant. values := c.destructureExpr(values[0]) for i, ident := range idents { if globals[i] != nil { v := values[i].Convert(ident.Obj.Type.(types.Type)) gv := globals[i].pointer.value gv.SetInitializer(llvm.Undef(gv.Type().ElementType())) c.builder.CreateStore(v.LLVMValue(), gv) } } } else { allconst := true for i, expr := range values { if globals[i] != nil { gv := globals[i].pointer.value ident := idents[i] value := c.VisitExpr(expr) value = value.Convert(ident.Obj.Type.(types.Type)) _, isconst := value.(ConstValue) if isconst { gv.SetInitializer(value.LLVMValue()) } else { allconst = false gv.SetInitializer(llvm.Undef(gv.Type().ElementType())) c.builder.CreateStore(value.LLVMValue(), gv) } } } if allconst { fn.EraseFromParentAsFunction() fn = llvm.Value{nil} } } // FIXME order global ctors if !fn.IsNil() { c.builder.CreateRetVoid() fnvalue := c.NewLLVMValue(fn, fntype) c.varinitfuncs = append(c.varinitfuncs, fnvalue) } }
func (c *compiler) NewConstValue(v exact.Value, typ types.Type) *LLVMValue { switch { case v.Kind() == exact.Unknown: // TODO nil literals should be represented more appropriately once the exact-package supports it. 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)", c.types.TypeString(typ), typ, v, v)) }
func (v *LLVMValue) Convert(dsttyp types.Type) Value { b := v.compiler.builder // If it's a stack allocated value, we'll want to compare the // value type, not the pointer type. srctyp := v.typ // Get the underlying type, if any. origdsttyp := dsttyp dsttyp = dsttyp.Underlying() srctyp = srctyp.Underlying() // Identical (underlying) types? Just swap in the destination type. if types.IsIdentical(srctyp, dsttyp) { // A method converted to a function type without the // receiver is where we convert a "method value" into a // function. if srctyp, ok := srctyp.(*types.Signature); ok && srctyp.Recv() != nil { if dsttyp, ok := dsttyp.(*types.Signature); ok && dsttyp.Recv() == nil { return v.convertMethodValue(origdsttyp) } } // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } // Both pointer types with identical underlying types? Same as above. if srctyp, ok := srctyp.(*types.Pointer); ok { if dsttyp, ok := dsttyp.(*types.Pointer); ok { srctyp := srctyp.Elem().Underlying() dsttyp := dsttyp.Elem().Underlying() if types.IsIdentical(srctyp, dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } } // Convert from an interface type. if _, isinterface := srctyp.(*types.Interface); isinterface { if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.mustConvertI2I(interface_) } else { return v.mustConvertI2V(origdsttyp) } } // Converting to an interface type. if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.convertV2I(interface_) } byteslice := types.NewSlice(types.Typ[types.Byte]) runeslice := types.NewSlice(types.Typ[types.Rune]) // string -> if isString(srctyp) { // (untyped) string -> string // XXX should untyped strings be able to escape go/types? if isString(dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } // string -> []byte if types.IsIdentical(dsttyp, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(strdata.Type().ElementType(), strlen, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(strdata, c.target.IntPtrType(), ""), strlen, }, "") strdata = newdata struct_ := llvm.Undef(c.types.ToLLVM(byteslice)) struct_ = c.builder.CreateInsertValue(struct_, strdata, 0, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 1, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 2, "") return c.NewValue(struct_, byteslice) } // string -> []rune if types.IsIdentical(dsttyp, runeslice) { return v.stringToRuneSlice() } } // []byte -> string if types.IsIdentical(srctyp, byteslice) && isString(dsttyp) { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(data.Type().ElementType(), len, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(data, c.target.IntPtrType(), ""), len, }, "") data = newdata struct_ := llvm.Undef(c.types.ToLLVM(types.Typ[types.String])) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewValue(struct_, types.Typ[types.String]) } // []rune -> string if types.IsIdentical(srctyp, runeslice) && isString(dsttyp) { return v.runeSliceToString() } // rune -> string if isString(dsttyp) && isInteger(srctyp) { return v.runeToString() } // TODO other special conversions? llvm_type := v.compiler.types.ToLLVM(dsttyp) // Unsafe pointer conversions. if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer if _, isptr := srctyp.(*types.Pointer); isptr { value := b.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if srctyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X if _, isptr := dsttyp.(*types.Pointer); isptr { value := b.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if dsttyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } lv := v.LLVMValue() srcType := lv.Type() switch srcType.TypeKind() { case llvm.IntegerTypeKind: switch llvm_type.TypeKind() { case llvm.IntegerTypeKind: srcBits := srcType.IntTypeWidth() dstBits := llvm_type.IntTypeWidth() delta := srcBits - dstBits switch { case delta < 0: // TODO check if (un)signed, use S/ZExt accordingly. lv = b.CreateZExt(lv, llvm_type, "") case delta > 0: lv = b.CreateTrunc(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) case llvm.FloatTypeKind, llvm.DoubleTypeKind: if !isUnsigned(v.Type()) { lv = b.CreateSIToFP(lv, llvm_type, "") } else { lv = b.CreateUIToFP(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = b.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = b.CreateFPExt(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } } // Complex -> complex. Complexes are only convertible to other // complexes, contant conversions aside. So we can just check the // source type here; given that the types are not identical // (checked above), we can assume the destination type is the alternate // complex type. if isComplex(srctyp) { var fpcast func(*Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if srctyp == types.Typ[types.Complex64] { fpcast = (*Builder).CreateFPExt fptype = llvm.DoubleType() } else { fpcast = (*Builder).CreateFPTrunc fptype = llvm.FloatType() } if fpcast != nil { realv := b.CreateExtractValue(lv, 0, "") imagv := b.CreateExtractValue(lv, 1, "") realv = fpcast(b, realv, fptype, "") imagv = fpcast(b, imagv, fptype, "") lv = llvm.Undef(v.compiler.types.ToLLVM(dsttyp)) lv = b.CreateInsertValue(lv, realv, 0, "") lv = b.CreateInsertValue(lv, imagv, 1, "") return v.compiler.NewValue(lv, origdsttyp) } } srcstr := v.compiler.types.TypeString(v.typ) dststr := v.compiler.types.TypeString(origdsttyp) panic(fmt.Sprintf("unimplemented conversion: %s -> %s", srcstr, dststr)) }
// prepareCall returns the evaluated function and arguments. // // For builtins that may not be used in go/defer, prepareCall // will emits inline code. In this case, prepareCall returns // nil for fn and args, and returns a non-nil value for result. func (fr *frame) prepareCall(instr ssa.CallInstruction) (fn *LLVMValue, args []*LLVMValue, result *LLVMValue) { call := instr.Common() args = make([]*LLVMValue, len(call.Args)) for i, arg := range call.Args { args[i] = fr.value(arg) } if call.IsInvoke() { fn := fr.interfaceMethod(fr.value(call.Value), call.Method) return fn, args, nil } switch v := call.Value.(type) { case *ssa.Builtin: // handled below case *ssa.Function: // Function handled specially; value() will convert // a function to one with a context argument. fn = fr.resolveFunction(v) pair := llvm.ConstNull(fr.llvmtypes.ToLLVM(fn.Type())) pair = llvm.ConstInsertValue(pair, fn.LLVMValue(), []uint32{0}) fn = fr.NewValue(pair, fn.Type()) return fn, args, nil default: fn = fr.value(call.Value) return fn, args, nil } // Builtins may only be used in calls (i.e. can't be assigned), // and only print[ln], panic and recover may be used in go/defer. builtin := call.Value.(*ssa.Builtin) switch builtin.Name() { case "print", "println": // print/println generates a call-site specific anonymous // function to print the values. It's not inline because // print/println may be deferred. params := make([]*types.Var, len(call.Args)) for i, arg := range call.Args { // make sure to use args[i].Type(), not call.Args[i].Type(), // as the evaluated expression converts untyped. params[i] = types.NewParam(arg.Pos(), nil, arg.Name(), args[i].Type()) } sig := types.NewSignature(nil, nil, types.NewTuple(params...), nil, false) llfntyp := fr.llvmtypes.ToLLVM(sig) llfnptr := llvm.AddFunction(fr.module.Module, "", llfntyp.StructElementTypes()[0].ElementType()) currBlock := fr.builder.GetInsertBlock() entry := llvm.AddBasicBlock(llfnptr, "entry") fr.builder.SetInsertPointAtEnd(entry) internalArgs := make([]Value, len(args)) for i, arg := range args { internalArgs[i] = fr.NewValue(llfnptr.Param(i), arg.Type()) } fr.printValues(builtin.Name() == "println", internalArgs...) fr.builder.CreateRetVoid() fr.builder.SetInsertPointAtEnd(currBlock) return fr.NewValue(llfnptr, sig), args, nil case "panic": panic("TODO: panic") case "recover": // TODO(axw) determine number of frames to skip in pc check indirect := fr.NewValue(llvm.ConstNull(llvm.Int32Type()), types.Typ[types.Int32]) return fr.runtime.recover_, []*LLVMValue{indirect}, nil case "append": return nil, nil, fr.callAppend(args[0], args[1]) case "close": return fr.runtime.chanclose, args, nil case "cap": return nil, nil, fr.callCap(args[0]) case "len": return nil, nil, fr.callLen(args[0]) case "copy": return nil, nil, fr.callCopy(args[0], args[1]) case "delete": fr.callDelete(args[0], args[1]) return nil, nil, nil case "real": return nil, nil, args[0].extractComplexComponent(0) case "imag": return nil, nil, args[0].extractComplexComponent(1) case "complex": r := args[0].LLVMValue() i := args[1].LLVMValue() typ := instr.Value().Type() cmplx := llvm.Undef(fr.llvmtypes.ToLLVM(typ)) cmplx = fr.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = fr.builder.CreateInsertValue(cmplx, i, 1, "") return nil, nil, fr.NewValue(cmplx, typ) default: panic("unimplemented: " + builtin.Name()) } }
func (fr *frame) instruction(instr ssa.Instruction) { fr.logf("[%T] %v @ %s\n", instr, instr, fr.pkg.Prog.Fset.Position(instr.Pos())) // Check if we'll need to backpatch; see comment // in fr.value(). if v, ok := instr.(ssa.Value); ok { if b := fr.backpatcher(v); b != nil { defer b() } } switch instr := instr.(type) { case *ssa.Alloc: typ := fr.llvmtypes.ToLLVM(deref(instr.Type())) var value llvm.Value if instr.Heap { value = fr.createTypeMalloc(typ) value.SetName(instr.Comment) fr.env[instr] = fr.NewValue(value, instr.Type()) } else { value = fr.env[instr].LLVMValue() } fr.memsetZero(value, llvm.SizeOf(typ)) case *ssa.BinOp: lhs, rhs := fr.value(instr.X), fr.value(instr.Y) fr.env[instr] = lhs.BinaryOp(instr.Op, rhs).(*LLVMValue) case *ssa.Call: fn, args, result := fr.prepareCall(instr) // Some builtins may only be used immediately, and not // deferred; in this case, "fn" will be nil, and result // may be non-nil (it will be nil for builtins without // results.) if fn == nil { if result != nil { fr.env[instr] = result } } else { result = fr.createCall(fn, args) fr.env[instr] = result } case *ssa.ChangeInterface: x := fr.value(instr.X) // The source type must be a non-empty interface, // as ChangeInterface cannot fail (E2I may fail). if instr.Type().Underlying().(*types.Interface).NumMethods() > 0 { // TODO(axw) optimisation for I2I case where we // know statically the methods to carry over. x = x.convertI2E() x, _ = x.convertE2I(instr.Type()) } else { x = x.convertI2E() x = fr.NewValue(x.LLVMValue(), instr.Type()) } fr.env[instr] = x case *ssa.ChangeType: value := fr.value(instr.X).LLVMValue() if _, ok := instr.Type().Underlying().(*types.Pointer); ok { value = fr.builder.CreateBitCast(value, fr.llvmtypes.ToLLVM(instr.Type()), "") } v := fr.NewValue(value, instr.Type()) if _, ok := instr.X.(*ssa.Phi); ok { v = phiValue(fr.compiler, v) } fr.env[instr] = v case *ssa.Convert: v := fr.value(instr.X) if _, ok := instr.X.(*ssa.Phi); ok { v = phiValue(fr.compiler, v) } fr.env[instr] = v.Convert(instr.Type()).(*LLVMValue) //case *ssa.DebugRef: case *ssa.Defer: fn, args, result := fr.prepareCall(instr) if result != nil { panic("illegal use of builtin in defer statement") } fn = fr.indirectFunction(fn, args) fr.createCall(fr.runtime.pushdefer, []*LLVMValue{fn}) case *ssa.Extract: tuple := fr.value(instr.Tuple).LLVMValue() elem := fr.builder.CreateExtractValue(tuple, instr.Index, instr.Name()) elemtyp := instr.Type() fr.env[instr] = fr.NewValue(elem, elemtyp) case *ssa.Field: value := fr.value(instr.X).LLVMValue() field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name()) fieldtyp := instr.Type() fr.env[instr] = fr.NewValue(field, fieldtyp) case *ssa.FieldAddr: // TODO: implement nil check and panic. // TODO: combine a chain of {Field,Index}Addrs into a single GEP. ptr := fr.value(instr.X).LLVMValue() fieldptr := fr.builder.CreateStructGEP(ptr, instr.Field, instr.Name()) fieldptrtyp := instr.Type() fr.env[instr] = fr.NewValue(fieldptr, fieldptrtyp) case *ssa.Go: fn, args, result := fr.prepareCall(instr) if result != nil { panic("illegal use of builtin in go statement") } fn = fr.indirectFunction(fn, args) fr.createCall(fr.runtime.Go, []*LLVMValue{fn}) case *ssa.If: cond := fr.value(instr.Cond).LLVMValue() block := instr.Block() trueBlock := fr.block(block.Succs[0]) falseBlock := fr.block(block.Succs[1]) fr.builder.CreateCondBr(cond, trueBlock, falseBlock) case *ssa.Index: // FIXME Surely we should be dealing with an // *array, so we can do a GEP? array := fr.value(instr.X).LLVMValue() arrayptr := fr.builder.CreateAlloca(array.Type(), "") fr.builder.CreateStore(array, arrayptr) index := fr.value(instr.Index).LLVMValue() zero := llvm.ConstNull(index.Type()) addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "") fr.env[instr] = fr.NewValue(fr.builder.CreateLoad(addr, ""), instr.Type()) case *ssa.IndexAddr: // TODO: implement nil-check and panic. // TODO: combine a chain of {Field,Index}Addrs into a single GEP. x := fr.value(instr.X).LLVMValue() index := fr.value(instr.Index).LLVMValue() var addr llvm.Value var elemtyp types.Type zero := llvm.ConstNull(index.Type()) switch typ := instr.X.Type().Underlying().(type) { case *types.Slice: elemtyp = typ.Elem() x = fr.builder.CreateExtractValue(x, 0, "") addr = fr.builder.CreateGEP(x, []llvm.Value{index}, "") case *types.Pointer: // *array elemtyp = typ.Elem().Underlying().(*types.Array).Elem() addr = fr.builder.CreateGEP(x, []llvm.Value{zero, index}, "") } fr.env[instr] = fr.NewValue(addr, types.NewPointer(elemtyp)) case *ssa.Jump: succ := instr.Block().Succs[0] fr.builder.CreateBr(fr.block(succ)) case *ssa.Lookup: x := fr.value(instr.X) index := fr.value(instr.Index) if isString(x.Type().Underlying()) { fr.env[instr] = fr.stringIndex(x, index) } else { fr.env[instr] = fr.mapLookup(x, index, instr.CommaOk) } case *ssa.MakeChan: fr.env[instr] = fr.makeChan(instr.Type(), fr.value(instr.Size)) case *ssa.MakeClosure: fn := fr.resolveFunction(instr.Fn.(*ssa.Function)) bindings := make([]*LLVMValue, len(instr.Bindings)) for i, binding := range instr.Bindings { bindings[i] = fr.value(binding) } fr.env[instr] = fr.makeClosure(fn, bindings) case *ssa.MakeInterface: receiver := fr.value(instr.X) fr.env[instr] = fr.makeInterface(receiver, instr.Type()) case *ssa.MakeMap: fr.env[instr] = fr.makeMap(instr.Type(), fr.value(instr.Reserve)) case *ssa.MakeSlice: length := fr.value(instr.Len) capacity := fr.value(instr.Cap) fr.env[instr] = fr.makeSlice(instr.Type(), length, capacity) case *ssa.MapUpdate: m := fr.value(instr.Map) k := fr.value(instr.Key) v := fr.value(instr.Value) fr.mapUpdate(m, k, v) case *ssa.Next: iter := fr.value(instr.Iter) if !instr.IsString { fr.env[instr] = fr.mapIterNext(iter) return } // String range // // We make some assumptions for now around the // current state of affairs in go.tools/ssa. // // - Range's block is a predecessor of Next's. // (this is currently true, but may change in the future; // adonovan says he will expose the dominator tree // computation in the future, which we can use here). // - Next is the first non-Phi instruction in its block. // (this is not strictly necessary; we can move the Phi // to the top of the block, and defer the tuple creation // to Extract). assert(instr.Iter.(*ssa.Range).Block() == instr.Block().Preds[0]) for _, blockInstr := range instr.Block().Instrs { if instr == blockInstr { break } _, isphi := blockInstr.(*ssa.Phi) assert(isphi) } preds := instr.Block().Preds llpreds := make([]llvm.BasicBlock, len(preds)) for i, b := range preds { llpreds[i] = fr.block(b) } fr.env[instr] = fr.stringIterNext(iter, llpreds) case *ssa.Panic: arg := fr.value(instr.X).LLVMValue() fr.builder.CreateCall(fr.runtime.panic_.LLVMValue(), []llvm.Value{arg}, "") fr.builder.CreateUnreachable() case *ssa.Phi: typ := instr.Type() phi := fr.builder.CreatePHI(fr.llvmtypes.ToLLVM(typ), instr.Comment) fr.env[instr] = fr.NewValue(phi, typ) values := make([]llvm.Value, len(instr.Edges)) blocks := make([]llvm.BasicBlock, len(instr.Edges)) block := instr.Block() for i, edge := range instr.Edges { values[i] = fr.value(edge).LLVMValue() blocks[i] = fr.block(block.Preds[i]) } phi.AddIncoming(values, blocks) case *ssa.Range: x := fr.value(instr.X) switch x.Type().Underlying().(type) { case *types.Map: fr.env[instr] = fr.mapIterInit(x) case *types.Basic: // string fr.env[instr] = x default: panic(fmt.Sprintf("unhandled range for type %T", x.Type())) } case *ssa.Return: switch n := len(instr.Results); n { case 0: // https://code.google.com/p/go/issues/detail?id=7022 if r := instr.Parent().Signature.Results(); r != nil && r.Len() > 0 { fr.builder.CreateUnreachable() } else { fr.builder.CreateRetVoid() } case 1: fr.builder.CreateRet(fr.value(instr.Results[0]).LLVMValue()) default: values := make([]llvm.Value, n) for i, result := range instr.Results { values[i] = fr.value(result).LLVMValue() } fr.builder.CreateAggregateRet(values) } case *ssa.RunDefers: fr.builder.CreateCall(fr.runtime.rundefers.LLVMValue(), nil, "") case *ssa.Select: states := make([]selectState, len(instr.States)) for i, state := range instr.States { states[i] = selectState{ Dir: state.Dir, Chan: fr.value(state.Chan), Send: fr.value(state.Send), } } fr.env[instr] = fr.chanSelect(states, instr.Blocking) case *ssa.Send: fr.chanSend(fr.value(instr.Chan), fr.value(instr.X)) case *ssa.Slice: x := fr.value(instr.X) low := fr.value(instr.Low) high := fr.value(instr.High) fr.env[instr] = fr.slice(x, low, high) case *ssa.Store: addr := fr.value(instr.Addr).LLVMValue() value := fr.value(instr.Val).LLVMValue() // The bitcast is necessary to handle recursive pointer stores. addr = fr.builder.CreateBitCast(addr, llvm.PointerType(value.Type(), 0), "") fr.builder.CreateStore(value, addr) case *ssa.TypeAssert: x := fr.value(instr.X) if iface, ok := x.Type().Underlying().(*types.Interface); ok && iface.NumMethods() > 0 { x = x.convertI2E() } if !instr.CommaOk { if _, ok := instr.AssertedType.Underlying().(*types.Interface); ok { fr.env[instr] = x.mustConvertE2I(instr.AssertedType) } else { fr.env[instr] = x.mustConvertE2V(instr.AssertedType) } } else { var result, success *LLVMValue if _, ok := instr.AssertedType.Underlying().(*types.Interface); ok { result, success = x.convertE2I(instr.AssertedType) } else { result, success = x.convertE2V(instr.AssertedType) } resultval := result.LLVMValue() okval := success.LLVMValue() pairtyp := llvm.StructType([]llvm.Type{resultval.Type(), okval.Type()}, false) pair := llvm.Undef(pairtyp) pair = fr.builder.CreateInsertValue(pair, resultval, 0, "") pair = fr.builder.CreateInsertValue(pair, okval, 1, "") fr.env[instr] = fr.NewValue(pair, instr.Type()) } case *ssa.UnOp: operand := fr.value(instr.X) switch instr.Op { case token.ARROW: fr.env[instr] = fr.chanRecv(operand, instr.CommaOk) case token.MUL: // The bitcast is necessary to handle recursive pointer loads. llptr := fr.builder.CreateBitCast(operand.LLVMValue(), llvm.PointerType(fr.llvmtypes.ToLLVM(instr.Type()), 0), "") fr.env[instr] = fr.NewValue(fr.builder.CreateLoad(llptr, ""), instr.Type()) default: fr.env[instr] = operand.UnaryOp(instr.Op).(*LLVMValue) } default: panic(fmt.Sprintf("unhandled: %v", instr)) } }
func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value { selection := c.typeinfo.Selections[expr] // Imported package funcs/vars. if selection.Kind() == types.PackageObj { return c.Resolve(expr.Sel) } // Method expression. Returns an unbound function pointer. if selection.Kind() == types.MethodExpr { ftyp := c.typeinfo.Types[expr].(*types.Signature) recvtyp := ftyp.Params().At(0).Type() var name *types.Named var isptr bool if ptrtyp, ok := recvtyp.(*types.Pointer); ok { isptr = true name = ptrtyp.Elem().(*types.Named) } else { name = recvtyp.(*types.Named) } obj := c.methods(name).lookup(expr.Sel.Name, isptr) method := c.Resolve(c.objectdata[obj].Ident).(*LLVMValue) return c.NewValue(method.value, ftyp) } // Interface: search for method by name. lhs := c.VisitExpr(expr.X) name := expr.Sel.Name if iface, ok := lhs.Type().Underlying().(*types.Interface); ok { i := selection.Index()[0] ftype := selection.Type() methodset := iface.MethodSet() if methodset.At(i).Obj() != selection.Obj() { // TODO cache mapping from unsorted to sorted index. for j := 0; j < methodset.Len(); j++ { if methodset.At(j).Obj() == selection.Obj() { i = j break } } } structValue := lhs.LLVMValue() receiver := c.builder.CreateExtractValue(structValue, 1, "") f := c.builder.CreateExtractValue(structValue, i+2, "") types := []llvm.Type{f.Type(), receiver.Type()} llvmStructType := llvm.StructType(types, false) structValue = llvm.Undef(llvmStructType) structValue = c.builder.CreateInsertValue(structValue, f, 0, "") structValue = c.builder.CreateInsertValue(structValue, receiver, 1, "") return c.NewValue(structValue, ftype) } // Method. if selection.Kind() == types.MethodVal { var isptr bool typ := lhs.Type() if ptr, ok := typ.(*types.Pointer); ok { typ = ptr.Elem() isptr = true } else { isptr = lhs.(*LLVMValue).pointer != nil } recv := lhs.(*LLVMValue) if isptr && typ == lhs.Type() { recv = recv.pointer } method := c.methods(typ).lookup(name, isptr) if f, ok := method.(*types.Func); ok { method = c.methodfunc(f) } methodValue := c.Resolve(c.objectdata[method].Ident).LLVMValue() methodValue = c.builder.CreateExtractValue(methodValue, 0, "") recvValue := recv.LLVMValue() types := []llvm.Type{methodValue.Type(), recvValue.Type()} structType := llvm.StructType(types, false) value := llvm.Undef(structType) value = c.builder.CreateInsertValue(value, methodValue, 0, "") value = c.builder.CreateInsertValue(value, recvValue, 1, "") v := c.NewValue(value, method.Type()) v.method = method return v } // Get a pointer to the field. fieldValue := lhs.(*LLVMValue) if fieldValue.pointer == nil { // If we've got a temporary (i.e. no pointer), // then load the value onto the stack. v := fieldValue.value stackptr := c.builder.CreateAlloca(v.Type(), "") c.builder.CreateStore(v, stackptr) ptrtyp := types.NewPointer(fieldValue.Type()) fieldValue = c.NewValue(stackptr, ptrtyp).makePointee() } for _, i := range selection.Index() { if _, ok := fieldValue.Type().(*types.Pointer); ok { fieldValue = fieldValue.makePointee() } ptr := fieldValue.pointer.LLVMValue() structTyp := deref(fieldValue.typ).Underlying().(*types.Struct) field := structTyp.Field(i) fieldPtr := c.builder.CreateStructGEP(ptr, i, "") fieldPtrTyp := types.NewPointer(field.Type()) fieldValue = c.NewValue(fieldPtr, fieldPtrTyp).makePointee() } return fieldValue }
// indirectFunction creates an indirect function from a // given function, suitable for use with "defer" and "go". func (c *compiler) indirectFunction(fn *LLVMValue, args []Value, dotdotdot bool) *LLVMValue { nilarytyp := &types.Signature{} if len(args) == 0 { val := fn.LLVMValue() ptr := c.builder.CreateExtractValue(val, 0, "") ctx := c.builder.CreateExtractValue(val, 1, "") fnval := llvm.Undef(c.types.ToLLVM(nilarytyp)) ptr = c.builder.CreateBitCast(ptr, fnval.Type().StructElementTypes()[0], "") ctx = c.builder.CreateBitCast(ctx, fnval.Type().StructElementTypes()[1], "") fnval = c.builder.CreateInsertValue(fnval, ptr, 0, "") fnval = c.builder.CreateInsertValue(fnval, ctx, 1, "") fn = c.NewValue(fnval, nilarytyp) return fn } // TODO check if function pointer is global. I suppose // the same can be done with the context ptr... fnval := fn.LLVMValue() fnptr := c.builder.CreateExtractValue(fnval, 0, "") ctx := c.builder.CreateExtractValue(fnval, 1, "") nctx := 1 // fnptr if !ctx.IsNull() { nctx++ // fnctx } i8ptr := llvm.PointerType(llvm.Int8Type(), 0) llvmargs := make([]llvm.Value, len(args)+nctx) llvmargtypes := make([]llvm.Type, len(args)+nctx) for i, arg := range args { llvmargs[i+nctx] = arg.LLVMValue() llvmargtypes[i+nctx] = llvmargs[i+nctx].Type() } llvmargtypes[0] = fnptr.Type() llvmargs[0] = fnptr if nctx > 1 { llvmargtypes[1] = ctx.Type() llvmargs[1] = ctx } structtyp := llvm.StructType(llvmargtypes, false) argstruct := c.createTypeMalloc(structtyp) for i, llvmarg := range llvmargs { argptr := c.builder.CreateGEP(argstruct, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "") c.builder.CreateStore(llvmarg, argptr) } // Create a function that will take a pointer to a structure of the type // defined above, or no parameters if there are none to pass. fntype := llvm.FunctionType(llvm.VoidType(), []llvm.Type{argstruct.Type()}, false) indirectfn := llvm.AddFunction(c.module.Module, "", fntype) i8argstruct := c.builder.CreateBitCast(argstruct, i8ptr, "") currblock := c.builder.GetInsertBlock() c.builder.SetInsertPointAtEnd(llvm.AddBasicBlock(indirectfn, "entry")) argstruct = indirectfn.Param(0) for i := range llvmargs[nctx:] { argptr := c.builder.CreateGEP(argstruct, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i+nctx), false)}, "") ptrtyp := types.NewPointer(args[i].Type()) args[i] = c.NewValue(argptr, ptrtyp).makePointee() } // Extract the function pointer. // TODO if function is a global, elide. fnval = llvm.Undef(fnval.Type()) fnptrptr := c.builder.CreateGEP(argstruct, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), 0, false)}, "") fnptr = c.builder.CreateLoad(fnptrptr, "") fnval = c.builder.CreateInsertValue(fnval, fnptr, 0, "") if nctx > 1 { ctxptr := c.builder.CreateGEP(argstruct, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), 1, false)}, "") ctx = c.builder.CreateLoad(ctxptr, "") fnval = c.builder.CreateInsertValue(fnval, ctx, 1, "") fn = c.NewValue(fnval, fn.Type()) } c.createCall(fn, args, dotdotdot, false) // Indirect function calls' return values are always ignored. c.builder.CreateRetVoid() c.builder.SetInsertPointAtEnd(currblock) fnval = llvm.Undef(c.types.ToLLVM(nilarytyp)) indirectfn = c.builder.CreateBitCast(indirectfn, fnval.Type().StructElementTypes()[0], "") fnval = c.builder.CreateInsertValue(fnval, indirectfn, 0, "") fnval = c.builder.CreateInsertValue(fnval, i8argstruct, 1, "") fn = c.NewValue(fnval, nilarytyp) return fn }