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: 's avatarDavid Chase <drchase@google.com>
parent 9094e3ad
......@@ -4108,6 +4108,8 @@ func (s *genState) genValue(v *ssa.Value) {
case ssa.OpAMD64InvertFlags:
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:
Prog(x86.AREP)
Prog(x86.ASTOSQ)
......
......@@ -26,4 +26,18 @@ func copyelim(f *Func) {
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
// findlive returns the reachable blocks and live values in f.
func findlive(f *Func) (reachable []bool, live []bool) {
// After regalloc, consider all blocks and values to be reachable and live.
// See the comment at the top of regalloc.go and in deadcode for details.
if f.RegAlloc != nil {
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
}
reachable = reachableBlocks(f)
live = liveValues(f, reachable)
return
}
// Find all reachable basic blocks.
reachable = make([]bool, f.NumBlocks())
// reachableBlocks returns the reachable blocks in f.
func reachableBlocks(f *Func) []bool {
reachable := make([]bool, f.NumBlocks())
reachable[f.Entry.ID] = true
p := []*Block{f.Entry} // stack-like worklist
for len(p) > 0 {
......@@ -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
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.
for _, b := range f.Blocks {
......@@ -72,7 +79,7 @@ func findlive(f *Func) (reachable []bool, live []bool) {
}
}
return reachable, live
return live
}
// deadcode removes dead code from f.
......@@ -85,27 +92,8 @@ func deadcode(f *Func) {
f.Fatalf("deadcode after regalloc")
}
reachable, live := findlive(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]
}
// Find reachable blocks.
reachable := reachableBlocks(f)
// Get rid of edges from dead to live code.
for _, b := range f.Blocks {
......@@ -131,6 +119,7 @@ func deadcode(f *Func) {
b.Succs[1] = nil
b.Succs = b.Succs[:1]
b.Kind = BlockPlain
b.Likely = BranchUnknown
if reachable[c.ID] {
// Note: c must be reachable through some other edge.
......@@ -138,41 +127,20 @@ func deadcode(f *Func) {
}
}
// 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]
// Splice out any copies introduced during dead block removal.
copyelim(f)
// Find live values.
live := liveValues(f, reachable)
// Remove dead & duplicate entries from namedValues map.
s := newSparseSet(f.NumValues())
i = 0
i := 0
for _, name := range f.Names {
j := 0
s.clear()
values := f.NamedValues[name]
for _, v := range values {
for v.Op == OpCopy {
v = v.Args[0]
}
if live[v.ID] && !s.contains(v.ID) {
values[j] = v
j++
......@@ -195,6 +163,50 @@ func deadcode(f *Func) {
}
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: save dead Values and Blocks for reuse? Or should we just let GC handle it?
}
......
......@@ -472,6 +472,19 @@ func init() {
// gets correctly ordered with respect to GC safepoints.
// arg0=ptr/int arg1=mem, output=int/ptr
{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{
......
......@@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered
}
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
}
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()
......
......@@ -283,6 +283,11 @@ const (
OpAMD64LoweredGetClosurePtr
OpAMD64LoweredNilCheck
OpAMD64MOVQconvert
OpAMD64FlagEQ
OpAMD64FlagLT_ULT
OpAMD64FlagLT_UGT
OpAMD64FlagGT_UGT
OpAMD64FlagGT_ULT
OpAdd8
OpAdd16
......@@ -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",
......
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