diff --git a/src/compiler/nir/tests/opt_if_tests.cpp b/src/compiler/nir/tests/opt_if_tests.cpp index 02da4a7a867..91a3d0481f8 100644 --- a/src/compiler/nir/tests/opt_if_tests.cpp +++ b/src/compiler/nir/tests/opt_if_tests.cpp @@ -168,3 +168,172 @@ TEST_F(nir_opt_if_test, opt_if_alu_of_phi_progress) EXPECT_LE(progress_count, 2); ASSERT_FALSE(progress); } + +static const struct nir_opt_if_merge_test_config { + template + std::string + operator()(const ::testing::TestParamInfo &info) const + { + return info.param.name; + } + + const char *name; + + /* Test parameter: Location of a return statement within the control flow */ + enum return_location { + NO_RETURN, + RETURN_IN_1ST_THEN, + RETURN_IN_1ST_ELSE, + RETURN_IN_2ND_THEN, + RETURN_IN_2ND_ELSE, + } return_location; +} nir_opt_if_merge_test_configs [] = { + { "no_return", nir_opt_if_merge_test_config::NO_RETURN, }, + { "return_in_1st_then", nir_opt_if_merge_test_config::RETURN_IN_1ST_THEN, }, + { "return_in_1st_else", nir_opt_if_merge_test_config::RETURN_IN_1ST_ELSE, }, + { "return_in_2nd_then", nir_opt_if_merge_test_config::RETURN_IN_2ND_THEN, }, + { "return_in_2nd_else", nir_opt_if_merge_test_config::RETURN_IN_2ND_ELSE, }, +}; + +class nir_opt_if_merge_test + : public nir_opt_if_test + , public ::testing::WithParamInterface +{ +protected: + virtual void SetUp(); + + enum nir_opt_if_merge_test_config::return_location return_location; +}; + +void +nir_opt_if_merge_test::SetUp() +{ + struct nir_opt_if_merge_test_config config = GetParam(); + return_location = config.return_location; +} + +INSTANTIATE_TEST_SUITE_P( + nir_opt_if, nir_opt_if_merge_test, + ::testing::ValuesIn(nir_opt_if_merge_test_configs), + nir_opt_if_merge_test_config() +); + +TEST_P(nir_opt_if_merge_test, opt_if_merge) +{ + /* Tests that opt_if_merge correctly merges if statements with the same + * condition, parameterized on the location of a return statement added + * within the control-flow. + * + * block b0: // preds: + * 32 %0 = deref_var &in (shader_in int) + * 32 %1 = @load_deref (%0) (access=none) + * 32 %2 = load_const (0x00000001) + * 32 %3 = load_const (0x00000002) + * 32 %4 = load_const (0x00000006) + * 1 %5 = ige %1, %2 (0x1) + * // succs: b1 b2 + * if %5 { + * block b1: // preds: b0 + * 32 %6 = iadd %1, %2 (0x1) + * // succs: b3 + * } else { + * block b2: // preds: b0 + * 32 %7 = iadd %1, %3 (0x2) + * // succs: b3 + * } + * block b3: // preds: b1 b2, succs: b4 b5 + * if %5 { + * block b4: // preds: b3 + * 32 %8 = imul %6, %3 (0x2) + * // succs: b6 + * } else { + * block b5: // preds: b3 + * 32 %9 = imul %7, %4 (0x6) + * // succs: b6 + * } + * block b6: // preds: b4 b5 + * 32 %10 = phi b4: %8, b5: %9 + * 32 %11 = deref_var &out (shader_out int) + * @store_deref (%11, %10) (wrmask=x, access=none) + * // succs: b7 + * block b7: + */ + + int instr_in_1st_then = 1, instr_in_1st_else = 1; + int instr_in_2nd_then = 1, instr_in_2nd_else = 1; + + nir_def *one = nir_imm_int(b, 1); + nir_def *two = nir_imm_int(b, 2); + nir_def *six = nir_imm_int(b, 6); + + nir_def *cmp_result = nir_ige(b, in_def, one); + + nir_if *nif = nir_push_if(b, cmp_result); + + nir_def *x1 = nir_iadd(b, in_def, one); + if (return_location == nir_opt_if_merge_test_config::RETURN_IN_1ST_THEN) { + nir_jump(b, nir_jump_return); + instr_in_1st_then++; + } + + nir_push_else(b, NULL); + + nir_def *x2 = nir_iadd(b, in_def, two); + if (return_location == nir_opt_if_merge_test_config::RETURN_IN_1ST_ELSE) { + nir_jump(b, nir_jump_return); + instr_in_1st_else++; + } + + nir_pop_if(b, NULL); + + nir_phi_instr *phi = nir_phi_instr_create(b->shader); + nir_def_init(&phi->instr, &phi->def, + one->num_components, one->bit_size); + + nir_if *next_if = nir_push_if(b, cmp_result); + + nir_def *y1 = nir_imul(b, x1, two); + if (return_location == nir_opt_if_merge_test_config::RETURN_IN_2ND_THEN) { + nir_jump(b, nir_jump_return); + instr_in_2nd_then++; + } else { + nir_phi_instr_add_src(phi, nir_cursor_current_block(b->cursor), y1); + } + + nir_push_else(b, NULL); + + nir_def *y2 = nir_imul(b, x2, six); + if (return_location == nir_opt_if_merge_test_config::RETURN_IN_2ND_ELSE) { + nir_jump(b, nir_jump_return); + instr_in_2nd_else++; + } else { + nir_phi_instr_add_src(phi, nir_cursor_current_block(b->cursor), y2); + } + + nir_pop_if(b, NULL); + + nir_builder_instr_insert(b, &phi->instr); + + // do_work + nir_store_var(b, out_var, &phi->def, 1); + + if (return_location == nir_opt_if_merge_test_config::RETURN_IN_2ND_THEN || + return_location == nir_opt_if_merge_test_config::RETURN_IN_2ND_ELSE) { + ASSERT_FALSE(nir_opt_if(b->shader, nir_opt_if_optimize_phi_true_false)); + } else if (return_location == nir_opt_if_merge_test_config::NO_RETURN) { + ASSERT_TRUE(nir_opt_if(b->shader, nir_opt_if_optimize_phi_true_false)); + instr_in_1st_then = 2; + instr_in_1st_else = 2; + instr_in_2nd_then = 0; + instr_in_2nd_else = 0; + } else { + ASSERT_TRUE(nir_opt_if(b->shader, nir_opt_if_optimize_phi_true_false)); + } + + nir_validate_shader(b->shader, NULL); + + ASSERT_EQ(exec_list_length((&nir_if_first_then_block(nif)->instr_list)), instr_in_1st_then); + ASSERT_EQ(exec_list_length((&nir_if_first_else_block(nif)->instr_list)), instr_in_1st_else); + ASSERT_EQ(exec_list_length((&nir_if_first_then_block(next_if)->instr_list)), instr_in_2nd_then); + ASSERT_EQ(exec_list_length((&nir_if_first_else_block(next_if)->instr_list)), instr_in_2nd_else); +}