C++: How to prevent default constructor using AVX for initialisation

Consider the following:

// foo.h
class Foo
{
   public:
      int x = 2;
      int y = 3;
      void DoSomething_SSE();
      void DoSomething_AVX();
   // ( Implicit default constructor is generated "inline" here )
};

// Foo_AVX.cpp, compiled with -mavx or /arch:AVX
void Foo::DoSomething_AVX()
{
   // AVX optimised implementation here
}

// Foo_SSE.cpp, compiled with -msse2 or /arch:SSE2
void Foo::DoSomething_SSE()
{
   // SSE optimised implementation here
}

Here's the problem: the compiler will generate the implied default constructor with 'inline' semantics (note: inline semantics does not mean the function will necessarily be inlined) in each translation unit, and - in cases where the constructor is not inlined - the linker will then choose one implementation and discard the other.

If the linker chooses the constructor generated in the AVX compilation unit, this code will then crash with an illegal instruction on a machine which doesn't support AVX.

It is possible to stop the crash by putting in an explicit default constructor, either __forceinline (to make sure it's inlined once per compilation unit), or declared in the header and defined in a compilation unit which is compiled with the lowest common denominator instruction set.

However, surely there's a way to get the language to handle this better than having to write dummy functions..?

(llvm-clang++ 9.x.x/x64 on Mac OS X)

Compile the AVX Translation units with gcc or clang -mavx -fno-implement-inlines; the linker will have to find the symbol from the SSE translation units if the functions don't simply inline.


From the GCC manual:

-fno-implement-inlines To save space, do not emit out-of-line copies of inline functions controlled by #pragma implementation. This causes linker errors if these functions are not inlined everywhere they are called.

Clang supports this option, too.

This does not disable inlining of anything, it only disables emitting a stand-alone definition of functions declared as inline or in a class definition.

With optimization enabled, a small default constructor like in the question should inline (and use the target ISA options of the current function/compilation unit), making this irrelevant most of the time. But it will make sure that un-optimized builds work properly on non-AVX machines.

C Programming Tutorial for Beginners, This course will give you a full introduction into all of the core concepts in the C programming Duration: 3:46:13 Posted: Aug 15, 2018 C or c is the third letter in the English and ISO basic Latin alphabets.Its name in English is cee (pronounced / ˈ s iː /), plural cees.

It appears that another option is to not use compiler flags to set the instruction set - leave them on the default, and wrap only the functions which require the enhanced instruction set:

#include Foo.h

// Switch on AVX optimisations for the function where they're needed
#pragma clang attribute push (__attribute__((target("arch=sandybridge"))), apply_to = function)

void Foo::DoSomething_AVX()
{
   // AVX optimised implementation here
}
#pragma clang attribute pop

Using #pragma clang attribute push(...), while a bit more long-winded than simple [[]] or __attribute__(()), seems to have the advantage that the attribute is automatically applied to any template code etc. instantiated from within the pragma's scope.

"C" Programming Language: Brian Kernighan, "C" is one of the most widely used programming languages of all time. Prof Brian Kernighan Duration: 8:26 Posted: Aug 18, 2015 Stock analysis for Citigroup Inc (C:New York) including stock price, stock chart, company news, key statistics, fundamentals and company profile.

Put the implementation into a separate .cpp file and voila everything works. The other way is to make those functions/methods/constructors inline. The third way (compiler dependent) is to set them a 'weak reference' attribute.

Learn C, learn-c.org is a free interactive C tutorial for people who want to learn C, fast. C is a procedural programming language. It was initially developed by Dennis Ritchie as a system programming language to write operating system. The main features of C language include low-level access to memory, simple set of keywords, and clean style, these features make C language suitable for system programming like operating system or compiler development.

C Tutorial, C programming is a general-purpose, procedural, imperative computer programming language developed in 1972 by Dennis M. Ritchie at the Bell Telephone� This is a list of operators in the C and C++ programming languages.All the operators listed exist in C++; the fourth column "Included in C", states whether an operator is also present in C. Note that C does not support operator overloading.

C Language - Overview, C Language - Overview - C is a general-purpose, high-level language that was originally developed by Dennis M. Ritchie to develop the UNIX operating system � As well as C and Simula's influences, other languages also influenced this new language, including ALGOL 68, Ada, CLU and ML . Initially, Stroustrup's "C with Classes" added features to the C compiler, Cpre, including classes, derived classes, strong typing, inlining and default arguments.

Learn C Programming, C is a powerful general-purpose programming language. Our C tutorials will guide you to learn C programming one step at a time with the help of examples. Programming Languages Development - C++ has been used extensively in developing new programming languages like C#, Java, JavaScript, Perl, UNIX’s C Shell, PHP and Python, and Verilog etc. Computation Programming - C++ is the best friends of scientists because of fast speed and computational efficiencies.

Comments
  • Have you looked at gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/… or clang.llvm.org/docs/AttributeReference.html? Don't know if it works for constructors though.
  • Why do you link code compiled for different architectures into the same binary!?
  • You may find this useful: gcc.gnu.org/wiki/FunctionMultiVersioning
  • Dan M.: That would work in the simple case outlined above - however it's not possible AFAICT to tag template-instantiations (for example) with function attributes.
  • Michael Kenzel: Have been doing this fine for some years - they're the same underlying architecture (x64) but different cpu feature sets, so that one executable can run optimised on different generations of processor - SSE2, SSE4, AVX, AVX2, AVX512. Only when I began to rely more heavily on default constructors did it become a problem.
  • Thank you very much, that will do the trick. Another solution which I found with much digging around is outlined below.
  • Anything inlined into a function marked with __attribute__((target(arch=sandybridge))) will use the same code-gen. Instantiating templates that don't inline with AVX code-gen might be dangerous if non-AVX code could also use those stand-alone functions. Interesting idea, though.
  • Missing the point: there is no implementation. These are defaulted functions.