gallivm: don't use URem/UDiv when calculating offsets for blocks
authorRoland Scheidegger <sroland@vmware.com>
Fri, 24 Sep 2010 13:02:24 +0000 (15:02 +0200)
committerJosé Fonseca <jfonseca@vmware.com>
Sat, 25 Sep 2010 11:19:31 +0000 (12:19 +0100)
While it's true that llvm can and will indeed replace this with bit
arithmetic (since block height/width is POT), it does so (llvm 2.7) by element
and hence extracts/shifts/reinserts each element individually.
This costs about 16 instructions (and extract is not really fast) vs. 1...

src/gallium/auxiliary/gallivm/lp_bld_sample.c

index 19e380a..44f44ff 100644 (file)
@@ -655,11 +655,21 @@ lp_build_sample_partial_offset(struct lp_build_context *bld,
        * Pixel blocks have power of two dimensions. LLVM should convert the
        * rem/div to bit arithmetic.
        * TODO: Verify this.
+       * It does indeed BUT it does transform it to scalar (and back) when doing so
+       * (using roughly extract, shift/and, mov, unpack) (llvm 2.7).
+       * The generated code looks seriously unfunny and is quite expensive.
        */
-
+#if 0
       LLVMValueRef block_width = lp_build_const_int_vec(bld->type, block_length);
       subcoord = LLVMBuildURem(bld->builder, coord, block_width, "");
       coord    = LLVMBuildUDiv(bld->builder, coord, block_width, "");
+#else
+      unsigned logbase2 = util_unsigned_logbase2(block_length);
+      LLVMValueRef block_shift = lp_build_const_int_vec(bld->type, logbase2);
+      LLVMValueRef block_mask = lp_build_const_int_vec(bld->type, block_length - 1);
+      subcoord = LLVMBuildAnd(bld->builder, coord, block_mask, "");
+      coord = LLVMBuildLShr(bld->builder, coord, block_shift, "");
+#endif
    }
 
    offset = lp_build_mul(bld, coord, stride);