mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Lack of a well-defined order between VarDef and related address operations sometimes causes problems with store order and write barrier transformations; glitches in the order are made irreparable (by later optimizations) if the two parts of the glitch straddle a split in the original block caused by insertion of a write barrier diamond. Fix this by creating a LocalAddr for addresses of locals (what VarDef matters for) that takes a memory input to help make the order explicit. Addr is modified to only be legal for SB operand, so there is no overlap between Addr and LocalAddr uses (there may be some downstream cleanup from this). Changes to generic.rules and rewrite.go ensure that codegen tests continue to pass; CSE of LocalAddr is impaired, not quite sure of the cost. Fixes #26105. Change-Id: Id4192b4440aa4e9d7ba54a465c456df9b530b515 Reviewed-on: https://go-review.googlesource.com/122483 Run-TryBot: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@golang.org>
126 lines
4.1 KiB
Go
126 lines
4.1 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssa
|
|
|
|
import (
|
|
"cmd/compile/internal/types"
|
|
"testing"
|
|
)
|
|
|
|
type tstAux struct {
|
|
s string
|
|
}
|
|
|
|
// This tests for a bug found when partitioning, but not sorting by the Aux value.
|
|
func TestCSEAuxPartitionBug(t *testing.T) {
|
|
c := testConfig(t)
|
|
arg1Aux := &tstAux{"arg1-aux"}
|
|
arg2Aux := &tstAux{"arg2-aux"}
|
|
arg3Aux := &tstAux{"arg3-aux"}
|
|
|
|
// construct lots of values with args that have aux values and place
|
|
// them in an order that triggers the bug
|
|
fun := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("start", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
|
|
Valu("r7", OpAdd64, c.config.Types.Int64, 0, nil, "arg3", "arg1"),
|
|
Valu("r1", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"),
|
|
Valu("arg1", OpArg, c.config.Types.Int64, 0, arg1Aux),
|
|
Valu("arg2", OpArg, c.config.Types.Int64, 0, arg2Aux),
|
|
Valu("arg3", OpArg, c.config.Types.Int64, 0, arg3Aux),
|
|
Valu("r9", OpAdd64, c.config.Types.Int64, 0, nil, "r7", "r8"),
|
|
Valu("r4", OpAdd64, c.config.Types.Int64, 0, nil, "r1", "r2"),
|
|
Valu("r8", OpAdd64, c.config.Types.Int64, 0, nil, "arg3", "arg2"),
|
|
Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"),
|
|
Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"),
|
|
Valu("raddrdef", OpVarDef, types.TypeMem, 0, nil, "start"),
|
|
Valu("r6", OpAdd64, c.config.Types.Int64, 0, nil, "r4", "r5"),
|
|
Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"),
|
|
Valu("r5", OpAdd64, c.config.Types.Int64, 0, nil, "r2", "r3"),
|
|
Valu("r10", OpAdd64, c.config.Types.Int64, 0, nil, "r6", "r9"),
|
|
Valu("rstore", OpStore, types.TypeMem, 0, c.config.Types.Int64, "raddr", "r10", "raddrdef"),
|
|
Goto("exit")),
|
|
Bloc("exit",
|
|
Exit("rstore")))
|
|
|
|
CheckFunc(fun.f)
|
|
cse(fun.f)
|
|
deadcode(fun.f)
|
|
CheckFunc(fun.f)
|
|
|
|
s1Cnt := 2
|
|
// r1 == r2 == r3, needs to remove two of this set
|
|
s2Cnt := 1
|
|
// r4 == r5, needs to remove one of these
|
|
for k, v := range fun.values {
|
|
if v.Op == OpInvalid {
|
|
switch k {
|
|
case "r1":
|
|
fallthrough
|
|
case "r2":
|
|
fallthrough
|
|
case "r3":
|
|
if s1Cnt == 0 {
|
|
t.Errorf("cse removed all of r1,r2,r3")
|
|
}
|
|
s1Cnt--
|
|
|
|
case "r4":
|
|
fallthrough
|
|
case "r5":
|
|
if s2Cnt == 0 {
|
|
t.Errorf("cse removed all of r4,r5")
|
|
}
|
|
s2Cnt--
|
|
default:
|
|
t.Errorf("cse removed %s, but shouldn't have", k)
|
|
}
|
|
}
|
|
}
|
|
|
|
if s1Cnt != 0 || s2Cnt != 0 {
|
|
t.Errorf("%d values missed during cse", s1Cnt+s2Cnt)
|
|
}
|
|
}
|
|
|
|
// TestZCSE tests the zero arg cse.
|
|
func TestZCSE(t *testing.T) {
|
|
c := testConfig(t)
|
|
|
|
fun := c.Fun("entry",
|
|
Bloc("entry",
|
|
Valu("start", OpInitMem, types.TypeMem, 0, nil),
|
|
Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
|
|
Valu("sb1", OpSB, c.config.Types.Uintptr, 0, nil),
|
|
Valu("sb2", OpSB, c.config.Types.Uintptr, 0, nil),
|
|
Valu("addr1", OpAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sb1"),
|
|
Valu("addr2", OpAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sb2"),
|
|
Valu("a1ld", OpLoad, c.config.Types.Int64, 0, nil, "addr1", "start"),
|
|
Valu("a2ld", OpLoad, c.config.Types.Int64, 0, nil, "addr2", "start"),
|
|
Valu("c1", OpConst64, c.config.Types.Int64, 1, nil),
|
|
Valu("r1", OpAdd64, c.config.Types.Int64, 0, nil, "a1ld", "c1"),
|
|
Valu("c2", OpConst64, c.config.Types.Int64, 1, nil),
|
|
Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "a2ld", "c2"),
|
|
Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "r1", "r2"),
|
|
Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"),
|
|
Valu("raddrdef", OpVarDef, types.TypeMem, 0, nil, "start"),
|
|
Valu("rstore", OpStore, types.TypeMem, 0, c.config.Types.Int64, "raddr", "r3", "raddrdef"),
|
|
Goto("exit")),
|
|
Bloc("exit",
|
|
Exit("rstore")))
|
|
|
|
CheckFunc(fun.f)
|
|
zcse(fun.f)
|
|
deadcode(fun.f)
|
|
CheckFunc(fun.f)
|
|
|
|
if fun.values["c1"].Op != OpInvalid && fun.values["c2"].Op != OpInvalid {
|
|
t.Errorf("zsce should have removed c1 or c2")
|
|
}
|
|
if fun.values["sb1"].Op != OpInvalid && fun.values["sb2"].Op != OpInvalid {
|
|
t.Errorf("zsce should have removed sb1 or sb2")
|
|
}
|
|
}
|