Files
archived-xed/pysrc/ild_disp.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

422 lines
15 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 ild_nt
import genutil
import ildutil
import codegen
import ild_eosz
import ild_easz
import ild_info
import ild_codegen
import operand_storage
import mbuild
_disp_token = 'DISP_WIDTH'
_brdisp_token = 'BRDISP_WIDTH'
_disp_tokens = [_brdisp_token, _disp_token]
_ild_t_disp_member = 'disp_width'
_l3_header_fn = 'xed-ild-disp-l3.h'
_l3_c_fn = 'xed-ild-disp-l3.c'
_l2_header_fn = 'xed-ild-disp-l2.h'
_l1_header_fn = 'xed-ild-disp-l1.h'
_l2_c_fn = 'xed-ild-disp-l2.c'
_const_suffix = 'CONST'
_empty_fn = 'xed_lookup_function_EMPTY_DISP_CONST_l2'
_l1_header_fn = 'xed-ild-disp-l1.h'
_disp_lu_header_fn = 'xed-ild-disp-bytes.h'
_l1_ptr_typename = 'disp_bytes_l1_func_t'
def get_disp_nt_seq(ptrn_wrds, disp_nts):
"""
@param ptrn_wrds: list of tokens in instructions pattern
@type ptrn_wrds: [string]
@param disp_nts: list of names of [BR]?DISP_WIDTH-binding NTs
@type disp_nts: [string]
@return nt_names: list of names of [BR]?DISP_WIDTH binding NTs
@type nt_names: [string]
Returns a list of names of [BR]?DISP_WIDTH NTs in the pattern.
"""
return ild_nt.get_nt_seq(ptrn_wrds, list(disp_nts))
def get_all_disp_seq(united_lookup):
"""
@param uinted_lookup: lookup of ild_info.ild_info_t objects representing
current ISA. This lookup should have been built from storage+grammar
@type uinted_lookup: ild_info.ild_storage_t
@return seq_list: list of all variations of DISP-binding NT sequences in
united_lookup.
@type seq_list: [ [string] ]
"""
all_seq = set()
infos = united_lookup.get_all_infos()
#infos = plist
for info in infos:
#lists are unhashable, hence we have to use tuples instead
all_seq.add(tuple(info.disp_nt_seq))
#convert back to lists, in order not to surprise user
return_list = []
for nt_tuple in all_seq:
return_list.append(list(nt_tuple))
return return_list
def get_disp_binding_nts(agi):
"""
Go through all defined NTs in agi and return names of those,
that bind DISP_WIDTH
"""
return ild_nt.get_setting_nts(agi, _disp_token)
def get_brdisp_binding_nts(agi):
"""
Go through all defined NTs in agi and return names of those,
that bind BRDISP_WIDTH
"""
return ild_nt.get_setting_nts(agi, _brdisp_token)
def get_target_opnames():
"""
@return opnames: names of the DISP operand - [DISP_WIDTH, BRDISP_WIDTH]
@type opnames: [string]
"""
return _disp_tokens
def get_l2_fn_from_info(info, disp_dict):
"""
Return L2 function name defined by the info.
disp_dict is a dictionary from [BR]DISP NT name to codegen.array
of the corresponding NT.
"""
is_const = ild_codegen.is_constant_l2_func(info.disp_nt_seq, disp_dict)
if len(info.disp_nt_seq) == 0:
return _empty_fn
disp_nt = disp_dict[info.disp_nt_seq[0]]
disp_token = disp_nt.get_target_opname()
if ild_eosz.get_target_opname() in disp_nt.get_arg_names():
argname = ild_eosz.get_target_opname()
arg_seq = info.eosz_nt_seq
else:
argname = ild_easz.get_target_opname()
arg_seq = info.easz_nt_seq
if is_const:
arg_seq = []
arg_name = None
l2_fn = ild_codegen.get_l2_fn(info.disp_nt_seq, disp_token,
arg_seq,
argname,
_empty_fn, is_const)
return l2_fn
def _is_disp_conflict(info_list, disp_dict):
"""
Return True|False whether infos in info_list conflict on L2
functions (and then we need to define L1 function for this list).
"""
if len(info_list) <= 1:
return False
first = info_list[0]
l2_fn_first = get_l2_fn_from_info(first, disp_dict)
for info in info_list[1:]:
l2_fn_cur = get_l2_fn_from_info(info, disp_dict)
if (l2_fn_first != l2_fn_cur):
return True
return False
#a list of conflict resolution functions to use when we have conflicts
#between info objects in the same map-opcode
_resolution_functions = [
ild_codegen.gen_l1_byreg_resolution_function,
ild_codegen.gen_l1_bymode_resolution_function
]
def _resolve_conflicts(agi, info_list, disp_dict):
"""Try to resolve conflicts by applying the conflict resolution
functions defined in _resolution_functions list.
@param info_list: list of info objects to that have a conflict
@type info_list: [ild_info.ild_info_t
@param disp_dict: dictionary from DISP-NT names to corresponding
codegen.array_t objects describing those NTs
@type disp_dict: { string(nt_name) : codegen.array_t(nt_arr) }
@return: codegen.function_object_t defining the conflict resolution (L1)
function for info_list's map-opcode
"""
for func in _resolution_functions:
fo = func(agi,info_list, disp_dict, _is_disp_conflict,
get_l2_fn_from_info, 'DISP')
if fo:
return fo
return None
harcoded_res_functions = {}
def gen_l1_functions_and_lookup(agi, united_lookup, disp_dict):
"""Compute L1(conflict resolution) functions list and disp_bytes lookup
tables dict.
@param agi: all generators info
@param united_lookup: the 2D lookup by map-opcode to info objects list.
united_lookup['0x0']['0x78'] == [ild_info1, ild_info2, ... ]
@type united_lookup:
{string(insn_map) : {string(opcode): [ild_info.ild_info_t]} }
"""
#list of L1 function objects that resolve conflicts in map-opcodes
#functions. This list will be dumped to xed_ild_imm_l1.h
l1_resolution_fos = []
#dictionary l1_lookup[insn_map][opcode] = l1_function_name
#this dictionary will be used to dump the has_imm lookup tables
l1_lookup = {}
#dictionary from function body(as string) to list of function objects
#with that body.
#This dict will be used to bucket identical functions in order to
#not define same functions more than once.
l1_bucket_dict = collections.defaultdict(list)
for insn_map in ild_info.get_maps(united_lookup.is_amd):
l1_lookup[insn_map] = {}
for opcode in range(0, 256):
#look in the hardcoded resolution functions
if (insn_map, hex(opcode)) in harcoded_res_functions:
l1_fn = harcoded_res_functions[(insn_map, hex(opcode))]
l1_lookup[insn_map][hex(opcode)] = l1_fn
continue
info_list = united_lookup.get_info_list(insn_map, hex(opcode))
#get only info objects with minimum priority
info_list = ild_info.get_min_prio_list(info_list)
is_conflict = _is_disp_conflict(info_list, disp_dict)
if len(info_list) > 1 and is_conflict:
l1_fo = _resolve_conflicts(agi, info_list, disp_dict)
if not l1_fo:
ildutil.ild_err('FAILED TO GENERATE L1 CONFLICT ' +
'RESOLUTION FUNCTION FOR DISP\n infos: %s' %
"\n".join([str(info) for info in info_list]))
l1_bucket_dict[l1_fo.emit_body()].append(l1_fo)
l1_fn = l1_fo.function_name
elif len(info_list) == 0:
#if map-opcode pair is undefined the lookup function ptr is
#NULL.
#This will happen for opcodes like 0F in 0F map - totally
#illegal opcodes, that should never be looked up in runtime.
#We define NULL pointer for such map-opcodes
l1_fn = '(%s)0' % (ildutil.l1_ptr_typename)
else:
#there are no conflicts, we can use L2 function as L1
info = info_list[0]
l1_fn = get_l2_fn_from_info(info, disp_dict)
l1_lookup[insn_map][hex(opcode)] = l1_fn
#there are 18 L1 functions with same body (currently, may change
#in future)
#we are going to bucket L1 functions with identical body but different
#names in order to have only one function for each unique body
#FIXME: the bucketed function name is not self descriptive
bucket_name = 'xed_lookup_function_DISP_BUCKET_%s_l1'
cur_bucket = 0
for res_fun_list in list(l1_bucket_dict.values()):
if len(res_fun_list) == 1:
#only one such function - we should define it as is
l1_resolution_fos.append(res_fun_list[0])
else:
#more than one L1 function with identical body
#we should define L1 function with that body
#and fix references in the lookup table
#the function name
cur_buck_name = bucket_name % cur_bucket
cur_bucket += 1
#fix references in the lookup table
for res_fun in res_fun_list:
for insn_map in list(l1_lookup.keys()):
for opcode in list(l1_lookup[insn_map].keys()):
cur_fn = l1_lookup[insn_map][opcode]
if cur_fn == res_fun.function_name:
l1_lookup[insn_map][opcode] = cur_buck_name
#define the L1 function and add it to the list of L1 functions
#to dump
buck_fo = res_fun_list[0]
buck_fo.function_name = cur_buck_name
l1_resolution_fos.append(buck_fo)
return l1_resolution_fos,l1_lookup
def _gen_empty_function(agi):
"""
This function is for patterns that don't set [BR]DISP_WIDTH tokens.
These patterns have disp_bytes set earlier in xed-ild.c
and we define a L2 lookup function that does nothing
"""
operand_storage = agi.operand_storage
#return_type = operand_storage.get_ctype(_imm_token)
return_type = 'void'
fo = codegen.function_object_t(_empty_fn, return_type,
static=True, inline=True)
data_name = 'x'
fo.add_arg(ildutil.ild_c_type + ' %s' % data_name)
fo.add_code('/*This function does nothing for map-opcodes whose')
fo.add_code('disp_bytes value is set earlier in xed-ild.c')
fo.add_code('(regular displacement resolution by modrm/sib)*/\n')
fo.add_code('/*pacify the compiler*/')
fo.add_code_eol('(void)%s' % data_name)
return fo
def _gen_l3_array_dict(agi, nt_names, target_op):
"""
For each NT from nt_names, generate and codegen.array_t object
return a dictionary from nt_name to array_t.
"""
nt_dict = {}
for nt_name in nt_names:
array = ild_nt.gen_nt_lookup(agi, nt_name, target_op,
target_type=ildutil.ild_c_op_type, level='l3')
nt_dict[nt_name] = array
return nt_dict
def work(agi, united_lookup, disp_nts, brdisp_nts, ild_gendir,
eosz_dict, easz_dict, debug):
"""
Main entry point of the module.
Generates all the L1-3 functions and dumps disp_bytes lookup
tables.
"""
#get all used DISP NT sequences that appear in patterns
#we are going to generate L1-3 functions only for these sequences
all_disp_seq = get_all_disp_seq(united_lookup)
#check that all sequences are actually single NTs
#(each sequence has only one NT)
#my observation is that they really are. This simplifies things
#and we are going to rely on that.
all_nts = []
for ntseq in all_disp_seq:
if len(ntseq) > 1:
ildutil.ild_err("Unexpected DISP NT SEQ %s" % ntseq)
if len(ntseq) == 0:
continue #the empty NT sequence
all_nts.append(ntseq[0])
#get only those NTs that actually appear in PATTERNs
disp_nts = list(filter(lambda nt: nt in all_nts, disp_nts))
brdisp_nts = list(filter(lambda nt: nt in all_nts, brdisp_nts))
debug.write('DISP SEQS: %s\n' % all_disp_seq)
debug.write('DISP NTs: %s\n' % disp_nts)
debug.write('BRDISP NTs: %s\n' % brdisp_nts)
brdisp_dict = _gen_l3_array_dict(agi, brdisp_nts, _brdisp_token)
disp_dict = _gen_l3_array_dict(agi, disp_nts, _disp_token)
nt_arr_list = list(brdisp_dict.values()) + list(disp_dict.values())
#create function that calls all initialization functions
init_f = ild_nt.gen_init_function(nt_arr_list, 'xed_ild_disp_l3_init')
#dump L3 functions
ild_nt.dump_lu_arrays(agi, nt_arr_list, _l3_c_fn,
mbuild.join('include-private',_l3_header_fn),
init_f)
#create L2 functions
#The thing is that we need to know for each
#DISP NT whether it depends on EOSZ or EASZ and supply appropriate arg_dict
#to gen_l2_func_list()
l2_functions = []
eosz_op = ild_eosz.get_target_opname()
easz_op = ild_easz.get_target_opname()
for nt_name,array in list(disp_dict.items()) + list(brdisp_dict.items()):
#Some DISP NTs depend on EOSZ, others on EASZ, we need to know
#that when we generate L2 functions
if eosz_op in array.get_arg_names():
arg_dict = eosz_dict
else:
arg_dict = easz_dict
flist = ild_codegen.gen_l2_func_list(agi, {nt_name:array},
arg_dict, _ild_t_disp_member)
l2_functions.extend(flist)
#create the doing-nothing L2 function for map-opcodes
#with regular displacement resolution
l2_functions.append(_gen_empty_function(agi))
#dump L2 functions
l2_headers = [ild_eosz.get_ntseq_header_fn(),
ild_easz.get_ntseq_header_fn(),
_l3_header_fn, ildutil.ild_private_header,
operand_storage.get_operand_accessors_fn()]
ild_codegen.dump_flist_2_header(agi, _l2_header_fn, l2_headers,
l2_functions)
#create the L1 functions and lookup tables
#unite brdisp and dips dictionaries
disp_dict.update(brdisp_dict)
#generate L1 functions and lookup tables
res = gen_l1_functions_and_lookup(agi, united_lookup, disp_dict)
l1_functions,l1_lookup = res
#dump L1 functions
ild_codegen.dump_flist_2_header(agi, _l1_header_fn, [_l2_header_fn],
l1_functions)
#dump lookup tables
headers = [_l1_header_fn, ildutil.ild_private_header,
operand_storage.get_operand_accessors_fn()]
ild_codegen.dump_lookup(agi, l1_lookup, _ild_t_disp_member,
_disp_lu_header_fn, headers,
ildutil.l1_ptr_typename)