Jump Theading/GVN bug

Hal Finkel hfinkel at anl.gov
Sun Feb 22 08:22:57 PST 2015


Hi Katya,

My understanding of our general policy is that, if a pass cannot handle invalid unreachable code, it is responsible for avoiding it itself. Consider, for example the SLP vectorizer, which contains this:

  if (!DT->isReachableFromEntry(BB)) {
    // Don't go into unreachable blocks. They may contain instructions with
    // dependency cycles which confuse the final scheduling.
    DEBUG(dbgs() << "SLP: bundle in unreachable block.\n");
    newTreeEntry(VL, false);
    return;
  }

I suspect that GVN should also ignore unreachable code in a similar way.

 -Hal

----- Original Message -----
> From: "Katya Romanova" <Katya_Romanova at playstation.sony.com>
> To: llvm-commits at cs.uiuc.edu
> Sent: Sunday, February 22, 2015 3:34:58 AM
> Subject: Jump Theading/GVN bug
> 
> Hello,
> 
> I encountered a problem triggered by Jump-Threading optimization.
> This pass is creating an unreachable block with an instruction that
> is not well formed, which then causes the subsequent GVN pass to
> enter an infinite loop.
> 
> 
> 
> Here are the details:
> 
> Consider the following small testcase. I reduced it from a huge
> project that was failing during LTO. Unfortunately, I was not given
> any source files, only the bitcode.
> 
> 
> 
> -----------------------------------------------------------------------------
> 
> small.ll
> 
> ----------------------------------------------------------------
> 
> %"class.ls_file::Path" = type { [1024 x i8], i8*, i8*, i8*}
> 
> 
> 
> ; Function Attrs: nounwind sspstrong uwtable
> 
> define void @foo(%"class.ls_file::Path"* %this, i8* nocapture
> readonly %pPath) #0 {
> 
> %1 = load i8* %pPath, align 1
> 
> switch i8 %1, label %.loopexit.i.i [
> 
> i8 92, label %2
> 
> i8 47, label %2
> 
> ]
> 
> 
> 
> ; <label>:2 ; preds = %0, %0
> 
> %3 = load i8* %pPath, align 1
> 
> %4 = icmp eq i8 %3, 46
> 
> br i1 %4, label %.critedge.i.i, label %.outer.i.i
> 
> 
> 
> .critedge.i.i: ; preds = %.critedge.i.i, %.critedge.i.i, %2
> 
> %.0.i.i = phi i8* [ %pPath, %2 ], [ %5, %.critedge.i.i ], [ %5,
> %.critedge.i.i ]
> 
> %5 = getelementptr inbounds i8* %.0.i.i, i64 1
> 
> %6 = load i8* %5, align 1
> 
> switch i8 %6, label %.outer.i.i [
> 
> i8 92, label %.critedge.i.i
> 
> i8 47, label %.critedge.i.i
> 
> ]
> 
> 
> 
> .outer.i.i: ; preds = %.critedge.i.i, %2
> 
> %.2.ph2.i.i = phi i8* [ %5, %.critedge.i.i ], [ %pPath, %2 ]
> 
> %7 = load i8* %.2.ph2.i.i, align 1
> 
> %8 = icmp eq i8 %7, 0
> 
> br label %.loopexit.i.i
> 
> 
> 
> .loopexit.i.i: ; preds = %7
> 
> ret void
> 
> }
> 
> 
> 
> 
> 
> 
> If you run GVN after jump threading, opt will go into infinite
> recursion.
> 
> 
> 
> opt -jump-threading small.ll -o test2.o
> 
> opt -gvn test2.o -o test3.o
> 
> Segmentation fault (core dumped)
> 
> 
> 
> 
> 
> Here’s why it’s happening:
> 
> 
> 
> Jump threading transforms this block:
> 
> 
> 
> .critedge.i.i: ; preds = %.critedge.i.i, %.critedge.i.i, %2
> 
> %.0.i.i = phi i8* [ %pPath, %2 ], [ %5, %.critedge.i.i ], [ %5,
> %.critedge.i.i ]
> 
> %5 = getelementptr inbounds i8* %.0.i.i, i64 1
> 
> %6 = load i8* %5, align 1
> 
> switch i8 %6, label %.outer.i.i [
> 
> i8 92, label %.critedge.i.i
> 
> i8 47, label %.critedge.i.i
> 
> ]
> 
> 
> 
> into the following block:
> 
> 
> 
> .critedge.i.i: ; preds = %.critedge.i.i, %.critedge.i.i
> 
> %2 = getelementptr inbounds i8* %2, i64 1
> 
> %3 = load i8* %2, align 1
> 
> switch i8 %3, label %.outer.i.i [
> 
> i8 92, label %.critedge.i.i
> 
> i8 47, label %.critedge.i.i
> 
> ]
> 
> 
> 
> Note that the block .critedge.i.i is now unreachable and it contains
> the following instruction:
> 
> %2 = getelementptr inbounds i8* %2, i64 1
> 
> 
> 
> This instruction doesn’t appear to be well formed. However, one could
> argue that it is – formally %2 def does dominate all %2 uses because
> both are located in an unreachable block, so every path to the use
> is passing through the def (because there are 0 paths like that).
> 
> That is likely the reason why –verify doesn’t complain about it.
> 
> 
> 
> 
> 
> And this is where the problem actually happens:
> 
> When -gvn pass is invoked afterwards, it goes into an infinite
> recursion in function ‘ PHITranslateSubExpr ’ (located in
> PHITransAddr.cpp). When processing a GEP instruction (%2), this
> function calls itself recursively with its first operand (which is
> also %2).
> 
> 
> 
> // Handle getelementptr with at least one PHI translatable operand.
> 
> if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
> 
> SmallVector<Value*, 8> GEPOps;
> 
> bool AnyChanged = false;
> 
> for (unsigned i = 0, e = GEP->getNumOperands(); i != e; ++i) {
> 
> Value *GEPOp = PHITranslateSubExpr(GEP->getOperand(i), CurBB, PredBB,
> DT); // <- it goes into infinite recursion here.
> 
> if (GEPOp == 0) return 0;
> 
> 
> 
> There are several different ways to fix this problem.
> 
> 
> 
> (1) We have to remove the unreachable code immediately after or
> during Jump-Threading pass;
> 
> (2) GVN should skip unreachable code;
> 
> (3) GVN shouldn’t recursively invoke the same function with the same
> arguments.
> 
> 
> 
> 
> 
> Here is an extra check that implements the 3 rd approach. Please
> review.
> 
> 
> 
> ===================================================================
> 
> --- PHITransAddr.cpp (revision 229821)
> 
> +++ PHITransAddr.cpp (working copy)
> 
> @@ -217,10 +217,13 @@
> 
> SmallVector<Value*, 8> GEPOps;
> 
> bool AnyChanged = false;
> 
> for (unsigned i = 0, e = GEP->getNumOperands(); i != e; ++i) {
> 
> - Value *GEPOp = PHITranslateSubExpr(GEP->getOperand(i), CurBB,
> PredBB, DT);
> 
> + Value *Operand= GEP->getOperand(i);
> 
> + if (GEP==Operand)
> 
> + continue;
> 
> + Value *GEPOp = PHITranslateSubExpr(Operand, CurBB, PredBB, DT);
> 
> if (!GEPOp) return nullptr;
> 
> 
> 
> - AnyChanged |= GEPOp != GEP->getOperand(i);
> 
> + AnyChanged |= GEPOp != Operand;
> 
> GEPOps.push_back(GEPOp);
> 
> }
> 
> 
> 
> ===================================================================
> 
> One thing to note here is – unreachable code, if not removed, could
> push invalid code through subsequent passes. This invalid code will
> not be caught by the verifier. If these passes are not paranoid
> enough, the bad code may cause all sorts of issues. The case above
> is one example.
> 
> 
> 
> 
> 
> Katya.
> 
> 
> 
> 
> 
> 
> 
> 
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
> 

-- 
Hal Finkel
Assistant Computational Scientist
Leadership Computing Facility
Argonne National Laboratory




More information about the llvm-commits mailing list