blob: a851bc889c940bf75881f4b3f26729307ce6cf58 [file]
// 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)))
}