Discrepancy between Debug and Release+Asserts versions of Clang/LLVM

Hello,

I am editing the LowerTypeTests pass in LLVM, and part of my additions include the following 3 lines of code:

// newTypeName is a std::string
MDString* newMD = MDString::get(M.getContext(), newTypeName);
ArrayRef<Metadata*> mdArray {ConstantInt::get(Int64Ty, 0), newMD};
auto* node = MDTuple::get(M.getContext(), mdArray);

Thus far, I have been developing on a version of Clang with debugging symbols enabled, and this code has worked. However, on the Release+Asserts version, the third line results in a segmentation fault. The backtrace for the error, up to the method I’m working on, is as follows:

// signal handling…
#4 0x00005556e48d6120 llvm::ReplaceableMetadataImpl::getOrCreate(llvm::Metadata&) (/home/sjessu/build-no-debug/bin/clang-10+0x2787120)
#5 0x00005556e48db8c2 llvm::MetadataTracking::track(void*, llvm::Metadata&, llvm::PointerUnion<llvm::MetadataAsValue*, llvm::Metadata*>) (/home/sjessu/build-no-debug/bin/clang-10+0x278c8c2)
#6 0x00005556e48dbc12 llvm::MDNode::MDNode(llvm::LLVMContext&, unsigned int, llvm::Metadata::StorageType, llvm::ArrayRefllvm::Metadata*, llvm::ArrayRefllvm::Metadata*) (/home/sjessu/build-no-debug/bin/clang-10+0x278cc12)
#7 0x00005556e48dd70d llvm::MDTuple::getImpl(llvm::LLVMContext&, llvm::ArrayRefllvm::Metadata*, llvm::Metadata::StorageType, bool) (/home/sjessu/build-no-debug/bin/clang-10+0x278e70d)
#8 0x00005556e4a4b293 (anonymous namespace)::LowerTypeTestsModule::lower() (.part.1843) (/home/sjessu/build-no-debug/bin/clang-10+0x28fc293)

Crucially, this error does not occur in the Debug version, so I am lost as to how to diagnose it. I have checked that newMD is non-null and contains the value I expect. Are there any discrepancies in the Debug version as compared to the Release + Asserts version that would cause such an error? Thanks for your help!

Best,
Shishir Jessu

Hello,

I am editing the LowerTypeTests pass in LLVM, and part of my additions include the following 3 lines of code:

// newTypeName is a std::string
MDString* newMD = MDString::get(M.getContext(), newTypeName);
ArrayRef<Metadata*> mdArray {ConstantInt::get(Int64Ty, 0), newMD};
auto* node = MDTuple::get(M.getContext(), mdArray);

If I had to hazard a guess, it’s that mdArray is left with dangling pointers to an array (initializer list) that ceases to exist once the ‘;’ on line 2 is reached. So when the elements the ArrayRef references are accessed, it gets crashy.

I’d say try writing it like:

auto *node = MDTuple::get(M.getContext(), {ConstantInt::get(Int64Ty, 0), newMD});

Also try testing with ASan or MSan, perhaps?

I agree that the ArrayRef is likely the issue. I’ve debugged a crash caused by a temporary ArrayRef like that a couple times.

Either do what David suggested or use a normal array:

Metadata *mdArray[] = {ConstantInt::get(Int64Ty, 0), newMD};

Hi David and Craig,

That turned out to be the issue exactly! I used a normal Metadata* array as suggested and now the pass works as it does in the Debug version. Thanks for your quick responses!

Just for curiosity’s sake, any ideas as to why this error would only appear in the Release+Asserts version of LLVM?

Best,
Shishir Jessu

Best guess, the ArrayRef ends up containing a pointer to a location on the stack that was used for the std::initializer_list. Debug builds don’t have optimizations enabled so that space probably wasn’t reused. Release+Asserts was probably more aggressive about reusing that stack space for something else.