Эмоциональная история процессоров для первых компьютеров с 70-х до начала 90-х

AIM PowerPC

В первой части были обзорно описаны многие разные процессоры до середины 90-х. Во второй части был обзор процессоров мейнфреймов IBM. Недавно мне предоставилась возможность немного попрограммировать для PowerPC, на основе чего появилась возможность добавить ещё одну часть к этим обзорам.

Первым процессором архитектуры RISC стал разработанный в IBM во второй половине 70-х Джоном Коком процессор 801, который показал 2-кратный прирост производительности по сравнению с процессорами архитектуры CISC, используемой в мейнфреймах. На базе 801 был разработан процессор ROMP, который использовался в рабочей станции IBM RT PC (персональный компьютер технологии RISC), появившейся в 1986. Этот процессор использовал уже 32-разрядные регистры вместо 24-разрядных на 801 и имел поддержку работы с виртуальной памятью, которой не было на 801. Первый вариант ROMP был сделан в 1981 и это делает его одним из первым RISC-процессоров с 32-битной архитектурой (другими в этом ряду были AT&T Bellmac 32 и Berkeley RISC). Однако IBM задерживала внедрение этой технологии до 1985, что фактически означает, что успехи с производством ARM были решающими к склонению IBM к широкому использованию процессоров RISC. IBM могла банально отстать с инновациями от маленькой фирмы Acorn. Интересно, но IBM в итоге проиграла ARM, этот результат был возможно естественным следствием из задержек с внедрением новых технологий, что возможно было частью глобальной политики тех времен.

Дальнейшие разработки в области архитектуры RISC привели IBM в 1990 к созданию архитектуры POWER, которая впервые использовалась в процессорах POWER1, которые нашли своё применение, в частности, в рабочих станциях RS/6000. Архитектура POWER развивается до сих пор, последним здесь стал процессор POWER10 (2021). Архитектура POWER стала базой для разработки процессора PowerPC (оптимизация производительности c использованием усовершенствованной RISC архитектуры – производительность вычислений) альянсом AIM (Apple-IBM-Motorola), который смог сделать свой первый процессор в 1992.

До сих пор не вполне понятно, почему Apple предпочла PowerPC другим возможным вариантам. Она могла бы просто вложиться в разработку 68k и помочь Motorola добиться качественной эволюции своих процессоров, как это смогла сделать Intel. Хотя теоретически 68k хуже соответствуют мультискалярной архитектуре, чем x86, но практические результаты, показанные 68060, позволяют предположить, что это направление было не безнадежным. 68060 оказался даже быстрее Pentium на ряде типовых расчётов. Кроме того, у Motorola был и свой очень быстрый RISC-процессор 88000, который почему-то был проигнорирован практически всеми ИТ-компаниями. Apple, однако, пробовала использовать 88000 вместо 68к, был создан эмулятор 68к и два прототипа, которые успешно грузили macOS. Но дальнейшие разработки по не совсем понятным причинам были остановлены. Трудно поверить, что неофициальная кампания по подрыву позиций Motorola, начатая в 1980 Intel, не имела ко всем последующим неудачам и финальному краху Motorola никакого отношения. Apple могла бы вложиться и в ARM, но этот самый перспективный вариант возможно не прошел, из-за того, что тогда ARM ещё контролировался Acorn. Был ещё уже почти завершившийся успехом вариант перехода на архитектуру Sun SPARC, Sun сама перешла на SPARC с 68k, что могло бы сильно упростить подобный переход и для Apple, но в последний момент сделка сорвалась. Также по неясным причинам, имеющим отношение к большому бизнесу и политике, а не к техническим характеристикам, не состоялась сделка по переходу на процессоры архитектуры MIPS. Помимо компьютеров Apple, процессоры PowerPC были также использованы в малосерийных компьютерах AmigaOne, реинкарнации легендарных компьютеров Commodore Amiga. Были и другие системы на базе PowerPC.

Характерной особенностью систем Macintosh на базе PowerPC является очень качественная поддержка эмуляции процессоров 68k, что позволяет использовать почти все программы, написанные для 68k-систем, абсолютно также как и PowerPC-программы. Любопытно, что для первого Power Macintosh Apple предлагала вариант с дополнительным процессором Intel 80486 для возможности работы с операционными системами Microsoft и IBM. Такое трудно было представить с первыми Макинтошами, в те времена подобные дополнительные платы были типовыми только для компьютеров Commodore Amiga. Кстати, уже с 1995 Power Macintosh стали использовать шину PCI от Intel.

iMac G3, наиболее успешный компьютер с процессором PowerPC, их продано более 5 миллионов

PowerPC весьма необычный процессор на фоне других процессоров как RISC-, так и CISC-архитектуры. Особенно он необычен своим способом работы с флагами. Флаги результатов сравнений (четыре бита, тетрада) могут храниться в восьми разных слотах! Можно, получается, набирать результаты до 8-х операций и использовать их с задержкой в последующих кодах! Результаты сравнений можно перекладывать из одного слота в другой или даже делать со слотами битовые операции – тоже очень необычные свойства. Существуют инструкции для знаковых и беззнаковых сравнений, что просто унаследовано от архитектуры IBM/370. Флаг переноса не используется в сравнениях и поэтому присутствует только в единственном числе. Необычна и работа с двумя флагами переполнения. Есть единственный текущий флаг переполнения, при его установке устанавливается и флаг суммарного переполнения, который может быть сброшен только специальным образом, обычные арифметические команды сбросить его не могут. Этот флаг суммарного (не текущего!) переполнения копируется при выполнении арифметических команд в одну из тетрад флагов результатов. Как-то выглядит очень перемудрённо.

Другой характерной особенностью, унаследованной от IBM/370 и POWER, является архаичная система названия инструкций ассемблера. Название каждой инструкции отражает тип её аргументов. Например, LWZ означает грузить 32 бита (слово) из памяти в регистр, а LBZ – грузить байт. Такая система позволяет именовать регистры номерами, но этого давно уже практически никто не использует – все ассемблеры позволяют использовать имена типа R1 для регистров и в абсолютном большинстве кодов так и делают. Также от IBM/370 и POWER унаследована особая роль регистра 0, он при использовании в адресации и нескольких других случаях заменяется нулем.

Архитектура PowerPC отличается также необычностью работы с регистрами. Прежде всего, регистров реально много. Есть 32 регистра общего назначения (причем для указателя стека выбран почему-то регистр с номером 1), 32 регистра для работы с вещественными числами, регистр для хранения флагов результатов целочисленных математических операций, что, как это уже отмечалось, даёт возможность сохранять до 8 тетрад-слотов с такими флагами. Для вещественных чисел есть также специальный регистр для хранения флагов и других битовых значений. Есть ещё и специальные регистры, число которых может быть более 1000! Из этих специальных регистров только несколько доступны для несистемного программирования. Часть специальных регистров могут даже иметь разное назначение на разных вариантах PowerPC! Три специальных регистра необходимы для прикладного программирования: регистр исключений (хранит, в частности, перенос и оба флага переполнения), связной регистр LR (нужен для вызова подпрограмм, аналогичен подобному регистру в архитектурах IBM/370, POWER, ARM и др.) и регистр-счётчик. Два последних регистра фактически являются дополнением к регистрам общего назначения (РОН). Интересно, что регистр-счётчик можно использовать и как адрес. Можно заметить отсутствие в архитектуре PowerPC регистра PC (счётчика команд), что действительно весьма необычно. Регистр состояния – системный и закрыт поэтому для прикладного программирования. В PowerPC есть и много других системных регистров. При этом нужно иметь в виду, что системные регистры и специальные – это совершенно разные наборы регистров.

Система команд PowerPC весьма большая и скорее нетипичная для архитектур RISC. Хотя, строго говоря, из названия POWER и следует, что эта архитектура лишь использует архитектуру RISC как элемент, повышающий производительность, имея в своём составе и другие элементы. Кроме того, несмотря на обилие команд и регистров, PowerPC отличается весьма простыми методами адресации, что вполне соответствует идеологии RISC. Обычно инструкции имеют три операнда. Можно использовать регистры и 16-битные константы, что выгодно отличает от архитектуры ARM, где константы только 8-битные, или IBM/370, где они 12-битные. Для адресации можно использовать либо адрес в регистре с 16-битным явным смещением (у ARM смещение 12-битное), либо индексную адресацию из суммы двух регистров. Использование регистра 0 даёт возможность использовать просто явный 16-битный адрес или адрес в одном регистре. Можно заметить, что перемещаемая адресация отсутствует.

Арифметические команды отличаются несколькими необычностями. Например, наряду с обычным сложением есть ещё сложения с 16-битной константой, которая рассматривается как старшее полуслово, добавление 0 или -1 с переносом. Последние две команды – двухадресные. Подобные вариации есть и для вычитания, которое есть только в обратном варианте, когда вычитают от последнего операнда.

Математические команды по умолчанию не меняют флагов. Для их генерации нужно использовать специальные варианты инструкций, а для команд сравнений указывать ещё в какой из 8-и слотов флаги помещать (слот 0 используется по умолчанию для целых, а 1 для вещественных). Для генерации переноса или его использования также нужны специальные варианты инструкций. И для генерации переполнения также нужен свой вариант инструкций. В общем случае, инструкция может иметь до 12 вариантов, отличающихся только способом работы с флагами! Рассмотрим все варианты инструкций сложения:
1) ADD ADD. ADDO ADDO. ADDC ADDC. ADDCO ADDCO. ADDE ADDE. ADDEO ADDEO (основные 12);
2) ADDI ADDIS ADDIC ADDIC. (для работы с 16-битной константой);
3) ADDME ADDME. ADDMEO ADDMEO. ADDZE ADDZE. ADDZEO ADDZEO. (двухадресные, для работы с переносом и константами 0 и -1).
Как можно заметить не все возможные варианты инструкций реально существуют, нет, например, инструкции сложения с переносом без генерации переноса. Всего можно насчитать 24 вариант мнемоник для инструкции сложения. Для вычитаний существует чуть меньше, только 21. Для сравнения, для архитектуры x86 есть всего две мнемоники для сложений ADD и ADC. Для ARM – 4, соответствующие мнемоникам PowerPC: ADD, ADDS, ADC и ADCS.

Команд для целочисленного сравнения четыре, а с учётом указания слота – 32! В большинстве других процессоров вместо этого многообразия есть обычно одна единственная инструкция CMP.

Инструкций для работы с поразрядными логическими операциями рекордно много. Помимо типовых И, ИЛИ, исключающего ИЛИ и НЕ есть ещё и все остальные возможные поразрядные логические операции: И-НЕ, ИЛИ-НЕ, эквивалентность, ... В операциях И и ИЛИ один из аргументов можно инвертировать – это инструкции ANDC и ORC. Название второй инструкции получилось весьма звучным. Здесь буква C означает Complement (дополнение), для инструкций сложения и вычитания эта же буква означает Carry (перенос), что может немного сбивать с толку. Для каждой поразрядной логической инструкции с регистрами существуют варианты с установкой и без установки флагов. Кроме того, для операций AND, OR и XOR в качестве операнда можно использовать 16-битную константу в двух вариантах, как и для сложения или вычитания. Некоторые операции с константами, как и в случаях со сложением и вычитанием, можно использовать только в варианте с установкой флагов, а некоторые только без. Для операций AND и OR можно насчитать по 6 разных мнемоник, в других ассемблерах обычно хватает одной-двух.

Умножения на PowerPC похоже на умножение в ARM, одно умножение дает только половину результата. Но в отличие от ARM, в PowerPC есть умножения для получения обоих частей результата. Как обычно, есть варианты команд с установкой флагов и без. Есть также варианты с установкой флага переполнения и без. Для получения старшей половины результата опять есть два варианта для знакового и беззнакового умножения. Итого имеем 8 разных названий для операций умножения.

Деление на PowerPC отличается от этой операции на других процессорах, оно производит только частное. Для получения остатка нужно ещё умножать и вычитать. Как и для умножения, для деления есть 8 вариантов. Интересно, что деление на ноль можно обнаружить по флагу переполнения. При знаковом делении возможна ещё одна неопределенность, когда наибольшее отрицательное число делится на -1.

В системе команд PowerPC есть довольно специальные, например, подсчет числа лидирующих нулей в слове. Обычные сдвиги и вращения в PowerPC стали частными случаями более необычных и сложных операций, которые используют битовую маску. Благодаря этой маске можно, в частности, сдвигать любую битовую последовательность в слове, например, инструкция RLWINM R2,R3,4,0,31 просто вращает значение регистра 3 влево 4 раза и заносит результат в регистр 2, RLWINM R2,R3,2,0,29 делает сдвиг влево на 2, RLWINM R2,R3,30,2,31 – логический сдвиг вправо на 2, RLWINM R2,R3,4,0,6 берёт 7 бит с позиции 4 из регистра 3 и переносит их в начало регистра 2 с занулением всех остальных бит регистра 2, RLWINM R2,R3,11,25,31 – аналогично, но переносит эти 7 бит в конец регистра 2. Эта же команда может просто занулять последовательности бит, например, RLWINM R2,R2,0,5,5 зануляет все биты кроме 5-о в R2, RLWINM R2,R2,0,6,4 зануляет только бит 5. Маску можно использовать не для зануления, а для сохранения неизменными выбранных бит в регистре-результате, что реализует вставку бит, например, RLWIMI R2,R3,30,2,8 берёт 7 первых бит регистра 3 и вставляет их в позицию 2 регистра 2. Вращать можно только влево, но из-за наличия быстрого сдвигателя, замена правых вращений на левые не замедляет расчётов.

Для логических сдвигов есть зачем-то 4 дублирующие RLWNM операции, что весьма странно для архитектуры RISC. Для арифметических сдвигов вправо предусмотрены специальные операции и только они среди операций сдвигов и вращений устанавливают флаг переноса, хотя делают это совершенно не так, как могла бы подсказать интуиция. Среди команд PowerPC нет таких, которые могли бы делать вращения через перенос или даже просто устанавливать перенос типовым образом. Однако наличие операций для битовых вставок позволяет реализовать многословные сдвиги и вращения гораздо эффективнее, чем через перенос.

Операции для работы с памятью для PowerPC делятся на две группы: для отдельных регистров и для групп регистров. Инструкции первой группы весьма просты и удобны: можно как грузить байт, полуслово или слово в регистр, так и сохранять байты, полуслова и слова из регистра в память. Полуслова при загрузке в регистр расширяются знаком или нулями, а байты только нулями. Интересной особенностью PowerPC является адресация с обновлением, когда после загрузки или выгрузки значение регистра-адреса увеличивается на размер загруженных или выгруженных данных. Однако, в отличие от ARM нет возможности сначала увеличить адрес, а потом делать операцию с памятью. Весьма интересны операции PowerPC для загрузки-выгрузки слов и полуслов с обратным порядком байт.

Групповые загрузки и выгрузки сделаны в PowerPC неуклюже, гораздо с меньшей гибкостью, чем для ARM, 68k или даже древнего IBM/360. Так PowerPC позволяет грузить/выгружать только все регистры подряд от данного до последнего, т.е. R31. Возможно из-за неудобства этих инструкций они могут быть реализованы так, что исполняются медленнее, чем эквивалентная группа инструкций для индивидуальной загрузки-выгрузки! Для PowerPC есть ещё скорее экзотические инструкции строковой или побайтовой загрузки-выгрузки, которые позволяют как грузить невыровненные последовательности байт в группу регистров, так и выгружать последовательности байт по любому адресу в памяти. Из-за мультискалярности архитектуры, PowerPC поддерживает особые инструкции для синхронизации работы с памятью.

Наверное наибольшей неожиданностью при знакомстве с системой команд PowerPC может оказаться большая сложность инструкций перехода. Обычно их используют в упрощенных, а не в полных вариантах. В PowerPC такие инструкции обрабатываются на отдельном устройстве, параллельно исполнению других команд. В командах переходов, в отличие от других команд, можно использовать не только абсолютную адресацию, но и перемещаемую. Для безусловных переходов явный адрес задаётся 26 битами, что весьма хорошо для перемещаемых переходов, но вот для абсолютных переходов получается, что можно адресовать только первые 64 МБ памяти. Если нужен абсолютный переход за пределы 64 МВ, то адрес перехода надо сначала поместить в специальный регистр, связи или счётчик. Переходов по содержимому РОН нет. Однако, специальный регистр можно загрузить только содержимым РОН, что делает необходимым две дополнительные операции для выполнения абсолютного перехода в любое место памяти.

Самая большая заморочка связана с условными переходами. Явный адрес здесь задаётся только 15-ю битами, что вполне неплохо для перемещаемых переходов, но почти бессмысленно мало для абсолютных. Поэтому вместо последних нужно обычно использовать условные переходы через содержимое специальных регистров со всеми издержками, описанными выше. Почти во всех условных переходах используется специальный регистр-счётчик, который декрементируется и сравнивается с нулём, что создаёт одно из двух условий для перехода. Второе условие определяется одним из битов в регистре флагов, т.е. можно выбирать любой бит во всех восьми слотах. Можно произвольно комбинировать оба условия, в частности, игнорировать одно или даже оба. В последнем случае получим короткий безусловный переход, бесполезную дублирующую длинный переход инструкцию. Кроме того, можно делать подсказку предсказателю переходов, но эта возможность работает не на всех процессорах. Интересно, что переход через регистр-счётчик можно делать и при использовании этого регистра для генерации условия перехода – трудно поверить, что такое может быть как-то использовано практически. Интересно ещё, что вызовы подпрограмм можно делать через регистр связи, т.е. вызываем подпрограмму через адрес в этом регистре и сохраняем адрес возврата в нем же. Условные переходы по содержимому специальных регистров дают возможность условных возвратов из подпрограмм, что часто весьма полезно.

PowerPC в отличие от ARM изначально имел мощную поддержку системной работы с памятью и кэшем. Поддерживается страничная виртуальная память и многозадачность. Кроме того, в PowerPC изначально была заложена идея перехода на 64-разрядную архитектуру, поэтому переход на неё для этого процессора – это теоретически совершенно естественный процесс, в отличие от x86 или ARM.

Первый процессор PowerPC (601) унаследовал несколько десятков команд от архитектуры POWER, от которых отказались в последующих вариантах PowerPC. Однако macOS продолжает поддерживать эти команды через эмуляцию. Вообще, коды команд PowerPC имеют свои соответствия в архитектуре POWER, но ряд инструкций определяются несколько по-разному в обеих архитектурах. Интересно, что почему-то, несмотря на указанное соответствие, ассемблерные инструкций для POWER и PowerPC в большинстве случаев совершенно разные.

Архитектура PowerPC изначально поддерживает операции с вещественными числами. Более того, целочисленные и вещественные вычисления проходят параллельно, что позволяет, например, ускорить целочисленные расчёты, используя для целочисленных данных вещественные регистры и инструкции пересылки данных. Так как для других систем дробная арифметика не рассматривалась, то и для PowerPC с этим не будет исключения. Можно только упомянуть, что точность расчетов на математических сопроцессорах для x86 и 68k несколько выше, чем на PowerPC. Что создавало некоторые проблемы при переходе с 68k на PowerPC.

Интересно сравнить возможности первых PowerPC и ARM. PowerPC имеет несколько очевидных преимуществ. Главное из которых – это существенно большее количество РОН. Другие преимущества: встроенная поддержка управления памятью, встроенный математический сопроцессор, параллельное исполнение инструкций, аппаратное деление, инструкции для вращения с маской, индексная адресация, бо́льшие значения для констант и переходов, поддержка работы с полусловами. Но у ARM также есть свои заметные преимущества: очень гибкая работа с быстрым сдвигателем делает многие вычисления намного более быстрыми, инструкции для групповой загрузки-выгрузки регистров намного удобнее, наличие общей перемещаемой адресации, существенно меньшее энергопотребление, условность всех операций позволяет избегать части переходов и писать более быстрые и компактные коды, наличие умножения с накоплением. А современные ARM-32 могут использовать и полуслова, и полное умножение, и управление памятью.

В заключении, краткий список наиболее известных процессоров PowerPC. 601 появился в 1993 и использовался в первых Power Macintosh. 603 появился чуть позже, он медленнее 601 на одинаковых частотах, но проще, что позволяло выпускать его более высокочастотные варианты. 604 появился в 1994, он имеет расширенные мультискалярные возможности – 6 независимых устройств для исполнения команд, 601 имеет их только 3. 620 появился с задержкой, только в 1997 и был первым 64-битным PowerPC. Однако он оказался медленнее 603 и поэтому нашёл только весьма ограниченное применение. Наиболее успешными процессорами PowerPC стали варианты модели 750, появившейся впервые в 1997. В Apple этот процессор получил наименование G3. Это 32-битный процессор. Переход на 64-бита в Apple проходил не сразу. 7400 производился с 1999 и поддерживал векторную математику, в Apple его называли G4. Только в 2002 появился наконец процессор 970, который стал первым 64-битным процессором, нашедшим себе применение в массовых персональных компьютерах. В Apple этот процессор получил обозначение G5. И на этом история PowerPC в персональных компьютерах фактически подошла к концу. AIM распался в 2004. Интересно, что Apple стала готовить переход на архитектуру x86, ещё в конце 90-х.

Главной целью этого обзора было ознакомить читателей с типовыми особенностями первых процессоров PowerPC. Буду рад получить дополнения и критические замечания. Спасибо за прочтение.


Cтатья опубликована на geektimes, а также переведена на английский.