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
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)
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)
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
}
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
// 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)
// 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
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())
}
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"),
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")),
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 {
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)
(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)
(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)
(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)
(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)
(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)
(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)
// (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?
(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
(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)
(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)])
(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)
(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))))
// 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)
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),
// 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
}
}
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",
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
OpInitMem
OpArg
OpAddr
+ OpLocalAddr
OpSP
OpSB
OpLoad
symEffect: SymAddr,
generic: true,
},
+ {
+ name: "LocalAddr",
+ auxType: auxSym,
+ argLen: 2,
+ symEffect: SymAddr,
+ generic: true,
+ },
{
name: "SP",
argLen: 0,
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
// 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
}
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
}
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
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:
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:
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)])
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)])
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)])
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))
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))
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 {
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)])
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)])
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)])
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)
v.AddArg(p)
return true
}
+ return false
+}
+func rewriteValuegeneric_OpNeqPtr_20(v *Value) bool {
// match: (NeqPtr p (Const32 [0]))
// cond:
// result: (IsNonNil p)
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
v = v.Args[0]
}
switch v.Op {
- case OpSP:
+ case OpSP, OpLocalAddr:
return true
- case OpAddr:
- return v.Args[0].Op == OpSP
}
return false
}
v = v.Args[0]
}
switch v.Op {
- case OpSP:
+ case OpSP, OpLocalAddr:
// Stack addresses are always safe.
return true
case OpITab, OpStringPtr, OpGetClosurePtr:
// read-only once initialized.
return true
case OpAddr:
- switch v.Args[0].Op {
- case OpSP:
+ sym := v.Aux.(*obj.LSym)
+ // TODO(mdempsky): Find a cleaner way to
+ // detect this. It would be nice if we could
+ // test sym.Type==objabi.SRODATA, but we don't
+ // initialize sym.Type until after function
+ // compilation.
+ if strings.HasPrefix(sym.Name, `"".statictmp_`) {
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
- // test sym.Type==objabi.SRODATA, but we don't
- // initialize sym.Type until after function
- // compilation.
- if strings.HasPrefix(sym.Name, `"".statictmp_`) {
- return true
- }
}
}
return false
--- /dev/null
+// 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])
+}