// 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 rewriteppc64 import ( "math/bits" "cmd/compile/internal/ssa" ) func getPPC64ShiftMaskLength(v int64) int64 { return int64(bits.Len64(uint64(v))) } // Test if this mask is a valid, contiguous bitmask which can be // represented by a RLWNM mask and also clears the upper 32 bits // of the register. func isPPC64WordRotateMaskNonWrapping(v64 int64) bool { // Isolate rightmost 1 (if none 0) and add. v := uint32(v64) vp := (v & -v) + v return (v&vp == 0) && v != 0 && uint64(uint32(v64)) == uint64(v64) } // isU16Bit reports whether n can be represented as an unsigned 16 bit integer. func isU16Bit(n int64) bool { return n == int64(uint16(n)) } // Test if RLWINM feeding into an ANDconst can be merged. Return the encoded RLWINM constant, // or 0 if they cannot be merged. func mergePPC64AndRlwinm(mask uint32, rlw int64) int64 { r, _, _, mask_rlw := ssa.DecodePPC64RotateMask(rlw) mask_out := (mask_rlw & uint64(mask)) // Verify the result is still a valid bitmask of <= 32 bits. if !ssa.IsPPC64WordRotateMask(int64(mask_out)) { return 0 } return ssa.EncodePPC64RotateMask(r, int64(mask_out), 32) } // Combine (ANDconst [m] (SLDconst [s])) into (RLWINM [y]) or return 0 func mergePPC64AndSldi(m, s int64) int64 { mask := -1 << s & m // Verify the rotate and mask result only uses the lower 32 bits. rv := bits.RotateLeft64(0xFFFFFFFF00000000, int(s)) if rv&uint64(mask) != 0 { return 0 } if !isPPC64WordRotateMaskNonWrapping(mask) { return 0 } return ssa.EncodePPC64RotateMask(s&31, mask, 32) } // Combine (ANDconst [m] (SRDconst [s])) into (RLWINM [y]) or return 0 func mergePPC64AndSrdi(m, s int64) int64 { mask := ssa.MergePPC64RShiftMask(m, s, 64) // Verify the rotate and mask result only uses the lower 32 bits. rv := bits.RotateLeft64(0xFFFFFFFF00000000, -int(s)) if rv&uint64(mask) != 0 { return 0 } if !isPPC64WordRotateMaskNonWrapping(mask) { return 0 } return ssa.EncodePPC64RotateMask((32-s)&31, mask, 32) } // Test if a doubleword shift right feeding into a CLRLSLDI can be merged into RLWINM. // Return the encoded RLWINM constant, or 0 if they cannot be merged. func mergePPC64ClrlsldiSrd(sld, srd int64) int64 { mask_1 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(srd) // for CLRLSLDI, it's more convenient to think of it as a mask left bits then rotate left. mask_2 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(ssa.GetPPC64Shiftmb(sld)) // Rewrite mask to apply after the final left shift. mask_3 := (mask_1 & mask_2) << uint(ssa.GetPPC64Shiftsh(sld)) r_1 := 64 - srd r_2 := ssa.GetPPC64Shiftsh(sld) r_3 := (r_1 + r_2) & 63 // This can wrap. if uint64(uint32(mask_3)) != mask_3 || mask_3 == 0 { return 0 } // This combine only works when selecting and shifting the lower 32 bits. v1 := bits.RotateLeft64(0xFFFFFFFF00000000, int(r_3)) if v1&mask_3 != 0 { return 0 } return ssa.EncodePPC64RotateMask(r_3&31, int64(mask_3), 32) } // Test if RLWINM opcode rlw clears the upper 32 bits of the // result. Return rlw if it does, 0 otherwise. func mergePPC64MovwzregRlwinm(rlw int64) int64 { _, mb, me, _ := ssa.DecodePPC64RotateMask(rlw) if mb > me { return 0 } return rlw } // Test if AND feeding into an ANDconst can be merged. Return the encoded RLWINM constant, // or 0 if they cannot be merged. func mergePPC64RlwinmAnd(rlw int64, mask uint32) int64 { r, _, _, mask_rlw := ssa.DecodePPC64RotateMask(rlw) // Rotate the input mask, combine with the rlwnm mask, and test if it is still a valid rlwinm mask. r_mask := bits.RotateLeft32(mask, int(r)) mask_out := (mask_rlw & uint64(r_mask)) // Verify the result is still a valid bitmask of <= 32 bits. if !ssa.IsPPC64WordRotateMask(int64(mask_out)) { return 0 } return ssa.EncodePPC64RotateMask(r, int64(mask_out), 32) } // Test if RLWINM feeding into SRDconst can be merged. Return the encoded RLIWNM constant, // or 0 if they cannot be merged. func mergePPC64SldiRlwinm(sldi, rlw int64) int64 { r_1, mb, me, mask_1 := ssa.DecodePPC64RotateMask(rlw) if mb > me || mb < sldi { // Wrapping masks cannot be merged as the upper 32 bits are effectively undefined in this case. // Likewise, if mb is less than the shift amount, it cannot be merged. return 0 } // combine the masks, and adjust for the final left shift. mask_3 := mask_1 << sldi r_3 := (r_1 + sldi) & 31 // This can wrap. // Verify the result is still a valid bitmask of <= 32 bits. if uint64(uint32(mask_3)) != mask_3 { return 0 } return ssa.EncodePPC64RotateMask(r_3, int64(mask_3), 32) } // Convenience function to rotate a 32 bit constant value by another constant. func rotateLeft32(v, rotate int64) int64 { return int64(bits.RotateLeft32(uint32(v), int(rotate))) }