return s.curBlock.NewValue2(s.peekLine(), op, t, arg0, arg1)
}
+// newValue2I adds a new value with two arguments and an auxint value to the current block.
+func (s *state) newValue2I(op ssa.Op, t ssa.Type, aux int64, arg0, arg1 *ssa.Value) *ssa.Value {
+ return s.curBlock.NewValue2I(s.peekLine(), op, t, aux, arg0, arg1)
+}
+
// newValue3 adds a new value with three arguments to the current block.
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, arg0, arg1, arg2)
if right == nil {
// right == nil means use the zero value of the assigned type.
t := left.Type
+ if !canSSA(left) {
+ // if we can't ssa this memory, treat it as just zeroing out the backing memory
+ addr := s.addr(left)
+ s.vars[&memvar] = s.newValue2I(ssa.OpZero, ssa.TypeMem, t.Size(), addr, s.mem())
+ return
+ }
switch {
case t.IsString():
val = s.entryNewValue0A(ssa.OpConst, left.Type, "")
// n must be an ONAME.
func canSSA(n *Node) bool {
if n.Op != ONAME {
- Fatal("canSSA passed a non-ONAME %s %v", Oconv(int(n.Op), 0), n)
+ return false
}
if n.Addrtaken {
return false
if n.Class == PPARAMOUT {
return false
}
+ if Isfat(n.Type) {
+ return false
+ }
return true
// TODO: try to make more variables SSAable.
}
p.From.Reg = regnum(v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v)
+ case ssa.OpAMD64MOVXzero:
+ nb := v.AuxInt
+ offset := int64(0)
+ reg := regnum(v.Args[0])
+ for nb >= 8 {
+ nb, offset = movZero(x86.AMOVQ, 8, nb, offset, reg)
+ }
+ for nb >= 4 {
+ nb, offset = movZero(x86.AMOVL, 4, nb, offset, reg)
+ }
+ for nb >= 2 {
+ nb, offset = movZero(x86.AMOVW, 2, nb, offset, reg)
+ }
+ for nb >= 1 {
+ nb, offset = movZero(x86.AMOVB, 1, nb, offset, reg)
+ }
case ssa.OpCopy: // TODO: lower to MOVQ earlier?
if v.Type.IsMemory() {
return
}
}
+// movZero generates a register indirect move with a 0 immediate and keeps track of bytes left and next offset
+func movZero(as int, width int64, nbytes int64, offset int64, regnum int16) (nleft int64, noff int64) {
+ p := Prog(as)
+ // TODO: use zero register on archs that support it.
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 0
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = regnum
+ p.To.Offset = offset
+ offset += width
+ nleft = nbytes - width
+ return nleft, offset
+}
+
func genBlock(b, next *ssa.Block, branches []branch) []branch {
lineno = b.Line
switch b.Kind {
checkOpcodeCounts. Michael Matloob suggests using a similar
pattern matcher to the rewrite engine to check for certain
expression subtrees in the output.
+ - Implement memory zeroing with REPSTOSQ and DuffZero
+ - make deadstore work with zeroing.
return v
}
+// NewValue2I returns a new value in the block with two arguments and an auxint value.
+func (b *Block) NewValue2I(line int32, op Op, t Type, aux int64, arg0, arg1 *Value) *Value {
+ v := &Value{
+ ID: b.Func.vid.get(),
+ Op: op,
+ Type: t,
+ AuxInt: aux,
+ Block: b,
+ Line: line,
+ }
+ v.Args = v.argstorage[:2]
+ v.Args[0] = arg0
+ v.Args[1] = arg1
+ b.Values = append(b.Values, v)
+ return v
+}
+
// NewValue3 returns a new value in the block with three arguments and zero aux values.
func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value {
v := &Value{
(ADDQconst [0] x) -> (Copy x)
+// lower Zero instructions with word sizes
+(Zero [0] _ mem) -> (Copy mem)
+(Zero [1] destptr mem) -> (MOVBstore destptr (Const <TypeInt8> [0]) mem)
+(Zero [2] destptr mem) -> (MOVWstore destptr (Const <TypeInt16> [0]) mem)
+(Zero [4] destptr mem) -> (MOVLstore destptr (Const <TypeInt32> [0]) mem)
+(Zero [8] destptr mem) -> (MOVQstore destptr (Const <TypeInt64> [0]) mem)
+
+// rewrite anything less than 4 words into a series of MOV[BWLQ] $0, ptr(off) instructions
+(Zero [size] destptr mem) && size < 4*8 -> (MOVXzero [size] destptr mem)
+// Use STOSQ to zero memory. Rewrite this into storing the words with REPSTOSQ and then filling in the remainder with linear moves
+(Zero [size] destptr mem) && size >= 4*8 -> (Zero [size%8] (OffPtr <TypeUInt64> [size-(size%8)] destptr) (REPSTOSQ <TypeMem> destptr (Const <TypeUInt64> [size/8]) mem))
+
// Absorb InvertFlags into branches.
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
gpload := regInfo{[]regMask{gpspsb, 0}, 0, []regMask{gp}}
gploadidx := regInfo{[]regMask{gpspsb, gpsp, 0}, 0, []regMask{gp}}
gpstore := regInfo{[]regMask{gpspsb, gpsp, 0}, 0, nil}
+ gpstoreconst := regInfo{[]regMask{gpspsb, 0}, 0, nil}
gpstoreidx := regInfo{[]regMask{gpspsb, gpsp, gpsp, 0}, 0, nil}
flagsgp := regInfo{[]regMask{flags}, 0, []regMask{gp}}
cmov := regInfo{[]regMask{flags, gp, gp}, 0, []regMask{gp}}
{name: "MOVQstore", reg: gpstore, asm: "MOVQ"}, // store 8 bytes in arg1 to arg0+auxint. arg2=mem
{name: "MOVQstoreidx8", reg: gpstoreidx}, // store 8 bytes in arg2 to arg0+8*arg1+auxint. arg3=mem
+ {name: "MOVXzero", reg: gpstoreconst}, // store auxint 0 bytes into arg0 using a series of MOV instructions. arg1=mem.
+ // TODO: implement this when register clobbering works
+ {name: "REPSTOSQ", reg: regInfo{[]regMask{buildReg("DI"), buildReg("CX")}, buildReg("DI AX CX"), nil}}, // store arg1 8-byte words containing zero into arg0 using STOSQ. arg2=mem.
+
// Load/store from global. Same as the above loads, but arg0 is missing and
// aux is a GlobalOffset instead of an int64.
{name: "MOVQloadglobal"}, // Load from aux.(GlobalOffset). arg0 = memory
{name: "Load"}, // Load from arg0. arg1=memory
{name: "Store"}, // Store arg1 to arg0. arg2=memory. Returns memory.
{name: "Move"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size. Returns memory.
+ {name: "Zero"}, // arg0=destptr, arg1=mem, auxint=size. Returns memory.
// 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
OpAMD64MOVLstore
OpAMD64MOVQstore
OpAMD64MOVQstoreidx8
+ OpAMD64MOVXzero
+ OpAMD64REPSTOSQ
OpAMD64MOVQloadglobal
OpAMD64MOVQstoreglobal
OpAMD64CALLstatic
OpLoad
OpStore
OpMove
+ OpZero
OpClosureCall
OpStaticCall
OpConvert
outputs: []regMask{},
},
},
+ {
+ name: "MOVXzero",
+ reg: regInfo{
+ inputs: []regMask{
+ 4295032831, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15 .SB
+ 0,
+ },
+ clobbers: 0,
+ outputs: []regMask{},
+ },
+ },
+ {
+ name: "REPSTOSQ",
+ reg: regInfo{
+ inputs: []regMask{
+ 128, // .DI
+ 2, // .CX
+ },
+ clobbers: 131, // .AX .CX .DI
+ outputs: []regMask{},
+ },
+ },
{
name: "MOVQloadglobal",
reg: regInfo{
},
generic: true,
},
+ {
+ name: "Zero",
+ reg: regInfo{
+ inputs: []regMask{},
+ clobbers: 0,
+ outputs: []regMask{},
+ },
+ generic: true,
+ },
{
name: "ClosureCall",
reg: regInfo{
}
goto ende6ef29f885a8ecf3058212bb95917323
ende6ef29f885a8ecf3058212bb95917323:
+ ;
+ case OpZero:
+ // match: (Zero [0] _ mem)
+ // cond:
+ // result: (Copy mem)
+ {
+ if v.AuxInt != 0 {
+ goto endb85a34a7d102b0e0d801454f437db5bf
+ }
+ mem := v.Args[1]
+ v.Op = OpCopy
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(mem)
+ return true
+ }
+ goto endb85a34a7d102b0e0d801454f437db5bf
+ endb85a34a7d102b0e0d801454f437db5bf:
+ ;
+ // match: (Zero [1] destptr mem)
+ // cond:
+ // result: (MOVBstore destptr (Const <TypeInt8> [0]) mem)
+ {
+ if v.AuxInt != 1 {
+ goto end09ec7b1fc5ad40534e0e25c896323f5c
+ }
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ v.Op = OpAMD64MOVBstore
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(destptr)
+ v0 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
+ v0.Type = TypeInt8
+ v0.AuxInt = 0
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
+ goto end09ec7b1fc5ad40534e0e25c896323f5c
+ end09ec7b1fc5ad40534e0e25c896323f5c:
+ ;
+ // match: (Zero [2] destptr mem)
+ // cond:
+ // result: (MOVWstore destptr (Const <TypeInt16> [0]) mem)
+ {
+ if v.AuxInt != 2 {
+ goto end2dee246789dbd305bb1eaec768bdae14
+ }
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ v.Op = OpAMD64MOVWstore
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(destptr)
+ v0 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
+ v0.Type = TypeInt16
+ v0.AuxInt = 0
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
+ goto end2dee246789dbd305bb1eaec768bdae14
+ end2dee246789dbd305bb1eaec768bdae14:
+ ;
+ // match: (Zero [4] destptr mem)
+ // cond:
+ // result: (MOVLstore destptr (Const <TypeInt32> [0]) mem)
+ {
+ if v.AuxInt != 4 {
+ goto ende2bf4ecf21bc9e76700a9c5f62546e78
+ }
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ v.Op = OpAMD64MOVLstore
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(destptr)
+ v0 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
+ v0.Type = TypeInt32
+ v0.AuxInt = 0
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
+ goto ende2bf4ecf21bc9e76700a9c5f62546e78
+ ende2bf4ecf21bc9e76700a9c5f62546e78:
+ ;
+ // match: (Zero [8] destptr mem)
+ // cond:
+ // result: (MOVQstore destptr (Const <TypeInt64> [0]) mem)
+ {
+ if v.AuxInt != 8 {
+ goto enda65d5d60783daf9b9405f04c44f7adaf
+ }
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ v.Op = OpAMD64MOVQstore
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(destptr)
+ v0 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
+ v0.Type = TypeInt64
+ v0.AuxInt = 0
+ v.AddArg(v0)
+ v.AddArg(mem)
+ return true
+ }
+ goto enda65d5d60783daf9b9405f04c44f7adaf
+ enda65d5d60783daf9b9405f04c44f7adaf:
+ ;
+ // match: (Zero [size] destptr mem)
+ // cond: size < 4*8
+ // result: (MOVXzero [size] destptr mem)
+ {
+ size := v.AuxInt
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ if !(size < 4*8) {
+ goto endf0a22f1506977610ac0a310eee152075
+ }
+ v.Op = OpAMD64MOVXzero
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AuxInt = size
+ v.AddArg(destptr)
+ v.AddArg(mem)
+ return true
+ }
+ goto endf0a22f1506977610ac0a310eee152075
+ endf0a22f1506977610ac0a310eee152075:
+ ;
+ // match: (Zero [size] destptr mem)
+ // cond: size >= 4*8
+ // result: (Zero [size%8] (OffPtr <TypeUInt64> [size-(size%8)] destptr) (REPSTOSQ <TypeMem> destptr (Const <TypeUInt64> [size/8]) mem))
+ {
+ size := v.AuxInt
+ destptr := v.Args[0]
+ mem := v.Args[1]
+ if !(size >= 4*8) {
+ goto end7a358169d20d6834b21f2e03fbf351b2
+ }
+ v.Op = OpZero
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AuxInt = size % 8
+ v0 := v.Block.NewValue0(v.Line, OpOffPtr, TypeInvalid)
+ v0.Type = TypeUInt64
+ v0.AuxInt = size - (size % 8)
+ v0.AddArg(destptr)
+ v.AddArg(v0)
+ v1 := v.Block.NewValue0(v.Line, OpAMD64REPSTOSQ, TypeInvalid)
+ v1.Type = TypeMem
+ v1.AddArg(destptr)
+ v2 := v.Block.NewValue0(v.Line, OpConst, TypeInvalid)
+ v2.Type = TypeUInt64
+ v2.AuxInt = size / 8
+ v1.AddArg(v2)
+ v1.AddArg(mem)
+ v.AddArg(v1)
+ return true
+ }
+ goto end7a358169d20d6834b21f2e03fbf351b2
+ end7a358169d20d6834b21f2e03fbf351b2:
}
return false
}