diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index e8eeafd09a9cb..267389774bd5a 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -825,7 +825,7 @@ def LLVM_MemoryEffectsAttr : LLVM_Attr<"MemoryEffects", "memory_effects"> { def LLVM_AliasScopeDomainAttr : LLVM_Attr<"AliasScopeDomain", "alias_scope_domain"> { let parameters = (ins - "DistinctAttr":$id, + "Attribute":$id, OptionalParameter<"StringAttr">:$description ); @@ -853,7 +853,7 @@ def LLVM_AliasScopeDomainAttr : LLVM_Attr<"AliasScopeDomain", def LLVM_AliasScopeAttr : LLVM_Attr<"AliasScope", "alias_scope"> { let parameters = (ins - "DistinctAttr":$id, + "Attribute":$id, "AliasScopeDomainAttr":$domain, OptionalParameter<"StringAttr">:$description ); @@ -891,6 +891,8 @@ def LLVM_AliasScopeAttr : LLVM_Attr<"AliasScope", "alias_scope"> { } ``` + The first attribute can either be a DistinctAttr or a StringAttr. + See the following link for more details: https://llvm.org/docs/LangRef.html#noalias-and-alias-scope-metadata }]; @@ -898,6 +900,8 @@ def LLVM_AliasScopeAttr : LLVM_Attr<"AliasScope", "alias_scope"> { let summary = "LLVM dialect alias scope"; let assemblyFormat = "`<` struct(params) `>`"; + + let genVerifyDecl = 1; } def LLVM_AliasScopeArrayAttr diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp index 7490e8735f5fd..ff1636bc121b6 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp @@ -52,6 +52,23 @@ void LLVMDialect::registerAttributes() { >(); } +//===----------------------------------------------------------------------===// +// AliasScopeAttr +//===----------------------------------------------------------------------===// + +LogicalResult +AliasScopeAttr::verify(function_ref emitError, + Attribute id, AliasScopeDomainAttr domain, + StringAttr description) { + (void)domain; + (void)description; + if (!llvm::isa(id)) + return emitError() + << "id of an alias scope must be a StringAttr or a DistrinctAttr"; + + return success(); +} + //===----------------------------------------------------------------------===// // DINodeAttr //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index 95fb673fc72e3..2d8d7745eca9b 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -427,19 +427,33 @@ ModuleImport::processAliasScopeMetadata(const llvm::MDNode *node) { return node->getNumOperands() != 0 && node == dyn_cast(node->getOperand(0)); }; + auto verifySelfRefOrString = [](const llvm::MDNode *node) { + return node->getNumOperands() != 0 && + (node == dyn_cast(node->getOperand(0)) || + isa(node->getOperand(0))); + }; // Helper that verifies the given operand is a string or does not exist. auto verifyDescription = [](const llvm::MDNode *node, unsigned idx) { return idx >= node->getNumOperands() || isa(node->getOperand(idx)); }; + + auto getIdAttr = [&](const llvm::MDNode *node) -> Attribute { + if (verifySelfRef(node)) + return DistinctAttr::create(builder.getUnitAttr()); + + auto name = cast(node->getOperand(0)); + return builder.getStringAttr(name->getString()); + }; + // Helper that creates an alias scope domain attribute. auto createAliasScopeDomainOp = [&](const llvm::MDNode *aliasDomain) { StringAttr description = nullptr; if (aliasDomain->getNumOperands() >= 2) if (auto *operand = dyn_cast(aliasDomain->getOperand(1))) description = builder.getStringAttr(operand->getString()); - return builder.getAttr( - DistinctAttr::create(builder.getUnitAttr()), description); + Attribute idAttr = getIdAttr(aliasDomain); + return builder.getAttr(idAttr, description); }; // Collect the alias scopes and domains to translate them. @@ -452,10 +466,11 @@ ModuleImport::processAliasScopeMetadata(const llvm::MDNode *node) { // verifying its domain. Perform the verification before looking it up in // the alias scope mapping since it could have been inserted as a domain // node before. - if (!verifySelfRef(scope) || !domain || !verifyDescription(scope, 2)) + if (!verifySelfRefOrString(scope) || !domain || + !verifyDescription(scope, 2)) return emitError(loc) << "unsupported alias scope node: " << diagMD(scope, llvmModule.get()); - if (!verifySelfRef(domain) || !verifyDescription(domain, 1)) + if (!verifySelfRefOrString(domain) || !verifyDescription(domain, 1)) return emitError(loc) << "unsupported alias domain node: " << diagMD(domain, llvmModule.get()); @@ -473,9 +488,10 @@ ModuleImport::processAliasScopeMetadata(const llvm::MDNode *node) { StringAttr description = nullptr; if (!aliasScope.getName().empty()) description = builder.getStringAttr(aliasScope.getName()); + Attribute idAttr = getIdAttr(scope); auto aliasScopeOp = builder.getAttr( - DistinctAttr::create(builder.getUnitAttr()), - cast(it->second), description); + idAttr, cast(it->second), description); + aliasScopeMapping.try_emplace(aliasScope.getNode(), aliasScopeOp); } } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index ad62ae0cef57b..4367100e3aca6 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1724,25 +1724,36 @@ ModuleTranslation::getOrCreateAliasScope(AliasScopeAttr aliasScopeAttr) { aliasScopeAttr.getDomain(), nullptr); if (insertedDomain) { llvm::SmallVector operands; - // Placeholder for self-reference. + // Placeholder for potential self-reference. operands.push_back(dummy.get()); if (StringAttr description = aliasScopeAttr.getDomain().getDescription()) operands.push_back(llvm::MDString::get(ctx, description)); domainIt->second = llvm::MDNode::get(ctx, operands); // Self-reference for uniqueness. - domainIt->second->replaceOperandWith(0, domainIt->second); + llvm::Metadata *replacement; + if (auto stringAttr = + dyn_cast(aliasScopeAttr.getDomain().getId())) + replacement = llvm::MDString::get(ctx, stringAttr.getValue()); + else + replacement = domainIt->second; + domainIt->second->replaceOperandWith(0, replacement); } // Convert the scope metadata node. assert(domainIt->second && "Scope's domain should already be valid"); llvm::SmallVector operands; - // Placeholder for self-reference. + // Placeholder for potential self-reference. operands.push_back(dummy.get()); 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); + llvm::Metadata *replacement; + if (auto stringAttr = dyn_cast(aliasScopeAttr.getId())) + replacement = llvm::MDString::get(ctx, stringAttr.getValue()); + else + replacement = scopeIt->second; + scopeIt->second->replaceOperandWith(0, replacement); return scopeIt->second; } diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index aebfd7492093c..88660ce598f3c 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -750,6 +750,16 @@ llvm.func @experimental_noalias_scope_decl() { llvm.return } +#alias_scope_domain2 = #llvm.alias_scope_domain +#alias_scope2 = #llvm.alias_scope + +// CHECK-LABEL: @experimental_noalias_scope_with_string_id +llvm.func @experimental_noalias_scope_with_string_id() { + // CHECK: llvm.intr.experimental.noalias.scope.decl #{{.*}} + llvm.intr.experimental.noalias.scope.decl #alias_scope2 + llvm.return +} + // CHECK-LABEL: @experimental_constrained_fptrunc llvm.func @experimental_constrained_fptrunc(%in: f64) { // CHECK: llvm.intr.experimental.constrained.fptrunc %{{.*}} towardzero ignore : f64 to f32 diff --git a/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll b/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll index f5128ff76bc5f..bf4c85786216f 100644 --- a/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll +++ b/mlir/test/Target/LLVMIR/Import/metadata-alias-scopes.ll @@ -92,3 +92,38 @@ declare void @foo(ptr %arg1) !0 = distinct !{!0, !"The domain"} !1 = !{!1, !0} !2 = !{!1} + +; // ----- + +; CHECK: #[[DOMAIN:.*]] = #llvm.alias_scope_domain +; CHECK: #[[$SCOPE0:.*]] = #llvm.alias_scope +; CHECK: #[[$SCOPE1:.*]] = #llvm.alias_scope +; CHECK: #[[$SCOPE2:.*]] = #llvm.alias_scope + +; CHECK-LABEL: llvm.func @alias_scope +define void @alias_scope(ptr %arg1) { + ; CHECK: llvm.load + ; CHECK-SAME: alias_scopes = [#[[$SCOPE0]]] + ; CHECK-SAME: noalias_scopes = [#[[$SCOPE1]], #[[$SCOPE2]]] + %1 = load i32, ptr %arg1, !alias.scope !4, !noalias !7 + ; CHECK: llvm.load + ; CHECK-SAME: alias_scopes = [#[[$SCOPE1]]] + ; CHECK-SAME: noalias_scopes = [#[[$SCOPE0]], #[[$SCOPE2]]] + %2 = load i32, ptr %arg1, !alias.scope !5, !noalias !8 + ; CHECK: llvm.load + ; CHECK-SAME: alias_scopes = [#[[$SCOPE2]]] + ; CHECK-SAME: noalias_scopes = [#[[$SCOPE0]], #[[$SCOPE1]]] + %3 = load i32, ptr %arg1, !alias.scope !6, !noalias !9 + ret void +} + +!0 = !{!"domain1"} +!1 = !{!"scopeid1", !0, !"The first scope"} +!2 = !{!"scopeid2", !0} +!3 = !{!"scopeid3", !0} +!4 = !{!1} +!5 = !{!2} +!6 = !{!3} +!7 = !{!2, !3} +!8 = !{!1, !3} +!9 = !{!1, !2} diff --git a/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir b/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir index fa3395533af22..fb71a51512aee 100644 --- a/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir +++ b/mlir/test/Target/LLVMIR/attribute-alias-scopes.mlir @@ -104,3 +104,54 @@ llvm.func @self_reference() { // CHECK-DAG: ![[SCOPES]] = !{![[SCOPE]]} // CHECK-DAG: = !DISubroutineType(types: ![[TYPES:[0-9]+]]) // CHECK-DAG: ![[TYPES]] = !{null} + +// ----- + +llvm.func @foo(%arg0: !llvm.ptr) + +#alias_scope_domain = #llvm.alias_scope_domain +#alias_scope1 = #llvm.alias_scope +#alias_scope2 = #llvm.alias_scope +#alias_scope3 = #llvm.alias_scope + +// 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]+]] = !{!"domain1", !"The domain"} +// CHECK-DAG: ![[SCOPE1:[0-9]+]] = !{!"scope1", ![[DOMAIN]], !"The first scope"} +// CHECK-DAG: ![[SCOPE2:[0-9]+]] = !{!"scope2", ![[DOMAIN]]} +// CHECK-DAG: ![[SCOPE3:[0-9]+]] = !{!"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]]}