diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 2f8a80a8999265..518e909e8ab400 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3766,6 +3766,55 @@ bool InstCombinerImpl::run() { return MadeIRChange; } +// Track the scopes used by !alias.scope and !noalias. In a function, a +// @llvm.experimental.noalias.scope.decl is only useful if that scope is used +// by both sets. If not, the declaration of the scope can be safely omitted. +// The MDNode of the scope can be omitted as well for the instructions that are +// part of this function. We do not do that at this point, as this might become +// too time consuming to do. +class AliasScopeTracker { + SmallPtrSet UsedAliasScopesAndLists; + SmallPtrSet UsedNoAliasScopesAndLists; + +public: + void analyse(Instruction *I) { + // This seems to be faster than checking 'mayReadOrWriteMemory()'. + if (!I->hasMetadataOtherThanDebugLoc()) + return; + + auto Track = [](Metadata *ScopeList, auto &Container) { + const auto *MDScopeList = dyn_cast_or_null(ScopeList); + if (!MDScopeList || !Container.insert(MDScopeList).second) + return; + for (auto &MDOperand : MDScopeList->operands()) + if (auto *MDScope = dyn_cast(MDOperand)) + Container.insert(MDScope); + }; + + Track(I->getMetadata(LLVMContext::MD_alias_scope), UsedAliasScopesAndLists); + Track(I->getMetadata(LLVMContext::MD_noalias), UsedNoAliasScopesAndLists); + } + + bool isNoAliasScopeDeclDead(Instruction *Inst) { + NoAliasScopeDeclInst *Decl = dyn_cast(Inst); + if (!Decl) + return false; + + assert(Decl->use_empty() && + "llvm.experimental.noalias.scope.decl in use ?"); + const MDNode *MDSL = Decl->getScopeList(); + assert(MDSL->getNumOperands() == 1 && + "llvm.experimental.noalias.scope should refer to a single scope"); + auto &MDOperand = MDSL->getOperand(0); + if (auto *MD = dyn_cast(MDOperand)) + return !UsedAliasScopesAndLists.contains(MD) || + !UsedNoAliasScopesAndLists.contains(MD); + + // Not an MDNode ? throw away. + return true; + } +}; + /// Populate the IC worklist from a function, by walking it in depth-first /// order and adding all reachable code to the worklist. /// @@ -3784,6 +3833,7 @@ static bool prepareICWorklistFromFunction(Function &F, const DataLayout &DL, SmallVector InstrsForInstCombineWorklist; DenseMap FoldedConstants; + AliasScopeTracker SeenAliasScopes; do { BasicBlock *BB = Worklist.pop_back_val(); @@ -3830,8 +3880,10 @@ static bool prepareICWorklistFromFunction(Function &F, const DataLayout &DL, // Skip processing debug intrinsics in InstCombine. Processing these call instructions // consumes non-trivial amount of time and provides no value for the optimization. - if (!isa(Inst)) + if (!isa(Inst)) { InstrsForInstCombineWorklist.push_back(Inst); + SeenAliasScopes.analyse(Inst); + } } // Recursively visit successors. If this is a branch or switch on a @@ -3879,7 +3931,8 @@ static bool prepareICWorklistFromFunction(Function &F, const DataLayout &DL, for (Instruction *Inst : reverse(InstrsForInstCombineWorklist)) { // DCE instruction if trivially dead. As we iterate in reverse program // order here, we will clean up whole chains of dead instructions. - if (isInstructionTriviallyDead(Inst, TLI)) { + if (isInstructionTriviallyDead(Inst, TLI) || + SeenAliasScopes.isNoAliasScopeDeclDead(Inst)) { ++NumDeadInst; LLVM_DEBUG(dbgs() << "IC: DCE: " << *Inst << '\n'); salvageDebugInfo(*Inst); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll index 993fc5d01aec0e..c76ad50a88b9d7 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll @@ -67,10 +67,7 @@ entry: ; CHECK-NEXT: entry: ; CHECK: [[BUFFER:%.*]] = alloca [8 x i8], align 4 ; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: store i32 7, i32* [[SLOT]], align 4 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 7) ; CHECK-NEXT: ret i32 0 diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll index 42201a239f8757..2c66b6af99c698 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll @@ -79,17 +79,14 @@ entry: ; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* ; CHECK-NEXT: store i32 4, i32* [[SLOT]], align 4 ; CHECK-NEXT: call void @print(i32 4) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 ; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 ; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 ; CHECK-NEXT: call void @print(i32 [[INC]]) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 ; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 ; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 ; CHECK-NEXT: call void @print(i32 [[INC]]) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: ret i32 0 declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll index 2534af8494eab7..48e0b644eb8d4d 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -78,7 +78,6 @@ entry: ; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 ; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 ; CHECK-NEXT: call void @print(i32 [[INC]]) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: ret i32 0 define hidden { i8*, i8* } @g(i8* %buffer, i16* %ptr) { diff --git a/llvm/test/Transforms/Coroutines/ex2.ll b/llvm/test/Transforms/Coroutines/ex2.ll index 820f53814661e5..584bc909a4eb7c 100644 --- a/llvm/test/Transforms/Coroutines/ex2.ll +++ b/llvm/test/Transforms/Coroutines/ex2.ll @@ -49,9 +49,7 @@ return: ret i32 0 ; CHECK-NOT: call i8* @CustomAlloc ; CHECK: call void @print(i32 4) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 5) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 6) ; CHECK-NEXT: ret i32 0 } diff --git a/llvm/test/Transforms/Coroutines/ex3.ll b/llvm/test/Transforms/Coroutines/ex3.ll index c75c7a1e09b121..85cf53fb576de7 100644 --- a/llvm/test/Transforms/Coroutines/ex3.ll +++ b/llvm/test/Transforms/Coroutines/ex3.ll @@ -53,9 +53,7 @@ return: ret i32 0 ; CHECK-NOT: i8* @malloc ; CHECK: call void @print(i32 4) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 -5) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 5) ; CHECK: ret i32 0 } diff --git a/llvm/test/Transforms/Coroutines/ex4.ll b/llvm/test/Transforms/Coroutines/ex4.ll index 1a26107ecaf3b6..e60bc2c691fa27 100644 --- a/llvm/test/Transforms/Coroutines/ex4.ll +++ b/llvm/test/Transforms/Coroutines/ex4.ll @@ -50,9 +50,7 @@ entry: call void @llvm.coro.destroy(i8* %hdl) ret i32 0 ; CHECK: call void @print(i32 4) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 5) -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl ; CHECK-NEXT: call void @print(i32 6) ; CHECK: ret i32 0 } diff --git a/llvm/test/Transforms/InstCombine/noalias-scope-decl.ll b/llvm/test/Transforms/InstCombine/noalias-scope-decl.ll new file mode 100644 index 00000000000000..1251579e596cd9 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/noalias-scope-decl.ll @@ -0,0 +1,170 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -instcombine -S < %s | FileCheck %s + +define void @test01(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test01( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0 + store i8 43, i8* %ptr1 + ret void +} + +define void @test02_keep(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test02_keep( +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !0 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !3 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !0 + store i8 43, i8* %ptr1, !noalias !5 + ret void +} + +define void @test03(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test03( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !4 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !3 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !4 + store i8 43, i8* %ptr1, !noalias !5 + ret void +} + +define void @test04_keep(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test04_keep( +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !3 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !3 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !5 + store i8 43, i8* %ptr1, !noalias !5 + ret void +} + +define void @test05_keep(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test05_keep( +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !3 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !0 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !5 + store i8 43, i8* %ptr1, !noalias !0 + ret void +} + +define void @test06(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test06( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !3 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !4 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !5 + store i8 43, i8* %ptr1, !noalias !4 + ret void +} + +define void @test07(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test07( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !0 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !4 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !0 + store i8 43, i8* %ptr1, !noalias !4 + ret void +} + +define void @test08(i8* %ptr0, i8* %ptr1) { +; CHECK-LABEL: @test08( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !4 +; CHECK-NEXT: store i8 43, i8* [[PTR1:%.*]], align 1, !noalias !0 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !4 + store i8 43, i8* %ptr1, !noalias !0 + ret void +} + +define void @test11(i8* %ptr0) { +; CHECK-LABEL: @test11( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !0 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !0 + ret void +} + +define void @test12(i8* %ptr0) { +; CHECK-LABEL: @test12( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !4 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !4 + ret void +} + +define void @test13(i8* %ptr0) { +; CHECK-LABEL: @test13( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !alias.scope !3 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !alias.scope !5 + ret void +} + +define void @test14(i8* %ptr0) { +; CHECK-LABEL: @test14( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !noalias !0 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !noalias !0 + ret void +} + +define void @test15(i8* %ptr0) { +; CHECK-LABEL: @test15( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !noalias !4 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !noalias !4 + ret void +} + +define void @test16(i8* %ptr0) { +; CHECK-LABEL: @test16( +; CHECK-NEXT: store i8 42, i8* [[PTR0:%.*]], align 1, !noalias !3 +; CHECK-NEXT: ret void +; + call void @llvm.experimental.noalias.scope.decl(metadata !0) + store i8 42, i8* %ptr0, !noalias !5 + ret void +} + +declare void @llvm.experimental.noalias.scope.decl(metadata) + +!0 = !{ !1 } +!1 = distinct !{ !1, !2 } +!2 = distinct !{ !2 } +!3 = !{ !4 } +!4 = distinct !{ !4, !2 } +!5 = !{ !1, !4 } diff --git a/llvm/test/Transforms/PhaseOrdering/inlining-alignment-assumptions.ll b/llvm/test/Transforms/PhaseOrdering/inlining-alignment-assumptions.ll index 9e96ba081bac87..4fe75883959e3c 100644 --- a/llvm/test/Transforms/PhaseOrdering/inlining-alignment-assumptions.ll +++ b/llvm/test/Transforms/PhaseOrdering/inlining-alignment-assumptions.ll @@ -95,7 +95,6 @@ define internal void @callee2(i64* noalias sret(i64) align 32 %arg) { define amdgpu_kernel void @caller2() { ; CHECK-LABEL: @caller2( -; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl([[META0:metadata !.*]]) ; CHECK-NEXT: ret void ; %alloca = alloca i64, align 8, addrspace(5) diff --git a/llvm/test/Transforms/PhaseOrdering/instcombine-sroa-inttoptr.ll b/llvm/test/Transforms/PhaseOrdering/instcombine-sroa-inttoptr.ll index 2ce1c49e04c4b2..9052fd5a03e4ea 100644 --- a/llvm/test/Transforms/PhaseOrdering/instcombine-sroa-inttoptr.ll +++ b/llvm/test/Transforms/PhaseOrdering/instcombine-sroa-inttoptr.ll @@ -70,7 +70,6 @@ define dso_local i32* @_Z3foo1S(%0* byval(%0) align 8 %arg) { ; CHECK-NEXT: [[I2:%.*]] = alloca [[TMP0:%.*]], align 8 ; CHECK-NEXT: [[I1_SROA_0_0_I5_SROA_IDX:%.*]] = getelementptr inbounds [[TMP0]], %0* [[ARG:%.*]], i64 0, i32 0 ; CHECK-NEXT: [[I1_SROA_0_0_COPYLOAD:%.*]] = load i32*, i32** [[I1_SROA_0_0_I5_SROA_IDX]], align 8 -; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl([[META0:metadata !.*]]) ; CHECK-NEXT: [[I_SROA_0_0_I6_SROA_IDX:%.*]] = getelementptr inbounds [[TMP0]], %0* [[I2]], i64 0, i32 0 ; CHECK-NEXT: store i32* [[I1_SROA_0_0_COPYLOAD]], i32** [[I_SROA_0_0_I6_SROA_IDX]], align 8 ; CHECK-NEXT: tail call void @_Z7escape01S(%0* nonnull byval(%0) align 8 [[I2]]) @@ -110,7 +109,6 @@ define dso_local i32* @_Z3bar1S(%0* byval(%0) align 8 %arg) { ; CHECK-NEXT: bb: ; CHECK-NEXT: [[I1_SROA_0_0_I4_SROA_IDX:%.*]] = getelementptr inbounds [[TMP0:%.*]], %0* [[ARG:%.*]], i64 0, i32 0 ; CHECK-NEXT: [[I1_SROA_0_0_COPYLOAD:%.*]] = load i32*, i32** [[I1_SROA_0_0_I4_SROA_IDX]], align 8 -; CHECK-NEXT: tail call void @llvm.experimental.noalias.scope.decl([[META3:metadata !.*]]) ; CHECK-NEXT: [[I5:%.*]] = tail call i32 @_Z4condv() ; CHECK-NEXT: [[I6_NOT:%.*]] = icmp eq i32 [[I5]], 0 ; CHECK-NEXT: br i1 [[I6_NOT]], label [[BB10:%.*]], label [[BB7:%.*]]