Skip to content

[MLIR][LLVMIR][DLTI] Add #llvm.target, #llvm.data_layout and TargetAttrInterface #145899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Dialect/FIRType.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
18 changes: 16 additions & 2 deletions mlir/include/mlir/Dialect/LLVMIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand 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
Expand All @@ -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.
Expand Down Expand Up @@ -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_
10 changes: 10 additions & 0 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMAttrAndEnumDefs.td
Original file line number Diff line number Diff line change
@@ -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"
200 changes: 131 additions & 69 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<string name, string attrMnemonic,
list<Trait> traits = [],
string baseCppClass = "::mlir::Attribute">
: AttrDef<LLVM_Dialect, name, traits, baseCppClass> {
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<llvm::TargetMachine *> targetMachine = std::nullopt;

FailureOr<llvm::TargetMachine *> getTargetMachine();

std::optional<llvm::DataLayout> dataLayout = std::nullopt;
FailureOr<llvm::DataLayout> getDataLayout();
FailureOr<Attribute> 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 <typename Ty>
DataLayoutEntryList getSpecForType() {
return getDataLayoutSpec().getSpecForType(TypeID::get<Ty>());
}

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<Attribute> query(DataLayoutEntryKey key) const {
return getDataLayoutSpec().query(key);
}
}];
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
12 changes: 10 additions & 2 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment on lines +19 to +20
Copy link
Contributor

@fabianmcg fabianmcg Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding a lot of LLVM deps to the LLVM dialect that we don't want. The best way to remove this dep is adding a promised interface and implement LLVM_TargetAttrInterface for that attr as an external model in mlir/lib/Target.

#include <optional>

#include "mlir/Dialect/LLVMIR/LLVMOpsEnums.h.inc"
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVMIR_DIALECT

include "mlir/IR/DialectBase.td"
include "mlir/IR/AttrTypeBase.td"

def LLVM_Dialect : Dialect {
let name = "llvm";
Expand Down Expand Up @@ -123,4 +124,11 @@ def LLVM_Dialect : Dialect {
}];
}

class LLVM_Attr<string name, string attrMnemonic,
list<Trait> traits = [],
string baseCppClass = "::mlir::Attribute">
: AttrDef<LLVM_Dialect, name, traits, baseCppClass> {
let mnemonic = attrMnemonic;
}

#endif // LLVMIR_DIALECT
Loading