// 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) }
// 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) }
// 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) }
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 }
// 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 }
// 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_ }
// 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) }
// indirectFunction creates an indirect function from a // given function and arguments, suitable for use with // "defer" and "go". func (c *compiler) indirectFunction(fn *LLVMValue, args []*LLVMValue) *LLVMValue { nilarytyp := types.NewSignature(nil, nil, nil, nil, false) 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, "") return c.NewValue(fnval, nilarytyp) } // Check if function pointer or context pointer is global/null. fnval := fn.LLVMValue() fnptr := fnval var nctx int var fnctx llvm.Value var fnctxindex uint64 var globalfn bool if fnptr.Type().TypeKind() == llvm.StructTypeKind { fnptr = c.builder.CreateExtractValue(fnval, 0, "") fnctx = c.builder.CreateExtractValue(fnval, 1, "") globalfn = !fnptr.IsAFunction().IsNil() if !globalfn { nctx++ } if !fnctx.IsNull() { fnctxindex = uint64(nctx) nctx++ } } else { // We've got a raw global function pointer. Convert to <ptr,ctx>. fnval = llvm.ConstNull(c.types.ToLLVM(fn.Type())) fnval = llvm.ConstInsertValue(fnval, fnptr, []uint32{0}) fn = c.NewValue(fnval, fn.Type()) fnctx = llvm.ConstExtractValue(fnval, []uint32{1}) globalfn = true } 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() } if !globalfn { llvmargtypes[0] = fnptr.Type() llvmargs[0] = fnptr } if !fnctx.IsNull() { llvmargtypes[fnctxindex] = fnctx.Type() llvmargs[fnctxindex] = fnctx } // TODO(axw) investigate an option for go statements // to allocate argument structure on the stack in the // initiator, and block until the spawned goroutine // has loaded the arguments from it. 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) newargs := make([]*LLVMValue, len(args)) 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)}, "") newargs[i] = c.NewValue(c.builder.CreateLoad(argptr, ""), args[i].Type()) } // Unless we've got a global function, extract the // function pointer from the context. if !globalfn { 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 !fnctx.IsNull() { fnctxptr := c.builder.CreateGEP(argstruct, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), fnctxindex, false)}, "") fnctx = c.builder.CreateLoad(fnctxptr, "") fnval = c.builder.CreateInsertValue(fnval, fnctx, 1, "") fn = c.NewValue(fnval, fn.Type()) } c.createCall(fn, newargs) // 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 }
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 (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.Identical(srctyp, dsttyp) { 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.Identical(srctyp, dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } } 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.Identical(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.createMalloc(strlen) memcpy := c.runtime.memcpy.LLVMValue() c.builder.CreateCall(memcpy, []llvm.Value{ newdata, c.builder.CreatePtrToInt(strdata, c.target.IntPtrType(), ""), strlen, }, "") strdata = c.builder.CreateIntToPtr(newdata, strdata.Type(), "") 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.Identical(dsttyp, runeslice) { return v.stringToRuneSlice() } } // []byte -> string if types.Identical(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.createMalloc(len) memcpy := c.runtime.memcpy.LLVMValue() c.builder.CreateCall(memcpy, []llvm.Value{ newdata, c.builder.CreatePtrToInt(data, c.target.IntPtrType(), ""), len, }, "") data = c.builder.CreateIntToPtr(newdata, data.Type(), "") 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.Identical(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: if !isUnsigned(srctyp) { lv = b.CreateSExt(lv, llvm_type, "") } else { 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(llvm.Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if srctyp == types.Typ[types.Complex64] { fpcast = (llvm.Builder).CreateFPExt fptype = llvm.DoubleType() } else { fpcast = (llvm.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) } } panic(fmt.Sprintf("unimplemented conversion: %s (%s) -> %s", v.typ, lv.Type(), origdsttyp)) }
// 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())) if fr.GenerateDebug { fr.debug.setLocation(fr.builder, 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)) } }