#include "lp_bld_arit.h"
#include "lp_bld_flow.h"
+#if defined(PIPE_ARCH_SSE)
+#include <xmmintrin.h>
+#endif
+
+#ifndef _MM_DENORMALS_ZERO_MASK
+#define _MM_DENORMALS_ZERO_MASK 0x0040
+#endif
+
+#ifndef _MM_FLUSH_ZERO_MASK
+#define _MM_FLUSH_ZERO_MASK 0x8000
+#endif
#define EXP_POLY_DEGREE 5
}
}
else if (type.floating && util_cpu_caps.has_altivec) {
- if (nan_behavior == GALLIVM_NAN_RETURN_NAN) {
+ if (nan_behavior == GALLIVM_NAN_RETURN_NAN ||
+ nan_behavior == GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN) {
debug_printf("%s: altivec doesn't support nan return nan behavior\n",
__FUNCTION__);
}
}
}
- if(intrinsic) {
+ if (intrinsic) {
/* We need to handle nan's for floating point numbers. If one of the
* inputs is nan the other should be returned (required by both D3D10+
* and OpenCL).
*/
if (util_cpu_caps.has_sse && type.floating &&
nan_behavior != GALLIVM_NAN_BEHAVIOR_UNDEFINED &&
- nan_behavior != GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN) {
- LLVMValueRef isnan, max;
- max = lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
+ nan_behavior != GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN &&
+ nan_behavior != GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN) {
+ LLVMValueRef isnan, min;
+ min = lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
type,
intr_size, a, b);
if (nan_behavior == GALLIVM_NAN_RETURN_OTHER) {
isnan = lp_build_isnan(bld, b);
- return lp_build_select(bld, isnan, a, max);
+ return lp_build_select(bld, isnan, a, min);
} else {
assert(nan_behavior == GALLIVM_NAN_RETURN_NAN);
isnan = lp_build_isnan(bld, a);
- return lp_build_select(bld, isnan, a, max);
+ return lp_build_select(bld, isnan, a, min);
}
} else {
return lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
case GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN:
cond = lp_build_cmp_ordered(bld, PIPE_FUNC_LESS, a, b);
return lp_build_select(bld, cond, a, b);
+ case GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN:
+ cond = lp_build_cmp(bld, PIPE_FUNC_LESS, b, a);
+ return lp_build_select(bld, cond, b, a);
case GALLIVM_NAN_BEHAVIOR_UNDEFINED:
cond = lp_build_cmp(bld, PIPE_FUNC_LESS, a, b);
return lp_build_select(bld, cond, a, b);
}
}
else if (type.floating && util_cpu_caps.has_altivec) {
- if (nan_behavior == GALLIVM_NAN_RETURN_NAN) {
+ if (nan_behavior == GALLIVM_NAN_RETURN_NAN ||
+ nan_behavior == GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN) {
debug_printf("%s: altivec doesn't support nan return nan behavior\n",
__FUNCTION__);
}
}
}
- if(intrinsic) {
+ if (intrinsic) {
if (util_cpu_caps.has_sse && type.floating &&
nan_behavior != GALLIVM_NAN_BEHAVIOR_UNDEFINED &&
- nan_behavior != GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN) {
- LLVMValueRef isnan, min;
- min = lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
+ nan_behavior != GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN &&
+ nan_behavior != GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN) {
+ LLVMValueRef isnan, max;
+ max = lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
type,
intr_size, a, b);
if (nan_behavior == GALLIVM_NAN_RETURN_OTHER) {
isnan = lp_build_isnan(bld, b);
- return lp_build_select(bld, isnan, a, min);
+ return lp_build_select(bld, isnan, a, max);
} else {
assert(nan_behavior == GALLIVM_NAN_RETURN_NAN);
isnan = lp_build_isnan(bld, a);
- return lp_build_select(bld, isnan, a, min);
+ return lp_build_select(bld, isnan, a, max);
}
} else {
return lp_build_intrinsic_binary_anylength(bld->gallivm, intrinsic,
case GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN:
cond = lp_build_cmp_ordered(bld, PIPE_FUNC_GREATER, a, b);
return lp_build_select(bld, cond, a, b);
+ case GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN:
+ cond = lp_build_cmp(bld, PIPE_FUNC_GREATER, b, a);
+ return lp_build_select(bld, cond, b, a);
case GALLIVM_NAN_BEHAVIOR_UNDEFINED:
cond = lp_build_cmp(bld, PIPE_FUNC_GREATER, a, b);
return lp_build_select(bld, cond, a, b);
}
}
- if(intrinsic)
+ if (intrinsic)
return lp_build_intrinsic_binary(builder, intrinsic, lp_build_vec_type(bld->gallivm, bld->type), a, b);
}
- /* TODO: handle signed case */
- if(type.norm && !type.floating && !type.fixed && !type.sign)
- a = lp_build_min_simple(bld, a, lp_build_comp(bld, b), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ if(type.norm && !type.floating && !type.fixed) {
+ if (type.sign) {
+ uint64_t sign = (uint64_t)1 << (type.width - 1);
+ LLVMValueRef max_val = lp_build_const_int_vec(bld->gallivm, type, sign - 1);
+ LLVMValueRef min_val = lp_build_const_int_vec(bld->gallivm, type, sign);
+ /* a_clamp_max is the maximum a for positive b,
+ a_clamp_min is the minimum a for negative b. */
+ LLVMValueRef a_clamp_max = lp_build_min_simple(bld, a, LLVMBuildSub(builder, max_val, b, ""), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ LLVMValueRef a_clamp_min = lp_build_max_simple(bld, a, LLVMBuildSub(builder, min_val, b, ""), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ a = lp_build_select(bld, lp_build_cmp(bld, PIPE_FUNC_GREATER, b, bld->zero), a_clamp_max, a_clamp_min);
+ } else {
+ a = lp_build_min_simple(bld, a, lp_build_comp(bld, b), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ }
+ }
if(LLVMIsConstant(a) && LLVMIsConstant(b))
if (type.floating)
}
}
- if(intrinsic)
+ if (intrinsic)
return lp_build_intrinsic_binary(builder, intrinsic, lp_build_vec_type(bld->gallivm, bld->type), a, b);
}
- /* TODO: handle signed case */
- if(type.norm && !type.floating && !type.fixed && !type.sign)
- a = lp_build_max_simple(bld, a, b, GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ if(type.norm && !type.floating && !type.fixed) {
+ if (type.sign) {
+ uint64_t sign = (uint64_t)1 << (type.width - 1);
+ LLVMValueRef max_val = lp_build_const_int_vec(bld->gallivm, type, sign - 1);
+ LLVMValueRef min_val = lp_build_const_int_vec(bld->gallivm, type, sign);
+ /* a_clamp_max is the maximum a for negative b,
+ a_clamp_min is the minimum a for positive b. */
+ LLVMValueRef a_clamp_max = lp_build_min_simple(bld, a, LLVMBuildAdd(builder, max_val, b, ""), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ LLVMValueRef a_clamp_min = lp_build_max_simple(bld, a, LLVMBuildAdd(builder, min_val, b, ""), GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ a = lp_build_select(bld, lp_build_cmp(bld, PIPE_FUNC_GREATER, b, bld->zero), a_clamp_min, a_clamp_max);
+ } else {
+ a = lp_build_max_simple(bld, a, b, GALLIVM_NAN_BEHAVIOR_UNDEFINED);
+ }
+ }
if(LLVMIsConstant(a) && LLVMIsConstant(b))
if (type.floating)
* half = sgn(ab) * 0.5 * (2 ** n) = sgn(ab) * (1 << (n - 1))
*/
- half = lp_build_const_int_vec(gallivm, wide_type, 1 << (n - 1));
+ half = lp_build_const_int_vec(gallivm, wide_type, 1LL << (n - 1));
if (wide_type.sign) {
LLVMValueRef minus_half = LLVMBuildNeg(builder, half, "");
LLVMValueRef sign = lp_build_shr_imm(&bld, ab, wide_type.width - 1);
if(a == bld->zero)
return bld->zero;
- if(a == bld->one)
+ if(a == bld->one && type.floating)
return lp_build_rcp(bld, b);
if(b == bld->zero)
return bld->undef;
*
* @sa http://www.stereopsis.com/doubleblend.html
*/
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_lerp_simple(struct lp_build_context *bld,
LLVMValueRef x,
LLVMValueRef v0,
assert(lp_check_value(bld->type, a));
-#if HAVE_LLVM >= 0x0207
if (bld->type.floating)
a = LLVMBuildFNeg(builder, a, "");
else
-#endif
a = LLVMBuildNeg(builder, a, "");
return a;
* NOTE: In the SSE4.1's nearest mode, if two values are equally close, the
* result is the even value. That is, rounding 2.5 will be 2.0, and not 3.0.
*/
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_round_sse41(struct lp_build_context *bld,
LLVMValueRef a,
enum lp_build_round_mode mode)
args[2] = LLVMConstInt(i32t, mode, 0);
res = lp_build_intrinsic(builder, intrinsic,
- vec_type, args, Elements(args));
+ vec_type, args, Elements(args), 0);
res = LLVMBuildExtractElement(builder, res, index0, "");
}
}
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_iround_nearest_sse2(struct lp_build_context *bld,
LLVMValueRef a)
{
/*
*/
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_round_altivec(struct lp_build_context *bld,
LLVMValueRef a,
enum lp_build_round_mode mode)
assert(lp_check_value(type, a));
assert(util_cpu_caps.has_altivec);
+ (void)type;
+
switch (mode) {
case LP_BUILD_ROUND_NEAREST:
intrinsic = "llvm.ppc.altivec.vrfin";
return lp_build_intrinsic_unary(builder, intrinsic, bld->vec_type, a);
}
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_round_arch(struct lp_build_context *bld,
LLVMValueRef a,
enum lp_build_round_mode mode)
const struct lp_type type = bld->type;
struct lp_type inttype;
struct lp_build_context intbld;
- LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 2^24);
+ LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 1<<24);
LLVMValueRef trunc, res, anosign, mask;
LLVMTypeRef int_vec_type = bld->int_vec_type;
LLVMTypeRef vec_type = bld->vec_type;
const struct lp_type type = bld->type;
struct lp_type inttype;
struct lp_build_context intbld;
- LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 2^24);
+ LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 1<<24);
LLVMValueRef res, anosign, mask;
LLVMTypeRef int_vec_type = bld->int_vec_type;
LLVMTypeRef vec_type = bld->vec_type;
const struct lp_type type = bld->type;
struct lp_type inttype;
struct lp_build_context intbld;
- LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 2^24);
+ LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 1<<24);
LLVMValueRef trunc, res, anosign, mask;
LLVMTypeRef int_vec_type = bld->int_vec_type;
LLVMTypeRef vec_type = bld->vec_type;
+ if (type.width != 32) {
+ char intrinsic[32];
+ util_snprintf(intrinsic, sizeof intrinsic, "llvm.floor.v%uf%u", type.length, type.width);
+ return lp_build_intrinsic_unary(builder, intrinsic, vec_type, a);
+ }
+
assert(type.width == 32); /* might want to handle doubles at some point */
inttype = type;
const struct lp_type type = bld->type;
struct lp_type inttype;
struct lp_build_context intbld;
- LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 2^24);
+ LLVMValueRef cmpval = lp_build_const_vec(bld->gallivm, type, 1<<24);
LLVMValueRef trunc, res, anosign, mask, tmp;
LLVMTypeRef int_vec_type = bld->int_vec_type;
LLVMTypeRef vec_type = bld->vec_type;
+ if (type.width != 32) {
+ char intrinsic[32];
+ util_snprintf(intrinsic, sizeof intrinsic, "llvm.ceil.v%uf%u", type.length, type.width);
+ return lp_build_intrinsic_unary(builder, intrinsic, vec_type, a);
+ }
+
assert(type.width == 32); /* might want to handle doubles at some point */
inttype = type;
* - http://en.wikipedia.org/wiki/Division_(digital)#Newton.E2.80.93Raphson_division
* - http://softwarecommunity.intel.com/articles/eng/1818.htm
*/
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_rcp_refine(struct lp_build_context *bld,
LLVMValueRef a,
LLVMValueRef rcp_a)
*
* See also Intel 64 and IA-32 Architectures Optimization Manual.
*/
-static INLINE LLVMValueRef
+static inline LLVMValueRef
lp_build_rsqrt_refine(struct lp_build_context *bld,
LLVMValueRef a,
LLVMValueRef rsqrt_a)
lp_build_rsqrt(struct lp_build_context *bld,
LLVMValueRef a)
{
- LLVMBuilderRef builder = bld->gallivm->builder;
const struct lp_type type = bld->type;
assert(lp_check_value(type, a));
* All numbers smaller than FLT_MIN will result in +infinity
* (rsqrtps treats all denormals as zero).
*/
- /*
- * Certain non-c99 compilers don't know INFINITY and might not support
- * hacks to evaluate it at compile time neither.
- */
- const unsigned posinf_int = 0x7F800000;
LLVMValueRef cmp;
LLVMValueRef flt_min = lp_build_const_vec(bld->gallivm, type, FLT_MIN);
- LLVMValueRef inf = lp_build_const_int_vec(bld->gallivm, type, posinf_int);
-
- inf = LLVMBuildBitCast(builder, inf, lp_build_vec_type(bld->gallivm, type), "");
+ LLVMValueRef inf = lp_build_const_vec(bld->gallivm, type, INFINITY);
for (i = 0; i < num_iterations; ++i) {
res = lp_build_rsqrt_refine(bld, a, res);
assert(lp_check_value(bld->type, x));
-
/* TODO: optimize the constant case */
if (gallivm_debug & GALLIVM_DEBUG_PERF &&
LLVMIsConstant(x)) {
/* We want to preserve NaN and make sure than for exp2 if x > 128,
* the result is INF and if it's smaller than -126.9 the result is 0 */
x = lp_build_min_ext(bld, lp_build_const_vec(bld->gallivm, type, 128.0), x,
- GALLIVM_NAN_RETURN_OTHER_SECOND_NONNAN);
- x = lp_build_max(bld, lp_build_const_vec(bld->gallivm, type, -126.99999), x);
+ GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN);
+ x = lp_build_max_ext(bld, lp_build_const_vec(bld->gallivm, type, -126.99999),
+ x, GALLIVM_NAN_RETURN_NAN_FIRST_NONNAN);
/* ipart = floor(x) */
/* fpart = x - ipart */
lp_build_ifloor_fract(bld, x, &ipart, &fpart);
-
-
/* expipart = (float) (1 << ipart) */
expipart = LLVMBuildAdd(builder, ipart,
lp_build_const_int_vec(bld->gallivm, type, 127), "");
lp_build_const_int_vec(bld->gallivm, type, 23), "");
expipart = LLVMBuildBitCast(builder, expipart, vec_type, "");
-
expfpart = lp_build_polynomial(bld, fpart, lp_build_exp2_polynomial,
Elements(lp_build_exp2_polynomial));
res = LLVMBuildFMul(builder, expipart, expfpart, "");
-
return res;
}
logexp = LLVMBuildSIToFP(builder, logexp, vec_type, "");
}
- if(p_log2) {
+ if (p_log2) {
/* mant = 1 + (float) mantissa(x) */
mant = LLVMBuildAnd(builder, i, mantmask, "");
mant = LLVMBuildOr(builder, mant, one, "");
}
}
- if(p_exp) {
+ if (p_exp) {
exp = LLVMBuildBitCast(builder, exp, vec_type, "");
*p_exp = exp;
}
- if(p_floor_log2)
+ if (p_floor_log2)
*p_floor_log2 = logexp;
- if(p_log2)
+ if (p_log2)
*p_log2 = res;
}
return ret;
}
+
+LLVMValueRef
+lp_build_fpstate_get(struct gallivm_state *gallivm)
+{
+ if (util_cpu_caps.has_sse) {
+ LLVMBuilderRef builder = gallivm->builder;
+ LLVMValueRef mxcsr_ptr = lp_build_alloca(
+ gallivm,
+ LLVMInt32TypeInContext(gallivm->context),
+ "mxcsr_ptr");
+ LLVMValueRef mxcsr_ptr8 = LLVMBuildPointerCast(builder, mxcsr_ptr,
+ LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0), "");
+ lp_build_intrinsic(builder,
+ "llvm.x86.sse.stmxcsr",
+ LLVMVoidTypeInContext(gallivm->context),
+ &mxcsr_ptr8, 1, 0);
+ return mxcsr_ptr;
+ }
+ return 0;
+}
+
+void
+lp_build_fpstate_set_denorms_zero(struct gallivm_state *gallivm,
+ boolean zero)
+{
+ if (util_cpu_caps.has_sse) {
+ /* turn on DAZ (64) | FTZ (32768) = 32832 if available */
+ int daz_ftz = _MM_FLUSH_ZERO_MASK;
+
+ LLVMBuilderRef builder = gallivm->builder;
+ LLVMValueRef mxcsr_ptr = lp_build_fpstate_get(gallivm);
+ LLVMValueRef mxcsr =
+ LLVMBuildLoad(builder, mxcsr_ptr, "mxcsr");
+
+ if (util_cpu_caps.has_daz) {
+ /* Enable denormals are zero mode */
+ daz_ftz |= _MM_DENORMALS_ZERO_MASK;
+ }
+ if (zero) {
+ mxcsr = LLVMBuildOr(builder, mxcsr,
+ LLVMConstInt(LLVMTypeOf(mxcsr), daz_ftz, 0), "");
+ } else {
+ mxcsr = LLVMBuildAnd(builder, mxcsr,
+ LLVMConstInt(LLVMTypeOf(mxcsr), ~daz_ftz, 0), "");
+ }
+
+ LLVMBuildStore(builder, mxcsr, mxcsr_ptr);
+ lp_build_fpstate_set(gallivm, mxcsr_ptr);
+ }
+}
+
+void
+lp_build_fpstate_set(struct gallivm_state *gallivm,
+ LLVMValueRef mxcsr_ptr)
+{
+ if (util_cpu_caps.has_sse) {
+ LLVMBuilderRef builder = gallivm->builder;
+ mxcsr_ptr = LLVMBuildPointerCast(builder, mxcsr_ptr,
+ LLVMPointerType(LLVMInt8TypeInContext(gallivm->context), 0), "");
+ lp_build_intrinsic(builder,
+ "llvm.x86.sse.ldmxcsr",
+ LLVMVoidTypeInContext(gallivm->context),
+ &mxcsr_ptr, 1, 0);
+ }
+}