Почему rbp и rsp называются регистрами общего назначения?
согласно Intel в x64 следующие регистры называются регистрами общего назначения (RAX, RBX, RCX, RDX, RBP, RSI, RDI, RSP и R8-R15)https://software.intel.com/en-us/articles/introduction-to-x64-assembly.
в следующей статье написано, что RBP и RSP-регистры специального назначения (RBP указывает на основание текущего кадра стека, а RSP - на верхнюю часть текущего стека рамка.) https://www.recurse.com/blog/7-understanding-c-by-learning-assembly
теперь у меня есть два противоречивых утверждения. Заявление Intel должно быть доверенным, но что правильно и почему RBP и RSP вообще называют общим назначением ?
Спасибо за любую помощь.
2 ответов
общее назначение означает, что все эти регистры могут использоваться с любыми инструкциями, выполняющими вычисления с регистрами общего назначения, в то время как, например, вы не можете делать все, что хотите, с указателем инструкции (RIP) или регистром флагов (RFLAGS).
некоторые из этих регистров предполагалось использовать для конкретного использования, и обычно они используются. Наиболее важными из них являются RSP и RBP.
Если вам нужно использовать их в своих целях, вы должны сохранить их содержимое, прежде чем хранить что-то еще внутри, и восстановить их первоначальное значение, когда это будет сделано.
если регистр может быть операндом для add
, или используется в режиме адресации, это "общего назначения", в отличие от регистров как FS
регистр сегмента или RIP. Регистры GP также называются "целочисленными регистрами", хотя другие типы регистров также могут содержать целые числа.
в компьютерной архитектуре процессоры обычно внутренне обрабатывают целочисленные регистры / инструкции отдельно от регистров/инструкций FP / SIMD. например. Intel Sandybridge-семейные процессоры имеют отдельные физические файлы регистров для переименования GP integer против FP / vector регистров. Это просто называется целое число и зарегистрировать файлы ФП. (Где FP короткая рука для всего, что ядру не нужно сохранять/восстанавливать, чтобы использовать регистры GP, оставляя состояние FPU / SIMD пользовательского пространства нетронутым.) Каждая запись в файле регистра FP имеет ширину 256 бит (для хранения вектора AVX ymm), но целочисленные записи файла регистра должны быть только 64 бит широкий.
на процессорах, которые переименовывают регистры сегментов (Skylake не), я думаю, что это будет частью целочисленного состояния, а также RFLAGS + RIP. Но когда мы говорим "целочисленный регистр", мы обычно имеем в виду конкретно регистр общего назначения.
каждый регистр имеет некоторую особенность для некоторых инструкций, за исключением некоторых совершенно новых регистров, добавленных с x86-64: R8-R15. Они не дисквалифицируют их как общего назначения (Низкий 16 из) оригинала 8 датируется 8086, и были неявные использования каждого из них даже в оригинальном 8086.
для RSP это специально для push/pop/call / ret, поэтому большинство кодов никогда не использует его для чего-либо еще. (И в режиме ядра, используемом асинхронно для прерываний, поэтому вы действительно не можете спрятать его где-то, чтобы получить дополнительный GP-регистр, как вы можете в коде пользовательского пространства: является ли ESP таким же универсальным, как EAX?)
но в контролируемом условном (как нет обработчиков сигналов) вам не нужно использовать RSP для указателя стека. например, вы можете использовать его для чтения массива в цикле с pop, например в этом коде-гольф ответа. (Я действительно использовал esp
в 32-битном коде, но с той же разницей:pop
быстрее lodsd
на Skylake, пока оба 1 байт.)
неявное использование и особая для каждого регистра:
см. также x86 Assembly-почему [e] bx сохраняется в соглашениях о вызовах? для частичного списка.
я в основном ограничиваю это инструкциями пользовательского пространства, особенно теми, которые современный компилятор может фактически испускать из кода C или c++. Я не пытаюсь быть исчерпывающим для правил, которые имеют много неявных применений.
-
rax
: один операнд [i]mul /[i]div / cdq / cdqe, строковые инструкции (stos),cmpxchg
, etc. так далее. А также специальные более короткие кодировки для многих инструкций, таких какxchg
- С-eax, где0x90 nop
вышла (до это стало отдельной инструкцией в x86-64, потому чтоxchg eax,eax
ноль-расширяет eax в RAX и, таким образом, не может использовать0x90
кодировка. Но!--12--> все еще может собраться в REX.W=1 0x90. -
rcx
: счетчики смену,rep
-строка рассчитывает, медленноloop
- инструкции -
rdx
:rdx:rax
используется divide и multiply и cwd / cdq / cqo для настройки для них.rdtsc
. BMI2mulx
. -
rbx
: 8086xlatb
.cpuid
используйте все четыре EAX..EDX. 486cmpxchg8b
, x86-64cmpxchg16b
. Компиляторы будут испускатьcmpxchg8
наstd::atomic<long long>::compare_exchange_weak
, и 16b дляatomic<struct_16_bytes>
. RBX имеет наименьшее неявное использование исходного 8, ноlock cmpxchg16b
один из немногих компиляторов будет использовать. -
rsi
/rdi
: строка ops, в том числеrep movsb
которые некоторые компиляторы иногда встроены. (gcc также inlinesrep cmpsb
для строковых литералов в некоторых случаях, но это, вероятно, не оптимальная). -
rbp
:leave
(только 1 uop медленнее, чемmov rsp, rbp
/pop rbp
. gcc фактически использует его в функциях с указателем кадра, когда он не может простоpop rbp
). Также ужасно-медленныйenter
которых никто никогда не использует. rsp
: stack ops: push / pop / call / reg иleave
. (Иenter
).r11
:syscall
/sysret
использовать его для сохранение / восстановление rflags пользовательского пространства. (Вместе с RCX для сохранения / восстановления RIP пользовательского пространства).
адресация-режим кодирования особых случаях:
rbp
/r13
не может быть базовым регистром без смещения: эта кодировка вместо этого означает: (в ModRM) rel32
(RIP-relative), или (в SIB) disp32
без Базового регистра. (r13
использует те же 3 бита в ModRM/SIB, поэтому этот выбор упрощает декодирование, не делая длину инструкции декодер посмотрите на Рекс.B бит чтобы получить бит 4-го Базового регистра). [r13]
собирает на [r13 + disp8=0]
. [r13+rdx]
собирает на [rdx+r13]
(избегая проблемы путем замены базы / индекса, когда это опция).
rsp
/r12
в качестве Базового регистра всегда требуется байт SIB. (Кодировка ModR/M base=RSP-это escape-код для сигнала байта SIB, и опять же, больше декодера должно заботиться о префиксе REX, если r12
была обработана иначе.)
rsp
не может быть регистра индекса. Это позволяет кодировать [rsp]
, что более полезно, чем [rsp + rsp]
. (Intel могла бы разработать кодировки ModRM/SIB для 32-разрядных режимов адресации (новый в 386), поэтому SIB-with-no-index был возможен только с base=ESP. Это было бы [eax + esp*4]
можно только исключить [esp + esp*1/2/4/8]
. Но это не полезно, поэтому они упростили аппаратное обеспечение, сделав index=ESP кодом для no index независимо от основа. Это позволяет двумя избыточными способами кодировать любую базу или режим адресации base+disp: С или без SIB.)
r12
can быть регистром индекса. В отличие от других случаев, это не влияет на декодирование длины инструкции. Кроме того, его нельзя обойти с кодировкой, как в других случаях. AMD хотела, чтобы регистр AMD64 был максимально ортогональным, поэтому имеет смысл потратить несколько дополнительных транзисторов для проверки REX.X как часть декодирования индекса / no-index. Например, [rsp + r12*4]
требует index=r12, поэтому имея r12
не полностью общая цель сделает AMD64 худшей целью компилятора.
0: 41 8b 03 mov eax,DWORD PTR [r11]
3: 41 8b 04 24 mov eax,DWORD PTR [r12] # needs a SIB like RSP
7: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] # needs a disp8 like RBP
b: 41 8b 06 mov eax,DWORD PTR [r14]
e: 41 8b 07 mov eax,DWORD PTR [r15]
11: 43 8b 04 e3 mov eax,DWORD PTR [r11+r12*8] # *can* be an index
компиляторы любят, когда все регистры can использоваться для чего угодно, только ограничивая выделение регистра для нескольких операций с особым случаем. Это означает зарегистрировать ортогональности.