[PATCH] D55974: [GVN] Force a sequence point when comparing two DenseMap values.

Matt Davis via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 20 17:43:15 PST 2018


mattd created this revision.
mattd added a reviewer: t.p.northover.

This patch forces a sequence point when comparing the values from a DenseMap.
In particular, there is a comparison in GVN::performScalarPRE that accesses the
same DenseMap via two different keys in the same expression.

This patch preserves the intended semantics, but forces a sequence  point and store of
the map's values.   Since DenseMap does not invalidate it's pointers, it looks like
we might want to fix how we present the conditional, in particular, what this patch solves.

I'm opening this patch up, mainly for discussion.  I realize a decent reproducer test is probably
warranted, but I have been unable to create a reliable reproducer.

Backstory
---------

I was investigating an ICE (segfault accessing a DenseMap item).  This failure happened periodically, with no apparent reason and only on a Windows build of LLVM (from October 2018).

After looking into the crashes (multiple core files) and running DynamoRio, the cores and DynamoRio (DR) log pointed to the same code in `GVN::performScalarPRE()`. The values in the map are unsigned integers, the keys are `llvm::BasicBlock*`.  Our test case that triggered this warning and periodic crash is rather involved.  But the problematic line looks to be:

GVN.cpp: Line 2197

  if (BlockRPONumber[P] >= BlockRPONumber[CurrentBlock] &&

Just to test things out, I cooked up this patch (for our llvm branch that was breaking). DynamoRio stopped warning of the issue, and the test didn't seem to crash after 1000+ runs.

My investigation was on an older version of LLVM, (source from October this year). What it looks like was occurring is the following, and the assembly from the latest pull of llvm today seems to confirm this might still be an issue; however, I have not witnessed the crash on more recent builds. Of course the asm in question is generated from the host compiler on that Windows box (not clang), but it hints that we might want to consider how we access the BlockRPONumber map in this conditional (line 2197, listed above).  In any case, I don't think the host compiler is wrong, rather I think it is pointing out a possibly latent bug in llvm.

1. There is no sequence point for the `>=` operation.

2. A call to a `DenseMapBase::operator[]` can have the side effect of the map reallocating a larger store (more Buckets, via a call to `DenseMap::grow`).

3. It seems perfectly legal for a host compiler to generate assembly that stores the result of a call to `operator[]` on the stack (that's what my host compile of GVN.cpp is doing) .  A second call to `operator[]` //might// encourage the map to 'grow' thus making any pointers to the map's store invalid.  The `>=` compares the first and second values. If the first happens to be a pointer produced from operator[], it could be invalid when dereferenced at the time of comparison.

In any case, I do think the safest thing to do is implement this patch.  It's simple and adheres to the intended semantics.
Now, this could be a red herring (e.g., perhaps performScalarPRE is called and GVN::assignBlockRPONumber() was never called).
But after this patch, the error and warnings seem to go away on that problematic branch of llvm.

The assembly generated from the Window's host compiler does show the result of the first access to the map via `operator[]` produces a pointer to an unsigned int.  And that pointer is being stored on  the stack.  If a second call to the map (which does occur) causes the map to grow, that address (on the stack) is now invalid.


https://reviews.llvm.org/D55974

Files:
  lib/Transforms/Scalar/GVN.cpp


Index: lib/Transforms/Scalar/GVN.cpp
===================================================================
--- lib/Transforms/Scalar/GVN.cpp
+++ lib/Transforms/Scalar/GVN.cpp
@@ -2195,7 +2195,9 @@
     // It is not safe to do PRE when P->CurrentBlock is a loop backedge, and
     // when CurInst has operand defined in CurrentBlock (so it may be defined
     // by phi in the loop header).
-    if (BlockRPONumber[P] >= BlockRPONumber[CurrentBlock] &&
+    const auto PNum = BlockRPONumber[P];
+    const auto CNum = BlockRPONumber[CurrentBlock];
+    if (PNum >= CNum &&
         llvm::any_of(CurInst->operands(), [&](const Use &U) {
           if (auto *Inst = dyn_cast<Instruction>(U.get()))
             return Inst->getParent() == CurrentBlock;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D55974.179206.patch
Type: text/x-patch
Size: 748 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20181221/a4a651d5/attachment.bin>


More information about the llvm-commits mailing list