diff --git a/llvm/include/llvm/Analysis/SeparateStorageAliasAnalysis.h b/llvm/include/llvm/Analysis/SeparateStorageAliasAnalysis.h --- a/llvm/include/llvm/Analysis/SeparateStorageAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/SeparateStorageAliasAnalysis.h @@ -16,17 +16,54 @@ #define LLVM_ANALYSIS_SEPARATESTORAGEALIASANALYSIS #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/ValueHandle.h" #include "llvm/Pass.h" namespace llvm { /// A simple AA result that uses calls to the separate storage intrinsics. class SeparateStorageAAResult : public AAResultBase { public: + SeparateStorageAAResult(Function &F); + + AliasResult aliasAt(const MemoryLocation &LocA, const MemoryLocation &LocB, + const Instruction *I, AAQueryInfo &AAQI); + bool invalidate(Function &, const PreservedAnalyses &, FunctionAnalysisManager::Invalidator &) { + // The AA framework is otherwise stateless, and AA-dependent passes have + // gotten a little bit sloppy in their consideration of whether or not + // they're effective without alias analysis (and so rely on getCachedResult + // instead of getResult sort of haphazardly). This is extra bad in our case, + // since if we get invalidate it also invalidates the AAResults we're + // associated with, so we drop the stateless analyses too. + // + // For now, we just recompute on demand. We do this lazily, in the hope + // that maybe we can avoid it entirely. + // + // There's some ways we can make this less wasteful: + // - Generalize the AssumptionCache machinery work for separate-storage + // assumptions too. + // - Make the PassManager know about "partially invalidated" results, which + // then get recomputed the next time an analysis is requested via + // getResult rather than getCachedResult. + // - Do a (one-time) module scan for separate-storage assumptions. If none + // are present, we can skip any function-level scanning. + CachedState.reset(); return false; } + +private: + struct State { + DominatorTree DT; + SmallVector Hints; + }; + + Function &F; + Optional CachedState; + + State &getState(); }; /// Analysis pass providing a never-invalidated alias analysis result. diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -643,6 +643,7 @@ break; case Intrinsic::annotation: case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::sideeffect: case Intrinsic::pseudoprobe: case Intrinsic::arithmetic_fence: @@ -1124,7 +1125,7 @@ // destination type of the trunc instruction rather than the load to // accurately estimate the cost of this load instruction. if (CostKind == TTI::TCK_CodeSize && LI->hasOneUse() && - !LoadType->isVectorTy()) { + !LoadType->isVectorTy()) { if (const TruncInst *TI = dyn_cast(*LI->user_begin())) LoadType = TI->getDestTy(); } diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -78,6 +78,7 @@ case Intrinsic::umul_fix_sat: case Intrinsic::fma: case Intrinsic::fmuladd: + case Intrinsic::experimental_separate_storage: // facebook T130678741 return true; default: return false; @@ -89,6 +90,7 @@ switch (getIntrinsicID()) { default: break; case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::sideeffect: case Intrinsic::pseudoprobe: case Intrinsic::dbg_declare: @@ -1437,6 +1439,19 @@ } }; +// facebook begin T130678741 +/// This represents the llvm.experimental.separate.storage intrinsic. +class SeparateStorageInst : public IntrinsicInst { +public: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::experimental_separate_storage; + } + static bool classof(const Value *V) { + return isa(V) && classof(cast(V)); + } +}; +// facebook end T130678741 + } // end namespace llvm #endif // LLVM_IR_INTRINSICINST_H diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -579,6 +579,13 @@ def int_assume : DefaultAttrsIntrinsic< [], [llvm_i1_ty], [IntrInaccessibleMemOnly, NoUndef>]>; +// facebook begin T130678741 +// No pointer derived from the first argument will alias any pointer derived +// from the second. +def int_experimental_separate_storage : DefaultAttrsIntrinsic< + [], [llvm_ptr_ty, llvm_ptr_ty], [IntrInaccessibleMemOnly]>; +// facebook end T130678741 + // 'llvm.experimental.noalias.scope.decl' intrinsic: Inserted at the location of // noalias scope declaration. Makes it possible to identify that a noalias scope // is only valid inside the body of a loop. diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp --- a/llvm/lib/Analysis/MemorySSA.cpp +++ b/llvm/lib/Analysis/MemorySSA.cpp @@ -302,6 +302,7 @@ case Intrinsic::invariant_start: case Intrinsic::invariant_end: case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::experimental_noalias_scope_decl: case Intrinsic::pseudoprobe: return false; @@ -1742,6 +1743,7 @@ default: break; case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::experimental_noalias_scope_decl: case Intrinsic::pseudoprobe: return nullptr; diff --git a/llvm/lib/Analysis/SeparateStorageAliasAnalysis.cpp b/llvm/lib/Analysis/SeparateStorageAliasAnalysis.cpp --- a/llvm/lib/Analysis/SeparateStorageAliasAnalysis.cpp +++ b/llvm/lib/Analysis/SeparateStorageAliasAnalysis.cpp @@ -12,17 +12,70 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/SeparateStorageAliasAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/InitializePasses.h" using namespace llvm; +static cl::opt EnableSeparateStorageAA("enable-separate-storage-aa", + cl::Hidden, cl::init(false)); + +SeparateStorageAAResult::SeparateStorageAAResult(Function &F) : F(F) {} + +SeparateStorageAAResult::State &SeparateStorageAAResult::getState() { + if (!CachedState) { + CachedState.emplace(); + CachedState.value().DT.recalculate(F); + for (BasicBlock &B : F) + for (Instruction &I : B) + if (SeparateStorageInst *SSI = dyn_cast(&I)) + CachedState.value().Hints.push_back(SSI); + } + return *CachedState; +} + +AliasResult SeparateStorageAAResult::aliasAt(const MemoryLocation &LocA, + const MemoryLocation &LocB, + const Instruction *I, + AAQueryInfo &AAQI) { + if (!EnableSeparateStorageAA) { + return AAResultBase::aliasAt(LocA, LocB, I, AAQI); + } + + const Value *PtrA = LocA.Ptr; + const Value *PtrB = LocB.Ptr; + if (!PtrA || !PtrB) + return AAResultBase::aliasAt(LocA, LocB, I, AAQI); + + auto &State = getState(); + const Value *UnderlyingA = getUnderlyingObject(PtrA); + const Value *UnderlyingB = getUnderlyingObject(PtrB); + + for (WeakVH &VH : State.Hints) { + if (!VH) + continue; + auto *SSI = cast(VH); + const Value *Hint0 = SSI->getOperand(0); + const Value *Hint1 = SSI->getOperand(1); + const Value *UnderlyingHint0 = getUnderlyingObject(Hint0); + const Value *UnderlyingHint1 = getUnderlyingObject(Hint1); + + if (((UnderlyingA == UnderlyingHint0 && UnderlyingB == UnderlyingHint1) || + (UnderlyingA == UnderlyingHint1 && UnderlyingB == UnderlyingHint0)) && + isValidAssumeForContext(SSI, I, &State.DT)) + return AliasResult::NoAlias; + } + return AAResultBase::aliasAt(LocA, LocB, I, AAQI); +} + AnalysisKey SeparateStorageAA::Key; SeparateStorageAAResult SeparateStorageAA::run(Function &F, FunctionAnalysisManager &AM) { - return SeparateStorageAAResult(); + return SeparateStorageAAResult(F); } - char SeparateStorageAAWrapperPass::ID = 0; INITIALIZE_PASS(SeparateStorageAAWrapperPass, "separatestorage-aa", "Separate Storage Alias Analysis", false, true) @@ -37,7 +90,7 @@ } bool SeparateStorageAAWrapperPass::runOnFunction(Function &F) { - Result.reset(new SeparateStorageAAResult()); + Result.reset(new SeparateStorageAAResult(F)); return false; } diff --git a/llvm/lib/CodeGen/Analysis.cpp b/llvm/lib/CodeGen/Analysis.cpp --- a/llvm/lib/CodeGen/Analysis.cpp +++ b/llvm/lib/CodeGen/Analysis.cpp @@ -553,6 +553,9 @@ if (const IntrinsicInst *II = dyn_cast(BBI)) if (II->getIntrinsicID() == Intrinsic::lifetime_end || II->getIntrinsicID() == Intrinsic::assume || + // facebook begin T130678741 + II->getIntrinsicID() == Intrinsic::experimental_separate_storage || + // facebook end T130678741 II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl) continue; if (BBI->mayHaveSideEffects() || BBI->mayReadFromMemory() || diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -713,7 +713,19 @@ resetIteratorIfInvalidatedWhileCalling(&BB, [&]() { RecursivelyDeleteTriviallyDeadInstructions(Operand, TLInfo, nullptr); }); + // facebook begin T130678741 + } else if (auto *SSI = dyn_cast(I)) { + MadeChange = true; + Value *Operand1 = SSI->getOperand(0); + Value *Operand2 = SSI->getOperand(1); + SSI->eraseFromParent(); + + resetIteratorIfInvalidatedWhileCalling(&BB, [&]() { + RecursivelyDeleteTriviallyDeadInstructions(Operand1, TLInfo, nullptr); + RecursivelyDeleteTriviallyDeadInstructions(Operand2, TLInfo, nullptr); + }); } + // facebook end T130678741 } } return MadeChange; @@ -2288,6 +2300,11 @@ break; case Intrinsic::assume: llvm_unreachable("llvm.assume should have been removed already"); + // facebook begin T130678741 + case Intrinsic::experimental_separate_storage: + llvm_unreachable("llvm.experimental.separate.storage should have been " + "removed already"); + // facebook end T130678741 case Intrinsic::experimental_widenable_condition: { // Give up on future widening oppurtunties so that we can fold away dead // paths and merge blocks before going into block-local instruction diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2200,6 +2200,7 @@ return true; } case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::experimental_noalias_scope_decl: case Intrinsic::var_annotation: case Intrinsic::sideeffect: diff --git a/llvm/lib/CodeGen/IntrinsicLowering.cpp b/llvm/lib/CodeGen/IntrinsicLowering.cpp --- a/llvm/lib/CodeGen/IntrinsicLowering.cpp +++ b/llvm/lib/CodeGen/IntrinsicLowering.cpp @@ -329,6 +329,7 @@ break; case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::experimental_noalias_scope_decl: case Intrinsic::var_annotation: break; // Strip out these intrinsics diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1202,6 +1202,10 @@ case Intrinsic::sideeffect: // Neither does the assume intrinsic; it's also OK not to codegen its operand. case Intrinsic::assume: + // facebook begin T130678741 + // Ditto + case Intrinsic::experimental_separate_storage: + // facebook end T130678741 // Neither does the llvm.experimental.noalias.scope.decl intrinsic case Intrinsic::experimental_noalias_scope_decl: return true; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6739,6 +6739,7 @@ return; case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::experimental_noalias_scope_decl: case Intrinsic::var_annotation: case Intrinsic::sideeffect: diff --git a/llvm/lib/IR/User.cpp b/llvm/lib/IR/User.cpp --- a/llvm/lib/IR/User.cpp +++ b/llvm/lib/IR/User.cpp @@ -113,7 +113,10 @@ } bool User::isDroppable() const { - return isa(this) || isa(this); + // facebook begin T130678741 + return isa(this) || isa(this) || + isa(this); + // facebook end T130678741 } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -224,10 +224,13 @@ CallInst::BundleOpInfo &BOI = Assume->getBundleOpInfoForOperand(OpNo); BOI.Tag = Assume->getContext().pImpl->getOrInsertBundleTag("ignore"); } - return; + // facebook begin T130678741 + } else if (auto *SSI = dyn_cast(U.getUser())) { + U.set(Constant::getNullValue(U->getType())); + } else { + llvm_unreachable("unkown droppable use"); } - - llvm_unreachable("unkown droppable use"); + // facebook end T130678741 } bool Value::isUsedInBasicBlock(const BasicBlock *BB) const { @@ -818,7 +821,7 @@ // which is why we need the explicit opt in on a per collector basis. if (!F->hasGC()) return true; - + const auto &GCName = F->getGC(); if (GCName == "statepoint-example") { auto *PT = cast(this->getType()); diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2781,6 +2781,7 @@ [[fallthrough]]; } case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 case Intrinsic::invariant_start: case Intrinsic::invariant_end: case Intrinsic::lifetime_start: diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -694,6 +694,7 @@ case Intrinsic::invariant_end: case Intrinsic::launder_invariant_group: case Intrinsic::assume: + case Intrinsic::experimental_separate_storage: // facebook T130678741 return true; case Intrinsic::dbg_addr: case Intrinsic::dbg_declare: diff --git a/llvm/lib/Transforms/Scalar/GVNHoist.cpp b/llvm/lib/Transforms/Scalar/GVNHoist.cpp --- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -1205,6 +1205,10 @@ if (auto *Intr = dyn_cast(Call)) { if (isa(Intr) || Intr->getIntrinsicID() == Intrinsic::assume || + // facebook begin T130678741 + Intr->getIntrinsicID() == + Intrinsic::experimental_separate_storage || + // facebook end T130678741 Intr->getIntrinsicID() == Intrinsic::sideeffect) continue; } diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -3090,7 +3090,12 @@ Pass.DeadInsts.push_back(&II); if (II.isDroppable()) { - assert(II.getIntrinsicID() == Intrinsic::assume && "Expected assume"); + // facebook begin T130678741 + assert((II.getIntrinsicID() == Intrinsic::experimental_separate_storage || + II.getIntrinsicID() == Intrinsic::assume) && + "Expected assume"); + // facebook end T130678741 + // TODO For now we forget assumed information, this can be improved. OldPtr->dropDroppableUsesIn(II); return true; @@ -4686,7 +4691,7 @@ bool Changed = false; while (!DeadInsts.empty()) { Instruction *I = dyn_cast_or_null(DeadInsts.pop_back_val()); - if (!I) continue; + if (!I) continue; LLVM_DEBUG(dbgs() << "Deleting dead instruction: " << *I << "\n"); // If the instruction is an alloca, find the possible dbg.declare connected diff --git a/llvm/lib/Transforms/Utils/Evaluator.cpp b/llvm/lib/Transforms/Utils/Evaluator.cpp --- a/llvm/lib/Transforms/Utils/Evaluator.cpp +++ b/llvm/lib/Transforms/Utils/Evaluator.cpp @@ -468,6 +468,13 @@ LLVM_DEBUG(dbgs() << "Skipping assume intrinsic.\n"); ++CurInst; continue; + // facebook begin T130678741 + } else if (II->getIntrinsicID() == + Intrinsic::experimental_separate_storage) { + LLVM_DEBUG(dbgs() << "Skipping separate storage intrinsic.\n"); + ++CurInst; + continue; + // facebook end T130678741 } else if (II->getIntrinsicID() == Intrinsic::sideeffect) { LLVM_DEBUG(dbgs() << "Skipping sideeffect intrinsic.\n"); ++CurInst; diff --git a/llvm/test/Analysis/CostModel/X86/free-intrinsics.ll b/llvm/test/Analysis/CostModel/X86/free-intrinsics.ll --- a/llvm/test/Analysis/CostModel/X86/free-intrinsics.ll +++ b/llvm/test/Analysis/CostModel/X86/free-intrinsics.ll @@ -6,6 +6,7 @@ ; CHECK-SIZE-LABEL: 'trivially_free' ; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: %a0 = call i32 @llvm.annotation.i32(i32 undef, i8* undef, i8* undef, i32 undef) ; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.assume(i1 undef) +; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.experimental.separate.storage(i8* undef, i8* undef) ; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.experimental.noalias.scope.decl(metadata !0) ; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.sideeffect() ; CHECK-SIZE-NEXT: Cost Model: Found an estimated cost of 0 for instruction: %a1 = call {}* @llvm.invariant.start.p0i8(i64 1, i8* undef) @@ -23,6 +24,7 @@ ; CHECK-THROUGHPUT-LABEL: 'trivially_free' ; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: %a0 = call i32 @llvm.annotation.i32(i32 undef, i8* undef, i8* undef, i32 undef) ; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.assume(i1 undef) +; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.experimental.separate.storage(i8* undef, i8* undef) ; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.experimental.noalias.scope.decl(metadata !0) ; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: call void @llvm.sideeffect() ; CHECK-THROUGHPUT-NEXT: Cost Model: Found an estimated cost of 0 for instruction: %a1 = call {}* @llvm.invariant.start.p0i8(i64 1, i8* undef) @@ -39,6 +41,9 @@ ; %a0 = call i32 @llvm.annotation.i32(i32 undef, i8* undef, i8* undef, i32 undef) call void @llvm.assume(i1 undef) + ; facebook begin T130678741 + call void @llvm.experimental.separate.storage(i8* undef, i8* undef) + ; facebook end T130678741 call void @llvm.experimental.noalias.scope.decl(metadata !4) call void @llvm.sideeffect() call void @llvm.dbg.declare(metadata i8** undef, metadata !0, metadata !DIExpression()) @@ -59,6 +64,9 @@ declare i32 @llvm.annotation.i32(i32, i8*, i8*, i32) declare void @llvm.assume(i1) +; facebook begin T130678741 +declare void @llvm.experimental.separate.storage(i8*, i8*) +; facebook end T130678741 declare void @llvm.experimental.noalias.scope.decl(metadata) declare void @llvm.sideeffect() declare void @llvm.dbg.declare(metadata, metadata, metadata) diff --git a/llvm/test/Analysis/SeparateStorageAliasAnalysis/alias_test.ll b/llvm/test/Analysis/SeparateStorageAliasAnalysis/alias_test.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/SeparateStorageAliasAnalysis/alias_test.ll @@ -0,0 +1,123 @@ +; facebook begin T130678741 +; RUN: opt < %s -aa-pipeline=separatestorage-aa,basic-aa -gvn -enable-separate-storage-aa -S | FileCheck %s + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +declare void @llvm.experimental.separate.storage(ptr, ptr) + +; Test basic queries. + +; CHECK-LAEBL: @simple_no +; CHECK: ret i8 %loadofstore +define i8 @simple_no(ptr %p1, ptr %p2) { +entry: + store i8 0, ptr %p1 + store i8 1, ptr %p2 + %loadofstore = load i8, ptr %p1 + ret i8 %loadofstore +} + +; CHECK-LABEL: @simple_yes +; CHECK: ret i8 0 +define i8 @simple_yes(ptr %p1, ptr %p2) { +entry: + call void @llvm.experimental.separate.storage(ptr %p1, ptr %p2) + store i8 0, ptr %p1 + store i8 1, ptr %p2 + %loadofstore = load i8, ptr %p1 + ret i8 %loadofstore +} + +; CHECK-LABEL: @ptr_to_ptr_no +; CHECK: ret i8 %loadofstore +define i8 @ptr_to_ptr_no(ptr %pp) { +entry: + %p_base = load ptr, ptr %pp + store i8 0, ptr %p_base + %p_base2 = load ptr, ptr %pp + %loadofstore = load i8, ptr %p_base2 + ret i8 %loadofstore +} + +; CHECK-LABEL: @ptr_to_ptr_yes +; CHECK: ret i8 0 +define i8 @ptr_to_ptr_yes(ptr %pp) { +entry: + %p_base = load ptr, ptr %pp + call void @llvm.experimental.separate.storage(ptr %p_base, ptr %pp) + store i8 0, ptr %p_base + %p_base2 = load ptr, ptr %pp + %loadofstore = load i8, ptr %p_base2 + ret i8 %loadofstore +} + +; The analysis should only kick in if executed (or will be executed) at the +; given program point. + +; CHECK-LABEL: @flow_sensitive +; CHECK: %loadofstore = phi i8 [ %loadofstore_true, %true_branch ], [ 33, %false_branch ] +define i8 @flow_sensitive(ptr %p1, ptr %p2, i1 %cond) { +entry: + br i1 %cond, label %true_branch, label %false_branch + +true_branch: + store i8 11, ptr %p1 + store i8 22, ptr %p2 + %loadofstore_true = load i8, ptr %p1 + br label %endif + +false_branch: + call void @llvm.experimental.separate.storage(ptr %p1, ptr %p2) + store i8 33, ptr %p1 + store i8 44, ptr %p2 + %loadofstore_false = load i8, ptr %p1 + br label %endif + +endif: + %loadofstore = phi i8 [ %loadofstore_true, %true_branch ], [ %loadofstore_false, %false_branch ] + ret i8 %loadofstore +} + +; CHECK-LABEL: @flow_sensitive_with_dominator +; CHECK: %loadofstore = phi i8 [ 11, %true_branch ], [ 33, %false_branch ] +define i8 @flow_sensitive_with_dominator(ptr %p1, ptr %p2, i1 %cond) { +entry: + call void @llvm.experimental.separate.storage(ptr %p1, ptr %p2) + br i1 %cond, label %true_branch, label %false_branch + +true_branch: + store i8 11, ptr %p1 + store i8 22, ptr %p2 + %loadofstore_true = load i8, ptr %p1 + br label %endif + +false_branch: + store i8 33, ptr %p1 + store i8 44, ptr %p2 + %loadofstore_false = load i8, ptr %p1 + br label %endif + +endif: + %loadofstore = phi i8 [ %loadofstore_true, %true_branch ], [ %loadofstore_false, %false_branch ] + ret i8 %loadofstore +} + +; Hints are relative to entire regions of storage, not particular pointers +; inside them. We should know that the whole ranges are disjoint given hints at +; offsets. + +; CHECK-LABEL: @offset_agnostic +; CHECK: ret i8 0 +define i8 @offset_agnostic(ptr %p1, ptr %p2) { + %access1 = getelementptr inbounds i8, ptr %p1, i64 12 + %access2 = getelementptr inbounds i8, ptr %p2, i64 34 + + %hint1 = getelementptr inbounds i8, ptr %p1, i64 56 + %hint2 = getelementptr inbounds i8, ptr %p2, i64 78 + call void @llvm.experimental.separate.storage(ptr %hint1, ptr %hint2) + + store i8 0, ptr %access1 + store i8 1, ptr %access2 + %loadofstore = load i8, ptr %access1 + ret i8 %loadofstore +} +; facebook end T130678741 diff --git a/llvm/test/CodeGen/Generic/experimental-separate-storage.ll b/llvm/test/CodeGen/Generic/experimental-separate-storage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Generic/experimental-separate-storage.ll @@ -0,0 +1,10 @@ +; facebook begin T130678741 +; RUN: llc < %s + +declare void @llvm.experimental.separate.storage(ptr, ptr) + +define void @f() { + call void @llvm.experimental.separate.storage(i8* undef, i8* undef) + ret void +} +; facebook end T130678741