From e008d965ca9231730f6d86b4d99fc1f181939ecb Mon Sep 17 00:00:00 2001 From: "xueliang.zhong" Date: Tue, 22 Jan 2019 18:01:45 +0000 Subject: [PATCH] Move Half implementations to libcore to allow ART optimizations Move Half FP16 implementations to libcore, to allow ART compiler to optimize these methods with intrinsic implementations. For example, on ARM64 with ARMv8.2 FP16 half registers and instructions: - Half toFloat/toHalf can be implemented with FCVT; - Half floor/ceil/round can be implmented with FRINT(pna); - Half max/min can be implmented with FMIN/FMAX. Such fast Half FP16 intrinsics can help accelerate ColorLong ARGB encoding/decoding in Android framework. Change-Id: I6225ebf8aa825b0394ce8f13e12db317f5c6e3fd --- core/java/android/util/Half.java | 274 +++++---------------------------------- 1 file changed, 33 insertions(+), 241 deletions(-) diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java index 70d049a6e985..fe536a6e4e68 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -20,6 +20,8 @@ import android.annotation.HalfFloat; import android.annotation.NonNull; import android.annotation.Nullable; +import libcore.util.FP16; + /** *

The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit * IEEE 754 @@ -148,25 +150,6 @@ public final class Half extends Number implements Comparable { */ public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000; - private static final int FP16_SIGN_SHIFT = 15; - private static final int FP16_SIGN_MASK = 0x8000; - private static final int FP16_EXPONENT_SHIFT = 10; - private static final int FP16_EXPONENT_MASK = 0x1f; - private static final int FP16_SIGNIFICAND_MASK = 0x3ff; - private static final int FP16_EXPONENT_BIAS = 15; - private static final int FP16_COMBINED = 0x7fff; - private static final int FP16_EXPONENT_MAX = 0x7c00; - - private static final int FP32_SIGN_SHIFT = 31; - private static final int FP32_EXPONENT_SHIFT = 23; - private static final int FP32_EXPONENT_MASK = 0xff; - private static final int FP32_SIGNIFICAND_MASK = 0x7fffff; - private static final int FP32_EXPONENT_BIAS = 127; - private static final int FP32_QNAN_MASK = 0x400000; - - private static final int FP32_DENORMAL_MAGIC = 126 << 23; - private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC); - private final @HalfFloat short mValue; /** @@ -414,16 +397,7 @@ public final class Half extends Number implements Comparable { * than {@code y} */ public static int compare(@HalfFloat short x, @HalfFloat short y) { - if (less(x, y)) return -1; - if (greater(x, y)) return 1; - - // Collapse NaNs, akin to halfToIntBits(), but we want to keep - // (signed) short value types to preserve the ordering of -0.0 - // and +0.0 - short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x; - short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y; - - return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1)); + return FP16.compare(x, y); } /** @@ -440,7 +414,7 @@ public final class Half extends Number implements Comparable { * @see #halfToIntBits(short) */ public static @HalfFloat short halfToShortBits(@HalfFloat short h) { - return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h; + return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h; } /** @@ -459,7 +433,7 @@ public final class Half extends Number implements Comparable { * @see #intBitsToHalf(int) */ public static int halfToIntBits(@HalfFloat short h) { - return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff; + return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff; } /** @@ -505,7 +479,7 @@ public final class Half extends Number implements Comparable { * of the second parameter */ public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) { - return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED)); + return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK)); } /** @@ -523,7 +497,7 @@ public final class Half extends Number implements Comparable { * @return The absolute value of the specified half-precision float */ public static @HalfFloat short abs(@HalfFloat short h) { - return (short) (h & FP16_COMBINED); + return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK); } /** @@ -538,26 +512,18 @@ public final class Half extends Number implements Comparable { * the result is zero (with the same sign) * * + *

+ * Note: Unlike the identically named + * int java.lang.Math.round(float) method, + * this returns a Half value stored in a short, not an + * actual short integer result. + * * @param h A half-precision float value * @return The value of the specified half-precision float rounded to the nearest * half-precision float value */ public static @HalfFloat short round(@HalfFloat short h) { - int bits = h & 0xffff; - int e = bits & 0x7fff; - int result = bits; - - if (e < 0x3c00) { - result &= FP16_SIGN_MASK; - result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0)); - } else if (e < 0x6400) { - e = 25 - (e >> 10); - int mask = (1 << e) - 1; - result += (1 << (e - 1)); - result &= ~mask; - } - - return (short) result; + return FP16.rint(h); } /** @@ -577,21 +543,7 @@ public final class Half extends Number implements Comparable { * greater than or equal to the specified half-precision float value */ public static @HalfFloat short ceil(@HalfFloat short h) { - int bits = h & 0xffff; - int e = bits & 0x7fff; - int result = bits; - - if (e < 0x3c00) { - result &= FP16_SIGN_MASK; - result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0)); - } else if (e < 0x6400) { - e = 25 - (e >> 10); - int mask = (1 << e) - 1; - result += mask & ((bits >> 15) - 1); - result &= ~mask; - } - - return (short) result; + return FP16.ceil(h); } /** @@ -611,21 +563,7 @@ public final class Half extends Number implements Comparable { * less than or equal to the specified half-precision float value */ public static @HalfFloat short floor(@HalfFloat short h) { - int bits = h & 0xffff; - int e = bits & 0x7fff; - int result = bits; - - if (e < 0x3c00) { - result &= FP16_SIGN_MASK; - result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0); - } else if (e < 0x6400) { - e = 25 - (e >> 10); - int mask = (1 << e) - 1; - result += mask & -(bits >> 15); - result &= ~mask; - } - - return (short) result; + return FP16.floor(h); } /** @@ -644,19 +582,7 @@ public final class Half extends Number implements Comparable { * half-precision float value */ public static @HalfFloat short trunc(@HalfFloat short h) { - int bits = h & 0xffff; - int e = bits & 0x7fff; - int result = bits; - - if (e < 0x3c00) { - result &= FP16_SIGN_MASK; - } else if (e < 0x6400) { - e = 25 - (e >> 10); - int mask = (1 << e) - 1; - result &= ~mask; - } - - return (short) result; + return FP16.trunc(h); } /** @@ -672,15 +598,7 @@ public final class Half extends Number implements Comparable { * @return The smaller of the two specified half-precision values */ public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; - - if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) { - return (x & FP16_SIGN_MASK) != 0 ? x : y; - } - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + return FP16.min(x, y); } /** @@ -697,15 +615,7 @@ public final class Half extends Number implements Comparable { * @return The larger of the two specified half-precision values */ public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN; - - if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) { - return (x & FP16_SIGN_MASK) != 0 ? y : x; - } - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y; + return FP16.max(x, y); } /** @@ -719,11 +629,7 @@ public final class Half extends Number implements Comparable { * @return True if x is less than y, false otherwise */ public static boolean less(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) < - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + return FP16.less(x, y); } /** @@ -737,11 +643,7 @@ public final class Half extends Number implements Comparable { * @return True if x is less than or equal to y, false otherwise */ public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <= - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + return FP16.lessEquals(x, y); } /** @@ -755,11 +657,7 @@ public final class Half extends Number implements Comparable { * @return True if x is greater than y, false otherwise */ public static boolean greater(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) > - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + return FP16.greater(x, y); } /** @@ -773,11 +671,7 @@ public final class Half extends Number implements Comparable { * @return True if x is greater than y, false otherwise */ public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - - return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >= - ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff); + return FP16.greaterEquals(x, y); } /** @@ -791,10 +685,7 @@ public final class Half extends Number implements Comparable { * @return True if x is equal to y, false otherwise */ public static boolean equals(@HalfFloat short x, @HalfFloat short y) { - if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false; - - return x == y || ((x | y) & FP16_COMBINED) == 0; + return FP16.equals(x, y); } /** @@ -804,7 +695,7 @@ public final class Half extends Number implements Comparable { * @return 1 if the value is positive, -1 if the value is negative */ public static int getSign(@HalfFloat short h) { - return (h & FP16_SIGN_MASK) == 0 ? 1 : -1; + return (h & FP16.SIGN_MASK) == 0 ? 1 : -1; } /** @@ -818,7 +709,7 @@ public final class Half extends Number implements Comparable { * @return The unbiased exponent of the specified value */ public static int getExponent(@HalfFloat short h) { - return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS; + return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS; } /** @@ -829,7 +720,7 @@ public final class Half extends Number implements Comparable { * @return The significand, or significand, of the specified vlaue */ public static int getSignificand(@HalfFloat short h) { - return h & FP16_SIGNIFICAND_MASK; + return h & FP16.SIGNIFICAND_MASK; } /** @@ -841,7 +732,7 @@ public final class Half extends Number implements Comparable { * false otherwise */ public static boolean isInfinite(@HalfFloat short h) { - return (h & FP16_COMBINED) == FP16_EXPONENT_MAX; + return FP16.isInfinite(h); } /** @@ -852,7 +743,7 @@ public final class Half extends Number implements Comparable { * @return True if the value is a NaN, false otherwise */ public static boolean isNaN(@HalfFloat short h) { - return (h & FP16_COMBINED) > FP16_EXPONENT_MAX; + return FP16.isNaN(h); } /** @@ -866,7 +757,7 @@ public final class Half extends Number implements Comparable { * @return True if the value is normalized, false otherwise */ public static boolean isNormalized(@HalfFloat short h) { - return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX; + return FP16.isNormalized(h); } /** @@ -885,35 +776,7 @@ public final class Half extends Number implements Comparable { * @return A normalized single-precision float value */ public static float toFloat(@HalfFloat short h) { - int bits = h & 0xffff; - int s = bits & FP16_SIGN_MASK; - int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (bits ) & FP16_SIGNIFICAND_MASK; - - int outE = 0; - int outM = 0; - - if (e == 0) { // Denormal or 0 - if (m != 0) { - // Convert denorm fp16 into normalized fp32 - float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m); - o -= FP32_DENORMAL_FLOAT; - return s == 0 ? o : -o; - } - } else { - outM = m << 13; - if (e == 0x1f) { // Infinite or NaN - outE = 0xff; - if (outM != 0) { // SNaNs are quieted - outM |= FP32_QNAN_MASK; - } - } else { - outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS; - } - } - - int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM; - return Float.intBitsToFloat(out); + return FP16.toFloat(h); } /** @@ -940,44 +803,7 @@ public final class Half extends Number implements Comparable { */ @SuppressWarnings("StatementWithEmptyBody") public static @HalfFloat short toHalf(float f) { - int bits = Float.floatToRawIntBits(f); - int s = (bits >>> FP32_SIGN_SHIFT ); - int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK; - int m = (bits ) & FP32_SIGNIFICAND_MASK; - - int outE = 0; - int outM = 0; - - if (e == 0xff) { // Infinite or NaN - outE = 0x1f; - outM = m != 0 ? 0x200 : 0; - } else { - e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS; - if (e >= 0x1f) { // Overflow - outE = 0x31; - } else if (e <= 0) { // Underflow - if (e < -10) { - // The absolute fp32 value is less than MIN_VALUE, flush to +/-0 - } else { - // The fp32 value is a normalized float less than MIN_NORMAL, - // we convert to a denorm fp16 - m = (m | 0x800000) >> (1 - e); - if ((m & 0x1000) != 0) m += 0x2000; - outM = m >> 13; - } - } else { - outE = e; - outM = m >> 13; - if ((m & 0x1000) != 0) { - // Round to nearest "0.5" up - int out = (outE << FP16_EXPONENT_SHIFT) | outM; - out++; - return (short) (out | (s << FP16_SIGN_SHIFT)); - } - } - } - - return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM); + return FP16.toHalf(f); } /** @@ -1073,40 +899,6 @@ public final class Half extends Number implements Comparable { */ @NonNull public static String toHexString(@HalfFloat short h) { - StringBuilder o = new StringBuilder(); - - int bits = h & 0xffff; - int s = (bits >>> FP16_SIGN_SHIFT ); - int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK; - int m = (bits ) & FP16_SIGNIFICAND_MASK; - - if (e == 0x1f) { // Infinite or NaN - if (m == 0) { - if (s != 0) o.append('-'); - o.append("Infinity"); - } else { - o.append("NaN"); - } - } else { - if (s == 1) o.append('-'); - if (e == 0) { - if (m == 0) { - o.append("0x0.0p0"); - } else { - o.append("0x0."); - String significand = Integer.toHexString(m); - o.append(significand.replaceFirst("0{2,}$", "")); - o.append("p-14"); - } - } else { - o.append("0x1."); - String significand = Integer.toHexString(m); - o.append(significand.replaceFirst("0{2,}$", "")); - o.append('p'); - o.append(Integer.toString(e - FP16_EXPONENT_BIAS)); - } - } - - return o.toString(); + return FP16.toHexString(h); } } -- 2.11.0