[mlir] Add LLDB visualizers for MLIR constructs

This commit adds a significant amount of visualizers attempting
to cover the majority of our visualization needs. It covers:

* Operations/OperationName/Ops/OpInterfaces
* Attributes/Types/Attr|TypeInterfaces/NamedAttribute
* Blocks/Regions
* Various range types (e.g. ValueRange/TypeRange)
* Values/BlockArguments/OpResults

This does require an NFC change to interfaces to rename
the concept field to avoid clash with the base class. It
also requires exposing a few method to the debugger
to help resolve information that is non-trivial to reconstruct.
These methods are re-exported using a debug_Blah naming
scheme to avoid messing with hot methods.

Note that this makes use of the new callback feature in lldb-16
(currently trunk) that allows for providing visualizers based on
a dynamic callback, instead of just the typename. It requires
a very new lldb, but allows for providing good default visualization
for all attributes/operations/types out of the box.

Differential Revision: https://reviews.llvm.org/D139602
This commit is contained in:
River Riddle 2022-12-07 20:09:28 -08:00
parent 18546ff8dd
commit 62fec084d6
4 changed files with 864 additions and 9 deletions

View File

@ -798,6 +798,19 @@ private:
/// model.
Block *getParent() const { return block; }
/// Expose a few methods explicitly for the debugger to call for
/// visualization.
#ifndef NDEBUG
LLVM_DUMP_METHOD operand_range debug_getOperands() { return getOperands(); }
LLVM_DUMP_METHOD result_range debug_getResults() { return getResults(); }
LLVM_DUMP_METHOD SuccessorRange debug_getSuccessors() {
return getSuccessors();
}
LLVM_DUMP_METHOD MutableArrayRef<Region> debug_getRegions() {
return getRegions();
}
#endif
/// The operation block that contains this operation.
Block *block = nullptr;

View File

@ -71,6 +71,14 @@ public:
protected:
ValueImpl(Type type, Kind kind) : typeAndKind(type, kind) {}
/// Expose a few methods explicitly for the debugger to call for
/// visualization.
#ifndef NDEBUG
LLVM_DUMP_METHOD Type debug_getType() const { return getType(); }
LLVM_DUMP_METHOD Kind debug_getKind() const { return getKind(); }
#endif
/// The type of this result and the kind.
llvm::PointerIntPair<Type, 3, Kind> typeAndKind;
};

View File

@ -92,22 +92,26 @@ public:
/// Construct an interface from an instance of the value type.
Interface(ValueT t = ValueT())
: BaseType(t), impl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
assert((!t || impl) && "expected value to provide interface instance");
: BaseType(t),
conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
assert((!t || conceptImpl) &&
"expected value to provide interface instance");
}
Interface(std::nullptr_t) : BaseType(ValueT()), impl(nullptr) {}
Interface(std::nullptr_t) : BaseType(ValueT()), conceptImpl(nullptr) {}
/// Construct an interface instance from a type that implements this
/// interface's trait.
template <typename T,
std::enable_if_t<std::is_base_of<Trait<T>, T>::value> * = nullptr>
Interface(T t)
: BaseType(t), impl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
assert((!t || impl) && "expected value to provide interface instance");
: BaseType(t),
conceptImpl(t ? ConcreteType::getInterfaceFor(t) : nullptr) {
assert((!t || conceptImpl) &&
"expected value to provide interface instance");
}
/// Constructor for DenseMapInfo's empty key and tombstone key.
Interface(ValueT t, std::nullptr_t) : BaseType(t), impl(nullptr) {}
Interface(ValueT t, std::nullptr_t) : BaseType(t), conceptImpl(nullptr) {}
/// Support 'classof' by checking if the given object defines the concrete
/// interface.
@ -118,12 +122,12 @@ public:
protected:
/// Get the raw concept in the correct derived concept type.
const Concept *getImpl() const { return impl; }
Concept *getImpl() { return impl; }
const Concept *getImpl() const { return conceptImpl; }
Concept *getImpl() { return conceptImpl; }
private:
/// A pointer to the impl concept object.
Concept *impl;
Concept *conceptImpl;
};
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,830 @@
"""
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::BFloat16Type": '"bf16"',
"mlir::Float16Type": '"f16"',
"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:
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")