1 /* 2 * Copyright 2016 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 #include "compiler/v3d_compiler.h" 25 #include "qpu/qpu_instr.h" 26 #include "qpu/qpu_disasm.h" 27 28 static inline struct qpu_reg 29 qpu_reg(int index) 30 { 31 struct qpu_reg reg = { 32 .magic = false, 33 .index = index, 34 }; 35 return reg; 36 } 37 38 static inline struct qpu_reg 39 qpu_magic(enum v3d_qpu_waddr waddr) 40 { 41 struct qpu_reg reg = { 42 .magic = true, 43 .index = waddr, 44 }; 45 return reg; 46 } 47 48 static inline struct qpu_reg 49 qpu_acc(int acc) 50 { 51 return qpu_magic(V3D_QPU_WADDR_R0 + acc); 52 } 53 54 struct v3d_qpu_instr 55 v3d_qpu_nop(void) 56 { 57 struct v3d_qpu_instr instr = { 58 .type = V3D_QPU_INSTR_TYPE_ALU, 59 .alu = { 60 .add = { 61 .op = V3D_QPU_A_NOP, 62 .waddr = V3D_QPU_WADDR_NOP, 63 .magic_write = true, 64 }, 65 .mul = { 66 .op = V3D_QPU_M_NOP, 67 .waddr = V3D_QPU_WADDR_NOP, 68 .magic_write = true, 69 }, 70 } 71 }; 72 73 return instr; 74 } 75 76 static struct qinst * 77 vir_nop(void) 78 { 79 struct qreg undef = { QFILE_NULL, 0 }; 80 struct qinst *qinst = vir_add_inst(V3D_QPU_A_NOP, undef, undef, undef); 81 82 return qinst; 83 } 84 85 static struct qinst * 86 new_qpu_nop_before(struct qinst *inst) 87 { 88 struct qinst *q = vir_nop(); 89 90 list_addtail(&q->link, &inst->link); 91 92 return q; 93 } 94 95 static void 96 new_ldunif_instr(struct qinst *inst, int i) 97 { 98 struct qinst *ldunif = new_qpu_nop_before(inst); 99 100 ldunif->qpu.sig.ldunif = true; 101 assert(inst->src[i].file == QFILE_UNIF); 102 ldunif->uniform = inst->src[i].index; 103 } 104 105 /** 106 * Allocates the src register (accumulator or register file) into the RADDR 107 * fields of the instruction. 108 */ 109 static void 110 set_src(struct v3d_qpu_instr *instr, enum v3d_qpu_mux *mux, struct qpu_reg src) 111 { 112 if (src.magic) { 113 assert(src.index >= V3D_QPU_WADDR_R0 && 114 src.index <= V3D_QPU_WADDR_R5); 115 *mux = src.index - V3D_QPU_WADDR_R0 + V3D_QPU_MUX_R0; 116 return; 117 } 118 119 if (instr->alu.add.a != V3D_QPU_MUX_A && 120 instr->alu.add.b != V3D_QPU_MUX_A && 121 instr->alu.mul.a != V3D_QPU_MUX_A && 122 instr->alu.mul.b != V3D_QPU_MUX_A) { 123 instr->raddr_a = src.index; 124 *mux = V3D_QPU_MUX_A; 125 } else { 126 if (instr->raddr_a == src.index) { 127 *mux = V3D_QPU_MUX_A; 128 } else { 129 assert(!(instr->alu.add.a == V3D_QPU_MUX_B && 130 instr->alu.add.b == V3D_QPU_MUX_B && 131 instr->alu.mul.a == V3D_QPU_MUX_B && 132 instr->alu.mul.b == V3D_QPU_MUX_B) || 133 src.index == instr->raddr_b); 134 135 instr->raddr_b = src.index; 136 *mux = V3D_QPU_MUX_B; 137 } 138 } 139 } 140 141 static void 142 v3d_generate_code_block(struct v3d_compile *c, 143 struct qblock *block, 144 struct qpu_reg *temp_registers) 145 { 146 int last_vpm_read_index = -1; 147 148 vir_for_each_inst(qinst, block) { 149 #if 0 150 fprintf(stderr, "translating qinst to qpu: "); 151 vir_dump_inst(c, qinst); 152 fprintf(stderr, "\n"); 153 #endif 154 155 struct qinst *temp; 156 157 if (vir_has_implicit_uniform(qinst)) { 158 int src = vir_get_implicit_uniform_src(qinst); 159 assert(qinst->src[src].file == QFILE_UNIF); 160 qinst->uniform = qinst->src[src].index; 161 c->num_uniforms++; 162 } 163 164 int nsrc = vir_get_non_sideband_nsrc(qinst); 165 struct qpu_reg src[ARRAY_SIZE(qinst->src)]; 166 bool emitted_ldunif = false; 167 for (int i = 0; i < nsrc; i++) { 168 int index = qinst->src[i].index; 169 switch (qinst->src[i].file) { 170 case QFILE_REG: 171 src[i] = qpu_reg(qinst->src[i].index); 172 break; 173 case QFILE_MAGIC: 174 src[i] = qpu_magic(qinst->src[i].index); 175 break; 176 case QFILE_NULL: 177 case QFILE_LOAD_IMM: 178 src[i] = qpu_acc(0); 179 break; 180 case QFILE_TEMP: 181 src[i] = temp_registers[index]; 182 break; 183 case QFILE_UNIF: 184 if (!emitted_ldunif) { 185 new_ldunif_instr(qinst, i); 186 c->num_uniforms++; 187 emitted_ldunif = true; 188 } 189 190 src[i] = qpu_acc(5); 191 break; 192 case QFILE_SMALL_IMM: 193 abort(); /* XXX */ 194 #if 0 195 src[i].mux = QPU_MUX_SMALL_IMM; 196 src[i].addr = qpu_encode_small_immediate(qinst->src[i].index); 197 /* This should only have returned a valid 198 * small immediate field, not ~0 for failure. 199 */ 200 assert(src[i].addr <= 47); 201 #endif 202 break; 203 204 case QFILE_VPM: 205 assert((int)qinst->src[i].index >= 206 last_vpm_read_index); 207 (void)last_vpm_read_index; 208 last_vpm_read_index = qinst->src[i].index; 209 210 temp = new_qpu_nop_before(qinst); 211 temp->qpu.sig.ldvpm = true; 212 213 src[i] = qpu_acc(3); 214 break; 215 216 case QFILE_TLB: 217 case QFILE_TLBU: 218 unreachable("bad vir src file"); 219 } 220 } 221 222 struct qpu_reg dst; 223 switch (qinst->dst.file) { 224 case QFILE_NULL: 225 dst = qpu_magic(V3D_QPU_WADDR_NOP); 226 break; 227 228 case QFILE_REG: 229 dst = qpu_reg(qinst->dst.index); 230 break; 231 232 case QFILE_MAGIC: 233 dst = qpu_magic(qinst->dst.index); 234 break; 235 236 case QFILE_TEMP: 237 dst = temp_registers[qinst->dst.index]; 238 break; 239 240 case QFILE_VPM: 241 dst = qpu_magic(V3D_QPU_WADDR_VPM); 242 break; 243 244 case QFILE_TLB: 245 dst = qpu_magic(V3D_QPU_WADDR_TLB); 246 break; 247 248 case QFILE_TLBU: 249 dst = qpu_magic(V3D_QPU_WADDR_TLBU); 250 break; 251 252 case QFILE_UNIF: 253 case QFILE_SMALL_IMM: 254 case QFILE_LOAD_IMM: 255 assert(!"not reached"); 256 break; 257 } 258 259 if (qinst->qpu.type == V3D_QPU_INSTR_TYPE_ALU) { 260 if (v3d_qpu_sig_writes_address(c->devinfo, 261 &qinst->qpu.sig)) { 262 assert(qinst->qpu.alu.add.op == V3D_QPU_A_NOP); 263 assert(qinst->qpu.alu.mul.op == V3D_QPU_M_NOP); 264 265 qinst->qpu.sig_addr = dst.index; 266 qinst->qpu.sig_magic = dst.magic; 267 } else if (qinst->qpu.alu.add.op != V3D_QPU_A_NOP) { 268 assert(qinst->qpu.alu.mul.op == V3D_QPU_M_NOP); 269 if (nsrc >= 1) { 270 set_src(&qinst->qpu, 271 &qinst->qpu.alu.add.a, src[0]); 272 } 273 if (nsrc >= 2) { 274 set_src(&qinst->qpu, 275 &qinst->qpu.alu.add.b, src[1]); 276 } 277 278 qinst->qpu.alu.add.waddr = dst.index; 279 qinst->qpu.alu.add.magic_write = dst.magic; 280 } else { 281 if (nsrc >= 1) { 282 set_src(&qinst->qpu, 283 &qinst->qpu.alu.mul.a, src[0]); 284 } 285 if (nsrc >= 2) { 286 set_src(&qinst->qpu, 287 &qinst->qpu.alu.mul.b, src[1]); 288 } 289 290 qinst->qpu.alu.mul.waddr = dst.index; 291 qinst->qpu.alu.mul.magic_write = dst.magic; 292 } 293 } else { 294 assert(qinst->qpu.type == V3D_QPU_INSTR_TYPE_BRANCH); 295 } 296 } 297 } 298 299 300 static void 301 v3d_dump_qpu(struct v3d_compile *c) 302 { 303 fprintf(stderr, "%s prog %d/%d QPU:\n", 304 vir_get_stage_name(c), 305 c->program_id, c->variant_id); 306 307 for (int i = 0; i < c->qpu_inst_count; i++) { 308 const char *str = v3d_qpu_disasm(c->devinfo, c->qpu_insts[i]); 309 fprintf(stderr, "0x%016"PRIx64" %s\n", c->qpu_insts[i], str); 310 } 311 fprintf(stderr, "\n"); 312 } 313 314 void 315 v3d_vir_to_qpu(struct v3d_compile *c, struct qpu_reg *temp_registers) 316 { 317 /* Reset the uniform count to how many will be actually loaded by the 318 * generated QPU code. 319 */ 320 c->num_uniforms = 0; 321 322 vir_for_each_block(block, c) 323 v3d_generate_code_block(c, block, temp_registers); 324 325 uint32_t cycles = v3d_qpu_schedule_instructions(c); 326 327 c->qpu_insts = rzalloc_array(c, uint64_t, c->qpu_inst_count); 328 int i = 0; 329 vir_for_each_inst_inorder(inst, c) { 330 bool ok = v3d_qpu_instr_pack(c->devinfo, &inst->qpu, 331 &c->qpu_insts[i++]); 332 assert(ok); (void) ok; 333 } 334 assert(i == c->qpu_inst_count); 335 336 if (V3D_DEBUG & V3D_DEBUG_SHADERDB) { 337 fprintf(stderr, "SHADER-DB: %s prog %d/%d: %d estimated cycles\n", 338 vir_get_stage_name(c), 339 c->program_id, c->variant_id, 340 cycles); 341 } 342 343 if (V3D_DEBUG & (V3D_DEBUG_QPU | 344 v3d_debug_flag_for_shader_stage(c->s->info.stage))) { 345 v3d_dump_qpu(c); 346 } 347 348 qpu_validate(c); 349 350 free(temp_registers); 351 } 352