views:

889

answers:

7

Is there a way to tell the compiler (g++ in my case) to not optimize certain code away, even if that code is not reachable? I just want those symbols in the object file.

Example: Here is a simple function, and I do want this function to be compiled, even if it's never called.

void foo(){
  Foo<int> v;
}

If there is no official compiler directive, is there a trick to make the compiler think that it's an important function? Or at least make it think that it can't safely be ignored? I tried something like this:

extern bool bar;
void foo(){
  if(bar){
    Foo<int> v;
  }
}

but that didn't seem to do it.

(If you really want to know why I on earth would want that -- it has to do with this question, where, instead of explicit template instantiation with template class Foo<int> I simply want to be able to write Foo<int> v, since in many cases that's easier since it implicitly instantiates all functions needed, and it does work fine in debug mode without optimizations ...)

UPDATE:

Here is what I want to do (as a compilable mini example):

foo.h (such files are given to me and not changeable)

template<class T>
struct Foo {
  T val_;
  Foo(T val) : val_(val) {
      // heavy code, long compile times
  }
};

foo-instantiation.cpp

#include "foo.h"
void neverCalled() {
  Foo<int> f(1);
}

// The standard way to instantiate it is this:
// template class Foo<int>;
// but in reality it is often hard to find out 
// exactly what types I have to declare.
// Usage like Foo<int> f(1); will instantiate all
// dependent types if necessary.

foo-decl.h (an interface that I extracted from foo.h)

template<class T>
struct Foo {
  T val_;
  Foo(T val); // no heavy code, can include anywhere and compile fast
};

main.cpp

#include <iostream>
#include "foo-decl.h"

int main(int argc, char** argv){
  Foo<int> foo(1);
  return 0;
}

Compilation (no optimization)

g++ -c main.cpp
g++ -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo

Compilation (optimization)

g++ -O2 -c main.cpp
g++ -O2 -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo
main.o(.text+0x13): In function `main':
: undefined reference to `Foo<int>::Foo(int)'
collect2: ld returned 1 exit status
  • I tried precompiled headers instead, but the template instantiation method makes for much faster compilation.
  • Compiling foo-instantiation.cpp without optimization is not so ideal because then the library code (foo.h and others) will run slower.
+2  A: 

search the documentation under the topic of #pragma. This define is a kind of escape hatch that allows you to specify all kinds of properties. gcc supports, so there is a good bet that g++ will as well. Be warned that these are likely not portable which may or may not be of concern for your project.

MikeJ
These are not standard. #pragma is a precompiler keyword to change implementation details of the compiler. If you depend on #pragma you will loose portability.
David Rodríguez - dribeas
I mentioned that they are likely not portable and for some projects this is not a concernI know this and I stated as such. I left it up to the question proponent to decide if this is a means that is appropriate for their project
MikeJ
+4  A: 

The compiler cannot optimise a function body away, whether you declare it extern or not, because it cannot know that the function is not called from another compilation unit. It could optimise it away if you declared it static, but I don;t believe any compilers actually do this.

The compiler can optimise away function calls:

while(false) {
  foo();
}

In the above the call to foo() can be elided.

OTOH, the linker can remove function bodies from the final excecutable if they are not called.

For the above and other reasons, we really need to see some real code in order to diagnose your problem.

anon
Thanks, I added the code, see Update. You're quite right that the compiler actually can't optimize it away since it doesn't know what will be called from the outside. So I don't know why compilation with optimization gives an error, then, and w/o optimization it's fine.
Actually yes, the compiler can see that Foo<int> v is never used and optimize it away.
@dehmann - post some evidence for this assertion please
anon
When I compile without optimization, the nm tool shows 3 symbols in foo-instantiations.o: _Z11neverCalledv, _ZN3FooIiEC1Ei, __gxx_personality_v0. But when I compile with -O2, there is only one symbol: _Z11neverCalledv. So ...Foo... isn't there anymore.
See my post regarding the ODR
anon
The compiler can know that the local variable will never be used and optimize it away, leaving an empty function body that cannot be removed but then again, you have lost the instantiation.
David Rodríguez - dribeas
The linker can optimize the symbol away (the intantiation function) but would not optimize the instantiated template away as it is in fact used (the linker is complaining about the non-definition of the templated constructor)
David Rodríguez - dribeas
+5  A: 

You are running into the One Definition Rule. In one file you have a definition:

template<class T>
struct Foo {
  T val_;
  Foo(T val) : val_(val) {
      // heavy code, long compile times
  }
};

and in another a different definition:

template<class T>
struct Foo {
  T val_;
  Foo(T val); // no heavy code, can include anywhere and compile fast
};

This is explicitly not allowed in C++ (only one identical definition allowed) and if you break the rule your code may well seem to work sometimes, but what you actually have is the dreaded "undefined behaviour" - anything may happen depending on the phase of the moon (but more likely the internal state of the compiler at certain critical junctures).

Basically, you can't write code like that - sorry.

anon
I think you're right. Too bad. :(
dehmann, i still haven't understood what the heck is better with your way of doing it than with explicit instantiation. both have the same drawbacks don't they? (and yours in addition is undefined behavior)
Johannes Schaub - litb
It is not a ODR violation problem. The compiler is clearly stating that there is no definition of the class, not that there are multiple. ODR violations with templates is a tricky issue, and a comment has no space for a full description, but this is not the case.
David Rodríguez - dribeas
@dribeas: Actually if you change foo.h to include foo-decl.h and to add the function definition outside of the class body it works fine. (But that's not an option for me since I can't change all my foo.h-like files, there are too many of them, and modifying them is too dangerous.)
A: 

Off hand I'm not sure. Perhaps Precompiled headers will solve this problem though?

Just to modify this a bit, obviously this won't help with the problem of being able to use the smaller template header in your code, but it might help with the compile time problem (and thereby remove the need for the template).

sharth
The compiler cannot 'precompile' templates without knowing what types it will be defined for.
David Rodríguez - dribeas
+1  A: 

This is generally done by a compiler directive. In C it will be a #pragma, and in Delphi Pascal it's {$O-}, {$O+} around the code in question. The precise syntax and method is implementation specific, so it's a question of checking the documentation for whatever system you are using.

Not optimising a function away is quite straightforward, but once or twice I've seen cases where it's been necessary to tell the compiler not to optimise specific code. This is extremely rare and not something I've come across for a very long time, but it can occasionally happen. Cases where it does are generally where one is compiling against some old legacy code that was built before a later development in cpu technology - hyperthreading being a classic case in point.

Cruachan
+1  A: 

The compiler is optimizing away a variable that is never used, it cannot optimize a function on the grounds that it will not be used, as it could be used from a different compilation unit. You could try to force the compiler into considering the variable as used with something akin to:

void instantiation()
{
   Foo<int> f;
   f; // mark the variable as if it is used.
}

// or:
Foo<int>* instantiation()
{
   Foo<int> *p = new Foo<int>();
   return p; // The compiler cannot know if p will be used, it must compile
}

A better solution would be to explicitly instantiate the template if you want it:

// .h
template <typename T>
class Foo
{
public:
   Foo( T const & value );
   void set( T const & ); // whatever else
private:
   T value_;
};

// template implementation another file, not included from .h
// instantiation.cpp??
template <typename T>
Foo<T>::Foo<T>( T const & value ) : value_(value) {}

template <typename T>
void Foo<T>::set( T const & v )
{
   value_ = value;
}

// explicit instantiation
template class Foo<int>;
template class Foo<double>;

// test.cpp
#include "header.h"
int main()
{
    Foo<int> f(5);
    f.set( 7 );

    Foo<char> f2; // linker error Foo<char>() not defined
}

User code will only see the header and know what methods exist, but not the real implementation. The implementation will be compiled in one compilation unit, where the explicit template instantiation occurs.

Note that if you forget to explicitly instantiate one type, it will be a linker error, not a compilation error.

The One Definition Rule

The one definition rule in c++ states that there can be only one definition for each symbol or class. Having multiple definitions can be detected easily for regular symbols (if you define two void f() { } the linker will detect the duplicated symbol) but is a little trickier with templates. With templates it is trickier as they are usually declared and defined in header files. The compiler generates the used symbols in each compilation unit [1] and the linker usually finds more than one equivalent symbol ( std::vector::push_back() is compiled into each compilation unit that has a std::vector and calls push_back)

The compiler flags the templated code as a 'weak' symbol, signifying that while the symbol is defined here, it can also be defined in another compilation unit and the linker is free to discard the symbol without yielding a link error. That is a requirement if you want to link different compilation units that make use of the same STL facilities, for example, with the same types.

Up to gcc 4.2, gcc linux linker discards all but one of the weak symbols without further checking. Some linkers (gcc linker in linux will in the near future, not as 4.2, don't know 4.3 nor 4.4 it could be alredy there) do check that the different 'weak' symbols are in fact the same and provide an error/warning to the user.

Your code is breaking the ODR in that you are redeclaring the template in a different place. You should declare the template once and the implement the methods externally as posted above. Anyway if both definitions are compatible (as they are in the snippet you posted): all member methods and attributes are exactly the same and with the same qualifiers (virtual/const-ness...) it should be accepted by the compiler as there is only one definition (alas repeated) of the template.

[1] Only those methods that are actually called in the code will be compiled:

template <typename T>
struct Test
{
   void f() { std::cout << "f()" << std::endl; }
   void g() { std::cout << "g()" << std::endl; }
};
int main()
{
   Test<int> t;
   t.f(); // compiler generates Test<int>::f, but not Test<int>::g
}
David Rodríguez - dribeas
A: 

Declare your variable as volatile:

volatile Foo<int> v;

Usually, it prevents any optimizations. I've checked it with Intel C++ Compiler and Microsoft Visual Studio 2008.

Vova