vc4: Keep an array of pointers to instructions defining the temps around.
[mesa.git] / src / gallium / drivers / vc4 / vc4_qir_lower_uniforms.c
1 /*
2 * Copyright © 2014 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /**
25 * @file vc4_opt_algebraic.c
26 *
27 * This is the optimization pass for miscellaneous changes to instructions
28 * where we can simplify the operation by some knowledge about the specific
29 * operations.
30 *
31 * Mostly this will be a matter of turning things into MOVs so that they can
32 * later be copy-propagated out.
33 */
34
35 #include "vc4_qir.h"
36 #include "util/hash_table.h"
37 #include "util/u_math.h"
38
39 static inline uint32_t
40 index_hash(const void *key)
41 {
42 return (uintptr_t)key;
43 }
44
45 static inline bool
46 index_compare(const void *a, const void *b)
47 {
48 return a == b;
49 }
50
51 static void
52 add_uniform(struct hash_table *ht, struct qreg reg)
53 {
54 struct hash_entry *entry;
55 void *key = (void *)(uintptr_t)reg.index;
56
57 entry = _mesa_hash_table_search(ht, key);
58 if (entry) {
59 entry->data++;
60 } else {
61 _mesa_hash_table_insert(ht, key, (void *)(uintptr_t)1);
62 }
63 }
64
65 static void
66 remove_uniform(struct hash_table *ht, struct qreg reg)
67 {
68 struct hash_entry *entry;
69 void *key = (void *)(uintptr_t)reg.index;
70
71 entry = _mesa_hash_table_search(ht, key);
72 assert(entry);
73 entry->data--;
74 if (entry->data == NULL)
75 _mesa_hash_table_remove(ht, entry);
76 }
77
78 static bool
79 is_lowerable_uniform(struct qinst *inst, int i)
80 {
81 if (inst->src[i].file != QFILE_UNIF)
82 return false;
83 if (qir_is_tex(inst))
84 return i != 1;
85 return true;
86 }
87
88 void
89 qir_lower_uniforms(struct vc4_compile *c)
90 {
91 struct simple_node *node;
92 struct hash_table *ht =
93 _mesa_hash_table_create(c, index_hash, index_compare);
94
95 /* Walk the instruction list, finding which instructions have more
96 * than one uniform referenced, and add those uniform values to the
97 * ht.
98 */
99 foreach(node, &c->instructions) {
100 struct qinst *inst = (struct qinst *)node;
101 uint32_t nsrc = qir_get_op_nsrc(inst->op);
102
103 uint32_t count = 0;
104 for (int i = 0; i < nsrc; i++) {
105 if (inst->src[i].file == QFILE_UNIF)
106 count++;
107 }
108
109 if (count <= 1)
110 continue;
111
112 for (int i = 0; i < nsrc; i++) {
113 if (is_lowerable_uniform(inst, i))
114 add_uniform(ht, inst->src[i]);
115 }
116 }
117
118 while (ht->entries) {
119 /* Find the most commonly used uniform in instructions that
120 * need a uniform lowered.
121 */
122 uint32_t max_count = 0;
123 uint32_t max_index = 0;
124 struct hash_entry *entry;
125 hash_table_foreach(ht, entry) {
126 uint32_t count = (uintptr_t)entry->data;
127 uint32_t index = (uintptr_t)entry->key;
128 if (count > max_count) {
129 max_count = count;
130 max_index = index;
131 }
132 }
133
134 /* Now, find the instructions using this uniform and make them
135 * reference a temp instead.
136 */
137 struct qreg temp = qir_get_temp(c);
138 struct qreg unif = { QFILE_UNIF, max_index };
139 struct qinst *mov = qir_inst(QOP_MOV, temp, unif, c->undef);
140 insert_at_head(&c->instructions, &mov->link);
141 c->defs[temp.index] = mov;
142 foreach(node, &c->instructions) {
143 struct qinst *inst = (struct qinst *)node;
144 uint32_t nsrc = qir_get_op_nsrc(inst->op);
145
146 uint32_t count = 0;
147 for (int i = 0; i < nsrc; i++) {
148 if (inst->src[i].file == QFILE_UNIF)
149 count++;
150 }
151
152 if (count <= 1)
153 continue;
154
155 for (int i = 0; i < nsrc; i++) {
156 if (is_lowerable_uniform(inst, i) &&
157 inst->src[i].index == max_index) {
158 inst->src[i] = temp;
159 remove_uniform(ht, unif);
160 count--;
161 }
162 }
163
164 /* If the instruction doesn't need lowering any more,
165 * then drop it from the list.
166 */
167 if (count <= 1) {
168 for (int i = 0; i < nsrc; i++) {
169 if (is_lowerable_uniform(inst, i))
170 remove_uniform(ht, inst->src[i]);
171 }
172 }
173 }
174 }
175
176 _mesa_hash_table_destroy(ht, NULL);
177 }