Index: llvm/include/llvm/InitializePasses.h
===================================================================
--- llvm/include/llvm/InitializePasses.h
+++ llvm/include/llvm/InitializePasses.h
@@ -102,6 +102,7 @@
 void initializeCallSiteSplittingLegacyPassPass(PassRegistry&);
 void initializeCalledValuePropagationLegacyPassPass(PassRegistry &);
 void initializeCodeGenPreparePass(PassRegistry&);
+void initializeConnectNoAliasDeclLegacyPassPass(PassRegistry &);
 void initializeConstantHoistingLegacyPassPass(PassRegistry&);
 void initializeConstantMergeLegacyPassPass(PassRegistry&);
 void initializeConstantPropagationPass(PassRegistry&);
Index: llvm/include/llvm/Transforms/Scalar.h
===================================================================
--- llvm/include/llvm/Transforms/Scalar.h
+++ llvm/include/llvm/Transforms/Scalar.h
@@ -34,6 +34,12 @@
 //
 FunctionPass *createConstantPropagationPass();
 
+//===----------------------------------------------------------------------===//
+//
+// ConnectNoAliasDecl - Connects llvm.noalias.XX intrinsics to llvm.noalias.decl
+//
+FunctionPass *createConnectNoAliasDeclPass();
+
 //===----------------------------------------------------------------------===//
 //
 // AlignmentFromAssumptions - Use assume intrinsics to set load/store
Index: llvm/include/llvm/Transforms/Scalar/ConnectNoAliasDecl.h
===================================================================
--- /dev/null
+++ llvm/include/llvm/Transforms/Scalar/ConnectNoAliasDecl.h
@@ -0,0 +1,34 @@
+//===- ConnectNoAliasDecl.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This pass connects side.noalias intrinsics to the corresponding
+/// llvm.noalias.decl, based on the alloca of the pointer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_CONNECTNOALIASDECL_H
+#define LLVM_TRANSFORMS_SCALAR_CONNECTNOALIASDECL_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+class DominatorTree;
+
+class ConnectNoAliasDeclPass : public PassInfoMixin<ConnectNoAliasDeclPass> {
+public:
+  ConnectNoAliasDeclPass();
+
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+
+  // Glue for old PM
+  bool runImpl(Function &F);
+};
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_CONNECTNOALIASDECL_H
Index: llvm/include/llvm/Transforms/Utils/Cloning.h
===================================================================
--- llvm/include/llvm/Transforms/Utils/Cloning.h
+++ llvm/include/llvm/Transforms/Utils/Cloning.h
@@ -272,6 +272,11 @@
     Function *Callee, int64_t entryDelta,
     const ValueMap<const Value *, WeakTrackingVH> *VMap = nullptr);
 
+/// Connects noalias, side.noalias, noalias.copy.guard intrinsics to the
+/// corresponding llvm.noalias.decl, based on the alloca of the underlying
+/// p.addr.
+/// \returns true when the function was modified.
+bool propagateAndConnectNoAliasDecl(Function *F);
 } // end namespace llvm
 
 #endif // LLVM_TRANSFORMS_UTILS_CLONING_H
Index: llvm/lib/Passes/PassBuilder.cpp
===================================================================
--- llvm/lib/Passes/PassBuilder.cpp
+++ llvm/lib/Passes/PassBuilder.cpp
@@ -110,6 +110,7 @@
 #include "llvm/Transforms/Scalar/AlignmentFromAssumptions.h"
 #include "llvm/Transforms/Scalar/BDCE.h"
 #include "llvm/Transforms/Scalar/CallSiteSplitting.h"
+#include "llvm/Transforms/Scalar/ConnectNoAliasDecl.h"
 #include "llvm/Transforms/Scalar/ConstantHoisting.h"
 #include "llvm/Transforms/Scalar/CorrelatedValuePropagation.h"
 #include "llvm/Transforms/Scalar/DCE.h"
Index: llvm/lib/Passes/PassRegistry.def
===================================================================
--- llvm/lib/Passes/PassRegistry.def
+++ llvm/lib/Passes/PassRegistry.def
@@ -162,6 +162,7 @@
 FUNCTION_PASS("bounds-checking", BoundsCheckingPass())
 FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass())
 FUNCTION_PASS("callsite-splitting", CallSiteSplittingPass())
+FUNCTION_PASS("connect-noaliasdecl", ConnectNoAliasDeclPass())
 FUNCTION_PASS("consthoist", ConstantHoistingPass())
 FUNCTION_PASS("convert-noalias", PropagateAndConvertNoAliasPass())
 FUNCTION_PASS("chr", ControlHeightReductionPass())
Index: llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
===================================================================
--- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
+++ llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
@@ -260,6 +260,7 @@
   addInitialAliasAnalysisPasses(FPM);
 
   FPM.add(createCFGSimplificationPass());
+  FPM.add(createConnectNoAliasDeclPass());
   FPM.add(createSROAPass());
   FPM.add(createEarlyCSEPass());
 
@@ -297,6 +298,7 @@
     IP.HintThreshold = 325;
 
     MPM.add(createFunctionInliningPass(IP));
+    MPM.add(createConnectNoAliasDeclPass());
     MPM.add(createSROAPass());
     MPM.add(createEarlyCSEPass());             // Catch trivial redundancies
 
@@ -333,6 +335,7 @@
 void PassManagerBuilder::addFunctionSimplificationPasses(
     legacy::PassManagerBase &MPM) {
   // Start of function pass.
+  MPM.add(createConnectNoAliasDeclPass());
   // Break up aggregate allocas, using SSAUpdater.
   MPM.add(createSROAPass());
   MPM.add(createEarlyCSEPass(true /* Enable mem-ssa. */)); // Catch trivial redundancies
@@ -423,6 +426,8 @@
   // opened up by them.
   addInstructionCombiningPass(MPM);
   addExtensionsToPM(EP_Peephole, MPM);
+
+  MPM.add(createConnectNoAliasDeclPass());
   // Propagate and Convert as early as possible.
   // But do it after SROA!
   MPM.add(createPropagateAndConvertNoAliasPass());
Index: llvm/lib/Transforms/Scalar/CMakeLists.txt
===================================================================
--- llvm/lib/Transforms/Scalar/CMakeLists.txt
+++ llvm/lib/Transforms/Scalar/CMakeLists.txt
@@ -3,6 +3,7 @@
   AlignmentFromAssumptions.cpp
   BDCE.cpp
   CallSiteSplitting.cpp
+  ConnectNoAliasDecl.cpp
   ConstantHoisting.cpp
   ConstantProp.cpp
   CorrelatedValuePropagation.cpp
Index: llvm/lib/Transforms/Scalar/ConnectNoAliasDecl.cpp
===================================================================
--- /dev/null
+++ llvm/lib/Transforms/Scalar/ConnectNoAliasDecl.cpp
@@ -0,0 +1,112 @@
+//===- ConnectNoAliasDecl.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This pass connects side.noalias intrinsics to the corresponding
+/// llvm.noalias.decl, based on the alloca of the pointer.
+///
+//===----------------------------------------------------------------------===//
+//
+// When the original restrict declaration is not directly available,
+// llvm.noalias, llvm.side.noalias and llvm.noalias.copy.guard can be associated
+// with an 'unknown' (out of function) noalias scope. After certain
+// optimizations, like SROA, inlining, ... it is possible that a
+// llvm.noalias.decl is associated to an alloca, to which a llvm.noalias,
+// llvm.side.noalias orllvm.noalias.copy.guard intrinsics is also associated.
+// When the latter intrinsics are still refering to the 'unknown' scope, we can
+// now refine the information by associating the llvm.noalias.decl and its
+// information to the other noalias intrinsics that are depending on the same
+// alloca.
+//
+// This pass will connect those llvm.noalias.decl to those
+// llvm.noalias,llvm.side.noalias and llvm.noalias.copy.guard. It will also
+// propagate the embedded information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar/ConnectNoAliasDecl.h"
+#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+using namespace llvm;
+
+namespace {
+class ConnectNoAliasDeclLegacyPass : public FunctionPass {
+public:
+  static char ID; // Pass identification, replacement for typeid
+  ConnectNoAliasDeclLegacyPass() : FunctionPass(ID) {
+    initializeConnectNoAliasDeclLegacyPassPass(
+        *PassRegistry::getPassRegistry());
+  }
+
+  bool runOnFunction(Function &F) override;
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    // FIXME: is all of this valid ?
+    AU.addPreserved<GlobalsAAWrapperPass>();
+    AU.addPreserved<CallGraphWrapperPass>(); // FIXME: not sure this is
+                                             // valid. It ensures the same pass
+                                             // order as if this pass was not
+                                             // there
+    AU.addPreserved<DominatorTreeWrapperPass>();
+    AU.addRequiredTransitive<DominatorTreeWrapperPass>();
+    AU.setPreservesCFG();
+  }
+
+private:
+  ConnectNoAliasDeclPass Impl;
+};
+} // namespace
+
+char ConnectNoAliasDeclLegacyPass::ID = 0;
+INITIALIZE_PASS_BEGIN(
+    ConnectNoAliasDeclLegacyPass, "connect-noaliasdecl",
+    "Connect llvm.noalias.decl", false,
+    false) //  to llvm.noalias/llvm.side.noalias/llvm.noalias.copy.guard
+           //  intrinsics
+INITIALIZE_PASS_END(
+    ConnectNoAliasDeclLegacyPass, "connect-noaliasdecl",
+    "Connect llvm.noalias.decl", false,
+    false) //  to llvm.noalias/llvm.side.noalias/llvm.noalias.copy.guard
+           //  intrinsics
+
+bool ConnectNoAliasDeclLegacyPass::runOnFunction(Function &F) {
+  if (skipFunction(F))
+    return false;
+
+  return Impl.runImpl(F);
+}
+
+namespace llvm {
+
+bool ConnectNoAliasDeclPass::runImpl(Function &F) {
+  return llvm::propagateAndConnectNoAliasDecl(&F);
+}
+
+ConnectNoAliasDeclPass::ConnectNoAliasDeclPass() {}
+
+PreservedAnalyses ConnectNoAliasDeclPass::run(Function &F,
+                                              FunctionAnalysisManager &AM) {
+  bool Changed = runImpl(F);
+
+  if (!Changed)
+    return PreservedAnalyses::all();
+  PreservedAnalyses PA;
+  PA.preserve<GlobalsAA>();
+  //?? PA.preserve<CallGraphWrapperPass>(); // FIXME: not sure this is valid,
+  // see above
+
+  return PA;
+}
+
+FunctionPass *createConnectNoAliasDeclPass() {
+  return new ConnectNoAliasDeclLegacyPass();
+}
+} // namespace llvm
Index: llvm/lib/Transforms/Scalar/Scalar.cpp
===================================================================
--- llvm/lib/Transforms/Scalar/Scalar.cpp
+++ llvm/lib/Transforms/Scalar/Scalar.cpp
@@ -37,6 +37,7 @@
   initializeBDCELegacyPassPass(Registry);
   initializeAlignmentFromAssumptionsPass(Registry);
   initializeCallSiteSplittingLegacyPassPass(Registry);
+  initializeConnectNoAliasDeclLegacyPassPass(Registry);
   initializeConstantHoistingLegacyPassPass(Registry);
   initializeConstantPropagationPass(Registry);
   initializeCorrelatedValuePropagationPass(Registry);
Index: llvm/test/Other/opt-O2-pipeline.ll
===================================================================
--- llvm/test/Other/opt-O2-pipeline.ll
+++ llvm/test/Other/opt-O2-pipeline.ll
@@ -13,6 +13,7 @@
 ; CHECK-NEXT:     Instrument function entry/exit with calls to e.g. mcount() (pre inlining)
 ; CHECK-NEXT:     Simplify the CFG
 ; CHECK-NEXT:     Dominator Tree Construction
+; CHECK-NEXT:     Connect llvm.noalias.decl
 ; CHECK-NEXT:     SROA
 ; CHECK-NEXT:     Early CSE
 ; CHECK-NEXT:     Propagate and Convert Noalias intrinsics
@@ -59,6 +60,7 @@
 ; CHECK-NEXT:       Deduce function attributes
 ; CHECK-NEXT:       FunctionPass Manager
 ; CHECK-NEXT:         Dominator Tree Construction
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         SROA
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
@@ -151,6 +153,7 @@
 ; CHECK-NEXT:         Lazy Block Frequency Analysis
 ; CHECK-NEXT:         Optimization Remark Emitter
 ; CHECK-NEXT:         Combine redundant instructions
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         Propagate and Convert Noalias intrinsics
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
Index: llvm/test/Other/opt-O3-pipeline.ll
===================================================================
--- llvm/test/Other/opt-O3-pipeline.ll
+++ llvm/test/Other/opt-O3-pipeline.ll
@@ -13,6 +13,7 @@
 ; CHECK-NEXT:     Instrument function entry/exit with calls to e.g. mcount() (pre inlining)
 ; CHECK-NEXT:     Simplify the CFG
 ; CHECK-NEXT:     Dominator Tree Construction
+; CHECK-NEXT:     Connect llvm.noalias.decl
 ; CHECK-NEXT:     SROA
 ; CHECK-NEXT:     Early CSE
 ; CHECK-NEXT:     Propagate and Convert Noalias intrinsics
@@ -63,6 +64,7 @@
 ; CHECK-NEXT:       Promote 'by reference' arguments to scalars
 ; CHECK-NEXT:       FunctionPass Manager
 ; CHECK-NEXT:         Dominator Tree Construction
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         SROA
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
@@ -156,6 +158,7 @@
 ; CHECK-NEXT:         Lazy Block Frequency Analysis
 ; CHECK-NEXT:         Optimization Remark Emitter
 ; CHECK-NEXT:         Combine redundant instructions
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         Propagate and Convert Noalias intrinsics
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
Index: llvm/test/Other/opt-Os-pipeline.ll
===================================================================
--- llvm/test/Other/opt-Os-pipeline.ll
+++ llvm/test/Other/opt-Os-pipeline.ll
@@ -13,6 +13,7 @@
 ; CHECK-NEXT:     Instrument function entry/exit with calls to e.g. mcount() (pre inlining)
 ; CHECK-NEXT:     Simplify the CFG
 ; CHECK-NEXT:     Dominator Tree Construction
+; CHECK-NEXT:     Connect llvm.noalias.decl
 ; CHECK-NEXT:     SROA
 ; CHECK-NEXT:     Early CSE
 ; CHECK-NEXT:     Propagate and Convert Noalias intrinsics
@@ -59,6 +60,7 @@
 ; CHECK-NEXT:       Deduce function attributes
 ; CHECK-NEXT:       FunctionPass Manager
 ; CHECK-NEXT:         Dominator Tree Construction
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         SROA
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
@@ -138,6 +140,7 @@
 ; CHECK-NEXT:         Lazy Block Frequency Analysis
 ; CHECK-NEXT:         Optimization Remark Emitter
 ; CHECK-NEXT:         Combine redundant instructions
+; CHECK-NEXT:         Connect llvm.noalias.decl
 ; CHECK-NEXT:         Propagate and Convert Noalias intrinsics
 ; CHECK-NEXT:         Basic Alias Analysis (stateless AA impl)
 ; CHECK-NEXT:         Function Alias Analysis Results
Index: llvm/test/Transforms/ConnectNoAliasDecl/basictest.ll
===================================================================
--- /dev/null
+++ llvm/test/Transforms/ConnectNoAliasDecl/basictest.ll
@@ -0,0 +1,179 @@
+; RUN: opt < %s -connect-noaliasdecl -verify -S | FileCheck %s
+; RUN: opt < %s -passes=connect-noaliasdecl,verify -S | FileCheck %s
+
+target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
+
+
+%struct.FOO = type { i32*, i32*, i32* }
+
+; Function Attrs: nounwind
+define dso_local void @test_01_before(i32** %_p, i32 %c) #0 !noalias !2 {
+entry:
+  %rp = alloca [2 x i32*], align 4
+  %other = alloca i32*, align 4
+  %local_tmp = alloca i32*, align 4
+  %tmp.0 = bitcast [2 x i32*]* %rp to i8*
+  %.fca.0.gep = getelementptr inbounds [2 x i32*], [2 x i32*]* %rp, i32 0, i32 0
+  %.fca.1.gep = getelementptr inbounds [2 x i32*], [2 x i32*]* %rp, i32 0, i32 1
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* %tmp.0) #5, !noalias !5
+  %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0a2p0i32.i32([2 x i32*]* %rp, i32 0, metadata !7)
+  %tmp.2 = load i32*, i32** %_p, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.2, i32** %.fca.0.gep, align 4, !tbaa !8, !noalias !5
+  %arrayinit.element = getelementptr inbounds i32*, i32** %.fca.0.gep, i32 1
+  %arrayidx1 = getelementptr inbounds i32*, i32** %_p, i32 1
+  %tmp.3 = load i32*, i32** %arrayidx1, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.3, i32** %arrayinit.element, align 4, !tbaa !8, !noalias !5
+  %tmp.4 = bitcast i32** %other to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %tmp.4) #5, !noalias !5
+  %arrayidx2 = getelementptr inbounds i32*, i32** %_p, i32 2
+  %tmp.5 = load i32*, i32** %arrayidx2, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.5, i32** %other, align 4, !tbaa !8, !noalias !5
+  %tobool = icmp ne i32 %c, 0
+  %cond = select i1 %tobool, i32** %.fca.0.gep, i32** %other
+  %tmp.6 = load i32*, i32** %arrayinit.element, align 4, !tbaa !8, !noalias !5
+  %through_local_tmp = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.6, i8* null, i32** %arrayinit.element, i32 0, metadata !2), !tbaa !8, !noalias !5
+  %tmp.7 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %through_local_tmp, i8* null, i32** %local_tmp, i32 0, metadata !2), !tbaa !8, !noalias !5
+  %tmp.8 = load i32, i32* %tmp.7, align 4, !tbaa !12, !noalias !5
+  %tmp.9 = load i32*, i32** %.fca.0.gep, align 4, !tbaa !8, !noalias !5
+  %tmp.10 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.9, i8* null, i32** %.fca.0.gep, i32 0, metadata !2), !tbaa !8, !noalias !5
+  store i32 %tmp.8, i32* %tmp.10, align 4, !tbaa !12, !noalias !5
+  %tmp.11 = load i32*, i32** %cond, align 4, !tbaa !8, !noalias !5
+  %tmp.12 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.11, i8* null, i32** %cond, i32 0, metadata !2), !tbaa !8, !noalias !5
+  store i32 42, i32* %tmp.12, align 4, !tbaa !12, !noalias !5
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %tmp.4) #5
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* %tmp.0) #5
+  ret void
+}
+
+; CHECK-LABEL: @test_01_before(
+; CHECK: %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0a2p0i32.i32([2 x i32*]* %rp, i32 0, metadata !7)
+; CHECK: %through_local_tmp = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.6, i8* %tmp.1, i32** %arrayinit.element, i32 0, metadata !7), !tbaa !8, !noalias !5
+; CHECK: %tmp.10 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.9, i8* %tmp.1, i32** %.fca.0.gep, i32 0, metadata !7), !tbaa !8, !noalias !5
+; CHECK: %tmp.12 = call i32* @llvm.noalias.p0i32.p0i8.p0p0i32.i32(i32* %tmp.11, i8* null, i32** %cond, i32 0, metadata !2), !tbaa !8, !noalias !5
+; CHECK-NOT: llvm.noalias
+
+; Function Attrs: nounwind
+define dso_local void @test_02(i32** %_p, i32 %c) #0 !noalias !14 {
+entry:
+  %foo = alloca %struct.FOO, align 4
+  %tmp = alloca %struct.FOO, align 4
+  %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0s_struct.FOOs.i32(%struct.FOO* %foo, i32 0, metadata !17)
+  %tmp.10 = call i8* @llvm.noalias.decl.p0i8.p0s_struct.FOOs.i32(%struct.FOO* %tmp, i32 0, metadata !19)
+  %tmp.12 = call %struct.FOO* @llvm.noalias.copy.guard.p0s_struct.FOOs.p0i8(%struct.FOO* %foo, i8* null, metadata !21, metadata !14)
+  %tmp.13 = load %struct.FOO, %struct.FOO* %tmp.12, align 4, !noalias !25
+  store %struct.FOO %tmp.13, %struct.FOO* %tmp, !noalias !25
+  ret void
+}
+
+; CHECK-LABEL: @test_02(
+; CHECK: %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0s_struct.FOOs.i32(%struct.FOO* %foo, i32 0, metadata !17)
+; CHECK: %tmp.10 = call i8* @llvm.noalias.decl.p0i8.p0s_struct.FOOs.i32(%struct.FOO* %tmp, i32 0, metadata !19)
+; CHECK: %tmp.12 = call %struct.FOO* @llvm.noalias.copy.guard.p0s_struct.FOOs.p0i8(%struct.FOO* %foo, i8* %tmp.1, metadata !21, metadata !17)
+; CHECK-NOT: llvm.noalias
+
+; Function Attrs: nounwind
+define dso_local void @test_01_after(i32** %_p, i32 %c) #0 !noalias !2 {
+entry:
+  %rp = alloca [2 x i32*], align 4
+  %local_tmp = alloca i32*, align 4
+  %other = alloca i32*, align 4
+  %.fca.0.gep = getelementptr inbounds [2 x i32*], [2 x i32*]* %rp, i32 0, i32 0
+  %.fca.1.gep = getelementptr inbounds [2 x i32*], [2 x i32*]* %rp, i32 0, i32 1
+  %tmp.0 = bitcast [2 x i32*]* %rp to i8*
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* %tmp.0) #5, !noalias !5
+  %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0a2p0i32.i32([2 x i32*]* %rp, i32 0, metadata !7)
+  %tmp.2 = load i32*, i32** %_p, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.2, i32** %.fca.0.gep, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %arrayinit.element = getelementptr inbounds i32*, i32** %.fca.0.gep, i32 1
+  %arrayidx1 = getelementptr inbounds i32*, i32** %_p, i32 1
+  %tmp.3 = load i32*, i32** %arrayidx1, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.3, i32** %arrayinit.element, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %tmp.4 = bitcast i32** %other to i8*
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %tmp.4) #5, !noalias !5
+  %arrayidx2 = getelementptr inbounds i32*, i32** %_p, i32 2
+  %tmp.5 = load i32*, i32** %arrayidx2, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  store i32* %tmp.5, i32** %other, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %tobool = icmp ne i32 %c, 0
+  %cond = select i1 %tobool, i32** %.fca.0.gep, i32** %other
+  %tmp.6 = load i32*, i32** %arrayinit.element, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %through_local_tmp = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.6, i8* null, i32** %arrayinit.element, i32** undef, i32 0, metadata !2), !tbaa !8, !noalias !5
+  %tmp.7 = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %through_local_tmp, i8* null, i32** %local_tmp, i32** undef, i32 0, metadata !2), !tbaa !8, !noalias !5
+  %tmp.8 = load i32, i32* %tmp.6, noalias_sidechannel i32* %tmp.7, align 4, !tbaa !12, !noalias !5
+  %tmp.9 = load i32*, i32** %.fca.0.gep, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %tmp.10 = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.9, i8* null, i32** %.fca.0.gep, i32** undef, i32 0, metadata !2), !tbaa !8, !noalias !5
+  store i32 %tmp.8, i32* %tmp.9, noalias_sidechannel i32* %tmp.10, align 4, !tbaa !12, !noalias !5
+  %tmp.11 = load i32*, i32** %cond, noalias_sidechannel i32** undef, align 4, !tbaa !8, !noalias !5
+  %tmp.12 = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.11, i8* null, i32** %cond, i32** undef, i32 0, metadata !2), !tbaa !8, !noalias !5
+  store i32 42, i32* %tmp.11, noalias_sidechannel i32* %tmp.12, align 4, !tbaa !12, !noalias !5
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %tmp.4) #5
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* %tmp.0) #5
+  ret void
+}
+
+; CHECK-LABEL: @test_01_after(
+; CHECK: %tmp.1 = call i8* @llvm.noalias.decl.p0i8.p0a2p0i32.i32([2 x i32*]* %rp, i32 0, metadata !7)
+; CHECK: %through_local_tmp = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.6, i8* %tmp.1, i32** %arrayinit.element, i32** undef, i32 0, metadata !7), !tbaa !8, !noalias !5
+; CHECK: %tmp.10 = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.9, i8* %tmp.1, i32** %.fca.0.gep, i32** undef, i32 0, metadata !7), !tbaa !8, !noalias !5
+; CHECK: %tmp.12 = call i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %tmp.11, i8* null, i32** %cond, i32** undef, i32 0, metadata !2), !tbaa !8, !noalias !5
+; CHECK-NOT: llvm.noalias
+
+; CHECK: declare
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: argmemonly nounwind
+declare i8* @llvm.noalias.decl.p0i8.p0a2p0i32.i32([2 x 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: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: argmemonly nounwind
+declare i8* @llvm.noalias.decl.p0i8.p0s_struct.FOOs.i32(%struct.FOO*, i32, metadata) #1
+
+; Function Attrs: nounwind readnone
+declare %struct.FOO* @llvm.noalias.copy.guard.p0s_struct.FOOs.p0i8(%struct.FOO*, i8*, metadata, metadata) #3
+
+; Function Attrs: nounwind readnone speculatable
+declare i32* @llvm.side.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32*, i8*, i32**, i32**, i32, metadata) #4
+
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "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 }
+attributes #4 = { nounwind readnone speculatable }
+attributes #5 = { nounwind }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!"clang version 10.0.0 (sgasip@krachtcs10:/u/sgasip/ipd/repositories/llvm_ipd 8efa7b085788c7ef2e29af74da59051ee86c2fd1)"}
+!2 = !{!3}
+!3 = distinct !{!3, !4, !"test_01: unknown scope"}
+!4 = distinct !{!4, !"test_01"}
+!5 = !{!6, !3}
+!6 = distinct !{!6, !4, !"test_01: rp"}
+!7 = !{!6}
+!8 = !{!9, !9, i64 0, i64 4}
+!9 = !{!10, i64 4, !"any pointer"}
+!10 = !{!11, i64 1, !"omnipotent char"}
+!11 = !{!"Simple C/C++ TBAA"}
+!12 = !{!13, !13, i64 0, i64 4}
+!13 = !{!10, i64 4, !"int"}
+!14 = !{!15}
+!15 = distinct !{!15, !16, !"test_02: unknown scope"}
+!16 = distinct !{!16, !"test_02"}
+!17 = !{!18}
+!18 = distinct !{!18, !16, !"test_02: foo"}
+!19 = !{!20}
+!20 = distinct !{!20, !16, !"test_02: tmp"}
+!21 = !{!22, !23, !24}
+!22 = !{i64 -1, i64 0}
+!23 = !{i64 -1, i64 1}
+!24 = !{i64 -1, i64 2}
+!25 = !{!20, !18, !15}