[flang-commits] [flang] [mlir] [acc] Pre-cg pass for handling flang data mapping to offload runtime (PR #221282)

via flang-commits flang-commits at lists.llvm.org
Fri Sep 4 09:53:09 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Razvan Lupusoru (razvanlupusoru)

<details>
<summary>Changes</summary>

Introduce acc.map_info as the acc dialect operation that captures a single mapped object's offload facts (attach point, descriptor kind, bounds, map flags, and size) so later codegen can lower data clauses to the offload runtime from one representation.

Add a Flang pre-codegen pass, ACCMapInfoPrep, that materializes acc.map_info for FIR-typed operands: it infers Fortran descriptor attach and CFI facts, sizes mapped and privatized storage, and folds paired enter/exit clause effects into map flags. This keeps FIR-specific mapping logic out of ACCToLLVM and leaves that conversion to consume map_info verbatim.

---

Patch is 147.29 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221282.diff


29 Files Affected:

- (modified) flang/include/flang/Optimizer/OpenACC/Passes.td (+18) 
- (modified) flang/include/flang/Semantics/runtime-type-info.h (+4) 
- (modified) flang/lib/Optimizer/Dialect/FIRType.cpp (+3) 
- (modified) flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp (+2-1) 
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp (+6) 
- (added) flang/lib/Optimizer/OpenACC/Transforms/ACCMapInfoPrep.cpp (+562) 
- (modified) flang/lib/Optimizer/OpenACC/Transforms/CMakeLists.txt (+3-1) 
- (modified) flang/lib/Semantics/runtime-type-info.cpp (+2-2) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-attach.mlir (+81) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-clauses.mlir (+229) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-declare-global.mlir (+40) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-exit.mlir (+222) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-implicit-present.mlir (+135) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-privatize.mlir (+122) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep-types.mlir (+228) 
- (added) flang/test/Fir/OpenACC/acc-fir-map-info-prep.mlir (+86) 
- (modified) flang/unittests/Optimizer/OpenACC/FIROpenACCSupportAnalysisTest.cpp (+14) 
- (added) mlir/include/mlir/Dialect/OpenACC/OpenACCCGEnums.td (+85) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td (+122) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td (+22-3) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCUtilsCG.h (+57) 
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCUtilsType.h (+12-5) 
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp (+78-46) 
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp (+33) 
- (modified) mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsCG.cpp (+302) 
- (modified) mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsType.cpp (+18-3) 
- (added) mlir/test/Dialect/OpenACC/invalid-cg-mapinfo.mlir (+44) 
- (added) mlir/test/Dialect/OpenACC/ops-cg-mapinfo.mlir (+143) 
- (modified) mlir/test/Dialect/OpenACC/ops.mlir (+12) 


``````````diff
diff --git a/flang/include/flang/Optimizer/OpenACC/Passes.td b/flang/include/flang/Optimizer/OpenACC/Passes.td
index a2c5e4677fcd2..0a1f636e1849c 100644
--- a/flang/include/flang/Optimizer/OpenACC/Passes.td
+++ b/flang/include/flang/Optimizer/OpenACC/Passes.td
@@ -109,6 +109,24 @@ def ACCOptimizeFirstprivateMap
   let dependentDialects = ["mlir::acc::OpenACCDialect", "fir::FIROpsDialect"];
 }
 
+def ACCMapInfoPrep
+    : InterfacePass<"acc-fir-map-info-prep", "mlir::FunctionOpInterface"> {
+  let summary = "Materialize acc.map_info for FIR mappings and privatization";
+  let description = [{
+    Replaces FIR-typed OpenACC data-entry operations, and the data-exit
+    operations paired with them, with `acc.map_info`. FIR-specific facts
+    include attach points inferred from Fortran descriptors (allocatable,
+    pointer, and class), CFI `descKind`, mapped sizes, and `mapFlags` that
+    fold enter and exit clause effects. Privatized storage is wrapped with its
+    byte size and private parallel-level flags, forming the contract for the
+    runtime allocation and replication of that storage. The result is a single
+    representation of runtime map and allocation metadata that no longer
+    depends on FIR types or attributes.
+  }];
+  let dependentDialects = ["mlir::acc::OpenACCDialect", "fir::FIROpsDialect",
+                           "mlir::arith::ArithDialect"];
+}
+
 def ACCDevicePtrToCUFKernel
     : Pass<"acc-device-ptr-to-cuf-kernel", "mlir::ModuleOp"> {
   let summary = "Pass device addresses to CUDA Fortran kernels launched inside "
diff --git a/flang/include/flang/Semantics/runtime-type-info.h b/flang/include/flang/Semantics/runtime-type-info.h
index 06a0807462b61..27e355f35a299 100644
--- a/flang/include/flang/Semantics/runtime-type-info.h
+++ b/flang/include/flang/Semantics/runtime-type-info.h
@@ -40,6 +40,10 @@ constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
 /// derived type descriptors.
 constexpr char typeDescriptorTypeName[]{"derivedtype"};
 
+/// Name of the size-in-bytes component in the DerivedType type of the
+/// __Fortran_type_info module
+constexpr char sizeInBytesCompName[]{"sizeinbytes"};
+
 /// Name of the bindings descriptor component in the DerivedType type of the
 /// __Fortran_type_info module
 constexpr char bindingDescCompName[]{"binding"};
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 178860239e17d..5f0dd68aa9396 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1650,6 +1650,9 @@ fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
     return std::pair{size, alignment};
   }
   if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) {
+    // Dynamic / unknown shapes have no compile-time byte size.
+    if (seqTy.hasDynamicExtents() || seqTy.hasUnknownShape())
+      return std::nullopt;
     auto result = getTypeSizeAndAlignment(loc, seqTy.getEleTy(), dl, kindMap);
     if (!result)
       return result;
diff --git a/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp b/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
index d8a0c43e40117..d0fe71657a6bf 100644
--- a/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
+++ b/flang/lib/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.cpp
@@ -63,7 +63,8 @@ FIROpenACCSupportAnalysis::getTypeSizeAndAlignment(
   if (!dl)
     return std::nullopt;
 
-  if (isa<fir::ReferenceType, fir::HeapType, fir::LLVMPointerType>(ty))
+  if (isa<fir::ReferenceType, fir::PointerType, fir::HeapType,
+          fir::LLVMPointerType>(ty))
     return mlir::acc::getTypeSizeAndAlignment(
         LLVM::LLVMPointerType::get(ty.getContext()), module, *dl, &support);
 
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 2a1230dc7f5c9..e070de2d908b2 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -27,6 +27,7 @@
 #include "flang/Optimizer/Support/Utils.h"
 #include "flang/Optimizer/Transforms/FIRToMemRefTypeConverter.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
 #include "mlir/Dialect/OpenACC/OpenACCUtils.h"
 #include "mlir/IR/BuiltinOps.h"
@@ -100,6 +101,11 @@ std::optional<llvm::TypeSize> OpenACCMappableModel<Ty>::getSizeInBytes(
   if (auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(eleTy))
     return mappableTy.getSizeInBytes(var, accBounds, dataLayout);
 
+  // Procedure pointers map as a single address-sized slot.
+  if (mlir::isa<mlir::FunctionType>(eleTy))
+    return llvm::TypeSize::getFixed(dataLayout.getTypeSize(
+        mlir::LLVM::LLVMPointerType::get(type.getContext())));
+
   // Dynamic extents or unknown ranks generally do not have compile-time
   // computable dimensions.
   auto seqType = mlir::dyn_cast<fir::SequenceType>(eleTy);
diff --git a/flang/lib/Optimizer/OpenACC/Transforms/ACCMapInfoPrep.cpp b/flang/lib/Optimizer/OpenACC/Transforms/ACCMapInfoPrep.cpp
new file mode 100644
index 0000000000000..4ba5cc8b2d909
--- /dev/null
+++ b/flang/lib/Optimizer/OpenACC/Transforms/ACCMapInfoPrep.cpp
@@ -0,0 +1,562 @@
+//===- ACCMapInfoPrep.cpp - Materialize acc.map_info for FIR --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass replaces the OpenACC data clause operations on FIR-typed operands
+// with acc.map_info. A map entry states everything the offload runtime needs
+// about one mapped object: the address to transfer, the pointer slot to attach
+// it to, the Fortran descriptor that describes it, the element and object
+// sizes, the bounds of the section being mapped, and the map-type flags. All
+// of that is derived from FIR types and attributes here, so that lowering to
+// runtime calls can work from the map entry alone.
+//
+// A data entry operation and the data exit operations paired with it describe
+// the same object, so they collapse into a single map entry whose flags carry
+// the effects of both directions. Privatized storage (acc.privatize,
+// acc.firstprivate_map) is wrapped the same way, with the parallel levels that
+// govern its replication.
+//
+// Example transformation, for an allocatable scalar in a copy clause:
+//
+//   Before:
+//     %slot = fir.declare %alloca : !fir.ref<!fir.box<!fir.heap<i32>>>
+//     %in = acc.copyin varPtr(%slot : !fir.ref<!fir.box<!fir.heap<i32>>>)
+//         dataClause(acc_copy) name("n") -> !fir.ref<!fir.box<!fir.heap<i32>>>
+//     acc.data dataOperands(%in : !fir.ref<!fir.box<!fir.heap<i32>>>) {
+//       ...
+//     }
+//     acc.copyout accPtr(%in : !fir.ref<!fir.box<!fir.heap<i32>>>)
+//         to varPtr(%slot : !fir.ref<!fir.box<!fir.heap<i32>>>)
+//         dataClause(acc_copy) name("n")
+//
+//   After:
+//     %slot = fir.declare %alloca : !fir.ref<!fir.box<!fir.heap<i32>>>
+//     %c0 = arith.constant 0 : i64
+//     // The copyin and the copyout fold into one entry, so the flags name both
+//     // directions. The descriptor makes this an attach (ptr_and_obj) of a
+//     // CFI-described object, and a size of zero defers the byte count to that
+//     // descriptor. exitLoc points at the erased copyout.
+//     %map = acc.map_info varPtr(%slot : !fir.ref<!fir.box<!fir.heap<i32>>>)
+//         size(%c0 : i64) elementSize(4) name("n") exitLoc(...)
+//         descKind(cfi) mapFlags(to,from,ptr_and_obj)
+//         -> !fir.ref<!fir.box<!fir.heap<i32>>>
+//     acc.data dataOperands(%map : !fir.ref<!fir.box<!fir.heap<i32>>>) {
+//       ...
+//     }
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Dialect/CUF/Attributes/CUFAttr.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/OpenACC/Analysis/FIROpenACCSupportAnalysis.h"
+#include "flang/Optimizer/OpenACC/Passes.h"
+#include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+#include "flang/Optimizer/Support/InternalNames.h"
+#include "flang/Semantics/runtime-type-info.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtilsCG.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtilsType.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/SymbolTable.h"
+#include "mlir/Pass/Pass.h"
+
+namespace fir {
+namespace acc {
+#define GEN_PASS_DEF_ACCMAPINFOPREP
+#include "flang/Optimizer/OpenACC/Passes.h.inc"
+} // namespace acc
+} // namespace fir
+
+using namespace mlir;
+
+namespace {
+
+/// Returns the pointer slot holding the address of \p mapVar, which the runtime
+/// rewrites once the pointee has a device copy. Such a slot exists only when
+/// the clause maps a pointee obtained by dereferencing it; mapping the slot
+/// itself has no second indirection and therefore no attach point. Only slots
+/// reached through a Fortran descriptor are recognized here.
+static Value findAttachPoint(Value mapVar) {
+  if (auto boxAddr = mapVar.getDefiningOp<fir::BoxAddrOp>()) {
+    if (auto load = boxAddr.getVal().getDefiningOp<fir::LoadOp>())
+      return load.getMemref();
+  }
+  if (fir::isa_box_type(fir::unwrapRefType(mapVar.getType()))) {
+    if (auto load = mapVar.getDefiningOp<fir::LoadOp>())
+      return load.getMemref();
+  }
+  return {};
+}
+
+/// True when the clause maps descriptor storage rather than a plain object. The
+/// runtime must then treat the entry as pointer-and-object: besides the
+/// descriptor bytes it fixes up the base address recorded inside them.
+///
+/// The base-address slot is identified downstream by the descriptor address
+/// (`desc`) alone, which currently works only because `base_addr` sits at
+/// offset 0 of the F18/CFI descriptor, so `&desc == &desc->base_addr`. If that
+/// layout changes, the attach point must be formed from the descriptor's actual
+/// `base_addr` field rather than the descriptor address.
+static bool mapsDescriptorStorage(Value mapVar) {
+  auto refTy = dyn_cast<fir::ReferenceType>(mapVar.getType());
+  return refTy && fir::isa_box_type(refTy.getEleTy());
+}
+
+static bool isManagedData(Value var) {
+  auto hasManagedAttr = [](Value v) {
+    Operation *op = v.getDefiningOp();
+    return op && cuf::hasDataAttr(op, cuf::DataAttribute::Managed);
+  };
+  if (hasManagedAttr(var))
+    return true;
+  Value orig = fir::acc::getOriginalDef(var, /*stripDeclare=*/false);
+  return orig && orig != var && hasManagedAttr(orig);
+}
+
+static std::pair<acc::DataDescKind, Value>
+findDescriptorFacts(Value mapVar, Type mappedObjectType, bool isImplicit) {
+  Type mapTy = mapVar.getType();
+  if (auto refTy = dyn_cast<fir::ReferenceType>(mapTy)) {
+    if (fir::isa_box_type(refTy.getEleTy()))
+      return {acc::DataDescKind::cfi, mapVar};
+  }
+  if (fir::isa_box_type(fir::unwrapRefType(mapTy)))
+    return {acc::DataDescKind::cfi, mapVar};
+  // box_addr of a loaded box can be either the pointee of a nested descriptor
+  // map or a host data-base address derived from an already-mapped box. The
+  // latter is always an implicit clause; only treat the explicit case as CFI.
+  if (!isImplicit) {
+    if (auto boxAddr = mapVar.getDefiningOp<fir::BoxAddrOp>()) {
+      Value boxVal = boxAddr.getVal();
+      if (fir::isa_box_type(boxVal.getType()) &&
+          boxVal.getDefiningOp<fir::LoadOp>())
+        return {acc::DataDescKind::cfi, boxVal};
+    }
+  }
+  (void)mappedObjectType;
+  return {acc::DataDescKind::none, {}};
+}
+
+/// Byte size of \p type as storage - what the type alone describes, without a
+/// value to interpret it as a mapped object. The types here are mostly FIR
+/// ones, which only the OpenACCSupport implementation sizes; it falls back to
+/// the dialect-agnostic acc::getTypeSizeAndAlignment for the rest.
+static std::optional<int64_t> computeTypeSizeBytes(acc::OpenACCSupport &support,
+                                                   ModuleOp module, Type type) {
+  std::optional<acc::TypeSizeAndAlignment> sizeAndAlignment =
+      support.getTypeSizeAndAlignment(type, module);
+  if (!sizeAndAlignment || sizeAndAlignment->first.isScalable())
+    return std::nullopt;
+  return static_cast<int64_t>(sizeAndAlignment->first.getFixedValue());
+}
+
+/// Load the size-in-bytes field from the Fortran type descriptor for
+/// \p recordType. Returns null when no matching type-descriptor global is
+/// present.
+static Value loadRecordTypeSizeFromTypeDesc(
+    Location loc, fir::RecordType recordType, Operation *entryOp,
+    std::optional<SymbolTable> &symbolTable, OpBuilder &builder) {
+  ModuleOp module = entryOp->getParentOfType<ModuleOp>();
+  if (!module)
+    return {};
+
+  // Keep a TypeDesc use so later passes see the record as referenced.
+  (void)fir::TypeDescOp::create(builder, loc, TypeAttr::get(recordType));
+
+  if (!symbolTable)
+    symbolTable.emplace(module);
+  StringAttr typeDescName = builder.getStringAttr(
+      fir::NameUniquer::getTypeDescriptorAssemblyName(recordType.getName()));
+  auto global = symbolTable->lookup<fir::GlobalOp>(typeDescName);
+  if (!global)
+    return {};
+
+  auto typeDescRecTy = dyn_cast<fir::RecordType>(global.getType());
+  if (!typeDescRecTy)
+    return {};
+
+  Value typeDescAddr = fir::AddrOfOp::create(
+      builder, loc, fir::ReferenceType::get(typeDescRecTy), global.getSymbol());
+  Type fieldTy = fir::FieldType::get(builder.getContext());
+  Value field = fir::FieldIndexOp::create(
+      builder, loc, fieldTy, Fortran::semantics::sizeInBytesCompName,
+      typeDescRecTy, ValueRange{});
+  Type coorTy = fir::ReferenceType::get(
+      typeDescRecTy.getType(Fortran::semantics::sizeInBytesCompName));
+  Value addr =
+      fir::CoordinateOp::create(builder, loc, coorTy, typeDescAddr, field);
+  return fir::LoadOp::create(builder, loc, addr);
+}
+
+static Value materializeMapSize(acc::OpenACCSupport &support,
+                                Operation *entryOp, Value var, Type varType,
+                                acc::DataDescKind descKind, ValueRange bounds,
+                                acc::MapFlags mapFlags,
+                                std::optional<SymbolTable> &symbolTable,
+                                OpBuilder &builder) {
+  Location loc = entryOp->getLoc();
+  Type i64Ty = builder.getI64Type();
+
+  int64_t staticSize = -1;
+  if (std::optional<DataLayout> dl = acc::getDataLayout(entryOp)) {
+    // Privatized maps keep the full object ArgSize; AccDataDesc carries the
+    // section. Device firstprivate copies still index with the parent lower
+    // bound, so a compact section size (or ArgSize 0) is incorrect.
+    if (bitEnumContainsAny(mapFlags, acc::MapFlags::private_))
+      staticSize = acc::computeMapInfoSizeBytes(
+          var, varType, acc::DataDescKind::none, /*bounds=*/{}, *dl, &support);
+    else
+      staticSize = acc::computeMapInfoSizeBytes(var, varType, descKind, bounds,
+                                                *dl, &support);
+  }
+
+  // Derived types with descriptor fields often have no compile-time layout
+  // size; load the type descriptor's size-in-bytes field instead.
+  if (staticSize < 0) {
+    if (auto recordType =
+            dyn_cast<fir::RecordType>(fir::unwrapRefType(varType))) {
+      if (Value dynamicSize = loadRecordTypeSizeFromTypeDesc(
+              loc, recordType, entryOp, symbolTable, builder))
+        return dynamicSize;
+    }
+  }
+
+  // An implicit present of an object whose size is not recoverable is only an
+  // address lookup. Size 0 matches the present-table entry whatever its
+  // extents are, including a zero-sized array, whereas an unknown size does
+  // not. An explicit clause keeps the unknown size so that the runtime can
+  // report the missing data instead.
+  if (staticSize < 0 && bounds.empty() &&
+      bitEnumContainsAll(mapFlags,
+                         acc::MapFlags::present | acc::MapFlags::implicit))
+    staticSize = 0;
+
+  return arith::ConstantIntOp::create(builder, loc, i64Ty, staticSize);
+}
+
+/// Describes the storage of \p baseTy - the base type of `acc.private_type` -
+/// as an element type whose extents are appended to \p extents. Extents that
+/// the type does not encode are `ShapedType::kDynamic` and are supplied by the
+/// `acc.privatize` dynamic sizes. Returns a null type when the type does not
+/// describe the storage, such as a descriptor that carries its own extents.
+static Type getPrivateStorageShape(Type baseTy,
+                                   SmallVectorImpl<int64_t> &extents) {
+  if (auto memrefTy = dyn_cast<MemRefType>(baseTy)) {
+    llvm::append_range(extents, memrefTy.getShape());
+    return memrefTy.getElementType();
+  }
+
+  Type storageTy = baseTy;
+  if (Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(baseTy))
+    storageTy = eleTy;
+  if (fir::isa_box_type(storageTy))
+    return {};
+
+  if (auto seqTy = dyn_cast<fir::SequenceType>(storageTy)) {
+    if (seqTy.hasUnknownShape())
+      return {};
+    llvm::append_range(extents, seqTy.getShape());
+    return seqTy.getEleTy();
+  }
+  return storageTy;
+}
+
+/// Materializes the byte size of privatized storage. The extents that the type
+/// encodes are sized as an array, which is what applies the padded element
+/// stride; the dynamic extents are then multiplied in. Returns null when the
+/// size is not obtainable.
+static Value materializePrivateStorageSize(
+    acc::OpenACCSupport &support, ModuleOp module, acc::PrivatizeOp privatizeOp,
+    Type elementType, ArrayRef<int64_t> extents,
+    std::optional<SymbolTable> &symbolTable, OpBuilder &builder) {
+  Location loc = privatizeOp.getLoc();
+  ValueRange dynamicSizes = privatizeOp.getDynamicSizes();
+
+  SmallVector<int64_t> staticExtents;
+  for (int64_t extent : extents)
+    if (!ShapedType::isDynamic(extent))
+      staticExtents.push_back(extent);
+  if (extents.size() - staticExtents.size() != dynamicSizes.size())
+    return {};
+
+  // Size the extents the type encodes as a FIR array, which is what applies
+  // the padded element stride - also for a memref base, since the stride rule
+  // does not depend on where the element type comes from. A single element
+  // stands in when all extents are dynamic: its size is the stride that those
+  // extents multiply.
+  Type staticTy = elementType;
+  if (!extents.empty()) {
+    if (staticExtents.empty())
+      staticExtents.push_back(1);
+    staticTy = fir::SequenceType::get(staticExtents, elementType);
+  }
+
+  Value size;
+  if (std::optional<int64_t> staticBytes =
+          computeTypeSizeBytes(support, module, staticTy)) {
+    size = arith::ConstantIntOp::create(builder, loc, builder.getI64Type(),
+                                        *staticBytes);
+  } else if (auto recordType = dyn_cast<fir::RecordType>(elementType)) {
+    // A derived type whose layout is not computable here carries its padded
+    // size in the Fortran type descriptor.
+    size = loadRecordTypeSizeFromTypeDesc(
+        loc, recordType, privatizeOp.getOperation(), symbolTable, builder);
+    if (!size)
+      return {};
+    int64_t staticExtent = 1;
+    for (int64_t extent : staticExtents)
+      staticExtent *= extent;
+    if (staticExtent != 1) {
+      Value extentVal = arith::ConstantIntOp::create(
+          builder, loc, size.getType(), staticExtent);
+      size = arith::MulIOp::create(builder, loc, size, extentVal);
+    }
+  } else {
+    return {};
+  }
+
+  for (Value dynamicSize : dynamicSizes) {
+    Value extentVal =
+        arith::IndexCastOp::create(builder, loc, size.getType(), dynamicSize);
+    size = arith::MulIOp::create(builder, loc, size, extentVal);
+  }
+  return size;
+}
+
+/// Wraps \p privatizeOp so that privatized storage carries offload facts in
+/// `acc.map_info` like any other mapped variable, including the parallel
+/// levels that select gang/worker/vector private replication.
+static std::optional<acc::MapInfoOp> buildPriv...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/221282


More information about the flang-commits mailing list