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 *)&reg_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 *)&reg_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