Может ли GCC оптимизировать методы класса с постоянными переменными времени компиляции?

преамбула

Я использую avr-g ++ для программирования микроконтроллеров AVR, и поэтому мне всегда нужно получать очень эффективный код.

GCC обычно может оптимизировать функцию, если ее аргументом являются константы времени компиляции, например, У меня есть функция pin_write(uint8_t pin, bool val) которые определяют регистры AVR для pin (используя мою специальную карту из целого числа pin в пару порт / пин) и записать в эти регистры соответствующие значения. Эта функция не слишком мала из-за своей общности. Но если я вызову эту функцию с постоянной времени компиляции pin а также valGCC может выполнять все вычисления во время компиляции и исключать этот вызов для пары инструкций 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

Но GCC не делает этого.

Удивительно, но если я удалю глобальный 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 ассемблерных инструкций. A a(8); a.foo() тоже! Но если я объявлю A a(8) так как глобальный компилятор выдает большой общий код. Я пытался добавить static — это не помогло. Зачем?

5

Решение

Но если я объявлю 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());
}

Я нашел 2 способа добиться того, чтобы сгенерированная сборка соответствовала этому:

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

Работает только с Clang на -O3, Видимо постоянное складывание в gcc это не так агрессивно. (Как показывает clang, это можно сделать, но gcc 4.7.2 не реализовал это.)

3

Другие решения

Вы можете заставить компилятор полностью оптимизировать функцию со всеми известными константами, изменив функцию pin_write в шаблон. Я не знаю, гарантируется ли конкретное поведение стандартом.

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

Это, вероятно, потребует исправления всех строк, где используется pin_write.

Кроме того, вы можете объявить функцию как встроенную. Компилятору не гарантируется встроенная функция (ключевое слово inline — просто подсказка), но если это так, у него больше шансов оптимизировать константы времени компиляции (при условии, что компилятор может знать, что это константа времени компиляции, что может быть не всегда так).

1

Ваш a имеет внешнюю связь, поэтому компилятор не может быть уверен, что где-то другой код его модифицирует.

Если бы вы должны были объявить a const тогда вы даете понять, что это не должно измениться, а также перестаете иметь внешнюю связь; оба из них должны помочь компилятору быть менее пессимистичным.

(Я бы наверное объявил x const тоже — здесь это может не помочь, но если ничего другого, то это ясно покажет компилятору и следующему читателю кода, что вы никогда его не измените.)

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector