[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