Конференция "Прочее" » Передача параметров функции
 
  • SergP © (30.05.16 14:38) [0]
    Допустим у меня есть такая функция с тремя параметрами:
    function Search(count:integer; keyword:int64; var source):integer;

    первый параметр, как я понимаю передается через EAX
    Результат тоже вроде через EAX передается.
    Третий похоже через EDX
    А что со вторым?
    Я не силен в ассемблере, но судя по коду он передается через стек. Если да, то где именно в стеке он находится?
    насколько я понял, то на вершине стека находится адрес возврата (4 байта), а следом за ним параметр, ближе к вершине более младшие его байты. Правильно?

    Т.е. такая функция будет работать?
    (поиск в массиве по полю field. Если надено - возвращается номер элемента массива, если не найдено, то возвращается -1)

    ...
    type TWork=packed record
      field:int64;
      MSet:TMset;
      Probe:Extended;
      end;
    ...
    workarr:packed array of TWork;
    ...
    function Search(count:integer; keyword:int64; var source):integer;
    label
     lbloop, lbcont,lbbreak;
    const
     step=sizeof(TWork);
    asm
           push edx;
           push ecx;
           push ebx;
           mov  ecx,[esp+$10];
           mov  ebx,[esp+$14];
           dec  eax;
    lbloop: cmp  ebx,[edx+$04];
           jnz  lbcont;
           cmp  ecx,[edx];
           jz   lbbreak;
    lbcont: add  edx,step;
           dec  eax;
           jns  lbloop;
    lbbreak:pop  ebx;
           pop  ecx;
           pop  edx;
    end;

  • SergP © (30.05.16 14:50) [1]
    Глянул скомпилированный код.
    Оно там вначале добавило:
    push ebp;    // Зачем делать то, что я не просил?
    mov ebp,esp;
    push ebx;

    и в конце:

    mov eax,ebx; //Зачем оно мне result портит?
    pop ebx;
    pop ebp;
    ret $0008; //что за параметр?
    lea eax,[eax+$00]; //а это вообще что за чудо? или это просто мусор связанный с выравниванием?
  • SergP © (30.05.16 14:53) [2]

    > ret $0008; //что за параметр?


    хотя что это такое вроде понял, если я правильно понял, то это число на которое дополнительно увеличивается ESP после возврата, из-за того что в стеке передавался параметр...
    А вот как насчет всего остального?
  • Rouse_ © (30.05.16 15:14) [3]
    Вот так напиши:

    function Search2(count:integer; keyword:int64; var source):integer;
    const
    step = sizeof(TWork);
    asm
     push eax
     mov  ecx, eax
     mov  edx, [edx]
    @compare:
     mov eax, [esp + 12]
     cmp [edx], eax
     jne @next
     mov eax, [esp + 16]
     cmp [edx + 4], eax
     je @done
    @next:
     add edx, step
     dec ecx
     jnz @compare
     mov ecx, [esp]
     inc ecx
    @done:
     pop eax
     sub eax, ecx
    end;
  • SergP © (30.05.16 15:23) [4]

    >   mov eax, [esp + 12]
    >   cmp [edx], eax
    >   jne @next
    >   mov eax, [esp + 16]
    >   cmp [edx + 4], eax
    >   je @done


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

    lbloop: cmp  eax,[edx+$04];
           jnz  lbcont;
           cmp  ecx,[edx];
           jz   lbbreak;



    Вроде так заработало...:

    function Search(count:integer; keyword:int64; var source):integer;
    label
     lbloop, lbcont,lbbreak;
    const
     step=sizeof(TWork);
    asm
           push edx;
           push ecx;
           mov  ebx,eax;
           mov  ecx,[ebp+$08];
           mov  eax,[ebp+$0C];
           dec  ebx;
    lbloop: cmp  eax,[edx+$04];
           jnz  lbcont;
           cmp  ecx,[edx];
           jz   lbbreak;
    lbcont: add  edx,step;
           dec  ebx;
           jns  lbloop;
    lbbreak:pop  ecx;
           pop  edx;
    end;



    Хотя какие-то странности пока имеются... Но возможно они связаны с другими участками кода.
  • SergP © (30.05.16 15:25) [5]

    > Хотя какие-то странности пока имеются... Но возможно они
    > связаны с другими участками кода.


    А. ну да. понял. я же по массиву вверх иду, а счетчик вниз считает... :)))
  • Rouse_ © (30.05.16 15:28) [6]
    в регистрах? ну тогда вот так:

    function Search2(count:integer; keyword:int64; var source):integer;
    const
    step = sizeof(TWork);
    asm
     push eax
     push esi
     mov  esi, [esp + 16]
     push edi
     mov  edi, [esp + 24]
     mov  ecx, eax
     mov  edx, [edx]
    @compare:
     cmp  [edx], esi
     jne  @next
     cmp  [edx + 4], edi
     je   @done
    @next:
     add  edx, step
     dec  ecx
     jnz  @compare
     mov  ecx, [esp + 8]
     inc  ecx
    @done:
     pop  edi
     pop  esi
     pop  eax
     sub  eax, ecx
    end;
  • Rouse_ © (30.05.16 15:30) [7]
    ну и пример вызова:

    var
     workarr: packed array of TWork;
    begin
     try
       SetLength(workarr, 10);
       workarr[0].field := 456;
       workarr[7].field := $CDB81F21CDB81F20;
       Writeln(Search2(10, $CDB81F21CDB81F20, workarr)); // result must be 7
  • SergP © (30.05.16 16:13) [8]

    >   dec  ecx
    >   jnz  @compare


    и еще вопрос: Вроде для таких вещей можно использовать команду loop, если бы она работала с 32-разрядным регистром.

    Но вот по информации в инете не могу понять: какой все-таки регистр использует команда loop :  CX или ECX?

    например как понять вот это:

    > Команда LOOP уменьшает регистр-счетчик без изменения какого-
    > либо из флагов. Затем проверяются условия характерные для
    > конкретной формы используемой команды LOOP. Если условия
    > удовлетворяются, то происходит короткий переход на метку,
    >  заданную операндом команды LOOP.
    >
    > Если атрибут размера адреса равен 16 бит, то в качестве
    > счетчика используется регистр CX, иначе — ECX.


    Что подразумевается под атрибутом размера адреса?
  • Rouse_ © (30.05.16 16:21) [9]

    > и еще вопрос: Вроде для таких вещей можно использовать команду
    > loop, если бы она работала с 32-разрядным регистром.

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


    > Что подразумевается под атрибутом размера адреса?

    Префикс, указывающий с каким размером адресного регистра мы работаем.

    Вот так цикл будет идти 65538 раз
     mov ecx, 65538
    @loop:
     loop @loop


    А вот так только два раза (т.к. в регистр CX будет равен двойке):

     mov ecx, 65538
    @loop:
     db $67 // Address-size override prefix
     loop @loop
  • SergP © (30.05.16 17:08) [10]
    Чем отличаются команды JE от JZ и JNE от JNZ?
  • SergP © (30.05.16 17:19) [11]

    > add  edx, step


    и еще: будет ли LEA edx,[edx+step] в данном случае быстрее?
  • Rouse_ © (30.05.16 17:21) [12]
    Ничем, ну только разве что читабельностью.
    Для булевых операций обычно примеряют E (после test/cmp), для математических (add/sub/inc/dec) Z. В данном случае вычитается единица, и ожидаем результатом ноль, поэтому JNZ (опкоды у этих инструкций идентичные)
  • Rouse_ © (30.05.16 17:22) [13]

    > и еще: будет ли LEA edx,[edx+step] в данном случае быстрее?

    нет, идентичны по тактам будут.
  • SergP © (31.05.16 14:59) [14]
    Почему в случае сабжевого кода (пусть он даже и не совсем верный), компилятор добавлял:

    В начале:
    push ebp;    
    mov ebp,esp;
    push ebx;


    и в конце:
    mov eax,ebx;
    pop ebx;
    pop ebp;
    ret $0008;



    А в случае [6], только

    В начале
    push ebp;    
    mov ebp,esp;



    и в конце:


    pop ebp;
    ret $0008;



    т.е. чем вызвана эта самодеятельность компилятора:
    ...
    push ebx;
    ...
    mov eax,ebx;
    pop ebx;
    ...


    ?
    и как в будущем предугадать такие козни компилятора?
  • dmk © (31.05.16 15:39) [15]
    >т.е. чем вызвана эта самодеятельность компилятора:
    Отладкой. Для Debug Kernel нужно. Точки останова и т.п.
  • dmk © (31.05.16 15:40) [16]
    Можно внешний obj подтянуть без использования ebp, но там точки останова не будут работать.
  • SergP © (31.05.16 16:00) [17]

    > dmk ©   (31.05.16 15:40) [16]
    >
    > Можно внешний obj подтянуть без использования ebp, но там
    > точки останова не будут работать.


    я не про ebp (то что компилятор всегда вставляет - то не страшно), а про то, что он вставляет непонятно когда... Иногда вставляет, а иногда нет.
    Вот это:

    ...
    push ebx;
    ...
    mov eax,ebx;
    pop ebx;
    ...

  • dmk © (31.05.16 16:31) [18]
    Сложно сказать. У меня Delphi XE6 и такого нет. Только BP, EBP, RBP.
    Не нравится — вставляй по старинке obj'екты. Компилируй masm или еще чем.
    Тогда точно будет так как написано.
    Во времена Trubo Pascal и Delphi 3 я так и делал. Вставлял OBJ.

    Кстати проверить еще можно отключив debug.
    Run without Debugging Shitf+ Ctrl + F9.
  • Rouse_ © (31.05.16 16:57) [19]

    > Почему в случае сабжевого кода (пусть он даже и не совсем
    > верный), компилятор добавлял:
    >

    Потому что, по пунктам:

    1.
    В начале
    push ebp;    
    mov ebp,esp;

    и в конце:

    pop ebp;
    ret $0008;


    Это стековый фрейм, он добавляется автоматом при использовании процедурой самого стека (под локальные переменные, под входные параметры).

    2.
    ret $0008
    чистится стек из-за соглашения FASTCALL (в частности нивелируется передача второго параметра через стек)

    3.
    push ebx;
    и в конце:
    mov eax,ebx;
    pop ebx;

    EBX обьектный регистр, который нужно сохранять, видя что ты его используешь, компилер тебя подстраховывает
 
Конференция "Прочее" » Передача параметров функции
Есть новые Нет новых   [134432   +19][b:0][p:0.002]