diff --git a/src/compiler/nir/nir_opt_algebraic.py b/src/compiler/nir/nir_opt_algebraic.py index bab6d40d53ab..c662151747ef 100644 --- a/src/compiler/nir/nir_opt_algebraic.py +++ b/src/compiler/nir/nir_opt_algebraic.py @@ -3028,15 +3028,28 @@ def ubfe_ubfe(a, b, c, d, e): # upcasts where the type differs from the other cast for N, M in itertools.product(type_sizes('uint'), type_sizes('uint')): if N < M: - # The outer cast is a down-cast. It doesn't matter what the size of the - # argument of the inner cast is because we'll never been in the upcast - # of downcast case. Regardless of types, we'll always end up with y2yN - # in the end. - for x, y in itertools.product(['i', 'u'], ['i', 'u']): - x2xN = '{0}2{0}{1}'.format(x, N) - y2yM = '{0}2{0}{1}'.format(y, M) - y2yN = '{0}2{0}{1}'.format(y, N) - optimizations.append(((x2xN, (y2yM, a)), (y2yN, a))) + # Down-casts are always in the u2uN form + u2uN = 'u2u{0}'.format(N) + + if N == 1: + for y in ['i', 'u']: + y2yM = '{0}2{0}{1}'.format(y, M) + optimizations.append(((u2uN, (y2yM, a)), (u2uN, a))) + continue + + u2uM = 'u2u{0}'.format(M) + optimizations.append(((u2uN, (u2uM, a)), (u2uN, a))) + + for P in type_sizes('uint'): + if P > M: + continue + + # Keep the sign extension for down-casts of signed up-casts. + res = u2uN if P >= N else 'i2i{0}'.format(N) + + i2iM = 'i2i{0}'.format(M) + aP = 'a@{0}'.format(P) + optimizations.append(((u2uN, (i2iM, aP)), (res, a))) elif N > M: # If the outer cast is an up-cast, we have to be more careful about the # size of the argument of the inner cast and with types. In this case, diff --git a/src/compiler/nir/tests/algebraic_tests.cpp b/src/compiler/nir/tests/algebraic_tests.cpp index af70fb7d1b0d..54413572c2f2 100644 --- a/src/compiler/nir/tests/algebraic_tests.cpp +++ b/src/compiler/nir/tests/algebraic_tests.cpp @@ -1331,4 +1331,42 @@ TEST_F(nir_opt_algebraic_test, commutation_masks_preserve_direction_order) } } +static unsigned +count_narrowing_i2i(nir_shader *shader) +{ + unsigned count = 0; + nir_foreach_function_impl(impl, shader) { + nir_foreach_block(block, impl) { + nir_foreach_instr(instr, block) { + if (instr->type != nir_instr_type_alu) + continue; + + nir_alu_instr *alu = nir_instr_as_alu(instr); + if ((alu->op == nir_op_i2i8 || alu->op == nir_op_i2i16 || + alu->op == nir_op_i2i32) && + nir_src_bit_size(alu->src[0].src) > alu->def.bit_size) + count++; + } + } + } + return count; +} + +TEST_F(nir_opt_algebraic_test, downcast_of_sign_extended_value_is_u2u) +{ + nir_def *base = nir_imm_int64(b, 0x100000); + nir_def *value16 = nir_load_global(b, 1, 16, base, (gl_access_qualifier)0, 2, 0); + nir_def *value32 = nir_i2i32(b, value16); + nir_def *byte0 = nir_u2u8(b, value32); + nir_store_global(b, nir_i2f32(b, byte0), nir_imm_int64(b, 0x200000), 1, + (gl_access_qualifier)0, 4, 0); + + nir_validate_shader(b->shader, "before the pass"); + run_pass(); + + /* An i2i narrowing would fail validation; the downcast has to stay u2u. */ + EXPECT_EQ(count_narrowing_i2i(b->shader), 0u); + nir_validate_shader(b->shader, "after the pass"); +} + }