[dev.ssa] cmd/internal/gc: convert standard IR into SSA.
Hook into the current compiler to convert the existing
IR (after walk) into SSA. Any function ending in "_ssa"
will take this path. The resulting assembly is printed
and then discarded.
Use gc.Type directly in ssa instead of a wrapper for go types.
It makes the IR->SSA rewrite a lot simpler.
Only a few opcodes are implemented in this change. It is
enough to compile simple examples like
func f(p *int) int { return *p }
func g(a []int, i int) int { return a[i] }
Change-Id: I5e18841b752a83ca0519aa1b2d36ef02ce1de6f9
Reviewed-on: https://go-review.googlesource.com/8971
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 69e077c..daaf66c 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -46,6 +46,7 @@
"internal/obj/arm64",
"internal/obj/ppc64",
"internal/obj/x86",
+ "internal/ssa",
"old5a",
"old6a",
"old8a",
diff --git a/src/cmd/internal/gc/pgen.go b/src/cmd/internal/gc/pgen.go
index b6c9f30..78b41ee 100644
--- a/src/cmd/internal/gc/pgen.go
+++ b/src/cmd/internal/gc/pgen.go
@@ -418,6 +418,15 @@
goto ret
}
+ // Build an SSA backend function
+ {
+ name := Curfn.Nname.Sym.Name
+ if len(name) > 4 && name[len(name)-4:] == "_ssa" {
+ buildssa(Curfn)
+ // TODO(khr): use result of buildssa
+ }
+ }
+
continpc = nil
breakpc = nil
diff --git a/src/cmd/internal/gc/ssa.go b/src/cmd/internal/gc/ssa.go
new file mode 100644
index 0000000..415e9dc
--- /dev/null
+++ b/src/cmd/internal/gc/ssa.go
@@ -0,0 +1,450 @@
+// Copyright 2015 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 gc
+
+import (
+ "log"
+
+ "cmd/internal/ssa"
+)
+
+func buildssa(fn *Node) {
+ dumplist("buildssa", Curfn.Nbody)
+
+ var s ssaState
+
+ // TODO(khr): build config just once at the start of the compiler binary
+ s.config = ssa.NewConfig(Thearch.Thestring)
+ s.f = s.config.NewFunc()
+ s.f.Name = fn.Nname.Sym.Name
+
+ // We construct SSA using an algorithm similar to
+ // Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
+ // http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
+ // TODO: check this comment
+
+ // Allocate starting block
+ s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
+
+ // Allocate exit block
+ s.exit = s.f.NewBlock(ssa.BlockExit)
+
+ // TODO(khr): all args. Make a struct containing args/returnvals, declare
+ // an FP which contains a pointer to that struct.
+
+ s.vars = map[string]*ssa.Value{}
+ s.labels = map[string]*ssa.Block{}
+ s.argOffsets = map[string]int64{}
+
+ // Convert the AST-based IR to the SSA-based IR
+ s.startBlock(s.f.Entry)
+ s.stmtList(fn.Nbody)
+
+ // Finish up exit block
+ s.startBlock(s.exit)
+ s.exit.Control = s.mem()
+ s.endBlock()
+
+ // Link up variable uses to variable definitions
+ s.linkForwardReferences()
+
+ ssa.Compile(s.f)
+
+ // TODO(khr): Use the resulting s.f to generate code
+}
+
+type ssaState struct {
+ // configuration (arch) information
+ config *ssa.Config
+
+ // function we're building
+ f *ssa.Func
+
+ // exit block that "return" jumps to (and panics jump to)
+ exit *ssa.Block
+
+ // the target block for each label in f
+ labels map[string]*ssa.Block
+
+ // current location where we're interpreting the AST
+ curBlock *ssa.Block
+
+ // variable assignments in the current block (map from variable name to ssa value)
+ vars map[string]*ssa.Value
+
+ // all defined variables at the end of each block. Indexed by block ID.
+ defvars []map[string]*ssa.Value
+
+ // offsets of argument slots
+ // unnamed and unused args are not listed.
+ argOffsets map[string]int64
+}
+
+// startBlock sets the current block we're generating code in to b.
+func (s *ssaState) startBlock(b *ssa.Block) {
+ s.curBlock = b
+ s.vars = map[string]*ssa.Value{}
+}
+
+// endBlock marks the end of generating code for the current block.
+// Returns the (former) current block. Returns nil if there is no current
+// block, i.e. if no code flows to the current execution point.
+func (s *ssaState) endBlock() *ssa.Block {
+ b := s.curBlock
+ if b == nil {
+ return nil
+ }
+ for len(s.defvars) <= int(b.ID) {
+ s.defvars = append(s.defvars, nil)
+ }
+ s.defvars[b.ID] = s.vars
+ s.curBlock = nil
+ s.vars = nil
+ return b
+}
+
+// ssaStmtList converts the statement n to SSA and adds it to s.
+func (s *ssaState) stmtList(l *NodeList) {
+ for ; l != nil; l = l.Next {
+ s.stmt(l.N)
+ }
+}
+
+// ssaStmt converts the statement n to SSA and adds it to s.
+func (s *ssaState) stmt(n *Node) {
+ s.stmtList(n.Ninit)
+ switch n.Op {
+
+ case OBLOCK:
+ s.stmtList(n.List)
+
+ case ODCL:
+ // TODO: ??? Assign 0?
+
+ case OLABEL, OGOTO:
+ // get block at label, or make one
+ t := s.labels[n.Left.Sym.Name]
+ if t == nil {
+ t = s.f.NewBlock(ssa.BlockPlain)
+ s.labels[n.Left.Sym.Name] = t
+ }
+ // go to that label (we pretend "label:" is preceded by "goto label")
+ b := s.endBlock()
+ addEdge(b, t)
+
+ if n.Op == OLABEL {
+ // next we work on the label's target block
+ s.startBlock(t)
+ }
+
+ case OAS:
+ // TODO(khr): colas?
+ val := s.expr(n.Right)
+ if n.Left.Op == OINDREG {
+ // indirect off a register (TODO: always SP?)
+ // used for storing arguments to callees
+ addr := s.f.Entry.NewValue(ssa.OpSPAddr, Ptrto(n.Right.Type), n.Left.Xoffset)
+ s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
+ } else if n.Left.Op != ONAME {
+ // some more complicated expression. Rewrite to a store. TODO
+ addr := s.expr(n.Left) // TODO: wrap in &
+
+ // TODO(khr): nil check
+ s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, n.Right.Type, nil, addr, val, s.mem())
+ } else if n.Left.Addable == 0 {
+ // TODO
+ log.Fatalf("assignment to non-addable value")
+ } else if n.Left.Class&PHEAP != 0 {
+ // TODO
+ log.Fatalf("assignment to heap value")
+ } else if n.Left.Class == PPARAMOUT {
+ // store to parameter slot
+ addr := s.f.Entry.NewValue(ssa.OpFPAddr, Ptrto(n.Right.Type), n.Left.Xoffset)
+ s.vars[".mem"] = s.curBlock.NewValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem())
+ } else {
+ // normal variable
+ s.vars[n.Left.Sym.Name] = val
+ }
+ case OIF:
+ cond := s.expr(n.Ntest)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Control = cond
+ // TODO(khr): likely direction
+
+ bThen := s.f.NewBlock(ssa.BlockPlain)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+ var bElse *ssa.Block
+
+ if n.Nelse == nil {
+ addEdge(b, bThen)
+ addEdge(b, bEnd)
+ } else {
+ bElse = s.f.NewBlock(ssa.BlockPlain)
+ addEdge(b, bThen)
+ addEdge(b, bElse)
+ }
+
+ s.startBlock(bThen)
+ s.stmtList(n.Nbody)
+ b = s.endBlock()
+ if b != nil {
+ addEdge(b, bEnd)
+ }
+
+ if n.Nelse != nil {
+ s.startBlock(bElse)
+ s.stmtList(n.Nelse)
+ b = s.endBlock()
+ if b != nil {
+ addEdge(b, bEnd)
+ }
+ }
+ s.startBlock(bEnd)
+
+ case ORETURN:
+ s.stmtList(n.List)
+ b := s.endBlock()
+ addEdge(b, s.exit)
+
+ case OFOR:
+ bCond := s.f.NewBlock(ssa.BlockPlain)
+ bBody := s.f.NewBlock(ssa.BlockPlain)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+
+ // first, jump to condition test
+ b := s.endBlock()
+ addEdge(b, bCond)
+
+ // generate code to test condition
+ // TODO(khr): Ntest == nil exception
+ s.startBlock(bCond)
+ cond := s.expr(n.Ntest)
+ b = s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Control = cond
+ // TODO(khr): likely direction
+ addEdge(b, bBody)
+ addEdge(b, bEnd)
+
+ // generate body
+ s.startBlock(bBody)
+ s.stmtList(n.Nbody)
+ s.stmt(n.Nincr)
+ b = s.endBlock()
+ addEdge(b, bCond)
+
+ s.startBlock(bEnd)
+
+ case OVARKILL:
+ // 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])
+ }
+}
+
+// expr converts the expression n to ssa, adds it to s and returns the ssa result.
+func (s *ssaState) expr(n *Node) *ssa.Value {
+ if n == nil {
+ // TODO(khr): is this nil???
+ return s.f.Entry.NewValue(ssa.OpConst, n.Type, nil)
+ }
+ switch n.Op {
+ case ONAME:
+ // remember offsets for PPARAM names
+ s.argOffsets[n.Sym.Name] = n.Xoffset
+ return s.variable(n.Sym.Name, n.Type)
+ // binary ops
+ case OLITERAL:
+ switch n.Val.Ctype {
+ case CTINT:
+ return s.f.ConstInt(n.Type, Mpgetfix(n.Val.U.Xval))
+ default:
+ log.Fatalf("unhandled OLITERAL %v", n.Val.Ctype)
+ return nil
+ }
+ case OLT:
+ a := s.expr(n.Left)
+ b := s.expr(n.Right)
+ return s.curBlock.NewValue2(ssa.OpLess, ssa.TypeBool, nil, a, b)
+ case OADD:
+ a := s.expr(n.Left)
+ b := s.expr(n.Right)
+ return s.curBlock.NewValue2(ssa.OpAdd, a.Type, nil, a, b)
+
+ case OSUB:
+ // TODO:(khr) fold code for all binary ops together somehow
+ a := s.expr(n.Left)
+ b := s.expr(n.Right)
+ return s.curBlock.NewValue2(ssa.OpSub, a.Type, nil, a, b)
+
+ case OIND:
+ p := s.expr(n.Left)
+ c := s.curBlock.NewValue1(ssa.OpCheckNil, ssa.TypeBool, nil, p)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Control = c
+ bNext := s.f.NewBlock(ssa.BlockPlain)
+ addEdge(b, bNext)
+ addEdge(b, s.exit)
+ s.startBlock(bNext)
+ // TODO(khr): if ptr check fails, don't go directly to exit.
+ // Instead, go to a call to panicnil or something.
+ // TODO: implicit nil checks somehow?
+
+ return s.curBlock.NewValue2(ssa.OpLoad, n.Type, nil, p, s.mem())
+ case ODOTPTR:
+ p := s.expr(n.Left)
+ // TODO: nilcheck
+ p = s.curBlock.NewValue2(ssa.OpAdd, p.Type, nil, p, s.f.ConstInt(s.config.UIntPtr, n.Xoffset))
+ return s.curBlock.NewValue2(ssa.OpLoad, n.Type, nil, p, s.mem())
+
+ case OINDEX:
+ // TODO: slice vs array? Map index is already reduced to a function call
+ a := s.expr(n.Left)
+ i := s.expr(n.Right)
+ // convert index to full width
+ // TODO: if index is 64-bit and we're compiling to 32-bit, check that high
+ // 32 bits are zero (and use a low32 op instead of convnop here).
+ i = s.curBlock.NewValue1(ssa.OpConvNop, s.config.UIntPtr, nil, i)
+
+ // bounds check
+ len := s.curBlock.NewValue1(ssa.OpSliceLen, s.config.UIntPtr, nil, a)
+ cmp := s.curBlock.NewValue2(ssa.OpCheckBound, ssa.TypeBool, nil, i, len)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.Control = cmp
+ bNext := s.f.NewBlock(ssa.BlockPlain)
+ addEdge(b, bNext)
+ addEdge(b, s.exit)
+ s.startBlock(bNext)
+ // TODO: don't go directly to s.exit. Go to a stub that calls panicindex first.
+
+ return s.curBlock.NewValue3(ssa.OpSliceIndex, n.Left.Type.Type, nil, a, i, s.mem())
+
+ case OCALLFUNC:
+ // run all argument assignments
+ // TODO(khr): do we need to evaluate function first?
+ // Or is it already side-effect-free and does not require a call?
+ s.stmtList(n.List)
+
+ if n.Left.Op != ONAME {
+ // TODO(khr): closure calls?
+ log.Fatalf("can't handle CALLFUNC with non-ONAME fn %s", opnames[n.Left.Op])
+ }
+ bNext := s.f.NewBlock(ssa.BlockPlain)
+ call := s.curBlock.NewValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym.Name, s.mem())
+ b := s.endBlock()
+ b.Kind = ssa.BlockCall
+ b.Control = call
+ addEdge(b, bNext)
+ addEdge(b, s.exit)
+
+ // read result from stack at the start of the fallthrough block
+ s.startBlock(bNext)
+ var titer Iter
+ fp := Structfirst(&titer, Getoutarg(n.Left.Type))
+ a := s.f.Entry.NewValue(ssa.OpSPAddr, Ptrto(fp.Type), fp.Width)
+ return s.curBlock.NewValue2(ssa.OpLoad, fp.Type, nil, a, call)
+ default:
+ log.Fatalf("unhandled expr %s", opnames[n.Op])
+ return nil
+ }
+}
+
+// variable returns the value of a variable at the current location.
+func (s *ssaState) variable(name string, t ssa.Type) *ssa.Value {
+ if s.curBlock == nil {
+ log.Fatalf("nil curblock!")
+ }
+ v := s.vars[name]
+ if v == nil {
+ // TODO: get type? Take Sym as arg?
+ v = s.curBlock.NewValue(ssa.OpFwdRef, t, name)
+ s.vars[name] = v
+ }
+ return v
+}
+
+func (s *ssaState) mem() *ssa.Value {
+ return s.variable(".mem", ssa.TypeMem)
+}
+
+func (s *ssaState) linkForwardReferences() {
+ // Build ssa graph. Each variable on its first use in a basic block
+ // leaves a FwdRef in that block representing the incoming value
+ // of that variable. This function links that ref up with possible definitions,
+ // inserting Phi values as needed. This is essentially the algorithm
+ // described by Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau:
+ // http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
+ for _, b := range s.f.Blocks {
+ for _, v := range b.Values {
+ if v.Op != ssa.OpFwdRef {
+ continue
+ }
+ name := v.Aux.(string)
+ v.Op = ssa.OpCopy
+ v.Aux = nil
+ v.SetArgs1(s.lookupVarIncoming(b, v.Type, name))
+ }
+ }
+}
+
+// lookupVarIncoming finds the variable's value at the start of block b.
+func (s *ssaState) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Value {
+ // TODO(khr): have lookupVarIncoming overwrite the fwdRef or copy it
+ // will be used in, instead of having the result used in a copy value.
+ if b == s.f.Entry {
+ if name == ".mem" {
+ return b.NewValue(ssa.OpArg, t, name)
+ }
+ // variable is live at the entry block. Load it.
+ a := s.f.Entry.NewValue(ssa.OpFPAddr, Ptrto(t.(*Type)), s.argOffsets[name])
+ m := b.NewValue(ssa.OpArg, ssa.TypeMem, ".mem") // TODO: reuse mem starting value
+ return b.NewValue2(ssa.OpLoad, t, nil, a, m)
+ }
+ var vals []*ssa.Value
+ for _, p := range b.Preds {
+ vals = append(vals, s.lookupVarOutgoing(p, t, name))
+ }
+ v0 := vals[0]
+ for i := 1; i < len(vals); i++ {
+ if vals[i] != v0 {
+ // need a phi value
+ v := b.NewValue(ssa.OpPhi, t, nil)
+ v.AddArgs(vals...)
+ return v
+ }
+ }
+ return v0
+}
+
+// lookupVarOutgoing finds the variable's value at the end of block b.
+func (s *ssaState) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name string) *ssa.Value {
+ m := s.defvars[b.ID]
+ if v, ok := m[name]; ok {
+ return v
+ }
+ // The variable is not defined by b and we haven't
+ // looked it up yet. Generate v, a copy value which
+ // will be the outgoing value of the variable. Then
+ // look up w, the incoming value of the variable.
+ // Make v = copy(w). We need the extra copy to
+ // prevent infinite recursion when looking up the
+ // incoming value of the variable.
+ v := b.NewValue(ssa.OpCopy, t, nil)
+ m[name] = v
+ v.AddArg(s.lookupVarIncoming(b, t, name))
+ return v
+}
+
+// TODO: the above mutually recursive functions can lead to very deep stacks. Fix that.
+
+// addEdge adds an edge from b to c.
+func addEdge(b, c *ssa.Block) {
+ b.Succs = append(b.Succs, c)
+ c.Preds = append(c.Preds, b)
+}
diff --git a/src/cmd/internal/gc/type.go b/src/cmd/internal/gc/type.go
new file mode 100644
index 0000000..e88ca7c
--- /dev/null
+++ b/src/cmd/internal/gc/type.go
@@ -0,0 +1,62 @@
+// Copyright 2015 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.
+
+// This file provides methods that let us export a Type as an ../ssa:Type.
+// We don't export this package's Type directly because it would lead
+// to an import cycle with this package and ../ssa.
+// TODO: move Type to its own package, then we don't need to dance around import cycles.
+
+package gc
+
+import (
+ "cmd/internal/ssa"
+)
+
+func (t *Type) Size() int64 {
+ dowidth(t)
+ return t.Width
+}
+
+func (t *Type) IsBoolean() bool {
+ return t.Etype == TBOOL
+}
+
+func (t *Type) IsInteger() bool {
+ switch t.Etype {
+ case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR:
+ return true
+ }
+ return false
+}
+
+func (t *Type) IsSigned() bool {
+ switch t.Etype {
+ case TINT8, TINT16, TINT32, TINT64, TINT:
+ return true
+ }
+ return false
+}
+
+func (t *Type) IsFloat() bool {
+ return t.Etype == TFLOAT32 || t.Etype == TFLOAT64
+}
+
+func (t *Type) IsPtr() bool {
+ return t.Etype == TPTR32 || t.Etype == TPTR64 ||
+ t.Etype == TMAP || t.Etype == TCHAN || t.Etype == TFUNC
+}
+
+func (t *Type) Elem() ssa.Type {
+ return t.Type
+}
+func (t *Type) PtrTo() ssa.Type {
+ return Ptrto(t)
+}
+
+func (t *Type) IsMemory() bool { return false }
+func (t *Type) IsFlags() bool { return false }
+
+func (t *Type) String() string {
+ return typefmt(t, 0)
+}
diff --git a/src/cmd/internal/ssa/cgen.go b/src/cmd/internal/ssa/cgen.go
index 4b1a90b..c13e715 100644
--- a/src/cmd/internal/ssa/cgen.go
+++ b/src/cmd/internal/ssa/cgen.go
@@ -4,7 +4,11 @@
package ssa
-import "fmt"
+import (
+ "bytes"
+ "fmt"
+ "os"
+)
// cgen selects machine instructions for the function.
// This pass generates assembly output for now, but should
@@ -20,27 +24,30 @@
for idx, b := range f.Blocks {
fmt.Printf("%d:\n", b.ID)
for _, v := range b.Values {
+ var buf bytes.Buffer
asm := opcodeTable[v.Op].asm
- fmt.Print("\t")
- if asm == "" {
- fmt.Print("\t")
- }
+ buf.WriteString(" ")
for i := 0; i < len(asm); i++ {
switch asm[i] {
default:
- fmt.Printf("%c", asm[i])
+ buf.WriteByte(asm[i])
+ case '\t':
+ buf.WriteByte(' ')
+ for buf.Len()%8 != 0 {
+ buf.WriteByte(' ')
+ }
case '%':
i++
switch asm[i] {
case '%':
- fmt.Print("%")
+ buf.WriteByte('%')
case 'I':
i++
n := asm[i] - '0'
if f.RegAlloc[v.Args[n].ID] != nil {
- fmt.Print(f.RegAlloc[v.Args[n].ID].Name())
+ buf.WriteString(f.RegAlloc[v.Args[n].ID].Name())
} else {
- fmt.Printf("v%d", v.Args[n].ID)
+ fmt.Fprintf(&buf, "v%d", v.Args[n].ID)
}
case 'O':
i++
@@ -49,17 +56,22 @@
panic("can only handle 1 output for now")
}
if f.RegAlloc[v.ID] != nil {
- // TODO: output tuple
- fmt.Print(f.RegAlloc[v.ID].Name())
+ buf.WriteString(f.RegAlloc[v.ID].Name())
} else {
- fmt.Printf("v%d", v.ID)
+ fmt.Fprintf(&buf, "v%d", v.ID)
}
case 'A':
- fmt.Print(v.Aux)
+ fmt.Fprint(&buf, v.Aux)
}
}
}
- fmt.Println("\t; " + v.LongString())
+ for buf.Len() < 40 {
+ buf.WriteByte(' ')
+ }
+ buf.WriteString("; ")
+ buf.WriteString(v.LongString())
+ buf.WriteByte('\n')
+ os.Stdout.Write(buf.Bytes())
}
// find next block in layout sequence
var next *Block
@@ -106,6 +118,15 @@
fmt.Printf("\tJLT\t%d\n", b.Succs[0].ID)
fmt.Printf("\tJMP\t%d\n", b.Succs[1].ID)
}
+ case BlockULT:
+ if b.Succs[0] == next {
+ fmt.Printf("\tJAE\t%d\n", b.Succs[1].ID)
+ } else if b.Succs[1] == next {
+ fmt.Printf("\tJB\t%d\n", b.Succs[0].ID)
+ } else {
+ fmt.Printf("\tJB\t%d\n", b.Succs[0].ID)
+ fmt.Printf("\tJMP\t%d\n", b.Succs[1].ID)
+ }
default:
fmt.Printf("\t%s ->", b.Kind.String())
for _, s := range b.Succs {
diff --git a/src/cmd/internal/ssa/check.go b/src/cmd/internal/ssa/check.go
index b501cdb..453388a 100644
--- a/src/cmd/internal/ssa/check.go
+++ b/src/cmd/internal/ssa/check.go
@@ -106,7 +106,6 @@
log.Panicf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b)
}
- // TODO: check idom
// TODO: check for cycles in values
// TODO: check type
}
diff --git a/src/cmd/internal/ssa/config.go b/src/cmd/internal/ssa/config.go
new file mode 100644
index 0000000..80acda4
--- /dev/null
+++ b/src/cmd/internal/ssa/config.go
@@ -0,0 +1,48 @@
+// Copyright 2015 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
+
+import "log"
+
+type Config struct {
+ arch string // "amd64", etc.
+ ptrSize int64 // 4 or 8
+ UIntPtr Type // pointer arithmetic type
+ lower func(*Value) bool // lowering function
+
+ // TODO: more stuff. Compiler flags of interest, ...
+}
+
+// NewConfig returns a new configuration object for the given architecture.
+func NewConfig(arch string) *Config {
+ c := &Config{arch: arch}
+ switch arch {
+ case "amd64":
+ c.ptrSize = 8
+ c.lower = lowerAmd64
+ case "386":
+ c.ptrSize = 4
+ c.lower = lowerAmd64 // TODO(khr): full 32-bit support
+ default:
+ log.Fatalf("arch %s not implemented", arch)
+ }
+
+ // cache the intptr type in the config
+ c.UIntPtr = TypeUInt32
+ if c.ptrSize == 8 {
+ c.UIntPtr = TypeUInt64
+ }
+
+ return c
+}
+
+// NewFunc returns a new, empty function object
+func (c *Config) NewFunc() *Func {
+ // TODO(khr): should this function take name, type, etc. as arguments?
+ return &Func{Config: c}
+}
+
+// TODO(khr): do we really need a separate Config, or can we just
+// store all its fields inside a Func?
diff --git a/src/cmd/internal/ssa/cse.go b/src/cmd/internal/ssa/cse.go
index c44b08f..aba24ae 100644
--- a/src/cmd/internal/ssa/cse.go
+++ b/src/cmd/internal/ssa/cse.go
@@ -4,9 +4,7 @@
package ssa
-import (
- "sort"
-)
+import "sort"
// cse does common-subexpression elimination on the Function.
// Values are just relinked, nothing is deleted. A subsequent deadcode
@@ -115,7 +113,9 @@
// Replace all elements of e which v dominates
for i := 0; i < len(e); {
w := e[i]
- if w != v && dom(v.Block, w.Block, idom) {
+ if w == v {
+ e, e[i] = e[:len(e)-1], e[len(e)-1]
+ } else if dom(v.Block, w.Block, idom) {
rewrite[w.ID] = v
e, e[i] = e[:len(e)-1], e[len(e)-1]
} else {
diff --git a/src/cmd/internal/ssa/deadcode.go b/src/cmd/internal/ssa/deadcode.go
index f9e4b18..a805861 100644
--- a/src/cmd/internal/ssa/deadcode.go
+++ b/src/cmd/internal/ssa/deadcode.go
@@ -115,6 +115,7 @@
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?
}
// There was an edge b->c. It has been removed from b's successors.
diff --git a/src/cmd/internal/ssa/deadcode_test.go b/src/cmd/internal/ssa/deadcode_test.go
index 94fc359..1b7c81c 100644
--- a/src/cmd/internal/ssa/deadcode_test.go
+++ b/src/cmd/internal/ssa/deadcode_test.go
@@ -27,7 +27,7 @@
addEdge(deadblock, exit)
// dead value in dead block
- deadval := deadblock.NewValue(OpConstBool, TypeBool, true)
+ deadval := deadblock.NewValue(OpConst, TypeBool, true)
deadblock.Control = deadval
CheckFunc(f)
@@ -55,7 +55,7 @@
mem := entry.NewValue(OpArg, TypeMem, ".mem")
exit.Control = mem
- deadval := entry.NewValue(OpConstInt, TypeInt, 37)
+ deadval := entry.NewValue(OpConst, TypeInt64, int64(37))
CheckFunc(f)
Deadcode(f)
@@ -84,7 +84,7 @@
mem := entry.NewValue(OpArg, TypeMem, ".mem")
exit.Control = mem
- cond := entry.NewValue(OpConstBool, TypeBool, false)
+ cond := entry.NewValue(OpConst, TypeBool, false)
entry.Control = cond
CheckFunc(f)
diff --git a/src/cmd/internal/ssa/func.go b/src/cmd/internal/ssa/func.go
index b4677c9..bdc8815 100644
--- a/src/cmd/internal/ssa/func.go
+++ b/src/cmd/internal/ssa/func.go
@@ -7,6 +7,7 @@
// A Func represents a Go func declaration (or function literal) and
// its body. This package compiles each Func independently.
type Func struct {
+ Config *Config // architecture information
Name string // e.g. bytes·Compare
Type Type // type signature of the function.
Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
@@ -53,9 +54,53 @@
return v
}
+// NewValue1 returns a new value in the block with one argument.
+func (b *Block) NewValue1(op Op, t Type, aux interface{}, arg *Value) *Value {
+ v := &Value{
+ ID: b.Func.vid.get(),
+ Op: op,
+ Type: t,
+ Aux: aux,
+ Block: b,
+ }
+ v.Args = v.argstorage[:1]
+ v.Args[0] = arg
+ b.Values = append(b.Values, v)
+ return v
+}
+
+// NewValue2 returns a new value in the block with two arguments.
+func (b *Block) NewValue2(op Op, t Type, aux interface{}, arg0, arg1 *Value) *Value {
+ v := &Value{
+ ID: b.Func.vid.get(),
+ Op: op,
+ Type: t,
+ Aux: aux,
+ Block: b,
+ }
+ v.Args = v.argstorage[:2]
+ v.Args[0] = arg0
+ v.Args[1] = arg1
+ b.Values = append(b.Values, v)
+ return v
+}
+
+// NewValue3 returns a new value in the block with three arguments.
+func (b *Block) NewValue3(op Op, t Type, aux interface{}, arg0, arg1, arg2 *Value) *Value {
+ v := &Value{
+ ID: b.Func.vid.get(),
+ Op: op,
+ Type: t,
+ Aux: aux,
+ Block: b,
+ }
+ v.Args = []*Value{arg0, arg1, arg2}
+ b.Values = append(b.Values, v)
+ return v
+}
+
// ConstInt returns an int constant representing its argument.
-func (f *Func) ConstInt(c int64) *Value {
+func (f *Func) ConstInt(t Type, c int64) *Value {
// TODO: cache?
- // TODO: different types?
- return f.Entry.NewValue(OpConst, TypeInt64, c)
+ return f.Entry.NewValue(OpConst, t, c)
}
diff --git a/src/cmd/internal/ssa/fuse.go b/src/cmd/internal/ssa/fuse.go
index bfce9ef..af3e8a8 100644
--- a/src/cmd/internal/ssa/fuse.go
+++ b/src/cmd/internal/ssa/fuse.go
@@ -30,6 +30,9 @@
}
}
}
+ if f.Entry == b {
+ f.Entry = c
+ }
// trash b, just in case
b.Kind = BlockUnknown
diff --git a/src/cmd/internal/ssa/generic.go b/src/cmd/internal/ssa/generic.go
index 3118b3a..2a96793 100644
--- a/src/cmd/internal/ssa/generic.go
+++ b/src/cmd/internal/ssa/generic.go
@@ -11,23 +11,24 @@
{
t := v.Type
if v.Args[0].Op != OpConst {
- goto end0
+ goto endc86f5c160a87f6f5ec90b6551ec099d9
}
c := v.Args[0].Aux
if v.Args[1].Op != OpConst {
- goto end0
+ goto endc86f5c160a87f6f5ec90b6551ec099d9
}
d := v.Args[1].Aux
if !(is64BitInt(t) && isSigned(t)) {
- goto end0
+ goto endc86f5c160a87f6f5ec90b6551ec099d9
}
v.Op = OpConst
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.Aux = c.(int64) + d.(int64)
return true
}
- end0:
+ goto endc86f5c160a87f6f5ec90b6551ec099d9
+ endc86f5c160a87f6f5ec90b6551ec099d9:
;
// match: (Add <t> (Const [c]) (Const [d]))
// cond: is64BitInt(t) && !isSigned(t)
@@ -35,101 +36,130 @@
{
t := v.Type
if v.Args[0].Op != OpConst {
- goto end1
+ goto end8941c2a515c1bd38530b7fd96862bac4
}
c := v.Args[0].Aux
if v.Args[1].Op != OpConst {
- goto end1
+ goto end8941c2a515c1bd38530b7fd96862bac4
}
d := v.Args[1].Aux
if !(is64BitInt(t) && !isSigned(t)) {
- goto end1
+ goto end8941c2a515c1bd38530b7fd96862bac4
}
v.Op = OpConst
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.Aux = c.(uint64) + d.(uint64)
return true
}
- end1:
+ goto end8941c2a515c1bd38530b7fd96862bac4
+ end8941c2a515c1bd38530b7fd96862bac4:
;
- case OpLoad:
- // match: (Load (FPAddr [offset]) mem)
+ case OpSliceCap:
+ // match: (SliceCap (Load ptr mem))
// cond:
- // result: (LoadFP [offset] mem)
+ // result: (Load (Add <ptr.Type> ptr (Const <v.Block.Func.Config.UIntPtr> [int64(v.Block.Func.Config.ptrSize*2)])) mem)
{
- if v.Args[0].Op != OpFPAddr {
- goto end2
+ if v.Args[0].Op != OpLoad {
+ goto ende03f9b79848867df439b56889bb4e55d
}
- offset := v.Args[0].Aux
- mem := v.Args[1]
- v.Op = OpLoadFP
+ ptr := v.Args[0].Args[0]
+ mem := v.Args[0].Args[1]
+ v.Op = OpLoad
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
+ v.resetArgs()
+ v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil)
+ v0.Type = ptr.Type
+ v0.AddArg(ptr)
+ v1 := v.Block.NewValue(OpConst, TypeInvalid, nil)
+ v1.Type = v.Block.Func.Config.UIntPtr
+ v1.Aux = int64(v.Block.Func.Config.ptrSize * 2)
+ v0.AddArg(v1)
+ v.AddArg(v0)
v.AddArg(mem)
return true
}
- end2:
+ goto ende03f9b79848867df439b56889bb4e55d
+ ende03f9b79848867df439b56889bb4e55d:
;
- // match: (Load (SPAddr [offset]) mem)
+ case OpSliceIndex:
+ // match: (SliceIndex s i mem)
// cond:
- // result: (LoadSP [offset] mem)
+ // result: (Load (Add <s.Type.Elem().PtrTo()> (SlicePtr <s.Type.Elem().PtrTo()> s) (Mul <v.Block.Func.Config.UIntPtr> i (Const <v.Block.Func.Config.UIntPtr> [s.Type.Elem().Size()]))) mem)
{
- if v.Args[0].Op != OpSPAddr {
- goto end3
- }
- offset := v.Args[0].Aux
- mem := v.Args[1]
- v.Op = OpLoadSP
- v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
- v.AddArg(mem)
- return true
- }
- end3:
- ;
- case OpStore:
- // match: (Store (FPAddr [offset]) val mem)
- // cond:
- // result: (StoreFP [offset] val mem)
- {
- if v.Args[0].Op != OpFPAddr {
- goto end4
- }
- offset := v.Args[0].Aux
- val := v.Args[1]
+ s := v.Args[0]
+ i := v.Args[1]
mem := v.Args[2]
- v.Op = OpStoreFP
+ v.Op = OpLoad
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
- v.AddArg(val)
+ v.resetArgs()
+ v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil)
+ v0.Type = s.Type.Elem().PtrTo()
+ v1 := v.Block.NewValue(OpSlicePtr, TypeInvalid, nil)
+ v1.Type = s.Type.Elem().PtrTo()
+ v1.AddArg(s)
+ v0.AddArg(v1)
+ v2 := v.Block.NewValue(OpMul, TypeInvalid, nil)
+ v2.Type = v.Block.Func.Config.UIntPtr
+ v2.AddArg(i)
+ v3 := v.Block.NewValue(OpConst, TypeInvalid, nil)
+ v3.Type = v.Block.Func.Config.UIntPtr
+ v3.Aux = s.Type.Elem().Size()
+ v2.AddArg(v3)
+ v0.AddArg(v2)
+ v.AddArg(v0)
v.AddArg(mem)
return true
}
- end4:
+ goto end733704831a61760840348f790b3ab045
+ end733704831a61760840348f790b3ab045:
;
- // match: (Store (SPAddr [offset]) val mem)
+ case OpSliceLen:
+ // match: (SliceLen (Load ptr mem))
// cond:
- // result: (StoreSP [offset] val mem)
+ // result: (Load (Add <ptr.Type> ptr (Const <v.Block.Func.Config.UIntPtr> [int64(v.Block.Func.Config.ptrSize)])) mem)
{
- if v.Args[0].Op != OpSPAddr {
- goto end5
+ if v.Args[0].Op != OpLoad {
+ goto ende94950a57eca1871c93afdeaadb90223
}
- offset := v.Args[0].Aux
- val := v.Args[1]
- mem := v.Args[2]
- v.Op = OpStoreSP
+ ptr := v.Args[0].Args[0]
+ mem := v.Args[0].Args[1]
+ v.Op = OpLoad
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
- v.AddArg(val)
+ v.resetArgs()
+ v0 := v.Block.NewValue(OpAdd, TypeInvalid, nil)
+ v0.Type = ptr.Type
+ v0.AddArg(ptr)
+ v1 := v.Block.NewValue(OpConst, TypeInvalid, nil)
+ v1.Type = v.Block.Func.Config.UIntPtr
+ v1.Aux = int64(v.Block.Func.Config.ptrSize)
+ v0.AddArg(v1)
+ v.AddArg(v0)
v.AddArg(mem)
return true
}
- end5:
+ goto ende94950a57eca1871c93afdeaadb90223
+ ende94950a57eca1871c93afdeaadb90223:
+ ;
+ case OpSlicePtr:
+ // match: (SlicePtr (Load ptr mem))
+ // cond:
+ // result: (Load ptr mem)
+ {
+ if v.Args[0].Op != OpLoad {
+ goto end459613b83f95b65729d45c2ed663a153
+ }
+ ptr := v.Args[0].Args[0]
+ mem := v.Args[0].Args[1]
+ v.Op = OpLoad
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(ptr)
+ v.AddArg(mem)
+ return true
+ }
+ goto end459613b83f95b65729d45c2ed663a153
+ end459613b83f95b65729d45c2ed663a153:
}
return false
}
diff --git a/src/cmd/internal/ssa/id.go b/src/cmd/internal/ssa/id.go
index 43f23c8..3f53e1a 100644
--- a/src/cmd/internal/ssa/id.go
+++ b/src/cmd/internal/ssa/id.go
@@ -31,8 +31,6 @@
// put deallocates an ID.
func (a *idAlloc) put(x ID) {
a.free = append(a.free, x)
- // TODO: IR check should make sure that the IR contains
- // no IDs that are in the free list.
}
// num returns the maximum ID ever returned + 1.
diff --git a/src/cmd/internal/ssa/lower.go b/src/cmd/internal/ssa/lower.go
index 18fe986..82e5d23 100644
--- a/src/cmd/internal/ssa/lower.go
+++ b/src/cmd/internal/ssa/lower.go
@@ -4,19 +4,12 @@
package ssa
-var (
- // TODO(khr): put arch configuration constants together somewhere
- intSize = 8
- ptrSize = 8
-)
-
//go:generate go run rulegen/rulegen.go rulegen/lower_amd64.rules lowerAmd64 lowerAmd64.go
// convert to machine-dependent ops
func lower(f *Func) {
// repeat rewrites until we find no more rewrites
- // TODO: pick the target arch from config
- applyRewrite(f, lowerAmd64)
+ applyRewrite(f, f.Config.lower)
// TODO: check for unlowered opcodes, fail if we find one
@@ -29,6 +22,12 @@
case OpSETL:
b.Kind = BlockLT
b.Control = b.Control.Args[0]
+ case OpSETNE:
+ b.Kind = BlockNE
+ b.Control = b.Control.Args[0]
+ case OpSETB:
+ b.Kind = BlockULT
+ b.Control = b.Control.Args[0]
// TODO: others
}
case BlockLT:
@@ -36,6 +35,21 @@
b.Kind = BlockGE
b.Control = b.Control.Args[0]
}
+ case BlockULT:
+ if b.Control.Op == OpInvertFlags {
+ b.Kind = BlockUGE
+ b.Control = b.Control.Args[0]
+ }
+ case BlockEQ:
+ if b.Control.Op == OpInvertFlags {
+ b.Kind = BlockNE
+ b.Control = b.Control.Args[0]
+ }
+ case BlockNE:
+ if b.Control.Op == OpInvertFlags {
+ b.Kind = BlockEQ
+ b.Control = b.Control.Args[0]
+ }
// TODO: others
}
}
diff --git a/src/cmd/internal/ssa/lowerAmd64.go b/src/cmd/internal/ssa/lowerAmd64.go
index 88f0e43..6c0a42d 100644
--- a/src/cmd/internal/ssa/lowerAmd64.go
+++ b/src/cmd/internal/ssa/lowerAmd64.go
@@ -4,6 +4,65 @@
func lowerAmd64(v *Value) bool {
switch v.Op {
+ case OpADDCQ:
+ // match: (ADDCQ [c] (LEAQ8 [d] x y))
+ // cond:
+ // result: (LEAQ8 [c.(int64)+d.(int64)] x y)
+ {
+ c := v.Aux
+ if v.Args[0].Op != OpLEAQ8 {
+ goto end16348939e556e99e8447227ecb986f01
+ }
+ d := v.Args[0].Aux
+ x := v.Args[0].Args[0]
+ y := v.Args[0].Args[1]
+ v.Op = OpLEAQ8
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = c.(int64) + d.(int64)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ goto end16348939e556e99e8447227ecb986f01
+ end16348939e556e99e8447227ecb986f01:
+ ;
+ // match: (ADDCQ [off1] (FPAddr [off2]))
+ // cond:
+ // result: (FPAddr [off1.(int64)+off2.(int64)])
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpFPAddr {
+ goto end28e093ab0618066e6b2609db7aaf309b
+ }
+ off2 := v.Args[0].Aux
+ v.Op = OpFPAddr
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ return true
+ }
+ goto end28e093ab0618066e6b2609db7aaf309b
+ end28e093ab0618066e6b2609db7aaf309b:
+ ;
+ // match: (ADDCQ [off1] (SPAddr [off2]))
+ // cond:
+ // result: (SPAddr [off1.(int64)+off2.(int64)])
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpSPAddr {
+ goto endd0c27c62d150b88168075c5ba113d1fa
+ }
+ off2 := v.Args[0].Aux
+ v.Op = OpSPAddr
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ return true
+ }
+ goto endd0c27c62d150b88168075c5ba113d1fa
+ endd0c27c62d150b88168075c5ba113d1fa:
+ ;
case OpADDQ:
// match: (ADDQ x (Const [c]))
// cond:
@@ -11,55 +70,82 @@
{
x := v.Args[0]
if v.Args[1].Op != OpConst {
- goto end0
+ goto endef6908cfdf56e102cc327a3ddc14393d
}
c := v.Args[1].Aux
v.Op = OpADDCQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.Aux = c
v.AddArg(x)
return true
}
- end0:
+ goto endef6908cfdf56e102cc327a3ddc14393d
+ endef6908cfdf56e102cc327a3ddc14393d:
;
// match: (ADDQ (Const [c]) x)
// cond:
// result: (ADDCQ [c] x)
{
if v.Args[0].Op != OpConst {
- goto end1
+ goto endb54a32cf3147f424f08b46db62c69b23
}
c := v.Args[0].Aux
x := v.Args[1]
v.Op = OpADDCQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.Aux = c
v.AddArg(x)
return true
}
- end1:
+ goto endb54a32cf3147f424f08b46db62c69b23
+ endb54a32cf3147f424f08b46db62c69b23:
+ ;
+ // match: (ADDQ x (SHLCQ [shift] y))
+ // cond: shift.(int64) == 3
+ // result: (LEAQ8 [int64(0)] x y)
+ {
+ x := v.Args[0]
+ if v.Args[1].Op != OpSHLCQ {
+ goto end7fa0d837edd248748cef516853fd9475
+ }
+ shift := v.Args[1].Aux
+ y := v.Args[1].Args[0]
+ if !(shift.(int64) == 3) {
+ goto end7fa0d837edd248748cef516853fd9475
+ }
+ v.Op = OpLEAQ8
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = int64(0)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ goto end7fa0d837edd248748cef516853fd9475
+ end7fa0d837edd248748cef516853fd9475:
;
case OpAdd:
// match: (Add <t> x y)
- // cond: is64BitInt(t)
+ // cond: (is64BitInt(t) || isPtr(t))
// result: (ADDQ x y)
{
t := v.Type
x := v.Args[0]
y := v.Args[1]
- if !(is64BitInt(t)) {
- goto end2
+ if !(is64BitInt(t) || isPtr(t)) {
+ goto endf031c523d7dd08e4b8e7010a94cd94c9
}
v.Op = OpADDQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
v.AddArg(y)
return true
}
- end2:
+ goto endf031c523d7dd08e4b8e7010a94cd94c9
+ endf031c523d7dd08e4b8e7010a94cd94c9:
;
// match: (Add <t> x y)
// cond: is32BitInt(t)
@@ -69,16 +155,17 @@
x := v.Args[0]
y := v.Args[1]
if !(is32BitInt(t)) {
- goto end3
+ goto end35a02a1587264e40cf1055856ff8445a
}
v.Op = OpADDL
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
v.AddArg(y)
return true
}
- end3:
+ goto end35a02a1587264e40cf1055856ff8445a
+ end35a02a1587264e40cf1055856ff8445a:
;
case OpCMPQ:
// match: (CMPQ x (Const [c]))
@@ -87,30 +174,31 @@
{
x := v.Args[0]
if v.Args[1].Op != OpConst {
- goto end4
+ goto end1770a40e4253d9f669559a360514613e
}
c := v.Args[1].Aux
v.Op = OpCMPCQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
v.Aux = c
return true
}
- end4:
+ goto end1770a40e4253d9f669559a360514613e
+ end1770a40e4253d9f669559a360514613e:
;
// match: (CMPQ (Const [c]) x)
// cond:
// result: (InvertFlags (CMPCQ <TypeFlags> x [c]))
{
if v.Args[0].Op != OpConst {
- goto end5
+ goto enda4e64c7eaeda16c1c0db9dac409cd126
}
c := v.Args[0].Aux
x := v.Args[1]
v.Op = OpInvertFlags
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v0 := v.Block.NewValue(OpCMPCQ, TypeInvalid, nil)
v0.Type = TypeFlags
v0.AddArg(x)
@@ -118,7 +206,47 @@
v.AddArg(v0)
return true
}
- end5:
+ goto enda4e64c7eaeda16c1c0db9dac409cd126
+ enda4e64c7eaeda16c1c0db9dac409cd126:
+ ;
+ case OpCheckBound:
+ // match: (CheckBound idx len)
+ // cond:
+ // result: (SETB (CMPQ <TypeFlags> idx len))
+ {
+ idx := v.Args[0]
+ len := v.Args[1]
+ v.Op = OpSETB
+ v.Aux = nil
+ v.resetArgs()
+ v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil)
+ v0.Type = TypeFlags
+ v0.AddArg(idx)
+ v0.AddArg(len)
+ v.AddArg(v0)
+ return true
+ }
+ goto end249426f6f996d45a62f89a591311a954
+ end249426f6f996d45a62f89a591311a954:
+ ;
+ case OpCheckNil:
+ // match: (CheckNil p)
+ // cond:
+ // result: (SETNE (TESTQ <TypeFlags> p p))
+ {
+ p := v.Args[0]
+ v.Op = OpSETNE
+ v.Aux = nil
+ v.resetArgs()
+ v0 := v.Block.NewValue(OpTESTQ, TypeInvalid, nil)
+ v0.Type = TypeFlags
+ v0.AddArg(p)
+ v0.AddArg(p)
+ v.AddArg(v0)
+ return true
+ }
+ goto end90d3057824f74ef953074e473aa0b282
+ end90d3057824f74ef953074e473aa0b282:
;
case OpLess:
// match: (Less x y)
@@ -128,11 +256,11 @@
x := v.Args[0]
y := v.Args[1]
if !(is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)) {
- goto end6
+ goto endcecf13a952d4c6c2383561c7d68a3cf9
}
v.Op = OpSETL
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil)
v0.Type = TypeFlags
v0.AddArg(x)
@@ -140,49 +268,292 @@
v.AddArg(v0)
return true
}
- end6:
+ goto endcecf13a952d4c6c2383561c7d68a3cf9
+ endcecf13a952d4c6c2383561c7d68a3cf9:
;
- case OpLoadFP:
- // match: (LoadFP <t> [offset] mem)
- // cond: typeSize(t) == 8
- // result: (LoadFP8 <t> [offset] mem)
+ case OpLoad:
+ // match: (Load <t> ptr mem)
+ // cond: (is64BitInt(t) || isPtr(t))
+ // result: (MOVQload [int64(0)] ptr mem)
{
t := v.Type
- offset := v.Aux
- mem := v.Args[0]
- if !(typeSize(t) == 8) {
- goto end7
+ ptr := v.Args[0]
+ mem := v.Args[1]
+ if !(is64BitInt(t) || isPtr(t)) {
+ goto end581ce5a20901df1b8143448ba031685b
}
- v.Op = OpLoadFP8
+ v.Op = OpMOVQload
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Type = t
- v.Aux = offset
+ v.resetArgs()
+ v.Aux = int64(0)
+ v.AddArg(ptr)
v.AddArg(mem)
return true
}
- end7:
+ goto end581ce5a20901df1b8143448ba031685b
+ end581ce5a20901df1b8143448ba031685b:
;
- case OpLoadSP:
- // match: (LoadSP <t> [offset] mem)
- // cond: typeSize(t) == 8
- // result: (LoadSP8 <t> [offset] mem)
+ case OpMOVQload:
+ // match: (MOVQload [off1] (FPAddr [off2]) mem)
+ // cond:
+ // result: (MOVQloadFP [off1.(int64)+off2.(int64)] mem)
{
- t := v.Type
- offset := v.Aux
- mem := v.Args[0]
- if !(typeSize(t) == 8) {
- goto end8
+ off1 := v.Aux
+ if v.Args[0].Op != OpFPAddr {
+ goto endce972b1aa84b56447978c43def87fa57
}
- v.Op = OpLoadSP8
+ off2 := v.Args[0].Aux
+ mem := v.Args[1]
+ v.Op = OpMOVQloadFP
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Type = t
- v.Aux = offset
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
v.AddArg(mem)
return true
}
- end8:
+ goto endce972b1aa84b56447978c43def87fa57
+ endce972b1aa84b56447978c43def87fa57:
+ ;
+ // match: (MOVQload [off1] (SPAddr [off2]) mem)
+ // cond:
+ // result: (MOVQloadSP [off1.(int64)+off2.(int64)] mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpSPAddr {
+ goto end3d8628a6536350a123be81240b8a1376
+ }
+ off2 := v.Args[0].Aux
+ mem := v.Args[1]
+ v.Op = OpMOVQloadSP
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(mem)
+ return true
+ }
+ goto end3d8628a6536350a123be81240b8a1376
+ end3d8628a6536350a123be81240b8a1376:
+ ;
+ // match: (MOVQload [off1] (ADDCQ [off2] ptr) mem)
+ // cond:
+ // result: (MOVQload [off1.(int64)+off2.(int64)] ptr mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpADDCQ {
+ goto enda68a39292ba2a05b3436191cb0bb0516
+ }
+ off2 := v.Args[0].Aux
+ ptr := v.Args[0].Args[0]
+ mem := v.Args[1]
+ v.Op = OpMOVQload
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(ptr)
+ v.AddArg(mem)
+ return true
+ }
+ goto enda68a39292ba2a05b3436191cb0bb0516
+ enda68a39292ba2a05b3436191cb0bb0516:
+ ;
+ // match: (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem)
+ // cond:
+ // result: (MOVQload8 [off1.(int64)+off2.(int64)] ptr idx mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpLEAQ8 {
+ goto end35060118a284c93323ab3fb827156638
+ }
+ off2 := v.Args[0].Aux
+ ptr := v.Args[0].Args[0]
+ idx := v.Args[0].Args[1]
+ mem := v.Args[1]
+ v.Op = OpMOVQload8
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(ptr)
+ v.AddArg(idx)
+ v.AddArg(mem)
+ return true
+ }
+ goto end35060118a284c93323ab3fb827156638
+ end35060118a284c93323ab3fb827156638:
+ ;
+ case OpMOVQstore:
+ // match: (MOVQstore [off1] (FPAddr [off2]) val mem)
+ // cond:
+ // result: (MOVQstoreFP [off1.(int64)+off2.(int64)] val mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpFPAddr {
+ goto end0a2a81a20558dfc93790aecb1e9cc81a
+ }
+ off2 := v.Args[0].Aux
+ val := v.Args[1]
+ mem := v.Args[2]
+ v.Op = OpMOVQstoreFP
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(val)
+ v.AddArg(mem)
+ return true
+ }
+ goto end0a2a81a20558dfc93790aecb1e9cc81a
+ end0a2a81a20558dfc93790aecb1e9cc81a:
+ ;
+ // match: (MOVQstore [off1] (SPAddr [off2]) val mem)
+ // cond:
+ // result: (MOVQstoreSP [off1.(int64)+off2.(int64)] val mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpSPAddr {
+ goto end1cb5b7e766f018270fa434c6f46f607f
+ }
+ off2 := v.Args[0].Aux
+ val := v.Args[1]
+ mem := v.Args[2]
+ v.Op = OpMOVQstoreSP
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(val)
+ v.AddArg(mem)
+ return true
+ }
+ goto end1cb5b7e766f018270fa434c6f46f607f
+ end1cb5b7e766f018270fa434c6f46f607f:
+ ;
+ // match: (MOVQstore [off1] (ADDCQ [off2] ptr) val mem)
+ // cond:
+ // result: (MOVQstore [off1.(int64)+off2.(int64)] ptr val mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpADDCQ {
+ goto end271e3052de832e22b1f07576af2854de
+ }
+ off2 := v.Args[0].Aux
+ ptr := v.Args[0].Args[0]
+ val := v.Args[1]
+ mem := v.Args[2]
+ v.Op = OpMOVQstore
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(ptr)
+ v.AddArg(val)
+ v.AddArg(mem)
+ return true
+ }
+ goto end271e3052de832e22b1f07576af2854de
+ end271e3052de832e22b1f07576af2854de:
+ ;
+ // match: (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem)
+ // cond:
+ // result: (MOVQstore8 [off1.(int64)+off2.(int64)] ptr idx val mem)
+ {
+ off1 := v.Aux
+ if v.Args[0].Op != OpLEAQ8 {
+ goto endb5cba0ee3ba21d2bd8e5aa163d2b984e
+ }
+ off2 := v.Args[0].Aux
+ ptr := v.Args[0].Args[0]
+ idx := v.Args[0].Args[1]
+ val := v.Args[1]
+ mem := v.Args[2]
+ v.Op = OpMOVQstore8
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = off1.(int64) + off2.(int64)
+ v.AddArg(ptr)
+ v.AddArg(idx)
+ v.AddArg(val)
+ v.AddArg(mem)
+ return true
+ }
+ goto endb5cba0ee3ba21d2bd8e5aa163d2b984e
+ endb5cba0ee3ba21d2bd8e5aa163d2b984e:
+ ;
+ case OpMULCQ:
+ // match: (MULCQ [c] x)
+ // cond: c.(int64) == 8
+ // result: (SHLCQ [int64(3)] x)
+ {
+ c := v.Aux
+ x := v.Args[0]
+ if !(c.(int64) == 8) {
+ goto end90a1c055d9658aecacce5e101c1848b4
+ }
+ v.Op = OpSHLCQ
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = int64(3)
+ v.AddArg(x)
+ return true
+ }
+ goto end90a1c055d9658aecacce5e101c1848b4
+ end90a1c055d9658aecacce5e101c1848b4:
+ ;
+ case OpMULQ:
+ // match: (MULQ x (Const [c]))
+ // cond:
+ // result: (MULCQ [c] x)
+ {
+ x := v.Args[0]
+ if v.Args[1].Op != OpConst {
+ goto endc427f4838d2e83c00cc097b20bd20a37
+ }
+ c := v.Args[1].Aux
+ v.Op = OpMULCQ
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = c
+ v.AddArg(x)
+ return true
+ }
+ goto endc427f4838d2e83c00cc097b20bd20a37
+ endc427f4838d2e83c00cc097b20bd20a37:
+ ;
+ // match: (MULQ (Const [c]) x)
+ // cond:
+ // result: (MULCQ [c] x)
+ {
+ if v.Args[0].Op != OpConst {
+ goto endd70de938e71150d1c9e8173c2a5b2d95
+ }
+ c := v.Args[0].Aux
+ x := v.Args[1]
+ v.Op = OpMULCQ
+ v.Aux = nil
+ v.resetArgs()
+ v.Aux = c
+ v.AddArg(x)
+ return true
+ }
+ goto endd70de938e71150d1c9e8173c2a5b2d95
+ endd70de938e71150d1c9e8173c2a5b2d95:
+ ;
+ case OpMul:
+ // match: (Mul <t> x y)
+ // cond: is64BitInt(t)
+ // result: (MULQ x y)
+ {
+ t := v.Type
+ x := v.Args[0]
+ y := v.Args[1]
+ if !(is64BitInt(t)) {
+ goto endfab0d598f376ecba45a22587d50f7aff
+ }
+ v.Op = OpMULQ
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+ goto endfab0d598f376ecba45a22587d50f7aff
+ endfab0d598f376ecba45a22587d50f7aff:
;
case OpSETL:
// match: (SETL (InvertFlags x))
@@ -190,16 +561,17 @@
// result: (SETGE x)
{
if v.Args[0].Op != OpInvertFlags {
- goto end9
+ goto end456c7681d48305698c1ef462d244bdc6
}
x := v.Args[0].Args[0]
v.Op = OpSETGE
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
return true
}
- end9:
+ goto end456c7681d48305698c1ef462d244bdc6
+ end456c7681d48305698c1ef462d244bdc6:
;
case OpSUBQ:
// match: (SUBQ x (Const [c]))
@@ -208,17 +580,18 @@
{
x := v.Args[0]
if v.Args[1].Op != OpConst {
- goto end10
+ goto endb31e242f283867de4722665a5796008c
}
c := v.Args[1].Aux
v.Op = OpSUBCQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
v.Aux = c
return true
}
- end10:
+ goto endb31e242f283867de4722665a5796008c
+ endb31e242f283867de4722665a5796008c:
;
// match: (SUBQ <t> (Const [c]) x)
// cond:
@@ -226,13 +599,13 @@
{
t := v.Type
if v.Args[0].Op != OpConst {
- goto end11
+ goto end569cc755877d1f89a701378bec05c08d
}
c := v.Args[0].Aux
x := v.Args[1]
v.Op = OpNEGQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v0 := v.Block.NewValue(OpSUBCQ, TypeInvalid, nil)
v0.Type = t
v0.AddArg(x)
@@ -240,49 +613,31 @@
v.AddArg(v0)
return true
}
- end11:
+ goto end569cc755877d1f89a701378bec05c08d
+ end569cc755877d1f89a701378bec05c08d:
;
- case OpStoreFP:
- // match: (StoreFP [offset] val mem)
- // cond: typeSize(val.Type) == 8
- // result: (StoreFP8 [offset] val mem)
+ case OpStore:
+ // match: (Store ptr val mem)
+ // cond: (is64BitInt(val.Type) || isPtr(val.Type))
+ // result: (MOVQstore [int64(0)] ptr val mem)
{
- offset := v.Aux
- val := v.Args[0]
- mem := v.Args[1]
- if !(typeSize(val.Type) == 8) {
- goto end12
+ ptr := v.Args[0]
+ val := v.Args[1]
+ mem := v.Args[2]
+ if !(is64BitInt(val.Type) || isPtr(val.Type)) {
+ goto end9680b43f504bc06f9fab000823ce471a
}
- v.Op = OpStoreFP8
+ v.Op = OpMOVQstore
v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
+ v.resetArgs()
+ v.Aux = int64(0)
+ v.AddArg(ptr)
v.AddArg(val)
v.AddArg(mem)
return true
}
- end12:
- ;
- case OpStoreSP:
- // match: (StoreSP [offset] val mem)
- // cond: typeSize(val.Type) == 8
- // result: (StoreSP8 [offset] val mem)
- {
- offset := v.Aux
- val := v.Args[0]
- mem := v.Args[1]
- if !(typeSize(val.Type) == 8) {
- goto end13
- }
- v.Op = OpStoreSP8
- v.Aux = nil
- v.Args = v.argstorage[:0]
- v.Aux = offset
- v.AddArg(val)
- v.AddArg(mem)
- return true
- }
- end13:
+ goto end9680b43f504bc06f9fab000823ce471a
+ end9680b43f504bc06f9fab000823ce471a:
;
case OpSub:
// match: (Sub <t> x y)
@@ -293,16 +648,17 @@
x := v.Args[0]
y := v.Args[1]
if !(is64BitInt(t)) {
- goto end14
+ goto ende6ef29f885a8ecf3058212bb95917323
}
v.Op = OpSUBQ
v.Aux = nil
- v.Args = v.argstorage[:0]
+ v.resetArgs()
v.AddArg(x)
v.AddArg(y)
return true
}
- end14:
+ goto ende6ef29f885a8ecf3058212bb95917323
+ ende6ef29f885a8ecf3058212bb95917323:
}
return false
}
diff --git a/src/cmd/internal/ssa/op.go b/src/cmd/internal/ssa/op.go
index 19d9739..600dc9f 100644
--- a/src/cmd/internal/ssa/op.go
+++ b/src/cmd/internal/ssa/op.go
@@ -17,8 +17,8 @@
// machine-independent opcodes
- OpNop // should never be used, appears only briefly during construction, Has type Void.
- OpThunk // used during ssa construction. Like OpCopy, but the arg has not been specified yet.
+ OpNop // should never be used, appears only briefly during construction, Has type Void.
+ OpFwdRef // used during ssa construction. Like OpCopy, but the arg has not been specified yet.
// 2-input arithmetic
OpAdd
@@ -28,7 +28,12 @@
// 2-input comparisons
OpLess
- // constants
+ // constants. Constant values are stored in the aux field.
+ // booleans have a bool aux field, strings have a string aux
+ // field, and so on. All integer types store their value
+ // in the aux field as an int64 (including int, uint64, etc.).
+ // We could store int8 as an int8, but that won't work for int,
+ // as it may be different widths on the host and target.
OpConst
OpArg // address of a function parameter/result. Memory input is an arg called ".mem".
@@ -46,12 +51,11 @@
OpStringPtr
OpStringLen
- OpSlice
- OpIndex
- OpIndexAddr
+ OpSliceIndex
+ OpSliceIndexAddr
- OpLoad // args are ptr, memory
- OpStore // args are ptr, value, memory, returns memory
+ OpLoad // args are ptr, memory. Loads from ptr+aux.(int64)
+ OpStore // args are ptr, value, memory, returns memory. Stores to ptr+aux.(int64)
OpCheckNil // arg[0] != nil
OpCheckBound // 0 <= arg[0] < arg[1]
@@ -71,14 +75,6 @@
OpFPAddr // offset from FP (+ == args from caller, - == locals)
OpSPAddr // offset from SP
- // load/store from constant offsets from SP/FP
- // The distinction between FP/SP needs to be maintained until after
- // register allocation because we don't know the size of the frame yet.
- OpLoadFP
- OpLoadSP
- OpStoreFP
- OpStoreSP
-
// spill&restore ops for the register allocator. These are
// semantically identical to OpCopy; they do not take/return
// stores like regular memory ops do. We can get away without memory
@@ -93,12 +89,22 @@
OpSUBQ
OpADDCQ // 1 input arg. output = input + aux.(int64)
OpSUBCQ // 1 input arg. output = input - aux.(int64)
+ OpMULQ
+ OpMULCQ // output = input * aux.(int64)
+ OpSHLQ // output = input0 << input1
+ OpSHLCQ // output = input << aux.(int64)
OpNEGQ
OpCMPQ
OpCMPCQ // 1 input arg. Compares input with aux.(int64)
OpADDL
- OpSETL // generate bool = "flags encode less than"
- OpSETGE
+ OpTESTQ // compute flags of arg[0] & arg[1]
+ OpSETEQ
+ OpSETNE
+
+ // generate boolean based on the flags setting
+ OpSETL // less than
+ OpSETGE // >=
+ OpSETB // "below" = unsigned less than
// InvertFlags reverses direction of flags register interpretation:
// (InvertFlags (OpCMPQ a b)) == (OpCMPQ b a)
@@ -110,11 +116,16 @@
OpLEAQ4 // x+4*y
OpLEAQ8 // x+8*y
+ OpMOVQload // (ptr, mem): loads from ptr+aux.(int64)
+ OpMOVQstore // (ptr, val, mem): stores val to ptr+aux.(int64), returns mem
+ OpMOVQload8 // (ptr,idx,mem): loads from ptr+idx*8+aux.(int64)
+ OpMOVQstore8 // (ptr,idx,val,mem): stores to ptr+idx*8+aux.(int64), returns mem
+
// load/store 8-byte integer register from stack slot.
- OpLoadFP8
- OpLoadSP8
- OpStoreFP8
- OpStoreSP8
+ OpMOVQloadFP
+ OpMOVQloadSP
+ OpMOVQstoreFP
+ OpMOVQstoreSP
OpMax // sentinel
)
@@ -184,7 +195,9 @@
var gp2_flags = [2][]regMask{{gp, gp}, {flags}}
var gp1_flags = [2][]regMask{{gp}, {flags}}
var gpload = [2][]regMask{{gp, 0}, {gp}}
+var gploadX = [2][]regMask{{gp, gp, 0}, {gp}} // indexed loads
var gpstore = [2][]regMask{{gp, gp, 0}, {0}}
+var gpstoreX = [2][]regMask{{gp, gp, gp, 0}, {0}} // indexed stores
// Opcodes that represent the input Go program
var genericTable = [...]OpInfo{
@@ -197,7 +210,7 @@
OpLess: {},
OpConst: {}, // aux matches the type (e.g. bool, int64 float64)
- OpArg: {}, // aux is the name of the input variable TODO:?
+ OpArg: {}, // aux is the name of the input variable. Currently only ".mem" is used
OpGlobal: {}, // address of a global variable
OpFunc: {},
OpCopy: {},
@@ -251,17 +264,25 @@
OpADDCQ: {asm: "ADDQ\t$%A,%I0,%O0", reg: gp11_overwrite}, // aux = int64 constant to add
OpSUBQ: {asm: "SUBQ\t%I0,%I1,%O0", reg: gp21},
OpSUBCQ: {asm: "SUBQ\t$%A,%I0,%O0", reg: gp11_overwrite},
+ OpMULQ: {asm: "MULQ\t%I0,%I1,%O0", reg: gp21},
+ OpMULCQ: {asm: "MULQ\t$%A,%I0,%O0", reg: gp11_overwrite},
+ OpSHLQ: {asm: "SHLQ\t%I0,%I1,%O0", reg: gp21},
+ OpSHLCQ: {asm: "SHLQ\t$%A,%I0,%O0", reg: gp11_overwrite},
OpCMPQ: {asm: "CMPQ\t%I0,%I1", reg: gp2_flags}, // compute arg[0]-arg[1] and produce flags
OpCMPCQ: {asm: "CMPQ\t$%A,%I0", reg: gp1_flags},
+ OpTESTQ: {asm: "TESTQ\t%I0,%I1", reg: gp2_flags},
OpLEAQ: {flags: OpFlagCommutative, asm: "LEAQ\t%A(%I0)(%I1*1),%O0", reg: gp21}, // aux = int64 constant to add
OpLEAQ2: {asm: "LEAQ\t%A(%I0)(%I1*2),%O0"},
OpLEAQ4: {asm: "LEAQ\t%A(%I0)(%I1*4),%O0"},
OpLEAQ8: {asm: "LEAQ\t%A(%I0)(%I1*8),%O0"},
- //OpLoad8: {asm: "MOVQ\t%A(%I0),%O0", reg: gpload},
- //OpStore8: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore},
+ // loads and stores
+ OpMOVQload: {asm: "MOVQ\t%A(%I0),%O0", reg: gpload},
+ OpMOVQstore: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore},
+ OpMOVQload8: {asm: "MOVQ\t%A(%I0)(%I1*8),%O0", reg: gploadX},
+ OpMOVQstore8: {asm: "MOVQ\t%I2,%A(%I0)(%I1*8)", reg: gpstoreX},
OpStaticCall: {asm: "CALL\t%A(SB)"},
@@ -271,10 +292,10 @@
OpSETL: {},
// ops for load/store to stack
- OpLoadFP8: {asm: "MOVQ\t%A(FP),%O0"},
- OpLoadSP8: {asm: "MOVQ\t%A(SP),%O0"},
- OpStoreFP8: {asm: "MOVQ\t%I0,%A(FP)"},
- OpStoreSP8: {asm: "MOVQ\t%I0,%A(SP)"},
+ OpMOVQloadFP: {asm: "MOVQ\t%A(FP),%O0"},
+ OpMOVQloadSP: {asm: "MOVQ\t%A(SP),%O0"},
+ OpMOVQstoreFP: {asm: "MOVQ\t%I0,%A(FP)"},
+ OpMOVQstoreSP: {asm: "MOVQ\t%I0,%A(SP)"},
// ops for spilling of registers
// unlike regular loads & stores, these take no memory argument.
diff --git a/src/cmd/internal/ssa/op_string.go b/src/cmd/internal/ssa/op_string.go
index dba1725..5c42d22 100644
--- a/src/cmd/internal/ssa/op_string.go
+++ b/src/cmd/internal/ssa/op_string.go
@@ -4,9 +4,9 @@
import "fmt"
-const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpSETLOpSETGEOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
+const _Op_name = "OpUnknownOpNopOpFwdRefOpAddOpSubOpMulOpLessOpConstOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceIndexOpSliceIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpMULQOpMULCQOpSHLQOpSHLCQOpNEGQOpCMPQOpCMPCQOpADDLOpTESTQOpSETEQOpSETNEOpSETLOpSETGEOpSETBOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpMOVQloadOpMOVQstoreOpMOVQload8OpMOVQstore8OpMOVQloadFPOpMOVQloadSPOpMOVQstoreFPOpMOVQstoreSPOpMax"
-var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 49, 54, 62, 68, 74, 79, 90, 100, 110, 120, 132, 143, 154, 161, 168, 179, 185, 192, 202, 214, 220, 232, 241, 250, 258, 266, 274, 282, 291, 300, 311, 321, 327, 333, 340, 347, 353, 359, 366, 372, 378, 385, 398, 404, 411, 418, 425, 434, 443, 453, 463, 468}
+var _Op_index = [...]uint16{0, 9, 14, 22, 27, 32, 37, 43, 50, 55, 63, 69, 75, 80, 91, 101, 111, 121, 133, 144, 155, 167, 183, 189, 196, 206, 218, 224, 236, 245, 254, 262, 270, 281, 291, 297, 303, 310, 317, 323, 330, 336, 343, 349, 355, 362, 368, 375, 382, 389, 395, 402, 408, 421, 427, 434, 441, 448, 458, 469, 480, 492, 504, 516, 529, 542, 547}
func (i Op) String() string {
if i < 0 || i+1 >= Op(len(_Op_index)) {
diff --git a/src/cmd/internal/ssa/rewrite.go b/src/cmd/internal/ssa/rewrite.go
index d22926e..855719a 100644
--- a/src/cmd/internal/ssa/rewrite.go
+++ b/src/cmd/internal/ssa/rewrite.go
@@ -4,16 +4,22 @@
package ssa
-import (
- "cmd/internal/ssa/types" // TODO: use golang.org/x/tools/go/types instead
-)
+import "fmt"
func applyRewrite(f *Func, r func(*Value) bool) {
// repeat rewrites until we find no more rewrites
+ var curv *Value
+ defer func() {
+ if curv != nil {
+ fmt.Printf("panic during rewrite of %s\n", curv.LongString())
+ // TODO(khr): print source location also
+ }
+ }()
for {
change := false
for _, b := range f.Blocks {
for _, v := range b.Values {
+ curv = v
if r(v) {
change = true
}
@@ -28,36 +34,21 @@
// Common functions called from rewriting rules
func is64BitInt(t Type) bool {
- if b, ok := t.Underlying().(*types.Basic); ok {
- switch b.Kind() {
- case types.Int64, types.Uint64:
- return true
- }
- }
- return false
+ return t.Size() == 8 && t.IsInteger()
}
func is32BitInt(t Type) bool {
- if b, ok := t.Underlying().(*types.Basic); ok {
- switch b.Kind() {
- case types.Int32, types.Uint32:
- return true
- }
- }
- return false
+ return t.Size() == 4 && t.IsInteger()
+}
+
+func isPtr(t Type) bool {
+ return t.IsPtr()
}
func isSigned(t Type) bool {
- if b, ok := t.Underlying().(*types.Basic); ok {
- switch b.Kind() {
- case types.Int8, types.Int16, types.Int32, types.Int64:
- return true
- }
- }
- return false
+ return t.IsSigned()
}
-var sizer types.Sizes = &types.StdSizes{int64(ptrSize), int64(ptrSize)} // TODO(khr): from config
func typeSize(t Type) int64 {
- return sizer.Sizeof(t)
+ return t.Size()
}
diff --git a/src/cmd/internal/ssa/rulegen/generic.rules b/src/cmd/internal/ssa/rulegen/generic.rules
index 1fc1620c..d174499 100644
--- a/src/cmd/internal/ssa/rulegen/generic.rules
+++ b/src/cmd/internal/ssa/rulegen/generic.rules
@@ -6,12 +6,14 @@
(Add <t> (Const [c]) (Const [d])) && is64BitInt(t) && isSigned(t) -> (Const [{c.(int64)+d.(int64)}])
(Add <t> (Const [c]) (Const [d])) && is64BitInt(t) && !isSigned(t) -> (Const [{c.(uint64)+d.(uint64)}])
-// load/store to stack
-(Load (FPAddr [offset]) mem) -> (LoadFP [offset] mem)
-(Store (FPAddr [offset]) val mem) -> (StoreFP [offset] val mem)
-
-(Load (SPAddr [offset]) mem) -> (LoadSP [offset] mem)
-(Store (SPAddr [offset]) val mem) -> (StoreSP [offset] val mem)
+// tear apart slices
+// TODO: anything that generates a slice needs to go in here.
+(SlicePtr (Load ptr mem)) -> (Load ptr mem)
+(SliceLen (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <v.Block.Func.Config.UIntPtr> [int64(v.Block.Func.Config.ptrSize)])) mem)
+(SliceCap (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <v.Block.Func.Config.UIntPtr> [int64(v.Block.Func.Config.ptrSize*2)])) mem)
// expand array indexing
// others? Depends on what is already done by frontend
+
+// Note: bounds check has already been done
+(SliceIndex s i mem) -> (Load (Add <s.Type.Elem().PtrTo()> (SlicePtr <s.Type.Elem().PtrTo()> s) (Mul <v.Block.Func.Config.UIntPtr> i (Const <v.Block.Func.Config.UIntPtr> [s.Type.Elem().Size()]))) mem)
diff --git a/src/cmd/internal/ssa/rulegen/lower_amd64.rules b/src/cmd/internal/ssa/rulegen/lower_amd64.rules
index f60ac36..10c8dcc 100644
--- a/src/cmd/internal/ssa/rulegen/lower_amd64.rules
+++ b/src/cmd/internal/ssa/rulegen/lower_amd64.rules
@@ -13,35 +13,72 @@
// - aux will be nil if not specified.
// x86 register conventions:
-// - Integer types live in the low portion of registers. Upper portions are junk.
+// - Integer types live in the low portion of registers.
+// Upper portions are correctly extended.
// - Boolean types use the low-order byte of a register. Upper bytes are junk.
// - We do not use AH,BH,CH,DH registers.
// - Floating-point types will live in the low natural slot of an sse2 register.
// Unused portions are junk.
// These are the lowerings themselves
-(Add <t> x y) && is64BitInt(t) -> (ADDQ x y)
+(Add <t> x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y)
(Add <t> x y) && is32BitInt(t) -> (ADDL x y)
(Sub <t> x y) && is64BitInt(t) -> (SUBQ x y)
+(Mul <t> x y) && is64BitInt(t) -> (MULQ x y)
+
(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ <TypeFlags> x y))
-// stack loads/stores
-(LoadFP <t> [offset] mem) && typeSize(t) == 8 -> (LoadFP8 <t> [offset] mem)
-(StoreFP [offset] val mem) && typeSize(val.Type) == 8 -> (StoreFP8 [offset] val mem)
-(LoadSP <t> [offset] mem) && typeSize(t) == 8 -> (LoadSP8 <t> [offset] mem)
-(StoreSP [offset] val mem) && typeSize(val.Type) == 8 -> (StoreSP8 [offset] val mem)
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload [int64(0)] ptr mem)
+(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem)
+
+// checks
+(CheckNil p) -> (SETNE (TESTQ <TypeFlags> p p))
+(CheckBound idx len) -> (SETB (CMPQ <TypeFlags> idx len))
// Rules below here apply some simple optimizations after lowering.
// TODO: Should this be a separate pass?
+// stack loads/stores
+(MOVQload [off1] (FPAddr [off2]) mem) -> (MOVQloadFP [off1.(int64)+off2.(int64)] mem)
+(MOVQload [off1] (SPAddr [off2]) mem) -> (MOVQloadSP [off1.(int64)+off2.(int64)] mem)
+(MOVQstore [off1] (FPAddr [off2]) val mem) -> (MOVQstoreFP [off1.(int64)+off2.(int64)] val mem)
+(MOVQstore [off1] (SPAddr [off2]) val mem) -> (MOVQstoreSP [off1.(int64)+off2.(int64)] val mem)
+
+// fold constants into instructions
(ADDQ x (Const [c])) -> (ADDCQ [c] x) // TODO: restrict c to int32 range?
(ADDQ (Const [c]) x) -> (ADDCQ [c] x)
(SUBQ x (Const [c])) -> (SUBCQ x [c])
(SUBQ <t> (Const [c]) x) -> (NEGQ (SUBCQ <t> x [c]))
+(MULQ x (Const [c])) -> (MULCQ [c] x)
+(MULQ (Const [c]) x) -> (MULCQ [c] x)
(CMPQ x (Const [c])) -> (CMPCQ x [c])
(CMPQ (Const [c]) x) -> (InvertFlags (CMPCQ <TypeFlags> x [c]))
+// strength reduction
+// TODO: do this a lot more generically
+(MULCQ [c] x) && c.(int64) == 8 -> (SHLCQ [int64(3)] x)
+
+// fold add/shift into leaq
+(ADDQ x (SHLCQ [shift] y)) && shift.(int64) == 3 -> (LEAQ8 [int64(0)] x y)
+(ADDCQ [c] (LEAQ8 [d] x y)) -> (LEAQ8 [c.(int64)+d.(int64)] x y)
+
// reverse ordering of compare instruction
(SETL (InvertFlags x)) -> (SETGE x)
+
+// fold constants into memory operations
+// Note that this is not always a good idea because if not all the uses of
+// the ADDCQ get eliminated, we still have to compute the ADDCQ and we now
+// have potentially two live values (ptr and (ADDCQ [off] ptr)) instead of one.
+// Nevertheless, let's do it!
+(MOVQload [off1] (ADDCQ [off2] ptr) mem) -> (MOVQload [off1.(int64)+off2.(int64)] ptr mem)
+(MOVQstore [off1] (ADDCQ [off2] ptr) val mem) -> (MOVQstore [off1.(int64)+off2.(int64)] ptr val mem)
+
+// indexed loads and stores
+(MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQload8 [off1.(int64)+off2.(int64)] ptr idx mem)
+(MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstore8 [off1.(int64)+off2.(int64)] ptr idx val mem)
+
+// Combine the offset of a stack object with the offset within a stack object
+(ADDCQ [off1] (FPAddr [off2])) -> (FPAddr [off1.(int64)+off2.(int64)])
+(ADDCQ [off1] (SPAddr [off2])) -> (SPAddr [off1.(int64)+off2.(int64)])
diff --git a/src/cmd/internal/ssa/rulegen/rulegen.go b/src/cmd/internal/ssa/rulegen/rulegen.go
index 4038662..31f46f7 100644
--- a/src/cmd/internal/ssa/rulegen/rulegen.go
+++ b/src/cmd/internal/ssa/rulegen/rulegen.go
@@ -14,6 +14,7 @@
import (
"bufio"
"bytes"
+ "crypto/md5"
"fmt"
"go/format"
"io"
@@ -96,10 +97,15 @@
ops = append(ops, op)
}
sort.Strings(ops)
- rulenum := 0
for _, op := range ops {
fmt.Fprintf(w, "case Op%s:\n", op)
for _, rule := range oprules[op] {
+ // Note: we use a hash to identify the rule so that its
+ // identity is invariant to adding/removing rules elsewhere
+ // in the rules file. This is useful to squash spurious
+ // diffs that would occur if we used rule index.
+ rulehash := fmt.Sprintf("%02x", md5.Sum([]byte(rule)))
+
// split at ->
s := strings.Split(rule, "->")
if len(s) != 2 {
@@ -120,7 +126,7 @@
fmt.Fprintf(w, "// cond: %s\n", cond)
fmt.Fprintf(w, "// result: %s\n", result)
- fail := fmt.Sprintf("{\ngoto end%d\n}\n", rulenum)
+ fail := fmt.Sprintf("{\ngoto end%s\n}\n", rulehash)
fmt.Fprintf(w, "{\n")
genMatch(w, match, fail)
@@ -133,8 +139,8 @@
fmt.Fprintf(w, "return true\n")
fmt.Fprintf(w, "}\n")
- fmt.Fprintf(w, "end%d:;\n", rulenum)
- rulenum++
+ fmt.Fprintf(w, "goto end%s\n", rulehash) // use label
+ fmt.Fprintf(w, "end%s:;\n", rulehash)
}
}
fmt.Fprintf(w, "}\n")
@@ -249,7 +255,7 @@
v = "v"
fmt.Fprintf(w, "v.Op = Op%s\n", s[0])
fmt.Fprintf(w, "v.Aux = nil\n")
- fmt.Fprintf(w, "v.Args = v.argstorage[:0]\n")
+ fmt.Fprintf(w, "v.resetArgs()\n")
hasType = true
} else {
v = fmt.Sprintf("v%d", *alloc)
diff --git a/src/cmd/internal/ssa/ssac/main.go b/src/cmd/internal/ssa/ssac/main.go
index 361bc87..2afa7c6 100644
--- a/src/cmd/internal/ssa/ssac/main.go
+++ b/src/cmd/internal/ssa/ssac/main.go
@@ -16,8 +16,6 @@
"strconv"
"strings"
- "cmd/internal/ssa/types"
-
"cmd/internal/ssa"
)
@@ -227,9 +225,9 @@
b.Control = v
}
}
- // link up thunks to their actual values
+ // link up forward references to their actual values
for _, v := range b.Values {
- if v.Op != ssa.OpThunk {
+ if v.Op != ssa.OpFwdRef {
continue
}
varid := v.Aux.(int)
@@ -302,7 +300,7 @@
if err != nil {
panic("bad cint value")
}
- return b.Func.ConstInt(c)
+ return b.Func.ConstInt(ssa.TypeInt64, c)
case "LT":
x := genExpr(state, b, e.parts[1])
y := genExpr(state, b, e.parts[2])
@@ -310,28 +308,30 @@
v.AddArg(x)
v.AddArg(y)
return v
- case "FP":
- typ := state.types[e.parts[1].name]
- offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
- if err != nil {
- panic(err)
- }
- v := b.NewValue(ssa.OpFPAddr, types.NewPointer(typ), offset)
- return v
- case "SP":
- typ := state.types[e.parts[1].name]
- offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
- if err != nil {
- panic(err)
- }
- v := b.NewValue(ssa.OpSPAddr, types.NewPointer(typ), offset)
- return v
- case "LOAD":
- p := genExpr(state, b, e.parts[1])
- v := b.NewValue(ssa.OpLoad, p.Type.(*types.Pointer).Elem(), nil)
- v.AddArg(p)
- v.AddArg(genVar(state, b, state.memID))
- return v
+ /*
+ case "FP":
+ typ := state.types[e.parts[1].name]
+ offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ v := b.NewValue(ssa.OpFPAddr, types.NewPointer(typ), offset)
+ return v
+ case "SP":
+ typ := state.types[e.parts[1].name]
+ offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ v := b.NewValue(ssa.OpSPAddr, types.NewPointer(typ), offset)
+ return v
+ case "LOAD":
+ p := genExpr(state, b, e.parts[1])
+ v := b.NewValue(ssa.OpLoad, p.Type.(*types.Pointer).Elem(), nil)
+ v.AddArg(p)
+ v.AddArg(genVar(state, b, state.memID))
+ return v
+ */
default:
fmt.Println(e.parts[0].name)
panic("unknown op")
@@ -372,9 +372,9 @@
return v
}
// We don't know about defined variables in this block (yet).
- // Make a thunk for this variable.
- fmt.Printf("making thunk for var=%d in block=%d\n", id, b.ID)
- v = b.NewValue(ssa.OpThunk, state.vartypes[id], id)
+ // Make a forward reference for this variable.
+ fmt.Printf("making fwdRef for var=%d in block=%d\n", id, b.ID)
+ v = b.NewValue(ssa.OpFwdRef, state.vartypes[id], id)
// memoize result
state.defs[blockvar{b.ID, id}] = v
@@ -400,7 +400,7 @@
args[i] = lookupVarOutgoing(state, p, id)
}
- // if <=1 value that isn't this variable's thunk, don't make phi
+ // if <=1 value that isn't this variable's fwdRef, don't make phi
v.Op = ssa.OpPhi
v.AddArgs(args...) // note: order corresponding to b.Pred
}
@@ -418,20 +418,22 @@
panic("unknown type")
}
}
- if e.parts[0].name == "FUNC" {
- // TODO: receiver? Already folded into args? Variadic?
- var args, rets []*types.Var
- for _, s := range e.parts[1].parts {
- t := parseSexprType(s)
- args = append(args, types.NewParam(0, nil, "noname", t))
+ /*
+ if e.parts[0].name == "FUNC" {
+ // TODO: receiver? Already folded into args? Variadic?
+ var args, rets []*types.Var
+ for _, s := range e.parts[1].parts {
+ t := parseSexprType(s)
+ args = append(args, types.NewParam(0, nil, "noname", t))
+ }
+ for _, s := range e.parts[2].parts {
+ t := parseSexprType(s)
+ rets = append(rets, types.NewParam(0, nil, "noname", t))
+ }
+ sig := types.NewSignature(nil, nil, types.NewTuple(args...), types.NewTuple(rets...), false)
+ return ssa.Type(sig)
}
- for _, s := range e.parts[2].parts {
- t := parseSexprType(s)
- rets = append(rets, types.NewParam(0, nil, "noname", t))
- }
- sig := types.NewSignature(nil, nil, types.NewTuple(args...), types.NewTuple(rets...), false)
- return ssa.Type(sig)
- }
+ */
// TODO: array/struct/...
panic("compound type")
}
diff --git a/src/cmd/internal/ssa/type.go b/src/cmd/internal/ssa/type.go
index 98efe54..611c858 100644
--- a/src/cmd/internal/ssa/type.go
+++ b/src/cmd/internal/ssa/type.go
@@ -4,89 +4,71 @@
package ssa
-import (
- "cmd/internal/ssa/types" // TODO: use golang.org/x/tools/go/types instead
-)
+// TODO: use go/types instead?
-// We just inherit types from go/types
-type Type types.Type
+// A type interface used to import cmd/internal/gc:Type
+// Type instances are not guaranteed to be canonical.
+type Type interface {
+ Size() int64 // return the size in bytes
+
+ IsBoolean() bool // is a named or unnamed boolean type
+ IsInteger() bool // ... ditto for the others
+ IsSigned() bool
+ IsFloat() bool
+ IsPtr() bool
+
+ IsMemory() bool // special ssa-package-only types
+ IsFlags() bool
+
+ Elem() Type // given []T or *T, return T
+ PtrTo() Type // given T, return *T
+
+ String() string
+}
+
+// Stub implementation for now, until we are completely using ../gc:Type
+type TypeImpl struct {
+ Size_ int64
+ Boolean bool
+ Integer bool
+ Signed bool
+ Float bool
+ Ptr bool
+
+ Memory bool
+ Flags bool
+
+ Name string
+}
+
+func (t *TypeImpl) Size() int64 { return t.Size_ }
+func (t *TypeImpl) IsBoolean() bool { return t.Boolean }
+func (t *TypeImpl) IsInteger() bool { return t.Integer }
+func (t *TypeImpl) IsSigned() bool { return t.Signed }
+func (t *TypeImpl) IsFloat() bool { return t.Float }
+func (t *TypeImpl) IsPtr() bool { return t.Ptr }
+func (t *TypeImpl) IsMemory() bool { return t.Memory }
+func (t *TypeImpl) IsFlags() bool { return t.Flags }
+func (t *TypeImpl) String() string { return t.Name }
+func (t *TypeImpl) Elem() Type { panic("not implemented"); return nil }
+func (t *TypeImpl) PtrTo() Type { panic("not implemented"); return nil }
var (
// shortcuts for commonly used basic types
- //TypeInt = types.Typ[types.Int]
- //TypeUint = types.Typ[types.Uint]
- TypeInt8 = types.Typ[types.Int8]
- TypeInt16 = types.Typ[types.Int16]
- TypeInt32 = types.Typ[types.Int32]
- TypeInt64 = types.Typ[types.Int64]
- TypeUint8 = types.Typ[types.Uint8]
- TypeUint16 = types.Typ[types.Uint16]
- TypeUint32 = types.Typ[types.Uint32]
- TypeUint64 = types.Typ[types.Uint64]
- //TypeUintptr = types.Typ[types.Uintptr]
- TypeBool = types.Typ[types.Bool]
- TypeString = types.Typ[types.String]
+ TypeInt8 = &TypeImpl{Size_: 1, Integer: true, Signed: true, Name: "int8"}
+ TypeInt16 = &TypeImpl{Size_: 2, Integer: true, Signed: true, Name: "int16"}
+ TypeInt32 = &TypeImpl{Size_: 4, Integer: true, Signed: true, Name: "int32"}
+ TypeInt64 = &TypeImpl{Size_: 8, Integer: true, Signed: true, Name: "int64"}
+ TypeUInt8 = &TypeImpl{Size_: 1, Integer: true, Name: "uint8"}
+ TypeUInt16 = &TypeImpl{Size_: 2, Integer: true, Name: "uint16"}
+ TypeUInt32 = &TypeImpl{Size_: 4, Integer: true, Name: "uint32"}
+ TypeUInt64 = &TypeImpl{Size_: 8, Integer: true, Name: "uint64"}
+ TypeBool = &TypeImpl{Size_: 1, Boolean: true, Name: "bool"}
+ //TypeString = types.Typ[types.String]
- TypeInvalid = types.Typ[types.Invalid]
+ TypeInvalid = &TypeImpl{Name: "invalid"}
// Additional compiler-only types go here.
- TypeMem = &Memory{}
- TypeFlags = &Flags{}
-
- // TODO(khr): we probably shouldn't use int/uint/uintptr as Value types in the compiler.
- // In OpConst's case, their width is the compiler's width, not the to-be-compiled
- // program's width. For now, we can translate int/uint/uintptr to their specific
- // widths variants before SSA.
- // However, we may need at some point to maintain all possible user types in the
- // compiler to handle things like interface conversion. At that point, we may
- // need to revisit this decision.
+ TypeMem = &TypeImpl{Memory: true, Name: "mem"}
+ TypeFlags = &TypeImpl{Flags: true, Name: "flags"}
)
-
-// typeIdentical reports whether its two arguments are the same type.
-func typeIdentical(t, u Type) bool {
- if t == TypeMem {
- return u == TypeMem
- }
- if t == TypeFlags {
- return u == TypeFlags
- }
- return types.Identical(t, u)
-}
-
-// A type representing all of memory
-type Memory struct {
-}
-
-func (t *Memory) Underlying() types.Type { panic("Underlying of Memory") }
-func (t *Memory) String() string { return "mem" }
-
-// A type representing the unknown type
-type Unknown struct {
-}
-
-func (t *Unknown) Underlying() types.Type { panic("Underlying of Unknown") }
-func (t *Unknown) String() string { return "unk" }
-
-// A type representing the void type. Used during building, should always
-// be eliminated by the first deadcode pass.
-type Void struct {
-}
-
-func (t *Void) Underlying() types.Type { panic("Underlying of Void") }
-func (t *Void) String() string { return "void" }
-
-// A type representing the results of a nil check or bounds check.
-// TODO: or type check?
-// TODO: just use bool?
-type Check struct {
-}
-
-func (t *Check) Underlying() types.Type { panic("Underlying of Check") }
-func (t *Check) String() string { return "check" }
-
-// x86 flags type
-type Flags struct {
-}
-
-func (t *Flags) Underlying() types.Type { panic("Underlying of Flags") }
-func (t *Flags) String() string { return "flags" }
diff --git a/src/cmd/internal/ssa/types/object.go b/src/cmd/internal/ssa/types/object.go
deleted file mode 100644
index cd0be16..0000000
--- a/src/cmd/internal/ssa/types/object.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2015 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.
-
-// This package is a drop-in replacement for go/types
-// for use until go/types is included in the main repo.
-
-package types
-
-// An Object describes a named language entity such as a package,
-// constant, type, variable, function (incl. methods), or label.
-// All objects implement the Object interface.
-//
-type Object interface {
- Name() string // package local object name
- Type() Type // object type
-}
-
-// An object implements the common parts of an Object.
-type object struct {
- name string
- typ Type
-}
-
-func (obj *object) Name() string { return obj.name }
-func (obj *object) Type() Type { return obj.typ }
-
-// A Variable represents a declared variable (including function parameters and results, and struct fields).
-type Var struct {
- object
- anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
- visited bool // for initialization cycle detection
- isField bool // var is struct field
- used bool // set if the variable was used
-}
-
-func NewParam(pos int, pkg *int, name string, typ Type) *Var {
- return &Var{object: object{name, typ}, used: true} // parameters are always 'used'
-}
diff --git a/src/cmd/internal/ssa/types/sizes.go b/src/cmd/internal/ssa/types/sizes.go
deleted file mode 100644
index b52f636..0000000
--- a/src/cmd/internal/ssa/types/sizes.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2013 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.
-
-// This file implements Sizes.
-
-package types
-
-import "log"
-
-// Sizes defines the sizing functions for package unsafe.
-type Sizes interface {
- // Alignof returns the alignment of a variable of type T.
- // Alignof must implement the alignment guarantees required by the spec.
- Alignof(T Type) int64
-
- // Offsetsof returns the offsets of the given struct fields, in bytes.
- // Offsetsof must implement the offset guarantees required by the spec.
- Offsetsof(fields []*Var) []int64
-
- // Sizeof returns the size of a variable of type T.
- // Sizeof must implement the size guarantees required by the spec.
- Sizeof(T Type) int64
-}
-
-// StdSizes is a convenience type for creating commonly used Sizes.
-// It makes the following simplifying assumptions:
-//
-// - The size of explicitly sized basic types (int16, etc.) is the
-// specified size.
-// - The size of strings and interfaces is 2*WordSize.
-// - The size of slices is 3*WordSize.
-// - The size of an array of n elements corresponds to the size of
-// a struct of n consecutive fields of the array's element type.
-// - The size of a struct is the offset of the last field plus that
-// field's size. As with all element types, if the struct is used
-// in an array its size must first be aligned to a multiple of the
-// struct's alignment.
-// - All other types have size WordSize.
-// - Arrays and structs are aligned per spec definition; all other
-// types are naturally aligned with a maximum alignment MaxAlign.
-//
-// *StdSizes implements Sizes.
-//
-type StdSizes struct {
- WordSize int64 // word size in bytes - must be >= 4 (32bits)
- MaxAlign int64 // maximum alignment in bytes - must be >= 1
-}
-
-func (s *StdSizes) Alignof(T Type) int64 {
- a := s.Sizeof(T) // may be 0
- // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
- if a < 1 {
- return 1
- }
- if a > s.MaxAlign {
- return s.MaxAlign
- }
- return a
-}
-
-func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
- offsets := make([]int64, len(fields))
- var o int64
- for i, f := range fields {
- a := s.Alignof(f.typ)
- o = align(o, a)
- offsets[i] = o
- o += s.Sizeof(f.typ)
- }
- return offsets
-}
-
-var basicSizes = [...]byte{
- Bool: 1,
- Int8: 1,
- Int16: 2,
- Int32: 4,
- Int64: 8,
- Uint8: 1,
- Uint16: 2,
- Uint32: 4,
- Uint64: 8,
- Float32: 4,
- Float64: 8,
- Complex64: 8,
- Complex128: 16,
-}
-
-func (s *StdSizes) Sizeof(T Type) int64 {
- switch t := T.Underlying().(type) {
- case *Basic:
- k := t.kind
- if int(k) < len(basicSizes) {
- if s := basicSizes[k]; s > 0 {
- return int64(s)
- }
- }
- if k == String {
- return s.WordSize * 2
- }
- case *Slice:
- return s.WordSize * 3
- default:
- log.Fatalf("not implemented")
- }
- return s.WordSize // catch-all
-}
-
-// stdSizes is used if Config.Sizes == nil.
-var stdSizes = StdSizes{8, 8}
-
-// align returns the smallest y >= x such that y % a == 0.
-func align(x, a int64) int64 {
- y := x + a - 1
- return y - y%a
-}
diff --git a/src/cmd/internal/ssa/types/type.go b/src/cmd/internal/ssa/types/type.go
deleted file mode 100644
index e01de5c..0000000
--- a/src/cmd/internal/ssa/types/type.go
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2015 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.
-
-// This package is a drop-in replacement for go/types
-// for use until go/types is included in the main repo.
-
-package types
-
-// A Type represents a type of Go.
-// All types implement the Type interface.
-type Type interface {
- // Underlying returns the underlying type of a type.
- Underlying() Type
-
- // String returns a string representation of a type.
- String() string
-}
-
-// BasicKind describes the kind of basic type.
-type BasicKind int
-
-const (
- Invalid BasicKind = iota // type is invalid
-
- // predeclared types
- Bool
- Int
- Int8
- Int16
- Int32
- Int64
- Uint
- Uint8
- Uint16
- Uint32
- Uint64
- Uintptr
- Float32
- Float64
- Complex64
- Complex128
- String
- UnsafePointer
-
- // types for untyped values
- UntypedBool
- UntypedInt
- UntypedRune
- UntypedFloat
- UntypedComplex
- UntypedString
- UntypedNil
-
- // aliases
- Byte = Uint8
- Rune = Int32
-)
-
-// BasicInfo is a set of flags describing properties of a basic type.
-type BasicInfo int
-
-// Properties of basic types.
-const (
- IsBoolean BasicInfo = 1 << iota
- IsInteger
- IsUnsigned
- IsFloat
- IsComplex
- IsString
- IsUntyped
-
- IsOrdered = IsInteger | IsFloat | IsString
- IsNumeric = IsInteger | IsFloat | IsComplex
- IsConstType = IsBoolean | IsNumeric | IsString
-)
-
-// A Basic represents a basic type.
-type Basic struct {
- kind BasicKind
- info BasicInfo
- name string
-}
-
-// Kind returns the kind of basic type b.
-func (b *Basic) Kind() BasicKind { return b.kind }
-
-// Info returns information about properties of basic type b.
-func (b *Basic) Info() BasicInfo { return b.info }
-
-// Name returns the name of basic type b.
-func (b *Basic) Name() string { return b.name }
-
-// A Pointer represents a pointer type.
-type Pointer struct {
- base Type // element type
-}
-
-// NewPointer returns a new pointer type for the given element (base) type.
-func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
-
-// Elem returns the element type for the given pointer p.
-func (p *Pointer) Elem() Type { return p.base }
-
-// A Slice represents a slice type.
-type Slice struct {
- elem Type
-}
-
-// NewSlice returns a new slice type for the given element type.
-func NewSlice(elem Type) *Slice { return &Slice{elem} }
-
-// Elem returns the element type of slice s.
-func (s *Slice) Elem() Type { return s.elem }
-
-// Implementations for Type methods.
-func (t *Basic) Underlying() Type { return t }
-func (t *Slice) Underlying() Type { return t }
-func (t *Pointer) Underlying() Type { return t }
-func (t *Signature) Underlying() Type { return t }
-
-func (b *Basic) String() string { return b.name }
-func (t *Slice) String() string { return "[]" + t.elem.String() }
-func (t *Pointer) String() string { return "*" + t.base.String() }
-func (t *Signature) String() string { return "sig" /* TODO */ }
-
-var Typ = [...]*Basic{
- Invalid: {Invalid, 0, "invalid type"},
-
- Bool: {Bool, IsBoolean, "bool"},
- Int: {Int, IsInteger, "int"},
- Int8: {Int8, IsInteger, "int8"},
- Int16: {Int16, IsInteger, "int16"},
- Int32: {Int32, IsInteger, "int32"},
- Int64: {Int64, IsInteger, "int64"},
- Uint: {Uint, IsInteger | IsUnsigned, "uint"},
- Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"},
- Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"},
- Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"},
- Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"},
- Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"},
- Float32: {Float32, IsFloat, "float32"},
- Float64: {Float64, IsFloat, "float64"},
- Complex64: {Complex64, IsComplex, "complex64"},
- Complex128: {Complex128, IsComplex, "complex128"},
- String: {String, IsString, "string"},
- UnsafePointer: {UnsafePointer, 0, "Pointer"},
-
- UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
- UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"},
- UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
- UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
- UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
- UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"},
- UntypedNil: {UntypedNil, IsUntyped, "untyped nil"},
-}
-
-// Identical reports whether x and y are identical.
-func Identical(x, y Type) bool {
- if x == y {
- return true
- }
-
- switch x := x.(type) {
- case *Basic:
- // Basic types are singletons except for the rune and byte
- // aliases, thus we cannot solely rely on the x == y check
- // above.
- if y, ok := y.(*Basic); ok {
- return x.kind == y.kind
- }
- default:
- panic("can't handle yet")
- }
- return false
-}
-
-// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
-// Tuples are used as components of signatures and to represent the type of multiple
-// assignments; they are not first class types of Go.
-type Tuple struct {
- vars []*Var
-}
-
-// NewTuple returns a new tuple for the given variables.
-func NewTuple(x ...*Var) *Tuple {
- if len(x) > 0 {
- return &Tuple{x}
- }
- return nil
-}
-
-// Len returns the number variables of tuple t.
-func (t *Tuple) Len() int {
- if t != nil {
- return len(t.vars)
- }
- return 0
-}
-
-// At returns the i'th variable of tuple t.
-func (t *Tuple) At(i int) *Var { return t.vars[i] }
-
-// A Signature represents a (non-builtin) function or method type.
-type Signature struct {
- recv *Var // nil if not a method
- params *Tuple // (incoming) parameters from left to right; or nil
- results *Tuple // (outgoing) results from left to right; or nil
- variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
-}
-
-// NewSignature returns a new function type for the given receiver, parameters,
-// and results, either of which may be nil. If variadic is set, the function
-// is variadic, it must have at least one parameter, and the last parameter
-// must be of unnamed slice type.
-func NewSignature(scope *int, recv *Var, params, results *Tuple, variadic bool) *Signature {
- // TODO(gri) Should we rely on the correct (non-nil) incoming scope
- // or should this function allocate and populate a scope?
- if variadic {
- n := params.Len()
- if n == 0 {
- panic("types.NewSignature: variadic function must have at least one parameter")
- }
- if _, ok := params.At(n - 1).typ.(*Slice); !ok {
- panic("types.NewSignature: variadic parameter must be of unnamed slice type")
- }
- }
- return &Signature{recv, params, results, variadic}
-}
diff --git a/src/cmd/internal/ssa/value.go b/src/cmd/internal/ssa/value.go
index 389ba1f..dab6239 100644
--- a/src/cmd/internal/ssa/value.go
+++ b/src/cmd/internal/ssa/value.go
@@ -101,15 +101,3 @@
v.argstorage[1] = nil
v.Args = v.argstorage[:0]
}
-
-// CopyFrom converts v to be the same value as w. v and w must
-// have the same type.
-func (v *Value) CopyFrom(w *Value) {
- if !typeIdentical(v.Type, w.Type) {
- panic("copyFrom with unequal types")
- }
- v.Op = w.Op
- v.Aux = w.Aux
- v.resetArgs()
- v.AddArgs(w.Args...)
-}