[dev.ssa] cmd/compiler/internal/ssa: Add auxint field

Add an additional int64 auxiliary field to Value.

There are two main reasons for doing this:
1) Ints in interfaces require allocation, and we store ints in Aux a lot.
2) I'd like to have both *gc.Sym and int offsets included in lots
   of operations (e.g. MOVQloadidx8).  It will be more efficient to
   store them as separate fields instead of a pointer to a sym/int pair.

It also simplifies a bunch of code.

This is just the refactoring.  I'll start using this some more in a
subsequent changelist.

Change-Id: I1ca797ff572553986cf90cab3ac0a0c1d01ad241
Reviewed-on: https://go-review.googlesource.com/10929
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Keith Randall 2015-06-11 21:29:25 -07:00
parent 0ad9c8c720
commit 8f22b5292f
20 changed files with 756 additions and 1003 deletions

View file

@ -38,9 +38,9 @@ func buildssa(fn *Node) *ssa.Func {
s.exit = s.f.NewBlock(ssa.BlockExit) s.exit = s.f.NewBlock(ssa.BlockExit)
// Allocate starting values // Allocate starting values
s.startmem = s.entryNewValue(ssa.OpArg, ssa.TypeMem, ".mem") s.startmem = s.entryNewValue0(ssa.OpArg, ssa.TypeMem)
s.fp = s.entryNewValue(ssa.OpFP, s.config.Uintptr, nil) // TODO: use generic pointer type (unsafe.Pointer?) instead s.fp = s.entryNewValue0(ssa.OpFP, s.config.Uintptr) // TODO: use generic pointer type (unsafe.Pointer?) instead
s.sp = s.entryNewValue(ssa.OpSP, s.config.Uintptr, nil) s.sp = s.entryNewValue0(ssa.OpSP, s.config.Uintptr)
s.vars = map[string]*ssa.Value{} s.vars = map[string]*ssa.Value{}
s.labels = map[string]*ssa.Block{} s.labels = map[string]*ssa.Block{}
@ -147,39 +147,59 @@ func (s *state) peekLine() int32 {
return s.line[len(s.line)-1] return s.line[len(s.line)-1]
} }
// newValue adds a new value with no argueents to the current block. // newValue0 adds a new value with no arguments to the current block.
func (s *state) newValue(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { func (s *state) newValue0(op ssa.Op, t ssa.Type) *ssa.Value {
return s.curBlock.NewValue(s.peekLine(), op, t, aux) return s.curBlock.NewValue0(s.peekLine(), op, t)
}
// newValue0A adds a new value with no arguments and an aux value to the current block.
func (s *state) newValue0A(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value {
return s.curBlock.NewValue0A(s.peekLine(), op, t, aux)
} }
// newValue1 adds a new value with one argument to the current block. // newValue1 adds a new value with one argument to the current block.
func (s *state) newValue1(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { func (s *state) newValue1(op ssa.Op, t ssa.Type, arg *ssa.Value) *ssa.Value {
return s.curBlock.NewValue1(s.peekLine(), op, t, aux, arg) return s.curBlock.NewValue1(s.peekLine(), op, t, arg)
}
// newValue1A adds a new value with one argument and an aux value to the current block.
func (s *state) newValue1A(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value {
return s.curBlock.NewValue1A(s.peekLine(), op, t, aux, arg)
} }
// newValue2 adds a new value with two arguments to the current block. // newValue2 adds a new value with two arguments to the current block.
func (s *state) newValue2(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { func (s *state) newValue2(op ssa.Op, t ssa.Type, arg0, arg1 *ssa.Value) *ssa.Value {
return s.curBlock.NewValue2(s.peekLine(), op, t, aux, arg0, arg1) return s.curBlock.NewValue2(s.peekLine(), op, t, arg0, arg1)
} }
// newValue3 adds a new value with three arguments to the current block. // newValue3 adds a new value with three arguments to the current block.
func (s *state) newValue3(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1, arg2 *ssa.Value) *ssa.Value { func (s *state) newValue3(op ssa.Op, t ssa.Type, arg0, arg1, arg2 *ssa.Value) *ssa.Value {
return s.curBlock.NewValue3(s.peekLine(), op, t, aux, arg0, arg1, arg2) return s.curBlock.NewValue3(s.peekLine(), op, t, arg0, arg1, arg2)
} }
// entryNewValue adds a new value with no arguments to the entry block. // entryNewValue adds a new value with no arguments to the entry block.
func (s *state) entryNewValue(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value { func (s *state) entryNewValue0(op ssa.Op, t ssa.Type) *ssa.Value {
return s.f.Entry.NewValue(s.peekLine(), op, t, aux) return s.f.Entry.NewValue0(s.peekLine(), op, t)
}
// entryNewValue adds a new value with no arguments and an aux value to the entry block.
func (s *state) entryNewValue0A(op ssa.Op, t ssa.Type, aux interface{}) *ssa.Value {
return s.f.Entry.NewValue0A(s.peekLine(), op, t, aux)
} }
// entryNewValue1 adds a new value with one argument to the entry block. // entryNewValue1 adds a new value with one argument to the entry block.
func (s *state) entryNewValue1(op ssa.Op, t ssa.Type, aux interface{}, arg *ssa.Value) *ssa.Value { func (s *state) entryNewValue1(op ssa.Op, t ssa.Type, arg *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue1(s.peekLine(), op, t, aux, arg) return s.f.Entry.NewValue1(s.peekLine(), op, t, arg)
}
// entryNewValue1 adds a new value with one argument and an auxint value to the entry block.
func (s *state) entryNewValue1I(op ssa.Op, t ssa.Type, auxint int64, arg *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue1I(s.peekLine(), op, t, auxint, arg)
} }
// entryNewValue2 adds a new value with two arguments to the entry block. // entryNewValue2 adds a new value with two arguments to the entry block.
func (s *state) entryNewValue2(op ssa.Op, t ssa.Type, aux interface{}, arg0, arg1 *ssa.Value) *ssa.Value { func (s *state) entryNewValue2(op ssa.Op, t ssa.Type, arg0, arg1 *ssa.Value) *ssa.Value {
return s.f.Entry.NewValue2(s.peekLine(), op, t, aux, arg0, arg1) return s.f.Entry.NewValue2(s.peekLine(), op, t, arg0, arg1)
} }
// constInt adds a new const int value to the entry block. // constInt adds a new const int value to the entry block.
@ -234,11 +254,11 @@ func (s *state) stmt(n *Node) {
t := n.Left.Type t := n.Left.Type
switch { switch {
case t.IsString(): case t.IsString():
val = s.entryNewValue(ssa.OpConst, n.Left.Type, "") val = s.entryNewValue0(ssa.OpConst, n.Left.Type)
case t.IsInteger(): case t.IsInteger():
val = s.entryNewValue(ssa.OpConst, n.Left.Type, int64(0)) val = s.entryNewValue0(ssa.OpConst, n.Left.Type)
case t.IsBoolean(): case t.IsBoolean():
val = s.entryNewValue(ssa.OpConst, n.Left.Type, false) val = s.entryNewValue0A(ssa.OpConst, n.Left.Type, false) // TODO: store bools as 0/1 in AuxInt?
default: default:
log.Fatalf("zero for type %v not implemented", t) log.Fatalf("zero for type %v not implemented", t)
} }
@ -252,7 +272,7 @@ func (s *state) stmt(n *Node) {
} }
// not ssa-able. Treat as a store. // not ssa-able. Treat as a store.
addr := s.addr(n.Left) addr := s.addr(n.Left)
s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, nil, addr, val, s.mem()) s.vars[".mem"] = s.newValue3(ssa.OpStore, ssa.TypeMem, addr, val, s.mem())
case OIF: case OIF:
cond := s.expr(n.Left) cond := s.expr(n.Left)
b := s.endBlock() b := s.endBlock()
@ -341,20 +361,20 @@ func (s *state) expr(n *Node) *ssa.Value {
case ONAME: case ONAME:
if n.Class == PFUNC { if n.Class == PFUNC {
// "value" of a function is the address of the function's closure // "value" of a function is the address of the function's closure
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym)) return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), funcsym(n.Sym))
} }
s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way? s.argOffsets[n.Sym.Name] = n.Xoffset // TODO: remember this another way?
if canSSA(n) { if canSSA(n) {
return s.variable(n.Sym.Name, n.Type) return s.variable(n.Sym.Name, n.Type)
} }
addr := s.addr(n) addr := s.addr(n)
return s.newValue2(ssa.OpLoad, n.Type, nil, addr, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
case OLITERAL: case OLITERAL:
switch n.Val().Ctype() { switch n.Val().Ctype() {
case CTINT: case CTINT:
return s.constInt(n.Type, Mpgetfix(n.Val().U.(*Mpint))) return s.constInt(n.Type, Mpgetfix(n.Val().U.(*Mpint)))
case CTSTR: case CTSTR:
return s.entryNewValue(ssa.OpConst, n.Type, n.Val().U) return s.entryNewValue0A(ssa.OpConst, n.Type, n.Val().U)
default: default:
log.Fatalf("unhandled OLITERAL %v", n.Val().Ctype()) log.Fatalf("unhandled OLITERAL %v", n.Val().Ctype())
return nil return nil
@ -367,24 +387,24 @@ func (s *state) expr(n *Node) *ssa.Value {
case OLT: case OLT:
a := s.expr(n.Left) a := s.expr(n.Left)
b := s.expr(n.Right) b := s.expr(n.Right)
return s.newValue2(ssa.OpLess, ssa.TypeBool, nil, a, b) return s.newValue2(ssa.OpLess, ssa.TypeBool, a, b)
case OADD: case OADD:
a := s.expr(n.Left) a := s.expr(n.Left)
b := s.expr(n.Right) b := s.expr(n.Right)
return s.newValue2(ssa.OpAdd, a.Type, nil, a, b) return s.newValue2(ssa.OpAdd, a.Type, a, b)
case OSUB: case OSUB:
// TODO:(khr) fold code for all binary ops together somehow // TODO:(khr) fold code for all binary ops together somehow
a := s.expr(n.Left) a := s.expr(n.Left)
b := s.expr(n.Right) b := s.expr(n.Right)
return s.newValue2(ssa.OpSub, a.Type, nil, a, b) return s.newValue2(ssa.OpSub, a.Type, a, b)
case OLSH: case OLSH:
a := s.expr(n.Left) a := s.expr(n.Left)
b := s.expr(n.Right) b := s.expr(n.Right)
return s.newValue2(ssa.OpLsh, a.Type, nil, a, b) return s.newValue2(ssa.OpLsh, a.Type, a, b)
case ORSH: case ORSH:
a := s.expr(n.Left) a := s.expr(n.Left)
b := s.expr(n.Right) b := s.expr(n.Right)
return s.newValue2(ssa.OpRsh, a.Type, nil, a, b) return s.newValue2(ssa.OpRsh, a.Type, a, b)
case OADDR: case OADDR:
return s.addr(n.Left) return s.addr(n.Left)
@ -392,13 +412,13 @@ func (s *state) expr(n *Node) *ssa.Value {
case OIND: case OIND:
p := s.expr(n.Left) p := s.expr(n.Left)
s.nilCheck(p) s.nilCheck(p)
return s.newValue2(ssa.OpLoad, n.Type, nil, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case ODOTPTR: case ODOTPTR:
p := s.expr(n.Left) p := s.expr(n.Left)
s.nilCheck(p) s.nilCheck(p)
p = s.newValue2(ssa.OpAdd, p.Type, nil, p, s.constInt(s.config.Uintptr, n.Xoffset)) p = s.newValue2(ssa.OpAdd, p.Type, p, s.constInt(s.config.Uintptr, n.Xoffset))
return s.newValue2(ssa.OpLoad, n.Type, nil, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
case OINDEX: case OINDEX:
if n.Left.Type.Bound >= 0 { // array or string if n.Left.Type.Bound >= 0 { // array or string
@ -407,17 +427,17 @@ func (s *state) expr(n *Node) *ssa.Value {
var elemtype *Type var elemtype *Type
var len *ssa.Value var len *ssa.Value
if n.Left.Type.IsString() { if n.Left.Type.IsString() {
len = s.newValue1(ssa.OpStringLen, s.config.Uintptr, nil, a) len = s.newValue1(ssa.OpStringLen, s.config.Uintptr, a)
elemtype = Types[TUINT8] elemtype = Types[TUINT8]
} else { } else {
len = s.constInt(s.config.Uintptr, n.Left.Type.Bound) len = s.constInt(s.config.Uintptr, n.Left.Type.Bound)
elemtype = n.Left.Type.Type elemtype = n.Left.Type.Type
} }
s.boundsCheck(i, len) s.boundsCheck(i, len)
return s.newValue2(ssa.OpArrayIndex, elemtype, nil, a, i) return s.newValue2(ssa.OpArrayIndex, elemtype, a, i)
} else { // slice } else { // slice
p := s.addr(n) p := s.addr(n)
return s.newValue2(ssa.OpLoad, n.Left.Type.Type, nil, p, s.mem()) return s.newValue2(ssa.OpLoad, n.Left.Type.Type, p, s.mem())
} }
case OCALLFUNC: case OCALLFUNC:
@ -435,10 +455,10 @@ func (s *state) expr(n *Node) *ssa.Value {
bNext := s.f.NewBlock(ssa.BlockPlain) bNext := s.f.NewBlock(ssa.BlockPlain)
var call *ssa.Value var call *ssa.Value
if static { if static {
call = s.newValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem()) call = s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem())
} else { } else {
entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, nil, closure, s.mem()) entry := s.newValue2(ssa.OpLoad, s.config.Uintptr, closure, s.mem())
call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, nil, entry, closure, s.mem()) call = s.newValue3(ssa.OpClosureCall, ssa.TypeMem, entry, closure, s.mem())
} }
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockCall b.Kind = ssa.BlockCall
@ -450,8 +470,8 @@ func (s *state) expr(n *Node) *ssa.Value {
s.startBlock(bNext) s.startBlock(bNext)
var titer Iter var titer Iter
fp := Structfirst(&titer, Getoutarg(n.Left.Type)) fp := Structfirst(&titer, Getoutarg(n.Left.Type))
a := s.entryNewValue1(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp) a := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Width, s.sp)
return s.newValue2(ssa.OpLoad, fp.Type, nil, a, call) return s.newValue2(ssa.OpLoad, fp.Type, a, call)
default: default:
log.Fatalf("unhandled expr %s", opnames[n.Op]) log.Fatalf("unhandled expr %s", opnames[n.Op])
return nil return nil
@ -465,10 +485,10 @@ func (s *state) addr(n *Node) *ssa.Value {
switch n.Class { switch n.Class {
case PEXTERN: case PEXTERN:
// global variable // global variable
return s.entryNewValue(ssa.OpGlobal, Ptrto(n.Type), n.Sym) return s.entryNewValue0A(ssa.OpGlobal, Ptrto(n.Type), n.Sym)
case PPARAMOUT: case PPARAMOUT:
// store to parameter slot // store to parameter slot
return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp) return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.fp)
default: default:
// TODO: address of locals // TODO: address of locals
log.Fatalf("variable address of %v not implemented", n) log.Fatalf("variable address of %v not implemented", n)
@ -477,21 +497,21 @@ func (s *state) addr(n *Node) *ssa.Value {
case OINDREG: case OINDREG:
// indirect off a register (TODO: always SP?) // indirect off a register (TODO: always SP?)
// used for storing/loading arguments/returns to/from callees // used for storing/loading arguments/returns to/from callees
return s.entryNewValue1(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp) return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp)
case OINDEX: case OINDEX:
if n.Left.Type.Bound >= 0 { // array if n.Left.Type.Bound >= 0 { // array
a := s.addr(n.Left) a := s.addr(n.Left)
i := s.expr(n.Right) i := s.expr(n.Right)
len := s.constInt(s.config.Uintptr, n.Left.Type.Bound) len := s.constInt(s.config.Uintptr, n.Left.Type.Bound)
s.boundsCheck(i, len) s.boundsCheck(i, len)
return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), nil, a, i) return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), a, i)
} else { // slice } else { // slice
a := s.expr(n.Left) a := s.expr(n.Left)
i := s.expr(n.Right) i := s.expr(n.Right)
len := s.newValue1(ssa.OpSliceLen, s.config.Uintptr, nil, a) len := s.newValue1(ssa.OpSliceLen, s.config.Uintptr, a)
s.boundsCheck(i, len) s.boundsCheck(i, len)
p := s.newValue1(ssa.OpSlicePtr, Ptrto(n.Left.Type.Type), nil, a) p := s.newValue1(ssa.OpSlicePtr, Ptrto(n.Left.Type.Type), a)
return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), nil, p, i) return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Type), p, i)
} }
default: default:
log.Fatalf("addr: bad op %v", Oconv(int(n.Op), 0)) log.Fatalf("addr: bad op %v", Oconv(int(n.Op), 0))
@ -524,7 +544,7 @@ func canSSA(n *Node) bool {
// nilCheck generates nil pointer checking code. // nilCheck generates nil pointer checking code.
// Starts a new block on return. // Starts a new block on return.
func (s *state) nilCheck(ptr *ssa.Value) { func (s *state) nilCheck(ptr *ssa.Value) {
c := s.newValue1(ssa.OpIsNonNil, ssa.TypeBool, nil, ptr) c := s.newValue1(ssa.OpIsNonNil, ssa.TypeBool, ptr)
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockIf b.Kind = ssa.BlockIf
b.Control = c b.Control = c
@ -543,7 +563,7 @@ func (s *state) boundsCheck(idx, len *ssa.Value) {
// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero. // TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.
// bounds check // bounds check
cmp := s.newValue2(ssa.OpIsInBounds, ssa.TypeBool, nil, idx, len) cmp := s.newValue2(ssa.OpIsInBounds, ssa.TypeBool, idx, len)
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockIf b.Kind = ssa.BlockIf
b.Control = cmp b.Control = cmp
@ -562,7 +582,7 @@ func (s *state) variable(name string, t ssa.Type) *ssa.Value {
v := s.vars[name] v := s.vars[name]
if v == nil { if v == nil {
// TODO: get type? Take Sym as arg? // TODO: get type? Take Sym as arg?
v = s.newValue(ssa.OpFwdRef, t, name) v = s.newValue0A(ssa.OpFwdRef, t, name)
s.vars[name] = v s.vars[name] = v
} }
return v return v
@ -601,8 +621,8 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va
return s.startmem return s.startmem
} }
// variable is live at the entry block. Load it. // variable is live at the entry block. Load it.
addr := s.entryNewValue1(ssa.OpOffPtr, Ptrto(t.(*Type)), s.argOffsets[name], s.fp) addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(t.(*Type)), s.argOffsets[name], s.fp)
return s.entryNewValue2(ssa.OpLoad, t, nil, addr, s.startmem) return s.entryNewValue2(ssa.OpLoad, t, addr, s.startmem)
} }
var vals []*ssa.Value var vals []*ssa.Value
@ -613,7 +633,7 @@ func (s *state) lookupVarIncoming(b *ssa.Block, t ssa.Type, name string) *ssa.Va
for i := 1; i < len(vals); i++ { for i := 1; i < len(vals); i++ {
if vals[i] != v0 { if vals[i] != v0 {
// need a phi value // need a phi value
v := b.NewValue(s.peekLine(), ssa.OpPhi, t, nil) v := b.NewValue0(s.peekLine(), ssa.OpPhi, t)
v.AddArgs(vals...) v.AddArgs(vals...)
return v return v
} }
@ -634,7 +654,7 @@ func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name string) *ssa.Va
// Make v = copy(w). We need the extra copy to // Make v = copy(w). We need the extra copy to
// prevent infinite recursion when looking up the // prevent infinite recursion when looking up the
// incoming value of the variable. // incoming value of the variable.
v := b.NewValue(s.peekLine(), ssa.OpCopy, t, nil) v := b.NewValue0(s.peekLine(), ssa.OpCopy, t)
m[name] = v m[name] = v
v.AddArg(s.lookupVarIncoming(b, t, name)) v.AddArg(s.lookupVarIncoming(b, t, name))
return v return v
@ -728,7 +748,7 @@ func genValue(v *ssa.Value) {
p := Prog(x86.ALEAQ) p := Prog(x86.ALEAQ)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
case ssa.OpAMD64MULQconst: case ssa.OpAMD64MULQconst:
@ -736,7 +756,7 @@ func genValue(v *ssa.Value) {
// has ever been taught to compile imul $c, r1, r2. // has ever been taught to compile imul $c, r1, r2.
p := Prog(x86.AIMULQ) p := Prog(x86.AIMULQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.From3.Type = obj.TYPE_REG p.From3.Type = obj.TYPE_REG
p.From3.Reg = regnum(v.Args[0]) p.From3.Reg = regnum(v.Args[0])
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
@ -756,7 +776,7 @@ func genValue(v *ssa.Value) {
} }
p := Prog(x86.ASUBQ) p := Prog(x86.ASUBQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
case ssa.OpAMD64SHLQ: case ssa.OpAMD64SHLQ:
@ -829,7 +849,7 @@ func genValue(v *ssa.Value) {
} }
p := Prog(x86.ASHLQ) p := Prog(x86.ASHLQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
case ssa.OpAMD64SHRQconst: case ssa.OpAMD64SHRQconst:
@ -845,7 +865,7 @@ func genValue(v *ssa.Value) {
} }
p := Prog(x86.ASHRQ) p := Prog(x86.ASHRQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
case ssa.OpAMD64SARQconst: case ssa.OpAMD64SARQconst:
@ -861,7 +881,7 @@ func genValue(v *ssa.Value) {
} }
p := Prog(x86.ASARQ) p := Prog(x86.ASARQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
case ssa.OpAMD64SBBQcarrymask: case ssa.OpAMD64SBBQcarrymask:
@ -921,7 +941,7 @@ func genValue(v *ssa.Value) {
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.From.Scale = 1 p.From.Scale = 1
p.From.Index = regnum(v.Args[1]) p.From.Index = regnum(v.Args[1])
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
case ssa.OpAMD64CMPQ: case ssa.OpAMD64CMPQ:
@ -935,7 +955,7 @@ func genValue(v *ssa.Value) {
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.To.Type = obj.TYPE_CONST p.To.Type = obj.TYPE_CONST
p.To.Offset = v.Aux.(int64) p.To.Offset = v.AuxInt
case ssa.OpAMD64TESTB: case ssa.OpAMD64TESTB:
p := Prog(x86.ATESTB) p := Prog(x86.ATESTB)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
@ -946,28 +966,28 @@ func genValue(v *ssa.Value) {
x := regnum(v) x := regnum(v)
p := Prog(x86.AMOVQ) p := Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = x p.To.Reg = x
case ssa.OpAMD64MOVQload: case ssa.OpAMD64MOVQload:
p := Prog(x86.AMOVQ) p := Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
case ssa.OpAMD64MOVBload: case ssa.OpAMD64MOVBload:
p := Prog(x86.AMOVB) p := Prog(x86.AMOVB)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
case ssa.OpAMD64MOVQloadidx8: case ssa.OpAMD64MOVQloadidx8:
p := Prog(x86.AMOVQ) p := Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = regnum(v.Args[0]) p.From.Reg = regnum(v.Args[0])
p.From.Offset = v.Aux.(int64) p.From.Offset = v.AuxInt
p.From.Scale = 8 p.From.Scale = 8
p.From.Index = regnum(v.Args[1]) p.From.Index = regnum(v.Args[1])
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
@ -978,7 +998,7 @@ func genValue(v *ssa.Value) {
p.From.Reg = regnum(v.Args[1]) p.From.Reg = regnum(v.Args[1])
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Reg = regnum(v.Args[0]) p.To.Reg = regnum(v.Args[0])
p.To.Offset = v.Aux.(int64) p.To.Offset = v.AuxInt
case ssa.OpCopy: // TODO: lower to MOVQ earlier? case ssa.OpCopy: // TODO: lower to MOVQ earlier?
if v.Type.IsMemory() { if v.Type.IsMemory() {
return return
@ -1021,14 +1041,13 @@ func genValue(v *ssa.Value) {
} }
case ssa.OpArg: case ssa.OpArg:
// memory arg needs no code // memory arg needs no code
// TODO: only mem arg goes here. // TODO: check that only mem arg goes here.
case ssa.OpAMD64LEAQglobal: case ssa.OpAMD64LEAQglobal:
g := v.Aux.(ssa.GlobalOffset)
p := Prog(x86.ALEAQ) p := Prog(x86.ALEAQ)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN p.From.Name = obj.NAME_EXTERN
p.From.Sym = Linksym(g.Global.(*Sym)) p.From.Sym = Linksym(v.Aux.(*Sym))
p.From.Offset = g.Offset p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v) p.To.Reg = regnum(v)
case ssa.OpAMD64CALLstatic: case ssa.OpAMD64CALLstatic:

View file

@ -10,14 +10,14 @@ func TestDeadLoop(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{}) c := NewConfig("amd64", DummyFrontend{})
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem")), Exit("mem")),
// dead loop // dead loop
Bloc("deadblock", Bloc("deadblock",
// dead value in dead block // dead value in dead block
Valu("deadval", OpConst, TypeBool, true), Valu("deadval", OpConst, TypeBool, 0, true),
If("deadval", "deadblock", "exit"))) If("deadval", "deadblock", "exit")))
CheckFunc(fun.f) CheckFunc(fun.f)
@ -40,8 +40,8 @@ func TestDeadValue(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{}) c := NewConfig("amd64", DummyFrontend{})
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("deadval", OpConst, TypeInt64, int64(37)), Valu("deadval", OpConst, TypeInt64, 37, nil),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))) Exit("mem")))
@ -63,8 +63,8 @@ func TestNeverTaken(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{}) c := NewConfig("amd64", DummyFrontend{})
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("cond", OpConst, TypeBool, false), Valu("cond", OpConst, TypeBool, 0, false),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
If("cond", "then", "else")), If("cond", "then", "else")),
Bloc("then", Bloc("then",
Goto("exit")), Goto("exit")),

View file

@ -13,13 +13,13 @@ func TestDeadStore(t *testing.T) {
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, ".mem"), Valu("start", OpArg, TypeMem, 0, ".mem"),
Valu("v", OpConst, TypeBool, true), Valu("v", OpConst, TypeBool, 0, true),
Valu("addr1", OpGlobal, ptrType, nil), Valu("addr1", OpGlobal, ptrType, 0, nil),
Valu("addr2", OpGlobal, ptrType, nil), Valu("addr2", OpGlobal, ptrType, 0, nil),
Valu("store1", OpStore, TypeMem, nil, "addr1", "v", "start"), Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"),
Valu("store2", OpStore, TypeMem, nil, "addr2", "v", "store1"), Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"),
Valu("store3", OpStore, TypeMem, nil, "addr1", "v", "store2"), Valu("store3", OpStore, TypeMem, 0, nil, "addr1", "v", "store2"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("store3"))) Exit("store3")))
@ -39,13 +39,13 @@ func TestDeadStorePhi(t *testing.T) {
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, ".mem"), Valu("start", OpArg, TypeMem, 0, ".mem"),
Valu("v", OpConst, TypeBool, true), Valu("v", OpConst, TypeBool, 0, true),
Valu("addr", OpGlobal, ptrType, nil), Valu("addr", OpGlobal, ptrType, 0, nil),
Goto("loop")), Goto("loop")),
Bloc("loop", Bloc("loop",
Valu("phi", OpPhi, TypeMem, nil, "start", "store"), Valu("phi", OpPhi, TypeMem, 0, nil, "start", "store"),
Valu("store", OpStore, TypeMem, nil, "addr", "v", "phi"), Valu("store", OpStore, TypeMem, 0, nil, "addr", "v", "phi"),
If("v", "loop", "exit")), If("v", "loop", "exit")),
Bloc("exit", Bloc("exit",
Exit("store"))) Exit("store")))
@ -65,12 +65,12 @@ func TestDeadStoreTypes(t *testing.T) {
t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"} t2 := &TypeImpl{Size_: 4, Ptr: true, Name: "t2"}
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("start", OpArg, TypeMem, ".mem"), Valu("start", OpArg, TypeMem, 0, ".mem"),
Valu("v", OpConst, TypeBool, true), Valu("v", OpConst, TypeBool, 0, true),
Valu("addr1", OpGlobal, t1, nil), Valu("addr1", OpGlobal, t1, 0, nil),
Valu("addr2", OpGlobal, t2, nil), Valu("addr2", OpGlobal, t2, 0, nil),
Valu("store1", OpStore, TypeMem, nil, "addr1", "v", "start"), Valu("store1", OpStore, TypeMem, 0, nil, "addr1", "v", "start"),
Valu("store2", OpStore, TypeMem, nil, "addr2", "v", "store1"), Valu("store2", OpStore, TypeMem, 0, nil, "addr2", "v", "store1"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("store2"))) Exit("store2")))

View file

@ -4,6 +4,8 @@
package ssa package ssa
import "log"
// A Func represents a Go func declaration (or function literal) and // A Func represents a Go func declaration (or function literal) and
// its body. This package compiles each Func independently. // its body. This package compiles each Func independently.
type Func struct { type Func struct {
@ -42,8 +44,41 @@ func (f *Func) NewBlock(kind BlockKind) *Block {
return b return b
} }
// NewValue returns a new value in the block with no arguments. // NewValue0 returns a new value in the block with no arguments and zero aux values.
func (b *Block) NewValue(line int32, op Op, t Type, aux interface{}) *Value { func (b *Block) NewValue0(line int32, op Op, t Type) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue returns a new value in the block with no arguments and an auxint value.
func (b *Block) NewValue0I(line int32, op Op, t Type, auxint int64) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Block: b,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue returns a new value in the block with no arguments and an aux value.
func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value {
if _, ok := aux.(int64); ok {
// 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)
}
v := &Value{ v := &Value{
ID: b.Func.vid.get(), ID: b.Func.vid.get(),
Op: op, Op: op,
@ -56,8 +91,52 @@ func (b *Block) NewValue(line int32, op Op, t Type, aux interface{}) *Value {
return v return v
} }
// NewValue1 returns a new value in the block with one argument. // NewValue returns a new value in the block with no arguments and both an auxint and aux values.
func (b *Block) NewValue1(line int32, op Op, t Type, aux interface{}, arg *Value) *Value { func (b *Block) NewValue0IA(line int32, op Op, t Type, auxint int64, aux interface{}) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Aux: aux,
Block: b,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue1 returns a new value in the block with one argument and zero aux values.
func (b *Block) NewValue1(line int32, op Op, t Type, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue1I returns a new value in the block with one argument and an auxint value.
func (b *Block) NewValue1I(line int32, op Op, t Type, auxint int64, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Block: b,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue1A returns a new value in the block with one argument and an aux value.
func (b *Block) NewValue1A(line int32, op Op, t Type, aux interface{}, arg *Value) *Value {
v := &Value{ v := &Value{
ID: b.Func.vid.get(), ID: b.Func.vid.get(),
Op: op, Op: op,
@ -71,13 +150,28 @@ func (b *Block) NewValue1(line int32, op Op, t Type, aux interface{}, arg *Value
return v return v
} }
// NewValue2 returns a new value in the block with two arguments. // NewValue1IA returns a new value in the block with one argument and both an auxint and aux values.
func (b *Block) NewValue2(line int32, op Op, t Type, aux interface{}, arg0, arg1 *Value) *Value { func (b *Block) NewValue1IA(line int32, op Op, t Type, auxint int64, aux interface{}, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
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 and zero aux values.
func (b *Block) NewValue2(line int32, op Op, t Type, arg0, arg1 *Value) *Value {
v := &Value{ v := &Value{
ID: b.Func.vid.get(), ID: b.Func.vid.get(),
Op: op, Op: op,
Type: t, Type: t,
Aux: aux,
Block: b, Block: b,
} }
v.Args = v.argstorage[:2] v.Args = v.argstorage[:2]
@ -87,13 +181,12 @@ func (b *Block) NewValue2(line int32, op Op, t Type, aux interface{}, arg0, arg1
return v return v
} }
// NewValue3 returns a new value in the block with three arguments. // NewValue3 returns a new value in the block with three arguments and zero aux values.
func (b *Block) NewValue3(line int32, op Op, t Type, aux interface{}, arg0, arg1, arg2 *Value) *Value { func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value {
v := &Value{ v := &Value{
ID: b.Func.vid.get(), ID: b.Func.vid.get(),
Op: op, Op: op,
Type: t, Type: t,
Aux: aux,
Block: b, Block: b,
} }
v.Args = []*Value{arg0, arg1, arg2} v.Args = []*Value{arg0, arg1, arg2}
@ -104,5 +197,5 @@ func (b *Block) NewValue3(line int32, op Op, t Type, aux interface{}, arg0, arg1
// ConstInt returns an int constant representing its argument. // ConstInt returns an int constant representing its argument.
func (f *Func) ConstInt(line int32, t Type, c int64) *Value { func (f *Func) ConstInt(line int32, t Type, c int64) *Value {
// TODO: cache? // TODO: cache?
return f.Entry.NewValue(line, OpConst, t, c) return f.Entry.NewValue0I(line, OpConst, t, c)
} }

View file

@ -18,12 +18,12 @@
// //
// fun := Fun("entry", // fun := Fun("entry",
// Bloc("entry", // Bloc("entry",
// Valu("mem", OpArg, TypeMem, ".mem"), // Valu("mem", OpArg, TypeMem, 0, ".mem"),
// Goto("exit")), // Goto("exit")),
// Bloc("exit", // Bloc("exit",
// Exit("mem")), // Exit("mem")),
// Bloc("deadblock", // Bloc("deadblock",
// Valu("deadval", OpConst, TypeBool, true), // Valu("deadval", OpConst, TypeBool, 0, true),
// If("deadval", "deadblock", "exit"))) // If("deadval", "deadblock", "exit")))
// //
// and the Blocks or Values used in the Func can be accessed // and the Blocks or Values used in the Func can be accessed
@ -61,7 +61,7 @@ func Equiv(f, g *Func) bool {
// Ignore ids. Ops and Types are compared for equality. // Ignore ids. Ops and Types are compared for equality.
// TODO(matloob): Make sure types are canonical and can // TODO(matloob): Make sure types are canonical and can
// be compared for equality. // be compared for equality.
if fv.Op != gv.Op || fv.Type != gv.Type { if fv.Op != gv.Op || fv.Type != gv.Type || fv.AuxInt != gv.AuxInt {
return false return false
} }
if !reflect.DeepEqual(fv.Aux, gv.Aux) { if !reflect.DeepEqual(fv.Aux, gv.Aux) {
@ -149,7 +149,7 @@ func Fun(c *Config, entry string, blocs ...bloc) fun {
blocks[bloc.name] = b blocks[bloc.name] = b
for _, valu := range bloc.valus { for _, valu := range bloc.valus {
// args are filled in the second pass. // args are filled in the second pass.
values[valu.name] = b.NewValue(0, valu.op, valu.t, valu.aux) values[valu.name] = b.NewValue0IA(0, valu.op, valu.t, valu.auxint, valu.aux)
} }
} }
// Connect the blocks together and specify control values. // Connect the blocks together and specify control values.
@ -212,8 +212,8 @@ func Bloc(name string, entries ...interface{}) bloc {
} }
// Valu defines a value in a block. // Valu defines a value in a block.
func Valu(name string, op Op, t Type, aux interface{}, args ...string) valu { func Valu(name string, op Op, t Type, auxint int64, aux interface{}, args ...string) valu {
return valu{name, op, t, aux, args} return valu{name, op, t, auxint, aux, args}
} }
// Goto specifies that this is a BlockPlain and names the single successor. // Goto specifies that this is a BlockPlain and names the single successor.
@ -248,11 +248,12 @@ type ctrl struct {
} }
type valu struct { type valu struct {
name string name string
op Op op Op
t Type t Type
aux interface{} auxint int64
args []string aux interface{}
args []string
} }
func addEdge(b, c *Block) { func addEdge(b, c *Block) {
@ -264,10 +265,10 @@ func TestArgs(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{}) c := NewConfig("amd64", DummyFrontend{})
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))) Exit("mem")))
@ -287,19 +288,19 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
@ -308,10 +309,10 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
@ -319,10 +320,10 @@ func TestEquiv(t *testing.T) {
Bloc("exit", Bloc("exit",
Exit("mem")), Exit("mem")),
Bloc("entry", Bloc("entry",
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit"))), Goto("exit"))),
}, },
} }
@ -339,58 +340,71 @@ func TestEquiv(t *testing.T) {
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Exit("mem"))), Exit("mem"))),
}, },
// value order changed // value order changed
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Exit("mem"))),
},
// value auxint different
{
Fun(c, "entry",
Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 14, nil),
Exit("mem"))),
Fun(c, "entry",
Bloc("entry",
Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 26, nil),
Exit("mem"))), Exit("mem"))),
}, },
// value aux different // value aux different
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 0, 14),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 26), Valu("a", OpConst, TypeInt64, 0, 26),
Exit("mem"))), Exit("mem"))),
}, },
// value args different // value args different
{ {
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 14), Valu("a", OpConst, TypeInt64, 14, nil),
Valu("b", OpConst, TypeInt64, 26), Valu("b", OpConst, TypeInt64, 26, nil),
Valu("sum", OpAdd, TypeInt64, nil, "a", "b"), Valu("sum", OpAdd, TypeInt64, 0, nil, "a", "b"),
Exit("mem"))), Exit("mem"))),
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("a", OpConst, TypeInt64, 0), Valu("a", OpConst, TypeInt64, 0, nil),
Valu("b", OpConst, TypeInt64, 14), Valu("b", OpConst, TypeInt64, 14, nil),
Valu("sum", OpAdd, TypeInt64, nil, "b", "a"), Valu("sum", OpAdd, TypeInt64, 0, nil, "b", "a"),
Exit("mem"))), Exit("mem"))),
}, },
} }

View file

@ -23,30 +23,30 @@
// mask = shift >= 64 ? 0 : 0xffffffffffffffff // mask = shift >= 64 ? 0 : 0xffffffffffffffff
// result = mask & arg << shift // result = mask & arg << shift
(Lsh <t> x y) && is64BitInt(t) -> (Lsh <t> x y) && is64BitInt(t) ->
(ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [int64(64)] y))) (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
(Rsh <t> x y) && is64BitInt(t) && !t.IsSigned() -> (Rsh <t> x y) && is64BitInt(t) && !t.IsSigned() ->
(ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [int64(64)] y))) (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
// Note: signed right shift needs to return 0/-1 if shift amount is >= 64. // Note: signed right shift needs to return 0/-1 if shift amount is >= 64.
// if shift > 63 { shift = 63 } // if shift > 63 { shift = 63 }
// result = arg >> shift // result = arg >> shift
(Rsh <t> x y) && is64BitInt(t) && t.IsSigned() -> (Rsh <t> x y) && is64BitInt(t) && t.IsSigned() ->
(SARQ <t> x (CMOVQCC <t> (SARQ <t> x (CMOVQCC <t>
(CMPQconst <TypeFlags> [int64(64)] y) (CMPQconst <TypeFlags> [64] y)
(Const <t> [int64(63)]) (Const <t> [63])
y)) y))
(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ <TypeFlags> x y)) (Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ <TypeFlags> x y))
(Load <t> ptr mem) && t.IsBoolean() -> (MOVBload [int64(0)] ptr mem) (Load <t> ptr mem) && t.IsBoolean() -> (MOVBload ptr mem)
(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload [int64(0)] ptr mem) (Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem)
(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem) (Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore ptr val mem)
// checks // checks
(IsNonNil p) -> (SETNE (TESTQ <TypeFlags> p p)) (IsNonNil p) -> (SETNE (TESTQ <TypeFlags> p p))
(IsInBounds idx len) -> (SETB (CMPQ <TypeFlags> idx len)) (IsInBounds idx len) -> (SETB (CMPQ <TypeFlags> idx len))
(Move [size] dst src mem) -> (REPMOVSB dst src (Const <TypeUInt64> [size.(int64)]) mem) (Move [size] dst src mem) -> (REPMOVSB dst src (Const <TypeUInt64> [size]) mem)
(OffPtr [off] ptr) -> (ADDQconst [off] ptr) (OffPtr [off] ptr) -> (ADDQconst [off] ptr)
@ -65,14 +65,14 @@
// TODO: Should this be a separate pass? // TODO: Should this be a separate pass?
// global loads/stores // global loads/stores
(Global [sym]) -> (LEAQglobal [GlobalOffset{sym,0}]) (Global {sym}) -> (LEAQglobal {sym})
// fold constants into instructions // fold constants into instructions
(ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? (ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range?
(ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) (ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x)
(SUBQ x (MOVQconst [c])) -> (SUBQconst x [c]) (SUBQ x (MOVQconst [c])) -> (SUBQconst x [c])
(SUBQ <t> (MOVQconst [c]) x) -> (NEGQ (SUBQconst <t> x [c])) (SUBQ <t> (MOVQconst [c]) x) -> (NEGQ (SUBQconst <t> x [c]))
(MULQ x (MOVQconst [c])) && c.(int64) == int64(int32(c.(int64))) -> (MULQconst [c] x) (MULQ x (MOVQconst [c])) && c == int64(int32(c)) -> (MULQconst [c] x)
(MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) (MULQ (MOVQconst [c]) x) -> (MULQconst [c] x)
(ANDQ x (MOVQconst [c])) -> (ANDQconst [c] x) (ANDQ x (MOVQconst [c])) -> (ANDQconst [c] x)
(ANDQ (MOVQconst [c]) x) -> (ANDQconst [c] x) (ANDQ (MOVQconst [c]) x) -> (ANDQconst [c] x)
@ -84,11 +84,11 @@
// strength reduction // strength reduction
// TODO: do this a lot more generically // TODO: do this a lot more generically
(MULQconst [c] x) && c.(int64) == 8 -> (SHLQconst [int64(3)] x) (MULQconst [8] x) -> (SHLQconst [3] x)
(MULQconst [c] x) && c.(int64) == 64 -> (SHLQconst [int64(5)] x) (MULQconst [64] x) -> (SHLQconst [5] x)
// fold add/shift into leaq // fold add/shift into leaq
(ADDQ x (SHLQconst [shift] y)) && shift.(int64) == 3 -> (LEAQ8 [int64(0)] x y) (ADDQ x (SHLQconst [3] y)) -> (LEAQ8 x y)
(ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y) (ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y)
// reverse ordering of compare instruction // reverse ordering of compare instruction
@ -110,7 +110,7 @@
(MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) (MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem)
(MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) (MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem)
(ADDQconst [off] x) && off.(int64) == 0 -> (Copy x) (ADDQconst [0] x) -> (Copy x)
// Absorb InvertFlags into branches. // Absorb InvertFlags into branches.
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no) (LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
@ -125,9 +125,9 @@
(NE (InvertFlags cmp) yes no) -> (NE cmp yes no) (NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
// get rid of >=64 code for constant shifts // get rid of >=64 code for constant shifts
(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d.(int64), c.(int64)) -> (Const [int64(-1)]) (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d, c) -> (Const [-1])
(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d.(int64), c.(int64)) -> (Const [int64(0)]) (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d, c) -> (Const [0])
(ANDQconst [c] _) && c.(int64) == 0 -> (MOVQconst [int64(0)]) (ANDQconst [0] _) -> (MOVQconst [0])
(ANDQconst [c] x) && c.(int64) == -1 -> (Copy x) (ANDQconst [-1] x) -> (Copy x)
(CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d.(int64), c.(int64)) -> (Copy x) (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d, c) -> (Copy x)
(CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d.(int64), c.(int64)) -> (Copy x) (CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d, c) -> (Copy x)

View file

@ -93,24 +93,24 @@ func init() {
// TODO: 2-address instructions. Mark ops as needing matching input/output regs. // TODO: 2-address instructions. Mark ops as needing matching input/output regs.
var AMD64ops = []opData{ var AMD64ops = []opData{
{name: "ADDQ", reg: gp21}, // arg0 + arg1 {name: "ADDQ", reg: gp21}, // arg0 + arg1
{name: "ADDQconst", reg: gp11}, // arg0 + aux.(int64) {name: "ADDQconst", reg: gp11}, // arg0 + auxint
{name: "SUBQ", reg: gp21}, // arg0 - arg1 {name: "SUBQ", reg: gp21}, // arg0 - arg1
{name: "SUBQconst", reg: gp11}, // arg0 - aux.(int64) {name: "SUBQconst", reg: gp11}, // arg0 - auxint
{name: "MULQ", reg: gp21}, // arg0 * arg1 {name: "MULQ", reg: gp21}, // arg0 * arg1
{name: "MULQconst", reg: gp11}, // arg0 * aux.(int64) {name: "MULQconst", reg: gp11}, // arg0 * auxint
{name: "ANDQ", reg: gp21}, // arg0 & arg1 {name: "ANDQ", reg: gp21}, // arg0 & arg1
{name: "ANDQconst", reg: gp11}, // arg0 & aux.(int64) {name: "ANDQconst", reg: gp11}, // arg0 & auxint
{name: "SHLQ", reg: gp21shift}, // arg0 << arg1, shift amount is mod 64 {name: "SHLQ", reg: gp21shift}, // arg0 << arg1, shift amount is mod 64
{name: "SHLQconst", reg: gp11}, // arg0 << aux.(int64), shift amount 0-63 {name: "SHLQconst", reg: gp11}, // arg0 << auxint, shift amount 0-63
{name: "SHRQ", reg: gp21shift}, // unsigned arg0 >> arg1, shift amount is mod 64 {name: "SHRQ", reg: gp21shift}, // unsigned arg0 >> arg1, shift amount is mod 64
{name: "SHRQconst", reg: gp11}, // unsigned arg0 >> aux.(int64), shift amount 0-63 {name: "SHRQconst", reg: gp11}, // unsigned arg0 >> auxint, shift amount 0-63
{name: "SARQ", reg: gp21shift}, // signed arg0 >> arg1, shift amount is mod 64 {name: "SARQ", reg: gp21shift}, // signed arg0 >> arg1, shift amount is mod 64
{name: "SARQconst", reg: gp11}, // signed arg0 >> aux.(int64), shift amount 0-63 {name: "SARQconst", reg: gp11}, // signed arg0 >> auxint, shift amount 0-63
{name: "NEGQ", reg: gp11}, // -arg0 {name: "NEGQ", reg: gp11}, // -arg0
{name: "CMPQ", reg: gp2flags}, // arg0 compare to arg1 {name: "CMPQ", reg: gp2flags}, // arg0 compare to arg1
{name: "CMPQconst", reg: gp1flags}, // arg0 compare to aux.(int64) {name: "CMPQconst", reg: gp1flags}, // arg0 compare to auxint
{name: "TESTQ", reg: gp2flags}, // (arg0 & arg1) compare to 0 {name: "TESTQ", reg: gp2flags}, // (arg0 & arg1) compare to 0
{name: "TESTB", reg: gp2flags}, // (arg0 & arg1) compare to 0 {name: "TESTB", reg: gp2flags}, // (arg0 & arg1) compare to 0
@ -125,21 +125,21 @@ func init() {
{name: "CMOVQCC", reg: cmov}, // carry clear {name: "CMOVQCC", reg: cmov}, // carry clear
{name: "MOVQconst", reg: gp01}, // aux.(int64) {name: "MOVQconst", reg: gp01}, // auxint
{name: "LEAQ", reg: gp21}, // arg0 + arg1 + aux.(int64) {name: "LEAQ", reg: gp21}, // arg0 + arg1 + auxint
{name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + aux.(int64) {name: "LEAQ2", reg: gp21}, // arg0 + 2*arg1 + auxint
{name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + aux.(int64) {name: "LEAQ4", reg: gp21}, // arg0 + 4*arg1 + auxint
{name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + aux.(int64) {name: "LEAQ8", reg: gp21}, // arg0 + 8*arg1 + auxint
{name: "LEAQglobal", reg: gp01}, // no args. address of aux.(GlobalOffset) {name: "LEAQglobal", reg: gp01}, // no args. address of aux.(*gc.Sym)
{name: "MOVBload", reg: gpload}, // load byte from arg0+aux.(int64). arg1=mem {name: "MOVBload", reg: gpload}, // load byte from arg0+auxint. arg1=mem
{name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64 {name: "MOVBQZXload", reg: gpload}, // ditto, extend to uint64
{name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64 {name: "MOVBQSXload", reg: gpload}, // ditto, extend to int64
{name: "MOVQload", reg: gpload}, // load 8 bytes from arg0+aux.(int64). arg1=mem {name: "MOVQload", reg: gpload}, // load 8 bytes from arg0+auxint. arg1=mem
{name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+aux.(int64). arg2=mem {name: "MOVQloadidx8", reg: gploadidx}, // load 8 bytes from arg0+8*arg1+auxint. arg2=mem
{name: "MOVBstore", reg: gpstore}, // store byte in arg1 to arg0+aux.(int64). arg2=mem {name: "MOVBstore", reg: gpstore}, // store byte in arg1 to arg0+auxint. arg2=mem
{name: "MOVQstore", reg: gpstore}, // store 8 bytes in arg1 to arg0+aux.(int64). arg2=mem {name: "MOVQstore", reg: gpstore}, // store 8 bytes in arg1 to arg0+auxint. arg2=mem
{name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+aux.(int64). arg3=mem {name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+auxint. arg3=mem
// Load/store from global. Same as the above loads, but arg0 is missing and // Load/store from global. Same as the above loads, but arg0 is missing and
// aux is a GlobalOffset instead of an int64. // aux is a GlobalOffset instead of an int64.
@ -147,7 +147,7 @@ func init() {
{name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns memory. {name: "MOVQstoreglobal"}, // store arg0 to aux.(GlobalOffset). arg1=memory, returns memory.
//TODO: set register clobber to everything? //TODO: set register clobber to everything?
{name: "CALLstatic"}, // call static function. arg0=mem, returns mem {name: "CALLstatic"}, // call static function aux.(*gc.Sym). arg0=mem, returns mem
{name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, 0, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem returns mem {name: "CALLclosure", reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, 0, nil}}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem returns mem
{name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory {name: "REPMOVSB", reg: regInfo{[]regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")}, buildReg("DI SI CX"), nil}}, // move arg2 bytes from arg1 to arg0. arg3=mem, returns memory

View file

@ -3,13 +3,14 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// values are specified using the following format: // values are specified using the following format:
// (op <type> [aux] arg0 arg1 ...) // (op <type> [auxint] {aux} arg0 arg1 ...)
// the type and aux fields are optional // the type and aux fields are optional
// on the matching side // on the matching side
// - the types and aux fields must match if they are specified. // - the type, aux, and auxint fields must match if they are specified.
// on the generated side // on the generated side
// - the type of the top-level expression is the same as the one on the left-hand side. // - the type of the top-level expression is the same as the one on the left-hand side.
// - the type of any subexpressions must be specified explicitly. // - the type of any subexpressions must be specified explicitly.
// - auxint will be 0 if not specified.
// - aux will be nil if not specified. // - aux will be nil if not specified.
// blocks are specified using the following format: // blocks are specified using the following format:
@ -19,15 +20,15 @@
// For now, the generated successors must be a permutation of the matched successors. // For now, the generated successors must be a permutation of the matched successors.
// constant folding // constant folding
(Add <t> (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)+d.(int64)}]) (Add <t> (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c+d])
(Mul <t> (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [{c.(int64)*d.(int64)}]) (Mul <t> (Const [c]) (Const [d])) && is64BitInt(t) -> (Const [c*d])
(IsInBounds (Const [c]) (Const [d])) -> (Const [inBounds(c.(int64),d.(int64))]) (IsInBounds (Const [c]) (Const [d])) -> (Const {inBounds(c,d)})
// tear apart slices // tear apart slices
// TODO: anything that generates a slice needs to go in here. // TODO: anything that generates a slice needs to go in here.
(SlicePtr (Load ptr mem)) -> (Load ptr mem) (SlicePtr (Load ptr mem)) -> (Load ptr mem)
(SliceLen (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize)])) mem) (SliceLen (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [config.ptrSize])) mem)
(SliceCap (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize*2)])) mem) (SliceCap (Load ptr mem)) -> (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [config.ptrSize*2])) mem)
// indexing operations // indexing operations
// Note: bounds check has already been done // Note: bounds check has already been done
@ -39,11 +40,11 @@
(Store dst (Load <t> src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem) (Store dst (Load <t> src mem) mem) && t.Size() > 8 -> (Move [t.Size()] dst src mem)
// string ops // string ops
(Const <t> [s]) && t.IsString() -> (StringMake (OffPtr <TypeBytePtr> [2*config.ptrSize] (Global <TypeBytePtr> [config.fe.StringSym(s.(string))])) (Const <config.Uintptr> [int64(len(s.(string)))])) // TODO: ptr (Const <t> {s}) && t.IsString() -> (StringMake (OffPtr <TypeBytePtr> [2*config.ptrSize] (Global <TypeBytePtr> {config.fe.StringSym(s.(string))})) (Const <config.Uintptr> [int64(len(s.(string)))])) // TODO: ptr
(Load <t> ptr mem) && t.IsString() -> (StringMake (Load <TypeBytePtr> ptr mem) (Load <config.Uintptr> (OffPtr <TypeBytePtr> [config.ptrSize] ptr) mem)) (Load <t> ptr mem) && t.IsString() -> (StringMake (Load <TypeBytePtr> ptr mem) (Load <config.Uintptr> (OffPtr <TypeBytePtr> [config.ptrSize] ptr) mem))
(StringPtr (StringMake ptr _)) -> ptr (StringPtr (StringMake ptr _)) -> ptr
(StringLen (StringMake _ len)) -> len (StringLen (StringMake _ len)) -> len
(Store dst str mem) && str.Type.IsString() -> (Store (OffPtr <TypeBytePtr> [config.ptrSize] dst) (StringLen <config.Uintptr> str) (Store <TypeMem> dst (StringPtr <TypeBytePtr> str) mem)) (Store dst str mem) && str.Type.IsString() -> (Store (OffPtr <TypeBytePtr> [config.ptrSize] dst) (StringLen <config.Uintptr> str) (Store <TypeMem> dst (StringPtr <TypeBytePtr> str) mem))
(If (Const [c]) yes no) && c.(bool) -> (Plain nil yes) (If (Const {c}) yes no) && c.(bool) -> (Plain nil yes)
(If (Const [c]) yes no) && !c.(bool) -> (Plain nil no) (If (Const {c}) yes no) && !c.(bool) -> (Plain nil no)

View file

@ -37,9 +37,9 @@ var genericOps = []opData{
{name: "Func"}, // entry address of a function {name: "Func"}, // entry address of a function
// Memory operations // Memory operations
{name: "Load"}, // Load from arg0+aux.(int64). arg1=memory {name: "Load"}, // Load from arg0. arg1=memory
{name: "Store"}, // Store arg1 to arg0+aux.(int64). arg2=memory. Returns memory. {name: "Store"}, // Store arg1 to arg0. arg2=memory. Returns memory.
{name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, aux.(int64)=size. Returns memory. {name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size. Returns memory.
// Function calls. Arguments to the call have already been written to the stack. // Function calls. Arguments to the call have already been written to the stack.
// Return values appear on the stack. The method receiver, if any, is treated // Return values appear on the stack. The method receiver, if any, is treated
@ -58,7 +58,7 @@ var genericOps = []opData{
// Indexing operations // Indexing operations
{name: "ArrayIndex"}, // arg0=array, arg1=index. Returns a[i] {name: "ArrayIndex"}, // arg0=array, arg1=index. Returns a[i]
{name: "PtrIndex"}, // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type {name: "PtrIndex"}, // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
{name: "OffPtr"}, // arg0 + aux.(int64) (arg0 and result are pointers) {name: "OffPtr"}, // arg0 + auxint (arg0 and result are pointers)
// Slices // Slices
{name: "SliceMake"}, // arg0=ptr, arg1=len, arg2=cap {name: "SliceMake"}, // arg0=ptr, arg1=len, arg2=cap

View file

@ -19,6 +19,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"regexp"
"sort" "sort"
"strings" "strings"
) )
@ -29,9 +30,9 @@ import (
// sexpr are s-expressions (lisp-like parenthesized groupings) // sexpr are s-expressions (lisp-like parenthesized groupings)
// sexpr ::= (opcode sexpr*) // sexpr ::= (opcode sexpr*)
// | variable // | variable
// | [aux]
// | <type> // | <type>
// | {code} // | [auxint]
// | {aux}
// //
// aux ::= variable | {code} // aux ::= variable | {code}
// type ::= variable | {code} // type ::= variable | {code}
@ -310,9 +311,9 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin
if a[0] == '<' { if a[0] == '<' {
// type restriction // type restriction
t := a[1 : len(a)-1] // remove <> t := a[1 : len(a)-1] // remove <>
if t[0] == '{' { if !isVariable(t) {
// code. We must match the results of this code. // code. We must match the results of this code.
fmt.Fprintf(w, "if %s.Type != %s %s", v, t[1:len(t)-1], fail) fmt.Fprintf(w, "if %s.Type != %s %s", v, t, fail)
} else { } else {
// variable // variable
if u, ok := m[t]; ok { if u, ok := m[t]; ok {
@ -324,11 +325,26 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin
} }
} }
} else if a[0] == '[' { } else if a[0] == '[' {
// aux restriction // auxint restriction
x := a[1 : len(a)-1] // remove [] x := a[1 : len(a)-1] // remove []
if x[0] == '{' { if !isVariable(x) {
// code // code
fmt.Fprintf(w, "if %s.Aux != %s %s", v, x[1:len(x)-1], fail) fmt.Fprintf(w, "if %s.AuxInt != %s %s", v, x, fail)
} else {
// variable
if y, ok := m[x]; ok {
fmt.Fprintf(w, "if %s.AuxInt != %s %s", v, y, fail)
} else {
m[x] = v + ".AuxInt"
fmt.Fprintf(w, "%s := %s.AuxInt\n", x, v)
}
}
} else if a[0] == '{' {
// auxint restriction
x := a[1 : len(a)-1] // remove {}
if !isVariable(x) {
// code
fmt.Fprintf(w, "if %s.Aux != %s %s", v, x, fail)
} else { } else {
// variable // variable
if y, ok := m[x]; ok { if y, ok := m[x]; ok {
@ -338,9 +354,6 @@ func genMatch0(w io.Writer, arch arch, match, v, fail string, m map[string]strin
fmt.Fprintf(w, "%s := %s.Aux\n", x, v) fmt.Fprintf(w, "%s := %s.Aux\n", x, v)
} }
} }
} else if a[0] == '{' {
fmt.Fprintf(w, "if %s.Args[%d] != %s %s", v, argnum, a[1:len(a)-1], fail)
argnum++
} else { } else {
// variable or sexpr // variable or sexpr
genMatch0(w, arch, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false) genMatch0(w, arch, a, fmt.Sprintf("%s.Args[%d]", v, argnum), fail, m, false)
@ -357,6 +370,7 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top bool) str
// variable // variable
if top { if top {
fmt.Fprintf(w, "v.Op = %s.Op\n", result) fmt.Fprintf(w, "v.Op = %s.Op\n", result)
fmt.Fprintf(w, "v.AuxInt = %s.AuxInt\n", result)
fmt.Fprintf(w, "v.Aux = %s.Aux\n", result) fmt.Fprintf(w, "v.Aux = %s.Aux\n", result)
fmt.Fprintf(w, "v.resetArgs()\n") fmt.Fprintf(w, "v.resetArgs()\n")
fmt.Fprintf(w, "v.AddArgs(%s.Args...)\n", result) fmt.Fprintf(w, "v.AddArgs(%s.Args...)\n", result)
@ -370,32 +384,29 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top bool) str
if top { if top {
v = "v" v = "v"
fmt.Fprintf(w, "v.Op = %s\n", opName(s[0], arch)) fmt.Fprintf(w, "v.Op = %s\n", opName(s[0], arch))
fmt.Fprintf(w, "v.AuxInt = 0\n")
fmt.Fprintf(w, "v.Aux = nil\n") fmt.Fprintf(w, "v.Aux = nil\n")
fmt.Fprintf(w, "v.resetArgs()\n") fmt.Fprintf(w, "v.resetArgs()\n")
hasType = true hasType = true
} else { } else {
v = fmt.Sprintf("v%d", *alloc) v = fmt.Sprintf("v%d", *alloc)
*alloc++ *alloc++
fmt.Fprintf(w, "%s := v.Block.NewValue(v.Line, %s, TypeInvalid, nil)\n", v, opName(s[0], arch)) fmt.Fprintf(w, "%s := v.Block.NewValue0(v.Line, %s, TypeInvalid)\n", v, opName(s[0], arch))
} }
for _, a := range s[1:] { for _, a := range s[1:] {
if a[0] == '<' { if a[0] == '<' {
// type restriction // type restriction
t := a[1 : len(a)-1] // remove <> t := a[1 : len(a)-1] // remove <>
if t[0] == '{' {
t = t[1 : len(t)-1] // remove {}
}
fmt.Fprintf(w, "%s.Type = %s\n", v, t) fmt.Fprintf(w, "%s.Type = %s\n", v, t)
hasType = true hasType = true
} else if a[0] == '[' { } else if a[0] == '[' {
// aux restriction // auxint restriction
x := a[1 : len(a)-1] // remove [] x := a[1 : len(a)-1] // remove []
if x[0] == '{' { fmt.Fprintf(w, "%s.AuxInt = %s\n", v, x)
x = x[1 : len(x)-1] // remove {}
}
fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
} else if a[0] == '{' { } else if a[0] == '{' {
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, a[1:len(a)-1]) // aux restriction
x := a[1 : len(a)-1] // remove {}
fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
} else { } else {
// regular argument (sexpr or variable) // regular argument (sexpr or variable)
x := genResult0(w, arch, a, alloc, false) x := genResult0(w, arch, a, alloc, false)
@ -504,3 +515,12 @@ func unbalanced(s string) bool {
} }
return left != right return left != right
} }
// isVariable reports whether s is a single Go alphanumeric identifier.
func isVariable(s string) bool {
b, err := regexp.MatchString("[A-Za-z_][A-Za-z_0-9]*", s)
if err != nil {
panic("bad variable regexp")
}
return b
}

View file

@ -1,424 +0,0 @@
// autogenerated from rulegen/generic.rules: do not edit!
// generated with: go run rulegen/rulegen.go rulegen/generic.rules genericBlockRules genericValueRules generic.go
package ssa
func genericValueRules(v *Value, config *Config) bool {
switch v.Op {
case OpAdd:
// match: (Add <t> (Const [c]) (Const [d]))
// cond: is64BitInt(t)
// result: (Const [{c.(int64)+d.(int64)}])
{
t := v.Type
if v.Args[0].Op != OpConst {
goto end8d047ed0ae9537b840adc79ea82c6e05
}
c := v.Args[0].Aux
if v.Args[1].Op != OpConst {
goto end8d047ed0ae9537b840adc79ea82c6e05
}
d := v.Args[1].Aux
if !(is64BitInt(t)) {
goto end8d047ed0ae9537b840adc79ea82c6e05
}
v.Op = OpConst
v.Aux = nil
v.resetArgs()
v.Aux = c.(int64) + d.(int64)
return true
}
goto end8d047ed0ae9537b840adc79ea82c6e05
end8d047ed0ae9537b840adc79ea82c6e05:
;
case OpArrayIndex:
// match: (ArrayIndex (Load ptr mem) idx)
// cond:
// result: (Load (PtrIndex <ptr.Type.Elem().Elem().PtrTo()> ptr idx) mem)
{
if v.Args[0].Op != OpLoad {
goto end3809f4c52270a76313e4ea26e6f0b753
}
ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1]
idx := v.Args[1]
v.Op = OpLoad
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpPtrIndex, TypeInvalid, nil)
v0.Type = ptr.Type.Elem().Elem().PtrTo()
v0.AddArg(ptr)
v0.AddArg(idx)
v.AddArg(v0)
v.AddArg(mem)
return true
}
goto end3809f4c52270a76313e4ea26e6f0b753
end3809f4c52270a76313e4ea26e6f0b753:
;
case OpConst:
// match: (Const <t> [s])
// cond: t.IsString()
// result: (StringMake (OffPtr <TypeBytePtr> [2*config.ptrSize] (Global <TypeBytePtr> [config.fe.StringSym(s.(string))])) (Const <config.Uintptr> [int64(len(s.(string)))]))
{
t := v.Type
s := v.Aux
if !(t.IsString()) {
goto end8442aa5b3f4e5b840055475883110372
}
v.Op = OpStringMake
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil)
v0.Type = TypeBytePtr
v0.Aux = 2 * config.ptrSize
v1 := v.Block.NewValue(v.Line, OpGlobal, TypeInvalid, nil)
v1.Type = TypeBytePtr
v1.Aux = config.fe.StringSym(s.(string))
v0.AddArg(v1)
v.AddArg(v0)
v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil)
v2.Type = config.Uintptr
v2.Aux = int64(len(s.(string)))
v.AddArg(v2)
return true
}
goto end8442aa5b3f4e5b840055475883110372
end8442aa5b3f4e5b840055475883110372:
;
case OpIsInBounds:
// match: (IsInBounds (Const [c]) (Const [d]))
// cond:
// result: (Const [inBounds(c.(int64),d.(int64))])
{
if v.Args[0].Op != OpConst {
goto enddbd1a394d9b71ee64335361b8384865c
}
c := v.Args[0].Aux
if v.Args[1].Op != OpConst {
goto enddbd1a394d9b71ee64335361b8384865c
}
d := v.Args[1].Aux
v.Op = OpConst
v.Aux = nil
v.resetArgs()
v.Aux = inBounds(c.(int64), d.(int64))
return true
}
goto enddbd1a394d9b71ee64335361b8384865c
enddbd1a394d9b71ee64335361b8384865c:
;
case OpLoad:
// match: (Load <t> ptr mem)
// cond: t.IsString()
// result: (StringMake (Load <TypeBytePtr> ptr mem) (Load <config.Uintptr> (OffPtr <TypeBytePtr> [config.ptrSize] ptr) mem))
{
t := v.Type
ptr := v.Args[0]
mem := v.Args[1]
if !(t.IsString()) {
goto endd0afd003b70d726a1c5bbaf51fe06182
}
v.Op = OpStringMake
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil)
v0.Type = TypeBytePtr
v0.AddArg(ptr)
v0.AddArg(mem)
v.AddArg(v0)
v1 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil)
v1.Type = config.Uintptr
v2 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil)
v2.Type = TypeBytePtr
v2.Aux = config.ptrSize
v2.AddArg(ptr)
v1.AddArg(v2)
v1.AddArg(mem)
v.AddArg(v1)
return true
}
goto endd0afd003b70d726a1c5bbaf51fe06182
endd0afd003b70d726a1c5bbaf51fe06182:
;
case OpMul:
// match: (Mul <t> (Const [c]) (Const [d]))
// cond: is64BitInt(t)
// result: (Const [{c.(int64)*d.(int64)}])
{
t := v.Type
if v.Args[0].Op != OpConst {
goto end776610f88cf04f438242d76ed2b14f1c
}
c := v.Args[0].Aux
if v.Args[1].Op != OpConst {
goto end776610f88cf04f438242d76ed2b14f1c
}
d := v.Args[1].Aux
if !(is64BitInt(t)) {
goto end776610f88cf04f438242d76ed2b14f1c
}
v.Op = OpConst
v.Aux = nil
v.resetArgs()
v.Aux = c.(int64) * d.(int64)
return true
}
goto end776610f88cf04f438242d76ed2b14f1c
end776610f88cf04f438242d76ed2b14f1c:
;
case OpPtrIndex:
// match: (PtrIndex <t> ptr idx)
// cond:
// result: (Add ptr (Mul <config.Uintptr> idx (Const <config.Uintptr> [t.Elem().Size()])))
{
t := v.Type
ptr := v.Args[0]
idx := v.Args[1]
v.Op = OpAdd
v.Aux = nil
v.resetArgs()
v.AddArg(ptr)
v0 := v.Block.NewValue(v.Line, OpMul, TypeInvalid, nil)
v0.Type = config.Uintptr
v0.AddArg(idx)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil)
v1.Type = config.Uintptr
v1.Aux = t.Elem().Size()
v0.AddArg(v1)
v.AddArg(v0)
return true
}
goto end88c7c383675420d1581daeb899039fa8
end88c7c383675420d1581daeb899039fa8:
;
case OpSliceCap:
// match: (SliceCap (Load ptr mem))
// cond:
// result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize*2)])) mem)
{
if v.Args[0].Op != OpLoad {
goto endc871dcd9a720b4290c9cae78fe147c8a
}
ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1]
v.Op = OpLoad
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil)
v0.Type = ptr.Type
v0.AddArg(ptr)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil)
v1.Type = config.Uintptr
v1.Aux = int64(config.ptrSize * 2)
v0.AddArg(v1)
v.AddArg(v0)
v.AddArg(mem)
return true
}
goto endc871dcd9a720b4290c9cae78fe147c8a
endc871dcd9a720b4290c9cae78fe147c8a:
;
case OpSliceLen:
// match: (SliceLen (Load ptr mem))
// cond:
// result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize)])) mem)
{
if v.Args[0].Op != OpLoad {
goto end1eec05e44f5fc8944e7c176f98a74d92
}
ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1]
v.Op = OpLoad
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil)
v0.Type = ptr.Type
v0.AddArg(ptr)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil)
v1.Type = config.Uintptr
v1.Aux = int64(config.ptrSize)
v0.AddArg(v1)
v.AddArg(v0)
v.AddArg(mem)
return true
}
goto end1eec05e44f5fc8944e7c176f98a74d92
end1eec05e44f5fc8944e7c176f98a74d92:
;
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:
;
case OpStore:
// match: (Store dst (Load <t> src mem) mem)
// cond: t.Size() > 8
// result: (Move [t.Size()] dst src mem)
{
dst := v.Args[0]
if v.Args[1].Op != OpLoad {
goto end324ffb6d2771808da4267f62c854e9c8
}
t := v.Args[1].Type
src := v.Args[1].Args[0]
mem := v.Args[1].Args[1]
if v.Args[2] != v.Args[1].Args[1] {
goto end324ffb6d2771808da4267f62c854e9c8
}
if !(t.Size() > 8) {
goto end324ffb6d2771808da4267f62c854e9c8
}
v.Op = OpMove
v.Aux = nil
v.resetArgs()
v.Aux = t.Size()
v.AddArg(dst)
v.AddArg(src)
v.AddArg(mem)
return true
}
goto end324ffb6d2771808da4267f62c854e9c8
end324ffb6d2771808da4267f62c854e9c8:
;
// match: (Store dst str mem)
// cond: str.Type.IsString()
// result: (Store (OffPtr <TypeBytePtr> [config.ptrSize] dst) (StringLen <config.Uintptr> str) (Store <TypeMem> dst (StringPtr <TypeBytePtr> str) mem))
{
dst := v.Args[0]
str := v.Args[1]
mem := v.Args[2]
if !(str.Type.IsString()) {
goto end410559d97aed8018f820cd88723de442
}
v.Op = OpStore
v.Aux = nil
v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil)
v0.Type = TypeBytePtr
v0.Aux = config.ptrSize
v0.AddArg(dst)
v.AddArg(v0)
v1 := v.Block.NewValue(v.Line, OpStringLen, TypeInvalid, nil)
v1.Type = config.Uintptr
v1.AddArg(str)
v.AddArg(v1)
v2 := v.Block.NewValue(v.Line, OpStore, TypeInvalid, nil)
v2.Type = TypeMem
v2.AddArg(dst)
v3 := v.Block.NewValue(v.Line, OpStringPtr, TypeInvalid, nil)
v3.Type = TypeBytePtr
v3.AddArg(str)
v2.AddArg(v3)
v2.AddArg(mem)
v.AddArg(v2)
return true
}
goto end410559d97aed8018f820cd88723de442
end410559d97aed8018f820cd88723de442:
;
case OpStringLen:
// match: (StringLen (StringMake _ len))
// cond:
// result: len
{
if v.Args[0].Op != OpStringMake {
goto end0d922460b7e5ca88324034f4bd6c027c
}
len := v.Args[0].Args[1]
v.Op = len.Op
v.Aux = len.Aux
v.resetArgs()
v.AddArgs(len.Args...)
return true
}
goto end0d922460b7e5ca88324034f4bd6c027c
end0d922460b7e5ca88324034f4bd6c027c:
;
case OpStringPtr:
// match: (StringPtr (StringMake ptr _))
// cond:
// result: ptr
{
if v.Args[0].Op != OpStringMake {
goto end061edc5d85c73ad909089af2556d9380
}
ptr := v.Args[0].Args[0]
v.Op = ptr.Op
v.Aux = ptr.Aux
v.resetArgs()
v.AddArgs(ptr.Args...)
return true
}
goto end061edc5d85c73ad909089af2556d9380
end061edc5d85c73ad909089af2556d9380:
}
return false
}
func genericBlockRules(b *Block) bool {
switch b.Kind {
case BlockIf:
// match: (BlockIf (Const [c]) yes no)
// cond: c.(bool)
// result: (BlockPlain nil yes)
{
v := b.Control
if v.Op != OpConst {
goto endbe39807508a6192b4022c7293eb6e114
}
c := v.Aux
yes := b.Succs[0]
no := b.Succs[1]
if !(c.(bool)) {
goto endbe39807508a6192b4022c7293eb6e114
}
removePredecessor(b, no)
b.Kind = BlockPlain
b.Control = nil
b.Succs = b.Succs[:1]
b.Succs[0] = yes
return true
}
goto endbe39807508a6192b4022c7293eb6e114
endbe39807508a6192b4022c7293eb6e114:
;
// match: (BlockIf (Const [c]) yes no)
// cond: !c.(bool)
// result: (BlockPlain nil no)
{
v := b.Control
if v.Op != OpConst {
goto end69ac35957ebe0a77a5ef5103c1f79fbf
}
c := v.Aux
yes := b.Succs[0]
no := b.Succs[1]
if !(!c.(bool)) {
goto end69ac35957ebe0a77a5ef5103c1f79fbf
}
removePredecessor(b, yes)
b.Kind = BlockPlain
b.Control = nil
b.Succs = b.Succs[:1]
b.Succs[0] = no
return true
}
goto end69ac35957ebe0a77a5ef5103c1f79fbf
end69ac35957ebe0a77a5ef5103c1f79fbf:
}
return false
}

View file

@ -4,11 +4,6 @@
package ssa package ssa
import (
"fmt"
"log"
)
// An Op encodes the specific operation that a Value performs. // An Op encodes the specific operation that a Value performs.
// Opcodes' semantics can be modified by the type and aux fields of the Value. // Opcodes' semantics can be modified by the type and aux fields of the Value.
// For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type. // For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type.
@ -17,26 +12,6 @@ import (
// for each architecture. // for each architecture.
type Op int32 type Op int32
// GlobalOffset represents a fixed offset within a global variable
type GlobalOffset struct {
Global interface{} // holds a *gc.Sym
Offset int64
}
// offset adds x to the location specified by g and returns it.
func (g GlobalOffset) offset(x int64) GlobalOffset {
y := g.Offset
z := x + y
if x^y >= 0 && x^z < 0 {
log.Panicf("offset overflow %d %d\n", x, y)
}
return GlobalOffset{g.Global, z}
}
func (g GlobalOffset) String() string {
return fmt.Sprintf("%v+%d", g.Global, g.Offset)
}
type opInfo struct { type opInfo struct {
name string name string
reg regInfo reg regInfo

View file

@ -262,25 +262,23 @@ func regalloc(f *Func) {
if len(w.Args) == 0 { if len(w.Args) == 0 {
// Materialize w // Materialize w
if w.Op == OpFP || w.Op == OpSP || w.Op == OpGlobal { if w.Op == OpFP || w.Op == OpSP || w.Op == OpGlobal {
c = b.NewValue1(w.Line, OpCopy, w.Type, nil, w) c = b.NewValue1(w.Line, OpCopy, w.Type, w)
} else { } else {
c = b.NewValue(w.Line, w.Op, w.Type, w.Aux) c = b.NewValue0IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux)
} }
} else if len(w.Args) == 1 && (w.Args[0].Op == OpFP || w.Args[0].Op == OpSP || w.Args[0].Op == OpGlobal) { } else if len(w.Args) == 1 && (w.Args[0].Op == OpFP || w.Args[0].Op == OpSP || w.Args[0].Op == OpGlobal) {
// Materialize offsets from SP/FP/Global // Materialize offsets from SP/FP/Global
c = b.NewValue1(w.Line, w.Op, w.Type, w.Aux, w.Args[0]) c = b.NewValue1IA(w.Line, w.Op, w.Type, w.AuxInt, w.Aux, w.Args[0])
} else if wreg != 0 { } else if wreg != 0 {
// Copy from another register. // Copy from another register.
// Typically just an optimization, but this is // Typically just an optimization, but this is
// required if w is dirty. // required if w is dirty.
s := pickReg(wreg) s := pickReg(wreg)
// inv: s != r // inv: s != r
c = b.NewValue(w.Line, OpCopy, w.Type, nil) c = b.NewValue1(w.Line, OpCopy, w.Type, regs[s].c)
c.AddArg(regs[s].c)
} else { } else {
// Load from home location // Load from home location
c = b.NewValue(w.Line, OpLoadReg8, w.Type, nil) c = b.NewValue1(w.Line, OpLoadReg8, w.Type, w)
c.AddArg(w)
} }
home = setloc(home, c, &registers[r]) home = setloc(home, c, &registers[r])
// Remember what we did // Remember what we did
@ -337,7 +335,7 @@ func regalloc(f *Func) {
} }
// Reissue v with new op, with r as its home. // Reissue v with new op, with r as its home.
c := b.NewValue(v.Line, v.Op, v.Type, v.Aux) c := b.NewValue0IA(v.Line, v.Op, v.Type, v.AuxInt, v.Aux)
c.AddArgs(v.Args...) c.AddArgs(v.Args...)
home = setloc(home, c, &registers[r]) home = setloc(home, c, &registers[r])
@ -406,7 +404,7 @@ func addPhiCopies(f *Func) {
} }
for i, w := range v.Args { for i, w := range v.Args {
c := b.Preds[i] c := b.Preds[i]
cpy := c.NewValue1(w.Line, OpCopy, v.Type, nil, w) cpy := c.NewValue1(w.Line, OpCopy, v.Type, w)
v.Args[i] = cpy v.Args[i] = cpy
} }
} }

View file

@ -82,11 +82,8 @@ func typeSize(t Type) int64 {
return t.Size() return t.Size()
} }
// addOff adds two offset aux values. Each should be an int64. Fails if wraparound happens. // addOff adds two int64 offsets. Fails if wraparound happens.
func addOff(a, b interface{}) interface{} { func addOff(x, y int64) int64 {
return addOffset(a.(int64), b.(int64))
}
func addOffset(x, y int64) int64 {
z := x + y z := x + y
// x and y have same sign and z has a different sign => overflow // x and y have same sign and z has a different sign => overflow
if x^y >= 0 && x^z < 0 { if x^y >= 0 && x^z < 0 {

File diff suppressed because it is too large Load diff

View file

@ -7,28 +7,29 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
case OpAdd: case OpAdd:
// match: (Add <t> (Const [c]) (Const [d])) // match: (Add <t> (Const [c]) (Const [d]))
// cond: is64BitInt(t) // cond: is64BitInt(t)
// result: (Const [{c.(int64)+d.(int64)}]) // result: (Const [c+d])
{ {
t := v.Type t := v.Type
if v.Args[0].Op != OpConst { if v.Args[0].Op != OpConst {
goto end8d047ed0ae9537b840adc79ea82c6e05 goto end279f4ea85ed10e5ffc5b53f9e060529b
} }
c := v.Args[0].Aux c := v.Args[0].AuxInt
if v.Args[1].Op != OpConst { if v.Args[1].Op != OpConst {
goto end8d047ed0ae9537b840adc79ea82c6e05 goto end279f4ea85ed10e5ffc5b53f9e060529b
} }
d := v.Args[1].Aux d := v.Args[1].AuxInt
if !(is64BitInt(t)) { if !(is64BitInt(t)) {
goto end8d047ed0ae9537b840adc79ea82c6e05 goto end279f4ea85ed10e5ffc5b53f9e060529b
} }
v.Op = OpConst v.Op = OpConst
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.Aux = c.(int64) + d.(int64) v.AuxInt = c + d
return true return true
} }
goto end8d047ed0ae9537b840adc79ea82c6e05 goto end279f4ea85ed10e5ffc5b53f9e060529b
end8d047ed0ae9537b840adc79ea82c6e05: end279f4ea85ed10e5ffc5b53f9e060529b:
; ;
case OpArrayIndex: case OpArrayIndex:
// match: (ArrayIndex (Load ptr mem) idx) // match: (ArrayIndex (Load ptr mem) idx)
@ -42,9 +43,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
mem := v.Args[0].Args[1] mem := v.Args[0].Args[1]
idx := v.Args[1] idx := v.Args[1]
v.Op = OpLoad v.Op = OpLoad
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpPtrIndex, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpPtrIndex, TypeInvalid)
v0.Type = ptr.Type.Elem().Elem().PtrTo() v0.Type = ptr.Type.Elem().Elem().PtrTo()
v0.AddArg(ptr) v0.AddArg(ptr)
v0.AddArg(idx) v0.AddArg(idx)
@ -56,56 +58,58 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
end3809f4c52270a76313e4ea26e6f0b753: end3809f4c52270a76313e4ea26e6f0b753:
; ;
case OpConst: case OpConst:
// match: (Const <t> [s]) // match: (Const <t> {s})
// cond: t.IsString() // cond: t.IsString()
// result: (StringMake (OffPtr <TypeBytePtr> [2*config.ptrSize] (Global <TypeBytePtr> [config.fe.StringSym(s.(string))])) (Const <config.Uintptr> [int64(len(s.(string)))])) // result: (StringMake (OffPtr <TypeBytePtr> [2*config.ptrSize] (Global <TypeBytePtr> {config.fe.StringSym(s.(string))})) (Const <config.Uintptr> [int64(len(s.(string)))]))
{ {
t := v.Type t := v.Type
s := v.Aux s := v.Aux
if !(t.IsString()) { if !(t.IsString()) {
goto end8442aa5b3f4e5b840055475883110372 goto end6d6321106a054a5984b2ed0acec52a5b
} }
v.Op = OpStringMake v.Op = OpStringMake
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid)
v0.Type = TypeBytePtr v0.Type = TypeBytePtr
v0.Aux = 2 * config.ptrSize v0.AuxInt = 2 * config.ptrSize
v1 := v.Block.NewValue(v.Line, OpGlobal, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpGlobal, TypeInvalid)
v1.Type = TypeBytePtr v1.Type = TypeBytePtr
v1.Aux = config.fe.StringSym(s.(string)) v1.Aux = config.fe.StringSym(s.(string))
v0.AddArg(v1) v0.AddArg(v1)
v.AddArg(v0) v.AddArg(v0)
v2 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) v2 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
v2.Type = config.Uintptr v2.Type = config.Uintptr
v2.Aux = int64(len(s.(string))) v2.AuxInt = int64(len(s.(string)))
v.AddArg(v2) v.AddArg(v2)
return true return true
} }
goto end8442aa5b3f4e5b840055475883110372 goto end6d6321106a054a5984b2ed0acec52a5b
end8442aa5b3f4e5b840055475883110372: end6d6321106a054a5984b2ed0acec52a5b:
; ;
case OpIsInBounds: case OpIsInBounds:
// match: (IsInBounds (Const [c]) (Const [d])) // match: (IsInBounds (Const [c]) (Const [d]))
// cond: // cond:
// result: (Const [inBounds(c.(int64),d.(int64))]) // result: (Const {inBounds(c,d)})
{ {
if v.Args[0].Op != OpConst { if v.Args[0].Op != OpConst {
goto enddbd1a394d9b71ee64335361b8384865c goto enda96ccac78df2d17ae96c8baf2af2e189
} }
c := v.Args[0].Aux c := v.Args[0].AuxInt
if v.Args[1].Op != OpConst { if v.Args[1].Op != OpConst {
goto enddbd1a394d9b71ee64335361b8384865c goto enda96ccac78df2d17ae96c8baf2af2e189
} }
d := v.Args[1].Aux d := v.Args[1].AuxInt
v.Op = OpConst v.Op = OpConst
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.Aux = inBounds(c.(int64), d.(int64)) v.Aux = inBounds(c, d)
return true return true
} }
goto enddbd1a394d9b71ee64335361b8384865c goto enda96ccac78df2d17ae96c8baf2af2e189
enddbd1a394d9b71ee64335361b8384865c: enda96ccac78df2d17ae96c8baf2af2e189:
; ;
case OpLoad: case OpLoad:
// match: (Load <t> ptr mem) // match: (Load <t> ptr mem)
@ -119,18 +123,19 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
goto endd0afd003b70d726a1c5bbaf51fe06182 goto endd0afd003b70d726a1c5bbaf51fe06182
} }
v.Op = OpStringMake v.Op = OpStringMake
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpLoad, TypeInvalid)
v0.Type = TypeBytePtr v0.Type = TypeBytePtr
v0.AddArg(ptr) v0.AddArg(ptr)
v0.AddArg(mem) v0.AddArg(mem)
v.AddArg(v0) v.AddArg(v0)
v1 := v.Block.NewValue(v.Line, OpLoad, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpLoad, TypeInvalid)
v1.Type = config.Uintptr v1.Type = config.Uintptr
v2 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) v2 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid)
v2.Type = TypeBytePtr v2.Type = TypeBytePtr
v2.Aux = config.ptrSize v2.AuxInt = config.ptrSize
v2.AddArg(ptr) v2.AddArg(ptr)
v1.AddArg(v2) v1.AddArg(v2)
v1.AddArg(mem) v1.AddArg(mem)
@ -143,28 +148,29 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
case OpMul: case OpMul:
// match: (Mul <t> (Const [c]) (Const [d])) // match: (Mul <t> (Const [c]) (Const [d]))
// cond: is64BitInt(t) // cond: is64BitInt(t)
// result: (Const [{c.(int64)*d.(int64)}]) // result: (Const [c*d])
{ {
t := v.Type t := v.Type
if v.Args[0].Op != OpConst { if v.Args[0].Op != OpConst {
goto end776610f88cf04f438242d76ed2b14f1c goto endd82095c6a872974522d33aaff1ee07be
} }
c := v.Args[0].Aux c := v.Args[0].AuxInt
if v.Args[1].Op != OpConst { if v.Args[1].Op != OpConst {
goto end776610f88cf04f438242d76ed2b14f1c goto endd82095c6a872974522d33aaff1ee07be
} }
d := v.Args[1].Aux d := v.Args[1].AuxInt
if !(is64BitInt(t)) { if !(is64BitInt(t)) {
goto end776610f88cf04f438242d76ed2b14f1c goto endd82095c6a872974522d33aaff1ee07be
} }
v.Op = OpConst v.Op = OpConst
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.Aux = c.(int64) * d.(int64) v.AuxInt = c * d
return true return true
} }
goto end776610f88cf04f438242d76ed2b14f1c goto endd82095c6a872974522d33aaff1ee07be
end776610f88cf04f438242d76ed2b14f1c: endd82095c6a872974522d33aaff1ee07be:
; ;
case OpPtrIndex: case OpPtrIndex:
// match: (PtrIndex <t> ptr idx) // match: (PtrIndex <t> ptr idx)
@ -175,15 +181,16 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
ptr := v.Args[0] ptr := v.Args[0]
idx := v.Args[1] idx := v.Args[1]
v.Op = OpAdd v.Op = OpAdd
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.AddArg(ptr) v.AddArg(ptr)
v0 := v.Block.NewValue(v.Line, OpMul, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpMul, TypeInvalid)
v0.Type = config.Uintptr v0.Type = config.Uintptr
v0.AddArg(idx) v0.AddArg(idx)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
v1.Type = config.Uintptr v1.Type = config.Uintptr
v1.Aux = t.Elem().Size() v1.AuxInt = t.Elem().Size()
v0.AddArg(v1) v0.AddArg(v1)
v.AddArg(v0) v.AddArg(v0)
return true return true
@ -194,56 +201,58 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
case OpSliceCap: case OpSliceCap:
// match: (SliceCap (Load ptr mem)) // match: (SliceCap (Load ptr mem))
// cond: // cond:
// result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize*2)])) mem) // result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [config.ptrSize*2])) mem)
{ {
if v.Args[0].Op != OpLoad { if v.Args[0].Op != OpLoad {
goto endc871dcd9a720b4290c9cae78fe147c8a goto end919cfa3d3539eb2e06a435d5f89654b9
} }
ptr := v.Args[0].Args[0] ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1] mem := v.Args[0].Args[1]
v.Op = OpLoad v.Op = OpLoad
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid)
v0.Type = ptr.Type v0.Type = ptr.Type
v0.AddArg(ptr) v0.AddArg(ptr)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
v1.Type = config.Uintptr v1.Type = config.Uintptr
v1.Aux = int64(config.ptrSize * 2) v1.AuxInt = config.ptrSize * 2
v0.AddArg(v1) v0.AddArg(v1)
v.AddArg(v0) v.AddArg(v0)
v.AddArg(mem) v.AddArg(mem)
return true return true
} }
goto endc871dcd9a720b4290c9cae78fe147c8a goto end919cfa3d3539eb2e06a435d5f89654b9
endc871dcd9a720b4290c9cae78fe147c8a: end919cfa3d3539eb2e06a435d5f89654b9:
; ;
case OpSliceLen: case OpSliceLen:
// match: (SliceLen (Load ptr mem)) // match: (SliceLen (Load ptr mem))
// cond: // cond:
// result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [int64(config.ptrSize)])) mem) // result: (Load (Add <ptr.Type> ptr (Const <config.Uintptr> [config.ptrSize])) mem)
{ {
if v.Args[0].Op != OpLoad { if v.Args[0].Op != OpLoad {
goto end1eec05e44f5fc8944e7c176f98a74d92 goto end3d74a5ef07180a709a91052da88bcd01
} }
ptr := v.Args[0].Args[0] ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1] mem := v.Args[0].Args[1]
v.Op = OpLoad v.Op = OpLoad
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpAdd, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpAdd, TypeInvalid)
v0.Type = ptr.Type v0.Type = ptr.Type
v0.AddArg(ptr) v0.AddArg(ptr)
v1 := v.Block.NewValue(v.Line, OpConst, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
v1.Type = config.Uintptr v1.Type = config.Uintptr
v1.Aux = int64(config.ptrSize) v1.AuxInt = config.ptrSize
v0.AddArg(v1) v0.AddArg(v1)
v.AddArg(v0) v.AddArg(v0)
v.AddArg(mem) v.AddArg(mem)
return true return true
} }
goto end1eec05e44f5fc8944e7c176f98a74d92 goto end3d74a5ef07180a709a91052da88bcd01
end1eec05e44f5fc8944e7c176f98a74d92: end3d74a5ef07180a709a91052da88bcd01:
; ;
case OpSlicePtr: case OpSlicePtr:
// match: (SlicePtr (Load ptr mem)) // match: (SlicePtr (Load ptr mem))
@ -256,6 +265,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
ptr := v.Args[0].Args[0] ptr := v.Args[0].Args[0]
mem := v.Args[0].Args[1] mem := v.Args[0].Args[1]
v.Op = OpLoad v.Op = OpLoad
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.AddArg(ptr) v.AddArg(ptr)
@ -284,9 +294,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
goto end324ffb6d2771808da4267f62c854e9c8 goto end324ffb6d2771808da4267f62c854e9c8
} }
v.Op = OpMove v.Op = OpMove
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v.Aux = t.Size() v.AuxInt = t.Size()
v.AddArg(dst) v.AddArg(dst)
v.AddArg(src) v.AddArg(src)
v.AddArg(mem) v.AddArg(mem)
@ -306,21 +317,22 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
goto end410559d97aed8018f820cd88723de442 goto end410559d97aed8018f820cd88723de442
} }
v.Op = OpStore v.Op = OpStore
v.AuxInt = 0
v.Aux = nil v.Aux = nil
v.resetArgs() v.resetArgs()
v0 := v.Block.NewValue(v.Line, OpOffPtr, TypeInvalid, nil) v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid)
v0.Type = TypeBytePtr v0.Type = TypeBytePtr
v0.Aux = config.ptrSize v0.AuxInt = config.ptrSize
v0.AddArg(dst) v0.AddArg(dst)
v.AddArg(v0) v.AddArg(v0)
v1 := v.Block.NewValue(v.Line, OpStringLen, TypeInvalid, nil) v1 := v.Block.NewValue0(v.Line, OpStringLen, TypeInvalid)
v1.Type = config.Uintptr v1.Type = config.Uintptr
v1.AddArg(str) v1.AddArg(str)
v.AddArg(v1) v.AddArg(v1)
v2 := v.Block.NewValue(v.Line, OpStore, TypeInvalid, nil) v2 := v.Block.NewValue0(v.Line, OpStore, TypeInvalid)
v2.Type = TypeMem v2.Type = TypeMem
v2.AddArg(dst) v2.AddArg(dst)
v3 := v.Block.NewValue(v.Line, OpStringPtr, TypeInvalid, nil) v3 := v.Block.NewValue0(v.Line, OpStringPtr, TypeInvalid)
v3.Type = TypeBytePtr v3.Type = TypeBytePtr
v3.AddArg(str) v3.AddArg(str)
v2.AddArg(v3) v2.AddArg(v3)
@ -341,6 +353,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
} }
len := v.Args[0].Args[1] len := v.Args[0].Args[1]
v.Op = len.Op v.Op = len.Op
v.AuxInt = len.AuxInt
v.Aux = len.Aux v.Aux = len.Aux
v.resetArgs() v.resetArgs()
v.AddArgs(len.Args...) v.AddArgs(len.Args...)
@ -359,6 +372,7 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
} }
ptr := v.Args[0].Args[0] ptr := v.Args[0].Args[0]
v.Op = ptr.Op v.Op = ptr.Op
v.AuxInt = ptr.AuxInt
v.Aux = ptr.Aux v.Aux = ptr.Aux
v.resetArgs() v.resetArgs()
v.AddArgs(ptr.Args...) v.AddArgs(ptr.Args...)
@ -372,19 +386,19 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
func rewriteBlockgeneric(b *Block) bool { func rewriteBlockgeneric(b *Block) bool {
switch b.Kind { switch b.Kind {
case BlockIf: case BlockIf:
// match: (If (Const [c]) yes no) // match: (If (Const {c}) yes no)
// cond: c.(bool) // cond: c.(bool)
// result: (Plain nil yes) // result: (Plain nil yes)
{ {
v := b.Control v := b.Control
if v.Op != OpConst { if v.Op != OpConst {
goto end60cde11c1be8092f493d9cda982445ca goto end915e334b6388fed7d63e09aa69ecb05c
} }
c := v.Aux c := v.Aux
yes := b.Succs[0] yes := b.Succs[0]
no := b.Succs[1] no := b.Succs[1]
if !(c.(bool)) { if !(c.(bool)) {
goto end60cde11c1be8092f493d9cda982445ca goto end915e334b6388fed7d63e09aa69ecb05c
} }
removePredecessor(b, no) removePredecessor(b, no)
b.Kind = BlockPlain b.Kind = BlockPlain
@ -393,22 +407,22 @@ func rewriteBlockgeneric(b *Block) bool {
b.Succs[0] = yes b.Succs[0] = yes
return true return true
} }
goto end60cde11c1be8092f493d9cda982445ca goto end915e334b6388fed7d63e09aa69ecb05c
end60cde11c1be8092f493d9cda982445ca: end915e334b6388fed7d63e09aa69ecb05c:
; ;
// match: (If (Const [c]) yes no) // match: (If (Const {c}) yes no)
// cond: !c.(bool) // cond: !c.(bool)
// result: (Plain nil no) // result: (Plain nil no)
{ {
v := b.Control v := b.Control
if v.Op != OpConst { if v.Op != OpConst {
goto endf2a5efbfd2d40dead087c33685c8f30b goto end6452ee3a5bb02c708bddc3181c3ea3cb
} }
c := v.Aux c := v.Aux
yes := b.Succs[0] yes := b.Succs[0]
no := b.Succs[1] no := b.Succs[1]
if !(!c.(bool)) { if !(!c.(bool)) {
goto endf2a5efbfd2d40dead087c33685c8f30b goto end6452ee3a5bb02c708bddc3181c3ea3cb
} }
removePredecessor(b, yes) removePredecessor(b, yes)
b.Kind = BlockPlain b.Kind = BlockPlain
@ -417,8 +431,8 @@ func rewriteBlockgeneric(b *Block) bool {
b.Succs[0] = no b.Succs[0] = no
return true return true
} }
goto endf2a5efbfd2d40dead087c33685c8f30b goto end6452ee3a5bb02c708bddc3181c3ea3cb
endf2a5efbfd2d40dead087c33685c8f30b: end6452ee3a5bb02c708bddc3181c3ea3cb:
} }
return false return false
} }

View file

@ -11,15 +11,15 @@ func TestSchedule(t *testing.T) {
cases := []fun{ cases := []fun{
Fun(c, "entry", Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem0", OpArg, TypeMem, ".mem"), Valu("mem0", OpArg, TypeMem, 0, ".mem"),
Valu("ptr", OpConst, TypeInt64, 0xABCD), Valu("ptr", OpConst, TypeInt64, 0xABCD, nil),
Valu("v", OpConst, TypeInt64, 12), Valu("v", OpConst, TypeInt64, 12, nil),
Valu("mem1", OpStore, TypeMem, 32, "ptr", "v", "mem0"), Valu("mem1", OpStore, TypeMem, 0, nil, "ptr", "v", "mem0"),
Valu("mem2", OpStore, TypeMem, 32, "ptr", "v", "mem1"), Valu("mem2", OpStore, TypeMem, 0, nil, "ptr", "v", "mem1"),
Valu("mem3", OpStore, TypeInt64, "ptr", "sum", "mem2"), Valu("mem3", OpStore, TypeInt64, 0, nil, "ptr", "sum", "mem2"),
Valu("l1", OpLoad, TypeInt64, 16, "ptr", "mem1"), Valu("l1", OpLoad, TypeInt64, 0, nil, "ptr", "mem1"),
Valu("l2", OpLoad, TypeInt64, 8, "ptr", "mem2"), Valu("l2", OpLoad, TypeInt64, 0, nil, "ptr", "mem2"),
Valu("sum", OpAdd, TypeInt64, "l1", "l2"), Valu("sum", OpAdd, TypeInt64, 0, nil, "l1", "l2"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",
Exit("mem3"))), Exit("mem3"))),

View file

@ -28,14 +28,14 @@ func makeConstShiftFunc(c *Config, amount int64, op Op, typ Type) fun {
ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"} ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"}
fun := Fun(c, "entry", fun := Fun(c, "entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpArg, TypeMem, ".mem"), Valu("mem", OpArg, TypeMem, 0, ".mem"),
Valu("FP", OpFP, TypeUInt64, nil), Valu("FP", OpFP, TypeUInt64, 0, nil),
Valu("argptr", OpOffPtr, ptyp, int64(8), "FP"), Valu("argptr", OpOffPtr, ptyp, 8, nil, "FP"),
Valu("resptr", OpOffPtr, ptyp, int64(16), "FP"), Valu("resptr", OpOffPtr, ptyp, 16, nil, "FP"),
Valu("load", OpLoad, typ, nil, "argptr", "mem"), Valu("load", OpLoad, typ, 0, nil, "argptr", "mem"),
Valu("c", OpConst, TypeUInt64, amount), Valu("c", OpConst, TypeUInt64, amount, nil),
Valu("shift", op, typ, nil, "load", "c"), Valu("shift", op, typ, 0, nil, "load", "c"),
Valu("store", OpStore, TypeMem, nil, "resptr", "shift", "mem"), Valu("store", OpStore, TypeMem, 0, nil, "resptr", "shift", "mem"),
Exit("store"))) Exit("store")))
Compile(fun.f) Compile(fun.f)
return fun return fun

View file

@ -92,14 +92,14 @@ func stackalloc(f *Func) {
case OpAMD64ADDQ: case OpAMD64ADDQ:
// (ADDQ (FP) x) -> (LEAQ [n] (SP) x) // (ADDQ (FP) x) -> (LEAQ [n] (SP) x)
v.Op = OpAMD64LEAQ v.Op = OpAMD64LEAQ
v.Aux = n v.AuxInt = n
case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVBload, OpAMD64MOVQloadidx8: case OpAMD64LEAQ, OpAMD64MOVQload, OpAMD64MOVQstore, OpAMD64MOVBload, OpAMD64MOVQloadidx8:
if v.Op == OpAMD64MOVQloadidx8 && i == 1 { if v.Op == OpAMD64MOVQloadidx8 && i == 1 {
// Note: we could do it, but it is probably an error // 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) log.Panicf("can't do FP->SP adjust on index slot of load %s", v.Op)
} }
// eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem) // eg: (MOVQload [c] (FP) mem) -> (MOVQload [c+n] (SP) mem)
v.Aux = addOffset(v.Aux.(int64), n) v.AuxInt = addOff(v.AuxInt, n)
default: default:
log.Panicf("can't do FP->SP adjust on %s", v.Op) log.Panicf("can't do FP->SP adjust on %s", v.Op)
// TODO: OpCopy -> ADDQ // TODO: OpCopy -> ADDQ

View file

@ -22,7 +22,9 @@ type Value struct {
Type Type Type Type
// Auxiliary info for this value. The type of this information depends on the opcode and type. // Auxiliary info for this value. The type of this information depends on the opcode and type.
Aux interface{} // AuxInt is used for integer values, Aux is used for other values.
AuxInt int64
Aux interface{}
// Arguments of this value // Arguments of this value
Args []*Value Args []*Value
@ -53,8 +55,11 @@ func (v *Value) String() string {
func (v *Value) LongString() string { func (v *Value) LongString() string {
s := fmt.Sprintf("v%d = %s", v.ID, v.Op.String()) s := fmt.Sprintf("v%d = %s", v.ID, v.Op.String())
s += " <" + v.Type.String() + ">" s += " <" + v.Type.String() + ">"
if v.AuxInt != 0 {
s += fmt.Sprintf(" [%d]", v.AuxInt)
}
if v.Aux != nil { if v.Aux != nil {
s += fmt.Sprintf(" [%v]", v.Aux) s += fmt.Sprintf(" {%v}", v.Aux)
} }
for _, a := range v.Args { for _, a := range v.Args {
s += fmt.Sprintf(" %v", a) s += fmt.Sprintf(" %v", a)