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)
578 lines
18 KiB
Python
Executable File
578 lines
18 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- python -*-
|
|
#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
|
|
|
|
# FIXME: could cut table sizes by rusing identical entries in the
|
|
# simple flags table. 2007-05-02
|
|
from __future__ import print_function
|
|
import re
|
|
import os
|
|
import sys
|
|
from genutil import *
|
|
|
|
def _convert_to_list_of_string(x):
|
|
return [ str(a) for a in x]
|
|
def _curly_list(x):
|
|
return ('{' + ','.join(x) + '},')
|
|
def _curly_string(x):
|
|
return ('{' + x + '}')
|
|
|
|
class flag_set_t(object):
|
|
field_pairs = [('cf',1), ('must_be_1',1),
|
|
('pf',1), ('must_be_0a',1),
|
|
('af',1), ('must_be_0b',1),
|
|
('zf',1), ('sf',1),
|
|
('tf',1), ('_if',1),
|
|
('df',1), ('of',1),
|
|
('iopl',2), # 2b wide field
|
|
('nt',1), ('must_be_0c',1),
|
|
('rf',1), ('vm',1),
|
|
('ac',1), ('vif',1),
|
|
('vip',1), ('id',1),
|
|
('must_be_0d',2),
|
|
('must_be_0e',4),
|
|
|
|
# not part of [er]flags, just stored that way for convenience.
|
|
('fc0',1),
|
|
('fc1',1),
|
|
('fc2',1),
|
|
('fc3',1) ]
|
|
field_names = [ x[0] for x in field_pairs]
|
|
|
|
def __init__(self, very_technically_accurate=False):
|
|
for (f,w) in flag_set_t.field_pairs:
|
|
if very_technically_accurate and f.startswith('must_be_1'):
|
|
setattr(self,f,1)
|
|
else:
|
|
setattr(self,f,0)
|
|
|
|
def set(self,fld,val=1):
|
|
if fld == 'if':
|
|
fld = '_if' # recode this one to avoid keyword clash
|
|
if fld == 'iopl':
|
|
val = 3 # turn on both bits for IOPL. Just a convention
|
|
|
|
if fld in flag_set_t.field_names:
|
|
setattr(self,fld,val)
|
|
else:
|
|
die("Bad flags field name: {}".format(fld) )
|
|
|
|
def as_integer(self):
|
|
s = 0
|
|
n = 0
|
|
for (f,w) in flag_set_t.field_pairs:
|
|
mask = (1<<w)-1
|
|
s = s | (getattr(self,f) & mask) << n
|
|
n = n + w
|
|
return s
|
|
|
|
def as_hex(self):
|
|
return hex(self.as_integer())
|
|
|
|
class flag_action_t(object):
|
|
"""Simple flag/actions pairs. If the input is 'nothing' we do not have any flag action"""
|
|
valid_flag_actions = ['mod','tst','u','0','1','ah', 'pop'] # FIXME: x86 specific
|
|
def __init__(self, s):
|
|
self.flag = None
|
|
self.action = None # Could be mod,tst,u,0,1, ah, pop
|
|
if s != 'nothing':
|
|
(self.flag,self.action) = s.split('-')
|
|
if self.action not in flag_action_t.valid_flag_actions:
|
|
die("Invalid flag_action_t in %s" % s)
|
|
def __str__(self):
|
|
if self.flag == None:
|
|
return 'nothing'
|
|
else:
|
|
return "%s-%s" % (self.flag , self.action)
|
|
def is_nothing(self):
|
|
if self.flag == None:
|
|
return True
|
|
return False
|
|
def reads_flag(self):
|
|
if self.action == 'tst':
|
|
return True
|
|
return False
|
|
def writes_flag(self):
|
|
if self.action != 'tst':
|
|
return True
|
|
return False
|
|
def makes_flag_undefined(self):
|
|
return self.action == 'u'
|
|
|
|
|
|
|
|
class flags_rec_t(object):
|
|
"""Collection of flag_action_t records with an optional qualifier
|
|
for things conditioned on immediates, and may/must/readonly field"""
|
|
|
|
valid_flags_semantics_specifiers = ['MAY', 'MUST', 'READONLY' ]
|
|
valid_flags_qualifiers = ['REP', 'NOREP', 'IMM0', 'IMM1', 'IMMx' ]
|
|
|
|
_flag_pattern = re.compile(r'\s*(?P<qualifiers>.*)\s+[[](?P<flags>.*)[]]')
|
|
def __init__(self,input_chunk):
|
|
self.qualifier = None
|
|
self.may_must = None # Could be READONLY, MAY or MUST, REP_MAY
|
|
self.flag_actions = [] # [ flag_action_t ]
|
|
m = flags_rec_t._flag_pattern.search(input_chunk)
|
|
if m:
|
|
flags_input = m.group('flags').strip().split()
|
|
qualifiers = m.group('qualifiers').strip().split()
|
|
else:
|
|
die("Could not find flags in %s" % input_chunk)
|
|
|
|
first = qualifiers[0]
|
|
if first in flags_rec_t.valid_flags_qualifiers:
|
|
self.qualifier = first
|
|
qualifiers.pop(0)
|
|
self.may_must = qualifiers[0]
|
|
if self.may_must not in flags_rec_t.valid_flags_semantics_specifiers:
|
|
die("Invalid flags specification: %s" % input_chunk)
|
|
|
|
self.read_set = flag_set_t()
|
|
self.write_set = flag_set_t()
|
|
self.undefined_set = flag_set_t()
|
|
|
|
self.flag_action_index = -1
|
|
self.simple_id = -1
|
|
|
|
for flag_action_str in flags_input:
|
|
fa = flag_action_t(flag_action_str)
|
|
self.flag_actions.append(fa)
|
|
if fa.flag:
|
|
if fa.reads_flag():
|
|
self.read_set.set(fa.flag)
|
|
if fa.writes_flag():
|
|
self.write_set.set(fa.flag)
|
|
if fa.makes_flag_undefined():
|
|
self.undefined_set.set(fa.flag)
|
|
else:
|
|
sys.stderr.write("Bogus flag: {}\n".format(flag_action_str))
|
|
|
|
def get_number_of_flag_actions(self):
|
|
return len(self.flag_actions)
|
|
|
|
def get_flag_action_index(self):
|
|
return self.flag_action_index
|
|
def set_flag_action_index(self,n):
|
|
self.flag_action_index = n
|
|
def get_simple_id(self):
|
|
return self.simple_id
|
|
def set_simple_id(self,n):
|
|
self.simple_id = n
|
|
|
|
def x86_flags(self):
|
|
"""Return True if any of the flags are x86 flags. False otherwise"""
|
|
for flag_action in self.flag_actions:
|
|
s = flag_action.flag
|
|
if s != 'fc0' and s != 'fc1' and s != 'fc2' and s != 'fc3':
|
|
return True
|
|
return False
|
|
|
|
def reads_flags(self):
|
|
for fa in self.flag_actions:
|
|
if fa.reads_flag():
|
|
return True
|
|
return False
|
|
def writes_flags(self):
|
|
for fa in self.flag_actions:
|
|
if fa.writes_flag():
|
|
return True
|
|
return False
|
|
def conditional_writes_flags(self):
|
|
if self.writes_flags() and (self.may_must == 'MAY' or self.may_must == 'REP_MAY'):
|
|
return True
|
|
return False
|
|
|
|
def is_simple(self):
|
|
if self.qualifier == None:
|
|
return True
|
|
return False
|
|
def is_complex(self):
|
|
if self.qualifier != None:
|
|
return True
|
|
return False
|
|
def is_rep(self):
|
|
if self.qualifier == 'REP' or self.qualifier == 'NOREP':
|
|
return True
|
|
return False
|
|
def is_imm(self):
|
|
if self.qualifier == 'IMM0' or \
|
|
self.qualifier == 'IMM1' or \
|
|
self.qualifier == 'IMMx':
|
|
return True
|
|
return False
|
|
|
|
def __str__(self):
|
|
s = []
|
|
if self.qualifier != None:
|
|
s.append(self.qualifier)
|
|
s.append(self.may_must)
|
|
s.append('[')
|
|
s.extend([str(x) for x in self.flag_actions])
|
|
s.append(']')
|
|
return ' '.join(s)
|
|
|
|
def is_nothing(self):
|
|
if len(self.flag_actions) == 1:
|
|
fa = self.flag_actions[0]
|
|
if fa.is_nothing():
|
|
return True
|
|
return False
|
|
|
|
def emit_code(self, prefix,fo):
|
|
for fa in self.flag_actions:
|
|
if fa.flag:
|
|
s = "xed_simple_flag_set_flag_action(%s,XED_FLAG_%s,XED_FLAG_ACTION_%s)" % \
|
|
(prefix, fa.flag, fa.action)
|
|
fo.add_code_eol(s)
|
|
|
|
def identifier(self,limit=99):
|
|
s = []
|
|
for i,fa in enumerate(self.flag_actions):
|
|
if not fa.flag:
|
|
die("Bogus flag!")
|
|
s.append("{}-{}".format(fa.flag, fa.action))
|
|
if i >= limit:
|
|
break
|
|
return ":".join(s)
|
|
|
|
def emit_data_record(self, fo):
|
|
for i,fa in enumerate(self.flag_actions):
|
|
if not fa.flag:
|
|
die("Bogus flag!")
|
|
s = "/* {} */ {{ XED_FLAG_{},XED_FLAG_ACTION_{} }},".format(
|
|
self.get_flag_action_index() + i, fa.flag, fa.action)
|
|
fo.add_code(s)
|
|
|
|
# Need to emit 3 tables:
|
|
# xed_flag_action_t array
|
|
# xed_simple_flag_t array
|
|
# xed_complex_flag_t array
|
|
#
|
|
# constraint: need hand out indices to the simple and complex table as
|
|
# instructions are encountered during code gen so that I can give the
|
|
# instruction record the index to the complex or simple flags table.
|
|
#
|
|
# most instructions are simple flags and have one simple record per
|
|
# instruction some are complex and have multiple simple flags records
|
|
# per instruction.
|
|
#
|
|
|
|
|
|
|
|
class flags_info_t(object):
|
|
"""Collection of flags_rec_t records"""
|
|
|
|
_flag_simple_rec = 1
|
|
_flag_complex_rec = 1
|
|
_max_actions_per_simple_flag = 0
|
|
_max_flag_actions = 0
|
|
# for unique-ifying the flag-actions array.
|
|
_fa_table = {}
|
|
_fr_table = {}
|
|
|
|
def __init__(self,input_line):
|
|
# flags_recs is a list of flag_rec_t's. Usually 0 or 1. Sometimes 3
|
|
if input_line != '':
|
|
lst = input_line.split(',')
|
|
self.flags_recs = [ flags_rec_t(x.strip()) for x in lst]
|
|
else:
|
|
self.flags_recs = []
|
|
|
|
self.complex_id = -1
|
|
def set_complex_id(self,n):
|
|
self.complex_id = n
|
|
def get_complex_id(self,n):
|
|
return self.complex_id
|
|
|
|
def __str__(self):
|
|
s = ', '.join( [str(x) for x in self.flags_recs] )
|
|
return s
|
|
|
|
def x87_flags(self):
|
|
"""Return True if all the flags are x87 flags. And False if any are x86 flags"""
|
|
for fr in self.flags_recs:
|
|
if fr.x86_flags():
|
|
return False
|
|
return True
|
|
|
|
def x86_flags(self):
|
|
"""Return True if any flags are x86 flags"""
|
|
for fr in self.flags_recs:
|
|
if fr.x86_flags():
|
|
return True
|
|
return False
|
|
|
|
|
|
def rw_action(self):
|
|
"""Return one of: r, w, cw, rcw or rw. This is the r/w action
|
|
for a rFLAGS() NTLUF."""
|
|
r = ''
|
|
w = ''
|
|
c= ''
|
|
has_nothing_record = False
|
|
for fr in self.flags_recs:
|
|
if fr.is_nothing():
|
|
has_nothing_record=True
|
|
if fr.reads_flags():
|
|
r = 'r'
|
|
if fr.writes_flags():
|
|
w = 'w'
|
|
if fr.conditional_writes_flags():
|
|
# things that are conditional writes are also writes
|
|
c = 'c'
|
|
|
|
if has_nothing_record:
|
|
c = 'c'
|
|
retval = "%s%s%s" % (r,c,w)
|
|
return retval
|
|
|
|
def make_case_name(self,qualifier):
|
|
if qualifier == 'IMMx':
|
|
return 'IMMED_OTHER'
|
|
elif qualifier == 'IMM0':
|
|
return 'IMMED_ZERO'
|
|
elif qualifier == 'IMM1':
|
|
return 'IMMED_ONE'
|
|
elif qualifier == 'REP':
|
|
return 'HAS_REP'
|
|
elif qualifier == 'NOREP':
|
|
return 'NO_REP'
|
|
else:
|
|
die("Unhandled flags qualifier: %s" % qualifier)
|
|
|
|
def is_complex(self):
|
|
for x in self.flags_recs:
|
|
if x.is_complex():
|
|
return True
|
|
return False
|
|
|
|
def is_rep(self):
|
|
for x in self.flags_recs:
|
|
if x.is_rep():
|
|
return True
|
|
return False
|
|
|
|
def is_imm(self):
|
|
for x in self.flags_recs:
|
|
if x.is_imm():
|
|
return True
|
|
return False
|
|
|
|
|
|
def _compute_assign_flag_action_id(self, fr, fo_flag_actions):
|
|
|
|
# compute an identifying string as an identifier of the (flag, action)+ sequence
|
|
id = fr.identifier()
|
|
|
|
try:
|
|
t = flags_info_t._fa_table[id]
|
|
is_new = False
|
|
except:
|
|
is_new = True
|
|
t = flags_info_t._max_flag_actions
|
|
# skip ahead by # of (flag,actions) entries
|
|
flags_info_t._max_flag_actions += fr.get_number_of_flag_actions()
|
|
|
|
# install all the prefixes of the current flag-action group that
|
|
# share the same starting point for extra compression
|
|
|
|
# FIXME: could do all possible singletons and subranges.
|
|
for i in range(0,fr.get_number_of_flag_actions()):
|
|
id = fr.identifier(i)
|
|
flags_info_t._fa_table[id] = t
|
|
|
|
# set the flag_action index
|
|
fr.set_flag_action_index( t )
|
|
|
|
if is_new:
|
|
# emit the flag actions (using the indices assigned above)
|
|
fr.emit_data_record(fo_flag_actions)
|
|
|
|
return t
|
|
|
|
def emit_data_record(self, fo_simple, fo_complex, fo_flag_actions):
|
|
|
|
# emit the simple flag records
|
|
# (a) remember and id/number for the flags records; complex flags need it later
|
|
# as do the instructions.
|
|
# (b) number the flag actions so that we can refer to them from the simple flag recs.
|
|
|
|
flag_id = -1
|
|
for fr in self.flags_recs:
|
|
|
|
may = '1' if fr.may_must == 'MAY' else '0'
|
|
must = '1' if fr.may_must == 'MUST' else '0'
|
|
|
|
s = []
|
|
s.append( str( fr.get_number_of_flag_actions() ))
|
|
s.append(may)
|
|
s.append(must)
|
|
s.append(_curly_string(fr.read_set.as_hex()))
|
|
s.append(_curly_string(fr.write_set.as_hex()))
|
|
s.append(_curly_string(fr.undefined_set.as_hex()))
|
|
|
|
flag_action_id = self._compute_assign_flag_action_id(fr, fo_flag_actions)
|
|
s.append(str(fr.get_flag_action_index()))
|
|
|
|
t = _curly_list(s)
|
|
try:
|
|
# Attempt to reuse an identical flag record
|
|
flag_id = flags_info_t._fr_table[t]
|
|
is_new = False
|
|
except:
|
|
is_new = True
|
|
# Assign an id/number to the simple flag records.
|
|
# This gets used by the xed_inst_t to find the flags rec
|
|
# and by the complex flags to find the right simple flags.
|
|
flag_id = flags_info_t._flag_simple_rec
|
|
flags_info_t._flag_simple_rec += 1
|
|
# remember pattern for later use
|
|
flags_info_t._fr_table[t] = flag_id
|
|
|
|
fr.set_simple_id( flag_id )
|
|
if is_new:
|
|
comment = '/* {} */ '.format(flag_id)
|
|
fo_simple.add_code( comment + t )
|
|
|
|
# emit the complex flag table
|
|
if self.is_complex():
|
|
flag_id = flags_info_t._flag_complex_rec
|
|
self.set_complex_id( flag_id )
|
|
flags_info_t._flag_complex_rec += 1
|
|
|
|
s = []
|
|
s.append('1' if self.is_rep() else '0')
|
|
s.append('1' if self.is_imm() else '0')
|
|
|
|
cases = []
|
|
ordered_cases = ['IMMED_ZERO', # FIXME: IMMED_ZERO is unused
|
|
'IMMED_ONE',
|
|
'IMMED_OTHER',
|
|
'HAS_REP',
|
|
'NO_REP' ]
|
|
|
|
# go through ordered cases, find a flag rec that matches
|
|
# and add it if found.
|
|
|
|
for c in ordered_cases:
|
|
found = None
|
|
for fr in self.flags_recs:
|
|
cname = self.make_case_name(fr.qualifier)
|
|
if c == cname:
|
|
found = fr
|
|
break
|
|
if found:
|
|
cases.append( found.get_simple_id())
|
|
else:
|
|
cases.append( 0 )
|
|
if not cases:
|
|
die("Complex flag with no cases")
|
|
|
|
cases = _convert_to_list_of_string(cases)
|
|
s.append(_curly_list(cases))
|
|
comment = '/* {} */ '.format(flag_id)
|
|
fo_complex.add_code(comment + _curly_list(s))
|
|
|
|
|
|
if flag_id == -1:
|
|
die("flag_id was not set")
|
|
if self.is_complex():
|
|
return (flag_id, True) # True for complex
|
|
return (flag_id, False) # False for simple
|
|
|
|
|
|
def code_gen(self, itable_prefix_string, fo): # FIXME: DEAD: DELETE THIS
|
|
"""
|
|
- set xed_inst_t::_flag_info_index in to the index of one of the next 2 tables
|
|
- initialize the xed_flags_simple_table
|
|
- initialize the xed_flags_complex_table
|
|
|
|
@type itable_prefix_string: string
|
|
@param itable_prefix_string:
|
|
@type fo: function_object_t
|
|
@param fo: the function object for the instruction we are initializing.
|
|
"""
|
|
|
|
complex = self.is_complex()
|
|
if complex:
|
|
flags_info_t._flag_complex_rec += 1
|
|
retval = (flags_info_t._flag_complex_rec, True)
|
|
|
|
complex_prefix = "xed_flags_complex_table[%d]" % \
|
|
(flags_info_t._flag_complex_rec)
|
|
if self.is_rep():
|
|
fo.add_code_eol("%s.check_rep=1" % (complex_prefix))
|
|
elif self.is_imm():
|
|
fo.add_code_eol("%s.check_imm=1" % (complex_prefix))
|
|
else:
|
|
die("Unhandled complex flags condition: %s" % str(self))
|
|
else: # SIMPLE
|
|
retval = (flags_info_t._flag_simple_rec, False)
|
|
|
|
for fr in self.flags_recs:
|
|
|
|
if complex:
|
|
# emit the complex decider information for this record FIXME
|
|
case_name = self.make_case_name(fr.qualifier)
|
|
if fr.is_nothing():
|
|
srec = 0
|
|
else:
|
|
srec = flags_info_t._flag_simple_rec
|
|
|
|
s="%s.cases[XED_FLAG_CASE_%s]=%d" % ( complex_prefix,
|
|
case_name,
|
|
srec)
|
|
fo.add_code_eol(s)
|
|
|
|
|
|
simple_prefix = "xed_flags_simple_table+%d" % \
|
|
(flags_info_t._flag_simple_rec)
|
|
flags_info_t._flag_simple_rec += 1
|
|
|
|
may_must = None
|
|
if fr.may_must == 'MUST':
|
|
may_must = "xed_simple_flag_set_must_write"
|
|
elif fr.may_must == 'MAY':
|
|
may_must = "xed_simple_flag_set_may_write"
|
|
if may_must:
|
|
fo.add_code_eol("%s(%s)" % (may_must,simple_prefix))
|
|
|
|
# emit the individual bits
|
|
fr.emit_code(simple_prefix, fo)
|
|
|
|
x = len(fr.flag_actions)
|
|
if x > flags_info_t._max_actions_per_simple_flag:
|
|
flags_info_t._max_actions_per_simple_flag = x
|
|
# track the max # of flag actions
|
|
flags_info_t._max_flag_actions += x
|
|
return retval
|
|
|
|
def _testflags():
|
|
"""Test function for flag_set_t objects"""
|
|
a = flag_set_t()
|
|
a.set('cf')
|
|
a.set('zf')
|
|
a.set('of')
|
|
print(a.as_integer())
|
|
print(a.as_hex())
|
|
|
|
if __name__ == '__main__':
|
|
_testflags()
|