mirror of https://github.com/golang/go.git
cmd/compile: improve s390x sign/zero extension removal
This CL gets rid of the MOVDreg and MOVDnop SSA operations on
s390x. They were originally inserted to help avoid situations
where a sign/zero extension was elided but a spill invalidated
the optimization. It's not really clear we need to do this though
(amd64 doesn't have these ops for example) so long as we are
careful when removing sign/zero extensions. Also, the MOVDreg
technique doesn't work if the register is spilled before the
MOVDreg op (I haven't seen that in practice).
Removing these ops reduces the complexity of the rules and also
allows us to unblock optimizations. For example, the compiler can
now merge the loads in binary.{Big,Little}Endian.PutUint16 which
it wasn't able to do before. This CL reduces the size of the .text
section in the go tool by about 4.7KB (0.09%).
Change-Id: Icaddae7f2e4f9b2debb6fabae845adb3f73b41db
Reviewed-on: https://go-review.googlesource.com/c/go/+/173897
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
b38be35e4c
commit
5c5f217b63
|
|
@ -482,7 +482,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
|||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = v.Args[0].Reg()
|
||||
gc.AddAux2(&p.To, v, sc.Off())
|
||||
case ssa.OpCopy, ssa.OpS390XMOVDreg:
|
||||
case ssa.OpCopy:
|
||||
if v.Type.IsMemory() {
|
||||
return
|
||||
}
|
||||
|
|
@ -491,11 +491,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
|||
if x != y {
|
||||
opregreg(s, moveByType(v.Type), y, x)
|
||||
}
|
||||
case ssa.OpS390XMOVDnop:
|
||||
if v.Reg() != v.Args[0].Reg() {
|
||||
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
||||
}
|
||||
// nothing to do
|
||||
case ssa.OpLoadReg:
|
||||
if v.Type.IsFlags() {
|
||||
v.Fatalf("load flags not implemented: %v", v.LongString())
|
||||
|
|
|
|||
|
|
@ -443,61 +443,121 @@
|
|||
// ***************************
|
||||
// TODO: Should the optimizations be a separate pass?
|
||||
|
||||
// Fold unnecessary type conversions.
|
||||
(MOVDreg <t> x) && t.Compare(x.Type) == types.CMPeq -> x
|
||||
(MOVDnop <t> x) && t.Compare(x.Type) == types.CMPeq -> x
|
||||
// Note: when removing unnecessary sign/zero extensions.
|
||||
//
|
||||
// After a value is spilled it is restored using a sign- or zero-extension
|
||||
// to register-width as appropriate for its type. For example, a uint8 will
|
||||
// be restored using a MOVBZ (llgc) instruction which will zero extend the
|
||||
// 8-bit value to 64-bits.
|
||||
//
|
||||
// This is a hazard when folding sign- and zero-extensions since we need to
|
||||
// ensure not only that the value in the argument register is correctly
|
||||
// extended but also that it will still be correctly extended if it is
|
||||
// spilled and restored.
|
||||
//
|
||||
// In general this means we need type checks when the RHS of a rule is an
|
||||
// OpCopy (i.e. "(... x:(...) ...) -> x").
|
||||
|
||||
// Propagate constants through type conversions.
|
||||
(MOVDreg (MOVDconst [c])) -> (MOVDconst [c])
|
||||
(MOVDnop (MOVDconst [c])) -> (MOVDconst [c])
|
||||
// Merge double extensions.
|
||||
(MOV(H|HZ)reg e:(MOV(B|BZ)reg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(W|WZ)reg e:(MOV(B|BZ)reg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(W|WZ)reg e:(MOV(H|HZ)reg x)) && clobberIfDead(e) -> (MOV(H|HZ)reg x)
|
||||
|
||||
// If a register move has only 1 use, just use the same register without emitting instruction.
|
||||
// MOVDnop doesn't emit instruction, only for ensuring the type.
|
||||
(MOVDreg x) && x.Uses == 1 -> (MOVDnop x)
|
||||
// Bypass redundant sign extensions.
|
||||
(MOV(B|BZ)reg e:(MOVBreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(B|BZ)reg e:(MOVHreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(B|BZ)reg e:(MOVWreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(H|HZ)reg e:(MOVHreg x)) && clobberIfDead(e) -> (MOV(H|HZ)reg x)
|
||||
(MOV(H|HZ)reg e:(MOVWreg x)) && clobberIfDead(e) -> (MOV(H|HZ)reg x)
|
||||
(MOV(W|WZ)reg e:(MOVWreg x)) && clobberIfDead(e) -> (MOV(W|WZ)reg x)
|
||||
|
||||
// Fold type changes into loads.
|
||||
(MOVDreg <t> x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVHload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <t> [off] {sym} ptr mem)
|
||||
(MOVDreg <t> x:(MOVDload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVDload <t> [off] {sym} ptr mem)
|
||||
// Bypass redundant zero extensions.
|
||||
(MOV(B|BZ)reg e:(MOVBZreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(B|BZ)reg e:(MOVHZreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(B|BZ)reg e:(MOVWZreg x)) && clobberIfDead(e) -> (MOV(B|BZ)reg x)
|
||||
(MOV(H|HZ)reg e:(MOVHZreg x)) && clobberIfDead(e) -> (MOV(H|HZ)reg x)
|
||||
(MOV(H|HZ)reg e:(MOVWZreg x)) && clobberIfDead(e) -> (MOV(H|HZ)reg x)
|
||||
(MOV(W|WZ)reg e:(MOVWZreg x)) && clobberIfDead(e) -> (MOV(W|WZ)reg x)
|
||||
|
||||
(MOVDnop <t> x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVHload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <t> [off] {sym} ptr mem)
|
||||
(MOVDnop <t> x:(MOVDload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVDload <t> [off] {sym} ptr mem)
|
||||
// Remove zero extensions after zero extending load.
|
||||
// Note: take care that if x is spilled it is restored correctly.
|
||||
(MOV(B|H|W)Zreg x:(MOVBZload _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 1) -> x
|
||||
(MOV(B|H|W)Zreg x:(MOVBZloadidx _ _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 1) -> x
|
||||
(MOV(H|W)Zreg x:(MOVHZload _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 2) -> x
|
||||
(MOV(H|W)Zreg x:(MOVHZloadidx _ _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 2) -> x
|
||||
(MOVWZreg x:(MOVWZload _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 4) -> x
|
||||
(MOVWZreg x:(MOVWZloadidx _ _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 4) -> x
|
||||
|
||||
(MOVDreg <t> x:(MOVBZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVBloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVHZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVHloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVWloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDreg <t> x:(MOVDloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVDloadidx <t> [off] {sym} ptr idx mem)
|
||||
// Remove sign extensions after sign extending load.
|
||||
// Note: take care that if x is spilled it is restored correctly.
|
||||
(MOV(B|H|W)reg x:(MOVBload _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
(MOV(B|H|W)reg x:(MOVBloadidx _ _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
(MOV(H|W)reg x:(MOVHload _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
(MOV(H|W)reg x:(MOVHloadidx _ _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
(MOVWreg x:(MOVWload _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
(MOVWreg x:(MOVWloadidx _ _ _)) && (x.Type.IsSigned() || x.Type.Size() == 8) -> x
|
||||
|
||||
(MOVDnop <t> x:(MOVBZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVBloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVHZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVHloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVWloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx <t> [off] {sym} ptr idx mem)
|
||||
(MOVDnop <t> x:(MOVDloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVDloadidx <t> [off] {sym} ptr idx mem)
|
||||
// Remove sign extensions after zero extending load.
|
||||
// These type checks are probably unnecessary but do them anyway just in case.
|
||||
(MOV(H|W)reg x:(MOVBZload _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 1) -> x
|
||||
(MOV(H|W)reg x:(MOVBZloadidx _ _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 1) -> x
|
||||
(MOVWreg x:(MOVHZload _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 2) -> x
|
||||
(MOVWreg x:(MOVHZloadidx _ _ _)) && (!x.Type.IsSigned() || x.Type.Size() > 2) -> x
|
||||
|
||||
// Fold sign extensions into conditional moves of constants.
|
||||
// Designed to remove the MOVBZreg inserted by the If lowering.
|
||||
(MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> (MOVDreg x)
|
||||
// Fold sign and zero extensions into loads.
|
||||
//
|
||||
// Note: The combined instruction must end up in the same block
|
||||
// as the original load. If not, we end up making a value with
|
||||
// memory type live in two different blocks, which can lead to
|
||||
// multiple memory values alive simultaneously.
|
||||
//
|
||||
// Make sure we don't combine these ops if the load has another use.
|
||||
// This prevents a single load from being split into multiple loads
|
||||
// which then might return different values. See test/atomicload.go.
|
||||
(MOV(B|H|W)Zreg <t> x:(MOV(B|H|W)load [o] {s} p mem))
|
||||
&& x.Uses == 1
|
||||
&& clobber(x)
|
||||
-> @x.Block (MOV(B|H|W)Zload <t> [o] {s} p mem)
|
||||
(MOV(B|H|W)reg <t> x:(MOV(B|H|W)Zload [o] {s} p mem))
|
||||
&& x.Uses == 1
|
||||
&& clobber(x)
|
||||
-> @x.Block (MOV(B|H|W)load <t> [o] {s} p mem)
|
||||
(MOV(B|H|W)Zreg <t> x:(MOV(B|H|W)loadidx [o] {s} p i mem))
|
||||
&& x.Uses == 1
|
||||
&& clobber(x)
|
||||
-> @x.Block (MOV(B|H|W)Zloadidx <t> [o] {s} p i mem)
|
||||
(MOV(B|H|W)reg <t> x:(MOV(B|H|W)Zloadidx [o] {s} p i mem))
|
||||
&& x.Uses == 1
|
||||
&& clobber(x)
|
||||
-> @x.Block (MOV(B|H|W)loadidx <t> [o] {s} p i mem)
|
||||
|
||||
// Remove zero extensions after argument load.
|
||||
(MOVBZreg x:(Arg <t>)) && !t.IsSigned() && t.Size() == 1 -> x
|
||||
(MOVHZreg x:(Arg <t>)) && !t.IsSigned() && t.Size() <= 2 -> x
|
||||
(MOVWZreg x:(Arg <t>)) && !t.IsSigned() && t.Size() <= 4 -> x
|
||||
|
||||
// Remove sign extensions after argument load.
|
||||
(MOVBreg x:(Arg <t>)) && t.IsSigned() && t.Size() == 1 -> x
|
||||
(MOVHreg x:(Arg <t>)) && t.IsSigned() && t.Size() <= 2 -> x
|
||||
(MOVWreg x:(Arg <t>)) && t.IsSigned() && t.Size() <= 4 -> x
|
||||
|
||||
// Fold zero extensions into constants.
|
||||
(MOVBZreg (MOVDconst [c])) -> (MOVDconst [int64( uint8(c))])
|
||||
(MOVHZreg (MOVDconst [c])) -> (MOVDconst [int64(uint16(c))])
|
||||
(MOVWZreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))])
|
||||
|
||||
// Fold sign extensions into constants.
|
||||
(MOVBreg (MOVDconst [c])) -> (MOVDconst [int64( int8(c))])
|
||||
(MOVHreg (MOVDconst [c])) -> (MOVDconst [int64(int16(c))])
|
||||
(MOVWreg (MOVDconst [c])) -> (MOVDconst [int64(int32(c))])
|
||||
|
||||
// Remove zero extension of conditional move.
|
||||
// Note: only for MOVBZreg for now since it is added as part of 'if' statement lowering.
|
||||
(MOVBZreg x:(MOVD(LT|LE|GT|GE|EQ|NE|GTnoinv|GEnoinv) (MOVDconst [c]) (MOVDconst [d]) _))
|
||||
&& int64(uint8(c)) == c
|
||||
&& int64(uint8(d)) == d
|
||||
&& (!x.Type.IsSigned() || x.Type.Size() > 1)
|
||||
-> x
|
||||
|
||||
// Fold boolean tests into blocks.
|
||||
(NE (CMPWconst [0] (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (LT cmp yes no)
|
||||
|
|
@ -547,12 +607,12 @@
|
|||
-> (S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst <typ.UInt32> [c&63] y))
|
||||
(S(LD|RD|RAD|LW|RW|RAW) x (ANDWconst [c] y)) && c&63 == 63
|
||||
-> (S(LD|RD|RAD|LW|RW|RAW) x y)
|
||||
(SLD x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SLD x y)
|
||||
(SRD x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SRD x y)
|
||||
(SRAD x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SRAD x y)
|
||||
(SLW x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SLW x y)
|
||||
(SRW x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SRW x y)
|
||||
(SRAW x (MOV(D|W|H|B|WZ|HZ|BZ)reg y)) -> (SRAW x y)
|
||||
(SLD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SLD x y)
|
||||
(SRD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SRD x y)
|
||||
(SRAD x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SRAD x y)
|
||||
(SLW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SLW x y)
|
||||
(SRW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SRW x y)
|
||||
(SRAW x (MOV(W|H|B|WZ|HZ|BZ)reg y)) -> (SRAW x y)
|
||||
|
||||
// Constant rotate generation
|
||||
(RLL x (MOVDconst [c])) -> (RLLconst x [c&31])
|
||||
|
|
@ -615,99 +675,8 @@
|
|||
(MOVDEQ x y (InvertFlags cmp)) -> (MOVDEQ x y cmp)
|
||||
(MOVDNE x y (InvertFlags cmp)) -> (MOVDNE x y cmp)
|
||||
|
||||
// don't extend after proper load
|
||||
(MOVBreg x:(MOVBload _ _)) -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVBZload _ _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVBload _ _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVBZload _ _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVHload _ _)) -> (MOVDreg x)
|
||||
(MOVHZreg x:(MOVBZload _ _)) -> (MOVDreg x)
|
||||
(MOVHZreg x:(MOVHZload _ _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVBload _ _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVBZload _ _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVHload _ _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVHZload _ _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVWload _ _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVBZload _ _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVHZload _ _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVWZload _ _)) -> (MOVDreg x)
|
||||
|
||||
// don't extend if argument is already extended
|
||||
(MOVBreg x:(Arg <t>)) && is8BitInt(t) && isSigned(t) -> (MOVDreg x)
|
||||
(MOVBZreg x:(Arg <t>)) && is8BitInt(t) && !isSigned(t) -> (MOVDreg x)
|
||||
(MOVHreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && isSigned(t) -> (MOVDreg x)
|
||||
(MOVHZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && !isSigned(t) -> (MOVDreg x)
|
||||
(MOVWreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t) -> (MOVDreg x)
|
||||
(MOVWZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t) -> (MOVDreg x)
|
||||
|
||||
// fold double extensions
|
||||
(MOVBreg x:(MOVBreg _)) -> (MOVDreg x)
|
||||
(MOVBZreg x:(MOVBZreg _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVBreg _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVBZreg _)) -> (MOVDreg x)
|
||||
(MOVHreg x:(MOVHreg _)) -> (MOVDreg x)
|
||||
(MOVHZreg x:(MOVBZreg _)) -> (MOVDreg x)
|
||||
(MOVHZreg x:(MOVHZreg _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVBreg _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVBZreg _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVHreg _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVHZreg _)) -> (MOVDreg x)
|
||||
(MOVWreg x:(MOVWreg _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVBZreg _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVHZreg _)) -> (MOVDreg x)
|
||||
(MOVWZreg x:(MOVWZreg _)) -> (MOVDreg x)
|
||||
|
||||
(MOVBreg (MOVBZreg x)) -> (MOVBreg x)
|
||||
(MOVBZreg (MOVBreg x)) -> (MOVBZreg x)
|
||||
(MOVHreg (MOVHZreg x)) -> (MOVHreg x)
|
||||
(MOVHZreg (MOVHreg x)) -> (MOVHZreg x)
|
||||
(MOVWreg (MOVWZreg x)) -> (MOVWreg x)
|
||||
(MOVWZreg (MOVWreg x)) -> (MOVWZreg x)
|
||||
|
||||
// fold extensions into constants
|
||||
(MOVBreg (MOVDconst [c])) -> (MOVDconst [int64(int8(c))])
|
||||
(MOVBZreg (MOVDconst [c])) -> (MOVDconst [int64(uint8(c))])
|
||||
(MOVHreg (MOVDconst [c])) -> (MOVDconst [int64(int16(c))])
|
||||
(MOVHZreg (MOVDconst [c])) -> (MOVDconst [int64(uint16(c))])
|
||||
(MOVWreg (MOVDconst [c])) -> (MOVDconst [int64(int32(c))])
|
||||
(MOVWZreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))])
|
||||
|
||||
// sign extended loads
|
||||
// Note: The combined instruction must end up in the same block
|
||||
// as the original load. If not, we end up making a value with
|
||||
// memory type live in two different blocks, which can lead to
|
||||
// multiple memory values alive simultaneously.
|
||||
// Make sure we don't combine these ops if the load has another use.
|
||||
// This prevents a single load from being split into multiple loads
|
||||
// which then might return different values. See test/atomicload.go.
|
||||
(MOVBreg x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVBreg x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVBZreg x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVBZreg x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVHreg x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVHreg x:(MOVHload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVHZreg x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVHZreg x:(MOVHload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVWreg x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVWreg x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVWZreg x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZload <v.Type> [off] {sym} ptr mem)
|
||||
(MOVWZreg x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZload <v.Type> [off] {sym} ptr mem)
|
||||
|
||||
(MOVBreg x:(MOVBZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVBreg x:(MOVBloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVBZreg x:(MOVBZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVBZreg x:(MOVBloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVHreg x:(MOVHZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVHreg x:(MOVHloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVHZreg x:(MOVHZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVHZreg x:(MOVHloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVWreg x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVWreg x:(MOVWloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVWZreg x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
(MOVWZreg x:(MOVWloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <v.Type> [off] {sym} ptr idx mem)
|
||||
|
||||
// replace load from same location as preceding store with copy
|
||||
(MOVDload [off] {sym} ptr1 (MOVDstore [off] {sym} ptr2 x _)) && isSamePtr(ptr1, ptr2) -> (MOVDreg x)
|
||||
(MOVDload [off] {sym} ptr1 (MOVDstore [off] {sym} ptr2 x _)) && isSamePtr(ptr1, ptr2) -> x
|
||||
(MOVWload [off] {sym} ptr1 (MOVWstore [off] {sym} ptr2 x _)) && isSamePtr(ptr1, ptr2) -> (MOVWreg x)
|
||||
(MOVHload [off] {sym} ptr1 (MOVHstore [off] {sym} ptr2 x _)) && isSamePtr(ptr1, ptr2) -> (MOVHreg x)
|
||||
(MOVBload [off] {sym} ptr1 (MOVBstore [off] {sym} ptr2 x _)) && isSamePtr(ptr1, ptr2) -> (MOVBreg x)
|
||||
|
|
@ -755,7 +724,7 @@
|
|||
|
||||
// remove unnecessary FPR <-> GPR moves
|
||||
(LDGR (LGDR x)) -> x
|
||||
(LGDR (LDGR x)) -> (MOVDreg x)
|
||||
(LGDR (LDGR x)) -> x
|
||||
|
||||
// Don't extend before storing
|
||||
(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
|
||||
|
|
|
|||
|
|
@ -373,9 +373,6 @@ func init() {
|
|||
{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
|
||||
{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"}, // sign extend arg0 from int32 to int64
|
||||
{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
|
||||
{name: "MOVDreg", argLength: 1, reg: gp11sp, asm: "MOVD"}, // move from arg0
|
||||
|
||||
{name: "MOVDnop", argLength: 1, reg: gp11, resultInArg0: true}, // nop, return arg0 in same register
|
||||
|
||||
{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
|
||||
|
||||
|
|
|
|||
|
|
@ -1979,8 +1979,6 @@ const (
|
|||
OpS390XMOVHZreg
|
||||
OpS390XMOVWreg
|
||||
OpS390XMOVWZreg
|
||||
OpS390XMOVDreg
|
||||
OpS390XMOVDnop
|
||||
OpS390XMOVDconst
|
||||
OpS390XLDGR
|
||||
OpS390XLGDR
|
||||
|
|
@ -26605,32 +26603,6 @@ var opcodeTable = [...]opInfo{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MOVDreg",
|
||||
argLen: 1,
|
||||
asm: s390x.AMOVD,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 56319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14 SP
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MOVDnop",
|
||||
argLen: 1,
|
||||
resultInArg0: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 23551}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "MOVDconst",
|
||||
auxType: auxInt64,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -57,6 +57,7 @@ func load_le16(b []byte) {
|
|||
// amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR`
|
||||
// ppc64le:`MOVHZ\s`,-`MOVBZ`
|
||||
// arm64:`MOVHU\s\(R[0-9]+\),`,-`MOVB`
|
||||
// s390x:`MOVHBR\s\(.*\),`
|
||||
sink16 = binary.LittleEndian.Uint16(b)
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +65,7 @@ func load_le16_idx(b []byte, idx int) {
|
|||
// amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR`
|
||||
// ppc64le:`MOVHZ\s`,-`MOVBZ`
|
||||
// arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB`
|
||||
// s390x:`MOVHBR\s\(.*\)\(.*\*1\),`
|
||||
sink16 = binary.LittleEndian.Uint16(b[idx:])
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +105,7 @@ func load_be16(b []byte) {
|
|||
// amd64:`ROLW\s\$8`,-`MOVB`,-`OR`
|
||||
// arm64:`REV16W`,`MOVHU\s\(R[0-9]+\),`,-`MOVB`
|
||||
// ppc64le:`MOVHBR`
|
||||
// s390x:`MOVHZ\s\(.*\),`,-`OR`,-`ORW`,-`SLD`,-`SLW`
|
||||
sink16 = binary.BigEndian.Uint16(b)
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +113,7 @@ func load_be16_idx(b []byte, idx int) {
|
|||
// amd64:`ROLW\s\$8`,-`MOVB`,-`OR`
|
||||
// arm64:`REV16W`,`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB`
|
||||
// ppc64le:`MOVHBR`
|
||||
// s390x:`MOVHZ\s\(.*\)\(.*\*1\),`,-`OR`,-`ORW`,-`SLD`,-`SLW`
|
||||
sink16 = binary.BigEndian.Uint16(b[idx:])
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +355,7 @@ func store_le64(b []byte) {
|
|||
// amd64:`MOVQ\s.*\(.*\)$`,-`SHR.`
|
||||
// arm64:`MOVD`,-`MOV[WBH]`
|
||||
// ppc64le:`MOVD\s`,-`MOV[BHW]\s`
|
||||
// s390x:`MOVDBR\s.*\(.*\)$`
|
||||
binary.LittleEndian.PutUint64(b, sink64)
|
||||
}
|
||||
|
||||
|
|
@ -358,6 +363,7 @@ func store_le64_idx(b []byte, idx int) {
|
|||
// amd64:`MOVQ\s.*\(.*\)\(.*\*1\)$`,-`SHR.`
|
||||
// arm64:`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`
|
||||
// ppc64le:`MOVD\s`,-`MOV[BHW]\s`
|
||||
// s390x:`MOVDBR\s.*\(.*\)\(.*\*1\)$`
|
||||
binary.LittleEndian.PutUint64(b[idx:], sink64)
|
||||
}
|
||||
|
||||
|
|
@ -365,6 +371,7 @@ func store_le32(b []byte) {
|
|||
// amd64:`MOVL\s`
|
||||
// arm64:`MOVW`,-`MOV[BH]`
|
||||
// ppc64le:`MOVW\s`
|
||||
// s390x:`MOVWBR\s.*\(.*\)$`
|
||||
binary.LittleEndian.PutUint32(b, sink32)
|
||||
}
|
||||
|
||||
|
|
@ -372,6 +379,7 @@ func store_le32_idx(b []byte, idx int) {
|
|||
// amd64:`MOVL\s`
|
||||
// arm64:`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`
|
||||
// ppc64le:`MOVW\s`
|
||||
// s390x:`MOVWBR\s.*\(.*\)\(.*\*1\)$`
|
||||
binary.LittleEndian.PutUint32(b[idx:], sink32)
|
||||
}
|
||||
|
||||
|
|
@ -379,6 +387,7 @@ func store_le16(b []byte) {
|
|||
// amd64:`MOVW\s`
|
||||
// arm64:`MOVH`,-`MOVB`
|
||||
// ppc64le:`MOVH\s`
|
||||
// s390x:`MOVHBR\s.*\(.*\)$`
|
||||
binary.LittleEndian.PutUint16(b, sink16)
|
||||
}
|
||||
|
||||
|
|
@ -386,6 +395,7 @@ func store_le16_idx(b []byte, idx int) {
|
|||
// amd64:`MOVW\s`
|
||||
// arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB`
|
||||
// ppc64le:`MOVH\s`
|
||||
// s390x:`MOVHBR\s.*\(.*\)\(.*\*1\)$`
|
||||
binary.LittleEndian.PutUint16(b[idx:], sink16)
|
||||
}
|
||||
|
||||
|
|
@ -393,6 +403,7 @@ func store_be64(b []byte) {
|
|||
// amd64:`BSWAPQ`,-`SHR.`
|
||||
// arm64:`MOVD`,`REV`,-`MOV[WBH]`,-`REVW`,-`REV16W`
|
||||
// ppc64le:`MOVDBR`
|
||||
// s390x:`MOVD\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint64(b, sink64)
|
||||
}
|
||||
|
||||
|
|
@ -400,6 +411,7 @@ func store_be64_idx(b []byte, idx int) {
|
|||
// amd64:`BSWAPQ`,-`SHR.`
|
||||
// arm64:`REV`,`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`,-`REV16W`,-`REVW`
|
||||
// ppc64le:`MOVDBR`
|
||||
// s390x:`MOVD\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint64(b[idx:], sink64)
|
||||
}
|
||||
|
||||
|
|
@ -407,6 +419,7 @@ func store_be32(b []byte) {
|
|||
// amd64:`BSWAPL`,-`SHR.`
|
||||
// arm64:`MOVW`,`REVW`,-`MOV[BH]`,-`REV16W`
|
||||
// ppc64le:`MOVWBR`
|
||||
// s390x:`MOVW\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint32(b, sink32)
|
||||
}
|
||||
|
||||
|
|
@ -414,6 +427,7 @@ func store_be32_idx(b []byte, idx int) {
|
|||
// amd64:`BSWAPL`,-`SHR.`
|
||||
// arm64:`REVW`,`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`,-`REV16W`
|
||||
// ppc64le:`MOVWBR`
|
||||
// s390x:`MOVW\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint32(b[idx:], sink32)
|
||||
}
|
||||
|
||||
|
|
@ -421,6 +435,7 @@ func store_be16(b []byte) {
|
|||
// amd64:`ROLW\s\$8`,-`SHR.`
|
||||
// arm64:`MOVH`,`REV16W`,-`MOVB`
|
||||
// ppc64le:`MOVHBR`
|
||||
// s390x:`MOVH\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint16(b, sink16)
|
||||
}
|
||||
|
||||
|
|
@ -428,6 +443,7 @@ func store_be16_idx(b []byte, idx int) {
|
|||
// amd64:`ROLW\s\$8`,-`SHR.`
|
||||
// arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`MOVB`
|
||||
// ppc64le:`MOVHBR`
|
||||
// s390x:`MOVH\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
|
||||
binary.BigEndian.PutUint16(b[idx:], sink16)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue