nir/gcm: Add global value numbering support
authorJason Ekstrand <jason.ekstrand@intel.com>
Wed, 10 Aug 2016 21:34:49 +0000 (14:34 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Fri, 9 Sep 2016 03:53:01 +0000 (20:53 -0700)
Unlike the current CSE pass, global value numbering is capable of detecting
common values even if one does not dominate the other.  For instance, in
you have

if (...) {
   ssa_1 = ssa_0 + 7;
   /* use ssa_1 */
} else {
   ssa_2 = ssa_0 + 7;
   /* use ssa_2 */
}

Global value numbering doesn't care about dominance relationships so it
figures out that ssa_1 and ssa_2 are the same and converts this to

if (...) {
   ssa_1 = ssa_0 + 7;
   /* use ssa_1 */
} else {
   /* use ssa_1 */
}

Obviously, we just broke SSA form which is bad.  Global code motion,
however, will repair this for us by turning this into

ssa_1 = ssa_0 + 7;
if (...) {
   /* use ssa_1 */
} else {
   /* use ssa_1 */
}

This intended to eventually mostly replace CSE.  However, conventional CSE
may still be useful because it's less of a scorched-earth approach and
doesn't require GCM.  This makes it a bit more appropriate for use as a
clean-up in a late optimization run.

Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/compiler/nir/nir.h
src/compiler/nir/nir_opt_gcm.c

index c1cf94001f6540540f7d9d9262f43fa8c713b891..ff7c422a6ea087ab8db57a58acd2fedf6b2315f5 100644 (file)
@@ -2587,7 +2587,7 @@ bool nir_opt_dce(nir_shader *shader);
 
 bool nir_opt_dead_cf(nir_shader *shader);
 
-void nir_opt_gcm(nir_shader *shader);
+bool nir_opt_gcm(nir_shader *shader, bool value_number);
 
 bool nir_opt_peephole_select(nir_shader *shader);
 
index 02a93489ebaf97e9eeff83ac8d62654c9d56ac39..77eb8e6da5ab6f0a6983ad0df8e65239dec975d4 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #include "nir.h"
+#include "nir_instr_set.h"
 
 /*
  * Implements Global Code Motion.  A description of GCM can be found in
@@ -451,8 +452,8 @@ gcm_place_instr(nir_instr *instr, struct gcm_state *state)
    block_info->last_instr = instr;
 }
 
-static void
-opt_gcm_impl(nir_function_impl *impl)
+static bool
+opt_gcm_impl(nir_function_impl *impl, bool value_number)
 {
    struct gcm_state state;
 
@@ -470,6 +471,18 @@ opt_gcm_impl(nir_function_impl *impl)
       gcm_pin_instructions_block(block, &state);
    }
 
+   bool progress = false;
+   if (value_number) {
+      struct set *gvn_set = nir_instr_set_create(NULL);
+      foreach_list_typed_safe(nir_instr, instr, node, &state.instrs) {
+         if (nir_instr_set_add_or_rewrite(gvn_set, instr)) {
+            nir_instr_remove(instr);
+            progress = true;
+         }
+      }
+      nir_instr_set_destroy(gvn_set);
+   }
+
    foreach_list_typed(nir_instr, instr, node, &state.instrs)
       gcm_schedule_early_instr(instr, &state);
 
@@ -486,13 +499,19 @@ opt_gcm_impl(nir_function_impl *impl)
 
    nir_metadata_preserve(impl, nir_metadata_block_index |
                                nir_metadata_dominance);
+
+   return progress;
 }
 
-void
-nir_opt_gcm(nir_shader *shader)
+bool
+nir_opt_gcm(nir_shader *shader, bool value_number)
 {
+   bool progress = false;
+
    nir_foreach_function(function, shader) {
       if (function->impl)
-         opt_gcm_impl(function->impl);
+         progress |= opt_gcm_impl(function->impl, value_number);
    }
+
+   return progress;
 }