xref: /aosp_15_r20/external/mesa3d/src/amd/registers/regdb.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#
2# Copyright 2017-2019 Advanced Micro Devices, Inc.
3#
4# SPDX-License-Identifier: MIT
5#
6"""
7Python package containing common tools for manipulating register JSON.
8"""
9
10import itertools
11import json
12import re
13import sys
14
15from collections import defaultdict
16from contextlib import contextmanager
17
18class UnionFind(object):
19    """
20    Simplistic implementation of a union-find data structure that also keeps
21    track of the sets that have been unified.
22
23    - add: add an element to the implied global set of elements
24    - union: unify the sets containing the two given elements
25    - find: return the representative element of the set containing the
26            given element
27    - get_set: get the set containing the given element
28    - sets: iterate over all sets (the sets form a partition of the set of all
29            elements that have ever been added)
30    """
31    def __init__(self):
32        self.d = {}
33
34    def add(self, k):
35        if k not in self.d:
36            self.d[k] = set([k])
37
38    def union(self, k1, k2):
39        k1 = self.find(k1)
40        k2 = self.find(k2)
41        if k1 == k2:
42            return
43        if len(k1) < len(k2):
44            k1, k2 = k2, k1
45        self.d[k1].update(self.d[k2])
46        self.d[k2] = (k1,)
47
48    def find(self, k):
49        e = self.d[k]
50        if isinstance(e, set):
51            return k
52        assert isinstance(e, tuple)
53        r = self.find(e[0])
54        self.d[k] = (r,)
55        return r
56
57    def get_set(self, k):
58        k = self.find(k)
59        assert isinstance(self.d[k], set)
60        return self.d[k]
61
62    def sets(self):
63        for v in self.d.values():
64            if isinstance(v, set):
65                yield v
66
67
68class Object(object):
69    """
70    Convenience helper class that essentially acts as a dictionary for convenient
71    conversion from and to JSON while allowing the use of .field notation
72    instead of subscript notation for member access.
73    """
74    def __init__(self, **kwargs):
75        for k, v in kwargs.items():
76            setattr(self, k, v)
77
78    def update(self, **kwargs):
79        for key, value in kwargs.items():
80            setattr(self, key, value)
81        return self
82
83    def __str__(self):
84        return 'Object(' + ', '.join(
85            '{k}={v}'.format(**locals()) for k, v, in self.__dict__.items()
86        ) + ')'
87
88    @staticmethod
89    def from_json(json, keys=None):
90        if isinstance(json, list):
91            return [Object.from_json(v) for v in json]
92        elif isinstance(json, dict):
93            obj = Object()
94            for k, v in json.items():
95                if keys is not None and k in keys:
96                    v = keys[k](v)
97                else:
98                    v = Object.from_json(v)
99                setattr(obj, k, v)
100            return obj
101        else:
102            return json
103
104    @staticmethod
105    def to_json(obj):
106        if isinstance(obj, Object):
107            return dict((k, Object.to_json(v)) for k, v in obj.__dict__.items())
108        elif isinstance(obj, dict):
109            return dict((k, Object.to_json(v)) for k, v in obj.items())
110        elif isinstance(obj, list):
111            return [Object.to_json(v) for v in obj]
112        else:
113            return obj
114
115class MergeError(Exception):
116    def __init__(self, msg):
117        super(MergeError, self).__init__(msg)
118
119class RegisterDatabaseError(Exception):
120    def __init__(self, msg):
121        super(RegisterDatabaseError, self).__init__(msg)
122
123@contextmanager
124def merge_scope(name):
125    """
126    Wrap a merge handling function in a "scope" whose name will be added when
127    propagating MergeErrors.
128    """
129    try:
130        yield
131    except Exception as e:
132        raise MergeError('{name}: {e}'.format(**locals()))
133
134def merge_dicts(dicts, keys=None, values=None):
135    """
136    Generic dictionary merging function.
137
138    dicts -- list of (origin, dictionary) pairs to merge
139    keys -- optional dictionary to provide a merge-strategy per key;
140            the merge strategy is a callable which will receive a list of
141            (origin, value) pairs
142    value -- optional function which provides a merge-strategy for values;
143             the merge strategy is a callable which will receive the name of
144             the key and a list of (origin, value) pairs
145
146    The default strategy is to allow merging keys if all origin dictionaries
147    that contain the key have the same value for it.
148    """
149    ks = set()
150    for _, d in dicts:
151        ks.update(d.keys())
152
153    result = {}
154    for k in ks:
155        vs = [(o, d[k]) for o, d in dicts if k in d]
156        with merge_scope('Key {k}'.format(**locals())):
157            if keys is not None and k in keys:
158                result[k] = keys[k](vs)
159            elif values is not None:
160                result[k] = values(k, vs)
161            else:
162                base_origin, base = vs[0]
163                for other_origin, other in vs[1:]:
164                    if base != other:
165                        raise MergeError('{base} (from {base_origin}) != {other} (from {other_origin})'.format(**locals()))
166                result[k] = base
167    return result
168
169def merge_objects(objects, keys=None):
170    """
171    Like merge_dicts, but applied to instances of Object.
172    """
173    return Object(**merge_dicts([(origin, obj.__dict__) for origin, obj in objects], keys=keys))
174
175class RegisterDatabase(object):
176    """
177    A register database containing:
178
179    - enums: these are lists of named values that can occur in a register field
180    - register types: description of a register type or template as a list of
181                      fields
182    - register mappings: named and typed registers mapped at locations in an
183                         address space
184    """
185    def __init__(self):
186        self.__enums = {}
187        self.__register_types = {}
188        self.__register_mappings = []
189        self.__regmap_by_addr = None
190        self.__chips = None
191
192    def __post_init(self):
193        """
194        Perform some basic canonicalization:
195        - enum entries are sorted by value
196        - register type fields are sorted by starting bit
197        - __register_mappings is sorted by offset
198        - the chips field of register mappings is sorted
199
200        Lazily computes the set of all chips mentioned by register mappings.
201        """
202        if self.__regmap_by_addr is not None:
203            return
204
205        for enum in self.__enums.values():
206            enum.entries.sort(key=lambda entry: entry.value)
207
208        for regtype in self.__register_types.values():
209            regtype.fields.sort(key=lambda field: field.bits[0])
210
211        self.__regmap_by_addr = defaultdict(list)
212        self.__chips = set()
213
214        # Merge register mappings using sort order and garbage collect enums
215        # and register types.
216        old_register_mappings = self.__register_mappings
217        old_register_mappings.sort(key=lambda regmap: regmap.map.at)
218
219        self.__register_mappings = []
220        for regmap in old_register_mappings:
221            addr = (regmap.map.to, regmap.map.at)
222            chips = set(getattr(regmap, 'chips', ['undef']))
223            type_ref = getattr(regmap, 'type_ref', None)
224
225            self.__chips.update(chips)
226
227            merged = False
228            for other in reversed(self.__register_mappings):
229                if other.name != regmap.name:
230                    break
231
232                other_addr = (other.map.to, other.map.at)
233                other_chips = getattr(other, 'chips', ['undef'])
234                other_type_ref = getattr(other, 'type_ref', None)
235
236                if addr == other_addr and\
237                   (type_ref is None or other_type_ref is None or type_ref == other_type_ref):
238                    other.chips = sorted(list(chips.union(other_chips)))
239                    if type_ref is not None:
240                        other.type_ref = type_ref
241                    merged = True
242                    break
243
244            if merged:
245                continue
246
247            addrmappings = self.__regmap_by_addr[addr]
248
249            for other in addrmappings:
250                other_type_ref = getattr(other, 'type_ref', None)
251                other_chips = getattr(other, 'chips', ['undef'])
252                if type_ref is not None and other_type_ref is not None and \
253                   type_ref != other_type_ref and chips.intersection(other_chips):
254                    raise RegisterDatabaseError(
255                        'Registers {0} and {1} overlap and have conflicting types'.format(
256                            other.name, regmap.name))
257
258            addrmappings.append(regmap)
259            self.__register_mappings.append(regmap)
260
261    def garbage_collect(self):
262        """
263        Remove unreferenced enums and register types.
264        """
265        old_enums = self.__enums
266        old_register_types = self.__register_types
267
268        self.__enums = {}
269        self.__register_types = {}
270        for regmap in self.__register_mappings:
271            if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:
272                regtype = old_register_types[regmap.type_ref]
273                self.__register_types[regmap.type_ref] = regtype
274                for field in regtype.fields:
275                    if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:
276                        self.__enums[field.enum_ref] = old_enums[field.enum_ref]
277
278    def __validate_register_type(self, regtype):
279        for field in regtype.fields:
280            if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:
281                raise RegisterDatabaseError(
282                    'Register type field {0} has unknown enum_ref {1}'.format(
283                        field.name, field.enum_ref))
284
285    def __validate_register_mapping(self, regmap):
286        if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:
287            raise RegisterDatabaseError(
288                'Register mapping {0} has unknown type_ref {1}'.format(
289                    regmap.name, regmap.type_ref))
290
291    def __validate(self):
292        for regtype in self.__register_types.values():
293            self.__validate_register_type(regtype)
294        for regmap in self.__register_mappings:
295            self.__validate_register_mapping(regmap)
296
297    @staticmethod
298    def enum_key(enum):
299        """
300        Return a key that uniquely describes the signature of the given
301        enum (assuming that it has been canonicalized). Two enums with the
302        same key can be merged.
303        """
304        return ''.join(
305            ':{0}:{1}'.format(entry.name, entry.value)
306            for entry in enum.entries
307        )
308
309    def add_enum(self, name, enum):
310        if name in self.__enums:
311            raise RegisterDatabaseError('Duplicate enum ' + name)
312        self.__enums[name] = enum
313
314    @staticmethod
315    def __merge_enums(enums, union=False):
316        def merge_entries(entries_lists):
317            values = defaultdict(list)
318            for origin, enum in entries_lists:
319                for entry in enum:
320                    values[entry.value].append((origin, entry))
321
322            if not union:
323                if any(len(entries) != len(enums) for entries in values.values()):
324                    raise RegisterDatabaseError(
325                        'Attempting to merge enums with different values')
326
327            return [
328                merge_objects(entries)
329                for entries in values.values()
330            ]
331
332        return merge_objects(
333            enums,
334            keys={
335                'entries': merge_entries,
336            }
337        )
338
339    def merge_enums(self, names, newname, union=False):
340        """
341        Given a list of enum names, merge them all into one with a new name and
342        update all references.
343        """
344        if newname not in names and newname in self.__enums:
345            raise RegisterDatabaseError('Enum {0} already exists'.format(newname))
346
347        newenum = self.__merge_enums(
348            [(name, self.__enums[name]) for name in names],
349            union=union
350        )
351
352        for name in names:
353            del self.__enums[name]
354        self.__enums[newname] = newenum
355
356        for regtype in self.__register_types.values():
357            for field in regtype.fields:
358                if getattr(field, 'enum_ref', None) in names:
359                    field.enum_ref = newname
360
361        self.__regmap_by_addr = None
362
363    def add_register_type(self, name, regtype):
364        if regtype in self.__register_types:
365            raise RegisterDatabaseError('Duplicate register type ' + name)
366        self.__register_types[name] = regtype
367        self.__validate_register_type(regtype)
368
369    def register_type(self, name):
370        self.__post_init()
371        return self.__register_types[name]
372
373    @staticmethod
374    def __merge_register_types(regtypes, union=False, field_keys={}):
375        def merge_fields(fields_lists):
376            fields = defaultdict(list)
377            for origin, fields_list in fields_lists:
378                for field in fields_list:
379                    fields[field.bits[0]].append((origin, field))
380
381            if not union:
382                if any(len(entries) != len(regtypes) for entries in fields.values()):
383                    raise RegisterDatabaseError(
384                        'Attempting to merge register types with different fields')
385
386            return [
387                merge_objects(field, keys=field_keys)
388                for field in fields.values()
389            ]
390
391        with merge_scope('Register types {0}'.format(', '.join(name for name, _ in regtypes))):
392            return merge_objects(
393                regtypes,
394                keys={
395                    'fields': merge_fields,
396                }
397            )
398
399    def merge_register_types(self, names, newname, union=False):
400        """
401        Given a list of register type names, merge them all into one with a
402        new name and update all references.
403        """
404        if newname not in names and newname in self.__register_types:
405            raise RegisterDatabaseError('Register type {0} already exists'.format(newname))
406
407        newregtype = self.__merge_register_types(
408            [(name, self.__register_types[name]) for name in names],
409            union=union
410        )
411
412        for name in names:
413            del self.__register_types[name]
414        self.__register_types[newname] = newregtype
415
416        for regmap in self.__register_mappings:
417            if getattr(regmap, 'type_ref', None) in names:
418                regmap.type_ref = newname
419
420        self.__regmap_by_addr = None
421
422    def add_register_mapping(self, regmap):
423        self.__regmap_by_addr = None
424        self.__register_mappings.append(regmap)
425        self.__validate_register_mapping(regmap)
426
427    def remove_register_mappings(self, regmaps_to_remove):
428        self.__post_init()
429
430        regmaps_to_remove = set(regmaps_to_remove)
431
432        regmaps = self.__register_mappings
433        self.__register_mappings = []
434        for regmap in regmaps:
435            if regmap not in regmaps_to_remove:
436                self.__register_mappings.append(regmap)
437
438        self.__regmap_by_addr = None
439
440    def enum(self, name):
441        """
442        Return the enum of the given name, if any.
443        """
444        self.__post_init()
445        return self.__enums.get(name, None)
446
447    def enums(self):
448        """
449        Yields all (name, enum) pairs.
450        """
451        self.__post_init()
452        for name, enum in self.__enums.items():
453            yield (name, enum)
454
455    def fields(self):
456        """
457        Yields all (register_type, fields) pairs.
458        """
459        self.__post_init()
460        for regtype in self.__register_types.values():
461            for field in regtype.fields:
462                yield (regtype, field)
463
464    def register_types(self):
465        """
466        Yields all (name, register_type) pairs.
467        """
468        self.__post_init()
469        for name, regtype in self.__register_types.items():
470            yield (name, regtype)
471
472    def register_mappings_by_name(self, name):
473        """
474        Return a list of register mappings with the given name.
475        """
476        self.__post_init()
477
478        begin = 0
479        end = len(self.__register_mappings)
480        while begin < end:
481            middle = (begin + end) // 2
482            if self.__register_mappings[middle].name < name:
483                begin = middle + 1
484            elif name < self.__register_mappings[middle].name:
485                end = middle
486            else:
487                break
488
489        if begin >= end:
490            return []
491
492        # We now have begin <= mid < end with begin.name <= name, mid.name == name, name < end.name
493        # Narrow down begin and end
494        hi = middle
495        while begin < hi:
496            mid = (begin + hi) // 2
497            if self.__register_mappings[mid].name < name:
498                begin = mid + 1
499            else:
500                hi = mid
501
502        lo = middle + 1
503        while lo < end:
504            mid = (lo + end) // 2
505            if self.__register_mappings[mid].name == name:
506                lo = mid + 1
507            else:
508                end = mid
509
510        return self.__register_mappings[begin:end]
511
512    def register_mappings(self):
513        """
514        Yields all register mappings.
515        """
516        self.__post_init()
517        for regmap in self.__register_mappings:
518            yield regmap
519
520    def chips(self):
521        """
522        Yields all chips.
523        """
524        self.__post_init()
525        return iter(self.__chips)
526
527    def merge_chips(self, chips, newchip):
528        """
529        Merge register mappings of the given chips into a single chip of the
530        given name. Recursively merges register types and enums when appropriate.
531        """
532        self.__post_init()
533
534        chips = set(chips)
535
536        regtypes_merge = UnionFind()
537        enums_merge = UnionFind()
538
539        # Walk register mappings to find register types that should be merged.
540        for idx, regmap in itertools.islice(enumerate(self.__register_mappings), 1, None):
541            if not hasattr(regmap, 'type_ref'):
542                continue
543            if chips.isdisjoint(regmap.chips):
544                continue
545
546            for other in self.__register_mappings[idx-1::-1]:
547                if regmap.name != other.name:
548                    break
549                if chips.isdisjoint(other.chips):
550                    continue
551                if regmap.map.to != other.map.to or regmap.map.at != other.map.at:
552                    raise RegisterDatabaseError(
553                        'Attempting to merge chips with incompatible addresses of {0}'.format(regmap.name))
554                if not hasattr(regmap, 'type_ref'):
555                    continue
556
557                if regmap.type_ref != other.type_ref:
558                    regtypes_merge.add(regmap.type_ref)
559                    regtypes_merge.add(other.type_ref)
560                    regtypes_merge.union(regmap.type_ref, other.type_ref)
561
562        # Walk over regtype sets that are to be merged and find enums that
563        # should be merged.
564        for type_refs in regtypes_merge.sets():
565            fields_merge = defaultdict(set)
566            for type_ref in type_refs:
567                regtype = self.__register_types[type_ref]
568                for field in regtype.fields:
569                    if hasattr(field, 'enum_ref'):
570                        fields_merge[field.name].add(field.enum_ref)
571
572            for enum_refs in fields_merge.values():
573                if len(enum_refs) > 1:
574                    enum_refs = list(enum_refs)
575                    enums_merge.add(enum_refs[0])
576                    for enum_ref in enum_refs[1:]:
577                        enums_merge.add(enum_ref)
578                        enums_merge.union(enum_ref, enum_refs[0])
579
580        # Merge all mergeable enum sets
581        remap_enum_refs = {}
582        for enum_refs in enums_merge.sets():
583            enum_refs = sorted(enum_refs)
584            newname = enum_refs[0] + '_' + newchip
585            i = 0
586            while newname in self.__enums:
587                newname = enum_refs[0] + '_' + newchip + str(i)
588                i += 1
589
590            for enum_ref in enum_refs:
591                remap_enum_refs[enum_ref] = newname
592
593            # Don't use self.merge_enums, because we don't want to automatically
594            # update _all_ references to the merged enums (some may be from
595            # register types that aren't going to be merged).
596            self.add_enum(newname, self.__merge_enums(
597                [(enum_ref, self.__enums[enum_ref]) for enum_ref in enum_refs],
598                union=True
599            ))
600
601        # Merge all mergeable type refs
602        remap_type_refs = {}
603        for type_refs in regtypes_merge.sets():
604            type_refs = sorted(type_refs)
605            newname = type_refs[0] + '_' + newchip
606            i = 0
607            while newname in self.__enums:
608                newname = type_refs[0] + '_' + newchip + str(i)
609                i += 1
610
611            updated_regtypes = []
612            for type_ref in type_refs:
613                remap_type_refs[type_ref] = newname
614
615                regtype = Object.from_json(Object.to_json(self.__register_types[type_ref]))
616                for field in regtype.fields:
617                    if hasattr(field, 'enum_ref'):
618                        field.enum_ref = remap_enum_refs.get(enum_ref, enum_ref)
619
620                updated_regtypes.append(regtype)
621
622            def merge_enum_refs(enum_refs):
623                enum_refs = set(
624                    remap_enum_refs.get(enum_ref, enum_ref)
625                    for origin, enum_ref in enum_refs
626                )
627                assert len(enum_refs) == 1 # should be ensured by how we determine the enums to be merged
628                return enum_refs.pop()
629
630            self.add_register_type(newname, self.__merge_register_types(
631                [(type_ref, self.__register_types[type_ref]) for type_ref in type_refs],
632                field_keys={
633                    'enum_ref': merge_enum_refs,
634                },
635                union=True
636            ))
637
638        # Merge register mappings
639        register_mappings = self.__register_mappings
640        self.__register_mappings = []
641
642        regmap_accum = None
643        for regmap in register_mappings:
644            if regmap_accum and regmap.name != regmap_accum.name:
645                regmap_accum.chips = [newchip]
646                self.__register_mappings.append(regmap_accum)
647                regmap_accum = None
648
649            joining_chips = chips.intersection(regmap.chips)
650            if not joining_chips:
651                self.__register_mappings.append(regmap)
652                continue
653            remaining_chips = set(regmap.chips).difference(chips)
654
655            type_ref = getattr(regmap, 'type_ref', None)
656            if type_ref is None:
657                regmap.chips = sorted(remaining_chips.union([newchip]))
658                self.__register_mappings.append(regmap)
659                continue
660
661            type_ref = remap_type_refs.get(type_ref, type_ref)
662            if remaining_chips:
663                regmap.chips = sorted(remaining_chips)
664                self.__register_mappings.append(regmap)
665                if not regmap_accum:
666                    regmap = Object.from_json(Object.to_json(regmap))
667                    if type_ref is not None:
668                        regmap.type_ref = type_ref
669
670            if not regmap_accum:
671                regmap_accum = regmap
672            else:
673                if not hasattr(regmap_accum.type_ref, 'type_ref'):
674                    if type_ref is not None:
675                        regmap_accum.type_ref = type_ref
676                else:
677                    assert type_ref is None or type_ref == regmap_accum.type_ref
678        if regmap_accum:
679            self.__register_mappings.append(regmap_accum)
680
681    def update(self, other):
682        """
683        Add the contents of the other database to self.
684
685        Doesn't de-duplicate entries.
686        """
687        self.__post_init()
688        other.__post_init()
689
690        enum_remap = {}
691        regtype_remap = {}
692
693        for regmap in other.__register_mappings:
694            regmap = Object.from_json(Object.to_json(regmap))
695
696            type_ref = getattr(regmap, 'type_ref', None)
697            if type_ref is not None and type_ref not in regtype_remap:
698                regtype = Object.from_json(Object.to_json(other.__register_types[type_ref]))
699
700                chips = getattr(regmap, 'chips', [])
701                suffix = '_' + chips[0] if chips else ''
702
703                for field in regtype.fields:
704                    enum_ref = getattr(field, 'enum_ref', None)
705                    if enum_ref is not None and enum_ref not in enum_remap:
706                        enum = Object.from_json(Object.to_json(other.__enums[enum_ref]))
707
708                        remapped = enum_ref + suffix if enum_ref in self.__enums else enum_ref
709                        i = 0
710                        while remapped in self.__enums:
711                            remapped = enum_ref + suffix + str(i)
712                            i += 1
713                        self.add_enum(remapped, enum)
714                        enum_remap[enum_ref] = remapped
715
716                    if enum_ref is not None:
717                        field.enum_ref = enum_remap[enum_ref]
718
719                remapped = type_ref + suffix if type_ref in self.__register_types else type_ref
720                i = 0
721                while remapped in self.__register_types:
722                    remapped = type_ref + suffix + str(i)
723                    i += 1
724                self.add_register_type(remapped, regtype)
725                regtype_remap[type_ref] = remapped
726
727            if type_ref is not None:
728                regmap.type_ref = regtype_remap[type_ref]
729
730            self.add_register_mapping(regmap)
731
732    def to_json(self):
733        self.__post_init()
734        return {
735            'enums': Object.to_json(self.__enums),
736            'register_types': Object.to_json(self.__register_types),
737            'register_mappings': Object.to_json(self.__register_mappings),
738        }
739
740    def encode_json_pretty(self):
741        """
742        Use a custom JSON encoder which pretty prints, but keeps inner structures compact
743        """
744        # Since the JSON module isn't very extensible, this ends up being
745        # really hacky.
746        obj = self.to_json()
747
748        replacements = []
749        def placeholder(s):
750            placeholder = "JSON-{key}-NOSJ".format(key=len(replacements))
751            replacements.append(json.dumps(s, sort_keys=True))
752            return placeholder
753
754        # Pre-create non-indented encodings for inner objects
755        for enum in obj['enums'].values():
756            enum['entries'] = [
757                placeholder(entry)
758                for entry in enum['entries']
759            ]
760
761        for regtype in obj['register_types'].values():
762            regtype['fields'] = [
763                placeholder(field)
764                for field in regtype['fields']
765            ]
766
767        for regmap in obj['register_mappings']:
768            regmap['map'] = placeholder(regmap['map'])
769            if 'chips' in regmap:
770                regmap['chips'] = placeholder(regmap['chips'])
771
772        # Now create the 'outer' encoding with indentation and search-and-replace
773        # placeholders
774        result = json.dumps(obj, indent=1, sort_keys=True)
775
776        result = re.sub(
777            '"JSON-([0-9]+)-NOSJ"',
778            lambda m: replacements[int(m.group(1))],
779            result
780        )
781
782        return result
783
784    @staticmethod
785    def from_json(json):
786        db = RegisterDatabase()
787
788        db.__enums = dict((k, Object.from_json(v)) for k, v in json['enums'].items())
789        if 'register_types' in json:
790            db.__register_types = dict(
791                (k, Object.from_json(v))
792                for k, v in json['register_types'].items()
793            )
794        if 'register_mappings' in json:
795            db.__register_mappings = Object.from_json(json['register_mappings'])
796
797        # Old format
798        if 'registers' in json:
799            for reg in json['registers']:
800                type_ref = None
801                if 'fields' in reg and reg['fields']:
802                    type_ref = reg['names'][0]
803                    db.add_register_type(type_ref, Object(
804                        fields=Object.from_json(reg['fields'])
805                    ))
806
807                for name in reg['names']:
808                    regmap = Object(
809                        name=name,
810                        map=Object.from_json(reg['map'])
811                    )
812                    if type_ref is not None:
813                        regmap.type_ref = type_ref
814                    db.add_register_mapping(regmap)
815
816        db.__post_init()
817        return db
818
819def deduplicate_enums(regdb):
820    """
821    Find enums that have the exact same entries and merge them.
822    """
823    buckets = defaultdict(list)
824    for name, enum in regdb.enums():
825        buckets[RegisterDatabase.enum_key(enum)].append(name)
826
827    for bucket in buckets.values():
828        if len(bucket) > 1:
829            regdb.merge_enums(bucket, bucket[0])
830
831def deduplicate_register_types(regdb):
832    """
833    Find register types with the exact same fields (identified by name and
834    bit range) and merge them.
835
836    However, register types *aren't* merged if they have different enums for
837    the same field (as an exception, if one of them has an enum and the other
838    one doesn't, we assume that one is simply missing a bit of information and
839    merge the register types).
840    """
841    buckets = defaultdict(list)
842    for name, regtype in regdb.register_types():
843        key = ''.join(
844            ':{0}:{1}:{2}:'.format(
845                field.name, field.bits[0], field.bits[1],
846            )
847            for field in regtype.fields
848        )
849        buckets[key].append((name, regtype.fields))
850
851    for bucket in buckets.values():
852        # Register types in the same bucket have the same fields in the same
853        # places, but they may have different enum_refs. Allow merging when
854        # one has an enum_ref and another doesn't, but don't merge if they
855        # have enum_refs that differ.
856        bucket_enum_refs = [
857            [getattr(field, 'enum_ref', None) for field in fields]
858            for name, fields in bucket
859        ]
860        while bucket:
861            regtypes = [bucket[0][0]]
862            enum_refs = bucket_enum_refs[0]
863            del bucket[0]
864            del bucket_enum_refs[0]
865
866            idx = 0
867            while idx < len(bucket):
868                if all([
869                    not lhs or not rhs or lhs == rhs
870                    for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])
871                ]):
872                    regtypes.append(bucket[idx][0])
873                    enum_refs = [lhs or rhs for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])]
874                    del bucket[idx]
875                    del bucket_enum_refs[idx]
876                else:
877                    idx += 1
878
879            if len(regtypes) > 1:
880                regdb.merge_register_types(regtypes, regtypes[0])
881
882# kate: space-indent on; indent-width 4; replace-tabs on;
883