[llvm] [IR] Add logical SROA pass (PR #192058)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 14 06:32:47 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Nathan Gauër (Keenuts)
<details>
<summary>Changes</summary>
This commit adds a logical-pointer compatible SROA pass. As-is, the pass does not optimizes nested structs, arrays, or usages of structured alloca with non-structured GEP instructions.
Adding support for both is not complex, but increase the size of the PR, hence we can start with this.
Next step will be to allow mem2reg to apply on logical alloca/ptr, and to add support for nesting/arrays.
---
Patch is 23.53 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192058.diff
13 Files Affected:
- (modified) llvm/include/llvm/IR/IRBuilder.h (+10)
- (modified) llvm/include/llvm/InitializePasses.h (+1)
- (modified) llvm/include/llvm/LinkAllPasses.h (+1)
- (modified) llvm/include/llvm/Transforms/Scalar.h (+7)
- (added) llvm/include/llvm/Transforms/Scalar/LSROA.h (+35)
- (modified) llvm/lib/Passes/PassBuilder.cpp (+1)
- (modified) llvm/lib/Passes/PassRegistry.def (+1)
- (modified) llvm/lib/Transforms/Scalar/CMakeLists.txt (+1)
- (added) llvm/lib/Transforms/Scalar/LSROA.cpp (+205)
- (added) llvm/test/Transforms/LSROA/array.ll (+24)
- (added) llvm/test/Transforms/LSROA/basictest.ll (+185)
- (added) llvm/test/Transforms/LSROA/nesting.ll (+33)
- (added) llvm/test/Transforms/LSROA/normal-gep.ll (+25)
``````````diff
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index bdec42516b8cd..05f744ad71246 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -1873,6 +1873,16 @@ class IRBuilderBase {
return Insert(new AllocaInst(Ty, AddrSpace, ArraySize, AllocaAlign), Name);
}
+ CallInst *CreateStructuredAlloca(Type *BaseType, const Twine &Name = "") {
+ const DataLayout &DL = BB->getDataLayout();
+ PointerType *PtrTy = DL.getAllocaPtrType(Context);
+ CallInst *Output =
+ CreateIntrinsic(Intrinsic::structured_alloca, {PtrTy}, {}, {}, Name);
+ Output->addRetAttr(
+ Attribute::get(getContext(), Attribute::ElementType, BaseType));
+ return Output;
+ }
+
/// Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of
/// converting the string to 'bool' for the isVolatile parameter.
LoadInst *CreateLoad(Type *Ty, Value *Ptr, const char *Name) {
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index 604118cee9e2e..f7f8fd3ecde27 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -294,6 +294,7 @@ LLVM_ABI void initializeResetMachineFunctionPass(PassRegistry &);
LLVM_ABI void initializeRuntimeLibraryInfoWrapperPass(PassRegistry &);
LLVM_ABI void initializeSCEVAAWrapperPassPass(PassRegistry &);
LLVM_ABI void initializeSROALegacyPassPass(PassRegistry &);
+LLVM_ABI void initializeLSROALegacyPassPass(PassRegistry &);
LLVM_ABI void initializeSafeStackLegacyPassPass(PassRegistry &);
LLVM_ABI void initializeSafepointIRVerifierPass(PassRegistry &);
LLVM_ABI void initializeSelectOptimizePass(PassRegistry &);
diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h
index a01a67f136710..00d56e0cb903c 100644
--- a/llvm/include/llvm/LinkAllPasses.h
+++ b/llvm/include/llvm/LinkAllPasses.h
@@ -119,6 +119,7 @@ struct ForcePassLinking {
(void)llvm::createRegionViewerPass();
(void)llvm::createSafeStackPass();
(void)llvm::createSROAPass();
+ (void)llvm::createLSROAPass();
(void)llvm::createSingleLoopExtractorPass();
(void)llvm::createTailCallEliminationPass();
(void)llvm::createConstantHoistingPass();
diff --git a/llvm/include/llvm/Transforms/Scalar.h b/llvm/include/llvm/Transforms/Scalar.h
index e2a236458dd79..9d53dba325652 100644
--- a/llvm/include/llvm/Transforms/Scalar.h
+++ b/llvm/include/llvm/Transforms/Scalar.h
@@ -46,6 +46,13 @@ LLVM_ABI FunctionPass *createDeadStoreEliminationPass();
//
LLVM_ABI FunctionPass *createSROAPass(bool PreserveCFG = true);
+//===----------------------------------------------------------------------===//
+//
+// LSROA - Replace aggregates or pieces of aggregates with scalar SSA values
+// when used when logical allocation/GEP instructions.
+//
+LLVM_ABI FunctionPass *createLSROAPass();
+
//===----------------------------------------------------------------------===//
//
// LICM - This pass is a loop invariant code motion and memory promotion pass.
diff --git a/llvm/include/llvm/Transforms/Scalar/LSROA.h b/llvm/include/llvm/Transforms/Scalar/LSROA.h
new file mode 100644
index 0000000000000..9c730a78489e0
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LSROA.h
@@ -0,0 +1,35 @@
+//===- LSROA.h - Logical Scalar Replacement Of Aggregates -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file provides the interface for LLVM's Logical Scalar Replacement of
+/// Aggregates pass. This pass provides both aggregate splitting and the
+/// primary SSA formation used in the compiler when used with structured GEP
+/// and allocas.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LSROA_H
+#define LLVM_TRANSFORMS_SCALAR_LSROA_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Function;
+
+class LSROAPass : public PassInfoMixin<LSROAPass> {
+public:
+ LSROAPass();
+
+ /// Run the pass over the function.
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LSROA_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 715f4d2c0abc4..acb88ef15d564 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -294,6 +294,7 @@
#include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LSROA.h"
#include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
#include "llvm/Transforms/Scalar/LoopBoundSplit.h"
#include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 9fe40d441e0af..97ae1f3170d9f 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -566,6 +566,7 @@ FUNCTION_PASS("view-dom-only", DomOnlyViewer())
FUNCTION_PASS("view-post-dom", PostDomViewer())
FUNCTION_PASS("view-post-dom-only", PostDomOnlyViewer())
FUNCTION_PASS("wasm-eh-prepare", WasmEHPreparePass())
+FUNCTION_PASS("lsroa", LSROAPass())
#undef FUNCTION_PASS
#ifndef FUNCTION_PASS_WITH_PARAMS
diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt
index 4f6b208c2e729..f631e60b01380 100644
--- a/llvm/lib/Transforms/Scalar/CMakeLists.txt
+++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt
@@ -69,6 +69,7 @@ add_llvm_component_library(LLVMScalarOpts
RewriteStatepointsForGC.cpp
SCCP.cpp
SROA.cpp
+ LSROA.cpp
Scalar.cpp
Scalarizer.cpp
ScalarizeMaskedMemIntrin.cpp
diff --git a/llvm/lib/Transforms/Scalar/LSROA.cpp b/llvm/lib/Transforms/Scalar/LSROA.cpp
new file mode 100644
index 0000000000000..0f85c4f4d649b
--- /dev/null
+++ b/llvm/lib/Transforms/Scalar/LSROA.cpp
@@ -0,0 +1,205 @@
+//===- LSROA.cpp - Logical Scalar Replacement Of Aggregates ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This transformation implements the well known scalar replacement of
+/// aggregates transformation. It tries to identify promotable elements of an
+/// aggregate alloca, and promote them to registers. It will also try to
+/// convert uses of an element (or set of elements) of an alloca into a vector
+/// or bitfield-style integer scalar if appropriate.
+///
+/// It works to do this with minimal slicing of the alloca so that regions
+/// which are merely transferred in and out of external memory remain unchanged
+/// and are not decomposed to scalar code.
+///
+/// Because this also performs alloca promotion, it can be thought of as also
+/// serving the purpose of SSA formation. The algorithm iterates on the
+/// function until all opportunities for promotion have been realized.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar/LSROA.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/DomTreeUpdater.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Scalar.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "lsroa"
+
+namespace {
+
+class LSROA {
+public:
+ LSROA() {}
+
+ bool runLSROA(Function &F);
+
+private:
+ bool runOnStructuredAlloca(StructuredAllocaInst &SAI);
+};
+
+} // end anonymous namespace
+
+bool LSROA::runOnStructuredAlloca(StructuredAllocaInst &SAI) {
+ // For now, LSROA only handles SGEP on structs.
+ StructType *ST = dyn_cast<StructType>(SAI.getAllocationType());
+ if (!ST)
+ return false;
+
+ SmallVector<SmallVector<StructuredGEPInst *, 4>, 4> FieldUsers(
+ ST->getNumElements());
+ for (const auto &user : SAI.users()) {
+ // Lifetime intrinsincs are handled differently.
+ auto II = dyn_cast<IntrinsicInst>(user);
+ if (II && II->isLifetimeStartOrEnd())
+ continue;
+
+ auto SGEP = dyn_cast<StructuredGEPInst>(user);
+ // If any user is not an SGEP, we bail out.
+ if (!SGEP) {
+ return false;
+ }
+
+ // If the SGEP has no indices, this means we have a pointer on the whole
+ // struct. For now, we bail out: if it was not used, it would be DCE'd, so
+ // there is probably a reference to the whole struct somewhere.
+ if (SGEP->getNumIndices() == 0)
+ return false;
+
+ // IR rule: SGEP on struct can only use constant int as indices.
+ ConstantInt *Index = cast<ConstantInt>(SGEP->getIndexOperand(0));
+ assert(Index->getZExtValue() < FieldUsers.size());
+ FieldUsers[Index->getZExtValue()].push_back(SGEP);
+ }
+
+ bool Changed = false;
+ SmallPtrSet<Instruction *, 4> DeadLifetimeInstrs;
+ IRBuilder B(&SAI);
+ for (size_t I = 0; I < FieldUsers.size(); ++I) {
+ if (FieldUsers[I].size() == 0)
+ continue;
+ Changed = true;
+
+ B.SetInsertPoint(&SAI);
+ StructuredAllocaInst *NSAI = cast<StructuredAllocaInst>(
+ B.CreateStructuredAlloca(ST->getElementType(I)));
+
+ // Step 1: for each lifetime intrinsic, generate one per newly created NSAI.
+ for (const auto &user : SAI.users()) {
+ auto II = dyn_cast<IntrinsicInst>(user);
+ if (II && II->getIntrinsicID() == Intrinsic::lifetime_start) {
+ B.SetInsertPoint(II);
+ B.CreateLifetimeStart(NSAI);
+ DeadLifetimeInstrs.insert(II);
+ continue;
+ }
+
+ if (II && II->getIntrinsicID() == Intrinsic::lifetime_end) {
+ B.SetInsertPoint(II);
+ B.CreateLifetimeEnd(NSAI);
+ DeadLifetimeInstrs.insert(II);
+ continue;
+ }
+ }
+
+ // Step 2: replace each SGEP usage with the new alloca
+ for (StructuredGEPInst *SGEP : FieldUsers[I]) {
+ if (SGEP->getNumIndices() == 1) {
+ SGEP->replaceAllUsesWith(NSAI);
+ SGEP->eraseFromParent();
+ continue;
+ }
+
+ SmallVector<Value *, 4> Indices;
+ for (unsigned J = 1; J < SGEP->getNumIndices(); ++J)
+ Indices.push_back(SGEP->getIndexOperand(J));
+
+ B.SetInsertPoint(SGEP);
+ StructuredGEPInst *NSGEP = cast<StructuredGEPInst>(B.CreateStructuredGEP(
+ ST->getElementType(I), NSAI, Indices, SGEP->getName()));
+ SGEP->replaceAllUsesWith(NSGEP);
+ SGEP->eraseFromParent();
+ }
+ }
+
+ for (Instruction *I : DeadLifetimeInstrs)
+ I->eraseFromParent();
+ SAI.eraseFromParent();
+
+ return Changed;
+}
+
+bool LSROA::runLSROA(Function &F) {
+ BasicBlock &EntryBB = F.getEntryBlock();
+ SmallVector<StructuredAllocaInst *> Worklist;
+
+ for (BasicBlock::iterator I = EntryBB.begin(), E = std::prev(EntryBB.end());
+ I != E; ++I) {
+ if (StructuredAllocaInst *SAI = dyn_cast<StructuredAllocaInst>(I))
+ Worklist.push_back(SAI);
+ }
+
+ bool Changed = false;
+ for (StructuredAllocaInst *SAI : Worklist)
+ Changed |= runOnStructuredAlloca(*SAI);
+ return Changed;
+}
+
+PreservedAnalyses LSROAPass::run(Function &F, FunctionAnalysisManager &AM) {
+ if (!LSROA().runLSROA(F))
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<CFGAnalyses>();
+ PA.preserve<DominatorTreeAnalysis>();
+ return PA;
+}
+
+LSROAPass::LSROAPass() {}
+
+namespace {
+
+/// A legacy pass for the legacy pass manager that wraps the LSROA pass.
+class LSROALegacyPass : public FunctionPass {
+public:
+ static char ID;
+
+ LSROALegacyPass() : FunctionPass(ID) {
+ initializeLSROALegacyPassPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnFunction(Function &F) override {
+ if (skipFunction(F))
+ return false;
+ return LSROA().runLSROA(F);
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addPreserved<DominatorTreeWrapperPass>();
+ }
+
+ StringRef getPassName() const override { return "LSROA"; }
+};
+
+} // end anonymous namespace
+
+char LSROALegacyPass::ID = 0;
+
+FunctionPass *llvm::createLSROAPass() { return new LSROALegacyPass(); }
+
+INITIALIZE_PASS_BEGIN(LSROALegacyPass, "lsroa",
+ "Logical Scalar Replacement Of Aggregates", false, false)
+INITIALIZE_PASS_END(LSROALegacyPass, "lsroa",
+ "Logical Scalar Replacement Of Aggregates", false, false)
diff --git a/llvm/test/Transforms/LSROA/array.ll b/llvm/test/Transforms/LSROA/array.ll
new file mode 100644
index 0000000000000..036d1d15092b6
--- /dev/null
+++ b/llvm/test/Transforms/LSROA/array.ll
@@ -0,0 +1,24 @@
+; RUN: opt < %s -passes='lsroa' -S -debug | FileCheck %s --check-prefixes=CHECK
+
+declare void @llvm.lifetime.start.p0(ptr nocapture)
+declare void @llvm.lifetime.end.p0(ptr nocapture)
+declare ptr @llvm.structured.alloca.p0()
+declare ptr @llvm.structured.gep.p0(ptr, ...)
+
+define i32 @test_simple_array() {
+; CHECK-LABEL: @test_simple_array(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype([10 x i32]) ptr @llvm.structured.alloca.p0()
+ %ptr = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %tmp, i32 0)
+ store i32 0, ptr %ptr
+ %res = load i32, ptr %ptr
+ ret i32 %res
+
+; CHECK-NEXT: %tmp = call elementtype([10 x i32]) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: %ptr = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([10 x i32]) %tmp, i32 0)
+; CHECK-NEXT: store i32 0, ptr %ptr
+; CHECK-NEXT: %res = load i32, ptr %ptr
+; CHECK-NEXT: ret i32 %res
+}
+
diff --git a/llvm/test/Transforms/LSROA/basictest.ll b/llvm/test/Transforms/LSROA/basictest.ll
new file mode 100644
index 0000000000000..a3fa797af9938
--- /dev/null
+++ b/llvm/test/Transforms/LSROA/basictest.ll
@@ -0,0 +1,185 @@
+; RUN: opt < %s -passes='lsroa' -S -debug | FileCheck %s --check-prefixes=CHECK
+
+declare void @llvm.lifetime.start.p0(ptr nocapture)
+declare void @llvm.lifetime.end.p0(ptr nocapture)
+declare ptr @llvm.structured.alloca.p0()
+declare ptr @llvm.structured.gep.p0(ptr, ...)
+
+define i32 @test_simple_scalar() {
+; CHECK-LABEL: @test_simple_scalar(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %tmp
+ %res = load i32, ptr %tmp
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+ ret i32 %res
+; CHECK: %tmp = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+; CHECK: call void @llvm.lifetime.start.p0(ptr %tmp)
+; CHECK: store i32 0, ptr %tmp
+; CHECK: %res = load i32, ptr %tmp
+; CHECK: call void @llvm.lifetime.end.p0(ptr %tmp)
+}
+
+define i32 @test_simple_struct_entire_write_read() {
+; CHECK-LABEL: @test_simple_struct_entire_write_read(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype({ i32, i32 }) ptr @llvm.structured.alloca.p0()
+ %ptr0 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+ %ptr1 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 1)
+
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %ptr0
+ store i32 1, ptr %ptr1
+ %a = load i32, ptr %ptr0
+ %b = load i32, ptr %ptr1
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+
+ %res = add i32 %a, %b
+ ret i32 %res
+
+; CHECK-NEXT: %[[#a:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: %[[#b:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#b]])
+; CHECK-NEXT: store i32 0, ptr %[[#a]]
+; CHECK-NEXT: store i32 1, ptr %[[#b]]
+; CHECK-NEXT: %a = load i32, ptr %[[#a]]
+; CHECK-NEXT: %b = load i32, ptr %[[#b]]
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#b]])
+}
+
+define i32 @test_simple_struct_aliasing() {
+; CHECK-LABEL: @test_simple_struct_aliasing(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype({ i32, i32 }) ptr @llvm.structured.alloca.p0()
+ %ptr0 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+ %ptr1 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %ptr0
+ store i32 1, ptr %ptr1
+ %a = load i32, ptr %ptr0
+ %b = load i32, ptr %ptr1
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+
+ %res = add i32 %a, %b
+ ret i32 %res
+
+; CHECK-NEXT: %[[#a:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#a]])
+; CHECK-NEXT: store i32 0, ptr %[[#a]]
+; CHECK-NEXT: store i32 1, ptr %[[#a]]
+; CHECK-NEXT: %a = load i32, ptr %[[#a]]
+; CHECK-NEXT: %b = load i32, ptr %[[#a]]
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#a]])
+}
+
+define i32 @test_simple_struct_partial_write_read() {
+; CHECK-LABEL: @test_simple_struct_partial_write_read(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype({ i32, i32 }) ptr @llvm.structured.alloca.p0()
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ %ptr0 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+ store i32 0, ptr %ptr0
+ %a = load i32, ptr %ptr0
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+
+; CHECK-NEXT: %[[#a:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#a]])
+; CHECK-NEXT: store i32 0, ptr %[[#a]]
+; CHECK-NEXT: %a = load i32, ptr %[[#a]]
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#a]])
+
+ ret i32 %a
+}
+
+define i32 @test_struct_use_across_lifetime() {
+; CHECK-LABEL: @test_struct_use_across_lifetime(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype({ i32, i32 }) ptr @llvm.structured.alloca.p0()
+ %ptr0 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+ %ptr1 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 1)
+; CHECK-NEXT: %[[#a:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: %[[#b:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %ptr0
+ %a = load i32, ptr %ptr0
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#b]])
+; CHECK-NEXT: store i32 0, ptr %[[#a]]
+; CHECK-NEXT: %a = load i32, ptr %[[#a]]
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#b]])
+
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %ptr1
+ %b = load i32, ptr %ptr1
+ call void @llvm.lifetime.end.p0(ptr %tmp)
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[#b]])
+; CHECK-NEXT: store i32 0, ptr %[[#b]]
+; CHECK-NEXT: %b = load i32, ptr %[[#b]]
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#a]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[#b]])
+
+ %c = add i32 %b, %a
+ ret i32 %c
+}
+
+define i32 @test_partial_use_phi_node(i1 %cond) {
+; CHECK-LABEL: @test_partial_use_phi_node(
+; CHECK-NEXT: entry:
+entry:
+ %tmp = call elementtype({ i32, i32 }) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: %[[#a:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+; CHECK-NEXT: %[[#b:]] = call elementtype(i32) ptr @llvm.structured.alloca.p0()
+ %ptr0 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 0)
+ %ptr1 = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype({ i32, i32 }) %tmp, i32 1)
+ br i1 %cond, label %l1, label %l2
+
+l1:
+ call void @llvm.lifetime.start.p0(ptr %tmp)
+ store i32 0, ptr %ptr0
+ br label %l3
+; CHECK: l1: ; preds = %entry
+; CHECK-NEXT: ca...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/192058
More information about the llvm-commits
mailing list