c++language-lawyerlink-time-optimization

Is LTO allowed to remove unused global object if there is code in a different translation unit relying on side effects of its construction?


First, just to avoid XY problem: this issue comes from https://github.com/cnjinhao/nana/issues/445#issuecomment-502080177. The library code should probably not do such thing (reliance on construction of unused global object) but the question is more about whether it's valid LTO behaviour rather than code quality issues.


Minimal code that showcases the same problem (untested, just to make example smaller):

// main.cpp
#include <lib/font.hpp>

int main()
{
    lib::font f;
}
// lib/font.hpp
namespace lib
{
struct font
{
    font();

    int font_id;
};
}
// lib/font.cpp
#include <lib/font.hpp>
#include <lib/font_abstraction.hpp>

namespace lib
{
font::font()
{
    font_id = get_default_font_id();
}
}
// lib/font_abstraction.hpp
namespace lib
{
int get_default_font_id();

void initialize_font();
}
// lib/font_abstraction.cpp
#include <lib/font_abstraction.hpp>

namespace lib
{
static int* default_font_id;

int get_default_font_id()
{
    return *default_font_id;
}

void initialize_font()
{
    default_font_id = new int(1);
}
}
// lib/platform_abstraction.hpp
namespace lib
{
struct platform_abstraction
{
    platform_abstraction();
};
}
// lib/platform_abstraction.cpp
#include <lib/platform_abstraction.hpp>
#include <lib/font_abstraction.hpp>

namespace lib
{
platform_abstraction::platform_abstraction()
{
    initialize_font();
}

static platform_abstraction object;
}

The construction of font object in main.cpp relies on the initialization of the pointer. The only thing that initializes the pointer is global object object but it's unsued - in the case of linked issue that object was removed by LTO. Is such optimization allowed? (See C++ draft 6.6.5.1.2)

Some notes:


Solution

  • VTT's answer gives a GCC answer, but the question is tagged language-lawyer.

    The ISO C++ reason is that objects defined in a Translation must be initialized before the first call to a function defined in the same Translation Unit. That means platform_abstraction::object must be initialized before platform_abstraction::platform_abstraction() is called. As the linker correctly figured out, there are no other platform_abstraction objects, so platform_abstraction::platform_abstraction is never called, so object's initialization can be postponed indefinitely. A conforming program cannot detect this.