diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td @@ -167,6 +167,31 @@ >]; } +def LLVM_NoAliasScopeDeclOp + : LLVM_ZeroResultIntrOp<"experimental.noalias.scope.decl"> { + let arguments = (ins SymbolRefAttr:$scope); + string llvmBuilder = [{ + // Wrap the scope argument into a list since the LLVM IR intrinsic takes + // a list containing exactly one scope rather than a scope itself. + llvm::MDNode* node = moduleTranslation.getAliasScopes(op, {$scope}); + builder.CreateNoAliasScopeDeclaration(node); + }]; + string mlirBuilder = [{ + FailureOr> scopeAttrs = + moduleImport.matchAliasScopeAttrs(llvmOperands[0]); + // Drop the intrinsic if the alias scope translation fails since the scope + // is not used by an aliasing operation, such as a load or store, that is + // used to convert the alias scope metadata. + if (failed(scopeAttrs)) + return success(); + if (scopeAttrs->size() != 1) + return failure(); + $_op = $_builder.create( + $_location, (*scopeAttrs)[0]); + }]; + let assemblyFormat = "$scope attr-dict"; +} + //===----------------------------------------------------------------------===// // Lifetime Markers //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h --- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h @@ -135,6 +135,11 @@ /// fails. DILocalVariableAttr matchLocalVariableAttr(llvm::Value *value); + /// Converts `value` to an array of symbol references pointing to alias scope + /// operations, or returns failure if the conversion fails. + FailureOr> + matchAliasScopeAttrs(llvm::Value *value); + /// Translates the debug location. Location translateLoc(llvm::DILocation *loc); diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -122,9 +122,14 @@ void forgetMapping(Region ®ion); /// Returns the LLVM metadata corresponding to a symbol reference to an mlir - /// LLVM dialect alias scope operation + /// LLVM dialect alias scope operation. llvm::MDNode *getAliasScope(Operation *op, SymbolRefAttr aliasScopeRef) const; + /// Returns the LLVM metadata corresponding to an array of symbol references + /// to mlir LLVM dialect alias scope operations. + llvm::MDNode *getAliasScopes(Operation *op, + ArrayRef aliasScopeRefs) const; + // Sets LLVM metadata for memory operations that are in a parallel loop. void setAccessGroupsMetadata(AccessGroupOpInterface op, llvm::Instruction *inst); diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -1215,6 +1215,13 @@ return debugImporter->translate(node); } +FailureOr> +ModuleImport::matchAliasScopeAttrs(llvm::Value *value) { + auto *nodeAsVal = cast(value); + auto *node = cast(nodeAsVal->getMetadata()); + return lookupAliasScopeAttrs(node); +} + Location ModuleImport::translateLoc(llvm::DILocation *loc) { return debugImporter->translateLoc(loc); } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1065,16 +1065,22 @@ return aliasScopeMetadataMapping.lookup(aliasScopeOp); } +llvm::MDNode *ModuleTranslation::getAliasScopes( + Operation *op, ArrayRef aliasScopeRefs) const { + SmallVector nodes; + nodes.reserve(aliasScopeRefs.size()); + for (SymbolRefAttr aliasScopeRef : aliasScopeRefs) + nodes.push_back(getAliasScope(op, aliasScopeRef)); + return llvm::MDNode::get(getLLVMContext(), nodes); +} + void ModuleTranslation::setAliasScopeMetadata(AliasAnalysisOpInterface op, llvm::Instruction *inst) { - auto populateScopeMetadata = [&](ArrayAttr scopeRefs, unsigned kind) { - if (!scopeRefs || scopeRefs.empty()) + auto populateScopeMetadata = [&](ArrayAttr aliasScopeRefs, unsigned kind) { + if (!aliasScopeRefs || aliasScopeRefs.empty()) return; - llvm::Module *module = inst->getModule(); - SmallVector scopeMDs; - for (SymbolRefAttr scopeRef : scopeRefs.getAsRange()) - scopeMDs.push_back(getAliasScope(op, scopeRef)); - llvm::MDNode *node = llvm::MDNode::get(module->getContext(), scopeMDs); + llvm::MDNode *node = getAliasScopes( + op, llvm::to_vector(aliasScopeRefs.getAsRange())); inst->setMetadata(kind, node); }; diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -596,3 +596,15 @@ llvm.intr.stackrestore %arg0 : !llvm.ptr llvm.return } + +// CHECK-LABEL: @experimental_noalias_scope_decl +llvm.func @experimental_noalias_scope_decl() { + // CHECK: llvm.intr.experimental.noalias.scope.decl @metadata::@scope + llvm.intr.experimental.noalias.scope.decl @metadata::@scope + llvm.return +} + +llvm.metadata @metadata { + llvm.alias_scope_domain @domain {description = "The domain"} + llvm.alias_scope @scope {domain = @domain, description = "The first scope"} +} diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll --- a/mlir/test/Target/LLVMIR/Import/import-failure.ll +++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll @@ -322,3 +322,18 @@ } !0 = !{!"function_entry_count", i64 42} + +; // ----- + +; CHECK: import-failure.ll +; CHECK-SAME: warning: dropped instruction: call void @llvm.experimental.noalias.scope.decl(metadata !0) +define void @unused_scope() { + call void @llvm.experimental.noalias.scope.decl(metadata !0) + ret void +} + +declare void @llvm.experimental.noalias.scope.decl(metadata) + +!0 = !{!1} +!1 = !{!1, !2} +!2 = distinct !{!2, !"The domain"} diff --git a/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll b/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll --- a/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll +++ b/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll @@ -62,27 +62,34 @@ ; // ----- +; CHECK: llvm.metadata @__llvm_global_metadata { +; CHECK: llvm.alias_scope_domain @[[DOMAIN:.*]] {description = "The domain"} +; CHECK: llvm.alias_scope @[[$SCOPE:.*]] {domain = @[[DOMAIN]]} +; CHECK: } + ; CHECK-LABEL: llvm.func @supported_ops define void @supported_ops(ptr %arg1, float %arg2, i32 %arg3, i32 %arg4) { - ; CHECK: llvm.load {{.*}}alias_scopes = - %1 = load i32, ptr %arg1, !alias.scope !3 - ; CHECK: llvm.store {{.*}}alias_scopes = - store i32 %1, ptr %arg1, !alias.scope !3 - ; CHECK: llvm.atomicrmw {{.*}}alias_scopes = - %2 = atomicrmw fmax ptr %arg1, float %arg2 acquire, !alias.scope !3 - ; CHECK: llvm.cmpxchg {{.*}}alias_scopes = - %3 = cmpxchg ptr %arg1, i32 %arg3, i32 %arg4 monotonic seq_cst, !alias.scope !3 - ; CHECK: "llvm.intr.memcpy"{{.*}}alias_scopes = - call void @llvm.memcpy.p0.p0.i32(ptr %arg1, ptr %arg1, i32 4, i1 false), !alias.scope !3 - ; CHECK: "llvm.intr.memset"{{.*}}alias_scopes = - call void @llvm.memset.p0.i32(ptr %arg1, i8 42, i32 4, i1 false), !alias.scope !3 + ; CHECK: llvm.intr.experimental.noalias.scope.decl @__llvm_global_metadata::@[[$SCOPE]] + call void @llvm.experimental.noalias.scope.decl(metadata !2) + ; CHECK: llvm.load {{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + %1 = load i32, ptr %arg1, !alias.scope !2 + ; CHECK: llvm.store {{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + store i32 %1, ptr %arg1, !alias.scope !2 + ; CHECK: llvm.atomicrmw {{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + %2 = atomicrmw fmax ptr %arg1, float %arg2 acquire, !alias.scope !2 + ; CHECK: llvm.cmpxchg {{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + %3 = cmpxchg ptr %arg1, i32 %arg3, i32 %arg4 monotonic seq_cst, !alias.scope !2 + ; CHECK: "llvm.intr.memcpy"{{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + call void @llvm.memcpy.p0.p0.i32(ptr %arg1, ptr %arg1, i32 4, i1 false), !alias.scope !2 + ; CHECK: "llvm.intr.memset"{{.*}}alias_scopes = [@__llvm_global_metadata::@[[$SCOPE]]] + call void @llvm.memset.p0.i32(ptr %arg1, i8 42, i32 4, i1 false), !alias.scope !2 ret void } +declare void @llvm.experimental.noalias.scope.decl(metadata) declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) !0 = distinct !{!0, !"The domain"} -!1 = distinct !{!1} -!2 = !{!2, !0} -!3 = !{!2} +!1 = !{!1, !0} +!2 = !{!1} diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -2016,38 +2016,41 @@ // ----- -module { - llvm.func @aliasScope(%arg1 : !llvm.ptr) { - %0 = llvm.mlir.constant(0 : i32) : i32 - llvm.store %0, %arg1 {alias_scopes = [@metadata::@scope1], noalias_scopes = [@metadata::@scope2, @metadata::@scope3]} : i32, !llvm.ptr - %1 = llvm.load %arg1 {alias_scopes = [@metadata::@scope2], noalias_scopes = [@metadata::@scope1, @metadata::@scope3]} : !llvm.ptr -> i32 - %2 = llvm.atomicrmw add %arg1, %0 monotonic {alias_scopes = [@metadata::@scope3], noalias_scopes = [@metadata::@scope1, @metadata::@scope2]} : !llvm.ptr, i32 - %3 = llvm.cmpxchg %arg1, %1, %2 acq_rel monotonic {alias_scopes = [@metadata::@scope3]} : !llvm.ptr, i32 - %4 = llvm.mlir.constant(0 : i1) : i1 - %5 = llvm.mlir.constant(42 : i8) : i8 - "llvm.intr.memcpy"(%arg1, %arg1, %0, %4) {alias_scopes = [@metadata::@scope3]} : (!llvm.ptr, !llvm.ptr, i32, i1) -> () - "llvm.intr.memset"(%arg1, %5, %0, %4) {noalias_scopes = [@metadata::@scope3]} : (!llvm.ptr, i8, i32, i1) -> () - llvm.return - } - - llvm.metadata @metadata { - llvm.alias_scope_domain @domain {description = "The domain"} - llvm.alias_scope @scope1 {domain = @domain, description = "The first scope"} - llvm.alias_scope @scope2 {domain = @domain} - llvm.alias_scope @scope3 {domain = @domain} - } -} - -// Function // CHECK-LABEL: aliasScope -// CHECK: store {{.*}}, !alias.scope ![[SCOPES1:[0-9]+]], !noalias ![[SCOPES23:[0-9]+]] -// CHECK: load {{.*}}, !alias.scope ![[SCOPES2:[0-9]+]], !noalias ![[SCOPES13:[0-9]+]] -// CHECK: atomicrmw {{.*}}, !alias.scope ![[SCOPES3:[0-9]+]], !noalias ![[SCOPES12:[0-9]+]] -// CHECK: cmpxchg {{.*}}, !alias.scope ![[SCOPES3]] -// CHECK: llvm.memcpy{{.*}}, !alias.scope ![[SCOPES3]] -// CHECK: llvm.memset{{.*}}, !noalias ![[SCOPES3]] - -// Metadata +llvm.func @aliasScope(%arg1 : !llvm.ptr) { + %0 = llvm.mlir.constant(0 : i32) : i32 + // CHECK: call void @llvm.experimental.noalias.scope.decl(metadata ![[SCOPES1:[0-9]+]]) + llvm.intr.experimental.noalias.scope.decl @metadata::@scope1 + // CHECK: store {{.*}}, !alias.scope ![[SCOPES1]], !noalias ![[SCOPES23:[0-9]+]] + llvm.store %0, %arg1 {alias_scopes = [@metadata::@scope1], noalias_scopes = [@metadata::@scope2, @metadata::@scope3]} : i32, !llvm.ptr + // CHECK: load {{.*}}, !alias.scope ![[SCOPES2:[0-9]+]], !noalias ![[SCOPES13:[0-9]+]] + %1 = llvm.load %arg1 {alias_scopes = [@metadata::@scope2], noalias_scopes = [@metadata::@scope1, @metadata::@scope3]} : !llvm.ptr -> i32 + // CHECK: atomicrmw {{.*}}, !alias.scope ![[SCOPES3:[0-9]+]], !noalias ![[SCOPES12:[0-9]+]] + %2 = llvm.atomicrmw add %arg1, %0 monotonic {alias_scopes = [@metadata::@scope3], noalias_scopes = [@metadata::@scope1, @metadata::@scope2]} : !llvm.ptr, i32 + // CHECK: cmpxchg {{.*}}, !alias.scope ![[SCOPES3]] + %3 = llvm.cmpxchg %arg1, %1, %2 acq_rel monotonic {alias_scopes = [@metadata::@scope3]} : !llvm.ptr, i32 + %4 = llvm.mlir.constant(0 : i1) : i1 + %5 = llvm.mlir.constant(42 : i8) : i8 + // CHECK: llvm.memcpy{{.*}}, !alias.scope ![[SCOPES3]] + "llvm.intr.memcpy"(%arg1, %arg1, %0, %4) {alias_scopes = [@metadata::@scope3]} : (!llvm.ptr, !llvm.ptr, i32, i1) -> () + // CHECK: llvm.memset{{.*}}, !noalias ![[SCOPES3]] + "llvm.intr.memset"(%arg1, %5, %0, %4) {noalias_scopes = [@metadata::@scope3]} : (!llvm.ptr, i8, i32, i1) -> () + llvm.return +} + +llvm.metadata @metadata { + llvm.alias_scope_domain @domain {description = "The domain"} + llvm.alias_scope @scope1 {domain = @domain, description = "The first scope"} + llvm.alias_scope @scope2 {domain = @domain} + llvm.alias_scope @scope3 {domain = @domain} +} + +// Check the intrinsic declarations. +// CHECK-DAG: declare void @llvm.experimental.noalias.scope.decl(metadata) +// CHECK-DAG: declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) +// CHECK-DAG: declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) + +// Check the translated metadata. // CHECK-DAG: ![[DOMAIN:[0-9]+]] = distinct !{![[DOMAIN]], !"The domain"} // CHECK-DAG: ![[SCOPE1:[0-9]+]] = distinct !{![[SCOPE1]], ![[DOMAIN]], !"The first scope"} // CHECK-DAG: ![[SCOPE2:[0-9]+]] = distinct !{![[SCOPE2]], ![[DOMAIN]]} @@ -2059,7 +2062,6 @@ // CHECK-DAG: ![[SCOPES13]] = !{![[SCOPE1]], ![[SCOPE3]]} // CHECK-DAG: ![[SCOPES23]] = !{![[SCOPE2]], ![[SCOPE3]]} - // ----- // It is okay to have repeated successors if they have no arguments.