Skip to content

Commit ae5834a

Browse files
committed
[mlir][llvm] Add llvm.target_features features attribute
This patch adds a target_features (TargetFeaturesAttr) to the LLVM dialect to allow setting and querying the features in use on a function. The features are stored as a sorted list rather plain string to allow efficiently checking a function's features. The motivation for this comes from the Arm SME dialect where we would like a convenient way to check what variants of an operation are available based on the CPU features. Intended usage: The target_features attribute is populated manually or by a pass: ```mlir func.func @example() attributes { target_features = #llvm.target_features<"+sme,+sve,+sme-f64f64"> } { // ... } ``` Then within a later rewrite the attribute can be checked, and used to make lowering decisions. ```c++ // Finds the "target_features" attribute on the parent // FunctionOpInterface. auto targetFeatures = LLVM::TargetFeaturesAttr::featuresAt(op); // Check a feature. // Returns false if targetFeatures is null or the feature is not in // the list. if (!targetFeatures.contains("+sme-f64f64")) return failure(); ``` For now, this is rather simple just checks if the exact feature is in the list, though it could be possible to extend with implied features using information from LLVM.
1 parent 3257e4c commit ae5834a

File tree

8 files changed

+175
-1
lines changed

8 files changed

+175
-1
lines changed

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,4 +933,57 @@ def LLVM_VScaleRangeAttr : LLVM_Attr<"VScaleRange", "vscale_range"> {
933933
"IntegerAttr":$maxRange);
934934
let assemblyFormat = "`<` struct(params) `>`";
935935
}
936+
937+
//===----------------------------------------------------------------------===//
938+
// TargetFeaturesAttr
939+
//===----------------------------------------------------------------------===//
940+
941+
def LLVM_TargetFeaturesAttr : LLVM_Attr<"TargetFeatures", "target_features"> {
942+
let summary = "LLVM target features attribute";
943+
944+
let description = [{
945+
Represents the LLVM target features in a manner that is efficient to query.
946+
947+
Example:
948+
```mlir
949+
#llvm.target_features<"+sme,+sve,+sme-f64f64">
950+
```
951+
952+
Then within a pass or rewrite the features active at an op can be queried:
953+
954+
```c++
955+
auto targetFeatures = LLVM::TargetFeaturesAttr::featuresAt(op);
956+
957+
if (!targetFeatures.contains("+sme-f64f64"))
958+
return failure();
959+
```
960+
}];
961+
962+
let parameters = (ins
963+
ArrayRefOfSelfAllocationParameter<"TargetFeature", "">: $features);
964+
965+
let builders = [
966+
TypeBuilder<(ins "::llvm::ArrayRef<TargetFeature>": $features)>,
967+
TypeBuilder<(ins "StringRef": $features)>
968+
];
969+
970+
let extraClassDeclaration = [{
971+
/// Checks if a feature is contained within the features list.
972+
bool contains(TargetFeature) const;
973+
bool contains(StringRef feature) const {
974+
return contains(TargetFeature{feature});
975+
}
976+
977+
/// Returns the list of features as an LLVM-compatible string.
978+
std::string getFeaturesString() const;
979+
980+
/// Finds the target features on the parent FunctionOpInterface.
981+
/// Note: This assumes the attribute is called "target_features".
982+
static TargetFeaturesAttr featuresAt(Operation* op);
983+
}];
984+
985+
let hasCustomAssemblyFormat = 1;
986+
let skipDefaultBuilders = 1;
987+
}
988+
936989
#endif // LLVMIR_ATTRDEFS

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ class TBAANodeAttr : public Attribute {
7474
}
7575
};
7676

77+
/// This struct represents a single LLVM target feature.
78+
struct TargetFeature {
79+
StringRef feature;
80+
81+
// Support allocating this struct into MLIR storage to ensure the feature
82+
// string remains valid.
83+
TargetFeature allocateInto(TypeStorageAllocator &alloc) const {
84+
return TargetFeature{alloc.copyInto(feature)};
85+
}
86+
87+
operator StringRef() const { return feature; }
88+
89+
bool operator==(TargetFeature const &other) const {
90+
return feature == other.feature;
91+
}
92+
93+
bool operator<(TargetFeature const &other) const {
94+
return feature < other.feature;
95+
}
96+
};
97+
98+
inline llvm::hash_code hash_value(const TargetFeature &feature) {
99+
return llvm::hash_value(feature.feature);
100+
}
101+
77102
// Inline the LLVM generated Linkage enum and utility.
78103
// This is only necessary to isolate the "enum generated code" from the
79104
// attribute definition itself.

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,8 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [
13941394
OptionalAttr<UnnamedAddr>:$unnamed_addr,
13951395
OptionalAttr<I64Attr>:$alignment,
13961396
OptionalAttr<LLVM_VScaleRangeAttr>:$vscale_range,
1397-
OptionalAttr<FramePointerKindAttr>:$frame_pointer
1397+
OptionalAttr<FramePointerKindAttr>:$frame_pointer,
1398+
OptionalAttr<LLVM_TargetFeaturesAttr>:$target_features
13981399
);
13991400

14001401
let regions = (region AnyRegion:$body);

mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
1515
#include "mlir/IR/Builders.h"
1616
#include "mlir/IR/DialectImplementation.h"
17+
#include "mlir/Interfaces/FunctionInterfaces.h"
1718
#include "llvm/ADT/StringExtras.h"
1819
#include "llvm/ADT/TypeSwitch.h"
1920
#include "llvm/BinaryFormat/Dwarf.h"
2021
#include "llvm/IR/DebugInfoMetadata.h"
22+
#include <algorithm>
2123
#include <optional>
24+
#include <set>
2225

2326
using namespace mlir;
2427
using namespace mlir::LLVM;
@@ -183,3 +186,69 @@ void printExpressionArg(AsmPrinter &printer, uint64_t opcode,
183186
i++;
184187
});
185188
}
189+
190+
//===----------------------------------------------------------------------===//
191+
// TargetFeaturesAttr
192+
//===----------------------------------------------------------------------===//
193+
194+
Attribute TargetFeaturesAttr::parse(mlir::AsmParser &parser, mlir::Type) {
195+
std::string targetFeatures;
196+
if (parser.parseLess() || parser.parseString(&targetFeatures) ||
197+
parser.parseGreater())
198+
return {};
199+
return get(parser.getContext(), targetFeatures);
200+
}
201+
202+
void TargetFeaturesAttr::print(mlir::AsmPrinter &printer) const {
203+
printer << "<\"";
204+
llvm::interleave(
205+
getFeatures(), printer,
206+
[&](auto &feature) { printer << StringRef(feature); }, ",");
207+
printer << "\">";
208+
}
209+
210+
TargetFeaturesAttr
211+
TargetFeaturesAttr::get(MLIRContext *context,
212+
llvm::ArrayRef<TargetFeature> featuresRef) {
213+
// Sort and de-duplicate target features.
214+
std::set<TargetFeature> features(featuresRef.begin(), featuresRef.end());
215+
return Base::get(context, llvm::to_vector(features));
216+
}
217+
218+
TargetFeaturesAttr TargetFeaturesAttr::get(MLIRContext *context,
219+
StringRef targetFeatures) {
220+
SmallVector<StringRef> features;
221+
StringRef{targetFeatures}.split(features, ',', /*MaxSplit=*/-1,
222+
/*KeepEmpty=*/false);
223+
return get(context, llvm::map_to_vector(features, [](StringRef feature) {
224+
return TargetFeature{feature};
225+
}));
226+
}
227+
228+
bool TargetFeaturesAttr::contains(TargetFeature feature) const {
229+
if (!bool(*this))
230+
return false; // Allows checking null target features.
231+
ArrayRef<TargetFeature> features = getFeatures();
232+
// Note: The attribute getter ensures the feature list is sorted.
233+
return std::binary_search(features.begin(), features.end(), feature);
234+
}
235+
236+
std::string TargetFeaturesAttr::getFeaturesString() const {
237+
std::string features;
238+
bool first = true;
239+
for (TargetFeature feature : getFeatures()) {
240+
if (!first)
241+
features += ",";
242+
features += StringRef(feature);
243+
first = false;
244+
}
245+
return features;
246+
}
247+
248+
TargetFeaturesAttr TargetFeaturesAttr::featuresAt(Operation *op) {
249+
auto parentFunction = op->getParentOfType<FunctionOpInterface>();
250+
if (!parentFunction)
251+
return {};
252+
return parentFunction.getOperation()->getAttrOfType<TargetFeaturesAttr>(
253+
"target_features");
254+
}

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,7 @@ static constexpr std::array ExplicitAttributes{
16271627
StringLiteral("aarch64_pstate_za_new"),
16281628
StringLiteral("vscale_range"),
16291629
StringLiteral("frame-pointer"),
1630+
StringLiteral("target-features"),
16301631
};
16311632

16321633
static void processPassthroughAttrs(llvm::Function *func, LLVMFuncOp funcOp) {
@@ -1717,6 +1718,12 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func,
17171718
stringRefFramePointerKind)
17181719
.value()));
17191720
}
1721+
1722+
if (llvm::Attribute attr = func->getFnAttribute("target-features");
1723+
attr.isStringAttribute()) {
1724+
funcOp.setTargetFeaturesAttr(
1725+
LLVM::TargetFeaturesAttr::get(context, attr.getValueAsString()));
1726+
}
17201727
}
17211728

17221729
DictionaryAttr

mlir/lib/Target/LLVMIR/ModuleTranslation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,9 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
968968
if (func.getArmNewZa())
969969
llvmFunc->addFnAttr("aarch64_pstate_za_new");
970970

971+
if (auto targetFeatures = func.getTargetFeatures())
972+
llvmFunc->addFnAttr("target-features", targetFeatures->getFeaturesString());
973+
971974
if (auto attr = func.getVscaleRange())
972975
llvmFunc->addFnAttr(llvm::Attribute::getWithVScaleRangeArgs(
973976
getLLVMContext(), attr->getMinRange().getInt(),
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
2+
3+
; CHECK-LABEL: llvm.func @target_features()
4+
; CHECK-SAME: #llvm.target_features<"+sme,+sme-f64f64,+sve">
5+
define void @target_features() #0 {
6+
ret void
7+
}
8+
9+
attributes #0 = { "target-features"="+sme,+sme-f64f64,+sve" }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
// CHECK-LABEL: define void @target_features
4+
// CHECK: attributes #{{.*}} = { "target-features"="+sme,+sme-f64f64,+sve" }
5+
llvm.func @target_features() attributes { target_features = #llvm.target_features<"+sme,+sve,+sme-f64f64"> } {
6+
llvm.return
7+
}

0 commit comments

Comments
 (0)