Как происходит резервирование места в стеке?

Тема в разделе "WASM.ASSEMBLER", создана пользователем 123dragon, 17 янв 2005.

  1. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Код (Text):
    1. function:
    2.     sub esp, local_vars_room    ; A
    3.     and esp, -16
    4.     ...
    5.     call    nested_function ; ESP here will point to its own stack frame
    6.     mov, ...    ; And here I want it to point to THIS stack frame (A).
    7.     ...
    8.     ret
     
  2. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Нет, ну если вы сохраняете ESP в стеке чтоб свободно использовать ESP как регистр общего назначения внутри функции, то как вы потом восстановите начальное значение ESP из стека? POP уже не поможет.
     
  3. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Как раз для этого и нужен пролог/эпилог - предыдущий ESP хранится в EBP:
    Код (Text):
    1. push ebp
    2. mov  ebp, esp
    3. sub  esp, room
    4. ...
    5. mov   esp, ebp
    6. pop  ebp
    7. ret
     
  4. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Код (Text):
    1. sub  esp, room
    2. ...
    3. add   esp, room
    4. ret
     
  5. Turkish

    Turkish New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2004
    Сообщения:
    80
    Адрес:
    Russia
    А потом менять смещение относительно esp после каждого push/pop
     
  6. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Quantum



    такой вариант не будет работать, т.к. у AsmGuru62 используется выравнивание стэка по границе 16 байт (and esp, -16). Хотя в таком случае совершенно не вижу смысла использовать esp вместо ebp - ничего, кроме увеличения опкодов это не даст. Можно делать так:
    Код (Text):
    1.  
    2. function:
    3.         push ebp
    4.         mov  ebp, esp
    5.     and  ebp, -16  ; aligned stack pointer
    6.     sub  esp, local_vars_room + 16  ; A
    7.     ...
    8.     ...
    9.     add  esp, local_vars_room + 16
    10.         pop  ebp
    11.     ret




    Другое дело, что можно построить все функции так, что стэк будет выровнен на 16 байт (добавить фиктивные аргументы, если необходимо). Правда придётся отказаться от линковки с чем попало.





    Turkish >




    C этим прекрасно справляется препроцессор fasm'а :derisive:
     
  7. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Quantum



    Я о таком тоже думал. Не подходит, потому что иногда я выравниваю ESP:
    Код (Text):
    1. and esp, -16
     
  8. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    AsmGuru62

    Да всё я прекрасно понимаю. Просто из вашего предыдущего поста (20:52:33) можно заключить что без пролога/эпилога вообще не обойтись, а это происходит лишь в редких случаях. Это скорее исключение из правил, нежели классический пример.
     
  9. Turkish

    Turkish New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2004
    Сообщения:
    80
    Адрес:
    Russia
    Если не нужна оптимизация по скорости напиши enter <скока надо байт>,0 и leave перед ret
     
  10. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
  11. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Вот такая идея была - берём блок памяти у Windows, скажем 256 Kb. Устанавливаем EBP на конец этого блока. А потом вот так:
    Код (Text):
    1. func:
    2.     push    ebp
    3.     sub ebp, local_size_aligned
    4.     ...
    5.     mov [ebp+4], eax    ; Use locals...
    6.     ...
    7.     pop ebp
    8.     ret


    Бредово, а?
     
  12. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Turkish



    enter не только медленнее, но и места больше занимает. Вообще не вижу смысла в этой инструкции.
     
  13. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    AsmGuru62

    А зачем это? Чтоб можно было много локальных переменных хранить?
     
  14. Turkish

    Turkish New Member

    Публикаций:
    0
    Регистрация:
    25 окт 2004
    Сообщения:
    80
    Адрес:
    Russia
    А у меня всегда получалось сочетание enter leave 5 байт, а вариант
    Код (Text):
    1.  
    2. push ebp
    3. mov ebp,esp
    4. sub esp,100
    5.  
    6. mov esp,ebp
    7. pop ebp


    получается в 9 байт
     
  15. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Turkish

    Хммм... Действительно, хотя leave я и сам всегда использую.
     
  16. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Quantum

    Если EBP никто не трогает (кроме самих процедур), тогда не нужно дополнительного кода по выравниванию и адресация идёт через [EBP+n]. И волки сыты, и овцы целы. Но появляется проблема с CALLBACK... или мне это кажется?
     
  17. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    AsmGuru62

    Да, с callback-ом будут проблемы, ведь callback использует стандартный стек, а не тот, что в ebp.
     
  18. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Дополнительный код для выравнивания стэкового фрейма по границе N байт не нужен только в таком случае:

    каждая функция в программе имеет количество dword аргументов равное K * N / 4 - 1 (где K = 1, 2, ...)

    т.е при N = 16 байт количество аргументов должно быть 3, 7, 11.

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



    Если условие не выполняется, то возможны такие варианты:



    1) - копируем esp в reg32 (любой отличныый от esp), "свободно" использовать его в функции нельзя будет. intel C++ использует ebx;

    - этот регистр используется для адресации аргументов функции;

    - выделение памяти sub esp, room

    - команда and [esp], -N;

    - адресация к локальным переменным функции идёт через esp.

    - восстановление esp в эпилоге: mov esp, reg32



    2) - копируем esp в любой (отличныый от esp) регистр reg32;

    - sub esp, room + N (выделяем место в стэке "с запасом" равным N); используется для адресации аргументов функции;

    - and reg32, -N; этот регистр используется для адресация к локальным переменным функции;

    - восстановтление esp в эпилоге: add esp, room + N



    Вариант 2 более компактен. Но оба эти варианта (и любые другие) будут использовать 2 регистра. Это происходить по тому, что при использовании команды and reg32, -N происходит "потеря информации" (т.е младших битов), и её приходится где-то сохранять.

    При выполнении условия K * N / 4 - 1 мы точно знаем значение мдадших битов, поэтому необходимости их сохранять нет. В таком случае можно обойтись одним регистром.





    AsmGuru62 >




    А в чём приимущества метода? Всё равно используются 2 регистра: один для адресации к аргументам, другой к локальным...
     
  19. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Вообще-то аргументы только в CALLBACK. Все остальные аргументы - в регистрах. Скорее всего, я пойду по пути [esp+n] адресации. Функции с локальными переменными будут немного длинее. В больших проектах это не так важно. А ещё лучше, генерировать обычный пролог/эпилог там где нет выравнивания локальных переменных. А где есть - туда [esp+n].
     
  20. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Передача аргументов в регистрах, помимо достоинств имеет и недостатки.

    Например, аргумент переданный через стэк можно сразу использовать в качестве переменной (которую не нужно сохранять при API вызовах):
    Код (Text):
    1.  
    2.         push     5
    3.         call     foo
    4. ;
    5. foo:
    6. .loop:
    7. ;
    8.         dec      dword[esp+4]
    9.         jnz      .loop
    10. ;




    AFAIK выравнивание по 8 или 16 байт очень редко необходимо. обычно для fpu или xmm регистров - а их-то как раз и можно загружать при вызове функции. Если же это всёже нужно, то как правило в очень ограниченном количестве функций (которые вполне вероятно ещё и оптимизируются особо) - а если так, то IMHO ничего страшного нет в ручном построении стэкового фрейма.



    Конечно, наиболее гибко - когда есть возможность использовать esp или ebp по желанию.

    Хотя я видел всего 2 аргумента "за" ebp:

    - это меньше на байт (что далеко не всегда верно, т.к. дополнительный свободный регистр - это меньшее количество push / pop и локальных переменных)

    - так исторически сложилось со времён 16 бит, и нужно уважать традиции :).



    Возможно, в языках вроде pasсal как-то можно извлечь выгоду из ebp, когда разрешены вложенные функции, и можно добраться до локальных переменных "родительской" функции, но я что-то не встречался с подобными вещами в коде произведённом современными компиляторами - да это и понятно: можно передавать адрес локальной переменной в качестве параметра, и не усложнять чрезмерно компилятор.