Почему можно использовать регистр xzr вместо литерала 0 на ARMv8?


Я читал sve whitepaper из ARM и наткнулся на что-то, что показалось мне странным (в Примере без SVE):

mov x8, xzr
Я не знал, что это за регистр xzr, поэтому я посмотрел его и нашел некоторое содержание из ARM, утверждающее, что во многих контекстах он был синонимом нуля.

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

Чего я не понимаю, так это почему не был использован литерал 0 вместо xzr? Например:

mov x8, 0
Подводя итог, я задаюсь вопросом: почему здесь можно использовать регистр xzr вместо литерала 0?
5 5

5 ответов:

Я думаю, что сравнение mov x8, xzr vs mov x8, #0 является чем-то вроде отвлекающего маневра.

Как показывает ответ @old_timer, нет никакого усиления кодирования, и вполне вероятно (хотя, по общему признанию, я не проверял) небольшое или полное увеличение производительности конвейера.

То, что xzr дает нам, однако - в дополнение к фиктивному регистру согласно ответу @InfinitelyManic - это доступ к нулевому операнду без необходимости загружать и занимать реальный регистр. Это имеет двойное преимущество одного меньше инструкций, и еще один регистр, доступный для хранения "реальных" данных.

Я думаю, что это важная характеристика, на которую оригинальный 'некоторый контент из ARM', упомянутый в OP, пренебрегает указать.

Вот что я имею в виду под mov x8, xzr против mov x8, #0 быть отвлекающим маневром. Если мы обнуляем x8 с намерением затем изменить его, то использование xzr или #0 довольно произвольно (хотя я бы предпочел #0 как более очевидное). Но если мы обнуляем x8 чисто для того, чтобы снабдить нулевым операндом последующую инструкцию, тогда нам было бы лучше использовать - где это разрешено - xzr вместо x8 в качестве операнда в этой инструкции, а не обнулять x8 вообще.

mov x8,xzr
mov x8,#0
mov x8,0

Производит

0000000000000000 <.text>:
   0:   aa1f03e8    mov x8, xzr
   4:   d2800008    mov x8, #0x0                    // #0
   8:   d2800008    mov x8, #0x0                    // #0

Никакого реального удивления там не было, кроме того, что это позволило немедленно без знака фунта. Это не проблема размера инструкции (опять же неудивительно, что с x86,например,xor rax, rax дешевле, чем mov rax, 0), возможно, есть увеличение производительности конвейера (несмотря на распространенное мнение, что инструкции берут больше, чем один запуск часов, чтобы закончить).

Скорее всего, это личное предпочтение вещь у нас есть этот прохладный mips как всегда нулевой регистр вещь позволяет использовать его только для веселье.

Эти две инструкции должны быть идентичны - как с точки зрения эффекта, так и ожидаемой эффективности.

На самом деле они оба псевдонимы инструкций более общего назначения.

mov x8, 0 кодируется как orr x8, xzr, 0

mov x8, xzr кодируется как orr x8, xzr, xzr

Псевдонимы полезны, потому что они делают ASM более читаемым. Вторая кодировка демонстрирует, почему наличие нулевого регистра xzr может быть полезным. Поскольку мы знаем, что xzr всегда равен нулю, мы можем повторно использовать orr инструкция для mov. Без него mov потребовало бы другой кодировки, и поэтому было бы потрачено впустую пространство кодировки.

Этот ответ не является "на четвереньках" Для OP.

XZR может использоваться для отбрасывания результатов; например, "ldr xzr, [sp], 16". См. GDB ниже

0x7fffffef40:   0x00000000      0x00000000      0x00400498      0x00000000
0x7fffffef50:   0x00000000      0x00000000      0x00000000      0x00000000
              ldr x0,=0xdead
(gdb)
              ldr x1,=0xc0de
(gdb)
              stp x0, x1, [sp, #-16]!
(gdb) x/8x $sp
0x7fffffef30:   0x0000dead      0x00000000      0x0000c0de      0x00000000
0x7fffffef40:   0x00000000      0x00000000      0x00400498      0x00000000

              ldr xzr, [sp], #16
(gdb) x/8x $sp
0x7fffffef40:   0x00000000      0x00000000      0x00400498      0x00000000
0x7fffffef50:   0x00000000      0x00000000      0x00000000      0x00000000

Также помните, что в ARMv8 стек должен быть выровнен по четырем словам или SP mod 16 = 0. Таким образом, вы можете использовать XZR один из регистров пары "pushed" или "pop".

stp x1, xzr, [sp, #-16]!

ldp x10, xzr, [sp], #16

TL; DR

Для загрузки 64-битного литерала в регистр требуется несколько команд, но только одна команда устанавливается в 0 с помощью zxr. Поэтому код короче и быстрее.


Чтобы переместить литерал в регистр, вы должны использовать инструкцию MOVL, смотрите это из ссылки arm:

Псевдо-инструкция MOVL

Загрузите регистр либо с помощью:

A 32-bit or 64-bit immediate value.

Any address.

MOVL генерирует либо две, либо четыре инструкции... мова, MOVK пара.

Таким образом, загрузка литерала в регистр-это многоступенчатый процесс. Если вы просто хотите очистить реестр, то у них есть ярлык. zxr-это псевдо-регистр, который всегда считывает ноль, что является общим значением, которое вам понадобится, и перемещение регистра в регистр может быть сделано в одной инструкции.

В сборке микрочипов они имеют схожую концепцию. Чтобы установить регистр в литерал, вы должны сделать что-то вроде:

MOVLW   10       (Move 10 to the working register) 
MOVWF   0x1234   (Move the working register to address 0x1234)

Но для установки в ноль у них есть инструкция:

CLRF    0x1234   (Set 0x1234 to zero)