Что же делать, если нам хочется как-то обойти это ограничение, не теряя совместимости со старыми приложениями? К счастью, инженеры, проектировавшие x86 ISA, были очень талантливыми и прозорливыми, а потому заложили в архитектуру возможность вводить перед инструкциями приставки - специальные указатели, так или иначе изменяющие значения инструкций. Скажем, приставка LOCK говорит, что инструкция должна быть выполнена в «атомном» режиме["Атомный" режим - это когда выполнение инструкции гарантированно не будет прервано каким-нибудь внешним событием. К примеру, если мы что-нибудь записываем в оперативную память, то начиная с момента исполнения и до завершения атомной инструкции никто «посторонний» не сможет ни записать в то же место оперативной памяти, ни прочитать оттуда. Используется в многопроцессорных системах для организации межпроцессорного взаимодействия], приставки 2E и 2F подсказывают процессору, произойдет условный переход или нет, а приставка 66 приказывает переключаться между 16-битным и 32-битным представлением данных в регистрах. Поэтому когда разработчикам x86-64 понадобилось добавить в архитектуру IA-32 поддержку 64-битности, они сделали очень простую и в то же время гениальную вещь, введя набор 64-битных приставок REX, которые не столько расширяют возможности инструкций, сколько служат для кодирования дополнительной информации в четырех своих полях. Поле REX.W задает «размер» обрабатываемых данных: если здесь записан нолик, то обрабатываемые регистры интерпретируются как 32-битные, если единичка - то как 64-битные; а поля REX.R, REX.X и REX.B - старшие биты, дополняющие трехбитные поля ModR/M.Reg, SIB.Index и, в зависимости от ситуации, ModR/M.R/M или SIB.Base соответственно. Знаю, что это звучит не слишком понятно, поэтому тут же поясню, что это означает. На самом деле в 64-битном режиме мы используем 4-битную кодировку регистров процессора, но три младших регистровых бита записываем на их «традиционные» места в инструкции, а старший бит - переносим в приставку REX, обходя тем самым архитектурное ограничение. А заодно, помимо поддержки восьми новых GPR-регистров R8-R15 и SSE-регистров XMM8-XMM15, получаем возможность отключить 64-битные вычисления, когда они нам не требуются, - и пропорционально сэкономить на времени исполнения и занимаемом данными месте! И все это - одним-единственным байтом!
Вторая группа усовершенствований - отказ от поддержки безнадежно устаревших и давно не использующихся возможностей IA-32. В расширенном режиме не поддерживаются Real Mode и Virtual 8086-mode[Да-да, Virtual 8086 - это именно то, о чем вы сейчас подумали, - полная имитация процессора Intel 8086. Много ли найдется людей, которые пожалеют о его отсутствии?], и почти полностью отключена сегментация[Поскольку читатель уже заскучал от обилия технических фактов, не буду утомлять его описанием сегментированной модели памяти 80286, скажу только, что сегментация - это очень упрощенный аналог виртуальной памяти] (хотя некоторые настройки сегмента CS и сегменты FS/GS все же можно использовать); не поддерживаются инструкции, работавшие с этими сегментами, и инструкции, оперировавшие BCD-числами[BCD (Binary Coded Decimal) - это когда десятичное число записывается шестнадцатеричными цифрами. Например, 54d - в виде 54h = 01010100b вместо традиционных 36h = 00110110b]. Все это позволяет заметно облегчить процессору жизнь и в перспективе - повысить его производительность. К примеру, отказ от сегментации позволяет при обращении к оперативной памяти не вычислять линейный адрес и не проверять его допустимость в рамках сегмента, а значит, эффективность этой подсистемы при переходе к x86-64 существенно возрастет.
«А как обстоят дела с совместимостью со старыми программами?» - спросит читатель. Посмотрим: если обойтись без приставки REX, то процессор будет считать, что все записанные там данные - нули, то есть новые регистры не используются, а размер операндов инструкции равен 32 битам, то есть старшие 32 бита каждого 64-разрядного регистра при подобных вычислениях явным образом забиваются нулями. Как легко догадаться, инструкция без префикса REX даже в 64-битном режиме будет работать точно так же, как она работала и в 32-битном; и если соблюдать меры предосторожности (в частности, не выходить при адресации за границу «нижних 4 Гбайт» виртуальной памяти, то даже в 64-битном режиме все программы будут работать как и в 32-битном! Красивое решение? Мне кажется, да. Если программе не требуется поддержка всяческих «64-битностей», то она может запросто продолжать работать на «физически» 64-битном процессоре в 32-битном режиме, не используя ни 64-битные указатели, ни 64-битные вычисления, зато «радуясь» удвоенному количеству регистров и другим улучшениям x86.
К сожалению, «нет в мире счастья», и по кодировке префиксы REX совпадают с шестнадцатью «сокращенными» инструкциями семейств INC и DEC (увеличение или уменьшение содержимого регистра на единичку). Вдобавок в 64-битном режиме не поддерживается ряд инструкций и «режимов» x86 (о чем речь пойдет ниже), а для нескольких инструкций изменены опкоды или их смысловая нагрузка[К примеру, инструкция 90h в классическом x86 означает XCHG EAX, EAX (поменять местами регистр EAX с регистром EAX). Поскольку от перестановки двух одинаковых регистров их содержимое не меняется, то эту комбинацию часто используют в качестве однобайтной «пустышки» (NOP), которая ничего не выполняет, зато занимает 1 байт машинного кода. Зачастую некоторые инструкции хочется «выровнять» в оперативной памяти, сделав так, чтобы они, например, не «пересекали 16-байтные границы» (если этого не сделать, то при декодировании инструкции возникнет «штраф», связанный с тем, что процессору придется «склеивать» инструкцию из нескольких 16-байтных кусочков); и если, скажем, эта инструкция - начало цикла, то непрерывная выплата «штрафа» может существенно замедлить выполнение программы. Вставка нескольких NOP’ов, «закрывающих» возникающие из-за выравнивания «дырки» в коде, - обычная практика, однако в 64-битном режиме процессор не просто переставит EAX с EAX местами, а еще и заполнит старшие 32 бита регистра RAX нулями - и наша инструкция уже не будет «настоящим» NOP’ом. Поэтому в x86-64 опкод 90h обрабатывается по-особому, всегда интерпретируясь как NOP]; так что даже в «тепличных» 32-битных условиях перекомпиляция программ для поддержки x86-64 все-таки требуется. Но и унывать по этому поводу не приходится: получить все преимущества от расширенного набора регистров без перекомпиляции все равно невозможно, а если очень хочется запустить 32-битное приложение, это можно сделать, временно переведя процессор в «режим совместимости» (Compatibility Mode), в котором полностью имитируется классический IA-32.
В случае AMD - все новые CPU без исключения. Athlon 64, Mobile Athlon 64, Turion и Opteron поддерживают технологию AMD64 изначально; процессоры Sempron (изначально этой поддержки лишенные) - начиная с определенного степпинга (E) или определенной даты (осени 2005 года). Отличить «новые» Sempron от старых проще всего по логотипу на коробке: у 64-разрядных Sempron’ов на упаковке стоит значок AMD64.
В случае Intel технологию EM64T поддерживают только процессоры новых степпингов (начиная с "E") в исполнении LGA775. Pentium D, Pentium eXtreme Edition и Pentium 4 семейства 6xx поддерживают EM64T изначально; процессоры Xeon - начиная c 90-нм ядра Nocona; процессоры Pentium 4 семейства 5xx и Celeron D семейства 3xx - только те модели, номер которых заканчивается на шестерку или единичку. Pentium 4 Extreme Edition 3,73 ГГц тоже поддерживает EM64T. Все остальные модели (в частности, Pentium M и процессоры в исполнении Socket 478) технологию EM64T не поддерживают и в ближайшее время эту поддержку не получат.