func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) { currBlock := c.builder.GetInsertBlock() resumeBlock := llvm.AddBasicBlock(currBlock.Parent(), "endif") resumeBlock.MoveAfter(currBlock) defer c.builder.SetInsertPointAtEnd(resumeBlock) var ifBlock, elseBlock llvm.BasicBlock if stmt.Else != nil { elseBlock = llvm.InsertBasicBlock(resumeBlock, "else") ifBlock = llvm.InsertBasicBlock(elseBlock, "if") } else { ifBlock = llvm.InsertBasicBlock(resumeBlock, "if") } if stmt.Else == nil { elseBlock = resumeBlock } if stmt.Init != nil { c.VisitStmt(stmt.Init) } cond_val := c.VisitExpr(stmt.Cond) c.builder.CreateCondBr(cond_val.LLVMValue(), ifBlock, elseBlock) c.builder.SetInsertPointAtEnd(ifBlock) c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(resumeBlock) if stmt.Else != nil { c.builder.SetInsertPointAtEnd(elseBlock) c.VisitStmt(stmt.Else) c.maybeImplicitBranch(resumeBlock) } }
// convertI2V converts an interface to a value. func (v *LLVMValue) convertI2V(typ types.Type) (result, success Value) { builder := v.compiler.builder predicate := v.interfaceTypeEquals(typ).LLVMValue() // If result is zero, then we've got a match. end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") builder.CreateCondBr(predicate, match, nonmatch) builder.SetInsertPointAtEnd(match) matchResultValue := v.loadI2V(typ).LLVMValue() builder.CreateBr(end) builder.SetInsertPointAtEnd(nonmatch) nonmatchResultValue := llvm.ConstNull(matchResultValue.Type()) builder.CreateBr(end) builder.SetInsertPointAtEnd(end) successValue := builder.CreatePHI(llvm.Int1Type(), "") resultValue := builder.CreatePHI(matchResultValue.Type(), "") successValues := []llvm.Value{llvm.ConstAllOnes(llvm.Int1Type()), llvm.ConstNull(llvm.Int1Type())} successBlocks := []llvm.BasicBlock{match, nonmatch} successValue.AddIncoming(successValues, successBlocks) success = v.compiler.NewValue(successValue, types.Typ[types.Bool]) resultValues := []llvm.Value{matchResultValue, nonmatchResultValue} resultBlocks := []llvm.BasicBlock{match, nonmatch} resultValue.AddIncoming(resultValues, resultBlocks) result = v.compiler.NewValue(resultValue, typ) return result, success }
func (lhs *LLVMValue) compareI2V(rhs *LLVMValue) Value { c := lhs.compiler predicate := lhs.interfaceTypeEquals(rhs.typ).LLVMValue() end := llvm.InsertBasicBlock(c.builder.GetInsertBlock(), "end") end.MoveAfter(c.builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") c.builder.CreateCondBr(predicate, match, nonmatch) c.builder.SetInsertPointAtEnd(match) lhsValue := lhs.loadI2V(rhs.typ) matchResultValue := lhsValue.BinaryOp(token.EQL, rhs).LLVMValue() c.builder.CreateBr(end) c.builder.SetInsertPointAtEnd(nonmatch) nonmatchResultValue := llvm.ConstNull(llvm.Int1Type()) c.builder.CreateBr(end) c.builder.SetInsertPointAtEnd(end) resultValue := c.builder.CreatePHI(matchResultValue.Type(), "") resultValues := []llvm.Value{matchResultValue, nonmatchResultValue} resultBlocks := []llvm.BasicBlock{match, nonmatch} resultValue.AddIncoming(resultValues, resultBlocks) return c.NewValue(resultValue, types.Typ[types.Bool]) }
func (c *compiler) VisitBlockStmt(stmt *ast.BlockStmt, createNewBlock bool) { // This is a little awkward, but it makes dealing with branching easier. // A free-standing block statement (i.e. one not attached to a control // statement) will splice in a new block. var doneBlock llvm.BasicBlock if createNewBlock { currBlock := c.builder.GetInsertBlock() doneBlock = llvm.InsertBasicBlock(currBlock, "") doneBlock.MoveAfter(currBlock) newBlock := llvm.InsertBasicBlock(doneBlock, "") c.builder.CreateBr(newBlock) c.builder.SetInsertPointAtEnd(newBlock) } for _, stmt := range stmt.List { c.VisitStmt(stmt) if _, ok := stmt.(*ast.BranchStmt); ok { // Ignore anything after a branch statement. break } } if createNewBlock { c.maybeImplicitBranch(doneBlock) c.builder.SetInsertPointAtEnd(doneBlock) } }
func (c *compiler) VisitBlockStmt(stmt *ast.BlockStmt, createNewBlock bool) { // This is a little awkward, but it makes dealing with branching easier. // A free-standing block statement (i.e. one not attached to a control // statement) will splice in a new block. var doneBlock llvm.BasicBlock if createNewBlock { currBlock := c.builder.GetInsertBlock() doneBlock = llvm.InsertBasicBlock(currBlock, "") doneBlock.MoveAfter(currBlock) newBlock := llvm.InsertBasicBlock(doneBlock, "") c.builder.CreateBr(newBlock) c.builder.SetInsertPointAtEnd(newBlock) } // Visit each statement in the block. When we have a terminator, // ignore everything until we get to a labeled statement. for _, stmt := range stmt.List { currBlock := c.builder.GetInsertBlock() in := currBlock.LastInstruction() if in.IsNil() || in.IsATerminatorInst().IsNil() { c.VisitStmt(stmt) } else if _, ok := stmt.(*ast.LabeledStmt); ok { // FIXME we might end up with a labeled statement // with no predecessors, due to dead code elimination. c.VisitStmt(stmt) } } if createNewBlock { c.maybeImplicitBranch(doneBlock) c.builder.SetInsertPointAtEnd(doneBlock) } }
// Binary logical operators are handled specially, outside of the Value // type, because of the need to perform lazy evaluation. // // Binary logical operators are implemented using a Phi node, which takes // on the appropriate value depending on which basic blocks branch to it. func (c *compiler) compileLogicalOp(op token.Token, lhs Value, rhsFunc func() Value) Value { lhsBlock := c.builder.GetInsertBlock() resultBlock := llvm.AddBasicBlock(lhsBlock.Parent(), "") resultBlock.MoveAfter(lhsBlock) rhsBlock := llvm.InsertBasicBlock(resultBlock, "") falseBlock := llvm.InsertBasicBlock(resultBlock, "") if op == token.LOR { c.builder.CreateCondBr(lhs.LLVMValue(), resultBlock, rhsBlock) } else { c.builder.CreateCondBr(lhs.LLVMValue(), rhsBlock, falseBlock) } c.builder.SetInsertPointAtEnd(rhsBlock) rhs := rhsFunc() rhsBlock = c.builder.GetInsertBlock() // rhsFunc may create blocks c.builder.CreateCondBr(rhs.LLVMValue(), resultBlock, falseBlock) c.builder.SetInsertPointAtEnd(falseBlock) c.builder.CreateBr(resultBlock) c.builder.SetInsertPointAtEnd(resultBlock) result := c.builder.CreatePHI(llvm.Int1Type(), "") trueValue := llvm.ConstAllOnes(llvm.Int1Type()) falseValue := llvm.ConstNull(llvm.Int1Type()) var values []llvm.Value var blocks []llvm.BasicBlock if op == token.LOR { values = []llvm.Value{trueValue, trueValue, falseValue} blocks = []llvm.BasicBlock{lhsBlock, rhsBlock, falseBlock} } else { values = []llvm.Value{trueValue, falseValue} blocks = []llvm.BasicBlock{rhsBlock, falseBlock} } result.AddIncoming(values, blocks) return c.NewLLVMValue(result, types.Bool) }
func (c *compiler) VisitForStmt(stmt *ast.ForStmt) { currBlock := c.builder.GetInsertBlock() doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done") doneBlock.MoveAfter(currBlock) loopBlock := llvm.InsertBasicBlock(doneBlock, "loop") defer c.builder.SetInsertPointAtEnd(doneBlock) condBlock := loopBlock if stmt.Cond != nil { condBlock = llvm.InsertBasicBlock(loopBlock, "cond") } postBlock := condBlock if stmt.Post != nil { postBlock = llvm.InsertBasicBlock(doneBlock, "post") } 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] }() // Is there an initializer? Create a new scope and visit the statement. if stmt.Init != nil { c.VisitStmt(stmt.Init) } // Start the loop. if stmt.Cond != nil { c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) condVal := c.VisitExpr(stmt.Cond) c.builder.CreateCondBr(condVal.LLVMValue(), loopBlock, doneBlock) } else { c.builder.CreateBr(loopBlock) } // Post. if stmt.Post != nil { c.builder.SetInsertPointAtEnd(postBlock) c.VisitStmt(stmt.Post) c.builder.CreateBr(condBlock) } // Loop body. c.builder.SetInsertPointAtEnd(loopBlock) c.VisitBlockStmt(stmt.Body, false) c.maybeImplicitBranch(postBlock) }
func (v *LLVMValue) mustConvertI2V(typ types.Type) Value { result, ok := v.convertI2V(typ) c, builder := v.compiler, v.compiler.builder end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) failed := llvm.InsertBasicBlock(end, "failed") builder.CreateCondBr(ok.LLVMValue(), end, failed) builder.SetInsertPointAtEnd(failed) s := fmt.Sprintf("convertI2V(%s, %s) failed", v.typ, typ) c.visitPanic(c.NewConstValue(exact.MakeString(s), types.Typ[types.String])) builder.SetInsertPointAtEnd(end) return result }
func (v *LLVMValue) mustConvertI2I(iface *types.Interface) Value { result, ok := v.convertI2I(iface) c, builder := v.compiler, v.compiler.builder end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) failed := llvm.InsertBasicBlock(end, "failed") builder.CreateCondBr(ok.LLVMValue(), end, failed) builder.SetInsertPointAtEnd(failed) s := fmt.Sprintf("convertI2I(%s, %s) failed", v.typ, iface) c.visitPanic(c.NewConstValue(token.STRING, strconv.Quote(s))) builder.SetInsertPointAtEnd(end) return result }
func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) { curr_block := c.builder.GetInsertBlock() resume_block := llvm.AddBasicBlock(curr_block.Parent(), "endif") resume_block.MoveAfter(curr_block) var if_block, else_block llvm.BasicBlock if stmt.Else != nil { else_block = llvm.InsertBasicBlock(resume_block, "else") if_block = llvm.InsertBasicBlock(else_block, "if") } else { if_block = llvm.InsertBasicBlock(resume_block, "if") } if stmt.Else == nil { else_block = resume_block } if stmt.Init != nil { c.PushScope() c.VisitStmt(stmt.Init) defer c.PopScope() } cond_val := c.VisitExpr(stmt.Cond) c.builder.CreateCondBr(cond_val.LLVMValue(), if_block, else_block) c.builder.SetInsertPointAtEnd(if_block) c.VisitBlockStmt(stmt.Body) if in := if_block.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() { c.builder.CreateBr(resume_block) } if stmt.Else != nil { c.builder.SetInsertPointAtEnd(else_block) c.VisitStmt(stmt.Else) if in := else_block.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() { c.builder.CreateBr(resume_block) } } // If there's a block after the "resume" block (i.e. a nested control // statement), then create a branch to it as the last instruction. c.builder.SetInsertPointAtEnd(resume_block) if last := resume_block.Parent().LastBasicBlock(); last != resume_block { c.builder.CreateBr(last) c.builder.SetInsertPointBefore(resume_block.FirstInstruction()) } }
func (c *compiler) VisitForStmt(stmt *ast.ForStmt) { currBlock := c.builder.GetInsertBlock() doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done") doneBlock.MoveAfter(currBlock) loopBlock := llvm.InsertBasicBlock(doneBlock, "loop") defer c.builder.SetInsertPointAtEnd(doneBlock) condBlock := loopBlock if stmt.Cond != nil { condBlock = llvm.InsertBasicBlock(loopBlock, "cond") } postBlock := condBlock if stmt.Post != nil { postBlock = llvm.InsertBasicBlock(doneBlock, "post") } // Is there an initializer? Create a new scope and visit the statement. if stmt.Init != nil { c.PushScope() c.VisitStmt(stmt.Init) defer c.PopScope() } // Start the loop. if stmt.Cond != nil { c.builder.CreateBr(condBlock) c.builder.SetInsertPointAtEnd(condBlock) condVal := c.VisitExpr(stmt.Cond) c.builder.CreateCondBr(condVal.LLVMValue(), loopBlock, doneBlock) } else { c.builder.CreateBr(loopBlock) } // Post. if stmt.Post != nil { c.builder.SetInsertPointAtEnd(postBlock) c.VisitStmt(stmt.Post) c.builder.CreateBr(condBlock) } // Loop body. c.builder.SetInsertPointAtEnd(loopBlock) c.VisitBlockStmt(stmt.Body) c.maybeImplicitBranch(postBlock) }
// convertI2V converts an interface to a value. func (v *LLVMValue) convertI2V(typ types.Type) (result, success Value) { typptrType := llvm.PointerType(llvm.Int8Type(), 0) runtimeType := v.compiler.types.ToRuntime(typ) runtimeType = llvm.ConstBitCast(runtimeType, typptrType) vval := v.LLVMValue() builder := v.compiler.builder ifaceType := builder.CreateExtractValue(vval, 0, "") diff := builder.CreatePtrDiff(runtimeType, ifaceType, "") zero := llvm.ConstNull(diff.Type()) predicate := builder.CreateICmp(llvm.IntEQ, diff, zero, "") // If result is zero, then we've got a match. end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") builder.CreateCondBr(predicate, match, nonmatch) builder.SetInsertPointAtEnd(match) matchResultValue := v.loadI2V(typ).LLVMValue() builder.CreateBr(end) builder.SetInsertPointAtEnd(nonmatch) nonmatchResultValue := llvm.ConstNull(matchResultValue.Type()) builder.CreateBr(end) builder.SetInsertPointAtEnd(end) successValue := builder.CreatePHI(llvm.Int1Type(), "") resultValue := builder.CreatePHI(matchResultValue.Type(), "") successValues := []llvm.Value{llvm.ConstAllOnes(llvm.Int1Type()), llvm.ConstNull(llvm.Int1Type())} successBlocks := []llvm.BasicBlock{match, nonmatch} successValue.AddIncoming(successValues, successBlocks) success = v.compiler.NewLLVMValue(successValue, types.Bool) resultValues := []llvm.Value{matchResultValue, nonmatchResultValue} resultBlocks := []llvm.BasicBlock{match, nonmatch} resultValue.AddIncoming(resultValues, resultBlocks) result = v.compiler.NewLLVMValue(resultValue, typ) return result, success }
func (c *compiler) getBoolString(v llvm.Value) llvm.Value { startBlock := c.builder.GetInsertBlock() resultBlock := llvm.InsertBasicBlock(startBlock, "") resultBlock.MoveAfter(startBlock) falseBlock := llvm.InsertBasicBlock(resultBlock, "") CharPtr := llvm.PointerType(llvm.Int8Type(), 0) falseString := c.builder.CreateGlobalStringPtr("false", "") falseString = c.builder.CreateBitCast(falseString, CharPtr, "") trueString := c.builder.CreateGlobalStringPtr("true", "") trueString = c.builder.CreateBitCast(trueString, CharPtr, "") c.builder.CreateCondBr(v, resultBlock, falseBlock) c.builder.SetInsertPointAtEnd(falseBlock) c.builder.CreateBr(resultBlock) c.builder.SetInsertPointAtEnd(resultBlock) result := c.builder.CreatePHI(CharPtr, "") result.AddIncoming([]llvm.Value{trueString, falseString}, []llvm.BasicBlock{startBlock, falseBlock}) return result }
// convertI2V converts an interface to a value. func (v *LLVMValue) convertI2V(typ types.Type) Value { typptrType := llvm.PointerType(llvm.Int8Type(), 0) runtimeType := v.compiler.types.ToRuntime(typ) runtimeType = llvm.ConstBitCast(runtimeType, typptrType) vptr := v.pointer.LLVMValue() builder := v.compiler.builder ifaceType := builder.CreateLoad(builder.CreateStructGEP(vptr, 1, ""), "") diff := builder.CreatePtrDiff(runtimeType, ifaceType, "") zero := llvm.ConstNull(diff.Type()) predicate := builder.CreateICmp(llvm.IntEQ, diff, zero, "") llvmtype := v.compiler.types.ToLLVM(typ) result := builder.CreateAlloca(llvmtype, "") // If result is zero, then we've got a match. end := llvm.InsertBasicBlock(builder.GetInsertBlock(), "end") end.MoveAfter(builder.GetInsertBlock()) nonmatch := llvm.InsertBasicBlock(end, "nonmatch") match := llvm.InsertBasicBlock(nonmatch, "match") builder.CreateCondBr(predicate, match, nonmatch) builder.SetInsertPointAtEnd(match) value := builder.CreateLoad(builder.CreateStructGEP(vptr, 0, ""), "") value = builder.CreateBitCast(value, result.Type(), "") value = builder.CreateLoad(value, "") builder.CreateStore(value, result) builder.CreateBr(end) // TODO should return {value, ok} builder.SetInsertPointAtEnd(nonmatch) builder.CreateStore(llvm.ConstNull(llvmtype), result) builder.CreateBr(end) //builder.SetInsertPointAtEnd(end) result = builder.CreateLoad(result, "") return v.compiler.NewLLVMValue(result, typ) }
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 } }
// 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) VisitTypeSwitchStmt(stmt *ast.TypeSwitchStmt) { if stmt.Init != nil { c.VisitStmt(stmt.Init) } var assignIdent *ast.Ident var typeAssertExpr *ast.TypeAssertExpr switch stmt := stmt.Assign.(type) { case *ast.AssignStmt: assignIdent = stmt.Lhs[0].(*ast.Ident) typeAssertExpr = stmt.Rhs[0].(*ast.TypeAssertExpr) case *ast.ExprStmt: typeAssertExpr = stmt.X.(*ast.TypeAssertExpr) } if len(stmt.Body.List) == 0 { // No case clauses, so just evaluate the expression. c.VisitExpr(typeAssertExpr.X) return } currBlock := c.builder.GetInsertBlock() endBlock := llvm.AddBasicBlock(currBlock.Parent(), "") endBlock.MoveAfter(currBlock) defer c.builder.SetInsertPointAtEnd(endBlock) // Add a "break" block to the stack. c.breakblocks = append(c.breakblocks, endBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] }() // TODO: investigate the use of a switch instruction // on the type's hash (when we compute type hashes). // Create blocks for each statement. defaultBlock := endBlock var condBlocks []llvm.BasicBlock var stmtBlocks []llvm.BasicBlock for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) if caseClause.List == nil { defaultBlock = llvm.InsertBasicBlock(endBlock, "") } else { condBlock := llvm.InsertBasicBlock(endBlock, "") stmtBlock := llvm.InsertBasicBlock(endBlock, "") condBlocks = append(condBlocks, condBlock) stmtBlocks = append(stmtBlocks, stmtBlock) } } stmtBlocks = append(stmtBlocks, defaultBlock) // Evaluate the expression, then jump to the first condition block. iface := c.VisitExpr(typeAssertExpr.X).(*LLVMValue) typptr := c.builder.CreateExtractValue(iface.LLVMValue(), 0, "") typptr = c.builder.CreatePtrToInt(typptr, c.target.IntPtrType(), "") if len(stmt.Body.List) == 1 && defaultBlock != endBlock { c.builder.CreateBr(defaultBlock) } else { c.builder.CreateBr(condBlocks[0]) } i := 0 for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) if caseClause.List != nil { c.builder.SetInsertPointAtEnd(condBlocks[i]) stmtBlock := stmtBlocks[i] nextCondBlock := defaultBlock if i+1 < len(condBlocks) { nextCondBlock = condBlocks[i+1] } // TODO handle multiple types // TODO use runtime type equality function if len(caseClause.List) > 1 { panic("unimplemented") } var cond llvm.Value if isNilIdent(caseClause.List[0]) { cond = c.builder.CreateIsNull(typptr, "") } else { typ := c.types.expr[caseClause.List[0]] check := c.types.ToRuntime(typ) check = c.builder.CreatePtrToInt(check, c.target.IntPtrType(), "") cond = c.builder.CreateICmp(llvm.IntEQ, typptr, check, "") } c.builder.CreateCondBr(cond, stmtBlock, nextCondBlock) i++ } } i = 0 for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) var block llvm.BasicBlock var typ types.Type if caseClause.List != nil { block = stmtBlocks[i] if len(caseClause.List) == 1 { typ = c.types.expr[caseClause.List[0]] } i++ } else { block = defaultBlock } c.builder.SetInsertPointAtEnd(block) if assignIdent != nil { if len(caseClause.List) == 1 && !isNilIdent(caseClause.List[0]) { assignIdent.Obj.Data = iface.loadI2V(typ) } else { assignIdent.Obj.Data = iface } } for _, stmt := range caseClause.Body { c.VisitStmt(stmt) } c.maybeImplicitBranch(endBlock) } }
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) VisitSwitchStmt(stmt *ast.SwitchStmt) { if stmt.Init != nil { c.VisitStmt(stmt.Init) } var tag Value if stmt.Tag != nil { tag = c.VisitExpr(stmt.Tag) } else { True := types.Universe.Lookup("true") tag = c.Resolve(True) } if len(stmt.Body.List) == 0 { return } // makeValueFunc takes an expression, evaluates it, and returns // a Value representing its equality comparison with the tag. makeValueFunc := func(expr ast.Expr) func() Value { return func() Value { return c.VisitExpr(expr).BinaryOp(token.EQL, tag) } } // Create a BasicBlock for each case clause and each associated // statement body. Each case clause will branch to either its // statement body (success) or to the next case (failure), or the // end block if there are no remaining cases. startBlock := c.builder.GetInsertBlock() endBlock := llvm.AddBasicBlock(startBlock.Parent(), "end") endBlock.MoveAfter(startBlock) defer c.builder.SetInsertPointAtEnd(endBlock) // Add a "break" block to the stack. c.breakblocks = append(c.breakblocks, endBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] }() caseBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) stmtBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) for _ = range stmt.Body.List { caseBlocks = append(caseBlocks, llvm.InsertBasicBlock(endBlock, "")) } for _ = range stmt.Body.List { stmtBlocks = append(stmtBlocks, llvm.InsertBasicBlock(endBlock, "")) } c.builder.CreateBr(caseBlocks[0]) for i, stmt := range stmt.Body.List { c.builder.SetInsertPointAtEnd(caseBlocks[i]) stmtBlock := stmtBlocks[i] nextBlock := endBlock if i+1 < len(caseBlocks) { nextBlock = caseBlocks[i+1] } clause := stmt.(*ast.CaseClause) if clause.List != nil { value := c.VisitExpr(clause.List[0]) result := value.BinaryOp(token.EQL, tag) for _, expr := range clause.List[1:] { rhsResultFunc := makeValueFunc(expr) result = c.compileLogicalOp(token.LOR, result, rhsResultFunc) } c.builder.CreateCondBr(result.LLVMValue(), stmtBlock, nextBlock) } else { // default case c.builder.CreateBr(stmtBlock) } c.builder.SetInsertPointAtEnd(stmtBlock) branchBlock := endBlock for _, stmt := range clause.Body { if br, isbr := stmt.(*ast.BranchStmt); isbr { if br.Tok == token.FALLTHROUGH { if i+1 < len(stmtBlocks) { branchBlock = stmtBlocks[i+1] } } else { c.VisitStmt(stmt) } // Ignore anything after a branch statement. break } else { c.VisitStmt(stmt) } } c.maybeImplicitBranch(branchBlock) } }
func (c *compiler) VisitTypeSwitchStmt(stmt *ast.TypeSwitchStmt) { if stmt.Init != nil { c.VisitStmt(stmt.Init) } var assignIdent *ast.Ident var typeAssertExpr *ast.TypeAssertExpr switch stmt := stmt.Assign.(type) { case *ast.AssignStmt: assignIdent = stmt.Lhs[0].(*ast.Ident) typeAssertExpr = stmt.Rhs[0].(*ast.TypeAssertExpr) case *ast.ExprStmt: typeAssertExpr = stmt.X.(*ast.TypeAssertExpr) } if len(stmt.Body.List) == 0 { // No case clauses, so just evaluate the expression. c.VisitExpr(typeAssertExpr.X) return } currBlock := c.builder.GetInsertBlock() endBlock := llvm.AddBasicBlock(currBlock.Parent(), "") endBlock.MoveAfter(currBlock) defer c.builder.SetInsertPointAtEnd(endBlock) // Add a "break" block to the stack. c.breakblocks = append(c.breakblocks, endBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] }() // TODO: investigate the use of a switch instruction // on the type's hash (when we compute type hashes). // Create blocks for each statement. defaultBlock := endBlock var condBlocks []llvm.BasicBlock var stmtBlocks []llvm.BasicBlock for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) if caseClause.List == nil { defaultBlock = llvm.InsertBasicBlock(endBlock, "") } else { condBlock := llvm.InsertBasicBlock(endBlock, "") stmtBlock := llvm.InsertBasicBlock(endBlock, "") condBlocks = append(condBlocks, condBlock) stmtBlocks = append(stmtBlocks, stmtBlock) } } stmtBlocks = append(stmtBlocks, defaultBlock) // Evaluate the expression, then jump to the first condition block. iface := c.VisitExpr(typeAssertExpr.X).(*LLVMValue) if len(stmt.Body.List) == 1 && defaultBlock != endBlock { c.builder.CreateBr(defaultBlock) } else { c.builder.CreateBr(condBlocks[0]) } i := 0 for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) if caseClause.List != nil { c.builder.SetInsertPointAtEnd(condBlocks[i]) stmtBlock := stmtBlocks[i] nextCondBlock := defaultBlock if i+1 < len(condBlocks) { nextCondBlock = condBlocks[i+1] } caseCond := func(j int) Value { if c.isNilIdent(caseClause.List[j]) { iface := iface.LLVMValue() ifacetyp := c.builder.CreateExtractValue(iface, 0, "") isnil := c.builder.CreateIsNull(ifacetyp, "") return c.NewValue(isnil, types.Typ[types.Bool]) } typ := c.typeinfo.Types[caseClause.List[j]] switch typ := typ.Underlying().(type) { case *types.Interface: _, ok := iface.convertI2I(typ) return ok } return iface.interfaceTypeEquals(typ) } cond := caseCond(0) for j := 1; j < len(caseClause.List); j++ { f := func() Value { return caseCond(j) } cond = c.compileLogicalOp(token.LOR, cond, f).(*LLVMValue) } c.builder.CreateCondBr(cond.LLVMValue(), stmtBlock, nextCondBlock) i++ } } i = 0 for _, stmt := range stmt.Body.List { caseClause := stmt.(*ast.CaseClause) var block llvm.BasicBlock var typ types.Type if caseClause.List != nil { block = stmtBlocks[i] if len(caseClause.List) == 1 { typ = c.typeinfo.Types[caseClause.List[0]] } i++ } else { block = defaultBlock } c.builder.SetInsertPointAtEnd(block) if assignIdent != nil { obj := c.typeinfo.Implicits[caseClause] if len(caseClause.List) == 1 && !c.isNilIdent(caseClause.List[0]) { switch utyp := typ.Underlying().(type) { case *types.Interface: // FIXME Use value from convertI2I in the case // clause condition test. c.objectdata[obj].Value, _ = iface.convertI2I(utyp) default: c.objectdata[obj].Value = iface.loadI2V(typ) } } else { c.objectdata[obj].Value = iface } } for _, stmt := range caseClause.Body { c.VisitStmt(stmt) } c.maybeImplicitBranch(endBlock) } }
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 } }
func (c *compiler) VisitSwitchStmt(stmt *ast.SwitchStmt) { if stmt.Init != nil { c.VisitStmt(stmt.Init) } var tag Value if stmt.Tag != nil { tag = c.VisitExpr(stmt.Tag) } else { tag = c.NewConstValue(exact.MakeBool(true), types.Typ[types.Bool]) } if len(stmt.Body.List) == 0 { return } // Convert untyped constant clauses. for _, clause := range stmt.Body.List { for _, expr := range clause.(*ast.CaseClause).List { if typ := c.typeinfo.Types[expr]; isUntyped(typ) { c.typeinfo.Types[expr] = tag.Type() } } } // makeValueFunc takes an expression, evaluates it, and returns // a Value representing its equality comparison with the tag. makeValueFunc := func(expr ast.Expr) func() Value { return func() Value { return c.VisitExpr(expr).BinaryOp(token.EQL, tag) } } // Create a BasicBlock for each case clause and each associated // statement body. Each case clause will branch to either its // statement body (success) or to the next case (failure), or the // end block if there are no remaining cases. startBlock := c.builder.GetInsertBlock() endBlock := llvm.AddBasicBlock(startBlock.Parent(), "end") endBlock.MoveAfter(startBlock) defer c.builder.SetInsertPointAtEnd(endBlock) if c.lastlabel != nil { labelData := c.labelData(c.lastlabel) labelData.Break = endBlock c.lastlabel = nil } // Add a "break" block to the stack. c.breakblocks = append(c.breakblocks, endBlock) defer func() { c.breakblocks = c.breakblocks[:len(c.breakblocks)-1] }() caseBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) stmtBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) for _ = range stmt.Body.List { caseBlocks = append(caseBlocks, llvm.InsertBasicBlock(endBlock, "")) } for _ = range stmt.Body.List { stmtBlocks = append(stmtBlocks, llvm.InsertBasicBlock(endBlock, "")) } // Move the "default" block to the end, if there is one. caseclauses := make([]*ast.CaseClause, 0, len(stmt.Body.List)) var defaultclause *ast.CaseClause for _, stmt := range stmt.Body.List { clause := stmt.(*ast.CaseClause) if clause.List == nil { defaultclause = clause } else { caseclauses = append(caseclauses, clause) } } if defaultclause != nil { caseclauses = append(caseclauses, defaultclause) } c.builder.CreateBr(caseBlocks[0]) for i, clause := range caseclauses { c.builder.SetInsertPointAtEnd(caseBlocks[i]) stmtBlock := stmtBlocks[i] nextBlock := endBlock if i+1 < len(caseBlocks) { nextBlock = caseBlocks[i+1] } if clause.List != nil { value := c.VisitExpr(clause.List[0]) result := value.BinaryOp(token.EQL, tag) for _, expr := range clause.List[1:] { rhsResultFunc := makeValueFunc(expr) result = c.compileLogicalOp(token.LOR, result, rhsResultFunc) } c.builder.CreateCondBr(result.LLVMValue(), stmtBlock, nextBlock) } else { // default case c.builder.CreateBr(stmtBlock) } c.builder.SetInsertPointAtEnd(stmtBlock) branchBlock := endBlock for _, stmt := range clause.Body { if br, isbr := stmt.(*ast.BranchStmt); isbr { if br.Tok == token.FALLTHROUGH { if i+1 < len(stmtBlocks) { branchBlock = stmtBlocks[i+1] } } else { c.VisitStmt(stmt) } // Ignore anything after a branch statement. break } else { c.VisitStmt(stmt) } } c.maybeImplicitBranch(branchBlock) } }
func (c *compiler) VisitSwitchStmt(stmt *ast.SwitchStmt) { if stmt.Init != nil { c.PushScope() defer c.PopScope() c.VisitStmt(stmt.Init) } var tag Value if stmt.Tag != nil { tag = c.VisitExpr(stmt.Tag) } else { True := types.Universe.Lookup("true") tag = c.Resolve(True) } if len(stmt.Body.List) == 0 { return } // Create a BasicBlock for each case clause and each associated // statement body. Each case clause will branch to either its // statement body (success) or to the next case (failure), or the // end block if there are no remaining cases. startBlock := c.builder.GetInsertBlock() endBlock := llvm.AddBasicBlock(startBlock.Parent(), "end") endBlock.MoveAfter(startBlock) defer c.builder.SetInsertPointAtEnd(endBlock) caseBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) stmtBlocks := make([]llvm.BasicBlock, 0, len(stmt.Body.List)) for _ = range stmt.Body.List { caseBlocks = append(caseBlocks, llvm.InsertBasicBlock(endBlock, "")) } for _ = range stmt.Body.List { stmtBlocks = append(stmtBlocks, llvm.InsertBasicBlock(endBlock, "")) } c.builder.CreateBr(caseBlocks[0]) for i, stmt := range stmt.Body.List { c.builder.SetInsertPointAtEnd(caseBlocks[i]) stmtBlock := stmtBlocks[i] nextBlock := endBlock if i+1 < len(caseBlocks) { nextBlock = caseBlocks[i+1] } clause := stmt.(*ast.CaseClause) if clause.List != nil { value := c.VisitExpr(clause.List[0]) result := value.BinaryOp(token.EQL, tag) for _, expr := range clause.List[1:] { value = c.compileLogicalOp(token.LOR, result, expr) } c.builder.CreateCondBr(result.LLVMValue(), stmtBlock, nextBlock) } else { // default case c.builder.CreateBr(stmtBlock) } c.builder.SetInsertPointAtEnd(stmtBlock) branchBlock := endBlock for _, stmt := range clause.Body { if br, isbr := stmt.(*ast.BranchStmt); isbr && br.Tok == token.FALLTHROUGH { if i+1 < len(stmtBlocks) { branchBlock = stmtBlocks[i+1] } } else { c.VisitStmt(stmt) } } c.maybeImplicitBranch(branchBlock) } }
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]) } }