freedreno/a4xx: format updates
[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 + 1);
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 + 1);
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 hash_table *ht =
92 _mesa_hash_table_create(c, index_hash, index_compare);
93
94 /* Walk the instruction list, finding which instructions have more
95 * than one uniform referenced, and add those uniform values to the
96 * ht.
97 */
98 list_for_each_entry(struct qinst, inst, &c->instructions, link) {
99 uint32_t nsrc = qir_get_op_nsrc(inst->op);
100
101 uint32_t count = 0;
102 for (int i = 0; i < nsrc; i++) {
103 if (inst->src[i].file == QFILE_UNIF)
104 count++;
105 }
106
107 if (count <= 1)
108 continue;
109
110 for (int i = 0; i < nsrc; i++) {
111 if (is_lowerable_uniform(inst, i))
112 add_uniform(ht, inst->src[i]);
113 }
114 }
115
116 while (ht->entries) {
117 /* Find the most commonly used uniform in instructions that
118 * need a uniform lowered.
119 */
120 uint32_t max_count = 0;
121 uint32_t max_index = 0;
122 struct hash_entry *entry;
123 hash_table_foreach(ht, entry) {
124 uint32_t count = (uintptr_t)entry->data;
125 uint32_t index = (uintptr_t)entry->key - 1;
126 if (count > max_count) {
127 max_count = count;
128 max_index = index;
129 }
130 }
131
132 /* Now, find the instructions using this uniform and make them
133 * reference a temp instead.
134 */
135 struct qreg temp = qir_get_temp(c);
136 struct qreg unif = { QFILE_UNIF, max_index };
137 struct qinst *mov = qir_inst(QOP_MOV, temp, unif, c->undef);
138 list_add(&mov->link, &c->instructions);
139 c->defs[temp.index] = mov;
140 list_for_each_entry(struct qinst, inst, &c->instructions, link) {
141 uint32_t nsrc = qir_get_op_nsrc(inst->op);
142
143 uint32_t count = 0;
144 for (int i = 0; i < nsrc; i++) {
145 if (inst->src[i].file == QFILE_UNIF)
146 count++;
147 }
148
149 if (count <= 1)
150 continue;
151
152 for (int i = 0; i < nsrc; i++) {
153 if (is_lowerable_uniform(inst, i) &&
154 inst->src[i].index == max_index) {
155 inst->src[i] = temp;
156 remove_uniform(ht, unif);
157 count--;
158 }
159 }
160
161 /* If the instruction doesn't need lowering any more,
162 * then drop it from the list.
163 */
164 if (count <= 1) {
165 for (int i = 0; i < nsrc; i++) {
166 if (is_lowerable_uniform(inst, i))
167 remove_uniform(ht, inst->src[i]);
168 }
169 }
170 }
171 }
172
173 _mesa_hash_table_destroy(ht, NULL);
174 }