[Mlir-commits] [mlir] 62fec08 - [mlir] Add LLDB visualizers for MLIR constructs
River Riddle
llvmlistbot at llvm.org
Sun Dec 11 22:45:58 PST 2022
Author: River Riddle
Date: 2022-12-11T22:45:34-08:00
New Revision: 62fec084d67af5b3d55b09271a5b9aab604698f5
URL: https://github.com/llvm/llvm-project/commit/62fec084d67af5b3d55b09271a5b9aab604698f5
DIFF: https://github.com/llvm/llvm-project/commit/62fec084d67af5b3d55b09271a5b9aab604698f5.diff
LOG: [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
Added:
mlir/utils/lldb-scripts/mlirDataFormatters.py
Modified:
mlir/include/mlir/IR/Operation.h
mlir/include/mlir/IR/Value.h
mlir/include/mlir/Support/InterfaceSupport.h
Removed:
################################################################################
diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h
index 6c8f2470e66f2..61f1c028ba32a 100644
--- a/mlir/include/mlir/IR/Operation.h
+++ b/mlir/include/mlir/IR/Operation.h
@@ -798,6 +798,19 @@ class alignas(8) Operation final
/// 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;
diff --git a/mlir/include/mlir/IR/Value.h b/mlir/include/mlir/IR/Value.h
index 3557d2d0e0d5d..bc51d59c745a2 100644
--- a/mlir/include/mlir/IR/Value.h
+++ b/mlir/include/mlir/IR/Value.h
@@ -71,6 +71,14 @@ class alignas(8) ValueImpl : public IRObjectWithUseList<OpOperand> {
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;
};
diff --git a/mlir/include/mlir/Support/InterfaceSupport.h b/mlir/include/mlir/Support/InterfaceSupport.h
index a612ce8a8e87b..d8f63e08ea1dc 100644
--- a/mlir/include/mlir/Support/InterfaceSupport.h
+++ b/mlir/include/mlir/Support/InterfaceSupport.h
@@ -92,22 +92,26 @@ class Interface : public BaseType {
/// 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 @@ class Interface : public BaseType {
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;
};
//===----------------------------------------------------------------------===//
diff --git a/mlir/utils/lldb-scripts/mlirDataFormatters.py b/mlir/utils/lldb-scripts/mlirDataFormatters.py
new file mode 100644
index 0000000000000..f04516b181112
--- /dev/null
+++ b/mlir/utils/lldb-scripts/mlirDataFormatters.py
@@ -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
diff erent 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")
More information about the Mlir-commits
mailing list