invoke и локальные переменные ...LOCAL wc:dword

Тема в разделе "WASM.ASSEMBLER", создана пользователем calidus, 19 дек 2007.

  1. calidus

    calidus Member

    Публикаций:
    0
    Регистрация:
    27 дек 2005
    Сообщения:
    618
    Допустим имеется LOCAL wc:dword переменная в процедуре обычно ссылка на нее
    invoke xxxx, addr wc

    Но адрес АПи функции находится в переменной dd то есть \то прототип инвока. просто идет пушы всех параметров и колл , везде стоят offset заместо ADDR . Проблема в том что я не могу получить ссылку на wc ...никак ! говорит неправильная метка для OFFSET
    ,если получить так lea eax ,wc то это фигня , здесь используются регистры ... Я вызываю много апи в цикле Например Гетмесадж с обработки окна приложения и значит там я буду постояно колдовать с регистрами ... это как то не нравится . Переносить метку из локальной в дату тоже не идет. Подскажите кто сталктвался ?
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    lea edx,[wc]

    typedef proto...

    А вообще, потрудись писать по-русски, т.к. твой поток символов непонятен.
     
  3. calidus

    calidus Member

    Публикаций:
    0
    Регистрация:
    27 дек 2005
    Сообщения:
    618
    =) помойму и так понятно ...если знаком с асмом понятно . Да допустим lea edx,[wc] но есть функции гду edx задействован это значит сохранять регистры и все такое , это лишний код. Например цикл обработки сообщений приложения ... там параметр идет пуш потом колл. Так значит я буду увеличивать код там. А с Offset можно что то сделать ?


    typedef proto...
    да да ...но у меня там за 50 штук апи и все тайпдефы делать это рехнуться ...
     
  4. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Если б ты на асме писал, а то ж кириллицу мучаешь :)

    Нет, т.к. offset для получения "статического" адреса, известного на стадии компиляции. А локальные переменные - это уже динамические адреса и получать их нужно во время работы с помощью lea или другими способами.
     
  5. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    Не используй invoke и LOCAL, используй FPO и будет тебе щастье. Если ты еще не знаешь, то FPO = Frame Pointer Omission, т.е. адресация локальных переменных непосредственно через ESP, что освободит тебе регистр EBP для прочих нужд.
     
  6. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    AshBone
    Руками смещения высчитывать? Или есть автоматизация в масме? Что-то подобное делал когда-то, через option prologue..
     
  7. t00x

    t00x New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2007
    Сообщения:
    1.921
    если переменных в стеке мало, будет понятнее весь механизм передачи параметров руками сделать.
    вообще есть команды enter и leave.
     
  8. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    IceStudent

    Не знаю... я в масме делаю так (привык давно):

    someproc PROC

    push EBX
    push EBP
    push ESI
    push EDI

    someproc_saved = 10h
    someproc_locals = 00h
    someproc_all = someproc_saved+someproc_locals

    if someproc_locals gt 0
    sub ESP,someproc_locals
    endif


    mov EAX,dword ptr [ESP+someproc_all+04h] ; 1й параметр (если stdcall).

    ....................

    if someproc_locals gt 0
    add ESP,someproc_locals
    endif

    pop EDI
    pop ESI
    pop EBP
    pop EBX

    ret <размер параметров в байтах>

    someproc ENDP

    Так привык уже давно и не знаю, как по другому... а то, что вручную считать стек... как по мне - это не сложно.


    t00x
    Если ты еще не в курсе, то push EBP/mov EBP,ESP/sub ESP,xx и mov ESP,EBP/pop EBP быстрее, чем enter и leave соответственно
     
  9. t00x

    t00x New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2007
    Сообщения:
    1.921
    AshBone
    в курсе. операции со стеком не отличаются быстродействием.
    с другой стороны эти операции зачем-то придумали.
     
  10. Mikl_

    Mikl_ New Member

    Публикаций:
    0
    Регистрация:
    14 ноя 2006
    Сообщения:
    907
    t00x
    обычно их (leave, enter) используют для уменьшения размера кода, кроме того можно сделать такую вещь mov ebx,esp и далее обращаться к стеку через ebx - кодировка
    cmp dword ptr [esp+8],2 - 837C240802 - 5 байт
    cmp dword ptr [ebp+8],2 - 837D0802 - 4 байта
    cmp dword ptr [ebx+8],2 - 837B0802 - 4 байта
     
  11. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    Mikl__

    Речь шла об освобождении регистров для общих цеей, а не наоборот.

    Тем более, что освобождение регистра EBP под другие (не фреймовые) нужды дает еще один плюс - его сохраняют API.
     
  12. Mikl_

    Mikl_ New Member

    Публикаций:
    0
    Регистрация:
    14 ноя 2006
    Сообщения:
    907
    AshBone
    Так API и EBX, и ESI, и EDI не портят;) просто кодировка косвенного обращения к регистру ESP на 1 байт длиннее чем через EBP ESI EDI EBX вот я об этом написал
     
  13. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    Mikl__

    Я ГОВОРЮ О ТОМ, что если тебе, например, нужно держать !!!! ЧЕТЫРЕ !!!! указателя на важные структуры все время выполнения процедуры, и при этом вызывать апи, то если четвертый указатель ты будешь держать в EDX,EAX или ECX, то на каждый вызов апи придется добавлять push/pop. Вот и посчитай, что чаще - обращение к локальным переменным или вызов апи.
     
  14. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    calidus
    AshBone
    Вам еще долго учится :) С такой постановкой задач вы еще не скоро будете готовы довести до конца хотя бы один маломальски сложный проект.
    Вам бы Агнера Фога почитать. Благо перевод есть тут на Васме.

    mov reg1, reg2 - 1 МОП
    mov reg1, [Base+Index*Scale+Offset] (как частный случай mov reg1, [Mem]) - 2 МОПА - при условии, что данные содержатся в кэше

    Использовать ebp для собственных нужд бредовая идея. Так как
    1. Снижается читабельность исходника. (Это самое меньшее из зол)
    2. Резко ухудщается модифицируемость кода. При любом внесении изменения в код придется локальные переменные пересчитывать. Ошибки попрут косяками.
    3. При обращеннии к участку памяти по регистру ebp по-умолчанию используется сегмент ss. то есть если вы захотите адресовать по нему собственные данные придется использовать переопределения сегмента на DS. А на это вроде тратится дополнительный такт.

    Таким образом вы ничего не выигрываете в производительности - вы в ней проигрываете. Плюс проигрываете по другим аспектам.

    Второй пример:
    Допустим самая малоизвестная и абсолютно нифига не документрованная API функция ReadFile, к примеру заняла 100000534 такта. То с вашей мега-оптимизацией она будет выполнена за 100000532 такта.

    AshBone
    Плюс к этому помнится мне у современных процессоров есть около 20 регистров недоступных программно, они используются для обеспечения работы такой штуки как выполнение команд непопорядку. Так что на то что ты держишь!!! ЧЕТЫРЕ!!! регистра процессору !!!НАЧХАТЬ!!!

    Не хватает регистров - используй локальные переменные. Если ты обращаешься по выровненным адресам памяти - это даст куда больший прирост производительности, чем махинации с ebp.

    Короче книжки умные читайте. Люди за 40 лет придумали куда больше интересных вещей чем вы за 2 часа (максимум) изучения ассемблера
     
  15. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    Мне кажется, что не нужно переопределять сегмент на DS. Дескриптор сегмента стека вроде настроен на те же адреса, что и сегмента данных...

    Это кому как.

    Это тоже ВЕСЬМА спорный вопрос.

    Ага. Также они придумали оптимизирующие компиляторы, которые тоже используют FPO.

    Пришел умный взрослый дядя и правильно развесил ярлыки )))
     
  16. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Ну что русскому хорошо, то фашисту смерть. Я в качестве С компилятора использую Vector C86. Он не только использует FPO он еще и процедуры умеет раскрывать и операции распараллеливать короче делает всю ту фигню на которую я не стал бы тратить время.
    С другой стороны у любого компилятора пускай даже оптимизирующего на первом месте идет корректность генерируемого кода, а только потом оптимальность. А корректность генерируемого кода во всех частных случаях требует жертв.

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

    Касательно читабельности кода приведу маленький пример
    Код (Text):
    1. proc SomeProc
    2.  local SomeVar: DWORD
    3.  
    4.  uses ebx, esi, edi                           ;push ebx push esi push edi
    5.  invoke SomeAPIFunction, SomeVar    ;здесь SomeVar имя локальной переменной без [], поэтому макрос pushd внутри invoke заменит
    6.                                                    ;его на lea edx, [SomeVar]
    7.  uses_end                                      ;mov edi, [esp] mov esi, [esp+4] mov ebx, [esp+8] add esp, 12
    8.  ret
    9. endp
    Данный код нисколько не уступает твоему в оптимальности. Плюс к этому он куда легче читается и с ним гораздо легче работать.
    Так как перед вызовом SomeAPIFunction можно легко добавить n-строк кода. И при этом не заморачиваться на том, сколько в очередной раз нужно прибавить к esp чтобы попасть куда надо. Это и есть так называемая читабельность и модифицируемость кода.

    Я поэкпериментировал по пункту 3 на Fasm'e да переопределения сегмента и впрямь не нужно. Так что пункт 3 лишний.
    Но сути это не меняет.

    А по поводу возможности выполнения больших проектов я на полном серьезе говорю.
    Если код забит такой фигней как ручное вычисление указателя стэка или того хуже длина строк вычисляется вручную, то при достижении
    определенного объема и сложности данному коду придет крындец.
     
  17. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    Оптимальность в данном случае зависит ОТ КОНТЕКСТА, повторю еще раз: ЕСЛИ ТРЕБУЕТСЯ ХРАНИТЬ ЧЕТЫРЕ УКАЗАТЕЛЯ и вызывать много раз апи, то, блин, придется сохранять и восстанавливать КАЖДЫЙ РАЗ четвертый указатель, который мог бы храниться в EBP и восстанавливаться непосредственно апи-функцией, которая и так будет это делать вследствие конвенции stdcall.

    Что есть "корректность" и почему она так важна?

    Если ТЕБЕ так удобней, то не надо подводить теоретическую базу вроде "корректности". Я и так не "заморачиваюсь", а делаю это уже практически на автомате.

    Отож. Перед тем как морозить глупость - поэкспериментируй. И, пожалуйста, поменьше обличающих, разоблачающих и прочих пафосных и безапелляционных заявлений. Если не уверен - говори "Я думаю" или "мне кажется".

    Опять же, отучаемся говорить за всех )
     
  18. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    AshBone
    Корректность генерируемого кода - это когда компилятор генерирует верный код даже при нестандартных приемах программирования.
    Ты слышал когда-нибудь о багах компиляторов?

    Например такой код
    invoke SomeApiFunc, edx, addr SomeVar - Будет скомпилирован некорректно
    Для Fasm'а это простительно так как такую ошибку легко обнаружить. Но если такое будет делать какой-нибудь компилятор C, то для него это будет фатальным. И для того чтобы избежать таких багов часть оптимизации приносится в жертву. Поробуй дизассемблировать что-нибудь сделанное на Visual Studio, например.Тогда поймешь о чем я

    Тебе мало аргументов?

    Хорошо вот тебе последний практический пример

    Эта процедура считывает весь файл в память.(Довольно примитивно, конечно, но для примера сойдет)
    Код (Text):
    1. proc ReadFileAll Path, pBufferSize
    2.   local hFile: DWORD,MemSize: DWORD, Mem:DWORD
    3.  
    4.     invoke CreateFile, [Path], GENERIC_READ, ...
    5.     mov [hFile], eax
    6.     add eax, 1
    7.     je .lbEnd  
    8.       invoke GetFileSize, [hFile], 0
    9.       test eax, eax
    10.       je .lbEnd
    11.         mov [MemSize], eax
    12.         invoke GlobalAlloc, GMEM_FIXED. eax
    13.         test eax, eax
    14.         je .lbEnd
    15.           mov [Mem], eax
    16.           invoke ReadFile, [hFile], eax, [MemSize], MemSize, 0
    17.           mov eсx, [MemSize]
    18.           mov edx, [pBufferSize]
    19.           mov [edx], eсx
    20.           mov eax, [Mem]
    21. .lbEnd:
    22. ret
    23. endp
    В данном коде можно обойтись без локальных переменных. вместо этого использовав регистры ebx, esi, edi.
    К твоему сведению подавляющее большинство API функций очень тяжелы. Так как их большинство заставляет поток переключаться в режим ядра. К тому же некоторые по природе своей асинхронные и им перед выходом необходимо ждать пока система вернет ответ на их запрос.
    Вообще если тупо замерить время от входа в данную функцию до выхода из нее например с помощью rdtsc. То результат будет исчислятся миллионами тактов. Причем результат будет варьироваться в очень широких пределах.
    Ты же заменив локальные переменный регистрами сэкономишь что-то где-то около 20 тактов. Потеряв при этом информацию о том, какая переменная для чего используется. А ведь процедура подобная может быть в разы сложнее все зависит от КОНТЕКСТА.

    Подобная оптимизация бесмысленна во всех контекстах.

    Другое дело, когда небольшой участок кода вызвается несколько тысяч раз в секунду. Он не содержит Api функций. Там каждый МОП на вес золота. Но там используются другие методы оптимизации. память выравнивают, команды распараллеливают, подгоняют данные под линии кэша. Это несоизмеримо выше поднимает производительность чем твои махинации с ebp. К тому же часто такой код гораздо лучше реализовать на MMX или SSE где ebp и рядом не стоит.

    Короче,кончай маяться дурью и потрать хотя бы третий час на изучение ассемблера.
     
  19. calidus

    calidus Member

    Публикаций:
    0
    Регистрация:
    27 дек 2005
    Сообщения:
    618
    Miller Rabin Написано хорошо , но обязательно обосрать надо было ))))))) хахаха
    вот как раз с таким отношением ничего и не бывает ))))) А проект уже завершен, и большой!!!! Проблема при переносе кода на кросплатформенность , при этом метки нужно по другому чуть использовать , поэтому пошел вот такой вопрос...

    А так про все остальное это понятно , я же не в фантазиях код переписываю =) Спасиб =)
     
  20. AshBone

    AshBone New Member

    Публикаций:
    0
    Регистрация:
    12 дек 2007
    Сообщения:
    101
    и

    и

    Обходись. Если ты до сих пор не догнал, что я имел в виду, то я умываю руки.