Встроенное и стековое управление кадрами - PullRequest
0 голосов
/ 22 мая 2018

Ниже приведены искусственные примеры.Очевидно, что оптимизация компилятора кардинально изменит конечный результат.Однако, и я не могу больше подчеркнуть это: , временно отключив оптимизацию, я намерен установить верхнюю границу использования стека , вероятно, я ожидаю, что дальнейшая оптимизация компилятора может улучшить ситуацию.

Обсуждение сосредоточено вокруг GCC только.Я хотел бы иметь точный контроль над тем, как автоматические переменные освобождаются из стека.Области применения с блоками не гарантирует, что память будет освобождена, когда автоматические переменные выходят из области видимости.Функции, насколько я знаю, гарантируют, что.

Однако, когда встраивается, что происходит?Например:

inline __attribute__((always_inline)) void foo()
{
    uint8_t buffer1[100];
    // Stack Size Measurement A
    // Do something 
}

void bar()
{
    foo();
    uint8_t buffer2[100];
    // Stack Size Measurement B
    // Do something else
}

Могу ли я всегда ожидать, что в точке измерения стек будет содержать только buffer2 и buffer1 был освобожден?

Кроме вызовов функций (которые приводят к дополнительному использованию стека), есть ли какой-нибудь способ, которым я могу иметь точный контроль над освобождением стека?

Ответы [ 3 ]

0 голосов
/ 22 мая 2018

mov BYTE PTR [rbp-20], 1 и mov BYTE PTR [rbp-10], 2 показывают только относительное смещение указателя стека в кадре стека.при рассмотрении ситуации во время выполнения они имеют одинаковое пиковое использование стека.

Существуют два различия в том, использовать ли inline: 1) В режиме вызова функции buffer1 будет освобожден при выходе из foo ().Но во встроенном методе buffer1 не будет сохраняться до выхода из bar (), это означает, что пиковое использование стека будет длиться дольше.2) вызов функции добавит несколько накладных расходов, таких как сохранение информации о кадре стека, по сравнению со встроенным режимом

0 голосов
/ 22 мая 2018

Я хотел бы иметь точный контроль над тем, как автоматические переменные освобождаются из стека.

Здесь много путаницы.Оптимизирующий компилятор 1006 * может хранить некоторые автоматические переменные только в регистрах , без использования какого-либо слота в кадре вызова.Спецификация языка C ( n1570 ) не требует никакого стека вызовов.

И данный регистр или слот в кадре вызова можно повторно использовать для различных целей (например, для различных автоматических переменных вразличные части функции). Распределение регистров играет важную роль компиляторов.

Могу ли я всегда ожидать, что в точке измерения стек будет содержать только буфер2 и буфер1 освобожден?

Конечно, нет.Компилятор может доказать, что на более позднем этапе в вашем коде пространство для buffer1 больше не будет полезным, поэтому повторно используйте это пространство для других целей.

, есть ли способ, которым я могу иметь точный контроль надосвобождение стека?

Нет , нет. стек вызовов является подробностью реализации и может не использоваться (или «злоупотребляться» с вашей точки зрения) компилятором и сгенерированным кодом.

В некоторых глупых примерах:если buffer1 не используется в foo, компилятор может не выделить для него место.И некоторые умные компиляторы могут просто выделить 8 байтов, если они могут доказать, что только 8 первых байтов buffer1 полезны.

Если серьезно, в некоторых случаях GCC может tail-call оптимизаций.

Вас должно заинтересовать , вызывающее GCC с -fstack-reuse=all, -Os, -Wstack-usage=256, -fstack-usage идругие варианты.

Конечно, использование конкретного стека зависит от уровней оптимизации.Вы также можете проверить сгенерированный ассемблерный код, например, с помощью -S -O2 -fverbose-asm

Например, следующий код e.c:

int f(int x, int y) {
    int t[100];
    t[0] = x;
    t[1] = y;
    return t[0]+t[1];
}

при компиляции с GCC8.1 в Linux / Debian /x86-64 с использованием gcc -S -fverbose-asm -O2 e.c дает e.s

        .text
        .p2align 4,,15
        .globl  f
        .type   f, @function
f:
.LFB0:
        .cfi_startproc
# e.c:5:      return t[0]+t[1];
        leal    (%rdi,%rsi), %eax       #, tmp90
# e.c:6: }
        ret     
        .cfi_endproc
.LFE0:
        .size   f, .-f

, и вы видите, что кадр стека не увеличен на 100 * 4 байта.И это все еще имеет место с:

int f(int x, int y, int n) {
    int t[n];
    t[0] = x;
    t[1] = y;
    return t[0]+t[1];
}

, который фактически генерирует тот же машинный код, что и выше.И если вместо + выше я вызываю inline int add(int u, int v) { return u+v; }, то сгенерированный код не меняется.

Помните о как-бы правиле и о сложном понятии неопределенное поведение (если n было 1 выше, это UB).

0 голосов
/ 22 мая 2018

Могу ли я всегда ожидать, что при измерении B в стеке будут только буфер2 и буфер1 освобожден?

Нет.Это будет зависеть от версии GCC, цели, уровня оптимизации, опций.

Помимо вызовов функций (которые приводят к дополнительному использованию стека), есть ли какой-нибудь способ, которым я могу точно контролировать освобождение стека?

Ваши требования настолько специфичны, что вам, скорее всего, придется написать код на ассемблере.

...