Выбор конкретных регистров для внешних вызовов

Я новичок в сборке и пытаюсь научиться самому.

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

  • Например, при передаче аргументов функции подкачки void asm_swap(int *x, int *y) компилятор C использует регистры rcx и rdx для передачи адресов переменных (в этом случае возвращаемое значение не требуется. ). Переключение с _cdecl на _fastcall не имело никакого значения.

  • Для другой функции int asm_fact(int x), то есть для вычисления факториала x, компилятор C использует rcx для передачи значения x и rax для возврата вычисленного факториала. Опять же, переключение с _cdecl на _fastcall не имело никакого значения.

В связи с проблемой у меня есть два вопроса:

  1. Могу ли я сделать вывод о том, что всякий раз, когда я компилирую один и тот же код, вызывающий абонент обязательно будет использовать одни и те же регистры для отправки и получения данных?
  2. Есть ли способ выбрать определенные регистры в качестве инструмента для передачи переменных (скажем, для функции asm_fact я бы предпочел использовать rdx для хранения значения x, а не rcx ?

Система: Windows 10 (64), VS-2013.

Пример кода: файл main.c

#include <stdlib.h>
#include <stdio.h>

extern void asm_swap();
extern signed long long int asm_fact();

typedef signed long long int sint64;

sint64 fact_sint64(sint64 n) {
    sint64 ret = (sint64)1;
    if (n > (sint64)1) {
        while (n > (sint64)1) {
            ret *= n--;
        }
    }
    return (ret);
}

void swap_sint64(sint64 *a, sint64 *b) {
    sint64 t = *a;
    *a = *b;
    *b = t;
}

int main(void) {
    sint64 x, y;
    x = 8;
    y = 3;
    printf("(initial)     ->   x = %lli     y = %lli\n\n", x, y);
    swap_sint64(&x, &y);
    printf("(swap in c)   ->   x = %lli     y = %lli\n\n", x, y);
    asm_swap(&x, &y);
    printf("(swap in asm) ->   x = %lli     y = %lli\n\n", x, y);
    y = fact_sint64(x);
    printf("(fact in c)   ->   fact(%lli) = %lli\n\n", x, fact_sint64(x));
    y = asm_fact(x);
    printf("(fact in asm) ->   fact(%lli) = %lli\n\n", x, y);
    getchar();
    return (0);
}

файл "Assembly.asm64"

.data

.code
asm_swap proc
    mov r8, [rcx]
    mov r9, [rdx]
    mov [rcx], r9
    mov [rdx], r8
    ret
asm_swap endp

asm_fact proc
    mov rax, 1
    cmp rcx, 1
    jle $exit@fact
$loop@fact:
    imul rax, rcx
    dec rcx
    cmp rcx, 1
    jg $loop@fact
$exit@fact:
    ret
asm_fact endp
end

person ssd    schedule 22.02.2016    source источник
comment
1) да 2) нет. См. msdn.   -  person Jester    schedule 23.02.2016
comment
Это часть соглашений о вызовах, которые являются частью ABI (двоичного интерфейса приложения), определенного реализацией.   -  person EOF    schedule 23.02.2016
comment
@Jester: Большое спасибо. Это было именно то, что я искал.   -  person ssd    schedule 23.02.2016


Ответы (1)


Для окон есть ABI, который указывает

  1. Как передаются параметры
  2. Какие регистры необходимо сохранить при вызове

Msdn: соглашения о вызовах Microsoft

Чтобы вызывать как функцию C, вы должны соблюдать эти правила.

Вы должны сохранить энергонезависимые регистры (сохранять и восстанавливать их)

person mksteve    schedule 24.02.2016