2016-12-16 21:09:38 +00:00
|
|
|
#BEGIN_LEGAL
|
|
|
|
#
|
2017-06-11 17:30:05 +00:00
|
|
|
#Copyright (c) 2017 Intel Corporation
|
2016-12-16 21:09:38 +00:00
|
|
|
#
|
|
|
|
# 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 codegen
|
|
|
|
import copy
|
|
|
|
import re
|
|
|
|
import opnds
|
|
|
|
|
|
|
|
|
|
|
|
def is_target_op(agi, op, target_op):
|
|
|
|
"""
|
|
|
|
@param op: instruction_info_t.operands[i] - the binded operand by an NT row
|
|
|
|
@param target_op: string - the name of the target operand
|
|
|
|
@param agi: all_generator_info_t - the main generator's data structure
|
|
|
|
(as usual)
|
|
|
|
Function returns true if op's name is target_op or if op is a macro which
|
|
|
|
expansion contains target_op
|
|
|
|
"""
|
|
|
|
state_dict = agi.common.state_bits
|
|
|
|
return (op.name.upper() == target_op or
|
|
|
|
(op.name.lower() in state_dict and
|
|
|
|
target_op in state_dict[op.name.lower()].dump_str()))
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
|
|
|
|
def get_setting_nts(agi, opname):
|
|
|
|
"""
|
|
|
|
@param opname: string - name of the operand
|
|
|
|
Function returns a list of strings which are the names of NTs that bind
|
|
|
|
an operand with name opname
|
|
|
|
"""
|
|
|
|
state_dict = agi.common.state_bits
|
|
|
|
nt_set = set()
|
2017-06-11 00:15:27 +00:00
|
|
|
for nt_name in list(agi.nonterminal_dict.keys()):
|
2016-12-16 21:09:38 +00:00
|
|
|
gi = agi.generator_dict[nt_name]
|
|
|
|
parser = gi.parser_output
|
|
|
|
for rule in parser.instructions:
|
|
|
|
for op in rule.operands:
|
|
|
|
if is_target_op(agi, op, opname):
|
|
|
|
nt_set.add(nt_name)
|
|
|
|
return nt_set
|
|
|
|
|
|
|
|
|
|
|
|
def get_nt_seq(ptrn_wrds, nt_list, implied_nt=None):
|
|
|
|
"""
|
|
|
|
@param ptrn_wrds: [string] - list of tokens of pattern string of an
|
|
|
|
instruction (result of split() on pattern string)
|
|
|
|
|
|
|
|
@param nt_list: [string] - list of strings which are names of NTs that
|
|
|
|
we look for in the pattern
|
|
|
|
|
|
|
|
@param implied_nt: string - name of an NT which is prepended to the
|
|
|
|
output list this NT is implied and doesn't appear in the instruction's
|
|
|
|
pattern (e.g. OSZ_NONTERM)
|
|
|
|
|
|
|
|
@return: a list of strings which are names of NTs from nt_list that
|
|
|
|
were found in ptrn_wrds first NT is implied default NT (for EOSZ for
|
|
|
|
example it's OSZ_NONTERM)
|
|
|
|
"""
|
|
|
|
seq = []
|
|
|
|
if implied_nt:
|
|
|
|
seq.append(implied_nt)
|
|
|
|
|
|
|
|
for w in ptrn_wrds:
|
|
|
|
no_brackets = re.sub('[(][)]', '',w)
|
|
|
|
if no_brackets in nt_list:
|
|
|
|
seq.append(no_brackets)
|
|
|
|
return seq
|
|
|
|
|
|
|
|
|
|
|
|
def gen_nt_seq_lookup(agi, nt_seq, target_op, target_type=None):
|
|
|
|
"""
|
|
|
|
@param nt_seq: [string] - list of strings which are names of the NTs that
|
|
|
|
bind the target_op. Nts appear in the same order as they were found
|
|
|
|
in instruction's pattern (e.g [OSZ_NONTERM, DF64]
|
|
|
|
|
|
|
|
@param target_op: string - name of the operand that is bound by NTs
|
|
|
|
(e.g. EOSZ)
|
|
|
|
|
|
|
|
@param target_type: string - the type of target operand
|
|
|
|
(xed_bits_t for example).
|
|
|
|
Used when we need to override the type specified in grammar.
|
|
|
|
|
|
|
|
@return: codegen.array_gen_t lookup array which defines a mapping
|
|
|
|
from certain operand deciders to the value of target_op
|
|
|
|
e.g. a mapping from {OSZ, MOD, REXW} to EOSZ
|
|
|
|
This mapping is defined by the sequence of NTs (nt_seq)
|
|
|
|
by collapsing individual mapping of each NT into one combined mapping
|
|
|
|
"""
|
|
|
|
|
|
|
|
#first NT in sequence is the implicit base one
|
|
|
|
#for EOSZ and EASZ. For immediate lookup we don't have such
|
|
|
|
#a notion of implicit base NT.
|
|
|
|
state_space = agi.common.state_space
|
|
|
|
gi = agi.generator_dict[nt_seq[0]]
|
|
|
|
argnames = generate_lookup_function_basis(gi,state_space)
|
|
|
|
base_dict = gen_lookup_dict(agi, nt_seq[0], target_op, argnames)
|
|
|
|
if not base_dict:
|
|
|
|
return None
|
|
|
|
map_list = []
|
|
|
|
for nt_name in nt_seq[1:]:
|
|
|
|
lookup_dict = gen_lookup_dict(agi, nt_name, target_op, argnames)
|
|
|
|
if not lookup_dict:
|
|
|
|
return None
|
|
|
|
map_list.append(lookup_dict)
|
|
|
|
|
|
|
|
comb_map = combine_mapping_seq(base_dict, map_list)
|
|
|
|
if not comb_map:
|
|
|
|
return None
|
|
|
|
return gen_lookup_array(agi, nt_seq, comb_map, target_op, argnames,
|
|
|
|
target_type)
|
|
|
|
|
|
|
|
#nt_name: string - the name of NT that defines the mapping
|
|
|
|
#target_opname: string - the name of the operand the mapping maps to
|
|
|
|
#(e.g. EOSZ)
|
|
|
|
#argnames: {string -> { string -> Boolean } } a dict of dicts
|
|
|
|
#first key is operand decider name, second key is operand decider value
|
|
|
|
#argnames['MOD']['0'] == True iff operand decider MOD can have value '0'
|
|
|
|
#Returns list of tuples
|
|
|
|
# [ ([{token:string -> index_value:string}], return-value:string) ]
|
|
|
|
#this list defines a mapping from operand deciders values to target_op value
|
|
|
|
#described by given NT (with nt_name)
|
|
|
|
#FIXME: sometimes (ONE():: NT) target_op bounded by all different rows has
|
|
|
|
#same value. It happens when there are other operands bounded too. We need
|
|
|
|
#to detect such cases and generate empty dict so that constant function would
|
|
|
|
#be generated for such NTs.
|
|
|
|
def gen_lookup_dict(agi, nt_name, target_opname, argnames):
|
|
|
|
gi = agi.generator_dict[nt_name]
|
|
|
|
options = agi.common.options
|
|
|
|
state_space = agi.common.state_space
|
|
|
|
operand_storage = agi.operand_storage
|
|
|
|
|
|
|
|
|
|
|
|
all_values = []
|
|
|
|
for ii in gi.parser_output.instructions:
|
|
|
|
#First check if current rule sets the operand, if not
|
|
|
|
#go to next rule
|
|
|
|
target_op = None
|
|
|
|
for op in ii.operands:
|
|
|
|
if is_target_op(agi, op, target_opname):
|
|
|
|
target_op = op
|
|
|
|
break
|
|
|
|
|
|
|
|
if not target_op:
|
|
|
|
continue
|
|
|
|
|
|
|
|
state_dict = agi.common.state_bits
|
|
|
|
#if binding operand is a macro
|
|
|
|
if target_op.name.lower() in state_dict:
|
|
|
|
op_spec = state_dict[target_op.name.lower()].list_of_str
|
|
|
|
found_op = False
|
|
|
|
for w in op_spec:
|
|
|
|
if w.startswith(target_opname):
|
|
|
|
found_op = True
|
|
|
|
break
|
|
|
|
if not found_op:
|
|
|
|
ildutil.ild_err("Failed to find operand %s" % str(target_op))
|
|
|
|
expansion = w
|
|
|
|
target_op = opnds.parse_one_operand(expansion)
|
|
|
|
|
|
|
|
# the operand is the table output value
|
|
|
|
if target_op.bits: # RHS of the 1st operand
|
|
|
|
this_row_output = target_op.bits
|
|
|
|
else:
|
|
|
|
ildutil.ild_err("NTLUF operand %s" % str(target_op))
|
|
|
|
# Now we must get the table index values as a dictionary
|
|
|
|
indices = _generate_lookup_function_indices(ii,state_space,argnames)
|
|
|
|
all_values.append((indices,this_row_output))
|
|
|
|
return all_values
|
|
|
|
|
|
|
|
def get_nt_from_lufname(fname):
|
|
|
|
suffix = re.sub('xed_lookup_function_', '', fname)
|
|
|
|
nt = re.sub('_getter', '', suffix)
|
|
|
|
return nt
|
|
|
|
|
|
|
|
def get_lufn_suffix(array):
|
|
|
|
lufn = array.lookup_fn.function_name
|
|
|
|
suffix = re.sub('xed_lookup_function_', '', lufn)
|
|
|
|
return suffix
|
|
|
|
|
|
|
|
|
|
|
|
def get_lufn(nt_seq, target_op, flevel=''):
|
|
|
|
lu_name = '_'.join(nt_seq)
|
|
|
|
|
|
|
|
lu_fn = 'xed_lookup_function_%s_%s' % (lu_name, target_op)
|
|
|
|
if len(flevel) > 0:
|
|
|
|
lu_fn += '_%s' % flevel
|
|
|
|
return lu_fn
|
|
|
|
|
|
|
|
def gen_lu_names(nt_seq, target_op, level=''):
|
|
|
|
"""
|
|
|
|
@param nt_seq: List of NT names.
|
|
|
|
@type nt_seq: C{[string]}
|
|
|
|
|
|
|
|
@param target_op: Name of bounded operand.
|
|
|
|
@type target_op: C{string}
|
|
|
|
|
|
|
|
@return (lu_arr, init_fn, lu_fn):
|
|
|
|
Tuple of 3 names: lookup array name, init function name and
|
|
|
|
lookup function name.
|
|
|
|
"""
|
|
|
|
lu_name = '_'.join(nt_seq)
|
|
|
|
lu_arr = 'xed_lookup_%s_%s' % (lu_name, target_op)
|
|
|
|
init_fn = 'xed_lookup_function_init_%s_%s' % (lu_name, target_op)
|
|
|
|
lu_fn = get_lufn(nt_seq, target_op, flevel=level)
|
|
|
|
return (lu_arr, init_fn, lu_fn)
|
|
|
|
|
|
|
|
def get_luf_name_suffix(luf_name):
|
|
|
|
return re.sub('xed_lookup_function_', '', luf_name)
|
|
|
|
|
|
|
|
|
|
|
|
def _is_constant_mapping(val_dict):
|
|
|
|
"""
|
|
|
|
@param val_dict:
|
|
|
|
Defines the mapping, by defining an output value for each row of
|
|
|
|
constrains. Each row is defined by a dictionary of operand names to
|
|
|
|
operand values.
|
|
|
|
@type val_dict:
|
|
|
|
[ ([ dict(opname:string -> opval:string) ], value:string) ]
|
|
|
|
The return type of gen_lookup_dict function
|
|
|
|
|
|
|
|
@return bool: True if mapping defined by val_dict always returns same
|
|
|
|
value. And hence we can define a constant function, not dependent on
|
|
|
|
parameters.
|
|
|
|
This is relevant for ONE() NT that has same IMM_WIDTH output operand
|
|
|
|
value for several different index values.
|
|
|
|
A good question is why it was defined that way.
|
|
|
|
"""
|
|
|
|
#check if we have same output values for all rows,
|
|
|
|
#then we should generate a constant function, independent from parameters
|
|
|
|
#This happens in ONE() NT for IMM_WIDTH
|
|
|
|
#ONE() seems to be pretty useless NT.
|
|
|
|
(_first_indices, first_output) = val_dict[0]
|
|
|
|
all_same = True
|
|
|
|
for _indices,out_val in val_dict[1:]:
|
|
|
|
if out_val != first_output:
|
|
|
|
all_same = False
|
|
|
|
break
|
|
|
|
return all_same
|
|
|
|
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
#nt_seq: [string] - list of NT names that define the mapping
|
|
|
|
#val_dict: [ ([{token:string -> index_value:string}], return-value:string) ]
|
|
|
|
#(the type returned by gen_lookup_dict), it defines the mapping
|
|
|
|
#opname: string - the name of target operand e.g. EOSZ
|
|
|
|
#argnames: {string -> { string -> Boolean } } a dict of dicts
|
|
|
|
#optype: string - the type of target op (the return type of the
|
|
|
|
#lookup function). If optype is specified it is used instead of
|
|
|
|
#agi's defined operand type for opname. Useful for IMM_WIDTH which is defined
|
|
|
|
#as xed_uint8_t by grammar, but for ILD purposes should be natural int
|
|
|
|
#(xed_bits_t), because byte-sized operations are sub-optimal in performance in
|
|
|
|
#32 or 64 modes.
|
|
|
|
#first key is operand decider name, second key is operand decider value
|
|
|
|
#argnames['MOD']['0'] == True iff operand decider MOD can have value '0'
|
|
|
|
#returns codegen.array_gen_t lookup array that defines the mapping
|
|
|
|
def gen_lookup_array(agi, nt_seq, val_dict, opname, argnames,
|
|
|
|
optype=None, flevel=''):
|
|
|
|
operand_storage = agi.operand_storage
|
|
|
|
(lu_arr, init_fn, lu_fn) = gen_lu_names(nt_seq, opname, level=flevel)
|
|
|
|
if not optype:
|
|
|
|
luf_return_type = operand_storage.get_ctype(opname)
|
|
|
|
else:
|
|
|
|
luf_return_type = optype
|
|
|
|
array= codegen.array_gen_t(lu_arr, type=luf_return_type, target_op=opname)
|
|
|
|
|
|
|
|
#check if the mapping is constant (ONE() NT), if so,
|
|
|
|
#redefine the mapping to have no index operands so that
|
|
|
|
#we will have lookup function with no parameters for this
|
|
|
|
#mapping
|
|
|
|
if _is_constant_mapping(val_dict):
|
|
|
|
argnames = {}
|
|
|
|
(_first_indices, value) = val_dict[0]
|
|
|
|
val_dict = [([{}], value)]
|
|
|
|
|
2017-06-11 00:15:27 +00:00
|
|
|
for od in list(argnames.keys()):
|
|
|
|
values = list(argnames[od].keys())
|
2016-12-16 21:09:38 +00:00
|
|
|
array.add_dimension(operand_storage.get_ctype(od),
|
|
|
|
min(values),
|
|
|
|
max(values) + 1,
|
|
|
|
argname = od)
|
|
|
|
# fill in all the values
|
|
|
|
for list_of_dict_of_indices, value in val_dict:
|
|
|
|
for index_dict in list_of_dict_of_indices:
|
|
|
|
array.add_value(index_dict, value)
|
|
|
|
|
|
|
|
static = True
|
|
|
|
|
|
|
|
#FIXME: these functions should be inline, but that leads to a compilation
|
|
|
|
#error on linux :
|
|
|
|
# cc1: warnings being treated as errors
|
|
|
|
# error: inline function ... declared but never defined
|
|
|
|
#making it not inline until I figure out how to fix that warning
|
|
|
|
inline = True
|
|
|
|
array.gen_lookup_function(lu_fn, static=static, inline=inline,
|
|
|
|
check_const=True)
|
|
|
|
|
|
|
|
array.make_initialization_function(init_fn)
|
|
|
|
return array
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
#array_list: [codegen.array_t] - list of arrays, each of them defines
|
|
|
|
#a c array, array init function and array lookup function
|
|
|
|
#c_fn: string - name of the c file, where the arrays and function definitions
|
|
|
|
#should be dumped
|
2016-12-23 21:24:53 +00:00
|
|
|
#header_fn: string - name of the .h file where declarations of functions should
|
2016-12-16 21:09:38 +00:00
|
|
|
#be dumped
|
|
|
|
#Dumps arrays and init and lookup functions if c and h files
|
|
|
|
def dump_lu_arrays(agi, array_list, c_fn, header_fn, init_f=None):
|
|
|
|
c_file = agi.open_file(c_fn, start=False)
|
|
|
|
header_file = agi.open_file(header_fn, start=False)
|
|
|
|
#header_file.replace_headers(['xed-types.h', 'xed-reg-enum.h'])
|
|
|
|
header_file.start()
|
|
|
|
|
|
|
|
c_file.start()
|
|
|
|
for array in array_list:
|
|
|
|
#the optimization for constant functions - we do not need
|
|
|
|
#arrays for them since their lookup functions are just "return const;"
|
|
|
|
if not array.is_const_lookup_fun():
|
|
|
|
c_file.add_code("/*Array declaration*/")
|
|
|
|
c_file.add_code(array.emit_declaration(static=False))
|
|
|
|
c_file.add_code("/*Array initialization*/")
|
|
|
|
array.init_fn.emit_file_emitter(c_file)
|
|
|
|
|
|
|
|
init_decl = array.emit_initialization_function_header()
|
|
|
|
header_file.add_code(init_decl)
|
|
|
|
|
|
|
|
#lookup functions need to be inline, hence we should put them
|
|
|
|
#in header
|
|
|
|
for array in array_list:
|
|
|
|
#the optimization for constant functions - we do not need
|
|
|
|
#arrays for them since their lookup functions are just "return const;"
|
|
|
|
if not array.is_const_lookup_fun():
|
|
|
|
#declare the lookup arrays
|
|
|
|
header_file.add_code("/*Array declaration*/")
|
|
|
|
header_file.add_code(array.emit_declaration(static=False,
|
|
|
|
extern=True))
|
|
|
|
|
|
|
|
#define the function
|
|
|
|
header_file.add_code("/*Lookup function*/")
|
|
|
|
array.lookup_fn.emit_file_emitter(header_file)
|
|
|
|
|
|
|
|
if init_f:
|
|
|
|
init_f.emit_file_emitter(c_file)
|
|
|
|
init_decl = init_f.emit_header()
|
|
|
|
header_file.add_code(init_decl)
|
|
|
|
c_file.close()
|
|
|
|
header_file.close()
|
|
|
|
|
|
|
|
|
|
|
|
def gen_init_function(arr_list, name):
|
|
|
|
#make a function_object_t to call all the individual init routines
|
|
|
|
overall_init_f = codegen.function_object_t(name,return_type='void')
|
|
|
|
for array in arr_list:
|
|
|
|
if not array.is_const_lookup_fun():
|
|
|
|
overall_init_f.add_code_eol(array.init_fn.function_name + '()')
|
|
|
|
return overall_init_f
|
|
|
|
|
|
|
|
#just for debugging.
|
|
|
|
#Parameters:
|
|
|
|
#nt_name: string - the name of the NT
|
|
|
|
#target_op: string - the name of the target operand
|
|
|
|
#target_type: string - the type of target operand (xed_bits_t for example).
|
|
|
|
#Used when we need to override the type specified in grammar.
|
|
|
|
#return lookup array:codegen.array_t and for a single NT
|
|
|
|
def gen_nt_lookup(agi, nt_name, target_op, target_type=None, level=''):
|
|
|
|
state_space = agi.common.state_space
|
|
|
|
gi = agi.generator_dict[nt_name]
|
|
|
|
argnames = generate_lookup_function_basis(gi,state_space)
|
|
|
|
all_values = gen_lookup_dict(agi, nt_name, target_op, argnames)
|
|
|
|
return gen_lookup_array(agi, [nt_name], all_values, target_op, argnames,
|
|
|
|
target_type, flevel=level)
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
#base_row: {op_name:string -> op_val:string}
|
|
|
|
#row: {op_name:string -> op_val:string}
|
|
|
|
#Rows here are the dispatching rows in NT definitions in grammar.
|
|
|
|
#something like
|
|
|
|
#MOD=0 |
|
|
|
|
#MOD=1 |
|
|
|
|
#MOD=2 |
|
|
|
|
#each one of these is a row.
|
|
|
|
#base_row matches a row if all constrains that are true in row are true
|
|
|
|
#also in base_row
|
|
|
|
#for example base_row REXW=0 MOD=0 matches a row MOD=0
|
|
|
|
#ASSUMPTION: base_row has all operands mentioned,
|
|
|
|
#e.g for EOSZ base_row dict must have OSZ,MOD,REXW operands as keys
|
|
|
|
def row_match(base_row, row):
|
|
|
|
#ildutil.ild_err("ILD_DEBUG BASE ROW %s" % (base_row,))
|
2017-06-11 00:15:27 +00:00
|
|
|
for (op, val) in list(row.items()):
|
2016-12-16 21:09:38 +00:00
|
|
|
if op in base_row:
|
|
|
|
if base_row[op] != val:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
ildutil.ild_err("BASE ROW %s doesn't have OD %s from row %s" %
|
|
|
|
(base_row, op, row))
|
|
|
|
return None
|
|
|
|
return True
|
|
|
|
|
|
|
|
#base_mapping and all_values are both of the type
|
|
|
|
#[ ([dict token->index_value], return-value) ]
|
|
|
|
#the gen_lookup_dict return type.
|
|
|
|
#For each row defined in all_values mapping that matches a row from bas_mapping
|
|
|
|
#this function sets the mapped value to the all_values mapping value.
|
|
|
|
#For example when we have OSZ_NONTERM-CR_BASE NT sequence,
|
|
|
|
#base_mapping is defined by OSZ_NONTERM and all_values mapping is
|
|
|
|
#defined by CR_BASE
|
|
|
|
#and we need to override the value of EOSZ in those rows of OSZ_NONTERM
|
|
|
|
#mapping, that match rows from CR_BASE mapping.
|
|
|
|
#This function behaves similarly to what decode graph traversing does to EOSZ
|
|
|
|
#operand value when it sees two EOSZ-binding NTs in the pattern.
|
|
|
|
def override_mapping(base_mapping, all_values):
|
|
|
|
for indices,value in all_values:
|
|
|
|
for row in indices:
|
|
|
|
temp_map = []
|
|
|
|
for base_indices,base_value in base_mapping:
|
|
|
|
for base_row in base_indices:
|
|
|
|
#if indices match (it is the same logical constraint)
|
|
|
|
#we override the value those indices map to
|
|
|
|
is_match = row_match(base_row, row)
|
|
|
|
#None is returned on internal error.
|
|
|
|
#We dontexit(1) on this because we don't want to break
|
|
|
|
#xed's build if ild's build fails.
|
|
|
|
#This is temporary.
|
|
|
|
if is_match == None:
|
|
|
|
return None
|
|
|
|
elif row_match(base_row, row):
|
|
|
|
temp_map.append(([base_row], value))
|
|
|
|
else:
|
|
|
|
temp_map.append(([base_row], base_value))
|
|
|
|
base_mapping = temp_map
|
|
|
|
return base_mapping
|
|
|
|
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
#base_mapping: [ ([{token:string -> index_value:string}], return-value:string)]
|
|
|
|
#the gen_lookup_dict return type, it is the object that defines the mapping
|
|
|
|
#map_list: list of objects of the same type with base_mapping
|
|
|
|
#take a list of mapping objects and return a mapping
|
|
|
|
#object that is a result of overriding of first mapping by next ones
|
|
|
|
def combine_mapping_seq(base_mapping, map_list):
|
|
|
|
cur_map = base_mapping
|
|
|
|
for all_values in map_list:
|
|
|
|
#this one overrides values of those entries in base_mapping
|
|
|
|
#that match entries in all_values mapping
|
2016-12-23 21:24:53 +00:00
|
|
|
#stores overridden mapping in cur_map
|
2016-12-16 21:09:38 +00:00
|
|
|
cur_map = override_mapping(cur_map, all_values)
|
|
|
|
if not cur_map:
|
|
|
|
return None
|
|
|
|
return cur_map
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#Parameters:
|
|
|
|
#ii: generator.instruction_info_t - a row from NT definition
|
|
|
|
#state_space: {opname:string -> [op_val:string] } a dict from operand
|
|
|
|
#name to a list of its possible values. Obtained from generator
|
|
|
|
#argnames: {string -> { string -> Boolean } } a dict of dicts
|
|
|
|
#first key is operand decider name, second key is operand decider value
|
|
|
|
#argnames['MOD']['0'] == True iff operand decider MOD can have value '0'
|
|
|
|
#Returns [{opname:string -> op_val:string}] - a list of dicts, each
|
|
|
|
#defining a row in NT definition. It is a list, because ii can define
|
|
|
|
#several logical rows e.g for EOSZ:
|
|
|
|
#if ii represents row: MOD=0 OSZ!=0
|
|
|
|
#then we will return representation of rows:
|
|
|
|
#MOD=0 OSZ=1 REXW=1
|
|
|
|
#MOD=0 OSZ=1 REXW=0
|
|
|
|
def _generate_lookup_function_indices(ii,state_space,argnames):
|
|
|
|
"""Return a list of dictionaries where each dictionary is a
|
|
|
|
complete set of token->index_value"""
|
|
|
|
|
|
|
|
indices = {} # dict describing index -> value or list of vlaues
|
|
|
|
for bt in ii.ipattern.bits:
|
|
|
|
if bt.is_operand_decider():
|
|
|
|
|
|
|
|
if bt.test == 'eq':
|
|
|
|
indices[bt.token] = bt.requirement
|
|
|
|
elif bt.test == 'ne':
|
|
|
|
all_values_for_this_od = state_space[bt.token]
|
2017-06-11 00:03:28 +00:00
|
|
|
trimmed_vals = list(filter(lambda x: x != bt.requirement,
|
|
|
|
all_values_for_this_od))
|
2016-12-16 21:09:38 +00:00
|
|
|
#Add the list of values; We flaten it later
|
|
|
|
indices[bt.token] = trimmed_vals
|
|
|
|
else:
|
|
|
|
ildutil.ild_err("Bad bit test (not eq or ne) in " + ii.dump_str())
|
|
|
|
elif bt.is_nonterminal():
|
|
|
|
pass # FIXME make a better test
|
|
|
|
else:
|
|
|
|
#We should ignore non-operand deciders: IMM Nts have captures in their
|
|
|
|
#rules, and it is OK, they don't affect mappings defined by NTs
|
|
|
|
pass
|
|
|
|
#ildutil.ild_err("Bad pattern bit (not an operand decider) in %s" %
|
|
|
|
# ii.dump_str())
|
|
|
|
#return None
|
|
|
|
|
|
|
|
#in order to match lookup rows correctly, we need to have all indices
|
|
|
|
#mentioned in the "indices" dict.
|
|
|
|
#For example if all operand deciders are [OSZ, REXW, MOD] and in the
|
|
|
|
#ii.ipattern we have only MOD=0 mentioned, it means that this row matches
|
|
|
|
#all combinations of MOD=0 with all other values for OSZ and REXW.
|
|
|
|
#We need to add all those combinations explicitly here, otherwise later
|
|
|
|
#when we match rows MOD=0 row may match MOD=0 OSZ=0 row and also
|
|
|
|
#MOD=0 OSZ=1 row and these rows define different binding value, we will not
|
|
|
|
#know which value to choose.
|
|
|
|
#of course there are other ways to solve this problem, but this seems to be
|
|
|
|
#the easiest.
|
2017-06-11 00:15:27 +00:00
|
|
|
for bt_token in list(argnames.keys()):
|
2016-12-16 21:09:38 +00:00
|
|
|
if not (bt_token in indices):
|
2017-06-11 00:15:27 +00:00
|
|
|
indices[bt_token] = list(argnames[bt_token].keys())
|
2016-12-16 21:09:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
### NOW, we must flatten any list-valued RHS's & return a list of
|
|
|
|
### dicts where the RHS is always a scalar.
|
|
|
|
indices_flattened = genutil.flatten_dict(indices)
|
|
|
|
|
|
|
|
return indices_flattened
|
|
|
|
|
|
|
|
def add_op_deciders_temp(ipattern, state_space, argnames): # NOT USED
|
|
|
|
"""
|
|
|
|
@param ipattern: the ipattern member of instruction_info_t
|
|
|
|
@param state_space: dictionary from op deciders tokens to list
|
|
|
|
of their legal values.
|
|
|
|
|
|
|
|
@param argnames: dict where to append op deciders values:
|
|
|
|
2D argnames[op_decider_token][accepted_value]=True
|
|
|
|
"""
|
|
|
|
for bt in ipattern.bits:
|
|
|
|
if bt.is_operand_decider():
|
|
|
|
if bt.token not in argnames:
|
|
|
|
argnames[bt.token] = {}
|
|
|
|
|
|
|
|
if bt.test == 'eq':
|
|
|
|
argnames[bt.token][bt.requirement]=True
|
|
|
|
elif bt.test == 'ne':
|
|
|
|
argnames[bt.token]['!=' + ('%s'%bt.requirement)]=True
|
|
|
|
else:
|
|
|
|
ildutil.ild_err("Bad bit test (not eq or ne) in %s" %
|
|
|
|
ipattern)
|
|
|
|
return
|
|
|
|
|
|
|
|
def add_op_deciders(ipattern, state_space, argnames):
|
|
|
|
"""
|
|
|
|
@param ipattern: the ipattern member of instruction_info_t
|
|
|
|
@param state_space: dictionary from op deciders tokens to list
|
|
|
|
of their legal values.
|
|
|
|
|
|
|
|
@param argnames: dict where to append op deciders values:
|
|
|
|
2D argnames[op_decider_token][accepted_value]=True
|
|
|
|
"""
|
|
|
|
for bt in ipattern.bits:
|
|
|
|
if bt.is_operand_decider():
|
|
|
|
if bt.token not in argnames:
|
|
|
|
argnames[bt.token] = {}
|
|
|
|
|
|
|
|
if bt.test == 'eq':
|
|
|
|
argnames[bt.token][bt.requirement]=True
|
|
|
|
elif bt.test == 'ne':
|
|
|
|
all_values_for_this_od = state_space[bt.token]
|
2017-06-11 00:03:28 +00:00
|
|
|
trimmed_vals = list(filter(lambda x: x != bt.requirement,
|
|
|
|
all_values_for_this_od))
|
2016-12-16 21:09:38 +00:00
|
|
|
for tv in trimmed_vals:
|
|
|
|
argnames[bt.token][tv]=True
|
|
|
|
else:
|
|
|
|
ildutil.ild_err("Bad bit test (not eq or ne) in %s" %
|
|
|
|
ipattern)
|
|
|
|
return
|
|
|
|
|
|
|
|
def extend_2d_dict(dst, src): # NOT USED
|
|
|
|
for key1 in src:
|
|
|
|
if key1 in dst:
|
|
|
|
dst[key1].update(src[key1])
|
|
|
|
else:
|
|
|
|
dst[key1] = src[key1]
|
|
|
|
return
|
|
|
|
|
|
|
|
def generate_lookup_function_basis(gi,state_space):
|
|
|
|
"""Return a dictionary whose values are dictionaries of all the values
|
|
|
|
that the operand decider might have"""
|
|
|
|
argnames = {} # tokens -> list of all values for that token
|
|
|
|
for ii in gi.parser_output.instructions:
|
|
|
|
add_op_deciders(ii.ipattern, state_space, argnames)
|
|
|
|
return argnames
|
|
|
|
|