LINUX.ORG.RU

непонятки с asm

 ,


0

1

Дизассемблировал простейший hello world и обнаружил такое тело для функции main:

080483d4 <main>:
  push   %ebp
  mov    %esp,%ebp
  and    $0xfffffff0,%esp
  sub    $0x10,%esp
  movl   $0x80484c0,(%esp)
  call   80482f0 <puts@plt>
  mov    $0x0,%eax
  leave  
  ret    

все достаточно понято, сохраняем старое значение %ebp на стеке, а указатель на стек - в %ebp, помещаем строку с hello world-ом на стек и вызываем puts, все хорошо, но зачем делается инструкция

  and    $0xfffffff0,%esp

зачем зануляются последние биты у указателя на стек?

> зачем зануляются последние биты у указателя на стек?

выравнивание по 16-байтной границе для локальных переменных. связано с тем, что у х86 (да и не только) «строчка» кэша — 16 байт (с выравниванием по 16-байтной границе). короче, не обращай внимания :)

arsi ★★★★★
()
Ответ на: комментарий от arsi

а, чорт, стек же вниз растет, туплю, вопрос снят =)

Goganchic ★★
() автор топика
Ответ на: комментарий от Harald

Примерно так:

10000310 <main>:
10000310:       94 21 ff f0     stwu    r1,-16(r1)
10000314:       7c 08 02 a6     mflr    r0
10000318:       3c 60 10 00     lis     r3,4096
1000031c:       38 63 06 8c     addi    r3,r3,1676
10000320:       90 01 00 14     stw     r0,20(r1)
10000324:       48 00 02 9d     bl      100005c0 <puts@plt>
10000328:       38 60 00 00     li      r3,0
1000032c:       80 01 00 14     lwz     r0,20(r1)
10000330:       38 21 00 10     addi    r1,r1,16
10000334:       7c 08 03 a6     mtlr    r0
10000338:       4e 80 00 20     blr
mr_doug
()
Ответ на: комментарий от mr_doug

Ну и тут очевидно видно, что инструкции

10000314:       7c 08 02 a6     mflr    r0
10000320:       90 01 00 14     stw     r0,20(r1)
1000032c:       80 01 00 14     lwz     r0,20(r1)
10000334:       7c 08 03 a6     mtlr    r0

Совершенно излшни. Хватило бы mr r30,r0 какого-нибудь, чтобы сохранить регистр адреса возврата (да, у нас передача аргументов и вызов процедур происходит вообще без участия какого-либо стека и памяти, если бы конпеляторы могли в это). И -O3 ничего не меняет по сравнению с -O0. Жесть. Заняться что ли доделкой Gimple и Generic'а для ppc.

mr_doug
()
Ответ на: комментарий от mr_doug

Ну и ещё. «Указатель на стэк» в POWER/PowerPC может быть любым регистром вообще. То, что GCC использует идеи RS6000 и юзает GPR1 (r1) — его дело. Расти он тоже может коть вниз (-16), хоть вверх (16(r1)) — это тоже предпочтения конпелятора.

Я бы, писав вручную, написал бы так:

lis   r3,4096
mflr  r30
addi  r3,r3,1676
bl    100005c0 <puts@plt>
mtlr  r30
li    r3,0
blr
mr_doug
()
Ответ на: комментарий от mr_doug

Хватило бы mr r30,r0 какого-нибудь, чтобы сохранить регистр адреса возврата

Расскажи лучше, как ты обеспечишь, чтобы никакие процедуры ниже по стеку вызовов не исопльзовали r30, и где они все будут сами хранить свои адреса возврата?

LamerOk ★★★★★
()
Ответ на: комментарий от mr_doug

> Жесть.

вот примерно то же самое, только более нецензурное, я тихо про себя прошептал, когда увидел код, который генерит гцц для арма %)

arsi ★★★★★
()
Ответ на: комментарий от LamerOk

Это соглашение ABI: регистры r0-r12 считаются «volatile», то есть могут быть изменяемы вызываемой процедурой (r3-r12 используются для передачи параметров, например) и в принципе и должны быть изменены, а вот r13-r31 считаются «non-volatile», то есть вызываемая процедура обязана выдать их именно такими, какими и получила.

Вопрос тут в другом: я должен вернуть r30 наверх неизменённым, да.

mr_doug
()
Ответ на: комментарий от Harald

mflr — move from link register

он используется после bl (branch and link), которая сохраняет адрес своей инструкции + 4 (размер всех инструкций фиксирован и равен точно 4 байта) и передаёт управление по указанному относительному смещению

mtlr — move to link register — перемещает значение из какого-либо регистра общего назначения в LR (link register)

blr переходит по значению в регистре LR (то есть +4 от bl)

mr_doug
()
Ответ на: комментарий от LamerOk

Кстати, да: пул вызовов. Конпелятор определяет максимальный размер, необходимый для цепочки вызовов, и сохраняет все адреса возврата из LR там. Неизвестно, конечно, будет ли это быстрее, чем «указатель стека», потому что последний гарантированно кеширован из избавлен от cache misses.

mr_doug
()
Ответ на: комментарий от mr_doug

я должен вернуть r30 наверх неизменённым, да.

Что потребует организации всё того же сохранения адреса возврата стека в ручную.

LamerOk ★★★★★
()
Ответ на: комментарий от mr_doug

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

Особенно - для рекурсивных вызовов, с глубиной, определяемой в рантайме. ;)

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

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

mr_doug
()
Ответ на: комментарий от LamerOk

А вот это нужно развернуть в «обычные» циклы. И это Generic'ом сейчас неплохо делается.

mr_doug
()
Ответ на: комментарий от mr_doug

> Поделись objdump -D hello для ARM!

objdump с чего? :) мне хелловорлд проще сразу на асме написать, без глибц вообще:

    .text
    .global _start
_start:
    mov     r0, #1
    add     r1, pc, #msg-.-8
    mov     r2, #msg_end-msg
    mov     r7, #4
    swi     #0
    mov     r7, #1
    swi     #0
msg:
    .ascii "Hello, World!\n"
msg_end:
$ /opt/sgpp/bin/arm-none-linux-gnueabi-as -o hello.o hello.s 
$ /opt/sgpp/bin/arm-none-linux-gnueabi-ld -o hello hello.o

здесь просто sys_exit(sys_write(1, «Hello, World!\n», sizeof «Hello, World!\n»)).

а тот код, с которого я угорал над гцц сейчас на работе, а у меня отпуск ;)

arsi ★★★★★
()
Ответ на: комментарий от LamerOk

Хотя для глубины, определяемой в рантайме, нет. А примеры такого можно? Рекурсивная процедура ждёт по сети от удалённого сервера количества её вложений и условия окончания?

mr_doug
()
Ответ на: комментарий от mr_doug

> Что такое #1, #4 и т.п.? Некие смещения в BSS?

ща, то же самое, но с комментами:

    .text
    .global _start
_start:
    mov     r0, #1              @ STDOUT_FILENO
    add     r1, pc, #msg-.-8    @ "Hello, World!\n"
    mov     r2, #msg_end-msg    @ sizeof "Hello, World!\n"
    mov     r7, #4              @ __NR_write
    swi     #0                  @ syscall: sys_write[r7] (int fd [r0], const void *buf [r1], sizeof_t size [r2])
    mov     r7, #1              @ __NR_exit
    swi     #0                  @ syscall: sys_exit[r7] (int retval [r0])
msg:
    .ascii "Hello, World!\n"
msg_end:
arsi ★★★★★
()
Ответ на: комментарий от arsi

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

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

slapin ★★★★★
()
Ответ на: комментарий от arsi

выравнивание по 16-байтной границе для локальных переменных. связано с тем, что у х86 (да и не только) «строчка» кэша — 16 байт

У более-менее современных процессоров — 64 байта. А выравнивание ESP на 16 байт нужно чтобы эффективно сохранять/загружать значения на стеке в XMM регистры.

Relan ★★★★★
()
Ответ на: комментарий от Relan

каким образом выравнивание ESP на 16 байт помогает эффективно сохранять/загружать значения на стеке в XMM регистры?

Goganchic ★★
() автор топика
Ответ на: комментарий от Goganchic

каким образом выравнивание ESP на 16 байт помогает эффективно сохранять/загружать значения на стеке в XMM регистры?

Смотри Intel 64 and IA-32 Architectures Optimization Reference Manual, 4.4. Stack and Data Alignment, а также Appendix D. Stack Alignment.

Relan ★★★★★
()
Ответ на: комментарий от mr_doug

arm

00008318 <main>:
    8318:	e92d4008 	push	{r3, lr}
    831c:	e30803fc 	movw	r0, #33788	; 0x83fc
    8320:	e3400000 	movt	r0, #0
    8324:	ebffffee 	bl	82e4 <_init+0x20>
    8328:	e3a00000 	mov	r0, #0
    832c:	e8bd8008 	pop	{r3, pc}

thumb

00008310 <main>:
    8310:	b508      	push	{r3, lr}
    8312:	f248 30f0 	movw	r0, #33776	; 0x83f0
    8316:	f2c0 0000 	movt	r0, #0
    831a:	f7ff efe0 	blx	82dc <_init+0x20>
    831e:	2000      	movs	r0, #0
    8320:	bd08      	pop	{r3, pc}
    8322:	bf00      	nop

AptGet ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.