разлив регистра аргументов gcc на x86-64 - PullRequest
6 голосов
/ 26 августа 2011

Я экспериментирую со сборкой x86-64. Скомпилировав эту фиктивную функцию:

long myfunc(long a, long b, long c, long d,
            long e, long f, long g, long h)
{
    long xx = a * b * c * d * e * f * g * h;
    long yy = a + b + c + d + e + f + g + h;
    long zz = utilfunc(xx, yy, xx % yy);
    return zz + 20;
}

С gcc -O0 -g Я был удивлен, обнаружив следующее в начале сборки функции:

0000000000400520 <myfunc>:
  400520:       55                      push   rbp
  400521:       48 89 e5                mov    rbp,rsp
  400524:       48 83 ec 50             sub    rsp,0x50
  400528:       48 89 7d d8             mov    QWORD PTR [rbp-0x28],rdi
  40052c:       48 89 75 d0             mov    QWORD PTR [rbp-0x30],rsi
  400530:       48 89 55 c8             mov    QWORD PTR [rbp-0x38],rdx
  400534:       48 89 4d c0             mov    QWORD PTR [rbp-0x40],rcx
  400538:       4c 89 45 b8             mov    QWORD PTR [rbp-0x48],r8
  40053c:       4c 89 4d b0             mov    QWORD PTR [rbp-0x50],r9
  400540:       48 8b 45 d8             mov    rax,QWORD PTR [rbp-0x28]
  400544:       48 0f af 45 d0          imul   rax,QWORD PTR [rbp-0x30]
  400549:       48 0f af 45 c8          imul   rax,QWORD PTR [rbp-0x38]
  40054e:       48 0f af 45 c0          imul   rax,QWORD PTR [rbp-0x40]
  400553:       48 0f af 45 b8          imul   rax,QWORD PTR [rbp-0x48]
  400558:       48 0f af 45 b0          imul   rax,QWORD PTR [rbp-0x50]
  40055d:       48 0f af 45 10          imul   rax,QWORD PTR [rbp+0x10]
  400562:       48 0f af 45 18          imul   rax,QWORD PTR [rbp+0x18]

gcc очень странно разливает все регистры аргументов в стек и затем извлекает их из памяти для дальнейших операций.

Это происходит только на -O0-O1 проблем нет), но все же, почему? Для меня это выглядит как антиоптимизация - почему бы gcc сделать это?

Ответы [ 2 ]

7 голосов
/ 26 августа 2011

Я ни в коем случае не эксперт по внутренним компонентам GCC, но я сделаю это.К сожалению, большая часть информации о распределении и распределении регистров GCC устарела (ссылаясь на файлы, такие как local-alloc.c, которые больше не существуют).

Я смотрю на исходный код gcc-4.5-20110825.

В Внутренние компоненты компилятора GNU C упоминается, что исходный код функции генерируется expand_function_start в gcc/function.c.Там мы находим следующее для обработки параметров:

4462   /* Initialize rtx for parameters and local variables.
4463      In some cases this requires emitting insns.  */
4464   assign_parms (subr);

В assign_parms код, который обрабатывает каждый аргумент, хранится в следующем:

3207       if (assign_parm_setup_block_p (&data))
3208         assign_parm_setup_block (&all, parm, &data);
3209       else if (data.passed_pointer || use_register_for_decl (parm))
3210         assign_parm_setup_reg (&all, parm, &data);
3211       else
3212         assign_parm_setup_stack (&all, parm, &data);

assign_parm_setup_block_p обрабатывает агрегированные данныетипы и не применимы в этом случае и поскольку данные не передаются в качестве указателя, GCC проверяет use_register_for_decl.

Здесь соответствующая часть:

1972   if (optimize)
1973     return true;
1974 
1975   if (!DECL_REGISTER (decl))
1976     return false;

DECL_REGISTER проверяет, была ли переменная объявлена ​​с ключевым словом register.И теперь у нас есть ответ: большинство параметров живут в стеке, когда оптимизация не включена, и затем обрабатываются assign_parm_setup_stack.Маршрут, пройденный через исходный код до того, как он закончится разливом значения, немного сложнее для аргументов указателя, но может быть отслежен в том же файле, если вам интересно.

Почему GCC выдает все аргументы и локальныепеременные с отключенной оптимизацией?Чтобы помочь отладке.Рассмотрим эту простую функцию:

1 extern int bar(int);
2 int foo(int a) {
3         int b = bar(a | 1);
4         b += 42;
5         return b;
6 }

Скомпилировано с gcc -O1 -c, на моем компьютере это генерирует следующее:

 0: 48 83 ec 08             sub    $0x8,%rsp
 4: 83 cf 01                or     $0x1,%edi
 7: e8 00 00 00 00          callq  c <foo+0xc>
 c: 83 c0 2a                add    $0x2a,%eax
 f: 48 83 c4 08             add    $0x8,%rsp
13: c3                      retq   

Это нормально, за исключением случаев, когда вы разбиваете строку 5 и пытаетесь распечататьзначение a, вы получите

(gdb) print a
$1 = <value optimized out>

, так как аргумент перезаписывается, так как он не используется после вызова bar.

6 голосов
/ 26 августа 2011

Пара причин:

  1. В общем случае аргумент функции должен рассматриваться как локальная переменная, потому что он может быть сохранен или иметь свой адрес внутри функции.Следовательно, проще всего просто выделить слот стека для каждого аргумента.
  2. Отладочная информация становится намного проще для вывода из стека: значение аргумента всегда находится в определенном месте, а не перемещается между регистрами и памятью..

Когда вы смотрите на код -O0 в целом, учтите, что главные приоритеты компилятора - максимально сократить время компиляции и генерировать высококачественную отладочную информацию.

...