mirror of
https://github.com/RPCSX/xed.git
synced 2026-01-31 01:05:17 +01:00
Change-Id: I166833daaa56c33eca01bdf7b9aa6e74a490ba9a (cherry picked from commit 1212ba962dff6dfbfa0bd2469327ff447ce59058)
355 lines
14 KiB
Python
Executable File
355 lines
14 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 collections
|
|
import actions
|
|
import types
|
|
import sys
|
|
|
|
class actions_codegen_t(object):
|
|
''' This file is about:
|
|
(a) examining the actions that we'll do after hashing.
|
|
Some actions may be conditional and this creates valid indicators for the
|
|
conditional bindings.
|
|
(b) printing the values of the actions for the hash table initialization.
|
|
(c) printing the conditional initialization of the xed fields after
|
|
successful hashing. Including calling other functions and returning values.
|
|
'''
|
|
def __init__(self, tuple2rule, default_actions, strings_dict):
|
|
''' params: tuple2rule: is a mapping from tuple to a rule
|
|
default_actions is a list action when we do not hit
|
|
a valid hash entry
|
|
strings_dict a is mapping of string to string'''
|
|
self.all_fbs = None
|
|
self.common_fbs = None
|
|
self.max_nt_number = 0
|
|
self.tuple2rule = tuple2rule
|
|
self.default_actions = default_actions
|
|
self.strings_dict = strings_dict
|
|
|
|
#this is a mapping from a tuple to a lost of action_t objects
|
|
self.tuple2actions = self._preprocess(tuple2rule)
|
|
|
|
def _gather_all_fb(self, rules):
|
|
''' returns a tuple of:
|
|
(1) the super set of all the field binding
|
|
(2) the intersection of all the fb
|
|
'''
|
|
if len(rules) == 0:
|
|
return ([], [])
|
|
|
|
#create a bin of fbs, one entry per rule
|
|
fbs_bin = []
|
|
for rule in rules:
|
|
rule_fbs = set()
|
|
for action in rule.actions:
|
|
if action.is_field_binding():
|
|
rule_fbs.add(action.field_name.lower())
|
|
fbs_bin.append(rule_fbs)
|
|
|
|
|
|
all_fbs = set()
|
|
common_fbs = fbs_bin[0]
|
|
for bin in fbs_bin:
|
|
all_fbs.update(bin)
|
|
common_fbs.intersection_update(bin)
|
|
|
|
return sorted(all_fbs), common_fbs
|
|
|
|
def _get_max_nt_number(self, rules):
|
|
''' find the maximal number of nt and ntlus among all the rules'''
|
|
nts_per_rule = []
|
|
ntlufs_per_rule = []
|
|
for rule in rules:
|
|
nts = 0
|
|
ntlufs = 0
|
|
for action in rule.actions:
|
|
if action.is_nonterminal():
|
|
nts += 1
|
|
if action.is_ntluf():
|
|
ntlufs += 1
|
|
nts_per_rule.append(nts)
|
|
ntlufs_per_rule.append(ntlufs)
|
|
|
|
if nts_per_rule:
|
|
return max(nts_per_rule), max(ntlufs_per_rule)
|
|
return 0,0
|
|
|
|
def _create_ntluf_actions(self, action_list):
|
|
''' return a list of all the ntluf actions '''
|
|
i = 0
|
|
ntluf_list = []
|
|
for action in action_list:
|
|
if action.is_ntluf():
|
|
if i < self.max_ntluf_number:
|
|
ntluf_list.append(action)
|
|
else:
|
|
genutil.die('currently do not support unequal \
|
|
number of ntluf among all the rules')
|
|
i += 1
|
|
#adding null pointer values to the actions list so all the rules will
|
|
#have the same number of actions
|
|
while i < self.max_ntluf_number:
|
|
nt_list.append(actions.gen_null_fb())
|
|
i += 1
|
|
|
|
return ntluf_list
|
|
def _create_nt_actions(self, action_list):
|
|
''' return a list of all the nt actions '''
|
|
i = 0
|
|
nt_list = []
|
|
for action in action_list:
|
|
if action.is_nonterminal():
|
|
if i < self.max_nt_number:
|
|
nt_list.append(action)
|
|
else:
|
|
genutil.die('currently do not support unequal number of \
|
|
nt among all the rules')
|
|
i += 1
|
|
#adding null pointer values to the actions list so all the rules will
|
|
#have the same number of actions
|
|
while i < self.max_nt_number:
|
|
nt_list.append(actions.gen_null_fb())
|
|
i += 1
|
|
|
|
return nt_list
|
|
|
|
|
|
def _create_fb_actions(self, all_fbs, common_fbs, rule):
|
|
''' creates a list fb actions for the given rule.
|
|
in case the given rule does not have an action for some fb in the
|
|
all_fbs list, we add a dummy action node '''
|
|
|
|
fbs = all_fbs
|
|
fb_list = []
|
|
for fb_name in fbs:
|
|
fb_found = False
|
|
for action in rule.actions:
|
|
if action.is_field_binding() and \
|
|
action.field_name.lower() == fb_name:
|
|
fb_found = True
|
|
fb_list.append(action)
|
|
|
|
if not fb_found:
|
|
#the rule does not have action for this fb, creating a dummy
|
|
#node for it
|
|
fb_list.append(actions.gen_dummy_fb(fb_name))
|
|
|
|
return fb_list
|
|
|
|
def _get_return_action(self, actions):
|
|
''' find a return action and return it '''
|
|
for action in actions:
|
|
if action.is_return():
|
|
return [action]
|
|
return []
|
|
|
|
def _has_emit(self, rules):
|
|
''' returns True if one of the rules has emit action'''
|
|
|
|
for rule in rules:
|
|
if rule.has_emit_action():
|
|
return True
|
|
return False
|
|
|
|
def _preprocess(self, tuple2rule):
|
|
''' generates the following information:
|
|
(1) the super set of all the field bindings among the rules
|
|
(2) the intersection of the fb.
|
|
(3) the max number of nonterminal functions
|
|
(4) if we have a 'return' action
|
|
(5) a mapping from tuple to a list of all the actions that were
|
|
captured '''
|
|
tuple2actions = {}
|
|
|
|
rules = list(tuple2rule.values())
|
|
self.rules = rules
|
|
self.all_fbs, self.common_fbs = self._gather_all_fb(rules)
|
|
self.max_nt_number, self.max_ntluf_number = self._get_max_nt_number(rules)
|
|
self.ret_action = False
|
|
self.has_emit = self._has_emit(rules)
|
|
|
|
for tuple, rule in tuple2rule.items():
|
|
actions = self._create_fb_actions(self.all_fbs, self.common_fbs, rule)
|
|
nts = self._create_nt_actions(rule.actions)
|
|
ntlufs = self._create_ntluf_actions(rule.actions)
|
|
ret_action = self._get_return_action(rule.actions)
|
|
if ret_action:
|
|
self.ret_action = True
|
|
tuple2actions[tuple] = actions + nts + ntlufs + ret_action
|
|
|
|
|
|
return tuple2actions
|
|
|
|
def get_actions_desc(self):
|
|
''' returns the description of the action types '''
|
|
desc = []
|
|
for fb in self.all_fbs:
|
|
desc.append("%s %s" % (self.strings_dict['fb_type'], fb))
|
|
for i in range(self.max_nt_number):
|
|
desc.append("%s ntptr%d" % (self.strings_dict['nt_fptr'], i))
|
|
for i in range(self.max_ntluf_number):
|
|
desc.append("%s ntlufptr%d" % (self.strings_dict['ntluf_fptr'], i))
|
|
if self.ret_action:
|
|
desc.append("%s value" % self.strings_dict['return_type'])
|
|
if self.has_emit:
|
|
desc.append("%s emit" % self.strings_dict['return_type'])
|
|
|
|
if desc:
|
|
return " ;".join(desc) + ";"
|
|
return ""
|
|
|
|
def no_actions(self):
|
|
''' returns True if there is no actions, of any kind.
|
|
returns False if there is at least one action '''
|
|
if self.all_fbs or self.common_fbs or self.max_nt_number or \
|
|
self.max_ntluf_number or self.ret_action or self.has_emit:
|
|
return False
|
|
return True
|
|
|
|
def get_values(self, tuple):
|
|
''' return the values of the actions for the specific given tuple'''
|
|
action_vals = []
|
|
|
|
actions_list = self.tuple2actions[tuple]
|
|
for action in actions_list:
|
|
val = action.get_str_value()
|
|
if action.is_nonterminal():
|
|
val = "%s_%s_BIND" % (self.strings_dict['nt_prefix'],val)
|
|
if action.is_ntluf():
|
|
val = "%s_%s" % (self.strings_dict['ntluf_prefix'],val)
|
|
action_vals.append(val)
|
|
|
|
if self.has_emit:
|
|
if self.tuple2rule[tuple].has_emit_action():
|
|
hash_index = self.tuple2rule[tuple].index
|
|
action_vals.append(str(hash_index))
|
|
else:
|
|
action_vals.append('0')
|
|
values = ",".join(action_vals)
|
|
return values
|
|
|
|
def emit_actions(self):
|
|
''' dump the code that executes the actions '''
|
|
|
|
actions_list = []
|
|
fb_template = "%s_set_%s(%s,%s)"
|
|
hash_entry = "%s[%s].%s"
|
|
|
|
#dump the code for the fb
|
|
for fb in self.all_fbs:
|
|
hash_val = hash_entry % (self.strings_dict['table_name'],
|
|
self.strings_dict['hidx_str'], fb)
|
|
action = fb_template % (self.strings_dict['op_accessor'],
|
|
fb, self.strings_dict['obj_str'], hash_val)
|
|
|
|
if fb not in self.common_fbs:
|
|
#we need to add fb validation
|
|
validation = "if(%s >= 0) " % hash_val
|
|
action = validation + action
|
|
actions_list.append(action)
|
|
|
|
#dump the code for the nonterminal
|
|
for i in range(self.max_nt_number):
|
|
fptri = "ntptr%d" % i
|
|
hash_val = hash_entry % (self.strings_dict['table_name'],
|
|
self.strings_dict['hidx_str'], fptri)
|
|
validation = "if(%s != 0) " % hash_val
|
|
f_call = "res=(*%s)(%s)" % (hash_val, self.strings_dict['obj_str'])
|
|
actions_list.append(validation + f_call)
|
|
|
|
nt = list(self.tuple2rule.values())[0].nt
|
|
obj_str = self.strings_dict['obj_str']
|
|
emit_call = "xed_encoder_request_iforms(%s)->x_%s=hidx+1"
|
|
actions_list.append(emit_call % (obj_str,nt))
|
|
|
|
#dump the code for the ntluf
|
|
for i in range(self.max_ntluf_number):
|
|
fptri = "ntlufptr%d" % i
|
|
hash_val = hash_entry % (self.strings_dict['table_name'],
|
|
self.strings_dict['hidx_str'], fptri)
|
|
validation = "if(%s != 0) " % hash_val
|
|
f_call = "res=(*%s)(%s,%s)" % (hash_val, self.strings_dict['obj_str'], 'arg_reg')
|
|
actions_list.append(validation + f_call)
|
|
#dump the return code
|
|
if self.ret_action:
|
|
ret_str = "return %s[%s].value" % (self.strings_dict['table_name'],
|
|
self.strings_dict['hidx_str'])
|
|
actions_list.append(ret_str)
|
|
|
|
#dump the emit action
|
|
if self.has_emit:
|
|
nt = list(self.tuple2rule.values())[0].nt
|
|
obj_str = self.strings_dict['obj_str']
|
|
emit_call = "xed_encoder_request_iforms(%s)->x_%s=%s"
|
|
hash_entry = "%s[%s].emit" % (self.strings_dict['table_name'],
|
|
self.strings_dict['hidx_str'])
|
|
actions_list.append(emit_call % (obj_str,nt,hash_entry))
|
|
return actions_list
|
|
|
|
def _has_return_stmt(self):
|
|
''' we assume it is enough to check only the first rule, since if
|
|
on rule has return stmt than all the rules will have one '''
|
|
for action in self.rules[0].actions:
|
|
if action.is_return():
|
|
return True
|
|
return False
|
|
|
|
def get_return_type(self):
|
|
''' get the c type of the return action '''
|
|
if self._has_return_stmt():
|
|
return self.strings_dict['return_type']
|
|
else:
|
|
return 'void'
|
|
|
|
def emit_default(self):
|
|
''' emit the action taken when we did not hit a valid hash table entry
|
|
'''
|
|
actions = []
|
|
for action in self.default_actions:
|
|
if action.is_return():
|
|
s = "return %s" % action.get_str_value()
|
|
actions.append(s)
|
|
if action.is_field_binding():
|
|
val = action.get_str_value()
|
|
fb = action.field_name.lower()
|
|
s = "%s_set_%s(%s,%s)" % (self.strings_dict['op_accessor'], fb,
|
|
self.strings_dict['obj_str'], val)
|
|
actions.append(s)
|
|
|
|
return actions
|
|
|
|
def get_empty_slots(self):
|
|
''' return a list of the empty slots that will be used in the lu table
|
|
whenever we do not have a valid hash entry '''
|
|
slots_num = 0
|
|
slots_num += len(self.all_fbs)
|
|
slots_num += self.max_nt_number
|
|
slots_num += self.max_ntluf_number
|
|
# FIXME: the ret_action seems like the only thing that matters
|
|
# for the decoder
|
|
if self.ret_action:
|
|
slots_num += 1
|
|
return ['0'] * slots_num
|
|
|
|
def has_fcall(self):
|
|
return self.max_nt_number > 0 or self.max_ntluf_number > 0
|
|
|
|
|
|
|