diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -68,8 +68,10 @@ static cl::opt EnableRecPhiAnalysis("basic-aa-recphi", cl::Hidden, cl::init(true)); -static cl::opt EnableSeparateStorageAnalysis("basic-aa-separate-storage", - cl::Hidden, cl::init(false)); +namespace llvm { +cl::opt EnableSeparateStorageAnalysis("basic-aa-separate-storage", + cl::Hidden, cl::init(false)); +} // namespace llvm /// SearchLimitReached / SearchTimes shows how often the limit of /// to decompose GEPs is reached. It will affect the precision @@ -1516,6 +1518,8 @@ assert(OBU.Inputs.size() == 2); const Value *Hint1 = OBU.Inputs[0].get(); const Value *Hint2 = OBU.Inputs[1].get(); + // This is often a no-op; instcombine rewrites this for us. No-op + // getUnderlyingObject calls are fast, though. const Value *HintO1 = getUnderlyingObject(Hint1); const Value *HintO2 = getUnderlyingObject(Hint2); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -92,6 +92,7 @@ /// enable preservation of attributes in assume like: /// call void @llvm.assume(i1 true) [ "nonnull"(i32* %PTR) ] extern cl::opt EnableKnowledgeRetention; +extern cl::opt EnableSeparateStorageAnalysis; } // namespace llvm /// Return the specified type promoted as it would be to pass though a va_arg @@ -2477,6 +2478,31 @@ // TODO: apply range metadata for range check patterns? } + // Separate storage assumptions apply to the underlying allocations, not any + // particular pointer within them. When evaluating the hints for AA purposes + // we getUnderlyingObject them; by precomputing the answers here we can + // avoid having to do so repeatedly there. + if (EnableSeparateStorageAnalysis) { + for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) { + OperandBundleUse OBU = II->getOperandBundleAt(Idx); + if (OBU.getTagName() == "separate_storage") { + assert(OBU.Inputs.size() == 2); + auto MaybeSimplifyHint = [&](const Use &U) { + Value *Hint = U.get(); + Value *UnderlyingObject = getUnderlyingObject(Hint, 0); + if (Hint != UnderlyingObject) { + const_cast(U).set(UnderlyingObject); + if (Instruction *HintI = dyn_cast(Hint)) { + Worklist.add(HintI); + } + } + }; + MaybeSimplifyHint(OBU.Inputs[0]); + MaybeSimplifyHint(OBU.Inputs[1]); + } + } + } + // Convert nonnull assume like: // %A = icmp ne i32* %PTR, null // call void @llvm.assume(i1 %A) diff --git a/llvm/test/Transforms/InstCombine/assume-separate_storage.ll b/llvm/test/Transforms/InstCombine/assume-separate_storage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/assume-separate_storage.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=instcombine -basic-aa-separate-storage < %s 2>&1 | FileCheck %s + +declare void @llvm.assume(i1 noundef) + +; Just something to let us check that separate_storage bundles don't break +; anything when given an operand that's not an instruction to fold. +@some_global = global i32 777 + +define void @simple_folding(ptr %a, ptr %b) { +; CHECK-LABEL: @simple_folding( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[A:%.*]], ptr [[B:%.*]]) ] +; CHECK-NEXT: ret void +; +entry: + %p1 = getelementptr i8, ptr %a, i64 123 + %p2 = getelementptr i8, ptr %b, i64 777 + call void @llvm.assume(i1 1) ["separate_storage"(ptr %p1, ptr %p2)] + ret void +} + +define i64 @folds_removed_operands(ptr %a, ptr %b, i64 %n1, i64 %n2) { +; CHECK-LABEL: @folds_removed_operands( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[REASS_ADD:%.*]] = shl i64 [[N2:%.*]], 1 +; CHECK-NEXT: [[Y:%.*]] = add i64 [[REASS_ADD]], [[N1:%.*]] +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[A:%.*]], ptr [[B:%.*]]) ] +; CHECK-NEXT: ret i64 [[Y]] +; +entry: + ; Ordinarily, n1 + n2 + n2 would get canonicalized into n1 + (n2 << 1) unless + ; there's another use of n1 + n2. Make sure that we remember to put removed + ; arguments to separate_storage bundles back on the worklist. + %x = add i64 %n1, %n2 + %y = add i64 %x, %n2 + %p1 = getelementptr i8, ptr %a, i64 %x + call void @llvm.assume(i1 1) ["separate_storage"(ptr %p1, ptr %b)] + ret i64 %y +} + +define void @handles_globals(ptr %a) { +; CHECK-LABEL: @handles_globals( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[A:%.*]], ptr @some_global) ] +; CHECK-NEXT: ret void +; + %derived = getelementptr i8, ptr @some_global, i65 3 + call void @llvm.assume(i1 1) ["separate_storage"(ptr %a, ptr %derived)] + ret void +}