CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
335 lines
11 KiB
C++
335 lines
11 KiB
C++
//===-- lib/cuda/memory.cpp -------------------------------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flang/Runtime/CUDA/memory.h"
|
|
#include "flang-rt/runtime/assign-impl.h"
|
|
#include "flang-rt/runtime/descriptor.h"
|
|
#include "flang-rt/runtime/environment.h"
|
|
#include "flang-rt/runtime/terminator.h"
|
|
#include "flang/Runtime/CUDA/common.h"
|
|
#include "flang/Runtime/CUDA/descriptor.h"
|
|
#include "flang/Runtime/CUDA/memmove-function.h"
|
|
#include "flang/Runtime/assign.h"
|
|
|
|
#include "cuda_runtime.h"
|
|
|
|
#include <cstddef>
|
|
#include <optional>
|
|
|
|
namespace Fortran::runtime::cuda {
|
|
|
|
struct Memcpy2DLayout {
|
|
void *base;
|
|
std::size_t widthBytes;
|
|
std::size_t height;
|
|
std::size_t pitchBytes;
|
|
};
|
|
|
|
// Get cudaMemcpy2D layout information for a descriptor that can be represented
|
|
// as fixed-pitch rows of widthBytes. Returns nullopt for layouts that need the
|
|
// general runtime assignment path.
|
|
static std::optional<Memcpy2DLayout> GetMemcpy2DLayout(
|
|
const Descriptor &desc, std::size_t widthBytes) {
|
|
if (desc.rank() == 0 || desc.Elements() == 0) {
|
|
return std::nullopt;
|
|
}
|
|
const auto elemBytes = desc.ElementBytes();
|
|
if (elemBytes == 0 || widthBytes == 0 || widthBytes % elemBytes != 0) {
|
|
return std::nullopt;
|
|
}
|
|
std::size_t contiguousBytes = elemBytes;
|
|
int rowDim = 0;
|
|
while (rowDim < desc.rank()) {
|
|
const auto &dim = desc.GetDimension(rowDim);
|
|
if (dim.Extent() != 1 &&
|
|
(dim.ByteStride() < 0 ||
|
|
static_cast<std::size_t>(dim.ByteStride()) != contiguousBytes)) {
|
|
break;
|
|
}
|
|
contiguousBytes *= dim.Extent();
|
|
++rowDim;
|
|
if (contiguousBytes == widthBytes) {
|
|
break;
|
|
}
|
|
}
|
|
if (contiguousBytes != widthBytes) {
|
|
return std::nullopt;
|
|
}
|
|
Memcpy2DLayout layout;
|
|
layout.base = desc.raw().base_addr;
|
|
layout.widthBytes = widthBytes;
|
|
layout.height = desc.Elements() * elemBytes / widthBytes;
|
|
if (rowDim == desc.rank()) {
|
|
layout.pitchBytes = widthBytes;
|
|
return layout;
|
|
}
|
|
auto pitch = desc.GetDimension(rowDim).ByteStride();
|
|
if (pitch <= 0 || static_cast<std::size_t>(pitch) < widthBytes) {
|
|
return std::nullopt;
|
|
}
|
|
SubscriptValue expected = pitch;
|
|
for (int j = rowDim; j < desc.rank(); ++j) {
|
|
const auto &dim = desc.GetDimension(j);
|
|
if (dim.Extent() != 1 && dim.ByteStride() != expected) {
|
|
return std::nullopt;
|
|
}
|
|
expected *= dim.Extent();
|
|
}
|
|
layout.pitchBytes = static_cast<std::size_t>(pitch);
|
|
return layout;
|
|
}
|
|
|
|
// Collect candidate row widths from the descriptor's leading contiguous
|
|
// dimensions, starting with one element.
|
|
static int GetContiguousLeadingBytes(
|
|
const Descriptor &desc, std::size_t *bytes) {
|
|
const auto elemBytes = desc.ElementBytes();
|
|
if (elemBytes == 0) {
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
bytes[count++] = elemBytes;
|
|
std::size_t contiguousBytes = elemBytes;
|
|
for (int j = 0; j < desc.rank(); ++j) {
|
|
const auto &dim = desc.GetDimension(j);
|
|
if (dim.Extent() != 1 &&
|
|
(dim.ByteStride() < 0 ||
|
|
static_cast<std::size_t>(dim.ByteStride()) != contiguousBytes)) {
|
|
break;
|
|
}
|
|
contiguousBytes *= dim.Extent();
|
|
if (contiguousBytes != bytes[count - 1]) {
|
|
bytes[count++] = contiguousBytes;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Choose the largest row width that is contiguous in both descriptors, so
|
|
// leading-dimension slices can be copied as wider cudaMemcpy2D rows.
|
|
static std::size_t GetMemcpy2DWidthBytes(
|
|
const Descriptor &dst, const Descriptor &src) {
|
|
std::size_t dstBytes[maxRank + 1];
|
|
std::size_t srcBytes[maxRank + 1];
|
|
const int dstCount = GetContiguousLeadingBytes(dst, dstBytes);
|
|
const int srcCount = GetContiguousLeadingBytes(src, srcBytes);
|
|
for (int j = dstCount - 1; j >= 0; --j) {
|
|
for (int k = srcCount - 1; k >= 0; --k) {
|
|
if (dstBytes[j] == srcBytes[k]) {
|
|
return dstBytes[j];
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Try to use cudaMemcpy2D for a memcpy of two descriptors, returning true if
|
|
// successful. False if the 2D data transfer is not possible.
|
|
static bool DoMemcpy2D(const Descriptor &dst, const Descriptor &src,
|
|
cudaMemcpyKind kind, const char *sourceFile, int sourceLine) {
|
|
if (dst.ElementBytes() != src.ElementBytes() ||
|
|
dst.Elements() != src.Elements())
|
|
return false;
|
|
|
|
std::size_t widthBytes = GetMemcpy2DWidthBytes(dst, src);
|
|
if (widthBytes == 0) {
|
|
return false;
|
|
}
|
|
auto dstLayout = GetMemcpy2DLayout(dst, widthBytes);
|
|
auto srcLayout = GetMemcpy2DLayout(src, widthBytes);
|
|
if (!dstLayout || !srcLayout) {
|
|
return false;
|
|
}
|
|
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMemcpy2D(dstLayout->base, dstLayout->pitchBytes, srcLayout->base,
|
|
srcLayout->pitchBytes, widthBytes, dstLayout->height, kind),
|
|
sourceFile, sourceLine);
|
|
return true;
|
|
}
|
|
|
|
static cudaMemcpyKind GetMemcpyKind(
|
|
unsigned mode, const char *sourceFile, int sourceLine) {
|
|
if (mode == kHostToDevice) {
|
|
return cudaMemcpyHostToDevice;
|
|
} else if (mode == kDeviceToHost) {
|
|
return cudaMemcpyDeviceToHost;
|
|
} else if (mode == kDeviceToDevice) {
|
|
return cudaMemcpyDeviceToDevice;
|
|
}
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash("host to host copy not supported");
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
void *RTDEF(CUFMemAlloc)(
|
|
std::size_t bytes, unsigned type, const char *sourceFile, int sourceLine) {
|
|
void *ptr = nullptr;
|
|
bytes = bytes ? bytes : 1;
|
|
if (type == kMemTypeDevice) {
|
|
if (Fortran::runtime::executionEnvironment.cudaDeviceIsManaged) {
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMallocManaged((void **)&ptr, bytes, cudaMemAttachGlobal),
|
|
sourceFile, sourceLine);
|
|
} else {
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMalloc((void **)&ptr, bytes), sourceFile, sourceLine);
|
|
}
|
|
} else if (type == kMemTypeManaged || type == kMemTypeUnified) {
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMallocManaged((void **)&ptr, bytes, cudaMemAttachGlobal),
|
|
sourceFile, sourceLine);
|
|
} else if (type == kMemTypePinned) {
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMallocHost((void **)&ptr, bytes), sourceFile, sourceLine);
|
|
} else {
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash("unsupported memory type");
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
void RTDEF(CUFMemFree)(
|
|
void *ptr, unsigned type, const char *sourceFile, int sourceLine) {
|
|
if (!ptr)
|
|
return;
|
|
if (type == kMemTypeDevice || type == kMemTypeManaged ||
|
|
type == kMemTypeUnified) {
|
|
CUDA_REPORT_IF_ERROR_LOC(cudaFree(ptr), sourceFile, sourceLine);
|
|
} else if (type == kMemTypePinned) {
|
|
CUDA_REPORT_IF_ERROR_LOC(cudaFreeHost(ptr), sourceFile, sourceLine);
|
|
} else {
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash("unsupported memory type");
|
|
}
|
|
}
|
|
|
|
void RTDEF(CUFMemsetDescriptor)(
|
|
Descriptor *desc, void *value, const char *sourceFile, int sourceLine) {
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash("not yet implemented: CUDA data transfer from a scalar "
|
|
"value to a descriptor");
|
|
}
|
|
|
|
void RTDEF(CUFDataTransferPtrPtr)(void *dst, void *src, std::size_t bytes,
|
|
unsigned mode, const char *sourceFile, int sourceLine) {
|
|
cudaMemcpyKind kind;
|
|
if (mode == kHostToDevice) {
|
|
kind = cudaMemcpyHostToDevice;
|
|
} else if (mode == kDeviceToHost) {
|
|
kind = cudaMemcpyDeviceToHost;
|
|
} else if (mode == kDeviceToDevice) {
|
|
kind = cudaMemcpyDeviceToDevice;
|
|
} else {
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash("host to host copy not supported");
|
|
}
|
|
// TODO: Use cudaMemcpyAsync when we have support for stream.
|
|
CUDA_REPORT_IF_ERROR_LOC(
|
|
cudaMemcpy(dst, src, bytes, kind), sourceFile, sourceLine);
|
|
}
|
|
|
|
void RTDEF(CUFDataTransferPtrDesc)(void *addr, Descriptor *desc,
|
|
std::size_t bytes, unsigned mode, const char *sourceFile, int sourceLine) {
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
terminator.Crash(
|
|
"not yet implemented: CUDA data transfer from a descriptor to a pointer");
|
|
}
|
|
|
|
void RTDECL(CUFDataTransferDescDesc)(Descriptor *dstDesc, Descriptor *srcDesc,
|
|
unsigned mode, const char *sourceFile, int sourceLine) {
|
|
MemmoveFct memmoveFct;
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
if (mode == kHostToDevice) {
|
|
memmoveFct = &MemmoveHostToDevice;
|
|
} else if (mode == kDeviceToHost) {
|
|
memmoveFct = &MemmoveDeviceToHost;
|
|
} else if (mode == kDeviceToDevice) {
|
|
memmoveFct = &MemmoveDeviceToDevice;
|
|
} else {
|
|
terminator.Crash("host to host copy not supported");
|
|
}
|
|
// Allocate dst descriptor if not allocated.
|
|
if (!dstDesc->IsAllocated()) {
|
|
dstDesc->ApplyMold(*srcDesc, dstDesc->rank());
|
|
dstDesc->Allocate(/*asyncObject=*/nullptr);
|
|
}
|
|
if ((srcDesc->rank() > 0) && (dstDesc->Elements() <= srcDesc->Elements()) &&
|
|
srcDesc->IsContiguous() && dstDesc->IsContiguous()) {
|
|
// Special case when rhs is bigger than lhs and both are contiguous arrays.
|
|
// In this case we do a simple ptr to ptr transfer with the size of lhs.
|
|
// This is be allowed in the reference compiler and it avoids error
|
|
// triggered in the Assign runtime function used for the main case below.
|
|
RTNAME(CUFDataTransferPtrPtr)(dstDesc->raw().base_addr,
|
|
srcDesc->raw().base_addr, dstDesc->Elements() * dstDesc->ElementBytes(),
|
|
mode, sourceFile, sourceLine);
|
|
} else {
|
|
cudaMemcpyKind kind = GetMemcpyKind(mode, sourceFile, sourceLine);
|
|
// Try to use cudaMemcpy2D first, if it fails, fall back to
|
|
// Fortran::runtime::Assign.
|
|
if (DoMemcpy2D(*dstDesc, *srcDesc, kind, sourceFile, sourceLine)) {
|
|
return;
|
|
}
|
|
Fortran::runtime::Assign(
|
|
*dstDesc, *srcDesc, terminator, MaybeReallocate, memmoveFct);
|
|
}
|
|
}
|
|
|
|
void RTDECL(CUFDataTransferCstDesc)(Descriptor *dstDesc, Descriptor *srcDesc,
|
|
unsigned mode, const char *sourceFile, int sourceLine) {
|
|
MemmoveFct memmoveFct;
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
if (mode == kHostToDevice) {
|
|
memmoveFct = &MemmoveHostToDevice;
|
|
} else if (mode == kDeviceToHost) {
|
|
memmoveFct = &MemmoveDeviceToHost;
|
|
} else if (mode == kDeviceToDevice) {
|
|
memmoveFct = &MemmoveDeviceToDevice;
|
|
} else {
|
|
terminator.Crash("host to host copy not supported");
|
|
}
|
|
|
|
Fortran::runtime::DoFromSourceAssign(
|
|
*dstDesc, *srcDesc, terminator, memmoveFct);
|
|
}
|
|
|
|
void RTDECL(CUFDataTransferDescDescNoRealloc)(Descriptor *dstDesc,
|
|
Descriptor *srcDesc, unsigned mode, const char *sourceFile,
|
|
int sourceLine) {
|
|
MemmoveFct memmoveFct;
|
|
Terminator terminator{sourceFile, sourceLine};
|
|
if (mode == kHostToDevice) {
|
|
memmoveFct = &MemmoveHostToDevice;
|
|
} else if (mode == kDeviceToHost) {
|
|
memmoveFct = &MemmoveDeviceToHost;
|
|
} else if (mode == kDeviceToDevice) {
|
|
memmoveFct = &MemmoveDeviceToDevice;
|
|
} else {
|
|
terminator.Crash("host to host copy not supported");
|
|
}
|
|
Fortran::runtime::Assign(
|
|
*dstDesc, *srcDesc, terminator, NoAssignFlags, memmoveFct);
|
|
}
|
|
|
|
void RTDECL(CUFDataTransferGlobalDescDesc)(Descriptor *dstDesc,
|
|
Descriptor *srcDesc, unsigned mode, const char *sourceFile,
|
|
int sourceLine) {
|
|
RTNAME(CUFDataTransferDescDesc)
|
|
(dstDesc, srcDesc, mode, sourceFile, sourceLine);
|
|
if ((mode == kHostToDevice) || (mode == kDeviceToDevice)) {
|
|
void *deviceAddr{
|
|
RTNAME(CUFGetDeviceAddress)((void *)dstDesc, sourceFile, sourceLine)};
|
|
RTNAME(CUFDescriptorSync)
|
|
((Descriptor *)deviceAddr, dstDesc, sourceFile, sourceLine);
|
|
}
|
|
}
|
|
}
|
|
} // namespace Fortran::runtime::cuda
|