]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile/ssa: separate logging, work in progress, and fatal errors
authorJosh Bleecher Snyder <josharian@gmail.com>
Fri, 12 Jun 2015 18:01:13 +0000 (11:01 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Sun, 21 Jun 2015 02:56:36 +0000 (02:56 +0000)
The SSA implementation logs for three purposes:

* debug logging
* fatal errors
* unimplemented features

Separating these three uses lets us attempt an SSA
implementation for all functions, not just
_ssa functions. This turns the entire standard
library into a compilation test, and makes it
easy to figure out things like
"how much coverage does SSA have now" and
"what should we do next to get more coverage?".

Functions called _ssa are still special.
They log profusely by default and
the output of the SSA implementation
is used. For all other functions,
logging is off, and the implementation
is built and discarded, due to lack of
support for the runtime.

While we're here, fix a few minor bugs and
add some extra Unimplementeds to allow
all.bash to pass.

As of now, SSA handles 20.79% of the functions
in the standard library (689 of 3314).

The top missing features are:

 10.03%  2597 SSA unimplemented: zero for type error not implemented
  7.79%  2016 SSA unimplemented: addr: bad op DOTPTR
  7.33%  1898 SSA unimplemented: unhandled expr EQ
  6.10%  1579 SSA unimplemented: unhandled expr OROR
  4.91%  1271 SSA unimplemented: unhandled expr NE
  4.49%  1163 SSA unimplemented: unhandled expr LROT
  4.00%  1036 SSA unimplemented: unhandled expr LEN
  3.56%   923 SSA unimplemented: unhandled stmt CALLFUNC
  2.37%   615 SSA unimplemented: zero for type []byte not implemented
  1.90%   492 SSA unimplemented: unhandled stmt CALLMETH
  1.74%   450 SSA unimplemented: unhandled expr CALLINTER
  1.74%   450 SSA unimplemented: unhandled expr DOT
  1.71%   444 SSA unimplemented: unhandled expr ANDAND
  1.65%   426 SSA unimplemented: unhandled expr CLOSUREVAR
  1.54%   400 SSA unimplemented: unhandled expr CALLMETH
  1.51%   390 SSA unimplemented: unhandled stmt SWITCH
  1.47%   380 SSA unimplemented: unhandled expr CONV
  1.33%   345 SSA unimplemented: addr: bad op *
  1.30%   336 SSA unimplemented: unhandled OLITERAL 6

Change-Id: I4ca07951e276714dc13c31de28640aead17a1be7
Reviewed-on: https://go-review.googlesource.com/11160
Reviewed-by: Keith Randall <khr@golang.org>
26 files changed:
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/TODO
src/cmd/compile/internal/ssa/block.go
src/cmd/compile/internal/ssa/check.go
src/cmd/compile/internal/ssa/compile.go
src/cmd/compile/internal/ssa/config.go
src/cmd/compile/internal/ssa/deadcode.go
src/cmd/compile/internal/ssa/deadcode_test.go
src/cmd/compile/internal/ssa/deadstore.go
src/cmd/compile/internal/ssa/deadstore_test.go
src/cmd/compile/internal/ssa/dom.go
src/cmd/compile/internal/ssa/export_test.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssa/func_test.go
src/cmd/compile/internal/ssa/gen/generic.rules
src/cmd/compile/internal/ssa/layout.go
src/cmd/compile/internal/ssa/lower.go
src/cmd/compile/internal/ssa/print.go
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/rewrite.go
src/cmd/compile/internal/ssa/rewritegeneric.go
src/cmd/compile/internal/ssa/schedule_test.go
src/cmd/compile/internal/ssa/shift_test.go
src/cmd/compile/internal/ssa/stackalloc.go
src/cmd/compile/internal/ssa/value.go

index e6b670f7a29004845aa9f8fd3f05378989cde2b1..6a6c213b848f7b1d8945bea5fc894c201499d15e 100644 (file)
@@ -355,6 +355,7 @@ func compile(fn *Node) {
        var gcargs *Sym
        var gclocals *Sym
        var ssafn *ssa.Func
+       var usessa bool
        if fn.Nbody == nil {
                if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
                        Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
@@ -406,13 +407,9 @@ func compile(fn *Node) {
                goto ret
        }
 
-       // Build an SSA backend function
-       {
-               name := Curfn.Func.Nname.Sym.Name
-               if len(name) > 4 && name[len(name)-4:] == "_ssa" {
-                       ssafn = buildssa(Curfn)
-               }
-       }
+       // Build an SSA backend function.
+       // TODO: get rid of usessa.
+       ssafn, usessa = buildssa(Curfn)
 
        continpc = nil
        breakpc = nil
@@ -475,7 +472,7 @@ func compile(fn *Node) {
                }
        }
 
-       if ssafn != nil {
+       if ssafn != nil && usessa {
                genssa(ssafn, ptxt, gcargs, gclocals)
                return
        }
index f2dbabe6ad0d37c788854370859911e907b4f294..1218a23488a9f5f9f7cdd6e7a1d308a14b908b65 100644 (file)
@@ -5,26 +5,48 @@
 package gc
 
 import (
-       "log"
+       "fmt"
 
        "cmd/compile/internal/ssa"
        "cmd/internal/obj"
        "cmd/internal/obj/x86" // TODO: remove
 )
 
-func buildssa(fn *Node) *ssa.Func {
-       dumplist("buildssa-enter", fn.Func.Enter)
-       dumplist("buildssa-body", fn.Nbody)
+// buildssa builds an SSA function
+// and reports whether it should be used.
+// Once the SSA implementation is complete,
+// it will never return nil, and the bool can be removed.
+func buildssa(fn *Node) (ssafn *ssa.Func, usessa bool) {
+       name := fn.Func.Nname.Sym.Name
+       usessa = len(name) > 4 && name[len(name)-4:] == "_ssa"
+
+       if usessa {
+               dumplist("buildssa-enter", fn.Func.Enter)
+               dumplist("buildssa-body", fn.Nbody)
+       }
 
        var s state
-
        s.pushLine(fn.Lineno)
        defer s.popLine()
 
        // TODO(khr): build config just once at the start of the compiler binary
-       s.config = ssa.NewConfig(Thearch.Thestring, ssaExport{})
+
+       var e ssaExport
+       e.log = usessa
+       s.config = ssa.NewConfig(Thearch.Thestring, &e)
        s.f = s.config.NewFunc()
-       s.f.Name = fn.Func.Nname.Sym.Name
+       s.f.Name = name
+
+       // If SSA support for the function is incomplete,
+       // assume that any panics are due to violated
+       // invariants. Swallow them silently.
+       defer func() {
+               if err := recover(); err != nil {
+                       if !e.unimplemented {
+                               panic(err)
+                       }
+               }
+       }()
 
        // We construct SSA using an algorithm similar to
        // Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
@@ -67,7 +89,15 @@ func buildssa(fn *Node) *ssa.Func {
        // Main call to ssa package to compile function
        ssa.Compile(s.f)
 
-       return s.f
+       // Calculate stats about what percentage of functions SSA handles.
+       if false {
+               fmt.Printf("SSA implemented: %t\n", !e.unimplemented)
+       }
+
+       if e.unimplemented {
+               return nil, false
+       }
+       return s.f, usessa // TODO: return s.f, true once runtime support is in (gc maps, write barriers, etc.)
 }
 
 type state struct {
@@ -105,10 +135,13 @@ type state struct {
        line []int32
 }
 
+func (s *state) Fatal(msg string, args ...interface{})         { s.config.Fatal(msg, args...) }
+func (s *state) Unimplemented(msg string, args ...interface{}) { s.config.Unimplemented(msg, args...) }
+
 // startBlock sets the current block we're generating code in to b.
 func (s *state) startBlock(b *ssa.Block) {
        if s.curBlock != nil {
-               log.Fatalf("starting block %v when block %v has not ended", b, s.curBlock)
+               s.Fatal("starting block %v when block %v has not ended", b, s.curBlock)
        }
        s.curBlock = b
        s.vars = map[string]*ssa.Value{}
@@ -230,7 +263,7 @@ func (s *state) stmt(n *Node) {
                        return
                }
                if compiling_runtime != 0 {
-                       log.Fatalf("%v escapes to heap, not allowed in runtime.", n)
+                       Fatal("%v escapes to heap, not allowed in runtime.", n)
                }
 
                // TODO: the old pass hides the details of PHEAP
@@ -260,6 +293,9 @@ func (s *state) stmt(n *Node) {
                        // next we work on the label's target block
                        s.startBlock(t)
                }
+               if n.Op == OGOTO && s.curBlock == nil {
+                       s.Unimplemented("goto at start of function; see test/goto.go")
+               }
 
        case OAS, OASWB:
                s.assign(n.Op, n.Left, n.Right)
@@ -317,6 +353,9 @@ func (s *state) stmt(n *Node) {
 
                // generate code to test condition
                // TODO(khr): Left == nil exception
+               if n.Left == nil {
+                       s.Unimplemented("cond n.Left == nil: %v", n)
+               }
                s.startBlock(bCond)
                cond := s.expr(n.Left)
                b = s.endBlock()
@@ -342,7 +381,7 @@ func (s *state) stmt(n *Node) {
                // TODO(khr): ??? anything to do here?  Only for addrtaken variables?
                // Maybe just link it in the store chain?
        default:
-               log.Fatalf("unhandled stmt %s", opnames[n.Op])
+               s.Unimplemented("unhandled stmt %s", opnames[n.Op])
        }
 }
 
@@ -370,7 +409,7 @@ func (s *state) expr(n *Node) *ssa.Value {
                case CTSTR:
                        return s.entryNewValue0A(ssa.OpConst, n.Type, n.Val().U)
                default:
-                       log.Fatalf("unhandled OLITERAL %v", n.Val().Ctype())
+                       s.Unimplemented("unhandled OLITERAL %v", n.Val().Ctype())
                        return nil
                }
        case OCONVNOP:
@@ -474,7 +513,7 @@ func (s *state) expr(n *Node) *ssa.Value {
                a := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp)
                return s.newValue2(ssa.OpLoad, fp.Type, a, call)
        default:
-               log.Fatalf("unhandled expr %s", opnames[n.Op])
+               s.Unimplemented("unhandled expr %s", opnames[n.Op])
                return nil
        }
 }
@@ -494,7 +533,7 @@ func (s *state) assign(op uint8, left *Node, right *Node) {
                case t.IsBoolean():
                        val = s.entryNewValue0A(ssa.OpConst, left.Type, false) // TODO: store bools as 0/1 in AuxInt?
                default:
-                       log.Fatalf("zero for type %v not implemented", t)
+                       s.Unimplemented("zero for type %v not implemented", t)
                }
        } else {
                val = s.expr(right)
@@ -524,7 +563,7 @@ func (s *state) addr(n *Node) *ssa.Value {
                        return s.expr(n.Name.Heapaddr)
                default:
                        // TODO: address of locals
-                       log.Fatalf("variable address of %v not implemented", n)
+                       s.Unimplemented("variable address of %v not implemented", n)
                        return nil
                }
        case OINDREG:
@@ -547,7 +586,7 @@ func (s *state) addr(n *Node) *ssa.Value {
                        return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i)
                }
        default:
-               log.Fatalf("addr: bad op %v", Oconv(int(n.Op), 0))
+               s.Unimplemented("addr: bad op %v", Oconv(int(n.Op), 0))
                return nil
        }
 }
@@ -556,7 +595,7 @@ func (s *state) addr(n *Node) *ssa.Value {
 // n must be an ONAME.
 func canSSA(n *Node) bool {
        if n.Op != ONAME {
-               log.Fatalf("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n)
+               Fatal("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n)
        }
        if n.Addrtaken {
                return false
@@ -610,7 +649,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value) {
 // variable returns the value of a variable at the current location.
 func (s *state) variable(name string, t ssa.Type) *ssa.Value {
        if s.curBlock == nil {
-               log.Fatalf("nil curblock!")
+               s.Fatal("nil curblock!")
        }
        v := s.vars[name]
        if v == nil {
@@ -662,6 +701,10 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va
        for _, p := range b.Preds {
                vals = append(vals, s.lookupVarOutgoing(p, t, name))
        }
+       if len(vals) == 0 {
+               s.Unimplemented("TODO: Handle fixedbugs/bug076.go")
+               return nil
+       }
        v0 := vals[0]
        for i := 1; i < len(vals); i++ {
                if vals[i] != v0 {
@@ -822,11 +865,14 @@ func genValue(v *ssa.Value) {
                p.To.Type = obj.TYPE_REG
                p.To.Reg = regnum(v)
        case ssa.OpAMD64MULQconst:
+               v.Unimplemented("IMULQ doasm")
+               return
                // TODO: this isn't right.  doasm fails on it.  I don't think obj
                // has ever been taught to compile imul $c, r1, r2.
                p := Prog(x86.AIMULQ)
                p.From.Type = obj.TYPE_CONST
                p.From.Offset = v.AuxInt
+               p.From3 = new(obj.Addr)
                p.From3.Type = obj.TYPE_REG
                p.From3.Reg = regnum(v.Args[0])
                p.To.Type = obj.TYPE_REG
@@ -854,7 +900,7 @@ func genValue(v *ssa.Value) {
                r := regnum(v)
                if x != r {
                        if r == x86.REG_CX {
-                               log.Fatalf("can't implement %s, target and shift both in CX", v.LongString())
+                               v.Fatal("can't implement %s, target and shift both in CX", v.LongString())
                        }
                        p := Prog(x86.AMOVQ)
                        p.From.Type = obj.TYPE_REG
@@ -1003,12 +1049,12 @@ func genValue(v *ssa.Value) {
                loc := f.RegAlloc[v.ID]
                for _, a := range v.Args {
                        if f.RegAlloc[a.ID] != loc { // TODO: .Equal() instead?
-                               log.Fatalf("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID])
+                               v.Fatal("phi arg at different location than phi %v %v %v %v", v, loc, a, f.RegAlloc[a.ID])
                        }
                }
        case ssa.OpConst:
                if v.Block.Func.RegAlloc[v.ID] != nil {
-                       log.Fatalf("const value %v shouldn't have a location", v)
+                       v.Fatal("const value %v shouldn't have a location", v)
                }
        case ssa.OpArg:
                // memory arg needs no code
@@ -1033,7 +1079,7 @@ func genValue(v *ssa.Value) {
        case ssa.OpFP, ssa.OpSP:
                // nothing to do
        default:
-               log.Fatalf("value %s not implemented", v.LongString())
+               v.Unimplemented("value %s not implemented", v.LongString())
        }
 }
 
@@ -1141,7 +1187,7 @@ func genBlock(b, next *ssa.Block, branches []branch) []branch {
                }
 
        default:
-               log.Fatalf("branch %s not implemented", b.LongString())
+               b.Unimplemented("branch %s not implemented", b.LongString())
        }
        return branches
 }
@@ -1183,10 +1229,40 @@ func localOffset(v *ssa.Value) int64 {
 }
 
 // ssaExport exports a bunch of compiler services for the ssa backend.
-type ssaExport struct{}
+type ssaExport struct {
+       log           bool
+       unimplemented bool
+}
 
 // StringSym returns a symbol (a *Sym wrapped in an interface) which
 // is a global string constant containing s.
-func (serv ssaExport) StringSym(s string) interface{} {
+func (*ssaExport) StringSym(s string) interface{} {
        return stringsym(s)
 }
+
+// Log logs a message from the compiler.
+func (e *ssaExport) Log(msg string, args ...interface{}) {
+       // If e was marked as unimplemented, anything could happen. Ignore.
+       if e.log && !e.unimplemented {
+               fmt.Printf(msg, args...)
+       }
+}
+
+// Fatal reports a compiler error and exits.
+func (e *ssaExport) Fatal(msg string, args ...interface{}) {
+       // If e was marked as unimplemented, anything could happen. Ignore.
+       if !e.unimplemented {
+               Fatal(msg, args...)
+       }
+}
+
+// Unimplemented reports that the function cannot be compiled.
+// It will be removed once SSA work is complete.
+func (e *ssaExport) Unimplemented(msg string, args ...interface{}) {
+       const alwaysLog = false // enable to calculate top unimplemented features
+       if !e.unimplemented && (e.log || alwaysLog) {
+               // first implementation failure, print explanation
+               fmt.Printf("SSA unimplemented: "+msg+"\n", args...)
+       }
+       e.unimplemented = true
+}
index e9b75535344d6a67779fac91344cade924553bce..64b581fac02ef272552e2bb29b90a0592af9a6de 100644 (file)
@@ -42,7 +42,6 @@ Common-Subexpression Elimination
  - Can we move control values out of their basic block?
 
 Other
- - Use gc.Fatal for errors.  Add a callback to Frontend?
  - Write barriers
  - For testing, do something more sophisticated than
    checkOpcodeCounts.  Michael Matloob suggests using a similar
index db16fb4a53c0b7abce1951c0809691683dead053..e0d5c1a55eb6ee34c92395813af58cbeefaa9b55 100644 (file)
@@ -69,3 +69,7 @@ func (b *Block) LongString() string {
        }
        return s
 }
+
+func (b *Block) Log(msg string, args ...interface{})           { b.Func.Log(msg, args...) }
+func (b *Block) Fatal(msg string, args ...interface{})         { b.Func.Fatal(msg, args...) }
+func (b *Block) Unimplemented(msg string, args ...interface{}) { b.Func.Unimplemented(msg, args...) }
index 667313ad9f825e34a9417f6fd5329b2a7e95b2ca..230d0ec11194e41b75ff6c738d7c12ef23d3d883 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // checkFunc checks invariants of f.
 func checkFunc(f *Func) {
        blockMark := make([]bool, f.NumBlocks())
@@ -13,17 +11,17 @@ func checkFunc(f *Func) {
 
        for _, b := range f.Blocks {
                if blockMark[b.ID] {
-                       log.Panicf("block %s appears twice in %s!", b, f.Name)
+                       f.Fatal("block %s appears twice in %s!", b, f.Name)
                }
                blockMark[b.ID] = true
                if b.Func != f {
-                       log.Panicf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)
+                       f.Fatal("%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 {
-                                       log.Panicf("%s.Succs has duplicate block %s", b, c)
+                                       f.Fatal("%s.Succs has duplicate block %s", b, c)
                                }
                        }
                }
@@ -46,64 +44,64 @@ func checkFunc(f *Func) {
                                }
                        }
                        if !found {
-                               log.Panicf("block %s is not a succ of its pred block %s", b, p)
+                               f.Fatal("block %s is not a succ of its pred block %s", b, p)
                        }
                }
 
                switch b.Kind {
                case BlockExit:
                        if len(b.Succs) != 0 {
-                               log.Panicf("exit block %s has successors", b)
+                               f.Fatal("exit block %s has successors", b)
                        }
                        if b.Control == nil {
-                               log.Panicf("exit block %s has no control value", b)
+                               f.Fatal("exit block %s has no control value", b)
                        }
                        if !b.Control.Type.IsMemory() {
-                               log.Panicf("exit block %s has non-memory control value %s", b, b.Control.LongString())
+                               f.Fatal("exit block %s has non-memory control value %s", b, b.Control.LongString())
                        }
                case BlockPlain:
                        if len(b.Succs) != 1 {
-                               log.Panicf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))
+                               f.Fatal("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))
                        }
                        if b.Control != nil {
-                               log.Panicf("plain block %s has non-nil control %s", b, b.Control.LongString())
+                               f.Fatal("plain block %s has non-nil control %s", b, b.Control.LongString())
                        }
                case BlockIf:
                        if len(b.Succs) != 2 {
-                               log.Panicf("if block %s len(Succs)==%d, want 2", b, len(b.Succs))
+                               f.Fatal("if block %s len(Succs)==%d, want 2", b, len(b.Succs))
                        }
                        if b.Control == nil {
-                               log.Panicf("if block %s has no control value", b)
+                               f.Fatal("if block %s has no control value", b)
                        }
                        if !b.Control.Type.IsBoolean() {
-                               log.Panicf("if block %s has non-bool control value %s", b, b.Control.LongString())
+                               f.Fatal("if block %s has non-bool control value %s", b, b.Control.LongString())
                        }
                case BlockCall:
                        if len(b.Succs) != 2 {
-                               log.Panicf("call block %s len(Succs)==%d, want 2", b, len(b.Succs))
+                               f.Fatal("call block %s len(Succs)==%d, want 2", b, len(b.Succs))
                        }
                        if b.Control == nil {
-                               log.Panicf("call block %s has no control value", b)
+                               f.Fatal("call block %s has no control value", b)
                        }
                        if !b.Control.Type.IsMemory() {
-                               log.Panicf("call block %s has non-memory control value %s", b, b.Control.LongString())
+                               f.Fatal("call block %s has non-memory control value %s", b, b.Control.LongString())
                        }
                        if b.Succs[1].Kind != BlockExit {
-                               log.Panicf("exception edge from call block %s does not go to exit but %s", b, b.Succs[1])
+                               f.Fatal("exception edge from call block %s does not go to exit but %s", b, b.Succs[1])
                        }
                }
 
                for _, v := range b.Values {
                        if valueMark[v.ID] {
-                               log.Panicf("value %s appears twice!", v.LongString())
+                               f.Fatal("value %s appears twice!", v.LongString())
                        }
                        valueMark[v.ID] = true
 
                        if v.Block != b {
-                               log.Panicf("%s.block != %s", v, b)
+                               f.Fatal("%s.block != %s", v, b)
                        }
                        if v.Op == OpPhi && len(v.Args) != len(b.Preds) {
-                               log.Panicf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b)
+                               f.Fatal("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b)
                        }
 
                        // TODO: check for cycles in values
@@ -113,12 +111,12 @@ func checkFunc(f *Func) {
 
        for _, id := range f.bid.free {
                if blockMark[id] {
-                       log.Panicf("used block b%d in free list", id)
+                       f.Fatal("used block b%d in free list", id)
                }
        }
        for _, id := range f.vid.free {
                if valueMark[id] {
-                       log.Panicf("used value v%d in free list", id)
+                       f.Fatal("used value v%d in free list", id)
                }
        }
 }
index 02c9b5a4a9e01b8bf5863fdd31c8090eb8728659..896be01b687a9f6ec244902f5a1294b3cb76d2cb 100644 (file)
@@ -4,10 +4,7 @@
 
 package ssa
 
-import (
-       "fmt"
-       "log"
-)
+import "log"
 
 // Compile is the main entry point for this package.
 // Compile modifies f so that on return:
@@ -18,13 +15,13 @@ import (
 func Compile(f *Func) {
        // TODO: debugging - set flags to control verbosity of compiler,
        // which phases to dump IR before/after, etc.
-       fmt.Printf("compiling %s\n", f.Name)
+       f.Log("compiling %s\n", f.Name)
 
        // hook to print function & phase if panic happens
        phaseName := "init"
        defer func() {
                if phaseName != "" {
-                       fmt.Printf("panic during %s while compiling %s\n", phaseName, f.Name)
+                       f.Fatal("panic during %s while compiling %s\n", phaseName, f.Name)
                }
        }()
 
@@ -33,9 +30,9 @@ func Compile(f *Func) {
        checkFunc(f)
        for _, p := range passes {
                phaseName = p.name
-               fmt.Printf("  pass %s begin\n", p.name)
+               f.Log("  pass %s begin\n", p.name)
                p.fn(f)
-               fmt.Printf("  pass %s end\n", p.name)
+               f.Log("  pass %s end\n", p.name)
                printFunc(f)
                checkFunc(f)
        }
index db2d80a7c46c7f186772c25bf5cd37c494420132..60c1a5a50b2de6731c23d2fd83ad003c0a4b121a 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 type Config struct {
        arch       string                     // "amd64", etc.
        ptrSize    int64                      // 4 or 8
@@ -22,6 +20,16 @@ type Frontend interface {
        // Strings are laid out in read-only memory with one word of pointer,
        // one word of length, then the contents of the string.
        StringSym(string) interface{} // returns *gc.Sym
+
+       // Log logs a message from the compiler.
+       Log(string, ...interface{})
+
+       // Fatal reports a compiler error and exits.
+       Fatal(string, ...interface{})
+
+       // Unimplemented reports that the function cannot be compiled.
+       // It will be removed once SSA work is complete.
+       Unimplemented(msg string, args ...interface{})
 }
 
 // NewConfig returns a new configuration object for the given architecture.
@@ -37,7 +45,7 @@ func NewConfig(arch string, fe Frontend) *Config {
                c.lowerBlock = rewriteBlockAMD64
                c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
        default:
-               log.Fatalf("arch %s not implemented", arch)
+               fe.Unimplemented("arch %s not implemented", arch)
        }
 
        // cache the intptr type in the config
@@ -55,5 +63,9 @@ func (c *Config) NewFunc() *Func {
        return &Func{Config: c}
 }
 
+func (c *Config) Log(msg string, args ...interface{})           { c.fe.Log(msg, args...) }
+func (c *Config) Fatal(msg string, args ...interface{})         { c.fe.Fatal(msg, args...) }
+func (c *Config) Unimplemented(msg string, args ...interface{}) { c.fe.Unimplemented(msg, args...) }
+
 // TODO(khr): do we really need a separate Config, or can we just
 // store all its fields inside a Func?
index 1a5589cd0aa68a55337d661bd320a65a95d91bc6..f4884520de9077ff2e94c1b56ce50b346f7e02d8 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // deadcode removes dead code from f.
 func deadcode(f *Func) {
 
@@ -82,7 +80,7 @@ func deadcode(f *Func) {
                        i++
                } else {
                        if len(b.Values) > 0 {
-                               log.Panicf("live values in unreachable block %v: %v", b, b.Values)
+                               b.Fatal("live values in unreachable block %v: %v", b, b.Values)
                        }
                        f.bid.put(b.ID)
                }
@@ -105,7 +103,7 @@ func removePredecessor(b, c *Block) {
        if n == 0 {
                // c is now dead - don't bother working on it
                if c.Preds[0] != b {
-                       log.Panicf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b)
+                       b.Fatal("%s.Preds[0]==%s, want %s", c, c.Preds[0], b)
                }
                return
        }
index edd38e1254ddb0d4ef348f089a9669fbf0775ef3..ff9e6800dabb71a4aa44a93c2acb75c87a3252fd 100644 (file)
@@ -7,7 +7,7 @@ package ssa
 import "testing"
 
 func TestDeadLoop(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        fun := Fun(c, "entry",
                Bloc("entry",
                        Valu("mem", OpArg, TypeMem, 0, ".mem"),
@@ -37,7 +37,7 @@ func TestDeadLoop(t *testing.T) {
 }
 
 func TestDeadValue(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        fun := Fun(c, "entry",
                Bloc("entry",
                        Valu("mem", OpArg, TypeMem, 0, ".mem"),
@@ -60,7 +60,7 @@ func TestDeadValue(t *testing.T) {
 }
 
 func TestNeverTaken(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        fun := Fun(c, "entry",
                Bloc("entry",
                        Valu("cond", OpConst, TypeBool, 0, false),
index b02b35460a175ddf3bd02fc56df9af6a187d0e17..e4d73e722659eec2a99ac52f92cf8aa20bb724e4 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // dse does dead-store elimination on the Function.
 // Dead stores are those which are unconditionally followed by
 // another store to the same location, with no intervening load.
@@ -58,12 +56,12 @@ func dse(f *Func) {
                                continue
                        }
                        if last != nil {
-                               log.Fatalf("two final stores - simultaneous live stores", last, v)
+                               b.Fatal("two final stores - simultaneous live stores", last, v)
                        }
                        last = v
                }
                if last == nil {
-                       log.Fatalf("no last store found - cycle?")
+                       b.Fatal("no last store found - cycle?")
                }
 
                // Walk backwards looking for dead stores.  Keep track of shadowed addresses.
index 5143afb6cb54eedbf3f4735ca30ac3aa6e95e266..48ea066aa3947236f49db3225bc63a8bc07f074d 100644 (file)
@@ -9,7 +9,7 @@ import (
 )
 
 func TestDeadStore(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
        fun := Fun(c, "entry",
                Bloc("entry",
@@ -35,7 +35,7 @@ func TestDeadStore(t *testing.T) {
 }
 func TestDeadStorePhi(t *testing.T) {
        // make sure we don't get into an infinite loop with phi values.
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
        fun := Fun(c, "entry",
                Bloc("entry",
@@ -60,7 +60,7 @@ func TestDeadStoreTypes(t *testing.T) {
        // stronger restriction, that one store can't shadow another unless the
        // types of the address fields are identical (where identicalness is
        // decided by the CSE pass).
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        t1 := &TypeImpl{Size_: 8, Ptr: true, Name: "t1"}
        t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"}
        fun := Fun(c, "entry",
index aaf3ab3da1f224f5fb027a9f172ebaf59040d4f4..fac2798a609cd3e6a286d993d33229b4c83d63db 100644 (file)
@@ -7,8 +7,6 @@ package ssa
 // This file contains code to compute the dominator tree
 // of a control-flow graph.
 
-import "log"
-
 // postorder computes a postorder traversal ordering for the
 // basic blocks in f.  Unreachable blocks will not appear.
 func postorder(f *Func) []*Block {
@@ -47,7 +45,7 @@ func postorder(f *Func) []*Block {
                                }
                        }
                default:
-                       log.Fatalf("bad stack state %v %d", b, mark[b.ID])
+                       b.Fatal("bad stack state %v %d", b, mark[b.ID])
                }
        }
        return order
@@ -73,7 +71,7 @@ func dominators(f *Func) []*Block {
        // Make the entry block a self-loop
        idom[f.Entry.ID] = f.Entry
        if postnum[f.Entry.ID] != len(post)-1 {
-               log.Fatalf("entry block %v not last in postorder", f.Entry)
+               f.Fatal("entry block %v not last in postorder", f.Entry)
        }
 
        // Compute relaxation of idom entries
index 103945a73e490d0c9e77adac22050742199c0a3c..6b006e92388b1c399371cf1387f98cc86dafe074 100644 (file)
@@ -4,13 +4,21 @@
 
 package ssa
 
+import "testing"
+
 var CheckFunc = checkFunc
 var PrintFunc = printFunc
 var Opt = opt
 var Deadcode = deadcode
 
-type DummyFrontend struct{}
+type DummyFrontend struct {
+       t *testing.T
+}
 
-func (DummyFrontend) StringSym(s string) interface{} {
+func (DummyFrontend) StringSym(s string) interface{} {
        return nil
 }
+
+func (d DummyFrontend) Log(msg string, args ...interface{})           { d.t.Logf(msg, args...) }
+func (d DummyFrontend) Fatal(msg string, args ...interface{})         { d.t.Fatalf(msg, args...) }
+func (d DummyFrontend) Unimplemented(msg string, args ...interface{}) { d.t.Fatalf(msg, args...) }
index d73e0ea9e02a42f2c1a3dc11819885e63f5407b1..56bee1aa3fa7e70cfaa9accffcdcd2f6d0b9f137 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // A Func represents a Go func declaration (or function literal) and
 // its body.  This package compiles each Func independently.
 type Func struct {
@@ -79,7 +77,7 @@ func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value {
                // Disallow int64 aux values.  They should be in the auxint field instead.
                // Maybe we want to allow this at some point, but for now we disallow it
                // to prevent errors like using NewValue1A instead of NewValue1I.
-               log.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux)
+               b.Fatal("aux field has int64 type op=%s type=%s aux=%v", op, t, aux)
        }
        v := &Value{
                ID:    b.Func.vid.get(),
@@ -209,3 +207,7 @@ func (f *Func) ConstInt(line int32, t Type, c int64) *Value {
        // TODO: cache?
        return f.Entry.NewValue0I(line, OpConst, t, c)
 }
+
+func (f *Func) Log(msg string, args ...interface{})           { f.Config.Log(msg, args...) }
+func (f *Func) Fatal(msg string, args ...interface{})         { f.Config.Fatal(msg, args...) }
+func (f *Func) Unimplemented(msg string, args ...interface{}) { f.Config.Unimplemented(msg, args...) }
index 7cfc7324ac7c33ce81bcff8a556404a22bcc4f39..b52d470e24134117a71dd1cdb41fa20cedafb634 100644 (file)
@@ -37,7 +37,7 @@ package ssa
 //                the parser can be used instead of Fun.
 
 import (
-       "log"
+       "fmt"
        "reflect"
        "testing"
 )
@@ -161,7 +161,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun {
                if c.control != "" {
                        cval, ok := values[c.control]
                        if !ok {
-                               log.Panicf("control value for block %s missing", bloc.name)
+                               f.Fatal("control value for block %s missing", bloc.name)
                        }
                        b.Control = cval
                }
@@ -171,7 +171,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun {
                        for _, arg := range valu.args {
                                a, ok := values[arg]
                                if !ok {
-                                       log.Panicf("arg %s missing for value %s in block %s",
+                                       b.Fatal("arg %s missing for value %s in block %s",
                                                arg, valu.name, bloc.name)
                                }
                                v.AddArg(a)
@@ -197,7 +197,7 @@ func Bloc(name string, entries ...interface{}) bloc {
                case ctrl:
                        // there should be exactly one Ctrl entry.
                        if seenCtrl {
-                               log.Panicf("already seen control for block %s", name)
+                               panic(fmt.Sprintf("already seen control for block %s", name))
                        }
                        b.control = v
                        seenCtrl = true
@@ -206,7 +206,7 @@ func Bloc(name string, entries ...interface{}) bloc {
                }
        }
        if !seenCtrl {
-               log.Panicf("block %s doesn't have control", b.name)
+               panic(fmt.Sprintf("block %s doesn't have control", b.name))
        }
        return b
 }
@@ -262,7 +262,7 @@ func addEdge(b, c *Block) {
 }
 
 func TestArgs(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        fun := Fun(c, "entry",
                Bloc("entry",
                        Valu("a", OpConst, TypeInt64, 14, nil),
@@ -282,7 +282,7 @@ func TestArgs(t *testing.T) {
 }
 
 func TestEquiv(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        equivalentCases := []struct{ f, g fun }{
                // simple case
                {
index e0bba1706fe12bf612a53f6a0529f9b532cbdc4e..9d08a35f1f807b9e74900502857782e605dc9bb9 100644 (file)
@@ -32,7 +32,7 @@
 
 // indexing operations
 // Note: bounds check has already been done
-(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex <ptr.Type.Elem().Elem().PtrTo()> ptr idx) mem)
+(ArrayIndex (Load ptr mem) idx) -> (Load (PtrIndex <v.Type.PtrTo()> ptr idx) mem)
 (PtrIndex <t> ptr idx) -> (Add ptr (Mul <config.Uintptr> idx (Const <config.Uintptr> [t.Elem().Size()])))
 
 // big-object moves
index 7123397c4c39a571e7117e8839a1e78ba327be00..0a271b39ade5032f07dfe445f68d09f969df51d2 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // layout orders basic blocks in f with the goal of minimizing control flow instructions.
 // After this phase returns, the order of f.Blocks matters and is the order
 // in which those blocks will appear in the assembly output.
@@ -82,7 +80,7 @@ blockloop:
                                continue blockloop
                        }
                }
-               log.Panicf("no block available for layout")
+               b.Fatal("no block available for layout")
        }
        f.Blocks = order
 }
index 2ca1db784eb0acd13c6b88a3d562b64b933bbd81..768ac124be508491203ce042917698806afd8d56 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // convert to machine-dependent ops
 func lower(f *Func) {
        // repeat rewrites until we find no more rewrites
@@ -15,7 +13,7 @@ func lower(f *Func) {
        for _, b := range f.Blocks {
                for _, v := range b.Values {
                        if opcodeTable[v.Op].generic && v.Op != OpFP && v.Op != OpSP && v.Op != OpArg && v.Op != OpCopy && v.Op != OpPhi {
-                               log.Panicf("%s not lowered", v.LongString())
+                               f.Unimplemented("%s not lowered", v.LongString())
                        }
                }
        }
index b9a958c18eee1f51b50620d09c18f01dfd482345..c1b97d2b8f571b74bda6838d56ec5b83ab439a82 100644 (file)
@@ -8,11 +8,10 @@ import (
        "bytes"
        "fmt"
        "io"
-       "os"
 )
 
 func printFunc(f *Func) {
-       fprintFunc(os.Stdout, f)
+       f.Log("%s", f.String())
 }
 
 func (f *Func) String() string {
index 6f7d6192479fc0668da9aeae84a263e6ba892630..d1489b20f2252b515f1402b0d51aa4d47fd2cf50 100644 (file)
@@ -4,11 +4,7 @@
 
 package ssa
 
-import (
-       "fmt"
-       "log"
-       "sort"
-)
+import "sort"
 
 func setloc(home []Location, v *Value, loc Location) []Location {
        for v.ID >= ID(len(home)) {
@@ -353,7 +349,7 @@ func regalloc(f *Func) {
                if b.Kind == BlockCall {
                        call = b.Control
                        if call != b.Values[len(b.Values)-1] {
-                               log.Fatalf("call not at end of block %b %v", b, call)
+                               b.Fatal("call not at end of block %b %v", b, call)
                        }
                        b.Values = b.Values[:len(b.Values)-1]
                        // TODO: do this for all control types?
@@ -423,7 +419,7 @@ func live(f *Func) [][]ID {
        t := newSparseSet(f.NumValues())
        for {
                for _, b := range f.Blocks {
-                       fmt.Printf("live %s %v\n", b, live[b.ID])
+                       f.Log("live %s %v\n", b, live[b.ID])
                }
                changed := false
 
index 77aa2b07b4ced84bd370ff6ec7ae7b4eb8f8585d..2bfd3813edc5e3632f94eba61c3083b8d121d7a0 100644 (file)
@@ -4,7 +4,7 @@
 
 package ssa
 
-import "log"
+import "fmt"
 
 func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) {
        // repeat rewrites until we find no more rewrites
@@ -12,11 +12,10 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool)
        var curv *Value
        defer func() {
                if curb != nil {
-                       log.Printf("panic during rewrite of block %s\n", curb.LongString())
+                       curb.Fatal("panic during rewrite of block %s\n", curb.LongString())
                }
                if curv != nil {
-                       log.Printf("panic during rewrite of value %s\n", curv.LongString())
-                       panic("rewrite failed")
+                       curv.Fatal("panic during rewrite of value %s\n", curv.LongString())
                        // TODO(khr): print source location also
                }
        }()
@@ -90,12 +89,12 @@ func typeSize(t Type) int64 {
        return t.Size()
 }
 
-// addOff adds two int64 offsets.  Fails if wraparound happens.
+// addOff adds two int64 offsets. Fails if wraparound happens.
 func addOff(x, y int64) int64 {
        z := x + y
        // x and y have same sign and z has a different sign => overflow
        if x^y >= 0 && x^z < 0 {
-               log.Panicf("offset overflow %d %d\n", x, y)
+               panic(fmt.Sprintf("offset overflow %d %d", x, y))
        }
        return z
 }
index 0ecc4363432559c01529ed27058fb32dc9685b37..ac4f00988134859e9a0416b8d12d1f415ea94cc7 100644 (file)
@@ -34,10 +34,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
        case OpArrayIndex:
                // match: (ArrayIndex (Load ptr mem) idx)
                // cond:
-               // result: (Load (PtrIndex <ptr.Type.Elem().Elem().PtrTo()> ptr idx) mem)
+               // result: (Load (PtrIndex <v.Type.PtrTo()> ptr idx) mem)
                {
                        if v.Args[0].Op != OpLoad {
-                               goto end3809f4c52270a76313e4ea26e6f0b753
+                               goto end4894dd7b58383fee5f8a92be08437c33
                        }
                        ptr := v.Args[0].Args[0]
                        mem := v.Args[0].Args[1]
@@ -47,15 +47,15 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
                        v.Aux = nil
                        v.resetArgs()
                        v0 := v.Block.NewValue0(v.Line, OpPtrIndex, TypeInvalid)
-                       v0.Type = ptr.Type.Elem().Elem().PtrTo()
+                       v0.Type = v.Type.PtrTo()
                        v0.AddArg(ptr)
                        v0.AddArg(idx)
                        v.AddArg(v0)
                        v.AddArg(mem)
                        return true
                }
-               goto end3809f4c52270a76313e4ea26e6f0b753
-       end3809f4c52270a76313e4ea26e6f0b753:
+               goto end4894dd7b58383fee5f8a92be08437c33
+       end4894dd7b58383fee5f8a92be08437c33:
                ;
        case OpConst:
                // match: (Const <t> {s})
index a7c33d9d59a57072d59305075df2d774829551d9..a9432579f7b02caa21abfb217e4fe7d7cd7f0e07 100644 (file)
@@ -7,7 +7,7 @@ package ssa
 import "testing"
 
 func TestSchedule(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        cases := []fun{
                Fun(c, "entry",
                        Bloc("entry",
index b4b4f47ff06bfbcb37690be42f8657f9c7fa8923..52ddbbe42de731b91d7248d38016679a0872a9ad 100644 (file)
@@ -9,7 +9,7 @@ import (
 )
 
 func TestShiftConstAMD64(t *testing.T) {
-       c := NewConfig("amd64", DummyFrontend{})
+       c := NewConfig("amd64", DummyFrontend{t})
        fun := makeConstShiftFunc(c, 18, OpLsh, TypeUInt64)
        checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0})
        fun = makeConstShiftFunc(c, 66, OpLsh, TypeUInt64)
index 5db7316dca4ab48553b05f14f25a36645e857c15..452d0c75a1b36c4cb8cc17b4245e71d7821b0ccf 100644 (file)
@@ -4,8 +4,6 @@
 
 package ssa
 
-import "log"
-
 // stackalloc allocates storage in the stack frame for
 // all Values that did not get a register.
 func stackalloc(f *Func) {
@@ -79,7 +77,7 @@ func stackalloc(f *Func) {
                for _, v := range b.Values {
                        if v.Op == OpFP {
                                if fp != nil {
-                                       log.Panicf("multiple FP ops: %s %s", fp, v)
+                                       b.Fatal("multiple FP ops: %s %s", fp, v)
                                }
                                fp = v
                        }
@@ -99,12 +97,12 @@ func stackalloc(f *Func) {
                                case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVLload, OpAMD64MOVLstore, OpAMD64MOVWload, OpAMD64MOVWstore, OpAMD64MOVBload, OpAMD64MOVBstore, OpAMD64MOVQloadidx8:
                                        if v.Op == OpAMD64MOVQloadidx8 && i == 1 {
                                                // Note: we could do it, but it is probably an error
-                                               log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op)
+                                               f.Fatal("can't do FP->SP adjust on index slot of load %s", v.Op)
                                        }
                                        // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem)
                                        v.AuxInt = addOff(v.AuxInt, n)
                                default:
-                                       log.Panicf("can't do FP->SP adjust on %s", v.Op)
+                                       f.Unimplemented("can't do FP->SP adjust on %s", v.Op)
                                        // TODO: OpCopy -> ADDQ
                                }
                        }
index 3ed1f3c2b906e11ca85332c04d19b7a54f4ec9be..bfba8dc3695e6522de0abe3ab69690fbc0210cf6 100644 (file)
@@ -106,3 +106,7 @@ func (v *Value) resetArgs() {
        v.argstorage[1] = nil
        v.Args = v.argstorage[:0]
 }
+
+func (v *Value) Log(msg string, args ...interface{})           { v.Block.Log(msg, args...) }
+func (v *Value) Fatal(msg string, args ...interface{})         { v.Block.Fatal(msg, args...) }
+func (v *Value) Unimplemented(msg string, args ...interface{}) { v.Block.Unimplemented(msg, args...) }