diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h index ecab12de55d61..83077aef8d08d 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRType.h +++ b/flang/include/flang/Optimizer/Dialect/FIRType.h @@ -13,6 +13,7 @@ #ifndef FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H #define FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H +#include "mlir/Dialect/LLVMIR/LLVMTypes.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/Interfaces/DataLayoutInterfaces.h" diff --git a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt index cfad07e57021f..f1385cdff62be 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt @@ -7,12 +7,26 @@ mlir_tablegen(LLVMOpsDialect.h.inc -gen-dialect-decls) mlir_tablegen(LLVMOpsDialect.cpp.inc -gen-dialect-defs) mlir_tablegen(LLVMOpsEnums.h.inc -gen-enum-decls) mlir_tablegen(LLVMOpsEnums.cpp.inc -gen-enum-defs) -mlir_tablegen(LLVMOpsAttrDefs.h.inc -gen-attrdef-decls - -attrdefs-dialect=llvm) +#For LLVMOpsAttrDefs.h.inc, see below. mlir_tablegen(LLVMOpsAttrDefs.cpp.inc -gen-attrdef-defs -attrdefs-dialect=llvm) add_public_tablegen_target(MLIRLLVMOpsIncGen) +# NB: Separate out LLVMOpsAttrDefs.h.inc generation as generating it +# through LLVMOps.td ends up defining LLVMTargetFeaturesAttr even +# though LLVMTargetFeaturesAttrDefs.* is responsible for that. +set(LLVM_TARGET_DEFINITIONS LLVMAttrAndEnumDefs.td) +mlir_tablegen(LLVMOpsAttrDefs.h.inc -gen-attrdef-decls -attrdefs-dialect=llvm) +add_public_tablegen_target(MLIRLLVMAttrsIncGen) + +# NB: LLVMTargetFeaturesAttr is split out into its own file +# to break a recursive dependency: LLVMInterfaces depends +# on it, and other LLVMAttrs depending on LLVMInterfaces. +set(LLVM_TARGET_DEFINITIONS LLVMTargetFeaturesAttrDefs.td) +mlir_tablegen(LLVMTargetFeaturesAttrDefs.h.inc -gen-attrdef-decls) +mlir_tablegen(LLVMTargetFeaturesAttrDefs.cpp.inc -gen-attrdef-defs) +add_public_tablegen_target(MLIRLLVMTargetFeaturesAttrsIncGen) + set(LLVM_TARGET_DEFINITIONS LLVMTypes.td) mlir_tablegen(LLVMTypes.h.inc -gen-typedef-decls -typedefs-dialect=llvm) mlir_tablegen(LLVMTypes.cpp.inc -gen-typedef-defs -typedefs-dialect=llvm) diff --git a/mlir/lib/Target/LLVMIR/DataLayoutImporter.h b/mlir/include/mlir/Dialect/LLVMIR/DataLayoutImporter.h similarity index 82% rename from mlir/lib/Target/LLVMIR/DataLayoutImporter.h rename to mlir/include/mlir/Dialect/LLVMIR/DataLayoutImporter.h index 88ceaf1a74e62..0f036f1c43492 100644 --- a/mlir/lib/Target/LLVMIR/DataLayoutImporter.h +++ b/mlir/include/mlir/Dialect/LLVMIR/DataLayoutImporter.h @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// -#ifndef MLIR_LIB_TARGET_LLVMIR_DATALAYOUTIMPORTER_H_ -#define MLIR_LIB_TARGET_LLVMIR_DATALAYOUTIMPORTER_H_ +#ifndef MLIR_LLVMIR_DATALAYOUTIMPORTER_H_ +#define MLIR_LLVMIR_DATALAYOUTIMPORTER_H_ #include "mlir/Dialect/LLVMIR/LLVMTypes.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/Interfaces/DataLayoutInterfaces.h" #include "llvm/ADT/MapVector.h" +#include "llvm/IR/DataLayout.h" namespace llvm { class StringRef; @@ -38,6 +39,8 @@ namespace detail { /// null if the bit width is not supported. FloatType getFloatType(MLIRContext *context, unsigned width); +} // namespace detail + /// Helper class that translates an LLVM data layout to an MLIR data layout /// specification. Only integer, float, pointer, alloca memory space, stack /// alignment, and endianness entries are translated. The class also returns all @@ -49,7 +52,21 @@ class DataLayoutImporter { DataLayoutImporter(MLIRContext *context, const llvm::DataLayout &llvmDataLayout) : context(context) { - translateDataLayout(llvmDataLayout); + // Transform the data layout to its string representation and append the + // default data layout string specified in the language reference + // (https://llvm.org/docs/LangRef.html#data-layout). The translation then + // parses the string and ignores the default value if a specific kind occurs + // in both strings. Additionally, the following default values exist: + // - non-default address space pointer specifications default to the default + // address space pointer specification + // - the alloca address space defaults to the default address space. + layoutStr = llvmDataLayout.getStringRepresentation(); + translateDataLayoutFromStr(); + } + + DataLayoutImporter(MLIRContext *context, StringRef dataLayoutStr) + : layoutStr(dataLayoutStr), context(context) { + translateDataLayoutFromStr(); } /// Returns the MLIR data layout specification translated from the LLVM @@ -66,7 +83,7 @@ class DataLayoutImporter { private: /// Translates the LLVM `dataLayout` to an MLIR data layout specification. - void translateDataLayout(const llvm::DataLayout &llvmDataLayout); + void translateDataLayoutFromStr(); /// Tries to parse the letter only prefix that identifies the specification /// and removes the consumed characters from the beginning of the string. @@ -125,8 +142,7 @@ class DataLayoutImporter { DataLayoutSpecInterface dataLayout; }; -} // namespace detail } // namespace LLVM } // namespace mlir -#endif // MLIR_LIB_TARGET_LLVMIR_DATALAYOUTIMPORTER_H_ +#endif // MLIR_LLVMIR_DATALAYOUTIMPORTER_H_ diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrAndEnumDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrAndEnumDefs.td new file mode 100644 index 0000000000000..e34375076ffd1 --- /dev/null +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrAndEnumDefs.td @@ -0,0 +1,10 @@ +//===-- LLVMAttrDefs.td - Solely LLVM Attribute and Enum definitions ----*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +include "mlir/Dialect/LLVMIR/LLVMAttrDefs.td" +include "mlir/Dialect/LLVMIR/LLVMEnums.td" diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index 790d2e77ea874..e563441d0102b 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -13,13 +13,138 @@ include "mlir/Dialect/LLVMIR/LLVMDialect.td" include "mlir/Dialect/LLVMIR/LLVMInterfaces.td" include "mlir/IR/AttrTypeBase.td" include "mlir/IR/CommonAttrConstraints.td" +include "mlir/Interfaces/DataLayoutInterfaces.td" -// All of the attributes will extend this class. -class LLVM_Attr traits = [], - string baseCppClass = "::mlir::Attribute"> - : AttrDef { - let mnemonic = attrMnemonic; +//===----------------------------------------------------------------------===// +// LLVM_TargetAttr +//===----------------------------------------------------------------------===// + +def LLVM_TargetAttr : LLVM_Attr<"Target", "target", + [LLVM_TargetAttrInterface]> { + let summary = "LLVM target info: triple, chip, features"; + let description = [{ + An attribute to hold LLVM target information, specifying LLVM's target + `triple` string, the target `chip` string (i.e. the `cpu` string), and + target `features` string as an attribute. The latter two are optional. + + Has facilities to obtain the corresponding `llvm::TargetMachine` and + `llvm::DataLayout`, given the relevant LLVM backend is loaded. + + --- + + Responds to DLTI-queries on the keys: + * A query for `"triple"` returns the `StringAttr` for the `triple`. + * A query for `"chip"` returns the `StringAttr` for the `chip`/`cpu`, if provided. + * A query for `"features"` returns the `TargetFeaturesAttr`, if provided. + * Individual features can be queried for on this attribute. + }]; + let parameters = (ins "StringAttr":$triple, + OptionalParameter<"StringAttr">:$chip, + OptionalParameter<"TargetFeaturesAttr">:$features); + + let assemblyFormat = [{`<` `triple` `=` $triple + (`,` `chip` `=` $chip^)? + (`,` qualified($features)^)? `>`}]; + + let extraClassDeclaration = [{ + std::optional targetMachine = std::nullopt; + + FailureOr getTargetMachine(); + + std::optional dataLayout = std::nullopt; + FailureOr getDataLayout(); + FailureOr query(DataLayoutEntryKey key); + }]; +} + +//===----------------------------------------------------------------------===// +// LLVM_DataLayoutAttr +//===----------------------------------------------------------------------===// + +def LLVM_DataLayoutAttr + : LLVM_Attr<"DataLayout", "data_layout", [DataLayoutSpecInterface]> { + let summary = "LLVM data layout string, exposed through DLTI"; + let description = [{ + An attribute to hold a LLVM data layout string. + + The LLVM data layout string is parsed and mapped to the corresponding MLIR + data layout specification. The `#llvm.data_layout` attribute then serves as + a proxy, forwarding all DLTI queries to the underlying MLIR data layout + specification. + }]; + let parameters = (ins "StringAttr":$data_layout_str, + OptionalParameter<"DataLayoutSpecInterface", "{}">:$data_layout_spec); + let builders = [ + AttrBuilder<(ins "llvm::StringRef":$data_layout_str), [{ + auto importer = LLVM::DataLayoutImporter($_ctxt, data_layout_str); + auto dataLayoutSpec = importer.getDataLayout(); + return $_get($_ctxt, mlir::StringAttr::get($_ctxt, data_layout_str), dataLayoutSpec); + }]> + ]; + let assemblyFormat = "`<` $data_layout_str `>`"; + let extraClassDeclaration = [{ + template + DataLayoutEntryList getSpecForType() { + return getDataLayoutSpec().getSpecForType(TypeID::get()); + } + + inline ::mlir::FailureOr<::mlir::Attribute> + queryHelper(::mlir::DataLayoutEntryKey key) const { + return getDataLayoutSpec().queryHelper(key); + } + + void bucketEntriesByType( + ::llvm::MapVector<::mlir::TypeID, ::mlir::DataLayoutEntryList> &types, + ::llvm::MapVector<::mlir::StringAttr, + ::mlir::DataLayoutEntryInterface> &ids) { + getDataLayoutSpec().bucketEntriesByType(types, ids); + }; + + ::mlir::DataLayoutSpecInterface + combineWith(ArrayRef<::mlir::DataLayoutSpecInterface> specs) const { + return getDataLayoutSpec().combineWith(specs); + } + DataLayoutEntryListRef getEntries() const { return getDataLayoutSpec().getEntries(); } + LogicalResult verifySpec(Location loc) { + return getDataLayoutSpec().verifySpec(loc); + } + StringAttr getEndiannessIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getEndiannessIdentifier(context); + } + StringAttr getDefaultMemorySpaceIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getDefaultMemorySpaceIdentifier(context); + } + StringAttr getAllocaMemorySpaceIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getAllocaMemorySpaceIdentifier(context); + } + StringAttr getManglingModeIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getManglingModeIdentifier(context); + } + StringAttr getProgramMemorySpaceIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getProgramMemorySpaceIdentifier(context); + } + StringAttr getGlobalMemorySpaceIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getGlobalMemorySpaceIdentifier(context); + } + StringAttr getStackAlignmentIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getStackAlignmentIdentifier(context); + } + StringAttr getFunctionPointerAlignmentIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getFunctionPointerAlignmentIdentifier(context); + } + StringAttr getLegalIntWidthsIdentifier(MLIRContext *context) const { + return getDataLayoutSpec().getLegalIntWidthsIdentifier(context); + } + ::mlir::DataLayoutEntryList getSpecForType(TypeID type) const { + return getDataLayoutSpec().getSpecForType(type); + } + ::mlir::DataLayoutEntryInterface getSpecForIdentifier(StringAttr identifier) const { + return getDataLayoutSpec().getSpecForIdentifier(identifier); + } + FailureOr query(DataLayoutEntryKey key) const { + return getDataLayoutSpec().query(key); + } + }]; } //===----------------------------------------------------------------------===// @@ -1241,69 +1366,6 @@ def LLVM_VScaleRangeAttr : LLVM_Attr<"VScaleRange", "vscale_range"> { let assemblyFormat = "`<` struct(params) `>`"; } -//===----------------------------------------------------------------------===// -// TargetFeaturesAttr -//===----------------------------------------------------------------------===// - -def LLVM_TargetFeaturesAttr : LLVM_Attr<"TargetFeatures", "target_features"> -{ - let summary = "LLVM target features attribute"; - - let description = [{ - Represents the LLVM target features as a list that can be checked within - passes/rewrites. - - Example: - ```mlir - #llvm.target_features<["+sme", "+sve", "+sme-f64f64"]> - ``` - - Then within a pass or rewrite the features active at an op can be queried: - - ```c++ - auto targetFeatures = LLVM::TargetFeaturesAttr::featuresAt(op); - - if (!targetFeatures.contains("+sme-f64f64")) - return failure(); - ``` - }]; - - let parameters = (ins OptionalArrayRefParameter<"StringAttr">:$features); - - let builders = [ - TypeBuilder<(ins "::llvm::StringRef":$features)>, - TypeBuilder<(ins "::llvm::ArrayRef<::llvm::StringRef>":$features)> - ]; - - let extraClassDeclaration = [{ - /// Checks if a feature is contained within the features list. - /// Note: Using a StringAttr allows doing pointer-comparisons. - bool contains(::mlir::StringAttr feature) const; - bool contains(::llvm::StringRef feature) const; - - bool nullOrEmpty() const { - // Checks if this attribute is null, or the features are empty. - return !bool(*this) || getFeatures().empty(); - } - - /// Returns the list of features as an LLVM-compatible string. - std::string getFeaturesString() const; - - /// Finds the target features on the parent FunctionOpInterface. - /// Note: This assumes the attribute name matches the return value of - /// `getAttributeName()`. - static TargetFeaturesAttr featuresAt(Operation* op); - - /// Canonical name for this attribute within MLIR. - static constexpr StringLiteral getAttributeName() { - return StringLiteral("target_features"); - } - }]; - - let assemblyFormat = "`<` `[` (`]`) : ($features^ `]`)? `>`"; - let genVerifyDecl = 1; -} - //===----------------------------------------------------------------------===// // UndefAttr //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h index 3ede857733242..14645d5dee95f 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h @@ -14,8 +14,10 @@ #ifndef MLIR_DIALECT_LLVMIR_LLVMATTRS_H_ #define MLIR_DIALECT_LLVMIR_LLVMATTRS_H_ -#include "mlir/Dialect/LLVMIR/LLVMTypes.h" #include "mlir/IR/OpImplementation.h" +#include "mlir/Interfaces/DataLayoutInterfaces.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" #include #include "mlir/Dialect/LLVMIR/LLVMOpsEnums.h.inc" @@ -89,11 +91,17 @@ class TBAANodeAttr : public Attribute { // TODO: this shouldn't be needed after we unify the attribute generation, i.e. // --gen-attr-* and --gen-attrdef-*. using cconv::CConv; -using tailcallkind::TailCallKind; using linkage::Linkage; +using tailcallkind::TailCallKind; } // namespace LLVM } // namespace mlir +// First obtain TargetFeaturesAttr definitions as it is used both an LLVMIR +// interface and that interface and this attribute are turn required by another +// LLVMIR attribute. +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/LLVMIR/LLVMTargetFeaturesAttrDefs.h.inc" + #include "mlir/Dialect/LLVMIR/LLVMAttrInterfaces.h.inc" #define GET_ATTRDEF_CLASSES diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td index b5ea8fc5da500..e924be32da10f 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td @@ -10,6 +10,7 @@ #define LLVMIR_DIALECT include "mlir/IR/DialectBase.td" +include "mlir/IR/AttrTypeBase.td" def LLVM_Dialect : Dialect { let name = "llvm"; @@ -123,4 +124,11 @@ def LLVM_Dialect : Dialect { }]; } +class LLVM_Attr traits = [], + string baseCppClass = "::mlir::Attribute"> + : AttrDef { + let mnemonic = attrMnemonic; +} + #endif // LLVMIR_DIALECT diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td index 138170f8c8762..0d2603debbc28 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td @@ -14,6 +14,7 @@ #define LLVMIR_INTERFACES include "mlir/IR/OpBase.td" +include "mlir/Interfaces/DataLayoutInterfaces.td" def FastmathFlagsInterface : OpInterface<"FastmathFlagsInterface"> { let description = [{ @@ -532,4 +533,56 @@ def LLVM_DIRecursiveTypeAttrInterface ]; } +def LLVM_TargetAttrInterface + : AttrInterface<"TargetAttrInterface", [DLTIQueryInterface]> { + let description = [{ + Interface for attributes that describe LLVM targets. + + These attributes should be able to return the specified target + `triple`, `chip` and `features` and are expected to be able to + produce the corresponding `llvm::TargetMachine` and + `llvm::DataLayout`. These methods can fail in case the backend + is not available. + + Implementing attributes should provide a + `DLTIQueryInterface::query()` implementation which responds to + keys `"triple"`, `"chip"` and `"features"` by returning an + appropriate `StringAttr`, `StringAttr` and + `LLVM_TargetFeaturesAttr`. + }]; + let cppNamespace = "::mlir::LLVM"; + let methods = [ + InterfaceMethod< + /*description=*/"Returns the target triple identifier.", + /*retTy=*/"::llvm::StringRef", + /*methodName=*/"getTriple", + /*args=*/(ins) + >, + InterfaceMethod< + /*description=*/"Returns the target chip (i.e. \"cpu\") identifier.", + /*retTy=*/"::llvm::StringRef", + /*methodName=*/"getChip", + /*args=*/(ins) + >, + InterfaceMethod< + /*description=*/"Returns the target features as a string.", + /*retTy=*/"::mlir::LLVM::TargetFeaturesAttr", + /*methodName=*/"getFeatures", + /*args=*/(ins) + >, + InterfaceMethod< + /*description=*/"Returns the target machine.", + /*retTy=*/"FailureOr<::llvm::TargetMachine *>", + /*methodName=*/"getTargetMachine", + /*args=*/(ins) + >, + InterfaceMethod< + /*description=*/"Returns the data layout associated to the target machine.", + /*retTy=*/"FailureOr<::llvm::DataLayout>", + /*methodName=*/"getDataLayout", + /*args=*/(ins) + > + ]; +} + #endif // LLVMIR_INTERFACES diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index f4c1640098320..36a6291b5e3a8 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -14,6 +14,7 @@ #define LLVMIR_OPS include "mlir/Dialect/LLVMIR/LLVMAttrDefs.td" +include "mlir/Dialect/LLVMIR/LLVMTargetFeaturesAttrDefs.td" include "mlir/Dialect/LLVMIR/LLVMEnums.td" include "mlir/Dialect/LLVMIR/LLVMOpBase.td" include "mlir/IR/EnumAttr.td" diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTargetFeaturesAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMTargetFeaturesAttrDefs.td new file mode 100644 index 0000000000000..2183f083103f5 --- /dev/null +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTargetFeaturesAttrDefs.td @@ -0,0 +1,82 @@ +//===-- LLVMAttrDefs.td - LLVM Attributes definition file --*- 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 LLVMIR_TARGETFEATURESATTRDEFS +#define LLVMIR_TARGETFEATURESATTRDEFS + +include "mlir/Dialect/LLVMIR/LLVMDialect.td" +include "mlir/IR/CommonAttrConstraints.td" +include "mlir/Interfaces/DataLayoutInterfaces.td" + +//===----------------------------------------------------------------------===// +// TargetFeaturesAttr +//===----------------------------------------------------------------------===// + +def LLVM_TargetFeaturesAttr : LLVM_Attr<"TargetFeatures", "target_features", + [DLTIQueryInterface]> { + let summary = "LLVM target features attribute"; + + let description = [{ + Represents the LLVM target features as a list that can be checked within + passes/rewrites. + + Example: + ```mlir + #llvm.target_features<["+sme", "+sve", "+sme-f64f64"]> + ``` + + Then within a pass or rewrite the features active at an op can be queried: + + ```c++ + auto targetFeatures = LLVM::TargetFeaturesAttr::featuresAt(op); + + if (!targetFeatures.contains("+sme-f64f64")) + return failure(); + ``` + }]; + + let parameters = (ins OptionalArrayRefParameter<"StringAttr">:$features); + + let builders = [ + TypeBuilder<(ins "::llvm::StringRef":$features)>, + TypeBuilder<(ins "::llvm::ArrayRef<::llvm::StringRef>":$features)> + ]; + + let extraClassDeclaration = [{ + /// Checks if a feature is contained within the features list. + /// Note: Using a StringAttr allows doing pointer-comparisons. + bool contains(::mlir::StringAttr feature) const; + bool contains(::llvm::StringRef feature) const; + + bool nullOrEmpty() const { + // Checks if this attribute is null, or the features are empty. + return !bool(*this) || getFeatures().empty(); + } + + /// Returns the list of features as an LLVM-compatible string. + std::string getFeaturesString() const; + + /// Finds the target features on the parent FunctionOpInterface. + /// Note: This assumes the attribute name matches the return value of + /// `getAttributeName()`. + static TargetFeaturesAttr featuresAt(Operation* op); + + /// Canonical name for this attribute within MLIR. + static constexpr StringLiteral getAttributeName() { + return StringLiteral("target_features"); + } + + /// Returns the attribute associated with the key. + FailureOr query(DataLayoutEntryKey key); + }]; + + let assemblyFormat = "`<` `[` (`]`) : ($features^ `]`)? `>`"; + let genVerifyDecl = 1; +} + +#endif // LLVMIR_TARGETFEATURESATTRDEFS diff --git a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td index 961909d5c8d27..5920c3b7b0332 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/LLVMIR/Transforms/Passes.td @@ -45,6 +45,14 @@ def LLVMRequestCWrappersPass }]; } +def LLVMDataLayoutFromTarget : Pass<"llvm-data-layout-from-target", "::mlir::ModuleOp"> { + let summary = "TODO"; + let dependentDialects = ["mlir::DLTIDialect"]; + let description = [{ + TODO + }]; +} + def NVVMOptimizeForTargetPass : Pass<"llvm-optimize-for-nvvm-target"> { let summary = "Optimize NVVM IR"; } diff --git a/mlir/lib/Dialect/DLTI/DLTI.cpp b/mlir/lib/Dialect/DLTI/DLTI.cpp index 0c4fe27ffa58e..62ecf7d759959 100644 --- a/mlir/lib/Dialect/DLTI/DLTI.cpp +++ b/mlir/lib/Dialect/DLTI/DLTI.cpp @@ -657,7 +657,7 @@ void DLTIDialect::initialize() { LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op, NamedAttribute attr) { if (attr.getName() == DLTIDialect::kDataLayoutAttrName) { - if (!llvm::isa(attr.getValue())) { + if (!llvm::dyn_cast(attr.getValue())) { return op->emitError() << "'" << DLTIDialect::kDataLayoutAttrName << "' is expected to be a #dlti.dl_spec attribute"; } diff --git a/mlir/lib/Dialect/DLTI/Traits.cpp b/mlir/lib/Dialect/DLTI/Traits.cpp index 34f2dd5896083..3f6dd2900a915 100644 --- a/mlir/lib/Dialect/DLTI/Traits.cpp +++ b/mlir/lib/Dialect/DLTI/Traits.cpp @@ -24,7 +24,7 @@ LogicalResult mlir::impl::verifyHasDefaultDLTIDataLayoutTrait(Operation *op) { } DataLayoutSpecInterface mlir::impl::getDataLayoutSpec(Operation *op) { - return op->getAttrOfType( + return op->getAttrOfType( DLTIDialect::kDataLayoutAttrName); } diff --git a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt index d987b72e98354..c3dea62df14c2 100644 --- a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt +++ b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Transforms) add_mlir_dialect_library(MLIRLLVMDialect + IR/DataLayoutImporter.cpp IR/FunctionCallUtils.cpp IR/LLVMAttrs.cpp IR/LLVMDialect.cpp @@ -13,6 +14,7 @@ add_mlir_dialect_library(MLIRLLVMDialect ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/LLVMIR DEPENDS + MLIRLLVMTargetFeaturesAttrsIncGen MLIRLLVMOpsIncGen MLIRLLVMTypesIncGen MLIRLLVMIntrinsicOpsIncGen @@ -26,10 +28,19 @@ add_mlir_dialect_library(MLIRLLVMDialect BitReader BitWriter Core + MC + Target + Support + TargetParser + AllTargetsAsmParsers + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos LINK_LIBS PUBLIC MLIRCallInterfaces MLIRControlFlowInterfaces + MLIRDLTIDialect MLIRDataLayoutInterfaces MLIRFunctionInterfaces MLIRInferTypeOpInterface diff --git a/mlir/lib/Target/LLVMIR/DataLayoutImporter.cpp b/mlir/lib/Dialect/LLVMIR/IR/DataLayoutImporter.cpp similarity index 94% rename from mlir/lib/Target/LLVMIR/DataLayoutImporter.cpp rename to mlir/lib/Dialect/LLVMIR/IR/DataLayoutImporter.cpp index fbad5c2fb78d9..988fe27e7cd8e 100644 --- a/mlir/lib/Target/LLVMIR/DataLayoutImporter.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/DataLayoutImporter.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "DataLayoutImporter.h" +#include "mlir/Dialect/LLVMIR/DataLayoutImporter.h" #include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinAttributes.h" @@ -274,19 +274,7 @@ DataLayoutImporter::tryToEmplaceLegalIntWidthsEntry(StringRef token) { return success(); } -void DataLayoutImporter::translateDataLayout( - const llvm::DataLayout &llvmDataLayout) { - dataLayout = {}; - - // Transform the data layout to its string representation and append the - // default data layout string specified in the language reference - // (https://llvm.org/docs/LangRef.html#data-layout). The translation then - // parses the string and ignores the default value if a specific kind occurs - // in both strings. Additionally, the following default values exist: - // - non-default address space pointer specifications default to the default - // address space pointer specification - // - the alloca address space defaults to the default address space. - layoutStr = llvmDataLayout.getStringRepresentation(); +void DataLayoutImporter::translateDataLayoutFromStr() { if (!layoutStr.empty()) layoutStr += "-"; layoutStr += kDefaultDataLayout; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp index 915954a7f5f51..dbe91e001f1cb 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" +#include "mlir/Dialect/LLVMIR/DataLayoutImporter.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/Builders.h" #include "mlir/IR/DialectImplementation.h" @@ -18,8 +19,11 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/TypeSwitch.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" -#include +#include "llvm/Support/TargetSelect.h" + +#define DEBUG_TYPE "llvm-attrs" using namespace mlir; using namespace mlir::LLVM; @@ -404,3 +408,74 @@ ModuleFlagAttr::verify(function_ref emitError, "supported for unknown key '" << key << "'"; } + +FailureOr TargetFeaturesAttr::query(DataLayoutEntryKey key) { + if (auto stringKey = dyn_cast(key)) + if (contains(stringKey)) + return UnitAttr::get(getContext()); + return failure(); +} + +//===----------------------------------------------------------------------===// +// LLVM_TargetAttr +//===----------------------------------------------------------------------===// + +FailureOr TargetAttr::getTargetMachine() { + if (targetMachine.has_value()) { + llvm::TargetMachine *tm = targetMachine.value(); + if (tm != nullptr) + return {tm}; + return failure(); + } + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + + std::string error; + const llvm::Target *target = + llvm::TargetRegistry::lookupTarget(getTriple(), error); + if (!target || !error.empty()) { + LLVM_DEBUG({ + llvm::dbgs() << "Failed to retrieve the target with: `" << error << "`\n"; + }); + targetMachine = {nullptr}; + return failure(); + } + + targetMachine = {target->createTargetMachine( + llvm::Triple(getTriple().strref()), getChip() ? getChip().strref() : "", + getFeatures() ? getFeatures().getFeaturesString() : "", {}, {})}; + + return {targetMachine.value()}; +} + +FailureOr TargetAttr::getDataLayout() { + if (dataLayout.has_value()) { + return dataLayout.value(); + } + + FailureOr targetMachine = getTargetMachine(); + if (failed(targetMachine)) { + LLVM_DEBUG({ + llvm::dbgs() + << "Failed to retrieve the target machine for data layout.\n"; + }); + dataLayout = std::nullopt; + return failure(); + } + dataLayout = (targetMachine.value())->createDataLayout(); + return dataLayout.value(); +} + +FailureOr<::mlir::Attribute> TargetAttr::query(DataLayoutEntryKey key) { + Attribute result; + if (auto stringAttrKey = dyn_cast(key)) { + if (stringAttrKey.getValue() == "triple") + return getTriple(); + if (stringAttrKey.getValue() == "chip" && (result = getChip())) + return result; + if (stringAttrKey.getValue() == "features" && (result = getFeatures())) + return result; + } + + return failure(); +} diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt index d4ff0955c5d0e..cc6289b485c55 100644 --- a/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/LLVMIR/Transforms/CMakeLists.txt @@ -7,6 +7,7 @@ add_mlir_dialect_library(MLIRLLVMIRTransforms LegalizeForExport.cpp OptimizeForNVVM.cpp RequestCWrappers.cpp + DataLayoutFromTarget.cpp DEPENDS MLIRLLVMPassIncGen diff --git a/mlir/lib/Dialect/LLVMIR/Transforms/DataLayoutFromTarget.cpp b/mlir/lib/Dialect/LLVMIR/Transforms/DataLayoutFromTarget.cpp new file mode 100644 index 0000000000000..06990525d87ac --- /dev/null +++ b/mlir/lib/Dialect/LLVMIR/Transforms/DataLayoutFromTarget.cpp @@ -0,0 +1,64 @@ +//===- DataLayoutFromTarget.cpp - extract data layout from TargetMachine --===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/DLTI/DLTI.h" +#include "mlir/Dialect/LLVMIR/DataLayoutImporter.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Pass/Pass.h" + +namespace mlir { +namespace LLVM { +#define GEN_PASS_DEF_LLVMDATALAYOUTFROMTARGET +#include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc" +} // namespace LLVM +} // namespace mlir + +using namespace mlir; +using namespace mlir::LLVM; + +struct DataLayoutFromTargetPass + : public LLVM::impl::LLVMDataLayoutFromTargetBase< + DataLayoutFromTargetPass> { + void runOnOperation() override { + ModuleOp mod = getOperation(); + + bool passFailed = false; + + mod->walk([&](ModuleOp mod) { + auto targetAttr = + mod->getAttrOfType("llvm.target"); + if (!targetAttr) + return; + + FailureOr dataLayout = targetAttr.getDataLayout(); + if (failed(dataLayout)) { + mod->emitError() << "failed to obtain llvm::DataLayout from " + << targetAttr; + passFailed = true; + return; + } + auto dataLayoutAttr = DataLayoutAttr::get( + &getContext(), dataLayout->getStringRepresentation()); + + StringRef dlSpecIdentifier = "dlti.dl_spec"; + auto existingDlSpec = + mod->getAttrOfType(dlSpecIdentifier); + if (existingDlSpec) { + DataLayoutSpecInterface dataLayoutSpec = + existingDlSpec.combineWith({dataLayoutAttr.getDataLayoutSpec()}); + mod->setAttr(dlSpecIdentifier, dataLayoutSpec); + } else { + mod->setAttr(dlSpecIdentifier, dataLayoutAttr); + } + }); + + if (passFailed) { + return signalPassFailure(); + } + } +}; diff --git a/mlir/lib/Target/LLVMIR/CMakeLists.txt b/mlir/lib/Target/LLVMIR/CMakeLists.txt index af22a7ff04bf0..f6d70fe05a163 100644 --- a/mlir/lib/Target/LLVMIR/CMakeLists.txt +++ b/mlir/lib/Target/LLVMIR/CMakeLists.txt @@ -3,7 +3,6 @@ add_subdirectory(Dialect) set(LLVM_OPTIONAL_SOURCES ConvertFromLLVMIR.cpp ConvertToLLVMIR.cpp - DataLayoutImporter.cpp DebugTranslation.cpp DebugImporter.cpp LoopAnnotationImporter.cpp @@ -63,7 +62,6 @@ add_mlir_translation_library(MLIRToLLVMIRTranslationRegistration ) add_mlir_translation_library(MLIRTargetLLVMIRImport - DataLayoutImporter.cpp DebugImporter.cpp LoopAnnotationImporter.cpp ModuleImport.cpp diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index bfda223fe0f5f..8b570d669a0f3 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -16,11 +16,11 @@ #include "mlir/Target/LLVMIR/Import.h" #include "AttrKindDetail.h" -#include "DataLayoutImporter.h" #include "DebugImporter.h" #include "LoopAnnotationImporter.h" #include "mlir/Dialect/DLTI/DLTI.h" +#include "mlir/Dialect/LLVMIR/DataLayoutImporter.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Matchers.h" diff --git a/mlir/test/Dialect/LLVMIR/data-layout-from-target-invalid.mlir b/mlir/test/Dialect/LLVMIR/data-layout-from-target-invalid.mlir new file mode 100644 index 0000000000000..71526663abf96 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/data-layout-from-target-invalid.mlir @@ -0,0 +1,8 @@ +// RUN: mlir-opt %s -llvm-data-layout-from-target --split-input-file --verify-diagnostics + +// expected-error @+1 {{failed to obtain llvm::DataLayout from #llvm.target}} +module attributes { dlti.dl_spec = #dlti.dl_spec, +llvm.target = + #llvm.target } { +} diff --git a/mlir/test/Dialect/LLVMIR/data-layout-from-target.mlir b/mlir/test/Dialect/LLVMIR/data-layout-from-target.mlir new file mode 100644 index 0000000000000..5eb698d6dd859 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/data-layout-from-target.mlir @@ -0,0 +1,69 @@ +// RUN: mlir-opt -llvm-data-layout-from-target -split-input-file %s | FileCheck %s + +// CHECK: module attributes +// CHECK-SAME: dlti.dl_spec = #llvm.data_layout +// CHECK-SAME: llvm.target = #llvm.target< +// CHECK-SAME: triple = "x86_64-unknown-linux" +// CHECK-NOT: chip = +// CHECK-NOT: #llvm.target_features + +module attributes { llvm.target = #llvm.target } { +} + +// ----- + +// TODO: module attributes +// TODO-SAME: dlti.dl_spec = #llvm.data_layout +// TODO-SAME: llvm.target = #llvm.target< +// TODO-SAME: triple = "x86_64-unknown-linux" +// TODO-NOT: chip = "skylake" +// TODO-SAME: #llvm.target_features<["+mmx", "+sse"]> +// TODO-NOT: chip = "skylake" + +//module attributes { llvm.target = #llvm.target> } { +//} + +// ----- + +// CHECK: module attributes +// CHECK-SAME: dlti.dl_spec = #llvm.data_layout +// CHECK-SAME: llvm.target = #llvm.target< +// CHECK-SAME: triple = "x86_64-unknown-linux" +// CHECK-SAME: chip = "skylake" +// CHECK-NOT: #llvm.target_features + +module attributes { llvm.target = #llvm.target } { +} + +// ----- + +// CHECK: module attributes +// CHECK-SAME: dlti.dl_spec = #llvm.data_layout +// CHECK-SAME: llvm.target = #llvm.target< +// CHECK-SAME: triple = "x86_64-unknown-linux" +// CHECK-SAME: chip = "skylake" +// CHECK-SAME: #llvm.target_features<["+mmx", "+sse"]> + +module attributes { llvm.target = #llvm.target> } { +} + +// ----- + +// CHECK: module attributes +// CHECK-SAME: dlti.dl_spec = #dlti.dl_spec<"dlti.endianness" = "little" +// CHECK-SAME: index = 32 +// CHECK-SAME: llvm.target = #llvm.target< +// CHECK-SAME: triple = "x86_64-unknown-linux" +// CHECK-SAME: chip = "skylake" +// CHECK-SAME: #llvm.target_features<["+mmx", "+sse"]> + +module attributes { dlti.dl_spec = #dlti.dl_spec, + llvm.target = #llvm.target> } { +}