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)
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
}
}
- if ssafn != nil {
+ if ssafn != nil && usessa {
genssa(ssafn, ptxt, gcargs, gclocals)
return
}
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
// 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 {
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{}
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
// 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)
// 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()
// 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])
}
}
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:
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
}
}
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)
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:
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
}
}
// 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
// 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 {
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 {
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
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
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
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())
}
}
}
default:
- log.Fatalf("branch %s not implemented", b.LongString())
+ b.Unimplemented("branch %s not implemented", b.LongString())
}
return branches
}
}
// 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
+}
- 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
}
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...) }
package ssa
-import "log"
-
// checkFunc checks invariants of f.
func checkFunc(f *Func) {
blockMark := make([]bool, f.NumBlocks())
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)
}
}
}
}
}
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
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)
}
}
}
package ssa
-import (
- "fmt"
- "log"
-)
+import "log"
// Compile is the main entry point for this package.
// Compile modifies f so that on return:
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)
}
}()
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)
}
package ssa
-import "log"
-
type Config struct {
arch string // "amd64", etc.
ptrSize int64 // 4 or 8
// 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.
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
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?
package ssa
-import "log"
-
// deadcode removes dead code from f.
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)
}
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
}
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"),
}
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"),
}
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),
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.
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.
)
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",
}
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",
// 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",
// 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 {
}
}
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
// 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
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 (d 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...) }
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 {
// 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(),
// 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...) }
// the parser can be used instead of Fun.
import (
- "log"
+ "fmt"
"reflect"
"testing"
)
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
}
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)
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
}
}
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
}
}
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),
}
func TestEquiv(t *testing.T) {
- c := NewConfig("amd64", DummyFrontend{})
+ c := NewConfig("amd64", DummyFrontend{t})
equivalentCases := []struct{ f, g fun }{
// simple case
{
// 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
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.
continue blockloop
}
}
- log.Panicf("no block available for layout")
+ b.Fatal("no block available for layout")
}
f.Blocks = order
}
package ssa
-import "log"
-
// convert to machine-dependent ops
func lower(f *Func) {
// repeat rewrites until we find no more rewrites
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())
}
}
}
"bytes"
"fmt"
"io"
- "os"
)
func printFunc(f *Func) {
- fprintFunc(os.Stdout, f)
+ f.Log("%s", f.String())
}
func (f *Func) String() string {
package ssa
-import (
- "fmt"
- "log"
- "sort"
-)
+import "sort"
func setloc(home []Location, v *Value, loc Location) []Location {
for v.ID >= ID(len(home)) {
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?
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
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
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
}
}()
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
}
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]
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})
import "testing"
func TestSchedule(t *testing.T) {
- c := NewConfig("amd64", DummyFrontend{})
+ c := NewConfig("amd64", DummyFrontend{t})
cases := []fun{
Fun(c, "entry",
Bloc("entry",
)
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)
package ssa
-import "log"
-
// stackalloc allocates storage in the stack frame for
// all Values that did not get a register.
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
}
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
}
}
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...) }