jdoerfert updated this revision to Diff 193123.
jdoerfert added a comment.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59980/new/

https://reviews.llvm.org/D59980

Files:
  clang/test/CodeGen/arm-vfp16-arguments.c
  clang/test/CodeGen/systemz-inline-asm.c
  clang/test/CodeGenCXX/wasm-args-returns.cpp
  clang/test/CodeGenObjC/os_log.m
  clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
  clang/test/CodeGenOpenCL/amdgpu-call-kernel.cl
  clang/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
  llvm/include/llvm/Transforms/IPO/Attributor.h
  llvm/lib/Transforms/IPO/Attributor.cpp
  llvm/test/Transforms/FunctionAttrs/SCC1.ll
  llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
  llvm/test/Transforms/FunctionAttrs/arg_returned.ll
  llvm/test/Transforms/FunctionAttrs/nocapture.ll
  llvm/test/Transforms/FunctionAttrs/readattrs.ll

Index: llvm/test/Transforms/FunctionAttrs/readattrs.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -33,7 +33,7 @@
   ret void
 }
 
-; CHECK: define void @test5(i8** nocapture %p, i8* %q)
+; CHECK: define void @test5(i8** nocapture writeonly %p, i8* %q)
 ; Missed optz'n: we could make %q readnone, but don't break test6!
 define void @test5(i8** %p, i8* %q) {
   store i8* %q, i8** %p
@@ -41,7 +41,7 @@
 }
 
 declare void @test6_1()
-; CHECK: define void @test6_2(i8** nocapture %p, i8* %q)
+; CHECK: define void @test6_2(i8** nocapture writeonly %p, i8* %q)
 ; This is not a missed optz'n.
 define void @test6_2(i8** %p, i8* %q) {
   store i8* %q, i8** %p
@@ -49,7 +49,7 @@
   ret void
 }
 
-; CHECK: define void @test7_1(i32* inalloca nocapture %a)
+; CHECK: define void @test7_1(i32* inalloca nocapture readnone %a)
 ; inalloca parameters are always considered written
 define void @test7_1(i32* inalloca %a) {
   ret void
@@ -61,7 +61,7 @@
   ret i32* %p
 }
 
-; CHECK: define void @test8_2(i32* nocapture %p)
+; CHECK: define void @test8_2(i32* nocapture writeonly %p)
 define void @test8_2(i32* %p) {
 entry:
   %call = call i32* @test8_1(i32* %p)
Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -134,15 +134,14 @@
 	ret void
 }
 
-; CHECK: define void @test1_1(i8* nocapture %x1_1, i8* nocapture %y1_1)
-; It would be acceptable to add readnone to %y1_1 and %y1_2.
+; CHECK: define void @test1_1(i8* nocapture readnone %x1_1, i8* nocapture readnone %y1_1)
 define void @test1_1(i8* %x1_1, i8* %y1_1) {
   call i8* @test1_2(i8* %x1_1, i8* %y1_1)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define i8* @test1_2(i8* nocapture %x1_2, i8* returned "no-capture-maybe-returned" %y1_2)
+; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* readnone returned "no-capture-maybe-returned" %y1_2)
 define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
   call void @test1_1(i8* %x1_2, i8* %y1_2)
   store i32* null, i32** @g
@@ -156,7 +155,7 @@
   ret void
 }
 
-; CHECK: define void @test3(i8* nocapture %x3, i8* nocapture readnone %y3, i8* nocapture %z3)
+; CHECK: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
 define void @test3(i8* %x3, i8* %y3, i8* %z3) {
   call void @test3(i8* %z3, i8* %y3, i8* %x3)
   store i32* null, i32** @g
@@ -237,7 +236,7 @@
   ret void
 }
 
-; CHECK: @nocaptureStrip(i8* nocapture %p)
+; CHECK: @nocaptureStrip(i8* nocapture writeonly %p)
 define void @nocaptureStrip(i8* %p) {
 entry:
   %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_returned.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -180,8 +180,8 @@
 ; FNATTR: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
 ; FNATTR: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]]
 ;
-; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
 ; ATTRIBUTOR: define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
 ;
 ; double* ptr_scc_r1(double* a, double* b, double* r);
@@ -309,7 +309,7 @@
 ;
 ; BOTH:       define dso_local i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable]]
 ; FNATTR:     define dso_local i32* @calls_unknown_fn(i32* readnone %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
-; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
+; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
 ;
 declare void @unknown_fn(i32* (i32*)*) #0
 
@@ -411,7 +411,7 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
 ; BOTH:       define dso_local double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @bitcast(i32* %b) #0 {
@@ -431,7 +431,7 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
 ; BOTH:       define dso_local double* @bitcasts_select_and_phi(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 {
@@ -466,7 +466,7 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @ret_arg_arg_undef(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @ret_arg_arg_undef(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_arg_arg_undef(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
 ; BOTH:       define dso_local double* @ret_arg_arg_undef(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @ret_arg_arg_undef(i32* %b) #0 {
@@ -501,7 +501,7 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @ret_undef_arg_arg(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @ret_undef_arg_arg(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_undef_arg_arg(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
 ; BOTH:       define dso_local double* @ret_undef_arg_arg(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @ret_undef_arg_arg(i32* %b) #0 {
@@ -536,7 +536,7 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @ret_undef_arg_undef(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @ret_undef_arg_undef(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
+; ATTRIBUTOR: define dso_local double* @ret_undef_arg_undef(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindUwtable]]
 ; BOTH:       define dso_local double* @ret_undef_arg_undef(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @ret_undef_arg_undef(i32* %b) #0 {
Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -129,8 +129,7 @@
 ;
 ; CHECK: define dso_local i64* @scc_B(double* readnone returned "no-capture-maybe-returned" %a)
 ;
-; FIXME: readnone missing for %s
-; CHECK: define dso_local i8* @scc_C(i16* returned "no-capture-maybe-returned" %a)
+; CHECK: define dso_local i8* @scc_C(i16* readnone returned "no-capture-maybe-returned" %a)
 ;
 ; float *scc_A(int *a) {
 ;   return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a);
@@ -259,7 +258,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a)
+; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned writeonly "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @not_captured_but_returned_0(i64* %a) #0 {
 entry:
@@ -275,7 +274,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* "no-capture-maybe-returned" %a)
+; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* writeonly "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @not_captured_but_returned_1(i64* %a) #0 {
 entry:
@@ -291,7 +290,7 @@
 ;   not_captured_but_returned_1(a);
 ; }
 ;
-; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* nocapture %a)
+; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* nocapture writeonly %a)
 ;
 define dso_local void @test_not_captured_but_returned_calls(i64* %a) #0 {
 entry:
@@ -307,7 +306,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned "no-capture-maybe-returned" %a)
+; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned writeonly "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* %a) #0 {
 entry:
@@ -322,7 +321,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local void @negative_test_not_captured_but_returned_call_0b(i64* %a)
+; CHECK: define dso_local void @negative_test_not_captured_but_returned_call_0b(i64* writeonly %a)
 ;
 define dso_local void @negative_test_not_captured_but_returned_call_0b(i64* %a) #0 {
 entry:
@@ -339,7 +338,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* "no-capture-maybe-returned" %a)
+; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* writeonly "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 {
 entry:
@@ -354,7 +353,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local void @negative_test_not_captured_but_returned_call_1b(i64* %a)
+; CHECK: define dso_local void @negative_test_not_captured_but_returned_call_1b(i64* writeonly %a)
 ;
 define dso_local void @negative_test_not_captured_but_returned_call_1b(i64* %a) #0 {
 entry:
@@ -376,10 +375,10 @@
 ;
 ; FNATTR:     define dso_local i32* @ret_arg_or_unknown(i32* %b)
 ; FNATTR:     define dso_local i32* @ret_arg_or_unknown_through_phi(i32* %b)
-; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown(i32* "no-capture-maybe-returned" %b)
-; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown_through_phi(i32* "no-capture-maybe-returned" %b)
-; BOTH:       define dso_local i32* @ret_arg_or_unknown(i32* "no-capture-maybe-returned" %b)
-; BOTH:       define dso_local i32* @ret_arg_or_unknown_through_phi(i32* "no-capture-maybe-returned" %b)
+; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown(i32* readnone "no-capture-maybe-returned" %b)
+; ATTRIBUTOR: define dso_local i32* @ret_arg_or_unknown_through_phi(i32* readnone "no-capture-maybe-returned" %b)
+; BOTH:       define dso_local i32* @ret_arg_or_unknown(i32* readnone "no-capture-maybe-returned" %b)
+; BOTH:       define dso_local i32* @ret_arg_or_unknown_through_phi(i32* readnone "no-capture-maybe-returned" %b)
 ;
 declare dso_local i32* @unknown()
 
Index: llvm/test/Transforms/FunctionAttrs/SCC1.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/SCC1.ll
+++ llvm/test/Transforms/FunctionAttrs/SCC1.ll
@@ -46,7 +46,7 @@
 ;
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
-; CHECK: define dso_local i32* @external_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND:[0-9]*]]
+; CHECK: define dso_local i32* @external_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned writeonly "no-capture-maybe-returned" %w0) #[[NOUNWIND:[0-9]*]]
 define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0)
@@ -56,7 +56,7 @@
   ret i32* %call3
 }
 
-; CHECK: define internal i32* @internal_ret0_nw(i32* returned "no-capture-maybe-returned" %n0, i32* nocapture %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret0_nw(i32* readnone returned "no-capture-maybe-returned" %n0, i32* nocapture writeonly %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
 entry:
   %r0 = alloca i32, align 4
@@ -84,7 +84,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define internal i32* @internal_ret1_rrw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %r1, i32* nocapture %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rrw(i32* nocapture readonly %r0, i32* readonly returned "no-capture-maybe-returned" %r1, i32* nocapture writeonly %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -115,7 +115,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOREC_NOUNWIND:[0-9]*]]
+; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned writeonly "no-capture-maybe-returned" %w0) #[[NOREC_NOUNWIND:[0-9]*]]
 define dso_local i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %tobool = icmp ne i32* %n0, null
@@ -133,7 +133,7 @@
   ret i32* %w0
 }
 
-; CHECK: define internal i32* @internal_ret1_rw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rw(i32* nocapture readonly %r0, i32* returned writeonly "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -158,7 +158,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
+; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned writeonly "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
 define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
Index: llvm/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/lib/Transforms/IPO/Attributor.cpp
+++ llvm/lib/Transforms/IPO/Attributor.cpp
@@ -59,6 +59,13 @@
 STATISTIC(NumFnArgumentNoCapture,
           "Number of function arguments marked no-capture");
 
+STATISTIC(NumFnArgumentReadNone,
+          "Number of function arguments marked read-none");
+STATISTIC(NumFnArgumentReadOnly,
+          "Number of function arguments marked read-only");
+STATISTIC(NumFnArgumentWriteOnly,
+          "Number of function arguments marked write-only");
+
 // TODO: Determine a good default value.
 //
 // In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads
@@ -195,6 +202,15 @@
   case Attribute::NoRecurse:
     NumFnNoRecurse++;
     return;
+  case Attribute::ReadNone:
+    NumFnArgumentReadNone++;
+    return;
+  case Attribute::ReadOnly:
+    NumFnArgumentReadOnly++;
+    return;
+  case Attribute::WriteOnly:
+    NumFnArgumentWriteOnly++;
+    return;
   default:
     return;
   }
@@ -1320,6 +1336,330 @@
                                         : ChangeStatus::CHANGED;
 }
 
+/// -------------------- Memory Behavior Attributes ----------------------------
+/// Includes read-none, read-only, and write-only.
+/// ----------------------------------------------------------------------------
+
+/// A class to hold the state of for memory behaviour attributes.
+///
+/// The state is encoded in the bits of the "char" variables known and assumed
+/// as defined by the encoding bits.
+struct AAMemoryBehaviorImpl : public AAMemoryBehavior, IntegerState {
+
+  AAMemoryBehaviorImpl(Value &V)
+      : AAMemoryBehavior(V), IntegerState(BEST_STATE) {
+    assert(getAssumed() == BEST_STATE && "Expected optimistic initialization!");
+  }
+
+  /// State encoding bits. A set bit in the state means the property holds.
+  /// BEST_STATE is the best possible state, 0 the worst possible state.
+  enum {
+    NO_READS = 1 << 0,
+    NO_WRITES = 1 << 1,
+    NO_ACCESSES = NO_READS | NO_WRITES,
+
+    BEST_STATE = NO_ACCESSES,
+  };
+
+  /// See AAMemoryBehavior::isKnownReadNone();
+  virtual bool isKnownReadNone() const override { return isKnown(NO_ACCESSES); }
+
+  /// See AAMemoryBehavior::isAssumedReadNone();
+  virtual bool isAssumedReadNone() const override {
+    return isAssumed(NO_ACCESSES);
+  }
+
+  /// See AAMemoryBehavior::isKnownReadOnly();
+  virtual bool isKnownReadOnly() const override { return isKnown(NO_WRITES); }
+
+  /// See AAMemoryBehavior::isAssumedReadOnly();
+  virtual bool isAssumedReadOnly() const override {
+    return isAssumed(NO_WRITES);
+  }
+
+  /// See AAMemoryBehavior::isKnownWriteOnly();
+  virtual bool isKnownWriteOnly() const override { return isKnown(NO_READS); }
+
+  /// See AAMemoryBehavior::isAssumedWriteOnly();
+  virtual bool isAssumedWriteOnly() const override {
+    return isAssumed(NO_READS);
+  }
+
+  /// See AbstractState::getAsStr().
+  const std::string getAsStr() const override {
+    std::string S = "";
+    if (isAssumedReadNone())
+      S = "readnone";
+    else if (isAssumedReadOnly())
+      S = "readonly";
+    else if (isAssumedWriteOnly())
+      S = "writeonly";
+    else
+      S = "may-read/write";
+
+    return S;
+  }
+
+  /// Return the memory behavior information of \p V encoded in the IR.
+  static void getKnownStateFromValue(const Value &V, IntegerState &State,
+                                     bool ValueOnly);
+
+  /// See AbstractAttribute::getDeducedAttributes(Attributor &A).
+  virtual void
+  getDeducedAttributes(SmallVectorImpl<Attribute> &Attrs) const override;
+
+  /// See AbstractAttribute::getState()
+  ///{
+  AbstractState &getState() override { return *this; }
+  const AbstractState &getState() const override { return *this; }
+  ///}
+};
+
+void AAMemoryBehaviorImpl::getKnownStateFromValue(const Value &V,
+                                                  IntegerState &State,
+                                                  bool ValueOnly) {
+
+  if (const Argument *Arg = dyn_cast<Argument>(&V)) {
+    if (Arg->hasAttribute(Attribute::ReadNone))
+      State.addKnownBits(NO_ACCESSES);
+    else if (Arg->hasAttribute(Attribute::ReadOnly))
+      State.addKnownBits(NO_WRITES);
+    else if (Arg->hasAttribute(Attribute::WriteOnly))
+      State.addKnownBits(NO_READS);
+    if (!ValueOnly)
+      getKnownStateFromValue(*Arg->getParent(), State, ValueOnly);
+  } else if (const Function *Fn = dyn_cast<Function>(&V)) {
+    if (Fn->hasFnAttribute(Attribute::ReadNone))
+      State.addKnownBits(NO_ACCESSES);
+    else if (Fn->hasFnAttribute(Attribute::ReadOnly))
+      State.addKnownBits(NO_WRITES);
+    else if (Fn->hasFnAttribute(Attribute::WriteOnly))
+      State.addKnownBits(NO_READS);
+  }
+}
+
+void AAMemoryBehaviorImpl::getDeducedAttributes(
+    SmallVectorImpl<Attribute> &Attrs) const {
+  LLVMContext &Ctx = getAnchoredValue().getContext();
+  assert(Attrs.size() == 0);
+  if (isAssumedReadNone())
+    Attrs.push_back(Attribute::get(Ctx, Attribute::ReadNone));
+  else if (isAssumedReadOnly())
+    Attrs.push_back(Attribute::get(Ctx, Attribute::ReadOnly));
+  else if (isAssumedWriteOnly())
+    Attrs.push_back(Attribute::get(Ctx, Attribute::WriteOnly));
+  assert(Attrs.size() <= 1);
+}
+
+/// An AA to represent the memory behavior argument attributes.
+struct AAMemoryBehaviorArgument final : public AAMemoryBehaviorImpl {
+
+  /// See AAMemoryBehaviorImpl::AAMemoryBehaviorImpl(...).
+  AAMemoryBehaviorArgument(Argument &Arg) : AAMemoryBehaviorImpl(Arg) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    Argument &Arg = cast<Argument>(getAnchoredValue());
+
+    if (Arg.getNumUses() == 0)
+      addKnownBits(NO_ACCESSES);
+    else
+      getKnownStateFromValue(Arg, *this, /* ValueOnly */ false);
+  }
+
+  /// See AbstractAttribute::updateImpl(Attributor &A).
+  virtual ChangeStatus updateImpl(Attributor &A) override;
+
+  /// See AbstractAttribute::getManifestPosition().
+  virtual ManifestPosition getManifestPosition() const override {
+    return MP_ARGUMENT;
+  }
+
+  /// See AbstractAttribute::manifest(Attributor &A).
+  virtual ChangeStatus manifest(Attributor &A) override;
+
+private:
+  /// Return true if users of \p UserI might access the underlying
+  /// variable/location described by \p U.
+  bool followUseIn(Attributor &A, const Use *U, const Instruction *UserI);
+
+  /// Update the state according to the effect of use \p U in \p UserI.
+  void analyzeUseIn(Attributor &A, const Use *U, const Instruction *UserI);
+
+  /// Container for (transitive) uses of the associated argument.
+  SmallVector<const Use *, 32> Uses;
+
+  /// Container for visited (transitive) uses of the associated argument.
+  SmallPtrSet<const Use *, 32> VisitedUses;
+};
+
+ChangeStatus AAMemoryBehaviorArgument::manifest(Attributor &A) {
+  Argument &Arg = cast<Argument>(getAnchoredValue());
+
+  // Check if we would improve the existing attributes first.
+  IntegerState ExistingState;
+  getKnownStateFromValue(Arg, ExistingState,
+                         /* value only */ true);
+  if (ExistingState.isKnown(getAssumed()))
+    return ChangeStatus::UNCHANGED;
+
+  // Remove existing attributes as they not always mix well with derived ones.
+  Arg.removeAttr(Attribute::ReadNone);
+  Arg.removeAttr(Attribute::ReadOnly);
+  Arg.removeAttr(Attribute::WriteOnly);
+  return AbstractAttribute::manifest(A);
+}
+
+bool AAMemoryBehaviorArgument::followUseIn(Attributor &A, const Use *U,
+                                           const Instruction *UserI) {
+  if (isa<LoadInst>(UserI)) {
+    // The loaded value is unrelated to the pointer argument, no need to
+    // follow the users of the load.
+    return false;
+  }
+
+  ImmutableCallSite ICS(UserI);
+  if (ICS) {
+    if (!ICS.isArgOperand(U))
+      return true;
+
+    // If the use is a call argument known not to be captured, the users of
+    // the call do not need to be visited because they have to be unrelated to
+    // the input. Note that this check is not trivial even though we disallow
+    // general capturing of the underlying argument. The reason is that the
+    // call might the argument "through return", which we allow and for which we
+    // need to check call users.
+    unsigned ArgNo = ICS.getArgumentNo(U);
+    const AANoCapture *ArgNoCaptureAA =
+        A.getAAFor<AANoCapture>(*this, *ICS.getCalledValue(), ArgNo);
+    if (ArgNoCaptureAA && ArgNoCaptureAA->getState().isValidState() &&
+        ArgNoCaptureAA->isAssumedNoCapture())
+      return false;
+  }
+
+  // By default we follow all uses assuming UserI might leak information on U.
+  return true;
+}
+
+void AAMemoryBehaviorArgument::analyzeUseIn(Attributor &A, const Use *U,
+                                            const Instruction *UserI) {
+  assert(UserI->mayReadOrWriteMemory());
+
+  switch (UserI->getOpcode()) {
+  case Instruction::Load:
+    // Loads cause the NO_READS property to disappear.
+    removeAssumedBits(NO_READS);
+    return;
+
+  case Instruction::Store:
+    // Stores cause the NO_WRITES property to disappear if the use is the
+    // pointer operand. Note that we do assume that capturing was taken care of
+    // somewhere else.
+    if (cast<StoreInst>(UserI)->getPointerOperand() == U->get())
+      removeAssumedBits(NO_WRITES);
+    return;
+
+  case Instruction::Call:
+  case Instruction::Invoke: {
+    // For call sites we look at the argument memory behavior attribute (this
+    // could be recursive!) in order to restrict our own state.
+    ImmutableCallSite ICS(UserI);
+
+    // Give up on operand bundles.
+    if (ICS.isBundleOperand(U)) {
+      indicateFixpoint(/* Optimistic */ false);
+      return;
+    }
+
+    if (ICS.isCallee(U))
+      break;
+
+    // Adjust the possible access behavior based on the information on the
+    // argument.
+    unsigned ArgNo = ICS.getArgumentNo(U);
+    auto *ArgMemAccBehavior = A.getAAFor<AAMemoryBehaviorArgument>(
+        *this, *ICS.getCalledValue(), ArgNo);
+    if (ArgMemAccBehavior && ArgMemAccBehavior->getState().isValidState()) {
+      // "assumed" has at most the same bits as the ArgMemAccBehavior assumed
+      // and at least "known".
+      intersectAssumedBits(ArgMemAccBehavior->getAssumed());
+      return;
+    }
+
+    if (const Function *Callee = ICS.getCalledFunction()) {
+      if (Callee->arg_size() > ArgNo) {
+        // "assumed" has at most the same bits known to hold for the argument
+        // and at least "known".
+        const Argument &CalleeArg = *(Callee->arg_begin() + ArgNo);
+        IntegerState ExistingState;
+        getKnownStateFromValue(CalleeArg, ExistingState,
+                               /* value only */ false);
+        intersectAssumedBits(ExistingState.getKnown());
+        return;
+      }
+    }
+    break;
+  }
+  };
+
+  // Generally, look at the "may-properties" and adjust the assumed state.
+  if (UserI->mayReadFromMemory())
+    removeAssumedBits(NO_READS);
+  if (UserI->mayWriteToMemory())
+    removeAssumedBits(NO_WRITES);
+}
+
+ChangeStatus AAMemoryBehaviorArgument::updateImpl(Attributor &A) {
+  assert(isAssumedReadOnly() || isAssumedWriteOnly());
+
+  // The associated argument.
+  Argument &Arg = cast<Argument>(getAnchoredValue());
+
+  // First make sure the argument is not captured (except through "return"), if
+  // it is, any information derived would be irrelevant anyway.
+  if (!Arg.hasNoCaptureAttr()) {
+    const AANoCapture *ArgNoCaptureAA = A.getAAFor<AANoCapture>(*this, Arg);
+    if (!ArgNoCaptureAA || !ArgNoCaptureAA->getState().isValidState() ||
+        !ArgNoCaptureAA->isAssumedNoCaptureMaybeReturned()) {
+      assert(!isAtFixpoint());
+      indicateFixpoint(/* Optimistic */ false);
+      return ChangeStatus::CHANGED;
+    }
+  }
+
+  // The current assumed state used to determine a change.
+  auto AssumedState = getAssumed();
+
+  // The first time update is called we initialize the use vector with all
+  // direct argument uses. Transitive uses are collected every time because the
+  // need to explore more of them may only be known in subsequent updates.
+  bool ExploreUses = VisitedUses.empty();
+  if (ExploreUses) {
+    for (const Use &U : Arg.uses())
+      if (VisitedUses.insert(&U).second)
+        Uses.push_back(&U);
+  }
+
+  // Visit and expand uses until all are analyzed or a fixpoint is reached.
+  for (unsigned i = 0; i < Uses.size() && !isAtFixpoint(); i++) {
+    const Use *U = Uses[i];
+    Instruction *UserI = cast<Instruction>(U->getUser());
+
+    // Check if the users of UserI should also be visited.
+    if (followUseIn(A, U, UserI))
+      for (const Use &UserIUse : UserI->uses())
+        if (VisitedUses.insert(&UserIUse).second)
+          Uses.push_back(&UserIUse);
+
+    // If UserI might touch memory we analyze the use in detail.
+    if (UserI->mayReadOrWriteMemory())
+      analyzeUseIn(A, U, UserI);
+  }
+
+  return (AssumedState != getAssumed()) ? ChangeStatus::CHANGED
+                                        : ChangeStatus::UNCHANGED;
+}
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -1522,6 +1862,7 @@
     // is also derived but as a "function return attribute" (see above).
     if (Arg.getType()->isPointerTy()) {
       registerAA(*new AANoCaptureArgument(Arg));
+      registerAA(*new AAMemoryBehaviorArgument(Arg));
     }
   }
 
Index: llvm/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -306,6 +306,43 @@
   static constexpr Attribute::AttrKind ID = Attribute::NoCapture;
 };
 
+/// An abstract interface for all nocapture attributes.
+struct AAMemoryBehavior : public AbstractAttribute {
+
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AAMemoryBehavior(Value &V) : AbstractAttribute(V) {}
+
+  /// Return true if we know that the underlying value is not read or accessed
+  /// in its respective scope.
+  virtual bool isKnownReadNone() const = 0;
+
+  /// Return true if we assume that the underlying value is not read or accessed
+  /// in its respective scope.
+  virtual bool isAssumedReadNone() const = 0;
+
+  /// Return true if we know that the underlying value is not accessed
+  /// (=written) in its respective scope.
+  virtual bool isKnownReadOnly() const = 0;
+
+  /// Return true if we assume that the underlying value is not accessed
+  /// (=written) in its respective scope.
+  virtual bool isAssumedReadOnly() const = 0;
+
+  /// Return true if we know that the underlying value is not read in its
+  /// respective scope.
+  virtual bool isKnownWriteOnly() const = 0;
+
+  /// Return true if we assume that the underlying value is not read in its
+  /// respective scope.
+  virtual bool isAssumedWriteOnly() const = 0;
+
+  /// See AbstractState::getAttrKind().
+  Attribute::AttrKind getAttrKind() const override { return ID; }
+
+  /// The identifier used by the Attributor for this class of attributes.
+  static constexpr Attribute::AttrKind ID = Attribute::ReadNone;
+};
+
 /// ----------------------------------------------------------------------------
 ///                       Pass (Manager) Boilerplate
 /// ----------------------------------------------------------------------------
Index: clang/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
===================================================================
--- clang/test/CodeGenOpenCL/kernels-have-spir-cc-by-default.cl
+++ clang/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* byval nocapture
-// 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* byval nocapture
-// 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* byval nocapture
-// 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;
 }
Index: clang/test/CodeGenOpenCL/amdgpu-call-kernel.cl
===================================================================
--- clang/test/CodeGenOpenCL/amdgpu-call-kernel.cl
+++ clang/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)
Index: clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
===================================================================
--- clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
+++ clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl
@@ -309,7 +309,7 @@
 // CHECK: void @func_different_size_type_pair_arg(i64 %arg1.coerce0, i32 %arg1.coerce1)
 void func_different_size_type_pair_arg(different_size_type_pair arg1) { }
 
-// CHECK: void @func_flexible_array_arg(%struct.flexible_array addrspace(5)* byval nocapture align 4 %arg)
+// CHECK: void @func_flexible_array_arg(%struct.flexible_array addrspace(5)* byval nocapture readnone align 4 %arg)
 void func_flexible_array_arg(flexible_array arg) { }
 
 // CHECK: define float @func_f32_ret()
@@ -404,14 +404,14 @@
   return s;
 }
 
-// CHECK: define void @func_ret_struct_arr32(%struct.struct_arr32 addrspace(5)* noalias nocapture sret %agg.result)
+// CHECK: define void @func_ret_struct_arr32(%struct.struct_arr32 addrspace(5)* noalias nocapture sret writeonly %agg.result)
 struct_arr32 func_ret_struct_arr32()
 {
   struct_arr32 s = { 0 };
   return s;
 }
 
-// CHECK: define void @func_ret_struct_arr33(%struct.struct_arr33 addrspace(5)* noalias nocapture sret %agg.result)
+// CHECK: define void @func_ret_struct_arr33(%struct.struct_arr33 addrspace(5)* noalias nocapture sret writeonly %agg.result)
 struct_arr33 func_ret_struct_arr33()
 {
   struct_arr33 s = { 0 };
@@ -440,7 +440,7 @@
   return s;
 }
 
-// CHECK: define void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture sret %agg.result)
+// CHECK: define void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture sret writeonly %agg.result)
 flexible_array func_flexible_array_ret()
 {
   flexible_array s = { 0 };
@@ -450,11 +450,11 @@
 // CHECK: define void @func_reg_state_lo(<4 x i32> %arg0, <4 x i32> %arg1, <4 x i32> %arg2, i32 %arg3, i32 %s.coerce0, float %s.coerce1, i32 %s.coerce2)
 void func_reg_state_lo(int4 arg0, int4 arg1, int4 arg2, int arg3, struct_arg_t s) { }
 
-// CHECK: define void @func_reg_state_hi(<4 x i32> %arg0, <4 x i32> %arg1, <4 x i32> %arg2, i32 %arg3, i32 %arg4, %struct.struct_arg addrspace(5)* byval nocapture align 4 %s)
+// CHECK: define void @func_reg_state_hi(<4 x i32> %arg0, <4 x i32> %arg1, <4 x i32> %arg2, i32 %arg3, i32 %arg4, %struct.struct_arg addrspace(5)* byval nocapture readnone align 4 %s)
 void func_reg_state_hi(int4 arg0, int4 arg1, int4 arg2, int arg3, int arg4, struct_arg_t s) { }
 
 // XXX - Why don't the inner structs flatten?
-// CHECK: define void @func_reg_state_num_regs_nested_struct(<4 x i32> %arg0, i32 %arg1, i32 %arg2.coerce0, %struct.nested %arg2.coerce1, i32 %arg3.coerce0, %struct.nested %arg3.coerce1, %struct.num_regs_nested_struct addrspace(5)* byval nocapture align 8 %arg4)
+// CHECK: define void @func_reg_state_num_regs_nested_struct(<4 x i32> %arg0, i32 %arg1, i32 %arg2.coerce0, %struct.nested %arg2.coerce1, i32 %arg3.coerce0, %struct.nested %arg3.coerce1, %struct.num_regs_nested_struct addrspace(5)* byval nocapture readnone align 8 %arg4)
 void func_reg_state_num_regs_nested_struct(int4 arg0, int arg1, num_regs_nested_struct arg2, num_regs_nested_struct arg3, num_regs_nested_struct arg4) { }
 
 // CHECK: define void @func_double_nested_struct_arg(<4 x i32> %arg0, i32 %arg1, i32 %arg2.coerce0, %struct.double_nested %arg2.coerce1, i16 %arg2.coerce2)
@@ -469,7 +469,7 @@
 // CHECK: define void @func_large_struct_padding_arg_direct(i8 %arg.coerce0, i32 %arg.coerce1, i8 %arg.coerce2, i32 %arg.coerce3, i8 %arg.coerce4, i8 %arg.coerce5, i16 %arg.coerce6, i16 %arg.coerce7, [3 x i8] %arg.coerce8, i64 %arg.coerce9, i32 %arg.coerce10, i8 %arg.coerce11, i32 %arg.coerce12, i16 %arg.coerce13, i8 %arg.coerce14)
 void func_large_struct_padding_arg_direct(large_struct_padding arg) { }
 
-// CHECK: define void @func_large_struct_padding_arg_store(%struct.large_struct_padding addrspace(1)* nocapture %out, %struct.large_struct_padding addrspace(5)* byval nocapture readonly align 8 %arg)
+// CHECK: define void @func_large_struct_padding_arg_store(%struct.large_struct_padding addrspace(1)* nocapture writeonly %out, %struct.large_struct_padding addrspace(5)* byval nocapture readonly align 8 %arg)
 void func_large_struct_padding_arg_store(global large_struct_padding* out, large_struct_padding arg) {
   *out = arg;
 }
@@ -479,7 +479,7 @@
 
 // Function signature from blender, nothing should be passed byval. The v3i32
 // should not count as 4 passed registers.
-// CHECK: define void @v3i32_pair_reg_count(%struct.int3_pair addrspace(5)* nocapture %arg0, <3 x i32> %arg1.coerce0, <3 x i32> %arg1.coerce1, <3 x i32> %arg2, <3 x i32> %arg3.coerce0, <3 x i32> %arg3.coerce1, <3 x i32> %arg4, float %arg5)
+// CHECK: define void @v3i32_pair_reg_count(%struct.int3_pair addrspace(5)* nocapture readnone %arg0, <3 x i32> %arg1.coerce0, <3 x i32> %arg1.coerce1, <3 x i32> %arg2, <3 x i32> %arg3.coerce0, <3 x i32> %arg3.coerce1, <3 x i32> %arg4, float %arg5)
 void v3i32_pair_reg_count(int3_pair *arg0, int3_pair arg1, int3 arg2, int3_pair arg3, int3 arg4, float arg5) { }
 
 // Each short4 should fit pack into 2 registers.
@@ -487,7 +487,7 @@
 void v4i16_reg_count(short4 arg0, short4 arg1, short4 arg2, short4 arg3,
                      short4 arg4, short4 arg5, struct_4regs arg6) { }
 
-// CHECK: define void @v4i16_pair_reg_count_over(<4 x i16> %arg0, <4 x i16> %arg1, <4 x i16> %arg2, <4 x i16> %arg3, <4 x i16> %arg4, <4 x i16> %arg5, <4 x i16> %arg6, %struct.struct_4regs addrspace(5)* byval nocapture align 4 %arg7)
+// CHECK: define void @v4i16_pair_reg_count_over(<4 x i16> %arg0, <4 x i16> %arg1, <4 x i16> %arg2, <4 x i16> %arg3, <4 x i16> %arg4, <4 x i16> %arg5, <4 x i16> %arg6, %struct.struct_4regs addrspace(5)* byval nocapture readnone align 4 %arg7)
 void v4i16_pair_reg_count_over(short4 arg0, short4 arg1, short4 arg2, short4 arg3,
                                short4 arg4, short4 arg5, short4 arg6, struct_4regs arg7) { }
 
@@ -495,7 +495,7 @@
 void v3i16_reg_count(short3 arg0, short3 arg1, short3 arg2, short3 arg3,
                      short3 arg4, short3 arg5, struct_4regs arg6) { }
 
-// CHECK: define void @v3i16_reg_count_over(<3 x i16> %arg0, <3 x i16> %arg1, <3 x i16> %arg2, <3 x i16> %arg3, <3 x i16> %arg4, <3 x i16> %arg5, <3 x i16> %arg6, %struct.struct_4regs addrspace(5)* byval nocapture align 4 %arg7)
+// CHECK: define void @v3i16_reg_count_over(<3 x i16> %arg0, <3 x i16> %arg1, <3 x i16> %arg2, <3 x i16> %arg3, <3 x i16> %arg4, <3 x i16> %arg5, <3 x i16> %arg6, %struct.struct_4regs addrspace(5)* byval nocapture readnone align 4 %arg7)
 void v3i16_reg_count_over(short3 arg0, short3 arg1, short3 arg2, short3 arg3,
                           short3 arg4, short3 arg5, short3 arg6, struct_4regs arg7) { }
 
@@ -505,7 +505,7 @@
                      short2 arg8, short2 arg9, short2 arg10, short2 arg11,
                      struct_4regs arg13) { }
 
-// CHECK: define void @v2i16_reg_count_over(<2 x i16> %arg0, <2 x i16> %arg1, <2 x i16> %arg2, <2 x i16> %arg3, <2 x i16> %arg4, <2 x i16> %arg5, <2 x i16> %arg6, <2 x i16> %arg7, <2 x i16> %arg8, <2 x i16> %arg9, <2 x i16> %arg10, <2 x i16> %arg11, <2 x i16> %arg12, %struct.struct_4regs addrspace(5)* byval nocapture align 4 %arg13)
+// CHECK: define void @v2i16_reg_count_over(<2 x i16> %arg0, <2 x i16> %arg1, <2 x i16> %arg2, <2 x i16> %arg3, <2 x i16> %arg4, <2 x i16> %arg5, <2 x i16> %arg6, <2 x i16> %arg7, <2 x i16> %arg8, <2 x i16> %arg9, <2 x i16> %arg10, <2 x i16> %arg11, <2 x i16> %arg12, %struct.struct_4regs addrspace(5)* byval nocapture readnone align 4 %arg13)
 void v2i16_reg_count_over(short2 arg0, short2 arg1, short2 arg2, short2 arg3,
                           short2 arg4, short2 arg5, short2 arg6, short2 arg7,
                           short2 arg8, short2 arg9, short2 arg10, short2 arg11,
@@ -515,7 +515,7 @@
 void v2i8_reg_count(char2 arg0, char2 arg1, char2 arg2, char2 arg3,
                     char2 arg4, char2 arg5, struct_4regs arg6) { }
 
-// CHECK: define void @v2i8_reg_count_over(<2 x i8> %arg0, <2 x i8> %arg1, <2 x i8> %arg2, <2 x i8> %arg3, <2 x i8> %arg4, <2 x i8> %arg5, i32 %arg6, %struct.struct_4regs addrspace(5)* byval nocapture align 4 %arg7)
+// CHECK: define void @v2i8_reg_count_over(<2 x i8> %arg0, <2 x i8> %arg1, <2 x i8> %arg2, <2 x i8> %arg3, <2 x i8> %arg4, <2 x i8> %arg5, i32 %arg6, %struct.struct_4regs addrspace(5)* byval nocapture readnone align 4 %arg7)
 void v2i8_reg_count_over(char2 arg0, char2 arg1, char2 arg2, char2 arg3,
                          char2 arg4, char2 arg5, int arg6, struct_4regs arg7) { }
 
Index: clang/test/CodeGenObjC/os_log.m
===================================================================
--- clang/test/CodeGenObjC/os_log.m
+++ clang/test/CodeGenObjC/os_log.m
@@ -14,7 +14,7 @@
 #ifdef __x86_64__
 // CHECK-LABEL: define i8* @test_builtin_os_log
 // CHECK-O0-LABEL: define i8* @test_builtin_os_log
-// CHECK: (i8* returned "no-capture-maybe-returned" %[[BUF:.*]])
+// CHECK: (i8* returned writeonly "no-capture-maybe-returned" %[[BUF:.*]])
 // CHECK-O0: (i8* %[[BUF:.*]])
 void *test_builtin_os_log(void *buf) {
   return __builtin_os_log_format(buf, "capabilities: %@", GenString());
Index: clang/test/CodeGenCXX/wasm-args-returns.cpp
===================================================================
--- clang/test/CodeGenCXX/wasm-args-returns.cpp
+++ clang/test/CodeGenCXX/wasm-args-returns.cpp
@@ -30,7 +30,7 @@
   double d, e;
 };
 test(two_fields);
-// CHECK: define void @_Z7forward10two_fields(%struct.two_fields* noalias nocapture sret %{{.*}}, %struct.two_fields* byval nocapture readonly align 8 %{{.*}})
+// CHECK: define void @_Z7forward10two_fields(%struct.two_fields* noalias nocapture sret writeonly %{{.*}}, %struct.two_fields* byval nocapture readonly align 8 %{{.*}})
 //
 // CHECK: define void @_Z15test_two_fieldsv()
 // CHECK: %[[tmp:.*]] = alloca %struct.two_fields, align 8
Index: clang/test/CodeGen/systemz-inline-asm.c
===================================================================
--- clang/test/CodeGen/systemz-inline-asm.c
+++ clang/test/CodeGen/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 [[DEST:%.*]], fp128* nocapture readonly, fp128* nocapture readonly)
+// CHECK: define void @test_f128(fp128* noalias nocapture sret writeonly [[DEST:%.*]], fp128* nocapture readonly, fp128* nocapture readonly)
 // 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)
Index: clang/test/CodeGen/arm-vfp16-arguments.c
===================================================================
--- clang/test/CodeGen/arm-vfp16-arguments.c
+++ clang/test/CodeGen/arm-vfp16-arguments.c
@@ -71,6 +71,6 @@
 
 hfa_t ghfa;
 hfa_t test_ret_hfa(void) { return ghfa; }
-// CHECK-SOFT: define void @test_ret_hfa(%struct.hfa_t* noalias nocapture sret %agg.result)
+// CHECK-SOFT: define void @test_ret_hfa(%struct.hfa_t* noalias nocapture sret writeonly %agg.result)
 // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @test_ret_hfa()
 // CHECK-FULL: define arm_aapcs_vfpcc %struct.hfa_t @test_ret_hfa()
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to