[PATCH] D136822: [InstCombine] Allow memcpys from constant memory to readonly noalias parameters to be elided.

Patrick Walton via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 27 01:51:29 PDT 2022


pcwalton created this revision.
Herald added subscribers: jeroen.dobbelaere, JDevlieghere, hiraditya.
Herald added a project: All.
pcwalton requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Currently, InstCombine can elide a memcpy from a constant to a local alloca if
that alloca is passed as a nocapture parameter to a *function* that's readnone
or readonly, but it can't forward the memcpy if the *argument* is marked
readonly noalias nocapture, even though readonly noalias guarantees that the
callee won't mutate the pointee. This patch adds support for detecting and
handling such situations, which arise relatively frequently in Rust, a frontend
that liberally emits noalias readonly.

A more general version of this optimization would use alias analysis to check
the call's ModRef info for the pointee, but I was concerned about blowing up
compile time, so for now I'm just checking for one of readnone on the function,
readonly on the function, or readonly noalias on the parameter.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D136822

Files:
  llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
  llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll


Index: llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll
===================================================================
--- /dev/null
+++ llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll
@@ -0,0 +1,22 @@
+; Tests that a local alloca that is the target of a memcpy from a constant that is
+; only passed as a readonly noalias parameter can be eliminated.
+;
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+%struct.Big = type { [1024 x i32] }
+
+ at x = constant { <{ i32, [1023 x i32] }> } { <{ i32, [1023 x i32] }> <{ i32 1, [1023 x i32] zeroinitializer }> }, align 4
+
+; CHECK-LABEL: @bar
+; CHECK-NEXT: call void @foo
+; CHECK-NEXT: ret void
+define void @bar() {
+  %1 = alloca %struct.Big, align 4
+  call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(4096) %1, ptr noundef nonnull align 4 dereferenceable(4096) @x, i64 4096, i1 false)
+  call void @foo(ptr noundef nonnull nocapture noalias readonly %1) #4
+  ret void
+}
+
+declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
+
+declare void @foo(ptr noundef nocapture noalias readonly)
Index: llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
===================================================================
--- llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -39,8 +39,8 @@
 /// the alloca, and if the source pointer is a pointer to a constant global, we
 /// can optimize this.
 static bool
-isOnlyCopiedFromConstantMemory(AAResults *AA,
-                               Value *V, MemTransferInst *&TheCopy,
+isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V,
+                               MemTransferInst *&TheCopy,
                                SmallVectorImpl<Instruction *> &ToDelete) {
   // We track lifetime intrinsics as we encounter them.  If we decide to go
   // ahead and replace the value with the global, this lets the caller quickly
@@ -85,10 +85,13 @@
         if (IsArgOperand && Call->isInAllocaArgument(DataOpNo))
           return false;
 
-        // If this is a readonly/readnone call site, then we know it is just a
-        // load (but one that potentially returns the value itself), so we can
+        // If this call site doesn't modify the memory, then we know it is just
+        // a load (but one that potentially returns the value itself), so we can
         // ignore it if we know that the value isn't captured.
-        if (Call->onlyReadsMemory() &&
+        if ((Call->onlyReadsMemory() ||
+             (IsArgOperand &&
+              Call->paramHasAttr(DataOpNo, Attribute::ReadOnly) &&
+              Call->paramHasAttr(DataOpNo, Attribute::NoAlias))) &&
             (Call->use_empty() || Call->doesNotCapture(DataOpNo)))
           continue;
 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D136822.471071.patch
Type: text/x-patch
Size: 2907 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20221027/a711994c/attachment.bin>


More information about the llvm-commits mailing list