Commit 8e36575e authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: don't mutate shared nodes in orderinit

A few gc.Node ops may be shared across functions.
The compiler is (mostly) already careful to avoid mutating them.
However, from a concurrency perspective, replacing (say)
an empty list with an empty list still counts as a mutation.
One place this occurs is orderinit. Avoid it.

This requires fixing one spot where shared nodes were mutated.
It doesn't result in any functional or performance changes.

Passes toolstash-check.

Updates #15756

Change-Id: I63c93b31baeeac62d7574804acb6b7f2bc9d14a9
Reviewed-on: https://go-review.googlesource.com/39196
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent a8b2e4a6
...@@ -314,6 +314,14 @@ func orderstmtinplace(n *Node) *Node { ...@@ -314,6 +314,14 @@ func orderstmtinplace(n *Node) *Node {
// Orderinit moves n's init list to order->out. // Orderinit moves n's init list to order->out.
func orderinit(n *Node, order *Order) { func orderinit(n *Node, order *Order) {
if n.mayBeShared() {
// For concurrency safety, don't mutate potentially shared nodes.
// First, ensure that no work is required here.
if n.Ninit.Len() > 0 {
Fatalf("orderinit shared node with ninit")
}
return
}
orderstmtlist(n.Ninit, order) orderstmtlist(n.Ninit, order)
n.Ninit.Set(nil) n.Ninit.Set(nil)
} }
...@@ -1107,7 +1115,7 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node { ...@@ -1107,7 +1115,7 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
var s []*Node var s []*Node
cleantempnopop(mark, order, &s) cleantempnopop(mark, order, &s)
n.Right.Ninit.Prepend(s...) n.Right = addinit(n.Right, s)
n.Right = orderexprinplace(n.Right, order) n.Right = orderexprinplace(n.Right, order)
case OCALLFUNC, case OCALLFUNC,
......
...@@ -2043,11 +2043,8 @@ func addinit(n *Node, init []*Node) *Node { ...@@ -2043,11 +2043,8 @@ func addinit(n *Node, init []*Node) *Node {
if len(init) == 0 { if len(init) == 0 {
return n return n
} }
if n.mayBeShared() {
switch n.Op { // Introduce OCONVNOP to hold init list.
// There may be multiple refs to this node;
// introduce OCONVNOP to hold init list.
case ONAME, OLITERAL:
n = nod(OCONVNOP, n, nil) n = nod(OCONVNOP, n, nil)
n.Type = n.Left.Type n.Type = n.Left.Type
n.Typecheck = 1 n.Typecheck = 1
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
// A Node is a single node in the syntax tree. // A Node is a single node in the syntax tree.
// Actually the syntax tree is a syntax DAG, because there is only one // Actually the syntax tree is a syntax DAG, because there is only one
// node with Op=ONAME for a given instance of a variable x. // node with Op=ONAME for a given instance of a variable x.
// The same is true for Op=OTYPE and Op=OLITERAL. // The same is true for Op=OTYPE and Op=OLITERAL. See Node.mayBeShared.
type Node struct { type Node struct {
// Tree structure. // Tree structure.
// Generic recursive walks should follow these fields. // Generic recursive walks should follow these fields.
...@@ -179,6 +179,16 @@ func (n *Node) SetIota(x int64) { ...@@ -179,6 +179,16 @@ func (n *Node) SetIota(x int64) {
n.Xoffset = x n.Xoffset = x
} }
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func (n *Node) mayBeShared() bool {
switch n.Op {
case ONAME, OLITERAL, OTYPE:
return true
}
return false
}
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL). // Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
type Name struct { type Name struct {
Pack *Node // real package for import . names Pack *Node // real package for import . names
......
...@@ -500,6 +500,7 @@ opswitch: ...@@ -500,6 +500,7 @@ opswitch:
case OTYPE, ONAME, OLITERAL: case OTYPE, ONAME, OLITERAL:
// TODO(mdempsky): Just return n; see discussion on CL 38655. // TODO(mdempsky): Just return n; see discussion on CL 38655.
// Perhaps refactor to use Node.mayBeShared for these instead.
case ONOT, OMINUS, OPLUS, OCOM, OREAL, OIMAG, ODOTMETH, ODOTINTER, case ONOT, OMINUS, OPLUS, OCOM, OREAL, OIMAG, ODOTMETH, ODOTINTER,
OIND, OSPTR, OITAB, OIDATA, OADDR: OIND, OSPTR, OITAB, OIDATA, OADDR:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment