gallivm: consolidate some half-to-float and r11g11b10-to-float code
authorRoland Scheidegger <sroland@vmware.com>
Fri, 29 Mar 2013 05:16:33 +0000 (06:16 +0100)
committerRoland Scheidegger <sroland@vmware.com>
Fri, 29 Mar 2013 15:39:40 +0000 (16:39 +0100)
Similar enough that we can try to use shared code.
v2: fix a stupid bug using wrong variable causing mayhem with Inf and NaNs.

Reviewed-by: Jose Fonseca <jfonseca@vmware.com
src/gallium/auxiliary/gallivm/lp_bld_conv.c
src/gallium/auxiliary/gallivm/lp_bld_format.h
src/gallium/auxiliary/gallivm/lp_bld_format_float.c

index dc3649db363aab8fc955f583ef04752852f1c010..43c59f36e57e09f22248b7ac35a3b2013fa5443b 100644 (file)
@@ -75,6 +75,7 @@
 #include "lp_bld_logic.h"
 #include "lp_bld_intr.h"
 #include "lp_bld_printf.h"
+#include "lp_bld_format.h"
 
 
 
@@ -156,61 +157,28 @@ lp_build_bswap_vec(struct gallivm_state *gallivm,
 
 /**
  * Converts int16 half-float to float32
- * Note this can be performed in 1 instruction if vcvtph2ps exists (sse5 i think?)
+ * Note this can be performed in 1 instruction if vcvtph2ps exists (f16c/cvt16)
  * [llvm.x86.vcvtph2ps / _mm_cvtph_ps]
  *
  * @param src           value to convert
  *
- * ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
- * ref https://gist.github.com/2144712
  */
 LLVMValueRef
 lp_build_half_to_float(struct gallivm_state *gallivm,
                        LLVMValueRef src)
 {
-   int src_length = LLVMGetVectorSize(LLVMTypeOf(src));
+   LLVMBuilderRef builder = gallivm->builder;
+   LLVMTypeRef src_type = LLVMTypeOf(src);
+   unsigned src_length = LLVMGetTypeKind(src_type) == LLVMVectorTypeKind ?
+                            LLVMGetVectorSize(src_type) : 1;
 
    struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
    struct lp_type i32_type = lp_type_int_vec(32, 32 * src_length);
-
-   LLVMBuilderRef builder = gallivm->builder;
    LLVMTypeRef int_vec_type = lp_build_vec_type(gallivm, i32_type);
-   LLVMTypeRef float_vec_type = lp_build_vec_type(gallivm, f32_type);
-
-   /* Constants */
-   LLVMValueRef i32_13          = lp_build_const_int_vec(gallivm, i32_type, 13);
-   LLVMValueRef i32_16          = lp_build_const_int_vec(gallivm, i32_type, 16);
-   LLVMValueRef i32_mask_nosign = lp_build_const_int_vec(gallivm, i32_type, 0x7fff);
-   LLVMValueRef i32_was_infnan  = lp_build_const_int_vec(gallivm, i32_type, 0x7bff);
-   LLVMValueRef i32_exp_infnan  = lp_build_const_int_vec(gallivm, i32_type, 0xff << 23);
-   LLVMValueRef f32_magic       = LLVMBuildBitCast(builder,
-                                                   lp_build_const_int_vec(gallivm, i32_type, (254 - 15) << 23),
-                                                   float_vec_type, "");
 
    /* Convert int16 vector to int32 vector by zero ext */
    LLVMValueRef h             = LLVMBuildZExt(builder, src, int_vec_type, "");
-
-   /* Exponent / mantissa bits */
-   LLVMValueRef expmant       = LLVMBuildAnd(builder, i32_mask_nosign, h, "");
-   LLVMValueRef shifted       = LLVMBuildBitCast(builder, LLVMBuildShl(builder, expmant, i32_13, ""), float_vec_type, "");
-
-   /* Exponent adjust */
-   LLVMValueRef scaled        = LLVMBuildBitCast(builder, LLVMBuildFMul(builder, shifted, f32_magic, ""), int_vec_type, "");
-
-   /* Make sure Inf/NaN survive */
-   LLVMValueRef b_wasinfnan   = lp_build_compare(gallivm, i32_type, PIPE_FUNC_GREATER, expmant, i32_was_infnan);
-   LLVMValueRef infnanexp     = LLVMBuildAnd(builder, b_wasinfnan, i32_exp_infnan, "");
-
-   /* Sign bit */
-   LLVMValueRef justsign      = LLVMBuildXor(builder, h, expmant, "");
-   LLVMValueRef sign          = LLVMBuildShl(builder, justsign, i32_16, "");
-
-   /* Combine result */
-   LLVMValueRef sign_inf      = LLVMBuildOr(builder, sign, infnanexp, "");
-   LLVMValueRef final         = LLVMBuildOr(builder, scaled, sign_inf, "");
-
-   /* Cast from int32 vector to float32 vector */
-   return LLVMBuildBitCast(builder, final, float_vec_type, "");
+   return lp_build_smallfloat_to_float(gallivm, f32_type, h, 10, 5, 0, true);
 }
 
 
index f9ddc68d53190c097d3449e0b170e16419f9db30..aa8c729b8198709e4bdca00564692ee149ffca02 100644 (file)
@@ -126,6 +126,15 @@ lp_build_fetch_subsampled_rgba_aos(struct gallivm_state *gallivm,
  * special float formats
  */
 
+LLVMValueRef
+lp_build_smallfloat_to_float(struct gallivm_state *gallivm,
+                             struct lp_type f32_type,
+                             LLVMValueRef src,
+                             unsigned mantissa_bits,
+                             unsigned exponent_bits,
+                             unsigned mantissa_start,
+                             boolean has_sign);
+
 LLVMValueRef
 lp_build_float_to_r11g11b10(struct gallivm_state *gallivm,
                             LLVMValueRef *src);
index efab344e6277cf6dd89f00a1a8797b0d4cb19844..352068a21b57aa281c06d30f5615d1c0a9e2e0bc 100644 (file)
@@ -186,30 +186,34 @@ lp_build_float_to_r11g11b10(struct gallivm_state *gallivm,
  * Convert a float-like value with less exponent and mantissa
  * bits than a normal float32 to a float32. The mantissa of
  * the source value is assumed to have an implied 1, and the exponent
- * is biased. There are no negative values.
- * The source value to extract must be in a 32bit int.
- * While this helper is generic, it is only ever going to be useful for
- * r11g11b10 (no other common format exists with the same properties).
+ * is biased. There may be a sign bit.
+ * The source value to extract must be in a 32bit int (bits not part of
+ * the value to convert will be masked off).
+ * This works for things like 11-bit floats or half-floats,
+ * mantissa, exponent (and sign if present) must be packed
+ * the same as they are in a ordinary float.
  *
  * @param src             (vector) value to convert
  * @param mantissa_bits   the number of mantissa bits
  * @param exponent_bits   the number of exponent bits
  * @param mantissa_start  the bit start position of the packed component
+ * @param has_sign        if the small float has a sign bit
  *
  * ref http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
  * ref https://gist.github.com/rygorous/2156668
  */
-static LLVMValueRef
-lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
-                                    struct lp_type f32_type,
-                                    LLVMValueRef src,
-                                    unsigned mantissa_bits,
-                                    unsigned exponent_bits,
-                                    unsigned mantissa_start)
+LLVMValueRef
+lp_build_smallfloat_to_float(struct gallivm_state *gallivm,
+                             struct lp_type f32_type,
+                             LLVMValueRef src,
+                             unsigned mantissa_bits,
+                             unsigned exponent_bits,
+                             unsigned mantissa_start,
+                             boolean has_sign)
 {
    LLVMBuilderRef builder = gallivm->builder;
    LLVMValueRef smallexpmask, i32_floatexpmask, magic;
-   LLVMValueRef wasinfnan, tmp, res, shift, mask;
+   LLVMValueRef wasinfnan, tmp, res, shift, maskabs, srcabs, sign;
    unsigned exponent_start = mantissa_start + mantissa_bits;
    struct lp_type i32_type = lp_type_int_vec(32, 32 * f32_type.length);
    struct lp_build_context f32_bld, i32_bld;
@@ -226,11 +230,11 @@ lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
       shift = lp_build_const_int_vec(gallivm, i32_type, exponent_start - 23);
       src = lp_build_shr(&i32_bld, src, shift);
    }
-   mask = lp_build_const_int_vec(gallivm, i32_type,
-                                 ((1 << (mantissa_bits + exponent_bits)) - 1) <<
-                                 (23 - mantissa_bits));
-   src = lp_build_and(&i32_bld, src, mask);
-   src = LLVMBuildBitCast(builder, src, f32_bld.vec_type, "");
+   maskabs = lp_build_const_int_vec(gallivm, i32_type,
+                                    ((1 << (mantissa_bits + exponent_bits)) - 1)
+                                    << (23 - mantissa_bits));
+   srcabs = lp_build_and(&i32_bld, src, maskabs);
+   srcabs = LLVMBuildBitCast(builder, srcabs, f32_bld.vec_type, "");
 
    /* now do the actual scaling */
    smallexpmask = lp_build_const_int_vec(gallivm, i32_type,
@@ -245,19 +249,27 @@ lp_build_smallfloat_nosign_to_float(struct gallivm_state *gallivm,
    magic = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, "");
 
    /* adjust exponent and fix denorms */
-   res = lp_build_mul(&f32_bld, src, magic);
+   res = lp_build_mul(&f32_bld, srcabs, magic);
 
    /*
     * if exp was max (== NaN or Inf) set new exp to max (keep mantissa),
     * so a simple "or" will do (because exp adjust will leave mantissa intact)
     */
-   /* use float compare (better for AVX 8-wide / no AVX2 though otherwise should use int) */
-   smallexpmask = LLVMBuildBitCast(builder, magic, f32_bld.vec_type, "");
-   wasinfnan = lp_build_compare(gallivm, f32_type, PIPE_FUNC_GEQUAL, src, smallexpmask);
+   /* use float compare (better for AVX 8-wide / no AVX2 but else should use int) */
+   smallexpmask = LLVMBuildBitCast(builder, smallexpmask, f32_bld.vec_type, "");
+   wasinfnan = lp_build_compare(gallivm, f32_type, PIPE_FUNC_GEQUAL, srcabs, smallexpmask);
    res = LLVMBuildBitCast(builder, res, i32_bld.vec_type, "");
    tmp = lp_build_and(&i32_bld, i32_floatexpmask, wasinfnan);
    res = lp_build_or(&i32_bld, tmp, res);
 
+   if (has_sign) {
+      LLVMValueRef signmask = lp_build_const_int_vec(gallivm, i32_type, 0x80000000);
+      shift = lp_build_const_int_vec(gallivm, i32_type, 8 - exponent_bits);
+      sign = lp_build_shl(&i32_bld, src, shift);
+      sign = lp_build_and(&i32_bld, signmask, src);
+      res = lp_build_or(&i32_bld, res, sign);
+   }
+
    return LLVMBuildBitCast(builder, res, f32_bld.vec_type, "");
 }
 
@@ -278,9 +290,9 @@ lp_build_r11g11b10_to_float(struct gallivm_state *gallivm,
                             LLVMGetVectorSize(src_type) : 1;
    struct lp_type f32_type = lp_type_float_vec(32, 32 * src_length);
 
-   dst[0] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 0);
-   dst[1] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 6, 5, 11);
-   dst[2] = lp_build_smallfloat_nosign_to_float(gallivm, f32_type, src, 5, 5, 22);
+   dst[0] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 6, 5, 0, false);
+   dst[1] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 6, 5, 11, false);
+   dst[2] = lp_build_smallfloat_to_float(gallivm, f32_type, src, 5, 5, 22, false);
 
    /* Just set alpha to one */
    dst[3] = lp_build_one(gallivm, f32_type);