// buildFunction takes a function Value, a list of parameters, and a body, // and generates code for the function. func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.Tuple, body *ast.BlockStmt, isvariadic bool) { if currblock := c.builder.GetInsertBlock(); !currblock.IsNil() { defer c.builder.SetInsertPointAtEnd(currblock) } llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0}) entry := llvm.AddBasicBlock(llvm_fn, "entry") c.builder.SetInsertPointAtEnd(entry) // For closures, context is the captured context values. var paramoffset int if context != nil { paramoffset++ // Store the existing values. We're going to temporarily // replace the values with offsets into the context param. oldvalues := make([]*LLVMValue, context.Len()) for i := range oldvalues { v := context.At(i) oldvalues[i] = c.objectdata[v].Value } defer func() { for i := range oldvalues { v := context.At(i) c.objectdata[v].Value = oldvalues[i] } }() // The context parameter is a pointer to a struct // whose elements are pointers to captured values. arg0 := llvm_fn.Param(0) for i := range oldvalues { v := context.At(i) argptr := c.builder.CreateStructGEP(arg0, i, "") argptr = c.builder.CreateLoad(argptr, "") ptrtyp := oldvalues[i].pointer.Type() newvalue := c.NewValue(argptr, ptrtyp) c.objectdata[v].Value = newvalue.makePointee() } } // Bind receiver, arguments and return values to their // identifiers/objects. We'll store each parameter on the stack so // they're addressable. nparams := int(params.Len()) for i := 0; i < nparams; i++ { v := params.At(i) name := v.Name() if name != "" { value := llvm_fn.Param(i + paramoffset) typ := v.Type() if isvariadic && i == nparams-1 { typ = types.NewSlice(typ) } stackvalue := c.builder.CreateAlloca(c.types.ToLLVM(typ), name) c.builder.CreateStore(value, stackvalue) ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ)) stackvar := ptrvalue.makePointee() stackvar.stack = f c.objectdata[v].Value = stackvar } } funcstate := &function{LLVMValue: f, results: results} c.functions.push(funcstate) hasdefer := hasDefer(funcstate, body) // Allocate space on the stack for named results. results.ForEach(func(v *types.Var) { name := v.Name() allocstack := name != "" if !allocstack && hasdefer { c.objectdata[v] = &ObjectData{} allocstack = true } if allocstack { typ := v.Type() llvmtyp := c.types.ToLLVM(typ) stackptr := c.builder.CreateAlloca(llvmtyp, name) c.builder.CreateStore(llvm.ConstNull(llvmtyp), stackptr) ptrvalue := c.NewValue(stackptr, types.NewPointer(typ)) stackvar := ptrvalue.makePointee() stackvar.stack = f c.objectdata[v].Value = stackvar } }) // Create the function body. if hasdefer { c.makeDeferBlock(funcstate, body) } c.VisitBlockStmt(body, false) c.functions.pop() // If the last instruction in the function is not a terminator, then // we either have unreachable code or a missing optional return statement // (the latter case is allowable only for functions without results). // // Use GetInsertBlock rather than LastBasicBlock, since the // last basic block might actually be a "defer" block. last := c.builder.GetInsertBlock() if in := last.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() { c.builder.SetInsertPointAtEnd(last) if results.Len() == 0 { if funcstate.deferblock.IsNil() { c.builder.CreateRetVoid() } else { c.builder.CreateBr(funcstate.deferblock) } } else { c.builder.CreateUnreachable() } } }