This is an archive of the discontinued LLVM Phabricator instance.

[NewGVN] Don't add virtual instructions as users.
AbandonedPublic

Authored by bryant on May 30 2017, 9:43 AM.

Details

Summary

I see the following crash:

16:10:04 ~/3rd/llvm/test> opt -newgvn CodeGen/X86/multiple-loop-post-inc.ll 
WARNING: You're attempting to print out a bitcode file.
This is inadvisable as it may cause display problems. If
you REALLY want to taste LLVM bitcode first-hand, you
can force output with the `-f' option.

opt: /llvm/lib/Transforms/Scalar/NewGVN.cpp:734: unsigned int (anonymous namespace)::NewGVN::InstrToDFSNum(const llvm::Value *) const: Assertion `isa<Instruction>(V) && "This should not be used for MemoryAccesses"' failed.
#0 0x00007fe792dac8c9 llvm::sys::PrintStackTrace(llvm::raw_ostream&) /llvm/build/../lib/Support/Unix/Signals.inc:398:11
#1 0x00007fe792dacae9 PrintStackTraceSignalHandler(void*) /llvm/build/../lib/Support/Unix/Signals.inc:462:1
#2 0x00007fe792da99a7 llvm::sys::RunSignalHandlers() /llvm/lib/Support/Signals.cpp:0:5
#3 0x00007fe792dace70 SignalHandler(int) /llvm/build/../lib/Support/Unix/Signals.inc:252:1
#4 0x00007fe792083890 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0xf890)
#5 0x00007fe7916d6067 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x35067)
#6 0x00007fe7916d7448 abort (/lib/x86_64-linux-gnu/libc.so.6+0x36448)
#7 0x00007fe7916cf266 (/lib/x86_64-linux-gnu/libc.so.6+0x2e266)
#8 0x00007fe7916cf312 (/lib/x86_64-linux-gnu/libc.so.6+0x2e312)
#9 0x00007fe79328f4e5 (anonymous namespace)::NewGVN::InstrToDFSNum(llvm::Value const*) const /llvm/lib/Transforms/Scalar/NewGVN.cpp:735:12
#10 0x00007fe79329f393 void (anonymous namespace)::NewGVN::touchAndErase<llvm::DenseMap<llvm::Value const*, llvm::SmallPtrSet<llvm::Value*, 2u>, llvm::DenseMapInfo<llvm::Value const*>, llvm::detail::DenseMapPair<llvm::Value const*, llvm::SmallPtrSet<llvm::Value*, 2u> > >, llvm::Value*>(llvm::DenseMap<llvm::Value const*, llvm::SmallPtrSet<llvm::Value*, 2u>, llvm::DenseMapInfo<llvm::Value const*>, llvm::detail::DenseMapPair<llvm::Value const*, llvm::SmallPtrSet<llvm::Value*, 2u> > >&, llvm::Value* const&) /llvm/lib/Transforms/Scalar/NewGVN.cpp:1947:27
#11 0x00007fe79329ea05 (anonymous namespace)::NewGVN::markUsersTouched(llvm::Value*) /llvm/lib/Transforms/Scalar/NewGVN.cpp:1963:1
#12 0x00007fe793294c2a (anonymous namespace)::NewGVN::performCongruenceFinding(llvm::Instruction*, llvm::GVNExpression::Expression const*) /llvm/lib/Transforms/Scalar/NewGVN.cpp:2275:44
#13 0x00007fe793290c15 (anonymous namespace)::NewGVN::valueNumberInstruction(llvm::Instruction*) /llvm/lib/Transforms/Scalar/NewGVN.cpp:2814:5
#14 0x00007fe7932898c6 (anonymous namespace)::NewGVN::iterateTouchedInstructions() /llvm/lib/Transforms/Scalar/NewGVN.cpp:3092:7
#15 0x00007fe793287a70 (anonymous namespace)::NewGVN::runGVN() /llvm/lib/Transforms/Scalar/NewGVN.cpp:3166:3
#16 0x00007fe7932882d7 (anonymous namespace)::NewGVNLegacyPass::runOnFunction(llvm::Function&) /llvm/lib/Transforms/Scalar/NewGVN.cpp:3904:8
#17 0x00007fe794216caf llvm::FPPassManager::runOnFunction(llvm::Function&) /llvm/lib/IR/LegacyPassManager.cpp:1519:27
#18 0x00007fe794216fc5 llvm::FPPassManager::runOnModule(llvm::Module&) /llvm/lib/IR/LegacyPassManager.cpp:1540:16
#19 0x00007fe794217cc8 (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) /llvm/lib/IR/LegacyPassManager.cpp:1596:27
#20 0x00007fe794217286 llvm::legacy::PassManagerImpl::run(llvm::Module&) /llvm/lib/IR/LegacyPassManager.cpp:1699:16
#21 0x00007fe7942188b1 llvm::legacy::PassManager::run(llvm::Module&) /llvm/lib/IR/LegacyPassManager.cpp:1730:3
#22 0x0000000000270e4f (opt+0x270e4f)
#23 0x00007fe7916c2b45 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b45)
#24 0x0000000000244029 (opt+0x244029)
Stack dump:
0.      Program arguments: opt -newgvn CodeGen/X86/multiple-loop-post-inc.ll 
1.      Running pass 'Function Pass Manager' on module 'CodeGen/X86/multiple-loop-post-inc.ll'.
2.      Running pass 'Global Value Numbering' on function '@foo'
Aborted

The problem starts in makePossiblePhiOfOps at line 2522:

2505        Instruction *ValueOp = I->clone();
2506        auto Iter = TempToMemory.end();
2507        if (MemAccess)
2508          Iter = TempToMemory.insert({ValueOp, MemAccess}).first;
2509
2510        for (auto &Op : ValueOp->operands()) {
2511          Op = Op->DoPHITranslation(PHIBlock, PredBB);
2512          // When this operand changes, it could change whether there is a
2513          // leader for us or not.
2514          addAdditionalUsers(Op, I);
2515        }
2516        // Make sure it's marked as a temporary instruction.
2517        AllTempInstructions.insert(ValueOp);
2518        // and make sure anything that tries to add it's DFS number is
2519        // redirected to the instruction we are making a phi of ops
2520        // for.
2521        InstrDFS.insert({ValueOp, IDFSNum});
2522        const Expression *E = performSymbolicEvaluation(ValueOp, Visited);
2523        InstrDFS.erase(ValueOp);
2524        AllTempInstructions.erase(ValueOp);
2525        ValueOp->deleteValue();

It's possible for ValueOp to be added as an additional user of some instruction
if, for instance, its sym eval were delegated to createExpression (which
checkSimplificationResults, which could addAdditionalUsers). Its pointer is kept
in AdditionalUsers[WhateverValueItSimplifiedTo].

The pointer remains after ValueOp deleted, leading to a use-after-free when
touchAndErase is called (frame 10 of the stack dump). In this particular case,
the memory previously occupied by ValueOp was reallocated to a llvm::Constant,
which breaks the implicit assumption by touchAndErase that all AdditionalUsers
are instructions:

void NewGVN::touchAndErase(Map &M, const KeyType &Key) {
  const auto Result = M.find_as(Key);
  if (Result != M.end()) {
    for (const typename Map::mapped_type::value_type Mapped : Result->second)
      TouchedInstructions.set(InstrToDFSNum(Mapped));
      //                      ^^^^^^^ assumes that Mapped is an Intruction.
    M.erase(Result);
  }
}

I'm not sure if this is the right fix (please correct me if it's wrong), but as
far as I can tell, the purpose of AdditionalUsers is to ensure that a def's
use(s) are touched and re-visited if the def itself is altered (simplified or
whatever). Touching requires that the use has an InstrDFS, which means that it
has to be a concrete instruction that actually exists in the function.

ValueOp fails this requirement by virtue of being a parent-less clone.

Diff Detail

Repository
rL LLVM

Event Timeline

bryant created this revision.May 30 2017, 9:43 AM

Adding davide since this was introduced in r303715.

bryant abandoned this revision.May 30 2017, 10:02 AM

This was fixed this morning already :)

Was fixed in r304194.