mirror of https://gitlab.freedesktop.org/mesa/mesa
456 lines
16 KiB
Python
Executable File
456 lines
16 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright © 2020 Google, Inc.
|
|
#
|
|
# 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.
|
|
|
|
from mako.template import Template
|
|
from isa import ISA
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
class FieldDecode(object):
|
|
def __init__(self, name, map_expr):
|
|
self.name = name
|
|
self.map_expr = map_expr
|
|
|
|
def get_c_name(self):
|
|
return self.name.lower().replace('-', '_')
|
|
|
|
# State and helpers used by the template:
|
|
class State(object):
|
|
def __init__(self, isa):
|
|
self.isa = isa
|
|
|
|
def case_name(self, bitset, name):
|
|
return bitset.encode.case_prefix + name.upper().replace('.', '_').replace('-', '_').replace('#', '')
|
|
|
|
# Return a list of all <map> entries for a leaf bitset, with the child
|
|
# bitset overriding the parent bitset's entries. Because we can't resolve
|
|
# which <map>s are used until we resolve which overload is used, we
|
|
# generate code for encoding all of these and then at runtime select which
|
|
# one to call based on the display.
|
|
def decode_fields(self, bitset):
|
|
if bitset.get_root().decode is None:
|
|
return
|
|
|
|
seen_fields = set()
|
|
if bitset.encode is not None:
|
|
for name, expr in bitset.encode.maps.items():
|
|
seen_fields.add(name)
|
|
yield FieldDecode(name, expr)
|
|
|
|
if bitset.extends is not None:
|
|
for field in self.decode_fields(self.isa.bitsets[bitset.extends]):
|
|
if field.name not in seen_fields:
|
|
yield field
|
|
|
|
# A limited resolver for field type which doesn't properly account for
|
|
# overrides. In particular, if a field is defined differently in multiple
|
|
# different cases, this just blindly picks the last one.
|
|
#
|
|
# TODO to do this properly, I don't think there is an alternative than
|
|
# to emit code which evaluates the case.expr
|
|
def resolve_simple_field(self, bitset, name):
|
|
field = None
|
|
for case in bitset.cases:
|
|
if name in case.fields:
|
|
field = case.fields[name]
|
|
if field is not None:
|
|
return field
|
|
if bitset.extends is not None:
|
|
return self.resolve_simple_field(bitset.isa.bitsets[bitset.extends], name)
|
|
return None
|
|
|
|
template = """\
|
|
/* Copyright (C) 2020 Google, Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "${header}"
|
|
|
|
#include <stdint.h>
|
|
#include <util/bitset.h>
|
|
|
|
#define BITMASK_WORDS BITSET_WORDS(${isa.bitsize})
|
|
|
|
typedef struct {
|
|
BITSET_WORD bitset[BITMASK_WORDS];
|
|
} bitmask_t;
|
|
|
|
|
|
#define BITSET_FORMAT ${isa.format()}
|
|
#define BITSET_VALUE(v) ${isa.value()}
|
|
|
|
static inline void
|
|
next_instruction(bitmask_t *instr, BITSET_WORD *start)
|
|
{
|
|
%for i in range(0, int(isa.bitsize / 32)):
|
|
instr->bitset[${i}] = *(start + ${i});
|
|
%endfor
|
|
}
|
|
|
|
static inline uint64_t
|
|
bitmask_to_uint64_t(bitmask_t mask)
|
|
{
|
|
% if isa.bitsize <= 32:
|
|
return mask.bitset[0];
|
|
% else:
|
|
return ((uint64_t)mask.bitset[1] << 32) | mask.bitset[0];
|
|
% endif
|
|
}
|
|
|
|
static inline bitmask_t
|
|
uint64_t_to_bitmask(uint64_t val)
|
|
{
|
|
bitmask_t mask = {
|
|
.bitset[0] = val & 0xffffffff,
|
|
% if isa.bitsize > 32:
|
|
.bitset[1] = (val >> 32) & 0xffffffff,
|
|
% endif
|
|
};
|
|
|
|
return mask;
|
|
}
|
|
|
|
#include "isaspec_decode_decl.h"
|
|
|
|
static uint64_t
|
|
isa_decode_field(struct decode_scope *scope, const char *field_name);
|
|
|
|
/*
|
|
* enum tables, these don't have any link back to other tables so just
|
|
* dump them up front before the bitset tables
|
|
*/
|
|
|
|
%for name, enum in isa.enums.items():
|
|
static const struct isa_enum ${enum.get_c_name()} = {
|
|
.num_values = ${len(enum.values)},
|
|
.values = {
|
|
% for val, display in enum.values.items():
|
|
{ .val = ${val}, .display = "${display}" },
|
|
% endfor
|
|
},
|
|
};
|
|
%endfor
|
|
|
|
/*
|
|
* generated expression functions, can be linked from bitset tables, so
|
|
* also dump them up front
|
|
*/
|
|
|
|
%for name, expr in isa.expressions.items():
|
|
static uint64_t
|
|
${expr.get_c_name()}(struct decode_scope *scope)
|
|
{
|
|
% for fieldname in sorted(expr.fieldnames):
|
|
int64_t ${fieldname} = isa_decode_field(scope, "${fieldname}");
|
|
% endfor
|
|
return ${expr.expr};
|
|
}
|
|
%endfor
|
|
|
|
/* forward-declarations of bitset decode functions */
|
|
%for name, bitset in isa.all_bitsets():
|
|
% for df in s.decode_fields(bitset):
|
|
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val);
|
|
% endfor
|
|
static const struct isa_field_decode decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields[] = {
|
|
% for df in s.decode_fields(bitset):
|
|
{
|
|
.name = "${df.name}",
|
|
.decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()},
|
|
},
|
|
% endfor
|
|
};
|
|
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope);
|
|
%endfor
|
|
|
|
/*
|
|
* Forward-declarations (so we don't have to figure out which order to
|
|
* emit various tables when they have pointers to each other)
|
|
*/
|
|
|
|
%for name, bitset in isa.all_bitsets():
|
|
static const struct isa_bitset bitset_${bitset.get_c_name()}_gen_${bitset.gen_min};
|
|
%endfor
|
|
|
|
%for root_name, root in isa.roots.items():
|
|
static const struct isa_bitset *${root.get_c_name()}[];
|
|
%endfor
|
|
|
|
/*
|
|
* bitset tables:
|
|
*/
|
|
|
|
%for name, bitset in isa.all_bitsets():
|
|
% for case in bitset.cases:
|
|
% for field_name, field in case.fields.items():
|
|
% if field.get_c_typename() == 'TYPE_BITSET':
|
|
% if len(field.params) > 0:
|
|
static const struct isa_field_params ${case.get_c_name()}_gen_${bitset.gen_min}_${field.get_c_name()} = {
|
|
.num_params = ${len(field.params)},
|
|
.params = {
|
|
% for param in field.params:
|
|
{ .name= "${param[0]}", .as = "${param[1]}" },
|
|
% endfor
|
|
|
|
},
|
|
};
|
|
% endif
|
|
% endif
|
|
% endfor
|
|
static const struct isa_case ${case.get_c_name()}_gen_${bitset.gen_min} = {
|
|
% if case.expr is not None:
|
|
.expr = &${isa.expressions[case.expr].get_c_name()},
|
|
% endif
|
|
% if case.display is not None:
|
|
.display = "${case.display}",
|
|
% endif
|
|
.num_fields = ${len(case.fields)},
|
|
.fields = {
|
|
% for field_name, field in case.fields.items():
|
|
{ .name = "${field_name}", .low = ${field.low}, .high = ${field.high},
|
|
% if field.expr is not None:
|
|
.expr = &${isa.expressions[field.expr].get_c_name()},
|
|
% endif
|
|
% if field.display is not None:
|
|
.display = "${field.display}",
|
|
% endif
|
|
.type = ${field.get_c_typename()},
|
|
% if field.get_c_typename() == 'TYPE_BITSET':
|
|
.bitsets = ${isa.roots[field.type].get_c_name()},
|
|
% if len(field.params) > 0:
|
|
.params = &${case.get_c_name()}_gen_${bitset.gen_min}_${field.get_c_name()},
|
|
% endif
|
|
% endif
|
|
% if field.get_c_typename() == 'TYPE_ENUM':
|
|
.enums = &${isa.enums[field.type].get_c_name()},
|
|
% endif
|
|
% if field.get_c_typename() == 'TYPE_ASSERT':
|
|
.val.bitset = { ${', '.join(isa.split_bits(field.val, 32))} },
|
|
% endif
|
|
% if field.get_c_typename() == 'TYPE_BRANCH' or field.get_c_typename() == 'TYPE_ABSBRANCH':
|
|
.call = ${str(field.call).lower()},
|
|
% endif
|
|
},
|
|
% endfor
|
|
},
|
|
};
|
|
% endfor
|
|
static const struct isa_bitset bitset_${bitset.get_c_name()}_gen_${bitset.gen_min} = {
|
|
<% pattern = bitset.get_pattern() %>
|
|
% if bitset.extends is not None:
|
|
.parent = &bitset_${isa.bitsets[bitset.extends].get_c_name()}_gen_${isa.bitsets[bitset.extends].gen_min},
|
|
% endif
|
|
.name = "${bitset.display_name}",
|
|
.gen = {
|
|
.min = ${bitset.get_gen_min()},
|
|
.max = ${bitset.get_gen_max()},
|
|
},
|
|
.match.bitset = { ${', '.join(isa.split_bits(pattern.match, 32))} },
|
|
.dontcare.bitset = { ${', '.join(isa.split_bits(pattern.dontcare, 32))} },
|
|
.mask.bitset = { ${', '.join(isa.split_bits(pattern.mask, 32))} },
|
|
.decode = decode_${bitset.get_c_name()}_gen_${bitset.gen_min},
|
|
.num_decode_fields = ARRAY_SIZE(decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields),
|
|
.decode_fields = decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_fields,
|
|
.num_cases = ${len(bitset.cases)},
|
|
.cases = {
|
|
% for case in bitset.cases:
|
|
&${case.get_c_name()}_gen_${bitset.gen_min},
|
|
% endfor
|
|
},
|
|
};
|
|
%endfor
|
|
|
|
/*
|
|
* bitset hierarchy root tables (where decoding starts from):
|
|
*/
|
|
|
|
%for root_name, root in isa.roots.items():
|
|
static const struct isa_bitset *${root.get_c_name()}[] = {
|
|
% for leaf_name, leafs in isa.leafs.items():
|
|
% for leaf in leafs:
|
|
% if leaf.get_root() == root:
|
|
&bitset_${leaf.get_c_name()}_gen_${leaf.gen_min},
|
|
% endif
|
|
% endfor
|
|
% endfor
|
|
(void *)0
|
|
};
|
|
%endfor
|
|
|
|
#include "isaspec_decode_impl.c"
|
|
|
|
%for name, bitset in isa.all_bitsets():
|
|
% for df in s.decode_fields(bitset):
|
|
<% field = s.resolve_simple_field(bitset, df.name) %>
|
|
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}_${df.get_c_name()}(void *out, struct decode_scope *scope, uint64_t val)
|
|
{
|
|
% if bitset.get_root().decode is not None and field is not None:
|
|
${bitset.get_root().encode.type} src = *(${bitset.get_root().encode.type} *)out;
|
|
% if field.get_c_typename() == 'TYPE_BITSET':
|
|
isa_decode_bitset(&${df.map_expr}, ${isa.roots[field.type].get_c_name()}, scope, uint64_t_to_bitmask(val));
|
|
% elif field.get_c_typename() in ['TYPE_BRANCH', 'TYPE_INT', 'TYPE_OFFSET']:
|
|
${df.map_expr} = util_sign_extend(val, ${field.get_size()});
|
|
% else:
|
|
${df.map_expr} = val;
|
|
% endif
|
|
*(${bitset.get_root().encode.type} *)out = src;
|
|
% endif
|
|
}
|
|
|
|
% endfor
|
|
static void decode_${bitset.get_c_name()}_gen_${bitset.gen_min}(void *out, struct decode_scope *scope)
|
|
{
|
|
% if bitset.get_root().decode is not None:
|
|
UNUSED ${bitset.get_root().encode.type} src;
|
|
% if bitset.get_root().encode.type.endswith('*') and name in isa.leafs and bitset.get_root().encode.case_prefix is not None:
|
|
src = ${bitset.get_root().get_c_name()}_create(${s.case_name(bitset.get_root(), bitset.name)});
|
|
*(${bitset.get_root().encode.type} *)out = src;
|
|
% endif
|
|
% endif
|
|
}
|
|
%endfor
|
|
|
|
void ${prefix}_isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *options)
|
|
{
|
|
isa_disasm(bin, sz, out, options);
|
|
}
|
|
|
|
bool ${prefix}_isa_decode(void *out, void *bin, const struct isa_decode_options *options)
|
|
{
|
|
return isa_decode(out, bin, options);
|
|
}
|
|
|
|
uint32_t ${prefix}_isa_get_gpu_id(struct decode_scope *scope)
|
|
{
|
|
return isa_get_gpu_id(scope);
|
|
}
|
|
"""
|
|
|
|
header = """\
|
|
/* Copyright (C) 2020 Google, Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef _${guard}_
|
|
#define _${guard}_
|
|
|
|
#include "compiler/isaspec/isaspec.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
void ${prefix}_isa_disasm(void *bin, int sz, FILE *out, const struct isa_decode_options *options);
|
|
bool ${prefix}_isa_decode(void *out, void *bin, const struct isa_decode_options *options);
|
|
|
|
struct decode_scope;
|
|
|
|
uint32_t ${prefix}_isa_get_gpu_id(struct decode_scope *scope);
|
|
|
|
/**
|
|
* Allows to use gpu_id in expr functions
|
|
*/
|
|
#define ISA_GPU_ID() ${prefix}_isa_get_gpu_id(scope)
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* _${guard}_ */
|
|
|
|
"""
|
|
|
|
def guard(p):
|
|
return os.path.basename(p).upper().replace("-", "_").replace(".", "_")
|
|
|
|
def prefix(p):
|
|
return os.path.basename(p).lower().replace("-", "_").replace(".", "_").split('_')[0]
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--xml', required=True, help='isaspec XML file.')
|
|
parser.add_argument('--out-c', required=True, help='Output C file.')
|
|
parser.add_argument('--out-h', required=True, help='Output H file.')
|
|
args = parser.parse_args()
|
|
|
|
isa = ISA(args.xml)
|
|
s = State(isa)
|
|
|
|
try:
|
|
with open(args.out_c, 'w', encoding='utf-8') as f:
|
|
out_h_basename = os.path.basename(args.out_h)
|
|
f.write(Template(template).render(isa=isa, s=s, header=out_h_basename, prefix=prefix(args.out_h)))
|
|
|
|
with open(args.out_h, 'w', encoding='utf-8') as f:
|
|
f.write(Template(header).render(isa=isa, guard=guard(args.out_h), prefix=prefix(args.out_h)))
|
|
|
|
except Exception:
|
|
# In the event there's an error, this imports some helpers from mako
|
|
# to print a useful stack trace and prints it, then exits with
|
|
# status 1, if python is run with debug; otherwise it just raises
|
|
# the exception
|
|
import sys
|
|
from mako import exceptions
|
|
print(exceptions.text_error_template().render(), file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|