parseinstrs: Make TrieEntry always hashable
This commit is contained in:
@@ -161,7 +161,7 @@ class EntryKind(Enum):
|
|||||||
|
|
||||||
class TrieEntry(NamedTuple):
|
class TrieEntry(NamedTuple):
|
||||||
kind: EntryKind
|
kind: EntryKind
|
||||||
items: Union[List[Optional[str]], Tuple[Optional[str]]]
|
items: Tuple[Optional[str]]
|
||||||
payload: Tuple[Union[int, str]]
|
payload: Tuple[Union[int, str]]
|
||||||
|
|
||||||
TABLE_LENGTH = {
|
TABLE_LENGTH = {
|
||||||
@@ -175,10 +175,10 @@ class TrieEntry(NamedTuple):
|
|||||||
}
|
}
|
||||||
@classmethod
|
@classmethod
|
||||||
def table(cls, kind):
|
def table(cls, kind):
|
||||||
return cls(kind, [None] * cls.TABLE_LENGTH[kind], ())
|
return cls(kind, (None,) * cls.TABLE_LENGTH[kind], ())
|
||||||
@classmethod
|
@classmethod
|
||||||
def instr(cls, payload):
|
def instr(cls, payload):
|
||||||
return cls(EntryKind.INSTR, [], payload)
|
return cls(EntryKind.INSTR, (), payload)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def encode_length(self):
|
def encode_length(self):
|
||||||
@@ -187,11 +187,11 @@ class TrieEntry(NamedTuple):
|
|||||||
enc_items = (encode_item(item) if item else 0 for item in self.items)
|
enc_items = (encode_item(item) if item else 0 for item in self.items)
|
||||||
return self.payload + tuple(enc_items)
|
return self.payload + tuple(enc_items)
|
||||||
|
|
||||||
def readonly(self):
|
def map(self, map_func):
|
||||||
return TrieEntry(self.kind, tuple(self.items), self.payload)
|
mapped_items = (map_func(i, v) for i, v in enumerate(self.items))
|
||||||
def map(self, mapping):
|
|
||||||
mapped_items = (mapping.get(v, v) for v in self.items)
|
|
||||||
return TrieEntry(self.kind, tuple(mapped_items), self.payload)
|
return TrieEntry(self.kind, tuple(mapped_items), self.payload)
|
||||||
|
def update(self, idx, new_val):
|
||||||
|
return self.map(lambda i, v: new_val if i == idx else v)
|
||||||
|
|
||||||
import re
|
import re
|
||||||
opcode_regex = re.compile(r"^(?:(?P<prefixes>(?P<vex>VEX\.)?(?P<legacy>NP|66|F2|F3)\.(?P<rexw>W[01]\.)?(?P<vexl>L[01]\.)?)|R(?P<repprefix>NP|F2|F3).)?(?P<opcode>(?:[0-9a-f]{2})+)(?P<modrm>//?[0-7]|//[c-f][0-9a-f])?(?P<extended>\+)?$")
|
opcode_regex = re.compile(r"^(?:(?P<prefixes>(?P<vex>VEX\.)?(?P<legacy>NP|66|F2|F3)\.(?P<rexw>W[01]\.)?(?P<vexl>L[01]\.)?)|R(?P<repprefix>NP|F2|F3).)?(?P<opcode>(?:[0-9a-f]{2})+)(?P<modrm>//?[0-7]|//[c-f][0-9a-f])?(?P<extended>\+)?$")
|
||||||
@@ -289,33 +289,34 @@ class Table:
|
|||||||
self.offsets = {}
|
self.offsets = {}
|
||||||
self.annotations = {}
|
self.annotations = {}
|
||||||
|
|
||||||
|
def _update_table(self, name, idx, entry_name, entry_val):
|
||||||
|
# Don't override existing entries. This only happens on invalid input,
|
||||||
|
# e.g. when an opcode is specified twice.
|
||||||
|
if self.data[name].items[idx]:
|
||||||
|
raise Exception("{}/{} set, not overriding to {}".format(name, idx, entry_name))
|
||||||
|
self.data[entry_name] = entry_val
|
||||||
|
self.data[name] = self.data[name].update(idx, entry_name)
|
||||||
|
|
||||||
def add_opcode(self, opcode, instr_encoding, root_idx=0):
|
def add_opcode(self, opcode, instr_encoding, root_idx=0):
|
||||||
name = "t{},{}".format(root_idx, format_opcode(opcode))
|
name = "t{},{}".format(root_idx, format_opcode(opcode))
|
||||||
|
|
||||||
table = self.data["root%d"%root_idx]
|
tn = "root%d"%root_idx
|
||||||
for i in range(len(opcode) - 1):
|
for i in range(len(opcode) - 1):
|
||||||
|
# kind is the table kind that we want to point to in the _next_.
|
||||||
kind, byte = opcode[i+1][0], opcode[i][1]
|
kind, byte = opcode[i+1][0], opcode[i][1]
|
||||||
if table.items[byte] is None:
|
# Retain prev_tn name so that we can update it.
|
||||||
table_name = "t{},{}".format(root_idx, format_opcode(opcode[:i+1]))
|
prev_tn, tn = tn, self.data[tn].items[byte]
|
||||||
table.items[byte] = table_name
|
if tn is None:
|
||||||
table = self.data[table_name] = TrieEntry.table(kind)
|
tn = "t{},{}".format(root_idx, format_opcode(opcode[:i+1]))
|
||||||
else:
|
self._update_table(prev_tn, byte, tn, TrieEntry.table(kind))
|
||||||
table = self.data[table.items[byte]]
|
|
||||||
if table.kind != kind:
|
|
||||||
raise Exception("have {}, new opcode ({}) has {}".format(
|
|
||||||
table.kind, name, kind))
|
|
||||||
|
|
||||||
# An opcode can occur once only.
|
if self.data[tn].kind != kind:
|
||||||
if table.items[opcode[-1][1]]:
|
raise Exception("{}, have {}, want {}".format(
|
||||||
raise Exception("opcode collision for {}".format(name))
|
name, self.data[tn].kind, kind))
|
||||||
|
|
||||||
table.items[opcode[-1][1]] = name
|
self._update_table(tn, opcode[-1][1], name, TrieEntry.instr(instr_encoding))
|
||||||
self.data[name] = TrieEntry.instr(instr_encoding)
|
|
||||||
|
|
||||||
def deduplicate(self):
|
def deduplicate(self):
|
||||||
# Make values hashable
|
|
||||||
for name, entry in self.data.items():
|
|
||||||
self.data[name] = entry.readonly()
|
|
||||||
synonyms = True
|
synonyms = True
|
||||||
while synonyms:
|
while synonyms:
|
||||||
entries = {} # Mapping from entry to name
|
entries = {} # Mapping from entry to name
|
||||||
@@ -326,7 +327,7 @@ class Table:
|
|||||||
else:
|
else:
|
||||||
entries[entry] = name
|
entries[entry] = name
|
||||||
for name, entry in self.data.items():
|
for name, entry in self.data.items():
|
||||||
self.data[name] = entry.map(synonyms)
|
self.data[name] = entry.map(lambda _, v: synonyms.get(v, v))
|
||||||
for key in synonyms:
|
for key in synonyms:
|
||||||
del self.data[key]
|
del self.data[key]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user