Commit 3425295e authored by Keith Randall's avatar Keith Randall

[dev.ssa] cmd/compile: clean up comparisons

Add new constant-flags opcodes.  These can be generated from
comparisons that we know the result of, like x&31 < 32.

Constant-fold the constant-flags opcodes into all flag users.

Reorder some CMPxconst args so they read in the comparison direction.

Reorg deadcode removal a bit - it needs to remove the OpCopy ops it
generates when strength-reducing Phi ops.  So it needs to splice out all
the dead blocks and do a copy elimination before it computes live
values.

Change-Id: Ie922602033592ad8212efe4345394973d3b94d9f
Reviewed-on: https://go-review.googlesource.com/18267
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 9094e3ad
...@@ -4108,6 +4108,8 @@ func (s *genState) genValue(v *ssa.Value) { ...@@ -4108,6 +4108,8 @@ func (s *genState) genValue(v *ssa.Value) {
case ssa.OpAMD64InvertFlags: case ssa.OpAMD64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v) v.Fatalf("InvertFlags should never make it to codegen %v", v)
case ssa.OpAMD64FlagEQ, ssa.OpAMD64FlagLT_ULT, ssa.OpAMD64FlagLT_UGT, ssa.OpAMD64FlagGT_ULT, ssa.OpAMD64FlagGT_UGT:
v.Fatalf("Flag* ops should never make it to codegen %v", v)
case ssa.OpAMD64REPSTOSQ: case ssa.OpAMD64REPSTOSQ:
Prog(x86.AREP) Prog(x86.AREP)
Prog(x86.ASTOSQ) Prog(x86.ASTOSQ)
......
...@@ -26,4 +26,18 @@ func copyelim(f *Func) { ...@@ -26,4 +26,18 @@ func copyelim(f *Func) {
b.Control = v b.Control = v
} }
} }
// Update named values.
for _, name := range f.Names {
values := f.NamedValues[name]
for i, v := range values {
x := v
for x.Op == OpCopy {
x = x.Args[0]
}
if x != v {
values[i] = v
}
}
}
} }
...@@ -6,22 +6,14 @@ package ssa ...@@ -6,22 +6,14 @@ package ssa
// findlive returns the reachable blocks and live values in f. // findlive returns the reachable blocks and live values in f.
func findlive(f *Func) (reachable []bool, live []bool) { func findlive(f *Func) (reachable []bool, live []bool) {
// After regalloc, consider all blocks and values to be reachable and live. reachable = reachableBlocks(f)
// See the comment at the top of regalloc.go and in deadcode for details. live = liveValues(f, reachable)
if f.RegAlloc != nil { return
reachable = make([]bool, f.NumBlocks()) }
for i := range reachable {
reachable[i] = true
}
live = make([]bool, f.NumValues())
for i := range live {
live[i] = true
}
return reachable, live
}
// Find all reachable basic blocks. // reachableBlocks returns the reachable blocks in f.
reachable = make([]bool, f.NumBlocks()) func reachableBlocks(f *Func) []bool {
reachable := make([]bool, f.NumBlocks())
reachable[f.Entry.ID] = true reachable[f.Entry.ID] = true
p := []*Block{f.Entry} // stack-like worklist p := []*Block{f.Entry} // stack-like worklist
for len(p) > 0 { for len(p) > 0 {
...@@ -40,10 +32,25 @@ func findlive(f *Func) (reachable []bool, live []bool) { ...@@ -40,10 +32,25 @@ func findlive(f *Func) (reachable []bool, live []bool) {
} }
} }
} }
return reachable
}
// liveValues returns the live values in f.
// reachable is a map from block ID to whether the block is reachable.
func liveValues(f *Func, reachable []bool) []bool {
live := make([]bool, f.NumValues())
// After regalloc, consider all values to be live.
// See the comment at the top of regalloc.go and in deadcode for details.
if f.RegAlloc != nil {
for i := range live {
live[i] = true
}
return live
}
// Find all live values // Find all live values
live = make([]bool, f.NumValues()) // flag to set for each live value var q []*Value // stack-like worklist of unscanned values
var q []*Value // stack-like worklist of unscanned values
// Starting set: all control values of reachable blocks are live. // Starting set: all control values of reachable blocks are live.
for _, b := range f.Blocks { for _, b := range f.Blocks {
...@@ -72,7 +79,7 @@ func findlive(f *Func) (reachable []bool, live []bool) { ...@@ -72,7 +79,7 @@ func findlive(f *Func) (reachable []bool, live []bool) {
} }
} }
return reachable, live return live
} }
// deadcode removes dead code from f. // deadcode removes dead code from f.
...@@ -85,27 +92,8 @@ func deadcode(f *Func) { ...@@ -85,27 +92,8 @@ func deadcode(f *Func) {
f.Fatalf("deadcode after regalloc") f.Fatalf("deadcode after regalloc")
} }
reachable, live := findlive(f) // Find reachable blocks.
reachable := reachableBlocks(f)
// Remove dead values from blocks' value list. Return dead
// value ids to the allocator.
for _, b := range f.Blocks {
i := 0
for _, v := range b.Values {
if live[v.ID] {
b.Values[i] = v
i++
} else {
f.vid.put(v.ID)
}
}
// aid GC
tail := b.Values[i:]
for j := range tail {
tail[j] = nil
}
b.Values = b.Values[:i]
}
// Get rid of edges from dead to live code. // Get rid of edges from dead to live code.
for _, b := range f.Blocks { for _, b := range f.Blocks {
...@@ -131,6 +119,7 @@ func deadcode(f *Func) { ...@@ -131,6 +119,7 @@ func deadcode(f *Func) {
b.Succs[1] = nil b.Succs[1] = nil
b.Succs = b.Succs[:1] b.Succs = b.Succs[:1]
b.Kind = BlockPlain b.Kind = BlockPlain
b.Likely = BranchUnknown
if reachable[c.ID] { if reachable[c.ID] {
// Note: c must be reachable through some other edge. // Note: c must be reachable through some other edge.
...@@ -138,41 +127,20 @@ func deadcode(f *Func) { ...@@ -138,41 +127,20 @@ func deadcode(f *Func) {
} }
} }
// Remove unreachable blocks. Return dead block ids to allocator. // Splice out any copies introduced during dead block removal.
i := 0 copyelim(f)
for _, b := range f.Blocks {
if reachable[b.ID] { // Find live values.
f.Blocks[i] = b live := liveValues(f, reachable)
i++
} else {
if len(b.Values) > 0 {
b.Fatalf("live values in unreachable block %v: %v", b, b.Values)
}
b.Preds = nil
b.Succs = nil
b.Control = nil
b.Kind = BlockDead
f.bid.put(b.ID)
}
}
// zero remainder to help GC
tail := f.Blocks[i:]
for j := range tail {
tail[j] = nil
}
f.Blocks = f.Blocks[:i]
// Remove dead & duplicate entries from namedValues map. // Remove dead & duplicate entries from namedValues map.
s := newSparseSet(f.NumValues()) s := newSparseSet(f.NumValues())
i = 0 i := 0
for _, name := range f.Names { for _, name := range f.Names {
j := 0 j := 0
s.clear() s.clear()
values := f.NamedValues[name] values := f.NamedValues[name]
for _, v := range values { for _, v := range values {
for v.Op == OpCopy {
v = v.Args[0]
}
if live[v.ID] && !s.contains(v.ID) { if live[v.ID] && !s.contains(v.ID) {
values[j] = v values[j] = v
j++ j++
...@@ -195,6 +163,50 @@ func deadcode(f *Func) { ...@@ -195,6 +163,50 @@ func deadcode(f *Func) {
} }
f.Names = f.Names[:i] f.Names = f.Names[:i]
// Remove dead values from blocks' value list. Return dead
// value ids to the allocator.
for _, b := range f.Blocks {
i := 0
for _, v := range b.Values {
if live[v.ID] {
b.Values[i] = v
i++
} else {
f.vid.put(v.ID)
}
}
// aid GC
tail := b.Values[i:]
for j := range tail {
tail[j] = nil
}
b.Values = b.Values[:i]
}
// Remove unreachable blocks. Return dead block ids to allocator.
i = 0
for _, b := range f.Blocks {
if reachable[b.ID] {
f.Blocks[i] = b
i++
} else {
if len(b.Values) > 0 {
b.Fatalf("live values in unreachable block %v: %v", b, b.Values)
}
b.Preds = nil
b.Succs = nil
b.Control = nil
b.Kind = BlockDead
f.bid.put(b.ID)
}
}
// zero remainder to help GC
tail := f.Blocks[i:]
for j := range tail {
tail[j] = nil
}
f.Blocks = f.Blocks[:i]
// TODO: renumber Blocks and Values densely? // TODO: renumber Blocks and Values densely?
// TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it? // TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it?
} }
......
...@@ -472,6 +472,19 @@ func init() { ...@@ -472,6 +472,19 @@ func init() {
// gets correctly ordered with respect to GC safepoints. // gets correctly ordered with respect to GC safepoints.
// arg0=ptr/int arg1=mem, output=int/ptr // arg0=ptr/int arg1=mem, output=int/ptr
{name: "MOVQconvert", reg: gp11nf, asm: "MOVQ"}, {name: "MOVQconvert", reg: gp11nf, asm: "MOVQ"},
// Constant flag values. For any comparison, there are 5 possible
// outcomes: the three from the signed total order (<,==,>) and the
// three from the unsigned total order. The == cases overlap.
// Note: there's a sixth "unordered" outcome for floating-point
// comparisons, but we don't use such a beast yet.
// These ops are for temporary use by rewrite rules. They
// cannot appear in the generated assembly.
{name: "FlagEQ"}, // equal
{name: "FlagLT_ULT"}, // signed < and unsigned <
{name: "FlagLT_UGT"}, // signed < and unsigned >
{name: "FlagGT_UGT"}, // signed > and unsigned <
{name: "FlagGT_ULT"}, // signed > and unsigned >
} }
var AMD64blocks = []blockData{ var AMD64blocks = []blockData{
......
...@@ -21,7 +21,7 @@ func checkLower(f *Func) { ...@@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered continue // lowered
} }
switch v.Op { switch v.Op {
case OpSP, OpSB, OpInitMem, OpArg, OpCopy, OpPhi, OpVarDef, OpVarKill: case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill:
continue // ok not to lower continue // ok not to lower
} }
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString() s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()
......
...@@ -283,6 +283,11 @@ const ( ...@@ -283,6 +283,11 @@ const (
OpAMD64LoweredGetClosurePtr OpAMD64LoweredGetClosurePtr
OpAMD64LoweredNilCheck OpAMD64LoweredNilCheck
OpAMD64MOVQconvert OpAMD64MOVQconvert
OpAMD64FlagEQ
OpAMD64FlagLT_ULT
OpAMD64FlagLT_UGT
OpAMD64FlagGT_UGT
OpAMD64FlagGT_ULT
OpAdd8 OpAdd8
OpAdd16 OpAdd16
...@@ -3232,6 +3237,26 @@ var opcodeTable = [...]opInfo{ ...@@ -3232,6 +3237,26 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "FlagEQ",
reg: regInfo{},
},
{
name: "FlagLT_ULT",
reg: regInfo{},
},
{
name: "FlagLT_UGT",
reg: regInfo{},
},
{
name: "FlagGT_UGT",
reg: regInfo{},
},
{
name: "FlagGT_ULT",
reg: regInfo{},
},
{ {
name: "Add8", name: "Add8",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment