for i, b := range f.Blocks {
s.bstart[b.ID] = Pc
// Emit values in block
+ s.markMoves(b)
for _, v := range b.Values {
x := Pc
s.genValue(v)
p.From.Offset = i
p.To.Type = obj.TYPE_REG
p.To.Reg = x
+ // If flags are live at this instruction, suppress the
+ // MOV $0,AX -> XOR AX,AX optimization.
+ if v.Aux != nil {
+ p.Mark |= x86.PRESERVEFLAGS
+ }
case ssa.OpAMD64MOVSSconst, ssa.OpAMD64MOVSDconst:
x := regnum(v)
p := Prog(v.Op.Asm())
}
}
+// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
+func (s *genState) markMoves(b *ssa.Block) {
+ flive := b.FlagsLiveAtEnd
+ if b.Control != nil && b.Control.Type.IsFlags() {
+ flive = true
+ }
+ for i := len(b.Values) - 1; i >= 0; i-- {
+ v := b.Values[i]
+ if flive && (v.Op == ssa.OpAMD64MOVWconst || v.Op == ssa.OpAMD64MOVLconst || v.Op == ssa.OpAMD64MOVQconst) {
+ // The "mark" is any non-nil Aux value.
+ v.Aux = v
+ }
+ if v.Type.IsFlags() {
+ flive = false
+ }
+ for _, a := range v.Args {
+ if a.Type.IsFlags() {
+ flive = true
+ }
+ }
+ }
+}
+
// 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)
// Ignored if len(Succs) < 2.
// Fatal if not BranchUnknown and len(Succs) > 2.
Likely BranchPrediction
+
+ // After flagalloc, records whether flags are live at the end of the block.
+ FlagsLiveAtEnd bool
}
// kind control successors
// standard regs, and it runs next.)
}
}
+
+ // Save live flag state for later.
+ for _, b := range f.Blocks {
+ b.FlagsLiveAtEnd = end[b.ID] != nil
+ }
}
// Common regInfo
var (
gp01 = regInfo{inputs: []regMask{}, outputs: gponly}
- gp01flags = regInfo{inputs: []regMask{}, outputs: gponly, clobbers: flags}
gp11 = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags}
gp11nf = regInfo{inputs: []regMask{gpsp}, outputs: gponly} // nf: no flags clobbered
gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly}
{name: "MOVLQSX", reg: gp11nf, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64
{name: "MOVLQZX", reg: gp11nf, asm: "MOVLQZX"}, // zero extend arg0 from int32 to int64
- // clobbers flags as liblink will rewrite these to XOR reg, reg if the constant is zero
- // TODO: revisit when issue 12405 is fixed
- {name: "MOVBconst", reg: gp01flags, asm: "MOVB", typ: "UInt8"}, // 8 low bits of auxint
- {name: "MOVWconst", reg: gp01flags, asm: "MOVW", typ: "UInt16"}, // 16 low bits of auxint
- {name: "MOVLconst", reg: gp01flags, asm: "MOVL", typ: "UInt32"}, // 32 low bits of auxint
- {name: "MOVQconst", reg: gp01flags, asm: "MOVQ", typ: "UInt64"}, // auxint
+ {name: "MOVBconst", reg: gp01, asm: "MOVB", typ: "UInt8"}, // 8 low bits of auxint
+ {name: "MOVWconst", reg: gp01, asm: "MOVW", typ: "UInt16"}, // 16 low bits of auxint
+ {name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32"}, // 32 low bits of auxint
+ {name: "MOVQconst", reg: gp01, asm: "MOVQ", typ: "UInt64"}, // auxint
{name: "CVTTSD2SL", reg: fpgp, asm: "CVTTSD2SL"}, // convert float64 to int32
{name: "CVTTSD2SQ", reg: fpgp, asm: "CVTTSD2SQ"}, // convert float64 to int64
name: "MOVBconst",
asm: x86.AMOVB,
reg: regInfo{
- clobbers: 8589934592, // .FLAGS
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
name: "MOVWconst",
asm: x86.AMOVW,
reg: regInfo{
- clobbers: 8589934592, // .FLAGS
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
name: "MOVLconst",
asm: x86.AMOVL,
reg: regInfo{
- clobbers: 8589934592, // .FLAGS
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
name: "MOVQconst",
asm: x86.AMOVQ,
reg: regInfo{
- clobbers: 8589934592, // .FLAGS
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
// We can't rematerialize instructions which
// clobber the flags register.
if regspec.clobbers&flagRegMask != 0 {
- if v.Op == OpAMD64MOVQconst && v.AuxInt != 0 ||
- v.Op == OpAMD64MOVLconst && int32(v.AuxInt) != 0 ||
- v.Op == OpAMD64MOVWconst && int16(v.AuxInt) != 0 ||
- v.Op == OpAMD64MOVBconst && int8(v.AuxInt) != 0 {
- // These are marked as clobbering flags, but only
- // the 0 versions actually do. TODO: fix MOV->XOR rewrites
- // to understand when they are allowed to clobber flags?
- return true
- }
return false
}
Spadj int32
As int16
Reg int16
- RegTo2 int16 // 2nd register output operand
- Mark uint16
+ RegTo2 int16 // 2nd register output operand
+ Mark uint16 // bitmask of arch-specific items
Optab uint16
Scond uint8
Back uint8
Ft uint8
Tt uint8
- Isize uint8
+ Isize uint8 // size of the instruction in bytes (x86 only)
Mode int8
Info ProgInfo
}
for p := sym.Text; p != nil; p = p.Link {
- p.Mark = 0 /* initialization for follow */
if p.Pcond != nil {
p.Pcond = brloop(ctxt, p.Pcond)
if p.Pcond != nil {
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p x86
+const (
+ /* mark flags */
+ DONE = 1 << iota
+ PRESERVEFLAGS // not allowed to clobber flags
+)
+
/*
* amd64
*/
// process forward jumps to p
for q = p.Rel; q != nil; q = q.Forwd {
- v = int32(p.Pc - (q.Pc + int64(q.Mark)))
+ v = int32(p.Pc - (q.Pc + int64(q.Isize)))
if q.Back&2 != 0 { // short
if v > 127 {
loop++
s.P[q.Pc+1] = byte(v)
}
} else {
- bp = s.P[q.Pc+int64(q.Mark)-4:]
+ bp = s.P[q.Pc+int64(q.Isize)-4:]
bp[0] = byte(v)
bp = bp[1:]
bp[0] = byte(v >> 8)
obj.Symgrow(ctxt, s, p.Pc+int64(m))
copy(s.P[p.Pc:][:m], ctxt.And[:m])
- p.Mark = uint16(m)
c += int32(m)
}
v = int64(int32(v))
}
if v == 0 {
+ if p.Mark&PRESERVEFLAGS != 0 {
+ // If PRESERVEFLAGS is set, avoid MOV $0, AX turning into XOR AX, AX.
+ return Yu7
+ }
return Yi0
}
if v == 1 {
q = p.Pcond
if q != nil && q.As != obj.ATEXT {
/* mark instruction as done and continue layout at target of jump */
- p.Mark = 1
+ p.Mark |= DONE
p = q
- if p.Mark == 0 {
+ if p.Mark&DONE == 0 {
goto loop
}
}
}
- if p.Mark != 0 {
+ if p.Mark&DONE != 0 {
/*
* p goes here, but already used it elsewhere.
* copy up to 4 instructions or else branch to other copy.
if nofollow(a) || pushpop(a) {
break // NOTE(rsc): arm does goto copy
}
- if q.Pcond == nil || q.Pcond.Mark != 0 {
+ if q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
continue
}
if a == obj.ACALL || a == ALOOP {
q = obj.Copyp(ctxt, p)
p = p.Link
- q.Mark = 1
+ q.Mark |= DONE
(*last).Link = q
*last = q
- if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 {
+ if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 {
continue
}
q.Link = p
xfol(ctxt, q.Link, last)
p = q.Link
- if p.Mark != 0 {
+ if p.Mark&DONE != 0 {
return
}
goto loop
}
/* emit p */
- p.Mark = 1
+ p.Mark |= DONE
(*last).Link = p
*last = p
}
} else {
q = p.Link
- if q.Mark != 0 {
+ if q.Mark&DONE != 0 {
if a != ALOOP {
p.As = relinv(int16(a))
p.Link = p.Pcond
}
xfol(ctxt, p.Link, last)
- if p.Pcond.Mark != 0 {
+ if p.Pcond.Mark&DONE != 0 {
return
}
p = p.Pcond