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 @@ -329,7 +329,7 @@ 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({$scope}); + llvm::MDNode* node = moduleTranslation.getOrCreateAliasScopes({$scope}); builder.CreateNoAliasScopeDeclaration(node); }]; string mlirBuilder = [{ 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 @@ -44,6 +44,8 @@ class LoopAnnotationTranslation; } // namespace detail +class AliasScopeAttr; +class AliasScopeDomainAttr; class DINodeAttr; class LLVMFuncOp; class ComdatSelectorOp; @@ -137,12 +139,14 @@ void forgetMapping(Region ®ion); /// Returns the LLVM metadata corresponding to a mlir LLVM dialect alias scope - /// attribute. - llvm::MDNode *getAliasScope(AliasScopeAttr aliasScopeAttr) const; + /// attribute. Creates the metadata node if it has not been converted before. + llvm::MDNode *getOrCreateAliasScope(AliasScopeAttr aliasScopeAttr); /// Returns the LLVM metadata corresponding to an array of mlir LLVM dialect - /// alias scope attributes. - llvm::MDNode *getAliasScopes(ArrayRef aliasScopeAttrs) const; + /// alias scope attributes. Creates the metadata nodes if they have not been + /// converted before. + llvm::MDNode * + getOrCreateAliasScopes(ArrayRef aliasScopeAttrs); // Sets LLVM metadata for memory operations that are in a parallel loop. void setAccessGroupsMetadata(AccessGroupOpInterface op, @@ -295,10 +299,6 @@ LogicalResult convertGlobals(); LogicalResult convertOneFunction(LLVMFuncOp func); - /// Process alias.scope LLVM Metadata operations and create LLVM - /// metadata nodes for them and their domains. - LogicalResult createAliasScopeMetadata(); - /// Returns the LLVM metadata corresponding to the given mlir LLVM dialect /// TBAATagAttr. llvm::MDNode *getTBAANode(TBAATagAttr tbaaAttr) const; @@ -350,9 +350,13 @@ /// have been converted. DenseMap callMapping; - /// Mapping from an alias scope metadata operation to its LLVM metadata. - /// This map is populated on module entry. - DenseMap aliasScopeMetadataMapping; + /// Mapping from an alias scope attribute to its LLVM metadata. + /// This map is populated lazily. + DenseMap aliasScopeMetadataMapping; + + /// Mapping from an alias scope domain attribute to its LLVM metadata. + /// This map is populated lazily. + DenseMap aliasDomainMetadataMapping; /// Mapping from a tbaa attribute to its LLVM metadata. /// This map is populated on module entry. 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 @@ -1100,57 +1100,46 @@ inst->setMetadata(llvm::LLVMContext::MD_access_group, node); } -LogicalResult ModuleTranslation::createAliasScopeMetadata() { - DenseMap aliasScopeDomainMetadataMapping; - - AttrTypeWalker walker; - walker.addWalk([&](LLVM::AliasScopeDomainAttr op) { - llvm::LLVMContext &ctx = llvmModule->getContext(); +llvm::MDNode * +ModuleTranslation::getOrCreateAliasScope(AliasScopeAttr aliasScopeAttr) { + auto [scopeIt, scopeInserted] = + aliasScopeMetadataMapping.try_emplace(aliasScopeAttr, nullptr); + if (!scopeInserted) + return scopeIt->second; + llvm::LLVMContext &ctx = llvmModule->getContext(); + // Convert the domain metadata node if necessary. + auto [domainIt, insertedDomain] = aliasDomainMetadataMapping.try_emplace( + aliasScopeAttr.getDomain(), nullptr); + if (insertedDomain) { llvm::SmallVector operands; - operands.push_back({}); // Placeholder for self-reference - if (StringAttr description = op.getDescription()) + // Placeholder for self-reference. + operands.push_back({}); + if (StringAttr description = aliasScopeAttr.getDomain().getDescription()) operands.push_back(llvm::MDString::get(ctx, description)); - llvm::MDNode *domain = llvm::MDNode::get(ctx, operands); - domain->replaceOperandWith(0, domain); // Self-reference for uniqueness - aliasScopeDomainMetadataMapping.insert({op, domain}); - }); - - walker.addWalk([&](LLVM::AliasScopeAttr op) { - llvm::LLVMContext &ctx = llvmModule->getContext(); - llvm::MDNode *domain = - aliasScopeDomainMetadataMapping.lookup(op.getDomain()); - assert(domain && "Scope's domain should already be valid"); - llvm::SmallVector operands; - operands.push_back({}); // Placeholder for self-reference - operands.push_back(domain); - if (StringAttr description = op.getDescription()) - operands.push_back(llvm::MDString::get(ctx, description)); - llvm::MDNode *scope = llvm::MDNode::get(ctx, operands); - scope->replaceOperandWith(0, scope); // Self-reference for uniqueness - aliasScopeMetadataMapping.insert({op, scope}); - }); - - mlirModule->walk([&](AliasAnalysisOpInterface op) { - if (auto aliasScopes = op.getAliasScopesOrNull()) - walker.walk(aliasScopes); - if (auto noAliasScopes = op.getNoAliasScopesOrNull()) - walker.walk(noAliasScopes); - }); - - return success(); -} - -llvm::MDNode * -ModuleTranslation::getAliasScope(AliasScopeAttr aliasScopeAttr) const { - return aliasScopeMetadataMapping.lookup(aliasScopeAttr); + domainIt->second = llvm::MDNode::get(ctx, operands); + // Self-reference for uniqueness. + domainIt->second->replaceOperandWith(0, domainIt->second); + } + // Convert the scope metadata node. + assert(domainIt->second && "Scope's domain should already be valid"); + llvm::SmallVector operands; + // Placeholder for self-reference. + operands.push_back({}); + operands.push_back(domainIt->second); + if (StringAttr description = aliasScopeAttr.getDescription()) + operands.push_back(llvm::MDString::get(ctx, description)); + scopeIt->second = llvm::MDNode::get(ctx, operands); + // Self-reference for uniqueness. + scopeIt->second->replaceOperandWith(0, scopeIt->second); + return scopeIt->second; } -llvm::MDNode *ModuleTranslation::getAliasScopes( - ArrayRef aliasScopeAttrs) const { +llvm::MDNode *ModuleTranslation::getOrCreateAliasScopes( + ArrayRef aliasScopeAttrs) { SmallVector nodes; nodes.reserve(aliasScopeAttrs.size()); for (AliasScopeAttr aliasScopeAttr : aliasScopeAttrs) - nodes.push_back(getAliasScope(aliasScopeAttr)); + nodes.push_back(getOrCreateAliasScope(aliasScopeAttr)); return llvm::MDNode::get(getLLVMContext(), nodes); } @@ -1159,7 +1148,7 @@ auto populateScopeMetadata = [&](ArrayAttr aliasScopeAttrs, unsigned kind) { if (!aliasScopeAttrs || aliasScopeAttrs.empty()) return; - llvm::MDNode *node = getAliasScopes( + llvm::MDNode *node = getOrCreateAliasScopes( llvm::to_vector(aliasScopeAttrs.getAsRange())); inst->setMetadata(kind, node); }; @@ -1394,8 +1383,6 @@ return nullptr; if (failed(translator.convertGlobals())) return nullptr; - if (failed(translator.createAliasScopeMetadata())) - return nullptr; if (failed(translator.createTBAAMetadata())) return nullptr; diff --git a/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir b/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir @@ -0,0 +1,72 @@ +// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s + +llvm.func @foo(%arg0: !llvm.ptr) + +#alias_scope_domain = #llvm.alias_scope_domain, description = "The domain"> +#alias_scope1 = #llvm.alias_scope, domain = #alias_scope_domain, description = "The first scope"> +#alias_scope2 = #llvm.alias_scope, domain = #alias_scope_domain> +#alias_scope3 = #llvm.alias_scope, domain = #alias_scope_domain> + +// CHECK-LABEL: @alias_scopes +llvm.func @alias_scopes(%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 #alias_scope1 + // CHECK: store {{.*}}, !alias.scope ![[SCOPES1]], !noalias ![[SCOPES23:[0-9]+]] + llvm.store %0, %arg1 {alias_scopes = [#alias_scope1], noalias_scopes = [#alias_scope2, #alias_scope3]} : i32, !llvm.ptr + // CHECK: load {{.*}}, !alias.scope ![[SCOPES2:[0-9]+]], !noalias ![[SCOPES13:[0-9]+]] + %1 = llvm.load %arg1 {alias_scopes = [#alias_scope2], noalias_scopes = [#alias_scope1, #alias_scope3]} : !llvm.ptr -> i32 + // CHECK: atomicrmw {{.*}}, !alias.scope ![[SCOPES3:[0-9]+]], !noalias ![[SCOPES12:[0-9]+]] + %2 = llvm.atomicrmw add %arg1, %0 monotonic {alias_scopes = [#alias_scope3], noalias_scopes = [#alias_scope1, #alias_scope2]} : !llvm.ptr, i32 + // CHECK: cmpxchg {{.*}}, !alias.scope ![[SCOPES3]] + %3 = llvm.cmpxchg %arg1, %1, %2 acq_rel monotonic {alias_scopes = [#alias_scope3]} : !llvm.ptr, i32 + %5 = llvm.mlir.constant(42 : i8) : i8 + // CHECK: llvm.memcpy{{.*}}, !alias.scope ![[SCOPES3]] + "llvm.intr.memcpy"(%arg1, %arg1, %0) <{isVolatile = false}> {alias_scopes = [#alias_scope3]} : (!llvm.ptr, !llvm.ptr, i32) -> () + // CHECK: llvm.memset{{.*}}, !noalias ![[SCOPES3]] + "llvm.intr.memset"(%arg1, %5, %0) <{isVolatile = false}> {noalias_scopes = [#alias_scope3]} : (!llvm.ptr, i8, i32) -> () + // CHECK: call void @foo({{.*}} !alias.scope ![[SCOPES3]] + llvm.call @foo(%arg1) {alias_scopes = [#alias_scope3]} : (!llvm.ptr) -> () + // CHECK: call void @foo({{.*}} !noalias ![[SCOPES3]] + llvm.call @foo(%arg1) {noalias_scopes = [#alias_scope3]} : (!llvm.ptr) -> () + llvm.return +} + +// 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]]} +// CHECK-DAG: ![[SCOPE3:[0-9]+]] = distinct !{![[SCOPE3]], ![[DOMAIN]]} +// CHECK-DAG: ![[SCOPES1]] = !{![[SCOPE1]]} +// CHECK-DAG: ![[SCOPES2]] = !{![[SCOPE2]]} +// CHECK-DAG: ![[SCOPES3]] = !{![[SCOPE3]]} +// CHECK-DAG: ![[SCOPES12]] = !{![[SCOPE1]], ![[SCOPE2]]} +// CHECK-DAG: ![[SCOPES13]] = !{![[SCOPE1]], ![[SCOPE3]]} +// CHECK-DAG: ![[SCOPES23]] = !{![[SCOPE2]], ![[SCOPE3]]} + +// ----- + +// This test verifies the noalias scope intrinsice can be translated in +// isolation. It is the only operation using alias scopes attributes without +// implementing AliasAnalysisOpInterface. + +#alias_scope_domain = #llvm.alias_scope_domain, description = "The domain"> +#alias_scope1 = #llvm.alias_scope, domain = #alias_scope_domain> + +// CHECK-LABEL: @noalias_intr_only +llvm.func @noalias_intr_only(%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 #alias_scope1 + llvm.return +} + +// Check the translated metadata. +// CHECK-DAG: ![[DOMAIN:[0-9]+]] = distinct !{![[DOMAIN]], !"The domain"} +// CHECK-DAG: ![[SCOPE1:[0-9]+]] = distinct !{![[SCOPE1]], ![[DOMAIN]]} +// CHECK-DAG: ![[SCOPES1]] = !{![[SCOPE1]]} diff --git a/mlir/test/Target/LLVMIR/tbaa.mlir b/mlir/test/Target/LLVMIR/attribute-tbaa.mlir rename from mlir/test/Target/LLVMIR/tbaa.mlir rename to mlir/test/Target/LLVMIR/attribute-tbaa.mlir 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 @@ -2122,57 +2122,6 @@ // ----- -llvm.func @foo(%arg0: !llvm.ptr) - -#alias_scope_domain = #llvm.alias_scope_domain, description = "The domain"> -#alias_scope1 = #llvm.alias_scope, domain = #alias_scope_domain, description = "The first scope"> -#alias_scope2 = #llvm.alias_scope, domain = #alias_scope_domain> -#alias_scope3 = #llvm.alias_scope, domain = #alias_scope_domain> - -// CHECK-LABEL: aliasScope -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 #alias_scope1 - // CHECK: store {{.*}}, !alias.scope ![[SCOPES1]], !noalias ![[SCOPES23:[0-9]+]] - llvm.store %0, %arg1 {alias_scopes = [#alias_scope1], noalias_scopes = [#alias_scope2, #alias_scope3]} : i32, !llvm.ptr - // CHECK: load {{.*}}, !alias.scope ![[SCOPES2:[0-9]+]], !noalias ![[SCOPES13:[0-9]+]] - %1 = llvm.load %arg1 {alias_scopes = [#alias_scope2], noalias_scopes = [#alias_scope1, #alias_scope3]} : !llvm.ptr -> i32 - // CHECK: atomicrmw {{.*}}, !alias.scope ![[SCOPES3:[0-9]+]], !noalias ![[SCOPES12:[0-9]+]] - %2 = llvm.atomicrmw add %arg1, %0 monotonic {alias_scopes = [#alias_scope3], noalias_scopes = [#alias_scope1, #alias_scope2]} : !llvm.ptr, i32 - // CHECK: cmpxchg {{.*}}, !alias.scope ![[SCOPES3]] - %3 = llvm.cmpxchg %arg1, %1, %2 acq_rel monotonic {alias_scopes = [#alias_scope3]} : !llvm.ptr, i32 - %5 = llvm.mlir.constant(42 : i8) : i8 - // CHECK: llvm.memcpy{{.*}}, !alias.scope ![[SCOPES3]] - "llvm.intr.memcpy"(%arg1, %arg1, %0) <{isVolatile = false}> {alias_scopes = [#alias_scope3]} : (!llvm.ptr, !llvm.ptr, i32) -> () - // CHECK: llvm.memset{{.*}}, !noalias ![[SCOPES3]] - "llvm.intr.memset"(%arg1, %5, %0) <{isVolatile = false}> {noalias_scopes = [#alias_scope3]} : (!llvm.ptr, i8, i32) -> () - // CHECK: call void @foo({{.*}} !alias.scope ![[SCOPES3]] - llvm.call @foo(%arg1) {alias_scopes = [#alias_scope3]} : (!llvm.ptr) -> () - // CHECK: call void @foo({{.*}} !noalias ![[SCOPES3]] - llvm.call @foo(%arg1) {noalias_scopes = [#alias_scope3]} : (!llvm.ptr) -> () - llvm.return -} - -// 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]]} -// CHECK-DAG: ![[SCOPE3:[0-9]+]] = distinct !{![[SCOPE3]], ![[DOMAIN]]} -// CHECK-DAG: ![[SCOPES1]] = !{![[SCOPE1]]} -// CHECK-DAG: ![[SCOPES2]] = !{![[SCOPE2]]} -// CHECK-DAG: ![[SCOPES3]] = !{![[SCOPE3]]} -// CHECK-DAG: ![[SCOPES12]] = !{![[SCOPE1]], ![[SCOPE2]]} -// CHECK-DAG: ![[SCOPES13]] = !{![[SCOPE1]], ![[SCOPE3]]} -// CHECK-DAG: ![[SCOPES23]] = !{![[SCOPE2]], ![[SCOPE3]]} - -// ----- - // It is okay to have repeated successors if they have no arguments. // CHECK-LABEL: @duplicate_block_in_switch