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) } }
// maybeImplicitBranch creates a branch from the current position to the // specified basic block, if and only if the current basic block's last // instruction is not a terminator. // // If dest is nil, then branch to the next basic block, if any. func (c *compiler) maybeImplicitBranch(dest llvm.BasicBlock) { currBlock := c.builder.GetInsertBlock() if in := currBlock.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() { if dest.IsNil() { dest = llvm.NextBasicBlock(currBlock) if !dest.IsNil() { c.builder.CreateBr(dest) } } else { c.builder.CreateBr(dest) } } }
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()) } }
// 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) }