[Mlir-commits] [mlir] 0a2131b - [mlir] LLVMFuncOp: provide a capability to pass attributes through to LLVM IR

Alex Zinenko llvmlistbot at llvm.org
Thu Apr 2 03:52:54 PDT 2020


Author: Alex Zinenko
Date: 2020-04-02T12:52:46+02:00
New Revision: 0a2131b7e2213fc8b45f7ff1e02f29fb4d544259

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

LOG: [mlir] LLVMFuncOp: provide a capability to pass attributes through to LLVM IR

Summary:
LLVM IR functions can have arbitrary attributes attached to them, some of which
affect may affect code transformations. Until we can model all attributes
consistently, provide a pass-through mechanism that forwards attributes from
the LLVMFuncOp in MLIR to LLVM IR functions during translation. This mechanism
relies on LLVM IR being able to recognize string representations of the
attributes and performs some additional checking to avoid hitting assertions
within LLVM code.

Differential Revision: https://reviews.llvm.org/D77072

Added: 
    

Modified: 
    mlir/docs/Dialects/LLVM.md
    mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
    mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
    mlir/test/Target/llvmir-invalid.mlir
    mlir/test/Target/llvmir.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/docs/Dialects/LLVM.md b/mlir/docs/Dialects/LLVM.md
index bbf675f06023..e512675565fd 100644
--- a/mlir/docs/Dialects/LLVM.md
+++ b/mlir/docs/Dialects/LLVM.md
@@ -79,6 +79,32 @@ llvm.func internal @internal_func() {
 
 ```
 
+#### Attribute pass-through
+
+An LLVM IR dialect function provides a mechanism to forward function-level
+attributes to LLVM IR using the `passthrough` attribute. This is an array
+attribute containing either string attributes or array attributes. In the former
+case, the value of the string is interpreted as the name of LLVM IR function
+attribute. In the latter case, the array is expected to contain exactly two
+string attributes, the first corresponding to the name of LLVM IR function
+attribute, and the second corresponding to its value. Note that even integer
+LLVM IR function attributes have their value represented in the string form.
+
+Example:
+
+```mlir
+llvm.func @func() attributes {
+  passthrough = ["noinline",           // value-less attribute
+                 ["alignstack", "4"],  // integer attribute with value
+                 ["other", "attr"]]    // attrbute unknown to LLVM
+} {
+  llvm.return
+}
+```
+
+If the attribute is not known to LLVM IR, it will be attached as a string
+attribute.
+
 ### LLVM IR operations
 
 The following operations are currently supported. The semantics of these

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 462584400a41..54b2e9429bc3 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -663,7 +663,8 @@ def LLVM_LLVMFuncOp
     : LLVM_ZeroResultOp<"func", [IsolatedFromAbove, FunctionLike, Symbol]>,
       Arguments<(ins DefaultValuedAttr<Linkage,
                                        "Linkage::External">:$linkage,
-                     OptionalAttr<FlatSymbolRefAttr>:$personality)> {
+                     OptionalAttr<FlatSymbolRefAttr>:$personality,
+                     OptionalAttr<ArrayAttr>:$passthrough)> {
   let summary = "LLVM dialect function, has wrapped LLVM IR function type";
 
   let regions = (region AnyRegion:$body);

diff  --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 89ad372025b9..da58eb7559af 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -560,6 +560,83 @@ static llvm::SetVector<Block *> topologicalSort(LLVMFuncOp f) {
   return blocks;
 }
 
+/// Attempts to add an attribute identified by `key`, optionally with the given
+/// `value` to LLVM function `llvmFunc`. Reports errors at `loc` if any. If the
+/// attribute has a kind known to LLVM IR, create the attribute of this kind,
+/// otherwise keep it as a string attribute. Performs additional checks for
+/// attributes known to have or not have a value in order to avoid assertions
+/// inside LLVM upon construction.
+static LogicalResult checkedAddLLVMFnAttribute(Location loc,
+                                               llvm::Function *llvmFunc,
+                                               StringRef key,
+                                               StringRef value = StringRef()) {
+  auto kind = llvm::Attribute::getAttrKindFromName(key);
+  if (kind == llvm::Attribute::None) {
+    llvmFunc->addFnAttr(key, value);
+    return success();
+  }
+
+  if (llvm::Attribute::doesAttrKindHaveArgument(kind)) {
+    if (value.empty())
+      return emitError(loc) << "LLVM attribute '" << key << "' expects a value";
+
+    int result;
+    if (!value.getAsInteger(/*Radix=*/0, result))
+      llvmFunc->addFnAttr(
+          llvm::Attribute::get(llvmFunc->getContext(), kind, result));
+    else
+      llvmFunc->addFnAttr(key, value);
+    return success();
+  }
+
+  if (!value.empty())
+    return emitError(loc) << "LLVM attribute '" << key
+                          << "' does not expect a value, found '" << value
+                          << "'";
+
+  llvmFunc->addFnAttr(kind);
+  return success();
+}
+
+/// Attaches the attributes listed in the given array attribute to `llvmFunc`.
+/// Reports error to `loc` if any and returns immediately. Expects `attributes`
+/// to be an array attribute containing either string attributes, treated as
+/// value-less LLVM attributes, or array attributes containing two string
+/// attributes, with the first string being the name of the corresponding LLVM
+/// attribute and the second string beings its value. Note that even integer
+/// attributes are expected to have their values expressed as strings.
+static LogicalResult
+forwardPassthroughAttributes(Location loc, Optional<ArrayAttr> attributes,
+                             llvm::Function *llvmFunc) {
+  if (!attributes)
+    return success();
+
+  for (Attribute attr : *attributes) {
+    if (auto stringAttr = attr.dyn_cast<StringAttr>()) {
+      if (failed(
+              checkedAddLLVMFnAttribute(loc, llvmFunc, stringAttr.getValue())))
+        return failure();
+      continue;
+    }
+
+    auto arrayAttr = attr.dyn_cast<ArrayAttr>();
+    if (!arrayAttr || arrayAttr.size() != 2)
+      return emitError(loc)
+             << "expected 'passthrough' to contain string or array attributes";
+
+    auto keyAttr = arrayAttr[0].dyn_cast<StringAttr>();
+    auto valueAttr = arrayAttr[1].dyn_cast<StringAttr>();
+    if (!keyAttr || !valueAttr)
+      return emitError(loc)
+             << "expected arrays within 'passthrough' to contain two strings";
+
+    if (failed(checkedAddLLVMFnAttribute(loc, llvmFunc, keyAttr.getValue(),
+                                         valueAttr.getValue())))
+      return failure();
+  }
+  return success();
+}
+
 LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
   // Clear the block and value mappings, they are only relevant within one
   // function.
@@ -637,9 +714,13 @@ LogicalResult ModuleTranslation::convertFunctions() {
     llvm::FunctionCallee llvmFuncCst = llvmModule->getOrInsertFunction(
         function.getName(),
         cast<llvm::FunctionType>(function.getType().getUnderlyingType()));
-    assert(isa<llvm::Function>(llvmFuncCst.getCallee()));
-    functionMapping[function.getName()] =
-        cast<llvm::Function>(llvmFuncCst.getCallee());
+    llvm::Function *llvmFunc = cast<llvm::Function>(llvmFuncCst.getCallee());
+    functionMapping[function.getName()] = llvmFunc;
+
+    // Forward the pass-through attributes to LLVM.
+    if (failed(forwardPassthroughAttributes(function.getLoc(),
+                                            function.passthrough(), llvmFunc)))
+      return failure();
   }
 
   // Convert functions.

diff  --git a/mlir/test/Target/llvmir-invalid.mlir b/mlir/test/Target/llvmir-invalid.mlir
index dce4189f733f..692975b2cf08 100644
--- a/mlir/test/Target/llvmir-invalid.mlir
+++ b/mlir/test/Target/llvmir-invalid.mlir
@@ -17,3 +17,23 @@ llvm.func @no_nested_struct() -> !llvm<"[2 x [2 x [2 x {i32}]]]"> {
 
 // expected-error @+1 {{unsupported constant value}}
 llvm.mlir.global internal constant @test([2.5, 7.4]) : !llvm<"[2 x double]">
+
+// -----
+
+// expected-error @+1 {{LLVM attribute 'noinline' does not expect a value}}
+llvm.func @passthrough_unexpected_value() attributes {passthrough = [["noinline", "42"]]}
+
+// -----
+
+// expected-error @+1 {{LLVM attribute 'alignstack' expects a value}}
+llvm.func @passthrough_expected_value() attributes {passthrough = ["alignstack"]}
+
+// -----
+
+// expected-error @+1 {{expected 'passthrough' to contain string or array attributes}}
+llvm.func @passthrough_wrong_type() attributes {passthrough = [42]}
+
+// -----
+
+// expected-error @+1 {{expected arrays within 'passthrough' to contain two strings}}
+llvm.func @passthrough_wrong_type() attributes {passthrough = [[42, 42]]}

diff  --git a/mlir/test/Target/llvmir.mlir b/mlir/test/Target/llvmir.mlir
index c7a5802f95d7..3909a8a31228 100644
--- a/mlir/test/Target/llvmir.mlir
+++ b/mlir/test/Target/llvmir.mlir
@@ -1201,3 +1201,15 @@ llvm.func @callFenceInst() {
   llvm.fence syncscope("") release
   llvm.return
 }
+
+// CHECK-LABEL @passthrough
+// CHECK: #[[ATTR_GROUP:[0-9]*]]
+llvm.func @passthrough() attributes {passthrough = ["noinline", ["alignstack", "4"], "null-pointer-is-valid", ["foo", "bar"]]} {
+  llvm.return
+}
+
+// CHECK: attributes #[[ATTR_GROUP]] = {
+// CHECK-DAG: noinline
+// CHECK-DAG: alignstack=4
+// CHECK-DAG: "null-pointer-is-valid"
+// CHECK-DAG: "foo"="bar"


        


More information about the Mlir-commits mailing list