Skip to content
Snippets Groups Projects
reader.go 54.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		}
    
    		r.funcarg(param, sym, ir.PPARAMOUT)
    	}
    }
    
    func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
    	if sym == nil {
    		assert(ctxt == ir.PPARAM)
    		if r.inlCall != nil {
    			r.inlvars.Append(ir.BlankNode)
    		}
    		return
    	}
    
    	name := ir.NewNameAt(r.updatePos(param.Pos), sym)
    
    	setType(name, param.Type)
    
    	r.addLocal(name, ctxt)
    
    	if r.inlCall == nil {
    		if !r.funarghack {
    			param.Sym = sym
    			param.Nname = name
    		}
    	} else {
    		if ctxt == ir.PPARAMOUT {
    			r.retvars.Append(name)
    		} else {
    			r.inlvars.Append(name)
    		}
    	}
    }
    
    func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
    	assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
    
    	r.sync(syncAddLocal)
    
    		want := r.int()
    		if have := len(r.locals); have != want {
    			base.FatalfAt(name.Pos(), "locals table has desynced")
    		}
    	}
    
    	name.SetUsed(true)
    	r.locals = append(r.locals, name)
    
    	// TODO(mdempsky): Move earlier.
    	if ir.IsBlank(name) {
    		return
    	}
    
    	if r.inlCall != nil {
    		if ctxt == ir.PAUTO {
    			name.SetInlLocal(true)
    		} else {
    			name.SetInlFormal(true)
    			ctxt = ir.PAUTO
    		}
    
    		// TODO(mdempsky): Rethink this hack.
    		if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 {
    			name.SetPos(r.inlCall.Pos())
    			name.SetInlFormal(false)
    			name.SetInlLocal(false)
    		}
    	}
    
    	name.Class = ctxt
    	name.Curfn = r.curfn
    
    	r.curfn.Dcl = append(r.curfn.Dcl, name)
    
    	if ctxt == ir.PAUTO {
    		name.SetFrameOffset(0)
    	}
    }
    
    func (r *reader) useLocal() *ir.Name {
    	r.sync(syncUseObjLocal)
    
    	if r.bool() {
    		return r.locals[r.len()]
    	}
    	return r.closureVars[r.len()]
    
    }
    
    func (r *reader) openScope() {
    	r.sync(syncOpenScope)
    	pos := r.pos()
    
    	if base.Flag.Dwarf {
    		r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl))
    		r.marker.Push(pos)
    	}
    }
    
    func (r *reader) closeScope() {
    	r.sync(syncCloseScope)
    	r.lastCloseScopePos = r.pos()
    
    	r.closeAnotherScope()
    }
    
    // closeAnotherScope is like closeScope, but it reuses the same mark
    // position as the last closeScope call. This is useful for "for" and
    // "if" statements, as their implicit blocks always end at the same
    // position as an explicit block.
    func (r *reader) closeAnotherScope() {
    	r.sync(syncCloseAnotherScope)
    
    	if base.Flag.Dwarf {
    		scopeVars := r.scopeVars[len(r.scopeVars)-1]
    		r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
    
    
    		// Quirkish: noder decides which scopes to keep before
    		// typechecking, whereas incremental typechecking during IR
    		// construction can result in new autotemps being allocated. To
    		// produce identical output, we ignore autotemps here for the
    		// purpose of deciding whether to retract the scope.
    		//
    		// This is important for net/http/fcgi, because it contains:
    		//
    		//	var body io.ReadCloser
    		//	if len(content) > 0 {
    		//		body, req.pw = io.Pipe()
    		//	} else { … }
    		//
    		// Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
    		// variable at the call site.
    		//
    		// Noder does not preserve the scope where the io.Pipe() call
    		// resides, because it doesn't contain any declared variables in
    		// source. So the ~R0 variable ends up being assigned to the
    		// enclosing scope instead.
    		//
    		// However, typechecking this assignment also introduces
    		// autotemps, because io.Pipe's results need conversion before
    		// they can be assigned to their respective destination variables.
    		//
    		// TODO(mdempsky): We should probably just keep all scopes, and
    		// let dwarfgen take care of pruning them instead.
    		retract := true
    		for _, n := range r.curfn.Dcl[scopeVars:] {
    			if !n.AutoTemp() {
    				retract = false
    				break
    			}
    		}
    
    		if retract {
    
    			// no variables were declared in this scope, so we can retract it.
    			r.marker.Unpush()
    		} else {
    			r.marker.Pop(r.lastCloseScopePos)
    		}
    	}
    }
    
    // @@@ Statements
    
    func (r *reader) stmt() ir.Node {
    	switch stmts := r.stmts(); len(stmts) {
    	case 0:
    		return nil
    	case 1:
    		return stmts[0]
    	default:
    		return ir.NewBlockStmt(stmts[0].Pos(), stmts)
    	}
    }
    
    func (r *reader) stmts() []ir.Node {
    
    	var res ir.Nodes
    
    	r.sync(syncStmts)
    	for {
    		tag := codeStmt(r.code(syncStmt1))
    		if tag == stmtEnd {
    			r.sync(syncStmtsEnd)
    			return res
    		}
    
    		if n := r.stmt1(tag, &res); n != nil {
    
    		}
    	}
    }
    
    func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
    	var label *types.Sym
    	if n := len(*out); n > 0 {
    		if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok {
    			label = ls.Label
    		}
    	}
    
    	switch tag {
    	default:
    		panic("unexpected statement")
    
    	case stmtAssign:
    		pos := r.pos()
    
    
    		// TODO(mdempsky): After quirks mode is gone, swap these
    		// statements so we visit LHS before RHS again.
    
    
    		if len(rhs) == 0 {
    			for _, name := range names {
    				as := ir.NewAssignStmt(pos, name, nil)
    				as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
    
    			}
    			return nil
    		}
    
    		if len(lhs) == 1 && len(rhs) == 1 {
    			n := ir.NewAssignStmt(pos, lhs[0], rhs[0])
    			n.Def = r.initDefn(n, names)
    			return n
    		}
    
    		n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
    		n.Def = r.initDefn(n, names)
    		return n
    
    	case stmtAssignOp:
    		op := r.op()
    		lhs := r.expr()
    		pos := r.pos()
    		rhs := r.expr()
    		return ir.NewAssignOpStmt(pos, op, lhs, rhs)
    
    	case stmtIncDec:
    		op := r.op()
    		lhs := r.expr()
    		pos := r.pos()
    		n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one))
    		n.IncDec = true
    		return n
    
    	case stmtBlock:
    		out.Append(r.blockStmt()...)
    		return nil
    
    	case stmtBranch:
    		pos := r.pos()
    		op := r.op()
    		sym := r.optLabel()
    		return ir.NewBranchStmt(pos, op, sym)
    
    	case stmtCall:
    		pos := r.pos()
    		op := r.op()
    		call := r.expr()
    		return ir.NewGoDeferStmt(pos, op, call)
    
    	case stmtExpr:
    		return r.expr()
    
    	case stmtFor:
    		return r.forStmt(label)
    
    	case stmtIf:
    		return r.ifStmt()
    
    	case stmtLabel:
    		pos := r.pos()
    		sym := r.label()
    		return ir.NewLabelStmt(pos, sym)
    
    	case stmtReturn:
    		pos := r.pos()
    		results := r.exprList()
    		return ir.NewReturnStmt(pos, results)
    
    	case stmtSelect:
    		return r.selectStmt(label)
    
    	case stmtSend:
    		pos := r.pos()
    		ch := r.expr()
    		value := r.expr()
    		return ir.NewSendStmt(pos, ch, value)
    
    	case stmtSwitch:
    		return r.switchStmt(label)
    	}
    }
    
    func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
    	lhs := make([]ir.Node, r.len())
    	var names []*ir.Name
    
    	for i := range lhs {
    		if r.bool() {
    			pos := r.pos()
    			_, sym := r.localIdent()
    			typ := r.typ()
    
    			name := ir.NewNameAt(pos, sym)
    			lhs[i] = name
    			names = append(names, name)
    
    	}
    
    	return names, lhs
    }
    
    func (r *reader) blockStmt() []ir.Node {
    	r.sync(syncBlockStmt)
    	r.openScope()
    	stmts := r.stmts()
    	r.closeScope()
    	return stmts
    }
    
    func (r *reader) forStmt(label *types.Sym) ir.Node {
    	r.sync(syncForStmt)
    
    	r.openScope()
    
    	if r.bool() {
    		pos := r.pos()
    
    
    		// TODO(mdempsky): After quirks mode is gone, swap these
    		// statements so we read LHS before X again.
    
    		body := r.blockStmt()
    		r.closeAnotherScope()
    
    		rang := ir.NewRangeStmt(pos, nil, nil, x, body)
    		if len(lhs) >= 1 {
    			rang.Key = lhs[0]
    			if len(lhs) >= 2 {
    				rang.Value = lhs[1]
    			}
    		}
    		rang.Def = r.initDefn(rang, names)
    		rang.Label = label
    		return rang
    	}
    
    	pos := r.pos()
    	init := r.stmt()
    	cond := r.expr()
    	post := r.stmt()
    	body := r.blockStmt()
    	r.closeAnotherScope()
    
    	stmt := ir.NewForStmt(pos, init, cond, post, body)
    	stmt.Label = label
    	return stmt
    }
    
    func (r *reader) ifStmt() ir.Node {
    	r.sync(syncIfStmt)
    	r.openScope()
    	pos := r.pos()
    	init := r.stmts()
    	cond := r.expr()
    	then := r.blockStmt()
    	els := r.stmts()
    	n := ir.NewIfStmt(pos, cond, then, els)
    	n.SetInit(init)
    	r.closeAnotherScope()
    	return n
    }
    
    func (r *reader) selectStmt(label *types.Sym) ir.Node {
    	r.sync(syncSelectStmt)
    
    	pos := r.pos()
    	clauses := make([]*ir.CommClause, r.len())
    	for i := range clauses {
    		if i > 0 {
    			r.closeScope()
    		}
    		r.openScope()
    
    		pos := r.pos()
    		comm := r.stmt()
    		body := r.stmts()
    
    		clauses[i] = ir.NewCommStmt(pos, comm, body)
    	}
    	if len(clauses) > 0 {
    		r.closeScope()
    	}
    	n := ir.NewSelectStmt(pos, clauses)
    	n.Label = label
    	return n
    }
    
    func (r *reader) switchStmt(label *types.Sym) ir.Node {
    	r.sync(syncSwitchStmt)
    
    	r.openScope()
    	pos := r.pos()
    	init := r.stmt()
    
    
    	var tag ir.Node
    	if r.bool() {
    		pos := r.pos()
    		var ident *ir.Ident
    		if r.bool() {
    			pos := r.pos()
    			sym := typecheck.Lookup(r.string())
    			ident = ir.NewIdent(pos, sym)
    		}
    		x := r.expr()
    		tag = ir.NewTypeSwitchGuard(pos, ident, x)
    	} else {
    		tag = r.expr()
    	}
    
    
    	tswitch, ok := tag.(*ir.TypeSwitchGuard)
    	if ok && tswitch.Tag == nil {
    		tswitch = nil
    	}
    
    	clauses := make([]*ir.CaseClause, r.len())
    	for i := range clauses {
    		if i > 0 {
    			r.closeScope()
    		}
    		r.openScope()
    
    		pos := r.pos()
    		cases := r.exprList()
    
    		clause := ir.NewCaseStmt(pos, cases, nil)
    		if tswitch != nil {
    			pos := r.pos()
    			typ := r.typ()
    
    			name := ir.NewNameAt(pos, tswitch.Tag.Sym())
    
    			r.addLocal(name, ir.PAUTO)
    			clause.Var = name
    			name.Defn = tswitch
    		}
    
    		clause.Body = r.stmts()
    		clauses[i] = clause
    	}
    	if len(clauses) > 0 {
    		r.closeScope()
    	}
    	r.closeScope()
    
    	n := ir.NewSwitchStmt(pos, tag, clauses)
    	n.Label = label
    	if init != nil {
    		n.SetInit([]ir.Node{init})
    	}
    	return n
    }
    
    func (r *reader) label() *types.Sym {
    	r.sync(syncLabel)
    	name := r.string()
    	if r.inlCall != nil {
    		name = fmt.Sprintf("~%s·%d", name, inlgen)
    	}
    	return typecheck.Lookup(name)
    }
    
    func (r *reader) optLabel() *types.Sym {
    	r.sync(syncOptLabel)
    	if r.bool() {
    		return r.label()
    	}
    	return nil
    }
    
    // initDefn marks the given names as declared by defn and populates
    // its Init field with ODCL nodes. It then reports whether any names
    // were so declared, which can be used to initialize defn.Def.
    func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
    	if len(names) == 0 {
    		return false
    	}
    
    	init := make([]ir.Node, len(names))
    	for i, name := range names {
    		name.Defn = defn
    		init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
    	}
    	defn.SetInit(init)
    	return true
    }
    
    // @@@ Expressions
    
    
    // expr reads and returns a typechecked expression.
    
    func (r *reader) expr() (res ir.Node) {
    	defer func() {
    		if res != nil && res.Typecheck() == 0 {
    			base.FatalfAt(res.Pos(), "%v missed typecheck", res)
    		}
    	}()
    
    	switch tag := codeExpr(r.code(syncExpr)); tag {
    	default:
    		panic("unhandled expression")
    
    	case exprNone:
    		return nil
    
    	case exprBlank:
    
    		// blank only allowed in LHS of assignments
    		// TODO(mdempsky): Handle directly in assignList instead?
    		return typecheck.AssignExpr(ir.BlankNode)
    
    		// Callee instead of Expr allows builtins
    		// TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
    		return typecheck.Callee(r.obj())
    
    		// TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node.
    		n := ir.TypeNode(r.typ())
    		n.SetTypecheck(1)
    		return n
    
    		typ := r.typ()
    		val := FixValue(typ, r.value())
    
    		op := r.op()
    		orig := r.string()
    
    		return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
    
    
    	case exprCompLit:
    		return r.compLit()
    
    	case exprFuncLit:
    		return r.funcLit()
    
    	case exprSelector:
    		x := r.expr()
    		pos := r.pos()
    		_, sym := r.selector()
    
    		n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
    		if n.Op() == ir.OMETHVALUE {
    			wrapper := methodValueWrapper{
    				rcvr:   n.X.Type(),
    				method: n.Selection,
    			}
    			if r.importedDef() {
    				haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
    			} else {
    				needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
    			}
    		}
    		return n
    
    
    	case exprIndex:
    		x := r.expr()
    		pos := r.pos()
    		index := r.expr()
    
    		return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
    
    
    	case exprSlice:
    		x := r.expr()
    		pos := r.pos()
    		var index [3]ir.Node
    		for i := range index {
    			index[i] = r.expr()
    		}
    		op := ir.OSLICE
    		if index[2] != nil {
    			op = ir.OSLICE3
    		}
    
    		return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2]))
    
    
    	case exprAssert:
    		x := r.expr()
    		pos := r.pos()
    		typ := r.expr().(ir.Ntype)
    
    		return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ))
    
    
    	case exprUnaryOp:
    		op := r.op()
    		pos := r.pos()
    		x := r.expr()
    
    		switch op {
    		case ir.OADDR:
    
    			return typecheck.Expr(typecheck.NodAddrAt(pos, x))
    
    			return typecheck.Expr(ir.NewStarExpr(pos, x))
    
    		return typecheck.Expr(ir.NewUnaryExpr(pos, op, x))
    
    
    	case exprBinaryOp:
    		op := r.op()
    		x := r.expr()
    		pos := r.pos()
    		y := r.expr()
    
    		switch op {
    		case ir.OANDAND, ir.OOROR:
    
    			return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
    
    		return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
    
    		fun := r.expr()
    		if r.bool() { // method call
    			pos := r.pos()
    			_, sym := r.selector()
    			fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym))
    		}
    
    		pos := r.pos()
    		args := r.exprs()
    		dots := r.bool()
    
    		return typecheck.Call(pos, fun, args, dots)
    
    		return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
    
    	}
    }
    
    func (r *reader) compLit() ir.Node {
    	r.sync(syncCompLit)
    	pos := r.pos()
    
    		typ = typ.Elem()
    	}
    	if typ.Kind() == types.TFORW {
    		base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
    	}
    	isStruct := typ.Kind() == types.TSTRUCT
    
    	elems := make([]ir.Node, r.len())
    	for i := range elems {
    		elemp := &elems[i]
    
    		if isStruct {
    			sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.len()), nil)
    			*elemp, elemp = sk, &sk.Value
    		} else if r.bool() {
    			kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
    			*elemp, elemp = kv, &kv.Value
    		}
    
    		*elemp = wrapName(r.pos(), r.expr())
    	}
    
    
    	lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), elems))
    	if typ0.IsPtr() {
    		lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
    		lit.SetType(typ0)
    
    	}
    	return lit
    }
    
    func wrapName(pos src.XPos, x ir.Node) ir.Node {
    	// These nodes do not carry line numbers.
    	// Introduce a wrapper node to give them the correct line.
    	switch ir.Orig(x).Op() {
    	case ir.OTYPE, ir.OLITERAL:
    		if x.Sym() == nil {
    			break
    		}
    		fallthrough
    	case ir.ONAME, ir.ONONAME, ir.OPACK, ir.ONIL:
    		p := ir.NewParenExpr(pos, x)
    		p.SetImplicit(true)
    		return p
    	}
    	return x
    }
    
    func (r *reader) funcLit() ir.Node {
    	r.sync(syncFuncLit)
    
    	pos := r.pos()
    	xtype2 := r.signature(types.LocalPkg, nil)
    
    	opos := pos
    
    	fn := ir.NewClosureFunc(opos, r.curfn != nil)
    
    	clo := fn.OClosure
    	ir.NameClosure(clo, r.curfn)
    
    	setType(fn.Nname, xtype2)
    
    	fn.ClosureVars = make([]*ir.Name, 0, r.len())
    	for len(fn.ClosureVars) < cap(fn.ClosureVars) {
    
    		ir.NewClosureVar(r.pos(), fn, r.useLocal())
    
    	// TODO(mdempsky): Remove hard-coding of typecheck.Target.
    	return ir.UseClosure(clo, typecheck.Target)
    
    }
    
    func (r *reader) exprList() []ir.Node {
    	r.sync(syncExprList)
    	return r.exprs()
    }
    
    func (r *reader) exprs() []ir.Node {
    	r.sync(syncExprs)
    	nodes := make([]ir.Node, r.len())
    	if len(nodes) == 0 {
    		return nil // TODO(mdempsky): Unclear if this matters.
    	}
    	for i := range nodes {
    		nodes[i] = r.expr()
    	}
    	return nodes
    }
    
    func (r *reader) op() ir.Op {
    	r.sync(syncOp)
    	return ir.Op(r.len())
    }
    
    // @@@ Package initialization
    
    func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
    	cgoPragmas := make([][]string, r.len())
    	for i := range cgoPragmas {
    		cgoPragmas[i] = r.strings()
    	}
    	target.CgoPragmas = cgoPragmas
    
    	r.pkgDecls(target)
    
    	r.sync(syncEOF)
    }
    
    func (r *reader) pkgDecls(target *ir.Package) {
    	r.sync(syncDecls)
    	for {
    		switch code := codeDecl(r.code(syncDecl)); code {
    		default:
    			panic(fmt.Sprintf("unhandled decl: %v", code))
    
    		case declEnd:
    			return
    
    		case declFunc:
    			names := r.pkgObjs(target)
    			assert(len(names) == 1)
    			target.Decls = append(target.Decls, names[0].Func)
    
    		case declMethod:
    			typ := r.typ()
    			_, sym := r.selector()
    
    			method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0)
    			target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func)
    
    		case declVar:
    			pos := r.pos()
    			names := r.pkgObjs(target)
    			values := r.exprList()
    
    			if len(names) > 1 && len(values) == 1 {
    				as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values)
    				for _, name := range names {
    					as.Lhs.Append(name)
    					name.Defn = as
    				}
    				target.Decls = append(target.Decls, as)
    			} else {
    				for i, name := range names {
    					as := ir.NewAssignStmt(pos, name, nil)
    					if i < len(values) {
    						as.Y = values[i]
    					}
    					name.Defn = as
    					target.Decls = append(target.Decls, as)
    				}
    			}
    
    			if n := r.len(); n > 0 {
    				assert(len(names) == 1)
    				embeds := make([]ir.Embed, n)
    				for i := range embeds {
    					embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.strings()}
    				}
    				names[0].Embed = &embeds
    				target.Embeds = append(target.Embeds, names[0])
    			}
    
    		case declOther:
    			r.pkgObjs(target)
    		}
    	}
    }
    
    func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
    	r.sync(syncDeclNames)
    	nodes := make([]*ir.Name, r.len())
    	for i := range nodes {
    		r.sync(syncDeclName)
    
    		name := r.obj().(*ir.Name)
    		nodes[i] = name
    
    		sym := name.Sym()
    		if sym.IsBlank() {
    			continue
    		}
    
    		switch name.Class {
    		default:
    			base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class)
    
    		case ir.PEXTERN:
    			target.Externs = append(target.Externs, name)
    
    		case ir.PFUNC:
    			assert(name.Type().Recv() == nil)
    
    			// TODO(mdempsky): Cleaner way to recognize init?
    			if strings.HasPrefix(sym.Name, "init.") {
    				target.Inits = append(target.Inits, name.Func)
    			}
    		}
    
    		if types.IsExported(sym.Name) {
    			assert(!sym.OnExportList())
    			target.Exports = append(target.Exports, name)
    			sym.SetOnExportList(true)
    		}
    
    		if base.Flag.AsmHdr != "" {
    			assert(!sym.Asm())
    			target.Asms = append(target.Asms, name)
    			sym.SetAsm(true)
    		}
    	}
    
    	return nodes
    }
    
    // @@@ Inlining
    
    var inlgen = 0
    
    func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
    	// TODO(mdempsky): Turn callerfn into an explicit parameter.
    	callerfn := ir.CurFunc
    
    	pri, ok := bodyReader[fn]
    	if !ok {
    		// Assume it's an imported function or something that we don't
    		// have access to in quirks mode.
    		if haveLegacyImports {
    			return nil
    		}
    
    		base.FatalfAt(call.Pos(), "missing function body for call to %v", fn)
    	}
    
    	if fn.Inl.Body == nil {
    		expandInline(fn, pri)
    	}
    
    	r := pri.asReader(relocBody, syncFuncBody)
    
    	// TODO(mdempsky): This still feels clumsy. Can we do better?
    	tmpfn := ir.NewFunc(fn.Pos())
    	tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym())
    	tmpfn.Closgen = callerfn.Closgen
    	defer func() { callerfn.Closgen = tmpfn.Closgen }()
    
    
    	setType(tmpfn.Nname, fn.Type())
    
    	r.inlCall = call
    	r.inlFunc = fn
    	r.inlTreeIndex = inlIndex
    	r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
    
    
    	r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
    	for i, cv := range r.inlFunc.ClosureVars {
    		r.closureVars[i] = cv.Outer
    
    	}
    
    	r.funcargs(fn)
    
    	assert(r.bool()) // have body
    	r.delayResults = fn.Inl.CanDelayResults
    
    	r.retlabel = typecheck.AutoLabel(".i")
    	inlgen++
    
    	init := ir.TakeInit(call)
    
    	// For normal function calls, the function callee expression
    
    	// may contain side effects. Make sure to preserve these,
    
    	// if necessary (#42703).
    	if call.Op() == ir.OCALLFUNC {
    
    		inline.CalleeEffects(&init, call.X)
    
    	}
    
    	var args ir.Nodes
    	if call.Op() == ir.OCALLMETH {
    
    		base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
    
    	}
    	args.Append(call.Args...)
    
    	// Create assignment to declare and initialize inlvars.
    	as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args)
    	as2.Def = true
    	var as2init ir.Nodes
    	for _, name := range r.inlvars {
    		if ir.IsBlank(name) {
    			continue
    		}
    		// TODO(mdempsky): Use inlined position of name.Pos() instead?
    		name := name.(*ir.Name)
    		as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
    		name.Defn = as2
    	}
    	as2.SetInit(as2init)
    	init.Append(typecheck.Stmt(as2))
    
    	if !r.delayResults {
    		// If not delaying retvars, declare and zero initialize the
    		// result variables now.
    		for _, name := range r.retvars {
    			// TODO(mdempsky): Use inlined position of name.Pos() instead?
    			name := name.(*ir.Name)
    			init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
    			ras := ir.NewAssignStmt(call.Pos(), name, nil)
    			init.Append(typecheck.Stmt(ras))
    		}
    	}
    
    	// Add an inline mark just before the inlined body.
    	// This mark is inline in the code so that it's a reasonable spot
    	// to put a breakpoint. Not sure if that's really necessary or not
    	// (in which case it could go at the end of the function instead).
    	// Note issue 28603.
    	init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex)))
    
    	nparams := len(r.curfn.Dcl)
    
    
    	ir.WithFunc(r.curfn, func() {
    		r.curfn.Body = r.stmts()
    		r.curfn.Endlineno = r.pos()
    
    		// Replace any "return" statements within the function body.
    
    		var edit func(ir.Node) ir.Node
    		edit = func(n ir.Node) ir.Node {
    			if ret, ok := n.(*ir.ReturnStmt); ok {
    				n = typecheck.Stmt(r.inlReturn(ret))
    			}
    			ir.EditChildren(n, edit)
    			return n
    		}
    		edit(r.curfn)
    
    
    	body := ir.Nodes(r.curfn.Body)
    
    	// Quirkish: We need to eagerly prune variables added during
    	// inlining, but removed by deadcode.FuncBody above. Unused
    	// variables will get removed during stack frame layout anyway, but
    	// len(fn.Dcl) ends up influencing things like autotmp naming.
    
    	used := usedLocals(body)
    
    	for i, name := range r.curfn.Dcl {
    		if i < nparams || used.Has(name) {
    			name.Curfn = callerfn
    			callerfn.Dcl = append(callerfn.Dcl, name)