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

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()