1#encoding=utf-8 2 3# Copyright (C) 2021 Collabora, Ltd. 4# 5# Permission is hereby granted, free of charge, to any person obtaining a 6# copy of this software and associated documentation files (the "Software"), 7# to deal in the Software without restriction, including without limitation 8# the rights to use, copy, modify, merge, publish, distribute, sublicense, 9# and/or sell copies of the Software, and to permit persons to whom the 10# Software is furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice (including the next 13# paragraph) shall be included in all copies or substantial portions of the 14# Software. 15# 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22# IN THE SOFTWARE. 23 24import sys 25from valhall import valhall_parse_isa 26from mako.template import Template 27from mako import exceptions 28 29(instructions, immediates, enums, typesize, safe_name) = valhall_parse_isa() 30 31template = """ 32#include "disassemble.h" 33 34#define BIT(b) (1ull << (b)) 35#define MASK(count) ((1ull << (count)) - 1) 36#define SEXT(b, count) ((b ^ BIT(count - 1)) - BIT(count - 1)) 37#define UNUSED __attribute__((unused)) 38 39#define VA_SRC_UNIFORM_TYPE 0x2 40#define VA_SRC_IMM_TYPE 0x3 41 42% for name, en in ENUMS.items(): 43UNUSED static const char *valhall_${name}[] = { 44% for v in en.values: 45 "${"" if v.default else "." + v.value}", 46% endfor 47}; 48 49% endfor 50static const uint32_t va_immediates[32] = { 51% for imm in IMMEDIATES: 52 ${hex(imm)}, 53% endfor 54}; 55 56static inline void 57va_print_src(FILE *fp, uint8_t src, unsigned fau_page) 58{ 59 unsigned type = (src >> 6); 60 unsigned value = (src & 0x3F); 61 62 if (type == VA_SRC_IMM_TYPE) { 63 if (value >= 32) { 64 if (fau_page == 0) 65 fputs(valhall_fau_special_page_0[(value - 0x20) >> 1] + 1, fp); 66 else if (fau_page == 1) 67 fputs(valhall_fau_special_page_1[(value - 0x20) >> 1] + 1, fp); 68 else if (fau_page == 3) 69 fputs(valhall_fau_special_page_3[(value - 0x20) >> 1] + 1, fp); 70 else 71 fprintf(fp, "reserved_page2"); 72 73 fprintf(fp, ".w%u", value & 1); 74 } else { 75 fprintf(fp, "0x%X", va_immediates[value]); 76 } 77 } else if (type == VA_SRC_UNIFORM_TYPE) { 78 fprintf(fp, "u%u", value | (fau_page << 6)); 79 } else { 80 bool discard = (type & 1); 81 fprintf(fp, "%sr%u", discard ? "^" : "", value); 82 } 83} 84 85static inline void 86va_print_float_src(FILE *fp, uint8_t src, unsigned fau_page, bool neg, bool abs) 87{ 88 unsigned type = (src >> 6); 89 unsigned value = (src & 0x3F); 90 91 if (type == VA_SRC_IMM_TYPE) { 92 assert(value < 32 && "overflow in LUT"); 93 fprintf(fp, "0x%X", va_immediates[value]); 94 } else { 95 va_print_src(fp, src, fau_page); 96 } 97 98 if (neg) 99 fprintf(fp, ".neg"); 100 101 if (abs) 102 fprintf(fp, ".abs"); 103} 104 105static inline void 106va_print_dest(FILE *fp, uint8_t dest, bool can_mask) 107{ 108 unsigned mask = (dest >> 6); 109 unsigned value = (dest & 0x3F); 110 fprintf(fp, "r%u", value); 111 112 /* Should write at least one component */ 113 // assert(mask != 0); 114 // assert(mask == 0x3 || can_mask); 115 116 if (mask != 0x3) 117 fprintf(fp, ".h%u", (mask == 1) ? 0 : 1); 118} 119 120void 121va_disasm_instr(FILE *fp, uint64_t instr) 122{ 123 unsigned primary_opc = (instr >> 48) & MASK(9); 124 unsigned fau_page = (instr >> 57) & MASK(2); 125 unsigned secondary_opc = 0; 126 127 switch (primary_opc) { 128% for bucket in OPCODES: 129 <% 130 ops = OPCODES[bucket] 131 ambiguous = (len(ops) > 1) 132 %> 133% if len(ops) > 0: 134 case ${hex(bucket)}: 135% if ambiguous: 136 secondary_opc = (instr >> ${ops[0].secondary_shift}) & ${hex(ops[0].secondary_mask)}; 137% endif 138% for op in ops: 139<% no_comma = True %> 140% if ambiguous: 141 142 if (secondary_opc == ${op.opcode2}) { 143% endif 144 fputs("${op.name}", fp); 145% for mod in op.modifiers: 146% if mod.name not in ["left", "memory_width", "descriptor_type", "staging_register_count", "staging_register_write_count"]: 147% if mod.is_enum: 148 fputs(valhall_${safe_name(mod.enum)}[(instr >> ${mod.start}) & ${hex((1 << mod.size) - 1)}], fp); 149% else: 150 if (instr & BIT(${mod.start})) fputs(".${mod.name}", fp); 151% endif 152% endif 153% endfor 154 assert((instr & (1ull << 63)) == 0 /* reserved */); 155 fprintf(fp, "%s ", valhall_flow[instr >> 59]); 156% if len(op.dests) > 0: 157<% no_comma = False %> 158 va_print_dest(fp, (instr >> 40), true); 159% endif 160% for index, sr in enumerate(op.staging): 161% if not no_comma: 162 fputs(", ", fp); 163% endif 164<% 165 no_comma = False 166 167 if sr.count != 0: 168 sr_count = sr.count 169 elif "staging_register_write_count" in [x.name for x in op.modifiers] and sr.write: 170 sr_count = "(((instr >> 36) & MASK(3)) + 1)" 171 elif "staging_register_count" in [x.name for x in op.modifiers]: 172 sr_count = "((instr >> 33) & MASK(3))" 173 else: 174 assert(0) 175%> 176// assert(((instr >> ${sr.start}) & 0xC0) == ${sr.encoded_flags}); 177 fprintf(fp, "@"); 178 for (unsigned i = 0; i < ${sr_count}; ++i) { 179 fprintf(fp, "%sr%u", (i == 0) ? "" : ":", 180 (uint32_t) (((instr >> ${sr.start}) & 0x3F) + i)); 181 } 182% endfor 183% for i, src in enumerate(op.srcs): 184% if not no_comma: 185 fputs(", ", fp); 186% endif 187<% no_comma = False %> 188% if src.absneg: 189 va_print_float_src(fp, instr >> ${src.start}, fau_page, 190 instr & BIT(${src.offset['neg']}), 191 instr & BIT(${src.offset['abs']})); 192% elif src.is_float: 193 va_print_float_src(fp, instr >> ${src.start}, fau_page, false, false); 194% else: 195 va_print_src(fp, instr >> ${src.start}, fau_page); 196% endif 197% if src.swizzle: 198% if src.size == 32: 199 fputs(valhall_widen[(instr >> ${src.offset['swizzle']}) & 3], fp); 200% else: 201 fputs(valhall_swizzles_16_bit[(instr >> ${src.offset['swizzle']}) & 3], fp); 202% endif 203% endif 204% if src.lanes: 205 fputs(valhall_lanes_8_bit[(instr >> ${src.offset['widen']}) & 0xF], fp); 206% elif src.halfswizzle: 207 fputs(valhall_half_swizzles_8_bit[(instr >> ${src.offset['widen']}) & 0xF], fp); 208% elif src.widen: 209 fputs(valhall_swizzles_${src.size}_bit[(instr >> ${src.offset['widen']}) & 0xF], fp); 210% elif src.combine: 211 fputs(valhall_combine[(instr >> ${src.offset['combine']}) & 0x7], fp); 212% endif 213% if src.lane: 214 fputs(valhall_lane_${src.size}_bit[(instr >> ${src.lane}) & 0x3], fp); 215% endif 216% if 'not' in src.offset: 217 if (instr & BIT(${src.offset['not']})) fputs(".not", fp); 218% endif 219% endfor 220% for imm in op.immediates: 221<% 222 prefix = "#" if imm.name == "constant" else imm.name + ":" 223 fmt = "%d" if imm.signed else "0x%X" 224%> 225 fprintf(fp, ", ${prefix}${fmt}", (uint32_t) ${"SEXT(" if imm.signed else ""} 226 ((instr >> ${imm.start}) & MASK(${imm.size})) ${f", {imm.size})" if imm.signed else ""}); 227% endfor 228% if ambiguous: 229 } 230% endif 231% endfor 232 break; 233 234% endif 235% endfor 236 } 237} 238 239void 240disassemble_valhall(FILE *fp, const void *code, size_t size, bool verbose) 241{ 242 assert((size & 7) == 0); 243 244 const uint64_t *words = (const uint64_t *)code; 245 246 /* Segment into 8-byte instructions */ 247 for (unsigned i = 0; i < (size / 8); ++i) { 248 uint64_t instr = words[i]; 249 250 if (instr == 0) { 251 fprintf(fp, "\\n"); 252 return; 253 } 254 255 if (verbose) { 256 /* Print byte pattern */ 257 for (unsigned j = 0; j < 8; ++j) 258 fprintf(fp, "%02x ", (uint8_t)(instr >> (j * 8))); 259 260 fprintf(fp, " "); 261 } else { 262 /* Print whitespace */ 263 fprintf(fp, " "); 264 } 265 266 va_disasm_instr(fp, instr); 267 fprintf(fp, "\\n"); 268 269 /* Detect branches */ 270 uint64_t opcode = (instr >> 48) & MASK(9); 271 bool branchz = (opcode == 0x1F); 272 bool branchzi = (opcode == 0x2F); 273 274 /* Separate blocks visually by inserting whitespace after branches */ 275 if (branchz || branchzi) 276 fprintf(fp, "\\n"); 277 } 278 279 fprintf(fp, "\\n"); 280} 281""" 282 283# Bucket by opcode for hierarchical disassembly 284OPCODE_BUCKETS = {} 285for ins in instructions: 286 opc = ins.opcode 287 OPCODE_BUCKETS[opc] = OPCODE_BUCKETS.get(opc, []) + [ins] 288 289# Check that each bucket may be disambiguated 290for op in OPCODE_BUCKETS: 291 bucket = OPCODE_BUCKETS[op] 292 293 # Nothing to disambiguate 294 if len(bucket) < 2: 295 continue 296 297 SECONDARY = {} 298 for ins in bucket: 299 # Number of sources determines opcode2 placement, must be consistent 300 assert(len(ins.srcs) == len(bucket[0].srcs)) 301 302 # Must not repeat, else we're ambiguous 303 assert(ins.opcode2 not in SECONDARY) 304 SECONDARY[ins.opcode2] = ins 305 306try: 307 print(Template(template).render(OPCODES = OPCODE_BUCKETS, IMMEDIATES = immediates, ENUMS = enums, typesize = typesize, safe_name = safe_name)) 308except: 309 print(exceptions.text_error_template().render()) 310