cmd/compile/internal/ir: add Func.DeclareParams

There's several copies of this function. We only need one.

While here, normalize so that we always declare parameters, and always
use the names ~pNN for params and ~rNN for results.

Change-Id: I49e90d3fd1820f3c07936227ed5cfefd75d49a1c
Reviewed-on: https://go-review.googlesource.com/c/go/+/528415
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Matthew Dempsky 2023-09-13 19:26:32 -07:00 committed by Gopher Robot
parent de4ead8102
commit d18e9407b0
15 changed files with 179 additions and 361 deletions

View file

@ -21,42 +21,12 @@ type paramsAnalyzer struct {
*condLevelTracker *condLevelTracker
} }
// dclParams returns a slice containing the non-blank, named params
// for the specific function (plus rcvr as well if applicable) in
// declaration order.
func dclParams(fn *ir.Func) []*ir.Name {
params := []*ir.Name{}
for _, n := range fn.Dcl {
if n.Op() != ir.ONAME {
continue
}
if n.Class != ir.PPARAM {
continue
}
params = append(params, n)
}
return params
}
// getParams returns an *ir.Name slice containing all params for the // getParams returns an *ir.Name slice containing all params for the
// function (plus rcvr as well if applicable). Note that this slice // function (plus rcvr as well if applicable).
// includes entries for blanks; entries in the returned slice corresponding
// to blanks or unnamed params will be nil.
func getParams(fn *ir.Func) []*ir.Name { func getParams(fn *ir.Func) []*ir.Name {
dclparms := dclParams(fn) sig := fn.Type()
dclidx := 0 numParams := sig.NumRecvs() + sig.NumParams()
recvrParms := fn.Type().RecvParams() return fn.Dcl[:numParams]
params := make([]*ir.Name, len(recvrParms))
for i := range recvrParms {
var v *ir.Name
if recvrParms[i].Sym != nil &&
!recvrParms[i].Sym.IsBlank() {
v = dclparms[dclidx]
dclidx++
}
params[i] = v
}
return params
} }
func makeParamsAnalyzer(fn *ir.Func) *paramsAnalyzer { func makeParamsAnalyzer(fn *ir.Func) *paramsAnalyzer {

View file

@ -65,9 +65,7 @@ type Func struct {
// include closurevars until transforming closures during walk. // include closurevars until transforming closures during walk.
// Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs, // Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
// with PPARAMs and PPARAMOUTs in order corresponding to the function signature. // with PPARAMs and PPARAMOUTs in order corresponding to the function signature.
// However, as anonymous or blank PPARAMs are not actually declared, // Anonymous and blank params are declared as ~pNN (for PPARAMs) and ~rNN (for PPARAMOUTs).
// they are omitted from Dcl.
// Anonymous and blank PPARAMOUTs are declared as ~rNN and ~bNN Names, respectively.
Dcl []*Name Dcl []*Name
// ClosureVars lists the free variables that are used within a // ClosureVars lists the free variables that are used within a
@ -455,3 +453,40 @@ func IsFuncPCIntrinsic(n *CallExpr) bool {
return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") && return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
fn.Pkg.Path == "internal/abi" fn.Pkg.Path == "internal/abi"
} }
// DeclareParams creates Names for all of the parameters in fn's
// signature and adds them to fn.Dcl.
//
// If setNname is true, then it also sets types.Field.Nname for each
// parameter.
func (fn *Func) DeclareParams(setNname bool) {
if fn.Dcl != nil {
base.FatalfAt(fn.Pos(), "%v already has Dcl", fn)
}
declareParams := func(params []*types.Field, ctxt Class, prefix string, offset int) {
for i, param := range params {
sym := param.Sym
if sym == nil || sym.IsBlank() {
sym = fn.Sym().Pkg.LookupNum(prefix, i)
}
name := NewNameAt(param.Pos, sym, param.Type)
name.Class = ctxt
name.Curfn = fn
fn.Dcl[offset+i] = name
if setNname {
param.Nname = name
}
}
}
sig := fn.Type()
params := sig.RecvParams()
results := sig.Results()
fn.Dcl = make([]*Name, len(params)+len(results))
declareParams(params, PPARAM, "~p", 0)
declareParams(results, PPARAMOUT, "~r", len(params))
}

View file

@ -102,16 +102,13 @@ func NewBuiltin(sym *types.Sym, op Op) *Name {
} }
// NewLocal returns a new function-local variable with the given name and type. // NewLocal returns a new function-local variable with the given name and type.
func (fn *Func) NewLocal(pos src.XPos, sym *types.Sym, class Class, typ *types.Type) *Name { func (fn *Func) NewLocal(pos src.XPos, sym *types.Sym, typ *types.Type) *Name {
switch class { if fn.Dcl == nil {
case PPARAM, PPARAMOUT, PAUTO: base.FatalfAt(pos, "must call DeclParams on %v first", fn)
// ok
default:
base.FatalfAt(pos, "NewLocal: unexpected class for %v: %v", sym, class)
} }
n := NewNameAt(pos, sym, typ) n := NewNameAt(pos, sym, typ)
n.Class = class n.Class = PAUTO
n.Curfn = fn n.Curfn = fn
fn.Dcl = append(fn.Dcl, n) fn.Dcl = append(fn.Dcl, n)
return n return n

View file

@ -318,11 +318,18 @@ func (op Op) IsCmp() bool {
return false return false
} }
// Nodes is a pointer to a slice of *Node. // Nodes is a slice of Node.
// For fields that are not used in most nodes, this is used instead of
// a slice to save space.
type Nodes []Node type Nodes []Node
// ToNodes returns s as a slice of Nodes.
func ToNodes[T Node](s []T) Nodes {
res := make(Nodes, len(s))
for i, n := range s {
res[i] = n
}
return res
}
// Append appends entries to Nodes. // Append appends entries to Nodes.
func (n *Nodes) Append(a ...Node) { func (n *Nodes) Append(a ...Node) {
if len(a) == 0 { if len(a) == 0 {

View file

@ -205,15 +205,15 @@ func s15a8(x *[15]int64) [15]int64 {
`"relatedInformation":[`+ `"relatedInformation":[`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~R0 = y:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 = y:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~r0 = \u0026y.b (assign-pair)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~R0:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~r0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~R0 (return)"}]}`) `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~r0 (return)"}]}`)
}) })
} }

View file

@ -107,6 +107,11 @@ type reader struct {
locals []*ir.Name locals []*ir.Name
closureVars []*ir.Name closureVars []*ir.Name
// funarghack is used during inlining to suppress setting
// Field.Nname to the inlined copies of the parameters. This is
// necessary because we reuse the same types.Type as the original
// function, and most of the compiler still relies on field.Nname to
// find parameters/results.
funarghack bool funarghack bool
// methodSym is the name of method's name, if reading a method. // methodSym is the name of method's name, if reading a method.
@ -145,14 +150,6 @@ type reader struct {
// Label to return to. // Label to return to.
retlabel *types.Sym retlabel *types.Sym
// inlvars is the list of variables that the inlinee's arguments are
// assigned to, one for each receiver and normal parameter, in order.
inlvars ir.Nodes
// retvars is the list of variables that the inlinee's results are
// assigned to, one for each result parameter, in order.
retvars ir.Nodes
} }
// A readerDict represents an instantiated "compile-time dictionary," // A readerDict represents an instantiated "compile-time dictionary,"
@ -1237,7 +1234,7 @@ func (r *reader) funcBody(fn *ir.Func) {
} }
ir.WithFunc(fn, func() { ir.WithFunc(fn, func() {
r.funcargs(fn) r.declareParams()
if r.syntheticBody(fn.Pos()) { if r.syntheticBody(fn.Pos()) {
return return
@ -1294,7 +1291,7 @@ func (r *reader) callShaped(pos src.XPos) {
shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym) shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym)
} }
recvs, params := r.syntheticArgs(pos) params := r.syntheticArgs()
// Construct the arguments list: receiver (if any), then runtime // Construct the arguments list: receiver (if any), then runtime
// dictionary, and finally normal parameters. // dictionary, and finally normal parameters.
@ -1306,7 +1303,10 @@ func (r *reader) callShaped(pos src.XPos) {
// putting the dictionary parameter after that is the least invasive // putting the dictionary parameter after that is the least invasive
// solution at the moment. // solution at the moment.
var args ir.Nodes var args ir.Nodes
args.Append(recvs...) if r.methodSym != nil {
args.Append(params[0])
params = params[1:]
}
args.Append(typecheck.Expr(ir.NewAddrExpr(pos, r.p.dictNameOf(r.dict)))) args.Append(typecheck.Expr(ir.NewAddrExpr(pos, r.p.dictNameOf(r.dict))))
args.Append(params...) args.Append(params...)
@ -1315,44 +1315,9 @@ func (r *reader) callShaped(pos src.XPos) {
// syntheticArgs returns the recvs and params arguments passed to the // syntheticArgs returns the recvs and params arguments passed to the
// current function. // current function.
func (r *reader) syntheticArgs(pos src.XPos) (recvs, params ir.Nodes) { func (r *reader) syntheticArgs() ir.Nodes {
sig := r.curfn.Nname.Type() sig := r.curfn.Nname.Type()
return ir.ToNodes(r.curfn.Dcl[:sig.NumRecvs()+sig.NumParams()])
inlVarIdx := 0
addParams := func(out *ir.Nodes, params []*types.Field) {
for _, param := range params {
var arg ir.Node
if param.Nname != nil {
name := param.Nname.(*ir.Name)
if !ir.IsBlank(name) {
if r.inlCall != nil {
// During inlining, we want the respective inlvar where we
// assigned the callee's arguments.
arg = r.inlvars[inlVarIdx]
} else {
// Otherwise, we can use the parameter itself directly.
base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn)
arg = name
}
}
}
// For anonymous and blank parameters, we don't have an *ir.Name
// to use as the argument. However, since we know the shaped
// function won't use the value either, we can just pass the
// zero value.
if arg == nil {
arg = ir.NewZero(pos, param.Type)
}
out.Append(arg)
inlVarIdx++
}
}
addParams(&recvs, sig.Recvs())
addParams(&params, sig.Params())
return
} }
// syntheticTailCall emits a tail call to fn, passing the given // syntheticTailCall emits a tail call to fn, passing the given
@ -1489,105 +1454,32 @@ func (dict *readerDict) varType() *types.Type {
return types.NewArray(types.Types[types.TUINTPTR], dict.numWords()) return types.NewArray(types.Types[types.TUINTPTR], dict.numWords())
} }
func (r *reader) funcargs(fn *ir.Func) { func (r *reader) declareParams() {
sig := fn.Nname.Type() r.curfn.DeclareParams(!r.funarghack)
if recv := sig.Recv(); recv != nil { for _, name := range r.curfn.Dcl {
r.funcarg(recv, recv.Sym, ir.PPARAM) if name.Sym().Name == dictParamName {
} r.dictParam = name
for _, param := range sig.Params() { continue
r.funcarg(param, param.Sym, ir.PPARAM)
}
for i, param := range sig.Results() {
sym := param.Sym
if sym == nil || sym.IsBlank() {
prefix := "~r"
if r.inlCall != nil {
prefix = "~R"
} else if sym != nil {
prefix = "~b"
}
sym = typecheck.LookupNum(prefix, i)
} }
r.funcarg(param, sym, ir.PPARAMOUT) r.addLocal(name)
} }
} }
func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) { func (r *reader) addLocal(name *ir.Name) {
if sym == nil { if r.synthetic == nil {
assert(ctxt == ir.PPARAM) r.Sync(pkgbits.SyncAddLocal)
if r.inlCall != nil { if r.p.SyncMarkers() {
r.inlvars.Append(ir.BlankNode) want := r.Int()
} if have := len(r.locals); have != want {
return base.FatalfAt(name.Pos(), "locals table has desynced")
}
name := r.addLocal(r.inlPos(param.Pos), sym, ctxt, param.Type)
if r.inlCall == nil {
if !r.funarghack {
param.Nname = name
}
} else {
if ctxt == ir.PPARAMOUT {
r.retvars.Append(name)
} else {
r.inlvars.Append(name)
}
}
}
func (r *reader) addLocal(pos src.XPos, sym *types.Sym, ctxt ir.Class, typ *types.Type) *ir.Name {
assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
name := ir.NewNameAt(pos, sym, typ)
if name.Sym().Name == dictParamName {
r.dictParam = name
} else {
if r.synthetic == nil {
r.Sync(pkgbits.SyncAddLocal)
if r.p.SyncMarkers() {
want := r.Int()
if have := len(r.locals); have != want {
base.FatalfAt(name.Pos(), "locals table has desynced")
}
} }
r.varDictIndex(name)
} }
r.varDictIndex(name)
r.locals = append(r.locals, name)
} }
name.SetUsed(true) r.locals = append(r.locals, name)
// TODO(mdempsky): Move earlier.
if ir.IsBlank(name) {
return name
}
if r.inlCall != nil {
if ctxt == ir.PAUTO {
name.SetInlLocal(true)
} else {
name.SetInlFormal(true)
ctxt = ir.PAUTO
}
}
name.Class = ctxt
name.Curfn = r.curfn
r.curfn.Dcl = append(r.curfn.Dcl, name)
if ctxt == ir.PAUTO {
name.SetFrameOffset(0)
}
return name
} }
func (r *reader) useLocal() *ir.Name { func (r *reader) useLocal() *ir.Name {
@ -1836,7 +1728,8 @@ func (r *reader) assign() (ir.Node, bool) {
_, sym := r.localIdent() _, sym := r.localIdent()
typ := r.typ() typ := r.typ()
name := r.addLocal(pos, sym, ir.PAUTO, typ) name := r.curfn.NewLocal(pos, sym, typ)
r.addLocal(name)
return name, true return name, true
case assignExpr: case assignExpr:
@ -2076,10 +1969,8 @@ func (r *reader) switchStmt(label *types.Sym) ir.Node {
clause.RTypes = rtypes clause.RTypes = rtypes
if ident != nil { if ident != nil {
pos := r.pos() name := r.curfn.NewLocal(r.pos(), ident.Sym(), r.typ())
typ := r.typ() r.addLocal(name)
name := r.addLocal(pos, ident.Sym(), ir.PAUTO, typ)
clause.Var = name clause.Var = name
name.Defn = tag name.Defn = tag
} }
@ -2651,14 +2542,11 @@ func (r *reader) curry(origPos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1
typ := types.NewSignature(nil, params, results) typ := types.NewSignature(nil, params, results)
addBody := func(pos src.XPos, r *reader, captured []ir.Node) { addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
recvs, params := r.syntheticArgs(pos)
assert(len(recvs) == 0)
fun := captured[0] fun := captured[0]
var args ir.Nodes var args ir.Nodes
args.Append(captured[1:]...) args.Append(captured[1:]...)
args.Append(params...) args.Append(r.syntheticArgs()...)
r.syntheticTailCall(pos, fun, args) r.syntheticTailCall(pos, fun, args)
} }
@ -2689,10 +2577,8 @@ func (r *reader) methodExprWrap(origPos src.XPos, recv *types.Type, implicits []
typ := types.NewSignature(nil, params, results) typ := types.NewSignature(nil, params, results)
addBody := func(pos src.XPos, r *reader, captured []ir.Node) { addBody := func(pos src.XPos, r *reader, captured []ir.Node) {
recvs, args := r.syntheticArgs(pos)
assert(len(recvs) == 0)
fn := captured[0] fn := captured[0]
args := r.syntheticArgs()
// Rewrite first argument based on implicits/deref/addr. // Rewrite first argument based on implicits/deref/addr.
{ {
@ -2805,17 +2691,13 @@ func syntheticSig(sig *types.Type) (params, results []*types.Field) {
clone := func(params []*types.Field) []*types.Field { clone := func(params []*types.Field) []*types.Field {
res := make([]*types.Field, len(params)) res := make([]*types.Field, len(params))
for i, param := range params { for i, param := range params {
sym := param.Sym
if sym == nil || sym.Name == "_" {
sym = typecheck.LookupNum(".anon", i)
}
// TODO(mdempsky): It would be nice to preserve the original // TODO(mdempsky): It would be nice to preserve the original
// parameter positions here instead, but at least // parameter positions here instead, but at least
// typecheck.NewMethodType replaces them with base.Pos, making // typecheck.NewMethodType replaces them with base.Pos, making
// them useless. Worse, the positions copied from base.Pos may // them useless. Worse, the positions copied from base.Pos may
// have inlining contexts, which we definitely don't want here // have inlining contexts, which we definitely don't want here
// (e.g., #54625). // (e.g., #54625).
res[i] = types.NewField(base.AutogeneratedPos, sym, param.Type) res[i] = types.NewField(base.AutogeneratedPos, param.Sym, param.Type)
res[i].SetIsDDD(param.IsDDD()) res[i].SetIsDDD(param.IsDDD())
} }
return res return res
@ -3492,6 +3374,7 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
r.inlFunc = fn r.inlFunc = fn
r.inlTreeIndex = inlIndex r.inlTreeIndex = inlIndex
r.inlPosBases = make(map[*src.PosBase]*src.PosBase) r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
r.funarghack = true
r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars)) r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
for i, cv := range r.inlFunc.ClosureVars { for i, cv := range r.inlFunc.ClosureVars {
@ -3506,7 +3389,17 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit
} }
r.funcargs(fn) r.declareParams()
var inlvars, retvars []*ir.Name
{
sig := r.curfn.Type()
endParams := sig.NumRecvs() + sig.NumParams()
endResults := endParams + sig.NumResults()
inlvars = r.curfn.Dcl[:endParams]
retvars = r.curfn.Dcl[endParams:endResults]
}
r.delayResults = fn.Inl.CanDelayResults r.delayResults = fn.Inl.CanDelayResults
@ -3529,15 +3422,14 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
args.Append(call.Args...) args.Append(call.Args...)
// Create assignment to declare and initialize inlvars. // Create assignment to declare and initialize inlvars.
as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args) as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, ir.ToNodes(inlvars), args)
as2.Def = true as2.Def = true
var as2init ir.Nodes var as2init ir.Nodes
for _, name := range r.inlvars { for _, name := range inlvars {
if ir.IsBlank(name) { if ir.IsBlank(name) {
continue continue
} }
// TODO(mdempsky): Use inlined position of name.Pos() instead? // TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
name.Defn = as2 name.Defn = as2
} }
@ -3547,9 +3439,8 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
if !r.delayResults { if !r.delayResults {
// If not delaying retvars, declare and zero initialize the // If not delaying retvars, declare and zero initialize the
// result variables now. // result variables now.
for _, name := range r.retvars { for _, name := range retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead? // TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
ras := ir.NewAssignStmt(call.Pos(), name, nil) ras := ir.NewAssignStmt(call.Pos(), name, nil)
init.Append(typecheck.Stmt(ras)) init.Append(typecheck.Stmt(ras))
@ -3582,7 +3473,7 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
var edit func(ir.Node) ir.Node var edit func(ir.Node) ir.Node
edit = func(n ir.Node) ir.Node { edit = func(n ir.Node) ir.Node {
if ret, ok := n.(*ir.ReturnStmt); ok { if ret, ok := n.(*ir.ReturnStmt); ok {
n = typecheck.Stmt(r.inlReturn(ret)) n = typecheck.Stmt(r.inlReturn(ret, retvars))
} }
ir.EditChildren(n, edit) ir.EditChildren(n, edit)
return n return n
@ -3595,17 +3486,20 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
// Reparent any declarations into the caller function. // Reparent any declarations into the caller function.
for _, name := range r.curfn.Dcl { for _, name := range r.curfn.Dcl {
name.Curfn = callerfn name.Curfn = callerfn
callerfn.Dcl = append(callerfn.Dcl, name)
if name.AutoTemp() { if name.Class != ir.PAUTO {
name.SetEsc(ir.EscUnknown) name.SetPos(r.inlPos(name.Pos()))
name.SetInlFormal(true)
name.Class = ir.PAUTO
} else {
name.SetInlLocal(true) name.SetInlLocal(true)
} }
} }
callerfn.Dcl = append(callerfn.Dcl, r.curfn.Dcl...)
body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel)) body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...)) res := ir.NewInlinedCallExpr(call.Pos(), body, ir.ToNodes(retvars))
res.SetInit(init) res.SetInit(init)
res.SetType(call.Type()) res.SetType(call.Type())
res.SetTypecheck(1) res.SetTypecheck(1)
@ -3618,20 +3512,19 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
// inlReturn returns a statement that can substitute for the given // inlReturn returns a statement that can substitute for the given
// return statement when inlining. // return statement when inlining.
func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt { func (r *reader) inlReturn(ret *ir.ReturnStmt, retvars []*ir.Name) *ir.BlockStmt {
pos := r.inlCall.Pos() pos := r.inlCall.Pos()
block := ir.TakeInit(ret) block := ir.TakeInit(ret)
if results := ret.Results; len(results) != 0 { if results := ret.Results; len(results) != 0 {
assert(len(r.retvars) == len(results)) assert(len(retvars) == len(results))
as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results) as2 := ir.NewAssignListStmt(pos, ir.OAS2, ir.ToNodes(retvars), ret.Results)
if r.delayResults { if r.delayResults {
for _, name := range r.retvars { for _, name := range retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead? // TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
block.Append(ir.NewDecl(pos, ir.ODCL, name)) block.Append(ir.NewDecl(pos, ir.ODCL, name))
name.Defn = as2 name.Defn = as2
} }
@ -3667,18 +3560,11 @@ func expandInline(fn *ir.Func, pri pkgReaderIndex) {
r.funcBody(tmpfn) r.funcBody(tmpfn)
} }
used := usedLocals(tmpfn.Body) // Move tmpfn's params to fn.Inl.Dcl, and reparent under fn.
for _, name := range tmpfn.Dcl { for _, name := range tmpfn.Dcl {
if name.Class != ir.PAUTO || used.Has(name) { name.Curfn = fn
name.Curfn = fn
fn.Inl.Dcl = append(fn.Inl.Dcl, name)
} else {
// TODO(mdempsky): Simplify code after confident that this never
// happens anymore.
base.FatalfAt(name.Pos(), "unused auto: %v", name)
}
} }
fn.Inl.Dcl = tmpfn.Dcl
fn.Inl.HaveDcl = true fn.Inl.HaveDcl = true
// Double check that we didn't change fn.Dcl by accident. // Double check that we didn't change fn.Dcl by accident.
@ -3895,19 +3781,9 @@ func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *t
sig := newWrapperType(wrapper, method) sig := newWrapperType(wrapper, method)
fn := ir.NewFunc(pos, pos, sym, sig) fn := ir.NewFunc(pos, pos, sym, sig)
fn.DeclareParams(true)
fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
// TODO(mdempsky): De-duplicate with similar logic in funcargs.
defParams := func(class ir.Class, params []*types.Field) {
for _, param := range params {
param.Nname = fn.NewLocal(param.Pos, param.Sym, class, param.Type)
}
}
defParams(ir.PPARAM, sig.Recvs())
defParams(ir.PPARAM, sig.Params())
defParams(ir.PPARAMOUT, sig.Results())
return fn return fn
} }
@ -3946,11 +3822,7 @@ func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
clone := func(params []*types.Field) []*types.Field { clone := func(params []*types.Field) []*types.Field {
res := make([]*types.Field, len(params)) res := make([]*types.Field, len(params))
for i, param := range params { for i, param := range params {
sym := param.Sym res[i] = types.NewField(param.Pos, param.Sym, param.Type)
if sym == nil || sym.Name == "_" {
sym = typecheck.LookupNum(".anon", i)
}
res[i] = types.NewField(param.Pos, sym, param.Type)
res[i].SetIsDDD(param.IsDDD()) res[i].SetIsDDD(param.IsDDD())
} }
return res return res
@ -3960,7 +3832,7 @@ func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
var recv *types.Field var recv *types.Field
if recvType != nil { if recvType != nil {
recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType) recv = types.NewField(sig.Recv().Pos, sig.Recv().Sym, recvType)
} }
params := clone(sig.Params()) params := clone(sig.Params())
results := clone(sig.Results()) results := clone(sig.Results())

View file

@ -1114,7 +1114,7 @@ func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dic
w.sig = sig w.sig = sig
w.dict = dict w.dict = dict
w.funcargs(sig) w.declareParams(sig)
if w.Bool(block != nil) { if w.Bool(block != nil) {
w.stmts(block.List) w.stmts(block.List)
w.pos(block.Rbrace) w.pos(block.Rbrace)
@ -1123,24 +1123,18 @@ func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dic
return w.Flush(), w.closureVars return w.Flush(), w.closureVars
} }
func (w *writer) funcargs(sig *types2.Signature) { func (w *writer) declareParams(sig *types2.Signature) {
do := func(params *types2.Tuple, result bool) { addLocals := func(params *types2.Tuple) {
for i := 0; i < params.Len(); i++ { for i := 0; i < params.Len(); i++ {
w.funcarg(params.At(i), result) w.addLocal(params.At(i))
} }
} }
if recv := sig.Recv(); recv != nil { if recv := sig.Recv(); recv != nil {
w.funcarg(recv, false) w.addLocal(recv)
}
do(sig.Params(), false)
do(sig.Results(), true)
}
func (w *writer) funcarg(param *types2.Var, result bool) {
if param.Name() != "" || result {
w.addLocal(param)
} }
addLocals(sig.Params())
addLocals(sig.Results())
} }
// addLocal records the declaration of a new local variable. // addLocal records the declaration of a new local variable.

View file

@ -156,9 +156,9 @@ func hashFunc(t *types.Type) *ir.Func {
sym.Def = fn.Nname sym.Def = fn.Nname
fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining. fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining.
params, _ := typecheck.DeclFunc(fn) typecheck.DeclFunc(fn)
np := params[0] np := fn.Dcl[0]
nh := params[1] nh := fn.Dcl[1]
switch t.Kind() { switch t.Kind() {
case types.TARRAY: case types.TARRAY:
@ -382,10 +382,10 @@ func eqFunc(t *types.Type) *ir.Func {
sym.Def = fn.Nname sym.Def = fn.Nname
fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining. fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining.
params, results := typecheck.DeclFunc(fn) typecheck.DeclFunc(fn)
np := params[0] np := fn.Dcl[0]
nq := params[1] nq := fn.Dcl[1]
nr := results[0] nr := fn.Dcl[2]
// Label to jump to if an equality test fails. // Label to jump to if an equality test fails.
neq := typecheck.AutoLabel(".neq") neq := typecheck.AutoLabel(".neq")

View file

@ -55,16 +55,15 @@ type Conf struct {
func (c *Conf) Frontend() Frontend { func (c *Conf) Frontend() Frontend {
if c.fe == nil { if c.fe == nil {
f := ir.NewFunc(src.NoXPos, src.NoXPos, &types.Sym{ pkg := types.NewPkg("my/import/path", "path")
Pkg: types.NewPkg("my/import/path", "path"), fn := ir.NewFunc(src.NoXPos, src.NoXPos, pkg.Lookup("function"), types.NewSignature(nil, nil, nil))
Name: "function", fn.DeclareParams(true)
}, nil) fn.LSym = &obj.LSym{Name: "my/import/path.function"}
f.LSym = &obj.LSym{Name: "my/import/path.function"}
c.fe = TestFrontend{ c.fe = TestFrontend{
t: c.tb, t: c.tb,
ctxt: c.config.ctxt, ctxt: c.config.ctxt,
f: f, f: fn,
} }
} }
return c.fe return c.fe

View file

@ -249,8 +249,8 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
// Reuse f's types.Sym to create a new ODCLFUNC/function. // Reuse f's types.Sym to create a new ODCLFUNC/function.
// TODO(mdempsky): Means we can't set sym.Def in Declfunc, ugh. // TODO(mdempsky): Means we can't set sym.Def in Declfunc, ugh.
fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil, fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
typecheck.NewFuncParams(ft.Params(), true), typecheck.NewFuncParams(ft.Params()),
typecheck.NewFuncParams(ft.Results(), false))) typecheck.NewFuncParams(ft.Results())))
fn.ABI = wrapperABI fn.ABI = wrapperABI
typecheck.DeclFunc(fn) typecheck.DeclFunc(fn)

View file

@ -673,7 +673,7 @@ func (s *state) setHeapaddr(pos src.XPos, n *ir.Name, ptr *ssa.Value) {
// Declare variable to hold address. // Declare variable to hold address.
sym := &types.Sym{Name: "&" + n.Sym().Name, Pkg: types.LocalPkg} sym := &types.Sym{Name: "&" + n.Sym().Name, Pkg: types.LocalPkg}
addr := s.curfn.NewLocal(pos, sym, ir.PAUTO, types.NewPtr(n.Type())) addr := s.curfn.NewLocal(pos, sym, types.NewPtr(n.Type()))
addr.SetUsed(true) addr.SetUsed(true)
types.CalcSize(addr.Type()) types.CalcSize(addr.Type())
@ -7928,7 +7928,7 @@ func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t
} }
sym := &types.Sym{Name: node.Sym().Name + suffix, Pkg: types.LocalPkg} sym := &types.Sym{Name: node.Sym().Name + suffix, Pkg: types.LocalPkg}
n := e.curfn.NewLocal(parent.N.Pos(), sym, ir.PAUTO, t) n := e.curfn.NewLocal(parent.N.Pos(), sym, t)
n.SetUsed(true) n.SetUsed(true)
n.SetEsc(ir.EscNever) n.SetEsc(ir.EscNever)
types.CalcSize(t) types.CalcSize(t)

View file

@ -16,32 +16,18 @@ import (
var funcStack []*ir.Func // stack of previous values of ir.CurFunc var funcStack []*ir.Func // stack of previous values of ir.CurFunc
// DeclFunc creates and returns ONAMEs for the parameters and results // DeclFunc declares the parameters for fn and adds it to
// of the given function. It also sets ir.CurFunc, and adds fn to
// Target.Funcs. // Target.Funcs.
// //
// After the caller is done constructing fn, it must call // Before returning, it sets CurFunc to fn. When the caller is done
// FinishFuncBody. // constructing fn, it must call FinishFuncBody to restore CurFunc.
func DeclFunc(fn *ir.Func) (params, results []*ir.Name) { func DeclFunc(fn *ir.Func) {
typ := fn.Type() fn.DeclareParams(true)
// Currently, DeclFunc is only used to create normal functions, not
// methods. If a use case for creating methods shows up, we can
// extend it to support those too.
if typ.Recv() != nil {
base.FatalfAt(fn.Pos(), "unexpected receiver parameter")
}
params = declareParams(fn, ir.PPARAM, typ.Params())
results = declareParams(fn, ir.PPARAMOUT, typ.Results())
funcStack = append(funcStack, ir.CurFunc)
ir.CurFunc = fn
fn.Nname.Defn = fn fn.Nname.Defn = fn
Target.Funcs = append(Target.Funcs, fn) Target.Funcs = append(Target.Funcs, fn)
return funcStack = append(funcStack, ir.CurFunc)
ir.CurFunc = fn
} }
// FinishFuncBody restores ir.CurFunc to its state before the last // FinishFuncBody restores ir.CurFunc to its state before the last
@ -56,65 +42,29 @@ func CheckFuncStack() {
} }
} }
func declareParams(fn *ir.Func, ctxt ir.Class, params []*types.Field) []*ir.Name {
names := make([]*ir.Name, len(params))
for i, param := range params {
names[i] = declareParam(fn, ctxt, i, param)
}
return names
}
func declareParam(fn *ir.Func, ctxt ir.Class, i int, param *types.Field) *ir.Name {
sym := param.Sym
if ctxt == ir.PPARAMOUT {
if sym == nil {
// Name so that escape analysis can track it. ~r stands for 'result'.
sym = LookupNum("~r", i)
} else if sym.IsBlank() {
// Give it a name so we can assign to it during return. ~b stands for 'blank'.
// The name must be different from ~r above because if you have
// func f() (_ int)
// func g() int
// f is allowed to use a plain 'return' with no arguments, while g is not.
// So the two cases must be distinguished.
sym = LookupNum("~b", i)
}
}
if sym == nil {
return nil
}
name := fn.NewLocal(param.Pos, sym, ctxt, param.Type)
param.Nname = name
return name
}
// make a new Node off the books. // make a new Node off the books.
func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name { func TempAt(pos src.XPos, curfn *ir.Func, typ *types.Type) *ir.Name {
if curfn == nil { if curfn == nil {
base.Fatalf("no curfn for TempAt") base.FatalfAt(pos, "no curfn for TempAt")
} }
if t == nil { if typ == nil {
base.Fatalf("TempAt called with nil type") base.FatalfAt(pos, "TempAt called with nil type")
} }
if t.Kind() == types.TFUNC && t.Recv() != nil { if typ.Kind() == types.TFUNC && typ.Recv() != nil {
base.Fatalf("misuse of method type: %v", t) base.FatalfAt(pos, "misuse of method type: %v", typ)
} }
types.CalcSize(typ)
s := &types.Sym{ sym := &types.Sym{
Name: autotmpname(len(curfn.Dcl)), Name: autotmpname(len(curfn.Dcl)),
Pkg: types.LocalPkg, Pkg: types.LocalPkg,
} }
n := curfn.NewLocal(pos, s, ir.PAUTO, t) name := curfn.NewLocal(pos, sym, typ)
s.Def = n // TODO(mdempsky): Should be unnecessary. name.SetEsc(ir.EscNever)
n.SetEsc(ir.EscNever) name.SetUsed(true)
n.SetUsed(true) name.SetAutoTemp(true)
n.SetAutoTemp(true)
types.CalcSize(t) return name
return n
} }
var ( var (

View file

@ -224,6 +224,7 @@ func tcGoDefer(n *ir.GoDeferStmt) {
// Create a new wrapper function without parameters or results. // Create a new wrapper function without parameters or results.
wrapperFn := ir.NewClosureFunc(n.Pos(), n.Pos(), n.Op(), types.NewSignature(nil, nil, nil), ir.CurFunc, Target) wrapperFn := ir.NewClosureFunc(n.Pos(), n.Pos(), n.Op(), types.NewSignature(nil, nil, nil), ir.CurFunc, Target)
wrapperFn.DeclareParams(true)
wrapperFn.SetWrapper(true) wrapperFn.SetWrapper(true)
// argps collects the list of operands within the call expression // argps collects the list of operands within the call expression

View file

@ -26,18 +26,10 @@ func LookupNum(prefix string, n int) *types.Sym {
} }
// Given funarg struct list, return list of fn args. // Given funarg struct list, return list of fn args.
func NewFuncParams(origs []*types.Field, mustname bool) []*types.Field { func NewFuncParams(origs []*types.Field) []*types.Field {
res := make([]*types.Field, len(origs)) res := make([]*types.Field, len(origs))
for i, orig := range origs { for i, orig := range origs {
s := orig.Sym p := types.NewField(orig.Pos, orig.Sym, orig.Type)
if mustname && (s == nil || s.Name == "_") {
// invent a name so that we can refer to it in the trampoline
s = LookupNum(".anon", i)
} else if s != nil && s.Pkg != types.LocalPkg {
// TODO(mdempsky): Preserve original position, name, and package.
s = Lookup(s.Name)
}
p := types.NewField(orig.Pos, s, orig.Type)
p.SetIsDDD(orig.IsDDD()) p.SetIsDDD(orig.IsDDD())
res[i] = p res[i] = p
} }

View file

@ -1,5 +1,6 @@
// errorcheck -0 -m -live -std // errorcheck -0 -m -live -std
//go:build !windows && !js && !wasip1
// +build !windows,!js,!wasip1 // +build !windows,!js,!wasip1
// Copyright 2015 The Go Authors. All rights reserved. // Copyright 2015 The Go Authors. All rights reserved.
@ -22,7 +23,7 @@ import (
"unsafe" "unsafe"
) )
func implicit(uintptr) // ERROR "assuming arg#1 is unsafe uintptr" func implicit(uintptr) // ERROR "assuming ~p0 is unsafe uintptr"
//go:uintptrkeepalive //go:uintptrkeepalive
//go:nosplit //go:nosplit
@ -47,13 +48,13 @@ func autotmpSyscall() { // ERROR "can inline autotmpSyscall"
func localImplicit() { // ERROR "can inline localImplicit" func localImplicit() { // ERROR "can inline localImplicit"
var t int var t int
p := unsafe.Pointer(&t) p := unsafe.Pointer(&t)
implicit(uintptr(p)) // ERROR "live at call to implicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" implicit(uintptr(p)) // ERROR "live at call to implicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
} }
func localExplicit() { // ERROR "can inline localExplicit" func localExplicit() { // ERROR "can inline localExplicit"
var t int var t int
p := unsafe.Pointer(&t) p := unsafe.Pointer(&t)
explicit(uintptr(p)) // ERROR "live at call to explicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" explicit(uintptr(p)) // ERROR "live at call to explicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
} }
func localSyscall() { // ERROR "can inline localSyscall" func localSyscall() { // ERROR "can inline localSyscall"