mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-07 04:58:05 +02:00
pan/va: Generalize opcode/opcode2
Rather than opcode/opcode2 hardcoded, treat the opcode as a list of one or more subcodes. This implies modifying the disassembler to hold an arbitrary depth dict of dicts and recursively build the switch statements used to look up each level. Reviewed-by: Christoph Pillmayer <christoph.pillmayer@arm.com> Acked-by: Lorenzo Rossi <lorenzo.rossi@collabora.com> Acked-by: Eric R. Smith <eric.smith@collabora.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40199>
This commit is contained in:
parent
11f243205c
commit
614d07c986
5 changed files with 1017 additions and 405 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -315,9 +315,8 @@ def parse_asm(line):
|
|||
operands = operands[len(ins.immediates):]
|
||||
|
||||
# Encode the operation itself
|
||||
encoded |= (ins.opcode.value << ins.opcode.start)
|
||||
if ins.opcode2:
|
||||
encoded |= (ins.opcode2.value << ins.opcode2.start)
|
||||
for subcode in ins.opcode:
|
||||
encoded |= (subcode.value << subcode.start)
|
||||
|
||||
# Encode FAU page
|
||||
if fau.page:
|
||||
|
|
|
|||
|
|
@ -194,39 +194,42 @@ va_print_dest(FILE *fp, uint8_t dest, bool can_mask)
|
|||
% endfor
|
||||
</%def>
|
||||
|
||||
<%def name="recurse_subcodes(op_bucket)">
|
||||
%if op_bucket.instr:
|
||||
${print_instr(op_bucket.instr)}
|
||||
%else:
|
||||
opcode = (instr >> ${op_bucket.start}) & ${hex(op_bucket.mask)};
|
||||
switch (opcode) {
|
||||
%for op in op_bucket.children:
|
||||
case ${hex(op)}:
|
||||
{
|
||||
${recurse_subcodes(op_bucket.children[op])}
|
||||
break;
|
||||
}
|
||||
%endfor
|
||||
}
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
|
||||
void
|
||||
va_disasm_instr(FILE *fp, uint64_t instr)
|
||||
{
|
||||
unsigned primary_opc = (instr >> 48) & MASK(9);
|
||||
unsigned opcode;
|
||||
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].opcode2.start}) & ${hex(ops[0].opcode2.mask)};
|
||||
% endif
|
||||
% for op in ops:
|
||||
% if ambiguous:
|
||||
${recurse_subcodes(OPCODES)}
|
||||
}
|
||||
|
||||
if (secondary_opc == ${op.opcode2.value}) {
|
||||
% endif
|
||||
${print_instr(op)}
|
||||
% if ambiguous:
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
break;
|
||||
|
||||
% endif
|
||||
% endfor
|
||||
}
|
||||
static bool is_branch(uint64_t instr)
|
||||
{
|
||||
<% (exact, mask) = OPCODES.get_exact_mask("BRANCHZ") %>
|
||||
if ((instr & ${hex(mask)}) == ${hex(exact)})
|
||||
return true;
|
||||
<% (exact, mask) = OPCODES.get_exact_mask("BRANCHZI") %>
|
||||
if ((instr & ${hex(mask)}) == ${hex(exact)})
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -259,13 +262,8 @@ disassemble_valhall(FILE *fp, const void *code, size_t size, bool verbose)
|
|||
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)
|
||||
if (is_branch(instr))
|
||||
fprintf(fp, "\\n");
|
||||
}
|
||||
|
||||
|
|
@ -273,30 +271,47 @@ disassemble_valhall(FILE *fp, const void *code, size_t size, bool verbose)
|
|||
}
|
||||
"""
|
||||
|
||||
# Bucket by opcode for hierarchical disassembly
|
||||
OPCODE_BUCKETS = {}
|
||||
class OpBucket:
|
||||
def __init__(self):
|
||||
self.start = None
|
||||
self.mask = None
|
||||
self.instr = None
|
||||
self.children = {}
|
||||
|
||||
def insert(self, subcodes, ins):
|
||||
if len(subcodes) == 0:
|
||||
self.instr = ins
|
||||
else:
|
||||
sc = subcodes[0]
|
||||
assert(self.start is None or self.start == sc.start)
|
||||
assert(self.mask is None or self.mask == sc.mask)
|
||||
self.start = sc.start
|
||||
self.mask = sc.mask
|
||||
if sc.value not in self.children:
|
||||
self.children[sc.value] = OpBucket()
|
||||
self.children[sc.value].insert(subcodes[1:], ins)
|
||||
|
||||
def get_exact_mask(self, op_name, exact = 0, mask = 0):
|
||||
if self.instr:
|
||||
if self.instr.name == op_name:
|
||||
return (exact, mask)
|
||||
else:
|
||||
return ()
|
||||
else:
|
||||
for op in self.children:
|
||||
exact_mask = self.children[op].get_exact_mask(op_name,
|
||||
exact | (op << self.start),
|
||||
mask | (self.mask << self.start))
|
||||
if exact_mask:
|
||||
return exact_mask
|
||||
return ()
|
||||
|
||||
# Build opcode hierarchy:
|
||||
OPCODES = OpBucket()
|
||||
for ins in instructions:
|
||||
opc = ins.opcode.value
|
||||
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.value not in SECONDARY)
|
||||
SECONDARY[ins.opcode2.value] = ins
|
||||
OPCODES.insert(ins.opcode, ins)
|
||||
|
||||
try:
|
||||
print(Template(template).render(OPCODES = OPCODE_BUCKETS, IMMEDIATES = immediates, ENUMS = enums, typesize = typesize, safe_name = safe_name))
|
||||
print(Template(template).render(OPCODES = OPCODES, IMMEDIATES = immediates, ENUMS = enums, typesize = typesize, safe_name = safe_name))
|
||||
except:
|
||||
print(exceptions.text_error_template().render())
|
||||
|
|
|
|||
|
|
@ -147,9 +147,9 @@ valhall_opcodes[BI_NUM_OPCODES] = {
|
|||
|
||||
# Exact value to be ORed in to every opcode
|
||||
def exact_op(op):
|
||||
exact_op = (op.opcode.value << op.opcode.start)
|
||||
if op.opcode2:
|
||||
exact_op |= (op.opcode2.value << op.opcode2.start)
|
||||
exact_op = 0
|
||||
for subcode in op.opcode:
|
||||
exact_op |= (subcode.value << subcode.start)
|
||||
return exact_op
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -164,12 +164,11 @@ class Opcode:
|
|||
self.mask = mask
|
||||
|
||||
class Instruction:
|
||||
def __init__(self, name, opcode, opcode2, srcs = [], dests = [], immediates = [], modifiers = [], staging = None, unit = None):
|
||||
def __init__(self, name, opcode, srcs = [], dests = [], immediates = [], modifiers = [], staging = None, unit = None):
|
||||
self.name = name
|
||||
self.srcs = srcs
|
||||
self.dests = dests
|
||||
self.opcode = opcode
|
||||
self.opcode2 = opcode2
|
||||
self.immediates = immediates
|
||||
self.modifiers = modifiers
|
||||
self.staging = staging
|
||||
|
|
@ -180,7 +179,6 @@ class Instruction:
|
|||
self.message = unit not in ["FMA", "CVT", "SFU"]
|
||||
|
||||
assert(len(dests) == 0 or not staging)
|
||||
assert(not opcode2 or (opcode2.value & opcode2.mask) == opcode2.value)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
@ -226,20 +224,25 @@ def build_modifier(el):
|
|||
return Modifier(name, start, size, implied)
|
||||
|
||||
def build_opcode(el, name):
|
||||
op_arr = []
|
||||
opcode = el.find(name)
|
||||
if opcode is None:
|
||||
return None
|
||||
value = int(opcode.get('val'), base=0)
|
||||
start = int(opcode.get('start'))
|
||||
mask = int(opcode.get('mask'), base=0)
|
||||
return Opcode(value, start, mask)
|
||||
|
||||
for subcode in opcode:
|
||||
value = int(subcode.get('val'), base=0)
|
||||
start = int(subcode.get('start'))
|
||||
mask = int(subcode.get('mask'), base=0)
|
||||
assert((value & mask) == value)
|
||||
op_arr.append(Opcode(value, start, mask))
|
||||
|
||||
return op_arr
|
||||
|
||||
# Build a single instruction from XML and group based overrides
|
||||
def build_instr(el, overrides = {}):
|
||||
# Get overridables
|
||||
name = overrides.get('name') or el.attrib.get('name')
|
||||
opcode = overrides.get('opcode') or build_opcode(el, 'opcode')
|
||||
opcode2 = overrides.get('opcode2') or build_opcode(el, 'opcode2')
|
||||
unit = overrides.get('unit') or el.attrib.get('unit')
|
||||
|
||||
# Get explicit sources/dests
|
||||
|
|
@ -279,7 +282,7 @@ def build_instr(el, overrides = {}):
|
|||
elif mod.tag =='va_mod':
|
||||
modifiers.append(build_modifier(mod))
|
||||
|
||||
instr = Instruction(name, opcode, opcode2, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging, unit = unit)
|
||||
instr = Instruction(name, opcode, srcs = sources, dests = dests, immediates = imms, modifiers = modifiers, staging = staging, unit = unit)
|
||||
|
||||
instructions.append(instr)
|
||||
|
||||
|
|
@ -290,7 +293,6 @@ def build_group(el):
|
|||
build_instr(el, overrides = {
|
||||
'name': ins.attrib['name'],
|
||||
'opcode': build_opcode(ins, 'opcode'),
|
||||
'opcode2': build_opcode(ins, 'opcode2'),
|
||||
'unit': ins.attrib.get('unit'),
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue