Skip to content

[MSYS2] Ordered dynamic initialization is not sequenced #55938

Closed
@silverqx

Description

@silverqx

Following simple code example crashes or doesn't work correctly on MSYS2 UCRT64 clang++ and on MSYS2 CLANG64 clang++. All other compilers don't have problem with this, gcc/clang on linux, msvc or clang-cl msvc and also MSYS2 UCRT64 g++.

It crashes because ConstAppInline::LEFT is uninitialized during the Model::m_fillable initialization.

I think that this is happening, here should apply Ordered dynamic initialization , so the initialization should be sequenced:

within a single translation unit, initialization of these variables is always sequenced in exact order their definitions appear in the source code.

The consequence is that you can not initialize inline static data member with Inline const variables at namespace scope variable with external linkage. ☝️

Click to expand!
#include <iostream>
#include <string>
#include <vector>

//#include <QDebug>
//#include <QStringLiteral>

namespace ConstAppInline
{

   // Common chars
//    inline const QChar DOT = QChar('.');

   // Common strings
   inline const std::string LEFT = "left";
//    inline const QString LEFT = "left";

} // namespace ConstAppInline


class Model
{
   inline static std::vector<std::string> m_fillable {ConstAppInline::LEFT};
//    inline static QStringList m_fillable {ConstAppInline::LEFT};

public:
   inline void run() const
   {
       std::cout << m_fillable.size() << "\n";

       for (const auto &s : m_fillable)
           std::cout << s << "\n";

//        qDebug() << m_fillable.size();

//        qDebug() << m_fillable;
   }
};

int main(int /*unused*/, char */*unused*/[])
{
   Model m;
   m.run();

   return 0;
}

The example contains two types, one types from stl library and other for Qt types, in both versions the ConstAppInline::LEFT is uninitialized.

Qt version crashes and the output with stl types is like following:

1

Of course, it should look like this with stl types:

1
left

And like this with Qt types:

1
QList("left")

This is the long-running bug that I'm experiencing with during the development of one of my libraries and hoped that it will be fixed, but it wasn't. The problem exists as long as I remember, from Clang 10/11.

Activity

llvmbot

llvmbot commented on Oct 1, 2023

@llvmbot
Member

@llvm/issue-subscribers-clang-codegen

Following simple code example crashes or doesn't work correctly on `MSYS2 UCRT64 clang++` and on `MSYS2 CLANG64 clang++`. All other compilers don't have problem with this, gcc/clang on linux, msvc or clang-cl msvc and also MSYS2 UCRT64 g++.

It crashes because ConstAppInline::LEFT is uninitialized during the Model::m_fillable initialization.

I think that this is happening, here should apply Ordered dynamic initialization , so the initialization should be sequenced:

> within a single translation unit, initialization of these variables is always sequenced in exact order their definitions appear in the source code.

The consequence is that you can not initialize inline static data member with Inline const variables at namespace scope variable with external linkage. ☝️

<details>
<summary>Click to expand!</summary>

#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

//#include &lt;QDebug&gt;
//#include &lt;QStringLiteral&gt;

namespace ConstAppInline
{

   // Common chars
//    inline const QChar DOT = QChar('.');

   // Common strings
   inline const std::string LEFT = "left";
//    inline const QString LEFT = "left";

} // namespace ConstAppInline


class Model
{
   inline static std::vector&lt;std::string&gt; m_fillable {ConstAppInline::LEFT};
//    inline static QStringList m_fillable {ConstAppInline::LEFT};

public:
   inline void run() const
   {
       std::cout &lt;&lt; m_fillable.size() &lt;&lt; "\n";

       for (const auto &amp;s : m_fillable)
           std::cout &lt;&lt; s &lt;&lt; "\n";

//        qDebug() &lt;&lt; m_fillable.size();

//        qDebug() &lt;&lt; m_fillable;
   }
};

int main(int /*unused*/, char */*unused*/[])
{
   Model m;
   m.run();

   return 0;
}

</details>

The example contains two types, one types from stl library and other for Qt types, in both versions the ConstAppInline::LEFT is uninitialized.

Qt version crashes and the output with stl types is like following:

1

Of course, it should look like this with stl types:

1
left

And like this with Qt types:

1
QList("left")

This is the long-running bug that I'm experiencing with during the development of one of my libraries and hoped that it will be fixed, but it wasn't. The problem exists as long as I remember, from Clang 10/11.

alvinhochun

alvinhochun commented on Oct 1, 2023

@alvinhochun
Contributor

This looks like to be a mishap with the .ctors section.

For your code snippet https://godbolt.org/z/ccreG5rdK, Clang emits two separate functions to initialize the two non-local variables:

  • __cxx_global_var_init initializes ConstAppInline::LEFT
  • __cxx_global_var_init.1 initializes Model::m_fillable

Pointers to these functions are placed in the .ctorssection in forward order:

        .section        .ctors,"dw",associative,ConstAppInline::LEFT[abi:cxx11]
        .p2align        3, 0x0
        .quad   __cxx_global_var_init
        .section        .ctors,"dw",associative,Model::m_fillable[abi:cxx11]
        .p2align        3, 0x0
        .quad   __cxx_global_var_init.1

However, the trap is that functions in the .ctors list are executed in reverse order. The end result is that the constructors get executed in reverse.

This turns out to not affect Linux because instead of the .ctors section, it uses the .init_array section which has forward order.

GCC for mingw-w64 target is not affected because GCC generates only one function _GLOBAL__sub_I_main to put in the .ctors section. This function in turn calls __static_initialization_and_destruction_0(), which handles the initialization of both ConstAppInline::LEFT and Model::m_fillable in sequence.

Note: I wrote the above entirely based on the output from Compiler Explorer without testing locally, so it is possible I may have got something wrong.

CC @MaskRay @mstorsjo

alvinhochun

alvinhochun commented on Oct 4, 2023

@alvinhochun
Contributor

I was trying to see if this can be reproduced on Linux: https://godbolt.org/z/zW4a8MYE3

I found that the option -fno-use-init-array can be used to tell Clang to use the .ctors section instead of .init_array. However, when I do this, Clang correctly generates the section in reverse order unlike for MinGW target:

        .section        .ctors,"aGw",@progbits,Model::m_fillable[abi:cxx11],comdat
        .p2align        3, 0x90
        .quad   __cxx_global_var_init.1
        .section        .ctors,"aGw",@progbits,ConstAppInline::LEFT[abi:cxx11],comdat
        .p2align        3, 0x90
        .quad   __cxx_global_var_init

(The linked executable ends up producing the wrong output, but that seems to be caused by different reasons, so irrelevant here.)

It turns out CodeGen/AsmPrinter has code to reverse the constructor / destructor list specifically for this purpose, but it's not being activated by the Clang MinGW driver:

// Emit the structors in reverse order if we are using the .ctor/.dtor
// initialization scheme.
if (!TM.Options.UseInitArray)
std::reverse(Structors.begin(), Structors.end());

Passing -fno-use-init-array to cc1 fixes the issue (https://godbolt.org/z/jhnn4r1YT). So the obvious fix for the issue is to add CC1Args.push_back("-fno-use-init-array"); to:

void toolchains::MinGW::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
Action::OffloadKind DeviceOffloadKind) const {

mstorsjo

mstorsjo commented on Oct 9, 2023

@mstorsjo
Member

See #68570 and #68571 for potential fixes for the issue.

silverqx

silverqx commented on Oct 9, 2023

@silverqx
Author

I will try to execute my project after it hits stable branch and let know if still crashes, also thx for fixes.

29 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
[MSYS2] Ordered dynamic initialization is not sequenced · Issue #55938 · llvm/llvm-project