コード例 #1
0
ファイル: stmt.go プロジェクト: rjammala/emgo
func (cdd *CDD) ReturnStmt(w *bytes.Buffer, s *ast.ReturnStmt, resultT string, tup *types.Tuple) (end bool) {
	switch len(s.Results) {
	case 0:
		if resultT == "void" {
			w.WriteString("return;\n")
		} else {
			w.WriteString("goto end;\n")
			end = true
		}

	case 1:
		w.WriteString("return ")
		cdd.Expr(w, s.Results[0], tup.At(0).Type())
		w.WriteString(";\n")

	default:
		w.WriteString("return (" + resultT + "){")
		for i, e := range s.Results {
			if i > 0 {
				w.WriteString(", ")
			}
			cdd.Expr(w, e, tup.At(i).Type())
		}
		w.WriteString("};\n")
	}
	return
}
コード例 #2
0
ファイル: export.go プロジェクト: hackrole/daily-program
func (p *exporter) tuple(t *types.Tuple) {
	n := t.Len()
	p.int(n)
	for i := 0; i < n; i++ {
		p.param(t.At(i))
	}
}
コード例 #3
0
ファイル: utils.go プロジェクト: hajimehoshi/gopherjs
func (c *funcContext) typeArray(t *types.Tuple) string {
	s := make([]string, t.Len())
	for i := range s {
		s[i] = c.typeName(t.At(i).Type())
	}
	return "[" + strings.Join(s, ", ") + "]"
}
コード例 #4
0
ファイル: typemap.go プロジェクト: minux/llgo
func (tm *TypeMap) rtypeSlice(t *types.Tuple) llvm.Value {
	rtypes := make([]llvm.Value, t.Len())
	for i := range rtypes {
		rtypes[i] = tm.ToRuntime(t.At(i).Type())
	}
	slicetyp := tm.runtime.funcType.llvm.StructElementTypes()[2]
	return tm.makeSlice(rtypes, slicetyp)
}
コード例 #5
0
ファイル: map.go プロジェクト: Karthikvb/15640_projects
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
	// See go/types.identicalTypes for rationale.
	n := tuple.Len()
	var hash uint32 = 9137 + 2*uint32(n)
	for i := 0; i < n; i++ {
		hash += 3 * h.Hash(tuple.At(i).Type())
	}
	return hash
}
コード例 #6
0
ファイル: goapi.go プロジェクト: bryanxu/go-zh
func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) {
	buf.WriteByte('(')
	for i, n := 0, t.Len(); i < n; i++ {
		if i > 0 {
			buf.WriteString(", ")
		}
		typ := t.At(i).Type()
		if variadic && i+1 == n {
			buf.WriteString("...")
			typ = typ.(*types.Slice).Elem()
		}
		w.writeType(buf, typ)
	}
	buf.WriteByte(')')
}
コード例 #7
0
ファイル: main.go プロジェクト: kisielk/uses
func checkTypes(args *types.Tuple, types []string) (any, all bool) {
	matched := make([]bool, len(types))
	for i := 0; i < args.Len(); i++ {
		for k, toCheck := range types {
			if args.At(i).Type().String() == toCheck {
				matched[k] = true
				any = true
			}
		}
	}

	for _, b := range matched {
		if !b {
			return any, false
		}
	}

	return any, true
}
コード例 #8
0
ファイル: writetype.go プロジェクト: Karthikvb/15640_projects
func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
	p.print("(")
	for i, n := 0, tup.Len(); i < n; i++ {
		if i > 0 {
			p.print(", ")
		}
		v := tup.At(i)
		if name := v.Name(); name != "" {
			p.print(name)
			p.print(" ")
		}
		typ := v.Type()
		if variadic && i == n-1 {
			p.print("...")
			typ = typ.(*types.Slice).Elem()
		}
		p.writeTypeInternal(this, typ, visited)
	}
	p.print(")")
}
コード例 #9
0
ファイル: type.go プロジェクト: rjammala/emgo
func (cdd *CDD) results(tup *types.Tuple) (res results) {
	if tup == nil {
		res.typ = "void"
		return
	}

	n := tup.Len()

	res.names = make([]string, n)
	for i := 0; i < n; i++ {
		name := tup.At(i).Name()
		switch name {
		case "":
			name = "_" + strconv.Itoa(n)

		case "_":
			res.hasNames = true

		default:
			name += "$"
			res.hasNames = true
		}
		res.names[i] = name
	}
	if n > 1 {
		res.typ, res.fields, res.acds = cdd.tupleName(tup)
		return
	}
	v := tup.At(0)
	field0 := types.NewField(v.Pos(), v.Pkg(), "_0", v.Type(), false)
	res.fields = []*types.Var{field0}
	res.typ, res.dim, res.acds = cdd.TypeStr(v.Type())
	return
}
コード例 #10
0
ファイル: main.go プロジェクト: kisielk/uses
func argsToString(args *types.Tuple) string {
	ret := make([]string, args.Len())
	for i := 0; i < args.Len(); i++ {
		name := noDot(args.At(i).Name())
		typ := args.At(i).Type().String()

		if len(name) == 0 {
			ret[i] = typ
		} else {
			ret[i] = name + " " + typ
		}
	}

	return strings.Join(ret, ", ")
}
コード例 #11
0
ファイル: types.go プロジェクト: hzmangel/llgo
func (ts *TypeStringer) writeParams(buf *bytes.Buffer, params *types.Tuple, isVariadic bool) {
	buf.WriteByte('(')
	for i := 0; i < int(params.Len()); i++ {
		par := params.At(i)
		if i > 0 {
			buf.WriteString(", ")
		}
		if isVariadic && i == int(params.Len()-1) {
			buf.WriteString("...")
		}
		ts.writeType(buf, par.Type())
	}
	buf.WriteByte(')')
}
コード例 #12
0
ファイル: gcexporter.go プロジェクト: kpsmith/gopherjs
func (e *exporter) makeParameters(tuple *types.Tuple, variadic bool) string {
	params := make([]string, tuple.Len())
	for i := range params {
		param := tuple.At(i)
		paramType := param.Type()
		dots := ""
		if variadic && i == tuple.Len()-1 {
			dots = "..."
			paramType = paramType.(*types.Slice).Elem()
		}
		params[i] = e.makeName(param) + " " + dots + e.makeType(paramType)
	}
	return strings.Join(params, ", ")
}
コード例 #13
0
ファイル: type.go プロジェクト: rjammala/emgo
func (cdd *CDD) tupleName(tup *types.Tuple) (tupName string, fields []*types.Var, acds []*CDD) {
	n := tup.Len()
	for i := 0; i < n; i++ {
		if i != 0 {
			tupName += "$$"
		}
		name, dim, a := cdd.TypeStr(tup.At(i).Type())
		tupName += dimFuncPtr(name, dim)
		acds = append(acds, a...)

	}
	tupName = strings.Map(symToDol, tupName)

	fields = make([]*types.Var, n)
	for i := 0; i < n; i++ {
		v := tup.At(i)
		fields[i] = types.NewField(
			v.Pos(), v.Pkg(), "_"+strconv.Itoa(i), v.Type(), false,
		)
	}

	if _, ok := cdd.gtc.tupNames[tupName]; ok {
		return
	}

	cdd.gtc.tupNames[tupName] = struct{}{}

	s := types.NewStruct(fields, nil)
	o := types.NewTypeName(tup.At(0).Pos(), cdd.gtc.pkg, tupName, s)
	acd := cdd.gtc.newCDD(o, TypeDecl, 0)
	acd.structDecl(new(bytes.Buffer), tupName, s)
	cdd.DeclUses[o] = true
	acds = append(acds, acd)

	return
}
コード例 #14
0
ファイル: stmt.go プロジェクト: rjammala/emgo
func (cdd *CDD) Stmt(w *bytes.Buffer, stmt ast.Stmt, label, resultT string, tup *types.Tuple) (end bool, acds []*CDD) {
	updateEA := func(e bool, a []*CDD) {
		if e {
			end = true
		}
		acds = append(acds, a...)
	}

	cdd.Complexity++

	switch s := stmt.(type) {
	case *ast.DeclStmt:
		cdds := cdd.gtc.Decl(s.Decl, cdd.il)
		for _, c := range cdds {
			for u, typPtr := range c.FuncBodyUses {
				cdd.FuncBodyUses[u] = typPtr
			}
			w.Write(c.Decl)
		}
		for _, c := range cdds {
			w.Write(c.Def)
		}

	case *ast.AssignStmt:
		rhs := make([]string, len(s.Lhs))
		typ := make([]types.Type, len(s.Lhs))

		rhsIsTuple := len(s.Lhs) > 1 && len(s.Rhs) == 1

		if rhsIsTuple {
			tup := cdd.exprType(s.Rhs[0]).(*types.Tuple)
			tupName, _, a := cdd.tupleName(tup)
			acds = append(acds, a...)
			w.WriteString(tupName)
			tupName = "tmp" + cdd.gtc.uniqueId()
			w.WriteString(" " + tupName + " = ")
			cdd.Expr(w, s.Rhs[0], nil)
			w.WriteString(";\n")
			cdd.indent(w)
			for i, n := 0, tup.Len(); i < n; i++ {
				rhs[i] = tupName + "._" + strconv.Itoa(i)
				if s.Tok == token.DEFINE {
					typ[i] = tup.At(i).Type()
				}
			}
		} else {
			for i, e := range s.Rhs {
				var t types.Type
				if s.Tok == token.DEFINE {
					t = cdd.exprType(e)
				} else {
					t = cdd.exprType(s.Lhs[i])
				}
				rhs[i] = cdd.ExprStr(e, t)
				typ[i] = t
			}
		}

		lhs := make([]string, len(s.Lhs))

		if s.Tok == token.DEFINE {
			for i, e := range s.Lhs {
				name := cdd.NameStr(cdd.object(e.(*ast.Ident)), true)
				if name == "_$" {
					lhs[i] = "_"
				} else {
					t, dim, a := cdd.TypeStr(typ[i])
					acds = append(acds, a...)
					lhs[i] = t + " " + dimFuncPtr(name, dim)
				}
			}
		} else {
			for i, e := range s.Lhs {
				lhs[i] = cdd.ExprStr(e, nil)
			}
		}

		if len(s.Rhs) == len(s.Lhs) && len(s.Lhs) > 1 && s.Tok != token.DEFINE {
			for i, t := range typ {
				if i > 0 {
					cdd.indent(w)
				}
				if lhs[i] == "_" {
					w.WriteString("(void)(")
					w.WriteString(rhs[i])
					w.WriteString(");\n")
				} else {
					dim, a := cdd.Type(w, t)
					acds = append(acds, a...)
					tmp := "tmp" + cdd.gtc.uniqueId()
					w.WriteString(" " + dimFuncPtr(tmp, dim))
					w.WriteString(" = " + rhs[i] + ";\n")
					rhs[i] = tmp
				}
			}
			cdd.indent(w)
		}

		var atok string
		switch s.Tok {
		case token.DEFINE:
			atok = " = "

		case token.AND_NOT_ASSIGN:
			atok = " &= "
			rhs[0] = "~(" + rhs[0] + ")"

		default:
			atok = " " + s.Tok.String() + " "
		}
		indent := false
		for i := 0; i < len(lhs); i++ {
			li := lhs[i]
			if li == "_" && rhsIsTuple {
				continue
			}
			if indent {
				cdd.indent(w)
			} else {
				indent = true
			}
			if li == "_" {
				w.WriteString("(void)(")
				w.WriteString(rhs[i])
				w.WriteString(");\n")
			} else {
				w.WriteString(li)
				w.WriteString(atok)
				w.WriteString(rhs[i])
				w.WriteString(";\n")
			}
		}

	case *ast.ExprStmt:
		cdd.Expr(w, s.X, nil)
		w.WriteString(";\n")

	case *ast.IfStmt:
		if s.Init != nil {
			w.WriteString("{\n")
			cdd.il++
			cdd.indent(w)
			updateEA(cdd.Stmt(w, s.Init, "", resultT, tup))
			cdd.indent(w)
		}

		w.WriteString("if (")
		cdd.Expr(w, s.Cond, nil)
		w.WriteString(") ")
		updateEA(cdd.BlockStmt(w, s.Body, resultT, tup))
		if s.Else == nil {
			w.WriteByte('\n')
		} else {
			w.WriteString(" else ")
			updateEA(cdd.Stmt(w, s.Else, "", resultT, tup))
		}

		if s.Init != nil {
			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}

	case *ast.IncDecStmt:
		w.WriteString(s.Tok.String())
		w.WriteByte('(')
		cdd.Expr(w, s.X, nil)
		w.WriteString(");\n")

	case *ast.BlockStmt:
		updateEA(cdd.BlockStmt(w, s, resultT, tup))
		w.WriteByte('\n')

	case *ast.ForStmt:
		if s.Init != nil {
			w.WriteString("{\n")
			cdd.il++
			cdd.indent(w)
			updateEA(cdd.Stmt(w, s.Init, "", resultT, tup))
			cdd.indent(w)
		}

		if label != "" && s.Post == nil {
			w.WriteString(label + "_continue: ")
		}

		w.WriteString("while (")
		if s.Cond != nil {
			cdd.Expr(w, s.Cond, nil)
		} else {
			w.WriteString("true")
		}
		w.WriteString(") ")

		if s.Post != nil {
			w.WriteString("{\n")
			cdd.il++
			cdd.indent(w)
		}
		updateEA(cdd.BlockStmt(w, s.Body, resultT, tup))
		w.WriteByte('\n')

		if s.Post != nil {
			if label != "" {
				cdd.label(w, label, "_continue")
			}
			cdd.indent(w)
			updateEA(cdd.Stmt(w, s.Post, "", resultT, tup))
			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}

		if s.Init != nil {
			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}

		if label != "" {
			cdd.label(w, label, "_break")
		}

	case *ast.RangeStmt:
		w.WriteString("{\n")
		cdd.il++
		xt := cdd.exprType(s.X)
		xs := "x"
		xl := ""

		array := false
		switch t := xt.(type) {
		case *types.Array:
			array = true
			xl = strconv.FormatInt(t.Len(), 10)

		case *types.Pointer:
			array = true
			xl = strconv.FormatInt(t.Elem().(*types.Array).Len(), 10)
		}

		if v, ok := s.Value.(*ast.Ident); ok && v.Name == "_" {
			s.Value = nil
		}

		switch e := s.X.(type) {
		case *ast.Ident:
			xs = cdd.NameStr(cdd.object(e), true)

		default:
			if s.Value != nil || !array {
				cdd.indent(w)
				cdd.varDecl(w, xt, false, xs, e)
			}
		}

		if !array {
			xl = "len(" + xs + ")"
		}

		switch xt.(type) {
		case *types.Slice, *types.Array, *types.Pointer:
			cdd.indent(w)

			ks := cdd.ExprStr(s.Key, nil)

			if s.Tok == token.DEFINE {
				w.WriteString("int ")
			}
			w.WriteString(ks + " = 0;\n")

			if label != "" {
				cdd.label(w, label, "_continue")
			}

			cdd.indent(w)
			w.WriteString("for (; " + ks + " < " + xl + "; ++" + ks + ") ")

			if s.Value != nil {
				w.WriteString("{\n")
				cdd.il++

				cdd.indent(w)
				if s.Tok == token.DEFINE {
					t := xt
					if pt, ok := xt.(*types.Pointer); ok {
						t = pt.Elem()
					}
					vt := t.(interface {
						Elem() types.Type
					}).Elem()
					dim, _ := cdd.Type(w, vt)
					w.WriteByte(' ')
					w.WriteString(dimFuncPtr(cdd.ExprStr(s.Value, nil), dim))
				} else {
					cdd.Expr(w, s.Value, nil)
				}

				w.WriteString(" = ")
				cdd.indexExpr(w, xt, xs, s.Key)
				w.WriteString(";\n")

				cdd.indent(w)
			}

		default:
			notImplemented(s, xt)
		}

		updateEA(cdd.BlockStmt(w, s.Body, resultT, tup))
		w.WriteByte('\n')

		if s.Value != nil {
			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}

		cdd.il--
		cdd.indent(w)
		w.WriteString("}\n")

		if label != "" {
			cdd.label(w, label, "_break")
		}

	case *ast.ReturnStmt:
		end = cdd.ReturnStmt(w, s, resultT, tup)

	case *ast.SwitchStmt:
		w.WriteString("switch(0) {\n")
		cdd.indent(w)
		w.WriteString("case 0:;\n")
		cdd.il++

		if s.Init != nil {
			cdd.indent(w)
			updateEA(cdd.Stmt(w, s.Init, "", resultT, tup))
		}

		cdd.indent(w)

		var typ types.Type
		if s.Tag != nil {
			typ = cdd.exprType(s.Tag)
			cdd.varDecl(w, typ, false, "tag", s.Tag)
		} else {
			typ = types.Typ[types.Bool]
			w.WriteString("bool tag = true;\n")
		}

		for _, stmt := range s.Body.List {
			cdd.indent(w)

			cs := stmt.(*ast.CaseClause)
			if cs.List != nil {
				w.WriteString("if (")
				for i, e := range cs.List {
					if i != 0 {
						w.WriteString(" || ")
					}
					eq(w, "tag", "==", cdd.ExprStr(e, typ), typ, typ)
				}
				w.WriteString(") ")
			}
			w.WriteString("{\n")
			cdd.il++

			brk := true
			if n := len(cs.Body) - 1; n >= 0 {
				bs, ok := cs.Body[n].(*ast.BranchStmt)
				if ok && bs.Tok == token.FALLTHROUGH {
					brk = false
					cs.Body = cs.Body[:n]
				}
			}
			for _, s := range cs.Body {
				cdd.indent(w)
				updateEA(cdd.Stmt(w, s, "", resultT, tup))
			}
			if brk {
				cdd.indent(w)
				w.WriteString("break;\n")
			}

			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}

		cdd.il--
		cdd.indent(w)
		w.WriteString("}\n")

		if label != "" {
			cdd.label(w, label, "_break")
		}

	case *ast.BranchStmt:
		if s.Label == nil {
			w.WriteString(s.Tok.String())
		} else {
			w.WriteString("goto " + s.Label.Name + "$")
			switch s.Tok {
			case token.BREAK:
				w.WriteString("_break")
			case token.CONTINUE:
				w.WriteString("_continue")
			}
		}
		w.WriteString(";\n")

	case *ast.GoStmt:
		a := cdd.GoStmt(w, s)
		acds = append(acds, a...)

	case *ast.SendStmt:
		et := cdd.exprType(s.Chan).(*types.Chan).Elem()
		w.WriteString("SEND(")
		cdd.Expr(w, s.Chan, nil)
		w.WriteString(", ")
		dim, a := cdd.Type(w, et)
		w.WriteString(dimFuncPtr("", dim))
		w.WriteString(", ")
		cdd.Expr(w, s.Value, et)
		w.WriteString(");\n")
		acds = append(acds, a...)

	case *ast.SelectStmt:
		w.WriteString("switch(0) {\n")
		cdd.indent(w)
		w.WriteString("case 0:;\n")
		cdd.il++
		dflt := false
		for i, stmt := range s.Body.List {
			switch s := stmt.(*ast.CommClause).Comm.(type) {
			case nil:
				dflt = true
				continue

			case *ast.SendStmt:
				cdd.indent(w)
				w.WriteString("SENDINIT(" + strconv.Itoa(i) + ", ")
				cdd.Expr(w, s.Chan, nil)
				w.WriteString(", ")
				et := cdd.exprType(s.Chan).(*types.Chan).Elem()
				dim, a := cdd.Type(w, et)
				dimFuncPtr("", dim)
				w.WriteString(", ")
				cdd.Expr(w, s.Value, et)
				w.WriteString(");\n")
				acds = append(acds, a...)

			default:
				cdd.indent(w)
				w.WriteString("RECVINIT(" + strconv.Itoa(i) + ", ")
				var c ast.Expr
				switch r := s.(type) {
				case *ast.AssignStmt:
					c = r.Rhs[0].(*ast.UnaryExpr).X
				case *ast.ExprStmt:
					c = r.X.(*ast.UnaryExpr).X
				default:
					notImplemented(s)
				}
				cdd.Expr(w, c, nil)
				w.WriteString(", ")
				et := cdd.exprType(c).(*types.Chan).Elem()
				dim, a := cdd.Type(w, et)
				dimFuncPtr("", dim)
				w.WriteString(");\n")
				acds = append(acds, a...)
			}
		}

		cdd.indent(w)
		n := len(s.Body.List)
		if dflt {
			w.WriteString("NBSELECT(\n")
			n--
		} else {
			w.WriteString("SELECT(\n")
		}

		cdd.il++
		for i, stmt := range s.Body.List {
			s := stmt.(*ast.CommClause).Comm
			switch s.(type) {
			case nil:
				continue

			case *ast.SendStmt:
				cdd.indent(w)
				w.WriteString("SENDCOMM(" + strconv.Itoa(i) + ")")

			default:
				cdd.indent(w)
				w.WriteString("RECVCOMM(" + strconv.Itoa(i) + ")")
			}
			if n--; n > 0 {
				w.WriteByte(',')
			}
			w.WriteByte('\n')
		}
		cdd.il--
		cdd.indent(w)
		w.WriteString(");\n")

		for i, stmt := range s.Body.List {
			cc := stmt.(*ast.CommClause)
			s := cc.Comm
			cdd.indent(w)
			switch s.(type) {
			case nil:
				w.WriteString("DEFAULT {\n")
			default:
				w.WriteString("CASE(" + strconv.Itoa(i) + ") {\n")
			}
			cdd.il++
			switch s := s.(type) {
			case nil:

			case *ast.SendStmt:
				cdd.indent(w)
				w.WriteString("SELSEND(" + strconv.Itoa(i) + ");\n")

			case *ast.AssignStmt:
				cdd.indent(w)
				name := cdd.ExprStr(s.Lhs[0], nil)
				if len(s.Lhs) == 1 {
					if name != "_$" {
						if s.Tok == token.DEFINE {
							dim, a := cdd.Type(w, cdd.exprType(s.Rhs[0]))
							w.WriteString(" " + dimFuncPtr(name, dim))
							acds = append(acds, a...)
						} else {
							w.WriteString(name)
						}
						w.WriteString(" = ")
					}
					w.WriteString("SELRECV(" + strconv.Itoa(i) + ");\n")
				} else {
					ok := cdd.ExprStr(s.Lhs[1], nil)
					tmp := ""
					var tup *types.Tuple
					if name != "_$" || ok != "_$" {
						tup = cdd.exprType(s.Rhs[0]).(*types.Tuple)
						tupName, _, a := cdd.tupleName(tup)
						acds = append(acds, a...)
						w.WriteString(tupName + " ")
						tmp = "tmp" + cdd.gtc.uniqueId()
						w.WriteString(tmp + " = ")
					}
					w.WriteString("SELRECVOK(" + strconv.Itoa(i) + ");\n")
					if name != "_$" {
						cdd.indent(w)
						if s.Tok == token.DEFINE {
							dim, a := cdd.Type(w, tup.At(0).Type())
							w.WriteString(" " + dimFuncPtr(name, dim))
							acds = append(acds, a...)
						} else {
							w.WriteString(name)
						}
						w.WriteString(" = " + tmp + "._0;\n")
					}
					if ok != "_$" {
						cdd.indent(w)
						if s.Tok == token.DEFINE {
							w.WriteString("bool ")
						}
						w.WriteString(ok + " = " + tmp + "._1;\n")
					}
				}

			case *ast.ExprStmt:
				cdd.indent(w)
				w.WriteString("SELRECV(" + strconv.Itoa(i) + ")\n")

			default:
				notImplemented(s)
			}
			for _, s = range cc.Body {
				cdd.indent(w)
				updateEA(cdd.Stmt(w, s, "", resultT, tup))
			}
			cdd.indent(w)
			w.WriteString("break;\n")
			cdd.il--
			cdd.indent(w)
			w.WriteString("}\n")
		}
		cdd.il--
		cdd.indent(w)
		w.WriteString("}\n")

	default:
		notImplemented(s)
	}
	return
}
コード例 #15
0
ファイル: decl.go プロジェクト: qioixiy/llgo
// 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) {
	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 !isBlank(name) {
			value := llvm_fn.Param(i + paramoffset)
			c.newArgStackVar(i+1, f, v, value, name)
		}
	}

	funcstate := &function{LLVMValue: f, results: results}
	c.functions.push(funcstate)
	hasdefer := hasDefer(funcstate, body)

	// Allocate space on the stack for named results.
	for i := 0; i < results.Len(); i++ {
		v := results.At(i)
		name := v.Name()
		allocstack := !isBlank(name)
		if !allocstack && hasdefer {
			c.objectdata[v] = &ObjectData{}
			allocstack = true
		}
		if allocstack {
			typ := v.Type()
			llvmtyp := c.types.ToLLVM(typ)
			c.newStackVar(f, v, llvm.ConstNull(llvmtyp), name)
		}
	}

	// Create the function body.
	if hasdefer {
		c.makeDeferBlock(funcstate, body)
	}
	c.VisitBlockStmt(body, false)
	c.functions.pop()

	c.setDebugLine(body.End())

	// 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()
		}
	}
}