17ec681f3Smrg/* Author(s): 27ec681f3Smrg * Connor Abbott 37ec681f3Smrg * Alyssa Rosenzweig 47ec681f3Smrg * 57ec681f3Smrg * Copyright (c) 2013 Connor Abbott (connor@abbott.cx) 67ec681f3Smrg * Copyright (c) 2018 Alyssa Rosenzweig (alyssa@rosenzweig.io) 77ec681f3Smrg * Copyright (C) 2019-2020 Collabora, Ltd. 87ec681f3Smrg * 97ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a copy 107ec681f3Smrg * of this software and associated documentation files (the "Software"), to deal 117ec681f3Smrg * in the Software without restriction, including without limitation the rights 127ec681f3Smrg * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 137ec681f3Smrg * copies of the Software, and to permit persons to whom the Software is 147ec681f3Smrg * furnished to do so, subject to the following conditions: 157ec681f3Smrg * 167ec681f3Smrg * The above copyright notice and this permission notice shall be included in 177ec681f3Smrg * all copies or substantial portions of the Software. 187ec681f3Smrg * 197ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 207ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 217ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 227ec681f3Smrg * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 237ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 247ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 257ec681f3Smrg * THE SOFTWARE. 267ec681f3Smrg */ 277ec681f3Smrg 287ec681f3Smrg#include <stdio.h> 297ec681f3Smrg#include <stdint.h> 307ec681f3Smrg#include <stdlib.h> 317ec681f3Smrg#include <assert.h> 327ec681f3Smrg#include <inttypes.h> 337ec681f3Smrg#include <ctype.h> 347ec681f3Smrg#include <string.h> 357ec681f3Smrg#include "midgard.h" 367ec681f3Smrg#include "midgard_ops.h" 377ec681f3Smrg#include "midgard_quirks.h" 387ec681f3Smrg#include "disassemble.h" 397ec681f3Smrg#include "helpers.h" 407ec681f3Smrg#include "util/bitscan.h" 417ec681f3Smrg#include "util/half_float.h" 427ec681f3Smrg#include "util/u_math.h" 437ec681f3Smrg 447ec681f3Smrg#define DEFINE_CASE(define, str) case define: { fprintf(fp, str); break; } 457ec681f3Smrg 467ec681f3Smrg/* These are not mapped to hardware values, they just represent the possible 477ec681f3Smrg * implicit arg modifiers that some midgard opcodes have, which can be decoded 487ec681f3Smrg * from the opcodes via midgard_{alu,ldst,tex}_special_arg_mod() */ 497ec681f3Smrgtypedef enum { 507ec681f3Smrg midgard_arg_mod_none = 0, 517ec681f3Smrg midgard_arg_mod_inv, 527ec681f3Smrg midgard_arg_mod_x2, 537ec681f3Smrg} midgard_special_arg_mod; 547ec681f3Smrg 557ec681f3Smrgtypedef struct { 567ec681f3Smrg unsigned *midg_tags; 577ec681f3Smrg struct midgard_disasm_stats midg_stats; 587ec681f3Smrg 597ec681f3Smrg /* For static analysis to ensure all registers are written at least once before 607ec681f3Smrg * use along the source code path (TODO: does this break done for complex CF?) 617ec681f3Smrg */ 627ec681f3Smrg 637ec681f3Smrg uint16_t midg_ever_written; 647ec681f3Smrg} disassemble_context; 657ec681f3Smrg 667ec681f3Smrg/* Transform an expanded writemask (duplicated 8-bit format) into its condensed 677ec681f3Smrg * form (one bit per component) */ 687ec681f3Smrg 697ec681f3Smrgstatic inline unsigned 707ec681f3Smrgcondense_writemask(unsigned expanded_mask, 717ec681f3Smrg unsigned bits_per_component) 727ec681f3Smrg{ 737ec681f3Smrg if (bits_per_component == 8) { 747ec681f3Smrg /* Duplicate every bit to go from 8 to 16-channel wrmask */ 757ec681f3Smrg unsigned omask = 0; 767ec681f3Smrg 777ec681f3Smrg for (unsigned i = 0; i < 8; ++i) { 787ec681f3Smrg if (expanded_mask & (1 << i)) 797ec681f3Smrg omask |= (3 << (2 * i)); 807ec681f3Smrg } 817ec681f3Smrg 827ec681f3Smrg return omask; 837ec681f3Smrg } 847ec681f3Smrg 857ec681f3Smrg unsigned slots_per_component = bits_per_component / 16; 867ec681f3Smrg unsigned max_comp = (16 * 8) / bits_per_component; 877ec681f3Smrg unsigned condensed_mask = 0; 887ec681f3Smrg 897ec681f3Smrg for (unsigned i = 0; i < max_comp; i++) { 907ec681f3Smrg if (expanded_mask & (1 << (i * slots_per_component))) 917ec681f3Smrg condensed_mask |= (1 << i); 927ec681f3Smrg } 937ec681f3Smrg 947ec681f3Smrg return condensed_mask; 957ec681f3Smrg} 967ec681f3Smrg 977ec681f3Smrgstatic bool 987ec681f3Smrgprint_alu_opcode(FILE *fp, midgard_alu_op op) 997ec681f3Smrg{ 1007ec681f3Smrg if (alu_opcode_props[op].name) 1017ec681f3Smrg fprintf(fp, "%s", alu_opcode_props[op].name); 1027ec681f3Smrg else 1037ec681f3Smrg fprintf(fp, "alu_op_%02X", op); 1047ec681f3Smrg 1057ec681f3Smrg /* For constant analysis */ 1067ec681f3Smrg return midgard_is_integer_op(op); 1077ec681f3Smrg} 1087ec681f3Smrg 1097ec681f3Smrgstatic void 1107ec681f3Smrgprint_ld_st_opcode(FILE *fp, midgard_load_store_op op) 1117ec681f3Smrg{ 1127ec681f3Smrg if (load_store_opcode_props[op].name) 1137ec681f3Smrg fprintf(fp, "%s", load_store_opcode_props[op].name); 1147ec681f3Smrg else 1157ec681f3Smrg fprintf(fp, "ldst_op_%02X", op); 1167ec681f3Smrg} 1177ec681f3Smrg 1187ec681f3Smrgstatic void 1197ec681f3Smrgvalidate_sampler_type(enum mali_texture_op op, enum mali_sampler_type sampler_type) 1207ec681f3Smrg{ 1217ec681f3Smrg if (op == midgard_tex_op_mov || op == midgard_tex_op_barrier) 1227ec681f3Smrg assert(sampler_type == 0); 1237ec681f3Smrg else 1247ec681f3Smrg assert(sampler_type > 0); 1257ec681f3Smrg} 1267ec681f3Smrg 1277ec681f3Smrgstatic void 1287ec681f3Smrgvalidate_expand_mode(midgard_src_expand_mode expand_mode, 1297ec681f3Smrg midgard_reg_mode reg_mode) 1307ec681f3Smrg{ 1317ec681f3Smrg switch (expand_mode) { 1327ec681f3Smrg case midgard_src_passthrough: 1337ec681f3Smrg break; 1347ec681f3Smrg 1357ec681f3Smrg case midgard_src_rep_low: 1367ec681f3Smrg assert(reg_mode == midgard_reg_mode_8 || 1377ec681f3Smrg reg_mode == midgard_reg_mode_16); 1387ec681f3Smrg break; 1397ec681f3Smrg 1407ec681f3Smrg case midgard_src_rep_high: 1417ec681f3Smrg assert(reg_mode == midgard_reg_mode_8 || 1427ec681f3Smrg reg_mode == midgard_reg_mode_16); 1437ec681f3Smrg break; 1447ec681f3Smrg 1457ec681f3Smrg case midgard_src_swap: 1467ec681f3Smrg assert(reg_mode == midgard_reg_mode_8 || 1477ec681f3Smrg reg_mode == midgard_reg_mode_16); 1487ec681f3Smrg break; 1497ec681f3Smrg 1507ec681f3Smrg case midgard_src_expand_low: 1517ec681f3Smrg assert(reg_mode != midgard_reg_mode_8); 1527ec681f3Smrg break; 1537ec681f3Smrg 1547ec681f3Smrg case midgard_src_expand_high: 1557ec681f3Smrg assert(reg_mode != midgard_reg_mode_8); 1567ec681f3Smrg break; 1577ec681f3Smrg 1587ec681f3Smrg case midgard_src_expand_low_swap: 1597ec681f3Smrg assert(reg_mode == midgard_reg_mode_16); 1607ec681f3Smrg break; 1617ec681f3Smrg 1627ec681f3Smrg case midgard_src_expand_high_swap: 1637ec681f3Smrg assert(reg_mode == midgard_reg_mode_16); 1647ec681f3Smrg break; 1657ec681f3Smrg 1667ec681f3Smrg default: 1677ec681f3Smrg unreachable("Invalid expand mode"); 1687ec681f3Smrg break; 1697ec681f3Smrg } 1707ec681f3Smrg} 1717ec681f3Smrg 1727ec681f3Smrgstatic void 1737ec681f3Smrgprint_alu_reg(disassemble_context *ctx, FILE *fp, unsigned reg, bool is_write) 1747ec681f3Smrg{ 1757ec681f3Smrg unsigned uniform_reg = 23 - reg; 1767ec681f3Smrg bool is_uniform = false; 1777ec681f3Smrg 1787ec681f3Smrg /* For r8-r15, it could be a work or uniform. We distinguish based on 1797ec681f3Smrg * the fact work registers are ALWAYS written before use, but uniform 1807ec681f3Smrg * registers are NEVER written before use. */ 1817ec681f3Smrg 1827ec681f3Smrg if ((reg >= 8 && reg < 16) && !(ctx->midg_ever_written & (1 << reg))) 1837ec681f3Smrg is_uniform = true; 1847ec681f3Smrg 1857ec681f3Smrg /* r16-r23 are always uniform */ 1867ec681f3Smrg 1877ec681f3Smrg if (reg >= 16 && reg <= 23) 1887ec681f3Smrg is_uniform = true; 1897ec681f3Smrg 1907ec681f3Smrg /* Update the uniform count appropriately */ 1917ec681f3Smrg 1927ec681f3Smrg if (is_uniform) 1937ec681f3Smrg ctx->midg_stats.uniform_count = 1947ec681f3Smrg MAX2(uniform_reg + 1, ctx->midg_stats.uniform_count); 1957ec681f3Smrg 1967ec681f3Smrg if (reg == REGISTER_UNUSED || reg == REGISTER_UNUSED + 1) 1977ec681f3Smrg fprintf(fp, "TMP%u", reg - REGISTER_UNUSED); 1987ec681f3Smrg else if (reg == REGISTER_TEXTURE_BASE || reg == REGISTER_TEXTURE_BASE + 1) 1997ec681f3Smrg fprintf(fp, "%s%u", is_write ? "AT" : "TA", reg - REGISTER_TEXTURE_BASE); 2007ec681f3Smrg else if (reg == REGISTER_LDST_BASE || reg == REGISTER_LDST_BASE + 1) 2017ec681f3Smrg fprintf(fp, "AL%u", reg - REGISTER_LDST_BASE); 2027ec681f3Smrg else if (is_uniform) 2037ec681f3Smrg fprintf(fp, "U%u", uniform_reg); 2047ec681f3Smrg else if (reg == 31 && !is_write) 2057ec681f3Smrg fprintf(fp, "PC_SP"); 2067ec681f3Smrg else 2077ec681f3Smrg fprintf(fp, "R%u", reg); 2087ec681f3Smrg} 2097ec681f3Smrg 2107ec681f3Smrgstatic void 2117ec681f3Smrgprint_ldst_write_reg(FILE *fp, unsigned reg) 2127ec681f3Smrg{ 2137ec681f3Smrg switch (reg) { 2147ec681f3Smrg case 26: 2157ec681f3Smrg case 27: 2167ec681f3Smrg fprintf(fp, "AL%u", reg - REGISTER_LDST_BASE); 2177ec681f3Smrg break; 2187ec681f3Smrg case 28: 2197ec681f3Smrg case 29: 2207ec681f3Smrg fprintf(fp, "AT%u", reg - REGISTER_TEXTURE_BASE); 2217ec681f3Smrg break; 2227ec681f3Smrg case 31: 2237ec681f3Smrg fprintf(fp, "PC_SP"); 2247ec681f3Smrg break; 2257ec681f3Smrg default: 2267ec681f3Smrg fprintf(fp, "R%d", reg); 2277ec681f3Smrg break; 2287ec681f3Smrg } 2297ec681f3Smrg} 2307ec681f3Smrg 2317ec681f3Smrgstatic void 2327ec681f3Smrgprint_ldst_read_reg(FILE *fp, unsigned reg) 2337ec681f3Smrg{ 2347ec681f3Smrg switch (reg) { 2357ec681f3Smrg case 0: 2367ec681f3Smrg case 1: 2377ec681f3Smrg fprintf(fp, "AL%u", reg); 2387ec681f3Smrg break; 2397ec681f3Smrg case 2: 2407ec681f3Smrg fprintf(fp, "PC_SP"); 2417ec681f3Smrg break; 2427ec681f3Smrg case 3: 2437ec681f3Smrg fprintf(fp, "LOCAL_STORAGE_PTR"); 2447ec681f3Smrg break; 2457ec681f3Smrg case 4: 2467ec681f3Smrg fprintf(fp, "LOCAL_THREAD_ID"); 2477ec681f3Smrg break; 2487ec681f3Smrg case 5: 2497ec681f3Smrg fprintf(fp, "GROUP_ID"); 2507ec681f3Smrg break; 2517ec681f3Smrg case 6: 2527ec681f3Smrg fprintf(fp, "GLOBAL_THREAD_ID"); 2537ec681f3Smrg break; 2547ec681f3Smrg case 7: 2557ec681f3Smrg fprintf(fp, "0"); 2567ec681f3Smrg break; 2577ec681f3Smrg default: 2587ec681f3Smrg unreachable("Invalid load/store register read"); 2597ec681f3Smrg } 2607ec681f3Smrg} 2617ec681f3Smrg 2627ec681f3Smrgstatic void 2637ec681f3Smrgprint_tex_reg(FILE *fp, unsigned reg, bool is_write) 2647ec681f3Smrg{ 2657ec681f3Smrg char *str = is_write ? "TA" : "AT"; 2667ec681f3Smrg int select = reg & 1; 2677ec681f3Smrg 2687ec681f3Smrg switch (reg) { 2697ec681f3Smrg case 0: 2707ec681f3Smrg case 1: 2717ec681f3Smrg fprintf(fp, "R%d", select); 2727ec681f3Smrg break; 2737ec681f3Smrg case 26: 2747ec681f3Smrg case 27: 2757ec681f3Smrg fprintf(fp, "AL%d", select); 2767ec681f3Smrg break; 2777ec681f3Smrg case 28: 2787ec681f3Smrg case 29: 2797ec681f3Smrg fprintf(fp, "%s%d", str, select); 2807ec681f3Smrg break; 2817ec681f3Smrg default: 2827ec681f3Smrg unreachable("Invalid texture register"); 2837ec681f3Smrg } 2847ec681f3Smrg} 2857ec681f3Smrg 2867ec681f3Smrg 2877ec681f3Smrgstatic char *outmod_names_float[4] = { 2887ec681f3Smrg "", 2897ec681f3Smrg ".clamp_0_inf", 2907ec681f3Smrg ".clamp_m1_1", 2917ec681f3Smrg ".clamp_0_1" 2927ec681f3Smrg}; 2937ec681f3Smrg 2947ec681f3Smrgstatic char *outmod_names_int[4] = { 2957ec681f3Smrg ".ssat", 2967ec681f3Smrg ".usat", 2977ec681f3Smrg ".keeplo", 2987ec681f3Smrg ".keephi" 2997ec681f3Smrg}; 3007ec681f3Smrg 3017ec681f3Smrgstatic char *srcmod_names_int[4] = { 3027ec681f3Smrg ".sext", 3037ec681f3Smrg ".zext", 3047ec681f3Smrg ".replicate", 3057ec681f3Smrg ".lshift", 3067ec681f3Smrg}; 3077ec681f3Smrg 3087ec681f3Smrgstatic char *argmod_names[3] = { 3097ec681f3Smrg "", 3107ec681f3Smrg ".inv", 3117ec681f3Smrg ".x2", 3127ec681f3Smrg}; 3137ec681f3Smrg 3147ec681f3Smrgstatic char *index_format_names[4] = { 3157ec681f3Smrg "", 3167ec681f3Smrg ".u64", 3177ec681f3Smrg ".u32", 3187ec681f3Smrg ".s32" 3197ec681f3Smrg}; 3207ec681f3Smrg 3217ec681f3Smrgstatic void 3227ec681f3Smrgprint_outmod(FILE *fp, unsigned outmod, bool is_int) 3237ec681f3Smrg{ 3247ec681f3Smrg fprintf(fp, "%s", is_int ? outmod_names_int[outmod] : 3257ec681f3Smrg outmod_names_float[outmod]); 3267ec681f3Smrg} 3277ec681f3Smrg 3287ec681f3Smrgstatic void 3297ec681f3Smrgprint_alu_outmod(FILE *fp, unsigned outmod, bool is_int, bool half) 3307ec681f3Smrg{ 3317ec681f3Smrg if (is_int && !half) { 3327ec681f3Smrg assert(outmod == midgard_outmod_keeplo); 3337ec681f3Smrg return; 3347ec681f3Smrg } 3357ec681f3Smrg 3367ec681f3Smrg if (!is_int && half) 3377ec681f3Smrg fprintf(fp, ".shrink"); 3387ec681f3Smrg 3397ec681f3Smrg print_outmod(fp, outmod, is_int); 3407ec681f3Smrg} 3417ec681f3Smrg 3427ec681f3Smrg/* arg == 0 (dest), arg == 1 (src1), arg == 2 (src2) */ 3437ec681f3Smrgstatic midgard_special_arg_mod 3447ec681f3Smrgmidgard_alu_special_arg_mod(midgard_alu_op op, unsigned arg) { 3457ec681f3Smrg midgard_special_arg_mod mod = midgard_arg_mod_none; 3467ec681f3Smrg 3477ec681f3Smrg switch (op) { 3487ec681f3Smrg case midgard_alu_op_ishladd: 3497ec681f3Smrg case midgard_alu_op_ishlsub: 3507ec681f3Smrg if (arg == 1) mod = midgard_arg_mod_x2; 3517ec681f3Smrg break; 3527ec681f3Smrg 3537ec681f3Smrg default: 3547ec681f3Smrg break; 3557ec681f3Smrg } 3567ec681f3Smrg 3577ec681f3Smrg return mod; 3587ec681f3Smrg} 3597ec681f3Smrg 3607ec681f3Smrgstatic void 3617ec681f3Smrgprint_quad_word(FILE *fp, uint32_t *words, unsigned tabs) 3627ec681f3Smrg{ 3637ec681f3Smrg unsigned i; 3647ec681f3Smrg 3657ec681f3Smrg for (i = 0; i < 4; i++) 3667ec681f3Smrg fprintf(fp, "0x%08X%s ", words[i], i == 3 ? "" : ","); 3677ec681f3Smrg 3687ec681f3Smrg fprintf(fp, "\n"); 3697ec681f3Smrg} 3707ec681f3Smrg 3717ec681f3Smrgstatic const char components[16] = "xyzwefghijklmnop"; 3727ec681f3Smrg 3737ec681f3Smrgstatic int 3747ec681f3Smrgbits_for_mode(midgard_reg_mode mode) 3757ec681f3Smrg{ 3767ec681f3Smrg switch (mode) { 3777ec681f3Smrg case midgard_reg_mode_8: 3787ec681f3Smrg return 8; 3797ec681f3Smrg case midgard_reg_mode_16: 3807ec681f3Smrg return 16; 3817ec681f3Smrg case midgard_reg_mode_32: 3827ec681f3Smrg return 32; 3837ec681f3Smrg case midgard_reg_mode_64: 3847ec681f3Smrg return 64; 3857ec681f3Smrg default: 3867ec681f3Smrg unreachable("Invalid reg mode"); 3877ec681f3Smrg return 0; 3887ec681f3Smrg } 3897ec681f3Smrg} 3907ec681f3Smrg 3917ec681f3Smrgstatic int 3927ec681f3Smrgbits_for_mode_halved(midgard_reg_mode mode, bool half) 3937ec681f3Smrg{ 3947ec681f3Smrg unsigned bits = bits_for_mode(mode); 3957ec681f3Smrg 3967ec681f3Smrg if (half) 3977ec681f3Smrg bits >>= 1; 3987ec681f3Smrg 3997ec681f3Smrg return bits; 4007ec681f3Smrg} 4017ec681f3Smrg 4027ec681f3Smrgstatic void 4037ec681f3Smrgprint_vec_selectors_64(FILE *fp, unsigned swizzle, 4047ec681f3Smrg midgard_reg_mode reg_mode, 4057ec681f3Smrg midgard_src_expand_mode expand_mode, 4067ec681f3Smrg unsigned selector_offset, uint8_t mask) 4077ec681f3Smrg{ 4087ec681f3Smrg bool expands = INPUT_EXPANDS(expand_mode); 4097ec681f3Smrg 4107ec681f3Smrg unsigned comp_skip = expands ? 1 : 2; 4117ec681f3Smrg unsigned mask_bit = 0; 4127ec681f3Smrg for (unsigned i = selector_offset; i < 4; i += comp_skip, mask_bit += 4) { 4137ec681f3Smrg if (!(mask & (1 << mask_bit))) continue; 4147ec681f3Smrg 4157ec681f3Smrg unsigned a = (swizzle >> (i * 2)) & 3; 4167ec681f3Smrg 4177ec681f3Smrg if (INPUT_EXPANDS(expand_mode)) { 4187ec681f3Smrg fprintf(fp, "%c", components[a]); 4197ec681f3Smrg continue; 4207ec681f3Smrg } 4217ec681f3Smrg 4227ec681f3Smrg unsigned b = (swizzle >> ((i+1) * 2)) & 3; 4237ec681f3Smrg 4247ec681f3Smrg /* Normally we're adjacent, but if there's an issue, 4257ec681f3Smrg * don't make it ambiguous */ 4267ec681f3Smrg 4277ec681f3Smrg if (b == a + 1) 4287ec681f3Smrg fprintf(fp, "%c", a >> 1 ? 'Y' : 'X'); 4297ec681f3Smrg else 4307ec681f3Smrg fprintf(fp, "[%c%c]", components[a], components[b]); 4317ec681f3Smrg } 4327ec681f3Smrg} 4337ec681f3Smrg 4347ec681f3Smrgstatic void 4357ec681f3Smrgprint_vec_selectors(FILE *fp, unsigned swizzle, 4367ec681f3Smrg midgard_reg_mode reg_mode, 4377ec681f3Smrg unsigned selector_offset, uint8_t mask, 4387ec681f3Smrg unsigned *mask_offset) 4397ec681f3Smrg{ 4407ec681f3Smrg assert(reg_mode != midgard_reg_mode_64); 4417ec681f3Smrg 4427ec681f3Smrg unsigned mask_skip = MAX2(bits_for_mode(reg_mode) / 16, 1); 4437ec681f3Smrg 4447ec681f3Smrg bool is_vec16 = reg_mode == midgard_reg_mode_8; 4457ec681f3Smrg 4467ec681f3Smrg for (unsigned i = 0; i < 4; i++, *mask_offset += mask_skip) { 4477ec681f3Smrg if (!(mask & (1 << *mask_offset))) continue; 4487ec681f3Smrg 4497ec681f3Smrg unsigned c = (swizzle >> (i * 2)) & 3; 4507ec681f3Smrg 4517ec681f3Smrg /* Vec16 has two components per swizzle selector. */ 4527ec681f3Smrg if (is_vec16) 4537ec681f3Smrg c *= 2; 4547ec681f3Smrg 4557ec681f3Smrg c += selector_offset; 4567ec681f3Smrg 4577ec681f3Smrg fprintf(fp, "%c", components[c]); 4587ec681f3Smrg if (is_vec16) 4597ec681f3Smrg fprintf(fp, "%c", components[c+1]); 4607ec681f3Smrg } 4617ec681f3Smrg} 4627ec681f3Smrg 4637ec681f3Smrgstatic void 4647ec681f3Smrgprint_vec_swizzle(FILE *fp, unsigned swizzle, 4657ec681f3Smrg midgard_src_expand_mode expand, 4667ec681f3Smrg midgard_reg_mode mode, 4677ec681f3Smrg uint8_t mask) 4687ec681f3Smrg{ 4697ec681f3Smrg unsigned bits = bits_for_mode_halved(mode, INPUT_EXPANDS(expand)); 4707ec681f3Smrg 4717ec681f3Smrg /* Swizzle selectors are divided in two halves that are always 4727ec681f3Smrg * mirrored, the only difference is the starting component offset. 4737ec681f3Smrg * The number represents an offset into the components[] array. */ 4747ec681f3Smrg unsigned first_half = 0; 4757ec681f3Smrg unsigned second_half = (128 / bits) / 2; /* only used for 8 and 16-bit */ 4767ec681f3Smrg 4777ec681f3Smrg switch (expand) { 4787ec681f3Smrg case midgard_src_passthrough: 4797ec681f3Smrg if (swizzle == 0xE4) return; /* identity swizzle */ 4807ec681f3Smrg break; 4817ec681f3Smrg 4827ec681f3Smrg case midgard_src_expand_low: 4837ec681f3Smrg second_half /= 2; 4847ec681f3Smrg break; 4857ec681f3Smrg 4867ec681f3Smrg case midgard_src_expand_high: 4877ec681f3Smrg first_half = second_half; 4887ec681f3Smrg second_half += second_half / 2; 4897ec681f3Smrg break; 4907ec681f3Smrg 4917ec681f3Smrg /* The rest of the cases are only used for 8 and 16-bit */ 4927ec681f3Smrg 4937ec681f3Smrg case midgard_src_rep_low: 4947ec681f3Smrg second_half = 0; 4957ec681f3Smrg break; 4967ec681f3Smrg 4977ec681f3Smrg case midgard_src_rep_high: 4987ec681f3Smrg first_half = second_half; 4997ec681f3Smrg break; 5007ec681f3Smrg 5017ec681f3Smrg case midgard_src_swap: 5027ec681f3Smrg first_half = second_half; 5037ec681f3Smrg second_half = 0; 5047ec681f3Smrg break; 5057ec681f3Smrg 5067ec681f3Smrg case midgard_src_expand_low_swap: 5077ec681f3Smrg first_half = second_half / 2; 5087ec681f3Smrg second_half = 0; 5097ec681f3Smrg break; 5107ec681f3Smrg 5117ec681f3Smrg case midgard_src_expand_high_swap: 5127ec681f3Smrg first_half = second_half + second_half / 2; 5137ec681f3Smrg break; 5147ec681f3Smrg 5157ec681f3Smrg default: 5167ec681f3Smrg unreachable("Invalid expand mode"); 5177ec681f3Smrg break; 5187ec681f3Smrg } 5197ec681f3Smrg 5207ec681f3Smrg fprintf(fp, "."); 5217ec681f3Smrg 5227ec681f3Smrg /* Vec2 are weird so we use a separate function to simplify things. */ 5237ec681f3Smrg if (mode == midgard_reg_mode_64) { 5247ec681f3Smrg print_vec_selectors_64(fp, swizzle, mode, expand, first_half, mask); 5257ec681f3Smrg return; 5267ec681f3Smrg } 5277ec681f3Smrg 5287ec681f3Smrg unsigned mask_offs = 0; 5297ec681f3Smrg print_vec_selectors(fp, swizzle, mode, first_half, mask, &mask_offs); 5307ec681f3Smrg if (mode == midgard_reg_mode_8 || mode == midgard_reg_mode_16) 5317ec681f3Smrg print_vec_selectors(fp, swizzle, mode, second_half, mask, &mask_offs); 5327ec681f3Smrg} 5337ec681f3Smrg 5347ec681f3Smrgstatic void 5357ec681f3Smrgprint_scalar_constant(FILE *fp, unsigned src_binary, 5367ec681f3Smrg const midgard_constants *consts, 5377ec681f3Smrg midgard_scalar_alu *alu) 5387ec681f3Smrg{ 5397ec681f3Smrg midgard_scalar_alu_src *src = (midgard_scalar_alu_src *)&src_binary; 5407ec681f3Smrg assert(consts != NULL); 5417ec681f3Smrg 5427ec681f3Smrg fprintf(fp, "#"); 5437ec681f3Smrg mir_print_constant_component(fp, consts, src->component, 5447ec681f3Smrg src->full ? 5457ec681f3Smrg midgard_reg_mode_32 : midgard_reg_mode_16, 5467ec681f3Smrg false, src->mod, alu->op); 5477ec681f3Smrg} 5487ec681f3Smrg 5497ec681f3Smrgstatic void 5507ec681f3Smrgprint_vector_constants(FILE *fp, unsigned src_binary, 5517ec681f3Smrg const midgard_constants *consts, 5527ec681f3Smrg midgard_vector_alu *alu) 5537ec681f3Smrg{ 5547ec681f3Smrg midgard_vector_alu_src *src = (midgard_vector_alu_src *)&src_binary; 5557ec681f3Smrg bool expands = INPUT_EXPANDS(src->expand_mode); 5567ec681f3Smrg unsigned bits = bits_for_mode_halved(alu->reg_mode, expands); 5577ec681f3Smrg unsigned max_comp = (sizeof(*consts) * 8) / bits; 5587ec681f3Smrg unsigned comp_mask, num_comp = 0; 5597ec681f3Smrg 5607ec681f3Smrg assert(consts); 5617ec681f3Smrg assert(max_comp <= 16); 5627ec681f3Smrg 5637ec681f3Smrg comp_mask = effective_writemask(alu->op, condense_writemask(alu->mask, bits)); 5647ec681f3Smrg num_comp = util_bitcount(comp_mask); 5657ec681f3Smrg 5667ec681f3Smrg fprintf(fp, "<"); 5677ec681f3Smrg bool first = true; 5687ec681f3Smrg 5697ec681f3Smrg for (unsigned i = 0; i < max_comp; ++i) { 5707ec681f3Smrg if (!(comp_mask & (1 << i))) continue; 5717ec681f3Smrg 5727ec681f3Smrg unsigned c = (src->swizzle >> (i * 2)) & 3; 5737ec681f3Smrg 5747ec681f3Smrg if (bits == 16 && !expands) { 5757ec681f3Smrg bool upper = i >= 4; 5767ec681f3Smrg 5777ec681f3Smrg switch (src->expand_mode) { 5787ec681f3Smrg case midgard_src_passthrough: 5797ec681f3Smrg c += upper * 4; 5807ec681f3Smrg break; 5817ec681f3Smrg case midgard_src_rep_low: 5827ec681f3Smrg break; 5837ec681f3Smrg case midgard_src_rep_high: 5847ec681f3Smrg c += 4; 5857ec681f3Smrg break; 5867ec681f3Smrg case midgard_src_swap: 5877ec681f3Smrg c += !upper * 4; 5887ec681f3Smrg break; 5897ec681f3Smrg default: 5907ec681f3Smrg unreachable("invalid expand mode"); 5917ec681f3Smrg break; 5927ec681f3Smrg } 5937ec681f3Smrg } else if (bits == 32 && !expands) { 5947ec681f3Smrg /* Implicitly ok */ 5957ec681f3Smrg } else if (bits == 64 && !expands) { 5967ec681f3Smrg /* Implicitly ok */ 5977ec681f3Smrg } else if (bits == 8 && !expands) { 5987ec681f3Smrg bool upper = i >= 8; 5997ec681f3Smrg 6007ec681f3Smrg unsigned index = (i >> 1) & 3; 6017ec681f3Smrg unsigned base = (src->swizzle >> (index * 2)) & 3; 6027ec681f3Smrg c = base * 2; 6037ec681f3Smrg 6047ec681f3Smrg switch (src->expand_mode) { 6057ec681f3Smrg case midgard_src_passthrough: 6067ec681f3Smrg c += upper * 8; 6077ec681f3Smrg break; 6087ec681f3Smrg case midgard_src_rep_low: 6097ec681f3Smrg break; 6107ec681f3Smrg case midgard_src_rep_high: 6117ec681f3Smrg c += 8; 6127ec681f3Smrg break; 6137ec681f3Smrg case midgard_src_swap: 6147ec681f3Smrg c += !upper * 8; 6157ec681f3Smrg break; 6167ec681f3Smrg default: 6177ec681f3Smrg unreachable("invalid expand mode"); 6187ec681f3Smrg break; 6197ec681f3Smrg } 6207ec681f3Smrg 6217ec681f3Smrg /* We work on twos, actually */ 6227ec681f3Smrg if (i & 1) 6237ec681f3Smrg c++; 6247ec681f3Smrg } else { 6257ec681f3Smrg printf(" (%u)", src->expand_mode); 6267ec681f3Smrg } 6277ec681f3Smrg 6287ec681f3Smrg if (first) 6297ec681f3Smrg first = false; 6307ec681f3Smrg else 6317ec681f3Smrg fprintf(fp, ", "); 6327ec681f3Smrg 6337ec681f3Smrg mir_print_constant_component(fp, consts, c, alu->reg_mode, 6347ec681f3Smrg expands, src->mod, alu->op); 6357ec681f3Smrg } 6367ec681f3Smrg 6377ec681f3Smrg if (num_comp > 1) 6387ec681f3Smrg fprintf(fp, ">"); 6397ec681f3Smrg} 6407ec681f3Smrg 6417ec681f3Smrgstatic void 6427ec681f3Smrgprint_srcmod(FILE *fp, bool is_int, bool expands, unsigned mod, bool scalar) 6437ec681f3Smrg{ 6447ec681f3Smrg /* Modifiers change meaning depending on the op's context */ 6457ec681f3Smrg 6467ec681f3Smrg if (is_int) { 6477ec681f3Smrg if (expands) 6487ec681f3Smrg fprintf(fp, "%s", srcmod_names_int[mod]); 6497ec681f3Smrg } else { 6507ec681f3Smrg if (mod & MIDGARD_FLOAT_MOD_ABS) 6517ec681f3Smrg fprintf(fp, ".abs"); 6527ec681f3Smrg if (mod & MIDGARD_FLOAT_MOD_NEG) 6537ec681f3Smrg fprintf(fp, ".neg"); 6547ec681f3Smrg if (expands) 6557ec681f3Smrg fprintf(fp, ".widen"); 6567ec681f3Smrg } 6577ec681f3Smrg} 6587ec681f3Smrg 6597ec681f3Smrgstatic void 6607ec681f3Smrgprint_vector_src(disassemble_context *ctx, FILE *fp, unsigned src_binary, 6617ec681f3Smrg midgard_reg_mode mode, unsigned reg, 6627ec681f3Smrg midgard_shrink_mode shrink_mode, 6637ec681f3Smrg uint8_t src_mask, bool is_int, 6647ec681f3Smrg midgard_special_arg_mod arg_mod) 6657ec681f3Smrg{ 6667ec681f3Smrg midgard_vector_alu_src *src = (midgard_vector_alu_src *)&src_binary; 6677ec681f3Smrg 6687ec681f3Smrg validate_expand_mode(src->expand_mode, mode); 6697ec681f3Smrg 6707ec681f3Smrg print_alu_reg(ctx, fp, reg, false); 6717ec681f3Smrg 6727ec681f3Smrg print_vec_swizzle(fp, src->swizzle, src->expand_mode, mode, src_mask); 6737ec681f3Smrg 6747ec681f3Smrg fprintf(fp, "%s", argmod_names[arg_mod]); 6757ec681f3Smrg 6767ec681f3Smrg print_srcmod(fp, is_int, INPUT_EXPANDS(src->expand_mode), src->mod, false); 6777ec681f3Smrg} 6787ec681f3Smrg 6797ec681f3Smrgstatic uint16_t 6807ec681f3Smrgdecode_vector_imm(unsigned src2_reg, unsigned imm) 6817ec681f3Smrg{ 6827ec681f3Smrg uint16_t ret; 6837ec681f3Smrg ret = src2_reg << 11; 6847ec681f3Smrg ret |= (imm & 0x7) << 8; 6857ec681f3Smrg ret |= (imm >> 3) & 0xFF; 6867ec681f3Smrg return ret; 6877ec681f3Smrg} 6887ec681f3Smrg 6897ec681f3Smrgstatic void 6907ec681f3Smrgprint_immediate(FILE *fp, uint16_t imm, bool is_instruction_int) 6917ec681f3Smrg{ 6927ec681f3Smrg if (is_instruction_int) 6937ec681f3Smrg fprintf(fp, "#%u", imm); 6947ec681f3Smrg else 6957ec681f3Smrg fprintf(fp, "#%g", _mesa_half_to_float(imm)); 6967ec681f3Smrg} 6977ec681f3Smrg 6987ec681f3Smrgstatic void 6997ec681f3Smrgupdate_dest(disassemble_context *ctx, unsigned reg) 7007ec681f3Smrg{ 7017ec681f3Smrg /* We should record writes as marking this as a work register. Store 7027ec681f3Smrg * the max register in work_count; we'll add one at the end */ 7037ec681f3Smrg 7047ec681f3Smrg if (reg < 16) { 7057ec681f3Smrg ctx->midg_stats.work_count = MAX2(reg, ctx->midg_stats.work_count); 7067ec681f3Smrg ctx->midg_ever_written |= (1 << reg); 7077ec681f3Smrg } 7087ec681f3Smrg} 7097ec681f3Smrg 7107ec681f3Smrgstatic void 7117ec681f3Smrgprint_dest(disassemble_context *ctx, FILE *fp, unsigned reg) 7127ec681f3Smrg{ 7137ec681f3Smrg update_dest(ctx, reg); 7147ec681f3Smrg print_alu_reg(ctx, fp, reg, true); 7157ec681f3Smrg} 7167ec681f3Smrg 7177ec681f3Smrg/* For 16-bit+ masks, we read off from the 8-bit mask field. For 16-bit (vec8), 7187ec681f3Smrg * it's just one bit per channel, easy peasy. For 32-bit (vec4), it's one bit 7197ec681f3Smrg * per channel with one duplicate bit in the middle. For 64-bit (vec2), it's 7207ec681f3Smrg * one-bit per channel with _3_ duplicate bits in the middle. Basically, just 7217ec681f3Smrg * subdividing the 128-bit word in 16-bit increments. For 64-bit, we uppercase 7227ec681f3Smrg * the mask to make it obvious what happened */ 7237ec681f3Smrg 7247ec681f3Smrgstatic void 7257ec681f3Smrgprint_alu_mask(FILE *fp, uint8_t mask, unsigned bits, midgard_shrink_mode shrink_mode) 7267ec681f3Smrg{ 7277ec681f3Smrg /* Skip 'complete' masks */ 7287ec681f3Smrg 7297ec681f3Smrg if (shrink_mode == midgard_shrink_mode_none && mask == 0xFF) 7307ec681f3Smrg return; 7317ec681f3Smrg 7327ec681f3Smrg fprintf(fp, "."); 7337ec681f3Smrg 7347ec681f3Smrg unsigned skip = MAX2(bits / 16, 1); 7357ec681f3Smrg bool uppercase = bits > 32; 7367ec681f3Smrg bool tripped = false; 7377ec681f3Smrg 7387ec681f3Smrg /* To apply an upper destination shrink_mode, we "shift" the alphabet. 7397ec681f3Smrg * E.g. with an upper shrink_mode on 32-bit, instead of xyzw, print efgh. 7407ec681f3Smrg * For upper 16-bit, instead of xyzwefgh, print ijklmnop */ 7417ec681f3Smrg 7427ec681f3Smrg const char *alphabet = components; 7437ec681f3Smrg 7447ec681f3Smrg if (shrink_mode == midgard_shrink_mode_upper) { 7457ec681f3Smrg assert(bits != 8); 7467ec681f3Smrg alphabet += (128 / bits); 7477ec681f3Smrg } 7487ec681f3Smrg 7497ec681f3Smrg for (unsigned i = 0; i < 8; i += skip) { 7507ec681f3Smrg bool a = (mask & (1 << i)) != 0; 7517ec681f3Smrg 7527ec681f3Smrg for (unsigned j = 1; j < skip; ++j) { 7537ec681f3Smrg bool dupe = (mask & (1 << (i + j))) != 0; 7547ec681f3Smrg tripped |= (dupe != a); 7557ec681f3Smrg } 7567ec681f3Smrg 7577ec681f3Smrg if (a) { 7587ec681f3Smrg /* TODO: handle shrinking from 16-bit */ 7597ec681f3Smrg unsigned comp_idx = bits == 8 ? i * 2 : i; 7607ec681f3Smrg char c = alphabet[comp_idx / skip]; 7617ec681f3Smrg 7627ec681f3Smrg if (uppercase) { 7637ec681f3Smrg c = toupper(c); 7647ec681f3Smrg assert(c == 'X' || c == 'Y'); 7657ec681f3Smrg } 7667ec681f3Smrg 7677ec681f3Smrg fprintf(fp, "%c", c); 7687ec681f3Smrg if (bits == 8) 7697ec681f3Smrg fprintf(fp, "%c", alphabet[comp_idx+1]); 7707ec681f3Smrg } 7717ec681f3Smrg } 7727ec681f3Smrg 7737ec681f3Smrg if (tripped) 7747ec681f3Smrg fprintf(fp, " /* %X */", mask); 7757ec681f3Smrg} 7767ec681f3Smrg 7777ec681f3Smrg/* TODO: 16-bit mode */ 7787ec681f3Smrgstatic void 7797ec681f3Smrgprint_ldst_mask(FILE *fp, unsigned mask, unsigned swizzle) { 7807ec681f3Smrg fprintf(fp, "."); 7817ec681f3Smrg 7827ec681f3Smrg for (unsigned i = 0; i < 4; ++i) { 7837ec681f3Smrg bool write = (mask & (1 << i)) != 0; 7847ec681f3Smrg unsigned c = (swizzle >> (i * 2)) & 3; 7857ec681f3Smrg /* We can't omit the swizzle here since many ldst ops have a 7867ec681f3Smrg * combined swizzle/writemask, and it would be ambiguous to not 7877ec681f3Smrg * print the masked-out components. */ 7887ec681f3Smrg fprintf(fp, "%c", write ? components[c] : '~'); 7897ec681f3Smrg } 7907ec681f3Smrg} 7917ec681f3Smrg 7927ec681f3Smrgstatic void 7937ec681f3Smrgprint_tex_mask(FILE *fp, unsigned mask, bool upper) 7947ec681f3Smrg{ 7957ec681f3Smrg if (mask == 0xF) { 7967ec681f3Smrg if (upper) 7977ec681f3Smrg fprintf(fp, "'"); 7987ec681f3Smrg 7997ec681f3Smrg return; 8007ec681f3Smrg } 8017ec681f3Smrg 8027ec681f3Smrg fprintf(fp, "."); 8037ec681f3Smrg 8047ec681f3Smrg for (unsigned i = 0; i < 4; ++i) { 8057ec681f3Smrg bool a = (mask & (1 << i)) != 0; 8067ec681f3Smrg if (a) 8077ec681f3Smrg fprintf(fp, "%c", components[i + (upper ? 4 : 0)]); 8087ec681f3Smrg } 8097ec681f3Smrg} 8107ec681f3Smrg 8117ec681f3Smrgstatic void 8127ec681f3Smrgprint_vector_field(disassemble_context *ctx, FILE *fp, const char *name, 8137ec681f3Smrg uint16_t *words, uint16_t reg_word, 8147ec681f3Smrg const midgard_constants *consts, unsigned tabs, bool verbose) 8157ec681f3Smrg{ 8167ec681f3Smrg midgard_reg_info *reg_info = (midgard_reg_info *)®_word; 8177ec681f3Smrg midgard_vector_alu *alu_field = (midgard_vector_alu *) words; 8187ec681f3Smrg midgard_reg_mode mode = alu_field->reg_mode; 8197ec681f3Smrg midgard_alu_op op = alu_field->op; 8207ec681f3Smrg unsigned shrink_mode = alu_field->shrink_mode; 8217ec681f3Smrg bool is_int = midgard_is_integer_op(op); 8227ec681f3Smrg bool is_int_out = midgard_is_integer_out_op(op); 8237ec681f3Smrg 8247ec681f3Smrg if (verbose) 8257ec681f3Smrg fprintf(fp, "%s.", name); 8267ec681f3Smrg 8277ec681f3Smrg bool is_instruction_int = print_alu_opcode(fp, alu_field->op); 8287ec681f3Smrg 8297ec681f3Smrg /* Print lane width */ 8307ec681f3Smrg fprintf(fp, ".%c%d", is_int_out ? 'i' : 'f', bits_for_mode(mode)); 8317ec681f3Smrg 8327ec681f3Smrg fprintf(fp, " "); 8337ec681f3Smrg 8347ec681f3Smrg /* Mask denoting status of 8-lanes */ 8357ec681f3Smrg uint8_t mask = alu_field->mask; 8367ec681f3Smrg 8377ec681f3Smrg /* First, print the destination */ 8387ec681f3Smrg print_dest(ctx, fp, reg_info->out_reg); 8397ec681f3Smrg 8407ec681f3Smrg if (shrink_mode != midgard_shrink_mode_none) { 8417ec681f3Smrg bool shrinkable = (mode != midgard_reg_mode_8); 8427ec681f3Smrg bool known = shrink_mode != 0x3; /* Unused value */ 8437ec681f3Smrg 8447ec681f3Smrg if (!(shrinkable && known)) 8457ec681f3Smrg fprintf(fp, "/* do%u */ ", shrink_mode); 8467ec681f3Smrg } 8477ec681f3Smrg 8487ec681f3Smrg /* Instructions like fdot4 do *not* replicate, ensure the 8497ec681f3Smrg * mask is of only a single component */ 8507ec681f3Smrg 8517ec681f3Smrg unsigned rep = GET_CHANNEL_COUNT(alu_opcode_props[op].props); 8527ec681f3Smrg 8537ec681f3Smrg if (rep) { 8547ec681f3Smrg unsigned comp_mask = condense_writemask(mask, bits_for_mode(mode)); 8557ec681f3Smrg unsigned num_comp = util_bitcount(comp_mask); 8567ec681f3Smrg if (num_comp != 1) 8577ec681f3Smrg fprintf(fp, "/* err too many components */"); 8587ec681f3Smrg } 8597ec681f3Smrg print_alu_mask(fp, mask, bits_for_mode(mode), shrink_mode); 8607ec681f3Smrg 8617ec681f3Smrg /* Print output modifiers */ 8627ec681f3Smrg 8637ec681f3Smrg print_alu_outmod(fp, alu_field->outmod, is_int_out, shrink_mode != midgard_shrink_mode_none); 8647ec681f3Smrg 8657ec681f3Smrg /* Mask out unused components based on the writemask, but don't mask out 8667ec681f3Smrg * components that are used for interlane instructions like fdot3. */ 8677ec681f3Smrg uint8_t src_mask = 8687ec681f3Smrg rep ? expand_writemask(mask_of(rep), log2(128 / bits_for_mode(mode))) : mask; 8697ec681f3Smrg 8707ec681f3Smrg fprintf(fp, ", "); 8717ec681f3Smrg 8727ec681f3Smrg if (reg_info->src1_reg == REGISTER_CONSTANT) 8737ec681f3Smrg print_vector_constants(fp, alu_field->src1, consts, alu_field); 8747ec681f3Smrg else { 8757ec681f3Smrg midgard_special_arg_mod argmod = midgard_alu_special_arg_mod(op, 1); 8767ec681f3Smrg print_vector_src(ctx, fp, alu_field->src1, mode, reg_info->src1_reg, 8777ec681f3Smrg shrink_mode, src_mask, is_int, argmod); 8787ec681f3Smrg } 8797ec681f3Smrg 8807ec681f3Smrg fprintf(fp, ", "); 8817ec681f3Smrg 8827ec681f3Smrg if (reg_info->src2_imm) { 8837ec681f3Smrg uint16_t imm = decode_vector_imm(reg_info->src2_reg, alu_field->src2 >> 2); 8847ec681f3Smrg print_immediate(fp, imm, is_instruction_int); 8857ec681f3Smrg } else if (reg_info->src2_reg == REGISTER_CONSTANT) { 8867ec681f3Smrg print_vector_constants(fp, alu_field->src2, consts, alu_field); 8877ec681f3Smrg } else { 8887ec681f3Smrg midgard_special_arg_mod argmod = midgard_alu_special_arg_mod(op, 2); 8897ec681f3Smrg print_vector_src(ctx, fp, alu_field->src2, mode, reg_info->src2_reg, 8907ec681f3Smrg shrink_mode, src_mask, is_int, argmod); 8917ec681f3Smrg } 8927ec681f3Smrg 8937ec681f3Smrg ctx->midg_stats.instruction_count++; 8947ec681f3Smrg fprintf(fp, "\n"); 8957ec681f3Smrg} 8967ec681f3Smrg 8977ec681f3Smrgstatic void 8987ec681f3Smrgprint_scalar_src(disassemble_context *ctx, FILE *fp, bool is_int, unsigned src_binary, unsigned reg) 8997ec681f3Smrg{ 9007ec681f3Smrg midgard_scalar_alu_src *src = (midgard_scalar_alu_src *)&src_binary; 9017ec681f3Smrg 9027ec681f3Smrg print_alu_reg(ctx, fp, reg, false); 9037ec681f3Smrg 9047ec681f3Smrg unsigned c = src->component; 9057ec681f3Smrg 9067ec681f3Smrg if (src->full) { 9077ec681f3Smrg assert((c & 1) == 0); 9087ec681f3Smrg c >>= 1; 9097ec681f3Smrg } 9107ec681f3Smrg 9117ec681f3Smrg fprintf(fp, ".%c", components[c]); 9127ec681f3Smrg 9137ec681f3Smrg print_srcmod(fp, is_int, !src->full, src->mod, true); 9147ec681f3Smrg} 9157ec681f3Smrg 9167ec681f3Smrgstatic uint16_t 9177ec681f3Smrgdecode_scalar_imm(unsigned src2_reg, unsigned imm) 9187ec681f3Smrg{ 9197ec681f3Smrg uint16_t ret; 9207ec681f3Smrg ret = src2_reg << 11; 9217ec681f3Smrg ret |= (imm & 3) << 9; 9227ec681f3Smrg ret |= (imm & 4) << 6; 9237ec681f3Smrg ret |= (imm & 0x38) << 2; 9247ec681f3Smrg ret |= imm >> 6; 9257ec681f3Smrg return ret; 9267ec681f3Smrg} 9277ec681f3Smrg 9287ec681f3Smrgstatic void 9297ec681f3Smrgprint_scalar_field(disassemble_context *ctx, FILE *fp, const char *name, 9307ec681f3Smrg uint16_t *words, uint16_t reg_word, 9317ec681f3Smrg const midgard_constants *consts, unsigned tabs, bool verbose) 9327ec681f3Smrg{ 9337ec681f3Smrg midgard_reg_info *reg_info = (midgard_reg_info *)®_word; 9347ec681f3Smrg midgard_scalar_alu *alu_field = (midgard_scalar_alu *) words; 9357ec681f3Smrg bool is_int = midgard_is_integer_op(alu_field->op); 9367ec681f3Smrg bool is_int_out = midgard_is_integer_out_op(alu_field->op); 9377ec681f3Smrg bool full = alu_field->output_full; 9387ec681f3Smrg 9397ec681f3Smrg if (alu_field->unknown) 9407ec681f3Smrg fprintf(fp, "scalar ALU unknown bit set\n"); 9417ec681f3Smrg 9427ec681f3Smrg if (verbose) 9437ec681f3Smrg fprintf(fp, "%s.", name); 9447ec681f3Smrg 9457ec681f3Smrg bool is_instruction_int = print_alu_opcode(fp, alu_field->op); 9467ec681f3Smrg 9477ec681f3Smrg /* Print lane width, in this case the lane width is always 32-bit, but 9487ec681f3Smrg * we print it anyway to make it consistent with the other instructions. */ 9497ec681f3Smrg fprintf(fp, ".%c32", is_int_out ? 'i' : 'f'); 9507ec681f3Smrg 9517ec681f3Smrg fprintf(fp, " "); 9527ec681f3Smrg 9537ec681f3Smrg print_dest(ctx, fp, reg_info->out_reg); 9547ec681f3Smrg unsigned c = alu_field->output_component; 9557ec681f3Smrg 9567ec681f3Smrg if (full) { 9577ec681f3Smrg assert((c & 1) == 0); 9587ec681f3Smrg c >>= 1; 9597ec681f3Smrg } 9607ec681f3Smrg 9617ec681f3Smrg fprintf(fp, ".%c", components[c]); 9627ec681f3Smrg 9637ec681f3Smrg print_alu_outmod(fp, alu_field->outmod, is_int_out, !full); 9647ec681f3Smrg 9657ec681f3Smrg fprintf(fp, ", "); 9667ec681f3Smrg 9677ec681f3Smrg if (reg_info->src1_reg == REGISTER_CONSTANT) 9687ec681f3Smrg print_scalar_constant(fp, alu_field->src1, consts, alu_field); 9697ec681f3Smrg else 9707ec681f3Smrg print_scalar_src(ctx, fp, is_int, alu_field->src1, reg_info->src1_reg); 9717ec681f3Smrg 9727ec681f3Smrg fprintf(fp, ", "); 9737ec681f3Smrg 9747ec681f3Smrg if (reg_info->src2_imm) { 9757ec681f3Smrg uint16_t imm = decode_scalar_imm(reg_info->src2_reg, 9767ec681f3Smrg alu_field->src2); 9777ec681f3Smrg print_immediate(fp, imm, is_instruction_int); 9787ec681f3Smrg } else if (reg_info->src2_reg == REGISTER_CONSTANT) { 9797ec681f3Smrg print_scalar_constant(fp, alu_field->src2, consts, alu_field); 9807ec681f3Smrg } else 9817ec681f3Smrg print_scalar_src(ctx, fp, is_int, alu_field->src2, reg_info->src2_reg); 9827ec681f3Smrg 9837ec681f3Smrg ctx->midg_stats.instruction_count++; 9847ec681f3Smrg fprintf(fp, "\n"); 9857ec681f3Smrg} 9867ec681f3Smrg 9877ec681f3Smrgstatic void 9887ec681f3Smrgprint_branch_op(FILE *fp, unsigned op) 9897ec681f3Smrg{ 9907ec681f3Smrg switch (op) { 9917ec681f3Smrg case midgard_jmp_writeout_op_branch_uncond: 9927ec681f3Smrg fprintf(fp, "uncond."); 9937ec681f3Smrg break; 9947ec681f3Smrg 9957ec681f3Smrg case midgard_jmp_writeout_op_branch_cond: 9967ec681f3Smrg fprintf(fp, "cond."); 9977ec681f3Smrg break; 9987ec681f3Smrg 9997ec681f3Smrg case midgard_jmp_writeout_op_writeout: 10007ec681f3Smrg fprintf(fp, "write."); 10017ec681f3Smrg break; 10027ec681f3Smrg 10037ec681f3Smrg case midgard_jmp_writeout_op_tilebuffer_pending: 10047ec681f3Smrg fprintf(fp, "tilebuffer."); 10057ec681f3Smrg break; 10067ec681f3Smrg 10077ec681f3Smrg case midgard_jmp_writeout_op_discard: 10087ec681f3Smrg fprintf(fp, "discard."); 10097ec681f3Smrg break; 10107ec681f3Smrg 10117ec681f3Smrg default: 10127ec681f3Smrg fprintf(fp, "unk%u.", op); 10137ec681f3Smrg break; 10147ec681f3Smrg } 10157ec681f3Smrg} 10167ec681f3Smrg 10177ec681f3Smrgstatic void 10187ec681f3Smrgprint_branch_cond(FILE *fp, int cond) 10197ec681f3Smrg{ 10207ec681f3Smrg switch (cond) { 10217ec681f3Smrg case midgard_condition_write0: 10227ec681f3Smrg fprintf(fp, "write0"); 10237ec681f3Smrg break; 10247ec681f3Smrg 10257ec681f3Smrg case midgard_condition_false: 10267ec681f3Smrg fprintf(fp, "false"); 10277ec681f3Smrg break; 10287ec681f3Smrg 10297ec681f3Smrg case midgard_condition_true: 10307ec681f3Smrg fprintf(fp, "true"); 10317ec681f3Smrg break; 10327ec681f3Smrg 10337ec681f3Smrg case midgard_condition_always: 10347ec681f3Smrg fprintf(fp, "always"); 10357ec681f3Smrg break; 10367ec681f3Smrg 10377ec681f3Smrg default: 10387ec681f3Smrg fprintf(fp, "unk%X", cond); 10397ec681f3Smrg break; 10407ec681f3Smrg } 10417ec681f3Smrg} 10427ec681f3Smrg 10437ec681f3Smrgstatic bool 10447ec681f3Smrgprint_compact_branch_writeout_field(disassemble_context *ctx, FILE *fp, uint16_t word) 10457ec681f3Smrg{ 10467ec681f3Smrg midgard_jmp_writeout_op op = word & 0x7; 10477ec681f3Smrg ctx->midg_stats.instruction_count++; 10487ec681f3Smrg 10497ec681f3Smrg switch (op) { 10507ec681f3Smrg case midgard_jmp_writeout_op_branch_uncond: { 10517ec681f3Smrg midgard_branch_uncond br_uncond; 10527ec681f3Smrg memcpy((char *) &br_uncond, (char *) &word, sizeof(br_uncond)); 10537ec681f3Smrg fprintf(fp, "br.uncond "); 10547ec681f3Smrg 10557ec681f3Smrg if (br_uncond.unknown != 1) 10567ec681f3Smrg fprintf(fp, "unknown:%u, ", br_uncond.unknown); 10577ec681f3Smrg 10587ec681f3Smrg if (br_uncond.offset >= 0) 10597ec681f3Smrg fprintf(fp, "+"); 10607ec681f3Smrg 10617ec681f3Smrg fprintf(fp, "%d -> %s", br_uncond.offset, 10627ec681f3Smrg midgard_tag_props[br_uncond.dest_tag].name); 10637ec681f3Smrg fprintf(fp, "\n"); 10647ec681f3Smrg 10657ec681f3Smrg return br_uncond.offset >= 0; 10667ec681f3Smrg } 10677ec681f3Smrg 10687ec681f3Smrg case midgard_jmp_writeout_op_branch_cond: 10697ec681f3Smrg case midgard_jmp_writeout_op_writeout: 10707ec681f3Smrg case midgard_jmp_writeout_op_discard: 10717ec681f3Smrg default: { 10727ec681f3Smrg midgard_branch_cond br_cond; 10737ec681f3Smrg memcpy((char *) &br_cond, (char *) &word, sizeof(br_cond)); 10747ec681f3Smrg 10757ec681f3Smrg fprintf(fp, "br."); 10767ec681f3Smrg 10777ec681f3Smrg print_branch_op(fp, br_cond.op); 10787ec681f3Smrg print_branch_cond(fp, br_cond.cond); 10797ec681f3Smrg 10807ec681f3Smrg fprintf(fp, " "); 10817ec681f3Smrg 10827ec681f3Smrg if (br_cond.offset >= 0) 10837ec681f3Smrg fprintf(fp, "+"); 10847ec681f3Smrg 10857ec681f3Smrg fprintf(fp, "%d -> %s", br_cond.offset, 10867ec681f3Smrg midgard_tag_props[br_cond.dest_tag].name); 10877ec681f3Smrg fprintf(fp, "\n"); 10887ec681f3Smrg 10897ec681f3Smrg return br_cond.offset >= 0; 10907ec681f3Smrg } 10917ec681f3Smrg } 10927ec681f3Smrg 10937ec681f3Smrg return false; 10947ec681f3Smrg} 10957ec681f3Smrg 10967ec681f3Smrgstatic bool 10977ec681f3Smrgprint_extended_branch_writeout_field(disassemble_context *ctx, FILE *fp, uint8_t *words, 10987ec681f3Smrg unsigned next) 10997ec681f3Smrg{ 11007ec681f3Smrg midgard_branch_extended br; 11017ec681f3Smrg memcpy((char *) &br, (char *) words, sizeof(br)); 11027ec681f3Smrg 11037ec681f3Smrg fprintf(fp, "brx."); 11047ec681f3Smrg 11057ec681f3Smrg print_branch_op(fp, br.op); 11067ec681f3Smrg 11077ec681f3Smrg /* Condition codes are a LUT in the general case, but simply repeated 8 times for single-channel conditions.. Check this. */ 11087ec681f3Smrg 11097ec681f3Smrg bool single_channel = true; 11107ec681f3Smrg 11117ec681f3Smrg for (unsigned i = 0; i < 16; i += 2) { 11127ec681f3Smrg single_channel &= (((br.cond >> i) & 0x3) == (br.cond & 0x3)); 11137ec681f3Smrg } 11147ec681f3Smrg 11157ec681f3Smrg if (single_channel) 11167ec681f3Smrg print_branch_cond(fp, br.cond & 0x3); 11177ec681f3Smrg else 11187ec681f3Smrg fprintf(fp, "lut%X", br.cond); 11197ec681f3Smrg 11207ec681f3Smrg if (br.unknown) 11217ec681f3Smrg fprintf(fp, ".unknown%u", br.unknown); 11227ec681f3Smrg 11237ec681f3Smrg fprintf(fp, " "); 11247ec681f3Smrg 11257ec681f3Smrg if (br.offset >= 0) 11267ec681f3Smrg fprintf(fp, "+"); 11277ec681f3Smrg 11287ec681f3Smrg fprintf(fp, "%d -> %s\n", br.offset, 11297ec681f3Smrg midgard_tag_props[br.dest_tag].name); 11307ec681f3Smrg 11317ec681f3Smrg unsigned I = next + br.offset * 4; 11327ec681f3Smrg 11337ec681f3Smrg if (ctx->midg_tags[I] && ctx->midg_tags[I] != br.dest_tag) { 11347ec681f3Smrg fprintf(fp, "\t/* XXX TAG ERROR: jumping to %s but tagged %s \n", 11357ec681f3Smrg midgard_tag_props[br.dest_tag].name, 11367ec681f3Smrg midgard_tag_props[ctx->midg_tags[I]].name); 11377ec681f3Smrg } 11387ec681f3Smrg 11397ec681f3Smrg ctx->midg_tags[I] = br.dest_tag; 11407ec681f3Smrg 11417ec681f3Smrg ctx->midg_stats.instruction_count++; 11427ec681f3Smrg return br.offset >= 0; 11437ec681f3Smrg} 11447ec681f3Smrg 11457ec681f3Smrgstatic unsigned 11467ec681f3Smrgnum_alu_fields_enabled(uint32_t control_word) 11477ec681f3Smrg{ 11487ec681f3Smrg unsigned ret = 0; 11497ec681f3Smrg 11507ec681f3Smrg if ((control_word >> 17) & 1) 11517ec681f3Smrg ret++; 11527ec681f3Smrg 11537ec681f3Smrg if ((control_word >> 19) & 1) 11547ec681f3Smrg ret++; 11557ec681f3Smrg 11567ec681f3Smrg if ((control_word >> 21) & 1) 11577ec681f3Smrg ret++; 11587ec681f3Smrg 11597ec681f3Smrg if ((control_word >> 23) & 1) 11607ec681f3Smrg ret++; 11617ec681f3Smrg 11627ec681f3Smrg if ((control_word >> 25) & 1) 11637ec681f3Smrg ret++; 11647ec681f3Smrg 11657ec681f3Smrg return ret; 11667ec681f3Smrg} 11677ec681f3Smrg 11687ec681f3Smrgstatic bool 11697ec681f3Smrgprint_alu_word(disassemble_context *ctx, FILE *fp, uint32_t *words, 11707ec681f3Smrg unsigned num_quad_words, unsigned tabs, unsigned next, 11717ec681f3Smrg bool verbose) 11727ec681f3Smrg{ 11737ec681f3Smrg uint32_t control_word = words[0]; 11747ec681f3Smrg uint16_t *beginning_ptr = (uint16_t *)(words + 1); 11757ec681f3Smrg unsigned num_fields = num_alu_fields_enabled(control_word); 11767ec681f3Smrg uint16_t *word_ptr = beginning_ptr + num_fields; 11777ec681f3Smrg unsigned num_words = 2 + num_fields; 11787ec681f3Smrg const midgard_constants *consts = NULL; 11797ec681f3Smrg bool branch_forward = false; 11807ec681f3Smrg 11817ec681f3Smrg if ((control_word >> 17) & 1) 11827ec681f3Smrg num_words += 3; 11837ec681f3Smrg 11847ec681f3Smrg if ((control_word >> 19) & 1) 11857ec681f3Smrg num_words += 2; 11867ec681f3Smrg 11877ec681f3Smrg if ((control_word >> 21) & 1) 11887ec681f3Smrg num_words += 3; 11897ec681f3Smrg 11907ec681f3Smrg if ((control_word >> 23) & 1) 11917ec681f3Smrg num_words += 2; 11927ec681f3Smrg 11937ec681f3Smrg if ((control_word >> 25) & 1) 11947ec681f3Smrg num_words += 3; 11957ec681f3Smrg 11967ec681f3Smrg if ((control_word >> 26) & 1) 11977ec681f3Smrg num_words += 1; 11987ec681f3Smrg 11997ec681f3Smrg if ((control_word >> 27) & 1) 12007ec681f3Smrg num_words += 3; 12017ec681f3Smrg 12027ec681f3Smrg if (num_quad_words > (num_words + 7) / 8) { 12037ec681f3Smrg assert(num_quad_words == (num_words + 15) / 8); 12047ec681f3Smrg //Assume that the extra quadword is constants 12057ec681f3Smrg consts = (midgard_constants *)(words + (4 * num_quad_words - 4)); 12067ec681f3Smrg } 12077ec681f3Smrg 12087ec681f3Smrg if ((control_word >> 16) & 1) 12097ec681f3Smrg fprintf(fp, "unknown bit 16 enabled\n"); 12107ec681f3Smrg 12117ec681f3Smrg if ((control_word >> 17) & 1) { 12127ec681f3Smrg print_vector_field(ctx, fp, "vmul", word_ptr, *beginning_ptr, consts, tabs, verbose); 12137ec681f3Smrg beginning_ptr += 1; 12147ec681f3Smrg word_ptr += 3; 12157ec681f3Smrg } 12167ec681f3Smrg 12177ec681f3Smrg if ((control_word >> 18) & 1) 12187ec681f3Smrg fprintf(fp, "unknown bit 18 enabled\n"); 12197ec681f3Smrg 12207ec681f3Smrg if ((control_word >> 19) & 1) { 12217ec681f3Smrg print_scalar_field(ctx, fp, "sadd", word_ptr, *beginning_ptr, consts, tabs, verbose); 12227ec681f3Smrg beginning_ptr += 1; 12237ec681f3Smrg word_ptr += 2; 12247ec681f3Smrg } 12257ec681f3Smrg 12267ec681f3Smrg if ((control_word >> 20) & 1) 12277ec681f3Smrg fprintf(fp, "unknown bit 20 enabled\n"); 12287ec681f3Smrg 12297ec681f3Smrg if ((control_word >> 21) & 1) { 12307ec681f3Smrg print_vector_field(ctx, fp, "vadd", word_ptr, *beginning_ptr, consts, tabs, verbose); 12317ec681f3Smrg beginning_ptr += 1; 12327ec681f3Smrg word_ptr += 3; 12337ec681f3Smrg } 12347ec681f3Smrg 12357ec681f3Smrg if ((control_word >> 22) & 1) 12367ec681f3Smrg fprintf(fp, "unknown bit 22 enabled\n"); 12377ec681f3Smrg 12387ec681f3Smrg if ((control_word >> 23) & 1) { 12397ec681f3Smrg print_scalar_field(ctx, fp, "smul", word_ptr, *beginning_ptr, consts, tabs, verbose); 12407ec681f3Smrg beginning_ptr += 1; 12417ec681f3Smrg word_ptr += 2; 12427ec681f3Smrg } 12437ec681f3Smrg 12447ec681f3Smrg if ((control_word >> 24) & 1) 12457ec681f3Smrg fprintf(fp, "unknown bit 24 enabled\n"); 12467ec681f3Smrg 12477ec681f3Smrg if ((control_word >> 25) & 1) { 12487ec681f3Smrg print_vector_field(ctx, fp, "lut", word_ptr, *beginning_ptr, consts, tabs, verbose); 12497ec681f3Smrg word_ptr += 3; 12507ec681f3Smrg } 12517ec681f3Smrg 12527ec681f3Smrg if ((control_word >> 26) & 1) { 12537ec681f3Smrg branch_forward |= print_compact_branch_writeout_field(ctx, fp, *word_ptr); 12547ec681f3Smrg word_ptr += 1; 12557ec681f3Smrg } 12567ec681f3Smrg 12577ec681f3Smrg if ((control_word >> 27) & 1) { 12587ec681f3Smrg branch_forward |= print_extended_branch_writeout_field(ctx, fp, (uint8_t *) word_ptr, next); 12597ec681f3Smrg word_ptr += 3; 12607ec681f3Smrg } 12617ec681f3Smrg 12627ec681f3Smrg if (consts) 12637ec681f3Smrg fprintf(fp, "uconstants 0x%X, 0x%X, 0x%X, 0x%X\n", 12647ec681f3Smrg consts->u32[0], consts->u32[1], 12657ec681f3Smrg consts->u32[2], consts->u32[3]); 12667ec681f3Smrg 12677ec681f3Smrg return branch_forward; 12687ec681f3Smrg} 12697ec681f3Smrg 12707ec681f3Smrg/* TODO: how can we use this now that we know that these params can't be known 12717ec681f3Smrg * before run time in every single case? Maybe just use it in the cases we can? */ 12727ec681f3SmrgUNUSED static void 12737ec681f3Smrgprint_varying_parameters(FILE *fp, midgard_load_store_word *word) 12747ec681f3Smrg{ 12757ec681f3Smrg midgard_varying_params p = midgard_unpack_varying_params(*word); 12767ec681f3Smrg 12777ec681f3Smrg /* If a varying, there are qualifiers */ 12787ec681f3Smrg if (p.flat_shading) 12797ec681f3Smrg fprintf(fp, ".flat"); 12807ec681f3Smrg 12817ec681f3Smrg if (p.perspective_correction) 12827ec681f3Smrg fprintf(fp, ".correction"); 12837ec681f3Smrg 12847ec681f3Smrg if (p.centroid_mapping) 12857ec681f3Smrg fprintf(fp, ".centroid"); 12867ec681f3Smrg 12877ec681f3Smrg if (p.interpolate_sample) 12887ec681f3Smrg fprintf(fp, ".sample"); 12897ec681f3Smrg 12907ec681f3Smrg switch (p.modifier) { 12917ec681f3Smrg case midgard_varying_mod_perspective_y: 12927ec681f3Smrg fprintf(fp, ".perspectivey"); 12937ec681f3Smrg break; 12947ec681f3Smrg case midgard_varying_mod_perspective_z: 12957ec681f3Smrg fprintf(fp, ".perspectivez"); 12967ec681f3Smrg break; 12977ec681f3Smrg case midgard_varying_mod_perspective_w: 12987ec681f3Smrg fprintf(fp, ".perspectivew"); 12997ec681f3Smrg break; 13007ec681f3Smrg default: 13017ec681f3Smrg unreachable("invalid varying modifier"); 13027ec681f3Smrg break; 13037ec681f3Smrg } 13047ec681f3Smrg} 13057ec681f3Smrg 13067ec681f3Smrgstatic bool 13077ec681f3Smrgis_op_varying(unsigned op) 13087ec681f3Smrg{ 13097ec681f3Smrg switch (op) { 13107ec681f3Smrg case midgard_op_st_vary_16: 13117ec681f3Smrg case midgard_op_st_vary_32: 13127ec681f3Smrg case midgard_op_st_vary_32i: 13137ec681f3Smrg case midgard_op_st_vary_32u: 13147ec681f3Smrg case midgard_op_ld_vary_16: 13157ec681f3Smrg case midgard_op_ld_vary_32: 13167ec681f3Smrg case midgard_op_ld_vary_32i: 13177ec681f3Smrg case midgard_op_ld_vary_32u: 13187ec681f3Smrg return true; 13197ec681f3Smrg } 13207ec681f3Smrg 13217ec681f3Smrg return false; 13227ec681f3Smrg} 13237ec681f3Smrg 13247ec681f3Smrgstatic bool 13257ec681f3Smrgis_op_attribute(unsigned op) 13267ec681f3Smrg{ 13277ec681f3Smrg switch (op) { 13287ec681f3Smrg case midgard_op_ld_attr_16: 13297ec681f3Smrg case midgard_op_ld_attr_32: 13307ec681f3Smrg case midgard_op_ld_attr_32i: 13317ec681f3Smrg case midgard_op_ld_attr_32u: 13327ec681f3Smrg return true; 13337ec681f3Smrg } 13347ec681f3Smrg 13357ec681f3Smrg return false; 13367ec681f3Smrg} 13377ec681f3Smrg 13387ec681f3Smrg/* Helper to print integer well-formatted, but only when non-zero. */ 13397ec681f3Smrgstatic void 13407ec681f3Smrgmidgard_print_sint(FILE *fp, int n) 13417ec681f3Smrg{ 13427ec681f3Smrg if (n > 0) 13437ec681f3Smrg fprintf(fp, " + 0x%X", n); 13447ec681f3Smrg else if (n < 0) 13457ec681f3Smrg fprintf(fp, " - 0x%X", -n); 13467ec681f3Smrg} 13477ec681f3Smrg 13487ec681f3Smrgstatic void 13497ec681f3Smrgupdate_stats(signed *stat, unsigned address) 13507ec681f3Smrg{ 13517ec681f3Smrg if (*stat >= 0) 13527ec681f3Smrg *stat = MAX2(*stat, address + 1); 13537ec681f3Smrg} 13547ec681f3Smrg 13557ec681f3Smrgstatic void 13567ec681f3Smrgprint_load_store_instr(disassemble_context *ctx, FILE *fp, uint64_t data, bool verbose) 13577ec681f3Smrg{ 13587ec681f3Smrg midgard_load_store_word *word = (midgard_load_store_word *) &data; 13597ec681f3Smrg 13607ec681f3Smrg print_ld_st_opcode(fp, word->op); 13617ec681f3Smrg 13627ec681f3Smrg if (word->op == midgard_op_trap) { 13637ec681f3Smrg fprintf(fp, " 0x%X\n", word->signed_offset); 13647ec681f3Smrg return; 13657ec681f3Smrg } 13667ec681f3Smrg 13677ec681f3Smrg /* Print opcode modifiers */ 13687ec681f3Smrg 13697ec681f3Smrg if (OP_USES_ATTRIB(word->op)) { 13707ec681f3Smrg /* Print non-default attribute tables */ 13717ec681f3Smrg bool default_secondary = 13727ec681f3Smrg (word->op == midgard_op_st_vary_32) || 13737ec681f3Smrg (word->op == midgard_op_st_vary_16) || 13747ec681f3Smrg (word->op == midgard_op_st_vary_32u) || 13757ec681f3Smrg (word->op == midgard_op_st_vary_32i) || 13767ec681f3Smrg (word->op == midgard_op_ld_vary_32) || 13777ec681f3Smrg (word->op == midgard_op_ld_vary_16) || 13787ec681f3Smrg (word->op == midgard_op_ld_vary_32u) || 13797ec681f3Smrg (word->op == midgard_op_ld_vary_32i); 13807ec681f3Smrg 13817ec681f3Smrg bool default_primary = 13827ec681f3Smrg (word->op == midgard_op_ld_attr_32) || 13837ec681f3Smrg (word->op == midgard_op_ld_attr_16) || 13847ec681f3Smrg (word->op == midgard_op_ld_attr_32u) || 13857ec681f3Smrg (word->op == midgard_op_ld_attr_32i); 13867ec681f3Smrg 13877ec681f3Smrg bool has_default = (default_secondary || default_primary); 13887ec681f3Smrg bool is_secondary = (word->index_format >> 1); 13897ec681f3Smrg 13907ec681f3Smrg if (has_default && (is_secondary != default_secondary)) 13917ec681f3Smrg fprintf(fp, ".%s", is_secondary ? "secondary" : "primary"); 13927ec681f3Smrg } else if (word->op == midgard_op_ld_cubemap_coords || OP_IS_PROJECTION(word->op)) 13937ec681f3Smrg fprintf(fp, ".%s", word->bitsize_toggle ? "f32" : "f16"); 13947ec681f3Smrg 13957ec681f3Smrg fprintf(fp, " "); 13967ec681f3Smrg 13977ec681f3Smrg /* src/dest register */ 13987ec681f3Smrg 13997ec681f3Smrg if (!OP_IS_STORE(word->op)) { 14007ec681f3Smrg print_ldst_write_reg(fp, word->reg); 14017ec681f3Smrg 14027ec681f3Smrg /* Some opcodes don't have a swizzable src register, and 14037ec681f3Smrg * instead the swizzle is applied before the result is written 14047ec681f3Smrg * to the dest reg. For these ops, we combine the writemask 14057ec681f3Smrg * with the swizzle to display them in the disasm compactly. */ 14067ec681f3Smrg unsigned swizzle = word->swizzle; 14077ec681f3Smrg if ((OP_IS_REG2REG_LDST(word->op) && 14087ec681f3Smrg word->op != midgard_op_lea && 14097ec681f3Smrg word->op != midgard_op_lea_image) || OP_IS_ATOMIC(word->op)) 14107ec681f3Smrg swizzle = 0xE4; 14117ec681f3Smrg print_ldst_mask(fp, word->mask, swizzle); 14127ec681f3Smrg } else { 14137ec681f3Smrg print_ldst_read_reg(fp, word->reg); 14147ec681f3Smrg print_vec_swizzle(fp, word->swizzle, midgard_src_passthrough, 14157ec681f3Smrg midgard_reg_mode_32, 0xFF); 14167ec681f3Smrg } 14177ec681f3Smrg 14187ec681f3Smrg /* ld_ubo args */ 14197ec681f3Smrg if (OP_IS_UBO_READ(word->op)) { 14207ec681f3Smrg if (word->signed_offset & 1) { /* buffer index imm */ 14217ec681f3Smrg unsigned imm = midgard_unpack_ubo_index_imm(*word); 14227ec681f3Smrg fprintf(fp, ", %u", imm); 14237ec681f3Smrg } else { /* buffer index from reg */ 14247ec681f3Smrg fprintf(fp, ", "); 14257ec681f3Smrg print_ldst_read_reg(fp, word->arg_reg); 14267ec681f3Smrg fprintf(fp, ".%c", components[word->arg_comp]); 14277ec681f3Smrg } 14287ec681f3Smrg 14297ec681f3Smrg fprintf(fp, ", "); 14307ec681f3Smrg print_ldst_read_reg(fp, word->index_reg); 14317ec681f3Smrg fprintf(fp, ".%c", components[word->index_comp]); 14327ec681f3Smrg if (word->index_shift) 14337ec681f3Smrg fprintf(fp, " lsl %u", word->index_shift); 14347ec681f3Smrg midgard_print_sint(fp, UNPACK_LDST_UBO_OFS(word->signed_offset)); 14357ec681f3Smrg } 14367ec681f3Smrg 14377ec681f3Smrg /* mem addr expression */ 14387ec681f3Smrg if (OP_HAS_ADDRESS(word->op)) { 14397ec681f3Smrg fprintf(fp, ", "); 14407ec681f3Smrg bool first = true; 14417ec681f3Smrg 14427ec681f3Smrg /* Skip printing zero */ 14437ec681f3Smrg if (word->arg_reg != 7 || verbose) { 14447ec681f3Smrg print_ldst_read_reg(fp, word->arg_reg); 14457ec681f3Smrg fprintf(fp, ".u%d.%c", 14467ec681f3Smrg word->bitsize_toggle ? 64 : 32, components[word->arg_comp]); 14477ec681f3Smrg first = false; 14487ec681f3Smrg } 14497ec681f3Smrg 14507ec681f3Smrg if ((word->op < midgard_op_atomic_cmpxchg || 14517ec681f3Smrg word->op > midgard_op_atomic_cmpxchg64_be) && 14527ec681f3Smrg word->index_reg != 0x7) { 14537ec681f3Smrg if (!first) 14547ec681f3Smrg fprintf(fp, " + "); 14557ec681f3Smrg 14567ec681f3Smrg print_ldst_read_reg(fp, word->index_reg); 14577ec681f3Smrg fprintf(fp, "%s.%c", 14587ec681f3Smrg index_format_names[word->index_format], 14597ec681f3Smrg components[word->index_comp]); 14607ec681f3Smrg if (word->index_shift) 14617ec681f3Smrg fprintf(fp, " lsl %u", word->index_shift); 14627ec681f3Smrg } 14637ec681f3Smrg 14647ec681f3Smrg midgard_print_sint(fp, word->signed_offset); 14657ec681f3Smrg } 14667ec681f3Smrg 14677ec681f3Smrg /* src reg for reg2reg ldst opcodes */ 14687ec681f3Smrg if (OP_IS_REG2REG_LDST(word->op)) { 14697ec681f3Smrg fprintf(fp, ", "); 14707ec681f3Smrg print_ldst_read_reg(fp, word->arg_reg); 14717ec681f3Smrg print_vec_swizzle(fp, word->swizzle, midgard_src_passthrough, 14727ec681f3Smrg midgard_reg_mode_32, 0xFF); 14737ec681f3Smrg } 14747ec681f3Smrg 14757ec681f3Smrg /* atomic ops encode the source arg where the ldst swizzle would be. */ 14767ec681f3Smrg if (OP_IS_ATOMIC(word->op)) { 14777ec681f3Smrg unsigned src = (word->swizzle >> 2) & 0x7; 14787ec681f3Smrg unsigned src_comp = word->swizzle & 0x3; 14797ec681f3Smrg fprintf(fp, ", "); 14807ec681f3Smrg print_ldst_read_reg(fp, src); 14817ec681f3Smrg fprintf(fp, ".%c", components[src_comp]); 14827ec681f3Smrg } 14837ec681f3Smrg 14847ec681f3Smrg /* CMPXCHG encodes the extra comparison arg where the index reg would be. */ 14857ec681f3Smrg if (word->op >= midgard_op_atomic_cmpxchg && 14867ec681f3Smrg word->op <= midgard_op_atomic_cmpxchg64_be) { 14877ec681f3Smrg fprintf(fp, ", "); 14887ec681f3Smrg print_ldst_read_reg(fp, word->index_reg); 14897ec681f3Smrg fprintf(fp, ".%c", components[word->index_comp]); 14907ec681f3Smrg } 14917ec681f3Smrg 14927ec681f3Smrg /* index reg for attr/vary/images, selector for ld/st_special */ 14937ec681f3Smrg if (OP_IS_SPECIAL(word->op) || OP_USES_ATTRIB(word->op)) { 14947ec681f3Smrg fprintf(fp, ", "); 14957ec681f3Smrg print_ldst_read_reg(fp, word->index_reg); 14967ec681f3Smrg fprintf(fp, ".%c", components[word->index_comp]); 14977ec681f3Smrg if (word->index_shift) 14987ec681f3Smrg fprintf(fp, " lsl %u", word->index_shift); 14997ec681f3Smrg midgard_print_sint(fp, UNPACK_LDST_ATTRIB_OFS(word->signed_offset)); 15007ec681f3Smrg } 15017ec681f3Smrg 15027ec681f3Smrg /* vertex reg for attrib/varying ops, coord reg for image ops */ 15037ec681f3Smrg if (OP_USES_ATTRIB(word->op)) { 15047ec681f3Smrg fprintf(fp, ", "); 15057ec681f3Smrg print_ldst_read_reg(fp, word->arg_reg); 15067ec681f3Smrg 15077ec681f3Smrg if (OP_IS_IMAGE(word->op)) 15087ec681f3Smrg fprintf(fp, ".u%d", word->bitsize_toggle ? 64 : 32); 15097ec681f3Smrg 15107ec681f3Smrg fprintf(fp, ".%c", components[word->arg_comp]); 15117ec681f3Smrg 15127ec681f3Smrg if (word->bitsize_toggle && !OP_IS_IMAGE(word->op)) 15137ec681f3Smrg midgard_print_sint(fp, UNPACK_LDST_VERTEX_OFS(word->signed_offset)); 15147ec681f3Smrg } 15157ec681f3Smrg 15167ec681f3Smrg /* TODO: properly decode format specifier for PACK/UNPACK ops */ 15177ec681f3Smrg if (OP_IS_PACK_COLOUR(word->op) || OP_IS_UNPACK_COLOUR(word->op)) { 15187ec681f3Smrg fprintf(fp, ", "); 15197ec681f3Smrg unsigned format_specifier = (word->signed_offset << 4) | word->index_shift; 15207ec681f3Smrg fprintf(fp, "0x%X", format_specifier); 15217ec681f3Smrg } 15227ec681f3Smrg 15237ec681f3Smrg fprintf(fp, "\n"); 15247ec681f3Smrg 15257ec681f3Smrg /* Debugging stuff */ 15267ec681f3Smrg 15277ec681f3Smrg if (is_op_varying(word->op)) { 15287ec681f3Smrg /* Do some analysis: check if direct access */ 15297ec681f3Smrg 15307ec681f3Smrg if (word->index_reg == 0x7 && ctx->midg_stats.varying_count >= 0) 15317ec681f3Smrg update_stats(&ctx->midg_stats.varying_count, 15327ec681f3Smrg UNPACK_LDST_ATTRIB_OFS(word->signed_offset)); 15337ec681f3Smrg else 15347ec681f3Smrg ctx->midg_stats.varying_count = -16; 15357ec681f3Smrg } else if (is_op_attribute(word->op)) { 15367ec681f3Smrg if (word->index_reg == 0x7 && ctx->midg_stats.attribute_count >= 0) 15377ec681f3Smrg update_stats(&ctx->midg_stats.attribute_count, 15387ec681f3Smrg UNPACK_LDST_ATTRIB_OFS(word->signed_offset)); 15397ec681f3Smrg else 15407ec681f3Smrg ctx->midg_stats.attribute_count = -16; 15417ec681f3Smrg } 15427ec681f3Smrg 15437ec681f3Smrg if (!OP_IS_STORE(word->op)) 15447ec681f3Smrg update_dest(ctx, word->reg); 15457ec681f3Smrg 15467ec681f3Smrg if (OP_IS_UBO_READ(word->op)) 15477ec681f3Smrg update_stats(&ctx->midg_stats.uniform_buffer_count, 15487ec681f3Smrg UNPACK_LDST_UBO_OFS(word->signed_offset)); 15497ec681f3Smrg 15507ec681f3Smrg ctx->midg_stats.instruction_count++; 15517ec681f3Smrg} 15527ec681f3Smrg 15537ec681f3Smrgstatic void 15547ec681f3Smrgprint_load_store_word(disassemble_context *ctx, FILE *fp, uint32_t *word, bool verbose) 15557ec681f3Smrg{ 15567ec681f3Smrg midgard_load_store *load_store = (midgard_load_store *) word; 15577ec681f3Smrg 15587ec681f3Smrg if (load_store->word1 != 3) { 15597ec681f3Smrg print_load_store_instr(ctx, fp, load_store->word1, verbose); 15607ec681f3Smrg } 15617ec681f3Smrg 15627ec681f3Smrg if (load_store->word2 != 3) { 15637ec681f3Smrg print_load_store_instr(ctx, fp, load_store->word2, verbose); 15647ec681f3Smrg } 15657ec681f3Smrg} 15667ec681f3Smrg 15677ec681f3Smrgstatic void 15687ec681f3Smrgprint_texture_reg_select(FILE *fp, uint8_t u, unsigned base) 15697ec681f3Smrg{ 15707ec681f3Smrg midgard_tex_register_select sel; 15717ec681f3Smrg memcpy(&sel, &u, sizeof(u)); 15727ec681f3Smrg 15737ec681f3Smrg print_tex_reg(fp, base + sel.select, false); 15747ec681f3Smrg 15757ec681f3Smrg unsigned component = sel.component; 15767ec681f3Smrg 15777ec681f3Smrg /* Use the upper half in half-reg mode */ 15787ec681f3Smrg if (sel.upper) { 15797ec681f3Smrg assert(!sel.full); 15807ec681f3Smrg component += 4; 15817ec681f3Smrg } 15827ec681f3Smrg 15837ec681f3Smrg fprintf(fp, ".%c.%d", components[component], sel.full ? 32 : 16); 15847ec681f3Smrg 15857ec681f3Smrg assert(sel.zero == 0); 15867ec681f3Smrg} 15877ec681f3Smrg 15887ec681f3Smrgstatic void 15897ec681f3Smrgprint_texture_format(FILE *fp, int format) 15907ec681f3Smrg{ 15917ec681f3Smrg /* Act like a modifier */ 15927ec681f3Smrg fprintf(fp, "."); 15937ec681f3Smrg 15947ec681f3Smrg switch (format) { 15957ec681f3Smrg DEFINE_CASE(1, "1d"); 15967ec681f3Smrg DEFINE_CASE(2, "2d"); 15977ec681f3Smrg DEFINE_CASE(3, "3d"); 15987ec681f3Smrg DEFINE_CASE(0, "cube"); 15997ec681f3Smrg 16007ec681f3Smrg default: 16017ec681f3Smrg unreachable("Bad format"); 16027ec681f3Smrg } 16037ec681f3Smrg} 16047ec681f3Smrg 16057ec681f3Smrgstatic bool 16067ec681f3Smrgmidgard_op_has_helpers(unsigned op) 16077ec681f3Smrg{ 16087ec681f3Smrg switch (op) { 16097ec681f3Smrg case midgard_tex_op_normal: 16107ec681f3Smrg case midgard_tex_op_derivative: 16117ec681f3Smrg return true; 16127ec681f3Smrg default: 16137ec681f3Smrg return false; 16147ec681f3Smrg } 16157ec681f3Smrg} 16167ec681f3Smrg 16177ec681f3Smrgstatic void 16187ec681f3Smrgprint_texture_op(FILE *fp, unsigned op) 16197ec681f3Smrg{ 16207ec681f3Smrg if (tex_opcode_props[op].name) 16217ec681f3Smrg fprintf(fp, "%s", tex_opcode_props[op].name); 16227ec681f3Smrg else 16237ec681f3Smrg fprintf(fp, "tex_op_%02X", op); 16247ec681f3Smrg} 16257ec681f3Smrg 16267ec681f3Smrgstatic bool 16277ec681f3Smrgtexture_op_takes_bias(unsigned op) 16287ec681f3Smrg{ 16297ec681f3Smrg return op == midgard_tex_op_normal; 16307ec681f3Smrg} 16317ec681f3Smrg 16327ec681f3Smrgstatic char 16337ec681f3Smrgsampler_type_name(enum mali_sampler_type t) 16347ec681f3Smrg{ 16357ec681f3Smrg switch (t) { 16367ec681f3Smrg case MALI_SAMPLER_FLOAT: 16377ec681f3Smrg return 'f'; 16387ec681f3Smrg case MALI_SAMPLER_UNSIGNED: 16397ec681f3Smrg return 'u'; 16407ec681f3Smrg case MALI_SAMPLER_SIGNED: 16417ec681f3Smrg return 'i'; 16427ec681f3Smrg default: 16437ec681f3Smrg return '?'; 16447ec681f3Smrg } 16457ec681f3Smrg 16467ec681f3Smrg} 16477ec681f3Smrg 16487ec681f3Smrgstatic void 16497ec681f3Smrgprint_texture_barrier(FILE *fp, uint32_t *word) 16507ec681f3Smrg{ 16517ec681f3Smrg midgard_texture_barrier_word *barrier = (midgard_texture_barrier_word *) word; 16527ec681f3Smrg 16537ec681f3Smrg if (barrier->type != TAG_TEXTURE_4_BARRIER) 16547ec681f3Smrg fprintf(fp, "/* barrier tag %X != tex/bar */ ", barrier->type); 16557ec681f3Smrg 16567ec681f3Smrg if (!barrier->cont) 16577ec681f3Smrg fprintf(fp, "/* cont missing? */"); 16587ec681f3Smrg 16597ec681f3Smrg if (!barrier->last) 16607ec681f3Smrg fprintf(fp, "/* last missing? */"); 16617ec681f3Smrg 16627ec681f3Smrg if (barrier->zero1) 16637ec681f3Smrg fprintf(fp, "/* zero1 = 0x%X */ ", barrier->zero1); 16647ec681f3Smrg 16657ec681f3Smrg if (barrier->zero2) 16667ec681f3Smrg fprintf(fp, "/* zero2 = 0x%X */ ", barrier->zero2); 16677ec681f3Smrg 16687ec681f3Smrg if (barrier->zero3) 16697ec681f3Smrg fprintf(fp, "/* zero3 = 0x%X */ ", barrier->zero3); 16707ec681f3Smrg 16717ec681f3Smrg if (barrier->zero4) 16727ec681f3Smrg fprintf(fp, "/* zero4 = 0x%X */ ", barrier->zero4); 16737ec681f3Smrg 16747ec681f3Smrg if (barrier->zero5) 16757ec681f3Smrg fprintf(fp, "/* zero4 = 0x%" PRIx64 " */ ", barrier->zero5); 16767ec681f3Smrg 16777ec681f3Smrg if (barrier->out_of_order) 16787ec681f3Smrg fprintf(fp, ".ooo%u", barrier->out_of_order); 16797ec681f3Smrg 16807ec681f3Smrg fprintf(fp, "\n"); 16817ec681f3Smrg} 16827ec681f3Smrg 16837ec681f3Smrg#undef DEFINE_CASE 16847ec681f3Smrg 16857ec681f3Smrgstatic const char * 16867ec681f3Smrgtexture_mode(enum mali_texture_mode mode) 16877ec681f3Smrg{ 16887ec681f3Smrg switch (mode) { 16897ec681f3Smrg case TEXTURE_NORMAL: return ""; 16907ec681f3Smrg case TEXTURE_SHADOW: return ".shadow"; 16917ec681f3Smrg case TEXTURE_GATHER_SHADOW: return ".gather.shadow"; 16927ec681f3Smrg case TEXTURE_GATHER_X: return ".gatherX"; 16937ec681f3Smrg case TEXTURE_GATHER_Y: return ".gatherY"; 16947ec681f3Smrg case TEXTURE_GATHER_Z: return ".gatherZ"; 16957ec681f3Smrg case TEXTURE_GATHER_W: return ".gatherW"; 16967ec681f3Smrg default: return "unk"; 16977ec681f3Smrg } 16987ec681f3Smrg} 16997ec681f3Smrg 17007ec681f3Smrgstatic const char * 17017ec681f3Smrgderivative_mode(enum mali_derivative_mode mode) 17027ec681f3Smrg{ 17037ec681f3Smrg switch (mode) { 17047ec681f3Smrg case TEXTURE_DFDX: return ".x"; 17057ec681f3Smrg case TEXTURE_DFDY: return ".y"; 17067ec681f3Smrg default: return "unk"; 17077ec681f3Smrg } 17087ec681f3Smrg} 17097ec681f3Smrg 17107ec681f3Smrgstatic void 17117ec681f3Smrgprint_texture_word(disassemble_context *ctx, FILE *fp, uint32_t *word, 17127ec681f3Smrg unsigned tabs, unsigned in_reg_base, unsigned out_reg_base) 17137ec681f3Smrg{ 17147ec681f3Smrg midgard_texture_word *texture = (midgard_texture_word *) word; 17157ec681f3Smrg ctx->midg_stats.helper_invocations |= midgard_op_has_helpers(texture->op); 17167ec681f3Smrg validate_sampler_type(texture->op, texture->sampler_type); 17177ec681f3Smrg 17187ec681f3Smrg /* Broad category of texture operation in question */ 17197ec681f3Smrg print_texture_op(fp, texture->op); 17207ec681f3Smrg 17217ec681f3Smrg /* Barriers use a dramatically different code path */ 17227ec681f3Smrg if (texture->op == midgard_tex_op_barrier) { 17237ec681f3Smrg print_texture_barrier(fp, word); 17247ec681f3Smrg return; 17257ec681f3Smrg } else if (texture->type == TAG_TEXTURE_4_BARRIER) 17267ec681f3Smrg fprintf (fp, "/* nonbarrier had tex/bar tag */ "); 17277ec681f3Smrg else if (texture->type == TAG_TEXTURE_4_VTX) 17287ec681f3Smrg fprintf (fp, ".vtx"); 17297ec681f3Smrg 17307ec681f3Smrg if (texture->op == midgard_tex_op_derivative) 17317ec681f3Smrg fprintf(fp, "%s", derivative_mode(texture->mode)); 17327ec681f3Smrg else 17337ec681f3Smrg fprintf(fp, "%s", texture_mode(texture->mode)); 17347ec681f3Smrg 17357ec681f3Smrg /* Specific format in question */ 17367ec681f3Smrg print_texture_format(fp, texture->format); 17377ec681f3Smrg 17387ec681f3Smrg /* Instruction "modifiers" parallel the ALU instructions. */ 17397ec681f3Smrg 17407ec681f3Smrg if (texture->cont) 17417ec681f3Smrg fprintf(fp, ".cont"); 17427ec681f3Smrg 17437ec681f3Smrg if (texture->last) 17447ec681f3Smrg fprintf(fp, ".last"); 17457ec681f3Smrg 17467ec681f3Smrg if (texture->out_of_order) 17477ec681f3Smrg fprintf(fp, ".ooo%u", texture->out_of_order); 17487ec681f3Smrg 17497ec681f3Smrg fprintf(fp, " "); 17507ec681f3Smrg print_tex_reg(fp, out_reg_base + texture->out_reg_select, true); 17517ec681f3Smrg print_tex_mask(fp, texture->mask, texture->out_upper); 17527ec681f3Smrg fprintf(fp, ".%c%d", texture->sampler_type == MALI_SAMPLER_FLOAT ? 'f' : 'i', 17537ec681f3Smrg texture->out_full ? 32 : 16); 17547ec681f3Smrg assert(!(texture->out_full && texture->out_upper)); 17557ec681f3Smrg 17567ec681f3Smrg /* Output modifiers are only valid for float texture operations */ 17577ec681f3Smrg if (texture->sampler_type == MALI_SAMPLER_FLOAT) 17587ec681f3Smrg print_outmod(fp, texture->outmod, false); 17597ec681f3Smrg 17607ec681f3Smrg fprintf(fp, ", "); 17617ec681f3Smrg 17627ec681f3Smrg /* Depending on whether we read from textures directly or indirectly, 17637ec681f3Smrg * we may be able to update our analysis */ 17647ec681f3Smrg 17657ec681f3Smrg if (texture->texture_register) { 17667ec681f3Smrg fprintf(fp, "texture["); 17677ec681f3Smrg print_texture_reg_select(fp, texture->texture_handle, in_reg_base); 17687ec681f3Smrg fprintf(fp, "], "); 17697ec681f3Smrg 17707ec681f3Smrg /* Indirect, tut tut */ 17717ec681f3Smrg ctx->midg_stats.texture_count = -16; 17727ec681f3Smrg } else { 17737ec681f3Smrg fprintf(fp, "texture%u, ", texture->texture_handle); 17747ec681f3Smrg update_stats(&ctx->midg_stats.texture_count, texture->texture_handle); 17757ec681f3Smrg } 17767ec681f3Smrg 17777ec681f3Smrg /* Print the type, GL style */ 17787ec681f3Smrg fprintf(fp, "%csampler", sampler_type_name(texture->sampler_type)); 17797ec681f3Smrg 17807ec681f3Smrg if (texture->sampler_register) { 17817ec681f3Smrg fprintf(fp, "["); 17827ec681f3Smrg print_texture_reg_select(fp, texture->sampler_handle, in_reg_base); 17837ec681f3Smrg fprintf(fp, "]"); 17847ec681f3Smrg 17857ec681f3Smrg ctx->midg_stats.sampler_count = -16; 17867ec681f3Smrg } else { 17877ec681f3Smrg fprintf(fp, "%u", texture->sampler_handle); 17887ec681f3Smrg update_stats(&ctx->midg_stats.sampler_count, texture->sampler_handle); 17897ec681f3Smrg } 17907ec681f3Smrg 17917ec681f3Smrg print_vec_swizzle(fp, texture->swizzle, midgard_src_passthrough, midgard_reg_mode_32, 0xFF); 17927ec681f3Smrg 17937ec681f3Smrg fprintf(fp, ", "); 17947ec681f3Smrg 17957ec681f3Smrg midgard_src_expand_mode exp = 17967ec681f3Smrg texture->in_reg_upper ? midgard_src_expand_high : midgard_src_passthrough; 17977ec681f3Smrg print_tex_reg(fp, in_reg_base + texture->in_reg_select, false); 17987ec681f3Smrg print_vec_swizzle(fp, texture->in_reg_swizzle, exp, midgard_reg_mode_32, 0xFF); 17997ec681f3Smrg fprintf(fp, ".%d", texture->in_reg_full ? 32 : 16); 18007ec681f3Smrg assert(!(texture->in_reg_full && texture->in_reg_upper)); 18017ec681f3Smrg 18027ec681f3Smrg /* There is *always* an offset attached. Of 18037ec681f3Smrg * course, that offset is just immediate #0 for a 18047ec681f3Smrg * GLES call that doesn't take an offset. If there 18057ec681f3Smrg * is a non-negative non-zero offset, this is 18067ec681f3Smrg * specified in immediate offset mode, with the 18077ec681f3Smrg * values in the offset_* fields as immediates. If 18087ec681f3Smrg * this is a negative offset, we instead switch to 18097ec681f3Smrg * a register offset mode, where the offset_* 18107ec681f3Smrg * fields become register triplets */ 18117ec681f3Smrg 18127ec681f3Smrg if (texture->offset_register) { 18137ec681f3Smrg fprintf(fp, " + "); 18147ec681f3Smrg 18157ec681f3Smrg bool full = texture->offset & 1; 18167ec681f3Smrg bool select = texture->offset & 2; 18177ec681f3Smrg bool upper = texture->offset & 4; 18187ec681f3Smrg unsigned swizzle = texture->offset >> 3; 18197ec681f3Smrg midgard_src_expand_mode exp = 18207ec681f3Smrg upper ? midgard_src_expand_high : midgard_src_passthrough; 18217ec681f3Smrg 18227ec681f3Smrg print_tex_reg(fp, in_reg_base + select, false); 18237ec681f3Smrg print_vec_swizzle(fp, swizzle, exp, midgard_reg_mode_32, 0xFF); 18247ec681f3Smrg fprintf(fp, ".%d", full ? 32 : 16); 18257ec681f3Smrg assert(!(texture->out_full && texture->out_upper)); 18267ec681f3Smrg 18277ec681f3Smrg fprintf(fp, ", "); 18287ec681f3Smrg } else if (texture->offset) { 18297ec681f3Smrg /* Only select ops allow negative immediate offsets, verify */ 18307ec681f3Smrg 18317ec681f3Smrg signed offset_x = (texture->offset & 0xF); 18327ec681f3Smrg signed offset_y = ((texture->offset >> 4) & 0xF); 18337ec681f3Smrg signed offset_z = ((texture->offset >> 8) & 0xF); 18347ec681f3Smrg 18357ec681f3Smrg bool neg_x = offset_x < 0; 18367ec681f3Smrg bool neg_y = offset_y < 0; 18377ec681f3Smrg bool neg_z = offset_z < 0; 18387ec681f3Smrg bool any_neg = neg_x || neg_y || neg_z; 18397ec681f3Smrg 18407ec681f3Smrg if (any_neg && texture->op != midgard_tex_op_fetch) 18417ec681f3Smrg fprintf(fp, "/* invalid negative */ "); 18427ec681f3Smrg 18437ec681f3Smrg /* Regardless, just print the immediate offset */ 18447ec681f3Smrg 18457ec681f3Smrg fprintf(fp, " + <%d, %d, %d>, ", offset_x, offset_y, offset_z); 18467ec681f3Smrg } else { 18477ec681f3Smrg fprintf(fp, ", "); 18487ec681f3Smrg } 18497ec681f3Smrg 18507ec681f3Smrg char lod_operand = texture_op_takes_bias(texture->op) ? '+' : '='; 18517ec681f3Smrg 18527ec681f3Smrg if (texture->lod_register) { 18537ec681f3Smrg fprintf(fp, "lod %c ", lod_operand); 18547ec681f3Smrg print_texture_reg_select(fp, texture->bias, in_reg_base); 18557ec681f3Smrg fprintf(fp, ", "); 18567ec681f3Smrg 18577ec681f3Smrg if (texture->bias_int) 18587ec681f3Smrg fprintf(fp, " /* bias_int = 0x%X */", texture->bias_int); 18597ec681f3Smrg } else if (texture->op == midgard_tex_op_fetch) { 18607ec681f3Smrg /* For texel fetch, the int LOD is in the fractional place and 18617ec681f3Smrg * there is no fraction. We *always* have an explicit LOD, even 18627ec681f3Smrg * if it's zero. */ 18637ec681f3Smrg 18647ec681f3Smrg if (texture->bias_int) 18657ec681f3Smrg fprintf(fp, " /* bias_int = 0x%X */ ", texture->bias_int); 18667ec681f3Smrg 18677ec681f3Smrg fprintf(fp, "lod = %u, ", texture->bias); 18687ec681f3Smrg } else if (texture->bias || texture->bias_int) { 18697ec681f3Smrg signed bias_int = texture->bias_int; 18707ec681f3Smrg float bias_frac = texture->bias / 256.0f; 18717ec681f3Smrg float bias = bias_int + bias_frac; 18727ec681f3Smrg 18737ec681f3Smrg bool is_bias = texture_op_takes_bias(texture->op); 18747ec681f3Smrg char sign = (bias >= 0.0) ? '+' : '-'; 18757ec681f3Smrg char operand = is_bias ? sign : '='; 18767ec681f3Smrg 18777ec681f3Smrg fprintf(fp, "lod %c %f, ", operand, fabsf(bias)); 18787ec681f3Smrg } 18797ec681f3Smrg 18807ec681f3Smrg fprintf(fp, "\n"); 18817ec681f3Smrg 18827ec681f3Smrg /* While not zero in general, for these simple instructions the 18837ec681f3Smrg * following unknowns are zero, so we don't include them */ 18847ec681f3Smrg 18857ec681f3Smrg if (texture->unknown4 || 18867ec681f3Smrg texture->unknown8) { 18877ec681f3Smrg fprintf(fp, "// unknown4 = 0x%x\n", texture->unknown4); 18887ec681f3Smrg fprintf(fp, "// unknown8 = 0x%x\n", texture->unknown8); 18897ec681f3Smrg } 18907ec681f3Smrg 18917ec681f3Smrg ctx->midg_stats.instruction_count++; 18927ec681f3Smrg} 18937ec681f3Smrg 18947ec681f3Smrgstruct midgard_disasm_stats 18957ec681f3Smrgdisassemble_midgard(FILE *fp, uint8_t *code, size_t size, unsigned gpu_id, bool verbose) 18967ec681f3Smrg{ 18977ec681f3Smrg uint32_t *words = (uint32_t *) code; 18987ec681f3Smrg unsigned num_words = size / 4; 18997ec681f3Smrg int tabs = 0; 19007ec681f3Smrg 19017ec681f3Smrg bool branch_forward = false; 19027ec681f3Smrg 19037ec681f3Smrg int last_next_tag = -1; 19047ec681f3Smrg 19057ec681f3Smrg unsigned i = 0; 19067ec681f3Smrg 19077ec681f3Smrg disassemble_context ctx = { 19087ec681f3Smrg .midg_tags = calloc(sizeof(ctx.midg_tags[0]), num_words), 19097ec681f3Smrg .midg_stats = {0}, 19107ec681f3Smrg .midg_ever_written = 0, 19117ec681f3Smrg }; 19127ec681f3Smrg 19137ec681f3Smrg while (i < num_words) { 19147ec681f3Smrg unsigned tag = words[i] & 0xF; 19157ec681f3Smrg unsigned next_tag = (words[i] >> 4) & 0xF; 19167ec681f3Smrg unsigned num_quad_words = midgard_tag_props[tag].size; 19177ec681f3Smrg 19187ec681f3Smrg if (ctx.midg_tags[i] && ctx.midg_tags[i] != tag) { 19197ec681f3Smrg fprintf(fp, "\t/* XXX: TAG ERROR branch, got %s expected %s */\n", 19207ec681f3Smrg midgard_tag_props[tag].name, 19217ec681f3Smrg midgard_tag_props[ctx.midg_tags[i]].name); 19227ec681f3Smrg } 19237ec681f3Smrg 19247ec681f3Smrg ctx.midg_tags[i] = tag; 19257ec681f3Smrg 19267ec681f3Smrg /* Check the tag. The idea is to ensure that next_tag is 19277ec681f3Smrg * *always* recoverable from the disassembly, such that we may 19287ec681f3Smrg * safely omit printing next_tag. To show this, we first 19297ec681f3Smrg * consider that next tags are semantically off-byone -- we end 19307ec681f3Smrg * up parsing tag n during step n+1. So, we ensure after we're 19317ec681f3Smrg * done disassembling the next tag of the final bundle is BREAK 19327ec681f3Smrg * and warn otherwise. We also ensure that the next tag is 19337ec681f3Smrg * never INVALID. Beyond that, since the last tag is checked 19347ec681f3Smrg * outside the loop, we can check one tag prior. If equal to 19357ec681f3Smrg * the current tag (which is unique), we're done. Otherwise, we 19367ec681f3Smrg * print if that tag was > TAG_BREAK, which implies the tag was 19377ec681f3Smrg * not TAG_BREAK or TAG_INVALID. But we already checked for 19387ec681f3Smrg * TAG_INVALID, so it's just if the last tag was TAG_BREAK that 19397ec681f3Smrg * we're silent. So we throw in a print for break-next on at 19407ec681f3Smrg * the end of the bundle (if it's not the final bundle, which 19417ec681f3Smrg * we already check for above), disambiguating this case as 19427ec681f3Smrg * well. Hence in all cases we are unambiguous, QED. */ 19437ec681f3Smrg 19447ec681f3Smrg if (next_tag == TAG_INVALID) 19457ec681f3Smrg fprintf(fp, "\t/* XXX: invalid next tag */\n"); 19467ec681f3Smrg 19477ec681f3Smrg if (last_next_tag > TAG_BREAK && last_next_tag != tag) { 19487ec681f3Smrg fprintf(fp, "\t/* XXX: TAG ERROR sequence, got %s expexted %s */\n", 19497ec681f3Smrg midgard_tag_props[tag].name, 19507ec681f3Smrg midgard_tag_props[last_next_tag].name); 19517ec681f3Smrg } 19527ec681f3Smrg 19537ec681f3Smrg last_next_tag = next_tag; 19547ec681f3Smrg 19557ec681f3Smrg /* Tags are unique in the following way: 19567ec681f3Smrg * 19577ec681f3Smrg * INVALID, BREAK, UNKNOWN_*: verbosely printed 19587ec681f3Smrg * TEXTURE_4_BARRIER: verified by barrier/!barrier op 19597ec681f3Smrg * TEXTURE_4_VTX: .vtx tag printed 19607ec681f3Smrg * TEXTURE_4: tetxure lack of barriers or .vtx 19617ec681f3Smrg * TAG_LOAD_STORE_4: only load/store 19627ec681f3Smrg * TAG_ALU_4/8/12/16: by number of instructions/constants 19637ec681f3Smrg * TAG_ALU_4_8/12/16_WRITEOUT: ^^ with .writeout tag 19647ec681f3Smrg */ 19657ec681f3Smrg 19667ec681f3Smrg switch (tag) { 19677ec681f3Smrg case TAG_TEXTURE_4_VTX ... TAG_TEXTURE_4_BARRIER: { 19687ec681f3Smrg bool interpipe_aliasing = 19697ec681f3Smrg midgard_get_quirks(gpu_id) & MIDGARD_INTERPIPE_REG_ALIASING; 19707ec681f3Smrg 19717ec681f3Smrg print_texture_word(&ctx, fp, &words[i], tabs, 19727ec681f3Smrg interpipe_aliasing ? 0 : REG_TEX_BASE, 19737ec681f3Smrg interpipe_aliasing ? REGISTER_LDST_BASE : REG_TEX_BASE); 19747ec681f3Smrg break; 19757ec681f3Smrg } 19767ec681f3Smrg 19777ec681f3Smrg case TAG_LOAD_STORE_4: 19787ec681f3Smrg print_load_store_word(&ctx, fp, &words[i], verbose); 19797ec681f3Smrg break; 19807ec681f3Smrg 19817ec681f3Smrg case TAG_ALU_4 ... TAG_ALU_16_WRITEOUT: 19827ec681f3Smrg branch_forward = print_alu_word(&ctx, fp, &words[i], num_quad_words, tabs, i + 4*num_quad_words, verbose); 19837ec681f3Smrg 19847ec681f3Smrg /* TODO: infer/verify me */ 19857ec681f3Smrg if (tag >= TAG_ALU_4_WRITEOUT) 19867ec681f3Smrg fprintf(fp, "writeout\n"); 19877ec681f3Smrg 19887ec681f3Smrg break; 19897ec681f3Smrg 19907ec681f3Smrg default: 19917ec681f3Smrg fprintf(fp, "Unknown word type %u:\n", words[i] & 0xF); 19927ec681f3Smrg num_quad_words = 1; 19937ec681f3Smrg print_quad_word(fp, &words[i], tabs); 19947ec681f3Smrg fprintf(fp, "\n"); 19957ec681f3Smrg break; 19967ec681f3Smrg } 19977ec681f3Smrg 19987ec681f3Smrg /* We are parsing per bundle anyway. Add before we start 19997ec681f3Smrg * breaking out so we don't miss the final bundle. */ 20007ec681f3Smrg 20017ec681f3Smrg ctx.midg_stats.bundle_count++; 20027ec681f3Smrg ctx.midg_stats.quadword_count += num_quad_words; 20037ec681f3Smrg 20047ec681f3Smrg /* Include a synthetic "break" instruction at the end of the 20057ec681f3Smrg * bundle to signify that if, absent a branch, the shader 20067ec681f3Smrg * execution will stop here. Stop disassembly at such a break 20077ec681f3Smrg * based on a heuristic */ 20087ec681f3Smrg 20097ec681f3Smrg if (next_tag == TAG_BREAK) { 20107ec681f3Smrg if (branch_forward) { 20117ec681f3Smrg fprintf(fp, "break\n"); 20127ec681f3Smrg } else { 20137ec681f3Smrg fprintf(fp, "\n"); 20147ec681f3Smrg break; 20157ec681f3Smrg } 20167ec681f3Smrg } 20177ec681f3Smrg 20187ec681f3Smrg fprintf(fp, "\n"); 20197ec681f3Smrg 20207ec681f3Smrg i += 4 * num_quad_words; 20217ec681f3Smrg } 20227ec681f3Smrg 20237ec681f3Smrg if (last_next_tag != TAG_BREAK) { 20247ec681f3Smrg fprintf(fp, "/* XXX: shader ended with tag %s */\n", 20257ec681f3Smrg midgard_tag_props[last_next_tag].name); 20267ec681f3Smrg } 20277ec681f3Smrg 20287ec681f3Smrg free(ctx.midg_tags); 20297ec681f3Smrg 20307ec681f3Smrg /* We computed work_count as max_work_registers, so add one to get the 20317ec681f3Smrg * count. If no work registers are written, you still have one work 20327ec681f3Smrg * reported, which is exactly what the hardware expects */ 20337ec681f3Smrg 20347ec681f3Smrg ctx.midg_stats.work_count++; 20357ec681f3Smrg 20367ec681f3Smrg return ctx.midg_stats; 20377ec681f3Smrg} 2038