mirror of
https://github.com/RPCSX/xed.git
synced 2024-11-23 11:39:40 +00:00
e7d734962c
Change-Id: I166833daaa56c33eca01bdf7b9aa6e74a490ba9a (cherry picked from commit 1212ba962dff6dfbfa0bd2469327ff447ce59058)
772 lines
27 KiB
Python
Executable File
772 lines
27 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 sys
|
|
import re
|
|
import copy
|
|
import genutil
|
|
import ildutil
|
|
import mbuild
|
|
import ild_info
|
|
import ild_storage
|
|
import ild_storage_data
|
|
import ild_eosz
|
|
import ild_easz
|
|
import ild_nt
|
|
import ild_imm
|
|
import ild_disp
|
|
import ild_modrm
|
|
import codegen
|
|
import os
|
|
import collections
|
|
import shutil
|
|
import ild_phash
|
|
import ild_codegen
|
|
import math
|
|
import opnds
|
|
import ild_cdict
|
|
import xed3_nt
|
|
import actions
|
|
import verbosity
|
|
|
|
|
|
op_bin_pattern = re.compile(r'[_10]{2,}$')
|
|
op_hex_pattern = re.compile(r'[0-9a-f]{2}$', flags=re.IGNORECASE)
|
|
reg_binding_pattern = re.compile(r'REG[[](?P<bits>0b[01_]+)]')
|
|
mod_eq_pattern = re.compile(r'MOD=(?P<bits>[0123]{1})')
|
|
mod_neq_pattern = re.compile(r'MOD(!=|=!)(?P<bits>[0123]{1})')
|
|
|
|
|
|
#debugdir is updated in init_debug
|
|
#the module.
|
|
debugdir = '.'
|
|
|
|
#the debug file
|
|
debug = None
|
|
|
|
storage_fn = 'ild_storage_data.py'
|
|
|
|
#FIXME: can we get it from generator.py?
|
|
_xed_3dnow_category = '3DNOW'
|
|
|
|
_mode_token = 'MODE'
|
|
|
|
_vexvalid_op = 'VEXVALID'
|
|
|
|
|
|
#checks if we have 3dnow instructions
|
|
#some of these instructions have 0f 0f ... opcode pattern
|
|
#hence we need to discard second 0f and not treat it as opcode
|
|
#this is a special case, there is no way to treat it in a general way
|
|
#hence we need to check for it
|
|
def _is_amd3dnow(agi):
|
|
for g in agi.generator_list:
|
|
ii = g.parser_output.instructions[0]
|
|
if genutil.field_check(ii,'iclass'):
|
|
for ii in g.parser_output.instructions:
|
|
if ii.category == _xed_3dnow_category:
|
|
return True
|
|
return False
|
|
|
|
#just to know how many there are and how hard it is
|
|
#42 nested NTs..
|
|
#mostly modrm-related
|
|
def _get_nested_nts(agi):
|
|
nested_nts = set()
|
|
for nt_name in list(agi.nonterminal_dict.keys()):
|
|
g = agi.generator_dict[nt_name]
|
|
ii = g.parser_output.instructions[0]
|
|
if genutil.field_check(ii,'iclass'):
|
|
continue #only real NTs, not instructions
|
|
for rule in g.parser_output.instructions:
|
|
for bt in rule.ipattern.bits:
|
|
if bt.is_nonterminal():
|
|
nested_nts.add(nt_name)
|
|
for op in rule.operands:
|
|
if op.type == 'nt_lookup_fn':
|
|
nested_nts.add(nt_name)
|
|
return nested_nts
|
|
|
|
|
|
###########################################################################
|
|
## < Interface for generator >
|
|
###########################################################################
|
|
|
|
#needed to init the debug directory
|
|
def init_debug(agi):
|
|
global debug
|
|
global debugdir
|
|
debugdir = agi.common.options.gendir
|
|
debug = open(mbuild.join(debugdir, 'ild_debug.txt'), 'w')
|
|
|
|
def gen_xed3(agi,ild_info,is_3dnow,ild_patterns,
|
|
all_state_space,ild_gendir,all_ops_widths):
|
|
all_cnames = set()
|
|
ptrn_dict = {}
|
|
maps = ild_info.get_maps(is_3dnow)
|
|
for insn_map in maps:
|
|
ptrn_dict[insn_map] = collections.defaultdict(list)
|
|
for ptrn in ild_patterns:
|
|
ptrn_dict[ptrn.insn_map][ptrn.opcode].append(ptrn)
|
|
#FIXME:bad name
|
|
vv_lu = {}
|
|
#mapping between a operands to their look up function
|
|
op_lu_map = {}
|
|
|
|
for vv in sorted(all_state_space['VEXVALID'].keys()):
|
|
#cdict is a 2D dictionary:
|
|
#cdict[map][opcode] = ild_cdict.constraint_dict_t
|
|
#each constraint_dict_t describes all the patterns that fall
|
|
#into corresponding map-opcode
|
|
#cnames is a set of all constraint names from the patterns
|
|
#in the given vv space
|
|
cdict,cnames = ild_cdict.get_constraints_lu_table(ptrn_dict,
|
|
is_3dnow,
|
|
all_state_space,
|
|
vv,
|
|
all_ops_widths)
|
|
all_cnames = all_cnames.union(cnames)
|
|
_msg("vv%s cnames: %s" % (vv,cnames))
|
|
|
|
#now generate the C hash functions for the constraint
|
|
#dictionaries
|
|
(ph_lu,lu_fo_list,operands_lu_list) = ild_cdict.gen_ph_fos(
|
|
agi,
|
|
cdict,
|
|
is_3dnow,
|
|
mbuild.join(ild_gendir,
|
|
'all_constraints_vv%s.txt' %vv),
|
|
ptrn_dict,
|
|
vv)
|
|
#hold only one instance of each function
|
|
for op in operands_lu_list :
|
|
if op.function_name not in op_lu_map:
|
|
op_lu_map[op.function_name] = op
|
|
|
|
vv_lu[str(vv)] = (ph_lu,lu_fo_list)
|
|
_msg("all cnames: %s" % all_cnames)
|
|
#dump the hash functions and lookup tables for obtaining these
|
|
#hash functions in the decode time
|
|
ild_codegen.dump_vv_map_lookup(agi,
|
|
vv_lu,
|
|
is_3dnow,
|
|
list(op_lu_map.values()),
|
|
h_fn='xed3-phash.h')
|
|
#xed3_nt.work generates all the functions and lookup tables for
|
|
#dynamic decoding
|
|
xed3_nt.work(agi, all_state_space, all_ops_widths, ild_patterns)
|
|
|
|
#Main entry point of the module
|
|
def work(agi):
|
|
ild_gendir = agi.common.options.gendir
|
|
init_debug(agi)
|
|
is_3dnow = _is_amd3dnow(agi)
|
|
|
|
debug.write("state_space:\n %s" % agi.common.state_space)
|
|
#return
|
|
|
|
debug.write("DUMP STORAGE %s\n" % agi.common.options.gen_ild_storage)
|
|
|
|
# Collect up interesting NT names.
|
|
# We are going to use them when we generate pattern_t objects
|
|
# and also when we build resolution functions.
|
|
eosz_nts = ild_eosz.get_eosz_binding_nts(agi)
|
|
easz_nts = ild_easz.get_easz_binding_nts(agi)
|
|
imm_nts = ild_imm.get_imm_binding_nts(agi)
|
|
|
|
disp_nts = ild_disp.get_disp_binding_nts(agi)
|
|
brdisp_nts = ild_disp.get_brdisp_binding_nts(agi)
|
|
|
|
#just for debugging
|
|
_msg("EOSZ NTS:")
|
|
for nt_name in eosz_nts:
|
|
_msg(nt_name)
|
|
|
|
_msg("\nEASZ NTS:")
|
|
for nt_name in easz_nts:
|
|
_msg(nt_name)
|
|
|
|
_msg("\nIMMNTS:")
|
|
for nt_name in imm_nts:
|
|
_msg(nt_name)
|
|
|
|
_msg("\nDISP NTS:")
|
|
for nt_name in disp_nts:
|
|
_msg(nt_name)
|
|
|
|
_msg("\nBRDISP NTS:")
|
|
for nt_name in brdisp_nts:
|
|
_msg(nt_name)
|
|
|
|
nested_nts = _get_nested_nts(agi)
|
|
_msg("\nNESTED NTS:")
|
|
for nt_name in nested_nts:
|
|
_msg(nt_name)
|
|
|
|
#Get dictionary with all legal values for all interesting operands
|
|
all_state_space = ild_cdict.get_all_constraints_state_space(agi)
|
|
_msg("ALL_STATE_SPACE:")
|
|
for k,v in list(all_state_space.items()):
|
|
_msg("%s: %s"% (k,v))
|
|
|
|
#Get widths for the operands
|
|
all_ops_widths = ild_cdict.get_state_op_widths(agi, all_state_space)
|
|
|
|
_msg("ALL_OPS_WIDTHS:")
|
|
for k,v in list(all_ops_widths.items()):
|
|
_msg("%s: %s"% (k,v))
|
|
|
|
#generate a list of pattern_t objects that describes the ISA.
|
|
#This is the main data structure for XED3
|
|
ild_patterns = get_patterns(agi, is_3dnow, eosz_nts, easz_nts, imm_nts,
|
|
disp_nts, brdisp_nts, all_state_space)
|
|
|
|
if ild_patterns:
|
|
if agi.common.options.gen_ild_storage:
|
|
#dump the ild_storage_data.py file
|
|
emit_gen_info_lookup(agi, ild_patterns, is_3dnow, debug)
|
|
reload(ild_storage_data)
|
|
|
|
#get ild_storage_t object - the main data structure for ILD
|
|
#essentially a 2D dictionary:
|
|
#united_lookup[map][opcode] == [ ild_info_t ]
|
|
#the ild_info_t objects are obtained both from grammar and
|
|
#ild_storage_data.py file, so that if ild_info_t objects are
|
|
#defined in ild_storage_data.py file, ILD will have information
|
|
#about illegal map-opcodes too.
|
|
united_lookup = _get_united_lookup(ild_patterns,is_3dnow)
|
|
|
|
#generate modrm lookup tables
|
|
ild_modrm.work(agi, united_lookup, debug)
|
|
|
|
#dump_patterns is for debugging
|
|
if verbosity.vild():
|
|
dump_patterns(ild_patterns,
|
|
mbuild.join(ild_gendir, 'all_patterns.txt'))
|
|
|
|
|
|
eosz_dict = ild_eosz.work(agi, united_lookup,
|
|
eosz_nts, ild_gendir, debug)
|
|
|
|
easz_dict = ild_easz.work(agi, united_lookup,
|
|
easz_nts, ild_gendir, debug)
|
|
|
|
#dump operand accessor functions
|
|
agi.operand_storage.dump_operand_accessors(agi)
|
|
|
|
|
|
if eosz_dict and easz_dict:
|
|
ild_imm.work(agi, united_lookup, imm_nts, ild_gendir,
|
|
eosz_dict, debug)
|
|
ild_disp.work(agi, united_lookup, disp_nts, brdisp_nts,
|
|
ild_gendir, eosz_dict, easz_dict, debug)
|
|
|
|
|
|
#dump scanners headers - they might be different for different
|
|
#models.
|
|
scanners_dict = agi.common.ild_scanners_dict
|
|
dump_header_with_header(agi, 'xed-ild-scanners.h', scanners_dict)
|
|
|
|
getters_dict = agi.common.ild_getters_dict
|
|
dump_header_with_header(agi, 'xed-ild-getters.h', getters_dict)
|
|
|
|
gen_xed3(agi,ild_info,is_3dnow,ild_patterns,
|
|
all_state_space,ild_gendir,all_ops_widths)
|
|
|
|
def dump_header_with_header(agi, fname, header_dict):
|
|
""" emit the header fname.
|
|
add the header in header_dict with the maximal id.
|
|
|
|
this mechanism is used in order to choose header
|
|
files in the build time,
|
|
different build configuration use different header files.
|
|
e.g. when building without AVX512 we are using the basic getters.
|
|
when building with AVX512 the header that is used comes
|
|
form avx512 layer.
|
|
|
|
FIXME: when all avx512 will move into the base layer
|
|
we can removed this
|
|
mechanism and use C defines. """
|
|
|
|
_msg("HEADER DICT: %s" % header_dict)
|
|
|
|
xeddir = os.path.abspath(agi.common.options.xeddir)
|
|
gendir = mbuild.join(agi.common.options.gendir,'include-private')
|
|
if header_dict:
|
|
header = max(header_dict, key=header_dict.get)
|
|
header = os.path.normcase(header)
|
|
|
|
header_basename = os.path.basename(header)
|
|
|
|
_msg("HEADER: %s" %header)
|
|
if os.path.exists(header):
|
|
_msg("HEADER EXISTS: %s" %header)
|
|
else:
|
|
_msg("BADNESS - HEADER DOES NOT EXIST: %s" %header)
|
|
try:
|
|
shutil.copyfile(header, os.path.join(gendir, header_basename))
|
|
except:
|
|
ildutil.ild_err("Failed to copyfile src=%s dst=%s"%
|
|
(header,
|
|
os.path.join(gendir, header_basename)))
|
|
else:
|
|
header_basename = None
|
|
h_file = codegen.xed_file_emitter_t(xeddir,gendir,
|
|
fname, shell_file=False,
|
|
is_private= True)
|
|
if header_basename:
|
|
h_file.add_header(header_basename)
|
|
h_file.start()
|
|
h_file.close()
|
|
|
|
def get_patterns(agi, is_3dnow, eosz_nts, easz_nts,
|
|
imm_nts, disp_nts, brdisp_nts, all_state_space):
|
|
"""
|
|
This function generates the pattern_t objects that have all the necessary
|
|
information for the ILD. Returns these objects as a list.
|
|
"""
|
|
patterns = []
|
|
for g in agi.generator_list:
|
|
ii = g.parser_output.instructions[0]
|
|
if genutil.field_check(ii,'iclass'):
|
|
for ii in g.parser_output.instructions:
|
|
ptrn = pattern_t(ii, is_3dnow, eosz_nts,
|
|
easz_nts, imm_nts, disp_nts, brdisp_nts,
|
|
ildutil.mode_space, all_state_space)
|
|
patterns.append(ptrn)
|
|
if ptrn.incomplete_opcode:
|
|
expanded_ptrns = ptrn.expand_opcode()
|
|
patterns.extend(expanded_ptrns)
|
|
return patterns
|
|
|
|
|
|
def _get_united_lookup(ptrn_list,is_3dnow):
|
|
"""
|
|
Combine storage obtained from grammar and from ILD storage
|
|
@return: ild_info.storage_t object
|
|
"""
|
|
#build an ild_info_storage_t object from grammar
|
|
from_grammar = get_info_storage(ptrn_list, 0, is_3dnow)
|
|
|
|
#get an ild_info_storage_t object from ild python-based storage
|
|
from_storage = ild_storage_data.gen_ild_info()
|
|
|
|
#FIXME: should we make is_amd=(is_3dnow or from_storage.is_3dnow)?
|
|
united_lookup = ild_storage.ild_storage_t(is_amd=is_3dnow)
|
|
|
|
#unite the lookups, conflicts will be resolved by priority
|
|
for insn_map in ild_info.get_maps(is_3dnow):
|
|
for op in range(0, 256):
|
|
ulist = (from_grammar.get_info_list(insn_map, hex(op)) +
|
|
from_storage.get_info_list(insn_map, hex(op)))
|
|
united_lookup.set_info_list(insn_map, hex(op), ulist)
|
|
return united_lookup
|
|
|
|
|
|
def get_info_storage(ptrn_list, priority, is_3dnow):
|
|
"""
|
|
convert list of pattern_t objects to ild_storage_t object
|
|
"""
|
|
storage = ild_storage.ild_storage_t(is_amd=is_3dnow)
|
|
|
|
for p in ptrn_list:
|
|
info = ild_info.ptrn_to_info(p, priority)
|
|
if not (info in storage.get_info_list(p.insn_map,p.opcode)):
|
|
storage.append_info(p.insn_map, p.opcode, info)
|
|
return storage
|
|
|
|
|
|
def emit_gen_info_lookup(agi, ptrn_list, is_3dnow, debug):
|
|
debug.write("DUMPING ILD STORAGE\n")
|
|
f = codegen.xed_file_emitter_t(agi.common.options.xeddir,
|
|
agi.common.options.xeddir,
|
|
storage_fn,
|
|
shell_file=True)
|
|
|
|
storage = get_info_storage(ptrn_list, ild_info.storage_priority, is_3dnow)
|
|
#list_name = "info_lookup['%s']['%s']"
|
|
list_name = 'info_list'
|
|
indent = ' ' * 4
|
|
f.start()
|
|
f.add_code("import ild_info")
|
|
f.add_code("import ild_storage\n\n\n")
|
|
|
|
f.add_code("#GENERATED FILE - DO NOT EDIT\n\n\n")
|
|
f.write("def gen_ild_info():\n")
|
|
f.write(indent + "storage = ild_storage.ild_storage_t(is_amd=%s)\n" %
|
|
is_3dnow)
|
|
|
|
for insn_map in ild_info.get_dump_maps():
|
|
for op in range(0, 256):
|
|
for info in storage.get_info_list(insn_map, hex(op)):
|
|
f.write("%s#MAP:%s OPCODE:%s\n" %
|
|
(indent, info.insn_map, info.opcode))
|
|
f.write("%sinfo_list = storage.get_info_list('%s','%s')\n" %
|
|
(indent, insn_map, hex(op)))
|
|
emit_add_info_call(info, list_name,
|
|
f, indent)
|
|
f.write(indent + "return storage\n")
|
|
f.close()
|
|
|
|
def emit_add_info_call(info, list_name, f, indent=''):
|
|
s = []
|
|
tab = ' ' * 4
|
|
s.append(indent + "%s.append(ild_info.ild_info_t(\n%sinsn_map='%s'" %
|
|
(list_name,(indent+tab),info.insn_map))
|
|
s.append("opcode='%s'" % info.opcode)
|
|
s.append("incomplete_opcode=%s" % info.incomplete_opcode)
|
|
s.append("missing_bits=%s" % info.missing_bits)
|
|
s.append("has_modrm='%s'" % info.has_modrm)
|
|
s.append("imm_nt_seq=%s" % info.imm_nt_seq)
|
|
s.append("disp_nt_seq=%s" % info.disp_nt_seq)
|
|
s.append("eosz_nt_seq=%s" % info.eosz_nt_seq)
|
|
s.append("easz_nt_seq=%s" % info.easz_nt_seq)
|
|
s.append("ext_opcode=%s" % info.ext_opcode)
|
|
s.append("mode=%s" % info.mode)
|
|
s.append("priority=%s" % ild_info.storage_priority)
|
|
f.write((",\n%s" % (indent + tab)).join(s) + "))\n\n")
|
|
|
|
|
|
|
|
#this is for debugging mostly. Also a good source of info on instruction set.
|
|
def dump_patterns(patterns, fname):
|
|
f = open(fname, 'w')
|
|
for pattern in patterns:
|
|
f.write( '%s\n' % pattern)
|
|
f.close()
|
|
|
|
###########################################################################
|
|
## </ Interface for generator >
|
|
###########################################################################
|
|
|
|
|
|
|
|
#Maybe pattern_t should inherit from instruction_info_t
|
|
#Let it inherit from object for now.
|
|
class pattern_t(object):
|
|
|
|
# keys will be in a special order which is why we build it
|
|
# from a list.
|
|
phys_map_keys = []
|
|
phys_map_dir = {}
|
|
first = True
|
|
|
|
def __init__(self, ii, is_3dnow, eosz_nts,
|
|
easz_nts, imm_nts, disp_nts, brdisp_nts, mode_space,
|
|
state_space):
|
|
|
|
# FIXME 2012-06-19 MJC: is there a better way to do complex
|
|
# init of class attributes?
|
|
if pattern_t.first:
|
|
pattern_t.first = False
|
|
self._setup_phys_map(is_3dnow)
|
|
|
|
self.ptrn = ii.ipattern_input
|
|
self.ptrn_wrds = self.ptrn.split()
|
|
self.iclass = ii.iclass
|
|
self.legal = True
|
|
|
|
#amd 3dnow instructions have nasty 0f 0f ... opcode pattern
|
|
#in which second 0f is not an opcode! This should be treated
|
|
#in a special way
|
|
self.amd3dnow_build = is_3dnow #this one is NOT used DELETE IT ???
|
|
|
|
self.category = ii.category
|
|
#FIXME: remove all members of ii stored directly as members
|
|
self.ii = ii
|
|
|
|
#incomplete_opcode is used for expanding opcodes that have registers
|
|
#embedded in them
|
|
self.incomplete_opcode = False
|
|
|
|
#number of missing bits in incomplete opcode. usually 0 or 3
|
|
self.missing_bits = 0
|
|
|
|
self.insn_map = None
|
|
self.opcode = None
|
|
|
|
self.space = None # LEGACY|VEX|EVEX
|
|
self.has_modrm = False
|
|
|
|
self.imm_nt_seq = None
|
|
|
|
self.disp_nt_seq = None
|
|
|
|
#modrm.reg bits value, set only when it is explicitly
|
|
#e.g. bounded: REG[010]
|
|
self.ext_opcode = None
|
|
|
|
#all legal values for MODE operand in this pattern
|
|
self.mode = None
|
|
|
|
|
|
#an ordered string of EOSZ setting NTs in the pattern
|
|
#we will use it to create the eosz lookup table for the pattern
|
|
self.eosz_nt_seq = None
|
|
|
|
|
|
#same for EASZ
|
|
self.easz_nt_seq = None
|
|
|
|
#operand deciders of the pattern
|
|
#FIXME: not finished yet
|
|
self.constraints = collections.defaultdict(dict)
|
|
|
|
|
|
insn_map,opcode = self.get_map_opcode()
|
|
self.insn_map = insn_map
|
|
self.opcode = opcode
|
|
|
|
self.has_modrm = ild_modrm.get_hasmodrm(self.ptrn)
|
|
self.set_ext_opcode()
|
|
|
|
self.set_mode(ii, mode_space)
|
|
|
|
|
|
self.eosz_nt_seq = ild_eosz.get_eosz_nt_seq(self.ptrn_wrds,
|
|
eosz_nts)
|
|
|
|
self.easz_nt_seq = ild_easz.get_easz_nt_seq(self.ptrn_wrds,
|
|
easz_nts)
|
|
|
|
self.imm_nt_seq = ild_imm.get_imm_nt_seq(self.ptrn_wrds, imm_nts)
|
|
|
|
self.disp_nt_seq = ild_disp.get_disp_nt_seq(self.ptrn_wrds,
|
|
disp_nts.union(brdisp_nts))
|
|
|
|
self.set_constraints(ii, state_space)
|
|
self.actions = [actions.gen_return_action(ii.inum)]
|
|
|
|
#Not implementing this yet.
|
|
#Will implement after code review for has_modrm
|
|
#self.set_hasimm()
|
|
#self.set_pfx_table()
|
|
|
|
#FIXME: for anaisys only
|
|
if self.is_3dnow():
|
|
if not self.has_modrm:
|
|
_msg('3DNOW with no MODRM: %s\n' % self)
|
|
|
|
def is_3dnow(self):
|
|
return self.category == _xed_3dnow_category
|
|
|
|
def is_legal(self):
|
|
return (self.legal and
|
|
self.opcode != None and
|
|
self.insn_map != None and
|
|
self.space != None)
|
|
|
|
|
|
def set_ext_opcode(self):
|
|
#inline captures MOD[mm] REG[rrr] RM[nnn]
|
|
#or REG[111] etc. -- can be constant
|
|
for w in self.ptrn_wrds:
|
|
pb = reg_binding_pattern.match(w)
|
|
if pb:
|
|
bits = pb.group('bits')
|
|
self.ext_opcode = genutil.make_numeric(bits)
|
|
return
|
|
|
|
def set_constraints(self, ii, state_space):
|
|
#FIXME: this assumes, that there are no contradictions
|
|
#between constraints on the same operand.
|
|
#If there are, e.g. MOD=3 ... MOD=1, both values will be
|
|
#set as legal.. check such things here?
|
|
|
|
#set constraints that come from operands deciders
|
|
xed3_nt.get_ii_constraints(ii, state_space, self.constraints)
|
|
#print "CONSTRAINTS: {}".format(self.constraints)
|
|
|
|
#special care for VEXVALID - it makes it easy to dispatch
|
|
#vex and legacy instructions:
|
|
#for legacy we will explicitly set VEXVALID=0
|
|
if _vexvalid_op not in self.constraints:
|
|
self.constraints[_vexvalid_op] = {0:True}
|
|
|
|
def set_mode(self, ii, mode_space):
|
|
for bit_info in ii.ipattern.bits:
|
|
if bit_info.token == _mode_token:
|
|
#self.refining = [111555]
|
|
if bit_info.test == 'eq':
|
|
self.mode = [bit_info.requirement]
|
|
else:
|
|
mod_space = copy.deepcopy(mode_space)
|
|
mod_space.remove(bit_info.requirement)
|
|
self.mode = mod_space
|
|
return
|
|
#FIXME: when MODE is not mentioned, we assume that any value is
|
|
#legal, it should not mess the conflict resolution.
|
|
#But maybe None is better
|
|
self.mode = ildutil.mode_space
|
|
|
|
def parse_opcode(self, op_str):
|
|
val = None
|
|
if genutil.numeric(op_str):
|
|
val = genutil.make_numeric(op_str)
|
|
|
|
# special check for partial binary numbers as opcodes
|
|
if genutil.is_binary(op_str):
|
|
bin_str = re.sub('^0b', '', op_str)
|
|
bin_str = re.sub('_', '', bin_str)
|
|
#if it is bin string, it might be incomplete, and we
|
|
#assume that given bits are msb. Hence we have to
|
|
#shift left
|
|
|
|
if len(bin_str) > 8:
|
|
ildutil.ild_err("Unexpectedly long binary opcode: %s" %
|
|
bin_str)
|
|
|
|
if len(bin_str) < 8:
|
|
self.missing_bits = 8-len(bin_str)
|
|
val = val << (self.missing_bits)
|
|
self.incomplete_opcode = True
|
|
_msg('incomplete opcode for iclass %s, pttrn %s' %
|
|
(self.iclass, self.ptrn))
|
|
|
|
return val
|
|
|
|
def err(self, msg):
|
|
self.legal = False
|
|
sys.stderr.write("ILD_PARSER PATTERN ERROR: %s\n\nPattern:\n%s\n" %
|
|
(msg, self))
|
|
mbuild.msgb("ILD_PARSER ERROR", msg)
|
|
mbuild.msgb("ILD_PARSER", "ABORTED ILD generation")
|
|
#sys.exit(1)
|
|
|
|
#if opcode is incomplete, than we have 0's in all the missing bits and
|
|
#need to create copies of the pattern_t that have all other possible
|
|
#variations of the opcode. For example PUSH instruction has opcode 0x50
|
|
#and in expand_opcode method we will create a list of pattern_t objects
|
|
#that have opcodes 0x51-0x57
|
|
#We don't need to create the 0x50 variant, because it already exists
|
|
#(it is the current self)
|
|
#That way we will cover all the legal opcodes for the given incomplete
|
|
#opcode.
|
|
def expand_opcode(self):
|
|
expanded = []
|
|
if self.incomplete_opcode:
|
|
if 'RM[rrr]' in self.ptrn or 'REG[rrr]' in self.ptrn:
|
|
_msg('Expanding opcode for %s' % self.iclass)
|
|
#FIXME: MJC: was assuming hex for self.opcode (2012-06-19)
|
|
opcode = genutil.make_numeric(self.opcode)
|
|
#iterating through all legal variations of the incomplete
|
|
#opcode.
|
|
#Since the variant with all missing bits == 0 was already
|
|
#created (it is the current self), we need to iterate through
|
|
#1 to 2 ^ (self.missing_bits)
|
|
for i in range(1, 2**self.missing_bits):
|
|
new_ptrn = copy.deepcopy(self)
|
|
new_ptrn.opcode = hex(opcode | i)
|
|
expanded.append(new_ptrn)
|
|
return expanded
|
|
|
|
def get_opcode(self, tokens):
|
|
opcode = None
|
|
for token in tokens:
|
|
opcode = self.parse_opcode(token)
|
|
if opcode != None:
|
|
break
|
|
if opcode == None:
|
|
ildutil.ild_err("Failed to parse op_str with " +
|
|
"from tokens %s" %( tokens))
|
|
return hex(opcode)
|
|
|
|
def _setup_phys_map(self,include_amd):
|
|
phys_map_list = [
|
|
('0x0F 0x38','0x0F38'),
|
|
('0x0F 0x3A','0x0F3A'),
|
|
('V0F38','0x0F38'),
|
|
('V0F3A','0x0F3A'),
|
|
# V0F must be after the V0F38 & V0F3A
|
|
('V0F','0x0F'),
|
|
('MAP5','MAP5'),
|
|
('MAP6','MAP6') ]
|
|
|
|
# The AMD map must be before the naked 0x0F map
|
|
if include_amd:
|
|
phys_map_list.append(('0x0F 0x0F','0x0F0F'))
|
|
phys_map_list.append(('XMAP8','XMAP8'))
|
|
phys_map_list.append(('XMAP9','XMAP9'))
|
|
phys_map_list.append(('XMAPA','XMAPA'))
|
|
|
|
# and finally Map 1 ...
|
|
phys_map_list.append(('0x0F','0x0F'))
|
|
|
|
# keys will be in a special order which is why we build it
|
|
# from a list.
|
|
pattern_t.phys_map_keys = []
|
|
pattern_t.phys_map_dir = {}
|
|
|
|
for a,b in phys_map_list:
|
|
pattern_t.phys_map_keys.append(a)
|
|
pattern_t.phys_map_dir[a] = b
|
|
|
|
def get_map_opcode(self):
|
|
insn_map = '0x0'
|
|
s = self.ptrn
|
|
for mpat in pattern_t.phys_map_keys:
|
|
if s.find(mpat) != -1:
|
|
insn_map = pattern_t.phys_map_dir[mpat]
|
|
# remove the first matching map-like thing
|
|
s = re.sub(mpat,'',s, count=1)
|
|
break
|
|
|
|
tokens = s.split()
|
|
opcode = self.get_opcode(tokens)
|
|
return insn_map, opcode
|
|
|
|
def has_emit_action(self):
|
|
''' This function is needed in order to match the interface of rule_t
|
|
it has no real meaning for the docoder '''
|
|
return False
|
|
|
|
def __str__(self):
|
|
printed_members = []
|
|
printed_members.append('ICLASS\t: %s' % self.iclass)
|
|
printed_members.append('PATTERN\t: %s' % self.ptrn)
|
|
#putting map and opcode in one line for easier grepping
|
|
#of the pattern list dump - very handy for different kinds of analysis
|
|
printed_members.append('MAP:%s OPCODE:%s' %
|
|
(self.insn_map, self.opcode))
|
|
printed_members.append('EXT_OPCODE\t: %s' % self.ext_opcode)
|
|
printed_members.append('MODE\t: %s' % self.mode)
|
|
printed_members.append('INCOMPLETE_OPCODE\t: %s' %
|
|
self.incomplete_opcode)
|
|
printed_members.append('HAS_MODRM\t: %s' % self.has_modrm)
|
|
printed_members.append('EOSZ_SEQ:\t %s' % self.eosz_nt_seq)
|
|
printed_members.append('EASZ_SEQ:\t %s' % self.easz_nt_seq)
|
|
printed_members.append('IMM_SEQ\t: %s' % self.imm_nt_seq)
|
|
printed_members.append('DISP_SEQ\t: %s' % self.disp_nt_seq)
|
|
printed_members.append('CONSTRAINTS\t: %s' % self.constraints)
|
|
printed_members.append('INUM\t: %s' % self.ii.inum)
|
|
|
|
return "{\n"+ ",\n".join(printed_members) + "\n}"
|
|
|
|
def __eq__(self, other):
|
|
return (other != None and
|
|
self.ptrn == other.ptrn and
|
|
self.iclass == other.iclass)
|
|
|
|
def __ne__(self, other):
|
|
return (other == None or
|
|
self.ptrn != other.ptrn or
|
|
self.iclass != other.iclass)
|
|
|
|
def _msg(s):
|
|
debug.write("%s\n" % (s))
|
|
|