Question

Preamble

I'm using avr-g++ for programming AVR microcontrollers and therefore I always need to get very efficient code.

GCC usually can optimize a function if its argument are compile-time constants, e.g. I have function pin_write(uint8_t pin, bool val) which determine AVR's registers for the pin (using my special map from integer pin to a pair port/pin) and write to these registers correspondent values. This function isn't too small, because of its generality. But if I call this function with compile-time constant pin and val, GCC can make all calculations at compile-time and eliminate this call to a couple of AVR instructions, e.g.

sbi PORTB,1
sbi DDRB,1

Amble

Let's write a code like this:

class A {
        int x;
public:
        A(int x_): x(x_) {}
        void foo() { pin_write(x, 1); }
};

A a(8);
int main()  {
        a.foo();
}

We have only one object of class A and it's initialized with a constant (8). So, it's possible to make all calculations at compile-time:

foo() -> pin_write(x,1) -> pin_write(8,1) -> a couple of asm instructions

But GCC doesn't do so.

Surprisely, but if I remove global A a(8) and write just

 A(8).foo()

I get exactly what I want:

00000022 <main>:
  22:   c0 9a           sbi     0x18, 0 ; 24
  24:   b8 9a           sbi     0x17, 0 ; 23

Question

So, is there a way to force GCC make all possible calculation at compile-time for single global objects with constant initializers?

Because of this trouble I have to manually expand such cases and replace my original code with this:

const int x = 8; 
class A {
public:
    A() {}
    void foo() { pin_write(x, 1); }
}

UPD. It very wonderful: A(8).foo() inside main optimized to 2 asm instructions. A a(8); a.foo() too! But if I declare A a(8) as global -- compiler produce big general code. I tried to add static -- it didn't help. Why?

Était-ce utile?

La solution

But if I declare A a(8) as global -- compiler produce big general code. I tried to add static -- it didn't help. Why?

In my experience, gcc is very reluctant if the object / function has external linkage. Since we don't have your code to compile, I made a slightly modified version of your code:

#include <cstdio>

class A {
        int x;
public:
        A(int x_): x(x_) {}
        int f() { return x*x; }
};

A a(8);

int main()  {
        printf("%d", a.f());
}

I have found 2 ways to achive that the generated assembly corresponds to this:

int main()  {
        printf("%d", 64);
}

In words: to eliminate everything at compile time so that only the necessary minimum remains.

One way to achive this both with clang and gcc is:

#include <cstdio>

class A {
        int x;
public:
        constexpr A(int x_): x(x_) {}
        constexpr int f() const { return x*x; }
};

constexpr A a(8);

int main()  {
        printf("%d", a.f());
}

gcc 4.7.2 already eliminates everything at -O1, clang 3.5 trunk needs -O2.

Another way to achieve this is:

#include <cstdio>

class A {
        int x;
public:
        A(int x_): x(x_) {}
        int f() const { return x*x; }
};

static const A a(8);

int main()  {
        printf("%d", a.f());
}

It only works with clang at -O3. Apparently the constant folding in gcc is not that aggressive. (As clang shows, it can be done but gcc 4.7.2 did not implement it.)

Autres conseils

You can force the compiler to fully optimize the function with all known constants by changing the pin_write function into a template. I don't know if the particular behavior is guaranteed by the standard though.

template< int a, int b >
void pin_write() { some_instructions; }

This will probably require fixing all lines where pin_write is used.

Additionally, you can declare the function as inline. The compiler isn't guaranteed to inline the function (the inline keyword is just an hint), but if it does, it has a greater chance to optimize compile time constants away (assuming the compiler can know it is an compile time constant, which may be not always the case).

Your a has external linkage, so the compiler can't be sure that there isn't other code somewhere modifying it.

If you were to declare a const then you make clear it shouldn't change, and also stop it having external linkage; both of those should help the compiler to be less pessimistic.

(I'd probably declare x const too - it may not help here, but if nothing else it makes it clear to the compiler and the next reader of the code that you never change it.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top