Организация регистровых вызовов в FASM

Тема в разделе "WASM.ASSEMBLER", создана пользователем Miller Rabin, 7 май 2007.

  1. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Придумал тут такую фишку. Может кому интересно будет.
    Думал над организацией регистровых вызовов. Таких, чтобы, в процедуре можно было указывать те параметры которые, нужно помещать не в стэк, а в соответствующие регистры. А при вызове stdcall этой процедуры компилятор сам бы корректно генерировал необходимый код.

    Например:

    proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx
    mov eax, [Param1]
    mov ebx, Param2
    mov esi, [Param3]
    mov edi, Param4
    ret
    endp

    proc Main
    stdcall CheckPoint, 1, 2, 3, 4
    ret
    endp

    чтобы в после раскрытия получился бы такой код
    proc Main
    mov edx, 4
    push 3
    mov ecx, 2
    push 1
    call CheckPoint
    retn 8
    endp

    а внутри процедуры код получился бы такой

    proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx
    mov eax, [Param1]
    mov ebx, ecx
    mov esi, [Param3]
    mov edi, edx
    ret
    endp
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    а чем обычный фасткал не устраивает?
     
  3. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Вот что у меня получилось

    ;Надстройка над стандартным макросом proc
    ;Обеспечивает поддержку регистрового объявления аргументов
    ;Например: proc Test Arg1, Arg2:edx,Arg3, Arg4:ecx
    ;Здесь Arg1, и Arg3 - передаются через стэк
    ;Arg2 - edx
    ;Arg4 - ecx
    macro proc [args]
    {
    common
    match name params, args>
    \{
    define@call name,<params
    \}
    }

    ;Этот макрос объявляет регистровые переменные, анализируя списко параметров процедуры
    macro DefineRegVariables@proc [args]
    {
    forward
    match argname:reg, args
    \{
    argname equ reg
    \}
    restore reg
    }

    ;Этот макрос определяет все переменные процедуры
    ;В него параметры уже передаются корректно. Имя процедуры - один параметр,
    ;а аргументы процедру другой параметр
    macro define@call name, statement
    {
    common
    define@prototype name,<statement>
    DefineRegVariables@proc statement
    macro resetargs@proc
    \{
    resetargs <name#.ArgNameList>
    \}

    define@proc name, <name#.ParamList>
    }

    ;Этот макрос определяет списки аргументов процедры.
    ;Список Параметров - Для хранения списка параметров, который будет передан оригинальному макросу proc
    ;Списов Типов аргументов - Для возможности корректного вызова процедуры
    ;Список имен аргументов - Для объявления/удаления необходимых переменных внутри процедуры
    ;Все списки заканчиваются символом @@. Я ТАК ЗАХОТЕЛ!!!
    macro define@prototype name, statement
    {
    common
    TConstList name#.ParamList
    TConstList name#.ArgTypeList
    TConstList name#.ArgNameList
    setargs@proc name, statement
    name#.ArgTypeList.Append @@
    }

    ;Этот макрос заполняет списки, объявленные в макросе define@prototype
    ;ParamList заполняется списком параметров, которые будут переданы оригинальному макросу proc
    ;ArgTypeList заполняется типами аргументов. Если аргумент регистровый то, именем регистра, а если стэковый
    ;,то значением StackArg
    ;ArgName заполняется списком имен регистровых переменных
    macro setargs@proc name, [arg]
    {
    forward
    macro name#.PushParam
    \{
    name#.ParamList.Append arg
    name#.ArgTypeList.ReverseAppend <StackArg>
    \}
    match argname:reg, arg
    \{
    name#.ArgTypeList.ReverseAppend reg
    name#.ArgNameList.Append argname
    purge name#.PushParam
    macro name#.PushParam
    \\{ \\}
    \}
    name#.PushParam
    purge name#.PushParam
    }

    ;Этот макрос удалят объявленные переменные.
    macro resetargs arg
    {
    purge resetargs@proc
    match all,arg \{ restore all \}
    }

    ;Этот макрос завершает процедуру чистит внутренние переменные и вызывает оригинальный макрос endp
    macro endp
    {
    resetargs@proc
    endp
    }
     
  4. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Теперь для того чтобы корректно вызывать данный код. Можно модифицировать процедуру stdcall

    ;Этот макрос формирует код для корректного вызова процедур в Stdcall
    ;Поддерживаются регистровые вызовы
    macro stdcall name,[arg]
    {
    common
    CallList equ name#.ArgTypeList
    reverse
    match Param=,ResultList, CallList
    \{
    if Param in Registers
    argmov Param, arg
    else if (~Param eq @)
    pushd arg
    end if
    CallList equ ResultList
    \}
    common
    call name
    }
     
  5. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    К сожалению данная штука работает только если процедура объявлена непосредственно перед ее использованием.
    Связано это с тем что если вызвать процедуру до ее объявления, то списки не будут определены

    Поэтому можно добавить еще вот такую штуку

    ;Объявляет прототип процедуры. Это позволяет осуществлять корректный вызов процедур до ее объявления
    macro prototype [args]
    {
    common
    match name params, args>
    \{
    define@prototype name,<params
    \}

    }

    Такой код
    prototype CheckPoint Param1, Param2:ecx, Param3, Param4:edx

    proc Main
    stdcall 1,2,3,4
    ret
    endp

    proc CheckPoint Param1, Param2:ecx, Param3, Param4:edx
    ...
    ret
    endp

    Будет корректно скомпилирован
     
  6. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Great
    Я ничего не знаю про fastcall в Fasm. И примеров с его использованием не видел.
    По-крайне мере поиск по словам в его Файлах ничего не дал

    Может версия старая?
     
  7. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Файл proc64.inc
    Код (Text):
    1. macro fastcall proc,[arg]
    2.  { common local stackspace,argscount,counter
    3.     if argscount and 1
    4.      stackspace = (argscount+1)*8
    5.     else
    6.      stackspace = argscount*8
    7.     end if
    8.     counter = 0
    9.     if stackspace
    10.      sub rsp,stackspace
    11.     end if
    12.    forward
    13.     if ~ arg eq
    14.      counter = counter + 1
    15.      if counter <= 4
    16.        if arg eqtype 0
    17.     size@arg = 8
    18.        else if arg eqtype byte 0
    19.     virtual at 0
    20.      mov [rbx],arg
    21.      local opcode
    22.      load opcode byte from 0
    23.      if opcode and 0F8h = 48h
    24.       size@arg = 8
    25.      else if opcode = 66h
    26.       size@arg = 2
    27.      else if opcode = 0C7h
    28.       size@arg = 4
    29.      else
    30.       size@arg = 1
    31.      end if
    32.     end virtual
    33.        else
    34.     virtual at 0
    35.      inc arg
    36.      local opcode
    37.      load opcode byte from 0
    38.      if opcode = 67h
    39.       load opcode byte from 1
    40.      end if
    41.      if opcode and 0F8h = 48h
    42.       size@arg = 8
    43.      else if opcode = 66h
    44.       size@arg = 2
    45.      else if opcode = 0FFh
    46.       size@arg = 4
    47.      else
    48.       size@arg = 1
    49.      end if
    50.     end virtual
    51.        end if
    52.        if counter = 1
    53.     if size@arg = 8
    54.      if ~ arg eq rcx
    55.       mov rcx,arg
    56.      end if
    57.     else if size@arg = 4
    58.      if ~ arg eq ecx
    59.       mov ecx,arg
    60.      end if
    61.     else if size@arg = 2
    62.      if ~ arg eq cx
    63.       mov cx,arg
    64.      end if
    65.     else if size@arg = 1
    66.      if ~ arg eq cl
    67.       mov cl,arg
    68.      end if
    69.     end if
    70.        else if counter = 2
    71.     if size@arg = 8
    72.      if ~ arg eq rdx
    73.       mov rdx,arg
    74.      end if
    75.     else if size@arg = 4
    76.      if ~ arg eq edx
    77.       mov edx,arg
    78.      end if
    79.     else if size@arg = 2
    80.      if ~ arg eq dx
    81.       mov dx,arg
    82.      end if
    83.     else if size@arg = 1
    84.      if ~ arg eq dl
    85.       mov dl,arg
    86.      end if
    87.     end if
    88.        else if counter = 3
    89.     if size@arg = 8
    90.      if ~ arg eq r8
    91.       mov r8,arg
    92.      end if
    93.     else if size@arg = 4
    94.      if ~ arg eq r8d
    95.       mov r8d,arg
    96.      end if
    97.     else if size@arg = 2
    98.      if ~ arg eq r8w
    99.       mov r8w,arg
    100.      end if
    101.     else if size@arg = 1
    102.      if ~ arg eq r8b
    103.       mov r8b,arg
    104.      end if
    105.     end if
    106.        else if counter = 4
    107.     if size@arg = 8
    108.      if ~ arg eq r9
    109.       mov r9,arg
    110.      end if
    111.     else if size@arg = 4
    112.      if ~ arg eq r9d
    113.       mov r9d,arg
    114.      end if
    115.     else if size@arg = 2
    116.      if ~ arg eq r9w
    117.       mov r9w,arg
    118.      end if
    119.     else if size@arg = 1
    120.      if ~ arg eq r9b
    121.       mov r9b,arg
    122.      end if
    123.     end if
    124.        end if
    125.      else
    126.        if arg eqtype [0] | arg eqtype byte [0]
    127.     virtual at 0
    128.      inc arg
    129.      local opcode
    130.      load opcode byte from 0
    131.     end virtual
    132.     if opcode and 0F8h = 48h
    133.      mov rax,arg
    134.      mov [rsp+(counter-1)*8],rax
    135.     else if opcode = 66h
    136.      mov ax,arg
    137.      mov [rsp+(counter-1)*8],ax
    138.     else if opcode = 0FFh
    139.      mov eax,arg
    140.      mov [rsp+(counter-1)*8],eax
    141.     else
    142.      mov al,arg
    143.      mov [rsp+(counter-1)*8],al
    144.     end if
    145.        else if arg eqtype 0
    146.     mov qword [rsp+(counter-1)*8],arg
    147.        else
    148.     mov [rsp+(counter-1)*8],arg
    149.        end if
    150.      end if
    151.     end if
    152.    common
    153.     argscount = counter
    154.     call proc
    155.     if stackspace
    156.      add rsp,stackspace
    157.     end if }
     
  8. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    n0name
    Да я тоже вчера у себя его нашел. Но он только в 64 битной реализации.
    Да и он же совсем по другому работает. В данном макросе если у тебя параметров <=4, то он использует регистры.

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

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

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    это и есть fastcall :)

    а насчёт сабжа, хз, возможно, однако оптимизация работы с такими структурами это разовое дело, и можно всё это сделать вручную.
     
  10. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Проще так (по мотивам какой-то реализации ТЕА):
    Код (Text):
    1. arg1 equ eax
    2. arg2 equ ebx
    3. arg3 equ ecx
    4. ...
    5.  
    6. lea  arg1,[some_data]
    7. call func ; или любой другой способ вызова. func работает с eax,ebx,ecx
    8.  
    9. ; какой-то код, регистры сменились
    10. arg1 equ esi
    11. lea  arg1,[x]
    12. call func ; func работает с esi,ebx,ecx
    13. ...
    14. func:
    15.   cmp arg1,1
    16.   add  arg2,arg1
    17. ...