gallivm: obey clarified shift behavior
authorRoland Scheidegger <sroland@vmware.com>
Tue, 30 Jul 2013 19:26:27 +0000 (21:26 +0200)
committerRoland Scheidegger <sroland@vmware.com>
Fri, 2 Aug 2013 01:49:57 +0000 (03:49 +0200)
llvm shifts are undefined for shift counts exceeding (or matching) bit width,
so need to apply a mask for the tgsi shift instructions.

v2: only use mask for the tgsi shift instructions, not for the build shift
helpers. None of the internal callers need this behavior, and while llvm can
optimize away the masking for constants there are legitimate cases where it
might not be able to do so even if we know that shift count must be smaller
than type width (currently all such callers do not use the build shift
helpers).

Reviewed-by: Zack Rusin <zackr@vmware.com>
Reviewed-by: Jose Fonseca <jfonseca@vmware.com>
src/gallium/auxiliary/gallivm/lp_bld_bitarit.c
src/gallium/auxiliary/gallivm/lp_bld_tgsi_action.c

index 97ae1622c27b94edac6d49c34e5f532da4fe016b..9892d7aa2b8b26b251aef23fe686c6409fb1dbcb 100644 (file)
@@ -168,6 +168,7 @@ lp_build_not(struct lp_build_context *bld, LLVMValueRef a)
 
 /**
  * Shift left.
+ * Result is undefined if the shift count is not smaller than the type width.
  */
 LLVMValueRef
 lp_build_shl(struct lp_build_context *bld, LLVMValueRef a, LLVMValueRef b)
@@ -189,6 +190,7 @@ lp_build_shl(struct lp_build_context *bld, LLVMValueRef a, LLVMValueRef b)
 
 /**
  * Shift right.
+ * Result is undefined if the shift count is not smaller than the type width.
  */
 LLVMValueRef
 lp_build_shr(struct lp_build_context *bld, LLVMValueRef a, LLVMValueRef b)
@@ -214,23 +216,25 @@ lp_build_shr(struct lp_build_context *bld, LLVMValueRef a, LLVMValueRef b)
 
 /**
  * Shift left with immediate.
+ * The immediate shift count must be smaller than the type width.
  */
 LLVMValueRef
 lp_build_shl_imm(struct lp_build_context *bld, LLVMValueRef a, unsigned imm)
 {
    LLVMValueRef b = lp_build_const_int_vec(bld->gallivm, bld->type, imm);
-   assert(imm <= bld->type.width);
+   assert(imm < bld->type.width);
    return lp_build_shl(bld, a, b);
 }
 
 
 /**
  * Shift right with immediate.
+ * The immediate shift count must be smaller than the type width.
  */
 LLVMValueRef
 lp_build_shr_imm(struct lp_build_context *bld, LLVMValueRef a, unsigned imm)
 {
    LLVMValueRef b = lp_build_const_int_vec(bld->gallivm, bld->type, imm);
-   assert(imm <= bld->type.width);
+   assert(imm < bld->type.width);
    return lp_build_shr(bld, a, b);
 }
index d16ccae37fb5972eed20fa397302bfcb2313e75b..f46166101da339e877e86cf04ed81175856c13d1 100644 (file)
@@ -1203,8 +1203,12 @@ ishr_emit_cpu(
    struct lp_build_tgsi_context * bld_base,
    struct lp_build_emit_data * emit_data)
 {
-   emit_data->output[emit_data->chan] = lp_build_shr(&bld_base->int_bld,
-                                   emit_data->args[0], emit_data->args[1]);
+   struct lp_build_context *int_bld = &bld_base->int_bld;
+   LLVMValueRef mask = lp_build_const_vec(int_bld->gallivm, int_bld->type,
+                                          int_bld->type.width - 1);
+   LLVMValueRef masked_count = lp_build_and(int_bld, emit_data->args[1], mask);
+   emit_data->output[emit_data->chan] = lp_build_shr(int_bld, emit_data->args[0],
+                                                     masked_count);
 }
 
 /* TGSI_OPCODE_ISLT (CPU Only) */
@@ -1439,8 +1443,12 @@ shl_emit_cpu(
    struct lp_build_tgsi_context * bld_base,
    struct lp_build_emit_data * emit_data)
 {
-   emit_data->output[emit_data->chan] = lp_build_shl(&bld_base->uint_bld,
-                                   emit_data->args[0], emit_data->args[1]);
+   struct lp_build_context *uint_bld = &bld_base->uint_bld;
+   LLVMValueRef mask = lp_build_const_vec(uint_bld->gallivm, uint_bld->type,
+                                          uint_bld->type.width - 1);
+   LLVMValueRef masked_count = lp_build_and(uint_bld, emit_data->args[1], mask);
+   emit_data->output[emit_data->chan] = lp_build_shl(uint_bld, emit_data->args[0],
+                                                     masked_count);
 }
 
 /* TGSI_OPCODE_SIN (CPU Only) */
@@ -1647,8 +1655,12 @@ ushr_emit_cpu(
    struct lp_build_tgsi_context * bld_base,
    struct lp_build_emit_data * emit_data)
 {
-   emit_data->output[emit_data->chan] = lp_build_shr(&bld_base->uint_bld,
-                                   emit_data->args[0], emit_data->args[1]);
+   struct lp_build_context *uint_bld = &bld_base->uint_bld;
+   LLVMValueRef mask = lp_build_const_vec(uint_bld->gallivm, uint_bld->type,
+                                          uint_bld->type.width - 1);
+   LLVMValueRef masked_count = lp_build_and(uint_bld, emit_data->args[1], mask);
+   emit_data->output[emit_data->chan] = lp_build_shr(uint_bld, emit_data->args[0],
+                                                     masked_count);
 }
 
 /* TGSI_OPCODE_ISLT (CPU Only) */