From 13e2cd2f2ccb06cd6dc9acda0b6bbe268ee37879 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 16 Jul 2013 15:57:11 -0400 Subject: gallivm: add a version of log2 which handles edge cases That means that if input is: * - less than zero (to and including -inf) then NaN will be returned * - equal to zero (-denorm, -0, +0 or +denorm), then -inf will be returned * - +infinity, then +infinity will be returned * - NaN, then NaN will be returned It's a separate function because the checks are a little bit costly and in most cases are likely unnecessary. Signed-off-by: Zack Rusin Reviewed-by: Jose Fonseca Reviewed-by: Roland Scheidegger --- src/gallium/auxiliary/gallivm/lp_bld_arit.c | 58 +++++++++++++++++++++- src/gallium/auxiliary/gallivm/lp_bld_arit.h | 7 ++- src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c | 6 +-- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/gallium/auxiliary/gallivm/lp_bld_arit.c b/src/gallium/auxiliary/gallivm/lp_bld_arit.c index 34e3ed94542..b3b3c925808 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_arit.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_arit.c @@ -3349,13 +3349,25 @@ const double lp_build_log2_polynomial[] = { * See http://www.devmaster.net/forums/showthread.php?p=43580 * http://en.wikipedia.org/wiki/Logarithm#Calculation * http://www.nezumi.demon.co.uk/consult/logx.htm + * + * If handle_edge_cases is true the function will perform computations + * to match the required D3D10+ behavior for each of the edge cases. + * That means that if input is: + * - less than zero (to and including -inf) then NaN will be returned + * - equal to zero (-denorm, -0, +0 or +denorm), then -inf will be returned + * - +infinity, then +infinity will be returned + * - NaN, then NaN will be returned + * + * Those checks are fairly expensive so if you don't need them make sure + * handle_edge_cases is false. */ void lp_build_log2_approx(struct lp_build_context *bld, LLVMValueRef x, LLVMValueRef *p_exp, LLVMValueRef *p_floor_log2, - LLVMValueRef *p_log2) + LLVMValueRef *p_log2, + boolean handle_edge_cases) { LLVMBuilderRef builder = bld->gallivm->builder; const struct lp_type type = bld->type; @@ -3428,6 +3440,29 @@ lp_build_log2_approx(struct lp_build_context *bld, logmant = lp_build_mul(bld, y, logmant); res = lp_build_add(bld, logmant, logexp); + + if (type.floating && handle_edge_cases) { + LLVMValueRef negmask, infmask, zmask; + negmask = lp_build_cmp(bld, PIPE_FUNC_LESS, x, + lp_build_const_vec(bld->gallivm, type, 0.0f)); + zmask = lp_build_cmp(bld, PIPE_FUNC_EQUAL, x, + lp_build_const_vec(bld->gallivm, type, 0.0f)); + infmask = lp_build_cmp(bld, PIPE_FUNC_GEQUAL, x, + lp_build_const_vec(bld->gallivm, type, INFINITY)); + + /* If x is qual to inf make sure we return inf */ + res = lp_build_select(bld, infmask, + lp_build_const_vec(bld->gallivm, type, INFINITY), + res); + /* If x is qual to 0, return -inf */ + res = lp_build_select(bld, zmask, + lp_build_const_vec(bld->gallivm, type, -INFINITY), + res); + /* If x is nan or less than 0, return nan */ + res = lp_build_select(bld, negmask, + lp_build_const_vec(bld->gallivm, type, NAN), + res); + } } if(p_exp) { @@ -3443,12 +3478,31 @@ lp_build_log2_approx(struct lp_build_context *bld, } +/* + * log2 implementation which doesn't have special code to + * handle edge cases (-inf, 0, inf, NaN). It's faster but + * the results for those cases are undefined. + */ LLVMValueRef lp_build_log2(struct lp_build_context *bld, LLVMValueRef x) { LLVMValueRef res; - lp_build_log2_approx(bld, x, NULL, NULL, &res); + lp_build_log2_approx(bld, x, NULL, NULL, &res, FALSE); + return res; +} + +/* + * Version of log2 which handles all edge cases. + * Look at documentation of lp_build_log2_approx for + * description of the behavior for each of the edge cases. + */ +LLVMValueRef +lp_build_log2_safe(struct lp_build_context *bld, + LLVMValueRef x) +{ + LLVMValueRef res; + lp_build_log2_approx(bld, x, NULL, NULL, &res, TRUE); return res; } diff --git a/src/gallium/auxiliary/gallivm/lp_bld_arit.h b/src/gallium/auxiliary/gallivm/lp_bld_arit.h index 14b3a164faa..ac06a2c0971 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_arit.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_arit.h @@ -308,6 +308,10 @@ LLVMValueRef lp_build_log2(struct lp_build_context *bld, LLVMValueRef a); +LLVMValueRef +lp_build_log2_safe(struct lp_build_context *bld, + LLVMValueRef a); + LLVMValueRef lp_build_fast_log2(struct lp_build_context *bld, LLVMValueRef a); @@ -328,7 +332,8 @@ lp_build_log2_approx(struct lp_build_context *bld, LLVMValueRef x, LLVMValueRef *p_exp, LLVMValueRef *p_floor_log2, - LLVMValueRef *p_log2); + LLVMValueRef *p_log2, + boolean handle_nans); LLVMValueRef lp_build_mod(struct lp_build_context *bld, diff --git a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c index f23e08b77fb..d16ccae37fb 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c @@ -1236,8 +1236,8 @@ lg2_emit_cpu( struct lp_build_tgsi_context * bld_base, struct lp_build_emit_data * emit_data) { - emit_data->output[emit_data->chan] = lp_build_log2(&bld_base->base, - emit_data->args[0]); + emit_data->output[emit_data->chan] = lp_build_log2_safe(&bld_base->base, + emit_data->args[0]); } /* TGSI_OPCODE_LOG (CPU Only) */ @@ -1253,7 +1253,7 @@ log_emit_cpu( LLVMValueRef src0 = emit_data->args[0]; lp_build_log2_approx(&bld_base->base, src0, - &p_exp, &p_floor_log2, &p_log2); + &p_exp, &p_floor_log2, &p_log2, FALSE); emit_data->output[TGSI_CHAN_X] = p_floor_log2; -- cgit v1.2.3