[NVPTX] Assign correct memory attrs to args in NVPTXLowerArgs (#207461)
diff --git a/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp b/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
index 3e8eb66..659f729 100644
--- a/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXLowerArgs.cpp
@@ -268,6 +268,13 @@
       Arg.getParamAlign().value_or(DL.getPrefTypeAlign(ByValType)));
   Arg.replaceAllUsesWith(AllocA);
 
+  // If the parameter is never read (writeonly or readnone), there is nothing to
+  // copy in; the alloca above already provides the writable local storage the
+  // body needs, and reading the param here would contradict the attribute.
+  if (Arg.hasAttribute(Attribute::ReadNone) ||
+      Arg.hasAttribute(Attribute::WriteOnly))
+    return;
+
   // Be sure to propagate alignment to this copy; LLVM doesn't know that NVPTX
   // addrspacecast preserves alignment.  Since params are constant, this copy
   // is definitely not volatile.
@@ -343,6 +350,20 @@
   copyByValParam(F, OldArg, NewParamArg);
 }
 
+// Mark a param-space byval argument as non-writable.
+static void markArgNonWritable(Argument &Arg) {
+  if (Arg.onlyReadsMemory())
+    return;
+
+  if (Arg.hasAttribute(Attribute::WriteOnly)) {
+    Arg.removeAttr(Attribute::WriteOnly);
+    Arg.addAttr(Attribute::ReadNone);
+    return;
+  }
+
+  Arg.addAttr(Attribute::ReadOnly);
+}
+
 // Rewrite a kernel's signature so that each byval argument is declared directly
 // as a pointer in the param address space, then lower the body to match. This
 // creates a new function, moves the body across, and erases \p F.
@@ -366,11 +387,11 @@
 
   // ISel reads the param symbol directly for kernel byval arguments; this is
   // valid because the signature rewrite above puts them in the param address
-  // space. Mark them readonly: any mutation is redirected to a local copy
+  // space. Mark them non-writable: any mutation is redirected to a local copy
   // below, so the param itself is never written.
   for (Argument &NewArg : NF->args())
     if (NewArg.hasByValAttr())
-      NewArg.addAttr(Attribute::ReadOnly);
+      markArgNonWritable(NewArg);
 
   // Take over F's name and uses (e.g. @llvm.used, nvvm.annotations metadata),
   // then move the body across.
diff --git a/llvm/test/CodeGen/NVPTX/lower-byval-args-mem-attrs.ll b/llvm/test/CodeGen/NVPTX/lower-byval-args-mem-attrs.ll
new file mode 100644
index 0000000..5bfdca8
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/lower-byval-args-mem-attrs.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -mcpu=sm_70 -mattr=ptx77 -passes=nvptx-lower-args -S | FileCheck %s --check-prefixes=CHECK
+; RUN: opt < %s -mcpu=sm_70 -mattr=ptx77 -passes=nvptx-copy-byval-args -S | FileCheck %s --check-prefixes=COPY
+
+target triple = "nvptx64-nvidia-cuda"
+
+%struct.S = type { i32, i32 }
+
+declare void @escape(ptr)
+
+define ptx_kernel void @byval_write_only(ptr writeonly byval(%struct.S) align 4 %s) {
+; CHECK-LABEL: define ptx_kernel void @byval_write_only(
+; CHECK-SAME: ptr addrspace(101) readnone byval([[STRUCT_S:%.*]]) align 4 [[S1:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT_S]], align 4
+; CHECK-NEXT:    store i32 0, ptr [[S]], align 4
+; CHECK-NEXT:    ret void
+;
+; COPY-LABEL: define ptx_kernel void @byval_write_only(
+; COPY-SAME: ptr writeonly byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) #[[ATTR0:[0-9]+]] {
+; COPY-NEXT:    [[S1:%.*]] = alloca [[STRUCT_S]], align 4
+; COPY-NEXT:    store i32 0, ptr [[S1]], align 4
+; COPY-NEXT:    ret void
+;
+  store i32 0, ptr %s, align 4
+  ret void
+}
+
+define ptx_kernel void @byval_read_none(ptr readnone byval(%struct.S) align 4 %s, ptr %out) {
+; CHECK-LABEL: define ptx_kernel void @byval_read_none(
+; CHECK-SAME: ptr addrspace(101) readnone byval([[STRUCT_S:%.*]]) align 4 [[S1:%.*]], ptr [[OUT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT_S]], align 4
+; CHECK-NEXT:    [[I:%.*]] = ptrtoint ptr [[S]] to i64
+; CHECK-NEXT:    store i64 [[I]], ptr [[OUT]], align 8
+; CHECK-NEXT:    ret void
+;
+; COPY-LABEL: define ptx_kernel void @byval_read_none(
+; COPY-SAME: ptr readnone byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]], ptr [[OUT:%.*]]) #[[ATTR0]] {
+; COPY-NEXT:    [[S1:%.*]] = alloca [[STRUCT_S]], align 4
+; COPY-NEXT:    [[I:%.*]] = ptrtoint ptr [[S1]] to i64
+; COPY-NEXT:    store i64 [[I]], ptr [[OUT]], align 8
+; COPY-NEXT:    ret void
+;
+  %i = ptrtoint ptr %s to i64
+  store i64 %i, ptr %out, align 8
+  ret void
+}
+
+define ptx_kernel void @byval_plain(ptr byval(%struct.S) align 4 %s) {
+; CHECK-LABEL: define ptx_kernel void @byval_plain(
+; CHECK-SAME: ptr addrspace(101) readonly byval([[STRUCT_S:%.*]]) align 4 [[S1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT_S]], align 4
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p101.i64(ptr align 4 [[S]], ptr addrspace(101) align 4 [[S1]], i64 8, i1 false)
+; CHECK-NEXT:    call void @escape(ptr [[S]])
+; CHECK-NEXT:    ret void
+;
+; COPY-LABEL: define ptx_kernel void @byval_plain(
+; COPY-SAME: ptr byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) #[[ATTR0]] {
+; COPY-NEXT:    [[S1:%.*]] = alloca [[STRUCT_S]], align 4
+; COPY-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S1]], ptr align 4 [[S]], i64 8, i1 false)
+; COPY-NEXT:    call void @escape(ptr [[S1]])
+; COPY-NEXT:    ret void
+;
+  call void @escape(ptr %s)
+  ret void
+}
+
+define ptx_kernel void @byval_read_only(ptr readonly byval(%struct.S) align 4 %s) {
+; CHECK-LABEL: define ptx_kernel void @byval_read_only(
+; CHECK-SAME: ptr addrspace(101) readonly byval([[STRUCT_S:%.*]]) align 4 [[S1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT_S]], align 4
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p101.i64(ptr align 4 [[S]], ptr addrspace(101) align 4 [[S1]], i64 8, i1 false)
+; CHECK-NEXT:    call void @escape(ptr [[S]])
+; CHECK-NEXT:    ret void
+;
+; COPY-LABEL: define ptx_kernel void @byval_read_only(
+; COPY-SAME: ptr readonly byval([[STRUCT_S:%.*]]) align 4 [[S:%.*]]) #[[ATTR0]] {
+; COPY-NEXT:    [[S1:%.*]] = alloca [[STRUCT_S]], align 4
+; COPY-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S1]], ptr align 4 [[S]], i64 8, i1 false)
+; COPY-NEXT:    call void @escape(ptr [[S1]])
+; COPY-NEXT:    ret void
+;
+  call void @escape(ptr %s)
+  ret void
+}