Как языки повлияли на дизайн процессора? [закрыто]


44

Нам часто говорят, что аппаратному обеспечению не важно, на каком языке написана программа, поскольку оно видит только скомпилированный двоичный код, однако это не вся правда. Например, рассмотрим скромный Z80; его расширения к набору команд 8080 включают в себя такие инструкции, как CPIR, которые полезны для сканирования строк в стиле C (с нулевым символом в конце), например, для выполнения strlen(). Разработчики, должно быть, определили, что запуск программ на C (в отличие от Pascal, где длина строки находится в заголовке) - это то, для чего их дизайн, вероятно, будет использоваться. Другой классический пример - машина Лиспа .

Какие еще примеры есть? Например, инструкции, количество и тип регистров , режимы адресации, которые делают определенный процессор предпочтительным для соглашений конкретного языка? Я особенно заинтересован в пересмотрах той же семьи.


3
Не забывайте, что Z-80 также имел инструкцию LDIR, очень полезную при копировании строк, когда вы знаете длину (как в Pascal, где длина была сохранена в заголовке).
TMN

27
1. Z-80 был разработан в 1975 году, когда Unix и C были малоизвестной операционной системой и языком на нескольких компьютерах, за 3 года до первого выпуска K & R. 2. В Паскале нет ничего, что предписывало бы длину строки «в заголовке». 3. Строки в CP / M, основной операционной системе на тот момент, заканчивались символом «$», а не «\ 0». CPIR может искать любой персонаж. 4. CPIR сопоставляется с CPDR (поиск в обратном направлении), а также с другими инструкциями -IR и -DR. Вывод: CPIR не имеет ничего общего с языком программирования C. Это просто инструкция поиска байтов.
Либрик

4
Самая большая (и одна из самых раздражающих для разработчиков оборудования) вещей, навязываемых C, - это байтовая адресация. Процессоры были бы проще и быстрее без этой мерзости.
SK-logic

1
@ SK-logic: Хотя стандарт POSIX требует байтовой адресации, стандарт C не требует. Любая реализация, где sizeof(int)равно 1, должна требовать, чтобы этот тип charбыл подписан (поскольку он intдолжен содержать все значения типа char). Я написал код для машины, где charи int16-разрядные целые числа со знаком; самые большие трудности заключаются в том, что нельзя использовать объединения для преобразования типов, а эффективное хранение большого количества байтов требует ручной упаковки и распаковки. Эти проблемы незначительны по сравнению с тем, что в C возможно, что sizeof (int) == sizeof (long), так как ...
суперкат

2
... это означает, что не существует стандартного типа, который гарантированно содержал бы разницу между двумя unsigned intзначениями. C99 улучшил эту ситуацию, но до C99 не было гарантированного безопасного одношагового способа сравнения потенциально отрицательного значения со значением типа unsigned int(перед сравнением нужно было проверить, было ли число отрицательным).
суперкат

Ответы:


20

Существующие ответы сосредоточены на изменениях ISA . Есть и другие аппаратные изменения. Например, C ++ обычно использует виртуальные таблицы для виртуальных вызовов. Начиная с Pentium M , в Intel появился компонент «косвенный предсказатель ветвления», который ускоряет вызовы виртуальных функций.


6
А в архитектуре Berkeley RISC была реализована концепция «регистрового файла», поэтому вместо того, чтобы функции «разливали» регистры в стек, для каждой функции был выделен блок из 8 регистров. Это значительно ускорило объектно-ориентированный код, поскольку он состоит из множества вызовов методов для коротких методов.
TMN

1
Это неверный пример. Конструкция «Таблица указателей функций» также используется во многих сценариях динамического связывания, например, через импорт и экспорт DLL в Windows, а также используется в программах на Си. Хотя я полагаю, что вы можете утверждать, что он показывает, что процессор оптимизирован для конкретного использования, он не зависит от языка.
DeadMG

@DeadMG: другие дела принесли пользу, это правда. Но до тех пор, пока C ++ не стал популярным, на процессоры CPU не влияли . И это был поставленный вопрос. Точно так же у TMN есть пункт о регистрационных файлах. У Ассамблеи не было такой четкой концепции функций. Функции, как мы их обычно понимаем сегодня, восходят к Algol 60, и поэтому мы можем сказать, что Algol 60 повлиял на дизайн файла регистра ЦП.
MSalters

14

Набор команд Intel 8086 включает в себя вариант «ret», который добавляет значение к указателю стека после получения адреса возврата. Это полезно для многих реализаций Pascal, когда вызывающая функция помещает аргументы в стек перед выполнением вызова функции и выводит их позже. Если подпрограмма будет принимать, например, четыре байта параметров, она может закончиться «RET 0004» для очистки стека. В отсутствие такой инструкции такое соглашение о вызовах, вероятно, потребовало бы, чтобы код вставлял адрес возврата в регистр, обновлял указатель стека и затем переходил в этот регистр.

Интересно, что большинство кода (включая подпрограммы ОС) на исходном Macintosh использовали соглашение о вызовах Pascal, несмотря на отсутствие упрощающей инструкции в 68000. Использование этого соглашения о вызовах позволило сэкономить 2-4 байта кода на типичном сайте вызовов, но потребовало дополнительного 4-6 байт кода на сайте возврата каждой функции, которая принимала параметры.


Существует также ENTERаналог этого RET n...
Herby

1
@herby: я не думаю, что ENTERсуществовал в оригинальном 8086; это пришло с более поздними процессорами. Тем не менее, это поднимает интересный момент: режимы адресации на основе BP четко разработаны с использованием стековых параметров и локальных объектов, доступ к которым осуществляется через указатель кадра. Я нахожу это соглашение интересным по ряду причин, особенно учитывая, что (1) код на чистом ассемблере более склонен использовать значения в регистрах, чем в стеке, но (2) преимущества адресации [BP + nn] перед [SP +] nn] адресация более
важна

... для рукописного кода сборки. Компилятор, как правило, будет знать, для каждой сгенерированной инструкции, как сравниваются SP и BP; например, если SP - это BP-8, то компилятору не так-то просто обратиться к [BP + 12], чем [SP + 20]. Если при перекомпиляции компилятор должен добавить еще один PUSH / POP вокруг блока кода, он может соответствующим образом скорректировать смещения на основе SP. С другой стороны, в рукописной сборке добавление PUSH / POP, скорее всего, потребует настройки кода между ними. Таким образом, указатели на фреймы в основном приносят пользу комбинированному высокоуровневому / асм-коду.
суперкат

Возможно, возможность повторного использования кода без его перекомпиляции также имеет некоторую предельную пользу для адресации BP. И Бог знает, не являются ли инструкции адресации BP более быстрыми в схемах, чем инструкции, адресованные SP, поскольку адресация BP является своего рода стандартом ...
herby

3
@herby: На самом деле, я подозреваю, что большая часть причин, по которым компиляторы обычно используют указатели на фреймы, во многом связана с отладкой. Для отладки программы, которая не использовала такое соглашение, потребуется, чтобы компилятор сгенерировал - и использовал отладчик - файл, содержащий смещение SP-BP для каждой инструкции. Такие подробные метаданные распространены сегодня (и являются неотъемлемой частью того, что делает языки сбора мусора практичными), но требуемый объем оперативной памяти был бы неприемлемым 30 лет назад.
суперкат

10

Одним из примеров является MIPS, который имеет как addи adduдля перехвата и игнорирования переполнения соответственно. (Также subи subu.) Требовался первый тип инструкций для языков, таких как Ada (я думаю, я никогда не использовал Ada), которые явно имеют дело с переполнениями, и второй тип для языков, подобных C, которые игнорируют переполнения.

Если я правильно помню, фактический процессор имеет некоторые дополнительные схемы в ALU для отслеживания переполнений. Если бы единственный язык, который заботился о людях, был C, это бы не нуждалось в этом.


Не уверен, если связаны, но эти инструкции, вероятно, также полезны в других ситуациях, таких как безопасное выделение памяти, то есть, если вы выделяете nmemb*size+offsetбайты и вам нужно убедиться, что вы не получите переполнение.
NikiC

@NikC: Я думал , что adduи subuинструкции (те , которые не проверяют для перелива) были те , которые были добавлены , чтобы сделать C счастливым. Конечно, я действительно не знаю - мы только смутно освещали это в лекции, и я, конечно, не специалист по архитектуре: P.
Тихон Джелвис

О да, я думал об обратном, извините: /
NikiC

8

Серия Burroughs 5000 была разработана для эффективной поддержки ALGOL, а Intel iAPX-432 была разработана для эффективной работы Ada. У Inmos Transputer был свой собственный язык, Оккам. Я думаю, что процессор Parallax "Propeller" был разработан для программирования с использованием своего собственного варианта BASIC.

Это не язык, но набор инструкций VAX-11 содержит одну инструкцию для загрузки контекста процесса, которая была разработана по запросу команды разработчиков VMS. Я не помню деталей, но ISTR потребовалось так много инструкций, чтобы реализовать серьезный верхний предел количества процессов, которые они могли запланировать.


Что в этих конструкциях делает их особенно подходящими? Например, какая особенность iAPX особенно полезна для Ada?
Гай

ISTR, что цель Ada iAPX-432 больше пыталась сохранить неудачный дизайн, привязав его к чему-то с большими ожиданиями, чем к чему-либо еще.
AProgrammer

@AProgrammer: я уверен, что iAPX-432 с самого начала был разработан для использования Ada. Я даже вспоминаю некоторые слухи о том, что Intel не собирается публиковать набор инструкций, чтобы не поощрять программирование на ассемблере и заставлять людей использовать Аду для всего.
TMN

1
@ TMN, проект Intel 432, начатый в 1975 году и представленный в 1981 году (Википедия). Ironman (окончательные требования к Аде) был опубликован в январе 1977 года, а зеленый был выбран в мае 1979 года, изменен, а окончательный результат был опубликован в качестве военного стандарта в июле 1980 года. Существует проблема с указанием сроков, что iAPX-432 был разработан с начало использовать Ада. (Это поздний и типичный процессор «закрыть семантический пробел» с обычными недостатками в то время, когда начали искать альтернативы; продвигать его как процессор Ada было условно, чтобы спасти неудачный дизайн - ISTR, что никто, кроме Intel, не использовал его )
AProgrammer

1
@AProgrammer: Хммм, похоже ты прав. Я наткнулся на эту статью от ведущего архитектора 432 и в кратком изложении он говорит: «Такое тесное совпадение архитектуры и языка не произошло, потому что 432 был разработан для выполнения Ады - это не так». Мне придется выкопать мою старую книгу 432 и посмотреть, что она говорит.
TMN

8

Похоже, что до сих пор никто не упомянул о том, что успехи в оптимизации компилятора (где базовый язык в значительной степени не имеет значения) привели к переходу от наборов команд CISC (которые были в значительной степени разработаны для кодирования людьми) к наборам команд RISC (которые в основном были предназначен для кодирования компиляторами.)


5

Семейство Motorola 68000 представило несколько режимов автоинкремента, которые сделали копирование данных через процессор очень эффективным и компактным.

[Обновленный пример]

это был некоторый код C ++, который повлиял на ассемблер 68000

while(someCondition)
    destination[destinationOffset++] = source[sourceOffset++]

реализован в обычном ассемблере (псевдокод, я забыл 68000 команд ассемблера)

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1)
    move (adressRegister2), akku
    increment(adressRegister1, 1)
    increment(adressRegister2, 1)
}

с новым адресным режимом это стало чем-то похожим на

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1++)
    move (adressRegister2++), akku
}

только две инструкции в цикле вместо 4.


1
Как на это повлияли условные обозначения конкретного языка?
Гай

см обновленный пример
k3b

Ах, напоминает мне об оптимизации цикла DBxx в 68010.
Гай

7
На самом деле, я думаю, что у вас есть это задом наперед. Автоматическая адресация [in | de] была частью набора инструкций PDP-11, что, вероятно, повлияло на дизайн C.
TMN

5

Мэйнфрейм IBM серии Z является потомком IBM 360 1960-х годов.

Там было несколько инструкций, специально предназначенных для ускорения программ на COBOL и Fortran. Классическим примером является BXLE«Ветвь по индексу с низким или равным», которая представляет собой большую часть forцикла Фортрана или COBOL, PERFORM VARYING x from 1 by 1 until x > nинкапсулированных в одну инструкцию.

Существует также целое семейство упакованных десятичных инструкций для поддержки десятичной арифметики с фиксированной запятой, распространенной в программах на языке COBOL.


Я думаю, что вы имеете в виду потомок .
Заводная муза

@ X-Zero - упс! Рано утром, не хватает кофеина в системе и т.д .......
Джеймс Андерсон

1
Более интересной является инструкция повторения блока TI 32050 DSP. Его операндом является адрес инструкции, следующей за последней в цикле; загрузка регистра счетчика циклов и последующее выполнение инструкции повторения блока приведут к тому, что инструкции до (но не включая) цели будут повторяться указанное число раз. Очень сильно напоминает DOпетлю Фортрана .
суперкат

@supercat Каждый DSP, достойный этого имени, включает в себя три функции: цикл с нулевыми издержками, многократное накопление одной команды и какой-либо режим адресации с инверсией битов. Почти каждый алгоритм DSP, известный человеку, использует циклы. Двумя наиболее распространенными алгоритмами являются FIR-фильтр, представляющий собой цикл с многократным накоплением, и FFT, для которого адресация с обращенной битой является критической. Многие DSP включают в себя операцию бабочки FFT с одной инструкцией radix-2 или двойное умножение / сложение, которое можно использовать для создания бабочки с одной инструкцией.
Джон Р. Штром

@ JohnR.Strohm: Каждый DSP, который я видел, включает в себя повторное умножение-накопление, но не все из них включают в себя более обобщенные циклы с нулевыми издержками. На самом деле, я не совсем уверен, почему такие циклы следует рассматривать только как функцию «DSP», так как они были бы полезны также для большого количества кода «обычного процессора».
суперкат

3

Ранние процессоры Intel имели следующие функции, многие из которых теперь устарели в 64-битном режиме:

  • Инструкции ENTER, LEAVE и RET nn [в ранних руководствах было подробно сказано, что они были введены для блочно-структурированных языков, например, Pascal, который поддерживает вложенные процедуры]
  • инструкции по ускорению арифметики BCD (AAA, AAM и т. д.); также поддержка BCD в x87
  • Инструкции JCXZ и LOOP для реализации подсчитанных циклов
  • INTO, для генерации ловушки при арифметическом переполнении (например, в Ada)
  • XLAT для поиска по столу
  • BOUND для проверки границ массива

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

Набор инструкций SSE 4.1 представляет инструкции для обработки строк, как с нулевым, так и с нулевым окончанием (PCMPESTR и т. Д.)

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


3

Некоторые процессоры ARM, в основном мобильные, включают в себя (d) расширение Jazelle, которое является аппаратным интерпретатором JVM; он интерпретирует байт-код Java напрямую. JVM, поддерживающая Jazelle, может использовать аппаратное обеспечение для ускорения выполнения и устранения большей части JIT, но резервирование виртуальной машины программного обеспечения все еще гарантируется, если байт-код не может быть интерпретирован на кристалле.

Процессоры с таким модулем включают в себя инструкцию BXJ, которая переводит процессор в специальный «режим Jazelle», или, если активация модуля не удалась, она просто интерпретируется как обычная команда перехода. Устройство повторно использует регистры ARM для хранения состояния JVM.

Преемником технологии Jazelle является ThumbEE


2

Насколько я знаю, это было более распространенным в прошлом.

Есть сеанс вопросов, на котором Джеймс Гослинг сказал, что были люди, пытающиеся сделать аппаратное обеспечение, которое могло бы лучше справляться с байт-кодом JVM, но затем эти люди нашли бы способ сделать это с помощью обычного «универсального» intel x86 (возможно, компиляции байт-код каким-то умным способом).

Он упомянул, что есть преимущество в использовании универсального популярного чипа (такого как Intel), потому что у него есть крупная корпорация, которая тратит огромные суммы денег на продукт.

Видео стоит проверить. Он говорит об этом на 19 или 20 минуте.


2

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


2

Процессор Intel iAPX был специально разработан для ОО-языков. Не совсем получилось, хотя.

IAPX 432 ( Intel Advanced Processor архитектура ) был первый 32-разрядный микропроцессор Intel, дизайн, введенный в 1981 году в виде набора из трех интегральных схем. Он был задуман как основной проект Intel для 1980-х годов, в котором реализованы многие передовые функции многозадачности и управления памятью. Таким образом, дизайн был назван Micromainframe ...

IAPX 432 был «спроектирован так, чтобы полностью программироваться на языках высокого уровня» , причем Ada была основной и поддерживала объектно-ориентированное программирование и сборку мусора непосредственно в аппаратном обеспечении и микрокоде . Прямая поддержка различных структур данных была также предназначена для того, чтобы современные операционные системы для iAPX 432 могли быть реализованы с использованием гораздо меньшего количества программного кода, чем для обычных процессоров. Эти свойства и особенности привели к созданию аппаратного и микрокодов, который был намного сложнее, чем большинство процессоров той эпохи, особенно микропроцессоры.

Используя современную полупроводниковую технологию, инженеры Intel не смогли перевести проект в очень эффективную первую реализацию. Наряду с отсутствием оптимизации в преждевременном компиляторе Ada это способствовало довольно медленным, но дорогостоящим компьютерным системам, которые выполняли типичные тесты примерно на 1/4 скорости нового чипа 80286 на той же тактовой частоте (в начале 1982 года).

Этот первоначальный разрыв в производительности с довольно низкопрофильной и недорогой линией 8086, вероятно, был основной причиной, по которой план Intel заменить последний (позже известный как x86) на iAPX 432 провалился. Хотя инженеры нашли способы улучшить дизайн следующего поколения, архитектура iAPX 432 Capability теперь стала восприниматься скорее как накладные расходы на реализацию, а не как упрощенная поддержка, которой она должна была быть.

Проект iAPX 432 был коммерческим провалом для Intel ...


Читая статью, кажется, что многие аспекты дизайна могут быть полезны в объектно-ориентированных средах, таких как сегодня. Архитектура, которая использует комбинацию 32-битного идентификатора объекта и 32-битного смещения, может во многих случаях обеспечивать лучшую производительность кэширования, чем та, в которой все идентификаторы объектов были бы 64-битными (в большинстве случаев приложение, которое будет использовать миллиарды объектов, будет лучше обслуживать, вместо этого иметь больше, больше, один, который будет хранить миллиарды байт в одном объекте, лучше разделить на меньшие объекты
суперкат

1

У 68000 был MOVEM, который больше всего подходил для помещения нескольких регистров в стек в одной инструкции, чего и ожидали многие языки.

Если вы видели MOVEM (MOVE Multiple), предшествующий JSR (Jump SubRoutine) по всему коду, то вы, как правило, знали, что имеете дело с C-совместимым кодом.

MOVEM допускает автоматическое увеличение регистра назначения, позволяя при каждом использовании продолжать наложение на место назначения или удаление из стека в случае автоматического уменьшения значения.

http://68k.hax.com/MOVEM


1

Архитектура Atmel AVR полностью разработана с нуля для того, чтобы она подходила для программирования на C. Например, эта заметка по применению уточняется далее.

IMO, это тесно связано с отличным ответом rockets4kids: ранние PIC16 разрабатывались для прямого программирования на ассемблере (всего 40 инструкций), а более поздние семейства нацелены на C.


1

Когда был разработан числовой сопроцессор 8087, для языков было довольно распространенным делом выполнять всю математику с плавающей запятой, используя тип с наивысшей точностью, и округлять результат только до более низкой точности при назначении его переменной с более низкой точностью. В исходном стандарте C, например, последовательность:

float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;

будет способствовать aи bк double, добавить их, поощрять cк double, добавьте его, а затем сохранить результат округляется до float. Хотя во многих случаях компилятору было бы быстрее генерировать код, который выполнял бы операции непосредственно над типом float, было проще иметь набор подпрограмм с плавающей запятой, которые будут работать только над типом double, вместе с подпрограммами для преобразования в / от float, чем иметь отдельные наборы подпрограмм для обработки операций floatи double. 8087 был разработан вокруг этого подхода к арифметике, выполняя все арифметические операции с использованием 80-битного типа с плавающей запятой [80 битов, вероятно, было выбрано, потому что:

  1. На многих 16- и 32-разрядных процессорах быстрее работать с 64-разрядной мантиссой и отдельным показателем степени, чем со значением, которое делит байт между мантиссой и показателем степени.

  2. Очень трудно выполнять вычисления, которые являются точными с полной точностью используемых числовых типов; например, если кто-то пытается вычислить что-то вроде log10 (x), проще и быстрее вычислить результат с точностью до 100 муль для 80-битного типа, чем для вычисления результата с точностью до 1 муль от 64-битного тип и округление первого результата до 64-битной точности даст 64-битное значение, которое является более точным, чем второе.

К сожалению, будущие версии языка изменили семантику того, как должны работать типы с плавающей точкой; в то время как семантика 8087 была бы очень полезной, если бы языки поддерживали их последовательно, если бы функции f1 (), f2 () и т. д. возвращали тип float, многие авторы компилятора сами long doubleвзялись бы за создание псевдонима для 64-битного двойного типа а не 80-битный тип компилятора (и не предоставляют никаких других средств для создания 80-битных переменных), и произвольно оценивать что-то вроде:

double f = f1()*f2() - f3()*f4();

любым из следующих способов:

double f = (float)(f1()*f2()) - (extended_double)f3()*f4();
double f = (extended_double)f1()*f2() - (float)(f3()*f4());
double f = (float)(f1()*f2()) - (float)(f3()*f4());
double f = (extended_double)f1()*f2() - (extended_double)f3()*f4();

Обратите внимание, что если f3 и f4 возвращают те же значения, что и f1 и f2 соответственно, исходное выражение должно явно возвращать ноль, но многие из последних выражений могут и не быть. Это привело к тому, что люди осудили «дополнительную точность» 8087, хотя последняя формулировка обычно превосходила бы третью и - с кодом, соответствующим образом использующим расширенный тип double, - редко бы уступала.

За прошедшие годы Intel отреагировала на тенденцию языка (ИМХО) к тому, чтобы заставить промежуточные результаты округляться до точности операндов, проектируя свои более поздние процессоры, чтобы способствовать такому поведению, в ущерб коду, который выиграл бы от использования более высоких точность промежуточных расчетов.


Обратите внимание, что вы уже получили ответ ( выше ) в этом посте. Являются ли они ответами, которые можно / нужно объединить в один?

@MichaelT: я так не думаю - один описывает дизайн стека, а другой - семантику с плавающей точкой.
суперкат

Просто убеждаюсь. Лично я считаю, что можно было бы дать один более сильный ответ (используя заголовки для разделения разделов), но это мое мнение. Вы можете по-прежнему использовать заголовки, чтобы четко определить наверху, что адресует каждая часть ответа ( ## How the stack changed the processorи ## How floating point changed the processor), чтобы люди могли получить правильное мышление при чтении и с меньшей вероятностью считают, что вы либо не обращали внимания на ответы или репосты. такие же (похожие) ответы.

@MichaelT: два ответа достаточно разобщены, поэтому я думаю, что за них следует голосовать отдельно. Хотя 80486 вобрал в себя функции, ранее выполнявшиеся 8087/80287/80387, 8086 и 8087 были разработаны как отдельные микросхемы с почти независимой архитектурой. Хотя оба запускали код из общего потока команд, это было обработано с помощью 8086, который обрабатывал определенные последовательности байтов как запросы на генерацию запросов на чтение / запись адресов, игнорируя шину данных, а 8087 игнорировал все остальное, что происходило.
суперкат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.