// If the load is in a different block do not merge it.
return false
}
- mem := load.Args[len(load.Args)-1]
+ mem := load.MemoryArg()
// We need the load's memory arg to still be alive at target. That
// can't be the case if one of target's args depends on a memory
if len(m.Args) == 0 {
break
}
- m = m.Args[len(m.Args)-1]
+ m = m.MemoryArg()
}
}
if v.Op == OpInitMem || v.Op == OpPhi {
continue
}
- a := v.Args[len(v.Args)-1]
+ a := v
if v.Op == OpSelect1 {
- a = a.Args[len(a.Args)-1]
+ a = a.Args[0]
}
- sset.add(a.ID) // record that a is used
+ sset.add(a.MemoryArg().ID) // record that v's memory arg is used
}
if v.Op == OpNilCheck {
hasNilCheck = true
if w.Op == OpSelect1 {
w = w.Args[0]
}
- w = w.Args[len(w.Args)-1]
+ w = w.MemoryArg()
}
var stack []*Value
for _, v := range values {
// Tuple selectors must stay with the tuple generator.
continue
}
- if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
+ if v.MemoryArg() != nil {
// We can't move values which have a memory arg - it might
// make two memory values live across a block boundary.
continue
}
return reg.(*Register).name
}
+
+// MemoryArg returns the memory argument for the Value.
+// The returned value, if non-nil, will be memory-typed,
+// except in the case where v is Select1, in which case
+// the returned value will be a tuple containing a memory
+// type. Otherwise, nil is returned.
+func (v *Value) MemoryArg() *Value {
+ if v.Op == OpPhi {
+ v.Fatalf("MemoryArg on Phi")
+ }
+ na := len(v.Args)
+ if na == 0 {
+ return nil
+ }
+ if m := v.Args[na-1]; m.Type.IsMemory() ||
+ (v.Op == OpSelect1 && m.Type.FieldType(1).IsMemory()) {
+ return m
+ }
+ return nil
+}
b.Values = b.Values[:start]
// find the memory before the WB stores
- mem := stores[0].Args[len(stores[0].Args)-1]
+ mem := stores[0].MemoryArg()
pos := stores[0].Pos
bThen := f.NewBlock(BlockPlain)
bElse := f.NewBlock(BlockPlain)