blob: 2c2903a7729698dcdaa578c7e687200188124de4 [file] [log] [blame]
#encoding=utf-8
# Copyright (C) 2021 Collabora, Ltd.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import sys
from valhall import valhall_parse_isa
from mako.template import Template
from mako import exceptions
(instructions, immediates, enums, typesize, safe_name) = valhall_parse_isa()
template = """
#include "disassemble.h"
#define BIT(b) (1ull << (b))
#define MASK(count) ((1ull << (count)) - 1)
#define SEXT(b, count) ((b ^ BIT(count - 1)) - BIT(count - 1))
#define UNUSED __attribute__((unused))
#define VA_SRC_UNIFORM_TYPE 0x2
#define VA_SRC_IMM_TYPE 0x3
% for name, en in ENUMS.items():
UNUSED static const char *valhall_${name}[] = {
% for v in en.values:
"${"" if v.default else "." + v.value}",
% endfor
};
% endfor
static const uint32_t va_immediates[32] = {
% for imm in IMMEDIATES:
${hex(imm)},
% endfor
};
static inline void
va_print_src(FILE *fp, uint8_t src, unsigned fau_page)
{
unsigned type = (src >> 6);
unsigned value = (src & 0x3F);
if (type == VA_SRC_IMM_TYPE) {
if (value >= 32) {
if (fau_page == 0)
fputs(valhall_fau_special_page_0[(value - 0x20) >> 1] + 1, fp);
else if (fau_page == 1)
fputs(valhall_fau_special_page_1[(value - 0x20) >> 1] + 1, fp);
else if (fau_page == 3)
fputs(valhall_fau_special_page_3[(value - 0x20) >> 1] + 1, fp);
else
fprintf(fp, "reserved_page2");
fprintf(fp, ".w%u", value & 1);
} else {
fprintf(fp, "0x%X", va_immediates[value]);
}
} else if (type == VA_SRC_UNIFORM_TYPE) {
fprintf(fp, "u%u", value | (fau_page << 6));
} else {
bool discard = (type & 1);
fprintf(fp, "%sr%u", discard ? "^" : "", value);
}
}
static inline void
va_print_float_src(FILE *fp, uint8_t src, unsigned fau_page, bool neg, bool abs)
{
unsigned type = (src >> 6);
unsigned value = (src & 0x3F);
if (type == VA_SRC_IMM_TYPE) {
assert(value < 32 && "overflow in LUT");
fprintf(fp, "0x%X", va_immediates[value]);
} else {
va_print_src(fp, src, fau_page);
}
if (neg)
fprintf(fp, ".neg");
if (abs)
fprintf(fp, ".abs");
}
static inline void
va_print_dest(FILE *fp, uint8_t dest, bool can_mask)
{
unsigned mask = (dest >> 6);
unsigned value = (dest & 0x3F);
fprintf(fp, "r%u", value);
/* Should write at least one component */
// assert(mask != 0);
// assert(mask == 0x3 || can_mask);
if (mask != 0x3)
fprintf(fp, ".h%u", (mask == 1) ? 0 : 1);
}
void
va_disasm_instr(FILE *fp, uint64_t instr)
{
unsigned primary_opc = (instr >> 48) & MASK(9);
unsigned fau_page = (instr >> 57) & MASK(2);
unsigned secondary_opc = 0;
switch (primary_opc) {
% for bucket in OPCODES:
<%
ops = OPCODES[bucket]
ambiguous = (len(ops) > 1)
%>
% if len(ops) > 0:
case ${hex(bucket)}:
% if ambiguous:
secondary_opc = (instr >> ${ops[0].secondary_shift}) & ${hex(ops[0].secondary_mask)};
% endif
% for op in ops:
<% no_comma = True %>
% if ambiguous:
if (secondary_opc == ${op.opcode2}) {
% endif
fputs("${op.name}", fp);
% for mod in op.modifiers:
% if mod.name not in ["left", "memory_width", "descriptor_type", "staging_register_count", "staging_register_write_count"]:
% if mod.is_enum:
fputs(valhall_${safe_name(mod.enum)}[(instr >> ${mod.start}) & ${hex((1 << mod.size) - 1)}], fp);
% else:
if (instr & BIT(${mod.start})) fputs(".${mod.name}", fp);
% endif
% endif
% endfor
assert((instr & (1ull << 63)) == 0 /* reserved */);
fprintf(fp, "%s ", valhall_flow[instr >> 59]);
% if len(op.dests) > 0:
<% no_comma = False %>
va_print_dest(fp, (instr >> 40), true);
% endif
% for index, sr in enumerate(op.staging):
% if not no_comma:
fputs(", ", fp);
% endif
<%
no_comma = False
if sr.count != 0:
sr_count = sr.count
elif "staging_register_write_count" in [x.name for x in op.modifiers] and sr.write:
sr_count = "(((instr >> 36) & MASK(3)) + 1)"
elif "staging_register_count" in [x.name for x in op.modifiers]:
sr_count = "((instr >> 33) & MASK(3))"
else:
assert(0)
%>
// assert(((instr >> ${sr.start}) & 0xC0) == ${sr.encoded_flags});
fprintf(fp, "@");
for (unsigned i = 0; i < ${sr_count}; ++i) {
fprintf(fp, "%sr%u", (i == 0) ? "" : ":",
(uint32_t) (((instr >> ${sr.start}) & 0x3F) + i));
}
% endfor
% for i, src in enumerate(op.srcs):
% if not no_comma:
fputs(", ", fp);
% endif
<% no_comma = False %>
% if src.absneg:
va_print_float_src(fp, instr >> ${src.start}, fau_page,
instr & BIT(${src.offset['neg']}),
instr & BIT(${src.offset['abs']}));
% elif src.is_float:
va_print_float_src(fp, instr >> ${src.start}, fau_page, false, false);
% else:
va_print_src(fp, instr >> ${src.start}, fau_page);
% endif
% if src.swizzle:
% if src.size == 32:
fputs(valhall_widen[(instr >> ${src.offset['swizzle']}) & 3], fp);
% else:
fputs(valhall_swizzles_16_bit[(instr >> ${src.offset['swizzle']}) & 3], fp);
% endif
% endif
% if src.lanes:
fputs(valhall_lanes_8_bit[(instr >> ${src.offset['widen']}) & 0xF], fp);
% elif src.halfswizzle:
fputs(valhall_half_swizzles_8_bit[(instr >> ${src.offset['widen']}) & 0xF], fp);
% elif src.widen:
fputs(valhall_swizzles_${src.size}_bit[(instr >> ${src.offset['widen']}) & 0xF], fp);
% elif src.combine:
fputs(valhall_combine[(instr >> ${src.offset['combine']}) & 0x7], fp);
% endif
% if src.lane:
fputs(valhall_lane_${src.size}_bit[(instr >> ${src.lane}) & 0x3], fp);
% endif
% if 'not' in src.offset:
if (instr & BIT(${src.offset['not']})) fputs(".not", fp);
% endif
% endfor
% for imm in op.immediates:
<%
prefix = "#" if imm.name == "constant" else imm.name + ":"
fmt = "%d" if imm.signed else "0x%X"
%>
fprintf(fp, ", ${prefix}${fmt}", (uint32_t) ${"SEXT(" if imm.signed else ""}
((instr >> ${imm.start}) & MASK(${imm.size})) ${f", {imm.size})" if imm.signed else ""});
% endfor
% if ambiguous:
}
% endif
% endfor
break;
% endif
% endfor
}
}
void
disassemble_valhall(FILE *fp, const void *code, size_t size, bool verbose)
{
assert((size & 7) == 0);
const uint64_t *words = (const uint64_t *)code;
/* Segment into 8-byte instructions */
for (unsigned i = 0; i < (size / 8); ++i) {
uint64_t instr = words[i];
if (instr == 0) {
fprintf(fp, "\\n");
return;
}
if (verbose) {
/* Print byte pattern */
for (unsigned j = 0; j < 8; ++j)
fprintf(fp, "%02x ", (uint8_t)(instr >> (j * 8)));
fprintf(fp, " ");
} else {
/* Print whitespace */
fprintf(fp, " ");
}
va_disasm_instr(fp, instr);
fprintf(fp, "\\n");
/* Detect branches */
uint64_t opcode = (instr >> 48) & MASK(9);
bool branchz = (opcode == 0x1F);
bool branchzi = (opcode == 0x2F);
/* Separate blocks visually by inserting whitespace after branches */
if (branchz || branchzi)
fprintf(fp, "\\n");
}
fprintf(fp, "\\n");
}
"""
# Bucket by opcode for hierarchical disassembly
OPCODE_BUCKETS = {}
for ins in instructions:
opc = ins.opcode
OPCODE_BUCKETS[opc] = OPCODE_BUCKETS.get(opc, []) + [ins]
# Check that each bucket may be disambiguated
for op in OPCODE_BUCKETS:
bucket = OPCODE_BUCKETS[op]
# Nothing to disambiguate
if len(bucket) < 2:
continue
SECONDARY = {}
for ins in bucket:
# Number of sources determines opcode2 placement, must be consistent
assert(len(ins.srcs) == len(bucket[0].srcs))
# Must not repeat, else we're ambiguous
assert(ins.opcode2 not in SECONDARY)
SECONDARY[ins.opcode2] = ins
try:
print(Template(template).render(OPCODES = OPCODE_BUCKETS, IMMEDIATES = immediates, ENUMS = enums, typesize = typesize, safe_name = safe_name))
except:
print(exceptions.text_error_template().render())