https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=276170

Dimitry Andric <[email protected]> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|New                         |In Progress
                 CC|                            |[email protected]

--- Comment #44 from Dimitry Andric <[email protected]> ---
After some more investigation I found the cause, which is (again) the fact that
assertions are _disabled_ on stable and release branches.

This particular "error" is occurring in
contrib/llvm-project/clang/lib/CodeGen/CodeGenModule.cpp, on line 422:

   414    if (CodeGenOpts.hasProfileClangUse()) {
   415      auto ReaderOrErr = llvm::IndexedInstrProfReader::create(
   416          CodeGenOpts.ProfileInstrumentUsePath, *FS,
   417          CodeGenOpts.ProfileRemappingFile);
   418      // We're checking for profile read errors in CompilerInvocation, so
if
   419      // there was an error it should've already been caught. If it
hasn't been
   420      // somehow, trip an assertion.
   421      assert(ReaderOrErr);
   422      PGOReader = std::move(ReaderOrErr.get());
   423    }

llvm::IndexedInstrProfReader::create() returns a llvm::Expected<> instance,
which is similar to std::expected: it can contain either a "good" return value,
or an error.

In some llvm build configurations, when you attempt to use the return value
without checking whether it contains an error, you get "Expected<T> must be
checked before access or destruction". This occurs even if the Expected<>
object contains a valid value, in which case you get "Expected<T> value was in
success state".

When llvm is built with assertions (WITH_LLVM_ASSERTIONS, which is on by
default on -CURRENT, but not on stable or release branches), the assert()
statement on line 421 is calling llvm::Expected<>::operator bool(), which
checks whether the contained object is valid, and if so it resets the checked
state:

  /// Bool conversion. Returns true if this Error is in a failure state,
  /// and false if it is in an accept state. If the error is in a Success state
  /// it will be considered checked.
  explicit operator bool() {
    setChecked(getPtr() == nullptr);
    return getPtr() != nullptr;
  }

Then in line 422 the contained object is moved out of the 'ReaderOrErr'
variable, into the 'PGOReader' variable. Finally, the 'ReaderOrErr' variable,
which is now empty, is destroyed.

However, when llvm is built without assertions (WITHOUT_LLVM_ASSERTIONS), the
assert() statement on line 421 does nothing, and the  'ReaderOrErr' variable is
unaffected. Then in line 422, llvm::Expected<>::get() is called:

  /// Returns a reference to the stored T value.
  reference get() {
    assertIsChecked();
    return *getStorage();
  }

The first thing get() does is looking if the Expected<> object was checked, and
if not, it prints "Expected<T> must be checked before access or destruction"
and the whole program dies.

Finally, assertIsChecked() is only really doing something if the global llvm
macro LLVM_ENABLE_ABI_BREAKING_CHECKS is defined:

  void assertIsChecked() const {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
    if (LLVM_UNLIKELY(Unchecked))
      fatalUncheckedExpected();
#endif
  }

In the upstream build system, LLVM_ENABLE_ABI_BREAKING_CHECKS is typically 0
when assertions are off, and 1 when assertions are on. In the FreeBSD case,
this use to always be defined as 1 in
lib/clang/include/llvm/Config/abi-breaking.h, until base 1c83996beda7 ("Adjust
LLVM_ENABLE_ABI_BREAKING_CHECKS depending on NDEBUG"):

    15  /* Define to enable checks that alter the LLVM C++ ABI */
    16  #ifdef NDEBUG
    17  #define LLVM_ENABLE_ABI_BREAKING_CHECKS 0
    18  #else
    19  #define LLVM_ENABLE_ABI_BREAKING_CHECKS 1
    20  #endif

Unfortunately 14.2 does not have this commit, so the combination of
MK_LLVM_ASSERTIONS=no and LLVM_ENABLE_ABI_BREAKING_CHECKS=1 leads to the
scenario describe above: the assert() statement in CodeGenModule.cpp line 421
does nothing, but ReaderOrErr.get() still performs the check and aborts.

I merged base 1c83996beda7 to stable/14 in base 86de9cd1f1b5, and to stable/13
in base 44be5a00bedd, but this is unfortunately not available in 14.2-RELEASE
or 13.4-RELEASE, so for those releases there isn't much I can do except to try
to roll it into a Erratum.

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to