target/arm: Fix 48-bit saturating shifts

In do_sqrshl48_d() and do_uqrshl48_d() we got some of the edge
cases wrong and failed to saturate correctly:

(1) In do_sqrshl48_d() we used the same code that do_shrshl_bhs()
does to obtain the saturated most-negative and most-positive 48-bit
signed values for the large-shift-left case.  This gives (1 << 47)
for saturate-to-most-negative, but we weren't sign-extending this
value to the 64-bit output as the pseudocode requires.

(2) For left shifts by less than 48, we copied the "8/16 bit" code
from do_sqrshl_bhs() and do_uqrshl_bhs().  This doesn't do the right
thing because it assumes the C type we're working with is at least
twice the number of bits we're saturating to (so that a shift left by
bits-1 can't shift anything off the top of the value).  This isn't
true for bits == 48, so we would incorrectly return 0 rather than the
most-positive value for situations like "shift (1 << 44) right by
20".  Instead check for saturation by doing the shift and signextend
and then testing whether shifting back left again gives the original
value.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Peter Maydell 2021-08-13 17:11:48 +01:00
parent a5e59e8dcb
commit 95351aa76c
1 changed files with 5 additions and 7 deletions

View File

@ -1576,9 +1576,8 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift,
}
return src >> -shift;
} else if (shift < 48) {
int64_t val = src << shift;
int64_t extval = sextract64(val, 0, 48);
if (!sat || val == extval) {
int64_t extval = sextract64(src << shift, 0, 48);
if (!sat || src == (extval >> shift)) {
return extval;
}
} else if (!sat || src == 0) {
@ -1586,7 +1585,7 @@ static inline int64_t do_sqrshl48_d(int64_t src, int64_t shift,
}
*sat = 1;
return (1ULL << 47) - (src >= 0);
return src >= 0 ? MAKE_64BIT_MASK(0, 47) : MAKE_64BIT_MASK(47, 17);
}
/* Operate on 64-bit values, but saturate at 48 bits */
@ -1609,9 +1608,8 @@ static inline uint64_t do_uqrshl48_d(uint64_t src, int64_t shift,
return extval;
}
} else if (shift < 48) {
uint64_t val = src << shift;
uint64_t extval = extract64(val, 0, 48);
if (!sat || val == extval) {
uint64_t extval = extract64(src << shift, 0, 48);
if (!sat || src == (extval >> shift)) {
return extval;
}
} else if (!sat || src == 0) {