| /* |
| * Copyright (C) 2018 Alyssa Rosenzweig |
| * Copyright (C) 2019 Collabora, Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include "compiler.h" |
| #include "util/u_memory.h" |
| #include "midgard_ops.h" |
| |
| /* SIMD-aware dead code elimination. Perform liveness analysis step-by-step, |
| * removing dead components. If an instruction ends up with a zero mask, the |
| * instruction in total is dead and should be removed. */ |
| |
| static bool |
| can_cull_mask(compiler_context *ctx, midgard_instruction *ins) |
| { |
| if (ins->dest >= ctx->temp_count) |
| return false; |
| |
| if (ins->dest == ctx->blend_src1) |
| return false; |
| |
| if (ins->type == TAG_LOAD_STORE_4) |
| if (load_store_opcode_props[ins->op].props & LDST_SPECIAL_MASK) |
| return false; |
| |
| return true; |
| } |
| |
| static bool |
| can_dce(midgard_instruction *ins) |
| { |
| if (ins->mask) |
| return false; |
| |
| if (ins->compact_branch) |
| return false; |
| |
| if (ins->type == TAG_LOAD_STORE_4) |
| if (load_store_opcode_props[ins->op].props & LDST_SIDE_FX) |
| return false; |
| |
| if (ins->type == TAG_TEXTURE_4) |
| if (ins->op == TEXTURE_OP_BARRIER) |
| return false; |
| |
| return true; |
| } |
| |
| static bool |
| midgard_opt_dead_code_eliminate_block(compiler_context *ctx, midgard_block *block) |
| { |
| bool progress = false; |
| |
| uint16_t *live = mem_dup(block->base.live_out, ctx->temp_count * sizeof(uint16_t)); |
| |
| mir_foreach_instr_in_block_rev(block, ins) { |
| if (can_cull_mask(ctx, ins)) { |
| unsigned type_size = nir_alu_type_get_type_size(ins->dest_type); |
| unsigned round_size = type_size; |
| unsigned oldmask = ins->mask; |
| |
| /* Make sure we're packable */ |
| if (type_size == 16 && ins->type == TAG_LOAD_STORE_4) |
| round_size = 32; |
| |
| unsigned rounded = mir_round_bytemask_up(live[ins->dest], round_size); |
| unsigned cmask = mir_from_bytemask(rounded, type_size); |
| |
| ins->mask &= cmask; |
| progress |= (ins->mask != oldmask); |
| } |
| |
| mir_liveness_ins_update(live, ins, ctx->temp_count); |
| } |
| |
| mir_foreach_instr_in_block_safe(block, ins) { |
| if (can_dce(ins)) { |
| mir_remove_instruction(ins); |
| progress = true; |
| } |
| } |
| |
| free(live); |
| |
| return progress; |
| } |
| |
| bool |
| midgard_opt_dead_code_eliminate(compiler_context *ctx) |
| { |
| /* We track liveness. In fact, it's ok if we assume more things are |
| * live than they actually are, that just reduces the effectiveness of |
| * this iterations lightly. And DCE has the effect of strictly reducing |
| * liveness, so we can run DCE across all blocks while only computing |
| * liveness at the beginning. */ |
| |
| mir_invalidate_liveness(ctx); |
| mir_compute_liveness(ctx); |
| |
| bool progress = false; |
| |
| mir_foreach_block(ctx, block) { |
| progress |= midgard_opt_dead_code_eliminate_block(ctx, (midgard_block *) block); |
| } |
| |
| return progress; |
| } |
| |
| /* Removes dead moves, that is, moves with a destination overwritten before |
| * being read. Normally handled implicitly as part of DCE, but this has to run |
| * after the out-of-SSA pass */ |
| |
| bool |
| midgard_opt_dead_move_eliminate(compiler_context *ctx, midgard_block *block) |
| { |
| bool progress = false; |
| |
| mir_foreach_instr_in_block_safe(block, ins) { |
| if (ins->type != TAG_ALU_4) continue; |
| if (ins->compact_branch) continue; |
| if (!OP_IS_MOVE(ins->op)) continue; |
| |
| /* Check if it's overwritten in this block before being read */ |
| bool overwritten = false; |
| |
| mir_foreach_instr_in_block_from(block, q, mir_next_op(ins)) { |
| /* Check if used */ |
| if (mir_has_arg(q, ins->dest)) |
| break; |
| |
| /* Check if overwritten */ |
| if (q->dest == ins->dest) { |
| /* Special case to vec4; component tracking is |
| * harder */ |
| |
| overwritten = (q->mask == 0xF); |
| break; |
| } |
| } |
| |
| if (overwritten) { |
| mir_remove_instruction(ins); |
| progress = true; |
| } |
| } |
| |
| return progress; |
| } |