[Mlir-commits] [mlir] abd9534 - Reimplementing target description concept using DLTI attribute (#92138)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Jun 19 11:40:12 PDT 2024


Author: Niranjan Hasabnis
Date: 2024-06-19T19:40:08+01:00
New Revision: abd95342f0b94e140b36ac954b8f8c29b1393861

URL: https://github.com/llvm/llvm-project/commit/abd95342f0b94e140b36ac954b8f8c29b1393861
DIFF: https://github.com/llvm/llvm-project/commit/abd95342f0b94e140b36ac954b8f8c29b1393861.diff

LOG: Reimplementing target description concept using DLTI attribute (#92138)

and Interfaces. This is a newer implementation of PR
https://github.com/llvm/llvm-project/pull/85141 and
[RFC](https://discourse.llvm.org/t/rfc-target-description-and-cost-model-in-mlir/76990)
by considering reviews and comments on the original PR.

As an example of attributes supported by this commit:

```
module attributes {
   dlti.target_system_spec =
     #dlti.target_device_spec<
       #dlti.dl_entry<"dlti.device_id", 0: ui32>,
       #dlti.dl_entry<"dlti.device_type", "CPU">,
       #dlti.dl_entry<"dlti.L1_cache_size_in_bytes", 8192 : ui32>>,
     #dlti.target_device_spec <
       #dlti.dl_entry<"dlti.device_id", 1: ui32>,
       #dlti.dl_entry<"dlti.device_type", "GPU">,
       #dlti.dl_entry<"dlti.max_vector_op_width", 64 : ui32>>,
    #dlti.target_device_spec <
       #dlti.dl_entry<"dlti.device_id", 2: ui32>,
       #dlti.dl_entry<"dlti.device_type", "XPU">>>
}
```

Added: 
    mlir/include/mlir/Dialect/DLTI/DLTIAttrs.td
    mlir/test/Dialect/DLTI/valid.mlir

Modified: 
    mlir/include/mlir/Dialect/DLTI/CMakeLists.txt
    mlir/include/mlir/Dialect/DLTI/DLTI.h
    mlir/include/mlir/Dialect/DLTI/DLTIBase.td
    mlir/include/mlir/Dialect/DLTI/Traits.h
    mlir/include/mlir/IR/BuiltinOps.td
    mlir/include/mlir/Interfaces/DataLayoutInterfaces.h
    mlir/include/mlir/Interfaces/DataLayoutInterfaces.td
    mlir/lib/Dialect/DLTI/DLTI.cpp
    mlir/lib/Dialect/DLTI/Traits.cpp
    mlir/lib/IR/BuiltinDialect.cpp
    mlir/lib/Interfaces/DataLayoutInterfaces.cpp
    mlir/test/Dialect/DLTI/invalid.mlir
    mlir/test/Dialect/DLTI/roundtrip.mlir
    mlir/unittests/Interfaces/DataLayoutInterfacesTest.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/DLTI/CMakeLists.txt b/mlir/include/mlir/Dialect/DLTI/CMakeLists.txt
index e0b18b12cda34..44a814f1c8e82 100644
--- a/mlir/include/mlir/Dialect/DLTI/CMakeLists.txt
+++ b/mlir/include/mlir/Dialect/DLTI/CMakeLists.txt
@@ -1,2 +1,8 @@
 add_mlir_dialect(DLTI dlti)
 add_mlir_doc(DLTI DLTIDialect Dialects/ -gen-dialect-doc)
+
+set(LLVM_TARGET_DEFINITIONS DLTIAttrs.td)
+mlir_tablegen(DLTIAttrs.h.inc -gen-attrdef-decls -attrdefs-dialect=dlti)
+mlir_tablegen(DLTIAttrs.cpp.inc -gen-attrdef-defs -attrdefs-dialect=dlti)
+add_public_tablegen_target(MLIRDLTIAttrsIncGen)
+add_dependencies(mlir-headers MLIRDLTIAttrsIncGen)

diff  --git a/mlir/include/mlir/Dialect/DLTI/DLTI.h b/mlir/include/mlir/Dialect/DLTI/DLTI.h
index 5ac7c11e6ffee..f50a654f3885d 100644
--- a/mlir/include/mlir/Dialect/DLTI/DLTI.h
+++ b/mlir/include/mlir/Dialect/DLTI/DLTI.h
@@ -18,114 +18,13 @@
 #include "mlir/Interfaces/DataLayoutInterfaces.h"
 
 namespace mlir {
-namespace impl {
-class DataLayoutEntryStorage;
-class DataLayoutSpecStorage;
-} // namespace impl
-
-//===----------------------------------------------------------------------===//
-// DataLayoutEntryAttr
-//===----------------------------------------------------------------------===//
-
-/// A data layout entry attribute is a key-value pair where the key is a type or
-/// an identifier and the value is another attribute. These entries form a data
-/// layout specification.
-class DataLayoutEntryAttr
-    : public Attribute::AttrBase<DataLayoutEntryAttr, Attribute,
-                                 impl::DataLayoutEntryStorage,
-                                 DataLayoutEntryInterface::Trait> {
-public:
-  using Base::Base;
-
-  /// The keyword used for this attribute in custom syntax.
-  constexpr const static llvm::StringLiteral kAttrKeyword = "dl_entry";
-
-  /// Returns the entry with the given key and value.
-  static DataLayoutEntryAttr get(StringAttr key, Attribute value);
-  static DataLayoutEntryAttr get(Type key, Attribute value);
-
-  /// Returns the key of this entry.
-  DataLayoutEntryKey getKey() const;
-
-  /// Returns the value of this entry.
-  Attribute getValue() const;
-
-  /// Parses an instance of this attribute.
-  static DataLayoutEntryAttr parse(AsmParser &parser);
-
-  /// Prints this attribute.
-  void print(AsmPrinter &os) const;
-
-  static constexpr StringLiteral name = "builtin.data_layout_entry";
-};
-
-//===----------------------------------------------------------------------===//
-// DataLayoutSpecAttr
-//===----------------------------------------------------------------------===//
-
-/// A data layout specification is a list of entries that specify (partial) data
-/// layout information. It is expected to be attached to operations that serve
-/// as scopes for data layout requests.
-class DataLayoutSpecAttr
-    : public Attribute::AttrBase<DataLayoutSpecAttr, Attribute,
-                                 impl::DataLayoutSpecStorage,
-                                 DataLayoutSpecInterface::Trait> {
-public:
-  using Base::Base;
-
-  /// The keyword used for this attribute in custom syntax.
-  constexpr const static StringLiteral kAttrKeyword = "dl_spec";
-
-  /// Returns the specification containing the given list of keys.
-  static DataLayoutSpecAttr get(MLIRContext *ctx,
-                                ArrayRef<DataLayoutEntryInterface> entries);
-
-  /// Returns the specification containing the given list of keys. If the list
-  /// contains duplicate keys or is otherwise invalid, reports errors using the
-  /// given callback and returns null.
-  static DataLayoutSpecAttr
-  getChecked(function_ref<InFlightDiagnostic()> emitError, MLIRContext *context,
-             ArrayRef<DataLayoutEntryInterface> entries);
-
-  /// Checks that the given list of entries does not contain duplicate keys.
-  static LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
-                              ArrayRef<DataLayoutEntryInterface> entries);
-
-  /// Combines this specification with `specs`, enclosing specifications listed
-  /// from outermost to innermost. This overwrites the older entries with the
-  /// same key as the newer entries if the entries are compatible. Returns null
-  /// if the specifications are not compatible.
-  DataLayoutSpecAttr combineWith(ArrayRef<DataLayoutSpecInterface> specs) const;
-
-  /// Returns the list of entries.
-  DataLayoutEntryListRef getEntries() const;
-
-  /// Returns the endiannes identifier.
-  StringAttr getEndiannessIdentifier(MLIRContext *context) const;
-
-  /// Returns the alloca memory space identifier.
-  StringAttr getAllocaMemorySpaceIdentifier(MLIRContext *context) const;
-
-  /// Returns the program memory space identifier.
-  StringAttr getProgramMemorySpaceIdentifier(MLIRContext *context) const;
-
-  /// Returns the global memory space identifier.
-  StringAttr getGlobalMemorySpaceIdentifier(MLIRContext *context) const;
-
-  /// Returns the stack alignment identifier.
-  StringAttr getStackAlignmentIdentifier(MLIRContext *context) const;
-
-  /// Parses an instance of this attribute.
-  static DataLayoutSpecAttr parse(AsmParser &parser);
-
-  /// Prints this attribute.
-  void print(AsmPrinter &os) const;
-
-  static constexpr StringLiteral name = "builtin.data_layout_spec";
-};
-
+namespace detail {
+class DataLayoutEntryAttrStorage;
+} // namespace detail
 } // namespace mlir
 
+#define GET_ATTRDEF_CLASSES
+#include "mlir/Dialect/DLTI/DLTIAttrs.h.inc"
 #include "mlir/Dialect/DLTI/DLTIDialect.h.inc"
 
 #endif // MLIR_DIALECT_DLTI_DLTI_H

diff  --git a/mlir/include/mlir/Dialect/DLTI/DLTIAttrs.td b/mlir/include/mlir/Dialect/DLTI/DLTIAttrs.td
new file mode 100644
index 0000000000000..b3849e9b5be62
--- /dev/null
+++ b/mlir/include/mlir/Dialect/DLTI/DLTIAttrs.td
@@ -0,0 +1,188 @@
+//===- DLTIAttrs.td - DLTI dialect attributes definition --*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_DLTI_DLTIATTRS_TD
+#define MLIR_DIALECT_DLTI_DLTIATTRS_TD
+
+include "mlir/Dialect/DLTI/DLTI.td"
+include "mlir/IR/AttrTypeBase.td"
+
+class DLTIAttr<string name, list<Trait> traits = [],
+                string baseCppClass = "::mlir::Attribute">
+    : AttrDef<DLTI_Dialect, name, traits, baseCppClass> { }
+
+//===----------------------------------------------------------------------===//
+// DataLayoutEntryAttr
+//===----------------------------------------------------------------------===//
+
+def DataLayoutEntryTrait
+    : NativeAttrTrait<"DataLayoutEntryInterface::Trait"> {
+  let cppNamespace = "::mlir";
+}
+
+def DLTI_DataLayoutEntryAttr :
+    DLTIAttr<"DataLayoutEntry", [DataLayoutEntryTrait]> {
+  let summary = [{
+    An attribute to represent an entry of a data layout specification.
+  }];
+  let description = [{
+    A data layout entry attribute is a key-value pair where the key is a type or
+    an identifier and the value is another attribute. These entries form a data
+    layout specification.
+  }];
+  let parameters = (ins
+    "DataLayoutEntryKey":$key, "Attribute":$value
+  );
+  // TODO: We do not generate storage class because llvm::PointerUnion
+  // does not work with hash_key method.
+  let genStorageClass = 0;
+  let mnemonic = "dl_entry";
+  let genVerifyDecl = 0;
+  let hasCustomAssemblyFormat = 1;
+  let extraClassDeclaration = [{
+    /// Returns the entry with the given key and value.
+    static DataLayoutEntryAttr get(StringAttr key, Attribute value);
+    static DataLayoutEntryAttr get(MLIRContext *context, Type key, Attribute value);
+    static DataLayoutEntryAttr get(Type key, Attribute value);
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// DataLayoutSpecAttr
+//===----------------------------------------------------------------------===//
+def DataLayoutSpecTrait
+    : NativeAttrTrait<"DataLayoutSpecInterface::Trait"> {
+  let cppNamespace = "::mlir";
+}
+
+def DLTI_DataLayoutSpecAttr :
+    DLTIAttr<"DataLayoutSpec", [DataLayoutSpecTrait]> {
+  let summary = [{
+    An attribute to represent a data layout specification.
+  }];
+  let description = [{
+    A data layout specification is a list of entries that specify (partial) data
+    layout information. It is expected to be attached to operations that serve
+    as scopes for data layout requests.
+  }];
+  let parameters = (ins
+    ArrayRefParameter<"DataLayoutEntryInterface", "">:$entries
+  );
+  let mnemonic = "dl_spec";
+  let genVerifyDecl = 1;
+  let hasCustomAssemblyFormat = 1;
+  let extraClassDeclaration = [{
+    /// Combines this specification with `specs`, enclosing specifications listed
+    /// from outermost to innermost. This overwrites the older entries with the
+    /// same key as the newer entries if the entries are compatible. Returns null
+    /// if the specifications are not compatible.
+    DataLayoutSpecAttr combineWith(ArrayRef<DataLayoutSpecInterface> specs) const;
+  
+    /// Returns the endiannes identifier.
+    StringAttr getEndiannessIdentifier(MLIRContext *context) const;
+
+    /// Returns the alloca memory space identifier.
+    StringAttr getAllocaMemorySpaceIdentifier(MLIRContext *context) const;
+
+    /// Returns the program memory space identifier.
+    StringAttr getProgramMemorySpaceIdentifier(MLIRContext *context) const;
+
+    /// Returns the global memory space identifier.
+    StringAttr getGlobalMemorySpaceIdentifier(MLIRContext *context) const;
+
+    /// Returns the stack alignment identifier.
+    StringAttr getStackAlignmentIdentifier(MLIRContext *context) const;
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// TargetSystemSpecAttr
+//===----------------------------------------------------------------------===//
+
+def TargetSystemSpecTrait
+    : NativeAttrTrait<"TargetSystemSpecInterface::Trait"> {
+  let cppNamespace = "::mlir";
+}
+
+def DLTI_TargetSystemSpecAttr :
+    DLTIAttr<"TargetSystemSpec", [TargetSystemSpecTrait]> {
+  let summary = [{
+    An attribute to represent target system specification.
+  }];
+  let description = [{
+    A system specification describes the overall system containing
+    multiple devices, with each device having a unique ID (string)
+    and its corresponding TargetDeviceSpec object.
+
+    Example:
+          dlti.target_system_spec =
+           #dlti.target_system_spec<
+            "CPU": #dlti.target_device_spec<
+                    #dlti.dl_entry<"dlti.L1_cache_size_in_bytes", 4096: ui32>>,
+            "GPU": #dlti.target_device_spec<
+                    #dlti.dl_entry<"dlti.max_vector_op_width", 64 : ui32>>,
+            "XPU": #dlti.target_device_spec<
+                    #dlti.dl_entry<"dlti.max_vector_op_width", 4096 : ui32>>>
+  }];
+  let parameters = (ins
+    ArrayRefParameter<"DeviceIDTargetDeviceSpecPair", "">:$entries
+  );
+  let mnemonic = "target_system_spec";
+  let genVerifyDecl = 1;
+  let assemblyFormat = "`<` $entries `>`";
+  let extraClassDeclaration = [{
+    /// Return the device specification that matches the given device ID
+    std::optional<TargetDeviceSpecInterface>
+    getDeviceSpecForDeviceID(
+      TargetSystemSpecInterface::DeviceID deviceID);
+  }];
+  let extraClassDefinition = [{
+    std::optional<TargetDeviceSpecInterface>
+    $cppClass::getDeviceSpecForDeviceID(
+        TargetSystemSpecInterface::DeviceID deviceID) {
+      for (const auto& entry : getEntries()) {
+        if (entry.first == deviceID)
+          return entry.second;
+      }
+      return std::nullopt;
+    }
+  }];
+}
+
+//===----------------------------------------------------------------------===//
+// TargetDeviceSpecAttr
+//===----------------------------------------------------------------------===//
+
+def TargetDeviceSpecTrait
+    : NativeAttrTrait<"TargetDeviceSpecInterface::Trait"> {
+  let cppNamespace = "::mlir";
+}
+
+def DLTI_TargetDeviceSpecAttr :
+    DLTIAttr<"TargetDeviceSpec", [TargetDeviceSpecTrait]> {
+  let summary = [{
+    An attribute to represent target device specification.
+  }];
+  let description = [{
+    Each device specification describes a single device and its
+    hardware properties. Each device specification can contain any number
+    of optional hardware properties (e.g., max_vector_op_width below).
+
+    Example:
+      #dlti.target_device_spec<
+        #dlti.dl_entry<"dlti.max_vector_op_width", 64 : ui32>>
+  }];
+  let parameters = (ins
+    ArrayRefParameter<"DataLayoutEntryInterface", "">:$entries
+  );
+  let mnemonic = "target_device_spec";
+  let genVerifyDecl = 1;
+  let assemblyFormat = "`<` $entries `>`";
+}
+
+#endif  // MLIR_DIALECT_DLTI_DLTIATTRS_TD

diff  --git a/mlir/include/mlir/Dialect/DLTI/DLTIBase.td b/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
index 3572a99fad874..e26fbdb146645 100644
--- a/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
+++ b/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
@@ -27,6 +27,13 @@ def DLTI_Dialect : Dialect {
     constexpr const static ::llvm::StringLiteral
     kDataLayoutAttrName = "dlti.dl_spec";
 
+    // Top level attribute name for target system description
+    constexpr const static ::llvm::StringLiteral
+    kTargetSystemDescAttrName = "dlti.target_system_spec";
+
+    constexpr const static ::llvm::StringLiteral
+    kTargetDeviceDescAttrName = "dlti.target_device_spec";
+
     // Constants used in entries.
     constexpr const static ::llvm::StringLiteral
     kDataLayoutEndiannessKey = "dlti.endianness";
@@ -53,24 +60,6 @@ def DLTI_Dialect : Dialect {
   let useDefaultAttributePrinterParser = 1;
 }
 
-def DLTI_DataLayoutEntryAttr : DialectAttr<
-    DLTI_Dialect,
-    CPred<"::llvm::isa<::mlir::DataLayoutEntryAttr>($_self)">,
-    "Target data layout entry"> {
-  let storageType = "::mlir::DataLayoutEntryAttr";
-  let returnType = "::mlir::DataLayoutEntryAttr";
-  let convertFromStorage = "$_self";
-}
-
-def DLTI_DataLayoutSpecAttr : DialectAttr<
-    DLTI_Dialect,
-    CPred<"::llvm::isa<::mlir::DataLayoutSpecAttr>($_self)">,
-    "Target data layout specification"> {
-  let storageType = "::mlir::DataLayoutSpecAttr";
-  let returnType = "::mlir::DataLayoutSpecAttr";
-  let convertFromStorage = "$_self";
-}
-
 def HasDefaultDLTIDataLayout : NativeOpTrait<"HasDefaultDLTIDataLayout"> {
   let cppNamespace = "::mlir";
 }

diff  --git a/mlir/include/mlir/Dialect/DLTI/Traits.h b/mlir/include/mlir/Dialect/DLTI/Traits.h
index 5d86195305a95..edfbdffbd1ba1 100644
--- a/mlir/include/mlir/Dialect/DLTI/Traits.h
+++ b/mlir/include/mlir/Dialect/DLTI/Traits.h
@@ -18,6 +18,7 @@ class DataLayoutSpecAttr;
 namespace impl {
 LogicalResult verifyHasDefaultDLTIDataLayoutTrait(Operation *op);
 DataLayoutSpecInterface getDataLayoutSpec(Operation *op);
+TargetSystemSpecInterface getTargetSystemSpec(Operation *op);
 } // namespace impl
 
 /// Trait to be used by operations willing to use the implementation of the
@@ -37,6 +38,12 @@ class HasDefaultDLTIDataLayout
   DataLayoutSpecInterface getDataLayoutSpec() {
     return impl::getDataLayoutSpec(this->getOperation());
   }
+
+  /// Returns the target system description specification as provided by DLTI
+  /// dialect
+  TargetSystemSpecInterface getTargetSystemSpec() {
+    return impl::getTargetSystemSpec(this->getOperation());
+  }
 };
 } // namespace mlir
 

diff  --git a/mlir/include/mlir/IR/BuiltinOps.td b/mlir/include/mlir/IR/BuiltinOps.td
index eda24615c71ea..56edd7519cd67 100644
--- a/mlir/include/mlir/IR/BuiltinOps.td
+++ b/mlir/include/mlir/IR/BuiltinOps.td
@@ -78,6 +78,7 @@ def ModuleOp : Builtin_Op<"module", [
     //===------------------------------------------------------------------===//
 
     DataLayoutSpecInterface getDataLayoutSpec();
+    TargetSystemSpecInterface getTargetSystemSpec();
 
     //===------------------------------------------------------------------===//
     // OpAsmOpInterface Methods

diff  --git a/mlir/include/mlir/Interfaces/DataLayoutInterfaces.h b/mlir/include/mlir/Interfaces/DataLayoutInterfaces.h
index 76bf33e89a716..4cbad38df42ba 100644
--- a/mlir/include/mlir/Interfaces/DataLayoutInterfaces.h
+++ b/mlir/include/mlir/Interfaces/DataLayoutInterfaces.h
@@ -23,11 +23,18 @@
 namespace mlir {
 class DataLayout;
 class DataLayoutEntryInterface;
+class TargetDeviceSpecInterface;
+class TargetSystemSpecInterface;
 using DataLayoutEntryKey = llvm::PointerUnion<Type, StringAttr>;
 // Using explicit SmallVector size because we cannot infer the size from the
 // forward declaration, and we need the typedef in the actual declaration.
 using DataLayoutEntryList = llvm::SmallVector<DataLayoutEntryInterface, 4>;
 using DataLayoutEntryListRef = llvm::ArrayRef<DataLayoutEntryInterface>;
+using TargetDeviceSpecListRef = llvm::ArrayRef<TargetDeviceSpecInterface>;
+using DeviceIDTargetDeviceSpecPair =
+    std::pair<StringAttr, TargetDeviceSpecInterface>;
+using DeviceIDTargetDeviceSpecPairListRef =
+    llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>;
 class DataLayoutOpInterface;
 class DataLayoutSpecInterface;
 class ModuleOp;
@@ -84,6 +91,11 @@ Attribute getDefaultGlobalMemorySpace(DataLayoutEntryInterface entry);
 /// DataLayoutInterface if specified, otherwise returns the default.
 uint64_t getDefaultStackAlignment(DataLayoutEntryInterface entry);
 
+/// Returns the value of the property from the specified DataLayoutEntry. If the
+/// property is missing from the entry, returns std::nullopt.
+std::optional<int64_t>
+getDevicePropertyValueAsInt(DataLayoutEntryInterface entry);
+
 /// Given a list of data layout entries, returns a new list containing the
 /// entries with keys having the given type ID, i.e. belonging to the same type
 /// class.
@@ -95,6 +107,11 @@ DataLayoutEntryList filterEntriesForType(DataLayoutEntryListRef entries,
 DataLayoutEntryInterface
 filterEntryForIdentifier(DataLayoutEntryListRef entries, StringAttr id);
 
+/// Given a list of target device entries, returns the entry that has the given
+/// identifier as key, if such an entry exists in the list.
+TargetDeviceSpecInterface
+filterEntryForIdentifier(TargetDeviceSpecListRef entries, StringAttr id);
+
 /// Verifies that the operation implementing the data layout interface, or a
 /// module operation, is valid. This calls the verifier of the spec attribute
 /// and checks if the layout is compatible with specs attached to the enclosing
@@ -106,6 +123,12 @@ LogicalResult verifyDataLayoutOp(Operation *op);
 /// and dialect interfaces for type and identifier keys respectively.
 LogicalResult verifyDataLayoutSpec(DataLayoutSpecInterface spec, Location loc);
 
+/// Verifies that a target system desc spec is valid. This dispatches to
+/// individual entry verifiers, and then to the verifiers implemented by the
+/// relevant dialect interfaces for identifier keys.
+LogicalResult verifyTargetSystemSpec(TargetSystemSpecInterface spec,
+                                     Location loc);
+
 /// Divides the known min value of the numerator by the denominator and rounds
 /// the result up to the next integer. Preserves the scalable flag.
 llvm::TypeSize divideCeil(llvm::TypeSize numerator, uint64_t denominator);
@@ -137,6 +160,13 @@ class DataLayoutDialectInterface
     return success();
   }
 
+  /// Checks whether the given data layout entry is valid and reports any errors
+  /// at the provided location. Derived classes should override this.
+  virtual LogicalResult verifyEntry(TargetDeviceSpecInterface entry,
+                                    Location loc) const {
+    return success();
+  }
+
   /// Default implementation of entry combination that combines identical
   /// entries and returns null otherwise.
   static DataLayoutEntryInterface
@@ -214,10 +244,19 @@ class DataLayout {
   /// unspecified.
   uint64_t getStackAlignment() const;
 
+  /// Returns the value of the specified property if the property is defined for
+  /// the given device ID, otherwise returns std::nullopt.
+  std::optional<int64_t>
+  getDevicePropertyValueAsInt(TargetSystemSpecInterface::DeviceID,
+                              StringAttr propertyName) const;
+
 private:
   /// Combined layout spec at the given scope.
   const DataLayoutSpecInterface originalLayout;
 
+  /// Combined target system desc spec at the given scope.
+  const TargetSystemSpecInterface originalTargetSystemDesc;
+
 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
   /// List of enclosing layout specs.
   SmallVector<DataLayoutSpecInterface, 2> layoutStack;

diff  --git a/mlir/include/mlir/Interfaces/DataLayoutInterfaces.td b/mlir/include/mlir/Interfaces/DataLayoutInterfaces.td
index 9edc885b9c5a9..8ff0e3f5f863b 100644
--- a/mlir/include/mlir/Interfaces/DataLayoutInterfaces.td
+++ b/mlir/include/mlir/Interfaces/DataLayoutInterfaces.td
@@ -194,6 +194,102 @@ def DataLayoutSpecInterface : AttrInterface<"DataLayoutSpecInterface"> {
   }];
 }
 
+def TargetDeviceSpecInterface : AttrInterface<"TargetDeviceSpecInterface"> {
+  let cppNamespace = "::mlir";
+
+  let description = [{
+    Attribute interface describing a target device description specification.
+
+    A target device description specification is a list of device properties (key)
+    and their values for a specific device. The device is identified using "device_id"
+    (as a key and ui32 value) and "device_type" key which must have a string value.
+    Both "device_id" and "device_type" are mandatory keys. As an example, L1 cache
+    size could be a device property, and its value would be a device specific size.
+
+    A target device description specification is attached to a module as a module level
+    attribute.
+  }];
+
+  let methods = [
+    InterfaceMethod<
+      /*description=*/"Returns the list of layout entries.",
+      /*retTy=*/"::mlir::DataLayoutEntryListRef",
+      /*methodName=*/"getEntries",
+      /*args=*/(ins)
+    >,
+    InterfaceMethod<
+      /*description=*/"Returns the entry related to the given identifier, if "
+                      "present.",
+      /*retTy=*/"::mlir::DataLayoutEntryInterface",
+      /*methodName=*/"getSpecForIdentifier",
+      /*args=*/(ins "::mlir::StringAttr":$identifier),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return ::mlir::detail::filterEntryForIdentifier($_attr.getEntries(),
+                                                        identifier);
+      }]
+    >,
+    InterfaceMethod<
+      /*description=*/"Checks that the entry is well-formed, reports errors "
+                      "at the provided location.",
+      /*retTy=*/"::mlir::LogicalResult",
+      /*methodName=*/"verifyEntry",
+      /*args=*/(ins "::mlir::Location":$loc),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{ return ::mlir::success(); }]
+    >
+  ];
+}
+
+def TargetSystemSpecInterface : AttrInterface<"TargetSystemSpecInterface"> {
+  let cppNamespace = "::mlir";
+
+  let description = [{
+    Attribute interface describing a target system description specification.
+
+    A target system description specification is a list of target device
+    specifications, with one device specification for a device in the system. As
+    such, a target system description specification allows specifying a heterogenous
+    system, with devices of 
diff erent types (e.g., CPU, GPU, etc.)
+
+    The only requirement on a valid target system description specification is that
+    the "device_id" in every target device description specification needs to be
+    unique. This is because, ultimately, this "device_id" is used by the user to
+    query a value of a device property.
+  }];
+
+  let methods = [
+    InterfaceMethod<
+      /*description=*/"Returns the list of layout entries.",
+      /*retTy=*/"llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>",
+      /*methodName=*/"getEntries",
+      /*args=*/(ins)
+    >,
+    InterfaceMethod<
+      /*description=*/"Returns the device description spec for given device "
+                      "ID",
+      /*retTy=*/"std::optional<::mlir::TargetDeviceSpecInterface>",
+      /*methodName=*/"getDeviceSpecForDeviceID",
+      /*args=*/(ins "StringAttr":$deviceID)
+    >,
+    InterfaceMethod<
+      /*description=*/"Verifies the validity of the specification and "
+                      "reports any errors at the given location.",
+      /*retTy=*/"::mlir::LogicalResult",
+      /*methodName=*/"verifySpec",
+      /*args=*/(ins "::mlir::Location":$loc),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return ::mlir::detail::verifyTargetSystemSpec($_attr, loc);
+      }]
+    >
+  ];
+
+  let extraClassDeclaration = [{
+    using DeviceID = StringAttr;
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // Operation interface
 //===----------------------------------------------------------------------===//
@@ -227,6 +323,13 @@ def DataLayoutOpInterface : OpInterface<"DataLayoutOpInterface"> {
       /*methodName=*/"getDataLayoutSpec",
       /*args=*/(ins)
     >,
+    InterfaceMethod<
+      /*description=*/"Returns the target system desc specification for this "
+                      "op, or null if it does not exist.",
+      /*retTy=*/"::mlir::TargetSystemSpecInterface",
+      /*methodName=*/"getTargetSystemSpec",
+      /*args=*/(ins)
+    >,
     StaticInterfaceMethod<
       /*description=*/"Returns the size of the given type computed using the "
                       "relevant entries. The data layout object can be used "
@@ -362,6 +465,17 @@ def DataLayoutOpInterface : OpInterface<"DataLayoutOpInterface"> {
         return ::mlir::detail::getDefaultStackAlignment(entry);
       }]
     >,
+    StaticInterfaceMethod<
+      /*description=*/"Returns the value of the property, if the property is "
+                      "defined. Otherwise, it returns std::nullopt.",
+      /*retTy=*/"std::optional<int64_t>",
+      /*methodName=*/"getDevicePropertyValueAsInt",
+      /*args=*/(ins "::mlir::DataLayoutEntryInterface":$entry),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return ::mlir::detail::getDevicePropertyValueAsInt(entry);
+      }]
+    >
   ];
 
   let verify = [{ return ::mlir::detail::verifyDataLayoutOp($_op); }];

diff  --git a/mlir/lib/Dialect/DLTI/DLTI.cpp b/mlir/lib/Dialect/DLTI/DLTI.cpp
index 98a8865ef4da3..592fced0b2abd 100644
--- a/mlir/lib/Dialect/DLTI/DLTI.cpp
+++ b/mlir/lib/Dialect/DLTI/DLTI.cpp
@@ -10,33 +10,40 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/BuiltinDialect.h"
 #include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/DialectImplementation.h"
 #include "llvm/ADT/TypeSwitch.h"
 
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/MathExtras.h"
+
 using namespace mlir;
 
 #include "mlir/Dialect/DLTI/DLTIDialect.cpp.inc"
 
+#define GET_ATTRDEF_CLASSES
+#include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
+
+#define DEBUG_TYPE "dlti"
+
 //===----------------------------------------------------------------------===//
 // DataLayoutEntryAttr
 //===----------------------------------------------------------------------===//
-//
-constexpr const StringLiteral mlir::DataLayoutEntryAttr::kAttrKeyword;
-
 namespace mlir {
-namespace impl {
-class DataLayoutEntryStorage : public AttributeStorage {
+namespace detail {
+class DataLayoutEntryAttrStorage : public AttributeStorage {
 public:
   using KeyTy = std::pair<DataLayoutEntryKey, Attribute>;
 
-  DataLayoutEntryStorage(DataLayoutEntryKey entryKey, Attribute value)
+  DataLayoutEntryAttrStorage(DataLayoutEntryKey entryKey, Attribute value)
       : entryKey(entryKey), value(value) {}
 
-  static DataLayoutEntryStorage *construct(AttributeStorageAllocator &allocator,
-                                           const KeyTy &key) {
-    return new (allocator.allocate<DataLayoutEntryStorage>())
-        DataLayoutEntryStorage(key.first, key.second);
+  static DataLayoutEntryAttrStorage *
+  construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
+    return new (allocator.allocate<DataLayoutEntryAttrStorage>())
+        DataLayoutEntryAttrStorage(key.first, key.second);
   }
 
   bool operator==(const KeyTy &other) const {
@@ -46,7 +53,7 @@ class DataLayoutEntryStorage : public AttributeStorage {
   DataLayoutEntryKey entryKey;
   Attribute value;
 };
-} // namespace impl
+} // namespace detail
 } // namespace mlir
 
 DataLayoutEntryAttr DataLayoutEntryAttr::get(StringAttr key, Attribute value) {
@@ -65,7 +72,7 @@ Attribute DataLayoutEntryAttr::getValue() const { return getImpl()->value; }
 
 /// Parses an attribute with syntax:
 ///   attr ::= `#target.` `dl_entry` `<` (type | quoted-string) `,` attr `>`
-DataLayoutEntryAttr DataLayoutEntryAttr::parse(AsmParser &parser) {
+Attribute DataLayoutEntryAttr::parse(AsmParser &parser, Type ty) {
   if (failed(parser.parseLess()))
     return {};
 
@@ -93,7 +100,7 @@ DataLayoutEntryAttr DataLayoutEntryAttr::parse(AsmParser &parser) {
 }
 
 void DataLayoutEntryAttr::print(AsmPrinter &os) const {
-  os << DataLayoutEntryAttr::kAttrKeyword << "<";
+  os << "<";
   if (auto type = llvm::dyn_cast_if_present<Type>(getKey()))
     os << type;
   else
@@ -104,51 +111,6 @@ void DataLayoutEntryAttr::print(AsmPrinter &os) const {
 //===----------------------------------------------------------------------===//
 // DataLayoutSpecAttr
 //===----------------------------------------------------------------------===//
-//
-constexpr const StringLiteral mlir::DataLayoutSpecAttr::kAttrKeyword;
-constexpr const StringLiteral
-    mlir::DLTIDialect::kDataLayoutAllocaMemorySpaceKey;
-constexpr const StringLiteral
-    mlir::DLTIDialect::kDataLayoutProgramMemorySpaceKey;
-constexpr const StringLiteral
-    mlir::DLTIDialect::kDataLayoutGlobalMemorySpaceKey;
-
-constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutStackAlignmentKey;
-
-namespace mlir {
-namespace impl {
-class DataLayoutSpecStorage : public AttributeStorage {
-public:
-  using KeyTy = ArrayRef<DataLayoutEntryInterface>;
-
-  DataLayoutSpecStorage(ArrayRef<DataLayoutEntryInterface> entries)
-      : entries(entries) {}
-
-  bool operator==(const KeyTy &key) const { return key == entries; }
-
-  static DataLayoutSpecStorage *construct(AttributeStorageAllocator &allocator,
-                                          const KeyTy &key) {
-    return new (allocator.allocate<DataLayoutSpecStorage>())
-        DataLayoutSpecStorage(allocator.copyInto(key));
-  }
-
-  ArrayRef<DataLayoutEntryInterface> entries;
-};
-} // namespace impl
-} // namespace mlir
-
-DataLayoutSpecAttr
-DataLayoutSpecAttr::get(MLIRContext *ctx,
-                        ArrayRef<DataLayoutEntryInterface> entries) {
-  return Base::get(ctx, entries);
-}
-
-DataLayoutSpecAttr
-DataLayoutSpecAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
-                               MLIRContext *context,
-                               ArrayRef<DataLayoutEntryInterface> entries) {
-  return Base::getChecked(emitError, context, entries);
-}
 
 LogicalResult
 DataLayoutSpecAttr::verify(function_ref<InFlightDiagnostic()> emitError,
@@ -277,10 +239,6 @@ DataLayoutSpecAttr::combineWith(ArrayRef<DataLayoutSpecInterface> specs) const {
   return DataLayoutSpecAttr::get(getContext(), entries);
 }
 
-DataLayoutEntryListRef DataLayoutSpecAttr::getEntries() const {
-  return getImpl()->entries;
-}
-
 StringAttr
 DataLayoutSpecAttr::getEndiannessIdentifier(MLIRContext *context) const {
   return Builder(context).getStringAttr(DLTIDialect::kDataLayoutEndiannessKey);
@@ -303,6 +261,7 @@ DataLayoutSpecAttr::getGlobalMemorySpaceIdentifier(MLIRContext *context) const {
   return Builder(context).getStringAttr(
       DLTIDialect::kDataLayoutGlobalMemorySpaceKey);
 }
+
 StringAttr
 DataLayoutSpecAttr::getStackAlignmentIdentifier(MLIRContext *context) const {
   return Builder(context).getStringAttr(
@@ -313,7 +272,7 @@ DataLayoutSpecAttr::getStackAlignmentIdentifier(MLIRContext *context) const {
 ///   attr ::= `#target.` `dl_spec` `<` attr-list? `>`
 ///   attr-list ::= attr
 ///               | attr `,` attr-list
-DataLayoutSpecAttr DataLayoutSpecAttr::parse(AsmParser &parser) {
+Attribute DataLayoutSpecAttr::parse(AsmParser &parser, Type type) {
   if (failed(parser.parseLess()))
     return {};
 
@@ -332,11 +291,103 @@ DataLayoutSpecAttr DataLayoutSpecAttr::parse(AsmParser &parser) {
 }
 
 void DataLayoutSpecAttr::print(AsmPrinter &os) const {
-  os << DataLayoutSpecAttr::kAttrKeyword << "<";
+  os << "<";
   llvm::interleaveComma(getEntries(), os);
   os << ">";
 }
 
+//===----------------------------------------------------------------------===//
+// TargetDeviceSpecAttr
+//===----------------------------------------------------------------------===//
+
+namespace mlir {
+/// A FieldParser for key-value pairs of DeviceID-target device spec pairs that
+/// make up a target system spec.
+template <>
+struct FieldParser<DeviceIDTargetDeviceSpecPair> {
+  static FailureOr<DeviceIDTargetDeviceSpecPair> parse(AsmParser &parser) {
+    std::string deviceID;
+
+    if (failed(parser.parseString(&deviceID))) {
+      parser.emitError(parser.getCurrentLocation())
+          << "DeviceID is missing, or is not of string type";
+      return failure();
+    }
+
+    if (failed(parser.parseColon())) {
+      parser.emitError(parser.getCurrentLocation()) << "Missing colon";
+      return failure();
+    }
+
+    auto target_device_spec =
+        FieldParser<TargetDeviceSpecInterface>::parse(parser);
+    if (failed(target_device_spec)) {
+      parser.emitError(parser.getCurrentLocation())
+          << "Error in parsing target device spec";
+      return failure();
+    }
+
+    return std::make_pair(parser.getBuilder().getStringAttr(deviceID),
+                          *target_device_spec);
+  }
+};
+
+inline AsmPrinter &operator<<(AsmPrinter &printer,
+                              DeviceIDTargetDeviceSpecPair param) {
+  return printer << param.first << " : " << param.second;
+}
+
+} // namespace mlir
+
+LogicalResult
+TargetDeviceSpecAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+                             ArrayRef<DataLayoutEntryInterface> entries) {
+  // Entries in a target device spec can only have StringAttr as key. It does
+  // not support type as a key. Hence not reusing
+  // DataLayoutEntryInterface::verify.
+  DenseSet<StringAttr> ids;
+  for (DataLayoutEntryInterface entry : entries) {
+    if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
+      return emitError()
+             << "dlti.target_device_spec does not allow type as a key: "
+             << type;
+    } else {
+      auto id = entry.getKey().get<StringAttr>();
+      if (!ids.insert(id).second)
+        return emitError() << "repeated layout entry key: " << id.getValue();
+    }
+  }
+
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// TargetSystemSpecAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+TargetSystemSpecAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+                             ArrayRef<DeviceIDTargetDeviceSpecPair> entries) {
+  DenseSet<TargetSystemSpecInterface::DeviceID> device_ids;
+
+  for (const auto &entry : entries) {
+    TargetDeviceSpecInterface target_device_spec = entry.second;
+
+    // First verify that a target device spec is valid.
+    if (failed(TargetDeviceSpecAttr::verify(emitError,
+                                            target_device_spec.getEntries())))
+      return failure();
+
+    // Check that device IDs are unique across all entries.
+    TargetSystemSpecInterface::DeviceID device_id = entry.first;
+    if (!device_ids.insert(device_id).second) {
+      return emitError() << "repeated Device ID in dlti.target_system_spec: "
+                         << device_id;
+    }
+  }
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // DLTIDialect
 //===----------------------------------------------------------------------===//
@@ -376,33 +427,13 @@ class TargetDataLayoutInterface : public DataLayoutDialectInterface {
 } // namespace
 
 void DLTIDialect::initialize() {
-  addAttributes<DataLayoutEntryAttr, DataLayoutSpecAttr>();
+  addAttributes<
+#define GET_ATTRDEF_LIST
+#include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
+      >();
   addInterfaces<TargetDataLayoutInterface>();
 }
 
-Attribute DLTIDialect::parseAttribute(DialectAsmParser &parser,
-                                      Type type) const {
-  StringRef attrKind;
-  if (parser.parseKeyword(&attrKind))
-    return {};
-
-  if (attrKind == DataLayoutEntryAttr::kAttrKeyword)
-    return DataLayoutEntryAttr::parse(parser);
-  if (attrKind == DataLayoutSpecAttr::kAttrKeyword)
-    return DataLayoutSpecAttr::parse(parser);
-
-  parser.emitError(parser.getNameLoc(), "unknown attrribute type: ")
-      << attrKind;
-  return {};
-}
-
-void DLTIDialect::printAttribute(Attribute attr, DialectAsmPrinter &os) const {
-  llvm::TypeSwitch<Attribute>(attr)
-      .Case<DataLayoutEntryAttr, DataLayoutSpecAttr>(
-          [&](auto a) { a.print(os); })
-      .Default([](Attribute) { llvm_unreachable("unknown attribute kind"); });
-}
-
 LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op,
                                                     NamedAttribute attr) {
   if (attr.getName() == DLTIDialect::kDataLayoutAttrName) {
@@ -413,6 +444,13 @@ LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op,
     if (isa<ModuleOp>(op))
       return detail::verifyDataLayoutOp(op);
     return success();
+  } else if (attr.getName() == DLTIDialect::kTargetSystemDescAttrName) {
+    if (!llvm::isa<TargetSystemSpecAttr>(attr.getValue())) {
+      return op->emitError()
+             << "'" << DLTIDialect::kTargetSystemDescAttrName
+             << "' is expected to be a #dlti.target_system_spec attribute";
+    }
+    return success();
   }
 
   return op->emitError() << "attribute '" << attr.getName().getValue()

diff  --git a/mlir/lib/Dialect/DLTI/Traits.cpp b/mlir/lib/Dialect/DLTI/Traits.cpp
index 85acbee46defd..34f2dd5896083 100644
--- a/mlir/lib/Dialect/DLTI/Traits.cpp
+++ b/mlir/lib/Dialect/DLTI/Traits.cpp
@@ -27,3 +27,8 @@ DataLayoutSpecInterface mlir::impl::getDataLayoutSpec(Operation *op) {
   return op->getAttrOfType<DataLayoutSpecAttr>(
       DLTIDialect::kDataLayoutAttrName);
 }
+
+TargetSystemSpecInterface mlir::impl::getTargetSystemSpec(Operation *op) {
+  return op->getAttrOfType<TargetSystemSpecAttr>(
+      DLTIDialect::kTargetSystemDescAttrName);
+}

diff  --git a/mlir/lib/IR/BuiltinDialect.cpp b/mlir/lib/IR/BuiltinDialect.cpp
index dcb1119fe5207..99796c5f1c371 100644
--- a/mlir/lib/IR/BuiltinDialect.cpp
+++ b/mlir/lib/IR/BuiltinDialect.cpp
@@ -155,6 +155,16 @@ DataLayoutSpecInterface ModuleOp::getDataLayoutSpec() {
   return {};
 }
 
+TargetSystemSpecInterface ModuleOp::getTargetSystemSpec() {
+  // Take the first and only (if present) attribute that implements the
+  // interface. This needs a linear search, but is called only once per data
+  // layout object construction that is used for repeated queries.
+  for (NamedAttribute attr : getOperation()->getAttrs())
+    if (auto spec = llvm::dyn_cast<TargetSystemSpecInterface>(attr.getValue()))
+      return spec;
+  return {};
+}
+
 LogicalResult ModuleOp::verify() {
   // Check that none of the attributes are non-dialect attributes, except for
   // the symbol related attributes.

diff  --git a/mlir/lib/Interfaces/DataLayoutInterfaces.cpp b/mlir/lib/Interfaces/DataLayoutInterfaces.cpp
index 15cfb3dbaf745..df86bd757b628 100644
--- a/mlir/lib/Interfaces/DataLayoutInterfaces.cpp
+++ b/mlir/lib/Interfaces/DataLayoutInterfaces.cpp
@@ -293,6 +293,15 @@ mlir::detail::getDefaultStackAlignment(DataLayoutEntryInterface entry) {
   return value.getValue().getZExtValue();
 }
 
+std::optional<int64_t>
+mlir::detail::getDevicePropertyValueAsInt(DataLayoutEntryInterface entry) {
+  if (entry == DataLayoutEntryInterface())
+    return std::nullopt;
+
+  auto value = cast<IntegerAttr>(entry.getValue());
+  return value.getValue().getZExtValue();
+}
+
 DataLayoutEntryList
 mlir::detail::filterEntriesForType(DataLayoutEntryListRef entries,
                                    TypeID typeID) {
@@ -324,6 +333,16 @@ static DataLayoutSpecInterface getSpec(Operation *operation) {
       });
 }
 
+static TargetSystemSpecInterface getTargetSystemSpec(Operation *operation) {
+  if (operation) {
+    ModuleOp moduleOp = dyn_cast<ModuleOp>(operation);
+    if (!moduleOp)
+      moduleOp = operation->getParentOfType<ModuleOp>();
+    return moduleOp.getTargetSystemSpec();
+  }
+  return TargetSystemSpecInterface();
+}
+
 /// Populates `opsWithLayout` with the list of proper ancestors of `leaf` that
 /// are either modules or implement the `DataLayoutOpInterface`.
 static void
@@ -433,7 +452,8 @@ void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op) {
 mlir::DataLayout::DataLayout() : DataLayout(ModuleOp()) {}
 
 mlir::DataLayout::DataLayout(DataLayoutOpInterface op)
-    : originalLayout(getCombinedDataLayout(op)), scope(op),
+    : originalLayout(getCombinedDataLayout(op)),
+      originalTargetSystemDesc(getTargetSystemSpec(op)), scope(op),
       allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
       globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
@@ -443,7 +463,8 @@ mlir::DataLayout::DataLayout(DataLayoutOpInterface op)
 }
 
 mlir::DataLayout::DataLayout(ModuleOp op)
-    : originalLayout(getCombinedDataLayout(op)), scope(op),
+    : originalLayout(getCombinedDataLayout(op)),
+      originalTargetSystemDesc(getTargetSystemSpec(op)), scope(op),
       allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
       globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
@@ -640,6 +661,26 @@ uint64_t mlir::DataLayout::getStackAlignment() const {
   return *stackAlignment;
 }
 
+std::optional<int64_t> mlir::DataLayout::getDevicePropertyValueAsInt(
+    TargetSystemSpecInterface::DeviceID deviceID,
+    StringAttr propertyName) const {
+  checkValid();
+  DataLayoutEntryInterface entry;
+  if (originalTargetSystemDesc) {
+    if (std::optional<TargetDeviceSpecInterface> device =
+            originalTargetSystemDesc.getDeviceSpecForDeviceID(deviceID))
+      entry = device->getSpecForIdentifier(propertyName);
+  }
+  // Currently I am not caching the results because we do not return
+  // default values of these properties. Instead if the property is
+  // missing, we return std::nullopt so that the users can resort to
+  // the default value however they want.
+  if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
+    return iface.getDevicePropertyValueAsInt(entry);
+  else
+    return detail::getDevicePropertyValueAsInt(entry);
+}
+
 //===----------------------------------------------------------------------===//
 // DataLayoutSpecInterface
 //===----------------------------------------------------------------------===//
@@ -744,6 +785,55 @@ LogicalResult mlir::detail::verifyDataLayoutSpec(DataLayoutSpecInterface spec,
   return success();
 }
 
+LogicalResult
+mlir::detail::verifyTargetSystemSpec(TargetSystemSpecInterface spec,
+                                     Location loc) {
+  DenseMap<StringAttr, DataLayoutEntryInterface> deviceDescKeys;
+  DenseSet<TargetSystemSpecInterface::DeviceID> deviceIDs;
+  for (const auto &entry : spec.getEntries()) {
+    TargetDeviceSpecInterface targetDeviceSpec = entry.second;
+    // First, verify individual target device desc specs.
+    if (failed(targetDeviceSpec.verifyEntry(loc)))
+      return failure();
+
+    // Check that device IDs are unique across all entries.
+    TargetSystemSpecInterface::DeviceID deviceID = entry.first;
+    if (!deviceIDs.insert(deviceID).second) {
+      return failure();
+    }
+
+    // collect all the keys used by all the target device specs.
+    for (DataLayoutEntryInterface entry : targetDeviceSpec.getEntries()) {
+      if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
+        // targetDeviceSpec does not support Type as a key.
+        return failure();
+      } else {
+        deviceDescKeys[entry.getKey().get<StringAttr>()] = entry;
+      }
+    }
+  }
+
+  for (const auto &[keyName, keyVal] : deviceDescKeys) {
+    Dialect *dialect = keyName.getReferencedDialect();
+
+    // Ignore attributes that belong to an unknown dialect, the dialect may
+    // actually implement the relevant interface but we don't know about that.
+    if (!dialect)
+      return failure();
+
+    const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
+    if (!iface) {
+      return emitError(loc)
+             << "the '" << dialect->getNamespace()
+             << "' dialect does not support identifier data layout entries";
+    }
+    if (failed(iface->verifyEntry(keyVal, loc)))
+      return failure();
+  }
+
+  return success();
+}
+
 #include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
 #include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
 #include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc"

diff  --git a/mlir/test/Dialect/DLTI/invalid.mlir b/mlir/test/Dialect/DLTI/invalid.mlir
index 465ec72106f70..d732fb462e68e 100644
--- a/mlir/test/Dialect/DLTI/invalid.mlir
+++ b/mlir/test/Dialect/DLTI/invalid.mlir
@@ -36,7 +36,7 @@
 
 // -----
 
-// expected-error at below {{unknown attrribute type: unknown}}
+// expected-error at below {{unknown attribute `unknown` in dialect `dlti`}}
 "test.unknown_op"() { test.unknown_attr = #dlti.unknown } : () -> ()
 
 // -----
@@ -90,3 +90,68 @@ module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"unknown.unknown
   // expected-note at above {{enclosing op with data layout}}
   "test.op_with_data_layout"() { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"unknown.unknown", 32>>} : () -> ()
 }
+
+// -----
+
+// expected-error at below {{'dlti.target_system_spec' is expected to be a #dlti.target_system_spec attribute}}
+"test.unknown_op"() { dlti.target_system_spec = 42 } : () -> ()
+
+// -----
+
+// expected-error at below {{expected string}}
+// expected-error at below {{DeviceID is missing, or is not of string type}}
+// expected-error at below {{failed to parse DLTI_TargetSystemSpecAttr parameter 'entries' which is to be a `::llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>`}}
+"test.unknown_op"() { dlti.target_system_spec = #dlti.target_system_spec<[]> } : () -> ()
+
+// -----
+
+module attributes {
+  // Device ID is missing
+  //
+  // expected-error at +4 {{expected string}}
+  // expected-error at +3 {{DeviceID is missing, or is not of string type}}
+  // expected-error at +2 {{failed to parse DLTI_TargetSystemSpecAttr parameter 'entries' which is to be a `::llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>`}}
+  dlti.target_system_spec = #dlti.target_system_spec<
+    : #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096 : i32>>
+  >} {}
+
+// -----
+
+module attributes {
+  // Device ID is wrong type
+  //
+  // expected-error at +4 {{expected string}}
+  // expected-error at +3 {{DeviceID is missing, or is not of string type}}
+  // expected-error at +2 {{failed to parse DLTI_TargetSystemSpecAttr parameter 'entries' which is to be a `::llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>`}}
+  dlti.target_system_spec = #dlti.target_system_spec<
+    0: #dlti.target_device_spec<
+        #dlti.dl_entry<"L1_cache_size_in_bytes", 4096: i32>> 
+  >} {}
+
+// -----
+
+module attributes {
+  // Repeated Device ID
+  //
+  // expected-error at below {{repeated Device ID in dlti.target_system_spec: "CPU"}}
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+            #dlti.dl_entry<"L1_cache_size_in_bytes", 4096>>,
+    "CPU": #dlti.target_device_spec<
+            #dlti.dl_entry<"L1_cache_size_in_bytes", 8192>>
+  >} {}
+
+// -----
+
+module attributes {
+  // Repeated DLTI entry
+  //
+  // expected-error at +4 {{repeated layout entry key: L1_cache_size_in_bytes}}
+  // expected-error at +6 {{Error in parsing target device spec}}
+  // expected-error at +5 {{failed to parse DLTI_TargetSystemSpecAttr parameter 'entries' which is to be a `::llvm::ArrayRef<DeviceIDTargetDeviceSpecPair>`}}
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+            #dlti.dl_entry<"L1_cache_size_in_bytes", 4096>,
+            #dlti.dl_entry<"L1_cache_size_in_bytes", 8192>>
+  >} {}

diff  --git a/mlir/test/Dialect/DLTI/roundtrip.mlir b/mlir/test/Dialect/DLTI/roundtrip.mlir
index 613dc354d895d..7b8255ad71d4d 100644
--- a/mlir/test/Dialect/DLTI/roundtrip.mlir
+++ b/mlir/test/Dialect/DLTI/roundtrip.mlir
@@ -53,3 +53,21 @@
   }) { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"unknown.unknown", 32>> } : () -> ()
   "test.maybe_terminator_op"() : () -> ()
 }) { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<"unknown.unknown", 32>> } : () -> ()
+
+// A valid target system description
+// CHECK: module attributes {
+// CHECK: dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK:  "CPU" : #dlti.target_device_spec<
+// CHECK:   #dlti.dl_entry<"dlti.L1_cache_size_in_bytes", 4096 : ui32>>,
+// CHECK: "GPU" : #dlti.target_device_spec<
+// CHECK:   #dlti.dl_entry<"dlti.max_vector_op_width", 128 : ui32>>
+// CHECK: >} {
+// CHECK: }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"dlti.L1_cache_size_in_bytes", 4096: ui32>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"dlti.max_vector_op_width", 128: ui32>>
+  >} {}
+ 

diff  --git a/mlir/test/Dialect/DLTI/valid.mlir b/mlir/test/Dialect/DLTI/valid.mlir
new file mode 100644
index 0000000000000..98e9d0b8de491
--- /dev/null
+++ b/mlir/test/Dialect/DLTI/valid.mlir
@@ -0,0 +1,109 @@
+// RUN: mlir-opt -split-input-file %s | FileCheck %s
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096 : i32>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 128 : i32>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096: i32>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 128: i32>>
+  >} {}
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096 : i32>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"L1_cache_size_in_bytes", 8192 : i32>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096: i32>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 8192: i32>>
+  >} {}
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096 : i64>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"L1_cache_size_in_bytes", 8192 : i64>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 4096: i64>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"L1_cache_size_in_bytes", 8192: i64>>
+  >} {}
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 64 : i32>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 128 : i32>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 64: i32>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 128: i32>>
+  >} {}
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 64 : i64>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 128 : i64>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 64: i64>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 128: i64>>
+  >} {}
+
+// -----
+
+// CHECK:      module attributes {
+// CHECK-SAME:  dlti.target_system_spec = #dlti.target_system_spec<
+// CHECK-SAME:    "CPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 64 : i64>>,
+// CHECK-SAME:    "GPU" : #dlti.target_device_spec<
+// CHECK-SAME:      #dlti.dl_entry<"max_vector_op_width", 128 : i64>>
+// CHECK-SAME:  >} {
+// CHECK:        }
+module attributes {
+  dlti.target_system_spec = #dlti.target_system_spec<
+    "CPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 64>>,
+    "GPU": #dlti.target_device_spec<
+      #dlti.dl_entry<"max_vector_op_width", 128>>
+  >} {}

diff  --git a/mlir/unittests/Interfaces/DataLayoutInterfacesTest.cpp b/mlir/unittests/Interfaces/DataLayoutInterfacesTest.cpp
index 5f484294268ab..bcbfa88c5d815 100644
--- a/mlir/unittests/Interfaces/DataLayoutInterfacesTest.cpp
+++ b/mlir/unittests/Interfaces/DataLayoutInterfacesTest.cpp
@@ -32,6 +32,9 @@ constexpr static llvm::StringLiteral kGlobalKeyName =
 constexpr static llvm::StringLiteral kStackAlignmentKeyName =
     "dltest.stack_alignment";
 
+constexpr static llvm::StringLiteral kTargetSystemDescAttrName =
+    "dl_target_sys_desc_test.target_system_spec";
+
 /// Trivial array storage for the custom data layout spec attribute, just a list
 /// of entries.
 class DataLayoutSpecStorage : public AttributeStorage {
@@ -91,6 +94,52 @@ struct CustomDataLayoutSpec
   }
 };
 
+class TargetSystemSpecStorage : public AttributeStorage {
+public:
+  using KeyTy = ArrayRef<DeviceIDTargetDeviceSpecPair>;
+
+  TargetSystemSpecStorage(ArrayRef<DeviceIDTargetDeviceSpecPair> entries)
+      : entries(entries) {}
+
+  bool operator==(const KeyTy &key) const { return key == entries; }
+
+  static TargetSystemSpecStorage *
+  construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
+    return new (allocator.allocate<TargetSystemSpecStorage>())
+        TargetSystemSpecStorage(allocator.copyInto(key));
+  }
+
+  ArrayRef<DeviceIDTargetDeviceSpecPair> entries;
+};
+
+struct CustomTargetSystemSpec
+    : public Attribute::AttrBase<CustomTargetSystemSpec, Attribute,
+                                 TargetSystemSpecStorage,
+                                 TargetSystemSpecInterface::Trait> {
+  MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CustomDataLayoutSpec)
+
+  using Base::Base;
+
+  static constexpr StringLiteral name = "test.custom_target_system_spec";
+
+  static CustomTargetSystemSpec
+  get(MLIRContext *ctx, ArrayRef<DeviceIDTargetDeviceSpecPair> entries) {
+    return Base::get(ctx, entries);
+  }
+  DeviceIDTargetDeviceSpecPairListRef getEntries() const {
+    return getImpl()->entries;
+  }
+  LogicalResult verifySpec(Location loc) { return success(); }
+  std::optional<TargetDeviceSpecInterface>
+  getDeviceSpecForDeviceID(TargetSystemSpecInterface::DeviceID deviceID) {
+    for (const auto &entry : getEntries()) {
+      if (entry.first == deviceID)
+        return entry.second;
+    }
+    return std::nullopt;
+  }
+};
+
 /// A type subject to data layout that exits the program if it is queried more
 /// than once. Handy to check if the cache works.
 struct SingleQueryType
@@ -197,6 +246,11 @@ struct OpWithLayout : public Op<OpWithLayout, DataLayoutOpInterface::Trait> {
     return getOperation()->getAttrOfType<DataLayoutSpecInterface>(kAttrName);
   }
 
+  TargetSystemSpecInterface getTargetSystemSpec() {
+    return getOperation()->getAttrOfType<TargetSystemSpecInterface>(
+        kTargetSystemDescAttrName);
+  }
+
   static llvm::TypeSize getTypeSizeInBits(Type type,
                                           const DataLayout &dataLayout,
                                           DataLayoutEntryListRef params) {
@@ -244,6 +298,11 @@ struct OpWith7BitByte
     return getOperation()->getAttrOfType<DataLayoutSpecInterface>(kAttrName);
   }
 
+  TargetSystemSpecInterface getTargetSystemSpec() {
+    return getOperation()->getAttrOfType<TargetSystemSpecInterface>(
+        kTargetSystemDescAttrName);
+  }
+
   // Bytes are assumed to be 7-bit here.
   static llvm::TypeSize getTypeSize(Type type, const DataLayout &dataLayout,
                                     DataLayoutEntryListRef params) {
@@ -308,6 +367,74 @@ struct DLTestDialect : Dialect {
   }
 };
 
+/// A dialect to test DLTI's target system spec and related attributes
+struct DLTargetSystemDescTestDialect : public Dialect {
+  MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(DLTargetSystemDescTestDialect)
+
+  explicit DLTargetSystemDescTestDialect(MLIRContext *ctx)
+      : Dialect(getDialectNamespace(), ctx,
+                TypeID::get<DLTargetSystemDescTestDialect>()) {
+    ctx->getOrLoadDialect<DLTIDialect>();
+    addAttributes<CustomTargetSystemSpec>();
+  }
+  static StringRef getDialectNamespace() { return "dl_target_sys_desc_test"; }
+
+  void printAttribute(Attribute attr,
+                      DialectAsmPrinter &printer) const override {
+    printer << "target_system_spec<";
+    llvm::interleaveComma(
+        cast<CustomTargetSystemSpec>(attr).getEntries(), printer,
+        [&](const auto &it) { printer << it.first << ":" << it.second; });
+    printer << ">";
+  }
+
+  Attribute parseAttribute(DialectAsmParser &parser, Type type) const override {
+    bool ok = succeeded(parser.parseKeyword("target_system_spec")) &&
+              succeeded(parser.parseLess());
+    (void)ok;
+    assert(ok);
+    if (succeeded(parser.parseOptionalGreater()))
+      return CustomTargetSystemSpec::get(parser.getContext(), {});
+
+    auto parseDeviceIDTargetDeviceSpecPair =
+        [&](AsmParser &parser) -> FailureOr<DeviceIDTargetDeviceSpecPair> {
+      std::string deviceID;
+      if (failed(parser.parseString(&deviceID))) {
+        parser.emitError(parser.getCurrentLocation())
+            << "DeviceID is missing, or is not of string type";
+        return failure();
+      }
+      if (failed(parser.parseColon())) {
+        parser.emitError(parser.getCurrentLocation()) << "Missing colon";
+        return failure();
+      }
+
+      TargetDeviceSpecInterface targetDeviceSpec;
+      if (failed(parser.parseAttribute(targetDeviceSpec))) {
+        parser.emitError(parser.getCurrentLocation())
+            << "Error in parsing target device spec";
+        return failure();
+      }
+      return std::make_pair(parser.getBuilder().getStringAttr(deviceID),
+                            targetDeviceSpec);
+    };
+
+    SmallVector<DeviceIDTargetDeviceSpecPair> entries;
+    ok = succeeded(parser.parseCommaSeparatedList([&]() {
+      auto deviceIDAndTargetDeviceSpecPair =
+          parseDeviceIDTargetDeviceSpecPair(parser);
+      ok = succeeded(deviceIDAndTargetDeviceSpecPair);
+      assert(ok);
+      entries.push_back(*deviceIDAndTargetDeviceSpecPair);
+      return success();
+    }));
+    assert(ok);
+    ok = succeeded(parser.parseGreater());
+    assert(ok);
+    return CustomTargetSystemSpec::get(parser.getContext(), entries);
+  }
+};
+
 } // namespace
 
 TEST(DataLayout, FallbackDefault) {
@@ -367,6 +494,15 @@ TEST(DataLayout, NullSpec) {
   EXPECT_EQ(layout.getProgramMemorySpace(), Attribute());
   EXPECT_EQ(layout.getGlobalMemorySpace(), Attribute());
   EXPECT_EQ(layout.getStackAlignment(), 0u);
+
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU" /* device ID*/),
+                Builder(&ctx).getStringAttr("L1_cache_size_in_bytes")),
+            std::nullopt);
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU" /* device ID*/),
+                Builder(&ctx).getStringAttr("max_vector_width")),
+            std::nullopt);
 }
 
 TEST(DataLayout, EmptySpec) {
@@ -398,6 +534,15 @@ TEST(DataLayout, EmptySpec) {
   EXPECT_EQ(layout.getProgramMemorySpace(), Attribute());
   EXPECT_EQ(layout.getGlobalMemorySpace(), Attribute());
   EXPECT_EQ(layout.getStackAlignment(), 0u);
+
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU" /* device ID*/),
+                Builder(&ctx).getStringAttr("L1_cache_size_in_bytes")),
+            std::nullopt);
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU" /* device ID*/),
+                Builder(&ctx).getStringAttr("max_vector_width")),
+            std::nullopt);
 }
 
 TEST(DataLayout, SpecWithEntries) {
@@ -449,6 +594,32 @@ TEST(DataLayout, SpecWithEntries) {
   EXPECT_EQ(layout.getStackAlignment(), 128u);
 }
 
+TEST(DataLayout, SpecWithTargetSystemDescEntries) {
+  const char *ir = R"MLIR(
+  module attributes { dl_target_sys_desc_test.target_system_spec =
+    #dl_target_sys_desc_test.target_system_spec<
+      "CPU": #dlti.target_device_spec<
+              #dlti.dl_entry<"L1_cache_size_in_bytes", 4096 : ui32>,
+              #dlti.dl_entry<"max_vector_op_width", 128 : ui32>>
+    > } {}
+  )MLIR";
+
+  DialectRegistry registry;
+  registry.insert<DLTIDialect, DLTargetSystemDescTestDialect>();
+  MLIRContext ctx(registry);
+
+  OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
+  DataLayout layout(*module);
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU") /* device ID*/,
+                Builder(&ctx).getStringAttr("L1_cache_size_in_bytes")),
+            std::optional<int64_t>(4096));
+  EXPECT_EQ(layout.getDevicePropertyValueAsInt(
+                Builder(&ctx).getStringAttr("CPU") /* device ID*/,
+                Builder(&ctx).getStringAttr("max_vector_op_width")),
+            std::optional<int64_t>(128));
+}
+
 TEST(DataLayout, Caching) {
   const char *ir = R"MLIR(
 "dltest.op_with_layout"() { dltest.layout = #dltest.spec<> } : () -> ()


        


More information about the Mlir-commits mailing list