gallivm,tgsi: fix idiv by zero crash
authorrconde <rconde01@outlook.com>
Wed, 17 Sep 2014 16:30:23 +0000 (18:30 +0200)
committerRoland Scheidegger <sroland@vmware.com>
Wed, 17 Sep 2014 16:31:54 +0000 (18:31 +0200)
While the result of signed integer division by zero is undefined by glsl
(and doesn't exist with d3d10), we must not crash, so need to make sure we
don't get sigfpe much like udiv already does.
Unlike udiv where we return 0xffffffff (as required by d3d10) there is
no requirement right now to return anything specific so we use zero.

src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c
src/gallium/auxiliary/tgsi/tgsi_exec.c

index b9546dbc661b28bca259882e3ef5c91f03d16cd3..4a9bc1f0ebe5470c8e951e92318247accf5c1dac 100644 (file)
@@ -1248,8 +1248,26 @@ idiv_emit_cpu(
    struct lp_build_tgsi_context * bld_base,
    struct lp_build_emit_data * emit_data)
 {
-   emit_data->output[emit_data->chan] = lp_build_div(&bld_base->int_bld,
-                                   emit_data->args[0], emit_data->args[1]);
+   LLVMBuilderRef builder = bld_base->base.gallivm->builder;
+   LLVMValueRef div_mask = lp_build_cmp(&bld_base->uint_bld,
+                                        PIPE_FUNC_EQUAL, emit_data->args[1],
+                                        bld_base->uint_bld.zero);
+   /* We want to make sure that we never divide/mod by zero to not 
+    * generate sigfpe. We don't want to crash just because the 
+    * shader is doing something weird. */
+   LLVMValueRef divisor = LLVMBuildOr(builder,
+                                      div_mask,
+                                      emit_data->args[1], "");
+   LLVMValueRef result = lp_build_div(&bld_base->uint_bld,
+                                      emit_data->args[0], divisor);
+                                      
+   LLVMValueRef not_div_mask = LLVMBuildNot(builder,
+                                            div_mask,"");
+                                          
+   /* idiv by zero doesn't have a guaranteed return value chose 0 for now. */
+   emit_data->output[emit_data->chan] = LLVMBuildAnd(builder,
+                                                     not_div_mask,
+                                                     result, "");
 }
 
 /* TGSI_OPCODE_INEG (CPU Only) */
@@ -1683,7 +1701,7 @@ udiv_emit_cpu(
                                       emit_data->args[1], "");
    LLVMValueRef result = lp_build_div(&bld_base->uint_bld,
                                       emit_data->args[0], divisor);
-   /* udiv by zero is guaranteed to return 0xffffffff */
+   /* udiv by zero is guaranteed to return 0xffffffff at least with d3d10 */
    emit_data->output[emit_data->chan] = LLVMBuildOr(builder,
                                                     div_mask,
                                                     result, "");
index 6dae3798a39d820eabc91e14185dd170af706813..77948011ac7dc7a03d2048f9e9d914a6b24ad14a 100644 (file)
@@ -3343,10 +3343,10 @@ micro_idiv(union tgsi_exec_channel *dst,
            const union tgsi_exec_channel *src0,
            const union tgsi_exec_channel *src1)
 {
-   dst->i[0] = src0->i[0] / src1->i[0];
-   dst->i[1] = src0->i[1] / src1->i[1];
-   dst->i[2] = src0->i[2] / src1->i[2];
-   dst->i[3] = src0->i[3] / src1->i[3];
+   dst->i[0] = src1->i[0] ? src0->i[0] / src1->i[0] : 0;
+   dst->i[1] = src1->i[1] ? src0->i[1] / src1->i[1] : 0;
+   dst->i[2] = src1->i[2] ? src0->i[2] / src1->i[2] : 0;
+   dst->i[3] = src1->i[3] ? src0->i[3] / src1->i[3] : 0;
 }
 
 static void