From 9506f20b4dccc6334cc36e04c93033d575cfee52 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru Date: Wed, 1 Apr 2026 09:03:47 -0700 Subject: [PATCH] [flang][acc] Add AA implementation for acc operations (#189772) This PR extends flang's alias analysis so it can reason about values that originate from OpenACC data and privatization operations, including values passed through block arguments. --- .../OpenACC/Support/FIROpenACCUtils.h | 1 + .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 56 ++ flang/lib/Optimizer/Analysis/CMakeLists.txt | 4 + .../AliasAnalysis/alias-analysis-acc.mlir | 546 ++++++++++++++++++ mlir/include/mlir/Dialect/OpenACC/OpenACC.h | 2 + .../mlir/Dialect/OpenACC/OpenACCUtils.h | 5 + .../Dialect/OpenACC/Utils/OpenACCUtils.cpp | 16 + 7 files changed, 630 insertions(+) create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h index a8b9bcf5cfd4..ad045ea32a9a 100644 --- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h +++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h @@ -15,6 +15,7 @@ #include "mlir/Dialect/OpenACC/OpenACC.h" #include "mlir/IR/Builders.h" +#include "mlir/IR/Operation.h" #include "mlir/IR/Value.h" #include diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index 550e8a3a281d..3b8cac1196ce 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -15,6 +15,8 @@ #include "flang/Optimizer/HLFIR/HLFIROps.h" #include "flang/Optimizer/Support/InternalNames.h" #include "mlir/Analysis/AliasAnalysis.h" +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "mlir/Dialect/OpenACC/OpenACCUtils.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/Dialect/OpenMP/OpenMPInterfaces.h" #include "mlir/IR/BuiltinOps.h" @@ -24,6 +26,7 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include using namespace mlir; @@ -112,6 +115,39 @@ static bool isPrivateArg(omp::BlockArgOpenMPOpInterface &argIface, return false; } +/// Classify `mappedValue` when defined by OpenACC mapping op `accOp`. +/// Private-like ops use `SourceKind::Allocate`; other data clauses use +/// `getSourceFn` on the mapped host variable (`mlir::acc::getVar`). +static fir::AliasAnalysis::Source getSourceForACCMappedValue( + mlir::Value mappedValue, mlir::Operation *accOp, + llvm::function_ref getSourceFn, + bool originIsData, + fir::AliasAnalysis::Source::Attributes accumulatedAttrs) { + assert(accOp && "OpenACC mapping op required"); + // Private-like ops use SourceKind::Allocate. + if (mlir::isa( + accOp)) + return {{mappedValue, nullptr, originIsData}, + fir::AliasAnalysis::SourceKind::Allocate, + mappedValue.getType(), + accumulatedAttrs, + false, + false}; + + // Not private-like: classify using the corresponding host variable's source. + // + // Caveat: with discrete device memory, host and device copies do not alias + // even when this path makes them look related. Alias analysis here is usually + // about two values *inside* a compute region, not host-vs-device pointer + // queries, so using the host source remains a reasonable tradeoff for + // disambiguating in-region uses. Finer modeling would require extending + // AliasAnalysis::Source (with address space) and teaching AA to use it. + fir::AliasAnalysis::Source source = getSourceFn(mlir::acc::getVar(accOp)); + source.attributes |= accumulatedAttrs; + return source; +} + namespace fir { void AliasAnalysis::Source::print(llvm::raw_ostream &os) const { @@ -680,6 +716,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, auto opResult = mlir::cast(v); assert(opResult.getOwner() == defOp && "v must be a result of defOp"); ty = opResult.getType(); + std::optional accSourceReturn; llvm::TypeSwitch(defOp) .Case([&](hlfir::AsExprOp op) { // TODO: we should probably always report hlfir.as_expr @@ -937,10 +974,21 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, !mlir::isa(ty)) followBoxData = true; }) + .Case([&](auto op) { + accSourceReturn = getSourceForACCMappedValue( + v, op.getOperation(), + [&](mlir::Value x) { + return getSource(x, getLastInstantiationPoint); + }, + followingData, attributes); + breakFromLoop = true; + }) .Default([&](auto op) { defOp = nullptr; breakFromLoop = true; }); + if (accSourceReturn) + return *accSourceReturn; } if (!defOp && type == SourceKind::Unknown) { // Check if the memory source is coming through a dummy argument. @@ -956,6 +1004,14 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, // hlfir.eval_in_mem block operands is allocated by the operation. type = SourceKind::Allocate; ty = v.getType(); + } else if (mlir::Operation *accOp = + mlir::acc::getACCDataClauseOpForBlockArg(v)) { + return getSourceForACCMappedValue( + v, accOp, + [&](mlir::Value x) { + return getSource(x, getLastInstantiationPoint); + }, + followingData, attributes); } } diff --git a/flang/lib/Optimizer/Analysis/CMakeLists.txt b/flang/lib/Optimizer/Analysis/CMakeLists.txt index 398a6d3b8842..50a01240e44f 100644 --- a/flang/lib/Optimizer/Analysis/CMakeLists.txt +++ b/flang/lib/Optimizer/Analysis/CMakeLists.txt @@ -17,11 +17,15 @@ add_flang_library(FIRAnalysis MLIR_DEPS MLIRIR + MLIROpenACCDialect + MLIROpenACCUtils MLIROpenMPDialect MLIR_LIBS MLIRFuncDialect MLIRLLVMDialect MLIRMathTransforms + MLIROpenACCDialect + MLIROpenACCUtils MLIROpenMPDialect ) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir b/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir new file mode 100644 index 000000000000..9fb5ee221691 --- /dev/null +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-acc.mlir @@ -0,0 +1,546 @@ +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s + +// ----- + +// Two acc.copyin results from distinct host allocas do not alias. +// CHECK-LABEL: Testing : "testBothOutsideCopyinDistinctHosts" +// CHECK-DAG: cin_a#0 <-> cin_b#0: NoAlias + +func.func @testBothOutsideCopyinDistinctHosts() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a", test.ptr = "cin_a"} + %cb = acc.copyin varPtr(%db : !fir.ref) -> !fir.ref {name = "b", test.ptr = "cin_b"} + return +} + +// ----- + +// Two acc.copyin results from dummy arguments that are Fortran TARGET variables: +// they may alias. +// CHECK-LABEL: Testing : "testBothOutsideCopyinTargetDummyArgsMayAlias" +// CHECK-DAG: arg_cp_a#0 <-> arg_cp_b#0: MayAlias + +func.func @testBothOutsideCopyinTargetDummyArgsMayAlias(%arg0: !fir.ref {fir.bindc_name = "x"}, %arg1: !fir.ref {fir.bindc_name = "y"}) { + %ds = fir.dummy_scope : !fir.dscope + %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEex"} : (!fir.ref, !fir.dscope) -> !fir.ref + %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEey"} : (!fir.ref, !fir.dscope) -> !fir.ref + %cx = acc.copyin varPtr(%dx : !fir.ref) -> !fir.ref {name = "x", test.ptr = "arg_cp_a"} + %cy = acc.copyin varPtr(%dy : !fir.ref) -> !fir.ref {name = "y", test.ptr = "arg_cp_b"} + return +} + +// ----- + +// Two acc.copyin results mapping the same host ref must alias. +// CHECK-LABEL: Testing : "testBothOutsideCopyinSameHostMustAlias" +// CHECK-DAG: out_must_a#0 <-> out_must_b#0: MustAlias + +func.func @testBothOutsideCopyinSameHostMustAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca1 = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a", test.ptr = "out_must_a"} + %ca2 = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a", test.ptr = "out_must_b"} + return +} + +// ----- + +// CHECK-LABEL: Testing : "testBothOutsideCreateDistinctHosts" +// CHECK-DAG: crt_a#0 <-> crt_b#0: NoAlias + +func.func @testBothOutsideCreateDistinctHosts() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ta = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a", test.ptr = "crt_a"} + %tb = acc.create varPtr(%db : !fir.ref) -> !fir.ref {name = "b", test.ptr = "crt_b"} + return +} + +// ----- + +// Same distinct-host copyins as above, but threaded through acc.compute_region +// block arguments. +// CHECK-LABEL: Testing : "testComputeRegionCopyinDistinctHostsInsideConvert" +// CHECK-DAG: cr_dist_a#0 <-> cr_dist_b#0: NoAlias + +func.func @testComputeRegionCopyinDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %cb = acc.copyin varPtr(%db : !fir.ref) -> !fir.ref {name = "b"} + acc.compute_region ins(%arg0 = %ca, %arg1 = %cb) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_dist_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_dist_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.kernels"} + return +} + +// ----- + +// CHECK-LABEL: Testing : "testComputeRegionCreateDistinctHostsInsideConvert" +// CHECK-DAG: cr_crt_a#0 <-> cr_crt_b#0: NoAlias + +func.func @testComputeRegionCreateDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ta = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %tb = acc.create varPtr(%db : !fir.ref) -> !fir.ref {name = "b"} + acc.compute_region ins(%arg0 = %ta, %arg1 = %tb) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_crt_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_crt_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.kernels"} + return +} + +// ----- + +// Same TARGET dummy copyins as testBothOutsideCopyinTargetDummyArgsMayAlias, +// through acc.compute_region block args. +// CHECK-LABEL: Testing : "testComputeRegionCopyinTargetDummiesMayAliasInsideConvert" +// CHECK-DAG: cr_tgt_a#0 <-> cr_tgt_b#0: MayAlias + +func.func @testComputeRegionCopyinTargetDummiesMayAliasInsideConvert(%arg0: !fir.ref {fir.bindc_name = "x"}, %arg1: !fir.ref {fir.bindc_name = "y"}) { + %ds = fir.dummy_scope : !fir.dscope + %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEex"} : (!fir.ref, !fir.dscope) -> !fir.ref + %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEey"} : (!fir.ref, !fir.dscope) -> !fir.ref + %cx = acc.copyin varPtr(%dx : !fir.ref) -> !fir.ref {name = "x"} + %cy = acc.copyin varPtr(%dy : !fir.ref) -> !fir.ref {name = "y"} + acc.compute_region ins(%cr0 = %cx, %cr1 = %cy) : (!fir.ref, !fir.ref) { + %va = fir.convert %cr0 {test.ptr = "cr_tgt_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %cr1 {test.ptr = "cr_tgt_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// Single host copyin wired twice through arguments; both block args alias the +// same mapped host variable. +// CHECK-LABEL: Testing : "testComputeRegionCopyinSameHostMustAliasInsideConvert" +// CHECK-DAG: cr_must_a#0 <-> cr_must_b#0: MustAlias + +func.func @testComputeRegionCopyinSameHostMustAliasInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ca, %arg1 = %ca) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_must_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_must_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.kernels"} + return +} + +// ----- + +// Distinct-host copyins passed as acc.kernels dataOperands. +// CHECK-LABEL: Testing : "testKernelsCopyinDistinctHostsInsideConvert" +// CHECK-DAG: kern_dist_a#0 <-> kern_dist_b#0: NoAlias + +func.func @testKernelsCopyinDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %cb = acc.copyin varPtr(%db : !fir.ref) -> !fir.ref {name = "b"} + acc.kernels dataOperands(%ca, %cb : !fir.ref, !fir.ref) { + %va = fir.convert %ca {test.ptr = "kern_dist_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %cb {test.ptr = "kern_dist_b"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// CHECK-LABEL: Testing : "testKernelsCreateDistinctHostsInsideConvert" +// CHECK-DAG: kern_crt_a#0 <-> kern_crt_b#0: NoAlias + +func.func @testKernelsCreateDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ta = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %tb = acc.create varPtr(%db : !fir.ref) -> !fir.ref {name = "b"} + acc.kernels dataOperands(%ta, %tb : !fir.ref, !fir.ref) { + %va = fir.convert %ta {test.ptr = "kern_crt_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %tb {test.ptr = "kern_crt_b"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// TARGET dummy copyins as acc.kernels dataOperands. +// CHECK-LABEL: Testing : "testKernelsCopyinTargetDummiesMayAliasInsideConvert" +// CHECK-DAG: kern_tgt_a#0 <-> kern_tgt_b#0: MayAlias + +func.func @testKernelsCopyinTargetDummiesMayAliasInsideConvert(%arg0: !fir.ref {fir.bindc_name = "x"}, %arg1: !fir.ref {fir.bindc_name = "y"}) { + %ds = fir.dummy_scope : !fir.dscope + %dx = fir.declare %arg0 dummy_scope %ds arg 1 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEex"} : (!fir.ref, !fir.dscope) -> !fir.ref + %dy = fir.declare %arg1 dummy_scope %ds arg 2 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFEey"} : (!fir.ref, !fir.dscope) -> !fir.ref + %cx = acc.copyin varPtr(%dx : !fir.ref) -> !fir.ref {name = "x"} + %cy = acc.copyin varPtr(%dy : !fir.ref) -> !fir.ref {name = "y"} + acc.kernels dataOperands(%cx, %cy : !fir.ref, !fir.ref) { + %va = fir.convert %cx {test.ptr = "kern_tgt_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %cy {test.ptr = "kern_tgt_b"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// Same copyin value listed twice in dataOperands; both converts must alias. +// CHECK-LABEL: Testing : "testKernelsCopyinSameHostMustAliasInsideConvert" +// CHECK-DAG: kern_must_a#0 <-> kern_must_b#0: MustAlias + +func.func @testKernelsCopyinSameHostMustAliasInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ca : !fir.ref) { + %va = fir.convert %ca {test.ptr = "kern_must_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %ca {test.ptr = "kern_must_b"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// acc.compute_region: both queried values are inside the region; test.ptr is on +// fir.convert of each captured private (traces block operands without tagging +// the region op). +// CHECK-LABEL: Testing : "testComputeRegionPrivateInsideConvert" +// CHECK-DAG: cr_priv_a#0 <-> cr_priv_b#0: NoAlias + +func.func @testComputeRegionPrivateInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %pa = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pb = acc.private varPtr(%db : !fir.ref) -> !fir.ref {name = "b"} + acc.compute_region ins(%arg0 = %pa, %arg1 = %pb) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_priv_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_priv_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// Same host ref as both acc.copyin and acc.private operands (two copyins would +// must-alias); private-like mapping is a distinct allocation vs copyin. +// CHECK-LABEL: Testing : "testComputeRegionCopyinVsPrivateSameHostNoAlias" +// CHECK-DAG: cr_mix_cp#0 <-> cr_mix_pr#0: NoAlias + +func.func @testComputeRegionCopyinVsPrivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pp = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ca, %arg1 = %pp) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_mix_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// CHECK-LABEL: Testing : "testComputeRegionCreateVsPrivateSameHostNoAlias" +// CHECK-DAG: cr_mix_cr#0 <-> cr_mix_pr2#0: NoAlias + +func.func @testComputeRegionCreateVsPrivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ta = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pp = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ta, %arg1 = %pp) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_mix_cr"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr2"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// Same host: acc.copyin vs acc.firstprivate. +// CHECK-LABEL: Testing : "testComputeRegionCopyinVsFirstprivateSameHostNoAlias" +// CHECK-DAG: cr_fp_cp#0 <-> cr_fp_pr#0: NoAlias + +func.func @testComputeRegionCopyinVsFirstprivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pf = acc.firstprivate varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ca, %arg1 = %pf) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_fp_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_fp_pr"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// Same host: acc.copyin vs acc.firstprivate_map. +// CHECK-LABEL: Testing : "testComputeRegionCopyinVsFirstprivateMapSameHostNoAlias" +// CHECK-DAG: cr_fpm_cp#0 <-> cr_fpm_fm#0: NoAlias + +func.func @testComputeRegionCopyinVsFirstprivateMapSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %fm = acc.firstprivate_map varPtr(%da : !fir.ref) varType(f32) -> !fir.ref {name = "a"} + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ca, %arg1 = %fm) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_fpm_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_fpm_fm"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.kernels"} + return +} + +// ----- + +// acc.kernels: same host as copyin and acc.private dataOperands. +// CHECK-LABEL: Testing : "testKernelsCopyinVsPrivateSameHostNoAlias" +// CHECK-DAG: k_mix_cp#0 <-> k_mix_pr#0: NoAlias + +func.func @testKernelsCopyinVsPrivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pp = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ca : !fir.ref) private(%pp : !fir.ref) { + %va = fir.convert %ca {test.ptr = "k_mix_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %pp {test.ptr = "k_mix_pr"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// CHECK-LABEL: Testing : "testKernelsCreateVsPrivateSameHostNoAlias" +// CHECK-DAG: k_mix_cr#0 <-> k_mix_pr2#0: NoAlias + +func.func @testKernelsCreateVsPrivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ta = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pp = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ta : !fir.ref) private(%pp : !fir.ref) { + %va = fir.convert %ta {test.ptr = "k_mix_cr"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %pp {test.ptr = "k_mix_pr2"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// acc.kernels: acc.copyin vs acc.firstprivate. +// CHECK-LABEL: Testing : "testKernelsCopyinVsFirstprivateSameHostNoAlias" +// CHECK-DAG: k_fp_cp#0 <-> k_fp_pr#0: NoAlias + +func.func @testKernelsCopyinVsFirstprivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + %pf = acc.firstprivate varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ca : !fir.ref) firstprivate(%pf : !fir.ref) { + %va = fir.convert %ca {test.ptr = "k_fp_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %pf {test.ptr = "k_fp_pr"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// acc.kernels: acc.copyin vs acc.firstprivate_map. +// CHECK-LABEL: Testing : "testKernelsCopyinVsFirstprivateMapSameHostNoAlias" +// CHECK-DAG: k_fpm_cp#0 <-> k_fpm_fm#0: NoAlias + +func.func @testKernelsCopyinVsFirstprivateMapSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %fm = acc.firstprivate_map varPtr(%da : !fir.ref) varType(f32) -> !fir.ref {name = "a"} + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ca : !fir.ref) firstprivate(%fm : !fir.ref) { + %va = fir.convert %ca {test.ptr = "k_fpm_cp"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %fm {test.ptr = "k_fpm_fm"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +// acc.private inside acc.compute_region; ins carries acc.create from the host ref. +// CHECK-LABEL: Testing : "testComputeRegionPrivateOpInsideVsInsCreateNoAlias" +// CHECK-DAG: cr_body_pr#0 <-> cr_body_cr#0: NoAlias + +func.func @testComputeRegionPrivateOpInsideVsInsCreateNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %tc = acc.create varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %tc) : (!fir.ref) { + %pv = acc.private varPtr(%arg0 : !fir.ref) -> !fir.ref {name = "a"} + %vb = fir.convert %pv {test.ptr = "cr_body_pr"} : (!fir.ref) -> !fir.ref + %vc = fir.convert %arg0 {test.ptr = "cr_body_cr"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// ----- + +// acc.private inside acc.kernels on the dataOperand copyin of the host ref. +// CHECK-LABEL: Testing : "testKernelsPrivateOpInsideVsDataCopyinNoAlias" +// CHECK-DAG: k_body_pr#0 <-> k_body_cp#0: NoAlias + +func.func @testKernelsPrivateOpInsideVsDataCopyinNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ca = acc.copyin varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.kernels dataOperands(%ca : !fir.ref) { + %pv = acc.private varPtr(%ca : !fir.ref) -> !fir.ref {name = "a"} + %vb = fir.convert %pv {test.ptr = "k_body_pr"} : (!fir.ref) -> !fir.ref + %vc = fir.convert %ca {test.ptr = "k_body_cp"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// ----- + +acc.reduction.recipe @red_f32_aa : !fir.ref reduction_operator init { +^bb0(%arg0: !fir.ref): + %init = fir.alloca f32 + acc.yield %init : !fir.ref +} combiner { +^bb0(%lhs: !fir.ref, %rhs: !fir.ref): + %lv = fir.load %lhs : !fir.ref + %rv = fir.load %rhs : !fir.ref + %s = arith.addf %lv, %rv : f32 + %out = fir.alloca f32 + fir.store %s to %out : !fir.ref + acc.yield %out : !fir.ref +} + +// CHECK-LABEL: Testing : "testBothOutsideReductionDistinctHosts" +// CHECK-DAG: red_a#0 <-> red_b#0: NoAlias + +func.func @testBothOutsideReductionDistinctHosts() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ra = acc.reduction varPtr(%da : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "a", test.ptr = "red_a"} + %rb = acc.reduction varPtr(%db : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "b", test.ptr = "red_b"} + return +} + +// CHECK-LABEL: Testing : "testComputeRegionReductionDistinctHostsInsideConvert" +// CHECK-DAG: cr_red_a#0 <-> cr_red_b#0: NoAlias + +func.func @testComputeRegionReductionDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ra = acc.reduction varPtr(%da : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "a"} + %rb = acc.reduction varPtr(%db : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "b"} + acc.compute_region ins(%arg0 = %ra, %arg1 = %rb) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_red_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_red_b"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.parallel"} + return +} + +// CHECK-LABEL: Testing : "testKernelsReductionDistinctHostsInsideConvert" +// CHECK-DAG: kern_red_a#0 <-> kern_red_b#0: NoAlias + +func.func @testKernelsReductionDistinctHostsInsideConvert() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %b = fir.alloca f32 {uniq_name = "_QFEb"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %db = fir.declare %b {uniq_name = "_QFEb"} : (!fir.ref) -> !fir.ref + %ra = acc.reduction varPtr(%da : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "a"} + %rb = acc.reduction varPtr(%db : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "b"} + acc.kernels reduction(%ra, %rb : !fir.ref, !fir.ref) { + %va = fir.convert %ra {test.ptr = "kern_red_a"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %rb {test.ptr = "kern_red_b"} : (!fir.ref) -> !fir.ref + acc.terminator + } + return +} + +// CHECK-LABEL: Testing : "testComputeRegionReductionVsPrivateSameHostNoAlias" +// CHECK-DAG: cr_mix_rd#0 <-> cr_mix_pr3#0: NoAlias + +func.func @testComputeRegionReductionVsPrivateSameHostNoAlias() { + %a = fir.alloca f32 {uniq_name = "_QFEa"} + %da = fir.declare %a {uniq_name = "_QFEa"} : (!fir.ref) -> !fir.ref + %ra = acc.reduction varPtr(%da : !fir.ref) recipe(@red_f32_aa) -> !fir.ref {name = "a"} + %pp = acc.private varPtr(%da : !fir.ref) -> !fir.ref {name = "a"} + acc.compute_region ins(%arg0 = %ra, %arg1 = %pp) : (!fir.ref, !fir.ref) { + %va = fir.convert %arg0 {test.ptr = "cr_mix_rd"} : (!fir.ref) -> !fir.ref + %vb = fir.convert %arg1 {test.ptr = "cr_mix_pr3"} : (!fir.ref) -> !fir.ref + acc.yield + } {origin = "acc.kernels"} + return +} + +// ----- + +// Allocas inside acc.parallel. +// CHECK-LABEL: Testing : "testBothInsideParallel" +// CHECK-DAG: par_a#0 <-> par_b#0: NoAlias + +func.func @testBothInsideParallel() { + acc.parallel { + %a = fir.alloca f32 {uniq_name = "_QFEa", test.ptr = "par_a"} + %b = fir.alloca f32 {uniq_name = "_QFEb", test.ptr = "par_b"} + acc.yield + } + return +} + +// ----- + +// Allocas inside acc.kernels. +// CHECK-LABEL: Testing : "testBothInsideKernels" +// CHECK-DAG: kern_a#0 <-> kern_b#0: NoAlias + +func.func @testBothInsideKernels() { + acc.kernels { + %a = fir.alloca f32 {uniq_name = "_QFEa", test.ptr = "kern_a"} + %b = fir.alloca f32 {uniq_name = "_QFEb", test.ptr = "kern_b"} + acc.terminator + } + return +} diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h index 9e7a15feb2e7..682e8ff9784c 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h @@ -53,6 +53,8 @@ mlir::acc::UpdateDeviceOp, mlir::acc::UseDeviceOp, \ mlir::acc::ReductionOp, mlir::acc::DeclareDeviceResidentOp, \ mlir::acc::DeclareLinkOp, mlir::acc::CacheOp +#define ACC_DATA_ENTRY_AND_INIT_OPS \ + ACC_DATA_ENTRY_OPS, mlir::acc::ReductionInitOp #define ACC_DATA_EXIT_OPS \ mlir::acc::CopyoutOp, mlir::acc::DeleteOp, mlir::acc::DetachOp, \ mlir::acc::UpdateHostOp diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h index dd3d34b8252d..c26ddbd54f1b 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCUtils.h @@ -29,6 +29,11 @@ namespace acc { /// `ACC_COMPUTE_CONSTRUCT_OPS`. mlir::Operation *getEnclosingComputeOp(mlir::Region ®ion); +/// If `v` is not a block argument of an `acc.compute_region` body, returns +/// nullptr. Otherwise maps the block argument to its operand and returns the +/// defining operation if it is one of `ACC_DATA_ENTRY_OPS`. +mlir::Operation *getACCDataClauseOpForBlockArg(mlir::Value v); + /// Returns true if this value is only used by `acc.private` operations in the /// `region`. bool isOnlyUsedByPrivateClauses(mlir::Value val, mlir::Region ®ion); diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp index 1c63760a6984..1cc313206a99 100644 --- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp +++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp @@ -25,6 +25,22 @@ mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) { .getParentOfType(); } +mlir::Operation *mlir::acc::getACCDataClauseOpForBlockArg(mlir::Value v) { + auto barg = mlir::dyn_cast(v); + if (!barg) + return nullptr; + + mlir::Block *block = barg.getOwner(); + auto computeReg = + mlir::dyn_cast(block->getParentOp()); + if (!computeReg || block != computeReg.getBody()) + return nullptr; + + mlir::Value orig = computeReg.getOperand(barg); + mlir::Operation *def = orig.getDefiningOp(); + return mlir::isa_and_nonnull(def) ? def : nullptr; +} + template static bool isOnlyUsedByOpClauses(mlir::Value val, mlir::Region ®ion) { auto checkIfUsedOnlyByOpInside = [&](mlir::Operation *user) {