[Mlir-commits] [mlir] f9173c2 - [mlir][LLVM] Convert `noalias` parameters into alias scopes during inlining

Markus Böck llvmlistbot at llvm.org
Thu Jul 20 06:05:34 PDT 2023


Author: Markus Böck
Date: 2023-07-20T15:05:28+02:00
New Revision: f9173c2958bf6cb9bf23cb00184ce91dfba6acbc

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

LOG: [mlir][LLVM] Convert `noalias` parameters into alias scopes during inlining

Currently, inlining a function with a `noalias` parameter leads to a large loss of optimization potential as the `noalias` parameter, an important hint for alias analysis, is lost completely.

This patch fixes this with the same approach as LLVM by annotating all users of the `noalias` parameter with appropriate alias and noalias scope lists.
The implementation done here is not as sophisticated as LLVMs, which has more infrastructure related to escaping and captured pointers, but should work in the majority of important cases.
Any deficiency can be addressed in future patches.

Related LLVM code: https://github.com/llvm/llvm-project/blob/27ade4b554774187d2c0afcf64cd16fa6d5f619d/llvm/lib/Transforms/Utils/InlineFunction.cpp#L1090

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

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
    mlir/lib/Dialect/LLVMIR/IR/LLVMInlining.cpp
    mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
    mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir
    mlir/test/Dialect/LLVMIR/inlining.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
index 7b33ec8bb0c30e..c5d65f792254e6 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
@@ -199,6 +199,13 @@ def AliasAnalysisOpInterface : OpInterface<"AliasAnalysisOpInterface"> {
         auto op = cast<ConcreteOp>(this->getOperation());
         op.setTbaaAttr(attr);
       }]
+      >,
+    InterfaceMethod<
+      /*desc=*/        "Returns a list of all pointer operands accessed by the "
+                       "operation",
+      /*returnType=*/  "::llvm::SmallVector<::mlir::Value>",
+      /*methodName=*/  "getAccessedOperands",
+      /*args=*/        (ins)
       >
   ];
 }

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMInlining.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMInlining.cpp
index c2e319e84f9f0f..0426d5dcb25e38 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMInlining.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMInlining.cpp
@@ -16,6 +16,7 @@
 #include "mlir/IR/Matchers.h"
 #include "mlir/Interfaces/DataLayoutInterfaces.h"
 #include "mlir/Transforms/InliningUtils.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/Debug.h"
 
 #define DEBUG_TYPE "llvm-inliner"
@@ -200,6 +201,238 @@ static ArrayAttr concatArrayAttr(ArrayAttr lhs, ArrayAttr rhs) {
   return ArrayAttr::get(lhs.getContext(), result);
 }
 
+/// Attempts to return the underlying pointer value that `pointerValue` is based
+/// on. This traverses down the chain of operations to the last operation
+/// producing the base pointer and returns it. If it encounters an operation it
+/// cannot further traverse through, returns the operation's result.
+static Value getUnderlyingObject(Value pointerValue) {
+  while (true) {
+    if (auto gepOp = pointerValue.getDefiningOp<LLVM::GEPOp>()) {
+      pointerValue = gepOp.getBase();
+      continue;
+    }
+
+    if (auto addrCast = pointerValue.getDefiningOp<LLVM::AddrSpaceCastOp>()) {
+      pointerValue = addrCast.getOperand();
+      continue;
+    }
+
+    break;
+  }
+
+  return pointerValue;
+}
+
+/// Attempts to return the set of all underlying pointer values that
+/// `pointerValue` is based on. This function traverses through select
+/// operations and block arguments unlike getUnderlyingObject.
+static SmallVector<Value> getUnderlyingObjectSet(Value pointerValue) {
+  SmallVector<Value> result;
+
+  SmallVector<Value> workList{pointerValue};
+  // Avoid dataflow loops.
+  SmallPtrSet<Value, 4> seen;
+  do {
+    Value current = workList.pop_back_val();
+    current = getUnderlyingObject(current);
+
+    if (!seen.insert(current).second)
+      continue;
+
+    if (auto selectOp = current.getDefiningOp<LLVM::SelectOp>()) {
+      workList.push_back(selectOp.getTrueValue());
+      workList.push_back(selectOp.getFalseValue());
+      continue;
+    }
+
+    if (auto blockArg = dyn_cast<BlockArgument>(current)) {
+      Block *parentBlock = blockArg.getParentBlock();
+
+      // Attempt to find all block argument operands for every predecessor.
+      // If any operand to the block argument wasn't found in a predecessor,
+      // conservatively add the block argument to the result set.
+      SmallVector<Value> operands;
+      bool anyUnknown = false;
+      for (auto iter = parentBlock->pred_begin();
+           iter != parentBlock->pred_end(); iter++) {
+        auto branch = dyn_cast<BranchOpInterface>((*iter)->getTerminator());
+        if (!branch) {
+          result.push_back(blockArg);
+          anyUnknown = true;
+          break;
+        }
+
+        Value operand = branch.getSuccessorOperands(
+            iter.getSuccessorIndex())[blockArg.getArgNumber()];
+        if (!operand) {
+          result.push_back(blockArg);
+          anyUnknown = true;
+          break;
+        }
+
+        operands.push_back(operand);
+      }
+
+      if (!anyUnknown)
+        llvm::append_range(workList, operands);
+
+      continue;
+    }
+
+    result.push_back(current);
+  } while (!workList.empty());
+
+  return result;
+}
+
+/// Creates a new AliasScopeAttr for every noalias parameter and attaches it to
+/// the appropriate inlined memory operations in an attempt to preserve the
+/// original semantics of the parameter attribute.
+static void createNewAliasScopesFromNoAliasParameter(
+    Operation *call, iterator_range<Region::iterator> inlinedBlocks) {
+
+  // First collect all noalias parameters. These have been specially marked by
+  // the `handleArgument` implementation by using the `ssa.copy` intrinsic and
+  // attaching a `noalias` attribute to it.
+  // These are only meant to be temporary and should therefore be deleted after
+  // we're done using them here.
+  SetVector<LLVM::SSACopyOp> noAliasParams;
+  for (Value argument : cast<LLVM::CallOp>(call).getArgOperands()) {
+    for (Operation *user : argument.getUsers()) {
+      auto ssaCopy = llvm::dyn_cast<LLVM::SSACopyOp>(user);
+      if (!ssaCopy)
+        continue;
+      if (!ssaCopy->hasAttr(LLVM::LLVMDialect::getNoAliasAttrName()))
+        continue;
+
+      noAliasParams.insert(ssaCopy);
+    }
+  }
+
+  // If there were none, we have nothing to do here.
+  if (noAliasParams.empty())
+    return;
+
+  // Scope exit block to make it impossible to forget to get rid of the
+  // intrinsics.
+  auto exit = llvm::make_scope_exit([&] {
+    for (LLVM::SSACopyOp ssaCopyOp : noAliasParams) {
+      ssaCopyOp.replaceAllUsesWith(ssaCopyOp.getOperand());
+      ssaCopyOp->erase();
+    }
+  });
+
+  // Create a new domain for this specific inlining and a new scope for every
+  // noalias parameter.
+  auto functionDomain = LLVM::AliasScopeDomainAttr::get(
+      call->getContext(), cast<LLVM::CallOp>(call).getCalleeAttr().getAttr());
+  DenseMap<Value, LLVM::AliasScopeAttr> pointerScopes;
+  for (LLVM::SSACopyOp copyOp : noAliasParams) {
+    auto scope = LLVM::AliasScopeAttr::get(functionDomain);
+    pointerScopes[copyOp] = scope;
+
+    OpBuilder(call).create<LLVM::NoAliasScopeDeclOp>(call->getLoc(), scope);
+  }
+
+  // Go through every instruction and attempt to find which noalias parameters
+  // it is definitely based on and definitely not based on.
+  for (Block &inlinedBlock : inlinedBlocks) {
+    for (auto aliasInterface :
+         inlinedBlock.getOps<LLVM::AliasAnalysisOpInterface>()) {
+
+      // Collect the pointer arguments affected by the alias scopes.
+      SmallVector<Value> pointerArgs = aliasInterface.getAccessedOperands();
+
+      // Find the set of underlying pointers that this pointer is based on.
+      SmallPtrSet<Value, 4> basedOnPointers;
+      for (Value pointer : pointerArgs)
+        llvm::copy(getUnderlyingObjectSet(pointer),
+                   std::inserter(basedOnPointers, basedOnPointers.begin()));
+
+      bool aliasesOtherKnownObject = false;
+      // Go through the based on pointers and check that they are either:
+      // * Constants that can be ignored (undef, poison, null pointer).
+      // * Based on a noalias parameter.
+      // * Other pointers that we know can't alias with our noalias parameter.
+      //
+      // Any other value might be a pointer based on any noalias parameter that
+      // hasn't been identified. In that case conservatively don't add any
+      // scopes to this operation indicating either aliasing or not aliasing
+      // with any parameter.
+      if (llvm::any_of(basedOnPointers, [&](Value object) {
+            if (matchPattern(object, m_Constant()))
+              return false;
+
+            if (noAliasParams.contains(object.getDefiningOp<LLVM::SSACopyOp>()))
+              return false;
+
+            // TODO: This should include other arguments from the inlined
+            //       callable.
+            if (isa_and_nonnull<LLVM::AllocaOp, LLVM::AddressOfOp>(
+                    object.getDefiningOp())) {
+              aliasesOtherKnownObject = true;
+              return false;
+            }
+            return true;
+          }))
+        continue;
+
+      // Add all noalias parameter scopes to the noalias scope list that we are
+      // not based on.
+      SmallVector<Attribute> noAliasScopes;
+      for (LLVM::SSACopyOp noAlias : noAliasParams) {
+        if (basedOnPointers.contains(noAlias))
+          continue;
+
+        noAliasScopes.push_back(pointerScopes[noAlias]);
+      }
+
+      if (!noAliasScopes.empty())
+        aliasInterface.setNoAliasScopes(
+            concatArrayAttr(aliasInterface.getNoAliasScopesOrNull(),
+                            ArrayAttr::get(call->getContext(), noAliasScopes)));
+
+      // Don't add alias scopes to call operations or operations that might
+      // operate on pointers not based on any noalias parameter.
+      // Since we add all scopes to an operation's noalias list that it
+      // definitely doesn't alias, we mustn't do the same for the alias.scope
+      // list if other objects are involved.
+      //
+      // Consider the following case:
+      // %0 = llvm.alloca
+      // %1 = select %magic, %0, %noalias_param
+      // store 5, %1  (1) noalias=[scope(...)]
+      // ...
+      // store 3, %0  (2) noalias=[scope(noalias_param), scope(...)]
+      //
+      // We can add the scopes of any noalias parameters that aren't
+      // noalias_param's scope to (1) and add all of them to (2). We mustn't add
+      // the scope of noalias_param to the alias.scope list of (1) since
+      // that would mean (2) cannot alias with (1) which is wrong since both may
+      // store to %0.
+      //
+      // In conclusion, only add scopes to the alias.scope list if all pointers
+      // have a corresponding scope.
+      // Call operations are included in this list since we do not know whether
+      // the callee accesses any memory besides the ones passed as its
+      // arguments.
+      if (aliasesOtherKnownObject ||
+          isa<LLVM::CallOp>(aliasInterface.getOperation()))
+        continue;
+
+      SmallVector<Attribute> aliasScopes;
+      for (LLVM::SSACopyOp noAlias : noAliasParams)
+        if (basedOnPointers.contains(noAlias))
+          aliasScopes.push_back(pointerScopes[noAlias]);
+
+      if (!aliasScopes.empty())
+        aliasInterface.setAliasScopes(
+            concatArrayAttr(aliasInterface.getAliasScopesOrNull(),
+                            ArrayAttr::get(call->getContext(), aliasScopes)));
+    }
+  }
+}
+
 /// Appends any alias scopes of the call operation to any inlined memory
 /// operation.
 static void
@@ -235,6 +468,7 @@ appendCallOpAliasScopes(Operation *call,
 static void handleAliasScopes(Operation *call,
                               iterator_range<Region::iterator> inlinedBlocks) {
   deepCloneAliasScopes(inlinedBlocks);
+  createNewAliasScopesFromNoAliasParameter(call, inlinedBlocks);
   appendCallOpAliasScopes(call, inlinedBlocks);
 }
 
@@ -468,6 +702,7 @@ struct LLVMInlinerInterface : public DialectInlinerInterface {
             LLVM::LifetimeStartOp,
             LLVM::LoadOp,
             LLVM::MemcpyOp,
+            LLVM::MemcpyInlineOp,
             LLVM::MemmoveOp,
             LLVM::MemsetOp,
             LLVM::NoAliasScopeDeclOp,
@@ -528,6 +763,29 @@ struct LLVMInlinerInterface : public DialectInlinerInterface {
       return handleByValArgument(builder, callable, argument, elementType,
                                  requestedAlignment);
     }
+    if (std::optional<NamedAttribute> attr =
+            argumentAttrs.getNamed(LLVM::LLVMDialect::getNoAliasAttrName())) {
+      if (argument.use_empty())
+        return argument;
+
+      // This code is essentially a workaround for deficiencies in the
+      // inliner interface: We need to transform operations *after* inlined
+      // based on the argument attributes of the parameters *before* inlining.
+      // This method runs prior to actual inlining and thus cannot transform the
+      // post-inlining code, while `processInlinedCallBlocks` does not have
+      // access to pre-inlining function arguments. Additionally, it is required
+      // to distinguish which parameter an SSA value originally came from.
+      // As a workaround until this is changed: Create an ssa.copy intrinsic
+      // with the noalias attribute that can easily be found, and is extremely
+      // unlikely to exist in the code prior to inlining, using this to
+      // communicate between this method and `processInlinedCallBlocks`.
+      // TODO: Fix this by refactoring the inliner interface.
+      auto copyOp = builder.create<LLVM::SSACopyOp>(call->getLoc(), argument);
+      copyOp->setDiscardableAttr(
+          builder.getStringAttr(LLVM::LLVMDialect::getNoAliasAttrName()),
+          builder.getUnitAttr());
+      return copyOp;
+    }
     return argument;
   }
 

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
index a61326059ae05f..cff16afc73af3f 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
@@ -62,4 +62,43 @@ mlir::LLVM::detail::verifyAliasAnalysisOpInterface(Operation *op) {
   return isArrayOf<TBAATagAttr>(op, tags);
 }
 
+SmallVector<Value> mlir::LLVM::AtomicCmpXchgOp::getAccessedOperands() {
+  return {getPtr()};
+}
+
+SmallVector<Value> mlir::LLVM::AtomicRMWOp::getAccessedOperands() {
+  return {getPtr()};
+}
+
+SmallVector<Value> mlir::LLVM::LoadOp::getAccessedOperands() {
+  return {getAddr()};
+}
+
+SmallVector<Value> mlir::LLVM::StoreOp::getAccessedOperands() {
+  return {getAddr()};
+}
+
+SmallVector<Value> mlir::LLVM::MemcpyOp::getAccessedOperands() {
+  return {getDst(), getSrc()};
+}
+
+SmallVector<Value> mlir::LLVM::MemcpyInlineOp::getAccessedOperands() {
+  return {getDst(), getSrc()};
+}
+
+SmallVector<Value> mlir::LLVM::MemmoveOp::getAccessedOperands() {
+  return {getDst(), getSrc()};
+}
+
+SmallVector<Value> mlir::LLVM::MemsetOp::getAccessedOperands() {
+  return {getDst()};
+}
+
+SmallVector<Value> mlir::LLVM::CallOp::getAccessedOperands() {
+  return llvm::to_vector(
+      llvm::make_filter_range(getArgOperands(), [](Value arg) {
+        return isa<LLVMPointerType>(arg.getType());
+      }));
+}
+
 #include "mlir/Dialect/LLVMIR/LLVMInterfaces.cpp.inc"

diff  --git a/mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir b/mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir
index dd408048372d55..29450833bee598 100644
--- a/mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir
+++ b/mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir
@@ -195,3 +195,202 @@ llvm.func @caller(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
   llvm.call @callee_without_metadata(%arg1, %arg1, %arg0) {alias_scopes = [#alias_scope2]} : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> ()
   llvm.return
 }
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @foo(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i64
+  %1 = llvm.load %arg1 {alignment = 4 : i64} : !llvm.ptr -> f32
+  %2 = llvm.getelementptr inbounds %arg0[%0] : (!llvm.ptr, i64) -> !llvm.ptr, f32
+  llvm.store %1, %2 {alignment = 4 : i64} : f32, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.load
+// CHECK-SAME: alias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK: llvm.store
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @foo(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @might_return_arg_derived(!llvm.ptr) -> !llvm.ptr
+
+llvm.func @foo(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i32
+  %1 = llvm.call @might_return_arg_derived(%arg0) : (!llvm.ptr) -> !llvm.ptr
+  llvm.store %0, %1 : i32, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.call
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK: llvm.store
+// CHECK-NOT: alias_scopes
+// CHECK-NOT: noalias_scopes
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @foo(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @random() -> i1
+
+llvm.func @block_arg(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i32
+  %1 = llvm.call @random() : () -> i1
+  llvm.cond_br %1, ^bb0(%arg0 : !llvm.ptr), ^bb0(%arg1 : !llvm.ptr)
+
+^bb0(%arg2: !llvm.ptr):
+  llvm.store %0, %arg2 : i32, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.call
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK: llvm.store
+// CHECK: alias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @block_arg(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @random() -> i1
+
+llvm.func @block_arg(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i32
+  %1 = llvm.mlir.constant(1 : i64) : i64
+  %2 = llvm.alloca %1 x i32 : (i64) -> !llvm.ptr
+  %3 = llvm.call @random() : () -> i1
+  llvm.cond_br %3, ^bb0(%arg0 : !llvm.ptr), ^bb0(%2 : !llvm.ptr)
+
+^bb0(%arg2: !llvm.ptr):
+  llvm.store %0, %arg2 : i32, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.call
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK: llvm.store
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @block_arg(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @unknown() -> !llvm.ptr
+llvm.func @random() -> i1
+
+llvm.func @unknown_object(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i32
+  %1 = llvm.call @random() : () -> i1
+  %2 = llvm.call @unknown() : () -> !llvm.ptr
+  llvm.cond_br %1, ^bb0(%arg0 : !llvm.ptr), ^bb0(%2 : !llvm.ptr)
+
+^bb0(%arg2: !llvm.ptr):
+  llvm.store %0, %arg2 : i32, !llvm.ptr
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.call
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK: llvm.call
+// CHECK-NOT: alias_scopes
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK: llvm.store
+// CHECK-NOT: alias_scopes
+// CHECK-NOT: noalias_scopes
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @unknown_object(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}
+
+// -----
+
+// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
+// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
+
+llvm.func @supported_operations(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr {llvm.noalias}) {
+  %0 = llvm.mlir.constant(5 : i64) : i32
+  llvm.store %0, %arg1 : i32, !llvm.ptr
+  %1 = llvm.load %arg1 : !llvm.ptr -> i32
+  "llvm.intr.memcpy"(%arg0, %arg1, %1) <{ isVolatile = false }> : (!llvm.ptr, !llvm.ptr, i32) -> ()
+  "llvm.intr.memmove"(%arg0, %arg1, %1) <{ isVolatile = false }> : (!llvm.ptr, !llvm.ptr, i32) -> ()
+  "llvm.intr.memcpy.inline"(%arg0, %arg1) <{ isVolatile = false, len = 4 : i32}> : (!llvm.ptr, !llvm.ptr) -> ()
+  %2 = llvm.trunc %0 : i32 to i8
+  "llvm.intr.memset"(%arg0, %2, %1) <{ isVolatile = false}> : (!llvm.ptr, i8, i32) -> ()
+  %3 = llvm.cmpxchg %arg0, %0, %1 seq_cst seq_cst : !llvm.ptr, i32
+  %4 = llvm.atomicrmw add %arg0, %0 seq_cst : !llvm.ptr, i32
+  llvm.return
+}
+
+// CHECK-LABEL: llvm.func @bar
+// CHECK: llvm.store
+// CHECK-SAME: alias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK: llvm.load
+// CHECK-SAME: alias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK: "llvm.intr.memcpy"
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK-NOT: noalias_scopes
+// CHECK: "llvm.intr.memmove"
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK-NOT: noalias_scopes
+// CHECK: "llvm.intr.memcpy.inline"
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]], #[[$ARG1_SCOPE]]]
+// CHECK-NOT: noalias_scopes
+// CHECK: "llvm.intr.memset"
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK: llvm.cmpxchg
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+// CHECK: llvm.atomicrmw
+// CHECK-SAME: alias_scopes = [#[[$ARG0_SCOPE]]]
+// CHECK-SAME: noalias_scopes = [#[[$ARG1_SCOPE]]]
+llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
+  llvm.call @supported_operations(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
+  llvm.return
+}

diff  --git a/mlir/test/Dialect/LLVMIR/inlining.mlir b/mlir/test/Dialect/LLVMIR/inlining.mlir
index b22bfb4f493b2b..b59f141e7acd29 100644
--- a/mlir/test/Dialect/LLVMIR/inlining.mlir
+++ b/mlir/test/Dialect/LLVMIR/inlining.mlir
@@ -524,7 +524,7 @@ llvm.func @test_byval_global() {
 
 // -----
 
-llvm.func @ignored_attrs(%ptr : !llvm.ptr { llvm.inreg, llvm.noalias, llvm.nocapture, llvm.nofree, llvm.preallocated = i32, llvm.returned, llvm.alignstack = 32 : i64, llvm.writeonly, llvm.noundef, llvm.nonnull }, %x : i32 { llvm.zeroext }) -> (!llvm.ptr { llvm.noundef, llvm.inreg, llvm.nonnull }) {
+llvm.func @ignored_attrs(%ptr : !llvm.ptr { llvm.inreg, llvm.nocapture, llvm.nofree, llvm.preallocated = i32, llvm.returned, llvm.alignstack = 32 : i64, llvm.writeonly, llvm.noundef, llvm.nonnull }, %x : i32 { llvm.zeroext }) -> (!llvm.ptr { llvm.noundef, llvm.inreg, llvm.nonnull }) {
   llvm.return %ptr : !llvm.ptr
 }
 


        


More information about the Mlir-commits mailing list