[funcattrs] Infer writeonly argument attribute

This change extends the current logic for inferring readonly and readnone argument attributes to also infer writeonly.

This change is deliberately minimal; there's a couple of areas for follow up.
* I left out all call handling and thus any benefit from the SCC walk. When examining the test changes, I realized the existing code is imprecise, and am going to fix that in it's own revision before adding in the writeonly handling. (Mostly because updating the tests is hard when I, the human, can't figure out whether the result is correct.)
* I left out handling for storing a value (as opposed to storing to a pointer). This should benefit readonly/readnone as well, and applies to a bunch of other instructions. Seemed worth having as a separate review.

Differential Revision: https://reviews.llvm.org/D114963

GitOrigin-RevId: 740057d185ea0e8b5a4f8b7c78fdf38ae7a66f76
diff --git a/test/CodeGen/SystemZ/systemz-inline-asm.c b/test/CodeGen/SystemZ/systemz-inline-asm.c
index 0994b11..2a656ea 100644
--- a/test/CodeGen/SystemZ/systemz-inline-asm.c
+++ b/test/CodeGen/SystemZ/systemz-inline-asm.c
@@ -123,7 +123,7 @@
 long double test_f128(long double f, long double g) {
   asm("axbr %0, %2" : "=f" (f) : "0" (f), "f" (g));
   return f;
-// CHECK: define{{.*}} void @test_f128(fp128* noalias nocapture sret(fp128) align 8 [[DEST:%.*]], fp128* nocapture readonly %0, fp128* nocapture readonly %1)
+// CHECK: define{{.*}} void @test_f128(fp128* noalias nocapture writeonly sret(fp128) align 8 [[DEST:%.*]], fp128* nocapture readonly %0, fp128* nocapture readonly %1)
 // CHECK: %f = load fp128, fp128* %0
 // CHECK: %g = load fp128, fp128* %1
 // CHECK: [[RESULT:%.*]] = tail call fp128 asm "axbr $0, $2", "=f,0,f"(fp128 %f, fp128 %g)
diff --git a/test/CodeGen/aarch64-sve-acle-__ARM_FEATURE_SVE_VECTOR_OPERATORS.c b/test/CodeGen/aarch64-sve-acle-__ARM_FEATURE_SVE_VECTOR_OPERATORS.c
index 071932a..4fa72c6 100644
--- a/test/CodeGen/aarch64-sve-acle-__ARM_FEATURE_SVE_VECTOR_OPERATORS.c
+++ b/test/CodeGen/aarch64-sve-acle-__ARM_FEATURE_SVE_VECTOR_OPERATORS.c
@@ -59,7 +59,7 @@
 // CHECK128-NEXT:    ret <16 x i8> [[CASTFIXEDSVE]]
 
 // CHECK-LABEL: define{{.*}} void @f2(
-// CHECK-SAME:   <[[#div(VBITS,8)]] x i8>* noalias nocapture sret(<[[#div(VBITS,8)]] x i8>) align 16 %agg.result, <[[#div(VBITS,8)]] x i8>* nocapture readonly %0)
+// CHECK-SAME:   <[[#div(VBITS,8)]] x i8>* noalias nocapture writeonly sret(<[[#div(VBITS,8)]] x i8>) align 16 %agg.result, <[[#div(VBITS,8)]] x i8>* nocapture readonly %0)
 // CHECK-NEXT: entry:
 // CHECK-NEXT:   [[X:%.*]] = load <[[#div(VBITS,8)]] x i8>, <[[#div(VBITS,8)]] x i8>* [[TMP0:%.*]], align 16, [[TBAA6:!tbaa !.*]]
 // CHECK-NEXT:   [[TMP1:%.*]] = call <vscale x 16 x i1> @llvm.aarch64.sve.ptrue.nxv16i1(i32 31)
diff --git a/test/CodeGen/arm-vfp16-arguments2.cpp b/test/CodeGen/arm-vfp16-arguments2.cpp
index 35b7162..b1b466c 100644
--- a/test/CodeGen/arm-vfp16-arguments2.cpp
+++ b/test/CodeGen/arm-vfp16-arguments2.cpp
@@ -37,27 +37,27 @@
   B1 M[1];
 };
 
-// CHECK-SOFT: define{{.*}} void @_Z2f12S1(%struct.S1* noalias nocapture sret(%struct.S1) align 8 %agg.result, [2 x i64] %s1.coerce)
+// CHECK-SOFT: define{{.*}} void @_Z2f12S1(%struct.S1* noalias nocapture writeonly sret(%struct.S1) align 8 %agg.result, [2 x i64] %s1.coerce)
 // CHECK-HARD: define{{.*}} arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f12S1([2 x <2 x i32>] returned %s1.coerce)
 // CHECK-FULL: define{{.*}} arm_aapcs_vfpcc %struct.S1 @_Z2f12S1(%struct.S1 returned %s1.coerce)
 struct S1 f1(struct S1 s1) { return s1; }
 
-// CHECK-SOFT: define{{.*}} void @_Z2f22S2(%struct.S2* noalias nocapture sret(%struct.S2) align 8 %agg.result, [4 x i32] %s2.coerce)
+// CHECK-SOFT: define{{.*}} void @_Z2f22S2(%struct.S2* noalias nocapture writeonly sret(%struct.S2) align 8 %agg.result, [4 x i32] %s2.coerce)
 // CHECK-HARD: define{{.*}} arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f22S2([2 x <2 x i32>] returned %s2.coerce)
 // CHECK-FULL: define{{.*}} arm_aapcs_vfpcc %struct.S2 @_Z2f22S2(%struct.S2 returned %s2.coerce)
 struct S2 f2(struct S2 s2) { return s2; }
 
-// CHECK-SOFT: define{{.*}} void @_Z2f32S3(%struct.S3* noalias nocapture sret(%struct.S3) align 8 %agg.result, [2 x i64] %s3.coerce)
+// CHECK-SOFT: define{{.*}} void @_Z2f32S3(%struct.S3* noalias nocapture writeonly sret(%struct.S3) align 8 %agg.result, [2 x i64] %s3.coerce)
 // CHECK-HARD: define{{.*}} arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f32S3([2 x <2 x i32>] returned %s3.coerce)
 // CHECK-FULL: define{{.*}} arm_aapcs_vfpcc %struct.S3 @_Z2f32S3(%struct.S3 returned %s3.coerce)
 struct S3 f3(struct S3 s3) { return s3; }
 
-// CHECK-SOFT: define{{.*}} void @_Z2f42S4(%struct.S4* noalias nocapture sret(%struct.S4) align 8 %agg.result, [2 x i64] %s4.coerce)
+// CHECK-SOFT: define{{.*}} void @_Z2f42S4(%struct.S4* noalias nocapture writeonly sret(%struct.S4) align 8 %agg.result, [2 x i64] %s4.coerce)
 // CHECK-HARD: define{{.*}} arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f42S4([2 x <2 x i32>] returned %s4.coerce)
 // CHECK-FULL: define{{.*}} arm_aapcs_vfpcc %struct.S4 @_Z2f42S4(%struct.S4 returned %s4.coerce)
 struct S4 f4(struct S4 s4) { return s4; }
 
-// CHECK-SOFT: define{{.*}} void @_Z2f52S5(%struct.S5* noalias nocapture sret(%struct.S5) align 8 %agg.result, [2 x i64] %s5.coerce)
+// CHECK-SOFT: define{{.*}} void @_Z2f52S5(%struct.S5* noalias nocapture writeonly sret(%struct.S5) align 8 %agg.result, [2 x i64] %s5.coerce)
 // CHECK-HARD: define{{.*}} arm_aapcs_vfpcc %struct.S5 @_Z2f52S5(%struct.S5 returned %s5.coerce)
 // CHECK-FULL: define{{.*}} arm_aapcs_vfpcc %struct.S5 @_Z2f52S5(%struct.S5 returned %s5.coerce)
 struct S5 f5(struct S5 s5) { return s5; }
diff --git a/test/CodeGen/mips-vector-return.c b/test/CodeGen/mips-vector-return.c
index dd3c400..dfb0ca3 100644
--- a/test/CodeGen/mips-vector-return.c
+++ b/test/CodeGen/mips-vector-return.c
@@ -8,14 +8,14 @@
 typedef double v4df __attribute__ ((__vector_size__ (32)));
 typedef int v4i32 __attribute__ ((__vector_size__ (16)));
 
-// O32-LABEL: define dso_local void @test_v4sf(<4 x float>* noalias nocapture sret
+// O32-LABEL: define dso_local void @test_v4sf(<4 x float>* noalias nocapture writeonly sret
 // N64: define inreg { i64, i64 } @test_v4sf
 v4sf test_v4sf(float a) {
   return (v4sf){0.0f, a, 0.0f, 0.0f};
 }
 
-// O32-LABEL: define dso_local void @test_v4df(<4 x double>* noalias nocapture sret
-// N64-LABEL: define void @test_v4df(<4 x double>* noalias nocapture sret
+// O32-LABEL: define dso_local void @test_v4df(<4 x double>* noalias nocapture writeonly sret
+// N64-LABEL: define void @test_v4df(<4 x double>* noalias nocapture writeonly sret
 v4df test_v4df(double a) {
   return (v4df){0.0, a, 0.0, 0.0};
 }
diff --git a/test/CodeGen/mips64-nontrivial-return.cpp b/test/CodeGen/mips64-nontrivial-return.cpp
index 2164b20..31a3c1e 100644
--- a/test/CodeGen/mips64-nontrivial-return.cpp
+++ b/test/CodeGen/mips64-nontrivial-return.cpp
@@ -10,7 +10,7 @@
 
 extern D gd0;
 
-// CHECK: _Z4foo1v(%class.D* noalias nocapture sret
+// CHECK: _Z4foo1v(%class.D* noalias nocapture writeonly sret
 
 D foo1(void) {
   return gd0;
diff --git a/test/CodeGen/ms-mixed-ptr-sizes.c b/test/CodeGen/ms-mixed-ptr-sizes.c
index ececa42..294a891 100644
--- a/test/CodeGen/ms-mixed-ptr-sizes.c
+++ b/test/CodeGen/ms-mixed-ptr-sizes.c
@@ -7,32 +7,32 @@
 };
 void use_foo(struct Foo *f);
 void test_sign_ext(struct Foo *f, int * __ptr32 __sptr i) {
-// X64-LABEL: define dso_local void @test_sign_ext({{.*}}i32 addrspace(270)* %i)
-// X86-LABEL: define dso_local void @test_sign_ext(%struct.Foo* %f, i32* %i)
+// X64-LABEL: define dso_local void @test_sign_ext({{.*}}i32 addrspace(270)* writeonly %i)
+// X86-LABEL: define dso_local void @test_sign_ext(%struct.Foo* %f, i32* writeonly %i)
 // X64: %{{.+}} = addrspacecast i32 addrspace(270)* %i to i32*
 // X86: %{{.+}} = addrspacecast i32* %i to i32 addrspace(272)*
   f->p64 = i;
   use_foo(f);
 }
 void test_zero_ext(struct Foo *f, int * __ptr32 __uptr i) {
-// X64-LABEL: define dso_local void @test_zero_ext({{.*}}i32 addrspace(271)* %i)
-// X86-LABEL: define dso_local void @test_zero_ext({{.*}}i32 addrspace(271)* %i)
+// X64-LABEL: define dso_local void @test_zero_ext({{.*}}i32 addrspace(271)* writeonly %i)
+// X86-LABEL: define dso_local void @test_zero_ext({{.*}}i32 addrspace(271)* writeonly %i)
 // X64: %{{.+}} = addrspacecast i32 addrspace(271)* %i to i32*
 // X86: %{{.+}} = addrspacecast i32 addrspace(271)* %i to i32 addrspace(272)*
   f->p64 = i;
   use_foo(f);
 }
 void test_trunc(struct Foo *f, int * __ptr64 i) {
-// X64-LABEL: define dso_local void @test_trunc(%struct.Foo* %f, i32* %i)
-// X86-LABEL: define dso_local void @test_trunc({{.*}}i32 addrspace(272)* %i)
+// X64-LABEL: define dso_local void @test_trunc(%struct.Foo* %f, i32* writeonly %i)
+// X86-LABEL: define dso_local void @test_trunc({{.*}}i32 addrspace(272)* writeonly %i)
 // X64: %{{.+}} = addrspacecast i32* %i to i32 addrspace(270)*
 // X86: %{{.+}} = addrspacecast i32 addrspace(272)* %i to i32*
   f->p32 = i;
   use_foo(f);
 }
 void test_noop(struct Foo *f, int * __ptr32 i) {
-// X64-LABEL: define dso_local void @test_noop({{.*}}i32 addrspace(270)* %i)
-// X86-LABEL: define dso_local void @test_noop({{.*}}i32* %i)
+// X64-LABEL: define dso_local void @test_noop({{.*}}i32 addrspace(270)* writeonly %i)
+// X86-LABEL: define dso_local void @test_noop({{.*}}i32* writeonly %i)
 // X64-NOT: addrspacecast
 // X86-NOT: addrspacecast
   f->p32 = i;
@@ -40,8 +40,8 @@
 }
 
 void test_other(struct Foo *f, __attribute__((address_space(10))) int *i) {
-// X64-LABEL: define dso_local void @test_other({{.*}}i32 addrspace(10)* %i)
-// X86-LABEL: define dso_local void @test_other({{.*}}i32 addrspace(10)* %i)
+// X64-LABEL: define dso_local void @test_other({{.*}}i32 addrspace(10)* writeonly %i)
+// X86-LABEL: define dso_local void @test_other({{.*}}i32 addrspace(10)* writeonly %i)
 // X64: %{{.+}} = addrspacecast i32 addrspace(10)* %i to i32 addrspace(270)*
 // X86: %{{.+}} = addrspacecast i32 addrspace(10)* %i to i32*
   f->p32 = (int * __ptr32)i;
diff --git a/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl b/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
index 0a8abf9..17333cc 100644
--- a/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
+++ b/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
@@ -439,7 +439,7 @@
   return s;
 }
 
-// CHECK: define{{.*}} void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture sret(%struct.flexible_array) align 4 %agg.result)
+// CHECK: define{{.*}} void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture writeonly sret(%struct.flexible_array) align 4 %agg.result)
 flexible_array func_flexible_array_ret()
 {
   flexible_array s = { 0 };
diff --git a/test/CodeGenOpenCL/amdgpu-call-kernel.cl b/test/CodeGenOpenCL/amdgpu-call-kernel.cl
index 9ecdeeb..7bce2f9 100755
--- a/test/CodeGenOpenCL/amdgpu-call-kernel.cl
+++ b/test/CodeGenOpenCL/amdgpu-call-kernel.cl
@@ -1,6 +1,6 @@
 // REQUIRES: amdgpu-registered-target
 // RUN: %clang_cc1 -triple amdgcn-unknown-unknown -S -emit-llvm -o - %s | FileCheck %s
-// CHECK: define{{.*}} amdgpu_kernel void @test_call_kernel(i32 addrspace(1)* nocapture %out)
+// CHECK: define{{.*}} amdgpu_kernel void @test_call_kernel(i32 addrspace(1)* nocapture writeonly %out)
 // CHECK: store i32 4, i32 addrspace(1)* %out, align 4
 
 kernel void test_kernel(global int *out)
diff --git a/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl b/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
index 3b5a784..641e63c 100644
--- a/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
+++ b/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
@@ -28,7 +28,7 @@
 // CHECK: spir_kernel
 // AMDGCN: define{{.*}} amdgpu_kernel void @test_single
 // CHECK: struct.int_single* nocapture {{.*}} byval(%struct.int_single)
-// CHECK: i32* nocapture %output
+// CHECK: i32* nocapture writeonly %output
  output[0] = input.a;
 }
 
@@ -36,7 +36,7 @@
 // CHECK: spir_kernel
 // AMDGCN: define{{.*}} amdgpu_kernel void @test_pair
 // CHECK: struct.int_pair* nocapture {{.*}} byval(%struct.int_pair)
-// CHECK: i32* nocapture %output
+// CHECK: i32* nocapture writeonly %output
  output[0] = (int)input.a;
  output[1] = (int)input.b;
 }
@@ -45,7 +45,7 @@
 // CHECK: spir_kernel
 // AMDGCN: define{{.*}} amdgpu_kernel void @test_kernel
 // CHECK: struct.test_struct* nocapture {{.*}} byval(%struct.test_struct)
-// CHECK: i32* nocapture %output
+// CHECK: i32* nocapture writeonly %output
  output[0] = input.elementA;
  output[1] = input.elementB;
  output[2] = (int)input.elementC;
@@ -59,7 +59,7 @@
 void test_function(int_pair input, global int* output) {
 // CHECK-NOT: spir_kernel
 // AMDGCN-NOT: define{{.*}} amdgpu_kernel void @test_function
-// CHECK: i64 %input.coerce0, i64 %input.coerce1, i32* nocapture %output
+// CHECK: i64 %input.coerce0, i64 %input.coerce1, i32* nocapture writeonly %output
  output[0] = (int)input.a;
  output[1] = (int)input.b;
 }