Skip to content

Commit

Permalink
generators/asm_specs: Split parsers into new module
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Oct 22, 2023
1 parent c0b3220 commit cf1346b
Show file tree
Hide file tree
Showing 15 changed files with 732 additions and 160 deletions.
8 changes: 4 additions & 4 deletions generators/asm_specs/x86/global_defs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from generators.asm_specs.x86.map_info import MapInfo
from generators.asm_specs.x86.register import Registers
from generators.asm_specs.x86.width import Width
from generators.asm_specs.x86.xtype import XType
from generators.asm_specs.x86.parsers.map_info import MapInfo
from generators.asm_specs.x86.parsers.register import Registers
from generators.asm_specs.x86.parsers.width import Width
from generators.asm_specs.x86.parsers.xtype import XType

widths: dict[str, Width] = {}
states: dict[str, str] = {}
Expand Down
63 changes: 0 additions & 63 deletions generators/asm_specs/x86/operand.py

This file was deleted.

11 changes: 11 additions & 0 deletions generators/asm_specs/x86/parsers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# XED format parsers

* [`instruction.py`] - Parser for the decode instruction format defined in `all-dec-instructions.txt`
* [`instruction_flags.py`] - Parser for the "FLAGS" instruction field
* [`instruction_operand.py`] - Parser for the "OPERAND" instruction field
* [`instruction_pattern.py`] - Parser for the "PATTERN" instruction field
* [`map_info.py`] - Parser for the map descriptions defined in `all-map-descriptions.txt`
* [`register.py`] - Parser for the register table defined in `all-registers.txt`
* [`states.py`] - Parser for the text-replacement macros defined in `all-state.txt`
* [`width.py`] - Parser for the width definitions in `all-widths.txt`
* [`xtype.py`] - Parser for the xtype (datatype) definitions in `all-element-types.txt`
Empty file.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from copy import deepcopy
from typing import Optional, Tuple, Iterable
from typing import Tuple, Iterable, Optional

from generators.asm_specs.util import fatal
from generators.asm_specs.x86.flag import Flags
from generators.asm_specs.x86.pattern import Pattern
from generators.asm_specs.x86.parsers.instruction_flags import FlagCollection, Flags
from generators.asm_specs.x86.parsers.instruction_operand import Operand
from generators.asm_specs.x86.parsers.instruction_pattern import Pattern, REG_PATTERN
from generators.asm_specs.x86.text_utils import handle_continuations, key_value_pair
from generators.asm_specs.util import fatal


class Instruction:
Expand All @@ -31,23 +32,110 @@ class Instruction:
isa_set: Optional[str] = None
real_opcode: bool = True
# (optional) read/written flag bit values.
flags: Optional[list[Flags]] = None
flags: Optional[FlagCollection] = None
# (optional) a hopefully useful comment
comment: Optional[str] = None
pattern: Pattern

scalar: bool = False

def add_flags_register_operand(self):
if not self.pattern.operands:
return

"""If the instruction has flags, then add a flag register operand."""

# TODO rewriting the stackpush and stackpop registers and adding these flag registers
if self.flags and self.flags.x86_flags():
rw = self.flags.rw_action()
(memidx_dummy, regidx) = self.find_max_memidx_and_regidx()
s = "REG%d=rFLAGS():%s:SUPP" % (regidx, rw)
self.pattern.operands.append(Operand(s))

def rewrite_stack_push(self, memidx: int, operand: Operand) -> list[Operand]:
s = [
Operand("MEM%d:w:%s:SUPP" % (memidx, operand.width)),
Operand("BASE%d=SrSP():rw:SUPP" % memidx),
]
if memidx == 0:
s.append(Operand("SEG0=FINAL_SSEG0():r:SUPP")) # note FINAL_SSEG0()
else:
s.append(Operand("SEG1=FINAL_SSEG1():r:SUPP")) # note FINAL_SSEG1() ***
return s

def rewrite_stack_pop(self, memidx: int, operand: Operand) -> list[Operand]:
s = [
Operand("MEM%d:r:%s:SUPP" % (memidx, operand.width)),
Operand("BASE%d=SrSP():rw:SUPP" % memidx),
]
if memidx == 0:
s.append(Operand("SEG0=FINAL_SSEG0():r:SUPP")) # note FINAL_SSEG()
else:
s.append(Operand("SEG1=FINAL_SSEG1():r:SUPP")) # note FINAL_SSEG1() ***
return s

def find_max_memidx_and_regidx(self) -> Tuple[int, int]:
"""find the maximum memidx and regidx"""

memidx = 0
regidx = 0
verbose = False
for operand in self.pattern.operands:
if operand.name == "MEM0":
memidx = 1
elif operand.name == "MEM1":
memidx = 2 # this should cause an error if it is ever used
rnm = REG_PATTERN.match(operand.name)
if rnm:
current_regidx = int(rnm.group("regno"))
if verbose:
if current_regidx >= regidx:
regidx = current_regidx + 1
return memidx, regidx

def expand_stack_operand(self):
if not self.pattern.operands:
return

(memidx, regidx) = self.find_max_memidx_and_regidx()

for idx, operand in enumerate(self.pattern.operands):
if "XED_REG_STACKPUSH" in operand.name:
self.pattern.operands.pop(idx)
self.pattern.operands[idx:idx] = self.rewrite_stack_push(
memidx, operand
)
elif "XED_REG_STACKPOP" in operand.name:
self.pattern.operands.pop(idx)
self.pattern.operands[idx:idx] = self.rewrite_stack_pop(memidx, operand)

def modrm(self) -> Optional[int]:
if not self.pattern.has_modrm:
return None

return self.pattern.mod_required


class InstructionParser:
lines: Iterable[str]

_filters = ["INSTRUCTIONS()::", "XOP_INSTRUCTIONS()::", "AVX_INSTRUCTIONS()::", "EVEX_INSTRUCTIONS()::"]
_filters = [
"INSTRUCTIONS()::",
"XOP_INSTRUCTIONS()::",
"AVX_INSTRUCTIONS()::",
"EVEX_INSTRUCTIONS()::",
]

def __init__(self, instruction_lines: Iterable[str]):
expanded_continuations = handle_continuations(instruction_lines)

self.lines = iter([x for x in expanded_continuations if x not in self._filters and not x.startswith("UDELETE")])
self.lines = iter(
[
x
for x in expanded_continuations
if x not in self._filters and not x.startswith("UDELETE")
]
)

def parse(self) -> Optional[list[Instruction]]:
"""Parse an instruction definition, returning multiple if there
Expand Down Expand Up @@ -108,12 +196,20 @@ def parse(self) -> Optional[list[Instruction]]:
case "REAL_OPCODE":
instruction.real_opcode = val == "Y"
case "FLAGS":
instruction.flags = [Flags(x.strip()) for x in val.split(",")]
instruction.flags = FlagCollection(
[Flags(x.strip()) for x in val.split(",")]
)
case "COMMENT":
instruction.comment = val
case "PATTERN":
if current_pattern:
patterns.append(Pattern(current_pattern[0], current_pattern[1], current_pattern[2]))
patterns.append(
Pattern(
current_pattern[0],
current_pattern[1],
current_pattern[2],
)
)
current_pattern = val, "", None
case "OPERANDS":
if not current_pattern:
Expand All @@ -124,13 +220,17 @@ def parse(self) -> Optional[list[Instruction]]:
fatal("ERROR: Found key 'IFORM' outside of pattern")
current_pattern = current_pattern[0], current_pattern[1], val
case _:
fatal("ERROR: Unknown key in instruction definition: \"" + key + "\"")
fatal('ERROR: Unknown key in instruction definition: "' + key + '"')

if current_pattern:
patterns.append(Pattern(current_pattern[0], current_pattern[1], current_pattern[2]))
patterns.append(
Pattern(current_pattern[0], current_pattern[1], current_pattern[2])
)

if instruction.current_privilege_level not in [0, 3]:
fatal("ERROR: Invalid CPL value: " + str(instruction.current_privilege_level))
fatal(
"ERROR: Invalid CPL value: " + str(instruction.current_privilege_level)
)

if instruction.attributes and "scalar" in instruction.attributes:
instruction.scalar = True
Expand All @@ -141,4 +241,8 @@ def parse(self) -> Optional[list[Instruction]]:
copied.pattern = pat
instructions.append(copied)

for instruction in instructions:
instruction.expand_stack_operand()
instruction.add_flags_register_operand()

return instructions
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ def as_hex(self):

class FlagAction(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
self.action = None # Could be mod, tst, u, 0, 1, ah, pop
if s != 'nothing':
(self.flag, self.action) = s.lower().split('-')
if self.action not in FlagAction.valid_flag_actions:
Expand Down Expand Up @@ -163,3 +164,107 @@ def __init__(self, flags: str):
self.undefined_set.set(fa.flag)
else:
sys.stderr.write("WARN: Unknown flag: {}\n".format(flag_action_str))

def is_nothing(self) -> bool:
return len(self.flag_actions) == 1 and self.flag_actions[0].is_nothing()

def reads_flags(self) -> bool:
for action in self.flag_actions:
if action.reads_flag():
return True
return False

def writes_flags(self) -> bool:
for action in self.flag_actions:
if action.writes_flag():
return True
return False

def conditional_writes_flags(self) -> bool:
return self.writes_flags() and self.semantics == FlagSemantics.MAY

def is_x86(self) -> bool:
"""Return True if any of the flags are x86 flags. False otherwise"""

for action in self.flag_actions:
s = action.flag
if s != 'fc0' and s != 'fc1' and s != 'fc2' and s != 'fc3':
return True
return False

def rw_action(self) -> str:
"""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 action in self.flag_actions:
if action.is_nothing():
has_nothing_record = True
if action.reads_flag():
r = 'r'
if action.writes_flag():
w = 'w'
if self.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


class FlagCollection:
"""A container to act on sets of Flags"""

flags: list[Flags]

def __init__(self, flags: list[Flags]):
self.flags = flags

def reads_flags(self):
for flags in self.flags:
if flags.reads_flags():
return True
return False

def writes_flags(self):
for flags in self.flags:
if flags.writes_flags():
return True
return False

def x86_flags(self):
"""Return True if any flags are x86 flags"""

for flags in self.flags:
if flags.is_x86():
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 flags in self.flags:
if flags.is_nothing():
has_nothing_record = True
if flags.reads_flags():
r = 'r'
if flags.writes_flags():
w = 'w'
if flags.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
Loading

0 comments on commit cf1346b

Please sign in to comment.