diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go index 98627344b8..9a8fabf622 100644 --- a/src/cmd/compile/internal/arm/ssa.go +++ b/src/cmd/compile/internal/arm/ssa.go @@ -7,6 +7,7 @@ package arm import ( "fmt" "math" + "math/bits" "cmd/compile/internal/gc" "cmd/compile/internal/ssa" @@ -119,6 +120,28 @@ func genregshift(s *gc.SSAGenState, as obj.As, r0, r1, r2, r int16, typ int64) * return p } +// find a (lsb, width) pair for BFC +// lsb must be in [0, 31], width must be in [1, 32 - lsb] +// return (0xffffffff, 0) if v is not a binary like 0...01...10...0 +func getBFC(v uint32) (uint32, uint32) { + var m, l uint32 + // BFC is not applicable with zero + if v == 0 { + return 0xffffffff, 0 + } + // find the lowest set bit, for example l=2 for 0x3ffffffc + l = uint32(bits.TrailingZeros32(v)) + // m-1 represents the highest set bit index, for example m=30 for 0x3ffffffc + m = 32 - uint32(bits.LeadingZeros32(v)) + // check if v is a binary like 0...01...10...0 + if (1< l for non-zero v + return l, m - l + } + // invalid + return 0xffffffff, 0 +} + func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { switch v.Op { case ssa.OpCopy, ssa.OpARMMOVWreg: @@ -267,16 +290,38 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpARMANDconst, ssa.OpARMBICconst: + // try to optimize ANDconst and BICconst to BFC, which saves bytes and ticks + // BFC is only available on ARMv7, and its result and source are in the same register + if objabi.GOARM == 7 && v.Reg() == v.Args[0].Reg() { + var val uint32 + if v.Op == ssa.OpARMANDconst { + val = ^uint32(v.AuxInt) + } else { // BICconst + val = uint32(v.AuxInt) + } + lsb, width := getBFC(val) + // omit BFC for ARM's imm12 + if 8 < width && width < 24 { + p := s.Prog(arm.ABFC) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(width) + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: int64(lsb)}) + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + break + } + } + // fall back to ordinary form + fallthrough case ssa.OpARMADDconst, ssa.OpARMADCconst, ssa.OpARMSUBconst, ssa.OpARMSBCconst, ssa.OpARMRSBconst, ssa.OpARMRSCconst, - ssa.OpARMANDconst, ssa.OpARMORconst, ssa.OpARMXORconst, - ssa.OpARMBICconst, ssa.OpARMSLLconst, ssa.OpARMSRLconst, ssa.OpARMSRAconst: diff --git a/test/codegen/bits.go b/test/codegen/bits.go index c46f75845c..e95e3f64cd 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -284,9 +284,12 @@ func and_mask_2(a uint64) uint64 { return a & (1 << 63) } -func and_mask_3(a uint32) uint32 { +func and_mask_3(a, b uint32) (uint32, uint32) { // arm/7:`BIC`,-`AND` - return a & 0xffff0000 + a &= 0xffffaaaa + // arm/7:`BFC`,-`AND`,-`BIC` + b &= 0xffc003ff + return a, b } // Check generation of arm64 BIC/EON/ORN instructions