| """ | |
| LLDB Formatters for MLIR data types. | |
| Load into LLDB with 'command script import /path/to/mlirDataFormatters.py' | |
| """ | |
| import re | |
| import lldb | |
| def get_expression_path(val: lldb.SBValue): | |
| """Compute the expression path for the given value.""" | |
| stream = lldb.SBStream() | |
| if not val.GetExpressionPath(stream): | |
| return None | |
| return stream.GetData() | |
| def build_ptr_str_from_addr(addrValue: lldb.SBValue, type: lldb.SBType): | |
| """Build a string that computes a pointer using the given address value and type.""" | |
| if type.is_reference: | |
| type = type.GetDereferencedType() | |
| if not type.is_pointer: | |
| type = type.GetPointerType() | |
| return f"(({type}){addrValue.GetData().GetUnsignedInt64(lldb.SBError(), 0)})" | |
| # ===----------------------------------------------------------------------=== # | |
| # Attributes and Types | |
| # ===----------------------------------------------------------------------=== # | |
| # This variable defines various mnemonic strings for use by the builtin | |
| # dialect attributes and types, which often have special formatting within | |
| # the parser/printer. | |
| builtin_attr_type_mnemonics = { | |
| "mlir::AffineMapAttr": '"affine_map<...>"', | |
| "mlir::ArrayAttr": '"[...]"', | |
| "mlir::DenseArray": '"array<...>"', | |
| "mlir::DenseResourceElementsAttr": '"dense_resource<...>"', | |
| "mlir::DictionaryAttr": '"{...}"', | |
| "mlir::IntegerAttr": '"float"', | |
| "mlir::IntegerAttr": '"integer"', | |
| "mlir::IntegerSetAttr": '"affine_set<...>"', | |
| "mlir::SparseElementsAttr": '"sparse<...>"', | |
| "mlir::StringAttr": '""...""', | |
| "mlir::StridedLayout": '"strided_layout"', | |
| "mlir::UnitAttr": '"unit"', | |
| "mlir::CallSiteLoc": '"loc(callsite(...))"', | |
| "mlir::FusedLoc": '"loc(fused<...>[...])"', | |
| "mlir::UnknownLoc": '"loc(unknown)"', | |
| "mlir::Float4E2M1FNType": '"f4E2M1FN"', | |
| "mlir::Float6E2M3FNType": '"f6E2M3FN"', | |
| "mlir::Float6E3M2FNType": '"f6E3M2FN"', | |
| "mlir::Float8E5M2Type": '"f8E5M2"', | |
| "mlir::Float8E4M3Type": '"f8E4M3"', | |
| "mlir::Float8E4M3FNType": '"f8E4M3FN"', | |
| "mlir::Float8E5M2FNUZType": '"f8E5M2FNUZ"', | |
| "mlir::Float8E4M3FNUZType": '"f8E4M3FNUZ"', | |
| "mlir::Float8E4M3B11FNUZType": '"f8E4M3B11FNUZ"', | |
| "mlir::Float8E3M4Type": '"f8E3M4"', | |
| "mlir::Float8E8M0FNUType": '"f8E8M0FNU"', | |
| "mlir::Float8E5M3FNUType": '"f8E5M3FNU"', | |
| "mlir::BFloat16Type": '"bf16"', | |
| "mlir::Float16Type": '"f16"', | |
| "mlir::FloatTF32Type": '"tf32"', | |
| "mlir::Float32Type": '"f32"', | |
| "mlir::Float64Type": '"f64"', | |
| "mlir::Float80Type": '"f80"', | |
| "mlir::Float128Type": '"f128"', | |
| "mlir::FunctionType": '"(...) -> (...)"', | |
| "mlir::IndexType": '"index"', | |
| "mlir::IntegerType": '"iN"', | |
| "mlir::NoneType": '"none"', | |
| "mlir::TupleType": '"tuple<...>"', | |
| "mlir::MemRefType": '"memref<...>"', | |
| "mlir::UnrankedMemRef": '"memref<...>"', | |
| "mlir::UnrankedTensorType": '"tensor<...>"', | |
| "mlir::RankedTensorType": '"tensor<...>"', | |
| "mlir::VectorType": '"vector<...>"', | |
| } | |
| class ComputedTypeIDMap: | |
| """Compute a map of type ids to derived attributes, types, and locations. | |
| This is necessary for determining the C++ type when holding a base class, | |
| where we really only have access to dynamic information. | |
| """ | |
| def __init__(self, target: lldb.SBTarget, internal_dict: dict): | |
| self.resolved_typeids = {} | |
| # Find all of the `id` variables, which are the name of TypeID variables | |
| # defined within the TypeIDResolver. | |
| type_ids = target.FindGlobalVariables("id", lldb.UINT32_MAX) | |
| for type_id in type_ids: | |
| # Strip out any matches that didn't come from a TypeID resolver. This | |
| # also lets us extract the derived type name. | |
| name = type_id.GetName() | |
| match = re.search("^mlir::detail::TypeIDResolver<(.*), void>::id$", name) | |
| if not match: | |
| continue | |
| type_name = match.group(1) | |
| # Filter out types that we don't care about. | |
| if not type_name.endswith(("Attr", "Loc", "Type")): | |
| continue | |
| # Find the LLDB type for the derived type. | |
| type = None | |
| for typeIt in target.FindTypes(type_name): | |
| if not typeIt or not typeIt.IsValid(): | |
| continue | |
| type = typeIt | |
| break | |
| if not type or not type.IsValid(): | |
| continue | |
| # Map the raw address of the type id variable to the LLDB type. | |
| self.resolved_typeids[type_id.AddressOf().GetValueAsUnsigned()] = type | |
| # Resolve the type for the given TypeID address. | |
| def resolve_type(self, typeIdAddr: lldb.SBValue): | |
| try: | |
| return self.resolved_typeids[typeIdAddr.GetValueAsUnsigned()] | |
| except KeyError: | |
| return None | |
| def is_derived_attribute_or_type(sbtype: lldb.SBType, internal_dict): | |
| """Return if the given type is a derived attribute or type.""" | |
| # We only expect an AttrBase/TypeBase base class. | |
| if sbtype.num_bases != 1: | |
| return False | |
| base_name = sbtype.GetDirectBaseClassAtIndex(0).GetName() | |
| return base_name.startswith(("mlir::Attribute::AttrBase", "mlir::Type::TypeBase")) | |
| def get_typeid_map(target: lldb.SBTarget, internal_dict: dict): | |
| """Get or construct a TypeID map for the given target.""" | |
| if "typeIdMap" not in internal_dict: | |
| internal_dict["typeIdMap"] = ComputedTypeIDMap(target, internal_dict) | |
| return internal_dict["typeIdMap"] | |
| def is_attribute_or_type(sbtype: lldb.SBType, internal_dict): | |
| """Return if the given type is an attribute or type.""" | |
| num_bases = sbtype.GetNumberOfDirectBaseClasses() | |
| typeName = sbtype.GetName() | |
| # We bottom out at Attribute/Type/Location. | |
| if num_bases == 0: | |
| return typeName in ["mlir::Attribute", "mlir::Type", "mlir::Location"] | |
| # Check the easy cases of AttrBase/TypeBase. | |
| if typeName.startswith(("mlir::Attribute::AttrBase", "mlir::Type::TypeBase")): | |
| return True | |
| # Otherwise, recurse into the base class. | |
| return is_attribute_or_type( | |
| sbtype.GetDirectBaseClassAtIndex(0).GetType(), internal_dict | |
| ) | |
| def resolve_attr_type_from_value( | |
| valobj: lldb.SBValue, abstractVal: lldb.SBValue, internal_dict | |
| ): | |
| """Resolve the derived C++ type of an Attribute/Type value.""" | |
| # Derived attribute/types already have the desired type. | |
| if is_derived_attribute_or_type(valobj.GetType(), internal_dict): | |
| return valobj.GetType() | |
| # Otherwise, we need to resolve the ImplTy from the TypeID. This is | |
| # done dynamically, because we don't use C++ RTTI of any kind. | |
| typeIdMap = get_typeid_map(valobj.GetTarget(), internal_dict) | |
| return typeIdMap.resolve_type( | |
| abstractVal.GetChildMemberWithName("typeID").GetChildMemberWithName("storage") | |
| ) | |
| class AttrTypeSynthProvider: | |
| """Define an LLDB synthetic children provider for Attributes and Types.""" | |
| def __init__(self, valobj: lldb.SBValue, internal_dict): | |
| self.valobj = valobj | |
| # Grab the impl variable, which if this is a Location needs to be | |
| # resolved through the LocationAttr impl variable. | |
| impl: lldb.SBValue = self.valobj.GetChildMemberWithName("impl") | |
| if self.valobj.GetTypeName() == "mlir::Location": | |
| impl = impl.GetChildMemberWithName("impl") | |
| self.abstractVal = impl.GetChildMemberWithName("abstractType") | |
| if not self.abstractVal.IsValid(): | |
| self.abstractVal = impl.GetChildMemberWithName("abstractAttribute") | |
| self.type = resolve_attr_type_from_value( | |
| valobj, self.abstractVal, internal_dict | |
| ) | |
| if not self.type: | |
| self.impl_type = None | |
| return | |
| # Grab the ImplTy from the resolved type. This is the 3rd template | |
| # argument of the base class. | |
| self.impl_type = ( | |
| self.type.GetDirectBaseClassAtIndex(0).GetType().GetTemplateArgumentType(2) | |
| ) | |
| self.impl_pointer_ty = self.impl_type.GetPointerType() | |
| self.num_fields = self.impl_type.GetNumberOfFields() | |
| # Optionally add a mnemonic field. | |
| type_name = self.type.GetName() | |
| if type_name in builtin_attr_type_mnemonics: | |
| self.mnemonic = builtin_attr_type_mnemonics[type_name] | |
| elif type_name.startswith("mlir::Dense"): | |
| self.mnemonic = "dense<...>" | |
| else: | |
| self.mnemonic = self.valobj.CreateValueFromExpression( | |
| "mnemonic", f"(llvm::StringRef){type_name}::getMnemonic()" | |
| ) | |
| if not self.mnemonic.summary: | |
| self.mnemonic = None | |
| if self.mnemonic: | |
| self.num_fields += 1 | |
| def num_children(self): | |
| if not self.impl_type: | |
| return 0 | |
| return self.num_fields | |
| def get_child_index(self, name): | |
| if not self.impl_type: | |
| return None | |
| if self.mnemonic and name == "[mnemonic]": | |
| return self.impl_type.GetNumberOfFields() | |
| for i in range(self.impl_type.GetNumberOfFields()): | |
| if self.impl_type.GetFieldAtIndex(i).GetName() == name: | |
| return i | |
| return None | |
| def get_child_at_index(self, index): | |
| if not self.impl_type or index >= self.num_fields: | |
| return None | |
| impl: lldb.SBValue = self.valobj.GetChildMemberWithName("impl") | |
| impl_ptr: lldb.SBValue = self.valobj.CreateValueFromData( | |
| build_ptr_str_from_addr(impl, self.impl_pointer_ty), | |
| impl.GetData(), | |
| self.impl_pointer_ty, | |
| ) | |
| # Check for the mnemonic field. | |
| if index == self.impl_type.GetNumberOfFields(): | |
| return self.valobj.CreateValueFromExpression( | |
| "[mnemonic]", self.get_mnemonic_string(impl_ptr) | |
| ) | |
| # Otherwise, we expect the index to be a field. | |
| field: lldb.SBTypeMember = self.impl_type.GetFieldAtIndex(index) | |
| # Build the field access by resolving through the impl variable. | |
| return impl_ptr.GetChildMemberWithName(field.GetName()) | |
| def get_mnemonic_string(self, impl_ptr: lldb.SBValue): | |
| if isinstance(self.mnemonic, str): | |
| return self.mnemonic | |
| # If we don't already have the mnemonic in string form, compute | |
| # it from the dialect name and the mnemonic. | |
| dialect_name = self.abstractVal.GetChildMemberWithName( | |
| "dialect" | |
| ).GetChildMemberWithName("name") | |
| self.mnemonic = f'{dialect_name.summary}"."{self.mnemonic.summary}' | |
| return self.mnemonic | |
| def AttrTypeSummaryProvider(valobj: lldb.SBValue, internal_dict): | |
| """Define an LLDB summary provider for Attributes and Types.""" | |
| # Check for a value field. | |
| value = valobj.GetChildMemberWithName("value") | |
| if value and value.summary: | |
| return value.summary | |
| # Otherwise, try the mnemoic. | |
| mnemonic: lldb.SBValue = valobj.GetChildMemberWithName("[mnemonic]") | |
| if not mnemonic.summary: | |
| return "" | |
| mnemonicStr = mnemonic.summary.strip('"') | |
| # Handle a few extremely common builtin attributes/types. | |
| ## IntegerType | |
| if mnemonicStr == "iN": | |
| signedness = valobj.GetChildMemberWithName("signedness").GetValueAsUnsigned() | |
| prefix = "i" | |
| if signedness == 1: | |
| prefix = "si" | |
| elif signedness == 2: | |
| prefix = "ui" | |
| return f"{prefix}{valobj.GetChildMemberWithName('width').GetValueAsUnsigned()}" | |
| ## IntegerAttr | |
| if mnemonicStr == "integer": | |
| value = valobj.GetChildMemberWithName("value") | |
| bitwidth = value.GetChildMemberWithName("BitWidth").GetValueAsUnsigned() | |
| if bitwidth <= 64: | |
| intVal = ( | |
| value.GetChildMemberWithName("U") | |
| .GetChildMemberWithName("VAL") | |
| .GetValueAsUnsigned() | |
| ) | |
| if bitwidth == 1: | |
| return "true" if intVal else "false" | |
| return f"{intVal} : i{bitwidth}" | |
| return mnemonicStr | |
| # ===----------------------------------------------------------------------=== # | |
| # mlir::Block | |
| # ===----------------------------------------------------------------------=== # | |
| class BlockSynthProvider: | |
| """Define an LLDB synthetic children provider for Blocks.""" | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| def num_children(self): | |
| return 3 | |
| def get_child_index(self, name): | |
| if name == "parent": | |
| return 0 | |
| if name == "operations": | |
| return 1 | |
| if name == "arguments": | |
| return 2 | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= 3: | |
| return None | |
| if index == 1: | |
| return self.valobj.GetChildMemberWithName("operations") | |
| if index == 2: | |
| return self.valobj.GetChildMemberWithName("arguments") | |
| expr_path = build_ptr_str_from_addr(self.valobj, self.valobj.GetType()) | |
| return self.valobj.CreateValueFromExpression( | |
| "parent", f"{expr_path}->getParent()" | |
| ) | |
| # ===----------------------------------------------------------------------=== # | |
| # mlir::Operation | |
| # ===----------------------------------------------------------------------=== # | |
| def is_op(sbtype: lldb.SBType, internal_dict): | |
| """Return if the given type is an operation.""" | |
| # Bottom out at OpState/Op. | |
| typeName = sbtype.GetName() | |
| if sbtype.GetNumberOfDirectBaseClasses() == 0: | |
| return typeName == "mlir::OpState" | |
| if typeName == "mlir::Operation" or typeName.startswith("mlir::Op<"): | |
| return True | |
| # Otherwise, recurse into the base class. | |
| return is_op(sbtype.GetDirectBaseClassAtIndex(0).GetType(), internal_dict) | |
| class OperationSynthProvider: | |
| """Define an LLDB synthetic children provider for Operations.""" | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| self.fields = [] | |
| self.update() | |
| def num_children(self): | |
| return len(self.fields) | |
| def get_child_index(self, name): | |
| try: | |
| return self.fields.index(name) | |
| except ValueError: | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= len(self.fields): | |
| return None | |
| name = self.fields[index] | |
| if name == "name": | |
| return self.opobj.GetChildMemberWithName("name") | |
| if name == "parent": | |
| return self.opobj.GetChildMemberWithName("block").Clone("parent") | |
| if name == "location": | |
| return self.opobj.GetChildMemberWithName("location") | |
| if name == "attributes": | |
| return self.opobj.GetChildMemberWithName("attrs") | |
| expr_path = build_ptr_str_from_addr(self.opobj, self.opobj.GetType()) | |
| if name == "operands": | |
| return self.opobj.CreateValueFromExpression( | |
| "operands", f"{expr_path}->debug_getOperands()" | |
| ) | |
| if name == "results": | |
| return self.opobj.CreateValueFromExpression( | |
| "results", f"{expr_path}->debug_getResults()" | |
| ) | |
| if name == "successors": | |
| return self.opobj.CreateValueFromExpression( | |
| "successors", f"{expr_path}->debug_getSuccessors()" | |
| ) | |
| if name == "regions": | |
| return self.opobj.CreateValueFromExpression( | |
| "regions", f"{expr_path}->debug_getRegions()" | |
| ) | |
| return None | |
| def update(self): | |
| # If this is a derived operation, we need to resolve through the | |
| # state field. | |
| self.opobj = self.valobj | |
| if "mlir::Operation" not in self.valobj.GetTypeName(): | |
| self.opobj = self.valobj.GetChildMemberWithName("state") | |
| self.fields = ["parent", "name", "location", "attributes"] | |
| if ( | |
| self.opobj.GetChildMemberWithName("hasOperandStorage").GetValueAsUnsigned(0) | |
| != 0 | |
| ): | |
| self.fields.append("operands") | |
| if self.opobj.GetChildMemberWithName("numResults").GetValueAsUnsigned(0) != 0: | |
| self.fields.append("results") | |
| if self.opobj.GetChildMemberWithName("numSuccs").GetValueAsUnsigned(0) != 0: | |
| self.fields.append("successors") | |
| if self.opobj.GetChildMemberWithName("numRegions").GetValueAsUnsigned(0) != 0: | |
| self.fields.append("regions") | |
| def OperationSummaryProvider(valobj: lldb.SBValue, internal_dict): | |
| """Define an LLDB summary provider for Operations.""" | |
| name = valobj.GetChildMemberWithName("name") | |
| if name and name.summary: | |
| return name.summary | |
| return "" | |
| # ===----------------------------------------------------------------------=== # | |
| # Ranges | |
| # ===----------------------------------------------------------------------=== # | |
| class DirectRangeSynthProvider: | |
| """Define an LLDB synthetic children provider for direct ranges, i.e. those | |
| with a base pointer that points to the type of element we want to display. | |
| """ | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| self.update() | |
| def num_children(self): | |
| return self.length | |
| def get_child_index(self, name): | |
| try: | |
| return int(name.lstrip("[").rstrip("]")) | |
| except: | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= self.num_children(): | |
| return None | |
| offset = index * self.type_size | |
| return self.data.CreateChildAtOffset(f"[{index}]", offset, self.data_type) | |
| def update(self): | |
| length_obj = self.valobj.GetChildMemberWithName("count") | |
| self.length = length_obj.GetValueAsUnsigned(0) | |
| self.data = self.valobj.GetChildMemberWithName("base") | |
| self.data_type = self.data.GetType().GetPointeeType() | |
| self.type_size = self.data_type.GetByteSize() | |
| assert self.type_size != 0 | |
| class InDirectRangeSynthProvider: | |
| """Define an LLDB synthetic children provider for ranges | |
| that transform the underlying base pointer, e.g. to convert | |
| it to a different type depending on various characteristics | |
| (e.g. mlir::ValueRange). | |
| """ | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| self.update() | |
| def num_children(self): | |
| return self.length | |
| def get_child_index(self, name): | |
| try: | |
| return int(name.lstrip("[").rstrip("]")) | |
| except: | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= self.num_children(): | |
| return None | |
| expr_path = get_expression_path(self.valobj) | |
| return self.valobj.CreateValueFromExpression( | |
| f"[{index}]", f"{expr_path}[{index}]" | |
| ) | |
| def update(self): | |
| length_obj = self.valobj.GetChildMemberWithName("count") | |
| self.length = length_obj.GetValueAsUnsigned(0) | |
| class IPListRangeSynthProvider: | |
| """Define an LLDB synthetic children provider for an IPList.""" | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| self.update() | |
| def num_children(self): | |
| sentinel = self.valobj.GetChildMemberWithName("Sentinel") | |
| sentinel_addr = sentinel.AddressOf().GetValueAsUnsigned(0) | |
| # Iterate the next pointers looking for the sentinel. | |
| count = 0 | |
| current = sentinel.GetChildMemberWithName("Next") | |
| while current.GetValueAsUnsigned(0) != sentinel_addr: | |
| current = current.GetChildMemberWithName("Next") | |
| count += 1 | |
| return count | |
| def get_child_index(self, name): | |
| try: | |
| return int(name.lstrip("[").rstrip("]")) | |
| except: | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= self.num_children(): | |
| return None | |
| # Start from the sentinel and grab the next pointer. | |
| value: lldb.SBValue = self.valobj.GetChildMemberWithName("Sentinel") | |
| it = 0 | |
| while it <= index: | |
| value = value.GetChildMemberWithName("Next") | |
| it += 1 | |
| return value.CreateValueFromExpression( | |
| f"[{index}]", | |
| f"(({self.value_type})({value.GetTypeName()}){value.GetValueAsUnsigned()})", | |
| ) | |
| def update(self): | |
| self.value_type = ( | |
| self.valobj.GetType().GetTemplateArgumentType(0).GetPointerType() | |
| ) | |
| # ===----------------------------------------------------------------------=== # | |
| # mlir::Value | |
| # ===----------------------------------------------------------------------=== # | |
| class ValueSynthProvider: | |
| """Define an LLDB synthetic children provider for Values.""" | |
| def __init__(self, valobj, internal_dict): | |
| self.valobj = valobj | |
| self.update() | |
| def num_children(self): | |
| # 7: BlockArgument: | |
| # index, type, owner, firstUse, location | |
| if self.kind == 7: | |
| return 5 | |
| # 0-6: OpResult: | |
| # index, type, owner, firstUse | |
| return 4 | |
| def get_child_index(self, name): | |
| if name == "index": | |
| return 0 | |
| if name == "type": | |
| return 1 | |
| if name == "owner": | |
| return 2 | |
| if name == "firstUse": | |
| return 3 | |
| if name == "location": | |
| return 4 | |
| return None | |
| def get_child_at_index(self, index): | |
| if index >= self.num_children(): | |
| return None | |
| # Check if the current value is already an Impl struct. | |
| if self.valobj.GetTypeName().endswith("Impl"): | |
| impl_ptr_str = build_ptr_str_from_addr( | |
| self.valobj.AddressOf(), self.valobj.GetType().GetPointerType() | |
| ) | |
| else: | |
| impl = self.valobj.GetChildMemberWithName("impl") | |
| impl_ptr_str = build_ptr_str_from_addr(impl, impl.GetType()) | |
| # Cast to the derived Impl type. | |
| if self.kind == 7: | |
| derived_impl_str = f"((mlir::detail::BlockArgumentImpl *){impl_ptr_str})" | |
| elif self.kind == 6: | |
| derived_impl_str = f"((mlir::detail::OutOfLineOpResult *){impl_ptr_str})" | |
| else: | |
| derived_impl_str = f"((mlir::detail::InlineOpResult *){impl_ptr_str})" | |
| # Handle the shared fields when possible. | |
| if index == 1: | |
| return self.valobj.CreateValueFromExpression( | |
| "type", f"{derived_impl_str}->debug_getType()" | |
| ) | |
| if index == 3: | |
| return self.valobj.CreateValueFromExpression( | |
| "firstUse", f"{derived_impl_str}->firstUse" | |
| ) | |
| # Handle Block argument children. | |
| if self.kind == 7: | |
| impl = self.valobj.CreateValueFromExpression("impl", derived_impl_str) | |
| if index == 0: | |
| return impl.GetChildMemberWithName("index") | |
| if index == 2: | |
| return impl.GetChildMemberWithName("owner") | |
| if index == 4: | |
| return impl.GetChildMemberWithName("loc") | |
| # Handle OpResult children. | |
| if index == 0: | |
| # Handle the out of line case. | |
| if self.kind == 6: | |
| return self.valobj.CreateValueFromExpression( | |
| "index", f"{derived_impl_str}->outOfLineIndex + 6" | |
| ) | |
| return self.valobj.CreateValueFromExpression("index", f"{self.kind}") | |
| if index == 2: | |
| return self.valobj.CreateValueFromExpression( | |
| "owner", f"{derived_impl_str}->getOwner()" | |
| ) | |
| return None | |
| def update(self): | |
| # Check if the current value is already an Impl struct. | |
| if self.valobj.GetTypeName().endswith("Impl"): | |
| impl_ptr_str = build_ptr_str_from_addr( | |
| self.valobj, self.valobj.GetType().GetPointerType() | |
| ) | |
| else: | |
| impl = self.valobj.GetChildMemberWithName("impl") | |
| impl_ptr_str = build_ptr_str_from_addr(impl, impl.GetType()) | |
| # Compute the kind of value we are dealing with. | |
| self.kind = self.valobj.CreateValueFromExpression( | |
| "kind", f"{impl_ptr_str}->debug_getKind()" | |
| ).GetValueAsUnsigned() | |
| def ValueSummaryProvider(valobj: lldb.SBValue, internal_dict): | |
| """Define an LLDB summary provider for Values.""" | |
| index = valobj.GetChildMemberWithName("index").GetValueAsUnsigned() | |
| # Check if this is a block argument or not (block arguments have locations). | |
| if valobj.GetChildMemberWithName("location").IsValid(): | |
| summary = f"Block Argument {index}" | |
| else: | |
| owner_name = ( | |
| valobj.GetChildMemberWithName("owner") | |
| .GetChildMemberWithName("name") | |
| .summary | |
| ) | |
| summary = f"{owner_name} Result {index}" | |
| # Grab the type to help form the summary. | |
| type = valobj.GetChildMemberWithName("type") | |
| if type.summary: | |
| summary += f": {type.summary}" | |
| return summary | |
| # ===----------------------------------------------------------------------=== # | |
| # Initialization | |
| # ===----------------------------------------------------------------------=== # | |
| def __lldb_init_module(debugger: lldb.SBDebugger, internal_dict): | |
| cat: lldb.SBTypeCategory = debugger.CreateCategory("mlir") | |
| cat.SetEnabled(True) | |
| # Attributes and Types | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier( | |
| "mlirDataFormatters.is_attribute_or_type", lldb.eFormatterMatchCallback | |
| ), | |
| lldb.SBTypeSummary.CreateWithFunctionName( | |
| "mlirDataFormatters.AttrTypeSummaryProvider" | |
| ), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier( | |
| "mlirDataFormatters.is_attribute_or_type", lldb.eFormatterMatchCallback | |
| ), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.AttrTypeSynthProvider" | |
| ), | |
| ) | |
| # Operation | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier("mlir::Block", lldb.eFormatterMatchExact), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.BlockSynthProvider" | |
| ), | |
| ) | |
| # NamedAttribute | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier("mlir::NamedAttribute", lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithSummaryString("${var.name%S} = ${var.value%S}"), | |
| ) | |
| # OperationName | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier("mlir::OperationName", lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithSummaryString("${var.impl->name%S}"), | |
| ) | |
| # Operation | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier( | |
| "mlirDataFormatters.is_op", lldb.eFormatterMatchCallback | |
| ), | |
| lldb.SBTypeSummary.CreateWithFunctionName( | |
| "mlirDataFormatters.OperationSummaryProvider" | |
| ), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier( | |
| "mlirDataFormatters.is_op", lldb.eFormatterMatchCallback | |
| ), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.OperationSynthProvider" | |
| ), | |
| ) | |
| # Ranges | |
| def add_direct_range_summary_and_synth(name): | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithSummaryString("size=${svar%#}"), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.DirectRangeSynthProvider" | |
| ), | |
| ) | |
| def add_indirect_range_summary_and_synth(name): | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithSummaryString("size=${svar%#}"), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.InDirectRangeSynthProvider" | |
| ), | |
| ) | |
| def add_iplist_range_summary_and_synth(name): | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithSummaryString("size=${svar%#}"), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.IPListRangeSynthProvider" | |
| ), | |
| ) | |
| add_direct_range_summary_and_synth("mlir::Operation::operand_range") | |
| add_direct_range_summary_and_synth("mlir::OperandRange") | |
| add_direct_range_summary_and_synth("mlir::Operation::result_range") | |
| add_direct_range_summary_and_synth("mlir::ResultRange") | |
| add_direct_range_summary_and_synth("mlir::SuccessorRange") | |
| add_indirect_range_summary_and_synth("mlir::ValueRange") | |
| add_indirect_range_summary_and_synth("mlir::TypeRange") | |
| add_iplist_range_summary_and_synth("mlir::Block::OpListType") | |
| add_iplist_range_summary_and_synth("mlir::Region::BlockListType") | |
| # Values | |
| def add_value_summary_and_synth(name): | |
| cat.AddTypeSummary( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSummary.CreateWithFunctionName( | |
| "mlirDataFormatters.ValueSummaryProvider" | |
| ), | |
| ) | |
| cat.AddTypeSynthetic( | |
| lldb.SBTypeNameSpecifier(name, lldb.eFormatterMatchExact), | |
| lldb.SBTypeSynthetic.CreateWithClassName( | |
| "mlirDataFormatters.ValueSynthProvider" | |
| ), | |
| ) | |
| add_value_summary_and_synth("mlir::BlockArgument") | |
| add_value_summary_and_synth("mlir::Value") | |
| add_value_summary_and_synth("mlir::OpResult") | |
| add_value_summary_and_synth("mlir::detail::OpResultImpl") |