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

Если посмотреть на ассемблер (см. godbolt.org) этой простой функции

extern int global;
void doit(int *);
void call_doit(){
    doit(&global);
}

32-битное значение используется для хранения адреса global:

call_doit:
        movl    $global, %edi
        jmp     doit

Я понимаю, что использование 32-битных регистров (например, %edi) здесь превосходит 64-битные регистры (например, %rdi), потому что можно сохранить 2 байта (movl $global, %edi требует 5 байтов, а movq $global, %rdi требует 7 байтов + 4 дополнительных байта, если кто-то не предполагает что $global умещается в 32 бита).

(Примечание редактора: компиляторы на самом деле используют 7-байтовый lea global(%rip), %rdi для создания 64-битного адреса из RIP + 32-битное относительное смещение, которое компиляторы могут предположить в пределах диапазона по связанным причинам. И movabs $global, %rdi будет 10 байтов, а не 11, для 64 -битные абсолютные адреса.)

Но почему компилятору разрешено предполагать, что адрес глобальной переменной умещается в эти 32 бита? Какие гарантии есть у компилятора?


Для локальной переменной компилятор использует 64-битный регистр для хранения адреса стека, например:

void doit(int *);
void call_doit(){
    int local=0;
    doit(&local);
}

приводит к (см. на godbolt.org):

call_doit:
        subq    $24, %rsp
        leaq    12(%rsp), %rdi
        movl    $0, 12(%rsp)
        call    doit
        addq    $24, %rsp
        ret

Интересно. C требует, чтобы компилятор мог обрабатывать как минимум 4095 различных идентификаторов в единице перевода, но ничего не говорит о связывании всей программы. Есть ли ограничение на количество глобальных идентификаторов, которые назначает компоновщик GNU и на которые полагается?

Toby Speight 10.09.2018 10:22

@mhc это потому, что оптимизация хвостового вызова возможна только в первом случае (во втором случае вам нужно настроить% rsp)

ead 10.09.2018 10:29

Кстати, если вы когда-нибудь создадите программу с более чем 4 ГБ глобальных переменных, вероятность того, что вы делаете это неправильно, не равна нулю. ;-)

Toby Speight 10.09.2018 10:32

Возможно, 32 бита - это предел сегментов .data и .bss.

0___________ 10.09.2018 11:23

даже Clang и ICC генерируют 32-битный ход для глобального случая. Если вы перейдете на C++, у вас будет больше компиляторов для тестирования, и среди них Zapcc и ellcc также испускают movl $global, %edi.

phuclv 10.09.2018 11:50

У GCC есть опция -mcmodel=, которая может на это повлиять.

Mat 10.09.2018 12:28
4
6
129
0

Другие вопросы по теме