[clang] [llvm] [MemCpyOpt] Remove libcall availability checks for memory intrinsics (PR #198558)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 2 08:56:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Addmisol (addmisol)
<details>
<summary>Changes</summary>
Fixes #<!-- -->197683
- Delete the enable-memcpyopt-without-libcalls flag from MemCpyOptimizer
- Remove libcall availability checks in processStoreOfLoad() and processStore()
- Remove the flag from CUDA/OpenMP toolchain in Clang
- Update no-libcalls.ll test to expect optimizations on all targets
cc @<!-- -->arsenm
---
Full diff: https://github.com/llvm/llvm-project/pull/198558.diff
3 Files Affected:
- (modified) clang/lib/Driver/ToolChains/Cuda.cpp (+1-3)
- (modified) llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp (+1-18)
- (modified) llvm/test/Transforms/MemCpyOpt/no-libcalls.ll (+16-30)
``````````diff
diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp
index 7ad712adf3eb4..94d2c4e839236 100644
--- a/clang/lib/Driver/ToolChains/Cuda.cpp
+++ b/clang/lib/Driver/ToolChains/Cuda.cpp
@@ -917,9 +917,7 @@ void CudaToolChain::addClangTargetOptions(
DeviceOffloadingKind == Action::OFK_Cuda || UsesLLVMOffloading) &&
"Only OpenMP or CUDA offloading kinds are supported for NVIDIA GPUs.");
- CC1Args.append({"-fcuda-is-device", "-mllvm",
- "-enable-memcpyopt-without-libcalls",
- "-fno-threadsafe-statics"});
+ CC1Args.append({"-fcuda-is-device", "-fno-threadsafe-statics"});
if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr,
options::OPT_fno_cuda_short_ptr, false))
diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index 538e50d6fc74e..6f8c573a6dc69 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -64,10 +64,6 @@ using namespace llvm;
#define DEBUG_TYPE "memcpyopt"
-static cl::opt<bool> EnableMemCpyOptWithoutLibcalls(
- "enable-memcpyopt-without-libcalls", cl::Hidden,
- cl::desc("Enable memcpyopt even when libcalls are disabled"));
-
STATISTIC(NumMemCpyInstr, "Number of memcpy instructions deleted");
STATISTIC(NumMemMoveInstr, "Number of memmove instructions deleted");
STATISTIC(NumMemSetInfer, "Number of memsets inferred");
@@ -636,13 +632,7 @@ bool MemCpyOptPass::processStoreOfLoad(StoreInst *SI, LoadInst *LI,
BatchAAResults BAA(*AA, EEA);
auto *T = LI->getType();
- // Don't introduce calls to memcpy/memmove intrinsics out of thin air if
- // the corresponding libcalls are not available.
- // TODO: We should really distinguish between libcall availability and
- // our ability to introduce intrinsics.
- if (T->isAggregateType() &&
- (EnableMemCpyOptWithoutLibcalls ||
- (TLI->has(LibFunc_memcpy) && TLI->has(LibFunc_memmove)))) {
+ if (T->isAggregateType()) {
MemoryLocation LoadLoc = MemoryLocation::get(LI);
// We use alias analysis to check if an instruction may store to
@@ -768,13 +758,6 @@ bool MemCpyOptPass::processStore(StoreInst *SI, BasicBlock::iterator &BBI) {
if (auto *LI = dyn_cast<LoadInst>(StoredVal))
return processStoreOfLoad(SI, LI, DL, BBI);
- // The following code creates memset intrinsics out of thin air. Don't do
- // this if the corresponding libfunc is not available.
- // TODO: We should really distinguish between libcall availability and
- // our ability to introduce intrinsics.
- if (!(TLI->has(LibFunc_memset) || EnableMemCpyOptWithoutLibcalls))
- return false;
-
// There are two cases that are interesting for this code to handle: memcpy
// and memset. Right now we only handle memset.
diff --git a/llvm/test/Transforms/MemCpyOpt/no-libcalls.ll b/llvm/test/Transforms/MemCpyOpt/no-libcalls.ll
index f6b6fcd2d0ac7..bed32537e475d 100644
--- a/llvm/test/Transforms/MemCpyOpt/no-libcalls.ll
+++ b/llvm/test/Transforms/MemCpyOpt/no-libcalls.ll
@@ -1,29 +1,20 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=memcpyopt -mtriple=x86_64 < %s | FileCheck %s --check-prefixes=CHECK,LIBCALLS
-; RUN: opt -S -passes=memcpyopt -mtriple=amdgpu-- < %s | FileCheck %s --check-prefixes=CHECK,NO-LIBCALLS
-; RUN: opt -S -passes=memcpyopt -mtriple=amdgpu-- -enable-memcpyopt-without-libcalls < %s \
-; RUN: | FileCheck %s --check-prefixes=CHECK,LIBCALLS
+; RUN: opt -S -passes=memcpyopt -mtriple=x86_64 < %s | FileCheck %s
+; RUN: opt -S -passes=memcpyopt -mtriple=amdgcn-- < %s | FileCheck %s
+
+; Optimization should happen regardless of libcall availability, as
+; PreISelIntrinsicLowering can expand memory intrinsics into loops.
; REQUIRES: amdgpu-registered-target
; REQUIRES: x86-registered-target
-define void @dont_create_memset(ptr %p) {
-; LIBCALLS-LABEL: @dont_create_memset(
-; LIBCALLS-NEXT: [[P1:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1
-; LIBCALLS-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P]], i64 2
-; LIBCALLS-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P]], i64 3
-; LIBCALLS-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[P]], i8 0, i64 16, i1 false)
-; LIBCALLS-NEXT: ret void
-;
-; NO-LIBCALLS-LABEL: @dont_create_memset(
-; NO-LIBCALLS-NEXT: store i32 0, ptr [[P:%.*]], align 4
-; NO-LIBCALLS-NEXT: [[P1:%.*]] = getelementptr i32, ptr [[P]], i64 1
-; NO-LIBCALLS-NEXT: store i32 0, ptr [[P1]], align 4
-; NO-LIBCALLS-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P]], i64 2
-; NO-LIBCALLS-NEXT: store i32 0, ptr [[P2]], align 4
-; NO-LIBCALLS-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P]], i64 3
-; NO-LIBCALLS-NEXT: store i32 0, ptr [[P3]], align 4
-; NO-LIBCALLS-NEXT: ret void
+define void @stores_to_memset(ptr %p) {
+; CHECK-LABEL: @stores_to_memset(
+; CHECK-NEXT: [[P1:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1
+; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P]], i64 2
+; CHECK-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P]], i64 3
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[P]], i8 0, i64 16, i1 false)
+; CHECK-NEXT: ret void
;
store i32 0, ptr %p
%p1 = getelementptr i32, ptr %p, i64 1
@@ -37,15 +28,10 @@ define void @dont_create_memset(ptr %p) {
%ty = type { i64 }
-define void @dont_create_memcpy(ptr %p1, ptr %p2) {
-; LIBCALLS-LABEL: @dont_create_memcpy(
-; LIBCALLS-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 [[P2:%.*]], ptr align 8 [[P1:%.*]], i64 8, i1 false)
-; LIBCALLS-NEXT: ret void
-;
-; NO-LIBCALLS-LABEL: @dont_create_memcpy(
-; NO-LIBCALLS-NEXT: [[V:%.*]] = load [[TY:%.*]], ptr [[P1:%.*]], align 8
-; NO-LIBCALLS-NEXT: store [[TY]] [[V]], ptr [[P2:%.*]], align 8
-; NO-LIBCALLS-NEXT: ret void
+define void @load_store_to_memmove(ptr %p1, ptr %p2) {
+; CHECK-LABEL: @load_store_to_memmove(
+; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 [[P2:%.*]], ptr align 8 [[P1:%.*]], i64 8, i1 false)
+; CHECK-NEXT: ret void
;
%v = load %ty, ptr %p1
store %ty %v, ptr %p2
``````````
</details>
https://github.com/llvm/llvm-project/pull/198558
More information about the llvm-commits
mailing list