""" 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::Float8E5M2Type": '"f8E5M2"', "mlir::Float8E4M3FNType": '"f8E4M3FN"', "mlir::Float8E5M2FNUZType": '"f8E5M2FNUZ"', "mlir::Float8E4M3FNUZType": '"f8E4M3FNUZ"', "mlir::Float8E4M3B11FNUZType": '"f8E4M3B11FNUZ"', "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")