-
Notifications
You must be signed in to change notification settings - Fork 22
/
uniquification.py
100 lines (80 loc) · 3.1 KB
/
uniquification.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from dataclasses import dataclass
from enum import Enum, auto
from .is_definition import isdefinition
from .logging import warning, error
from .passes import DefinitionPass
class MultipleDefinitionException(Exception):
pass
class UniquificationMode(Enum):
WARN = auto()
ERROR = auto()
UNIQUIFY = auto()
@dataclass(frozen=True)
class _HashStruct:
defn_repr: str
is_verilog: bool
verilog_str: bool
def _make_hash_struct(definition):
repr_ = repr(definition)
if hasattr(definition, "verilogFile") and definition.verilogFile:
return _HashStruct(repr_, True, definition.verilogFile)
return _HashStruct(repr_, False, "")
def _hash(definition):
hash_struct = _make_hash_struct(definition)
return hash(hash_struct)
class UniquificationPass(DefinitionPass):
def __init__(self, main, mode):
super().__init__(main)
self.mode = mode
self.seen = {}
self.original_names = {}
def __call__(self, definition):
name = definition.name
key = _hash(definition)
seen = self.seen.setdefault(name, {})
if key not in seen:
if self.mode is UniquificationMode.UNIQUIFY and len(seen) > 0:
new_name = name + "_unq" + str(len(seen))
type(definition).rename(definition, new_name)
seen[key] = [definition]
else:
if self.mode is not UniquificationMode.UNIQUIFY:
assert seen[key][0].name == name
elif name != seen[key][0].name:
new_name = seen[key][0].name
type(definition).rename(definition, new_name)
seen[key].append(definition)
def run(self):
super().run()
duplicated = []
for name, definitions in self.seen.items():
if len(definitions) > 1:
duplicated.append((name, definitions))
UniquificationPass.handle(duplicated, self.mode)
@staticmethod
def handle(duplicated, mode):
if len(duplicated):
msg = f"Multiple definitions: {[name for name, _ in duplicated]}"
if mode is UniquificationMode.ERROR:
error(msg)
raise MultipleDefinitionException([name for name, _ in duplicated])
elif mode is UniquificationMode.WARN:
warning(msg)
def _get_mode(mode_or_str):
if isinstance(mode_or_str, str):
try:
return UniquificationMode[mode_or_str]
except KeyError as e:
modes = [k for k in UniquificationMode.__members__]
raise ValueError(f"Valid uniq. modes are {modes}")
if isinstance(mode_or_str, UniquificationMode):
return mode_or_str
raise NotImplementedError(f"Unsupported type: {type(mode_or_str)}")
# This pass runs uniquification according to @mode and returns a dictionary
# mapping any renamed circuits to their original names. If @mode is ERROR or
# WARN the returned dictionary should be empty.
def uniquification_pass(circuit, mode_or_str):
mode = _get_mode(mode_or_str)
pass_ = UniquificationPass(circuit, mode)
pass_.run()
return pass_.original_names