| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package rewritearm64 |
| |
| import ( |
| "math/bits" |
| |
| "cmd/compile/internal/ssa" |
| "cmd/compile/internal/ssa/ssaop" |
| ) |
| |
| // returns the bitfield width of mask >> rshift for arm64 bitfield ops. |
| func arm64BFWidth(mask, rshift int64) int64 { |
| shiftedMask := int64(uint64(mask) >> uint64(rshift)) |
| if shiftedMask == 0 { |
| panic("ARM64 BF mask is zero") |
| } |
| return nto(shiftedMask) |
| } |
| |
| // arm64Invert evaluates (InvertFlags op), which |
| // is the same as altering the condition codes such |
| // that the same result would be produced if the arguments |
| // to the flag-generating instruction were reversed, e.g. |
| // (InvertFlags (CMP x y)) -> (CMP y x) |
| func arm64Invert(op ssaop.Op) ssaop.Op { |
| switch op { |
| case ssaop.OpARM64LessThan: |
| return ssaop.OpARM64GreaterThan |
| case ssaop.OpARM64LessThanU: |
| return ssaop.OpARM64GreaterThanU |
| case ssaop.OpARM64GreaterThan: |
| return ssaop.OpARM64LessThan |
| case ssaop.OpARM64GreaterThanU: |
| return ssaop.OpARM64LessThanU |
| case ssaop.OpARM64LessEqual: |
| return ssaop.OpARM64GreaterEqual |
| case ssaop.OpARM64LessEqualU: |
| return ssaop.OpARM64GreaterEqualU |
| case ssaop.OpARM64GreaterEqual: |
| return ssaop.OpARM64LessEqual |
| case ssaop.OpARM64GreaterEqualU: |
| return ssaop.OpARM64LessEqualU |
| case ssaop.OpARM64Equal, ssaop.OpARM64NotEqual: |
| return op |
| case ssaop.OpARM64LessThanF: |
| return ssaop.OpARM64GreaterThanF |
| case ssaop.OpARM64GreaterThanF: |
| return ssaop.OpARM64LessThanF |
| case ssaop.OpARM64LessEqualF: |
| return ssaop.OpARM64GreaterEqualF |
| case ssaop.OpARM64GreaterEqualF: |
| return ssaop.OpARM64LessEqualF |
| case ssaop.OpARM64NotLessThanF: |
| return ssaop.OpARM64NotGreaterThanF |
| case ssaop.OpARM64NotGreaterThanF: |
| return ssaop.OpARM64NotLessThanF |
| case ssaop.OpARM64NotLessEqualF: |
| return ssaop.OpARM64NotGreaterEqualF |
| case ssaop.OpARM64NotGreaterEqualF: |
| return ssaop.OpARM64NotLessEqualF |
| default: |
| panic("unreachable") |
| } |
| } |
| |
| // arm64Negate finds the complement to an ARM64 condition code, |
| // for example !Equal -> NotEqual or !LessThan -> GreaterEqual |
| // |
| // For floating point, it's more subtle because NaN is unordered. We do |
| // !LessThanF -> NotLessThanF, the latter takes care of NaNs. |
| func arm64Negate(op ssaop.Op) ssaop.Op { |
| switch op { |
| case ssaop.OpARM64LessThan: |
| return ssaop.OpARM64GreaterEqual |
| case ssaop.OpARM64LessThanU: |
| return ssaop.OpARM64GreaterEqualU |
| case ssaop.OpARM64GreaterThan: |
| return ssaop.OpARM64LessEqual |
| case ssaop.OpARM64GreaterThanU: |
| return ssaop.OpARM64LessEqualU |
| case ssaop.OpARM64LessEqual: |
| return ssaop.OpARM64GreaterThan |
| case ssaop.OpARM64LessEqualU: |
| return ssaop.OpARM64GreaterThanU |
| case ssaop.OpARM64GreaterEqual: |
| return ssaop.OpARM64LessThan |
| case ssaop.OpARM64GreaterEqualU: |
| return ssaop.OpARM64LessThanU |
| case ssaop.OpARM64Equal: |
| return ssaop.OpARM64NotEqual |
| case ssaop.OpARM64NotEqual: |
| return ssaop.OpARM64Equal |
| case ssaop.OpARM64LessThanF: |
| return ssaop.OpARM64NotLessThanF |
| case ssaop.OpARM64NotLessThanF: |
| return ssaop.OpARM64LessThanF |
| case ssaop.OpARM64LessEqualF: |
| return ssaop.OpARM64NotLessEqualF |
| case ssaop.OpARM64NotLessEqualF: |
| return ssaop.OpARM64LessEqualF |
| case ssaop.OpARM64GreaterThanF: |
| return ssaop.OpARM64NotGreaterThanF |
| case ssaop.OpARM64NotGreaterThanF: |
| return ssaop.OpARM64GreaterThanF |
| case ssaop.OpARM64GreaterEqualF: |
| return ssaop.OpARM64NotGreaterEqualF |
| case ssaop.OpARM64NotGreaterEqualF: |
| return ssaop.OpARM64GreaterEqualF |
| default: |
| panic("unreachable") |
| } |
| } |
| |
| // evaluate an ARM64 op against a flags value |
| // that is potentially constant; return 1 for true, |
| // -1 for false, and 0 for not constant. |
| func ccARM64Eval(op ssaop.Op, flags *ssa.Value) int { |
| fop := flags.Op |
| if fop == ssaop.OpARM64InvertFlags { |
| return -ccARM64Eval(op, flags.Args[0]) |
| } |
| if fop != ssaop.OpARM64FlagConstant { |
| return 0 |
| } |
| fc := ssa.FlagConstant(flags.AuxInt) |
| b2i := func(b bool) int { |
| if b { |
| return 1 |
| } |
| return -1 |
| } |
| switch op { |
| case ssaop.OpARM64Equal: |
| return b2i(fc.Eq()) |
| case ssaop.OpARM64NotEqual: |
| return b2i(fc.Ne()) |
| case ssaop.OpARM64LessThan: |
| return b2i(fc.Lt()) |
| case ssaop.OpARM64LessThanU: |
| return b2i(fc.Ult()) |
| case ssaop.OpARM64GreaterThan: |
| return b2i(fc.Gt()) |
| case ssaop.OpARM64GreaterThanU: |
| return b2i(fc.Ugt()) |
| case ssaop.OpARM64LessEqual: |
| return b2i(fc.Le()) |
| case ssaop.OpARM64LessEqualU: |
| return b2i(fc.Ule()) |
| case ssaop.OpARM64GreaterEqual: |
| return b2i(fc.Ge()) |
| case ssaop.OpARM64GreaterEqualU: |
| return b2i(fc.Uge()) |
| } |
| return 0 |
| } |
| |
| // checks if mask >> rshift applied at lsb is a valid arm64 bitfield op mask. |
| func isARM64BFMask(lsb, mask, rshift int64) bool { |
| shiftedMask := int64(uint64(mask) >> uint64(rshift)) |
| return shiftedMask != 0 && ssa.IsPowerOfTwo(shiftedMask+1) && nto(shiftedMask)+lsb < 64 |
| } |
| |
| // nto returns the number of trailing ones. |
| func nto(x int64) int64 { |
| return int64(ssa.Ntz64(^x)) |
| } |
| |
| func rotateRight64(v, rotate int64) int64 { |
| return int64(bits.RotateLeft64(uint64(v), int(-rotate))) |
| } |