[Mesa-dev] [PATCH 02/12] i965/fs: Rewrite fsign64 to skip the float -> double conversion

Matt Turner mattst88 at gmail.com
Fri Sep 29 17:21:37 UTC 2017


On Fri, Sep 29, 2017 at 4:48 AM, Iago Toral <itoral at igalia.com> wrote:
> On Thu, 2017-09-28 at 23:05 -0700, Matt Turner wrote:
>> ... without the float -> double conversion. Low power parts have
>> additional restrictions when it comes to operating on 64-bit types,
>> and
>> the instruction used to do the conversion violates one of them:
>> specifically, the restriction that "Source and Destination horizontal
>> stride must be aligned to the same qword".
>>
>> Previously we generated a float and then converted, but we can avoid
>> the
>> conversion by using the same extract-the-sign-bit + or-in-1.0
>> algorithm
>> by directly operating on the high four bytes of each double-precision
>> component in the result.
>>
>> In SIMD8 and SIMD16 this cuts one instruction from the
>> implementation,
>> and more importantly that instruction is the one which violated the
>> regioning restriction.
>>
>> Along the way I removed some comments that I did not think helped,
>> and
>> some code about double comparisons which does not seem to be
>> necessary
>> today.
>>
>> This prevents validation failures caught by the new EU validation
>> code
>> added in later patches.
>> ---
>>  src/intel/compiler/brw_fs_nir.cpp | 49 +++++++--------------------
>> ------------
>>  1 file changed, 9 insertions(+), 40 deletions(-)
>>
>> diff --git a/src/intel/compiler/brw_fs_nir.cpp
>> b/src/intel/compiler/brw_fs_nir.cpp
>> index 8ea4297ceb..1b011172a6 100644
>> --- a/src/intel/compiler/brw_fs_nir.cpp
>> +++ b/src/intel/compiler/brw_fs_nir.cpp
>> @@ -697,49 +697,18 @@ fs_visitor::nir_emit_alu(const fs_builder &bld,
>> nir_alu_instr *instr)
>
> There is a comment right before this saying that CMP with SIMD16
> requires special handling that is no longer valid, so we want to remove
> that too.

Oh, thanks. I missed that.

>>            * - We need to produce a DF result.
>>            */
>>
>> -         /* 2-src instructions can't have 64-bit immediates, so put
>> 0.0 in
>> -          * a register and compare with that.
>> -          */
>> -         fs_reg tmp = vgrf(glsl_type::double_type);
>> -         bld.MOV(tmp, setup_imm_df(bld, 0.0));
>> -
>> -         /* A direct DF CMP using the flag register (null dst) won't
>> work in
>> -          * SIMD16 because the CMP will be split in two by
>> lower_simd_width,
>> -          * resulting in two CMP instructions with the same dst
>> (NULL),
>> -          * leading to dead code elimination of the first one. In
>> SIMD8,
>> -          * however, there is no need to split the CMP and we can
>> save some
>> -          * work.
>> -          */
>> -         fs_reg dst_tmp = vgrf(glsl_type::double_type);
>> -         bld.CMP(dst_tmp, op[0], tmp, BRW_CONDITIONAL_NZ);
>> -
>> -         /* In SIMD16 we want to avoid using a NULL dst register
>> with DF CMP,
>> -          * so we store the result of the comparison in a vgrf
>> instead and
>> -          * then we generate a UD comparison from that that won't
>> have to
>> -          * be split by lower_simd_width. This is what NIR does to
>> handle
>> -          * double comparisons in the general case.
>> -          */
>> -         if (bld.dispatch_width() == 16 ) {
>> -            fs_reg dst_tmp_ud = retype(dst_tmp,
>> BRW_REGISTER_TYPE_UD);
>> -            bld.MOV(dst_tmp_ud, subscript(dst_tmp,
>> BRW_REGISTER_TYPE_UD, 0));
>> -            bld.CMP(bld.null_reg_ud(),
>> -                    dst_tmp_ud, brw_imm_ud(0), BRW_CONDITIONAL_NZ);
>> -         }
>
> So this is fixed now, great! :)

I was very pleasantly surprised to see that it "just worked" and
marked split instructions reading/writing the flag register with the
correct quarter control bits :)

>> -
>> -         /* Get the high 32-bit of each double component where the
>> sign is */
>> -         fs_reg result_int = retype(result, BRW_REGISTER_TYPE_UD);
>> -         bld.MOV(result_int, subscript(op[0], BRW_REGISTER_TYPE_UD,
>> 1));
>> +         fs_reg zero = vgrf(glsl_type::double_type);
>> +         bld.MOV(zero, setup_imm_df(bld, 0.0));
>> +         bld.CMP(bld.null_reg_df(), op[0], zero,
>> BRW_CONDITIONAL_NZ);
>>
>> -         /* Get the sign bit */
>> -         bld.AND(result_int, result_int, brw_imm_ud(0x80000000u));
>> +         bld.MOV(result, zero);
>>
>> -         /* Add 1.0 to the sign, predicated to skip the case of
>> op[0] == 0.0 */
>> -         inst = bld.OR(result_int, result_int,
>> brw_imm_ud(0x3f800000u));
>> -         inst->predicate = BRW_PREDICATE_NORMAL;
>> +         fs_reg r = subscript(result, BRW_REGISTER_TYPE_UD, 1);
>> +         bld.AND(r, subscript(op[0], BRW_REGISTER_TYPE_UD, 1),
>> +                 brw_imm_ud(0x80000000u));
>>
>> -         /* Convert from 32-bit float to 64-bit double */
>> -         result.type = BRW_REGISTER_TYPE_DF;
>> -         inst = bld.MOV(result, retype(result_int,
>> BRW_REGISTER_TYPE_F));
>> +         set_predicate(BRW_PREDICATE_NORMAL,
>> +                       bld.OR(r, r, brw_imm_ud(0x3ff00000u)));
>
> This should be 0x3ff80000u, right?

I think that's the high 4 bytes of the double value 1.5. 0x3ff00000 is
for 1.0. (I just wrote some little test programs to confirm).

> With that fixed:
> Reviewed-by: Iago Toral Quiroga <itoral at igalia.com>

Thanks!


More information about the mesa-dev mailing list