Почему 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. BMI2 mulx.
  • rbx: 8086 xlatb. cpuid используйте все четыре EAX..EDX. 486 cmpxchg8b, x86-64 cmpxchg16b. Компиляторы будут испускать cmpxchg8 на std::atomic<long long>::compare_exchange_weak, и 16b для atomic<struct_16_bytes>. RBX имеет наименьшее неявное использование исходного 8, но lock cmpxchg16b один из немногих компиляторов будет использовать.
  • rsi/rdi: строка ops, в том числе rep movsb которые некоторые компиляторы иногда встроены. (gcc также inlines rep 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 использоваться для чего угодно, только ограничивая выделение регистра для нескольких операций с особым случаем. Это означает зарегистрировать ортогональности.