xed/pysrc/xed3_nt.py
Mark Charney e7d734962c update legal header & date for py3 ported files
Change-Id: I166833daaa56c33eca01bdf7b9aa6e74a490ba9a
(cherry picked from commit 1212ba962dff6dfbfa0bd2469327ff447ce59058)
2017-06-12 14:41:24 -04:00

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()