[llvm] [clang] Initial backend C-restrict support (PR #173395)

Vladislav Belov via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 23 08:56:48 PST 2025


https://github.com/vbe-sc created https://github.com/llvm/llvm-project/pull/173395

This patch relates to https://github.com/llvm/llvm-project/pull/173394

This commit introduces initial backend support for the C restrict qualifier by adding a new LLVM pass, Scopes2AliasScopeMetadataPass. The pass transforms frontend-emitted !scope metadata into !alias.scope and !noalias metadata on memory operations, enabling better alias analysis and subsequent optimizations based on C's restrict semantics. Key changes include:

* A new pass that propagates alias scope information through the CFG and annotates loads and stores.

* Comprehensive handling of control flow, PHI nodes, and alloca-based pointers.

This implementation follows the design outlined in the LLVM RFC on restrict support, providing a foundation for more advanced alias-aware optimizations in the backend.

>From 3d844e57136e1bb0e90e799d8c6aa7d0046d5174 Mon Sep 17 00:00:00 2001
From: vbe-sc <vladislav.v.belov at yandex.ru>
Date: Tue, 23 Dec 2025 19:50:59 +0300
Subject: [PATCH] [clang] Initial backend C-restrict support

This commit introduces initial backend support for the C restrict qualifier by adding a new LLVM pass, Scopes2AliasScopeMetadataPass.
The pass transforms frontend-emitted !scope metadata into !alias.scope and !noalias metadata on memory operations,
enabling better alias analysis and subsequent optimizations based on C's restrict semantics.
Key changes include:

    A new pass that propagates alias scope information through the CFG and annotates loads and stores.

    Comprehensive handling of control flow, PHI nodes, and alloca-based pointers.

This implementation follows the design outlined in the LLVM RFC on restrict support, providing a foundation for more advanced alias-aware optimizations in the backend.
---
 .../IPO/Scopes2AliasScopeMetadata.h           |  50 ++
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |   4 +
 llvm/lib/Passes/PassRegistry.def              |   1 +
 llvm/lib/Transforms/IPO/CMakeLists.txt        |   1 +
 .../IPO/Scopes2AliasScopeMetadata.cpp         | 487 ++++++++++++++++++
 ...copemetadata-cf-phi-propagation-failure.ll | 104 ++++
 ...s2aliasscopemetadata-cf-phi-propagation.ll | 128 +++++
 ...iasscopemetadata-cf-propagation-failure.ll | 111 ++++
 ...copes2aliasscopemetadata-cf-propagation.ll | 103 ++++
 .../Util/scopes2aliasscopemetadata.ll         |  56 ++
 11 files changed, 1046 insertions(+)
 create mode 100644 llvm/include/llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h
 create mode 100644 llvm/lib/Transforms/IPO/Scopes2AliasScopeMetadata.cpp
 create mode 100644 llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation-failure.ll
 create mode 100644 llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation.ll
 create mode 100644 llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation-failure.ll
 create mode 100644 llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation.ll
 create mode 100644 llvm/test/Transforms/Util/scopes2aliasscopemetadata.ll

diff --git a/llvm/include/llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h b/llvm/include/llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h
new file mode 100644
index 0000000000000..c14098508a157
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h
@@ -0,0 +1,50 @@
+//=== Scopes2AliasScopeMetadata.h - Transform !alias.scope metadata. 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
+//
+//===----------------------------------------------------------------------===//
+// Currently this pass is C language specific
+//
+// Propagates !alias.scope metadata based on scope metadata emitted by the
+// frontend. The !alias.scope metadata nodes contain information about pointer
+// alias annotations (such as the restrict qualifier in C) and the scopes in
+// which these pointers were declared. According to certain rules described at
+// https://discourse.llvm.org/t/rfc-yet-another-llvm-restrict-support/87612,
+// we can annotate certain loads/stores with !noalias metadata to enable further
+// optimizations.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_SCOPES2ALIASSCOPEMETADATA_H
+#define LLVM_TRANSFORMS_IPO_SCOPES2ALIASSCOPEMETADATA_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Module;
+class MDNode;
+class Instruction;
+class LoadInst;
+
+struct Scopes2AliasScopeMetadataPass
+    : public PassInfoMixin<Scopes2AliasScopeMetadataPass> {
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+
+  bool convertScopeInfo2AliasScopeMetadata(Module &M);
+
+private:
+  std::unordered_map<std::string, MDNode *> ScopeMap;
+  SmallVector<std::pair<Instruction *, std::string>> NoAliasInstrs;
+
+  bool runOnFunction(Function &F);
+
+  bool propagateAliasScopes(Function &F);
+
+  void setNoAliasMetadata() const;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_SCOPES2ALIASSCOPEMETADATA_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 84ee043b5da56..b25707c22dbdb 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -234,6 +234,7 @@
 #include "llvm/Transforms/IPO/SCCP.h"
 #include "llvm/Transforms/IPO/SampleProfile.h"
 #include "llvm/Transforms/IPO/SampleProfileProbe.h"
+#include "llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h"
 #include "llvm/Transforms/IPO/StripDeadPrototypes.h"
 #include "llvm/Transforms/IPO/StripSymbols.h"
 #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 67b9a61cc576f..fddcaac5dc38e 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -72,6 +72,7 @@
 #include "llvm/Transforms/IPO/SCCP.h"
 #include "llvm/Transforms/IPO/SampleProfile.h"
 #include "llvm/Transforms/IPO/SampleProfileProbe.h"
+#include "llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h"
 #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/Instrumentation/AllocToken.h"
@@ -1716,6 +1717,9 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level,
   if (Phase == ThinOrFullLTOPhase::None)
     MPM.addPass(MemProfRemoveInfo());
 
+  // Convert !scope metadata to !alias.scope one. Set !noalias nodes as well
+  MPM.addPass(Scopes2AliasScopeMetadataPass());
+
   // Convert @llvm.global.annotations to !annotation metadata.
   MPM.addPass(Annotation2MetadataPass());
 
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index daf6b3d6dbd28..8b2695a58fc59 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -51,6 +51,7 @@ MODULE_ALIAS_ANALYSIS("globals-aa", GlobalsAA())
 #ifndef MODULE_PASS
 #define MODULE_PASS(NAME, CREATE_PASS)
 #endif
+MODULE_PASS("scopes2aliasscopemetadata", Scopes2AliasScopeMetadataPass())
 MODULE_PASS("always-inline", AlwaysInlinerPass())
 MODULE_PASS("annotation2metadata", Annotation2MetadataPass())
 MODULE_PASS("assign-guid", AssignGUIDPass())
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt
index 1c4ee0336d4db..3dbbc614781d1 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -40,6 +40,7 @@ add_llvm_component_library(LLVMipo
   SampleProfileMatcher.cpp
   SampleProfileProbe.cpp
   SCCP.cpp
+  Scopes2AliasScopeMetadata.cpp
   StripDeadPrototypes.cpp
   StripSymbols.cpp
   ThinLTOBitcodeWriter.cpp
diff --git a/llvm/lib/Transforms/IPO/Scopes2AliasScopeMetadata.cpp b/llvm/lib/Transforms/IPO/Scopes2AliasScopeMetadata.cpp
new file mode 100644
index 0000000000000..bd5b0cf7b2ff4
--- /dev/null
+++ b/llvm/lib/Transforms/IPO/Scopes2AliasScopeMetadata.cpp
@@ -0,0 +1,487 @@
+//===- Scopes2AliasScopeMetadata.h - Add !alias.scope metadata. -----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/Scopes2AliasScopeMetadata.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/IR/Analysis.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ModRef.h"
+#include <algorithm>
+#include <iterator>
+#include <queue>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "scopes2aliasscopemetadata"
+
+namespace {
+static constexpr auto ScopeNumberDelimiter = '_';
+static constexpr auto NoaliasIDDelimiter = '#';
+
+using GenMapType = DenseMap<BasicBlock *, DenseMap<Value *, MDNode *>>;
+} // namespace
+
+static void processLoadInstruction(Instruction *LI) {
+  auto *MD = LI->getMetadata(llvm::LLVMContext::MD_scope);
+  std::queue<Value *> Worklist;
+  std::set<Value *> Visited;
+
+  Worklist.push(LI);
+  Visited.insert(LI);
+
+  while (!Worklist.empty()) {
+    Value *Current = Worklist.front();
+    Worklist.pop();
+
+    for (User *U : Current->users()) {
+      if (!Visited.insert(U).second)
+        continue;
+
+      auto *GEP = dyn_cast<GetElementPtrInst>(U);
+      if (!GEP)
+        continue;
+      auto *PrevMD = GEP->getMetadata(llvm::LLVMContext::MD_scope);
+      if (PrevMD == MD)
+        continue;
+
+      GEP->setMetadata(llvm::LLVMContext::MD_scope, MD);
+      Worklist.push(GEP);
+    }
+  }
+}
+
+bool Scopes2AliasScopeMetadataPass::propagateAliasScopes(Function &F) {
+  bool Changed = false;
+  LLVMContext &Ctx = F.getContext();
+  MDBuilder MDB(Ctx);
+  std::string DomainName = (Twine(F.getName()) + "Domain").str();
+  MDNode *NewDomain = MDB.createAnonymousAliasScopeDomain(DomainName);
+  for (auto &I : instructions(F)) {
+    auto *MD = I.getMetadata(llvm::LLVMContext::MD_scope);
+    if (!MD)
+      continue;
+
+    // FIXME: Allocas require new kind of metadata to prevent interference
+    if (dyn_cast<AllocaInst>(&I))
+      continue;
+    for (auto *U : I.users()) {
+      if (dyn_cast<StoreInst>(U) || dyn_cast<LoadInst>(U)) {
+        if (auto *SI = dyn_cast<StoreInst>(U))
+          if (SI->getOperand(1) != &I)
+            continue;
+        auto *UserInst = dyn_cast<Instruction>(U);
+        const auto *MDStr = dyn_cast<MDString>(MD->getOperand(0).get());
+        assert(MDStr && "Scope metadata is not a string");
+        auto *MDSet = cast<MDNode>(MD->getOperand(1).get());
+        auto *IntMD =
+            mdconst::extract_or_null<ConstantInt>(MDSet->operands().back());
+        assert(IntMD &&
+               "Metadata format is invalid: constant integer expected");
+        auto IntMDVal = IntMD->getZExtValue();
+        std::string ScopeName = Twine(MDStr->getString()).str();
+
+        if (IntMDVal) {
+          ScopeName.append((Twine(NoaliasIDDelimiter) + Twine(IntMDVal)).str());
+          NoAliasInstrs.emplace_back(UserInst, ScopeName);
+        }
+
+        Changed |= true;
+        auto It = ScopeMap.find(ScopeName);
+        if (It != ScopeMap.end()) {
+          UserInst->setMetadata(LLVMContext::MD_alias_scope, It->second);
+          continue;
+        }
+        MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, ScopeName);
+        auto *ScopeMDNode = MDNode::get(Ctx, NewScope);
+        UserInst->setMetadata(LLVMContext::MD_alias_scope, ScopeMDNode);
+        ScopeMap.emplace(ScopeName, ScopeMDNode);
+      }
+    }
+  }
+
+  return Changed;
+}
+
+static std::string getPureScopeNumber(StringRef MetadataStr) {
+  assert(MetadataStr.contains(ScopeNumberDelimiter) &&
+         "Metadata format is invalid");
+
+  auto ScopeNumberPos = MetadataStr.rfind(ScopeNumberDelimiter) + 1;
+  auto NoaliasIDPos = MetadataStr.rfind(NoaliasIDDelimiter);
+
+  auto Count = (NoaliasIDPos == StringRef::npos)
+                   ? (MetadataStr.size() - ScopeNumberPos)
+                   : (NoaliasIDPos - ScopeNumberPos);
+  return MetadataStr.substr(ScopeNumberPos, Count).str();
+}
+
+static bool areScopesNotAlias(APInt FirstScopeVal, APInt SecondScopeVal) {
+  auto FirstMSB = FirstScopeVal.getBitWidth();
+  auto SecondMSB = SecondScopeVal.getBitWidth();
+
+  if (FirstMSB > SecondMSB)
+    return false;
+
+  if (FirstMSB == SecondMSB) {
+    if (FirstScopeVal == SecondScopeVal)
+      return true;
+    return false;
+  }
+
+  FirstScopeVal = FirstScopeVal.zext(SecondMSB);
+
+  if (FirstScopeVal == SecondScopeVal)
+    return true;
+
+  auto ShiftedSecond = (SecondScopeVal.lshr(SecondMSB - FirstMSB - 1));
+  auto ShiftedFirst = FirstScopeVal << 1;
+
+  auto Check = ShiftedSecond ^ ShiftedFirst;
+  return Check.isZero();
+}
+
+void Scopes2AliasScopeMetadataPass::setNoAliasMetadata() const {
+  for (auto &&[I, FirstScopeName] : NoAliasInstrs) {
+    for (auto &&[SecondScopeName, MDNode] : ScopeMap) {
+      if (FirstScopeName == SecondScopeName)
+        continue;
+
+      auto FirstScopeNumber = getPureScopeNumber(FirstScopeName);
+      auto SecondScopeNumber = getPureScopeNumber(SecondScopeName);
+
+      APInt NoAlaisScopeVal =
+          APInt(FirstScopeNumber.length(), FirstScopeNumber, /*Radix*/ 2);
+      APInt SecondScopeVal =
+          APInt(SecondScopeNumber.length(), SecondScopeNumber, /*Radix*/ 2);
+      if (areScopesNotAlias(std::move(NoAlaisScopeVal),
+                            std::move(SecondScopeVal)))
+        I->setMetadata(LLVMContext::MD_noalias, MDNode);
+    }
+  }
+}
+
+static void collectScopedAllocas(SmallSet<AllocaInst *, 8> &Vars,
+                                 BasicBlock &EntryBB) {
+  auto Allocas = llvm::transform(
+      make_filter_range(EntryBB,
+                        [](auto &&I) {
+                          auto *AI = dyn_cast<AllocaInst>(&I);
+                          return AI &&
+                                 AI->getMetadata(llvm::LLVMContext::MD_scope);
+                        }),
+      std::inserter(Vars, Vars.begin()),
+      [](auto &&I) { return cast<AllocaInst>(&I); });
+
+  llvm::copy(Vars, Allocas);
+}
+
+static void initGenMap(GenMapType &GenMap,
+                       const SmallSet<AllocaInst *, 8> &Vars,
+                       BasicBlock &EntryBB) {
+  for (auto *AI : Vars) {
+    assert((AI->getMetadata(llvm::LLVMContext::MD_scope)) &&
+           "Instruction must have MD_scope metadata");
+
+    GenMap[&EntryBB][AI] = AI->getMetadata(llvm::LLVMContext::MD_scope);
+  }
+}
+
+static void collectAllocaSts(SmallVector<StoreInst *, 8> &AllocaSts,
+                             BasicBlock &BB,
+                             const SmallSet<AllocaInst *, 8> &Vars) {
+  for (auto &I : BB) {
+    auto *SI = dyn_cast<StoreInst>(&I);
+    if (!SI)
+      continue;
+
+    auto *AI = dyn_cast<AllocaInst>(SI->getPointerOperand());
+    if (!AI)
+      continue;
+
+    if (!Vars.contains(AI))
+      continue;
+
+    auto *MD = AI->getMetadata(llvm::LLVMContext::MD_scope);
+
+    auto *MDSet = cast<MDNode>(MD->getOperand(1).get());
+    auto *IntMD =
+        mdconst::extract_or_null<ConstantInt>(MDSet->operands().back());
+    assert(IntMD && "Metadata format is invalid: constant integer expected");
+    auto IntMDVal = IntMD->getZExtValue();
+    if (IntMDVal)
+      continue;
+
+    AllocaSts.push_back(SI);
+  }
+}
+
+static MDNode *getSrcScopeMD(Value *V, BasicBlock *BB) {
+  if (auto *AI = dyn_cast<Instruction>(V))
+    return AI->getMetadata(llvm::LLVMContext::MD_scope);
+
+  return nullptr;
+}
+
+template <typename ItTy>
+static bool promoteScope(ItTy Begin, ItTy End, AllocaInst *AI,
+                         MDNode *MDScope) {
+  assert(AI && "Non-null alloca pointer expected");
+  auto LoadsToProcess =
+      make_filter_range(make_range(Begin, End), [AI, MDScope](auto &&I) {
+        auto *LI = dyn_cast<LoadInst>(&I);
+        return LI && (LI->getPointerOperand() == AI) &&
+               (LI->getMetadata(llvm::LLVMContext::MD_scope) != MDScope);
+      });
+  if (!range_size(LoadsToProcess))
+    return false;
+
+  for (auto &&I : LoadsToProcess) {
+    auto *LI = cast<LoadInst>(&I);
+    LI->setMetadata(llvm::LLVMContext::MD_scope, MDScope);
+    processLoadInstruction(LI);
+  }
+  return true;
+}
+
+static bool promotePHIs(BasicBlock &BB, GenMapType &GenMap) {
+  bool Changed = false;
+  for (auto &&PHIIt : BB.phis()) {
+    SmallVector<MDNode *> MOP;
+    for (auto &&V : PHIIt.incoming_values()) {
+      auto *I = dyn_cast<Instruction>(&*V);
+      if (!I)
+        break;
+      if (auto *MD = I->getMetadata(llvm::LLVMContext::MD_scope))
+        MOP.emplace_back(MD);
+    }
+
+    bool AllEq = ((std::adjacent_find(MOP.begin(), MOP.end(),
+                                      [](const auto &Fst, const auto &Sec) {
+                                        return Fst != Sec;
+                                      })) == MOP.end());
+    if (MOP.size() == PHIIt.getNumIncomingValues() && AllEq)
+      PHIIt.setMetadata(llvm::LLVMContext::MD_scope, MOP.front());
+  }
+
+  return Changed;
+}
+
+template <typename IterTy>
+static IterTy getNextStoreInstToAIOrEnd(AllocaInst *AI, BasicBlock *BB,
+                                        IterTy StartIt) {
+  auto *MD = AI->getMetadata(llvm::LLVMContext::MD_scope);
+
+  auto *MDSet = cast<MDNode>(MD->getOperand(1).get());
+  auto *IntMD = mdconst::extract_or_null<ConstantInt>(MDSet->operands().back());
+  assert(IntMD && "Metadata format is invalid: constant integer expected");
+  auto IntMDVal = IntMD->getZExtValue();
+  if (IntMDVal)
+    return BB->end();
+
+  return std::find_if(StartIt, BB->end(), [AI](auto &&I) {
+    auto *OtherSI = dyn_cast<StoreInst>(&I);
+    return (OtherSI && OtherSI->getPointerOperand() == AI);
+  });
+}
+
+static bool meetAllocasForBB(BasicBlock *BB,
+                             const SmallSet<AllocaInst *, 8> &Vars,
+                             GenMapType &GenMap) {
+  bool Changed = false;
+  if (BB->isEntryBlock())
+    return Changed;
+
+  for (auto *V : Vars) {
+    SmallVector<MDNode *> MOP;
+    for (auto *PredBB :
+         make_filter_range(predecessors(BB), [&GenMap](auto *PredBB) {
+           return GenMap.contains(PredBB);
+         }))
+      MOP.emplace_back(GenMap[PredBB][V]);
+
+    bool AllEq = ((std::adjacent_find(MOP.begin(), MOP.end(),
+                                      [](const auto &Fst, const auto &Sec) {
+                                        return Fst != Sec;
+                                      })) == MOP.end());
+
+    // TODO: transfer function should be more intelligent
+    GenMap[BB][V] = AllEq ? MOP.front() : nullptr;
+  }
+
+  Changed |= promotePHIs(*BB, GenMap);
+  for (auto *AI : Vars) {
+    auto &&NextSI =
+        getNextStoreInstToAIOrEnd(dyn_cast<AllocaInst>(AI), BB, BB->begin());
+    Changed |= promoteScope(BB->begin(), NextSI, AI, GenMap[BB][AI]);
+  }
+  return Changed;
+}
+
+static bool
+propagateScopesThroughTheCFG(Function &F, GenMapType &GenMap,
+                             const SmallSet<AllocaInst *, 8> &Vars) {
+  bool Changed = false;
+  bool FixedPointReached = false;
+
+  ReversePostOrderTraversal<Function *> RPOT(&F);
+  while (!FixedPointReached) {
+    FixedPointReached = true;
+    for (auto *BB : RPOT) {
+      FixedPointReached = !meetAllocasForBB(BB, Vars, GenMap);
+      SmallVector<StoreInst *, 8> AllocaSts;
+      collectAllocaSts(AllocaSts, *BB, Vars);
+
+      for (auto *SI : AllocaSts) {
+        auto *AI = cast<AllocaInst>(SI->getPointerOperand());
+        auto *AISrc = SI->getValueOperand();
+
+        auto *MDScope = getSrcScopeMD(AISrc, BB);
+        if (!MDScope)
+          continue;
+        auto *Prev = GenMap[BB][AI];
+        if (Prev == MDScope)
+          continue;
+        GenMap[BB][AI] = MDScope;
+        FixedPointReached = false;
+        auto NextIt = std::next(SI->getIterator());
+
+        auto &&NextSI = getNextStoreInstToAIOrEnd(AI, BB, NextIt);
+        FixedPointReached |= !promoteScope(NextIt, NextSI, AI, MDScope);
+        Changed |= FixedPointReached;
+      }
+    }
+  }
+  return Changed;
+}
+
+static bool propagateBasedOnAllocas(Function &F,
+                                    SmallSet<AllocaInst *, 8> &Vars) {
+  GenMapType GenMap;
+
+  assert(!F.empty() && "Function body expected");
+  auto &EntryBB = F.front();
+  initGenMap(GenMap, Vars, EntryBB);
+  for (auto *V : Vars)
+    promoteScope(EntryBB.begin(), EntryBB.end(), V, GenMap[&EntryBB][V]);
+
+  auto Changed = propagateScopesThroughTheCFG(F, GenMap, Vars);
+  return Changed;
+}
+
+static bool isFunctionBasiclyAllowsAliasScopePropagation(const Function &F) {
+  if (!F.size())
+    return false;
+
+  // Currently we disable this pass if the frontend has already annotated
+  // instructions with llvm::LLVMContext::MD_alias_scope metadata. Otherwise, we
+  // may violate language-specific semantics (e.g., aliasing guarantees), as
+  // this pass could reorder memory accesses in ways that conflict with these
+  // annotations.
+  if (any_of(instructions(F), [](const auto &I) {
+        return I.getMetadata(llvm::LLVMContext::MD_alias_scope);
+      }))
+    return false;
+
+  return true;
+}
+
+static bool hasDangerousAllocaUsers(const AllocaInst *AI) {
+  for (auto *U : AI->users()) {
+    if (auto *SI = dyn_cast<StoreInst>(U)) {
+      if (SI->getValueOperand() == AI)
+        return true;
+      continue;
+    }
+
+    if (auto *II = dyn_cast<IntrinsicInst>(U)) {
+      if (!II->isLifetimeStartOrEnd())
+        return true;
+      continue;
+    }
+
+    if (!isa<LoadInst>(U))
+      return true;
+  }
+  return false;
+}
+
+static bool
+areVarsAllowAliasScopePropagation(const SmallSet<AllocaInst *, 8> &Vars) {
+  if (Vars.empty())
+    return false;
+  bool hasScopeMetadata = false;
+
+  for (auto *AI : Vars) {
+    hasScopeMetadata |=
+        (AI->getMetadata(llvm::LLVMContext::MD_scope) != nullptr);
+
+    // Currently we conservatively drop any cases with "escaped" allocas
+    if (hasDangerousAllocaUsers(AI))
+      return false;
+  }
+
+  return hasScopeMetadata;
+}
+
+bool Scopes2AliasScopeMetadataPass::runOnFunction(Function &F) {
+  bool Changed = false;
+  if (!isFunctionBasiclyAllowsAliasScopePropagation(F))
+    return Changed;
+
+  SmallSet<AllocaInst *, 8> Vars;
+  auto &EntryBB = F.front();
+
+  // TODO: support structures GEPs
+  collectScopedAllocas(Vars, EntryBB);
+  if (!areVarsAllowAliasScopePropagation(Vars))
+    return Changed;
+
+  Changed |= propagateBasedOnAllocas(F, Vars);
+  Changed |= propagateAliasScopes(F);
+  setNoAliasMetadata();
+
+  return Changed;
+}
+
+bool Scopes2AliasScopeMetadataPass::convertScopeInfo2AliasScopeMetadata(
+    Module &M) {
+  bool Changed = false;
+
+  for (auto &F : M)
+    Changed |= runOnFunction(F);
+
+  return Changed;
+}
+
+PreservedAnalyses
+Scopes2AliasScopeMetadataPass::run(Module &M, ModuleAnalysisManager &AM) {
+  return convertScopeInfo2AliasScopeMetadata(M) ? PreservedAnalyses::none()
+                                                : PreservedAnalyses::all();
+}
diff --git a/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation-failure.ll b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation-failure.ll
new file mode 100644
index 0000000000000..16a18741552d9
--- /dev/null
+++ b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation-failure.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='scopes2aliasscopemetadata' -S %s | FileCheck %s
+
+define dso_local signext i32 @foo(ptr %x, ptr %y, i32 %cond) {
+; CHECK-LABEL: define dso_local signext i32 @foo(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i32 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0:![0-9]+]]
+; CHECK-NEXT:    [[Y_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[COND_ADDR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = alloca ptr, align 8, !scope [[META2:![0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[X]], ptr [[X_ADDR]], align 8
+; CHECK-NEXT:    store ptr [[Y]], ptr [[Y_ADDR]], align 8
+; CHECK-NEXT:    store i32 [[COND]], ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[TMP1]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[TMP2]])
+; CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP9]], ptr [[TMP2]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    [[TMP10:%.*]] = load i32, ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP10]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+; CHECK:       [[COND_TRUE]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[TMP1]], align 8, !scope [[META2]]
+; CHECK-NEXT:    br label %[[COND_END:.*]]
+; CHECK:       [[COND_FALSE]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[TMP2]], align 8, !scope [[META0]]
+; CHECK-NEXT:    br label %[[COND_END]]
+; CHECK:       [[COND_END]]:
+; CHECK-NEXT:    [[COND1:%.*]] = phi ptr [ [[TMP3]], %[[COND_TRUE]] ], [ [[TMP4]], %[[COND_FALSE]] ]
+; CHECK-NEXT:    store ptr [[COND1]], ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store i32 42, ptr [[TMP5]], align 4, !alias.scope [[META4:![0-9]+]]
+; CHECK-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[P]], align 8
+; CHECK-NEXT:    store i32 14, ptr [[TMP6]], align 4
+; CHECK-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4, !alias.scope [[META4]]
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[TMP2]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[TMP1]])
+; CHECK-NEXT:    ret i32 [[TMP8]]
+;
+entry:
+  %x.addr = alloca ptr, align 8, !scope !1
+  %y.addr = alloca ptr, align 8, !scope !1
+  %cond.addr = alloca i32, align 4
+  %tmp_1 = alloca ptr, align 8, !scope !3
+  %tmp_2 = alloca ptr, align 8, !scope !1
+  %p = alloca ptr, align 8, !scope !1
+  store ptr %x, ptr %x.addr, align 8
+  store ptr %y, ptr %y.addr, align 8
+  store i32 %cond, ptr %cond.addr, align 4
+  call void @llvm.lifetime.start.p0(i64 8, ptr %tmp_1)
+  %0 = load ptr, ptr %x.addr, align 8
+  store ptr %0, ptr %tmp_1, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr %tmp_2)
+  %1 = load ptr, ptr %x.addr, align 8
+  store ptr %1, ptr %tmp_2, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr %p)
+  %2 = load i32, ptr %cond.addr, align 4
+  %tobool = icmp ne i32 %2, 0
+  br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  %3 = load ptr, ptr %tmp_1, align 8
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  %4 = load ptr, ptr %tmp_2, align 8
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond1 = phi ptr [ %3, %cond.true ], [ %4, %cond.false ]
+  store ptr %cond1, ptr %p, align 8
+  %5 = load ptr, ptr %y.addr, align 8
+  store i32 42, ptr %5, align 4
+  %6 = load ptr, ptr %p, align 8
+  store i32 14, ptr %6, align 4
+  %7 = load ptr, ptr %y.addr, align 8
+  %8 = load i32, ptr %7, align 4
+  call void @llvm.lifetime.end.p0(i64 8, ptr %p)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %tmp_2)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %tmp_1)
+  ret i32 %8
+}
+
+
+!1 = !{!"foo_1", !2}
+!2 = !{i64 0}
+!3 = !{!"foo_1", !4}
+!4 = !{i64 1}
+;.
+; CHECK: [[META0]] = !{!"foo_1", [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0}
+; CHECK: [[META2]] = !{!"foo_1", [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{i64 1}
+; CHECK: [[META4]] = !{[[META5:![0-9]+]]}
+; CHECK: [[META5]] = distinct !{[[META5]], [[META6:![0-9]+]], !"foo_1"}
+; CHECK: [[META6]] = distinct !{[[META6]], !"fooDomain"}
+;.
diff --git a/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation.ll b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation.ll
new file mode 100644
index 0000000000000..549f95662d5b4
--- /dev/null
+++ b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-phi-propagation.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='scopes2aliasscopemetadata' -S %s | FileCheck %s
+
+define dso_local signext i32 @foo(ptr %x, ptr %y, i32 %cond) {
+; CHECK-LABEL: define dso_local signext i32 @foo(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i32 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0:![0-9]+]]
+; CHECK-NEXT:    [[Y_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[COND_ADDR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[TMP:%.*]] = alloca ptr, align 8, !scope [[META2:![0-9]+]]
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[X]], ptr [[X_ADDR]], align 8
+; CHECK-NEXT:    store ptr [[Y]], ptr [[Y_ADDR]], align 8
+; CHECK-NEXT:    store i32 [[COND]], ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[TMP]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP0]], ptr [[TMP]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP1]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
+; CHECK:       [[COND_TRUE]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8, !scope [[META2]]
+; CHECK-NEXT:    br label %[[COND_END:.*]]
+; CHECK:       [[COND_FALSE]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[TMP]], align 8, !scope [[META2]]
+; CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 2, !scope [[META2]]
+; CHECK-NEXT:    br label %[[COND_END]]
+; CHECK:       [[COND_END]]:
+; CHECK-NEXT:    [[COND1:%.*]] = phi ptr [ [[TMP2]], %[[COND_TRUE]] ], [ [[ADD_PTR]], %[[COND_FALSE]] ], !scope [[META2]]
+; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[COND1]], align 4, !alias.scope [[META4:![0-9]+]], !noalias [[META7:![0-9]+]]
+; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp ne i32 [[TMP4]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL2]], label %[[COND_TRUE3:.*]], label %[[COND_FALSE4:.*]]
+; CHECK:       [[COND_TRUE3]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP]], align 8, !scope [[META2]]
+; CHECK-NEXT:    br label %[[COND_END6:.*]]
+; CHECK:       [[COND_FALSE4]]:
+; CHECK-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[TMP]], align 8, !scope [[META2]]
+; CHECK-NEXT:    [[ADD_PTR5:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i64 2, !scope [[META2]]
+; CHECK-NEXT:    br label %[[COND_END6]]
+; CHECK:       [[COND_END6]]:
+; CHECK-NEXT:    [[COND7:%.*]] = phi ptr [ [[TMP5]], %[[COND_TRUE3]] ], [ [[ADD_PTR5]], %[[COND_FALSE4]] ], !scope [[META2]]
+; CHECK-NEXT:    store ptr [[COND7]], ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store i32 42, ptr [[TMP7]], align 4, !alias.scope [[META7]]
+; CHECK-NEXT:    [[TMP8:%.*]] = load ptr, ptr [[P]], align 8, !scope [[META2]]
+; CHECK-NEXT:    store i32 14, ptr [[TMP8]], align 4, !alias.scope [[META4]], !noalias [[META7]]
+; CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[TMP10:%.*]] = load i32, ptr [[TMP9]], align 4, !alias.scope [[META7]]
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[TMP]])
+; CHECK-NEXT:    ret i32 [[TMP10]]
+;
+entry:
+  %x.addr = alloca ptr, align 8, !scope !1
+  %y.addr = alloca ptr, align 8, !scope !1
+  %cond.addr = alloca i32, align 4
+  %tmp = alloca ptr, align 8, !scope !3
+  %p = alloca ptr, align 8, !scope !1
+  store ptr %x, ptr %x.addr, align 8
+  store ptr %y, ptr %y.addr, align 8
+  store i32 %cond, ptr %cond.addr, align 4
+  call void @llvm.lifetime.start.p0(i64 8, ptr %tmp)
+  %0 = load ptr, ptr %x.addr, align 8
+  store ptr %0, ptr %tmp, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr %p)
+  %1 = load i32, ptr %cond.addr, align 4
+  %tobool = icmp ne i32 %1, 0
+  br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true:                                        ; preds = %entry
+  %2 = load ptr, ptr %tmp, align 8
+  br label %cond.end
+
+cond.false:                                       ; preds = %entry
+  %3 = load ptr, ptr %tmp, align 8
+  %add.ptr = getelementptr inbounds i32, ptr %3, i64 2
+  br label %cond.end
+
+cond.end:                                         ; preds = %cond.false, %cond.true
+  %cond1 = phi ptr [ %2, %cond.true ], [ %add.ptr, %cond.false ]
+  %4 = load i32, ptr %cond1, align 4
+  %tobool2 = icmp ne i32 %4, 0
+  br i1 %tobool2, label %cond.true3, label %cond.false4
+
+cond.true3:                                       ; preds = %cond.end
+  %5 = load ptr, ptr %tmp, align 8
+  br label %cond.end6
+
+cond.false4:                                      ; preds = %cond.end
+  %6 = load ptr, ptr %tmp, align 8
+  %add.ptr5 = getelementptr inbounds i32, ptr %6, i64 2
+  br label %cond.end6
+
+cond.end6:                                        ; preds = %cond.false4, %cond.true3
+  %cond7 = phi ptr [ %5, %cond.true3 ], [ %add.ptr5, %cond.false4 ]
+  store ptr %cond7, ptr %p, align 8
+  %7 = load ptr, ptr %y.addr, align 8
+  store i32 42, ptr %7, align 4
+  %8 = load ptr, ptr %p, align 8
+  store i32 14, ptr %8, align 4
+  %9 = load ptr, ptr %y.addr, align 8
+  %10 = load i32, ptr %9, align 4
+  call void @llvm.lifetime.end.p0(i64 8, ptr %p)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %tmp)
+  ret i32 %10
+}
+
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
+
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
+
+!1 = !{!"foo_1", !2}
+!2 = !{i64 0}
+!3 = !{!"foo_1", !4}
+!4 = !{i64 1}
+;.
+; CHECK: [[META0]] = !{!"foo_1", [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0}
+; CHECK: [[META2]] = !{!"foo_1", [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{i64 1}
+; CHECK: [[META4]] = !{[[META5:![0-9]+]]}
+; CHECK: [[META5]] = distinct !{[[META5]], [[META6:![0-9]+]], !"foo_1#1"}
+; CHECK: [[META6]] = distinct !{[[META6]], !"fooDomain"}
+; CHECK: [[META7]] = !{[[META8:![0-9]+]]}
+; CHECK: [[META8]] = distinct !{[[META8]], [[META6]], !"foo_1"}
+;.
diff --git a/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation-failure.ll b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation-failure.ll
new file mode 100644
index 0000000000000..2a637391115aa
--- /dev/null
+++ b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation-failure.ll
@@ -0,0 +1,111 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='scopes2aliasscopemetadata' -S %s | FileCheck %s
+
+define dso_local signext i32 @foo(ptr %x, ptr %y, i32 %cond) {
+; CHECK-LABEL: define dso_local signext i32 @foo(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i32 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0:![0-9]+]]
+; CHECK-NEXT:    [[Y_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[COND_ADDR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[Z:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META2:![0-9]+]]
+; CHECK-NEXT:    [[TMP:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[X]], ptr [[X_ADDR]], align 8
+; CHECK-NEXT:    store ptr [[Y]], ptr [[Y_ADDR]], align 8
+; CHECK-NEXT:    store i32 [[COND]], ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[Z]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP0]], ptr [[P]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[TMP]])
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 2, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[ADD_PTR]], ptr [[TMP]], align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store i32 14, ptr [[TMP2]], align 4, !alias.scope [[META4:![0-9]+]]
+; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP3]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[P]], align 8, !scope [[META2]]
+; CHECK-NEXT:    [[ADD_PTR1:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 1, !scope [[META2]]
+; CHECK-NEXT:    store ptr [[ADD_PTR1]], ptr [[Z]], align 8
+; CHECK-NEXT:    br label %[[IF_END:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[TMP]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP5]], ptr [[Z]], align 8
+; CHECK-NEXT:    br label %[[IF_END]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[Z]], align 8
+; CHECK-NEXT:    store i32 42, ptr [[TMP6]], align 4
+; CHECK-NEXT:    [[TMP7:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[TMP8:%.*]] = load i32, ptr [[TMP7]], align 4, !alias.scope [[META4]]
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[TMP]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[Z]])
+; CHECK-NEXT:    ret i32 [[TMP8]]
+;
+entry:
+  %x.addr = alloca ptr, align 8, !scope !1
+  %y.addr = alloca ptr, align 8, !scope !1
+%cond.addr = alloca i32, align 4
+  %z = alloca ptr, align 8, !scope !1
+  %p = alloca ptr, align 8, !scope !3
+  %tmp = alloca ptr, align 8, !scope !1
+  store ptr %x, ptr %x.addr, align 8
+  store ptr %y, ptr %y.addr, align 8
+  store i32 %cond, ptr %cond.addr, align 4
+  call void @llvm.lifetime.start.p0(i64 8, ptr %z)
+  call void @llvm.lifetime.start.p0(i64 8, ptr %p)
+  %0 = load ptr, ptr %x.addr, align 8
+  store ptr %0, ptr %p, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr %tmp)
+  %1 = load ptr, ptr %x.addr, align 8
+  %add.ptr = getelementptr inbounds i32, ptr %1, i64 2
+  store ptr %add.ptr, ptr %tmp, align 8
+  %2 = load ptr, ptr %y.addr, align 8
+  store i32 14, ptr %2, align 4
+  %3 = load i32, ptr %cond.addr, align 4
+  %tobool = icmp ne i32 %3, 0
+  br i1 %tobool, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %4 = load ptr, ptr %p, align 8
+  %add.ptr1 = getelementptr inbounds i32, ptr %4, i64 1
+  store ptr %add.ptr1, ptr %z, align 8
+  br label %if.end
+
+if.else:                                          ; preds = %entry
+  %5 = load ptr, ptr %tmp, align 8
+  store ptr %5, ptr %z, align 8
+  br label %if.end
+
+if.end:                                           ; preds = %if.else, %if.then
+  %6 = load ptr, ptr %z, align 8
+  store i32 42, ptr %6, align 4
+  %7 = load ptr, ptr %y.addr, align 8
+  %8 = load i32, ptr %7, align 4
+  call void @llvm.lifetime.end.p0(i64 8, ptr %tmp)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %p)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %z)
+  ret i32 %8
+}
+
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
+
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
+
+!1 = !{!"foo_1", !2}
+!2 = !{i64 0}
+!3 = !{!"foo_1", !4}
+!4 = !{i64 1}
+;.
+; CHECK: [[META0]] = !{!"foo_1", [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0}
+; CHECK: [[META2]] = !{!"foo_1", [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{i64 1}
+; CHECK: [[META4]] = !{[[META5:![0-9]+]]}
+; CHECK: [[META5]] = distinct !{[[META5]], [[META6:![0-9]+]], !"foo_1"}
+; CHECK: [[META6]] = distinct !{[[META6]], !"fooDomain"}
+;.
diff --git a/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation.ll b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation.ll
new file mode 100644
index 0000000000000..74cf5454e49b0
--- /dev/null
+++ b/llvm/test/Transforms/Util/scopes2aliasscopemetadata-cf-propagation.ll
@@ -0,0 +1,103 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='scopes2aliasscopemetadata' -S %s | FileCheck %s
+
+define dso_local signext i32 @foo(ptr %x, ptr %y, i32 %cond) {
+; CHECK-LABEL: define dso_local signext i32 @foo(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]], i32 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0:![0-9]+]]
+; CHECK-NEXT:    [[Y_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[COND_ADDR:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[Z:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META2:![0-9]+]]
+; CHECK-NEXT:    store ptr [[X]], ptr [[X_ADDR]], align 8
+; CHECK-NEXT:    store ptr [[Y]], ptr [[Y_ADDR]], align 8
+; CHECK-NEXT:    store i32 [[COND]], ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[Z]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP0]], ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store i32 14, ptr [[TMP1]], align 4, !alias.scope [[META4:![0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[COND_ADDR]], align 4
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP2]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[P]], align 8, !scope [[META2]]
+; CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 1, !scope [[META2]]
+; CHECK-NEXT:    store ptr [[ADD_PTR]], ptr [[Z]], align 8
+; CHECK-NEXT:    br label %[[IF_END:.*]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[P]], align 8, !scope [[META2]]
+; CHECK-NEXT:    [[ADD_PTR1:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 2, !scope [[META2]]
+; CHECK-NEXT:    store ptr [[ADD_PTR1]], ptr [[Z]], align 8
+; CHECK-NEXT:    br label %[[IF_END]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[Z]], align 8, !scope [[META2]]
+; CHECK-NEXT:    store i32 42, ptr [[TMP5]], align 4, !alias.scope [[META7:![0-9]+]], !noalias [[META4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4, !alias.scope [[META4]]
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[P]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[Z]])
+; CHECK-NEXT:    ret i32 [[TMP7]]
+;
+entry:
+  %x.addr = alloca ptr, align 8, !scope !1
+  %y.addr = alloca ptr, align 8, !scope !1
+  %cond.addr = alloca i32, align 4
+  %z = alloca ptr, align 8, !scope !1
+  %p = alloca ptr, align 8, !scope !3
+  store ptr %x, ptr %x.addr, align 8
+  store ptr %y, ptr %y.addr, align 8
+  store i32 %cond, ptr %cond.addr, align 4
+  call void @llvm.lifetime.start.p0(i64 8, ptr %z)
+  call void @llvm.lifetime.start.p0(i64 8, ptr %p)
+  %0 = load ptr, ptr %x.addr, align 8
+  store ptr %0, ptr %p, align 8
+  %1 = load ptr, ptr %y.addr, align 8
+  store i32 14, ptr %1, align 4
+  %2 = load i32, ptr %cond.addr, align 4
+  %tobool = icmp ne i32 %2, 0
+  br i1 %tobool, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %3 = load ptr, ptr %p, align 8
+  %add.ptr = getelementptr inbounds i32, ptr %3, i64 1
+  store ptr %add.ptr, ptr %z, align 8
+  br label %if.end
+
+if.else:                                          ; preds = %entry
+  %4 = load ptr, ptr %p, align 8
+  %add.ptr1 = getelementptr inbounds i32, ptr %4, i64 2
+  store ptr %add.ptr1, ptr %z, align 8
+  br label %if.end
+
+if.end:                                           ; preds = %if.else, %if.then
+  %5 = load ptr, ptr %z, align 8
+  store i32 42, ptr %5, align 4
+  %6 = load ptr, ptr %y.addr, align 8
+  %7 = load i32, ptr %6, align 4
+  call void @llvm.lifetime.end.p0(i64 8, ptr %p)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %z)
+  ret i32 %7
+}
+
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
+
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
+
+!1 = !{!"foo_1", !2}
+!2 = !{i64 0}
+!3 = !{!"foo_1", !4}
+!4 = !{i64 1}
+;.
+; CHECK: [[META0]] = !{!"foo_1", [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0}
+; CHECK: [[META2]] = !{!"foo_1", [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{i64 1}
+; CHECK: [[META4]] = !{[[META5:![0-9]+]]}
+; CHECK: [[META5]] = distinct !{[[META5]], [[META6:![0-9]+]], !"foo_1"}
+; CHECK: [[META6]] = distinct !{[[META6]], !"fooDomain"}
+; CHECK: [[META7]] = !{[[META8:![0-9]+]]}
+; CHECK: [[META8]] = distinct !{[[META8]], [[META6]], !"foo_1#1"}
+;.
diff --git a/llvm/test/Transforms/Util/scopes2aliasscopemetadata.ll b/llvm/test/Transforms/Util/scopes2aliasscopemetadata.ll
new file mode 100644
index 0000000000000..fe3cda7fa8e6e
--- /dev/null
+++ b/llvm/test/Transforms/Util/scopes2aliasscopemetadata.ll
@@ -0,0 +1,56 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='scopes2aliasscopemetadata' -S %s | FileCheck %s
+
+define dso_local signext i32 @foo(ptr %x, ptr %y) {
+; CHECK-LABEL: define dso_local signext i32 @foo(
+; CHECK-SAME: ptr [[X:%.*]], ptr [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0:![0-9]+]]
+; CHECK-NEXT:    [[Y_ADDR:%.*]] = alloca ptr, align 8, !scope [[META0]]
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8, !scope [[META2:![0-9]+]]
+; CHECK-NEXT:    store ptr [[X]], ptr [[X_ADDR]], align 8
+; CHECK-NEXT:    store ptr [[Y]], ptr [[Y_ADDR]], align 8
+; CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store ptr [[TMP0]], ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    store i32 14, ptr [[TMP1]], align 4, !alias.scope [[META4:![0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[P]], align 8, !scope [[META2]]
+; CHECK-NEXT:    store i32 42, ptr [[TMP2]], align 4, !alias.scope [[META7:![0-9]+]], !noalias [[META4]]
+; CHECK-NEXT:    [[TMP3:%.*]] = load ptr, ptr [[Y_ADDR]], align 8, !scope [[META0]]
+; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4, !alias.scope [[META4]]
+; CHECK-NEXT:    ret i32 [[TMP4]]
+;
+entry:
+  %x.addr = alloca ptr, align 8, !scope !1
+  %y.addr = alloca ptr, align 8, !scope !1
+  %p = alloca ptr, align 8, !scope !3
+  store ptr %x, ptr %x.addr, align 8
+  store ptr %y, ptr %y.addr, align 8
+  %0 = load ptr, ptr %x.addr, align 8
+  store ptr %0, ptr %p, align 8
+  %1 = load ptr, ptr %y.addr, align 8
+  store i32 14, ptr %1, align 4
+  %2 = load ptr, ptr %p, align 8
+  store i32 42, ptr %2, align 4
+  %3 = load ptr, ptr %y.addr, align 8
+  %4 = load i32, ptr %3, align 4
+  ret i32 %4
+}
+
+
+!1 = !{!"foo_1", !2}
+!2 = !{i64 0}
+!3 = !{!"foo_1", !4}
+!4 = !{i64 1}
+
+;.
+; CHECK: [[META0]] = !{!"foo_1", [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0}
+; CHECK: [[META2]] = !{!"foo_1", [[META3:![0-9]+]]}
+; CHECK: [[META3]] = !{i64 1}
+; CHECK: [[META4]] = !{[[META5:![0-9]+]]}
+; CHECK: [[META5]] = distinct !{[[META5]], [[META6:![0-9]+]], !"foo_1"}
+; CHECK: [[META6]] = distinct !{[[META6]], !"fooDomain"}
+; CHECK: [[META7]] = !{[[META8:![0-9]+]]}
+; CHECK: [[META8]] = distinct !{[[META8]], [[META6]], !"foo_1#1"}
+;.



More information about the llvm-commits mailing list