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)
422 lines
15 KiB
Python
Executable File
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)
|
|
|
|
|