Что сравнивает cmpq? - PullRequest
       7

Что сравнивает cmpq?

0 голосов
/ 19 сентября 2019

mystery имеет подпись этой функции:

int mystery(char *, int);

Это код сборки функции mystery:

mystery:
        movl    $0, %eax                ;set eax to 0
        leaq    (%rdi, %rsi), %rcx      ; rcx = rdi + rsi

loop:
        cmpq    %rdi, %rcx
        jle     endl
        decq    %rcx
        cmpb    $0x65, (%rcx)
        jne     loop
        incl    %eax
        jmp     loop

endl:
        ret

Что сравнивает эта строка cmpq %rdi, %rcx?Адрес или символьное значение?Если он сравнивает адрес, хранящийся в регистрах, то какой в ​​этом смысл?Если один адрес больше другого, значит?

Ответы [ 2 ]

3 голосов
/ 19 сентября 2019

кажется, что это происходит так:

char* buff = "abcdef"        //this is the rdi.
int64_t len = strlen(buff);  //this is the rsi.

for(char* pRCX = buf+len; pRCX >= buff/*this is the cmpq*/; pRCX--){
    //do something.
}

cmpq в коде проверяет, достигает ли rcx начала массива данных.он уменьшается в каждом цикле, потому что он запускается в последнем элементе массива.

yes, cmpq %rdi, %rcx сравнивает адрес.Кажется, оптимизированная версия циклически перебирает массив символов.вместо циклического перебора по индексу, он напрямую перебирает адрес.так быстрее, но немного трудно понять специально для начинающих.

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

2 голосов
/ 19 сентября 2019

Похоже на memrchr, с проверкой cmpq для позиции поиска, возвращающейся к началу буфера, и проверкой cmpb на соответствующий байт.

cmp просто устанавливает флаги в соответствии с dst - src, точно так же как sub.Так что он сравнивает свои входные операнды, конечно.В этом случае оба регистра qword содержат указатели.


Я бы не рекомендовал jle для сравнения адресов;лучше относиться к адресам как к неподписанным.Хотя для x86-64 это на самом деле не имеет значения;Вы не можете иметь массив, который охватывает границу переполнения со знаком, потому что там есть неканоническая "дыра". Должны ли сравнения указателей быть подписаны или не подписаны в 64-битной x86?

Тем не менее, jbe будет иметь больше смысла.Если у вас нет массивов, которые пересекают границу от самого высокого адреса до самого низкого адреса, указатель переносится с 0xfff...fff на 0.Но в любом случае вы могли бы исправить эту ошибку, выполнив if (p == start) break вместо p <= start.


Хотя в этой функции есть ошибка , при условии, что она написана для x86-64 System V ABI: его сигнатура принимает аргумент размером int, но она предполагает, что его знак расширяется до ширины указателя, когда он char *endp = start + len.

ABI позволяет узким аргументам иметь мусор в старших битахих реестра. Требуется ли расширение знака или нуля при добавлении 32-битного смещения к указателю для ABI x86-64?

При этом также возникают серьезные проблемы с производительностью: проверяется 1 байт за раз.общее количество мусора против SSE2 16 байт за раз.Кроме того, он не использует ни одну условную ветвь в качестве ветви цикла, поэтому он имеет 3 перехода на каждую итерацию вместо 2. т. Е. Дополнительную неиспользованную условную ветвь.

Кроме того, после цикла он вычитает указатель-вычитаетвместо того, чтобы тратить inc %eax внутри цикла.Если вы собираетесь делать inc %eax внутри цикла, вы можете также сравнить его с размером вместо сравнения указателя.

В любом случае, функция написана так, чтобы ее было легко перепроектировать, а небыть эффективным.jmp, а также 2 условных ветви ухудшают эту IMO по сравнению с идиоматическим циклом с условием внизу.

...