GCC可以用编译时常量变量优化类的方法吗?

时间:2014-03-02 16:46:49

标签: c++ gcc avr

序言

我正在使用avr-g ++编程AVR微控制器,因此我总是需要获得非常高效的代码。

GCC通常可以优化函数,如果它的参数是编译时常量,例如我有函数pin_write(uint8_t pin, bool val),它确定pin的AVR寄存器(使用我从整数pin到配对端口/引脚的特殊映射)并写入这些寄存器的对应值。由于其通用性,这个功能不是太小。但是如果我用编译时常量pinval调用这个函数,GCC可以在编译时进行所有计算并消除对几个AVR指令的调用,例如

sbi PORTB,1
sbi DDRB,1

缓行

让我们写一个这样的代码:

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

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

我们只有一个A类对象,它用常量(8)初始化。因此,可以在编译时进行所有计算:

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

但海湾合作委员会没有这样做。

令人惊讶的是,如果我删除全局A a(8)并只写

 A(8).foo()

我得到了我想要的东西:

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

问题

那么,有没有办法强制GCC在编译时为具有常量初始化器的单个全局对象进行所有可能的计算?

由于这个问题,我必须手动扩展这种情况并用以下代码替换原始代码:

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

UPD。非常精彩:A(8).foo()main优化为2个asm指令。 A a(8); a.foo()也是!但是,如果我将A a(8)声明为全局 - 编译器会生成大的通用代码。我试图添加static - 它没有帮助。为什么呢?

3 个答案:

答案 0 :(得分:3)

  

但如果我将A a(8)声明为全局 - 编译器会生成大的通用代码。我试图添加static - 它没有帮助。为什么呢?

根据我的经验,如果对象/功能具有外部链接,gcc非常不情愿。由于我们没有编译代码,因此我对代码进行了略微修改:

#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());
}

我找到了两种方法来实现生成的程序集对应于此:

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

用语言:在编译时消除所有内容,以便只剩下必要的最小值。

使用clang和gcc实现这一目标的一种方法是:

#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已经消除-O1处的所有内容,扼杀3.5中继需要-O2

实现这一目标的另一种方法是:

#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());
}

它仅适用于-O3的clang。显然,gcc中的constant folding并不具有攻击性。 (正如clang所示,它可以完成,但gcc 4.7.2没有实现它。)

答案 1 :(得分:1)

通过将pin_write函数更改为模板,可以强制编译器使用所有已知常量完全优化函数。我不知道标准是否保证了特定的行为。

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

这可能需要修复使用pin_write的所有行。

此外,您可以将该函数声明为内联函数。编译器不保证内联函数(内联关键字只是一个提示),但如果确实如此,它有更大的机会优化编译时常量(假设编译器可以知道它是一个编译时常量,可能并非总是如此)。

答案 2 :(得分:1)

您的a有外部链接,因此编译器无法确定是否有其他代码在某处修改它。

如果您要声明a const,那么您明确表示它不应该更改,并且还要阻止它具有外部链接;这两者都应该有助于编译器不那么悲观。

(我可能也会声明x const - 这可能没有帮助,但是如果没有别的话它会让编译器和代码的下一个读者清楚地知道你永远不会改变它。)