mirror of
https://github.com/RPCSX/xed.git
synced 2024-11-23 11:39:40 +00:00
e7d734962c
Change-Id: I166833daaa56c33eca01bdf7b9aa6e74a490ba9a (cherry picked from commit 1212ba962dff6dfbfa0bd2469327ff447ce59058)
796 lines
29 KiB
Python
Executable File
796 lines
29 KiB
Python
Executable File
#BEGIN_LEGAL
|
|
#
|
|
#Copyright (c) 2017 Intel Corporation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
#END_LEGAL
|
|
|
|
import genutil
|
|
import ildutil
|
|
import ild_nt
|
|
import ild_cdict
|
|
import mbuild
|
|
import codegen
|
|
import ild_phash
|
|
import os
|
|
import ild_codegen
|
|
import operand_storage
|
|
import verbosity
|
|
import tup2int
|
|
|
|
_xed3_ops_type = 'xed3_operands_struct_t*'
|
|
_xed3_ops_header = 'xed3-operands-struct.h'
|
|
_key_ctype = 'xed_uint32_t'
|
|
_xed3_err_op = 'error'
|
|
_xed3_gen_error = 'XED_ERROR_GENERAL_ERROR'
|
|
_xed_reg_error_val = 'XED_ERROR_BAD_REGISTER'
|
|
_xed_no_err_val = 'XED_ERROR_NONE'
|
|
_xed_op_type = 'xed_operand_values_t'
|
|
_xed3_opstruct_type = 'xed3_operands_struct_t'
|
|
|
|
def _vlog(f,s):
|
|
if verbosity.vcapture():
|
|
f.write(s)
|
|
|
|
#FIXME: this is not used currently, but it would be nice
|
|
#to have a special error enum with more specific error reasons
|
|
#instead of just XED_ERROR_GENERAL_ERROR
|
|
#for example for each NT we can create a separate error value
|
|
def _nt_2_xed3_err_enum(nt_name):
|
|
return 'XED3_ERROR_%s' % nt_name.upper()
|
|
|
|
#This is not used currently.
|
|
#I was going to use it for decoding the operands: for each
|
|
#operand enum value call the corresponding NT function,
|
|
#but it turned out to be better to generate chain capture functions
|
|
#for each operands combination
|
|
def dump_nt_enum_2_capture_fptr(agi, fname):
|
|
"""Dump mapping nt_enum -> nt_capture_fptr
|
|
"""
|
|
xeddir = os.path.abspath(agi.common.options.xeddir)
|
|
gendir = mbuild.join(agi.common.options.gendir,'include-private')
|
|
|
|
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
|
|
fname, shell_file=False,
|
|
is_private=True)
|
|
h_file.add_header('xed-lookup-functions.h')
|
|
h_file.add_header(_xed3_nt_capture_header)
|
|
h_file.start()
|
|
lu_name = 'xed3_nt_2_capture'
|
|
|
|
xed3_capture_f_t = 'xed3_capture_function_t'
|
|
|
|
fptr_typedef = 'typedef void(*%s)(%s*);' % (xed3_capture_f_t,
|
|
ildutil.xed3_decoded_inst_t)
|
|
|
|
fptr_typedef = 'typedef void(*%s)(xed_decoded_inst_t*);' % xed3_capture_f_t
|
|
|
|
h_file.add_code(fptr_typedef)
|
|
|
|
h_file.add_code(('static %s ' % xed3_capture_f_t) +\
|
|
'%s[XED_NONTERMINAL_LAST] = {' % lu_name)
|
|
nonterminals = list(agi.nonterminal_dict.keys())
|
|
|
|
invalid_line = '/*XED_NONTERMINAL_INVALID*/ (%s)0,' % xed3_capture_f_t
|
|
h_file.add_code(invalid_line)
|
|
for nt_name in list(agi.xed3_nt_enum_val_map.values()):
|
|
enum_val = 'XED_NONTERMINAL_%s' % nt_name.upper()
|
|
if _skip_nt(nt_name):
|
|
fn = '0'
|
|
else:
|
|
fn = get_xed3_nt_capture_fn(nt_name)
|
|
h_file.add_code('/*%s*/ (%s)%s,'% (enum_val, xed3_capture_f_t, fn))
|
|
h_file.add_code('};')
|
|
h_file.close()
|
|
|
|
|
|
def get_ii_constraints(ii, state_space, constraints):
|
|
"""
|
|
sets constraints[xed_operand_name][xed_operand_val] = True
|
|
|
|
xed_operandname and xed_operand_val correspond to operands
|
|
encountered in ii (both operand deciders and constant prebindings)
|
|
"""
|
|
#set constraints that come from operands deciders
|
|
ild_nt.add_op_deciders(ii.ipattern, state_space, constraints)
|
|
#set constraints that come from prebindings
|
|
for name,binding in list(ii.prebindings.items()):
|
|
if binding.is_constant():
|
|
if name not in constraints:
|
|
constraints[name] = {}
|
|
val = int(binding.get_value(), 2)
|
|
constraints[name][val] = True
|
|
|
|
def _get_all_cnames(gi):
|
|
"""
|
|
Returns a set of all constraints used by a given gi
|
|
(generator_info - represents a single NT)
|
|
"""
|
|
cnames = []
|
|
for rule in gi.parser_output.instructions:
|
|
cnames.extend(list(rule.xed3_constraints.keys()))
|
|
return set(cnames)
|
|
|
|
def _gen_cdict(agi, nt_name, all_state_space):
|
|
"""
|
|
Creates a ild_cdict.constraint_dict_t corresponding to NT
|
|
defined by gi.
|
|
"""
|
|
gi = agi.generator_dict[nt_name]
|
|
options = agi.common.options
|
|
|
|
state_space = {}
|
|
for opname in all_state_space:
|
|
state_space[opname] = list(all_state_space[opname].keys())
|
|
|
|
|
|
cdict_list = []
|
|
|
|
for rule in gi.parser_output.instructions:
|
|
rule.xed3_constraints = {}
|
|
get_ii_constraints(rule, state_space, rule.xed3_constraints)
|
|
|
|
cnames = _get_all_cnames(gi)
|
|
|
|
for rule in gi.parser_output.instructions:
|
|
cdict = ild_cdict.constraint_dict_t(
|
|
cnames,
|
|
rule.xed3_constraints,
|
|
all_state_space,
|
|
rule)
|
|
cdict_list.append(cdict)
|
|
msg = "cdict conflict in NT %s\n" % nt_name
|
|
united_dict = ild_cdict.constraint_dict_t.unite_dicts(
|
|
cdict_list,
|
|
msg,
|
|
cnames)
|
|
return united_dict
|
|
|
|
|
|
def get_xed3_member_name(xed2_opname):
|
|
"""
|
|
This is not used currently.
|
|
When we have a struct for operands instead of _operands array
|
|
this can be used to get struct member name from operand
|
|
"""
|
|
return xed2_opname.lower()
|
|
|
|
_xed3_capture_fn_pfx = 'xed3_capture'
|
|
|
|
def get_xed3_nt_capture_fn(nt_name):
|
|
"""
|
|
Return a xed3 capture function name for a given NT name.
|
|
"""
|
|
return '%s_nt_%s' % (_xed3_capture_fn_pfx, nt_name)
|
|
|
|
def get_xed3_capture_chain_fn(nt_names, is_ntluf=False):
|
|
"""
|
|
Return a xed3 chain capture function name from a given list of
|
|
NT names.
|
|
is_ntluf==True for operands chain functions.
|
|
"""
|
|
suffix = '_'.join(nt_names)
|
|
if is_ntluf:
|
|
suffix = 'ntluf_%s' % suffix
|
|
return '%s_chain_%s' % (_xed3_capture_fn_pfx, suffix)
|
|
|
|
def _add_cgen_key_lines(fo,
|
|
nt_name,
|
|
gi,
|
|
all_ops_widths,
|
|
key_str='key',
|
|
inst='d'):
|
|
"""
|
|
Add C code to compute the key from constraints' values.
|
|
"""
|
|
fo.add_code_eol('%s %s = 0' % (_key_ctype, key_str))
|
|
cdict = gi.xed3_cdict
|
|
bit_shift = 0
|
|
for i,cname in enumerate(cdict.cnames):
|
|
#eosz_set=True indicates that current value of EOSZ is correct
|
|
#in the _operands array and we can take it from there.
|
|
#Otherwise we would have to use special eosz computing functions
|
|
#the same way as we do in ILD.
|
|
#eosz_set=True here because we are doing dynamic decoding
|
|
#and have processed the NTs that come before the current NT.
|
|
access_str = ild_codegen.emit_ild_access_call(cname, inst,
|
|
eoasz_set=True)
|
|
|
|
#constraints might have 1,2 or 3 bit widths
|
|
#and we allocate bits in the key vector appropriately
|
|
#e.g REXB operand gets only 1 bit in the key
|
|
#and RM gets 3 bits
|
|
shift_val = ('(%s)' % bit_shift)
|
|
bit_shift += all_ops_widths[cname]
|
|
fo.add_code_eol('%s += (%s) << (%s)' % (key_str,access_str, shift_val))
|
|
|
|
def _get_pattern_nts(rule):
|
|
"""
|
|
Return a list of NT names present in given rule.
|
|
"""
|
|
nt_names = []
|
|
for bt in rule.ipattern.bits:
|
|
if bt.is_nonterminal():
|
|
nt_name = bt.nonterminal_name()
|
|
nt_names.append(nt_name)
|
|
return nt_names
|
|
|
|
def _is_error_rule(rule):
|
|
for op in rule.operands:
|
|
if op.type == 'error':
|
|
return True
|
|
return False
|
|
|
|
def _add_capture_nt_call(fo, nt_name, inst='d', indent=0):
|
|
capture_fn = get_xed3_nt_capture_fn(nt_name)
|
|
indent = ' ' * indent
|
|
fo.add_code_eol('%s%s(%s)' % (indent, capture_fn, inst))
|
|
|
|
def _add_op_assign_stmt(fo, opname, opval, inst='d', op=None,
|
|
indent=0):
|
|
if op:
|
|
fo.add_code('/* op.type=%s */' % op.type)
|
|
setter_fn = operand_storage.get_op_setter_fn(opname)
|
|
set_stmt = '%s(%s, %s)' %(setter_fn, inst, opval)
|
|
indentstr = ' ' * indent
|
|
fo.add_code_eol(indentstr + set_stmt)
|
|
|
|
def _is_reg_error_op(op):
|
|
return op.bits in ['XED_REG_ERROR']
|
|
|
|
def _add_nt_rhs_assignments(fo, nt_name, gi, rule, inst='d'):
|
|
#fo.add_code("/* %s */" % rule)
|
|
|
|
#first if it's error, we set general_error and quit
|
|
if _is_error_rule(rule):
|
|
_add_op_assign_stmt(fo, _xed3_err_op, _xed3_gen_error,
|
|
inst, indent=1)
|
|
return
|
|
|
|
#now check if there are NT calls in pattern, we need to call them first
|
|
pattern_nts = _get_pattern_nts(rule)
|
|
for nt_name in pattern_nts:
|
|
_add_capture_nt_call(fo, nt_name, inst, indent=1)
|
|
|
|
#now let's do the RHS - for each operand assign value
|
|
#FIXME: if we assign ERROR_REG or INVALID_REG set also error?
|
|
for op in rule.operands:
|
|
if op.name == 'ENCODER_PREFERRED':
|
|
#skip encoder preferred
|
|
continue
|
|
if op.type == 'imm':
|
|
#skip prebindings
|
|
continue
|
|
if op.type == 'nt_lookup_fn':
|
|
#NT as RHS, we call its capturing function
|
|
#and then assign op.name to OUTREG
|
|
_add_capture_nt_call(fo, op.lookupfn_name, inst, indent=1)
|
|
#now copy the outreg to op.name (unless it is outreg too!)
|
|
if op.name != 'OUTREG':
|
|
getter_fn = operand_storage.get_op_getter_fn('outreg')
|
|
outreg_expr = '%s(%s)' % (getter_fn, inst)
|
|
_add_op_assign_stmt(fo, op.name, outreg_expr, inst, indent=1)
|
|
|
|
else: #assignment of an operand to a constant
|
|
_add_op_assign_stmt(fo, op.name, op.bits, inst, indent=1)
|
|
if _is_reg_error_op(op):
|
|
_add_op_assign_stmt(fo, _xed3_err_op,
|
|
_xed_reg_error_val, inst, indent=1)
|
|
fo.add_code('/*pacify the compiler */')
|
|
fo.add_code_eol('(void)%s' % inst)
|
|
|
|
def _add_case_lines(fo, nt_name, gi, rule, inst='d'):
|
|
_add_nt_rhs_assignments(fo, nt_name, gi, rule, inst=inst)
|
|
fo.add_code_eol(' break')
|
|
|
|
def _add_switchcase_lines(fo,
|
|
nt_name,
|
|
gi,
|
|
all_ops_widths,
|
|
key_str='key',
|
|
inst='d'):
|
|
cdict = gi.xed3_cdict
|
|
fo.add_code('switch(%s) {' %key_str)
|
|
|
|
int2key = {}
|
|
key2int = {}
|
|
for key in list(cdict.tuple2rule.keys()):
|
|
keyval = tup2int.tuple2int(key, cdict.cnames, all_ops_widths)
|
|
#This checks for a nasty conflict that should never happen:
|
|
#when two different tuple keys have the same integer value.
|
|
#This conflict can happen when bit widths of all constraints are
|
|
#bigger than 32 bit (key is uint32 currently).
|
|
#In general such error will be caught by C compiler when we try
|
|
#to build a key and shift more than 32 bits.
|
|
#Checking here too just to be sure.
|
|
#FIXME: add an assertion to constraint_dict_t constructor to check
|
|
#for that?
|
|
#FIXME: this doesn't really checks for integer overflow, because
|
|
#python autmatically extends int32 if it overflows to int64.
|
|
#Need better checking.
|
|
if keyval in int2key:
|
|
msg = []
|
|
msg.append('CDICT TUPLE VALUE CONFLICT in nt %s !!!!' % nt_name)
|
|
msg.append('keyval %s' % keyval)
|
|
msg.append('key1 %s, key2 %s' % (key, int2key[keyval]))
|
|
msg.append('cdict %s')
|
|
msg = '\n'.join(msg)
|
|
ildutil.ild_err(msg)
|
|
int2key[keyval] = key
|
|
key2int[key] = keyval
|
|
|
|
covered_rules = set()
|
|
|
|
#we want cases sorted by value - prettier
|
|
for keyval in sorted(int2key.keys()):
|
|
key = int2key[keyval]
|
|
rule = cdict.tuple2rule[key]
|
|
if rule in covered_rules:
|
|
continue
|
|
covered_rules.add(rule)
|
|
keys = cdict.get_all_keys_by_val(rule)
|
|
for key in keys:
|
|
#FIXME: move tuple2int to ild_cdict?
|
|
keyval = key2int[key]
|
|
fo.add_code('case %s: /*%s -> %s*/' %(keyval, key, rule))
|
|
_add_case_lines(fo, nt_name, gi, rule)
|
|
fo.add_code('default:')
|
|
if gi.parser_output.otherwise_ok:
|
|
fo.add_code('/* otherwise_ok */')
|
|
else:
|
|
#FIXME: temporary using general error, later
|
|
#define more specific error enum
|
|
#errval = _nt_2_xed3_err_enum(nt_name)
|
|
errval = 'XED_ERROR_GENERAL_ERROR'
|
|
_add_op_assign_stmt(fo, _xed3_err_op, errval,
|
|
inst, indent=1)
|
|
fo.add_code_eol(' break')
|
|
fo.add_code('}')
|
|
|
|
|
|
def gen_capture_fo(agi, nt_name, all_ops_widths):
|
|
"""
|
|
Generate xed3 capturing function for a given NT name.
|
|
"""
|
|
gi = agi.generator_dict[nt_name]
|
|
cdict = gi.xed3_cdict
|
|
fname = get_xed3_nt_capture_fn(nt_name)
|
|
inst = 'd'
|
|
keystr = 'key'
|
|
fo = fo = codegen.function_object_t(fname,
|
|
return_type='void',
|
|
static=True,
|
|
inline=True)
|
|
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
|
|
if len(cdict.cnames) > 0:
|
|
_add_cgen_key_lines(fo, nt_name, gi, all_ops_widths, keystr, inst)
|
|
fo.add_code('/* now switch code..*/')
|
|
_add_switchcase_lines(fo, nt_name, gi, all_ops_widths, keystr, inst)
|
|
else:
|
|
rule = cdict.rule
|
|
_add_nt_rhs_assignments(fo, nt_name, gi, rule)
|
|
return fo
|
|
|
|
#FIXME: not used currently.
|
|
#Will be nice to use it and have different types for different members,
|
|
#especially for registers. 9debugging and overall tidy code)
|
|
def _gen_xed3_op_struct(agi, hfn):
|
|
"""
|
|
Dump xed3_oprands_struct_t definition
|
|
"""
|
|
xeddir = os.path.abspath(agi.common.options.xeddir)
|
|
gendir = mbuild.join(agi.common.options.gendir)
|
|
|
|
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
|
|
hfn, shell_file=False,
|
|
is_private=False)
|
|
h_file.add_header('xed-operand-storage.h')
|
|
h_file.start()
|
|
|
|
|
|
typedef_s = 'typedef struct xed3_operands_struct_s {'
|
|
h_file.add_code(typedef_s)
|
|
|
|
for op_name in agi.xed3_operand_names:
|
|
h_file.add_code('%s %s;'% (_xed_op_type, op_name.lower()))
|
|
h_file.add_code('} %s;' %_xed3_opstruct_type)
|
|
h_file.close()
|
|
|
|
def _get_op_nt_names_from_ii(ii):
|
|
nt_names = []
|
|
for op in ii.operands:
|
|
if op.type == 'nt_lookup_fn':
|
|
nt_names.append(op.name + '_' + op.lookupfn_name)
|
|
elif op.type == 'imm_const':
|
|
suffix = '_const%s' % op.bits
|
|
nt_names.append(op.name + suffix)
|
|
elif op.type == 'reg':
|
|
suffix = '_%s' % op.bits
|
|
nt_names.append(op.name + suffix)
|
|
return nt_names
|
|
|
|
def _get_nt_names_from_ii(ii):
|
|
"""
|
|
@param ii - instruction_info_t
|
|
@return list of NT names in ii's pattern
|
|
"""
|
|
nt_names = []
|
|
for bt in ii.ipattern.bits:
|
|
if bt.is_nonterminal():
|
|
name = bt.nonterminal_name()
|
|
if not name:
|
|
ildutil.ild_err('Failed to get NT name in %s for %s' % (ii,bt))
|
|
nt_names.append(name)
|
|
return nt_names
|
|
|
|
def _gen_ntluf_capture_chain_fo(nt_names, ii):
|
|
"""
|
|
Given a list of OP_NAME_NT_NAME strings(nt_names), generate a function
|
|
object (function_object_t)
|
|
that calls corresponding xed3 NT capturing functions.
|
|
Each such function captures everything that xed2 decode graph would
|
|
capture for a given pattern with operands that have nt_lokkupfns.
|
|
The difference between this function and _gen_capture_chain_fo
|
|
is that this function creates chain capturing functions for
|
|
operand decoding - assigns the REG[0,1] operands, etc.
|
|
"""
|
|
fname = get_xed3_capture_chain_fn(nt_names, is_ntluf=True)
|
|
inst = 'd'
|
|
fo = fo = codegen.function_object_t(fname,
|
|
return_type=_xed3_chain_return_t,
|
|
static=True,
|
|
inline=True)
|
|
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
|
|
|
|
for op in ii.operands:
|
|
if op.type == 'nt_lookup_fn':
|
|
nt_name = op.lookupfn_name
|
|
capture_fn = get_xed3_nt_capture_fn(nt_name)
|
|
capture_stmt = '%s(%s)' % (capture_fn, inst)
|
|
fo.add_code_eol(capture_stmt)
|
|
#if we have NTLUF functions, we need to assign OUTREG
|
|
getter_fn = operand_storage.get_op_getter_fn('outreg')
|
|
outreg_expr = '%s(%s)' % (getter_fn, inst)
|
|
fo.add_code('/*opname %s */' % op.name)
|
|
_add_op_assign_stmt(fo, op.name, outreg_expr, inst)
|
|
#now check if we have errors in current NT
|
|
#we don't need to check if there was a reg_error because
|
|
#we assign error operand inside the called nt_capture function
|
|
#if there was a reg_error
|
|
getter_fn = operand_storage.get_op_getter_fn(_xed3_err_op)
|
|
errval = '%s(%s)' % (getter_fn, inst)
|
|
fo.add_code('if (%s) {' % errval)
|
|
fo.add_code_eol('return %s' % errval)
|
|
fo.add_code('}')
|
|
elif op.type in ['imm_const', 'reg']:
|
|
opval = op.bits
|
|
_add_op_assign_stmt(fo, op.name, opval, inst)
|
|
|
|
|
|
fo.add_code_eol('return %s' % _xed_no_err_val)
|
|
return fo
|
|
|
|
def _gen_capture_chain_fo(nt_names, fname=None):
|
|
"""
|
|
Given a list of NT names, generate a function object (function_object_t)
|
|
that calls corresponding xed3 NT capturing functions.
|
|
Each such function captures everything that xed2 decode graph would
|
|
capture for a given pattern with NTs (nt_names) in it.
|
|
"""
|
|
if not fname:
|
|
fname = get_xed3_capture_chain_fn(nt_names)
|
|
inst = 'd'
|
|
fo = fo = codegen.function_object_t(fname,
|
|
return_type=_xed3_chain_return_t,
|
|
static=True,
|
|
inline=True)
|
|
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
|
|
|
|
for name in nt_names:
|
|
capture_fn = get_xed3_nt_capture_fn(name)
|
|
capture_stmt = '%s(%s)' % (capture_fn, inst)
|
|
fo.add_code_eol(capture_stmt)
|
|
#now check if we have errors in current NT
|
|
getter_fn = operand_storage.get_op_getter_fn(_xed3_err_op)
|
|
errval = '%s(%s)' % (getter_fn, inst)
|
|
fo.add_code('if (%s) {' % errval)
|
|
fo.add_code_eol('return %s' % errval)
|
|
fo.add_code('}')
|
|
|
|
fo.add_code_eol('return %s' % _xed_no_err_val)
|
|
return fo
|
|
|
|
_xed3_chain_header = 'xed3-chain-capture.h'
|
|
_xed3_op_chain_header = 'xed3-op-chain-capture.h'
|
|
_xed3_chain_lu_header = 'xed3-chain-capture-lu.h'
|
|
_xed3_op_chain_lu_header = 'xed3-op-chain-capture-lu.h'
|
|
_xed3_nt_capture_header = 'xed3-nt-capture.h'
|
|
_xed3_capture_lu_header = 'xed3-nt-capture-lu.h'
|
|
_xed3_empty_capture_func = 'xed3_capture_nt_nop'
|
|
_xed3_chain_return_t = 'xed_error_enum_t'
|
|
_xed3_dynamic_part1_header = 'xed3-dynamic-part1-capture.h'
|
|
|
|
|
|
def _gen_empty_capture_fo(is_ntluf=False):
|
|
"""
|
|
Generate capture function that does nothing.
|
|
For patterns without NTs.
|
|
"""
|
|
inst = 'd'
|
|
if is_ntluf:
|
|
fname = '%s_ntluf' % _xed3_empty_capture_func
|
|
else:
|
|
fname = _xed3_empty_capture_func
|
|
fo = fo = codegen.function_object_t(fname,
|
|
return_type=_xed3_chain_return_t,
|
|
static=True,
|
|
inline=True)
|
|
fo.add_arg(ildutil.xed3_decoded_inst_t + '* %s' % inst)
|
|
fo.add_code_eol('(void)%s' % inst)
|
|
fo.add_code_eol('return %s' % _xed_no_err_val)
|
|
return fo
|
|
|
|
def _dump_op_capture_chain_fo_lu(agi, patterns):
|
|
"""
|
|
Creates chain capturing functions for operands - for each pattern,
|
|
dumps those functions definitions, dumps a mapping
|
|
from inum(xed_inst_t index) to those functions.
|
|
"""
|
|
fn_2_fo = {}
|
|
inum_2_fn = {}
|
|
nop_fo = _gen_empty_capture_fo(is_ntluf=True)
|
|
fn_2_fo[nop_fo.function_name] = nop_fo
|
|
for ptrn in patterns:
|
|
ii = ptrn.ii
|
|
nt_names = _get_op_nt_names_from_ii(ii)
|
|
if len(nt_names) == 0:
|
|
#if no NTs we use empty capturing function
|
|
fn = nop_fo.function_name
|
|
else:
|
|
fn = get_xed3_capture_chain_fn(nt_names, is_ntluf=True)
|
|
if fn not in fn_2_fo:
|
|
fo = _gen_ntluf_capture_chain_fo(nt_names, ii)
|
|
fn_2_fo[fn] = fo
|
|
inum_2_fn[ii.inum] = (fn, ii.operands)
|
|
|
|
|
|
#dump chain functions
|
|
headers = [_xed3_nt_capture_header]
|
|
ild_codegen.dump_flist_2_header(agi,
|
|
_xed3_op_chain_header,
|
|
headers,
|
|
list(fn_2_fo.values()),
|
|
is_private=True)
|
|
|
|
lu_size = max(inum_2_fn.keys()) + 1
|
|
|
|
xeddir = os.path.abspath(agi.common.options.xeddir)
|
|
gendir = mbuild.join(agi.common.options.gendir,'include-private')
|
|
|
|
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
|
|
_xed3_op_chain_lu_header, shell_file=False,
|
|
is_private=True)
|
|
h_file.add_header(_xed3_op_chain_header)
|
|
h_file.start()
|
|
lu_name = 'xed3_op_chain_fptr_lu'
|
|
xed3_op_chain_f_t = 'xed3_op_chain_function_t'
|
|
|
|
fptr_typedef = 'typedef %s(*%s)(%s*);' % (_xed3_chain_return_t,
|
|
xed3_op_chain_f_t,
|
|
ildutil.xed3_decoded_inst_t)
|
|
|
|
h_file.add_code(fptr_typedef)
|
|
|
|
h_file.add_code(('static %s ' % xed3_op_chain_f_t) +\
|
|
'%s[%s] = {' % (lu_name, lu_size))
|
|
|
|
empty_line = '/*NO PATTERN*/ (%s)0,' % xed3_op_chain_f_t
|
|
|
|
for inum in range(0, lu_size):
|
|
if inum in inum_2_fn:
|
|
(fn, oplist) = inum_2_fn[inum]
|
|
op_str = '\n'.join([str(op) for op in oplist])
|
|
entry_str = '/*%s inum=%s*/ %s,' % (op_str,inum, fn)
|
|
else:
|
|
entry_str = empty_line
|
|
h_file.add_code(entry_str)
|
|
h_file.add_code('};')
|
|
h_file.close()
|
|
|
|
|
|
def _dump_capture_chain_fo_lu(agi, patterns):
|
|
"""
|
|
Creates chain capturing functions - for each pattern,
|
|
dumps those functions definitions, dumps a mapping
|
|
from inum(xed_inst_t index) to those functions.
|
|
"""
|
|
fn_2_fo = {}
|
|
inum_2_fn = {}
|
|
|
|
nop_fo = _gen_empty_capture_fo()
|
|
fn_2_fo[nop_fo.function_name] = nop_fo
|
|
for ptrn in patterns:
|
|
ii = ptrn.ii
|
|
nt_names = _get_nt_names_from_ii(ii)
|
|
if len(nt_names) == 0:
|
|
#if no NTs we use empty capturing function
|
|
fn = nop_fo.function_name
|
|
else:
|
|
fn = get_xed3_capture_chain_fn(nt_names)
|
|
if fn not in fn_2_fo:
|
|
fo = _gen_capture_chain_fo(nt_names)
|
|
fn_2_fo[fn] = fo
|
|
inum_2_fn[ii.inum] = (fn, ptrn.ptrn)
|
|
|
|
|
|
#dump chain functions
|
|
headers = [_xed3_nt_capture_header]
|
|
ild_codegen.dump_flist_2_header(agi,
|
|
_xed3_chain_header,
|
|
headers,
|
|
list(fn_2_fo.values()),
|
|
is_private=True)
|
|
|
|
lu_size = max(inum_2_fn.keys()) + 1
|
|
|
|
xeddir = os.path.abspath(agi.common.options.xeddir)
|
|
gendir = mbuild.join(agi.common.options.gendir,'include-private')
|
|
|
|
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
|
|
_xed3_chain_lu_header, shell_file=False,
|
|
is_private=True)
|
|
h_file.add_header(_xed3_chain_header)
|
|
h_file.start()
|
|
lu_name = 'xed3_chain_fptr_lu'
|
|
xed3_chain_f_t = 'xed3_chain_function_t'
|
|
|
|
fptr_typedef = 'typedef %s(*%s)(%s*);' % (_xed3_chain_return_t,
|
|
xed3_chain_f_t,
|
|
ildutil.xed3_decoded_inst_t)
|
|
|
|
h_file.add_code(fptr_typedef)
|
|
|
|
h_file.add_code(('static %s ' % xed3_chain_f_t) +\
|
|
'%s[%s] = {' % (lu_name, lu_size))
|
|
|
|
empty_line = '/*NO PATTERN*/ (%s)0,' % xed3_chain_f_t
|
|
|
|
for inum in range(0, lu_size):
|
|
if inum in inum_2_fn:
|
|
(fn, ptrn_str) = inum_2_fn[inum]
|
|
entry_str = '/*\n%s\ninum=%s*/ %s,' % (ptrn_str, inum,fn)
|
|
else:
|
|
entry_str = empty_line
|
|
h_file.add_code(entry_str)
|
|
h_file.add_code('};')
|
|
h_file.close()
|
|
|
|
def _dump_dynamic_part1_f(agi):
|
|
"""
|
|
Dumps the xed3_dynamic_decode_part1 function that captures all the
|
|
NTs in the spine that come before INSTRUCTIONS NT.
|
|
"""
|
|
fo = _gen_dynamic_part1_fo(agi)
|
|
#dump the function
|
|
headers = [_xed3_nt_capture_header]
|
|
ild_codegen.dump_flist_2_header(agi,
|
|
_xed3_dynamic_part1_header,
|
|
headers,
|
|
[fo],
|
|
is_private=True)
|
|
|
|
#things that are covered by ILD and static decoding
|
|
#FIXME: is there a better way to determine them instead of just
|
|
#hardcoding their names?
|
|
_nts_to_skip = ['PREFIXES', 'ISA']
|
|
_spine_nt_name = 'ISA'
|
|
_dynamic_part1_fn = 'xed3_dynamic_decode_part1'
|
|
|
|
def _skip_nt(nt_name):
|
|
"""
|
|
Return True if there is no need to generate a capturing
|
|
function for a given NT name.
|
|
"""
|
|
return (nt_name in _nts_to_skip or
|
|
'INSTRUCTIONS' in nt_name or
|
|
'SPLITTER' in nt_name)
|
|
|
|
def _gen_dynamic_part1_fo(agi):
|
|
"""
|
|
Generate the xed3_dynamic_decode_part1 function that
|
|
captures all the NTs that come before INSTRUCTIONS.
|
|
The generated function should be called after ILD and before
|
|
static decoding.
|
|
"""
|
|
gi = agi.generator_dict[_spine_nt_name]
|
|
if len(gi.parser_output.instructions) != 1:
|
|
ildutil.ild_err("Failed to gen dynamic part1 function!\n" +
|
|
"Unexpected number of rules in %s NT: %s" %
|
|
(_spine_nt_name, len(gi.parser_output.instructions)))
|
|
rule = gi.parser_output.instructions[0]
|
|
nt_names = _get_nt_names_from_ii(rule)
|
|
|
|
#filter NTs that we want to skip
|
|
nt_names = list(filter(lambda x: not _skip_nt(x), nt_names))
|
|
fo = _gen_capture_chain_fo(nt_names, fname=_dynamic_part1_fn)
|
|
return fo
|
|
|
|
|
|
|
|
def work(agi, all_state_space, all_ops_widths, patterns):
|
|
"""
|
|
Main entry point of the module.
|
|
For each NT generate a capturing function.
|
|
Then for each sequence of NTs in patterns generate a
|
|
chain capturing function that would call single capturing functions
|
|
for each NT.
|
|
Then for each combination of operands generate operands chain captuirng
|
|
function.
|
|
Also generate lookup tables to obtain those chain capturing functions
|
|
from inum (xed_inst_t index).
|
|
"""
|
|
gendir = ild_gendir = agi.common.options.gendir
|
|
logfn = mbuild.join(gendir, 'xed3_nt_cdicts.txt')
|
|
log_f = open(logfn, 'w')
|
|
|
|
#generate NT capturing functions
|
|
capture_fn_list = []
|
|
for nt_name in list(agi.nonterminal_dict.keys()):
|
|
#skip non terminals that we don't want to capture:
|
|
#PREFIXES, AVX_SPLITTER, *ISA, etc.
|
|
if _skip_nt(nt_name):
|
|
continue
|
|
|
|
_vlog(log_f,'processing %s\n' % nt_name)
|
|
#create a constraint_dict_t for each NT
|
|
nt_cdict = _gen_cdict(agi, nt_name, all_state_space)
|
|
_vlog(log_f,'NT:%s:\n%s\n' % (nt_name, nt_cdict))
|
|
gi = agi.generator_dict[nt_name]
|
|
gi.xed3_cdict = nt_cdict #just for transporting
|
|
#create a function_object_t for the NT
|
|
fo = gen_capture_fo(agi, nt_name, all_ops_widths)
|
|
gi.xed3_capture_fo = fo
|
|
capture_fn_list.append(fo)
|
|
_vlog(log_f,fo.emit())
|
|
|
|
#dump NT capturing functions
|
|
headers = [operand_storage.get_operand_accessors_fn(), ildutil.ild_header]
|
|
ild_codegen.dump_flist_2_header(agi,
|
|
_xed3_nt_capture_header,
|
|
headers,
|
|
capture_fn_list,
|
|
is_private=True)
|
|
|
|
#in each pattern we have a number of NTs,
|
|
#now that we have a capturing function for each NT
|
|
#we can create a create a function that would call
|
|
#needed capturing functions for each pattern.
|
|
#Also dump lookup tables from inum(xed_inst_t index) to
|
|
#chain capturing functions
|
|
_dump_capture_chain_fo_lu(agi, patterns)
|
|
|
|
#do the same for operands of each xed_inst_t
|
|
_dump_op_capture_chain_fo_lu(agi, patterns)
|
|
|
|
#create chain capturing functions for the NTs that come from
|
|
#spine, before the INSTRUCTIONS NT
|
|
_dump_dynamic_part1_f(agi)
|
|
|
|
log_f.close()
|
|
|
|
|
|
|
|
|
|
|