]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: short-circuit empty blocks
authorKeith Randall <khr@golang.org>
Thu, 21 Jan 2016 21:27:01 +0000 (13:27 -0800)
committerKeith Randall <khr@golang.org>
Fri, 22 Jan 2016 22:12:12 +0000 (22:12 +0000)
Empty blocks are introduced to remove critical edges.
After regalloc, we can remove any of the added blocks
that are still empty.

Change-Id: I0b40e95ac3a6cc1e632a479443479532b6c5ccd9
Reviewed-on: https://go-review.googlesource.com/18833
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/TODO
src/cmd/compile/internal/ssa/check.go
src/cmd/compile/internal/ssa/compile.go
src/cmd/compile/internal/ssa/trim.go [new file with mode: 0644]
test/nilptr3_ssa.go

index b57958a24db928ababa6d81cc84ba6aea726af65..9dd5859735a485b24f226bbe1bc16bc0e2830c30 100644 (file)
@@ -4183,23 +4183,36 @@ func (s *genState) genValue(v *ssa.Value) {
        case ssa.OpAMD64LoweredNilCheck:
                // Optimization - if the subsequent block has a load or store
                // at the same address, we don't need to issue this instruction.
+               mem := v.Args[1]
                for _, w := range v.Block.Succs[0].Values {
+                       if w.Op == ssa.OpPhi {
+                               if w.Type.IsMemory() {
+                                       mem = w
+                               }
+                               continue
+                       }
                        if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() {
                                // w doesn't use a store - can't be a memory op.
                                continue
                        }
-                       if w.Args[len(w.Args)-1] != v.Args[1] {
+                       if w.Args[len(w.Args)-1] != mem {
                                v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w)
                        }
                        switch w.Op {
                        case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload,
                                ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore:
                                if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage {
+                                       if Debug_checknil != 0 && int(v.Line) > 1 {
+                                               Warnl(int(v.Line), "removed nil check")
+                                       }
                                        return
                                }
                        case ssa.OpAMD64MOVQstoreconst, ssa.OpAMD64MOVLstoreconst, ssa.OpAMD64MOVWstoreconst, ssa.OpAMD64MOVBstoreconst:
                                off := ssa.StoreConst(v.AuxInt).Off()
                                if w.Args[0] == v.Args[0] && w.Aux == nil && off >= 0 && off < minZeroPage {
+                                       if Debug_checknil != 0 && int(v.Line) > 1 {
+                                               Warnl(int(v.Line), "removed nil check")
+                                       }
                                        return
                                }
                        }
index 403f98cf40c454d78afef67d3deac50dbef8e678..2f7973c5a349761344be0a8b5f6b71b464d979e1 100644 (file)
@@ -42,7 +42,6 @@ Optimizations (better compiled code)
   (all instructions, really)
 - combine LEAQs
 - store followed by load to same address
-- short circuit blocks which are just a jump (undo critical edge processing when no instructions are put in it by regalloc)
 - (CMPconst [0] (AND x y)) -> (TEST x y)
 - more (LOAD (ADDQ )) -> LOADIDX
 - CMPL/SETEQ/TESTB/JEQ -> CMPL/JEQ
index ca3bbfe494c3939239397f0e2c7b7553cf32778e..b74371008c04f487d4a169ac212e754366782e81 100644 (file)
@@ -18,10 +18,12 @@ func checkFunc(f *Func) {
                        f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)
                }
 
-               for i, c := range b.Succs {
-                       for j, d := range b.Succs {
-                               if i != j && c == d {
-                                       f.Fatalf("%s.Succs has duplicate block %s", b, c)
+               if f.RegAlloc == nil {
+                       for i, c := range b.Succs {
+                               for j, d := range b.Succs {
+                                       if i != j && c == d {
+                                               f.Fatalf("%s.Succs has duplicate block %s", b, c)
+                                       }
                                }
                        }
                }
@@ -34,6 +36,7 @@ func checkFunc(f *Func) {
                // all successors are distinct.  They will need to be distinct
                // anyway for register allocation (duplicate successors implies
                // the existence of critical edges).
+               // After regalloc we can allow non-distinct predecessors.
 
                for _, p := range b.Preds {
                        var found bool
index 64c1412f9d5cb17e12580b26307768de23141f43..7a515f898ce2b2eda8d6882c5556e34c3506d469 100644 (file)
@@ -105,7 +105,8 @@ var passes = [...]pass{
        {"layout", layout},       // schedule blocks
        {"schedule", schedule},   // schedule values
        {"flagalloc", flagalloc}, // allocate flags register
-       {"regalloc", regalloc},
+       {"regalloc", regalloc},   // allocate int & float registers
+       {"trim", trim},           // remove empty blocks
 }
 
 // Double-check phase ordering constraints.
@@ -148,6 +149,8 @@ var passOrder = [...]constraint{
        {"schedule", "flagalloc"},
        // regalloc needs flags to be allocated first.
        {"flagalloc", "regalloc"},
+       // trim needs regalloc to be done first.
+       {"regalloc", "trim"},
 }
 
 func init() {
diff --git a/src/cmd/compile/internal/ssa/trim.go b/src/cmd/compile/internal/ssa/trim.go
new file mode 100644 (file)
index 0000000..594d2aa
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2016 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.
+
+package ssa
+
+// trim removes blocks with no code in them.
+// These blocks were inserted to remove critical edges.
+func trim(f *Func) {
+       i := 0
+       for _, b := range f.Blocks {
+               if b.Kind != BlockPlain || len(b.Values) != 0 || len(b.Preds) != 1 {
+                       f.Blocks[i] = b
+                       i++
+                       continue
+               }
+               // TODO: handle len(b.Preds)>1 case.
+
+               // Splice b out of the graph.
+               pred := b.Preds[0]
+               succ := b.Succs[0]
+               for j, s := range pred.Succs {
+                       if s == b {
+                               pred.Succs[j] = succ
+                       }
+               }
+               for j, p := range succ.Preds {
+                       if p == b {
+                               succ.Preds[j] = pred
+                       }
+               }
+       }
+       for j := i; j < len(f.Blocks); j++ {
+               f.Blocks[j] = nil
+       }
+       f.Blocks = f.Blocks[:i]
+}
index 9824ce1cc00553b5e051fee0cf77a98b1614585a..d324076114d035bddd9a5d76226a9d319057dbcd 100644 (file)
@@ -156,7 +156,7 @@ func f4(x *[10]int) {
        // and the offset is small enough that if x is nil, the address will still be
        // in the first unmapped page of memory.
 
-       _ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
+       _ = x[9] // ERROR "removed nil check"
 
        for {
                if x[9] != 0 { // ERROR "removed nil check"