17ec681f3Smrg/* 27ec681f3Smrg * Copyright (C) 2018-2019 Alyssa Rosenzweig <alyssa@rosenzweig.io> 37ec681f3Smrg * Copyright (C) 2019-2020 Collabora, Ltd. 47ec681f3Smrg * 57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 67ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 77ec681f3Smrg * to deal in the Software without restriction, including without limitation 87ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 97ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 107ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 117ec681f3Smrg * 127ec681f3Smrg * The above copyright notice and this permission notice (including the next 137ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 147ec681f3Smrg * Software. 157ec681f3Smrg * 167ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 177ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 197ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 217ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 227ec681f3Smrg * SOFTWARE. 237ec681f3Smrg */ 247ec681f3Smrg 257ec681f3Smrg#include <math.h> 267ec681f3Smrg 277ec681f3Smrg#include "util/bitscan.h" 287ec681f3Smrg#include "util/half_float.h" 297ec681f3Smrg#include "compiler.h" 307ec681f3Smrg#include "helpers.h" 317ec681f3Smrg#include "midgard_ops.h" 327ec681f3Smrg 337ec681f3Smrg/* Pretty printer for Midgard IR, for use debugging compiler-internal 347ec681f3Smrg * passes like register allocation. The output superficially resembles 357ec681f3Smrg * Midgard assembly, with the exception that unit information and such is 367ec681f3Smrg * (normally) omitted, and generic indices are usually used instead of 377ec681f3Smrg * registers */ 387ec681f3Smrg 397ec681f3Smrgstatic void 407ec681f3Smrgmir_print_index(int source) 417ec681f3Smrg{ 427ec681f3Smrg if (source == ~0) { 437ec681f3Smrg printf("_"); 447ec681f3Smrg return; 457ec681f3Smrg } 467ec681f3Smrg 477ec681f3Smrg if (source >= SSA_FIXED_MINIMUM) { 487ec681f3Smrg /* Specific register */ 497ec681f3Smrg int reg = SSA_REG_FROM_FIXED(source); 507ec681f3Smrg 517ec681f3Smrg /* TODO: Moving threshold */ 527ec681f3Smrg if (reg > 16 && reg < 24) 537ec681f3Smrg printf("u%d", 23 - reg); 547ec681f3Smrg else 557ec681f3Smrg printf("r%d", reg); 567ec681f3Smrg } else { 577ec681f3Smrg printf("%d", source); 587ec681f3Smrg } 597ec681f3Smrg} 607ec681f3Smrg 617ec681f3Smrgstatic const char components[16] = "xyzwefghijklmnop"; 627ec681f3Smrg 637ec681f3Smrgstatic void 647ec681f3Smrgmir_print_mask(unsigned mask) 657ec681f3Smrg{ 667ec681f3Smrg printf("."); 677ec681f3Smrg 687ec681f3Smrg for (unsigned i = 0; i < 16; ++i) { 697ec681f3Smrg if (mask & (1 << i)) 707ec681f3Smrg putchar(components[i]); 717ec681f3Smrg } 727ec681f3Smrg} 737ec681f3Smrg 747ec681f3Smrgstatic void 757ec681f3Smrgmir_print_swizzle(unsigned *swizzle, nir_alu_type T) 767ec681f3Smrg{ 777ec681f3Smrg unsigned comps = mir_components_for_type(T); 787ec681f3Smrg 797ec681f3Smrg printf("."); 807ec681f3Smrg 817ec681f3Smrg for (unsigned i = 0; i < comps; ++i) { 827ec681f3Smrg unsigned C = swizzle[i]; 837ec681f3Smrg assert(C < comps); 847ec681f3Smrg putchar(components[C]); 857ec681f3Smrg } 867ec681f3Smrg} 877ec681f3Smrg 887ec681f3Smrgstatic const char * 897ec681f3Smrgmir_get_unit(unsigned unit) 907ec681f3Smrg{ 917ec681f3Smrg switch (unit) { 927ec681f3Smrg case ALU_ENAB_VEC_MUL: 937ec681f3Smrg return "vmul"; 947ec681f3Smrg case ALU_ENAB_SCAL_ADD: 957ec681f3Smrg return "sadd"; 967ec681f3Smrg case ALU_ENAB_VEC_ADD: 977ec681f3Smrg return "vadd"; 987ec681f3Smrg case ALU_ENAB_SCAL_MUL: 997ec681f3Smrg return "smul"; 1007ec681f3Smrg case ALU_ENAB_VEC_LUT: 1017ec681f3Smrg return "lut"; 1027ec681f3Smrg case ALU_ENAB_BR_COMPACT: 1037ec681f3Smrg return "br"; 1047ec681f3Smrg case ALU_ENAB_BRANCH: 1057ec681f3Smrg return "brx"; 1067ec681f3Smrg default: 1077ec681f3Smrg return "???"; 1087ec681f3Smrg } 1097ec681f3Smrg} 1107ec681f3Smrg 1117ec681f3Smrgstatic void 1127ec681f3Smrgmir_print_embedded_constant(midgard_instruction *ins, unsigned src_idx) 1137ec681f3Smrg{ 1147ec681f3Smrg assert(src_idx <= 1); 1157ec681f3Smrg 1167ec681f3Smrg unsigned base_size = max_bitsize_for_alu(ins); 1177ec681f3Smrg unsigned sz = nir_alu_type_get_type_size(ins->src_types[src_idx]); 1187ec681f3Smrg bool half = (sz == (base_size >> 1)); 1197ec681f3Smrg unsigned mod = mir_pack_mod(ins, src_idx, false); 1207ec681f3Smrg unsigned *swizzle = ins->swizzle[src_idx]; 1217ec681f3Smrg midgard_reg_mode reg_mode = reg_mode_for_bitsize(max_bitsize_for_alu(ins)); 1227ec681f3Smrg unsigned comp_mask = effective_writemask(ins->op, ins->mask); 1237ec681f3Smrg unsigned num_comp = util_bitcount(comp_mask); 1247ec681f3Smrg unsigned max_comp = mir_components_for_type(ins->dest_type); 1257ec681f3Smrg bool first = true; 1267ec681f3Smrg 1277ec681f3Smrg printf("#"); 1287ec681f3Smrg 1297ec681f3Smrg if (num_comp > 1) 1307ec681f3Smrg printf("vec%d(", num_comp); 1317ec681f3Smrg 1327ec681f3Smrg for (unsigned comp = 0; comp < max_comp; comp++) { 1337ec681f3Smrg if (!(comp_mask & (1 << comp))) 1347ec681f3Smrg continue; 1357ec681f3Smrg 1367ec681f3Smrg if (first) 1377ec681f3Smrg first = false; 1387ec681f3Smrg else 1397ec681f3Smrg printf(", "); 1407ec681f3Smrg 1417ec681f3Smrg mir_print_constant_component(stdout, &ins->constants, 1427ec681f3Smrg swizzle[comp], reg_mode, 1437ec681f3Smrg half, mod, ins->op); 1447ec681f3Smrg } 1457ec681f3Smrg 1467ec681f3Smrg if (num_comp > 1) 1477ec681f3Smrg printf(")"); 1487ec681f3Smrg} 1497ec681f3Smrg 1507ec681f3Smrg#define PRINT_SRC(ins, c) \ 1517ec681f3Smrg do { mir_print_index(ins->src[c]); \ 1527ec681f3Smrg if (ins->src[c] != ~0 && ins->src_types[c] != nir_type_invalid) { \ 1537ec681f3Smrg pan_print_alu_type(ins->src_types[c], stdout); \ 1547ec681f3Smrg mir_print_swizzle(ins->swizzle[c], ins->src_types[c]); \ 1557ec681f3Smrg } } while (0) 1567ec681f3Smrg 1577ec681f3Smrgvoid 1587ec681f3Smrgmir_print_instruction(midgard_instruction *ins) 1597ec681f3Smrg{ 1607ec681f3Smrg printf("\t"); 1617ec681f3Smrg 1627ec681f3Smrg if (midgard_is_branch_unit(ins->unit)) { 1637ec681f3Smrg const char *branch_target_names[] = { 1647ec681f3Smrg "goto", "break", "continue", "discard" 1657ec681f3Smrg }; 1667ec681f3Smrg 1677ec681f3Smrg printf("%s.", mir_get_unit(ins->unit)); 1687ec681f3Smrg if (ins->branch.target_type == TARGET_DISCARD) 1697ec681f3Smrg printf("discard."); 1707ec681f3Smrg else if (ins->writeout) 1717ec681f3Smrg printf("write."); 1727ec681f3Smrg else if (ins->unit == ALU_ENAB_BR_COMPACT && 1737ec681f3Smrg !ins->branch.conditional) 1747ec681f3Smrg printf("uncond."); 1757ec681f3Smrg else 1767ec681f3Smrg printf("cond."); 1777ec681f3Smrg 1787ec681f3Smrg if (!ins->branch.conditional) 1797ec681f3Smrg printf("always"); 1807ec681f3Smrg else if (ins->branch.invert_conditional) 1817ec681f3Smrg printf("false"); 1827ec681f3Smrg else 1837ec681f3Smrg printf("true"); 1847ec681f3Smrg 1857ec681f3Smrg if (ins->writeout) { 1867ec681f3Smrg printf(" (c: "); 1877ec681f3Smrg PRINT_SRC(ins, 0); 1887ec681f3Smrg printf(", z: "); 1897ec681f3Smrg PRINT_SRC(ins, 2); 1907ec681f3Smrg printf(", s: "); 1917ec681f3Smrg PRINT_SRC(ins, 3); 1927ec681f3Smrg printf(")"); 1937ec681f3Smrg } 1947ec681f3Smrg 1957ec681f3Smrg if (ins->branch.target_type != TARGET_DISCARD) 1967ec681f3Smrg printf(" %s -> block(%d)\n", 1977ec681f3Smrg ins->branch.target_type < 4 ? 1987ec681f3Smrg branch_target_names[ins->branch.target_type] : "??", 1997ec681f3Smrg ins->branch.target_block); 2007ec681f3Smrg 2017ec681f3Smrg return; 2027ec681f3Smrg } 2037ec681f3Smrg 2047ec681f3Smrg switch (ins->type) { 2057ec681f3Smrg case TAG_ALU_4: { 2067ec681f3Smrg midgard_alu_op op = ins->op; 2077ec681f3Smrg const char *name = alu_opcode_props[op].name; 2087ec681f3Smrg 2097ec681f3Smrg if (ins->unit) 2107ec681f3Smrg printf("%s.", mir_get_unit(ins->unit)); 2117ec681f3Smrg 2127ec681f3Smrg printf("%s", name ? name : "??"); 2137ec681f3Smrg break; 2147ec681f3Smrg } 2157ec681f3Smrg 2167ec681f3Smrg case TAG_LOAD_STORE_4: { 2177ec681f3Smrg midgard_load_store_op op = ins->op; 2187ec681f3Smrg const char *name = load_store_opcode_props[op].name; 2197ec681f3Smrg 2207ec681f3Smrg assert(name); 2217ec681f3Smrg printf("%s", name); 2227ec681f3Smrg break; 2237ec681f3Smrg } 2247ec681f3Smrg 2257ec681f3Smrg case TAG_TEXTURE_4: { 2267ec681f3Smrg printf("TEX"); 2277ec681f3Smrg 2287ec681f3Smrg if (ins->helper_terminate) 2297ec681f3Smrg printf(".terminate"); 2307ec681f3Smrg 2317ec681f3Smrg if (ins->helper_execute) 2327ec681f3Smrg printf(".execute"); 2337ec681f3Smrg 2347ec681f3Smrg break; 2357ec681f3Smrg } 2367ec681f3Smrg 2377ec681f3Smrg default: 2387ec681f3Smrg assert(0); 2397ec681f3Smrg } 2407ec681f3Smrg 2417ec681f3Smrg if (ins->compact_branch && ins->branch.invert_conditional) 2427ec681f3Smrg printf(".not"); 2437ec681f3Smrg 2447ec681f3Smrg printf(" "); 2457ec681f3Smrg mir_print_index(ins->dest); 2467ec681f3Smrg 2477ec681f3Smrg if (ins->dest != ~0) { 2487ec681f3Smrg pan_print_alu_type(ins->dest_type, stdout); 2497ec681f3Smrg mir_print_mask(ins->mask); 2507ec681f3Smrg } 2517ec681f3Smrg 2527ec681f3Smrg printf(", "); 2537ec681f3Smrg 2547ec681f3Smrg /* Only ALU can have an embedded constant, r26 as read on load/store is 2557ec681f3Smrg * something else entirely */ 2567ec681f3Smrg bool is_alu = ins->type == TAG_ALU_4; 2577ec681f3Smrg unsigned r_constant = SSA_FIXED_REGISTER(REGISTER_CONSTANT); 2587ec681f3Smrg 2597ec681f3Smrg if (ins->src[0] == r_constant && is_alu) 2607ec681f3Smrg mir_print_embedded_constant(ins, 0); 2617ec681f3Smrg else 2627ec681f3Smrg PRINT_SRC(ins, 0); 2637ec681f3Smrg 2647ec681f3Smrg printf(", "); 2657ec681f3Smrg 2667ec681f3Smrg if (ins->has_inline_constant) 2677ec681f3Smrg printf("#%d", ins->inline_constant); 2687ec681f3Smrg else if (ins->src[1] == r_constant && is_alu) 2697ec681f3Smrg mir_print_embedded_constant(ins, 1); 2707ec681f3Smrg else 2717ec681f3Smrg PRINT_SRC(ins, 1); 2727ec681f3Smrg 2737ec681f3Smrg for (unsigned c = 2; c <= 3; ++c) { 2747ec681f3Smrg printf(", "); 2757ec681f3Smrg PRINT_SRC(ins, c); 2767ec681f3Smrg } 2777ec681f3Smrg 2787ec681f3Smrg if (ins->no_spill) 2797ec681f3Smrg printf(" /* no spill */"); 2807ec681f3Smrg 2817ec681f3Smrg printf("\n"); 2827ec681f3Smrg} 2837ec681f3Smrg 2847ec681f3Smrg/* Dumps MIR for a block or entire shader respective */ 2857ec681f3Smrg 2867ec681f3Smrgvoid 2877ec681f3Smrgmir_print_block(midgard_block *block) 2887ec681f3Smrg{ 2897ec681f3Smrg printf("block%u: {\n", block->base.name); 2907ec681f3Smrg 2917ec681f3Smrg if (block->scheduled) { 2927ec681f3Smrg mir_foreach_bundle_in_block(block, bundle) { 2937ec681f3Smrg for (unsigned i = 0; i < bundle->instruction_count; ++i) 2947ec681f3Smrg mir_print_instruction(bundle->instructions[i]); 2957ec681f3Smrg 2967ec681f3Smrg printf("\n"); 2977ec681f3Smrg } 2987ec681f3Smrg } else { 2997ec681f3Smrg mir_foreach_instr_in_block(block, ins) { 3007ec681f3Smrg mir_print_instruction(ins); 3017ec681f3Smrg } 3027ec681f3Smrg } 3037ec681f3Smrg 3047ec681f3Smrg printf("}"); 3057ec681f3Smrg 3067ec681f3Smrg if (block->base.successors[0]) { 3077ec681f3Smrg printf(" -> "); 3087ec681f3Smrg pan_foreach_successor((&block->base), succ) 3097ec681f3Smrg printf(" block%u ", succ->name); 3107ec681f3Smrg } 3117ec681f3Smrg 3127ec681f3Smrg printf(" from { "); 3137ec681f3Smrg mir_foreach_predecessor(block, pred) 3147ec681f3Smrg printf("block%u ", pred->base.name); 3157ec681f3Smrg printf("}"); 3167ec681f3Smrg 3177ec681f3Smrg printf("\n\n"); 3187ec681f3Smrg} 3197ec681f3Smrg 3207ec681f3Smrgvoid 3217ec681f3Smrgmir_print_shader(compiler_context *ctx) 3227ec681f3Smrg{ 3237ec681f3Smrg mir_foreach_block(ctx, block) { 3247ec681f3Smrg mir_print_block((midgard_block *) block); 3257ec681f3Smrg } 3267ec681f3Smrg} 327