summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mesa/drivers/dri/i965/brw_fs.cpp160
-rw-r--r--src/mesa/drivers/dri/i965/brw_fs.h1
2 files changed, 161 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/i965/brw_fs.cpp b/src/mesa/drivers/dri/i965/brw_fs.cpp
index 09817b7058b..76ecd6958f0 100644
--- a/src/mesa/drivers/dri/i965/brw_fs.cpp
+++ b/src/mesa/drivers/dri/i965/brw_fs.cpp
@@ -32,6 +32,7 @@ extern "C" {
#include <sys/types.h>
+#include "main/hash_table.h"
#include "main/macros.h"
#include "main/shaderobj.h"
#include "main/uniforms.h"
@@ -1823,6 +1824,164 @@ fs_visitor::dead_code_eliminate()
return progress;
}
+struct dead_code_hash_key
+{
+ int vgrf;
+ int reg_offset;
+};
+
+static bool
+dead_code_hash_compare(const void *a, const void *b)
+{
+ return memcmp(a, b, sizeof(struct dead_code_hash_key)) == 0;
+}
+
+static void
+clear_dead_code_hash(struct hash_table *ht)
+{
+ struct hash_entry *entry;
+
+ hash_table_foreach(ht, entry) {
+ _mesa_hash_table_remove(ht, entry);
+ }
+}
+
+static void
+insert_dead_code_hash(struct hash_table *ht,
+ int vgrf, int reg_offset, fs_inst *inst)
+{
+ /* We don't bother freeing keys, because they'll be GCed with the ht. */
+ struct dead_code_hash_key *key = ralloc(ht, struct dead_code_hash_key);
+
+ key->vgrf = vgrf;
+ key->reg_offset = reg_offset;
+
+ _mesa_hash_table_insert(ht, _mesa_hash_data(key, sizeof(*key)), key, inst);
+}
+
+static struct hash_entry *
+get_dead_code_hash_entry(struct hash_table *ht, int vgrf, int reg_offset)
+{
+ struct dead_code_hash_key key;
+
+ key.vgrf = vgrf;
+ key.reg_offset = reg_offset;
+
+ return _mesa_hash_table_search(ht, _mesa_hash_data(&key, sizeof(key)), &key);
+}
+
+static void
+remove_dead_code_hash(struct hash_table *ht,
+ int vgrf, int reg_offset)
+{
+ struct hash_entry *entry = get_dead_code_hash_entry(ht, vgrf, reg_offset);
+ if (!entry)
+ return;
+
+ _mesa_hash_table_remove(ht, entry);
+}
+
+/**
+ * Walks basic blocks, removing any regs that are written but not read before
+ * being redefined.
+ *
+ * The dead_code_eliminate() function implements a global dead code
+ * elimination, but it only handles the removing the last write to a register
+ * if it's never read. This one can handle intermediate writes, but only
+ * within a basic block.
+ */
+bool
+fs_visitor::dead_code_eliminate_local()
+{
+ struct hash_table *ht;
+ bool progress = false;
+
+ ht = _mesa_hash_table_create(mem_ctx, dead_code_hash_compare);
+
+ foreach_list_safe(node, &this->instructions) {
+ fs_inst *inst = (fs_inst *)node;
+
+ /* At a basic block, empty the HT since we don't understand dataflow
+ * here.
+ */
+ if (inst->is_control_flow()) {
+ clear_dead_code_hash(ht);
+ continue;
+ }
+
+ /* Clear the HT of any instructions that got read. */
+ for (int i = 0; i < 3; i++) {
+ fs_reg src = inst->src[i];
+ if (src.file != GRF)
+ continue;
+
+ int read = 1;
+ if (inst->is_send_from_grf())
+ read = virtual_grf_sizes[src.reg] - src.reg_offset;
+
+ for (int reg_offset = src.reg_offset;
+ reg_offset < src.reg_offset + read;
+ reg_offset++) {
+ remove_dead_code_hash(ht, src.reg, reg_offset);
+ }
+ }
+
+ /* Add any update of a GRF to the HT, removing a previous write if it
+ * wasn't read.
+ */
+ if (inst->dst.file == GRF) {
+ if (inst->regs_written > 1) {
+ /* We don't know how to trim channels from an instruction's
+ * writes, so we can't incrementally remove unread channels from
+ * it. Just remove whatever it overwrites from the table
+ */
+ for (int i = 0; i < inst->regs_written; i++) {
+ remove_dead_code_hash(ht,
+ inst->dst.reg,
+ inst->dst.reg_offset + i);
+ }
+ } else {
+ struct hash_entry *entry =
+ get_dead_code_hash_entry(ht, inst->dst.reg,
+ inst->dst.reg_offset);
+
+ if (inst->is_partial_write()) {
+ /* For a partial write, we can't remove any previous dead code
+ * candidate, since we're just modifying their result, but we can
+ * be dead code eliminiated ourselves.
+ */
+ if (entry) {
+ entry->data = inst;
+ } else {
+ insert_dead_code_hash(ht, inst->dst.reg, inst->dst.reg_offset,
+ inst);
+ }
+ } else {
+ if (entry) {
+ /* We're completely updating a channel, and there was a
+ * previous write to the channel that wasn't read. Kill it!
+ */
+ fs_inst *inst = (fs_inst *)entry->data;
+ inst->remove();
+ progress = true;
+ _mesa_hash_table_remove(ht, entry);
+ }
+
+ insert_dead_code_hash(ht, inst->dst.reg, inst->dst.reg_offset,
+ inst);
+ }
+ }
+ }
+ }
+
+ _mesa_hash_table_destroy(ht, NULL);
+
+ if (progress)
+ live_intervals_valid = false;
+
+ return progress;
+}
+
/**
* Implements a second type of register coalescing: This one checks if
* the two regs involved in a raw move don't interfere, in which case
@@ -2806,6 +2965,7 @@ fs_visitor::run()
progress = opt_cse() || progress;
progress = opt_copy_propagate() || progress;
progress = dead_code_eliminate() || progress;
+ progress = dead_code_eliminate_local() || progress;
progress = register_coalesce() || progress;
progress = register_coalesce_2() || progress;
progress = compute_to_mrf() || progress;
diff --git a/src/mesa/drivers/dri/i965/brw_fs.h b/src/mesa/drivers/dri/i965/brw_fs.h
index bcaa485781e..86a9ec590a4 100644
--- a/src/mesa/drivers/dri/i965/brw_fs.h
+++ b/src/mesa/drivers/dri/i965/brw_fs.h
@@ -329,6 +329,7 @@ public:
bool register_coalesce_2();
bool compute_to_mrf();
bool dead_code_eliminate();
+ bool dead_code_eliminate_local();
bool remove_dead_constants();
bool remove_duplicate_mrf_writes();
bool virtual_grf_interferes(int a, int b);