TL: DR: нет, AFAIK не существует RISC ISA с установкой флага частичным регистром операций уже, чем 32 бита. Но многие 64-битные RISC ISA (например, AArch64), которые вообще имеют ФЛАГИ, могут установить их из результата 32-битной операции.
См. Последний раздел: это связано с общим отсутствием спроса на программную проверку переполнения целых чисел или с проблемой курицы / яйца. Обычно вам просто нужно сравнить / перейти к 16-битным значениям, и вы можете сделать это нормально, используя ноль или знак, расширенный до 32 или 64 бит.
Только RISC с шириной регистра 8 или 16 бит может устанавливать флаги из этого размера операнда. например AVR 8-битный RISC с 32 регистрами и 16-битными командными словами. Для реализации 16-битного int
требуется добавление / adc повышенной точности.
Это в основном исторический факт: x86 имеет 16-битный размер операнда для всего, потому что он эволюционировал от 16-битного только 286. Когда был разработан 80386, было важно, чтобы он мог работать только с 16-битным. код на полной скорости, и они предоставили способы постепенного добавления 32-битных операций к 16-битному коду. И использовал тот же механизм, чтобы разрешить 16-битные операции в 32-битном коде.
8-битные младшие / высокие регистры x86 (AX = AH: AL) снова частично связаны с тем, что 8086 был разработан как преемник 8080 и чтобы упростить перенос (и даже возможно автоматизировать) См. Почему первые четыре x86 GPR названы в таком неинтуитивном порядке? а>. (А также потому, что было просто полезно иметь одновременно восемь 1-байтовых регистров и четыре 2-байтовых регистра.)
Связанный: Какое целое число дополнения 2 операции можно использовать без обнуления старших битов во входных данных, если требуется только младшая часть результата? Для многих вычислений вам не нужно заново обнулять старшие биты после каждой операции, чтобы получить то же самое результат. Таким образом, отсутствие 8-битного / 16-битного размера операнда не является препятствием для эффективной реализации большей части кода, который логически ограничивает свои результаты 8 или 16 битами.
64-битные RISC-машины часто имеют 32-битную версию по крайней мере некоторых важных инструкций, таких как add
, поэтому вы можете получить результат add
с нулевым расширением бесплатно, не усекая его отдельно, например чтобы сделать код типа array[i++]
эффективным с uint32_t i
и 64-битными указателями. Но никогда не делайте частичные регистры размеров операндов меньше 32-битных на любом RISC, о котором я слышал.
DEC Alpha интересен тем, что это был новый дизайн, 64-битный с нуля, не 64-битное расширение существующего ISA, как MIPS64. Эта таблица мнемоник Alpha показывает, что добавление / sub / mul / div были доступны в 32- и 64-битных формах, но сдвиги и сравнения - нет. (Существуют также инструкции по манипулированию байтами, которые в основном представляют собой перетасовку / маску / вставку / извлечение SIMD внутри 64-битных целочисленных регистров, а также упакованное сравнение SIMD для эффективного строкового материала.)
Согласно этому официальному MIPS64 ISA doc (раздел 4.3 Регистры ЦП).
Процессор MIPS64 всегда дает 64-битный результат, даже для тех инструкций, которые архитектурно определены для работы с 32-битными. Такие инструкции обычно расширяют свой 32-битный результат до 64 битов. При этом 32-битные программы работают должным образом, хотя на самом деле регистры имеют ширину 64 бита, а не 32 бита.
(Вы используете специальные инструкции для полных 64-битных регистров, например DADDU
(двойное слово-добавление без знака) вместо ADDU
. Обратите внимание, что версии add
и dadd
, отличные от U, ловят перегрузку подписанного дополнения до 2 (с 32-битным или 64-битным размер операнда), поэтому вы должны использовать версию U для обертывания математических вычислений со знаком (ссылки на ISA-ссылки на mips.com). В любом случае, MIPS не имеет специального режима для 32-битной версии, но ОС должна заботиться о 32-битных программах по сравнению с 64-битными, потому что 32-битная версия предполагает все указатели находятся в младших 32 единицах виртуального адресного пространства.
На машине загрузки / сохранения RISC вы обычно просто используете нулевую (или расширяющую знак) загрузку байтов / полуслов. Когда вы закончите, вы должны использовать хранилище байтов / полуслов, чтобы получить усеченный результат. (Обычно используется для unsigned base2 или дополнения до 2 со знаком.) Именно так компилятор (или человек) реализует исходный код C, который использует short
или uint8_t
.
Наполовину связанный: правила продвижения целых чисел C автоматически продвигают все, что уже, чем int
, до int
, когда используется в качестве операнда для бинарного оператора, такого как +
, поэтому в большинстве случаев это хорошо соответствует этому способу вычислений. (т.е. unsigned result = (a+b) * c
в C не нужно усекать результат a+b
обратно до uint8_t
перед умножением, если a, b и c равны uint8_t
. Но очень плохо, что uint16_t
продвигается до int
со знаком, поэтому uint16_t a,b; unsigned c = a * b
рискует переполнить знак UB от продвижения до подписанного int
для умножения.) В любом случае правила продвижения C выглядят так, как будто они разработаны для машин без полной поддержки узких размеров операндов, потому что это характерно для большого количества оборудования.
Но вы спрашиваете о проверке переполнения / установке флага от узких сотрудников.
Не все RISC-машины даже имеют регистр FLAGS. ARM делает, но, например, MIPS и Alpha нет. ARM не устанавливает флаги для каждой инструкции: вы должны явно использовать форму установки флага инструкции.
ЦП без ФЛАГОВ обычно имеют несколько простых инструкций сравнения и перехода (часто с нулевым значением, например MIPS bltz
) и другие, которые сравнивают два ввода и записывают результат 0/1 в другой целочисленный регистр (например, MIPS SLTIU
- устанавливается на значение без знака, меньшее, чем немедленно). Вы можете использовать инструкции Set + a bne
с нулем для создания более сложных условий перехода.
Аппаратная и программная поддержка для эффективной проверки переполнения - это вообще проблема. Ставить jcc
после каждой инструкции x86 тоже довольно отстой.
Но отчасти потому, что большинство языков не упрощают написание кода, который требует проверки переполнения после каждой инструкции, архитекторы ЦП не предоставляют его аппаратно, особенно для узких размеров операндов.
MIPS интересен захватом add
для подписанного переполнения.
Способы его эффективной реализации могут включать наличие «липкого» флага, как фиксируются флаги исключения FPU: флаг Invalid остается установленным после деления на ноль (и получения NaN); другие инструкции FP не очищают его. Таким образом, вы можете проверить флаги исключения в конце серии вычислений или после цикла. Это делает его достаточно дешевым, чтобы его можно было использовать на практике, если бы для этого была программная среда.
С кодом FP обычно не нужно смотреть на флаги, потому что NaN сам по себе «липкий» или «заразительный». Большинство бинарных операторов производят NaN, если любой из входных данных равен NaN. Но представления целых чисел без знака и дополнения до 2 не имеют каких-либо резервных битовых шаблонов: все они представляют собой определенные числа. (Дополнение до 1 имеет отрицательный ноль ...)
Чтобы узнать больше о дизайне ISA, который сделает возможной проверку переполнения, посмотрите обсуждение предложения Агнера Фога о новой ISA, которая сочетает в себе лучшие функции x86 (плотность кода, много работы на инструкцию) и RISC (легко декодируется) для высокопроизводительной бумажной архитектуры. Некоторые интересные идеи SIMD, включая обеспечение прозрачности будущих расширений векторной ширины, чтобы вам не приходилось перекомпилировать, чтобы работать быстрее с более широкими векторами.
person
Peter Cordes
schedule
06.12.2017