X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcompiler%2Fnir%2Fnir_opt_if.c;h=4b2d0159baba5330648d4d561d283a25c9335831;hb=111b0a669979cf277f31c69f501982fee004e067;hp=e360c274bc92000d360bce5571a017f4daf9c968;hpb=7a19e05e8c84152af3a15868f5ef781142ac8e23;p=mesa.git diff --git a/src/compiler/nir/nir_opt_if.c b/src/compiler/nir/nir_opt_if.c index e360c274bc9..4b2d0159bab 100644 --- a/src/compiler/nir/nir_opt_if.c +++ b/src/compiler/nir/nir_opt_if.c @@ -58,25 +58,23 @@ find_continue_block(nir_loop *loop) static bool phi_has_constant_from_outside_and_one_from_inside_loop(nir_phi_instr *phi, const nir_block *entry_block, - uint32_t *entry_val, - uint32_t *continue_val) + bool *entry_val, + bool *continue_val) { /* We already know we have exactly one continue */ assert(exec_list_length(&phi->srcs) == 2); - *entry_val = 0; - *continue_val = 0; + *entry_val = false; + *continue_val = false; nir_foreach_phi_src(src, phi) { - assert(src->src.is_ssa); - nir_const_value *const_src = nir_src_as_const_value(src->src); - if (!const_src) - return false; + if (!nir_src_is_const(src->src)) + return false; if (src->pred != entry_block) { - *continue_val = const_src[0].u32; + *continue_val = nir_src_as_bool(src->src); } else { - *entry_val = const_src[0].u32; + *entry_val = nir_src_as_bool(src->src); } } @@ -169,7 +167,7 @@ opt_peel_loop_initial_if(nir_loop *loop) if (cond->parent_instr->block != header_block) return false; - uint32_t entry_val = 0, continue_val = 0; + bool entry_val = false, continue_val = false; if (!phi_has_constant_from_outside_and_one_from_inside_loop(cond_phi, prev_block, &entry_val, @@ -309,35 +307,29 @@ alu_instr_is_type_conversion(const nir_alu_instr *alu) * * - At least one source of the instruction is a phi node from the header block. * - * and either this rule - * - * - The phi node selects undef from the block before the loop and a value - * from the continue block of the loop. - * - * or these two rules - * - * - The phi node selects a constant from the block before the loop. + * - The phi node selects a constant or undef from the block before the loop. * - * - The non-phi source of the ALU instruction comes from a block that + * - Any non-phi sources of the ALU instruction come from a block that * dominates the block before the loop. The most common failure mode for * this check is sources that are generated in the loop header block. * - * The split process moves the original ALU instruction to the bottom of the - * loop. The phi node source is replaced with the value from the phi node - * selected from the continue block (i.e., the non-undef value). A new phi - * node is added to the header block that selects either undef from the block - * before the loop or the result of the (moved) ALU instruction. + * The split process splits the original ALU instruction into two, one at the + * bottom of the loop and one at the block before the loop. The instruction + * before the loop computes the value on the first iteration, and the + * instruction at the bottom computes the value on the second, third, and so + * on. A new phi node is added to the header block that selects either the + * instruction before the loop or the one at the end, and uses of the original + * instruction are replaced by this phi. * * The splitting transforms a loop like: * - * vec1 32 ssa_7 = undefined * vec1 32 ssa_8 = load_const (0x00000001) * vec1 32 ssa_10 = load_const (0x00000000) * // succs: block_1 * loop { * block block_1: * // preds: block_0 block_4 - * vec1 32 ssa_11 = phi block_0: ssa_7, block_4: ssa_15 + * vec1 32 ssa_11 = phi block_0: ssa_10, block_4: ssa_15 * vec1 32 ssa_12 = phi block_0: ssa_1, block_4: ssa_15 * vec1 32 ssa_13 = phi block_0: ssa_10, block_4: ssa_16 * vec1 32 ssa_14 = iadd ssa_11, ssa_8 @@ -348,27 +340,22 @@ alu_instr_is_type_conversion(const nir_alu_instr *alu) * * into: * - * vec1 32 ssa_7 = undefined * vec1 32 ssa_8 = load_const (0x00000001) * vec1 32 ssa_10 = load_const (0x00000000) + * vec1 32 ssa_22 = iadd ssa_10, ssa_8 * // succs: block_1 * loop { * block block_1: * // preds: block_0 block_4 - * vec1 32 ssa_11 = phi block_0: ssa_7, block_4: ssa_15 + * vec1 32 ssa_11 = phi block_0: ssa_10, block_4: ssa_15 * vec1 32 ssa_12 = phi block_0: ssa_1, block_4: ssa_15 * vec1 32 ssa_13 = phi block_0: ssa_10, block_4: ssa_16 - * vec1 32 ssa_21 = phi block_0: sss_7, block_4: ssa_20 + * vec1 32 ssa_21 = phi block_0: ssa_22, block_4: ssa_20 * vec1 32 ssa_15 = b32csel ssa_13, ssa_21, ssa_12 * ... * vec1 32 ssa_20 = iadd ssa_15, ssa_8 * // succs: block_1 * } - * - * If the phi does not select an undef, the instruction is duplicated in the - * loop continue block (as in the undef case) and in the previous block. When - * the ALU instruction is duplicated in the previous block, the correct source - * must be selected from the phi node. */ static bool opt_split_alu_of_phi(nir_builder *b, nir_loop *loop) @@ -394,22 +381,12 @@ opt_split_alu_of_phi(nir_builder *b, nir_loop *loop) nir_alu_instr *const alu = nir_instr_as_alu(instr); - /* Most ALU ops produce an undefined result if any source is undef. - * However, operations like bcsel only produce undefined results of the - * first operand is undef. Even in the undefined case, the result - * should be one of the other two operands, so the result of the bcsel - * should never be replaced with undef. - * - * nir_op_vec{2,3,4} and nir_op_mov are excluded because they can easily - * lead to infinite optimization loops. + /* nir_op_vec{2,3,4} and nir_op_mov are excluded because they can easily + * lead to infinite optimization loops. Splitting comparisons can lead + * to loop unrolling not recognizing loop termintators, and type + * conversions also lead to regressions. */ - if (alu->op == nir_op_bcsel || - alu->op == nir_op_b32csel || - alu->op == nir_op_fcsel || - alu->op == nir_op_vec2 || - alu->op == nir_op_vec3 || - alu->op == nir_op_vec4 || - alu->op == nir_op_mov || + if (nir_op_is_vec(alu->op) || alu_instr_is_comparison(alu) || alu_instr_is_type_conversion(alu)) continue; @@ -477,26 +454,9 @@ opt_split_alu_of_phi(nir_builder *b, nir_loop *loop) if (has_phi_src_from_prev_block && all_non_phi_exist_in_prev_block && (is_prev_result_undef || is_prev_result_const)) { nir_block *const continue_block = find_continue_block(loop); - nir_ssa_def *prev_value; - if (!is_prev_result_undef) { - b->cursor = nir_after_block(prev_block); - prev_value = clone_alu_and_replace_src_defs(b, alu, prev_srcs); - } else { - /* Since the undef used as the source of the original ALU - * instruction may have different number of components or - * bit size than the result of that instruction, a new - * undef must be created. - */ - nir_ssa_undef_instr *undef = - nir_ssa_undef_instr_create(b->shader, - alu->dest.dest.ssa.num_components, - alu->dest.dest.ssa.bit_size); - - nir_instr_insert_after_block(prev_block, &undef->instr); - - prev_value = &undef->def; - } + b->cursor = nir_after_block(prev_block); + nir_ssa_def *prev_value = clone_alu_and_replace_src_defs(b, alu, prev_srcs); /* Make a copy of the original ALU instruction. Replace the sources * of the new instruction that read a phi with an undef source from @@ -703,7 +663,7 @@ opt_simplify_bcsel_of_phi(nir_builder *b, nir_loop *loop) nir_phi_instr *const cond_phi = nir_instr_as_phi(bcsel->src[0].src.ssa->parent_instr); - uint32_t entry_val = 0, continue_val = 0; + bool entry_val = false, continue_val = false; if (!phi_has_constant_from_outside_and_one_from_inside_loop(cond_phi, prev_block, &entry_val, @@ -1036,7 +996,7 @@ opt_if_loop_terminator(nir_if *nif) if (is_block_empty(first_continue_from_blk)) return false; - if (!nir_is_trivial_loop_if(nif, break_blk)) + if (nir_block_ends_in_jump(continue_from_blk)) return false; /* Even though this if statement has a jump on one side, we may still have @@ -1372,7 +1332,6 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list, progress |= opt_if_cf_list(b, &loop->body, aggressive_last_continue); progress |= opt_simplify_bcsel_of_phi(b, loop); - progress |= opt_peel_loop_initial_if(loop); progress |= opt_if_loop_last_continue(loop, aggressive_last_continue); break; @@ -1386,6 +1345,37 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list, return progress; } +static bool +opt_peel_loop_initial_if_cf_list(struct exec_list *cf_list) +{ + bool progress = false; + foreach_list_typed(nir_cf_node, cf_node, node, cf_list) { + switch (cf_node->type) { + case nir_cf_node_block: + break; + + case nir_cf_node_if: { + nir_if *nif = nir_cf_node_as_if(cf_node); + progress |= opt_peel_loop_initial_if_cf_list(&nif->then_list); + progress |= opt_peel_loop_initial_if_cf_list(&nif->else_list); + break; + } + + case nir_cf_node_loop: { + nir_loop *loop = nir_cf_node_as_loop(cf_node); + progress |= opt_peel_loop_initial_if_cf_list(&loop->body); + progress |= opt_peel_loop_initial_if(loop); + break; + } + + case nir_cf_node_function: + unreachable("Invalid cf type"); + } + } + + return progress; +} + /** * These optimisations depend on nir_metadata_block_index and therefore must * not do anything to cause the metadata to become invalid. @@ -1440,17 +1430,26 @@ nir_opt_if(nir_shader *shader, bool aggressive_last_continue) nir_metadata_preserve(function->impl, nir_metadata_block_index | nir_metadata_dominance); - if (opt_if_cf_list(&b, &function->impl->body, - aggressive_last_continue)) { - nir_metadata_preserve(function->impl, nir_metadata_none); + bool preserve = true; + + if (opt_if_cf_list(&b, &function->impl->body, aggressive_last_continue)) { + preserve = false; + progress = true; + } + + if (opt_peel_loop_initial_if_cf_list(&function->impl->body)) { + preserve = false; + progress = true; /* If that made progress, we're no longer really in SSA form. We * need to convert registers back into SSA defs and clean up SSA defs * that don't dominate their uses. */ nir_lower_regs_to_ssa_impl(function->impl); + } - progress = true; + if (preserve) { + nir_metadata_preserve(function->impl, nir_metadata_none); } else { #ifndef NDEBUG function->impl->valid_metadata &= ~nir_metadata_not_properly_reset;