mirror of
https://github.com/golang/go.git
synced 2025-10-28 15:24:13 +00:00
[dev.regabi] cmd/compile: fix OCALLMETH desugaring
During walkCall, there's a half-hearted attempt at rewriting OCALLMETH expressions into regular function calls by moving the receiver argument into n.Args with the rest of the arguments. But the way it does this leaves the AST in an inconsistent state (an ODOTMETH node with no X expression), and leaves a lot of duplicate work for the rest of the backend to deal with. By simply rewriting OCALLMETH expressions into proper OCALLFUNC expressions, we eliminate a ton of unnecessary code duplication during SSA construction and avoid creation of invalid method-typed variables. Passes toolstash -cmp. Change-Id: I4d5c5f90a79f8994059b2d0ae472182e08096c0a Reviewed-on: https://go-review.googlesource.com/c/go/+/280294 Trust: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
1d9a1f67d5
commit
e4f293d853
4 changed files with 24 additions and 67 deletions
|
|
@ -836,8 +836,7 @@ func TypeSym(t *types.Type) *types.Sym {
|
||||||
base.Fatalf("typenamesym %v", t)
|
base.Fatalf("typenamesym %v", t)
|
||||||
}
|
}
|
||||||
if t.Kind() == types.TFUNC && t.Recv() != nil {
|
if t.Kind() == types.TFUNC && t.Recv() != nil {
|
||||||
// TODO(mdempsky): Fix callers and make fatal.
|
base.Fatalf("misuse of method type: %v", t)
|
||||||
t = typecheck.NewMethodType(t, t.Recv().Type)
|
|
||||||
}
|
}
|
||||||
s := types.TypeSym(t)
|
s := types.TypeSym(t)
|
||||||
signatmu.Lock()
|
signatmu.Lock()
|
||||||
|
|
|
||||||
|
|
@ -214,10 +214,7 @@ func InitConfig() {
|
||||||
func getParam(n *ir.CallExpr, i int) *types.Field {
|
func getParam(n *ir.CallExpr, i int) *types.Field {
|
||||||
t := n.X.Type()
|
t := n.X.Type()
|
||||||
if n.Op() == ir.OCALLMETH {
|
if n.Op() == ir.OCALLMETH {
|
||||||
if i == 0 {
|
base.Fatalf("OCALLMETH missed by walkCall")
|
||||||
return t.Recv()
|
|
||||||
}
|
|
||||||
return t.Params().Field(i - 1)
|
|
||||||
}
|
}
|
||||||
return t.Params().Field(i)
|
return t.Params().Field(i)
|
||||||
}
|
}
|
||||||
|
|
@ -1166,7 +1163,7 @@ func (s *state) stmt(n ir.Node) {
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
case ir.OCALLMETH, ir.OCALLINTER:
|
case ir.OCALLINTER:
|
||||||
n := n.(*ir.CallExpr)
|
n := n.(*ir.CallExpr)
|
||||||
s.callResult(n, callNormal)
|
s.callResult(n, callNormal)
|
||||||
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class_ == ir.PFUNC {
|
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class_ == ir.PFUNC {
|
||||||
|
|
@ -4396,16 +4393,7 @@ func (s *state) openDeferRecord(n *ir.CallExpr) {
|
||||||
opendefer.closure = closure
|
opendefer.closure = closure
|
||||||
}
|
}
|
||||||
} else if n.Op() == ir.OCALLMETH {
|
} else if n.Op() == ir.OCALLMETH {
|
||||||
if fn.Op() != ir.ODOTMETH {
|
base.Fatalf("OCALLMETH missed by walkCall")
|
||||||
base.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
|
|
||||||
}
|
|
||||||
fn := fn.(*ir.SelectorExpr)
|
|
||||||
closureVal := s.getMethodClosure(fn)
|
|
||||||
// We must always store the function value in a stack slot for the
|
|
||||||
// runtime panic code to use. But in the defer exit code, we will
|
|
||||||
// call the method directly.
|
|
||||||
closure := s.openDeferSave(nil, fn.Type(), closureVal)
|
|
||||||
opendefer.closureNode = closure.Aux.(*ir.Name)
|
|
||||||
} else {
|
} else {
|
||||||
if fn.Op() != ir.ODOTINTER {
|
if fn.Op() != ir.ODOTINTER {
|
||||||
base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
|
base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
|
||||||
|
|
@ -4679,18 +4667,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||||
s.maybeNilCheckClosure(closure, k)
|
s.maybeNilCheckClosure(closure, k)
|
||||||
}
|
}
|
||||||
case ir.OCALLMETH:
|
case ir.OCALLMETH:
|
||||||
if fn.Op() != ir.ODOTMETH {
|
base.Fatalf("OCALLMETH missed by walkCall")
|
||||||
s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
|
|
||||||
}
|
|
||||||
fn := fn.(*ir.SelectorExpr)
|
|
||||||
testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
|
|
||||||
if k == callNormal {
|
|
||||||
sym = fn.Sel
|
|
||||||
break
|
|
||||||
}
|
|
||||||
closure = s.getMethodClosure(fn)
|
|
||||||
// Note: receiver is already present in n.Rlist, so we don't
|
|
||||||
// want to set it here.
|
|
||||||
case ir.OCALLINTER:
|
case ir.OCALLINTER:
|
||||||
if fn.Op() != ir.ODOTINTER {
|
if fn.Op() != ir.ODOTINTER {
|
||||||
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
|
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
|
||||||
|
|
@ -4755,9 +4732,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||||
}
|
}
|
||||||
// Set receiver (for method calls).
|
// Set receiver (for method calls).
|
||||||
if n.Op() == ir.OCALLMETH {
|
if n.Op() == ir.OCALLMETH {
|
||||||
f := ft.Recv()
|
base.Fatalf("OCALLMETH missed by walkCall")
|
||||||
s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
|
|
||||||
args = args[1:]
|
|
||||||
}
|
}
|
||||||
// Set other args.
|
// Set other args.
|
||||||
for _, f := range ft.Params().Fields().Slice() {
|
for _, f := range ft.Params().Fields().Slice() {
|
||||||
|
|
@ -4825,11 +4800,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
|
||||||
t := n.X.Type()
|
t := n.X.Type()
|
||||||
args := n.Rargs
|
args := n.Rargs
|
||||||
if n.Op() == ir.OCALLMETH {
|
if n.Op() == ir.OCALLMETH {
|
||||||
f := t.Recv()
|
base.Fatalf("OCALLMETH missed by walkCall")
|
||||||
ACArg, arg := s.putArg(args[0], f.Type, argStart+f.Offset, testLateExpansion)
|
|
||||||
ACArgs = append(ACArgs, ACArg)
|
|
||||||
callArgs = append(callArgs, arg)
|
|
||||||
args = args[1:]
|
|
||||||
}
|
}
|
||||||
for i, n := range args {
|
for i, n := range args {
|
||||||
f := t.Params().Field(i)
|
f := t.Params().Field(i)
|
||||||
|
|
@ -4947,22 +4918,6 @@ func (s *state) maybeNilCheckClosure(closure *ssa.Value, k callKind) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMethodClosure returns a value representing the closure for a method call
|
|
||||||
func (s *state) getMethodClosure(fn *ir.SelectorExpr) *ssa.Value {
|
|
||||||
// Make a name n2 for the function.
|
|
||||||
// fn.Sym might be sync.(*Mutex).Unlock.
|
|
||||||
// Make a PFUNC node out of that, then evaluate it.
|
|
||||||
// We get back an SSA value representing &sync.(*Mutex).Unlock·f.
|
|
||||||
// We can then pass that to defer or go.
|
|
||||||
n2 := ir.NewNameAt(fn.Pos(), fn.Sel)
|
|
||||||
n2.Curfn = s.curfn
|
|
||||||
n2.Class_ = ir.PFUNC
|
|
||||||
// n2.Sym already existed, so it's already marked as a function.
|
|
||||||
n2.SetPos(fn.Pos())
|
|
||||||
n2.SetType(types.Types[types.TUINT8]) // fake type for a static closure. Could use runtime.funcval if we had it.
|
|
||||||
return s.expr(n2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getClosureAndRcvr returns values for the appropriate closure and receiver of an
|
// getClosureAndRcvr returns values for the appropriate closure and receiver of an
|
||||||
// interface call
|
// interface call
|
||||||
func (s *state) getClosureAndRcvr(fn *ir.SelectorExpr) (*ssa.Value, *ssa.Value) {
|
func (s *state) getClosureAndRcvr(fn *ir.SelectorExpr) (*ssa.Value, *ssa.Value) {
|
||||||
|
|
@ -5089,7 +5044,7 @@ func (s *state) addr(n ir.Node) *ssa.Value {
|
||||||
}
|
}
|
||||||
addr := s.addr(n.X)
|
addr := s.addr(n.X)
|
||||||
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
|
return s.newValue1(ssa.OpCopy, t, addr) // ensure that addr has the right type
|
||||||
case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
|
case ir.OCALLFUNC, ir.OCALLINTER:
|
||||||
n := n.(*ir.CallExpr)
|
n := n.(*ir.CallExpr)
|
||||||
return s.callAddr(n, callNormal)
|
return s.callAddr(n, callNormal)
|
||||||
case ir.ODOTTYPE:
|
case ir.ODOTTYPE:
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,9 @@ func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
base.Fatalf("tempAt called with nil type")
|
base.Fatalf("tempAt called with nil type")
|
||||||
}
|
}
|
||||||
|
if t.Kind() == types.TFUNC && t.Recv() != nil {
|
||||||
|
base.Fatalf("misuse of method type: %v", t)
|
||||||
|
}
|
||||||
|
|
||||||
s := &types.Sym{
|
s := &types.Sym{
|
||||||
Name: autotmpname(len(curfn.Dcl)),
|
Name: autotmpname(len(curfn.Dcl)),
|
||||||
|
|
|
||||||
|
|
@ -535,22 +535,31 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
|
||||||
return // already walked
|
return // already walked
|
||||||
}
|
}
|
||||||
|
|
||||||
params := n.X.Type().Params()
|
|
||||||
args := n.Args
|
args := n.Args
|
||||||
|
|
||||||
n.X = walkExpr(n.X, init)
|
n.X = walkExpr(n.X, init)
|
||||||
walkExprList(args, init)
|
walkExprList(args, init)
|
||||||
|
|
||||||
// If this is a method call, add the receiver at the beginning of the args.
|
// If this is a method call t.M(...),
|
||||||
|
// rewrite into a function call T.M(t, ...).
|
||||||
|
// TODO(mdempsky): Do this right after type checking.
|
||||||
if n.Op() == ir.OCALLMETH {
|
if n.Op() == ir.OCALLMETH {
|
||||||
withRecv := make([]ir.Node, len(args)+1)
|
withRecv := make([]ir.Node, len(args)+1)
|
||||||
dot := n.X.(*ir.SelectorExpr)
|
dot := n.X.(*ir.SelectorExpr)
|
||||||
withRecv[0] = dot.X
|
withRecv[0] = dot.X
|
||||||
dot.X = nil
|
|
||||||
copy(withRecv[1:], args)
|
copy(withRecv[1:], args)
|
||||||
args = withRecv
|
args = withRecv
|
||||||
|
|
||||||
|
dot = ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym)
|
||||||
|
fn := typecheck.Expr(dot).(*ir.MethodExpr).FuncName()
|
||||||
|
fn.Type().Size()
|
||||||
|
|
||||||
|
n.SetOp(ir.OCALLFUNC)
|
||||||
|
n.X = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params := n.X.Type().Params()
|
||||||
|
|
||||||
// For any argument whose evaluation might require a function call,
|
// For any argument whose evaluation might require a function call,
|
||||||
// store that argument into a temporary variable,
|
// store that argument into a temporary variable,
|
||||||
// to prevent that calls from clobbering arguments already on the stack.
|
// to prevent that calls from clobbering arguments already on the stack.
|
||||||
|
|
@ -559,16 +568,7 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
updateHasCall(arg)
|
updateHasCall(arg)
|
||||||
// Determine param type.
|
// Determine param type.
|
||||||
var t *types.Type
|
t := params.Field(i).Type
|
||||||
if n.Op() == ir.OCALLMETH {
|
|
||||||
if i == 0 {
|
|
||||||
t = n.X.Type().Recv().Type
|
|
||||||
} else {
|
|
||||||
t = params.Field(i - 1).Type
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t = params.Field(i).Type
|
|
||||||
}
|
|
||||||
if base.Flag.Cfg.Instrumenting || fncall(arg, t) {
|
if base.Flag.Cfg.Instrumenting || fncall(arg, t) {
|
||||||
// make assignment of fncall to tempAt
|
// make assignment of fncall to tempAt
|
||||||
tmp := typecheck.Temp(t)
|
tmp := typecheck.Temp(t)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue