1
0
mirror of https://github.com/golang/go synced 2024-11-26 07:17:59 -07:00

cmd/compile: fix and improve alias detection

"aliased" is the function responsible for detecting whether we can
turn "a, b = x, y" into just "a = x; b = y", or we need to pre-compute
y and save it in a temporary variable because it might depend on a.

It currently has two issues:

1. It suboptimally treats assignments to blank as writes to heap
   memory. Users generally won't write "_, b = x, y" directly, but it
   comes up a lot in generated code within the compiler.

   This CL changes it to ignore blank assignments.

2. When deciding whether the assigned variable might be referenced by
   pointers, it mistakenly checks Class() and Name.Addrtaken() on "n"
   (the *value* expression being assigned) rather than "a" (the
   destination expression).

   It doesn't appear to result in correctness issues (i.e.,
   incorrectly reporting no aliasing when there is potential aliasing),
   due to all the (overly conservative) rewrite passes before code
   reaches here. But it generates unnecessary code and could have
   correctness issues if we improve those other passes to be more
   aggressive.

   This CL fixes the misuse of "n" for "a" by renaming the variables
   to "r" and "l", respectively, to make their meaning clearer.

Improving these two cases shaves 4.6kB of text from cmd/go, and 93kB
from k8s.io/kubernetes/cmd/kubelet:

       text	   data	    bss	    dec	    hex	filename
    9732136	 290072	 231552	10253760	 9c75c0	go.before
    9727542	 290072	 231552	10249166	 9c63ce	go.after
    97977637	1007051	 301344	99286032	5eafc10	kubelet.before
    97884549	1007051	 301344	99192944	5e99070	kubelet.after

While here, this CL also collapses "memwrite" and "varwrite" into a
single variable. Logically, they're detecting the same thing: are we
assigning to a memory location that a pointer might alias. There's no
need for two variables.

Updates #6853.
Updates #23017.

Change-Id: I5a307b8e20bcd2196e85c55eb025d3f01e303008
Reviewed-on: https://go-review.googlesource.com/c/go/+/261677
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Matthew Dempsky 2020-10-12 14:02:36 -07:00
parent c9211577eb
commit 85bb4294c0

View File

@ -2157,7 +2157,7 @@ func reorder3(all []*Node) []*Node {
// The result of reorder3save MUST be assigned back to n, e.g.
// n.Left = reorder3save(n.Left, all, i, early)
func reorder3save(n *Node, all []*Node, i int, early *[]*Node) *Node {
if !aliased(n, all, i) {
if !aliased(n, all[:i]) {
return n
}
@ -2189,73 +2189,75 @@ func outervalue(n *Node) *Node {
}
}
// Is it possible that the computation of n might be
// affected by writes in as up to but not including the ith element?
func aliased(n *Node, all []*Node, i int) bool {
if n == nil {
// Is it possible that the computation of r might be
// affected by assignments in all?
func aliased(r *Node, all []*Node) bool {
if r == nil {
return false
}
// Treat all fields of a struct as referring to the whole struct.
// We could do better but we would have to keep track of the fields.
for n.Op == ODOT {
n = n.Left
for r.Op == ODOT {
r = r.Left
}
// Look for obvious aliasing: a variable being assigned
// during the all list and appearing in n.
// Also record whether there are any writes to main memory.
// Also record whether there are any writes to variables
// whose addresses have been taken.
// Also record whether there are any writes to addressable
// memory (either main memory or variables whose addresses
// have been taken).
memwrite := false
varwrite := false
for _, an := range all[:i] {
a := outervalue(an.Left)
for a.Op == ODOT {
a = a.Left
for _, as := range all {
// We can ignore assignments to blank.
if as.Left.isBlank() {
continue
}
if a.Op != ONAME {
l := outervalue(as.Left)
if l.Op != ONAME {
memwrite = true
continue
}
switch n.Class() {
switch l.Class() {
default:
varwrite = true
Fatalf("unexpected class: %v, %v", l, l.Class())
case PAUTOHEAP, PEXTERN:
memwrite = true
continue
case PAUTO, PPARAM, PPARAMOUT:
if n.Name.Addrtaken() {
varwrite = true
if l.Name.Addrtaken() {
memwrite = true
continue
}
if vmatch2(a, n) {
// Direct hit.
if vmatch2(l, r) {
// Direct hit: l appears in r.
return true
}
}
}
// The variables being written do not appear in n.
// However, n might refer to computed addresses
// The variables being written do not appear in r.
// However, r might refer to computed addresses
// that are being written.
// If no computed addresses are affected by the writes, no aliasing.
if !memwrite && !varwrite {
if !memwrite {
return false
}
// If n does not refer to computed addresses
// (that is, if n only refers to variables whose addresses
// If r does not refer to computed addresses
// (that is, if r only refers to variables whose addresses
// have not been taken), no aliasing.
if varexpr(n) {
if varexpr(r) {
return false
}
// Otherwise, both the writes and n refer to computed memory addresses.
// Otherwise, both the writes and r refer to computed memory addresses.
// Assume that they might conflict.
return true
}