2 # Copyright 2017-2019 Advanced Micro Devices, Inc.
4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the "Software"),
6 # to deal in the Software without restriction, including without limitation
7 # on the rights to use, copy, modify, merge, publish, distribute, sub
8 # license, and/or sell copies of the Software, and to permit persons to whom
9 # the Software is furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice (including the next
12 # paragraph) shall be included in all copies or substantial portions of the
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 # THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 # USE OR OTHER DEALINGS IN THE SOFTWARE.
24 Python package containing common tools for manipulating register JSON.
27 from __future__
import absolute_import
, division
, print_function
, unicode_literals
34 from collections
import defaultdict
35 from contextlib
import contextmanager
37 class UnionFind(object):
39 Simplistic implementation of a union-find data structure that also keeps
40 track of the sets that have been unified.
42 - add: add an element to the implied global set of elements
43 - union: unify the sets containing the two given elements
44 - find: return the representative element of the set containing the
46 - get_set: get the set containing the given element
47 - sets: iterate over all sets (the sets form a partition of the set of all
48 elements that have ever been added)
57 def union(self
, k1
, k2
):
64 self
.d
[k1
].update(self
.d
[k2
])
69 if isinstance(e
, set):
71 assert isinstance(e
, tuple)
78 assert isinstance(self
.d
[k
], set)
82 for v
in self
.d
.values():
83 if isinstance(v
, set):
89 Convenience helper class that essentially acts as a dictionary for convenient
90 conversion from and to JSON while allowing the use of .field notation
91 instead of subscript notation for member access.
93 def __init__(self
, **kwargs
):
94 for k
, v
in kwargs
.items():
97 def update(self
, **kwargs
):
98 for key
, value
in kwargs
.items():
99 setattr(self
, key
, value
)
103 return 'Object(' + ', '.join(
104 '{k}={v}'.format(**locals()) for k
, v
, in self
.__dict
__.items()
108 def from_json(json
, keys
=None):
109 if isinstance(json
, list):
110 return [Object
.from_json(v
) for v
in json
]
111 elif isinstance(json
, dict):
113 for k
, v
in json
.items():
114 if keys
is not None and k
in keys
:
117 v
= Object
.from_json(v
)
125 if isinstance(obj
, Object
):
126 return dict((k
, Object
.to_json(v
)) for k
, v
in obj
.__dict
__.items())
127 elif isinstance(obj
, dict):
128 return dict((k
, Object
.to_json(v
)) for k
, v
in obj
.items())
129 elif isinstance(obj
, list):
130 return [Object
.to_json(v
) for v
in obj
]
134 class MergeError(Exception):
135 def __init__(self
, msg
):
136 super(MergeError
, self
).__init
__(msg
)
138 class RegisterDatabaseError(Exception):
139 def __init__(self
, msg
):
140 super(RegisterDatabaseError
, self
).__init
__(msg
)
143 def merge_scope(name
):
145 Wrap a merge handling function in a "scope" whose name will be added when
146 propagating MergeErrors.
150 except Exception as e
:
151 raise MergeError('{name}: {e}'.format(**locals()))
153 def merge_dicts(dicts
, keys
=None, values
=None):
155 Generic dictionary merging function.
157 dicts -- list of (origin, dictionary) pairs to merge
158 keys -- optional dictionary to provide a merge-strategy per key;
159 the merge strategy is a callable which will receive a list of
160 (origin, value) pairs
161 value -- optional function which provides a merge-strategy for values;
162 the merge strategy is a callable which will receive the name of
163 the key and a list of (origin, value) pairs
165 The default strategy is to allow merging keys if all origin dictionaries
166 that contain the key have the same value for it.
174 vs
= [(o
, d
[k
]) for o
, d
in dicts
if k
in d
]
175 with
merge_scope('Key {k}'.format(**locals())):
176 if keys
is not None and k
in keys
:
177 result
[k
] = keys
[k
](vs
)
178 elif values
is not None:
179 result
[k
] = values(k
, vs
)
181 base_origin
, base
= vs
[0]
182 for other_origin
, other
in vs
[1:]:
184 raise MergeError('{base} (from {base_origin}) != {other} (from {other_origin})'.format(**locals()))
188 def merge_objects(objects
, keys
=None):
190 Like merge_dicts, but applied to instances of Object.
192 return Object(**merge_dicts([(origin
, obj
.__dict
__) for origin
, obj
in objects
], keys
=keys
))
194 class RegisterDatabase(object):
196 A register database containing:
198 - enums: these are lists of named values that can occur in a register field
199 - register types: description of a register type or template as a list of
201 - register mappings: named and typed registers mapped at locations in an
206 self
.__register
_types
= {}
207 self
.__register
_mappings
= []
208 self
.__regmap
_by
_addr
= None
211 def __post_init(self
):
213 Perform some basic canonicalization:
214 - enum entries are sorted by value
215 - register type fields are sorted by starting bit
216 - __register_mappings is sorted by offset
217 - the chips field of register mappings is sorted
219 Lazily computes the set of all chips mentioned by register mappings.
221 if self
.__regmap
_by
_addr
is not None:
224 for enum
in self
.__enums
.values():
225 enum
.entries
.sort(key
=lambda entry
: entry
.value
)
227 for regtype
in self
.__register
_types
.values():
228 regtype
.fields
.sort(key
=lambda field
: field
.bits
[0])
230 self
.__regmap
_by
_addr
= defaultdict(list)
233 # Merge register mappings using sort order and garbage collect enums
234 # and register types.
235 old_register_mappings
= self
.__register
_mappings
236 old_register_mappings
.sort(key
=lambda regmap
: regmap
.map.at
)
238 self
.__register
_mappings
= []
239 for regmap
in old_register_mappings
:
240 addr
= (regmap
.map.to
, regmap
.map.at
)
241 chips
= set(getattr(regmap
, 'chips', ['undef']))
242 type_ref
= getattr(regmap
, 'type_ref', None)
244 self
.__chips
.update(chips
)
247 for other
in reversed(self
.__register
_mappings
):
248 if other
.name
!= regmap
.name
:
251 other_addr
= (other
.map.to
, other
.map.at
)
252 other_chips
= getattr(other
, 'chips', ['undef'])
253 other_type_ref
= getattr(other
, 'type_ref', None)
255 if addr
== other_addr
and\
256 (type_ref
is None or other_type_ref
is None or type_ref
== other_type_ref
):
257 other
.chips
= sorted(list(chips
.union(other_chips
)))
258 if type_ref
is not None:
259 other
.type_ref
= type_ref
266 addrmappings
= self
.__regmap
_by
_addr
[addr
]
268 for other
in addrmappings
:
269 other_type_ref
= getattr(other
, 'type_ref', None)
270 other_chips
= getattr(other
, 'chips', ['undef'])
271 if type_ref
is not None and other_type_ref
is not None and \
272 type_ref
!= other_type_ref
and chips
.intersection(other_chips
):
273 raise RegisterDatabaseError(
274 'Registers {0} and {1} overlap and have conflicting types'.format(
275 other
.name
, regmap
.name
))
277 addrmappings
.append(regmap
)
278 self
.__register
_mappings
.append(regmap
)
280 def garbage_collect(self
):
282 Remove unreferenced enums and register types.
284 old_enums
= self
.__enums
285 old_register_types
= self
.__register
_types
288 self
.__register
_types
= {}
289 for regmap
in self
.__register
_mappings
:
290 if hasattr(regmap
, 'type_ref') and regmap
.type_ref
not in self
.__register
_types
:
291 regtype
= old_register_types
[regmap
.type_ref
]
292 self
.__register
_types
[regmap
.type_ref
] = regtype
293 for field
in regtype
.fields
:
294 if hasattr(field
, 'enum_ref') and field
.enum_ref
not in self
.__enums
:
295 self
.__enums
[field
.enum_ref
] = old_enums
[field
.enum_ref
]
297 def __validate_register_type(self
, regtype
):
298 for field
in regtype
.fields
:
299 if hasattr(field
, 'enum_ref') and field
.enum_ref
not in self
.__enums
:
300 raise RegisterDatabaseError(
301 'Register type field {0} has unknown enum_ref {1}'.format(
302 field
.name
, field
.enum_ref
))
304 def __validate_register_mapping(self
, regmap
):
305 if hasattr(regmap
, 'type_ref') and regmap
.type_ref
not in self
.__register
_types
:
306 raise RegisterDatabaseError(
307 'Register mapping {0} has unknown type_ref {1}'.format(
308 regmap
.name
, regmap
.type_ref
))
310 def __validate(self
):
311 for regtype
in self
.__register
_types
.values():
312 self
.__validate
_register
_type
(regtype
)
313 for regmap
in self
.__register
_mappings
:
314 self
.__validate
_register
_mapping
(regmap
)
319 Return a key that uniquely describes the signature of the given
320 enum (assuming that it has been canonicalized). Two enums with the
321 same key can be merged.
324 ':{0}:{1}'.format(entry
.name
, entry
.value
)
325 for entry
in enum
.entries
328 def add_enum(self
, name
, enum
):
329 if name
in self
.__enums
:
330 raise RegisterDatabaseError('Duplicate enum ' + name
)
331 self
.__enums
[name
] = enum
334 def __merge_enums(enums
, union
=False):
335 def merge_entries(entries_lists
):
336 values
= defaultdict(list)
337 for origin
, enum
in entries_lists
:
339 values
[entry
.value
].append((origin
, entry
))
342 if any(len(entries
) != len(enums
) for entries
in values
.values()):
343 raise RegisterDatabaseError(
344 'Attempting to merge enums with different values')
347 merge_objects(entries
)
348 for entries
in values
.values()
351 return merge_objects(
354 'entries': merge_entries
,
358 def merge_enums(self
, names
, newname
, union
=False):
360 Given a list of enum names, merge them all into one with a new name and
361 update all references.
363 if newname
not in names
and newname
in self
.__enums
:
364 raise RegisterDatabaseError('Enum {0} already exists'.format(newname
))
366 newenum
= self
.__merge
_enums
(
367 [(name
, self
.__enums
[name
]) for name
in names
],
372 del self
.__enums
[name
]
373 self
.__enums
[newname
] = newenum
375 for regtype
in self
.__register
_types
.values():
376 for field
in regtype
.fields
:
377 if getattr(field
, 'enum_ref', None) in names
:
378 field
.enum_ref
= newname
380 self
.__regmap
_by
_addr
= None
382 def add_register_type(self
, name
, regtype
):
383 if regtype
in self
.__register
_types
:
384 raise RegisterDatabaseError('Duplicate register type ' + name
)
385 self
.__register
_types
[name
] = regtype
386 self
.__validate
_register
_type
(regtype
)
388 def register_type(self
, name
):
390 return self
.__register
_types
[name
]
393 def __merge_register_types(regtypes
, union
=False, field_keys
={}):
394 def merge_fields(fields_lists
):
395 fields
= defaultdict(list)
396 for origin
, fields_list
in fields_lists
:
397 for field
in fields_list
:
398 fields
[field
.bits
[0]].append((origin
, field
))
401 if any(len(entries
) != len(regtypes
) for entries
in fields
.values()):
402 raise RegisterDatabaseError(
403 'Attempting to merge register types with different fields')
406 merge_objects(field
, keys
=field_keys
)
407 for field
in fields
.values()
410 with
merge_scope('Register types {0}'.format(', '.join(name
for name
, _
in regtypes
))):
411 return merge_objects(
414 'fields': merge_fields
,
418 def merge_register_types(self
, names
, newname
, union
=False):
420 Given a list of register type names, merge them all into one with a
421 new name and update all references.
423 if newname
not in names
and newname
in self
.__register
_types
:
424 raise RegisterDatabaseError('Register type {0} already exists'.format(newname
))
426 newregtype
= self
.__merge
_register
_types
(
427 [(name
, self
.__register
_types
[name
]) for name
in names
],
432 del self
.__register
_types
[name
]
433 self
.__register
_types
[newname
] = newregtype
435 for regmap
in self
.__register
_mappings
:
436 if getattr(regmap
, 'type_ref', None) in names
:
437 regmap
.type_ref
= newname
439 self
.__regmap
_by
_addr
= None
441 def add_register_mapping(self
, regmap
):
442 self
.__regmap
_by
_addr
= None
443 self
.__register
_mappings
.append(regmap
)
444 self
.__validate
_register
_mapping
(regmap
)
446 def remove_register_mappings(self
, regmaps_to_remove
):
449 regmaps_to_remove
= set(regmaps_to_remove
)
451 regmaps
= self
.__register
_mappings
452 self
.__register
_mappings
= []
453 for regmap
in regmaps
:
454 if regmap
not in regmaps_to_remove
:
455 self
.__register
_mappings
.append(regmap
)
457 self
.__regmap
_by
_addr
= None
459 def enum(self
, name
):
461 Return the enum of the given name, if any.
464 return self
.__enums
.get(name
, None)
468 Yields all (name, enum) pairs.
471 for name
, enum
in self
.__enums
.items():
476 Yields all (register_type, fields) pairs.
479 for regtype
in self
.__register
_types
.values():
480 for field
in regtype
.fields
:
481 yield (regtype
, field
)
483 def register_types(self
):
485 Yields all (name, register_type) pairs.
488 for name
, regtype
in self
.__register
_types
.items():
489 yield (name
, regtype
)
491 def register_mappings_by_name(self
, name
):
493 Return a list of register mappings with the given name.
498 end
= len(self
.__register
_mappings
)
500 middle
= (begin
+ end
) // 2
501 if self
.__register
_mappings
[middle
].name
< name
:
503 elif name
< self
.__register
_mappings
[middle
].name
:
511 # We now have begin <= mid < end with begin.name <= name, mid.name == name, name < end.name
512 # Narrow down begin and end
515 mid
= (begin
+ hi
) // 2
516 if self
.__register
_mappings
[mid
].name
< name
:
523 mid
= (lo
+ end
) // 2
524 if self
.__register
_mappings
[mid
].name
== name
:
529 return self
.__register
_mappings
[begin
:end
]
531 def register_mappings(self
):
533 Yields all register mappings.
536 for regmap
in self
.__register
_mappings
:
544 return iter(self
.__chips
)
546 def merge_chips(self
, chips
, newchip
):
548 Merge register mappings of the given chips into a single chip of the
549 given name. Recursively merges register types and enums when appropriate.
555 regtypes_merge
= UnionFind()
556 enums_merge
= UnionFind()
558 # Walk register mappings to find register types that should be merged.
559 for idx
, regmap
in itertools
.islice(enumerate(self
.__register
_mappings
), 1, None):
560 if not hasattr(regmap
, 'type_ref'):
562 if chips
.isdisjoint(regmap
.chips
):
565 for other
in self
.__register
_mappings
[idx
-1::-1]:
566 if regmap
.name
!= other
.name
:
568 if chips
.isdisjoint(other
.chips
):
570 if regmap
.map.to
!= other
.map.to
or regmap
.map.at
!= other
.map.at
:
571 raise RegisterDatabaseError(
572 'Attempting to merge chips with incompatible addresses of {0}'.format(regmap
.name
))
573 if not hasattr(regmap
, 'type_ref'):
576 if regmap
.type_ref
!= other
.type_ref
:
577 regtypes_merge
.add(regmap
.type_ref
)
578 regtypes_merge
.add(other
.type_ref
)
579 regtypes_merge
.union(regmap
.type_ref
, other
.type_ref
)
581 # Walk over regtype sets that are to be merged and find enums that
583 for type_refs
in regtypes_merge
.sets():
584 fields_merge
= defaultdict(set)
585 for type_ref
in type_refs
:
586 regtype
= self
.__register
_types
[type_ref
]
587 for field
in regtype
.fields
:
588 if hasattr(field
, 'enum_ref'):
589 fields_merge
[field
.name
].add(field
.enum_ref
)
591 for enum_refs
in fields_merge
.values():
592 if len(enum_refs
) > 1:
593 enum_refs
= list(enum_refs
)
594 enums_merge
.add(enum_refs
[0])
595 for enum_ref
in enum_refs
[1:]:
596 enums_merge
.add(enum_ref
)
597 enums_merge
.union(enum_ref
, enum_refs
[0])
599 # Merge all mergeable enum sets
601 for enum_refs
in enums_merge
.sets():
602 enum_refs
= sorted(enum_refs
)
603 newname
= enum_refs
[0] + '_' + newchip
605 while newname
in self
.__enums
:
606 newname
= enum_refs
[0] + '_' + newchip
+ str(i
)
609 for enum_ref
in enum_refs
:
610 remap_enum_refs
[enum_ref
] = newname
612 # Don't use self.merge_enums, because we don't want to automatically
613 # update _all_ references to the merged enums (some may be from
614 # register types that aren't going to be merged).
615 self
.add_enum(newname
, self
.__merge
_enums
(
616 [(enum_ref
, self
.__enums
[enum_ref
]) for enum_ref
in enum_refs
],
620 # Merge all mergeable type refs
622 for type_refs
in regtypes_merge
.sets():
623 type_refs
= sorted(type_refs
)
624 newname
= type_refs
[0] + '_' + newchip
626 while newname
in self
.__enums
:
627 newname
= type_refs
[0] + '_' + newchip
+ str(i
)
630 updated_regtypes
= []
631 for type_ref
in type_refs
:
632 remap_type_refs
[type_ref
] = newname
634 regtype
= Object
.from_json(Object
.to_json(self
.__register
_types
[type_ref
]))
635 for field
in regtype
.fields
:
636 if hasattr(field
, 'enum_ref'):
637 field
.enum_ref
= remap_enum_refs
.get(enum_ref
, enum_ref
)
639 updated_regtypes
.append(regtype
)
641 def merge_enum_refs(enum_refs
):
643 remap_enum_refs
.get(enum_ref
, enum_ref
)
644 for origin
, enum_ref
in enum_refs
646 assert len(enum_refs
) == 1 # should be ensured by how we determine the enums to be merged
647 return enum_refs
.pop()
649 self
.add_register_type(newname
, self
.__merge
_register
_types
(
650 [(type_ref
, self
.__register
_types
[type_ref
]) for type_ref
in type_refs
],
652 'enum_ref': merge_enum_refs
,
657 # Merge register mappings
658 register_mappings
= self
.__register
_mappings
659 self
.__register
_mappings
= []
662 for regmap
in register_mappings
:
663 if regmap_accum
and regmap
.name
!= regmap_accum
.name
:
664 regmap_accum
.chips
= [newchip
]
665 self
.__register
_mappings
.append(regmap_accum
)
668 joining_chips
= chips
.intersection(regmap
.chips
)
669 if not joining_chips
:
670 self
.__register
_mappings
.append(regmap
)
672 remaining_chips
= set(regmap
.chips
).difference(chips
)
674 type_ref
= getattr(regmap
, 'type_ref', None)
676 regmap
.chips
= sorted(remaining_chips
.union([newchip
]))
677 self
.__register
_mappings
.append(regmap
)
680 type_ref
= remap_type_refs
.get(type_ref
, type_ref
)
682 regmap
.chips
= sorted(remaining_chips
)
683 self
.__register
_mappings
.append(regmap
)
685 regmap
= Object
.from_json(Object
.to_json(regmap
))
686 if type_ref
is not None:
687 regmap
.type_ref
= type_ref
690 regmap_accum
= regmap
692 if not hasattr(regmap_accum
.type_ref
, 'type_ref'):
693 if type_ref
is not None:
694 regmap_accum
.type_ref
= type_ref
696 assert type_ref
is None or type_ref
== regmap_accum
.type_ref
698 self
.__register
_mappings
.append(regmap_accum
)
700 def update(self
, other
):
702 Add the contents of the other database to self.
704 Doesn't de-duplicate entries.
712 for regmap
in other
.__register
_mappings
:
713 regmap
= Object
.from_json(Object
.to_json(regmap
))
715 type_ref
= getattr(regmap
, 'type_ref', None)
716 if type_ref
is not None and type_ref
not in regtype_remap
:
717 regtype
= Object
.from_json(Object
.to_json(other
.__register
_types
[type_ref
]))
719 chips
= getattr(regmap
, 'chips', [])
720 suffix
= '_' + chips
[0] if chips
else ''
722 for field
in regtype
.fields
:
723 enum_ref
= getattr(field
, 'enum_ref', None)
724 if enum_ref
is not None and enum_ref
not in enum_remap
:
725 enum
= Object
.from_json(Object
.to_json(other
.__enums
[enum_ref
]))
727 remapped
= enum_ref
+ suffix
if enum_ref
in self
.__enums
else enum_ref
729 while remapped
in self
.__enums
:
730 remapped
= enum_ref
+ suffix
+ str(i
)
732 self
.add_enum(remapped
, enum
)
733 enum_remap
[enum_ref
] = remapped
735 if enum_ref
is not None:
736 field
.enum_ref
= enum_remap
[enum_ref
]
738 remapped
= type_ref
+ suffix
if type_ref
in self
.__register
_types
else type_ref
740 while remapped
in self
.__register
_types
:
741 remapped
= type_ref
+ suffix
+ str(i
)
743 self
.add_register_type(remapped
, regtype
)
744 regtype_remap
[type_ref
] = remapped
746 if type_ref
is not None:
747 regmap
.type_ref
= regtype_remap
[type_ref
]
749 self
.add_register_mapping(regmap
)
754 'enums': Object
.to_json(self
.__enums
),
755 'register_types': Object
.to_json(self
.__register
_types
),
756 'register_mappings': Object
.to_json(self
.__register
_mappings
),
759 def encode_json_pretty(self
):
761 Use a custom JSON encoder which pretty prints, but keeps inner structures compact
763 # Since the JSON module isn't very extensible, this ends up being
769 placeholder
= "JSON-{key}-NOSJ".format(key
=len(replacements
))
770 replacements
.append(json
.dumps(s
, sort_keys
=True))
773 # Pre-create non-indented encodings for inner objects
774 for enum
in obj
['enums'].values():
777 for entry
in enum
['entries']
780 for regtype
in obj
['register_types'].values():
781 regtype
['fields'] = [
783 for field
in regtype
['fields']
786 for regmap
in obj
['register_mappings']:
787 regmap
['map'] = placeholder(regmap
['map'])
788 if 'chips' in regmap
:
789 regmap
['chips'] = placeholder(regmap
['chips'])
791 # Now create the 'outer' encoding with indentation and search-and-replace
793 result
= json
.dumps(obj
, indent
=1, sort_keys
=True)
796 '"JSON-([0-9]+)-NOSJ"',
797 lambda m
: replacements
[int(m
.group(1))],
805 db
= RegisterDatabase()
807 db
.__enums
= dict((k
, Object
.from_json(v
)) for k
, v
in json
['enums'].items())
808 if 'register_types' in json
:
809 db
.__register
_types
= dict(
810 (k
, Object
.from_json(v
))
811 for k
, v
in json
['register_types'].items()
813 if 'register_mappings' in json
:
814 db
.__register
_mappings
= Object
.from_json(json
['register_mappings'])
817 if 'registers' in json
:
818 for reg
in json
['registers']:
820 if 'fields' in reg
and reg
['fields']:
821 type_ref
= reg
['names'][0]
822 db
.add_register_type(type_ref
, Object(
823 fields
=Object
.from_json(reg
['fields'])
826 for name
in reg
['names']:
829 map=Object
.from_json(reg
['map'])
831 if type_ref
is not None:
832 regmap
.type_ref
= type_ref
833 db
.add_register_mapping(regmap
)
838 def deduplicate_enums(regdb
):
840 Find enums that have the exact same entries and merge them.
842 buckets
= defaultdict(list)
843 for name
, enum
in regdb
.enums():
844 buckets
[RegisterDatabase
.enum_key(enum
)].append(name
)
846 for bucket
in buckets
.values():
848 regdb
.merge_enums(bucket
, bucket
[0])
850 def deduplicate_register_types(regdb
):
852 Find register types with the exact same fields (identified by name and
853 bit range) and merge them.
855 However, register types *aren't* merged if they have different enums for
856 the same field (as an exception, if one of them has an enum and the other
857 one doesn't, we assume that one is simply missing a bit of information and
858 merge the register types).
860 buckets
= defaultdict(list)
861 for name
, regtype
in regdb
.register_types():
863 ':{0}:{1}:{2}:'.format(
864 field
.name
, field
.bits
[0], field
.bits
[1],
866 for field
in regtype
.fields
868 buckets
[key
].append((name
, regtype
.fields
))
870 for bucket
in buckets
.values():
871 # Register types in the same bucket have the same fields in the same
872 # places, but they may have different enum_refs. Allow merging when
873 # one has an enum_ref and another doesn't, but don't merge if they
874 # have enum_refs that differ.
876 [getattr(field
, 'enum_ref', None) for field
in fields
]
877 for name
, fields
in bucket
880 regtypes
= [bucket
[0][0]]
881 enum_refs
= bucket_enum_refs
[0]
883 del bucket_enum_refs
[0]
886 while idx
< len(bucket
):
888 not lhs
or not rhs
or lhs
== rhs
889 for lhs
, rhs
in zip(enum_refs
, bucket_enum_refs
[idx
])
891 regtypes
.append(bucket
[idx
][0])
892 enum_refs
= [lhs
or rhs
for lhs
, rhs
in zip(enum_refs
, bucket_enum_refs
[idx
])]
894 del bucket_enum_refs
[idx
]
898 if len(regtypes
) > 1:
899 regdb
.merge_register_types(regtypes
, regtypes
[0])
901 # kate: space-indent on; indent-width 4; replace-tabs on;