mirror of
https://github.com/RPCSX/xed.git
synced 2024-11-23 11:39:40 +00:00
9bf8c93ad8
Change-Id: Iabfd28f74381c9123f6a1511e59148d57f0e2bcb (cherry picked from commit 2f0f348612c6bc6827d207cdd16bf1cfe454a0a8)
787 lines
30 KiB
Python
Executable File
787 lines
30 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
|
|
from __future__ import print_function
|
|
import sys
|
|
import os
|
|
import codegen
|
|
import encutil
|
|
import genutil
|
|
import actions
|
|
import verbosity
|
|
|
|
max_in_byte = 256 #max unsigned int per byte
|
|
emit_function_prefix = 'xed_encode_instruction_emit_pattern'
|
|
bind_function_prefix = 'xed_encode_instruction_fb_pattern'
|
|
get_field_value = 'xed_encoder_get_start_field_value'
|
|
legacy_maps = {'map0':'XED_ILD_MAP0',
|
|
'map1':'XED_ILD_MAP1',
|
|
'map2':'XED_ILD_MAP2',
|
|
'map3':'XED_ILD_MAP3',
|
|
'3dnow':'XED_ILD_MAPAMD' }
|
|
|
|
|
|
def key_field_binding_lower(x):
|
|
return x.field_name.lower()
|
|
|
|
def sort_field_bindings(a,b):
|
|
''' sort action_t of type emit '''
|
|
|
|
if a.field_name.lower() > b.field_name.lower():
|
|
return 1
|
|
elif a.field_name.lower() < b.field_name.lower():
|
|
return -1
|
|
return 0
|
|
|
|
def key_iform_by_bind_ptrn(x):
|
|
return x.bind_ptrn
|
|
|
|
def cmp_iforms_by_bind_ptrn(a,b): # FIXME:2017-06-10: PY3 port, no longer used
|
|
if a.bind_ptrn > b.bind_ptrn:
|
|
return 1
|
|
elif a.bind_ptrn < b.bind_ptrn:
|
|
return -1
|
|
return 0
|
|
|
|
def key_priority(x):
|
|
return x.priority
|
|
def key_rule_length(x):
|
|
return len(x.rule.get_all_emits() + x.rule.get_all_nts())
|
|
|
|
def cmp_iform_len(a,b): # FIXME:2017-06-10: PY3 port, no longer used
|
|
if a.priority > b.priority:
|
|
return 1
|
|
elif a.priority < b.priority:
|
|
return -1
|
|
|
|
alen = len(a.rule.get_all_emits() + a.rule.get_all_nts())
|
|
blen = len(b.rule.get_all_emits() + b.rule.get_all_nts())
|
|
if alen > blen:
|
|
return 1
|
|
elif alen < blen:
|
|
return -1
|
|
return cmp_iforms_by_bind_ptrn(a,b)
|
|
|
|
|
|
class instructions_group_t(object):
|
|
''' each encoding iform has:
|
|
1. conditions ( e.g.REG1=GPRv_r() )
|
|
2. actions, one type of the actions is a nonterminal (nt)
|
|
|
|
the conditions and the nt's are called "bind patterns".
|
|
|
|
if two different iclasses have the same bind patterns
|
|
for all of their iforms,
|
|
we can put those iclasses in the same group.
|
|
|
|
this is what we are doing in _join_iclasses_to_groups
|
|
'''
|
|
|
|
def __init__(self,iarray,log_dir):
|
|
self.groups = []
|
|
self.iclass2group = {}
|
|
self.log_name = 'groups_log.txt'
|
|
self._join_iclasses_to_groups(iarray,log_dir)
|
|
|
|
def _group_good_for_iclass(self,group,iforms):
|
|
''' Check if the incoming group represents the list of iforms.
|
|
A group represents a list of iforms if:
|
|
1. it has same number of iforms.
|
|
2. for each iform there is an iform in the group that has the same
|
|
bind pattern
|
|
|
|
@param group: ins_group_t object
|
|
@param iforms: a list of iform_t
|
|
|
|
@return: True if group represents the the ifoms list '''
|
|
|
|
if len(group.iforms) != len(iforms):
|
|
return False
|
|
for iform,group_iform in zip(iforms,group.iforms):
|
|
if iform.bind_ptrn != group_iform.bind_ptrn:
|
|
return False
|
|
|
|
return True
|
|
|
|
def _put_iclass_in_group(self,groups,iclass,instruction_iforms):
|
|
''' tries to find a group that represents the incoming iclass.
|
|
'represents' means that all the iforms have exactly the same
|
|
bind patterns.
|
|
if no group was found, then create new group for the iclass.
|
|
|
|
@param groups: a list of ins_group_t object
|
|
@param iclass: the iclass name
|
|
@param instruction_iforms: a list of iform_t, the
|
|
iforms of the iclass
|
|
|
|
@return: function_object_t '''
|
|
|
|
for group in groups:
|
|
# check if group represents the iclass
|
|
if self._group_good_for_iclass(group,instruction_iforms):
|
|
group.add_iclass(iclass,instruction_iforms)
|
|
return
|
|
|
|
#no matching groups
|
|
#create new one
|
|
group = ins_group_t()
|
|
group.add_iclass(iclass,instruction_iforms)
|
|
groups.append(group)
|
|
return
|
|
|
|
def _join_iclasses_to_groups(self,iarray,log_dir):
|
|
'''
|
|
1. dividing the iclasses into groups.
|
|
2. creating a mapping from iclass to its group Id.
|
|
3. generating a log
|
|
|
|
return: a list of ins_group_t objects '''
|
|
|
|
|
|
groups = []
|
|
#1. generate the groups
|
|
for iclass,iforms in list(iarray.items()):
|
|
iforms.sort(key=key_iform_by_bind_ptrn)
|
|
self._put_iclass_in_group(groups,iclass,iforms)
|
|
|
|
# 2. generate the iclass to group Id mapping
|
|
self.iclass2group = {}
|
|
for i,group in enumerate(groups):
|
|
for iclass in group.get_iclasses():
|
|
self.iclass2group[iclass] = i
|
|
|
|
# 3. print the log
|
|
if verbosity.vencode():
|
|
log_file = os.path.join(log_dir,self.log_name)
|
|
df = open(log_file,'w') #debug file
|
|
df.write("number of iclasses: %d\n" % len(iarray))
|
|
df.write("number of groups: %d\n" % len(groups))
|
|
for i,group in enumerate(groups):
|
|
df.write("GROUP Id: %d\n" % i)
|
|
df.write("ICLASSES: %s\n" % str(group.get_iclasses()))
|
|
for iform in group.iforms:
|
|
df.write("%s: %s\n" % ('BIND PATTERN: ', iform.bind_ptrn ))
|
|
df.write("\n\n")
|
|
|
|
df.close()
|
|
self.groups = groups
|
|
|
|
def get_groups(self):
|
|
''' return the groups list '''
|
|
return self.groups
|
|
|
|
def num_groups(self):
|
|
''' return the number of groups '''
|
|
return len(self.groups)
|
|
|
|
def get_iclass2group(self):
|
|
''' return a dic of iclass to it group Id'''
|
|
return self.iclass2group
|
|
|
|
def get_iclass2index_in_group(self):
|
|
''' return a dictionary of iclass to its index in the group'''
|
|
d = {}
|
|
for group in self.groups:
|
|
iclasses = sorted(group.get_iclasses())
|
|
for i,iclass in enumerate(iclasses):
|
|
d[iclass] = i
|
|
|
|
return d
|
|
|
|
class ins_group_t(object):
|
|
''' This class represents one group.
|
|
it holds the list of iclasses that have the same bind patterns.
|
|
'''
|
|
|
|
def __init__(self):
|
|
''' params:
|
|
1. iclass2iforms: mapping from iclass to a list of iforms
|
|
2.iforms: list of iform_t objects
|
|
'''
|
|
|
|
self.iclass2iforms = {}
|
|
self.iforms = []
|
|
|
|
def add_iclass(self,iclass,iforms):
|
|
''' add the iclass and iforms list to the group '''
|
|
|
|
self.iclass2iforms[iclass] = iforms
|
|
if not self.iforms:
|
|
self.iforms = iforms
|
|
|
|
def get_iclasses(self):
|
|
''' return a list of iclasses in the group'''
|
|
return list(self.iclass2iforms.keys())
|
|
|
|
def get_iform_ids_table(self):
|
|
''' generate C style table of iform Id's.
|
|
the table is 2D. one row per iclass.
|
|
the columns are the different iform Ids '''
|
|
|
|
table = []
|
|
iclasses = sorted(self.get_iclasses())
|
|
for iclass in iclasses:
|
|
values = ''
|
|
iforms_sorted_by_length = self.iclass2iforms[iclass]
|
|
|
|
iforms_sorted_by_length.sort(key=key_iform_by_bind_ptrn)
|
|
iforms_sorted_by_length.sort(key=key_rule_length)
|
|
iforms_sorted_by_length.sort(key=key_priority)
|
|
|
|
for iform in iforms_sorted_by_length:
|
|
values += '%4d,' % iform.rule.iform_id
|
|
line = "/*%10s*/ {%s}," % (iclass,values)
|
|
table.extend([ line ])
|
|
|
|
return table
|
|
|
|
|
|
class instruction_codegen_t():
|
|
def __init__(self,iform_list,iarray,logs_dir, amd_enabled=True):
|
|
self.amd_enabled = amd_enabled
|
|
self.iform_list = iform_list
|
|
self.iarray = iarray
|
|
self.logs_dir = logs_dir #directory for the log file
|
|
|
|
#list of field binding function_object_t
|
|
self.fb_ptrs_fo_list = None
|
|
#list of emit patterns function_object_t
|
|
self.emit_ptrs_fo_list = None
|
|
|
|
# number of field binding patterns
|
|
self.max_fb_ptrns = None
|
|
# number of emit patterns
|
|
self.max_emit_ptrns = None
|
|
|
|
# a list of all values been set to field ordered sequentially
|
|
self.fb_values_list = None
|
|
# the length of fb_values_list
|
|
self.fb_values_table_size = None
|
|
|
|
# list of groups (instructions_group_t)
|
|
self.instruction_groups = None
|
|
|
|
def get_values(self,encoder_config):
|
|
''' copy the necessary fields to encoder_confing object '''
|
|
|
|
encoder_config.fb_values_list = self.fb_values_list
|
|
encoder_config.fb_values_table_size = self.fb_values_table_size
|
|
|
|
encoder_config.emit_ptrs_fo_list = self.emit_ptrs_fo_list
|
|
encoder_config.max_emit_ptrns = self.max_emit_ptrns
|
|
|
|
encoder_config.fb_ptrs_fo_list = self.fb_ptrs_fo_list
|
|
encoder_config.max_fb_ptrns = self.max_fb_ptrns
|
|
|
|
encoder_config.ins_groups = self.instruction_groups
|
|
|
|
|
|
def _make_emit_fo(self, iform, i):
|
|
''' create the function object for this emit pattern
|
|
|
|
@param iform: iform_t object
|
|
@param i: index of the pattern function
|
|
@return: function_object_t
|
|
'''
|
|
|
|
fname = "%s_%d" % (emit_function_prefix,i)
|
|
fo = codegen.function_object_t(fname,
|
|
return_type='void')
|
|
|
|
# obj_str is the function parameters for the emit function
|
|
obj_str = encutil.enc_strings['obj_str']
|
|
enc_arg = "%s* %s" % (encutil.enc_strings['obj_type'],
|
|
obj_str)
|
|
fo.add_arg(enc_arg)
|
|
|
|
for action in iform.rule.actions:
|
|
if action.field_name and action.field_name == 'MAP':
|
|
emit_map = 'xed_encoder_request_emit_legacy_map'
|
|
code = " %s(%s)" % (emit_map,obj_str)
|
|
fo.add_code_eol(code)
|
|
|
|
elif action.field_name and action.field_name == 'NOM_OPCODE':
|
|
code = ''
|
|
get_opcode = 'xed_encoder_get_nominal_opcode(%s)' % obj_str
|
|
if action.nbits == 8:
|
|
emit_func = 'xed_encoder_request_emit_bytes'
|
|
else:
|
|
emit_func = 'xed_encoder_request_encode_emit'
|
|
code = ' '*4
|
|
code += '%s(%s,%d,%s)' % (emit_func,obj_str,
|
|
action.nbits,get_opcode)
|
|
fo.add_code_eol(code)
|
|
else:
|
|
code = action.emit_code('EMIT')
|
|
for c in code:
|
|
fo.add_code(c)
|
|
|
|
return fo
|
|
|
|
def _make_fb_setter_fo(self, iform, i):
|
|
''' create the function object for pattern of fields bindings
|
|
|
|
@param iform: iform_t object
|
|
@param i: index of the pattern function
|
|
@return: function_object_t
|
|
'''
|
|
|
|
fname = "%s_%d" % (bind_function_prefix,i)
|
|
fo = codegen.function_object_t(fname,
|
|
return_type='void')
|
|
|
|
obj_name = encutil.enc_strings['obj_str']
|
|
enc_arg = "%s* %s" % (encutil.enc_strings['obj_type'],
|
|
obj_name)
|
|
fo.add_arg(enc_arg)
|
|
|
|
if not iform.fbs:
|
|
#no field binding we need to set, pacify the compiler
|
|
fo.add_code_eol('(void)%s' % obj_name)
|
|
return fo
|
|
|
|
fo.add_code_eol(' const xed_uint8_t* val')
|
|
fo.add_code_eol(' val = %s(%s)' % (get_field_value, obj_name))
|
|
for i,fb_action in enumerate(iform.fbs):
|
|
value_from_lu_table = '*(val+%d)' % i
|
|
operand_setter = "%s_set_%s" % (encutil.enc_strings['op_accessor'],
|
|
fb_action.field_name.lower())
|
|
code = ' %s(%s,%s);' % (operand_setter,
|
|
obj_name, value_from_lu_table)
|
|
fo.add_code(code)
|
|
|
|
return fo
|
|
|
|
def _verify_naked_bits_in_unique_pattern(self):
|
|
''' calculate how many references we have per each full
|
|
instruction emit pattern.
|
|
|
|
naked bits are bits in the pattern without a field name
|
|
like 0x0F or 0b110. earlier functions decorated
|
|
opcode/legacy map.
|
|
|
|
If the naked bits just show up once, then we can hardcode
|
|
those bits in the emit function. This is a test for that.
|
|
|
|
Current design relies on the naked bits being the same in
|
|
similar instruction patterns. If two patterns differ in
|
|
any naked bits, they cannot share emit functions and we die.
|
|
The workaround would be to capture the bits in some field to
|
|
allow the emit function to be shared & generic.
|
|
|
|
The current inputs to XED have no such conflicts.
|
|
'''
|
|
refs_per_ptrn ={}
|
|
for iform in self.iform_list:
|
|
if iform.emit_actions not in refs_per_ptrn:
|
|
refs_per_ptrn[iform.emit_actions] = 1
|
|
else:
|
|
refs_per_ptrn[iform.emit_actions] += 1
|
|
if iform.rule.has_naked_bit_action():
|
|
err = 'emit pattern: %s has more than one reference ' +\
|
|
'use of naked bits is not allowed'%iform.emit_actions
|
|
genutil.die(err)
|
|
|
|
def _make_emit_pattern_fos(self):
|
|
''' collect all the different patterns for emit phase.
|
|
for each pattern create a function representing it.
|
|
adds to each rule in iform_t the index of the pattern function
|
|
|
|
@return: list of emit pattern function name to function object
|
|
'''
|
|
emit_patterns = {}
|
|
fo_list = []
|
|
i = 0
|
|
|
|
for iform in self.iform_list:
|
|
if iform.emit_actions not in emit_patterns:
|
|
fo = self._make_emit_fo(iform,i)
|
|
emit_patterns[iform.emit_actions] = (fo,i)
|
|
fo_list.append(fo)
|
|
iform.emit_func_index = i
|
|
i += 1
|
|
|
|
else:
|
|
fo, index = emit_patterns[iform.emit_actions]
|
|
iform.emit_func_index = index
|
|
|
|
return fo_list
|
|
|
|
|
|
def _make_fb_pattern_fos(self):
|
|
''' collect all the different patterns for bind phase.
|
|
for each pattern create a function representing it.
|
|
adds to each rule in iform_t the index of the pattern function
|
|
|
|
@return: list of emit pattern function name to function object
|
|
'''
|
|
bind_ptterns = {}
|
|
fo_list = []
|
|
i = 0
|
|
|
|
for iform in self.iform_list:
|
|
if iform.fb_ptrn not in bind_ptterns:
|
|
fo = self._make_fb_setter_fo(iform,i)
|
|
bind_ptterns[iform.fb_ptrn] = (fo,i)
|
|
fo_list.append(fo)
|
|
iform.bind_func_index = i
|
|
i += 1
|
|
|
|
else:
|
|
fo,index = bind_ptterns[iform.fb_ptrn]
|
|
iform.bind_func_index = index
|
|
|
|
return fo_list
|
|
|
|
def _identify_map_and_nominal_opcode(self,iform):
|
|
''' scan the list of actions and identify the nominal opcode and
|
|
the legacy map.
|
|
replace the actions that describe the bytes of the nom opcode
|
|
and map with dummy action as place holders.
|
|
'''
|
|
|
|
#list of all prefixes
|
|
prefixes = [0x66,0x67,0xf2,0xf3,0xf0,0x64,0x65,0x2e,0x3e,0x26,0x36]
|
|
vv = 0
|
|
first_naked_bits_index = None
|
|
for i,action in enumerate(iform.rule.actions):
|
|
if action.is_field_binding() and action.field_name == 'VEXVALID':
|
|
# we are in vex valid 1/2/3
|
|
vv = 1
|
|
if action.naked_bits():
|
|
if vv == 0 and action.int_value in prefixes:
|
|
#we are in legacy space and this byte is
|
|
#a prefix, don't care
|
|
continue
|
|
else:
|
|
#this byte represents the nominal opcode or the legacy map
|
|
first_naked_bits_index = i
|
|
break
|
|
|
|
if first_naked_bits_index == None:
|
|
err = "did not find nominal opcode for iform: %s" % str(iform)
|
|
genutil.die(err)
|
|
|
|
last_index = len(iform.rule.actions) - 1
|
|
|
|
first = iform.rule.actions[first_naked_bits_index]
|
|
if first.int_value != 0x0F or vv:
|
|
#this action represents the opcode
|
|
iform.nominal_opcode = first.int_value
|
|
iform.nom_opcode_bits = first.nbits
|
|
# FIXME: mjc: 2017-05-19: this ultimately gets dumped in
|
|
# to the xed-encoder-iforms-init.c file and all the
|
|
# VEX/EVEX/XOP encoded stuff are listed as XED_ILD_MAP0
|
|
# which is wrong. This is only used for encoding the legacy maps.
|
|
# The actual map comes from the field-bindings collected later
|
|
iform.map = legacy_maps['map0'] # legacy only, Not all vexvalid instr are map0.
|
|
#replacing with place holder
|
|
iform.rule.actions[i] = actions.dummy_emit(first,'NOM_OPCODE')
|
|
|
|
else: #first byte == 0x0F and we are legacy space
|
|
#check that we have at least one more byte to read
|
|
if first_naked_bits_index+1 > last_index:
|
|
genutil.die("not enough actions")
|
|
|
|
second = iform.rule.actions[first_naked_bits_index+1]
|
|
if not second.naked_bits():
|
|
genutil.die("expecting map/nominal opcode after 0x0F byte")
|
|
|
|
if self.amd_enabled and second.int_value == 0x0F: #3DNow
|
|
# the nominal opcode in 3DNow is in the last action.
|
|
# FIXME: it is best to not reference directly the last action
|
|
# but rather add a meaningful field name to the action
|
|
amd3dnow_opcode_action = iform.rule.actions[-1]
|
|
iform.nominal_opcode = amd3dnow_opcode_action.int_value
|
|
iform.nom_opcode_bits = 8
|
|
iform.map = legacy_maps['3dnow']
|
|
iform.rule.actions[-1] = actions.dummy_emit(
|
|
amd3dnow_opcode_action,'NOM_OPCODE')
|
|
iform.rule.actions[i] = actions.dummy_emit(first,'MAP')
|
|
#the second byte that describes the map
|
|
#is not needed, remove it
|
|
iform.rule.actions.remove(second)
|
|
|
|
elif second.int_value == 0x38 or second.int_value == 0x3A:
|
|
#check that we have at least one more byte to read
|
|
if first_naked_bits_index+2 > last_index:
|
|
genutil.die("not enough actions")
|
|
|
|
third = iform.rule.actions[first_naked_bits_index+2]
|
|
if not third.naked_bits():
|
|
genutil.die("expecting map/nominal opcode after 0x0F byte")
|
|
|
|
iform.nominal_opcode = third.int_value
|
|
iform.nom_opcode_bits = third.nbits
|
|
|
|
if second.int_value == 0x38:
|
|
iform.map = legacy_maps['map2']
|
|
else: #0x3A
|
|
iform.map = legacy_maps['map3']
|
|
iform.rule.actions[i+1] = actions.dummy_emit(second,'MAP')
|
|
iform.rule.actions[i+2] = actions.dummy_emit(third,
|
|
'NOM_OPCODE')
|
|
iform.rule.actions.remove(first)
|
|
|
|
else:
|
|
iform.nominal_opcode = second.int_value
|
|
iform.nom_opcode_bits = second.nbits
|
|
iform.map = legacy_maps['map1']
|
|
iform.rule.actions[i] = actions.dummy_emit(first,'MAP')
|
|
iform.rule.actions[i+1] = actions.dummy_emit(second,
|
|
'NOM_OPCODE')
|
|
|
|
def _find_sub_list(self,all_fbs_values, fbs_values):
|
|
''' find the the sub list: fbs_values
|
|
in the list: all_fbs_values.
|
|
|
|
if not found return -1
|
|
if found return the fist index of the recurrence '''
|
|
|
|
elems = len(fbs_values)
|
|
indices_to_scan = len(all_fbs_values) - elems + 1
|
|
for i in range(indices_to_scan):
|
|
if fbs_values == all_fbs_values[i:i+elems]:
|
|
return i
|
|
return -1
|
|
|
|
|
|
def _find_fb_occurrence(self,all_fbs_values, fbs_values):
|
|
''' find the the sub list: fbs_values
|
|
in the list: all_fbs_values.
|
|
|
|
if fbs_values is not a sub list to all_fbs_values
|
|
concatenate it.
|
|
|
|
return: the first index of fbs_values occurrence
|
|
in all_fbs_values.
|
|
'''
|
|
|
|
if not fbs_values:
|
|
return 0
|
|
|
|
if not all_fbs_values:
|
|
all_fbs_values.extend(fbs_values)
|
|
return 0
|
|
|
|
index = self._find_sub_list(all_fbs_values,fbs_values)
|
|
if index >= 0:
|
|
# found sub list
|
|
return index
|
|
|
|
# did not found sub list concatenate to the end
|
|
last_index = len(all_fbs_values)
|
|
all_fbs_values.extend(fbs_values)
|
|
return last_index
|
|
|
|
|
|
def _make_fb_values_list(self):
|
|
''' generate a list of the values being set by the FB actions.
|
|
for each iform find the start index of the values list.
|
|
|
|
All the field bindings get put in to a linear array.
|
|
This is finds the index in to that array.
|
|
|
|
This is a quick compression technique for sharing trailing
|
|
subsequences.
|
|
|
|
e.g.: iform1 sets the values: 0 1 2 (3 fields)
|
|
iform2 sets the values: 3 4 (2 fields)
|
|
iform3 sets the values: 1 2
|
|
iform4 sets the values: 2 3
|
|
|
|
the ordered list of unique sequence values across
|
|
all iforms is: 0 1 2 3 4.
|
|
|
|
start index of iform1: 0 (which picks up 0, 1 2)
|
|
start index of iform2: 3 (which picks up 3, 4)
|
|
start index of iform3: 1 (which picks up 1, 2)
|
|
start index of iform4: 2 (which picks up 2, 3)
|
|
|
|
Note: because of ordering, if iform3 happens to show
|
|
up before iform1, they won't share iform1's
|
|
subsequence 1,2.
|
|
'''
|
|
|
|
fbs_list = []
|
|
for iform in self.iform_list:
|
|
# collect all the actions that set fields
|
|
iform.fbs = iform.rule.get_all_fbs()
|
|
iform.fbs.sort(key=key_field_binding_lower)
|
|
|
|
# create a list of int values
|
|
fbs_values = [ x.int_value for x in iform.fbs]
|
|
|
|
#find the start index of this list of values in the general list
|
|
#and update the general list as needed
|
|
iform.fb_index = self._find_fb_occurrence(fbs_list, fbs_values)
|
|
|
|
fbs_list = [ str(x) for x in fbs_list]
|
|
return fbs_list
|
|
|
|
|
|
def _make_field_bindings_pattern(self,iform):
|
|
''' create the string that represents the field bindings pattern. '''
|
|
|
|
bind_actions = []
|
|
for action in iform.rule.actions:
|
|
if action.type == 'nt':
|
|
pass
|
|
elif action.type == 'FB':
|
|
bind_actions.append(action.field_name)
|
|
elif action.type == 'emit':
|
|
if action.emit_type == 'numeric' and action.field_name:
|
|
bind_actions.append(action.field_name)
|
|
else:
|
|
pass
|
|
else:
|
|
genutil.die("unexpected action type: %s" % action.type)
|
|
|
|
fb_ptrn = ''
|
|
if bind_actions:
|
|
fb_ptrn = ', '.join(sorted(bind_actions))
|
|
iform.fb_ptrn = fb_ptrn
|
|
|
|
def _make_emit_pattern(self,iform):
|
|
''' create the string that represents the action for the emit phase.
|
|
using this string we will classify all
|
|
the emit actions into patterns
|
|
'''
|
|
|
|
emit_pattern = []
|
|
|
|
for action in iform.rule.actions:
|
|
if action.type == 'emit':
|
|
emit_pattern.append("emit %s nbits=%d" % (action.field_name,
|
|
action.nbits))
|
|
elif action.type == 'nt':
|
|
emit_pattern.append(str(action))
|
|
elif action.type == 'FB':
|
|
# FB are not used in emit phase so we do not factor them
|
|
# in to the string that represents the pattern
|
|
pass
|
|
else:
|
|
genutil.die("unexpected action type: %s" % action.type)
|
|
|
|
iform.emit_actions = ', '.join(emit_pattern)
|
|
|
|
def _make_bind_pattern(self,iform):
|
|
''' create the string that represents the field bindings pattern. '''
|
|
|
|
bind_ptrn = [ str(iform.rule.conditions) ]
|
|
for action in iform.rule.actions:
|
|
if action.type == 'nt':
|
|
bind_ptrn.append(str(action))
|
|
|
|
iform.bind_ptrn = ''
|
|
if bind_ptrn:
|
|
iform.bind_ptrn = ', '.join(bind_ptrn)
|
|
|
|
def _print_log(self):
|
|
print("---- encoder log ----")
|
|
for i,iform in enumerate(self.iform_list):
|
|
print("%d\n" % i)
|
|
print("IFORM: %s" % str(iform))
|
|
print("iform index: %d" % iform.rule.iform_id)
|
|
bind_index = iform.bind_func_index
|
|
bind_fo = self.fb_ptrs_fo_list[bind_index]
|
|
print("BIND function: %d, %s" % (bind_index,
|
|
bind_fo.function_name))
|
|
emit_index = iform.emit_func_index
|
|
emit_fo = self.emit_ptrs_fo_list[emit_index]
|
|
print("EMIT function: %d, %s" % (emit_index,
|
|
emit_fo.function_name))
|
|
|
|
print("NOM_OPCODE: %d" % iform.nominal_opcode)
|
|
print("MAP: %s" % iform.map)
|
|
fbs_values = [ x.int_value for x in iform.fbs]
|
|
print("FB values: %s" % fbs_values)
|
|
print("\n\n")
|
|
print("-"*20)
|
|
|
|
def work(self): # main entry point
|
|
'''
|
|
Each instruction has
|
|
1) conditions (iclass, user registers, user inputs) and
|
|
|
|
2) actions. 3 types:
|
|
2a) field bindings,
|
|
2b) nonterminals,
|
|
2c) bit-emit of operand fields
|
|
(hard-coded or from NT output)).
|
|
|
|
fos = function output object (plural)
|
|
|
|
generate the following:
|
|
1) list of emit patterns fos (2c)
|
|
2) list of field bindings patterns fos (2a)
|
|
3) list of all field bindings values (values from prev step)
|
|
4) max number of emit patterns
|
|
5) max number of field binding patterns
|
|
6) max number of field bindings values
|
|
7) list of groups fos (see explanation in instructions_group_t)
|
|
|
|
'''
|
|
|
|
for iform in self.iform_list:
|
|
self._identify_map_and_nominal_opcode(iform)
|
|
self._make_field_bindings_pattern(iform)
|
|
self._make_emit_pattern(iform)
|
|
#see explanation about bind patterns in instructions_group_t
|
|
self._make_bind_pattern(iform)
|
|
|
|
self._verify_naked_bits_in_unique_pattern()
|
|
|
|
self.fb_values_list = self._make_fb_values_list() # step 3
|
|
self.fb_values_table_size = len(self.fb_values_list)
|
|
|
|
self.emit_ptrs_fo_list = self._make_emit_pattern_fos()
|
|
self.max_emit_ptrns = len(self.emit_ptrs_fo_list)
|
|
if self.max_emit_ptrns > max_in_byte:
|
|
# we are using uint8to hold the number of patterns,
|
|
# we need to make sure we don't exceeds
|
|
error = "total number of emit patterns(%d) exceeds 8 bits"
|
|
genutil.die(error % self.max_emit_ptrns)
|
|
|
|
self.instruction_groups = instructions_group_t(self.iarray,
|
|
self.logs_dir)
|
|
|
|
self.fb_ptrs_fo_list = self._make_fb_pattern_fos()
|
|
self.max_fb_ptrns = len(self.fb_ptrs_fo_list)
|
|
if self.max_fb_ptrns > max_in_byte:
|
|
# we are using uint8to hold the number of patterns,
|
|
# we need to make sure we don't exceeds
|
|
error = "total number of field binding patterns(%d) exceeds 8 bits"
|
|
genutil.die(error % self.max_fb_ptrns)
|
|
|
|
if verbosity.vencode():
|
|
self._print_log()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|