Fix a long-standing miscompile in the load analysis that was uncovered
authorChandler Carruth <chandlerc@gmail.com>
Sun, 19 Oct 2014 08:17:50 +0000 (08:17 +0000)
committerChandler Carruth <chandlerc@gmail.com>
Sun, 19 Oct 2014 08:17:50 +0000 (08:17 +0000)
by my refactoring of this code.

The method isSafeToLoadUnconditionally assumes that the load will
proceed with the preferred type alignment. Given that, it has to ensure
that the alloca or global is at least that aligned. It has always done
this historically when a datalayout is present, but has never checked it
when the datalayout is absent. When I refactored the code in r220156,
I exposed this path when datalayout was present and that turned the
latent bug into a patent bug.

This fixes the issue by just removing the special case which allows
folding things without datalayout. This isn't worth the complexity of
trying to tease apart when it is or isn't safe without actually knowing
the preferred alignment.

llvm-svn: 220161

llvm/lib/Analysis/Loads.cpp
llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
llvm/test/Transforms/InstCombine/load.ll
llvm/test/Transforms/InstCombine/select.ll
llvm/test/Transforms/TailCallElim/reorder_load.ll

index f5eb7055726e59ffed6ca07142eb7fdc433de7fb..4838d856ae34ae051d38c1a5460e979b364cac2c 100644 (file)
@@ -73,11 +73,6 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Instruction *ScanFrom,
   Type *BaseType = nullptr;
   unsigned BaseAlign = 0;
   if (const AllocaInst *AI = dyn_cast<AllocaInst>(Base)) {
-    // Loading directly from an alloca is trivially safe. We can't even look
-    // through pointer casts here though, as that might change the size loaded.
-    if (AI == V)
-      return true;
-
     // An alloca is safe to load from as load as it is suitably aligned.
     BaseType = AI->getAllocatedType();
     BaseAlign = AI->getAlignment();
@@ -86,12 +81,6 @@ bool llvm::isSafeToLoadUnconditionally(Value *V, Instruction *ScanFrom,
     // overridden. Their size may change or they may be weak and require a test
     // to determine if they were in fact provided.
     if (!GV->mayBeOverridden()) {
-      // Loading directly from the non-overridden global is trivially safe. We
-      // can't even look through pointer casts here though, as that might change
-      // the size loaded.
-      if (GV == V)
-        return true;
-
       BaseType = GV->getType()->getElementType();
       BaseAlign = GV->getAlignment();
     }
index b7580255150c48fae28ff2dbb02a62bb6ffafcca..6fe5e188b1ac993c04579ec7a4d36d757eb5b03a 100644 (file)
@@ -63,6 +63,7 @@
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/CallSite.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Function.h"
@@ -86,6 +87,7 @@ STATISTIC(NumAccumAdded, "Number of accumulators introduced");
 namespace {
   struct TailCallElim : public FunctionPass {
     const TargetTransformInfo *TTI;
+    const DataLayout *DL;
 
     static char ID; // Pass identification, replacement for typeid
     TailCallElim() : FunctionPass(ID) {
@@ -157,6 +159,8 @@ bool TailCallElim::runOnFunction(Function &F) {
   if (skipOptnoneFunction(F))
     return false;
 
+  DL = F.getParent()->getDataLayout();
+
   bool AllCallsAreTailCalls = false;
   bool Modified = markTails(F, AllCallsAreTailCalls);
   if (AllCallsAreTailCalls)
@@ -450,7 +454,7 @@ bool TailCallElim::CanMoveAboveCall(Instruction *I, CallInst *CI) {
       // being loaded from.
       if (CI->mayWriteToMemory() ||
           !isSafeToLoadUnconditionally(L->getPointerOperand(), L,
-                                       L->getAlignment()))
+                                       L->getAlignment(), DL))
         return false;
     }
   }
index c8ce70a5c03a58da1649112f29447719bb9d4943..20d40e2ccfd6b03ed04b54bd7a1861310ea02e7e 100644 (file)
@@ -2,6 +2,7 @@
 
 ; This test makes sure that these instructions are properly eliminated.
 
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
 @X = constant i32 42           ; <i32*> [#uses=2]
 @X2 = constant i32 47          ; <i32*> [#uses=1]
index d625f3b1b33d6fe5993165b8d82b18de6f8b8ddf..d1d8b888713eff746ba51c9b1f21e7a865e10b47 100644 (file)
@@ -1236,3 +1236,41 @@ define i32 @test75(i32 %x) {
 ; CHECK-NEXT: [[SEL:%[a-z0-9]+]] = select i1 [[CMP]], i32 68, i32 %x
 ; CHECK-NEXT: ret i32 [[SEL]]
 }
+
+@under_aligned = external global i32, align 1
+
+define i32 @test76(i1 %flag, i32* %x) {
+; The load here must not be speculated around the select. One side of the
+; select is trivially dereferencable but may have a lower alignment than the
+; load does.
+; CHECK-LABEL: @test76(
+; CHECK: store i32 0, i32* %x
+; CHECK: %[[P:.*]] = select i1 %flag, i32* @under_aligned, i32* %x
+; CHECK: load i32* %[[P]]
+
+  store i32 0, i32* %x
+  %p = select i1 %flag, i32* @under_aligned, i32* %x
+  %v = load i32* %p
+  ret i32 %v
+}
+
+declare void @scribble_on_memory(i32*)
+
+define i32 @test77(i1 %flag, i32* %x) {
+; The load here must not be speculated around the select. One side of the
+; select is trivially dereferencable but may have a lower alignment than the
+; load does.
+; CHECK-LABEL: @test77(
+; CHECK: %[[A:.*]] = alloca i32, align 1
+; CHECK: call void @scribble_on_memory(i32* %[[A]])
+; CHECK: store i32 0, i32* %x
+; CHECK: %[[P:.*]] = select i1 %flag, i32* %[[A]], i32* %x
+; CHECK: load i32* %[[P]]
+
+  %under_aligned = alloca i32, align 1
+  call void @scribble_on_memory(i32* %under_aligned)
+  store i32 0, i32* %x
+  %p = select i1 %flag, i32* %under_aligned, i32* %x
+  %v = load i32* %p
+  ret i32 %v
+}
index 53c65dab101b01defa496728f2da4c7963f19050..2e350d662a39eb27726db69d4f772aad89329ff1 100644 (file)
@@ -1,6 +1,8 @@
 ; RUN: opt < %s -tailcallelim -S | FileCheck %s
 ; PR4323
 
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
 ; Several cases where tail call elimination should move the load above the call,
 ; then eliminate the tail recursion.
 
 ; This load can be moved above the call because the function won't write to it
 ; and the call has no side effects.
 define fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly {
+; CHECK-LABEL: @raise_load_1(
+; CHECK-NOT: call
+; CHECK: load i32*
+; CHECK-NOT: call
+; CHECK: }
 entry:
        %tmp2 = icmp sge i32 %start_arg, %a_len_arg             ; <i1> [#uses=1]
        br i1 %tmp2, label %if, label %else
@@ -21,7 +28,6 @@ if:           ; preds = %entry
 
 else:          ; preds = %entry
        %tmp7 = add i32 %start_arg, 1           ; <i32> [#uses=1]
-; CHECK-NOT: call
        %tmp8 = call fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)           ; <i32> [#uses=1]
        %tmp9 = load i32* %a_arg                ; <i32> [#uses=1]
        %tmp10 = add i32 %tmp9, %tmp8           ; <i32> [#uses=1]
@@ -32,6 +38,11 @@ else:                ; preds = %entry
 ; This load can be moved above the call because the function won't write to it
 ; and the load provably can't trap.
 define fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly {
+; CHECK-LABEL: @raise_load_2(
+; CHECK-NOT: call
+; CHECK: load i32*
+; CHECK-NOT: call
+; CHECK: }
 entry:
        %tmp2 = icmp sge i32 %start_arg, %a_len_arg             ; <i1> [#uses=1]
        br i1 %tmp2, label %if, label %else
@@ -48,7 +59,6 @@ unwind:               ; preds = %else
 
 recurse:               ; preds = %else
        %tmp7 = add i32 %start_arg, 1           ; <i32> [#uses=1]
-; CHECK-NOT: call
        %tmp8 = call fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)           ; <i32> [#uses=1]
        %tmp9 = load i32* @global               ; <i32> [#uses=1]
        %tmp10 = add i32 %tmp9, %tmp8           ; <i32> [#uses=1]
@@ -59,6 +69,11 @@ recurse:             ; preds = %else
 ; This load can be safely moved above the call (even though it's from an
 ; extern_weak global) because the call has no side effects.
 define fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly {
+; CHECK-LABEL: @raise_load_3(
+; CHECK-NOT: call
+; CHECK: load i32*
+; CHECK-NOT: call
+; CHECK: }
 entry:
        %tmp2 = icmp sge i32 %start_arg, %a_len_arg             ; <i1> [#uses=1]
        br i1 %tmp2, label %if, label %else
@@ -68,7 +83,6 @@ if:           ; preds = %entry
 
 else:          ; preds = %entry
        %tmp7 = add i32 %start_arg, 1           ; <i32> [#uses=1]
-; CHECK-NOT: call
        %tmp8 = call fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)           ; <i32> [#uses=1]
        %tmp9 = load i32* @extern_weak_global           ; <i32> [#uses=1]
        %tmp10 = add i32 %tmp9, %tmp8           ; <i32> [#uses=1]
@@ -80,6 +94,12 @@ else:                ; preds = %entry
 ; unknown pointer (which normally means it might trap) because the first load
 ; proves it doesn't trap.
 define fastcc i32 @raise_load_4(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly {
+; CHECK-LABEL: @raise_load_4(
+; CHECK-NOT: call
+; CHECK: load i32*
+; CHECK-NEXT: load i32*
+; CHECK-NOT: call
+; CHECK: }
 entry:
        %tmp2 = icmp sge i32 %start_arg, %a_len_arg             ; <i1> [#uses=1]
        br i1 %tmp2, label %if, label %else
@@ -97,7 +117,6 @@ unwind:              ; preds = %else
 recurse:               ; preds = %else
        %tmp7 = add i32 %start_arg, 1           ; <i32> [#uses=1]
        %first = load i32* %a_arg               ; <i32> [#uses=1]
-; CHECK-NOT: call
        %tmp8 = call fastcc i32 @raise_load_4(i32* %a_arg, i32 %first, i32 %tmp7)               ; <i32> [#uses=1]
        %second = load i32* %a_arg              ; <i32> [#uses=1]
        %tmp10 = add i32 %second, %tmp8         ; <i32> [#uses=1]