Index: lib/Analysis/Loads.cpp =================================================================== --- lib/Analysis/Loads.cpp +++ lib/Analysis/Loads.cpp @@ -13,6 +13,7 @@ #include "llvm/Analysis/Loads.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/MemoryBuiltins.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalAlias.h" @@ -112,6 +113,17 @@ return isDereferenceableAndAlignedPointer(RV, Align, Size, DL, CtxI, DT, Visited); + // Check for cases where we know the minimum size of an object - this handles + // cases where we can see the allocation, or we're loading from global + // variable with a known size. This does assume that our definition of + // "object" size ensures the entire size is dereferenceable. + uint64_t ObjSize; + ObjectSizeOpts Opts; + Opts.EvalMode = ObjectSizeOpts::Mode::Min; + if (getObjectSize(V, ObjSize, DL, nullptr, Opts) && + Size.ule(ObjSize)) + return isAligned(V, Align, DL); + // If we don't know, assume the worst. return false; } Index: test/Transforms/LICM/hoisting.ll =================================================================== --- test/Transforms/LICM/hoisting.ll +++ test/Transforms/LICM/hoisting.ll @@ -320,3 +320,88 @@ loopexit: ret i32 %sum } + +@g = external global i1 + +declare align 8 i8* @my_alloc(i64) allocsize(0) +declare align 8 i8* @not_alloc(i64) + +; Can hoist a load if we know the size of the underlying allocation +; since we know the entire allocation must be dereferenceable +define void @test_sized_alloc() { +; CHECK-LABEL: test_sized_alloc +; CHECK: @my_alloc +; CHECK-NEXT: load i8, i8* %p + %p = call i8* @my_alloc(i64 20); + br label %header + +header: + %vol = load volatile i1, i1* @g + br i1 %vol, label %taken, label %exit +taken: + %v = load i8, i8* %p + %cmp = icmp eq i8 %v, 0 + br i1 %cmp, label %exit, label %header +exit: + ret void +} + +define void @test_sized_alloc_select(i1 %arg) { +; CHECK-LABEL: test_sized_alloc_select +; CHECK: @my_alloc +; CHECK-NEXT: @my_alloc +; CHECK-NEXT: %p = select i1 %arg, i8* %p1, i8* %p2 +; CHECK-NEXT: load i8, i8* %p + %p1 = call i8* @my_alloc(i64 20); + %p2 = call i8* @my_alloc(i64 24); + %p = select i1 %arg, i8* %p1, i8* %p2 + br label %header + +header: + %vol = load volatile i1, i1* @g + br i1 %vol, label %taken, label %exit +taken: + %v = load i8, i8* %p + %cmp = icmp eq i8 %v, 0 + br i1 %cmp, label %exit, label %header +exit: + ret void +} + +define void @test_sized_alloc_out_of_bounds() { +; CHECK-LABEL: test_sized_alloc_out_of_bounds +; CHECK: @my_alloc + %p = call i8* @my_alloc(i64 0); + br label %header + +header: + %vol = load volatile i1, i1* @g + br i1 %vol, label %taken, label %exit +taken: +; CHECK-LABEL: taken: +; CHECK-NEXT: load i8, i8* %p + %v = load i8, i8* %p + %cmp = icmp eq i8 %v, 0 + br i1 %cmp, label %exit, label %header +exit: + ret void +} + +define void @test_sized_not_alloc() { +; CHECK-LABEL: test_sized_not_alloc +; CHECK: @not_alloc + %p = call i8* @not_alloc(i64 20); + br label %header + +header: + %vol = load volatile i1, i1* @g + br i1 %vol, label %taken, label %exit +taken: +; CHECK-LABEL: taken: +; CHECK-NEXT: load i8, i8* %p + %v = load i8, i8* %p + %cmp = icmp eq i8 %v, 0 + br i1 %cmp, label %exit, label %header +exit: + ret void +}