static cl::opt<bool> EnableRecPhiAnalysis("basic-aa-recphi", cl::Hidden,
cl::init(true));
+static cl::opt<bool> EnableSeparateStorageAnalysis("basic-aa-separate-storage",
+ cl::Hidden, cl::init(false));
+
/// SearchLimitReached / SearchTimes shows how often the limit of
/// to decompose GEPs is reached. It will affect the precision
/// of basic alias analysis.
AliasResult BasicAAResult::alias(const MemoryLocation &LocA,
const MemoryLocation &LocB, AAQueryInfo &AAQI,
- const Instruction *) {
+ const Instruction *CtxI) {
assert(notDifferentParent(LocA.Ptr, LocB.Ptr) &&
"BasicAliasAnalysis doesn't support interprocedural queries.");
- return aliasCheck(LocA.Ptr, LocA.Size, LocB.Ptr, LocB.Size, AAQI);
+ return aliasCheck(LocA.Ptr, LocA.Size, LocB.Ptr, LocB.Size, AAQI, CtxI);
}
/// Checks to see if the specified callsite can clobber the specified memory
/// array references.
AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
const Value *V2, LocationSize V2Size,
- AAQueryInfo &AAQI) {
+ AAQueryInfo &AAQI,
+ const Instruction *CtxI) {
// If either of the memory references is empty, it doesn't matter what the
// pointer values are.
if (V1Size.isZero() || V2Size.isZero())
TLI, NullIsValidLocation)))
return AliasResult::NoAlias;
+ if (CtxI && EnableSeparateStorageAnalysis) {
+ for (auto &AssumeVH : AC.assumptions()) {
+ if (!AssumeVH)
+ continue;
+
+ AssumeInst *Assume = cast<AssumeInst>(AssumeVH);
+
+ for (unsigned Idx = 0; Idx < Assume->getNumOperandBundles(); Idx++) {
+ OperandBundleUse OBU = Assume->getOperandBundleAt(Idx);
+ if (OBU.getTagName() == "separate_storage") {
+ assert(OBU.Inputs.size() == 2);
+ const Value *Hint1 = OBU.Inputs[0].get();
+ const Value *Hint2 = OBU.Inputs[1].get();
+ const Value *HintO1 = getUnderlyingObject(Hint1);
+ const Value *HintO2 = getUnderlyingObject(Hint2);
+
+ if (((O1 == HintO1 && O2 == HintO2) ||
+ (O1 == HintO2 && O2 == HintO1)) &&
+ isValidAssumeForContext(Assume, CtxI, DT))
+ return AliasResult::NoAlias;
+ }
+ }
+ }
+ }
+
// If one the accesses may be before the accessed pointer, canonicalize this
// by using unknown after-pointer sizes for both accesses. This is
// equivalent, because regardless of which pointer is lower, one of them
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes='gvn' -basic-aa-separate-storage -S | FileCheck %s
+
+declare void @llvm.assume(i1)
+
+; Test basic queries.
+
+define i8 @simple_no(ptr %p1, ptr %p2) {
+; CHECK-LABEL: @simple_no(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: store i8 0, ptr [[P1:%.*]], align 1
+; CHECK-NEXT: store i8 1, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: [[LOADOFSTORE:%.*]] = load i8, ptr [[P1]], align 1
+; CHECK-NEXT: ret i8 [[LOADOFSTORE]]
+;
+entry:
+ store i8 0, ptr %p1
+ store i8 1, ptr %p2
+ %loadofstore = load i8, ptr %p1
+ ret i8 %loadofstore
+}
+
+define i8 @simple_yes(ptr %p1, ptr %p2) {
+; CHECK-LABEL: @simple_yes(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[P1:%.*]], ptr [[P2:%.*]]) ]
+; CHECK-NEXT: store i8 0, ptr [[P1]], align 1
+; CHECK-NEXT: store i8 1, ptr [[P2]], align 1
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ call void @llvm.assume(i1 1) ["separate_storage"(ptr %p1, ptr %p2)]
+ store i8 0, ptr %p1
+ store i8 1, ptr %p2
+ %loadofstore = load i8, ptr %p1
+ ret i8 %loadofstore
+}
+
+define i8 @ptr_to_ptr_no(ptr %pp) {
+; CHECK-LABEL: @ptr_to_ptr_no(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[P_BASE:%.*]] = load ptr, ptr [[PP:%.*]], align 8
+; CHECK-NEXT: store i8 0, ptr [[P_BASE]], align 1
+; CHECK-NEXT: [[P_BASE2:%.*]] = load ptr, ptr [[PP]], align 8
+; CHECK-NEXT: [[LOADOFSTORE:%.*]] = load i8, ptr [[P_BASE2]], align 1
+; CHECK-NEXT: ret i8 [[LOADOFSTORE]]
+;
+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
+}
+
+define i8 @ptr_to_ptr_yes(ptr %pp) {
+; CHECK-LABEL: @ptr_to_ptr_yes(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[P_BASE:%.*]] = load ptr, ptr [[PP:%.*]], align 8
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[P_BASE]], ptr [[PP]]) ]
+; CHECK-NEXT: store i8 0, ptr [[P_BASE]], align 1
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ %p_base = load ptr, ptr %pp
+ call void @llvm.assume(i1 1) ["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.
+
+define i8 @flow_sensitive(ptr %p1, ptr %p2, i1 %cond) {
+; CHECK-LABEL: @flow_sensitive(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[TRUE_BRANCH:%.*]], label [[FALSE_BRANCH:%.*]]
+; CHECK: true_branch:
+; CHECK-NEXT: store i8 11, ptr [[P1:%.*]], align 1
+; CHECK-NEXT: store i8 22, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: [[LOADOFSTORE_TRUE:%.*]] = load i8, ptr [[P1]], align 1
+; CHECK-NEXT: br label [[ENDIF:%.*]]
+; CHECK: false_branch:
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[P1]], ptr [[P2]]) ]
+; CHECK-NEXT: store i8 33, ptr [[P1]], align 1
+; CHECK-NEXT: store i8 44, ptr [[P2]], align 1
+; CHECK-NEXT: br label [[ENDIF]]
+; CHECK: endif:
+; CHECK-NEXT: [[LOADOFSTORE:%.*]] = phi i8 [ [[LOADOFSTORE_TRUE]], [[TRUE_BRANCH]] ], [ 33, [[FALSE_BRANCH]] ]
+; CHECK-NEXT: ret i8 [[LOADOFSTORE]]
+;
+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.assume(i1 1) ["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
+}
+
+define i8 @flow_sensitive_with_dominator(ptr %p1, ptr %p2, i1 %cond) {
+; CHECK-LABEL: @flow_sensitive_with_dominator(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[P1:%.*]], ptr [[P2:%.*]]) ]
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[TRUE_BRANCH:%.*]], label [[FALSE_BRANCH:%.*]]
+; CHECK: true_branch:
+; CHECK-NEXT: store i8 11, ptr [[P1]], align 1
+; CHECK-NEXT: store i8 22, ptr [[P2]], align 1
+; CHECK-NEXT: br label [[ENDIF:%.*]]
+; CHECK: false_branch:
+; CHECK-NEXT: store i8 33, ptr [[P1]], align 1
+; CHECK-NEXT: store i8 44, ptr [[P2]], align 1
+; CHECK-NEXT: br label [[ENDIF]]
+; CHECK: endif:
+; CHECK-NEXT: [[LOADOFSTORE:%.*]] = phi i8 [ 11, [[TRUE_BRANCH]] ], [ 33, [[FALSE_BRANCH]] ]
+; CHECK-NEXT: ret i8 [[LOADOFSTORE]]
+;
+entry:
+ call void @llvm.assume(i1 1) ["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.
+
+define i8 @offset_agnostic(ptr %p1, ptr %p2) {
+; CHECK-LABEL: @offset_agnostic(
+; CHECK-NEXT: [[ACCESS1:%.*]] = getelementptr inbounds i8, ptr [[P1:%.*]], i64 12
+; CHECK-NEXT: [[ACCESS2:%.*]] = getelementptr inbounds i8, ptr [[P2:%.*]], i64 34
+; CHECK-NEXT: [[HINT1:%.*]] = getelementptr inbounds i8, ptr [[P1]], i64 56
+; CHECK-NEXT: [[HINT2:%.*]] = getelementptr inbounds i8, ptr [[P2]], i64 78
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[HINT1]], ptr [[HINT2]]) ]
+; CHECK-NEXT: store i8 0, ptr [[ACCESS1]], align 1
+; CHECK-NEXT: store i8 1, ptr [[ACCESS2]], align 1
+; CHECK-NEXT: ret i8 0
+;
+ %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.assume(i1 1) ["separate_storage"(ptr %hint1, ptr %hint2)]
+
+ store i8 0, ptr %access1
+ store i8 1, ptr %access2
+ %loadofstore = load i8, ptr %access1
+ ret i8 %loadofstore
+}