func (tm *TypeMap) makeAlgorithmTable(t types.Type) llvm.Value { // TODO set these to actual functions. hashAlg := llvm.ConstNull(llvm.PointerType(tm.hashAlgFunctionType, 0)) printAlg := llvm.ConstNull(llvm.PointerType(tm.printAlgFunctionType, 0)) copyAlg := llvm.ConstNull(llvm.PointerType(tm.copyAlgFunctionType, 0)) const eqalgsig = "func(uintptr, unsafe.Pointer, unsafe.Pointer) bool" var equalAlg llvm.Value switch t := t.(type) { case *types.Basic: switch t.Kind() { case types.String: equalAlg = tm.functions.NamedFunction("runtime.streqalg", eqalgsig) case types.Float32: equalAlg = tm.functions.NamedFunction("runtime.f32eqalg", eqalgsig) case types.Float64: equalAlg = tm.functions.NamedFunction("runtime.f64eqalg", eqalgsig) case types.Complex64: equalAlg = tm.functions.NamedFunction("runtime.c64eqalg", eqalgsig) case types.Complex128: equalAlg = tm.functions.NamedFunction("runtime.c128eqalg", eqalgsig) } } if equalAlg.IsNil() { equalAlg = tm.functions.NamedFunction("runtime.memequal", eqalgsig) } elems := []llvm.Value{hashAlg, equalAlg, printAlg, copyAlg} return llvm.ConstStruct(elems, false) }
func (c *compiler) makeFunc(ident *ast.Ident, ftyp *types.Signature) *LLVMValue { fname := ident.String() if ftyp.Recv() == nil && fname == "init" { // Make "init" functions anonymous. fname = "" } else { var pkgname string if recv := ftyp.Recv(); recv != nil { var recvname string switch recvtyp := recv.Type().(type) { case *types.Pointer: if named, ok := recvtyp.Elem().(*types.Named); ok { obj := named.Obj() recvname = "*" + obj.Name() pkgname = obj.Pkg().Path() } case *types.Named: named := recvtyp obj := named.Obj() recvname = obj.Name() pkgname = obj.Pkg().Path() } if recvname != "" { fname = fmt.Sprintf("%s.%s", recvname, fname) } else { // If the receiver is an unnamed struct, we're // synthesising a method for an unnamed struct // type. There's no meaningful name to give the // function, so leave it up to LLVM. fname = "" } } else { obj := c.typeinfo.Objects[ident] pkgname = obj.Pkg().Path() } if fname != "" { fname = pkgname + "." + fname } } // gcimporter may produce multiple AST objects for the same function. llvmftyp := c.types.ToLLVM(ftyp) var fn llvm.Value if fname != "" { fn = c.module.Module.NamedFunction(fname) } if fn.IsNil() { llvmfptrtyp := llvmftyp.StructElementTypes()[0].ElementType() fn = llvm.AddFunction(c.module.Module, fname, llvmfptrtyp) } fn = llvm.ConstInsertValue(llvm.ConstNull(llvmftyp), fn, []uint32{0}) return c.NewValue(fn, ftyp) }
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) { typ := v.Type() // We need to put alloca instructions in the top block or the values // displayed when inspecting these variables in a debugger will be // completely messed up. curBlock := c.builder.GetInsertBlock() if p := curBlock.Parent(); !p.IsNil() { fb := p.FirstBasicBlock() fi := fb.FirstInstruction() if !fb.IsNil() && !fi.IsNil() { c.builder.SetInsertPointBefore(fi) } } old := c.builder.CurrentDebugLocation() c.builder.SetCurrentDebugLocation(llvm.Value{}) stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name) // For arguments we want to insert the store instruction // without debug information to ensure that they are executed // (and hence have proper values) before the debugger hits the // first line in a function. if argument == 0 { c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) } if !value.IsNil() { c.builder.CreateStore(value, stackvalue) } c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar = ptrvalue.makePointee() stackvar.stack = stackf c.objectdata[v].Value = stackvar file := c.fileset.File(v.Pos()) tag := llvm.DW_TAG_auto_variable if argument > 0 { tag = llvm.DW_TAG_arg_variable } ld := llvm.NewLocalVariableDescriptor(tag) ld.Argument = uint32(argument) ld.Line = uint32(file.Line(v.Pos())) ld.Name = name ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())} ld.Type = c.tollvmDebugDescriptor(typ) ld.Context = c.currentDebugContext() c.builder.InsertDeclare(c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(ld)) return stackvalue, stackvar }
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) { typ := v.Type() // We need to put alloca instructions in the top block or the values // displayed when inspecting these variables in a debugger will be // completely messed up. curBlock := c.builder.GetInsertBlock() if p := curBlock.Parent(); !p.IsNil() { fb := p.FirstBasicBlock() fi := fb.FirstInstruction() if !fb.IsNil() && !fi.IsNil() { c.builder.SetInsertPointBefore(fi) } } old := c.builder.CurrentDebugLocation() c.builder.SetCurrentDebugLocation(llvm.Value{}) stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name) // For arguments we want to insert the store instruction // without debug information to ensure that they are executed // (and hence have proper values) before the debugger hits the // first line in a function. if argument == 0 { c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) } if !value.IsNil() { c.builder.CreateStore(value, stackvalue) } c.builder.SetCurrentDebugLocation(old) c.builder.SetInsertPointAtEnd(curBlock) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar = ptrvalue.makePointee() stackvar.stack = stackf c.objectdata[v].Value = stackvar // Generate debug metadata (will return nil // if debug-data generation is disabled). if descriptor := c.createLocalVariableMetadata(v, argument); descriptor != nil { c.builder.InsertDeclare( c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(descriptor), ) } return stackvalue, stackvar }
func (v *LLVMValue) chanSend(value Value) { var ptr llvm.Value if value, ok := value.(*LLVMValue); ok && value.pointer != nil { ptr = value.pointer.LLVMValue() } elttyp := types.Underlying(v.typ).(*types.Chan).Elt c := v.compiler if ptr.IsNil() { ptr = c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "") value := value.Convert(elttyp).LLVMValue() c.builder.CreateStore(value, ptr) } uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") f := c.NamedFunction("runtime.chansend", "func f(c, ptr uintptr)") c.builder.CreateCall(f, []llvm.Value{v.LLVMValue(), uintptr_}, "") }
func (v *LLVMValue) chanSend(value Value) { var ptr llvm.Value if value, ok := value.(*LLVMValue); ok && value.pointer != nil { ptr = value.pointer.LLVMValue() } elttyp := v.typ.Underlying().(*types.Chan).Elem() c := v.compiler if ptr.IsNil() { ptr = c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "") value := value.Convert(elttyp).LLVMValue() c.builder.CreateStore(value, ptr) } uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "") nb := boolLLVMValue(false) f := c.NamedFunction("runtime.chansend", "func(t *chanType, c, ptr uintptr, nb bool) bool") chantyp := c.types.ToRuntime(v.typ.Underlying()) chantyp = c.builder.CreateBitCast(chantyp, f.Type().ElementType().ParamTypes()[0], "") c.builder.CreateCall(f, []llvm.Value{chantyp, v.LLVMValue(), uintptr_, nb}, "") // Ignore result; only used in runtime. }
func (c *compiler) VisitSelectStmt(stmt *ast.SelectStmt) { // TODO optimisations: // 1. No clauses: runtime.block. // 2. Single recv, and default clause: runtime.selectnbrecv // 2. Single send, and default clause: runtime.selectnbsend startBlock := c.builder.GetInsertBlock() function := startBlock.Parent() endBlock := llvm.AddBasicBlock(function, "end") endBlock.MoveAfter(startBlock) defer c.builder.SetInsertPointAtEnd(endBlock) // Cache runtime functions var selectrecv, selectsend llvm.Value getselectsend := func() llvm.Value { if selectsend.IsNil() { selectsend = c.NamedFunction("runtime.selectsend", "func(selectp, blockaddr, ch, elem unsafe.Pointer)") } return selectsend } getselectrecv := func() llvm.Value { if selectrecv.IsNil() { selectrecv = c.NamedFunction("runtime.selectrecv", "func(selectp, blockaddr, ch, elem unsafe.Pointer, received *bool)") } return selectrecv } // We create a pointer-pointer for each newly defined var on the // lhs of receive expressions, which will be assigned to when the // expressions are (conditionally) evaluated. lhsptrs := make([][]llvm.Value, len(stmt.Body.List)) for i, stmt := range stmt.Body.List { clause := stmt.(*ast.CommClause) if stmt, ok := clause.Comm.(*ast.AssignStmt); ok && stmt.Tok == token.DEFINE { lhs := make([]llvm.Value, len(stmt.Lhs)) for i, expr := range stmt.Lhs { ident := expr.(*ast.Ident) if !isBlank(ident.Name) { typ := c.target.IntPtrType() lhs[i] = c.builder.CreateAlloca(typ, "") c.builder.CreateStore(llvm.ConstNull(typ), lhs[i]) } } lhsptrs[i] = lhs } } // Create clause basic blocks. blocks := make([]llvm.BasicBlock, len(stmt.Body.List)) var basesize uint64 for i, stmt := range stmt.Body.List { clause := stmt.(*ast.CommClause) if clause.Comm == nil { basesize++ } currBlock := c.builder.GetInsertBlock() block := llvm.InsertBasicBlock(endBlock, "") c.builder.SetInsertPointAtEnd(block) blocks[i] = block lhs := lhsptrs[i] if stmt, ok := clause.Comm.(*ast.AssignStmt); ok { for i, expr := range stmt.Lhs { ident := expr.(*ast.Ident) if !isBlank(ident.Name) { ptr := c.builder.CreateLoad(lhs[i], "") obj := c.typeinfo.Objects[ident] ptrtyp := types.NewPointer(obj.Type()) ptr = c.builder.CreateIntToPtr(ptr, c.types.ToLLVM(ptrtyp), "") value := c.NewValue(ptr, ptrtyp).makePointee() c.objectdata[obj].Value = value } } } for _, stmt := range clause.Body { c.VisitStmt(stmt) } c.maybeImplicitBranch(endBlock) c.builder.SetInsertPointAtEnd(currBlock) } // We need to make an initial pass through the cases, // discarding those where the channel is nil. size := llvm.ConstInt(llvm.Int32Type(), basesize, false) channels := make([]Value, len(stmt.Body.List)) rhs := make([]*LLVMValue, len(stmt.Body.List)) for i, stmt := range stmt.Body.List { clause := stmt.(*ast.CommClause) switch comm := clause.Comm.(type) { case nil: case *ast.SendStmt: channels[i] = c.VisitExpr(comm.Chan) rhs[i] = c.VisitExpr(comm.Value).(*LLVMValue) case *ast.ExprStmt: channels[i] = c.VisitExpr(comm.X.(*ast.UnaryExpr).X) case *ast.AssignStmt: channels[i] = c.VisitExpr(comm.Rhs[0].(*ast.UnaryExpr).X) default: panic(fmt.Errorf("unhandled: %T", comm)) } if channels[i] != nil { nonnil := c.builder.CreateIsNotNull(channels[i].LLVMValue(), "") zero := llvm.ConstInt(llvm.Int32Type(), 0, false) one := llvm.ConstInt(llvm.Int32Type(), 1, false) addend := c.builder.CreateSelect(nonnil, one, zero, "") size = c.builder.CreateAdd(size, addend, "") } } f := c.NamedFunction("runtime.selectsize", "func(size int32) uintptr") selectsize := c.builder.CreateCall(f, []llvm.Value{size}, "") selectp := c.builder.CreateArrayAlloca(llvm.Int8Type(), selectsize, "selectp") c.memsetZero(selectp, selectsize) selectp = c.builder.CreatePtrToInt(selectp, c.target.IntPtrType(), "") f = c.NamedFunction("runtime.initselect", "func(size int32, ptr unsafe.Pointer)") c.builder.CreateCall(f, []llvm.Value{size, selectp}, "") for i, stmt := range stmt.Body.List { clause := stmt.(*ast.CommClause) blockaddr := llvm.BlockAddress(function, blocks[i]) blockaddr = c.builder.CreatePtrToInt(blockaddr, c.target.IntPtrType(), "") if clause.Comm == nil { // default clause f := c.NamedFunction("runtime.selectdefault", "func(selectp, blockaddr unsafe.Pointer)") c.builder.CreateCall(f, []llvm.Value{selectp, blockaddr}, "") continue } currBlock := c.builder.GetInsertBlock() nextBlock := llvm.InsertBasicBlock(currBlock, "") nextBlock.MoveAfter(currBlock) block := llvm.InsertBasicBlock(endBlock, "") chanval := channels[i].LLVMValue() nonnilchan := c.builder.CreateIsNotNull(chanval, "") c.builder.CreateCondBr(nonnilchan, block, nextBlock) c.builder.SetInsertPointAtEnd(block) switch comm := clause.Comm.(type) { case *ast.SendStmt: // c <- val elem := rhs[i] var elemptr llvm.Value if elem.pointer == nil { value := elem.LLVMValue() elemptr = c.builder.CreateAlloca(value.Type(), "") c.builder.CreateStore(value, elemptr) } else { elemptr = elem.pointer.LLVMValue() } elemptr = c.builder.CreatePtrToInt(elemptr, c.target.IntPtrType(), "") f := getselectsend() c.builder.CreateCall(f, []llvm.Value{selectp, blockaddr, chanval, elemptr}, "") case *ast.ExprStmt: // <-c f := getselectrecv() paramtypes := f.Type().ElementType().ParamTypes() elem := llvm.ConstNull(paramtypes[3]) received := llvm.ConstNull(paramtypes[4]) c.builder.CreateCall(f, []llvm.Value{selectp, blockaddr, chanval, elem, received}, "") case *ast.AssignStmt: // val := <-c // val, ok = <-c f := getselectrecv() lhs := c.assignees(comm) paramtypes := f.Type().ElementType().ParamTypes() var elem llvm.Value if lhs[0] != nil { elem = lhs[0].pointer.LLVMValue() elem = c.builder.CreatePtrToInt(elem, paramtypes[3], "") if !lhsptrs[i][0].IsNil() { c.builder.CreateStore(elem, lhsptrs[i][0]) } } else { elem = llvm.ConstNull(paramtypes[3]) } var received llvm.Value if len(lhs) == 2 && lhs[1] != nil { received = lhs[1].pointer.LLVMValue() received = c.builder.CreatePtrToInt(received, paramtypes[4], "") if !lhsptrs[i][1].IsNil() { c.builder.CreateStore(received, lhsptrs[i][1]) } } else { received = llvm.ConstNull(paramtypes[4]) } c.builder.CreateCall(f, []llvm.Value{selectp, blockaddr, chanval, elem, received}, "") } c.builder.CreateBr(nextBlock) c.builder.SetInsertPointAtEnd(nextBlock) } f = c.NamedFunction("runtime.selectgo", "func(selectp unsafe.Pointer) unsafe.Pointer") blockaddr := c.builder.CreateCall(f, []llvm.Value{selectp}, "") blockaddr = c.builder.CreateIntToPtr(blockaddr, llvm.PointerType(llvm.Int8Type(), 0), "") ibr := c.builder.CreateIndirectBr(blockaddr, len(stmt.Body.List)) for _, block := range blocks { ibr.AddDest(block) } }
func (c *compiler) VisitRangeStmt(stmt *ast.RangeStmt) { currBlock := c.builder.GetInsertBlock() doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done") doneBlock.MoveAfter(currBlock) postBlock := llvm.InsertBasicBlock(doneBlock, "post") loopBlock := llvm.InsertBasicBlock(postBlock, "loop") condBlock := llvm.InsertBasicBlock(loopBlock, "cond") defer c.builder.SetInsertPointAtEnd(doneBlock) // Evaluate range expression first. x := c.VisitExpr(stmt.X) // If it's a pointer type, we'll first check that it's non-nil. typ := x.Type().Underlying() if _, ok := typ.(*types.Pointer); ok { ifBlock := llvm.InsertBasicBlock(doneBlock, "if") isnotnull := c.builder.CreateIsNotNull(x.LLVMValue(), "") c.builder.CreateCondBr(isnotnull, ifBlock, doneBlock) c.builder.SetInsertPointAtEnd(ifBlock) } // Is it a new var definition? Then allocate some memory on the stack. var keyPtr, valuePtr llvm.Value if stmt.Tok == token.DEFINE { if key := stmt.Key.(*ast.Ident); !isBlank(key.Name) { keyobj := c.typeinfo.Objects[key] keyPtr, _ = c.newStackVar(c.functions.top().LLVMValue, keyobj, llvm.Value{}, key.Name) } if stmt.Value != nil { if value := stmt.Value.(*ast.Ident); !isBlank(value.Name) { valueobj := c.typeinfo.Objects[value] valuePtr, _ = c.newStackVar(c.functions.top().LLVMValue, valueobj, llvm.Value{}, value.Name) } } } else { // Simple assignment, resolve the key/value pointers. if key := stmt.Key.(*ast.Ident); !isBlank(key.Name) { keyPtr = c.Resolve(key).(*LLVMValue).pointer.LLVMValue() } if stmt.Value != nil { if value := stmt.Value.(*ast.Ident); !isBlank(value.Name) { valuePtr = c.Resolve(value).(*LLVMValue).pointer.LLVMValue() } } } if c.lastlabel != nil { labelData := c.labelData(c.lastlabel) labelData.Break = doneBlock labelData.Continue = postBlock c.lastlabel = nil } c.breakblocks = append(c.breakblocks, doneBlock) c.continueblocks = append(c.continueblocks, postBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] c.continueblocks = c.continueblocks[:len(c.continueblocks)-1] }() isarray := false var base, length llvm.Value _, isptr := typ.(*types.Pointer) if isptr { typ = typ.(*types.Pointer).Elem() } switch typ := typ.Underlying().(type) { case *types.Map: goto maprange case *types.Chan: goto chanrange case *types.Basic: stringvalue := x.LLVMValue() length = c.builder.CreateExtractValue(stringvalue, 1, "") goto stringrange case *types.Array: isarray = true x := x if !isptr { if x_, ok := x.(*LLVMValue); ok && x_.pointer != nil { x = x_.pointer } else { ptr := c.builder.CreateAlloca(c.types.ToLLVM(x.Type()), "") c.builder.CreateStore(x.LLVMValue(), ptr) x = c.NewValue(ptr, types.NewPointer(x.Type())) } } base = x.LLVMValue() length = llvm.ConstInt(c.llvmtypes.inttype, uint64(typ.Len()), false) goto arrayrange case *types.Slice: slicevalue := x.LLVMValue() base = c.builder.CreateExtractValue(slicevalue, 0, "") length = c.builder.CreateExtractValue(slicevalue, 1, "") goto arrayrange } panic("unreachable") maprange: { currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) nextptrphi := c.builder.CreatePHI(c.target.IntPtrType(), "next") nextptr, pk, pv := c.mapNext(x.(*LLVMValue), nextptrphi) notnull := c.builder.CreateIsNotNull(nextptr, "") c.builder.CreateCondBr(notnull, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { keyval := c.builder.CreateLoad(pk, "") c.builder.CreateStore(keyval, keyPtr) } if !valuePtr.IsNil() { valval := c.builder.CreateLoad(pv, "") c.builder.CreateStore(valval, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) c.builder.CreateBr(condBlock) nextptrphi.AddIncoming([]llvm.Value{llvm.ConstNull(c.target.IntPtrType()), nextptr}, []llvm.BasicBlock{currBlock, postBlock}) return } stringrange: { zero := llvm.ConstNull(c.types.inttype) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(c.types.inttype, "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) consumed, value := c.stringNext(x.LLVMValue(), index) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { c.builder.CreateStore(value, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, consumed, "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } arrayrange: { zero := llvm.ConstNull(c.types.inttype) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(c.types.inttype, "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { var indices []llvm.Value if isarray { indices = []llvm.Value{zero, index} } else { indices = []llvm.Value{index} } elementptr := c.builder.CreateGEP(base, indices, "") element := c.builder.CreateLoad(elementptr, "") c.builder.CreateStore(element, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, llvm.ConstInt(c.types.inttype, 1, false), "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } chanrange: { c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) value, received := x.(*LLVMValue).chanRecv(true) c.builder.CreateCondBr(received.LLVMValue(), loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { c.builder.CreateStore(value.LLVMValue(), keyPtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) c.builder.CreateBr(condBlock) return } }
// 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) }
func (c *compiler) VisitRangeStmt(stmt *ast.RangeStmt) { currBlock := c.builder.GetInsertBlock() doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done") doneBlock.MoveAfter(currBlock) postBlock := llvm.InsertBasicBlock(doneBlock, "post") loopBlock := llvm.InsertBasicBlock(postBlock, "loop") condBlock := llvm.InsertBasicBlock(loopBlock, "cond") defer c.builder.SetInsertPointAtEnd(doneBlock) // Evaluate range expression first. x := c.VisitExpr(stmt.X) // If it's a pointer type, we'll first check that it's non-nil. typ := types.Underlying(x.Type()) if _, ok := typ.(*types.Pointer); ok { ifBlock := llvm.InsertBasicBlock(doneBlock, "if") isnotnull := c.builder.CreateIsNotNull(x.LLVMValue(), "") c.builder.CreateCondBr(isnotnull, ifBlock, doneBlock) c.builder.SetInsertPointAtEnd(ifBlock) } // Is it a new var definition? Then allocate some memory on the stack. var keyType, valueType types.Type var keyPtr, valuePtr llvm.Value if stmt.Tok == token.DEFINE { if key := stmt.Key.(*ast.Ident); key.Name != "_" { keyType = key.Obj.Type.(types.Type) keyPtr = c.builder.CreateAlloca(c.types.ToLLVM(keyType), "") key.Obj.Data = c.NewLLVMValue(keyPtr, &types.Pointer{Base: keyType}).makePointee() } if stmt.Value != nil { if value := stmt.Value.(*ast.Ident); value.Name != "_" { valueType = value.Obj.Type.(types.Type) valuePtr = c.builder.CreateAlloca(c.types.ToLLVM(valueType), "") value.Obj.Data = c.NewLLVMValue(valuePtr, &types.Pointer{Base: valueType}).makePointee() } } } c.breakblocks = append(c.breakblocks, doneBlock) c.continueblocks = append(c.continueblocks, postBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] c.continueblocks = c.continueblocks[:len(c.continueblocks)-1] }() isarray := false var base, length llvm.Value _, isptr := typ.(*types.Pointer) if isptr { typ = typ.(*types.Pointer).Base } switch typ := types.Underlying(typ).(type) { case *types.Map: goto maprange case *types.Name: stringvalue := x.LLVMValue() length = c.builder.CreateExtractValue(stringvalue, 1, "") goto stringrange case *types.Array: isarray = true x := x if !isptr { if x_, ok := x.(*LLVMValue); ok && x_.pointer != nil { x = x_.pointer } else { // TODO load value onto stack for indexing? } } base = x.LLVMValue() length = llvm.ConstInt(llvm.Int32Type(), typ.Len, false) goto arrayrange case *types.Slice: slicevalue := x.LLVMValue() base = c.builder.CreateExtractValue(slicevalue, 0, "") length = c.builder.CreateExtractValue(slicevalue, 1, "") goto arrayrange } maprange: { currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) nextptrphi := c.builder.CreatePHI(c.target.IntPtrType(), "next") nextptr, pk, pv := c.mapNext(x.(*LLVMValue), nextptrphi) notnull := c.builder.CreateIsNotNull(nextptr, "") c.builder.CreateCondBr(notnull, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { keyval := c.builder.CreateLoad(pk, "") c.builder.CreateStore(keyval, keyPtr) } if !valuePtr.IsNil() { valval := c.builder.CreateLoad(pv, "") c.builder.CreateStore(valval, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) c.builder.CreateBr(condBlock) nextptrphi.AddIncoming([]llvm.Value{llvm.ConstNull(c.target.IntPtrType()), nextptr}, []llvm.BasicBlock{currBlock, postBlock}) return } stringrange: { zero := llvm.ConstNull(llvm.Int32Type()) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(llvm.Int32Type(), "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) consumed, value := c.stringNext(x.LLVMValue(), index) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { c.builder.CreateStore(value, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, consumed, "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } arrayrange: { zero := llvm.ConstNull(llvm.Int32Type()) currBlock = c.builder.GetInsertBlock() c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) index := c.builder.CreatePHI(llvm.Int32Type(), "index") lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "") c.builder.CreateCondBr(lessthan, loopBlock, doneBlock) c.builder.SetInsertPointAtEnd(loopBlock) if !keyPtr.IsNil() { c.builder.CreateStore(index, keyPtr) } if !valuePtr.IsNil() { var indices []llvm.Value if isarray { indices = []llvm.Value{zero, index} } else { indices = []llvm.Value{index} } elementptr := c.builder.CreateGEP(base, indices, "") element := c.builder.CreateLoad(elementptr, "") c.builder.CreateStore(element, valuePtr) } c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) c.builder.SetInsertPointAtEnd(postBlock) newindex := c.builder.CreateAdd(index, llvm.ConstInt(llvm.Int32Type(), 1, false), "") c.builder.CreateBr(condBlock) index.AddIncoming([]llvm.Value{zero, newindex}, []llvm.BasicBlock{currBlock, postBlock}) return } }