Skip to content

Commit 17de468

Browse files
authored
[mlir][llvm] Add llvm.target_features features attribute (#71510)
This patch adds a target_features (TargetFeaturesAttr) to the LLVM dialect to allow setting and querying the features in use on a function. 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 7724954 commit 17de468

File tree

8 files changed

+180
-1
lines changed

8 files changed

+180
-1
lines changed

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,4 +933,68 @@ 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+
{
943+
let summary = "LLVM target features attribute";
944+
945+
let description = [{
946+
Represents the LLVM target features as a list that can be checked within
947+
passes/rewrites.
948+
949+
Example:
950+
```mlir
951+
#llvm.target_features<["+sme", "+sve", "+sme-f64f64"]>
952+
```
953+
954+
Then within a pass or rewrite the features active at an op can be queried:
955+
956+
```c++
957+
auto targetFeatures = LLVM::TargetFeaturesAttr::featuresAt(op);
958+
959+
if (!targetFeatures.contains("+sme-f64f64"))
960+
return failure();
961+
```
962+
}];
963+
964+
let parameters = (ins OptionalArrayRefParameter<"StringAttr">:$features);
965+
966+
let builders = [
967+
TypeBuilder<(ins "::llvm::StringRef":$features)>,
968+
TypeBuilder<(ins "::llvm::ArrayRef<::llvm::StringRef>":$features)>
969+
];
970+
971+
let extraClassDeclaration = [{
972+
/// Checks if a feature is contained within the features list.
973+
/// Note: Using a StringAttr allows doing pointer-comparisons.
974+
bool contains(::mlir::StringAttr feature) const;
975+
bool contains(::llvm::StringRef feature) const;
976+
977+
bool nullOrEmpty() const {
978+
// Checks if this attribute is null, or the features are empty.
979+
return !bool(*this) || getFeatures().empty();
980+
}
981+
982+
/// Returns the list of features as an LLVM-compatible string.
983+
std::string getFeaturesString() const;
984+
985+
/// Finds the target features on the parent FunctionOpInterface.
986+
/// Note: This assumes the attribute name matches the return value of
987+
/// `getAttributeName()`.
988+
static TargetFeaturesAttr featuresAt(Operation* op);
989+
990+
/// Canonical name for this attribute within MLIR.
991+
static constexpr StringLiteral getAttributeName() {
992+
return StringLiteral("target_features");
993+
}
994+
}];
995+
996+
let assemblyFormat = "`<` `[` (`]`) : ($features^ `]`)? `>`";
997+
let genVerifyDecl = 1;
998+
}
999+
9361000
#endif // LLVMIR_ATTRDEFS

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: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
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"
@@ -183,3 +184,67 @@ void printExpressionArg(AsmPrinter &printer, uint64_t opcode,
183184
i++;
184185
});
185186
}
187+
188+
//===----------------------------------------------------------------------===//
189+
// TargetFeaturesAttr
190+
//===----------------------------------------------------------------------===//
191+
192+
TargetFeaturesAttr TargetFeaturesAttr::get(MLIRContext *context,
193+
llvm::ArrayRef<StringRef> features) {
194+
return Base::get(context,
195+
llvm::map_to_vector(features, [&](StringRef feature) {
196+
return StringAttr::get(context, feature);
197+
}));
198+
}
199+
200+
TargetFeaturesAttr TargetFeaturesAttr::get(MLIRContext *context,
201+
StringRef targetFeatures) {
202+
SmallVector<StringRef> features;
203+
targetFeatures.split(features, ',', /*MaxSplit=*/-1,
204+
/*KeepEmpty=*/false);
205+
return get(context, features);
206+
}
207+
208+
LogicalResult
209+
TargetFeaturesAttr::verify(function_ref<InFlightDiagnostic()> emitError,
210+
llvm::ArrayRef<StringAttr> features) {
211+
for (StringAttr featureAttr : features) {
212+
if (!featureAttr || featureAttr.empty())
213+
return emitError() << "target features can not be null or empty";
214+
auto feature = featureAttr.strref();
215+
if (feature[0] != '+' && feature[0] != '-')
216+
return emitError() << "target features must start with '+' or '-'";
217+
if (feature.contains(','))
218+
return emitError() << "target features can not contain ','";
219+
}
220+
return success();
221+
}
222+
223+
bool TargetFeaturesAttr::contains(StringAttr feature) const {
224+
if (nullOrEmpty())
225+
return false;
226+
// Note: Using StringAttr does pointer comparisons.
227+
return llvm::is_contained(getFeatures(), feature);
228+
}
229+
230+
bool TargetFeaturesAttr::contains(StringRef feature) const {
231+
if (nullOrEmpty())
232+
return false;
233+
return llvm::is_contained(getFeatures(), feature);
234+
}
235+
236+
std::string TargetFeaturesAttr::getFeaturesString() const {
237+
std::string featuresString;
238+
llvm::raw_string_ostream ss(featuresString);
239+
llvm::interleave(
240+
getFeatures(), ss, [&](auto &feature) { ss << feature.strref(); }, ",");
241+
return ss.str();
242+
}
243+
244+
TargetFeaturesAttr TargetFeaturesAttr::featuresAt(Operation *op) {
245+
auto parentFunction = op->getParentOfType<FunctionOpInterface>();
246+
if (!parentFunction)
247+
return {};
248+
return parentFunction.getOperation()->getAttrOfType<TargetFeaturesAttr>(
249+
getAttributeName());
250+
}

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" }

mlir/test/Target/LLVMIR/llvmir-invalid.mlir

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,27 @@ llvm.func @stepvector_intr_wrong_type() -> vector<7xf32> {
261261

262262
// -----
263263

264+
// expected-error @below{{target features can not contain ','}}
265+
llvm.func @invalid_target_feature() attributes { target_features = #llvm.target_features<["+bad,feature", "+test"]> }
266+
{
267+
}
268+
269+
// -----
270+
271+
// expected-error @below{{target features must start with '+' or '-'}}
272+
llvm.func @missing_target_feature_prefix() attributes { target_features = #llvm.target_features<["sme"]> }
273+
{
274+
}
275+
276+
// -----
277+
278+
// expected-error @below{{target features can not be null or empty}}
279+
llvm.func @empty_target_feature() attributes { target_features = #llvm.target_features<["", "+sve"]> }
280+
{
281+
}
282+
283+
// -----
284+
264285
llvm.comdat @__llvm_comdat {
265286
llvm.comdat_selector @foo any
266287
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
2+
3+
// CHECK-LABEL: define void @target_features
4+
// CHECK: attributes #{{.*}} = { "target-features"="+sme,+sve,+sme-f64f64" }
5+
llvm.func @target_features() attributes {
6+
target_features = #llvm.target_features<["+sme", "+sve", "+sme-f64f64"]>
7+
} {
8+
llvm.return
9+
}

0 commit comments

Comments
 (0)