Index: llvm/include/llvm/IR/Metadata.h
===================================================================
--- llvm/include/llvm/IR/Metadata.h
+++ llvm/include/llvm/IR/Metadata.h
@@ -641,18 +641,25 @@
 /// memory access used by the alias-analysis infrastructure.
 struct AAMDNodes {
   explicit AAMDNodes() = default;
-  explicit AAMDNodes(MDNode *T, MDNode *TS, MDNode *S, MDNode *N)
-      : TBAA(T), TBAAStruct(TS), Scope(S), NoAlias(N) {}
+  explicit AAMDNodes(MDNode *T, MDNode *TS, MDNode *S, MDNode *N,
+                     Value *NSC = nullptr)
+      : TBAA(T), TBAAStruct(TS), Scope(S), NoAlias(N), NoAliasProvenance(NSC) {}
 
   bool operator==(const AAMDNodes &A) const {
     return TBAA == A.TBAA && TBAAStruct == A.TBAAStruct && Scope == A.Scope &&
-           NoAlias == A.NoAlias;
+           NoAlias == A.NoAlias && NoAliasProvenance == A.NoAliasProvenance;
   }
 
   bool operator!=(const AAMDNodes &A) const { return !(*this == A); }
 
   explicit operator bool() const {
-    return TBAA || TBAAStruct || Scope || NoAlias;
+    return TBAA || TBAAStruct || Scope || NoAlias || NoAliasProvenance;
+  }
+
+  void clearNoAliasInfo() {
+    Scope = nullptr;
+    NoAlias = nullptr;
+    NoAliasProvenance = nullptr;
   }
 
   /// The tag for type-based alias analysis.
@@ -667,6 +674,9 @@
   /// The tag specifying the noalias scope.
   MDNode *NoAlias = nullptr;
 
+  /// The NoAlias Provenance pointer path
+  Value *NoAliasProvenance = nullptr;
+
   /// Given two sets of AAMDNodes that apply to the same pointer,
   /// give the best AAMDNodes that are compatible with both (i.e. a set of
   /// nodes whose allowable aliasing conclusions are a subset of those
@@ -678,6 +688,9 @@
     Result.TBAAStruct = Other.TBAAStruct == TBAAStruct ? TBAAStruct : nullptr;
     Result.Scope = Other.Scope == Scope ? Scope : nullptr;
     Result.NoAlias = Other.NoAlias == NoAlias ? NoAlias : nullptr;
+    Result.NoAliasProvenance = Other.NoAliasProvenance == NoAliasProvenance
+                                   ? NoAliasProvenance
+                                   : nullptr;
     return Result;
   }
 };
@@ -699,7 +712,8 @@
     return DenseMapInfo<MDNode *>::getHashValue(Val.TBAA) ^
            DenseMapInfo<MDNode *>::getHashValue(Val.TBAAStruct) ^
            DenseMapInfo<MDNode *>::getHashValue(Val.Scope) ^
-           DenseMapInfo<MDNode *>::getHashValue(Val.NoAlias);
+           DenseMapInfo<MDNode *>::getHashValue(Val.NoAlias) ^
+           DenseMapInfo<Value *>::getHashValue(Val.NoAliasProvenance);
   }
 
   static bool isEqual(const AAMDNodes &LHS, const AAMDNodes &RHS) {
Index: llvm/include/llvm/IR/Value.h
===================================================================
--- llvm/include/llvm/IR/Value.h
+++ llvm/include/llvm/IR/Value.h
@@ -583,6 +583,18 @@
                                    ->stripPointerCastsAndInvariantGroups());
   }
 
+  /// Strip off pointer casts, all-zero GEPs, aliases, invariant group
+  /// info and noalias intrinsics.
+  ///
+  /// Returns the original uncasted value.  If this is called on a non-pointer
+  /// value, it returns 'this'.
+  const Value *stripPointerCastsAndInvariantGroupsAndNoAliasIntr() const;
+  Value *stripPointerCastsAndInvariantGroupsAndNoAliasIntr() {
+    return const_cast<Value *>(
+        static_cast<const Value *>(this)
+            ->stripPointerCastsAndInvariantGroupsAndNoAliasIntr());
+  }
+
   /// Strip off pointer casts and all-constant inbounds GEPs.
   ///
   /// Returns the original pointer value.  If this is called on a non-pointer
Index: llvm/lib/Analysis/BasicAliasAnalysis.cpp
===================================================================
--- llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -1773,14 +1773,27 @@
                                       LocationSize V2Size, AAMDNodes V2AAInfo,
                                       AAQueryInfo &AAQI, const Value *O1,
                                       const Value *O2) {
+  auto hasValidNoAliasProvenance = [](AAMDNodes &AA) {
+    return AA.NoAliasProvenance && !isa<UndefValue>(AA.NoAliasProvenance);
+  };
+
   // If either of the memory references is empty, it doesn't matter what the
   // pointer values are.
   if (V1Size.isZero() || V2Size.isZero())
     return NoAlias;
 
   // Strip off any casts if they exist.
-  V1 = V1->stripPointerCastsAndInvariantGroups();
-  V2 = V2->stripPointerCastsAndInvariantGroups();
+  if (hasValidNoAliasProvenance(V1AAInfo) ||
+      hasValidNoAliasProvenance(V2AAInfo)) {
+    V1 = V1->stripPointerCastsAndInvariantGroups();
+    V2 = V2->stripPointerCastsAndInvariantGroups();
+  } else {
+    // No noalias info - look through noalias intrinsics in a safe way
+    V1 = V1->stripPointerCastsAndInvariantGroupsAndNoAliasIntr();
+    V2 = V2->stripPointerCastsAndInvariantGroupsAndNoAliasIntr();
+    V1AAInfo.clearNoAliasInfo();
+    V2AAInfo.clearNoAliasInfo();
+  }
 
   // If V1 or V2 is undef, the result is NoAlias because we can always pick a
   // value for undef that aliases nothing in the program.
Index: llvm/lib/IR/Value.cpp
===================================================================
--- llvm/lib/IR/Value.cpp
+++ llvm/lib/IR/Value.cpp
@@ -511,6 +511,7 @@
   PSK_ZeroIndicesAndAliases,
   PSK_ZeroIndicesSameRepresentation,
   PSK_ZeroIndicesAndInvariantGroups,
+  PSK_ZeroIndicesAndInvariantGroupsAndNoAliasIntr,
   PSK_InBoundsConstantIndices,
   PSK_InBounds
 };
@@ -532,6 +533,7 @@
       case PSK_ZeroIndicesAndAliases:
       case PSK_ZeroIndicesSameRepresentation:
       case PSK_ZeroIndicesAndInvariantGroups:
+      case PSK_ZeroIndicesAndInvariantGroupsAndNoAliasIntr:
         if (!GEP->hasAllZeroIndices())
           return V;
         break;
@@ -569,6 +571,17 @@
           V = Call->getArgOperand(0);
           continue;
         }
+        // Same as above, but also for noalias intrinsics
+        if (StripKind == PSK_ZeroIndicesAndInvariantGroupsAndNoAliasIntr &&
+            (Call->getIntrinsicID() == Intrinsic::launder_invariant_group ||
+             Call->getIntrinsicID() == Intrinsic::strip_invariant_group ||
+             Call->getIntrinsicID() == Intrinsic::noalias ||
+             Call->getIntrinsicID() == Intrinsic::provenance_noalias ||
+             Call->getIntrinsicID() == Intrinsic::noalias_arg_guard ||
+             Call->getIntrinsicID() == Intrinsic::noalias_copy_guard)) {
+          V = Call->getArgOperand(0);
+          continue;
+        }
       }
       return V;
     }
@@ -599,6 +612,11 @@
   return stripPointerCastsAndOffsets<PSK_ZeroIndicesAndInvariantGroups>(this);
 }
 
+const Value *Value::stripPointerCastsAndInvariantGroupsAndNoAliasIntr() const {
+  return stripPointerCastsAndOffsets<
+      PSK_ZeroIndicesAndInvariantGroupsAndNoAliasIntr>(this);
+}
+
 const Value *Value::stripAndAccumulateConstantOffsets(
     const DataLayout &DL, APInt &Offset, bool AllowNonInbounds,
     function_ref<bool(Value &, APInt &)> ExternalAnalysis) const {
Index: llvm/test/Analysis/BasicAA/noalias-intr.ll
===================================================================
--- /dev/null
+++ llvm/test/Analysis/BasicAA/noalias-intr.ll
@@ -0,0 +1,143 @@
+; RUN: opt < %s -basicaa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+; Function Attrs: nounwind
+define void @test01() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %t5 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %_pA, i8* %t1, i32** null, i32 0, metadata !2), !tbaa !7, !noalias !11
+  store i32 42, i32* %t5, align 4, !tbaa !12, !noalias !11
+  %t7 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %_pB, i8* %t3, i32** null, i32 0, metadata !5), !tbaa !7, !noalias !11
+  store i32 43, i32* %t7, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test01:
+; CHECK: NoAlias:      i32* %t5, i32* %t7
+
+; Function Attrs: nounwind
+define void @test02() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %t5 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %_pA, i8* %t1, i32** null, i32 0, metadata !2), !tbaa !7, !noalias !11
+  store i32 42, i32* %t5, align 4, !tbaa !12, !noalias !11
+  %t7 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %_pA, i8* %t3, i32** null, i32 0, metadata !5), !tbaa !7, !noalias !11
+  store i32 43, i32* %t7, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test02:
+; CHECK: MustAlias:      i32* %t5, i32* %t7
+
+
+; Function Attrs: nounwind
+define void @test11() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %t5 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %_pA, i8* %t1, i32** null, i32** undef, i32 0, metadata !2), !tbaa !7, !noalias !11
+  %.guard1 = call i32* @llvm.noalias.arg.guard.p0i32.p0i32(i32* %_pA, i32* %t5)
+  store i32 42, i32* %.guard1, align 4, !tbaa !12, !noalias !11
+  %.guard2 = call i32* @llvm.noalias.arg.guard.p0i32.p0i32(i32* %_pB, i32* %t5)
+  store i32 43, i32* %.guard2, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test11:
+; CHECK: NoAlias:      i32* %.guard1, i32* %.guard2
+
+; Function Attrs: nounwind
+define void @test12() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %t5 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %_pA, i8* %t1, i32** null, i32** undef, i32 0, metadata !2), !tbaa !7, !noalias !11
+  %.guard1 = call i32* @llvm.noalias.arg.guard.p0i32.p0i32(i32* %_pA, i32* %t5)
+  store i32 42, i32* %.guard1, align 4, !tbaa !12, !noalias !11
+  %.guard2 = call i32* @llvm.noalias.arg.guard.p0i32.p0i32(i32* %_pA, i32* %t5)
+  store i32 43, i32* %.guard2, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test12:
+; CHECK: MustAlias:      i32* %.guard1, i32* %.guard2
+
+; Function Attrs: nounwind
+define void @test21() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %.guard1 = call i32* @llvm.noalias.copy.guard.p0i32.p0i8(i32* %_pA, i8* %t1, metadata !14, metadata !2)
+  store i32 42, i32* %.guard1, align 4, !tbaa !12, !noalias !11
+  %.guard2 = call i32* @llvm.noalias.copy.guard.p0i32.p0i8(i32* %_pB, i8* %t3, metadata !14, metadata !5)
+  store i32 43, i32* %.guard2, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test21:
+; CHECK: NoAlias:      i32* %.guard1, i32* %.guard2
+
+; Function Attrs: nounwind
+define void @test22() #0 {
+entry:
+  %_pA = alloca i32, align 4
+  %_pB = alloca i32, align 4
+  %t1 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2)
+  %t3 = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !5)
+  %.guard1 = call i32* @llvm.noalias.copy.guard.p0i32.p0i8(i32* %_pA, i8* %t1, metadata !14, metadata !2)
+  store i32 42, i32* %.guard1, align 4, !tbaa !12, !noalias !11
+  %.guard2 = call i32* @llvm.noalias.copy.guard.p0i32.p0i8(i32* %_pA, i8* %t3, metadata !14, metadata !5)
+  store i32 43, i32* %.guard2, align 4, !tbaa !12, !noalias !11
+  ret void
+}
+; CHECK-LABEL: Function: test22:
+; CHECK: MustAlias:      i32* %.guard1, i32* %.guard2
+
+; Function Attrs: argmemonly nounwind
+declare i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32**, i32, metadata) #1
+
+; Function Attrs: argmemonly nounwind speculatable
+declare i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32*, i8*, i32**, i32, metadata) #2
+
+; Function Attrs: nounwind readnone speculatable
+declare i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32*, i8*, i32**, i32**, i32, metadata) #3
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.noalias.arg.guard.p0i32.p0i32(i32*, i32*) #4
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.noalias.copy.guard.p0i32.p0i8(i32*, i8*, metadata, metadata) #4
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { argmemonly nounwind }
+attributes #2 = { argmemonly nounwind speculatable }
+attributes #3 = { nounwind readnone speculatable }
+attributes #4 = { nounwind readnone }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!"clang"}
+!2 = !{!3}
+!3 = distinct !{!3, !4, !"test01: _pA"}
+!4 = distinct !{!4, !"test01"}
+!5 = !{!6}
+!6 = distinct !{!6, !4, !"test01: _pB"}
+!7 = !{!8, !8, i64 0, i64 4}
+!8 = !{!9, i64 4, !"any pointer"}
+!9 = !{!10, i64 1, !"omnipotent char"}
+!10 = !{!"Simple C/C++ TBAA"}
+!11 = !{!3, !6}
+!12 = !{!13, !13, i64 0, i64 4}
+!13 = !{!9, i64 4, !"int"}
+!14 = !{!15}
+!15 = !{i64 -1, i64 0}