cmd/compile: add LocalAddr that takes SP,mem operands

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>
This commit is contained in:
David Chase 2018-07-03 11:34:38 -04:00
parent bb364d49a9
commit 0029cd479e
34 changed files with 555 additions and 51 deletions

View File

@ -162,7 +162,7 @@ func buildssa(fn *Node, worker int) *ssa.Func {
for _, n := range fn.Func.Dcl {
switch n.Class() {
case PPARAM, PPARAMOUT:
s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, types.NewPtr(n.Type), n, s.sp)
s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type), n, s.sp, s.startmem)
if n.Class() == PPARAMOUT && s.canSSA(n) {
// Save ssa-able PPARAMOUT variables so we can
// store them back to the stack at the end of
@ -454,6 +454,16 @@ func (s *state) newValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value) *ssa.
return s.curBlock.NewValue2(s.peekPos(), op, t, arg0, arg1)
}
// newValue2Apos adds a new value with two arguments and an aux value to the current block.
// isStmt determines whether the created values may be a statement or not
// (i.e., false means never, yes means maybe).
func (s *state) newValue2Apos(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value, isStmt bool) *ssa.Value {
if isStmt {
return s.curBlock.NewValue2A(s.peekPos(), op, t, aux, arg0, arg1)
}
return s.curBlock.NewValue2A(s.peekPos().WithNotStmt(), op, t, aux, arg0, arg1)
}
// newValue2I adds a new value with two arguments and an auxint value to the current block.
func (s *state) newValue2I(op ssa.Op, t *types.Type, aux int64, arg0, arg1 *ssa.Value) *ssa.Value {
return s.curBlock.NewValue2I(s.peekPos(), op, t, aux, arg0, arg1)
@ -519,6 +529,11 @@ func (s *state) entryNewValue2(op ssa.Op, t *types.Type, arg0, arg1 *ssa.Value)
return s.f.Entry.NewValue2(src.NoXPos, op, t, arg0, arg1)
}
// entryNewValue2A adds a new value with two arguments and an aux value to the entry block.
func (s *state) entryNewValue2A(op ssa.Op, t *types.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue2A(src.NoXPos, op, t, aux, arg0, arg1)
}
// const* routines add a new const value to the entry block.
func (s *state) constSlice(t *types.Type) *ssa.Value {
return s.f.ConstSlice(t)
@ -2584,10 +2599,10 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
return
}
// Left is not ssa-able. Compute its address.
addr := s.addr(left, false)
if left.Op == ONAME && left.Class() != PEXTERN && skip == 0 {
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, left, s.mem(), !left.IsAutoTmp())
}
addr := s.addr(left, false)
if isReflectHeaderDataField(left) {
// Package unsafe's documentation says storing pointers into
// reflect.SliceHeader and reflect.StringHeader's Data fields
@ -3655,16 +3670,17 @@ func (s *state) addr(n *Node, bounded bool) *ssa.Value {
}
if n == nodfp {
// Special arg that points to the frame pointer (Used by ORECOVER).
return s.entryNewValue1A(ssa.OpAddr, t, n, s.sp)
return s.entryNewValue2A(ssa.OpLocalAddr, t, n, s.sp, s.startmem)
}
s.Fatalf("addr of undeclared ONAME %v. declared: %v", n, s.decladdrs)
return nil
case PAUTO:
return s.newValue1Apos(ssa.OpAddr, t, n, s.sp, !n.IsAutoTmp())
return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), !n.IsAutoTmp())
case PPARAMOUT: // Same as PAUTO -- cannot generate LEA early.
// ensure that we reuse symbols for out parameters so
// that cse works on their addresses
return s.newValue1A(ssa.OpAddr, t, n, s.sp)
return s.newValue2Apos(ssa.OpLocalAddr, t, n, s.sp, s.mem(), true)
default:
s.Fatalf("variable address class %v not implemented", n.Class())
return nil
@ -4578,8 +4594,8 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
// unSSAable type, use temporary.
// TODO: get rid of some of these temporaries.
tmp = tempAt(n.Pos, s.curfn, n.Type)
addr = s.addr(tmp, false)
s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp, s.mem())
addr = s.addr(tmp, false)
}
cond := s.newValue2(ssa.OpEqPtr, types.Types[TBOOL], itab, targetITab)
@ -5581,7 +5597,8 @@ func (e *ssafn) Log() bool {
// Fatal reports a compiler error and exits.
func (e *ssafn) Fatalf(pos src.XPos, msg string, args ...interface{}) {
lineno = pos
Fatalf(msg, args...)
nargs := append([]interface{}{e.curfn.funcname()}, args...)
Fatalf("'%s': "+msg, nargs...)
}
// Warnl reports a "warning", which is usually flag-triggered

View File

@ -203,11 +203,23 @@ func checkFunc(f *Func) {
if len(v.Args) == 0 {
f.Fatalf("no args for OpAddr %s", v.LongString())
}
if v.Args[0].Op != OpSP && v.Args[0].Op != OpSB {
if v.Args[0].Op != OpSB {
f.Fatalf("bad arg to OpAddr %v", v)
}
}
if v.Op == OpLocalAddr {
if len(v.Args) != 2 {
f.Fatalf("wrong # of args for OpLocalAddr %s", v.LongString())
}
if v.Args[0].Op != OpSP {
f.Fatalf("bad arg 0 to OpLocalAddr %v", v)
}
if !v.Args[1].Type.IsMemory() {
f.Fatalf("bad arg 1 to OpLocalAddr %v", v)
}
}
if f.RegAlloc != nil && f.Config.SoftFloat && v.Type.IsFloat() {
f.Fatalf("unexpected floating-point type %v", v.LongString())
}

View File

@ -35,7 +35,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
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", OpAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp"),
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"),
@ -105,7 +105,7 @@ func TestZCSE(t *testing.T) {
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", OpAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp"),
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")),

View File

@ -146,7 +146,7 @@ func elimDeadAutosGeneric(f *Func) {
visit := func(v *Value) (changed bool) {
args := v.Args
switch v.Op {
case OpAddr:
case OpAddr, OpLocalAddr:
// Propagate the address if it points to an auto.
n, ok := v.Aux.(GCNode)
if !ok || n.StorageClass() != ClassAuto {

View File

@ -390,6 +390,19 @@ func (b *Block) NewValue2(pos src.XPos, op Op, t *types.Type, arg0, arg1 *Value)
return v
}
// NewValue2A returns a new value in the block with two arguments and one aux values.
func (b *Block) NewValue2A(pos src.XPos, op Op, t *types.Type, aux interface{}, arg0, arg1 *Value) *Value {
v := b.Func.newValue(op, t, b, pos)
v.AuxInt = 0
v.Aux = aux
v.Args = v.argstorage[:2]
v.argstorage[0] = arg0
v.argstorage[1] = arg1
arg0.Uses++
arg1.Uses++
return v
}
// NewValue2I returns a new value in the block with two arguments and an auxint value.
func (b *Block) NewValue2I(pos src.XPos, op Op, t *types.Type, auxint int64, arg0, arg1 *Value) *Value {
v := b.Func.newValue(op, t, b, pos)

View File

@ -356,6 +356,7 @@
(GetCallerPC) -> (LoweredGetCallerPC)
(GetCallerSP) -> (LoweredGetCallerSP)
(Addr {sym} base) -> (LEAL {sym} base)
(LocalAddr {sym} base _) -> (LEAL {sym} base)
// block rewrites
(If (SETL cmp) yes no) -> (LT cmp yes no)

View File

@ -455,6 +455,8 @@
(GetCallerSP) -> (LoweredGetCallerSP)
(Addr {sym} base) && config.PtrSize == 8 -> (LEAQ {sym} base)
(Addr {sym} base) && config.PtrSize == 4 -> (LEAL {sym} base)
(LocalAddr {sym} base _) && config.PtrSize == 8 -> (LEAQ {sym} base)
(LocalAddr {sym} base _) && config.PtrSize == 4 -> (LEAL {sym} base)
(MOVBstore [off] {sym} ptr y:(SETL x) mem) && y.Uses == 1 -> (SETLstore [off] {sym} ptr x mem)
(MOVBstore [off] {sym} ptr y:(SETLE x) mem) && y.Uses == 1 -> (SETLEstore [off] {sym} ptr x mem)

View File

@ -253,6 +253,7 @@
(OffPtr [off] ptr) -> (ADDconst [off] ptr)
(Addr {sym} base) -> (MOVWaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVWaddr {sym} base)
// loads
(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)

View File

@ -317,6 +317,7 @@
(OffPtr [off] ptr) -> (ADDconst [off] ptr)
(Addr {sym} base) -> (MOVDaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVDaddr {sym} base)
// loads
(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)

View File

@ -219,6 +219,7 @@
(OffPtr [off] ptr) -> (ADDconst [off] ptr)
(Addr {sym} base) -> (MOVWaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVWaddr {sym} base)
// loads
(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)

View File

@ -229,6 +229,7 @@
(OffPtr [off] ptr) -> (ADDVconst [off] ptr)
(Addr {sym} base) -> (MOVVaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVVaddr {sym} base)
// loads
(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)

View File

@ -273,6 +273,7 @@
// (MaskIfNotCarry CarrySet) -> -1
(Addr {sym} base) -> (MOVDaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVDaddr {sym} base)
(OffPtr [off] ptr) -> (ADD (MOVDconst <typ.Int64> [off]) ptr)
// TODO: optimize these cases?

View File

@ -367,6 +367,7 @@
(GetCallerSP) -> (LoweredGetCallerSP)
(GetCallerPC) -> (LoweredGetCallerPC)
(Addr {sym} base) -> (MOVDaddr {sym} base)
(LocalAddr {sym} base _) -> (MOVDaddr {sym} base)
(ITab (Load ptr mem)) -> (MOVDload ptr mem)
// block rewrites

View File

@ -352,6 +352,7 @@
(GetCallerPC) -> (LoweredGetCallerPC)
(GetCallerSP) -> (LoweredGetCallerSP)
(Addr {sym} base) -> (LoweredAddr {sym} base)
(LocalAddr {sym} base _) -> (LoweredAddr {sym} base)
// Write barrier.
(WB {fn} destptr srcptr mem) -> (LoweredWB {fn} destptr srcptr mem)

View File

@ -1370,6 +1370,8 @@
(NeqPtr x x) -> (ConstBool [0])
(EqPtr (Addr {a} _) (Addr {b} _)) -> (ConstBool [b2i(a == b)])
(NeqPtr (Addr {a} _) (Addr {b} _)) -> (ConstBool [b2i(a != b)])
(EqPtr (LocalAddr {a} _ _) (LocalAddr {b} _ _)) -> (ConstBool [b2i(a == b)])
(NeqPtr (LocalAddr {a} _ _) (LocalAddr {b} _ _)) -> (ConstBool [b2i(a != b)])
(EqPtr (OffPtr [o1] p1) p2) && isSamePtr(p1, p2) -> (ConstBool [b2i(o1 == 0)])
(NeqPtr (OffPtr [o1] p1) p2) && isSamePtr(p1, p2) -> (ConstBool [b2i(o1 != 0)])
(EqPtr (OffPtr [o1] p1) (OffPtr [o2] p2)) && isSamePtr(p1, p2) -> (ConstBool [b2i(o1 == o2)])
@ -1377,6 +1379,11 @@
(EqPtr (Const(32|64) [c]) (Const(32|64) [d])) -> (ConstBool [b2i(c == d)])
(NeqPtr (Const(32|64) [c]) (Const(32|64) [d])) -> (ConstBool [b2i(c != d)])
(EqPtr (LocalAddr _ _) (Addr _)) -> (ConstBool [0])
(NeqPtr (LocalAddr _ _) (Addr _)) -> (ConstBool [1])
(EqPtr (Addr _) (LocalAddr _ _)) -> (ConstBool [0])
(NeqPtr (Addr _) (LocalAddr _ _)) -> (ConstBool [1])
// Simplify address comparisons.
(EqPtr (AddPtr p1 o1) p2) && isSamePtr(p1, p2) -> (Not (IsNonNil o1))
(NeqPtr (AddPtr p1 o1) p2) && isSamePtr(p1, p2) -> (IsNonNil o1)
@ -1389,6 +1396,7 @@
(IsNonNil (ConstNil)) -> (ConstBool [0])
(IsNonNil (Const(32|64) [c])) -> (ConstBool [b2i(c != 0)])
(IsNonNil (Addr _)) -> (ConstBool [1])
(IsNonNil (LocalAddr _ _)) -> (ConstBool [1])
// Inline small or disjoint runtime.memmove calls with constant length.
(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))

View File

@ -331,7 +331,8 @@ var genericOps = []opData{
// the Aux field will be a *obj.LSym.
// If the variable is a local, the base pointer will be SP and
// the Aux field will be a *gc.Node.
{name: "Addr", argLength: 1, aux: "Sym", symEffect: "Addr"}, // Address of a variable. Arg0=SP or SB. Aux identifies the variable.
{name: "Addr", argLength: 1, aux: "Sym", symEffect: "Addr"}, // Address of a variable. Arg0=SB. Aux identifies the variable.
{name: "LocalAddr", argLength: 2, aux: "Sym", symEffect: "Addr"}, // Address of a variable. Arg0=SP. Arg1=mem. Aux identifies the variable.
{name: "SP", zeroWidth: true}, // stack pointer
{name: "SB", typ: "Uintptr", zeroWidth: true}, // static base pointer (a.k.a. globals pointer)

View File

@ -50,7 +50,7 @@ func TestLoopConditionS390X(t *testing.T) {
Bloc("entry",
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
Valu("SP", OpSP, c.config.Types.Uintptr, 0, nil),
Valu("ret", OpAddr, c.config.Types.Int64.PtrTo(), 0, nil, "SP"),
Valu("ret", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "SP", "mem"),
Valu("N", OpArg, c.config.Types.Int64, 0, c.Frontend().Auto(src.NoXPos, c.config.Types.Int64)),
Valu("starti", OpConst64, c.config.Types.Int64, 0, nil),
Valu("startsum", OpConst64, c.config.Types.Int64, 0, nil),

View File

@ -47,7 +47,7 @@ func nilcheckelim(f *Func) {
// a value resulting from taking the address of a
// value, or a value constructed from an offset of a
// non-nil ptr (OpAddPtr) implies it is non-nil
if v.Op == OpAddr || v.Op == OpAddPtr {
if v.Op == OpAddr || v.Op == OpLocalAddr || v.Op == OpAddPtr {
nonNilValues[v.ID] = true
}
}

View File

@ -212,7 +212,7 @@ func TestNilcheckPhi(t *testing.T) {
Valu("mem", OpInitMem, types.TypeMem, 0, nil),
Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
Valu("baddr", OpAddr, c.config.Types.Bool, 0, "b", "sp"),
Valu("baddr", OpLocalAddr, c.config.Types.Bool, 0, "b", "sp", "mem"),
Valu("bool1", OpLoad, c.config.Types.Bool, 0, nil, "baddr", "mem"),
If("bool1", "b1", "b2")),
Bloc("b1",

View File

@ -14,7 +14,7 @@ func isPoorStatementOp(op Op) bool {
switch op {
// Note that Nilcheck often vanishes, but when it doesn't, you'd love to start the statement there
// so that a debugger-user sees the stop before the panic, and can examine the value.
case OpAddr, OpOffPtr, OpStructSelect, OpConstBool, OpConst8, OpConst16, OpConst32, OpConst64, OpConst32F, OpConst64F:
case OpAddr, OpLocalAddr, OpOffPtr, OpStructSelect, OpConstBool, OpConst8, OpConst16, OpConst32, OpConst64, OpConst32F, OpConst64F:
return true
}
return false

View File

@ -2172,6 +2172,7 @@ const (
OpInitMem
OpArg
OpAddr
OpLocalAddr
OpSP
OpSB
OpLoad
@ -27236,6 +27237,13 @@ var opcodeTable = [...]opInfo{
symEffect: SymAddr,
generic: true,
},
{
name: "LocalAddr",
auxType: auxSym,
argLen: 2,
symEffect: SymAddr,
generic: true,
},
{
name: "SP",
argLen: 0,

View File

@ -468,7 +468,7 @@ func isSamePtr(p1, p2 *Value) bool {
switch p1.Op {
case OpOffPtr:
return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0])
case OpAddr:
case OpAddr, OpLocalAddr:
// OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op.
// Checking for value equality only works after [z]cse has run.
return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op
@ -506,18 +506,17 @@ func disjoint(p1 *Value, n1 int64, p2 *Value, n2 int64) bool {
// If one pointer is on the stack and the other is an argument
// then they can't overlap.
switch p1.Op {
case OpAddr:
if p2.Op == OpAddr || p2.Op == OpSP {
case OpAddr, OpLocalAddr:
if p2.Op == OpAddr || p2.Op == OpLocalAddr || p2.Op == OpSP {
return true
}
return p2.Op == OpArg && p1.Args[0].Op == OpSP
case OpArg:
if p2.Op == OpSP {
if p2.Op == OpSP || p2.Op == OpLocalAddr {
return true
}
return p2.Op == OpAddr && p2.Args[0].Op == OpSP
case OpSP:
return p2.Op == OpAddr || p2.Op == OpArg || p2.Op == OpSP
return p2.Op == OpAddr || p2.Op == OpLocalAddr || p2.Op == OpArg || p2.Op == OpSP
}
return false
}

View File

@ -441,6 +441,8 @@ func rewriteValue386(v *Value) bool {
return rewriteValue386_OpLess8U_0(v)
case OpLoad:
return rewriteValue386_OpLoad_0(v)
case OpLocalAddr:
return rewriteValue386_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValue386_OpLsh16x16_0(v)
case OpLsh16x32:
@ -17878,6 +17880,20 @@ func rewriteValue386_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValue386_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (LEAL {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(Op386LEAL)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValue386_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -783,6 +783,8 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpLess8U_0(v)
case OpLoad:
return rewriteValueAMD64_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueAMD64_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueAMD64_OpLsh16x16_0(v)
case OpLsh16x32:
@ -56301,6 +56303,43 @@ func rewriteValueAMD64_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueAMD64_OpLocalAddr_0(v *Value) bool {
b := v.Block
_ = b
config := b.Func.Config
_ = config
// match: (LocalAddr {sym} base _)
// cond: config.PtrSize == 8
// result: (LEAQ {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
if !(config.PtrSize == 8) {
break
}
v.reset(OpAMD64LEAQ)
v.Aux = sym
v.AddArg(base)
return true
}
// match: (LocalAddr {sym} base _)
// cond: config.PtrSize == 4
// result: (LEAL {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
if !(config.PtrSize == 4) {
break
}
v.reset(OpAMD64LEAL)
v.Aux = sym
v.AddArg(base)
return true
}
return false
}
func rewriteValueAMD64_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -619,6 +619,8 @@ func rewriteValueARM(v *Value) bool {
return rewriteValueARM_OpLess8U_0(v)
case OpLoad:
return rewriteValueARM_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueARM_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueARM_OpLsh16x16_0(v)
case OpLsh16x32:
@ -19344,6 +19346,20 @@ func rewriteValueARM_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueARM_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVWaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpARMMOVWaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueARM_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -603,6 +603,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpLess8U_0(v)
case OpLoad:
return rewriteValueARM64_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueARM64_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueARM64_OpLsh16x16_0(v)
case OpLsh16x32:
@ -28229,6 +28231,20 @@ func rewriteValueARM64_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueARM64_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVDaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpARM64MOVDaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueARM64_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -213,6 +213,8 @@ func rewriteValueMIPS(v *Value) bool {
return rewriteValueMIPS_OpLess8U_0(v)
case OpLoad:
return rewriteValueMIPS_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueMIPS_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueMIPS_OpLsh16x16_0(v)
case OpLsh16x32:
@ -2511,6 +2513,20 @@ func rewriteValueMIPS_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueMIPS_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVWaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpMIPSMOVWaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueMIPS_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -253,6 +253,8 @@ func rewriteValueMIPS64(v *Value) bool {
return rewriteValueMIPS64_OpLess8U_0(v)
case OpLoad:
return rewriteValueMIPS64_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueMIPS64_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueMIPS64_OpLsh16x16_0(v)
case OpLsh16x32:
@ -2924,6 +2926,20 @@ func rewriteValueMIPS64_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueMIPS64_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVVaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpMIPS64MOVVaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueMIPS64_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -275,6 +275,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpLess8U_0(v)
case OpLoad:
return rewriteValuePPC64_OpLoad_0(v)
case OpLocalAddr:
return rewriteValuePPC64_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValuePPC64_OpLsh16x16_0(v)
case OpLsh16x32:
@ -3048,6 +3050,20 @@ func rewriteValuePPC64_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValuePPC64_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVDaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpPPC64MOVDaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValuePPC64_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -275,6 +275,8 @@ func rewriteValueS390X(v *Value) bool {
return rewriteValueS390X_OpLess8U_0(v)
case OpLoad:
return rewriteValueS390X_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueS390X_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueS390X_OpLsh16x16_0(v)
case OpLsh16x32:
@ -3477,6 +3479,20 @@ func rewriteValueS390X_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueS390X_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (MOVDaddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpS390XMOVDaddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueS390X_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -237,6 +237,8 @@ func rewriteValueWasm(v *Value) bool {
return rewriteValueWasm_OpLess8U_0(v)
case OpLoad:
return rewriteValueWasm_OpLoad_0(v)
case OpLocalAddr:
return rewriteValueWasm_OpLocalAddr_0(v)
case OpLsh16x16:
return rewriteValueWasm_OpLsh16x16_0(v)
case OpLsh16x32:
@ -2496,6 +2498,20 @@ func rewriteValueWasm_OpLoad_0(v *Value) bool {
}
return false
}
func rewriteValueWasm_OpLocalAddr_0(v *Value) bool {
// match: (LocalAddr {sym} base _)
// cond:
// result: (LoweredAddr {sym} base)
for {
sym := v.Aux
_ = v.Args[1]
base := v.Args[0]
v.reset(OpWasmLoweredAddr)
v.Aux = sym
v.AddArg(base)
return true
}
}
func rewriteValueWasm_OpLsh16x16_0(v *Value) bool {
b := v.Block
_ = b

View File

@ -114,7 +114,7 @@ func rewriteValuegeneric(v *Value) bool {
case OpEqInter:
return rewriteValuegeneric_OpEqInter_0(v)
case OpEqPtr:
return rewriteValuegeneric_OpEqPtr_0(v) || rewriteValuegeneric_OpEqPtr_10(v)
return rewriteValuegeneric_OpEqPtr_0(v) || rewriteValuegeneric_OpEqPtr_10(v) || rewriteValuegeneric_OpEqPtr_20(v)
case OpEqSlice:
return rewriteValuegeneric_OpEqSlice_0(v)
case OpGeq16:
@ -300,7 +300,7 @@ func rewriteValuegeneric(v *Value) bool {
case OpNeqInter:
return rewriteValuegeneric_OpNeqInter_0(v)
case OpNeqPtr:
return rewriteValuegeneric_OpNeqPtr_0(v) || rewriteValuegeneric_OpNeqPtr_10(v)
return rewriteValuegeneric_OpNeqPtr_0(v) || rewriteValuegeneric_OpNeqPtr_10(v) || rewriteValuegeneric_OpNeqPtr_20(v)
case OpNeqSlice:
return rewriteValuegeneric_OpNeqSlice_0(v)
case OpNilCheck:
@ -10542,6 +10542,48 @@ func rewriteValuegeneric_OpEqPtr_0(v *Value) bool {
v.AuxInt = b2i(a == b)
return true
}
// match: (EqPtr (LocalAddr {a} _ _) (LocalAddr {b} _ _))
// cond:
// result: (ConstBool [b2i(a == b)])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
a := v_0.Aux
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
b := v_1.Aux
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = b2i(a == b)
return true
}
// match: (EqPtr (LocalAddr {b} _ _) (LocalAddr {a} _ _))
// cond:
// result: (ConstBool [b2i(a == b)])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
b := v_0.Aux
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
a := v_1.Aux
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = b2i(a == b)
return true
}
// match: (EqPtr (OffPtr [o1] p1) p2)
// cond: isSamePtr(p1, p2)
// result: (ConstBool [b2i(o1 == 0)])
@ -10647,6 +10689,13 @@ func rewriteValuegeneric_OpEqPtr_0(v *Value) bool {
v.AuxInt = b2i(c == d)
return true
}
return false
}
func rewriteValuegeneric_OpEqPtr_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (EqPtr (Const32 [d]) (Const32 [c]))
// cond:
// result: (ConstBool [b2i(c == d)])
@ -10685,13 +10734,6 @@ func rewriteValuegeneric_OpEqPtr_0(v *Value) bool {
v.AuxInt = b2i(c == d)
return true
}
return false
}
func rewriteValuegeneric_OpEqPtr_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (EqPtr (Const64 [d]) (Const64 [c]))
// cond:
// result: (ConstBool [b2i(c == d)])
@ -10711,6 +10753,78 @@ func rewriteValuegeneric_OpEqPtr_10(v *Value) bool {
v.AuxInt = b2i(c == d)
return true
}
// match: (EqPtr (LocalAddr _ _) (Addr _))
// cond:
// result: (ConstBool [0])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpAddr {
break
}
v.reset(OpConstBool)
v.AuxInt = 0
return true
}
// match: (EqPtr (Addr _) (LocalAddr _ _))
// cond:
// result: (ConstBool [0])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAddr {
break
}
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = 0
return true
}
// match: (EqPtr (Addr _) (LocalAddr _ _))
// cond:
// result: (ConstBool [0])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAddr {
break
}
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = 0
return true
}
// match: (EqPtr (LocalAddr _ _) (Addr _))
// cond:
// result: (ConstBool [0])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpAddr {
break
}
v.reset(OpConstBool)
v.AuxInt = 0
return true
}
// match: (EqPtr (AddPtr p1 o1) p2)
// cond: isSamePtr(p1, p2)
// result: (Not (IsNonNil o1))
@ -10774,6 +10888,13 @@ func rewriteValuegeneric_OpEqPtr_10(v *Value) bool {
v.AddArg(v0)
return true
}
return false
}
func rewriteValuegeneric_OpEqPtr_20(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (EqPtr p (Const32 [0]))
// cond:
// result: (Not (IsNonNil p))
@ -12525,6 +12646,19 @@ func rewriteValuegeneric_OpIsNonNil_0(v *Value) bool {
v.AuxInt = 1
return true
}
// match: (IsNonNil (LocalAddr _ _))
// cond:
// result: (ConstBool [1])
for {
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
_ = v_0.Args[1]
v.reset(OpConstBool)
v.AuxInt = 1
return true
}
return false
}
func rewriteValuegeneric_OpIsSliceInBounds_0(v *Value) bool {
@ -20810,6 +20944,48 @@ func rewriteValuegeneric_OpNeqPtr_0(v *Value) bool {
v.AuxInt = b2i(a != b)
return true
}
// match: (NeqPtr (LocalAddr {a} _ _) (LocalAddr {b} _ _))
// cond:
// result: (ConstBool [b2i(a != b)])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
a := v_0.Aux
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
b := v_1.Aux
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = b2i(a != b)
return true
}
// match: (NeqPtr (LocalAddr {b} _ _) (LocalAddr {a} _ _))
// cond:
// result: (ConstBool [b2i(a != b)])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
b := v_0.Aux
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
a := v_1.Aux
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = b2i(a != b)
return true
}
// match: (NeqPtr (OffPtr [o1] p1) p2)
// cond: isSamePtr(p1, p2)
// result: (ConstBool [b2i(o1 != 0)])
@ -20915,6 +21091,9 @@ func rewriteValuegeneric_OpNeqPtr_0(v *Value) bool {
v.AuxInt = b2i(c != d)
return true
}
return false
}
func rewriteValuegeneric_OpNeqPtr_10(v *Value) bool {
// match: (NeqPtr (Const32 [d]) (Const32 [c]))
// cond:
// result: (ConstBool [b2i(c != d)])
@ -20953,9 +21132,6 @@ func rewriteValuegeneric_OpNeqPtr_0(v *Value) bool {
v.AuxInt = b2i(c != d)
return true
}
return false
}
func rewriteValuegeneric_OpNeqPtr_10(v *Value) bool {
// match: (NeqPtr (Const64 [d]) (Const64 [c]))
// cond:
// result: (ConstBool [b2i(c != d)])
@ -20975,6 +21151,78 @@ func rewriteValuegeneric_OpNeqPtr_10(v *Value) bool {
v.AuxInt = b2i(c != d)
return true
}
// match: (NeqPtr (LocalAddr _ _) (Addr _))
// cond:
// result: (ConstBool [1])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpAddr {
break
}
v.reset(OpConstBool)
v.AuxInt = 1
return true
}
// match: (NeqPtr (Addr _) (LocalAddr _ _))
// cond:
// result: (ConstBool [1])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAddr {
break
}
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = 1
return true
}
// match: (NeqPtr (Addr _) (LocalAddr _ _))
// cond:
// result: (ConstBool [1])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAddr {
break
}
v_1 := v.Args[1]
if v_1.Op != OpLocalAddr {
break
}
_ = v_1.Args[1]
v.reset(OpConstBool)
v.AuxInt = 1
return true
}
// match: (NeqPtr (LocalAddr _ _) (Addr _))
// cond:
// result: (ConstBool [1])
for {
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpLocalAddr {
break
}
_ = v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpAddr {
break
}
v.reset(OpConstBool)
v.AuxInt = 1
return true
}
// match: (NeqPtr (AddPtr p1 o1) p2)
// cond: isSamePtr(p1, p2)
// result: (IsNonNil o1)
@ -21032,6 +21280,9 @@ func rewriteValuegeneric_OpNeqPtr_10(v *Value) bool {
v.AddArg(p)
return true
}
return false
}
func rewriteValuegeneric_OpNeqPtr_20(v *Value) bool {
// match: (NeqPtr p (Const32 [0]))
// cond:
// result: (IsNonNil p)

View File

@ -306,7 +306,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
t := val.Type.Elem()
tmp = b.Func.fe.Auto(val.Pos, t)
mem = b.NewValue1A(pos, OpVarDef, types.TypeMem, tmp, mem)
tmpaddr := b.NewValue1A(pos, OpAddr, t.PtrTo(), tmp, sp)
tmpaddr := b.NewValue2A(pos, OpLocalAddr, t.PtrTo(), tmp, sp, mem)
siz := t.Size()
mem = b.NewValue3I(pos, OpMove, types.TypeMem, siz, tmpaddr, val, mem)
mem.Aux = t
@ -359,10 +359,8 @@ func IsStackAddr(v *Value) bool {
v = v.Args[0]
}
switch v.Op {
case OpSP:
case OpSP, OpLocalAddr:
return true
case OpAddr:
return v.Args[0].Op == OpSP
}
return false
}
@ -374,7 +372,7 @@ func IsSanitizerSafeAddr(v *Value) bool {
v = v.Args[0]
}
switch v.Op {
case OpSP:
case OpSP, OpLocalAddr:
// Stack addresses are always safe.
return true
case OpITab, OpStringPtr, OpGetClosurePtr:
@ -382,10 +380,6 @@ func IsSanitizerSafeAddr(v *Value) bool {
// read-only once initialized.
return true
case OpAddr:
switch v.Args[0].Op {
case OpSP:
return true
case OpSB:
sym := v.Aux.(*obj.LSym)
// TODO(mdempsky): Find a cleaner way to
// detect this. It would be nice if we could
@ -396,7 +390,6 @@ func IsSanitizerSafeAddr(v *Value) bool {
return true
}
}
}
return false
}

View File

@ -0,0 +1,25 @@
// compile
// Copyright 2018 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.
// Triggers a bug in writebarrier, which inserts one
// between (first block) OpAddr x and (second block) a VarDef x,
// which are then in the wrong order and unable to be
// properly scheduled.
package q
var S interface{}
func F(n int) {
fun := func(x int) int {
S = 1
return n
}
i := fun(([]int{})[n])
var fc [2]chan int
S = (([1][2]chan int{fc})[i][i])
}