i965: Remove duplicate MRF writes in the FS backend.
authorEric Anholt <eric@anholt.net>
Fri, 19 Nov 2010 07:57:05 +0000 (15:57 +0800)
committerEric Anholt <eric@anholt.net>
Sat, 20 Nov 2010 04:05:56 +0000 (20:05 -0800)
This is quite common for multitexture sampling, and not only cuts down
on the second and later set of MOVs, but typically also allows
compute-to-MRF on the first set.

No statistically siginficant performance difference in nexuiz (n=3),
but it reduces instruction count in one of its shaders and seems like
a good idea.

src/mesa/drivers/dri/i965/brw_fs.cpp
src/mesa/drivers/dri/i965/brw_fs.h

index 76794fa..edb02fa 100644 (file)
@@ -177,6 +177,46 @@ type_size(const struct glsl_type *type)
    }
 }
 
+/**
+ * Returns how many MRFs an FS opcode will write over.
+ *
+ * Note that this is not the 0 or 1 implied writes in an actual gen
+ * instruction -- the FS opcodes often generate MOVs in addition.
+ */
+int
+fs_visitor::implied_mrf_writes(fs_inst *inst)
+{
+   if (inst->mlen == 0)
+      return 0;
+
+   switch (inst->opcode) {
+   case FS_OPCODE_RCP:
+   case FS_OPCODE_RSQ:
+   case FS_OPCODE_SQRT:
+   case FS_OPCODE_EXP2:
+   case FS_OPCODE_LOG2:
+   case FS_OPCODE_SIN:
+   case FS_OPCODE_COS:
+      return 1;
+   case FS_OPCODE_POW:
+      return 2;
+   case FS_OPCODE_TEX:
+   case FS_OPCODE_TXB:
+   case FS_OPCODE_TXL:
+      return 1;
+   case FS_OPCODE_FB_WRITE:
+      return 2;
+   case FS_OPCODE_PULL_CONSTANT_LOAD:
+   case FS_OPCODE_UNSPILL:
+      return 1;
+   case FS_OPCODE_SPILL:
+      return 2;
+   default:
+      assert(!"not reached");
+      return inst->mlen;
+   }
+}
+
 int
 fs_visitor::virtual_grf_alloc(int size)
 {
@@ -3066,6 +3106,78 @@ fs_visitor::compute_to_mrf()
    return progress;
 }
 
+/**
+ * Walks through basic blocks, locking for repeated MRF writes and
+ * removing the later ones.
+ */
+bool
+fs_visitor::remove_duplicate_mrf_writes()
+{
+   fs_inst *last_mrf_move[16];
+   bool progress = false;
+
+   memset(last_mrf_move, 0, sizeof(last_mrf_move));
+
+   foreach_iter(exec_list_iterator, iter, this->instructions) {
+      fs_inst *inst = (fs_inst *)iter.get();
+
+      switch (inst->opcode) {
+      case BRW_OPCODE_DO:
+      case BRW_OPCODE_WHILE:
+      case BRW_OPCODE_IF:
+      case BRW_OPCODE_ELSE:
+      case BRW_OPCODE_ENDIF:
+        memset(last_mrf_move, 0, sizeof(last_mrf_move));
+        continue;
+      default:
+        break;
+      }
+
+      if (inst->opcode == BRW_OPCODE_MOV &&
+         inst->dst.file == MRF) {
+        fs_inst *prev_inst = last_mrf_move[inst->dst.hw_reg];
+        if (prev_inst && inst->equals(prev_inst)) {
+           inst->remove();
+           progress = true;
+           continue;
+        }
+      }
+
+      /* Clear out the last-write records for MRFs that were overwritten. */
+      if (inst->dst.file == MRF) {
+        last_mrf_move[inst->dst.hw_reg] = NULL;
+      }
+
+      if (inst->mlen > 0) {
+        /* Found a SEND instruction, which will include two of fewer
+         * implied MRF writes.  We could do better here.
+         */
+        for (int i = 0; i < implied_mrf_writes(inst); i++) {
+           last_mrf_move[inst->base_mrf + i] = NULL;
+        }
+      }
+
+      /* Clear out any MRF move records whose sources got overwritten. */
+      if (inst->dst.file == GRF) {
+        for (unsigned int i = 0; i < Elements(last_mrf_move); i++) {
+           if (last_mrf_move[i] &&
+               last_mrf_move[i]->src[0].reg == inst->dst.reg) {
+              last_mrf_move[i] = NULL;
+           }
+        }
+      }
+
+      if (inst->opcode == BRW_OPCODE_MOV &&
+         inst->dst.file == MRF &&
+         inst->src[0].file == GRF &&
+         !inst->predicated) {
+        last_mrf_move[inst->dst.hw_reg] = inst;
+      }
+   }
+
+   return progress;
+}
+
 bool
 fs_visitor::virtual_grf_interferes(int a, int b)
 {
@@ -3438,6 +3550,9 @@ brw_wm_fs_emit(struct brw_context *brw, struct brw_wm_compile *c)
       bool progress;
       do {
         progress = false;
+
+        progress = v.remove_duplicate_mrf_writes() || progress;
+
         v.calculate_live_intervals();
         progress = v.propagate_constants() || progress;
         progress = v.register_coalesce() || progress;
index f546fab..de7b153 100644 (file)
@@ -148,6 +148,21 @@ public:
    fs_reg(enum register_file file, int hw_reg, uint32_t type);
    fs_reg(class fs_visitor *v, const struct glsl_type *type);
 
+   bool equals(fs_reg *r)
+   {
+      return (file == r->file &&
+             reg == r->reg &&
+             reg_offset == r->reg_offset &&
+             hw_reg == r->hw_reg &&
+             type == r->type &&
+             negate == r->negate &&
+             abs == r->abs &&
+             memcmp(&fixed_hw_reg, &r->fixed_hw_reg,
+                    sizeof(fixed_hw_reg)) == 0 &&
+             smear == r->smear &&
+             imm.u == r->imm.u);
+   }
+
    /** Register file: ARF, GRF, MRF, IMM. */
    enum register_file file;
    /** virtual register number.  0 = fixed hw reg */
@@ -270,6 +285,26 @@ public:
         assert(src[2].reg_offset >= 0);
    }
 
+   bool equals(fs_inst *inst)
+   {
+      return (opcode == inst->opcode &&
+             dst.equals(&inst->dst) &&
+             src[0].equals(&inst->src[0]) &&
+             src[1].equals(&inst->src[1]) &&
+             src[2].equals(&inst->src[2]) &&
+             saturate == inst->saturate &&
+             predicated == inst->predicated &&
+             conditional_mod == inst->conditional_mod &&
+             mlen == inst->mlen &&
+             base_mrf == inst->base_mrf &&
+             sampler == inst->sampler &&
+             target == inst->target &&
+             eot == inst->eot &&
+             header_present == inst->header_present &&
+             shadow_compare == inst->shadow_compare &&
+             offset == inst->offset);
+   }
+
    int opcode; /* BRW_OPCODE_* or FS_OPCODE_* */
    fs_reg dst;
    fs_reg src[3];
@@ -372,6 +407,7 @@ public:
    bool register_coalesce();
    bool compute_to_mrf();
    bool dead_code_eliminate();
+   bool remove_duplicate_mrf_writes();
    bool virtual_grf_interferes(int a, int b);
    void generate_code();
    void generate_fb_write(fs_inst *inst);
@@ -409,6 +445,7 @@ public:
    struct brw_reg interp_reg(int location, int channel);
    int setup_uniform_values(int loc, const glsl_type *type);
    void setup_builtin_uniform_values(ir_variable *ir);
+   int implied_mrf_writes(fs_inst *inst);
 
    struct brw_context *brw;
    const struct gl_fragment_program *fp;