// 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_ }
// 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_ }
func (c *compiler) VisitLen(expr *ast.CallExpr) Value { if len(expr.Args) > 1 { panic("Expecting only one argument to len") } value := c.VisitExpr(expr.Args[0]) typ := value.Type() if name, ok := typ.(*types.Name); ok { typ = name.Underlying } switch typ := typ.(type) { case *types.Pointer: // XXX Converting to a string to be converted back to an int is // silly. The values need an overhaul? Perhaps have types based // on fundamental types, with the additional methods to make // them llgo.Value's. if a, isarray := typ.Base.(*types.Array); isarray { return c.NewConstValue(token.INT, strconv.FormatUint(a.Len, 10)) } v := strconv.FormatUint(uint64(unsafe.Sizeof(uintptr(0))), 10) return c.NewConstValue(token.INT, v) case *types.Slice: ptr := value.(*LLVMValue).pointer len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "") len_value := c.builder.CreateLoad(len_field, "") return c.NewLLVMValue(len_value, types.Int32).Convert(types.Int) case *types.Array: v := strconv.FormatUint(typ.Len, 10) return c.NewConstValue(token.INT, v) case *types.Struct: sz := llvm.SizeOf(c.types.ToLLVM(typ)) // FIXME // SizeOf returns a Constant, but not a ConstantInt, so we // can't call ZExtValue on it. Not sure how best to tackle // this, so for now returning this as a non-const value. //return c.NewConstValue(token.INT, string(sz.ZExtValue())) return c.NewLLVMValue(sz, types.Int) case *types.Basic: if typ == types.String.Underlying { ptr := value.(*LLVMValue).pointer len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "") len_value := c.builder.CreateLoad(len_field, "") return c.NewLLVMValue(len_value, types.Int32).Convert(types.Int) } } panic(fmt.Sprint("Unhandled value type: ", value.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 (tm *TypeMap) makeRtype(t types.Type, k reflect.Kind) llvm.Value { // Not sure if there's an easier way to do this, but if you just // use ConstStruct, you end up getting a different llvm.Type. lt := tm.ToLLVM(t) typ := llvm.ConstNull(tm.runtimeType) elementTypes := tm.runtimeType.StructElementTypes() // Size. size := llvm.SizeOf(lt) if size.Type().IntTypeWidth() > elementTypes[0].IntTypeWidth() { size = llvm.ConstTrunc(size, elementTypes[0]) } typ = llvm.ConstInsertValue(typ, size, []uint32{0}) // TODO hash // TODO padding // Alignment. align := llvm.ConstTrunc(llvm.AlignOf(lt), llvm.Int8Type()) typ = llvm.ConstInsertValue(typ, align, []uint32{3}) // var typ = llvm.ConstInsertValue(typ, align, []uint32{4}) // field // Kind. kind := llvm.ConstInt(llvm.Int8Type(), uint64(k), false) typ = llvm.ConstInsertValue(typ, kind, []uint32{5}) // Algorithm table. alg := tm.makeAlgorithmTable(t) algptr := llvm.AddGlobal(tm.module, alg.Type(), "") algptr.SetInitializer(alg) algptr = llvm.ConstBitCast(algptr, elementTypes[6]) typ = llvm.ConstInsertValue(typ, algptr, []uint32{6}) // String representation. stringrep := tm.globalStringPtr(tm.TypeString(t)) typ = llvm.ConstInsertValue(typ, stringrep, []uint32{8}) // TODO gc return typ }
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 (u *unit) defineFunction(f *ssa.Function) { // Nothing to do for functions without bodies. if len(f.Blocks) == 0 { return } // Only define functions from this package. if f.Pkg == nil { if r := f.Signature.Recv(); r != nil && r.Pkg() != nil && r.Pkg() != u.pkg.Object { return } } else if f.Pkg != u.pkg { return } fr := frame{ unit: u, blocks: make([]llvm.BasicBlock, len(f.Blocks)), env: make(map[ssa.Value]*LLVMValue), } fr.logf("Define function: %s", f.String()) llvmFunction := fr.resolveFunction(f).LLVMValue() delete(u.undefinedFuncs, f) // Functions that call recover must not be inlined, or we // can't tell whether the recover call is valid at runtime. if f.Recover != nil { llvmFunction.AddFunctionAttr(llvm.NoInlineAttribute) } for i, block := range f.Blocks { fr.blocks[i] = llvm.AddBasicBlock(llvmFunction, fmt.Sprintf(".%d.%s", i, block.Comment)) } fr.builder.SetInsertPointAtEnd(fr.blocks[0]) var paramOffset int if len(f.FreeVars) > 0 { // Extract captures from the first implicit parameter. arg0 := llvmFunction.Param(0) for i, fv := range f.FreeVars { addressPtr := fr.builder.CreateStructGEP(arg0, i, "") address := fr.builder.CreateLoad(addressPtr, "") fr.env[fv] = fr.NewValue(address, fv.Type()) } paramOffset++ } for i, param := range f.Params { fr.env[param] = fr.NewValue(llvmFunction.Param(i+paramOffset), param.Type()) } // Allocate stack space for locals in the prologue block. prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue") fr.builder.SetInsertPointAtEnd(prologueBlock) for _, local := range f.Locals { typ := fr.llvmtypes.ToLLVM(deref(local.Type())) alloca := fr.builder.CreateAlloca(typ, local.Comment) u.memsetZero(alloca, llvm.SizeOf(typ)) value := fr.NewValue(alloca, local.Type()) fr.env[local] = value } // Move any allocs relating to named results from the entry block // to the prologue block, so they dominate the rundefers and recover // blocks. // // TODO(axw) ask adonovan for a cleaner way of doing this, e.g. // have ssa generate an entry block that defines Allocs and related // stores, and then a separate block for function body instructions. if f.Synthetic == "" { if results := f.Signature.Results(); results != nil { for i := 0; i < results.Len(); i++ { result := results.At(i) if result.Name() == "" { break } for i, instr := range f.Blocks[0].Instrs { if instr, ok := instr.(*ssa.Alloc); ok && instr.Heap && instr.Pos() == result.Pos() { fr.instruction(instr) instrs := f.Blocks[0].Instrs instrs = append(instrs[:i], instrs[i+1:]...) f.Blocks[0].Instrs = instrs break } } } } } // If the function contains any defers, we must first call // setjmp so we can call rundefers in response to a panic. // We can short-circuit the check for defers with // f.Recover != nil. if f.Recover != nil || hasDefer(f) { rdblock := llvm.AddBasicBlock(llvmFunction, "rundefers") defers := fr.builder.CreateAlloca(fr.runtime.defers.llvm, "") fr.builder.CreateCall(fr.runtime.initdefers.LLVMValue(), []llvm.Value{defers}, "") jb := fr.builder.CreateStructGEP(defers, 0, "") jb = fr.builder.CreateBitCast(jb, llvm.PointerType(llvm.Int8Type(), 0), "") result := fr.builder.CreateCall(fr.runtime.setjmp.LLVMValue(), []llvm.Value{jb}, "") result = fr.builder.CreateIsNotNull(result, "") fr.builder.CreateCondBr(result, rdblock, fr.blocks[0]) // We'll only get here via a panic, which must either be // recovered or continue panicking up the stack without // returning from "rundefers". The recover block may be // nil even if we can recover, in which case we just need // to return the zero value for each result (if any). var recoverBlock llvm.BasicBlock if f.Recover != nil { recoverBlock = fr.block(f.Recover) } else { recoverBlock = llvm.AddBasicBlock(llvmFunction, "recover") fr.builder.SetInsertPointAtEnd(recoverBlock) var nresults int results := f.Signature.Results() if results != nil { nresults = results.Len() } switch nresults { case 0: fr.builder.CreateRetVoid() case 1: fr.builder.CreateRet(llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(0).Type()))) default: values := make([]llvm.Value, nresults) for i := range values { values[i] = llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(i).Type())) } fr.builder.CreateAggregateRet(values) } } fr.builder.SetInsertPointAtEnd(rdblock) fr.builder.CreateCall(fr.runtime.rundefers.LLVMValue(), nil, "") fr.builder.CreateBr(recoverBlock) } else { fr.builder.CreateBr(fr.blocks[0]) } for i, block := range f.Blocks { fr.translateBlock(block, fr.blocks[i]) } }
func (c *compiler) VisitGoStmt(stmt *ast.GoStmt) { //stmt.Call *ast.CallExpr // TODO var fn *LLVMValue switch x := (stmt.Call.Fun).(type) { case *ast.Ident: fn = c.Resolve(x.Obj).(*LLVMValue) if fn == nil { panic(fmt.Sprintf( "No function found with name '%s'", x.String())) } default: fn = c.VisitExpr(stmt.Call.Fun).(*LLVMValue) } // Evaluate arguments, store in a structure on the stack. var args_struct_type llvm.Type var args_mem llvm.Value var args_size llvm.Value if stmt.Call.Args != nil { param_types := make([]llvm.Type, 0) fn_type := types.Deref(fn.Type()).(*types.Func) for _, param := range fn_type.Params { typ := param.Type.(types.Type) param_types = append(param_types, c.types.ToLLVM(typ)) } args_struct_type = llvm.StructType(param_types, false) args_mem = c.builder.CreateAlloca(args_struct_type, "") for i, expr := range stmt.Call.Args { value_i := c.VisitExpr(expr) value_i = value_i.Convert(fn_type.Params[i].Type.(types.Type)) arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "") c.builder.CreateStore(value_i.LLVMValue(), arg_i) } args_size = llvm.SizeOf(args_struct_type) args_size = llvm.ConstTrunc(args_size, llvm.Int32Type()) } else { args_struct_type = llvm.VoidType() args_mem = llvm.ConstNull(llvm.PointerType(args_struct_type, 0)) args_size = llvm.ConstInt(llvm.Int32Type(), 0, false) } // When done, return to where we were. defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock()) // 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. indirect_fn_type := llvm.FunctionType( llvm.VoidType(), []llvm.Type{llvm.PointerType(args_struct_type, 0)}, false) indirect_fn := llvm.AddFunction(c.module.Module, "", indirect_fn_type) indirect_fn.SetFunctionCallConv(llvm.CCallConv) // Call "newgoroutine" with the indirect function and stored args. newgoroutine := getnewgoroutine(c.module.Module) ngr_param_types := newgoroutine.Type().ElementType().ParamTypes() fn_arg := c.builder.CreateBitCast(indirect_fn, ngr_param_types[0], "") args_arg := c.builder.CreateBitCast(args_mem, llvm.PointerType(llvm.Int8Type(), 0), "") c.builder.CreateCall(newgoroutine, []llvm.Value{fn_arg, args_arg, args_size}, "") entry := llvm.AddBasicBlock(indirect_fn, "entry") c.builder.SetInsertPointAtEnd(entry) var args []llvm.Value if stmt.Call.Args != nil { args_mem = indirect_fn.Param(0) args = make([]llvm.Value, len(stmt.Call.Args)) for i := range stmt.Call.Args { arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{ llvm.ConstInt(llvm.Int32Type(), 0, false), llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "") args[i] = c.builder.CreateLoad(arg_i, "") } } c.builder.CreateCall(fn.LLVMValue(), args, "") c.builder.CreateRetVoid() }
func (c *compiler) chanSelect(states []selectState, blocking bool) *LLVMValue { stackptr := c.stacksave() defer c.stackrestore(stackptr) n := uint64(len(states)) if !blocking { // blocking means there's no default case n++ } lln := llvm.ConstInt(llvm.Int32Type(), n, false) allocsize := c.builder.CreateCall(c.runtime.selectsize.LLVMValue(), []llvm.Value{lln}, "") selectp := c.builder.CreateArrayAlloca(llvm.Int8Type(), allocsize, "selectp") c.memsetZero(selectp, allocsize) selectp = c.builder.CreatePtrToInt(selectp, c.target.IntPtrType(), "") c.builder.CreateCall(c.runtime.selectinit.LLVMValue(), []llvm.Value{lln, selectp}, "") // Allocate stack for the values to send/receive. // // TODO(axw) request optimisation in ssa to special- // case receive cases with no assignment, so we know // not to allocate stack space or do a copy. resTypes := []types.Type{types.Typ[types.Int], types.Typ[types.Bool]} for _, state := range states { if state.Dir == types.RecvOnly { chantyp := state.Chan.Type().Underlying().(*types.Chan) resTypes = append(resTypes, chantyp.Elem()) } } resType := tupleType(resTypes...) llResType := c.types.ToLLVM(resType) tupleptr := c.builder.CreateAlloca(llResType, "") c.memsetZero(tupleptr, llvm.SizeOf(llResType)) var recvindex int ptrs := make([]llvm.Value, len(states)) for i, state := range states { chantyp := state.Chan.Type().Underlying().(*types.Chan) elemtyp := c.types.ToLLVM(chantyp.Elem()) if state.Dir == types.SendOnly { ptrs[i] = c.builder.CreateAlloca(elemtyp, "") c.builder.CreateStore(state.Send.LLVMValue(), ptrs[i]) } else { ptrs[i] = c.builder.CreateStructGEP(tupleptr, recvindex+2, "") recvindex++ } ptrs[i] = c.builder.CreatePtrToInt(ptrs[i], c.target.IntPtrType(), "") } // Create select{send,recv} calls. selectsend := c.runtime.selectsend.LLVMValue() selectrecv := c.runtime.selectrecv.LLVMValue() var received llvm.Value if recvindex > 0 { received = c.builder.CreateStructGEP(tupleptr, 1, "") } if !blocking { c.builder.CreateCall(c.runtime.selectdefault.LLVMValue(), []llvm.Value{selectp}, "") } for i, state := range states { ch := state.Chan.LLVMValue() if state.Dir == types.SendOnly { c.builder.CreateCall(selectsend, []llvm.Value{selectp, ch, ptrs[i]}, "") } else { c.builder.CreateCall(selectrecv, []llvm.Value{selectp, ch, ptrs[i], received}, "") } } // Fire off the select. index := c.builder.CreateCall(c.runtime.selectgo.LLVMValue(), []llvm.Value{selectp}, "") tuple := c.builder.CreateLoad(tupleptr, "") tuple = c.builder.CreateInsertValue(tuple, index, 0, "") return c.NewValue(tuple, resType) }
func (c *compiler) createTypeMalloc(t llvm.Type) llvm.Value { ptr := c.createMalloc(llvm.SizeOf(t)) return c.builder.CreateIntToPtr(ptr, llvm.PointerType(t, 0), "") }
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec, isconst bool) { // Check if the value-spec has already been visited (referenced // before definition visited.) if len(valspec.Names) > 0 { if _, ok := valspec.Names[0].Obj.Data.(Value); ok { return } } // Constants are evaluated during typechecking. We can just pull // out the value from the name's object data. if isconst { for _, name := range valspec.Names { if name.Name != "_" && name.Obj != nil { value := name.Obj.Data.(types.Const) typ := name.Obj.Type.(types.Type) name.Obj.Data = ConstValue{value, c, typ} } } return } pkgname, ispackagelevel := c.pkgmap[valspec.Names[0].Obj] if ispackagelevel { c.createGlobals(valspec.Names, valspec.Values, pkgname) return } var values []Value if len(valspec.Values) == 1 && len(valspec.Names) > 1 { values = c.destructureExpr(valspec.Values[0]) } else if len(valspec.Values) > 0 { values = make([]Value, len(valspec.Names)) for i := range valspec.Names { values[i] = c.VisitExpr(valspec.Values[i]) } } for i, name := range valspec.Names { if name.Name == "_" { continue } // The variable should be allocated on the stack if it's // declared inside a function. // // FIXME currently allocating all variables on the heap. // Change this to allocate on the stack, and perform // escape analysis to determine whether to promote. typ := name.Obj.Type.(types.Type) llvmtyp := c.types.ToLLVM(typ) ptr := c.createTypeMalloc(llvmtyp) if values == nil || values[i] == nil { // If no initialiser was specified, bzero it. bzero := c.NamedFunction("runtime.bzero", "func f(unsafe.Pointer, uintptr)") ptr := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") args := []llvm.Value{ptr, llvm.SizeOf(llvmtyp)} c.builder.CreateCall(bzero, args, "") } else { // FIXME we need to revisit how aggregate types // are initialised/copied/etc. A CreateStore will // try to do everything in registers, which is // going to hurt when the aggregate is large. llvmInit := values[i].Convert(typ).LLVMValue() c.builder.CreateStore(llvmInit, ptr) } stackvar := c.NewLLVMValue(ptr, &types.Pointer{Base: typ}).makePointee() stackvar.stack = c.functions[len(c.functions)-1] name.Obj.Data = stackvar } }
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) Value { typ := c.types.expr[lit] var valuemap map[interface{}]Value var valuelist []Value _, isstruct := types.Underlying(typ).(*types.Struct) if lit.Elts != nil { for _, elt := range lit.Elts { var value Value if kv, iskv := elt.(*ast.KeyValueExpr); iskv { value = c.VisitExpr(kv.Value) if valuemap == nil { valuemap = make(map[interface{}]Value) } var key interface{} if isstruct { key = kv.Key.(*ast.Ident).Name } else { key = c.VisitExpr(kv.Key) } valuemap[key] = value } else { value = c.VisitExpr(elt) valuelist = append(valuelist, value) } } } // For array/slice types, convert key:value to contiguous // values initialiser. switch types.Underlying(typ).(type) { case *types.Array, *types.Slice: if len(valuemap) > 0 { maxi := int64(-1) for key, _ := range valuemap { i := key.(ConstValue).Int64() if i < 0 { panic("array index must be non-negative integer constant") } else if i > maxi { maxi = i } } valuelist = make([]Value, maxi+1) for key, value := range valuemap { i := key.(ConstValue).Int64() valuelist[i] = value } } } origtyp := typ switch typ := types.Underlying(typ).(type) { case *types.Array: elttype := typ.Elt llvmelttype := c.types.ToLLVM(elttype) llvmvalues := make([]llvm.Value, typ.Len) for i := range llvmvalues { var value Value if i < len(valuelist) { value = valuelist[i] } if value == nil { llvmvalues[i] = llvm.ConstNull(llvmelttype) } else if _, ok := value.(ConstValue); ok || value.LLVMValue().IsConstant() { llvmvalues[i] = value.Convert(elttype).LLVMValue() } else { llvmvalues[i] = llvm.Undef(llvmelttype) } } array := llvm.ConstArray(llvmelttype, llvmvalues) for i, value := range valuelist { if llvmvalues[i].IsUndef() { value := value.Convert(elttype).LLVMValue() array = c.builder.CreateInsertValue(array, value, i, "") } } return c.NewLLVMValue(array, origtyp) case *types.Slice: ptr := c.createTypeMalloc(c.types.ToLLVM(typ)) eltType := c.types.ToLLVM(typ.Elt) arrayType := llvm.ArrayType(eltType, len(valuelist)) valuesPtr := c.createMalloc(llvm.SizeOf(arrayType)) valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "") //valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "") // TODO check result of mallocs length := llvm.ConstInt(llvm.Int32Type(), uint64(len(valuelist)), false) c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, "")) // len c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, "")) // cap null := llvm.ConstNull(c.types.ToLLVM(typ.Elt)) for i, value := range valuelist { index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "") if value == nil { c.builder.CreateStore(null, valuePtr) } else { c.builder.CreateStore(value.Convert(typ.Elt).LLVMValue(), valuePtr) } } m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp}) return m.makePointee() case *types.Struct: values := valuelist llvmtyp := c.types.ToLLVM(typ) ptr := c.createTypeMalloc(llvmtyp) bzero := c.NamedFunction("runtime.bzero", "func f(unsafe.Pointer, uintptr)") ptrintval := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") args := []llvm.Value{ptrintval, llvm.SizeOf(llvmtyp)} c.builder.CreateCall(bzero, args, "") if valuemap != nil { for key, value := range valuemap { fieldName := key.(string) index := typ.FieldIndices[fieldName] for len(values) <= int(index) { values = append(values, nil) } values[index] = value } } for i, value := range values { if value != nil { elttype := typ.Fields[i].Type.(types.Type) llvm_value := value.Convert(elttype).LLVMValue() ptr := c.builder.CreateStructGEP(ptr, i, "") c.builder.CreateStore(llvm_value, ptr) } } m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp}) return m.makePointee() case *types.Map: value := llvm.ConstNull(c.types.ToLLVM(typ)) // TODO initialise map return c.NewLLVMValue(value, origtyp) } panic(fmt.Sprint("Unhandled type kind: ", typ)) }
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) (v *LLVMValue) { typ := c.types.expr[lit].Type var valuemap map[interface{}]Value var valuelist []Value if ptr, ok := typ.(*types.Pointer); ok { typ = ptr.Elem() defer func() { v = v.pointer }() } var isstruct, isarray, isslice, ismap bool switch typ.Underlying().(type) { case *types.Struct: isstruct = true case *types.Array: isarray = true case *types.Slice: isslice = true case *types.Map: ismap = true default: panic(fmt.Errorf("Unhandled type: %s", typ)) } if lit.Elts != nil { for i, elt := range lit.Elts { if kv, iskv := elt.(*ast.KeyValueExpr); iskv { if valuemap == nil { valuemap = make(map[interface{}]Value) } var key interface{} var elttyp types.Type switch { case isstruct: name := kv.Key.(*ast.Ident).Name key = name typ := typ.Underlying().(*types.Struct) elttyp = typ.Field(fieldIndex(typ, name)).Type case isarray: key = c.types.expr[kv.Key].Value typ := typ.Underlying().(*types.Array) elttyp = typ.Elem() case isslice: key = c.types.expr[kv.Key].Value typ := typ.Underlying().(*types.Slice) elttyp = typ.Elem() case ismap: key = c.VisitExpr(kv.Key) typ := typ.Underlying().(*types.Map) elttyp = typ.Elem() default: panic("unreachable") } c.convertUntyped(kv.Value, elttyp) valuemap[key] = c.VisitExpr(kv.Value) } else { switch { case isstruct: typ := typ.Underlying().(*types.Struct) c.convertUntyped(elt, typ.Field(i).Type) case isarray: typ := typ.Underlying().(*types.Array) c.convertUntyped(elt, typ.Elem()) case isslice: typ := typ.Underlying().(*types.Slice) c.convertUntyped(elt, typ.Elem()) } value := c.VisitExpr(elt) valuelist = append(valuelist, value) } } } // For array/slice types, convert key:value to contiguous // values initialiser. switch typ.Underlying().(type) { case *types.Array, *types.Slice: if len(valuemap) > 0 { var maxkey uint64 for key, _ := range valuemap { key, _ := exact.Uint64Val(key.(exact.Value)) if key > maxkey { maxkey = key } } valuelist = make([]Value, maxkey+1) for key, value := range valuemap { key, _ := exact.Uint64Val(key.(exact.Value)) valuelist[key] = value } } } origtyp := typ switch typ := typ.Underlying().(type) { case *types.Array: elttype := typ.Elem() llvmelttype := c.types.ToLLVM(elttype) llvmvalues := make([]llvm.Value, typ.Len()) for i := range llvmvalues { var value Value if i < len(valuelist) { value = valuelist[i] } if value == nil { llvmvalues[i] = llvm.ConstNull(llvmelttype) } else if value.LLVMValue().IsConstant() { llvmvalues[i] = value.Convert(elttype).LLVMValue() } else { llvmvalues[i] = llvm.Undef(llvmelttype) } } array := llvm.ConstArray(llvmelttype, llvmvalues) for i, value := range valuelist { if llvmvalues[i].IsUndef() { value := value.Convert(elttype).LLVMValue() array = c.builder.CreateInsertValue(array, value, i, "") } } return c.NewValue(array, origtyp) case *types.Slice: ptr := c.createTypeMalloc(c.types.ToLLVM(typ)) eltType := c.types.ToLLVM(typ.Elem()) arrayType := llvm.ArrayType(eltType, len(valuelist)) valuesPtr := c.createMalloc(llvm.SizeOf(arrayType)) valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "") //valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "") length := llvm.ConstInt(c.types.inttype, uint64(len(valuelist)), false) c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, "")) // len c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, "")) // cap null := llvm.ConstNull(c.types.ToLLVM(typ.Elem())) for i, value := range valuelist { index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "") if value == nil { c.builder.CreateStore(null, valuePtr) } else { c.builder.CreateStore(value.Convert(typ.Elem()).LLVMValue(), valuePtr) } } m := c.NewValue(ptr, types.NewPointer(origtyp)) return m.makePointee() case *types.Struct: values := valuelist llvmtyp := c.types.ToLLVM(typ) ptr := c.createTypeMalloc(llvmtyp) if valuemap != nil { for key, value := range valuemap { index := fieldIndex(typ, key.(string)) for len(values) <= index { values = append(values, nil) } values[index] = value } } for i, value := range values { if value != nil { elttype := typ.Field(i).Type llvm_value := value.Convert(elttype).LLVMValue() ptr := c.builder.CreateStructGEP(ptr, i, "") c.builder.CreateStore(llvm_value, ptr) } } m := c.NewValue(ptr, types.NewPointer(origtyp)) return m.makePointee() case *types.Map: value := llvm.ConstNull(c.types.ToLLVM(typ)) // TODO initialise map return c.NewValue(value, origtyp) } panic(fmt.Sprint("Unhandled type kind: ", typ)) }