Edit

IABSD.fr/xenocara/lib/mesa/src/intel/compiler/brw_def_analysis.cpp

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-06-05 11:23:11
    Hash : 67d6f117
    Message : Import Mesa 25.0.7

  • lib/mesa/src/intel/compiler/brw_def_analysis.cpp
  • /*
     * Copyright © 2023 Intel Corporation
     *
     * SPDX-License-Identifier: MIT
     */
    
    #include "brw_fs.h"
    #include "brw_cfg.h"
    #include "brw_ir_analysis.h"
    
    /**
     * An opportunistic SSA-def analysis pass.
     *
     * VGRFs are considered defs (SSA values) when:
     *
     * 1. One instruction wholly defines the register (including all offsets)
     * 2. The single defining write dominates all uses
     * 3. All sources of the definition are also defs (for non-VGRF files)
     *
     * We don't consider non-VGRF sources to prevent an instruction from forming
     * an SSA def.  The other files represent immediates, pushed uniforms, inputs
     * to shaders, thread payload fields, and so on.  In theory, we could mutate
     * FIXED_GRF register values, but we don't today, so it isn't an issue.
     *
     * Limitations:
     * - We do not track uses, only definitions.
     * - We do not handle flags, address registers, or accumulators yet.
     *
     * Usage:
     *
     *    const def_analysis &defs = s.def_analysis.require();
     *    fs_inst *def = defs.get(inst->src[i]); // returns NULL if non-SSA
     *    bblock_t *block = defs.get_block(inst->src[i]); // block containing def
     *
     * Def analysis requires the dominator tree, but not liveness information.
     */
    
    using namespace brw;
    
    static fs_inst *const UNSEEN = (fs_inst *) (uintptr_t) 1;
    
    void
    def_analysis::mark_invalid(int nr)
    {
       def_blocks[nr] = NULL;
       def_insts[nr] = NULL;
    }
    
    void
    def_analysis::update_for_reads(const idom_tree &idom,
                                   bblock_t *block,
                                   fs_inst *inst)
    {
       /* We don't track accumulator use for def analysis, so if an instruction
        * implicitly reads the accumulator, we don't consider it to produce a def.
        */
       if (inst->reads_accumulator_implicitly())
          mark_invalid(inst->dst.nr);
    
       for (int i = 0; i < inst->sources; i++) {
          const int nr = inst->src[i].nr;
    
          if (inst->src[i].file != VGRF) {
             /* Similarly, explicit reads of accumulators, address registers,
              * and flags make the destination not a def, as we don't track those.
              */
             if (inst->src[i].file == ARF &&
                 (nr == BRW_ARF_ADDRESS ||
                  nr == BRW_ARF_ACCUMULATOR ||
                  nr == BRW_ARF_FLAG))
                mark_invalid(inst->dst.nr);
    
             continue;
          }
    
          def_use_counts[nr]++;
    
          if (def_insts[nr]) {
             /* Mark the source def invalid in two cases:
              *
              * 1. The register is used before being written
              * 2. The def doesn't dominate our use.
              *
              */
             if (def_insts[nr] == UNSEEN ||
                 !idom.dominates(def_blocks[nr], block))
                mark_invalid(nr);
          }
    
          /* Additionally, if one of our sources is not a def, then our
           * destination may have multiple dynamic assignments.
           */
          if (!def_insts[nr] && inst->dst.file == VGRF)
             mark_invalid(inst->dst.nr);
       }
    }
    
    bool
    def_analysis::fully_defines(const fs_visitor *v, fs_inst *inst)
    {
       return v->alloc.sizes[inst->dst.nr] * REG_SIZE == inst->size_written &&
              !inst->is_partial_write();
    }
    
    void
    def_analysis::update_for_write(const fs_visitor *v,
                                   bblock_t *block,
                                   fs_inst *inst)
    {
       const int nr = inst->dst.nr;
    
       if (inst->dst.file != VGRF || !def_insts[nr])
          return;
    
       /* If this is our first write to the destination, and it fully defines
        * the destination, then consider it an SSA def for now.
        */
       if (def_insts[nr] == UNSEEN && fully_defines(v, inst)) {
          def_insts[nr] = inst;
          def_blocks[nr] = block;
       } else {
          /* Otherwise this is a second write or a partial write, in which
           * case we know with certainty that this isn't an SSA def.
           */
          mark_invalid(nr);
       }
    }
    
    def_analysis::def_analysis(const fs_visitor *v)
    {
       const idom_tree &idom = v->idom_analysis.require();
    
       def_count = v->alloc.count;
    
       def_insts      = new fs_inst*[def_count]();
       def_blocks     = new bblock_t*[def_count]();
       def_use_counts = new uint32_t[def_count]();
    
       for (unsigned i = 0; i < def_count; i++)
          def_insts[i] = UNSEEN;
    
       foreach_block_and_inst(block, fs_inst, inst, v->cfg) {
          if (inst->opcode != SHADER_OPCODE_UNDEF) {
             update_for_reads(idom, block, inst);
             update_for_write(v, block, inst);
          }
       }
    
       bool iterate;
       do {
          iterate = false;
    
          for (unsigned d = 0; d < def_count; d++) {
             /* Anything still unseen was never written and thus dead code. */
             if (def_insts[d] == UNSEEN)
                def_insts[d] = NULL;
    
             fs_inst *def = def_insts[d];
             if (!def)
                continue;
    
             for (int i = 0; i < def->sources; i++) {
                if (def->src[i].file != VGRF)
                   continue;
    
                const int nr = def->src[i].nr;
    
                /* If our "def" reads a non-SSA source, then it isn't a def. */
                if (!def_insts[nr] || def_insts[nr] == UNSEEN) {
                   mark_invalid(def->dst.nr);
                   iterate = true;
                   break;
                }
             }
          }
       } while (iterate);
    }
    
    def_analysis::~def_analysis()
    {
       delete[] def_insts;
       delete[] def_blocks;
       delete[] def_use_counts;
    }
    
    bool
    def_analysis::validate(const fs_visitor *v) const
    {
       for (unsigned i = 0; i < def_count; i++) {
          assert(!def_insts[i] == !def_blocks[i]);
       }
    
       return true;
    }
    
    unsigned
    def_analysis::ssa_count() const
    {
       unsigned defs = 0;
    
       for (unsigned i = 0; i < def_count; i++) {
          if (def_insts[i])
             ++defs;
       }
    
       return defs;
    }
    
    void
    def_analysis::print_stats(const fs_visitor *v) const
    {
       const unsigned defs = ssa_count();
    
       fprintf(stderr, "DEFS: %u registers, %u SSA, %u non-SSA => %.1f SSA\n",
               def_count, defs, def_count - defs,
               100.0f * float(defs) / float(def_count));
    }