[llvm] 7314ad7 - Revert "[SimplifyCFG] Allow SimplifyCFG hoisting to skip over non-matching instructions"

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 1 00:21:03 PDT 2022


Author: Nikita Popov
Date: 2022-08-01T09:20:56+02:00
New Revision: 7314ad7a0661579e19eeec20c91e49f51f8c8d5e

URL: https://github.com/llvm/llvm-project/commit/7314ad7a0661579e19eeec20c91e49f51f8c8d5e
DIFF: https://github.com/llvm/llvm-project/commit/7314ad7a0661579e19eeec20c91e49f51f8c8d5e.diff

LOG: Revert "[SimplifyCFG] Allow SimplifyCFG hoisting to skip over non-matching instructions"

This reverts commit 7b0f6378e211e881c574748090a86beeab264ab3.

As commented on the review, this patch has a correctness issue
regarding the modelling of memory effects.

Added: 
    

Modified: 
    llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Removed: 
    llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
    llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 1b148aeb794d3..1806081678a86 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -115,12 +115,6 @@ static cl::opt<bool>
     HoistCommon("simplifycfg-hoist-common", cl::Hidden, cl::init(true),
                 cl::desc("Hoist common instructions up to the parent block"));
 
-static cl::opt<unsigned>
-    HoistCommonSkipLimit("simplifycfg-hoist-common-skip-limit", cl::Hidden,
-                         cl::init(20),
-                         cl::desc("Allow reordering across at most this many "
-                                  "instructions when hoisting"));
-
 static cl::opt<bool>
     SinkCommon("simplifycfg-sink-common", cl::Hidden, cl::init(true),
                cl::desc("Sink common instructions down to the end block"));
@@ -1436,32 +1430,6 @@ static bool isSafeToHoistInvoke(BasicBlock *BB1, BasicBlock *BB2,
   return true;
 }
 
-// Returns true if it is safe to reorder an instruction across preceding
-// instructions in a basic block.
-static bool isSafeToHoistInstr(Instruction *I, bool ForceNoSideEffects,
-                               bool ForceNoSpeculation) {
-  // If we have seen an instruction with side effects, it's unsafe to reorder an
-  // instruction which reads memory or itself has side effects.
-  if (ForceNoSideEffects && (I->mayReadFromMemory() || I->mayHaveSideEffects()))
-    return false;
-
-  // Reordering across an instruction which does not necessarily transfer
-  // control to the next instruction is speculation.
-  if (ForceNoSpeculation && !isSafeToSpeculativelyExecute(I))
-    return false;
-
-  // It's also unsafe/illegal to hoist an instruction above its instruction
-  // operands
-  BasicBlock *BB = I->getParent();
-  for (Value *Op : I->operands()) {
-    if (auto *J = dyn_cast<Instruction>(Op))
-      if (J->getParent() == BB)
-        return false;
-  }
-
-  return true;
-}
-
 static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified = false);
 
 /// Given a conditional branch that goes to BB1 and BB2, hoist any common code
@@ -1476,8 +1444,7 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
   // instructions in the two blocks.  In particular, we don't want to get into
   // O(M*N) situations here where M and N are the sizes of BB1 and BB2.  As
   // such, we currently just scan for obviously identical instructions in an
-  // identical order, possibly separated by the same number of non-identical
-  // instructions.
+  // identical order.
   BasicBlock *BB1 = BI->getSuccessor(0); // The true destination.
   BasicBlock *BB2 = BI->getSuccessor(1); // The false destination
 
@@ -1500,7 +1467,7 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
     while (isa<DbgInfoIntrinsic>(I2))
       I2 = &*BB2_Itr++;
   }
-  if (isa<PHINode>(I1))
+  if (isa<PHINode>(I1) || !I1->isIdenticalToWhenDefined(I2))
     return false;
 
   BasicBlock *BIParent = BI->getParent();
@@ -1526,122 +1493,75 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
     // terminator. Let the loop below handle those 2 cases.
   }
 
-  // Count how many instructions were not hoisted so far. There's a limit on how
-  // many instructions we skip, serving as a compilation time control as well as
-  // preventing excessive increase of life ranges.
-  unsigned NumSkipped = 0;
-  
-  // Record if any non-hoisted instruction contains side-effects, as it could
-  // make it illegal to reorder some instructions across.
-  bool ForceNoReadMemOrSideEffectsBB1 = false;
-  bool ForceNoReadMemOrSideEffectsBB2 = false;
-
-  // Record if any non-hoisted instructions does not necessarily transfer
-  // control to the next instruction. Then we can hoist only instrucions which
-  // are safe to speculate.
-  bool ForceNoSpeculationBB1 = false;
-  bool ForceNoSpeculationBB2 = false;
-
-  for (;;) {
+  do {
     // If we are hoisting the terminator instruction, don't move one (making a
     // broken BB), instead clone it, and remove BI.
-    if (I1->isTerminator() || I2->isTerminator()) {
-      // If any instructions remain in the block, we cannot hoist terminators.
-      if (NumSkipped || !I1->isIdenticalToWhenDefined(I2))
-        return Changed;
+    if (I1->isTerminator())
       goto HoistTerminator;
-    }
 
-    if (I1->isIdenticalToWhenDefined(I2)) {
-      // Hoisting token-returning instructions would obscure the origin.
-      if (I1->getType()->isTokenTy())
+    // If we're going to hoist a call, make sure that the two instructions we're
+    // commoning/hoisting are both marked with musttail, or neither of them is
+    // marked as such. Otherwise, we might end up in a situation where we hoist
+    // from a block where the terminator is a `ret` to a block where the terminator
+    // is a `br`, and `musttail` calls expect to be followed by a return.
+    auto *C1 = dyn_cast<CallInst>(I1);
+    auto *C2 = dyn_cast<CallInst>(I2);
+    if (C1 && C2)
+      if (C1->isMustTailCall() != C2->isMustTailCall())
         return Changed;
 
-      // Even if the instructions are identical, it may not be safe to hoist
-      // them if we have skipped over instructions with side effects or their
-      // operands weren't hoisted.
-      if (!isSafeToHoistInstr(I1, ForceNoReadMemOrSideEffectsBB1, ForceNoSpeculationBB1) ||
-          !isSafeToHoistInstr(I2, ForceNoReadMemOrSideEffectsBB2, ForceNoSpeculationBB2))
-        return Changed;
+    if (!TTI.isProfitableToHoist(I1) || !TTI.isProfitableToHoist(I2))
+      return Changed;
 
-      // If we're going to hoist a call, make sure that the two instructions
-      // we're commoning/hoisting are both marked with musttail, or neither of
-      // them is marked as such. Otherwise, we might end up in a situation where
-      // we hoist from a block where the terminator is a `ret` to a block where
-      // the terminator is a `br`, and `musttail` calls expect to be followed by
-      // a return.
-      auto *C1 = dyn_cast<CallInst>(I1);
-      auto *C2 = dyn_cast<CallInst>(I2);
-      if (C1 && C2)
-        if (C1->isMustTailCall() != C2->isMustTailCall())
-          return Changed;
-
-      if (!TTI.isProfitableToHoist(I1) || !TTI.isProfitableToHoist(I2))
+    // If any of the two call sites has nomerge attribute, stop hoisting.
+    if (const auto *CB1 = dyn_cast<CallBase>(I1))
+      if (CB1->cannotMerge())
+        return Changed;
+    if (const auto *CB2 = dyn_cast<CallBase>(I2))
+      if (CB2->cannotMerge())
         return Changed;
 
-      // If any of the two call sites has nomerge attribute, stop hoisting.
-      if (const auto *CB1 = dyn_cast<CallBase>(I1))
-        if (CB1->cannotMerge())
-          return Changed;
-      if (const auto *CB2 = dyn_cast<CallBase>(I2))
-        if (CB2->cannotMerge())
-          return Changed;
-
-      if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
-        assert(isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
-        // The debug location is an integral part of a debug info intrinsic
-        // and can't be separated from it or replaced.  Instead of attempting
-        // to merge locations, simply hoist both copies of the intrinsic.
-        BIParent->getInstList().splice(BI->getIterator(), BB1->getInstList(),
-                                       I1);
-        BIParent->getInstList().splice(BI->getIterator(), BB2->getInstList(),
-                                       I2);
-      } else {
-        // For a normal instruction, we just move one to right before the
-        // branch, then replace all uses of the other with the first.  Finally,
-        // we remove the now redundant second instruction.
-        BIParent->getInstList().splice(BI->getIterator(), BB1->getInstList(),
-                                       I1);
-        if (!I2->use_empty())
-          I2->replaceAllUsesWith(I1);
-        I1->andIRFlags(I2);
-        unsigned KnownIDs[] = {LLVMContext::MD_tbaa,
-                               LLVMContext::MD_range,
-                               LLVMContext::MD_fpmath,
-                               LLVMContext::MD_invariant_load,
-                               LLVMContext::MD_nonnull,
-                               LLVMContext::MD_invariant_group,
-                               LLVMContext::MD_align,
-                               LLVMContext::MD_dereferenceable,
-                               LLVMContext::MD_dereferenceable_or_null,
-                               LLVMContext::MD_mem_parallel_loop_access,
-                               LLVMContext::MD_access_group,
-                               LLVMContext::MD_preserve_access_index};
-        combineMetadata(I1, I2, KnownIDs, true);
-
-        // I1 and I2 are being combined into a single instruction.  Its debug
-        // location is the merged locations of the original instructions.
-        I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
-
-        I2->eraseFromParent();
-      }
+    if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
+      assert (isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
+      // The debug location is an integral part of a debug info intrinsic
+      // and can't be separated from it or replaced.  Instead of attempting
+      // to merge locations, simply hoist both copies of the intrinsic.
+      BIParent->getInstList().splice(BI->getIterator(),
+                                     BB1->getInstList(), I1);
+      BIParent->getInstList().splice(BI->getIterator(),
+                                     BB2->getInstList(), I2);
       Changed = true;
-      ++NumHoistCommonInstrs;
     } else {
-      if (NumSkipped >= HoistCommonSkipLimit)
-        return Changed;
-      // We are about to skip over a pair of non-identical instructions. Record
-      // if any of them has side effects.
-      if (I1->mayHaveSideEffects())
-        ForceNoReadMemOrSideEffectsBB1 = true;
-      if (I2->mayHaveSideEffects())
-        ForceNoReadMemOrSideEffectsBB2 = true;
-      if (!isGuaranteedToTransferExecutionToSuccessor(I1))
-        ForceNoSpeculationBB1 = true;
-      if (!isGuaranteedToTransferExecutionToSuccessor(I2))
-        ForceNoSpeculationBB2 = true;
-      ++NumSkipped;
+      // For a normal instruction, we just move one to right before the branch,
+      // then replace all uses of the other with the first.  Finally, we remove
+      // the now redundant second instruction.
+      BIParent->getInstList().splice(BI->getIterator(),
+                                     BB1->getInstList(), I1);
+      if (!I2->use_empty())
+        I2->replaceAllUsesWith(I1);
+      I1->andIRFlags(I2);
+      unsigned KnownIDs[] = {LLVMContext::MD_tbaa,
+                             LLVMContext::MD_range,
+                             LLVMContext::MD_fpmath,
+                             LLVMContext::MD_invariant_load,
+                             LLVMContext::MD_nonnull,
+                             LLVMContext::MD_invariant_group,
+                             LLVMContext::MD_align,
+                             LLVMContext::MD_dereferenceable,
+                             LLVMContext::MD_dereferenceable_or_null,
+                             LLVMContext::MD_mem_parallel_loop_access,
+                             LLVMContext::MD_access_group,
+                             LLVMContext::MD_preserve_access_index};
+      combineMetadata(I1, I2, KnownIDs, true);
+
+      // I1 and I2 are being combined into a single instruction.  Its debug
+      // location is the merged locations of the original instructions.
+      I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
+
+      I2->eraseFromParent();
+      Changed = true;
     }
+    ++NumHoistCommonInstrs;
 
     I1 = &*BB1_Itr++;
     I2 = &*BB2_Itr++;
@@ -1654,9 +1574,9 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
       while (isa<DbgInfoIntrinsic>(I2))
         I2 = &*BB2_Itr++;
     }
-  }
+  } while (I1->isIdenticalToWhenDefined(I2));
 
-  return Changed;
+  return true;
 
 HoistTerminator:
   // It may not be possible to hoist an invoke.

diff  --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
deleted file mode 100644
index 0e1a171dd9ebf..0000000000000
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
+++ /dev/null
@@ -1,97 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=0 %s | FileCheck %s --check-prefix=LIMIT0
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=1 %s | FileCheck %s --check-prefix=LIMIT1
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=2 %s | FileCheck %s --check-prefix=LIMIT2
-
-define void @f(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; LIMIT0-LABEL: @f(
-; LIMIT0-NEXT:  entry:
-; LIMIT0-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT0-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT0:       if.then:
-; LIMIT0-NEXT:    call void @no_side_effects0()
-; LIMIT0-NEXT:    [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT0-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT0-NEXT:    [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT0-NEXT:    br label [[IF_END:%.*]]
-; LIMIT0:       if.else:
-; LIMIT0-NEXT:    call void @no_side_effects1()
-; LIMIT0-NEXT:    [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT0-NEXT:    [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; LIMIT0-NEXT:    [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; LIMIT0-NEXT:    br label [[IF_END]]
-; LIMIT0:       if.end:
-; LIMIT0-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT0-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT0-NEXT:    ret void
-;
-; LIMIT1-LABEL: @f(
-; LIMIT1-NEXT:  entry:
-; LIMIT1-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT1-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT1:       if.then:
-; LIMIT1-NEXT:    call void @no_side_effects0()
-; LIMIT1-NEXT:    [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT1-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT1-NEXT:    [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT1-NEXT:    br label [[IF_END:%.*]]
-; LIMIT1:       if.else:
-; LIMIT1-NEXT:    call void @no_side_effects1()
-; LIMIT1-NEXT:    [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT1-NEXT:    [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; LIMIT1-NEXT:    [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; LIMIT1-NEXT:    br label [[IF_END]]
-; LIMIT1:       if.end:
-; LIMIT1-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT1-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT1-NEXT:    ret void
-;
-; LIMIT2-LABEL: @f(
-; LIMIT2-NEXT:  entry:
-; LIMIT2-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT2-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT2-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT2:       if.then:
-; LIMIT2-NEXT:    call void @no_side_effects0()
-; LIMIT2-NEXT:    [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT2-NEXT:    [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT2-NEXT:    br label [[IF_END:%.*]]
-; LIMIT2:       if.else:
-; LIMIT2-NEXT:    call void @no_side_effects1()
-; LIMIT2-NEXT:    [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT2-NEXT:    [[V:%.*]] = add i16 [[SUB]], [[TMP1]]
-; LIMIT2-NEXT:    br label [[IF_END]]
-; LIMIT2:       if.end:
-; LIMIT2-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT2-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT2-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @no_side_effects0()
-  %add = add nsw i16 %0, 1
-  %1 = load i16, ptr %m, align 2
-  %u = add i16 %add, %1
-  br label %if.end
-
-if.else:
-  %2 = load i16, ptr %b, align 2
-  call void @no_side_effects1()
-  %sub = sub nsw i16 %2, 1
-  %3 = load i16, ptr %m, align 2
-  %v = add i16 %sub, %3
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-declare void @side_effects0()
-declare void @side_effects1()
-declare void @no_side_effects0() readonly nounwind willreturn
-declare void @no_side_effects1() readonly nounwind willreturn

diff  --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
deleted file mode 100644
index 400f981cf552e..0000000000000
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
+++ /dev/null
@@ -1,277 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' %s | FileCheck %s
-
-;; Check that the two loads are hoisted to the common predecessor, skipping
-;; over the add/sub instructions.
-
-define void @f0(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m,  ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f0(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; CHECK-NEXT:    [[TMP2:%.*]] = add i16 [[SUB]], 3
-; CHECK-NEXT:    [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  %add = add nsw i16 %0, 1
-  %1 = load i16, ptr %m, align 2
-  %u = add i16 %add, %1
-  br label %if.end
-
-if.else:
-  %2 = load i16, ptr %b, align 2
-  %sub = sub nsw i16 %2, 1
-  %3 = load i16, ptr %m, align 2
-  %4 = add i16 %sub, 3
-  %v = add i16 %sub, %4
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-
-;; Check some instructions (e.g. add) can be reordered across instructions with side
-;; effects, while others (e.g. load) can't.
-define void @f2(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f2(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    call void @side_effects0()
-; CHECK-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    call void @no_side_effects0()
-; CHECK-NEXT:    [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; CHECK-NEXT:    [[V:%.*]] = add i16 [[ADD_0]], [[TMP2]]
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @side_effects0()
-  %add.0 = add nsw i16 %0, 1
-  %1 = load i16, ptr %m, align 2
-  %u = add i16 %add.0, %1
-  br label %if.end
-
-if.else:
-  %2 = load i16, ptr %b, align 2
-  call void @no_side_effects0()
-  %add.1 = add nsw i16 %2, 1
-  %3 = load i16, ptr %m, align 2
-  %v = add i16 %add.1, %3
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-
-;; Check indeed it was the side effects that prevented hoisting the load
-;; in the previous test.
-define void @f3(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f3(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT:    [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    call void @no_side_effects0()
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    call void @no_side_effects1()
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @no_side_effects0()
-  %add.0 = add nsw i16 %0, 1
-  %1 = load i16, ptr %m, align 2
-  %u = add i16 %add.0, %1
-  br label %if.end
-
-if.else:
-  %2 = load i16, ptr %b, align 2
-  call void @no_side_effects1()
-  %add.1 = add nsw i16 %2, 1
-  %3 = load i16, ptr %m, align 2
-  %v = add i16 %add.1, %3
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-;; Check some instructions (e.g. sdiv) are not speculatively executed.
-
-;; Division by non-zero constant OK to speculate ...
-define void @f4(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f4(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    [[DIV_0:%.*]] = sdiv i16 [[TMP0]], 2
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    call void @side_effects0()
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    call void @side_effects1()
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @side_effects0()
-  %div.0 = sdiv i16 %0, 2
-  %u = add i16 %div.0, %0
-  br label %if.end
-
-if.else:
-  %1 = load i16, ptr %b, align 2
-  call void @side_effects1()
-  %div.1 = sdiv i16 %1, 2
-  %v = add i16 %div.1, %1
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-;; ... but not a general division ...
-define void @f5(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f5(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    call void @side_effects0()
-; CHECK-NEXT:    [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    call void @side_effects1()
-; CHECK-NEXT:    [[DIV_1:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT:    [[V:%.*]] = add i16 [[DIV_1]], [[TMP0]]
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT:    store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @side_effects0()
-  %div.0 = sdiv i16 211, %0
-  %u = add i16 %div.0, %0
-  br label %if.end
-
-if.else:
-  %1 = load i16, ptr %b, align 2
-  call void @side_effects1()
-  %div.1 = sdiv i16 211, %1
-  %v = add i16 %div.1, %1
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-;; ... and it's also OK to hoist the division when there's no speculation happening.
-define void @f6(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f6(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT:    [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT:    [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK:       if.then:
-; CHECK-NEXT:    call void @no_side_effects0()
-; CHECK-NEXT:    br label [[IF_END:%.*]]
-; CHECK:       if.else:
-; CHECK-NEXT:    call void @no_side_effects1()
-; CHECK-NEXT:    br label [[IF_END]]
-; CHECK:       if.end:
-; CHECK-NEXT:    store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT:    ret void
-;
-entry:
-  br i1 %c, label %if.then, label %if.else
-
-if.then:
-  %0 = load i16, ptr %b, align 2
-  call void @no_side_effects0()
-  %div.0 = sdiv i16 211, %0
-  %u = add i16 %div.0, %0
-  br label %if.end
-
-if.else:
-  %1 = load i16, ptr %b, align 2
-  call void @no_side_effects1()
-  %div.1 = sdiv i16 211, %1
-  %v = add i16 %div.1, %1
-  br label %if.end
-
-if.end:
-  %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
-  store i16 %uv, ptr %d, align 2
-  ret void
-}
-
-declare void @side_effects0()
-declare void @side_effects1()
-declare void @no_side_effects0() readonly nounwind willreturn
-declare void @no_side_effects1() readonly nounwind willreturn


        


More information about the llvm-commits mailing list