From 5f41e08cf39d585d600aa506cdcd2f5380c60ddd Mon Sep 17 00:00:00 2001 From: Roland Scheidegger Date: Fri, 29 Mar 2013 06:16:33 +0100 Subject: [PATCH] gallivm: consolidate some half-to-float and r11g11b10-to-float code 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 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); } diff --git a/src/gallium/auxiliary/gallivm/lp_bld_format.h b/src/gallium/auxiliary/gallivm/lp_bld_format.h index f9ddc68d531..aa8c729b819 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_format.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_format.h @@ -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); diff --git a/src/gallium/auxiliary/gallivm/lp_bld_format_float.c b/src/gallium/auxiliary/gallivm/lp_bld_format_float.c index efab344e627..352068a21b5 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_format_float.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_format_float.c @@ -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); -- 2.30.2