Path: blob/21.2-virgl/src/amd/registers/regdb.py
7178 views
#1# Copyright 2017-2019 Advanced Micro Devices, Inc.2#3# Permission is hereby granted, free of charge, to any person obtaining a4# copy of this software and associated documentation files (the "Software"),5# to deal in the Software without restriction, including without limitation6# on the rights to use, copy, modify, merge, publish, distribute, sub7# license, and/or sell copies of the Software, and to permit persons to whom8# the Software is furnished to do so, subject to the following conditions:9#10# The above copyright notice and this permission notice (including the next11# paragraph) shall be included in all copies or substantial portions of the12# Software.13#14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL17# THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,18# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR19# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE20# USE OR OTHER DEALINGS IN THE SOFTWARE.21#22"""23Python package containing common tools for manipulating register JSON.24"""2526from __future__ import absolute_import, division, print_function, unicode_literals2728import itertools29import json30import re31import sys3233from collections import defaultdict34from contextlib import contextmanager3536class UnionFind(object):37"""38Simplistic implementation of a union-find data structure that also keeps39track of the sets that have been unified.4041- add: add an element to the implied global set of elements42- union: unify the sets containing the two given elements43- find: return the representative element of the set containing the44given element45- get_set: get the set containing the given element46- sets: iterate over all sets (the sets form a partition of the set of all47elements that have ever been added)48"""49def __init__(self):50self.d = {}5152def add(self, k):53if k not in self.d:54self.d[k] = set([k])5556def union(self, k1, k2):57k1 = self.find(k1)58k2 = self.find(k2)59if k1 == k2:60return61if len(k1) < len(k2):62k1, k2 = k2, k163self.d[k1].update(self.d[k2])64self.d[k2] = (k1,)6566def find(self, k):67e = self.d[k]68if isinstance(e, set):69return k70assert isinstance(e, tuple)71r = self.find(e[0])72self.d[k] = (r,)73return r7475def get_set(self, k):76k = self.find(k)77assert isinstance(self.d[k], set)78return self.d[k]7980def sets(self):81for v in self.d.values():82if isinstance(v, set):83yield v848586class Object(object):87"""88Convenience helper class that essentially acts as a dictionary for convenient89conversion from and to JSON while allowing the use of .field notation90instead of subscript notation for member access.91"""92def __init__(self, **kwargs):93for k, v in kwargs.items():94setattr(self, k, v)9596def update(self, **kwargs):97for key, value in kwargs.items():98setattr(self, key, value)99return self100101def __str__(self):102return 'Object(' + ', '.join(103'{k}={v}'.format(**locals()) for k, v, in self.__dict__.items()104) + ')'105106@staticmethod107def from_json(json, keys=None):108if isinstance(json, list):109return [Object.from_json(v) for v in json]110elif isinstance(json, dict):111obj = Object()112for k, v in json.items():113if keys is not None and k in keys:114v = keys[k](v)115else:116v = Object.from_json(v)117setattr(obj, k, v)118return obj119else:120return json121122@staticmethod123def to_json(obj):124if isinstance(obj, Object):125return dict((k, Object.to_json(v)) for k, v in obj.__dict__.items())126elif isinstance(obj, dict):127return dict((k, Object.to_json(v)) for k, v in obj.items())128elif isinstance(obj, list):129return [Object.to_json(v) for v in obj]130else:131return obj132133class MergeError(Exception):134def __init__(self, msg):135super(MergeError, self).__init__(msg)136137class RegisterDatabaseError(Exception):138def __init__(self, msg):139super(RegisterDatabaseError, self).__init__(msg)140141@contextmanager142def merge_scope(name):143"""144Wrap a merge handling function in a "scope" whose name will be added when145propagating MergeErrors.146"""147try:148yield149except Exception as e:150raise MergeError('{name}: {e}'.format(**locals()))151152def merge_dicts(dicts, keys=None, values=None):153"""154Generic dictionary merging function.155156dicts -- list of (origin, dictionary) pairs to merge157keys -- optional dictionary to provide a merge-strategy per key;158the merge strategy is a callable which will receive a list of159(origin, value) pairs160value -- optional function which provides a merge-strategy for values;161the merge strategy is a callable which will receive the name of162the key and a list of (origin, value) pairs163164The default strategy is to allow merging keys if all origin dictionaries165that contain the key have the same value for it.166"""167ks = set()168for _, d in dicts:169ks.update(d.keys())170171result = {}172for k in ks:173vs = [(o, d[k]) for o, d in dicts if k in d]174with merge_scope('Key {k}'.format(**locals())):175if keys is not None and k in keys:176result[k] = keys[k](vs)177elif values is not None:178result[k] = values(k, vs)179else:180base_origin, base = vs[0]181for other_origin, other in vs[1:]:182if base != other:183raise MergeError('{base} (from {base_origin}) != {other} (from {other_origin})'.format(**locals()))184result[k] = base185return result186187def merge_objects(objects, keys=None):188"""189Like merge_dicts, but applied to instances of Object.190"""191return Object(**merge_dicts([(origin, obj.__dict__) for origin, obj in objects], keys=keys))192193class RegisterDatabase(object):194"""195A register database containing:196197- enums: these are lists of named values that can occur in a register field198- register types: description of a register type or template as a list of199fields200- register mappings: named and typed registers mapped at locations in an201address space202"""203def __init__(self):204self.__enums = {}205self.__register_types = {}206self.__register_mappings = []207self.__regmap_by_addr = None208self.__chips = None209210def __post_init(self):211"""212Perform some basic canonicalization:213- enum entries are sorted by value214- register type fields are sorted by starting bit215- __register_mappings is sorted by offset216- the chips field of register mappings is sorted217218Lazily computes the set of all chips mentioned by register mappings.219"""220if self.__regmap_by_addr is not None:221return222223for enum in self.__enums.values():224enum.entries.sort(key=lambda entry: entry.value)225226for regtype in self.__register_types.values():227regtype.fields.sort(key=lambda field: field.bits[0])228229self.__regmap_by_addr = defaultdict(list)230self.__chips = set()231232# Merge register mappings using sort order and garbage collect enums233# and register types.234old_register_mappings = self.__register_mappings235old_register_mappings.sort(key=lambda regmap: regmap.map.at)236237self.__register_mappings = []238for regmap in old_register_mappings:239addr = (regmap.map.to, regmap.map.at)240chips = set(getattr(regmap, 'chips', ['undef']))241type_ref = getattr(regmap, 'type_ref', None)242243self.__chips.update(chips)244245merged = False246for other in reversed(self.__register_mappings):247if other.name != regmap.name:248break249250other_addr = (other.map.to, other.map.at)251other_chips = getattr(other, 'chips', ['undef'])252other_type_ref = getattr(other, 'type_ref', None)253254if addr == other_addr and\255(type_ref is None or other_type_ref is None or type_ref == other_type_ref):256other.chips = sorted(list(chips.union(other_chips)))257if type_ref is not None:258other.type_ref = type_ref259merged = True260break261262if merged:263continue264265addrmappings = self.__regmap_by_addr[addr]266267for other in addrmappings:268other_type_ref = getattr(other, 'type_ref', None)269other_chips = getattr(other, 'chips', ['undef'])270if type_ref is not None and other_type_ref is not None and \271type_ref != other_type_ref and chips.intersection(other_chips):272raise RegisterDatabaseError(273'Registers {0} and {1} overlap and have conflicting types'.format(274other.name, regmap.name))275276addrmappings.append(regmap)277self.__register_mappings.append(regmap)278279def garbage_collect(self):280"""281Remove unreferenced enums and register types.282"""283old_enums = self.__enums284old_register_types = self.__register_types285286self.__enums = {}287self.__register_types = {}288for regmap in self.__register_mappings:289if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:290regtype = old_register_types[regmap.type_ref]291self.__register_types[regmap.type_ref] = regtype292for field in regtype.fields:293if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:294self.__enums[field.enum_ref] = old_enums[field.enum_ref]295296def __validate_register_type(self, regtype):297for field in regtype.fields:298if hasattr(field, 'enum_ref') and field.enum_ref not in self.__enums:299raise RegisterDatabaseError(300'Register type field {0} has unknown enum_ref {1}'.format(301field.name, field.enum_ref))302303def __validate_register_mapping(self, regmap):304if hasattr(regmap, 'type_ref') and regmap.type_ref not in self.__register_types:305raise RegisterDatabaseError(306'Register mapping {0} has unknown type_ref {1}'.format(307regmap.name, regmap.type_ref))308309def __validate(self):310for regtype in self.__register_types.values():311self.__validate_register_type(regtype)312for regmap in self.__register_mappings:313self.__validate_register_mapping(regmap)314315@staticmethod316def enum_key(enum):317"""318Return a key that uniquely describes the signature of the given319enum (assuming that it has been canonicalized). Two enums with the320same key can be merged.321"""322return ''.join(323':{0}:{1}'.format(entry.name, entry.value)324for entry in enum.entries325)326327def add_enum(self, name, enum):328if name in self.__enums:329raise RegisterDatabaseError('Duplicate enum ' + name)330self.__enums[name] = enum331332@staticmethod333def __merge_enums(enums, union=False):334def merge_entries(entries_lists):335values = defaultdict(list)336for origin, enum in entries_lists:337for entry in enum:338values[entry.value].append((origin, entry))339340if not union:341if any(len(entries) != len(enums) for entries in values.values()):342raise RegisterDatabaseError(343'Attempting to merge enums with different values')344345return [346merge_objects(entries)347for entries in values.values()348]349350return merge_objects(351enums,352keys={353'entries': merge_entries,354}355)356357def merge_enums(self, names, newname, union=False):358"""359Given a list of enum names, merge them all into one with a new name and360update all references.361"""362if newname not in names and newname in self.__enums:363raise RegisterDatabaseError('Enum {0} already exists'.format(newname))364365newenum = self.__merge_enums(366[(name, self.__enums[name]) for name in names],367union=union368)369370for name in names:371del self.__enums[name]372self.__enums[newname] = newenum373374for regtype in self.__register_types.values():375for field in regtype.fields:376if getattr(field, 'enum_ref', None) in names:377field.enum_ref = newname378379self.__regmap_by_addr = None380381def add_register_type(self, name, regtype):382if regtype in self.__register_types:383raise RegisterDatabaseError('Duplicate register type ' + name)384self.__register_types[name] = regtype385self.__validate_register_type(regtype)386387def register_type(self, name):388self.__post_init()389return self.__register_types[name]390391@staticmethod392def __merge_register_types(regtypes, union=False, field_keys={}):393def merge_fields(fields_lists):394fields = defaultdict(list)395for origin, fields_list in fields_lists:396for field in fields_list:397fields[field.bits[0]].append((origin, field))398399if not union:400if any(len(entries) != len(regtypes) for entries in fields.values()):401raise RegisterDatabaseError(402'Attempting to merge register types with different fields')403404return [405merge_objects(field, keys=field_keys)406for field in fields.values()407]408409with merge_scope('Register types {0}'.format(', '.join(name for name, _ in regtypes))):410return merge_objects(411regtypes,412keys={413'fields': merge_fields,414}415)416417def merge_register_types(self, names, newname, union=False):418"""419Given a list of register type names, merge them all into one with a420new name and update all references.421"""422if newname not in names and newname in self.__register_types:423raise RegisterDatabaseError('Register type {0} already exists'.format(newname))424425newregtype = self.__merge_register_types(426[(name, self.__register_types[name]) for name in names],427union=union428)429430for name in names:431del self.__register_types[name]432self.__register_types[newname] = newregtype433434for regmap in self.__register_mappings:435if getattr(regmap, 'type_ref', None) in names:436regmap.type_ref = newname437438self.__regmap_by_addr = None439440def add_register_mapping(self, regmap):441self.__regmap_by_addr = None442self.__register_mappings.append(regmap)443self.__validate_register_mapping(regmap)444445def remove_register_mappings(self, regmaps_to_remove):446self.__post_init()447448regmaps_to_remove = set(regmaps_to_remove)449450regmaps = self.__register_mappings451self.__register_mappings = []452for regmap in regmaps:453if regmap not in regmaps_to_remove:454self.__register_mappings.append(regmap)455456self.__regmap_by_addr = None457458def enum(self, name):459"""460Return the enum of the given name, if any.461"""462self.__post_init()463return self.__enums.get(name, None)464465def enums(self):466"""467Yields all (name, enum) pairs.468"""469self.__post_init()470for name, enum in self.__enums.items():471yield (name, enum)472473def fields(self):474"""475Yields all (register_type, fields) pairs.476"""477self.__post_init()478for regtype in self.__register_types.values():479for field in regtype.fields:480yield (regtype, field)481482def register_types(self):483"""484Yields all (name, register_type) pairs.485"""486self.__post_init()487for name, regtype in self.__register_types.items():488yield (name, regtype)489490def register_mappings_by_name(self, name):491"""492Return a list of register mappings with the given name.493"""494self.__post_init()495496begin = 0497end = len(self.__register_mappings)498while begin < end:499middle = (begin + end) // 2500if self.__register_mappings[middle].name < name:501begin = middle + 1502elif name < self.__register_mappings[middle].name:503end = middle504else:505break506507if begin >= end:508return []509510# We now have begin <= mid < end with begin.name <= name, mid.name == name, name < end.name511# Narrow down begin and end512hi = middle513while begin < hi:514mid = (begin + hi) // 2515if self.__register_mappings[mid].name < name:516begin = mid + 1517else:518hi = mid519520lo = middle + 1521while lo < end:522mid = (lo + end) // 2523if self.__register_mappings[mid].name == name:524lo = mid + 1525else:526end = mid527528return self.__register_mappings[begin:end]529530def register_mappings(self):531"""532Yields all register mappings.533"""534self.__post_init()535for regmap in self.__register_mappings:536yield regmap537538def chips(self):539"""540Yields all chips.541"""542self.__post_init()543return iter(self.__chips)544545def merge_chips(self, chips, newchip):546"""547Merge register mappings of the given chips into a single chip of the548given name. Recursively merges register types and enums when appropriate.549"""550self.__post_init()551552chips = set(chips)553554regtypes_merge = UnionFind()555enums_merge = UnionFind()556557# Walk register mappings to find register types that should be merged.558for idx, regmap in itertools.islice(enumerate(self.__register_mappings), 1, None):559if not hasattr(regmap, 'type_ref'):560continue561if chips.isdisjoint(regmap.chips):562continue563564for other in self.__register_mappings[idx-1::-1]:565if regmap.name != other.name:566break567if chips.isdisjoint(other.chips):568continue569if regmap.map.to != other.map.to or regmap.map.at != other.map.at:570raise RegisterDatabaseError(571'Attempting to merge chips with incompatible addresses of {0}'.format(regmap.name))572if not hasattr(regmap, 'type_ref'):573continue574575if regmap.type_ref != other.type_ref:576regtypes_merge.add(regmap.type_ref)577regtypes_merge.add(other.type_ref)578regtypes_merge.union(regmap.type_ref, other.type_ref)579580# Walk over regtype sets that are to be merged and find enums that581# should be merged.582for type_refs in regtypes_merge.sets():583fields_merge = defaultdict(set)584for type_ref in type_refs:585regtype = self.__register_types[type_ref]586for field in regtype.fields:587if hasattr(field, 'enum_ref'):588fields_merge[field.name].add(field.enum_ref)589590for enum_refs in fields_merge.values():591if len(enum_refs) > 1:592enum_refs = list(enum_refs)593enums_merge.add(enum_refs[0])594for enum_ref in enum_refs[1:]:595enums_merge.add(enum_ref)596enums_merge.union(enum_ref, enum_refs[0])597598# Merge all mergeable enum sets599remap_enum_refs = {}600for enum_refs in enums_merge.sets():601enum_refs = sorted(enum_refs)602newname = enum_refs[0] + '_' + newchip603i = 0604while newname in self.__enums:605newname = enum_refs[0] + '_' + newchip + str(i)606i += 1607608for enum_ref in enum_refs:609remap_enum_refs[enum_ref] = newname610611# Don't use self.merge_enums, because we don't want to automatically612# update _all_ references to the merged enums (some may be from613# register types that aren't going to be merged).614self.add_enum(newname, self.__merge_enums(615[(enum_ref, self.__enums[enum_ref]) for enum_ref in enum_refs],616union=True617))618619# Merge all mergeable type refs620remap_type_refs = {}621for type_refs in regtypes_merge.sets():622type_refs = sorted(type_refs)623newname = type_refs[0] + '_' + newchip624i = 0625while newname in self.__enums:626newname = type_refs[0] + '_' + newchip + str(i)627i += 1628629updated_regtypes = []630for type_ref in type_refs:631remap_type_refs[type_ref] = newname632633regtype = Object.from_json(Object.to_json(self.__register_types[type_ref]))634for field in regtype.fields:635if hasattr(field, 'enum_ref'):636field.enum_ref = remap_enum_refs.get(enum_ref, enum_ref)637638updated_regtypes.append(regtype)639640def merge_enum_refs(enum_refs):641enum_refs = set(642remap_enum_refs.get(enum_ref, enum_ref)643for origin, enum_ref in enum_refs644)645assert len(enum_refs) == 1 # should be ensured by how we determine the enums to be merged646return enum_refs.pop()647648self.add_register_type(newname, self.__merge_register_types(649[(type_ref, self.__register_types[type_ref]) for type_ref in type_refs],650field_keys={651'enum_ref': merge_enum_refs,652},653union=True654))655656# Merge register mappings657register_mappings = self.__register_mappings658self.__register_mappings = []659660regmap_accum = None661for regmap in register_mappings:662if regmap_accum and regmap.name != regmap_accum.name:663regmap_accum.chips = [newchip]664self.__register_mappings.append(regmap_accum)665regmap_accum = None666667joining_chips = chips.intersection(regmap.chips)668if not joining_chips:669self.__register_mappings.append(regmap)670continue671remaining_chips = set(regmap.chips).difference(chips)672673type_ref = getattr(regmap, 'type_ref', None)674if type_ref is None:675regmap.chips = sorted(remaining_chips.union([newchip]))676self.__register_mappings.append(regmap)677continue678679type_ref = remap_type_refs.get(type_ref, type_ref)680if remaining_chips:681regmap.chips = sorted(remaining_chips)682self.__register_mappings.append(regmap)683if not regmap_accum:684regmap = Object.from_json(Object.to_json(regmap))685if type_ref is not None:686regmap.type_ref = type_ref687688if not regmap_accum:689regmap_accum = regmap690else:691if not hasattr(regmap_accum.type_ref, 'type_ref'):692if type_ref is not None:693regmap_accum.type_ref = type_ref694else:695assert type_ref is None or type_ref == regmap_accum.type_ref696if regmap_accum:697self.__register_mappings.append(regmap_accum)698699def update(self, other):700"""701Add the contents of the other database to self.702703Doesn't de-duplicate entries.704"""705self.__post_init()706other.__post_init()707708enum_remap = {}709regtype_remap = {}710711for regmap in other.__register_mappings:712regmap = Object.from_json(Object.to_json(regmap))713714type_ref = getattr(regmap, 'type_ref', None)715if type_ref is not None and type_ref not in regtype_remap:716regtype = Object.from_json(Object.to_json(other.__register_types[type_ref]))717718chips = getattr(regmap, 'chips', [])719suffix = '_' + chips[0] if chips else ''720721for field in regtype.fields:722enum_ref = getattr(field, 'enum_ref', None)723if enum_ref is not None and enum_ref not in enum_remap:724enum = Object.from_json(Object.to_json(other.__enums[enum_ref]))725726remapped = enum_ref + suffix if enum_ref in self.__enums else enum_ref727i = 0728while remapped in self.__enums:729remapped = enum_ref + suffix + str(i)730i += 1731self.add_enum(remapped, enum)732enum_remap[enum_ref] = remapped733734if enum_ref is not None:735field.enum_ref = enum_remap[enum_ref]736737remapped = type_ref + suffix if type_ref in self.__register_types else type_ref738i = 0739while remapped in self.__register_types:740remapped = type_ref + suffix + str(i)741i += 1742self.add_register_type(remapped, regtype)743regtype_remap[type_ref] = remapped744745if type_ref is not None:746regmap.type_ref = regtype_remap[type_ref]747748self.add_register_mapping(regmap)749750def to_json(self):751self.__post_init()752return {753'enums': Object.to_json(self.__enums),754'register_types': Object.to_json(self.__register_types),755'register_mappings': Object.to_json(self.__register_mappings),756}757758def encode_json_pretty(self):759"""760Use a custom JSON encoder which pretty prints, but keeps inner structures compact761"""762# Since the JSON module isn't very extensible, this ends up being763# really hacky.764obj = self.to_json()765766replacements = []767def placeholder(s):768placeholder = "JSON-{key}-NOSJ".format(key=len(replacements))769replacements.append(json.dumps(s, sort_keys=True))770return placeholder771772# Pre-create non-indented encodings for inner objects773for enum in obj['enums'].values():774enum['entries'] = [775placeholder(entry)776for entry in enum['entries']777]778779for regtype in obj['register_types'].values():780regtype['fields'] = [781placeholder(field)782for field in regtype['fields']783]784785for regmap in obj['register_mappings']:786regmap['map'] = placeholder(regmap['map'])787if 'chips' in regmap:788regmap['chips'] = placeholder(regmap['chips'])789790# Now create the 'outer' encoding with indentation and search-and-replace791# placeholders792result = json.dumps(obj, indent=1, sort_keys=True)793794result = re.sub(795'"JSON-([0-9]+)-NOSJ"',796lambda m: replacements[int(m.group(1))],797result798)799800return result801802@staticmethod803def from_json(json):804db = RegisterDatabase()805806db.__enums = dict((k, Object.from_json(v)) for k, v in json['enums'].items())807if 'register_types' in json:808db.__register_types = dict(809(k, Object.from_json(v))810for k, v in json['register_types'].items()811)812if 'register_mappings' in json:813db.__register_mappings = Object.from_json(json['register_mappings'])814815# Old format816if 'registers' in json:817for reg in json['registers']:818type_ref = None819if 'fields' in reg and reg['fields']:820type_ref = reg['names'][0]821db.add_register_type(type_ref, Object(822fields=Object.from_json(reg['fields'])823))824825for name in reg['names']:826regmap = Object(827name=name,828map=Object.from_json(reg['map'])829)830if type_ref is not None:831regmap.type_ref = type_ref832db.add_register_mapping(regmap)833834db.__post_init()835return db836837def deduplicate_enums(regdb):838"""839Find enums that have the exact same entries and merge them.840"""841buckets = defaultdict(list)842for name, enum in regdb.enums():843buckets[RegisterDatabase.enum_key(enum)].append(name)844845for bucket in buckets.values():846if len(bucket) > 1:847regdb.merge_enums(bucket, bucket[0])848849def deduplicate_register_types(regdb):850"""851Find register types with the exact same fields (identified by name and852bit range) and merge them.853854However, register types *aren't* merged if they have different enums for855the same field (as an exception, if one of them has an enum and the other856one doesn't, we assume that one is simply missing a bit of information and857merge the register types).858"""859buckets = defaultdict(list)860for name, regtype in regdb.register_types():861key = ''.join(862':{0}:{1}:{2}:'.format(863field.name, field.bits[0], field.bits[1],864)865for field in regtype.fields866)867buckets[key].append((name, regtype.fields))868869for bucket in buckets.values():870# Register types in the same bucket have the same fields in the same871# places, but they may have different enum_refs. Allow merging when872# one has an enum_ref and another doesn't, but don't merge if they873# have enum_refs that differ.874bucket_enum_refs = [875[getattr(field, 'enum_ref', None) for field in fields]876for name, fields in bucket877]878while bucket:879regtypes = [bucket[0][0]]880enum_refs = bucket_enum_refs[0]881del bucket[0]882del bucket_enum_refs[0]883884idx = 0885while idx < len(bucket):886if all([887not lhs or not rhs or lhs == rhs888for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])889]):890regtypes.append(bucket[idx][0])891enum_refs = [lhs or rhs for lhs, rhs in zip(enum_refs, bucket_enum_refs[idx])]892del bucket[idx]893del bucket_enum_refs[idx]894else:895idx += 1896897if len(regtypes) > 1:898regdb.merge_register_types(regtypes, regtypes[0])899900# kate: space-indent on; indent-width 4; replace-tabs on;901902903