func (tm *TypeMap) makeRuntimeTypeGlobal(v llvm.Value, name string) (global, ptr llvm.Value) { global = llvm.AddGlobal(tm.module, v.Type(), typeSymbol(name)) global.SetInitializer(v) global.SetLinkage(llvm.LinkOnceAnyLinkage) ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtime.rtype.llvm, 0)) return global, ptr }
func (c *compiler) createMalloc(size llvm.Value) llvm.Value { malloc := c.NamedFunction("runtime.malloc", "func f(uintptr) unsafe.Pointer") if size.Type().IntTypeWidth() > c.target.IntPtrType().IntTypeWidth() { size = c.builder.CreateTrunc(size, c.target.IntPtrType(), "") } return c.builder.CreateCall(malloc, []llvm.Value{size}, "") }
func (c *compiler) defineMallocFunction(fn llvm.Value) { entry := llvm.AddBasicBlock(fn, "entry") c.builder.SetInsertPointAtEnd(entry) size := fn.FirstParam() ptr := c.builder.CreateArrayMalloc(llvm.Int8Type(), size, "") // XXX memset to zero, or leave that to Go runtime code? fn_type := fn.Type().ElementType() result := c.builder.CreatePtrToInt(ptr, fn_type.ReturnType(), "") c.builder.CreateRet(result) }
func (c *compiler) createMalloc(size llvm.Value) llvm.Value { malloc := c.runtime.malloc.LLVMValue() switch n := size.Type().IntTypeWidth() - c.target.IntPtrType().IntTypeWidth(); { case n < 0: size = c.builder.CreateZExt(size, c.target.IntPtrType(), "") case n > 0: size = c.builder.CreateTrunc(size, c.target.IntPtrType(), "") } return c.builder.CreateCall(malloc, []llvm.Value{size}, "") }
func (c *compiler) createMalloc(size llvm.Value) llvm.Value { malloc := c.NamedFunction("runtime.malloc", "func(uintptr) unsafe.Pointer") switch n := size.Type().IntTypeWidth() - c.target.IntPtrType().IntTypeWidth(); { case n < 0: size = c.builder.CreateZExt(size, c.target.IntPtrType(), "") case n > 0: size = c.builder.CreateTrunc(size, c.target.IntPtrType(), "") } return c.builder.CreateCall(malloc, []llvm.Value{size}, "") }
func (b *Builder) CreateLoad(v llvm.Value, name string) llvm.Value { if v.Type().ElementType() == b.types.ptrstandin { // We represent recursive pointer types (T = *T) // in LLVM as a pointer to "ptrstdin", where // ptrstandin is a unique named struct. // // Cast the the pointer to a pointer to its own type first. v = b.CreateBitCast(v, llvm.PointerType(v.Type(), 0), "") } return b.Builder.CreateLoad(v, name) }
func (c *compiler) memsetZero(ptr llvm.Value, size llvm.Value) { memset := c.runtime.memset.LLVMValue() switch n := size.Type().IntTypeWidth() - c.target.IntPtrType().IntTypeWidth(); { case n < 0: size = c.builder.CreateZExt(size, c.target.IntPtrType(), "") case n > 0: size = c.builder.CreateTrunc(size, c.target.IntPtrType(), "") } ptr = c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") fill := llvm.ConstNull(llvm.Int8Type()) c.builder.CreateCall(memset, []llvm.Value{ptr, fill, size}, "") }
func (b *Builder) CreateStore(v, ptr llvm.Value) { if !b.types.ptrstandin.IsNil() { vtyp, ptrtyp := v.Type(), ptr.Type() if vtyp == ptrtyp { // We must be dealing with a pointer to a recursive pointer // type, so bitcast the pointer to a pointer to its own // type first. ptr = b.CreateBitCast(ptr, llvm.PointerType(ptrtyp, 0), "") } } b.Builder.CreateStore(v, ptr) }
func (c *compiler) memsetZero(ptr llvm.Value, size llvm.Value) { memset := c.NamedFunction("runtime.memset", "func f(dst unsafe.Pointer, fill byte, size uintptr)") switch n := size.Type().IntTypeWidth() - c.target.IntPtrType().IntTypeWidth(); { case n < 0: size = c.builder.CreateZExt(size, c.target.IntPtrType(), "") case n > 0: size = c.builder.CreateTrunc(size, c.target.IntPtrType(), "") } ptr = c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") fill := llvm.ConstNull(llvm.Int8Type()) c.builder.CreateCall(memset, []llvm.Value{ptr, fill, size}, "") }
func (b *Builder) CreateStore(v, ptr llvm.Value) { if !b.types.ptrstandin.IsNil() { vtyp, ptrtyp := v.Type(), ptr.Type() if vtyp == ptrtyp || ptrtyp.ElementType() == b.types.ptrstandin { // We must be dealing with a pointer to a recursive pointer // type, so bitcast the value to the pointer's base, opaque // pointer type. v = b.CreateBitCast(v, ptrtyp.ElementType(), "") } } b.Builder.CreateStore(v, ptr) }
// coerce yields a value of the the type specified, initialised // to the exact bit pattern as in the specified value. // // Note: the specified value must be a non-aggregate, and its type // and the specified type must have the same size. func (c *compiler) coerce(v llvm.Value, t llvm.Type) llvm.Value { switch t.TypeKind() { case llvm.ArrayTypeKind, llvm.StructTypeKind: ptr := c.builder.CreateAlloca(t, "") ptrv := c.builder.CreateBitCast(ptr, llvm.PointerType(v.Type(), 0), "") c.builder.CreateStore(v, ptrv) return c.builder.CreateLoad(ptr, "") default: return c.builder.CreateBitCast(v, t, "") } panic("unreachable") }
func (b *Builder) CreateLoad(v llvm.Value, name string) llvm.Value { result := b.Builder.CreateLoad(v, name) if !b.types.ptrstandin.IsNil() && result.Type() == b.types.ptrstandin { // We represent recursive pointer types (T = *T) // in LLVM as a pointer to "ptrstdin", where // ptrstandin is a pointer to a unique named struct. // // Cast the result of loading the pointer back to // the same type as the pointer loaded from. result = b.CreateBitCast(result, v.Type(), "") } return result }
func (c *compiler) buildPtrRecvFunction(fn llvm.Value) llvm.Value { defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock()) ifname := "*" + fn.Name() ifn := c.module.Module.NamedFunction(ifname) fntyp := fn.Type().ElementType() entry := llvm.AddBasicBlock(ifn, "entry") c.builder.SetInsertPointAtEnd(entry) args := ifn.Params() args[0] = c.builder.CreateLoad(args[0], "recv") result := c.builder.CreateCall(fn, args, "") if fntyp.ReturnType().TypeKind() == llvm.VoidTypeKind { c.builder.CreateRetVoid() } else { c.builder.CreateRet(result) } return ifn }
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 }
// coerce yields a value of the the type specified, initialised // to the exact bit pattern as in the specified value. // // Note: the value's type and the specified target type must have // the same size. If the source is an aggregate, then the target // must also be an aggregate with the same number of fields, each // of which must have the same size. func coerce(b llvm.Builder, v llvm.Value, t llvm.Type) llvm.Value { // FIXME don't do this with alloca switch t.TypeKind() { case llvm.ArrayTypeKind, llvm.StructTypeKind: ptr := b.CreateAlloca(t, "") ptrv := b.CreateBitCast(ptr, llvm.PointerType(v.Type(), 0), "") b.CreateStore(v, ptrv) return b.CreateLoad(ptr, "") } vt := v.Type() switch vt.TypeKind() { case llvm.ArrayTypeKind, llvm.StructTypeKind: ptr := b.CreateAlloca(vt, "") b.CreateStore(v, ptr) ptrt := b.CreateBitCast(ptr, llvm.PointerType(t, 0), "") return b.CreateLoad(ptrt, "") } return b.CreateBitCast(v, t, "") }
func (v *LLVMValue) UnaryOp(op token.Token) Value { b := v.compiler.builder switch op { case token.SUB: var value llvm.Value if isFloat(v.typ) { zero := llvm.ConstNull(v.compiler.types.ToLLVM(v.Type())) value = b.CreateFSub(zero, v.LLVMValue(), "") } else { value = b.CreateNeg(v.LLVMValue(), "") } return v.compiler.NewValue(value, v.typ) case token.ADD: return v // No-op case token.AND: if typ, ok := v.typ.Underlying().(*types.Pointer); ok { if typ.Elem().Underlying() == typ { // Taking the address of a recursive pointer // yields a value with the same type. value := v.pointer.value basetyp := value.Type().ElementType() value = b.CreateBitCast(value, basetyp, "") return v.compiler.NewValue(value, v.typ) } } return v.pointer case token.NOT: value := b.CreateNot(v.LLVMValue(), "") return v.compiler.NewValue(value, v.typ) case token.XOR: lhs := v.LLVMValue() rhs := llvm.ConstAllOnes(lhs.Type()) value := b.CreateXor(lhs, rhs, "") return v.compiler.NewValue(value, v.typ) case token.ARROW: value, _ := v.chanRecv(false) return value default: panic(fmt.Sprintf("Unhandled operator: %s", op)) } panic("unreachable") }
// coerce yields a value of the the type specified, initialised // to the exact bit pattern as in the specified value. // // Note: the specified value must be a non-aggregate, and its type // and the specified type must have the same size. func (c *compiler) coerce(v llvm.Value, t llvm.Type) llvm.Value { switch t.TypeKind() { case llvm.ArrayTypeKind, llvm.StructTypeKind: ptr := c.builder.CreateAlloca(t, "") ptrv := c.builder.CreateBitCast(ptr, llvm.PointerType(v.Type(), 0), "") c.builder.CreateStore(v, ptrv) return c.builder.CreateLoad(ptr, "") } vt := v.Type() switch vt.TypeKind() { case llvm.ArrayTypeKind, llvm.StructTypeKind: ptr := c.builder.CreateAlloca(vt, "") c.builder.CreateStore(v, ptr) ptrt := c.builder.CreateBitCast(ptr, llvm.PointerType(t, 0), "") return c.builder.CreateLoad(ptrt, "") } return c.builder.CreateBitCast(v, t, "") }
func (tm *TypeMap) interfaceFuncWrapper(f llvm.Value) llvm.Value { ftyp := f.Type().ElementType() paramTypes := ftyp.ParamTypes() recvType := paramTypes[0] paramTypes[0] = llvm.PointerType(llvm.Int8Type(), 0) newf := llvm.AddFunction(f.GlobalParent(), f.Name()+".ifn", llvm.FunctionType( ftyp.ReturnType(), paramTypes, ftyp.IsFunctionVarArg(), )) b := llvm.GlobalContext().NewBuilder() defer b.Dispose() entry := llvm.AddBasicBlock(newf, "entry") b.SetInsertPointAtEnd(entry) args := make([]llvm.Value, len(paramTypes)) for i := range paramTypes { args[i] = newf.Param(i) } recvBits := int(tm.target.TypeSizeInBits(recvType)) if recvBits > 0 { args[0] = b.CreatePtrToInt(args[0], tm.target.IntPtrType(), "") if args[0].Type().IntTypeWidth() > recvBits { args[0] = b.CreateTrunc(args[0], llvm.IntType(recvBits), "") } args[0] = coerce(b, args[0], recvType) } else { args[0] = llvm.ConstNull(recvType) } result := b.CreateCall(f, args, "") if result.Type().TypeKind() == llvm.VoidTypeKind { b.CreateRetVoid() } else { b.CreateRet(result) } return newf }
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 }
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)) } }
// createCall emits the code for a function call, taking into account // variadic functions, receivers, and panic/defer. // // dotdotdot is true if the last argument is followed with "...". func (c *compiler) createCall(fn *LLVMValue, argValues []Value, dotdotdot, invoke bool) *LLVMValue { fn_type := fn.Type().Underlying().(*types.Signature) var args []llvm.Value // TODO Move all of this to evalCallArgs? params := fn_type.Params() if nparams := int(params.Len()); nparams > 0 { if fn_type.IsVariadic() { nparams-- } for i := 0; i < nparams; i++ { value := argValues[i] args = append(args, value.LLVMValue()) } if fn_type.IsVariadic() { if dotdotdot { // Calling f(x...). Just pass the slice directly. slice_value := argValues[nparams].LLVMValue() args = append(args, slice_value) } else { varargs := make([]llvm.Value, len(argValues)-nparams) for i, value := range argValues[nparams:] { varargs[i] = value.LLVMValue() } param_type := params.At(nparams).Type().(*types.Slice).Elem() slice_value := c.makeLiteralSlice(varargs, param_type) args = append(args, slice_value) } } } var result_type types.Type switch results := fn_type.Results(); results.Len() { case 0: // no-op case 1: result_type = results.At(0).Type() default: result_type = results } // Depending on whether the function contains defer statements or not, // we'll generate either a "call" or an "invoke" instruction. var createCall = c.builder.CreateCall if invoke { f := c.functions.top() // TODO Create a method on compiler (avoid creating closures). createCall = func(fn llvm.Value, args []llvm.Value, name string) llvm.Value { currblock := c.builder.GetInsertBlock() returnblock := llvm.AddBasicBlock(currblock.Parent(), "") returnblock.MoveAfter(currblock) value := c.builder.CreateInvoke(fn, args, returnblock, f.unwindblock, "") c.builder.SetInsertPointAtEnd(returnblock) return value } } var fnptr llvm.Value fnval := fn.LLVMValue() if fnval.Type().TypeKind() == llvm.PointerTypeKind { fnptr = fnval } else { fnptr = c.builder.CreateExtractValue(fnval, 0, "") context := c.builder.CreateExtractValue(fnval, 1, "") fntyp := fnptr.Type().ElementType() paramTypes := fntyp.ParamTypes() // If the context is not a constant null, and we're not // dealing with a method (where we don't care about the value // of the receiver), then we must conditionally call the // function with the additional receiver/closure. if !context.IsNull() || fn_type.Recv() != nil { // Store the blocks for referencing in the Phi below; // note that we update the block after each createCall, // since createCall may create new blocks and we want // the predecessors to the Phi. var nullctxblock llvm.BasicBlock var nonnullctxblock llvm.BasicBlock var endblock llvm.BasicBlock var nullctxresult llvm.Value // len(paramTypes) == len(args) iff function is not a method. if !context.IsConstant() && len(paramTypes) == len(args) { currblock := c.builder.GetInsertBlock() endblock = llvm.AddBasicBlock(currblock.Parent(), "") endblock.MoveAfter(currblock) nonnullctxblock = llvm.InsertBasicBlock(endblock, "") nullctxblock = llvm.InsertBasicBlock(nonnullctxblock, "") nullctx := c.builder.CreateIsNull(context, "") c.builder.CreateCondBr(nullctx, nullctxblock, nonnullctxblock) // null context case. c.builder.SetInsertPointAtEnd(nullctxblock) nullctxresult = createCall(fnptr, args, "") nullctxblock = c.builder.GetInsertBlock() c.builder.CreateBr(endblock) c.builder.SetInsertPointAtEnd(nonnullctxblock) } // non-null context case. var result llvm.Value args := append([]llvm.Value{context}, args...) if len(paramTypes) < len(args) { returnType := fntyp.ReturnType() ctxType := context.Type() paramTypes := append([]llvm.Type{ctxType}, paramTypes...) vararg := fntyp.IsFunctionVarArg() fntyp := llvm.FunctionType(returnType, paramTypes, vararg) fnptrtyp := llvm.PointerType(fntyp, 0) fnptr = c.builder.CreateBitCast(fnptr, fnptrtyp, "") } result = createCall(fnptr, args, "") // If the return type is not void, create a // PHI node to select which value to return. if !nullctxresult.IsNil() { nonnullctxblock = c.builder.GetInsertBlock() c.builder.CreateBr(endblock) c.builder.SetInsertPointAtEnd(endblock) if result.Type().TypeKind() != llvm.VoidTypeKind { phiresult := c.builder.CreatePHI(result.Type(), "") values := []llvm.Value{nullctxresult, result} blocks := []llvm.BasicBlock{nullctxblock, nonnullctxblock} phiresult.AddIncoming(values, blocks) result = phiresult } } return c.NewValue(result, result_type) } } result := createCall(fnptr, args, "") return c.NewValue(result, result_type) }
// 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 (tm *TypeMap) makeRuntimeTypeGlobal(v llvm.Value) (global, ptr llvm.Value) { global = llvm.AddGlobal(tm.module, v.Type(), "") global.SetInitializer(v) ptr = llvm.ConstBitCast(global, llvm.PointerType(tm.runtimeType, 0)) return global, ptr }