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_qir_lower_uniforms.c 26 * 27 * This is the pre-code-generation pass for fixing up instructions that try to 28 * read from multiple uniform values. 29 */ 30 31 #include "vc4_qir.h" 32 #include "util/hash_table.h" 33 #include "util/u_math.h" 34 35 static inline uint32_t 36 index_hash(const void *key) 37 { 38 return (uintptr_t)key; 39 } 40 41 static inline bool 42 index_compare(const void *a, const void *b) 43 { 44 return a == b; 45 } 46 47 static void 48 add_uniform(struct hash_table *ht, struct qreg reg) 49 { 50 struct hash_entry *entry; 51 void *key = (void *)(uintptr_t)(reg.index + 1); 52 53 entry = _mesa_hash_table_search(ht, key); 54 if (entry) { 55 entry->data++; 56 } else { 57 _mesa_hash_table_insert(ht, key, (void *)(uintptr_t)1); 58 } 59 } 60 61 static void 62 remove_uniform(struct hash_table *ht, struct qreg reg) 63 { 64 struct hash_entry *entry; 65 void *key = (void *)(uintptr_t)(reg.index + 1); 66 67 entry = _mesa_hash_table_search(ht, key); 68 assert(entry); 69 entry->data--; 70 if (entry->data == NULL) 71 _mesa_hash_table_remove(ht, entry); 72 } 73 74 static bool 75 is_lowerable_uniform(struct qinst *inst, int i) 76 { 77 if (inst->src[i].file != QFILE_UNIF) 78 return false; 79 if (qir_is_tex(inst)) 80 return i != qir_get_tex_uniform_src(inst); 81 return true; 82 } 83 84 /* Returns the number of different uniform values referenced by the 85 * instruction. 86 */ 87 static uint32_t 88 qir_get_instruction_uniform_count(struct qinst *inst) 89 { 90 uint32_t count = 0; 91 92 for (int i = 0; i < qir_get_nsrc(inst); i++) { 93 if (inst->src[i].file != QFILE_UNIF) 94 continue; 95 96 bool is_duplicate = false; 97 for (int j = 0; j < i; j++) { 98 if (inst->src[j].file == QFILE_UNIF && 99 inst->src[j].index == inst->src[i].index) { 100 is_duplicate = true; 101 break; 102 } 103 } 104 if (!is_duplicate) 105 count++; 106 } 107 108 return count; 109 } 110 111 void 112 qir_lower_uniforms(struct vc4_compile *c) 113 { 114 struct hash_table *ht = 115 _mesa_hash_table_create(c, index_hash, index_compare); 116 117 /* Walk the instruction list, finding which instructions have more 118 * than one uniform referenced, and add those uniform values to the 119 * ht. 120 */ 121 qir_for_each_inst_inorder(inst, c) { 122 uint32_t nsrc = qir_get_nsrc(inst); 123 124 if (qir_get_instruction_uniform_count(inst) <= 1) 125 continue; 126 127 for (int i = 0; i < nsrc; i++) { 128 if (is_lowerable_uniform(inst, i)) 129 add_uniform(ht, inst->src[i]); 130 } 131 } 132 133 while (ht->entries) { 134 /* Find the most commonly used uniform in instructions that 135 * need a uniform lowered. 136 */ 137 uint32_t max_count = 0; 138 uint32_t max_index = 0; 139 struct hash_entry *entry; 140 hash_table_foreach(ht, entry) { 141 uint32_t count = (uintptr_t)entry->data; 142 uint32_t index = (uintptr_t)entry->key - 1; 143 if (count > max_count) { 144 max_count = count; 145 max_index = index; 146 } 147 } 148 149 struct qreg unif = qir_reg(QFILE_UNIF, max_index); 150 151 /* Now, find the instructions using this uniform and make them 152 * reference a temp instead. 153 */ 154 qir_for_each_block(block, c) { 155 struct qinst *mov = NULL; 156 157 qir_for_each_inst(inst, block) { 158 uint32_t nsrc = qir_get_nsrc(inst); 159 160 uint32_t count = qir_get_instruction_uniform_count(inst); 161 162 if (count <= 1) 163 continue; 164 165 /* If the block doesn't have a load of hte 166 * uniform yet, add it. We could potentially 167 * do better and CSE MOVs from multiple blocks 168 * into dominating blocks, except that may 169 * cause troubles for register allocation. 170 */ 171 if (!mov) { 172 mov = qir_inst(QOP_MOV, qir_get_temp(c), 173 unif, c->undef); 174 list_add(&mov->link, 175 &block->instructions); 176 c->defs[mov->dst.index] = mov; 177 } 178 179 bool removed = false; 180 for (int i = 0; i < nsrc; i++) { 181 if (is_lowerable_uniform(inst, i) && 182 inst->src[i].index == max_index) { 183 inst->src[i] = mov->dst; 184 remove_uniform(ht, unif); 185 removed = true; 186 } 187 } 188 if (removed) 189 count--; 190 191 /* If the instruction doesn't need lowering any more, 192 * then drop it from the list. 193 */ 194 if (count <= 1) { 195 for (int i = 0; i < nsrc; i++) { 196 if (is_lowerable_uniform(inst, i)) 197 remove_uniform(ht, inst->src[i]); 198 } 199 } 200 } 201 } 202 } 203 204 _mesa_hash_table_destroy(ht, NULL); 205 } 206