Почему rax и rdi работают одинаково в этой ситуации? - PullRequest
0 голосов
/ 25 февраля 2019

Я сделал этот код:

global  strlen
    ; int strlen(const char *string);
strlen:
    xor     rcx, rcx

retry:
    cmp byte    [rdi + rcx], 0
    je      result
    inc     rcx
    jmp     retry

result:
    mov     rax, rcx
    ret

И вот как я его проверяю:

#include <stdio.h>

int main(int argc, char **argv)
{
    char* bob = argv[1];
    printf("%i\n", strlen(bob));
    return 0;
}

Это рабочий strlen, здесь нет проблем, но я заметил, чтоЯ могу переключить rdi в первой строке блока повтора на rax, не меняя ничего, я не знаю, нормальное ли это поведение.какое из этих значений я должен сохранить?

1 Ответ

0 голосов
/ 25 февраля 2019

Это просто невезение.

GCC 8, без оптимизаций , использует rax в качестве промежуточного местоположения для перемещения argv[1] в bob и перемещения последнего в первый параметр strlen:

  push rbp
  mov rbp, rsp
  sub rsp, 32

  mov DWORD PTR [rbp-20], edi             ;argc
  mov QWORD PTR [rbp-32], rsi             ;argv

  mov rax, QWORD PTR [rbp-32]             ;argv
  mov rax, QWORD PTR [rax+8]              ;argv[1]
  mov QWORD PTR [rbp-8], rax              ;bob = argv[1]

  mov rax, QWORD PTR [rbp-8]
  mov rdi, rax
  call strlen                             ;strlen(bob)

  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf

  mov eax, 0
  leave
  ret

Это просто невезение, это не задокументированное поведение, на самом деле произойдет сбой, если вы используете строковый литерал :

printf("%i\n", strlen("bob"));

  mov edi, OFFSET FLAT:.LC1
  call strlen                     ;No RAX here

  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf

Документ, определяющий, какчтобы параметры передавались в функцию вашей операционной системы ABI, читайте больше в этом ответе .


GCC генерирует «тупой» код, который часто использует регистры, когда оптимизация отключена,это облегчает отладку (как движка GCC, так и скомпилированной программы) и по существу имитирует новичков: сначала переменная считывается из памяти и помещается в первый свободный регистр (одна проблема решена), затем она копируется в правильный регистр (еще один пропал) и, наконец, вызов сделан.
GCC только что подобрал первый свободный регистр, в этой простой программе нет давления регистров, и rax всегда поднимается.

...