Форум программистов, компьютерный форум, киберфорум
Низкоуровневое программирование
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.64/25: Рейтинг темы: голосов - 25, средняя оценка - 4.64
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472

Арифметические операции над небольшими целыми числами в процессоре SPARC

11.09.2016, 14:11. Показов 5368. Ответов 57
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте, форумчане!

Есть ли среди вас знатоки архитектуры SPARC? Если да, то просветите меня, пожалуйста, по такому вопросу.

Как в процессоре SPARC выполняются простые арифметические действия над целыми числами размера меньшего, чем стандартное машинное слово этого процессора (32 или 64 бит)? Допустим, нам надо выполнить какую-то простую операцию над байтом или 16-битным полусловом. Мне хотелось бы понять, каким набором машинных инструкций эта операция над байтом или полусловом длиной в 16 бит будет воплощена? Особенно мне интересно, как осуществляется контроль за переполнением разрядной сетки в этом случае.

В случае архитектуры x86 особых вопросов не возникает - как и в любой другой CISC-архитектуре код операции (поле КОП) в команде один и тот же независимо от размера данных, а собственно размер операндов задаётся специальными битами признаков внутри самой команды. Проблемы контроля переполнения данных в x86 (как, впрочем, и в других CISC-архитектурах) тоже нет. Фиксируется переполнение той разрядной сетки, которая указана в поле длины операндов. Т. е. если длина данных равняется одному байту - в регистре флагов фиксируется выход значения за границы одного байта, если операнды представляют собой 16-битные полуслова - фиксируется выход за пределы 16-битного представления целых чисел, если длина операндов равна 32-битному слову, результат должен помещаться в 32 бита (в противном случае будут установлены соответствующие флаги в регистре флагов). То же самое относится к 64-разрядным целым числам.

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

Был бы очень благодарен тем, кто мне ответит.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
11.09.2016, 14:11
Ответы с готовыми решениями:

Арифметические опреции над целыми числами
Привет ))) Народ, оч нужно решить задачку((( не как не могу сделать эту лабу... суть такова... Нужно на паскале или на ассемблере...

Арифметические операции над числами
Доброго вечера.Помогите-помогите,завтра нужно сдать,иначе не видать зачета( нужно написать программу,которая при запуске: 1.попросит...

Арифметические операции над числами
Пользователь вводит с клавиатуры два целочисленных значения: X и Y. Рассчитать сумму X+Y и вывести на экран. Результат суммы возвести в...

57
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
22.11.2016, 20:56
Студворк — интернет-сервис помощи студентам
Не, тут речь идёт не о том, что компиляторы делают более строгими, а о том, что исправляют ошибки. Т.е. по стандарту компилятор должен был ругаться, но он не ругался. В основном это из C++ лезет

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Уже вышла шестая версия gcc, а основная масса программистов из мира свободного софта сидит на последних версиях четвёртого
На пальцах это всё равно не объяснить. Но вот как только помудохаешься разок с переводом большого проекта с одной версии gcc на другую, то сразу поймёшь то, что словами описать сложно

Ну а коммерческие проекты практически никто не спешит переводить на более свежие версии. Особенно на самые свежие (которые всем миром ещё не успели отладить). Intel очень долго поддерживал (а может и до сих пор поддерживает) старую версию своего компилятора для (вроде бы) Oracle. Просто потому, что переход на другую версию выльется в очень большую трату денег

Добавлено через 1 минуту
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Можно ли в gcc сделать кросскомпиляцию?
Можно, но неэксперту это весьма геморройно. В своё время я пробовал, но очень быстро забил. Есть специальные утилиты для сборки кросскомпиляторов (они выкачивают нужный согласованный набор исходников и собирают). Только вот надо вспомнить, как оно правильно называется

Добавлено через 2 минуты
Согласованная сборка (обычно туда входят binutils, компилятор, glibc, хидера от linux'а) по умному называется toolchain. По запросу "как собрать кросс-toolchain" уже вываливаются полезные статьи. Но вот как называется утилита для автоматизации этого процесса - буду вспоминать

Добавлено через 3 минуты
Вроде бы вот это http://crosstool-ng.org/

Добавлено через 5 минут
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Можно ли это сделать с помощью gcc, идущем в обычном дистрибутиве Linux, не устанавливая никаких дополнительных пакетов, просто указав компилятору в командной строке какую-то опцию?
Так нельзя. Т.е. тебе нужно из исходников пересобрать весь набор необходимого софта, что чтобы он был настроен на генерацию кода под sparc, а не под intel

Добавлено через 59 секунд
И если вдруг чего-то соберёшь, то было бы полезно поделиться информацией с другими - кому-то это тоже пригодится, а кому-то будет просто интересно
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
22.11.2016, 21:41  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
На пальцах это всё равно не объяснить. Но вот как только помудохаешься разок с переводом большого проекта с одной версии gcc на другую, то сразу поймёшь то, что словами описать сложно
Я об этом как раз догадываюсь. Одно замысловатое объявление переменной, какого-нибудь указателя на функцию, которое не устраивает компилятор, и чтобы его исправить, придётся перелопатить строк 300 кода на Си. Разобраться и понять, как он весь работает, а потом еще и в него внести два-три мелких ничтожных исправления. Но таких, без которых нормально этот фрагмент уже работать не будет. Тяжёлая противная работа.


Можно, но неэксперту это весьма геморройно. В своё время я пробовал, но очень быстро забил. Есть специальные утилиты для сборки кросскомпиляторов (они выкачивают нужный согласованный набор исходников и собирают). Только вот надо вспомнить, как оно правильно называется.

Согласованная сборка (обычно туда входят binutils, компилятор, glibc, хидера от linux'а) по умному называется toolchain. По запросу "как собрать кросс-toolchain" уже вываливаются полезные статьи. Но вот как называется утилита для автоматизации этого процесса - буду вспоминать.

Вроде бы вот это http://crosstool-ng.org/.

Так нельзя. Т.е. тебе нужно из исходников пересобрать весь набор необходимого софта, чтобы он был настроен на генерацию кода под sparc, а не под intel.
Вот теперь понятно. Мне нужно собрать из исходников весь набор инструментов, всю их цепочку, приводящую к генерации кода. В нормальных дистрибутивах Линукс (в т. ч. в Дебиан) никаких кросскомпиляторов нет. Возможно, придётся заново собирать и фронтэнд, а весь бэкэнд — безоговорочно. Т. е. генератор кода на ассемблере, ассемблер gas, линкер и много ещё такого, о существовании чего я даже не догадываюсь, нужно собирать заново, ничего этого в готовом виде в дистрибутиве нет. При этом придётся связываться с инструментами вроде autoconf и кучей их параметров.

Есть 2 пути.
1. Делать это самому вручную. Нужно разобраться, из каких составляющих состоит gcc (их очень много), а потом каждую из них собирать со всеми нужными параметрами. Это огромная работа.

2. Воспользоваться готовой утилитой для сборки кросскомпилятора на основе gcc — crosstool-ng, в которой все эти шаги уже проработаны и запрограммированы (в виде там скриптов, сишных программ и т. п.). Но с её настройками тоже надо разбираться, и это не так просто.

Правильно я суть понимаю?


И если вдруг чего-то соберёшь, то было бы полезно поделиться информацией с другими - кому-то это тоже пригодится, а кому-то будет просто интересно
Посмотрим... Слишком большая и сложная работа... Но если что-то вдруг соберу, результатом работы здесь поделюсь.

Добавлено через 2 минуты
P.S.
Вот ещё что нашёл для Debian
http://wiki.debian.org/CrossToolchains

Вот ещё статья, не смотрел пока
http://vovanium.ru/cifra/kross-kompiljacija
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
22.11.2016, 21:47
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Правильно я суть понимаю?
В общем правильно

Добавлено через 4 минуты
Через crosstool-ng я когда-то давно собирал toolchain и вроде бы даже для sparc'а. Это гораздо менее геморройный процесс, чем собирать всё вручную. Но я затрудняюсь сказать, насколько там будет понятно человеку, который слабо себе представляет, что должно получиться на выходе (я-то понимал хорошо). Но по ощущениям собирать вручную ещё хуже

Добавлено через 58 секунд
Как вариант возможно пригодится, чтобы потренироваться на бабочках
Как установить gcc-4.6.3 параллельно с gcc-4.4?
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
24.11.2016, 22:52  [ТС]
Evg, в посте №17 Арифметические операции над небольшими целыми числами в процессоре SPARC ты мне привёл пример четырёх ассемблерных листингов для SPARC'а, получившихся в результате компиляции простенькой функции на C. Эти листинги получились при компиляции

1. компилятором gcc без ключей оптимизации
2. компилятором gcc с ключами оптимизации
3. компилятором cc фирмы Sun или Oracle без ключей оптимизации
4. компилятором cc фирмы Sun или Oracle с ключами оптимизации

Не мог бы ты мне для каждого из этих четырёх случаев выписать команду компиляции (в том виде, как она вызывается из командной строки), чтобы я при возможности всё это мог воспроизвести и в точности повторить у себя. Просто я не очень хорошо знаю ключи компилятора, знаком с небольшим их количеством и плохо представляю все их возможные режимы.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
25.11.2016, 16:05
Code
1
2
3
4
$ gcc t.c -S
$ gcc t.c -S -O3
$ cc t.c -S
$ cc t.c -S -xO4
Во всех случаях генерируется файл t.s
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
22.12.2016, 09:34  [ТС]
Evg, мы немножко ушли от основного вопроса и стали обсуждать, как собрать кросскомпилятор для SPARC, работающий на платформе x86. А мне бы хотелось ещё уточнить несколько вопросов по самой архитектуре SPARC. Для начала хочется поставить окончательную точку в вопросе, как процессор SPARC работает со словами разного размера при выполнении арифметических команд и команд побитовой логики.

Насколько я понял, SPARC v8, т. е. старая 32-разрядная версия процессора, способна работать только с 32-битными числами. Процессор, соответствующий стандарту SPARC v9, т. е. современный 64-разрядный SPARC, работает как с 32-битными, так и с 64-битными числами, а возможность работы с 32-разрядными словами в нём оставлена для совместимости со старой версией стандарта, т. е. для того чтобы программы, написанные для SPARC v8 спокойно запускались и на SPARC v9.

Таким образом, если мы выполняем на процессоре SPARC v9 какую-то арифметическую операцию над целыми числами (сложение, вычитание, умножение, деление), со знаком или без, и хотим проверить, не произошло ли переполнения, мы можем выполнить такую проверку аппаратными средствами процессора, для 32-ух и 64-разрядных целых. Регистр флагов этого процессора имеет флаги переполнения и переноса — OF и CF — как для 32-ух, так и для 64-разрядного случая, всего 4 флага, которые после любой такой операции каждый устанавливается в своё состояние. Дальше, после того как арифметическая операция выполнена, можно проверить состояние любого из этих четырёх флагов соответствующей командой условного перехода (jxxx) и выполнить необходимое ветвление.

Но вот ни для байта, ни для 16-разрядного целого устроить проверку таким способом нельзя, поскольку ни для байта, ни для слова в 16 бит у процессора SPARC нет ни своих особых арифметических команд, ни своих флагов переполнения. Т. е. для байта и двухбайтового слова нет никакой специальной поддержки на аппаратном уровне процессора ни собственно арифметики, ни проверки переполнения. Поэтому такую проверку, если она нужна, можно организовать программно, проверяя значения бит, находящихся левее разрядов, занимаемых нашим байтом или 16-битным словом.

Правильно я всё понимаю в случае SPARC'а, или в моих словах есть какие-то ошибки и неточности?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
22.12.2016, 15:13
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Процессор, соответствующий стандарту SPARC v9, т. е. современный 64-разрядный SPARC, работает как с 32-битными, так и с 64-битными числами, а возможность работы с 32-разрядными словами в нём оставлена для совместимости со старой версией стандарта, т. е. для того чтобы программы, написанные для SPARC v8 спокойно запускались и на SPARC v9
Не совсем так. Допустим, у тебя есть бинарный код под v8 с операцией "add %i1, %i2, %i3". Он берёт два 32-битных регистра и складывает их. Теперь если взять этот же код и запустить на машине v9, то в реальности он будет складывать два 64-битных регистра. Т.е. как бы выполнять другое действие. Но операция над регистрами всегда подразумевает некое промежуточное состояние вычислений, конечных же результат вычислений всегда должен заканчиваться операцией обращения в память (в файл, в порт и т.п.). А операции обращения в память уже имеют размер - "stb, sth, stw, std". Т.е. код "add %i1, %i2, %i3", который исполнялся на v8, в конечном итоге должен закончиться операцией "stw [addr], %i3", которая записывает 4 байта в память. И именно за счёт этого последовательность операций "add %i1, %i2, %i3" и "stw [addr], %i3" отработает ровно таким же образом на машине v9. Т.е. в 4-байтный кусок памяти будет записано нужное значение, но при этом в промежуточном состоянии мы будем иметь 64-битное значение регистра, а не 32-битное.

За счёт этого в процессоре v9 практически не понадобилось что-то делать для совместимости. В отличие от intel'ов в процессоре даже нет рубильника "режим v8 - режим v9". Все бинарные коды под v8, в реальности работают в режиме v9 и есть маленькая настроечка, что при обращении в память нужно игнорировать старшую часть адреса

Но кое где обеспечивать совместимость всё-таки пришлось. Например, в флаговых регистрах. Для 64-битной машины флаги нужны только для 64-битных операций, но пришлось им добавлять флаги на границе 32-го бита (с теми же самыми кодировками, что и на v8), чтобы бинарные коды от v8 запустились на машине v9. Ну и таких мелочей есть ещё всякие разные, надо сидеть и вспоминать, где такое понадобилось
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
22.12.2016, 22:00  [ТС]
Вот! Я на самом деле всё так и представлял себе. Что при работе с 32-разрядными числами работа на самом деле ведётся с 64-разрядными числами, хранящимися в 64-разрядных регистрах, поскольку отдельного доступа к младшей 32-битной части регистров в арифметических операциях у процессора SPARC v9 нет. И АЛУ SPARC v9 работает только с 64 линиями, работать только с 32 битами, отключив свою старшую половину, оно не умеет. Доступ к байту, полуслову и 32-битному слову регистра возможен, но только в операциях загрузки (load) и сохранения (store). Соответствующие команды для операции сохранения называются stb, sth, stw и std (последняя для полной загрузки всех 64 бит в регистр). Для операции сохранения наверное они будут называться ldb, ldh, ldw, ldd (или неправильно?). Все же арифметические операции и операции побитовой логики ведутся над 64-разрядными регистрами над всеми 64 разрядами. Просто старшие ненужные биты в дальнейшем не используются (они по сути дела выбрасываются) и работа процессора над ними проходит в общем-то вхолостую.

У меня вчера был как раз второй вопрос. Правда ли, что у процессора SPARC v9 отсутствуют отдельные команды для арифметических операций (и операций побитовой логики) для случая 32 и 64 бит? Что и в том, и в другом случае используются одни и те же команды. Для SPARC v9 все эти операции 64-битны, но чтобы сохранить совместимость со SPARC v8 оставили старую пару флагов переполнения (cf и оf) на границе 32 бит, и в итоге у SPARC v9 оказалось 4 флага переполнения. В результате каждая арифметическая команда производится всегда над 64-битными регистрами и по итогу выполнения команды устанавливаются все 4 флага, не важно, подразумевали мы 64-битное или 32-битное число. А дальше в зависимости от нашего разумения мы уже используем флаг переполнения по 32-ум или 64-ём битам в команде условного перехода (смотря какое число мы подразумевали, 32-ух или 64-битное), если нам нужна такая проверка. Но судя по твоему подробному ответу, так оно и есть.

И ещё один вопрос. Как в SPARC v9 обозначаются все эти 4 флага переполнения для случая 32-ух и 64-ёх бит? В intel их всего 2 и они называются Carriage Flag — CF (для беззнакового случая) и Overflow flag — OF (для операций над операндами со знаком). Используются одни и те же флаги для фиксации переполнения в операциях над байтами, 16-разрядными слова, 32-ух и 64-разрядными словами. Размер операнда определяется в самой арифметической команде (или операции побитовой логики). В SPARC v9 флагов не 2, а 4. Как они называются и обозначаются?

Проверить состояние флагов в Intel можно командами переходов
Code
1
2
3
4
jc  (jump on carry)
jnc (jump on no carry)
jo  (jump on overflow)
jno (jump on no overflow)
А как обозначаются и выглядят подобные команды у SPARC?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
22.12.2016, 23:25
Цитата Сообщение от JohnyWalker Посмотреть сообщение
std (последняя для полной загрузки всех 64 бит в регистр)
Не совсем так. На v8 операция std записывала 64 бита, т.е. два соседних (32-битных) регистра. Операция std на v9 должна работать аналогично, она записывает в память две младшие 32-битные половинки двух соседних 64-битных регистров. Операция ldd работает аналогично. А для работы с 64-битными регистрами добавили операции stx/ldx. Вообще во всех описания формат "d" (double) у них означает именно два 32-битных куска от двух соседних регистров, а формат "x" (наверное extended) означает 64-битный регистр

Цитата Сообщение от JohnyWalker Посмотреть сообщение
У меня вчера был как раз второй вопрос. Правда ли, что у процессора SPARC v9 отсутствуют отдельные команды для арифметических операций (и операций побитовой логики) для случая 32 и 64 бит? Что и в том, и в другом случае используются одни и те же команды
Да

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Для SPARC v9 все эти операции 64-битны, но чтобы сохранить совместимость со SPARC v8 оставили старую пару флагов переполнения (cf и оf) на границе 32 бит, и в итоге у SPARC v9 оказалось 4 флага переполнения
Наверное более правильно говорить о наборе флагов. Вообще в одном наборе для целочисленных операций должно быть как минимум 4 флага, потому что результатов сравнения 10 (==, !=, знаковые <, <=, >, >= и беззнаковые <, <=, >, >=). Т.е. на v8 был один набор флагов (32), на v9 их два (32 и 64). Это для целочисленных. Для вещественных там немного хитрее, на память я тонкости не припомню, но если я ничего не путаю, то вещественная арифметика там не менялась при переходе от v8 на v9, просто вместо одного набора флагов их стало четыре, а при сравнении и условных переходах можно явно указывать который из четырёх наборов надо использовать

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Как в SPARC v9 обозначаются все эти 4 флага переполнения для случая 32-ух и 64-ёх бит?
Я на память не помню, нужно просто запустить компилятор да посмотреть. Или ты кросс-компилятор так и не собрал?

Цитата Сообщение от JohnyWalker Посмотреть сообщение
А как обозначаются и выглядят подобные команды у SPARC?
На память не помню
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
23.12.2016, 03:44  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Не совсем так. На v8 операция std записывала 64 бита, т.е. два соседних (32-битных) регистра. Операция std на v9 должна работать аналогично, она записывает в память две младшие 32-битные половинки двух соседних 64-битных регистров. Операция ldd работает аналогично. А для работы с 64-битными регистрами добавили операции stx/ldx. Вообще во всех описания формат "d" (double) у них означает именно два 32-битных куска от двух соседних регистров, а формат "x" (наверное extended) означает 64-битный регистр
Даже так. Понятно, спасибо.

Я на память не помню, нужно просто запустить компилятор да посмотреть. Или ты кросс-компилятор так и не собрал?
Не собрал пока. Так руки и не дошли. Соберу или на днях, или после Нового года.

Добавлено через 1 час 32 минуты
Цитата Сообщение от Evg Посмотреть сообщение
Наверное более правильно говорить о наборе флагов. Вообще в одном наборе для целочисленных операций должно быть как минимум 4 флага, потому что результатов сравнения 10 (==, !=, знаковые <, <=, >, >= и беззнаковые <, <=, >, >=). Т.е. на v8 был один набор флагов (32), на v9 их два (32 и 64). Это для целочисленных. Для вещественных там немного хитрее, на память я тонкости не припомню, но если я ничего не путаю, то вещественная арифметика там не менялась при переходе от v8 на v9, просто вместо одного набора флагов их стало четыре, а при сравнении и условных переходах можно явно указывать который из четырёх наборов надо использовать
Ну, в общем-то логично. Т. е. для обеспечения полной совместимости SPARC v8 со SPARC v9, чтобы 32-разрядная программа, написанная для v8, гарантированно работала на v9, нужно продублировать на 32-разрядный случай все флаги результата арифметических и логических операций, а не только флаги of и cf. Я об этом как-то не подумал. Спасибо за ценное замечание

Добавлено через 1 час 9 минут
Evg, у меня ещё пара вопросов к тебе по SPARC'у.

Первый вопрос. Допускает ли этот процессор самомодификацию кода и нормальную работу такого самомодифицирующегося кода?

Я знаю, что многими "светилами" компьютерных наук, сторонниками структурного, объектно-ориентированного, правильного программирования, этот подход предан анафеме. Но я не принадлежу к числу этих "светил" и ничего плохого в этом подходе не вижу. В самом деле, ничего ужасного в самомодифицирующейся программе нет. Подход вполне логичен, разумен и не противоречит здравому смыслу и принципам вычислительной техники. Я вижу следующие сложности на этом пути. Этот подход сложноват в реализации, т. к. надо отслеживать адреса всех команд в памяти, которые мы хотим модифицировать. Нужно знать в деталях форматы машинных команд и отнюдь не на уровне ассемблера, а в двоичных кодах, в случае CISC-процессора команды могут быть достаточно сложны по устройству, т. е. надо помнить довольно большой объём информации. Подход плохо совместим с идеей языка высокого уровня, т. е. чтобы его реализовать всё с самого начала нужно делать на ассемблере. И подход очень плохо сочетается с конвейерным принципом организации процессора. Если некоторая команда оказалась модифицирована тогда, когда она уже была считана в буфер команд или, тем более, уже попала на конвейер и находилась на какой-то из его ступеней, все ступени конвейера, начиная с этой команды и позже, надо сбрасывать, очищать и загружать его по-новой. Т. е. если у нас процессор конвейерный и каждая пятая или десятая команда в процессе выполнения, допустим, меняет содержимое близлежащей команды, то все выгоды конвейерной организации процессора сводятся на нет — он работает как процессор без конвейерной структуры. Боюсь, что в случае суперскалярника всё окажется ещё хуже. Я даже не представляю себе, как суперскалярный процессор со всеми его ухищрениями — переименованием регистров в командах, перестановкой команд в потоке, разделением одного последовательного потока на несколько небольших цепочек, логически независимых, а потому способных исполниться параллельно, наличием нескольких конвейеров со своим АЛУ у каждого и блоком-диспетчером, который раскидывает команды по этим конвейерам, — обрабатывает такую ситуацию. По-моему это очень непросто. Хотя ведь Интел — суперскалярник со всеми этими штуками, а он вполне допускает самомодифицирующийся код и как-то такое событие обрабатывает, так что ошибок в вычислениях не происходит.

В общем, я ничего не имею против программ с самомодифицирующимся кодом ни с какой стороны, но понимаю, что они плохо сочетаются с устройством любого современного процессора и плохо влияют на его быстродействие. Но это всё философия, а вопрос в другом.

Допускают ли реально существующие экземпляры SPARC и сама эта архитектура в своём стандарте (т. е. если говорить даже не о реально существующих процессорах, а о самом стандарте, как формальном своде правил), программы, изменяющие свой собственный код? Или же в реально существующих процессорах и в стандартах SPARC (v7, v8, v9 и т. д.) это начисто запрещено? Если программа, запущенная на процессоре SPARC, всё-таки попробует изменить свой код, к чему это приведёт в действительности?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
23.12.2016, 13:29
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Первый вопрос. Допускает ли этот процессор самомодификацию кода и нормальную работу такого самомодифицирующегося кода?
Это зависит не от процессора, а от операционной системы. Как там устроена политика безопасности. Процессору-то всё равно что исполнять

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Я знаю, что многими "светилами" компьютерных наук, сторонниками структурного, объектно-ориентированного, правильного программирования, этот подход предан анафеме
Это источник уязвимостей. Т.е. соотношение пользы и возможного вреда от этого подхода сильно перевешивает в пользу вреда

Цитата Сообщение от JohnyWalker Посмотреть сообщение
И подход очень плохо сочетается с конвейерным принципом организации процессора. Если некоторая команда оказалась модифицирована тогда, когда она уже была считана в буфер команд или, тем более, уже попала на конвейер и находилась на какой-то из его ступеней, все ступени конвейера, начиная с этой команды и позже, надо сбрасывать, очищать и загружать его по-новой
Просто перед и после модификации кода нужно прочищать все кэши/буфера специальными командами

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Т. е. если у нас процессор конвейерный и каждая пятая или десятая команда в процессе выполнения, допустим, меняет содержимое близлежащей команды, то все выгоды конвейерной организации процессора сводятся на нет — он работает как процессор без конвейерной структуры
Если в таком варианте, то тут я с хожу даже и не скажу, как быть. В процессоре раздельный кэш команд и кэш данных. Насколько я знаю (но могу и ошибаться) операции записи в память проходят только через кэш данных и при такой организации (т.е. без принудительной работы с кэшем и буферами)

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Хотя ведь Интел — суперскалярник со всеми этими штуками, а он вполне допускает самомодифицирующийся код и как-то такое событие обрабатывает, так что ошибок в вычислениях не происходит
Насколько я понимаю, всё это находится в ведении подсистемы памяти, а не процессора. Почему интел пошёл по пути, когда вся синхронизация кэшей делается автоматически - хз. Возможно, что из-за проблем с совместимостью со старым софтом (во времена перехода от 486 на 586) дешевле было родить мегасложную подсистему работы с памятью, чем терять долю на рынке

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Допускают ли реально существующие экземпляры SPARC и сама эта архитектура в своём стандарте (т. е. если говорить даже не о реально существующих процессорах, а о самом стандарте, как формальном своде правил), программы, изменяющие свой собственный код? Или же в реально существующих процессорах и в стандартах SPARC (v7, v8, v9 и т. д.) это начисто запрещено? Если программа, запущенная на процессоре SPARC, всё-таки попробует изменить свой код, к чему это приведёт в действительности?
Как я уже говорил выше, самомодицировать код можно, просто надо это делать правильно. Более того, методы, на которых работает динамическая линковка, в момент своего зарождения подразумевали именно модификацию кода, и только в последнее время все (ну или основная масса) пытаются от этого избавляться

Добавлено через 4 минуты
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Как в SPARC v9 обозначаются все эти 4 флага переполнения для случая 32-ух и 64-ёх бит?
Вчера вечером я уже плохо соображал. Сейчас понял, что конкретно ты имел в виду вопросом. Ответ: никак. Там напрямую никто с флагами не работает. Условный переход, например, выглядит как операция cmp + операция условного перехода "перейти если равно", "перейти если не равно и т.п."

Ну а на v9 у этих операций перехода появляется дополнительный битик, говорящий о том, из какого набора флагов брать условие. По умолчанию (т.е. когда код совпадает с кодировкой v8) , условие будет браться из 32-битных флагов, с взведённой галочкой - из 64-битных

Добавлено через 6 минут
Очевидно, что "переход если равно" с технической точки зрения означает "переход, если такие-то флаги соответствуют таким-то значениям"
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
23.12.2016, 15:00  [ТС]
Ну, в общем-то смысл я понял. Программы с самоизменяющимся кодом SPARC допускает, перезаписывать код команд по ходу выполнения можно, но после каждой такой модификации команды необходимо выполнить дополнительную инструкцию — обновить кэш команд, обновить буфер команд и сбросить конвейер. Это всё делается одной единственной инструкцией, и такая инструкция в системе команд SPARC существует, но её нужно указывать ЯВНЫМ образом. Если этого не сделать, может исполниться вместо изменённой-модифицированной старая команда. В Интеле этой проблемы не возникает, поскольку и кэш команд, и кэш данных синхронизируются автоматически, распознавание такой ситуации и сброс буфера команд вместе с конвейерами в Интеле тоже происходит автоматически. Думаю, что подход, избранный Интелом, здесь более правильный и он гораздо лучше. Ручная синхронизация кэшей, буферов, конвейеров — это перекладывание задачи, которую следовало решать схемотехникам, создававшим процессор, на головы программистов, которые этим процессором будут пользоваться. Это очень здорово облегчает внутреннее устройство процессора, но думаю, это всё же неправильно. Не должны программисты этим всем заниматься и помнить об этом, это всё должно решаться на уровне схем и аппаратуры процессора.

Потом мне кажется, что наибольшая проблема возникает, когда одна команда меняет содержимое другой команды, лежащей рядышком с первой командой в потоке исполнения, допустим, через 3 - 5 инструкций. Просто самое веселье наступает тогда, когда некоторая команда уже загружена в конвейер и наполовину исполнена (находится на какой-то из его ступеней), а в это время её образ в оперативной памяти (ну, или в кэше, что в общем-то сути не меняет), вдруг оказывается изменённым и перезаписанным другой командой. Понятно, что та команда, которая находится в конвейере и "шагает" по нему, уже никак не связана со своим образом в ОЗУ/кэше. И тем не менее, логически (с точки зрения логики программиста) она должна быть изменена на новую. Ясно, что изменить её в конвейере по ходу выполнения уже никак не получится. Остаётся только одно — сбросить все стадии конвейера от самой первой до той, на которой находится эта команда, и загрузить эту команду на самую первую ступень конвейера (дешифрацию). После такого сброса нескольких ступеней и повторной перезагрузки в конвейере образуется несколько пропущенных пустых мест (слотов), которые, пока все не исчезнут на последней стадии конвейера, будут перемещаться по нему. То есть несколько тактов будут пропущены процессором и не будут использоваться. Но чтобы такой сброс нескольких ступеней конвейера и перезагрузка перезаписанной команды на первую ступень конвейера произошла, процессор должен ещё опознать такую ситуацию и автоматически все эти действия выполнить. В случае же если процессор опознавать её не умеет (а такое имеет место в случае SPARC, я так понял с твоих слов), это действие должно быть выполнено программистом "вручную". В потоке команд после модификации одной командой другой команды должна встретиться инструкция, которая заставит процессор всё это сделать явным образом. Т. е. отслеживание такой ситуации перекладывается с автоматики процессора на плечи программиста.

Ну, в общем-то с самомодификацией команд в SPARC всё понятно. Она возможна, она разрешена данной архитектурой, но не полностью автоматизирована. Было бы, конечно, интересно увидеть пример такого кода на ассемблере SPARC, но не буду тебя просить его найти. Отыскать такой пример в сети будет очень непросто. Так что не нужно.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
23.12.2016, 15:47
Цитата Сообщение от JohnyWalker Посмотреть сообщение
обновить кэш команд, обновить буфер команд и сбросить конвейер
Я это всё условно назвал. В точности я не могу сказать, что конкретно нужно flush'ить, тут уже тонкости работы надо знать

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Это всё делается одной единственной инструкцией, и такая инструкция в системе команд SPARC существует, но её нужно указывать ЯВНЫМ образом
Я тут тоже не знаю на 100%, одна-единственная инструкция, или их несколько

Цитата Сообщение от JohnyWalker Посмотреть сообщение
В Интеле этой проблемы не возникает, поскольку и кэш команд, и кэш данных синхронизируются автоматически, распознавание такой ситуации и сброс буфера команд вместе с конвейерами в Интеле тоже происходит автоматически
Это тоже не на 100%. Вирусы-то живут по такому принципу, но может какие-то дополнительные телодвижения для этого делаются. Интеловский процессор я вообще не знаю, просто приходилось читать про некоторые особенности, из которых выстраивается целиковая картина, но не на 100%

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Ручная синхронизация кэшей, буферов, конвейеров — это перекладывание задачи, которую следовало решать схемотехникам, создававшим процессор, на головы программистов, которые этим процессором будут пользоваться
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Не должны программисты этим всем заниматься и помнить об этом, это всё должно решаться на уровне схем и аппаратуры процессора
Программист не должен писать самомодифицирующийся код. Этим занимаются хакеры, так что разработчикам процессора совершенно точно не нужно что-то делать для облегчения жизни хакерам

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Потом мне кажется, что наибольшая проблема возникает, когда одна команда меняет содержимое другой команды, лежащей рядышком с первой командой в потоке исполнения, допустим, через 3 - 5 инструкций. Просто самое веселье наступает тогда, когда некоторая команда уже загружена в конвейер и наполовину исполнена (находится на какой-то из его ступеней), а в это время её образ в оперативной памяти (ну, или в кэше, что в общем-то сути не меняет), вдруг оказывается изменённым и перезаписанным другой командой. Понятно, что та команда, которая находится в конвейере и "шагает" по нему, уже никак не связана со своим образом в ОЗУ/кэше. И тем не менее, логически (с точки зрения логики программиста) она должна быть изменена на новую. Ясно, что изменить её в конвейере по ходу выполнения уже никак не получится
Здесь у интела скорее всего то же самое. Т.е. если команда уже встала на конвейер, то её там никто не сможет модифицировать. Если идёт модификация команды, которая ещё не попала в конвейер, но уже есть в кэше, то подсистема памяти с таким справится. А когда уже запущен конвейер - то попросту некому что-то там изменить

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Остаётся только одно — сбросить все стадии конвейера от самой первой до той, на которой находится эта команда, и загрузить эту команду на самую первую ступень конвейера (дешифрацию). После такого сброса нескольких ступеней и повторной перезагрузки в конвейере образуется несколько пропущенных пустых мест (слотов), которые, пока все не исчезнут на последней стадии конвейера, будут перемещаться по нему
Насчёт этого я не уверен, что программисту такая возможность доступна. Хотя в интеловском процессоре механизм предсказаний переходов работает схожим образом: в случае неугадывания направления перехода конвейер нужно откатывать назад. Но давать такой инструмент в руки пользователя было бы неразумным

Добавлено через 29 минут
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Ну, в общем-то с самомодификацией команд в SPARC всё понятно. Она возможна, она разрешена данной архитектурой, но не полностью автоматизирована. Было бы, конечно, интересно увидеть пример такого кода на ассемблере SPARC, но не буду тебя просить его найти. Отыскать такой пример в сети будет очень непросто. Так что не нужно
Скажем так, можно по простому увидеть вариант, когда код просто создаётся в runtime.

В gcc есть расширение "вложенные функции (nested functions)". При вызове такой функции имеется скрытый параметр, не предусмотренный программными соглашениями: дополнительно передаётся указатель на стек охватывающей функции. Теперь если мы взяли указатель на вложенную функцию и отдали её во внешний мир, то там уже не знают о том, что эта функция вложенная и у ней есть скрытый параметр. В этом случае gcc создаёт кусочек кода в стеке, указатель на которой и отдаёт во внешний мир вод видом указателя на функцию. А этот кусочек кода в стеке формирует скрытый параметр и передаёт управление на вложенную функцию. Собственно, код для этого можно и посмотреть из-под gcc:

C
int g;
void foo (void(*)(void));
void bar (void)
{
  int x;
  void qux (void)
  {
    g = x;
  }
  x = 100;
  foo (qux);
}
-----------------------------

Такой код будет на intel'е. Тут хорошо видно, что после создания кода в runtime, не производится никаких дополнительных телодвижений во время исполнения. В самой последней строке делается специальная пометка, что код содержит runtime создание кода, это нужно, чтобы операционная система разрешила исполнять код из страниц, соответствующих стеку

Code
        .text
        .type   qux.1374, @function
qux.1374:
.LFB1:
        .cfi_startproc
        movl    (%ecx), %eax
        movl    %eax, g
        ret
        .cfi_endproc
.LFE1:
        .size   qux.1374, .-qux.1374
        .globl  bar
        .type   bar, @function
bar:
.LFB0:
        .cfi_startproc
        subl    $44, %esp
        .cfi_def_cfa_offset 48
        leal    16(%esp), %edx
        leal    20(%esp), %eax
        movb    $-71, 20(%esp)
        movl    %edx, 21(%esp)
        movb    $-23, 25(%esp)
        movl    $qux.1374+2, %edx
        leal    32(%esp), %ecx
        subl    %ecx, %edx
        movl    %edx, 26(%esp)
        movl    $100, 16(%esp)
        movl    %eax, (%esp)
        call    foo
        addl    $44, %esp
        .cfi_def_cfa_offset 4
        ret
        .cfi_endproc
.LFE0:
        .size   bar, .-bar
        .comm   g,4,4
        .ident  "GCC: (GNU) 4.8.0"
        .section        .note.GNU-stack,"x",@progbits
-----------------------------

А вот такой код на sparc'е. Последняя строка аналогичная, но тут появились инструкции iflush для прочистки кэша команд

Code
        .section        ".text"
        .align 4
        .type   qux.1246, #function
        .proc   020
qux.1246:
        ld      [%g2], %g2
        sethi   %hi(g), %g1
        jmp     %o7+8
         st     %g2, [%g1+%lo(g)]
        .size   qux.1246, .-qux.1246
        .align 4
        .global bar
        .type   bar, #function
        .proc   020
bar:
        save    %sp, -128, %sp
        add     %fp, -32, %g2
        add     %fp, -9, %g1
        and     %g1, -16, %g1
        sethi   %hi(qux.1246), %g3
        or      %g3, %lo(qux.1246), %g3
        srl     %g3, 10, %o5
        sethi   %hi(50331648), %g4
        or      %o5, %g4, %g4
        st      %g4, [%g1]
        srl     %g2, 10, %o5
        sethi   %hi(83886080), %g4
        or      %o5, %g4, %g4
        st      %g4, [%g1+4]
        and     %g3, 1023, %g3
        sethi   %hi(-2118098944), %g4
        or      %g3, %g4, %g3
        st      %g3, [%g1+8]
        and     %g2, 1023, %g2
        sethi   %hi(-2079285248), %g3
        or      %g2, %g3, %g2
        st      %g2, [%g1+12]
        iflush  %g1
        iflush  %g1+8
        mov     100, %g1
        st      %g1, [%fp-32]
        add     %fp, -9, %o0
        call    foo, 0
         and    %o0, -16, %o0
        jmp     %i7+8
         restore
        .size   bar, .-bar
        .common g,4,4
        .ident  "GCC: (Gentoo 4.5.4 p1.1, pie-0.4.7) 4.5.4"
        .section        .note.GNU-stack,"x",@progbits
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
23.12.2016, 16:18  [ТС]
Нет, я сторонник того, чтобы возможность писать самомодифицирующийся код у программиста была. А хакер он или просто программёр, увлекающийся нестандартными трюками, уже вопрос второстепенный. Если возможность создавать такой код следует из логики программирования и общих соображений, то она должна быть реализована и на практике. И аппаратура процессора не должна как-то препятствовать этому или запрещать такой подход. Тут меня не переубедить Но нет смысла скатываться в идеологические споры, всё равно они ни к чему не ведут. Тут меня интересует другое.

А что, правда, что и в Интеле, и в SPARC'е, если команда уже попала на конвейер, будучи модифицированной после этого в памяти, она уже никак не изменится в конвейере? И по итогу будет выполнен именно старый вариант команды, тот, который находился в памяти по этому адресу ещё до модификации? Я-то считал, что по крайней мере Интел просчитает и распознает такую ситуацию и сбросит свой конвейер, загрузив его по-новой (ну, точнее говоря, весь свой буфер и все свои конвейеры, т. к. он суперсалярник, но суть не в этом). И после такого сброса и перезагрузки в конвейере окажется уже модифицированная команда, которая в конечном счёте и исполнится. Или я не прав?
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
23.12.2016, 16:56
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Нет, я сторонник того, чтобы возможность писать самомодифицирующийся код у программиста была. А хакер он или просто программёр, увлекающийся нестандартными трюками, уже вопрос второстепенный. Если возможность создавать такой код следует из логики программирования и общих соображений, то она должна быть реализована и на практике.
Вопрос ведь не в том, что лично кажется тебе. Компания, для которой процессор является инструментом для зарабатывания денег, а не сферическим конём в вакууме, будет с чистой совестью плевать на лично твои хотелки, поскольку они из разряда теоретических и никак им не помогут заработать больше денег или захватить бОльшую долю рынка

Цитата Сообщение от JohnyWalker Посмотреть сообщение
А что, правда, что и в Интеле, и в SPARC'е, если команда уже попала на конвейер, будучи модифицированной после этого в памяти, она уже никак не изменится в конвейере?
Я не могу знать на 100%, но более, чем уверен, что так оно и есть. Логическая цепочка тут несложная.

Такая ересь могла бы понадобиться только из соображений совместимости, в случае нормального развития никто в процессор столько лишних аппаратуры вставлять не будет, т.к. на практике кроме извращенцев оно никому не нужно. Чтобы такую хотелку реализовать, нужно уметь откатывать конвейер, как это делается в механизме предсказания переходов. Но предсказания переходов появились только в 586-м процессоре (в первом суперскаляре). До появления суперскаляра такого механизма скорее всего не было. Конвейер был уже в 086-м, в те времена откат конвейера был бы слишком жирной операцией для процессора, да и сам конвейер был свежим изобретением. Откат конвейера - это по сути борьба самим с собой, и вряд ли первый конвейер создавали так, чтобы с самим собой и бороться. Из этого можно сделать вывод, что в самых первых процессорах в общем-то невозможно было написать программу, где команда N1 модифицирует команду N2 (в предположении, что N1 и N2 исполняются друг за другом подряд). Поэтому в этом месте вопроса совместимости вроде бы как не должно возникать
0
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
23.12.2016, 18:00  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Вопрос ведь не в том, что лично кажется тебе. Компания, для которой процессор является инструментом для зарабатывания денег, а не сферическим конём в вакууме, будет с чистой совестью плевать на лично твои хотелки, поскольку они из разряда теоретических и никак им не помогут заработать больше денег или захватить бОльшую долю рынка
С этим я полностью согласен. Крупной компании, как и государству, на мои желания глубоко наплевать. В качестве примера я могу тебе привести Билла Гейтса. Для него покупатель его программ — Windows, MS Office и т. п. — это просто жирный телёнок, с которого нужно стянуть побольше денег. Но идти у них у всех на поводу и соглашаться со всеми их решениями я не считаю правильным и нужным.


Такая ересь могла бы понадобиться только из соображений совместимости, в случае нормального развития никто в процессор столько лишних аппаратуры вставлять не будет, т.к. на практике кроме извращенцев оно никому не нужно.
Я не согласен с тобой. Это не извращение, это нормальная возможность, хотя в современной вычислительной технике мало востребованная на практике. Кстати, в ранних компьютерах, еще в 50-ые и возможно даже в начале 60-ых годов она активно использовалась программистами. Компьютеры 1950-ых, похоже, не имели режима косвенной адресации, и для её фактической реализации приходилось постоянно модифицировать прямой операнд. Я не считаю программистов тех лет идиотами и извращенцами. С появлением косвенной адресации острая необходимость в этом отпала, хотя компьютеры вроде как ещё долгое время поддерживали эту возможность. В принципе востребованность в таком приёме может быть и сейчас. Очень редко когда, но она может иногда пригодиться. Смысла запрещать всё это я не вижу никакого.

Чтобы такую хотелку реализовать, нужно уметь откатывать конвейер, как это делается в механизме предсказания переходов.
Видишь, принцип там один и тот же. Значит, реализовать эту "хотелку" сейчас совсем несложно. Предсказатель переходов есть в любом нормальном современном процессоре.

Конвейер был уже в 086-м, в те времена откат конвейера был бы слишком жирной операцией для процессора, да и сам конвейер был свежим изобретением.
Ой ли. Сомневаюсь, что там при 20.000 транзисторов был полноценный настоящий конвейер. Да и затрачивал 8086 по нескольку тактов на любую, даже несложную, арифметическую операцию. Для конвейерного процессора это должно быть 1 - 2 такта. А на счёт того, что конвейер тогда был свежим изобретением... Intel 8086 был создан в середине - конце 1970-ых годов. Конвейерный принцип организации процессора был известен уже в середине 60-ых и активно использовался в лучших наиболее передовых разработках тех лет как в США, так и в Советском Союзе. Был он уже реализован в полной мере в БЭСМ-6, был он безоговорочно и в IBM System/360 (и как следствие в ЭВМ ЕС). Как там с возможностью самомодификации друг другом подряд стоящих инструкций — не знаю. Т. е. конвейер был известен к моменту создания Intel 8086 уже более десяти лет. Ну а уж считать ли его к тому времени свежим изобретением — судить тебе. Я бы так не сказал.

Откат конвейера - это по сути борьба самим с собой, и вряд ли первый конвейер создавали так, чтобы с самим собой и бороться. Из этого можно сделать вывод, что в самых первых процессорах в общем-то невозможно было написать программу, где команда N1 модифицирует команду N2 (в предположении, что N1 и N2 исполняются друг за другом подряд). Поэтому в этом месте вопроса совместимости вроде бы как не должно возникать
Это можно проверить лишь экспериментом. Мне кажется посылка о существовании в Intel 8086 полноценного конвейера ложной, а именно на основании неё ты строишь дальнейшую цепь рассуждений. Хотя надо проверять на примере реальных программ.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
23.12.2016, 21:47
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Компьютеры 1950-ых, похоже, не имели режима косвенной адресации, и для её фактической реализации приходилось постоянно модифицировать прямой операнд
Хороша ложка к обеду. В компьютерах 50-х годов количество памяти исчислялось единицами килобайт, а частоты килогерцами. Когда современные процессоры летают со скоростями, в сотни тысяч раз превышающими скорости процессоров 50-х годов, то все как бы с этим согласны. А то, что некоторыми из факторов такой скорости стали технологии, противоречащие стилю программирования 50-х годов, тут почему-то возникает недовольство. В 50-х годах не было ни кэша, ни конвейера, ни многопроцессорности, программисты тех времён могли себе позволить писать подобные коды

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Смысла запрещать всё это я не вижу никакого
А никто и не запрещает. Если хочешь самомодифицирующийся код - пиши его, но по правилам. Ну и с пониманием того, что производитель процессора не будет тебе жизнь облегчать, потому что ему это нафиг не сдалось

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Значит, реализовать эту "хотелку" сейчас совсем несложно
Вопрос не в том, сложно или не сложно, а в том, что нецелесообразно. Никто не будет делать процессор на 100 баксов более дорогим ради наполнения его функциональностью, от которой пользы в общем-то никакой и нет, а вот геморроя с хакерами только прибавится

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Да и затрачивал 8086 по нескольку тактов на любую, даже несложную, арифметическую операцию
Не надо путать время реального исполнения команды и задержку между попаданиями команды в конвейер. Пока одна команда исполняется, другая дешифруется - в этом принцип конвейера

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Т. е. конвейер был известен к моменту создания Intel 8086 уже более десяти лет. Ну а уж считать ли его к тому времени свежим изобретением — судить тебе
В те годы технологии не развивались такими бурными темпами. Это сейчас чуть ли не раз в 1-2 года выходит новое поколение процессоров. По тем временам 10 лет - вполне себе срок для того, чтобы считаться "свежим". Не говоря уж о том, что не было вообще никакой необходимости где-бы то ни было откатывать конвейер

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Это можно проверить лишь экспериментом
Ну так проверь

Добавлено через 37 минут
Хотя если посмотреть на код с вложенными функциями, полученными Gcc'ями, то ситуация почти такая, просто расстояние в тактах от модификации кода до его исполнение немного побольше. Правда тут ситуация немного другая - динамический вызов. Но при достаточно коротком тесте вполне может получиться так, что оно в динамике может случиться именно так: исполнение попадёт в код трамплина до того, как операция формирования кода трамплина будет завершена в конвейере. При таком раскладе получается, что фразу "процессор на 100 баксов более дорогим ради наполнения его функциональностью" следует поменять на "мы платим лишние 100 баксов ради функциональности, которая мало кому нужна". Пичаль

Правда как вариант тут может быть по другому: при исполнении операции call/ct процессор поймёт, что динамический переход происходит по адресу, про который известно, что в конвейере выполняется в него запись, правда для этого нужные операции должны пройти стадию дешифрации и вычисления адреса. В любом случае это другая ситуация, так что эксперимент по прежнему нужно проводить

Добавлено через 6 минут
Проблема с командами N1 и N2 она не столько в конвейере, сколько в out-of-order execution. Если команда N1 - это запись в память по адресу R1, а команда N2 - запись в регистр R2, то таким операции выглядят как независимые и могут быть переставлены местами. Поэтому ещё и эксперимент надо как-то правильно провести, правда у меня нет соображений, какие конкретно вещи надо учитывать. Всё сильно зависит от динами и от того, решит ли процессор притормозить операцию записи в память
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
24.12.2016, 10:07  [ТС]
В те годы технологии не развивались такими бурными темпами. Это сейчас чуть ли не раз в 1-2 года выходит новое поколение процессоров. По тем временам 10 лет - вполне себе срок для того, чтобы считаться "свежим". Не говоря уж о том, что не было вообще никакой необходимости где-бы то ни было откатывать конвейер
Вычислительная техника тогда тоже очень бурно и лихо развивалась. Посмотри любые сайты с названиями computer museum. Проходило 10 лет, и возникало совершенно новое поколение компьютеров. Не могу сказать, что темпы развития отличались сильно от нынешних.

Цитата Сообщение от Evg Посмотреть сообщение
Хороша ложка к обеду. В компьютерах 50-х годов количество памяти исчислялось единицами килобайт, а частоты килогерцами. Когда современные процессоры летают со скоростями, в сотни тысяч раз превышающими скорости процессоров 50-х годов, то все как бы с этим согласны. А то, что некоторыми из факторов такой скорости стали технологии, противоречащие стилю программирования 50-х годов, тут почему-то возникает недовольство. В 50-х годах не было ни кэша, ни конвейера, ни многопроцессорности, программисты тех времён могли себе позволить писать подобные коды
Ну, то что уровень технологий вырос до неузнаваемости, никто не спорит. Совсем другое оборудование стало, совсем другой уровень схемотехники. Конвейеры, суперскалярники, многоуровневые кэши, многопроцессорные системы и проблема синхронизации кэшей... Защищенный режим, страничная виртуальная память, о которой в 50-ые - самом начале 60-ых ещё никто не думал. Всё это так. Только вот тебе не кажется, что базовые принципы программирования и основы функционирования компьютера с тех пор мало изменились? Всё та же программа как набор машинных инструкций, те же опкоды, биты признаков и операнды, образующие формат машинной команды. Последовательное исполнение и переходы. Регистры и память. Программа, хранящаяся в одной и той же памяти вместе со своими данными, - принцип, заложенный фон Нейманом. Фортран и Алгол к 1960-ому году уже появились. Думать, что C++ и Java тут сделали революцию, наивно. Это продолжение тех же самых идей, просто побольше наворотов. Лисп родился примерно в то же время. Современные ОС так или иначе восходят к Multix и Unix - это 70-ые годы. К концу 1970-ых основные идеи были сформулированы. Многопоточность и синхронизация потоков в примитивном виде к концу 70-ых существовала. Конвейерный процессор появился в середине 60-ых. Тогда же уже появилось микропрограммное управление процессором (узел микрокоманд). Не знаю, в середине-конце 70-ых использовали ли лучшие процессоры какие-то принципы суперскалярности. Может, да, может - нет (интересно, был ли процессор IBM System/360 и 370 суперскалярником начального уровня или нет; вообще это была суперсложная система для своего времени, так что, кто знает). Просто все эти идеи развились сейчас необыкновенно, ну и уровень техники, элементная база, степень сложности всего этого сейчас не сравнимы с теми временами. А основные идеи и принципы мало изменились с тех пор.

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

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

Но это в общем-то всё философствования

Добавлено через 39 минут
Цитата Сообщение от Evg Посмотреть сообщение
Хотя если посмотреть на код с вложенными функциями, полученными Gcc'ями, то ситуация почти такая, просто расстояние в тактах от модификации кода до его исполнение немного побольше. Правда тут ситуация немного другая - динамический вызов. Но при достаточно коротком тесте вполне может получиться так, что оно в динамике может случиться именно так: исполнение попадёт в код трамплина до того, как операция формирования кода трамплина будет завершена в конвейере.
Вот об этом я тоже подумал, когда смотрел присланный тобой пример (очень интересный, кстати!). Там всего-то после кода формирования трамплина должно произойти 2 вызова функции, а второй вызов передаст управление этому трамплину. Если на это уйдет мало тактов, может оказаться так, что код трамплина (он сам крошечный, насколько я понял) окажется загружен в буфер команд и пойдёт на дешифрацию, ещё не будучи сформированным, т. е. в каком-то промежуточном состоянии, что приведёт, скорее всего (с наибольшей вероятностью) к illegal instruction. Так что если формирование кода в памяти и его обработка в процессоре никак не синхронизированы, этим трюком, по идее, пользоваться нельзя! А раз им пользуются, значит, не исключено, что Intel допускает модификацию близлежащими командами друг друга безо всяких ограничений (хотя я этого не утверждаю!). Потом тут ещё другое. У разных процессоров в зависимости от цены разные возможности по ускорению и оптимизации выполнения команд. Разная глубина конвейера, разное качество дробления потока на параллельные цепочки. По идее, более хороший и более дорогой процессор должен захватывать на исполнение сразу больше команд, чем более дешёвый. Т. е. у разных процессоров того же Интела или AMD будет разная глубина видимости. И если всё обстоит так, как говоришь ты, что нельзя модифицировать команды, которые уже оказались в процессоре на исполнении, то в пределах этой "области видимости" нельзя одной командой изменять другую. Но эта "область видимости" у разных моделей процессоров будет разная и она будет очень сильно изменяться даже в разных частях кода программы. Так что тут не будет вообще никакой предсказуемости и стандарта. Не понятно будет, до каких пор нельзя, а откуда уже можно

Но я не знаю, что там на самом деле. Мне очень интересно!

Добавлено через 23 минуты
Цитата Сообщение от Evg Посмотреть сообщение
Правда как вариант тут может быть по другому: при исполнении операции call/ct процессор поймёт, что динамический переход происходит по адресу, про который известно, что в конвейере выполняется в него запись, правда для этого нужные операции должны пройти стадию дешифрации и вычисления адреса. В любом случае это другая ситуация, так что эксперимент по прежнему нужно проводить
call/ct - имелось в виду call/ret? Просто инструкции ct не встречал. По идее, при классической конвейерное организации процессора (безо всякого внеочередного исполнения) это не так и сложно сделать (в теории несложно, разумеется). Эту ситуацию можно отследить, что была модифицирована память по адресу, соответствующему одной из инструкций, находящихся сейчас на выполнении в конвейере. Достаточно просто все эти адреса в процессоре хранить, пока соответстующие им инструкции выполняются в процессоре (и возможно даже привязывать их к каждой ступени конвейера и по каждому шагу конвейера привязку тоже менять, чтобы с движением команд синхронно менялись и привязки адресов к ступеням). А дальше, при каждой операции записи в память, одним махом сверять адрес, по которому произойдёт запись в память, со всеми этими адресами. Если хоть одно такое совпадение обнаружено, значит, фиксировать особое событие.

А вот тут
Проблема с командами N1 и N2 она не столько в конвейере, сколько в out-of-order execution. Если команда N1 - это запись в память по адресу R1, а команда N2 - запись в регистр R2, то таким операции выглядят как независимые и могут быть переставлены местами. Поэтому ещё и эксперимент надо как-то правильно провести, правда у меня нет соображений, какие конкретно вещи надо учитывать. Всё сильно зависит от динами и от того, решит ли процессор притормозить операцию записи в память
боюсь, что всё гораздо сложнее.

Добавлено через 23 минуты
Команда N1 логически исполняется раньше команды N2, и она меняет содержимое команды N2 (перезаписывает её). Но процессор на момент анализа двух этих команд этого понять не может и решает, что это две независимые команды, которые можно исполнять параллельно или даже в обратном порядке. В результате, сначала исполняется команда N2 (потому что так этого захотел процессор), она что-то пишет в регистр или даже в память, потом запускается команда N1, она перезаписывает команду N2 и обнаруживается, что команда N2 выполнила бред, поскольку на её месте должна была исполниться совсем другая команда. Ведь логически первой всё равно должна была исполниться N1, а она-то и перезаписала N2.

Отследить эту ситуацию несложно, но вот что делать дальше? Возвращать всё назад? А это может оказаться невозможно, поскольку команда N2 (её изначальная, неперезаписанная, "неправильная" с точки зрения логики версия) уже безнадёжно испортила регистр или даже память. Даже не знаю, разрешима ли эта ситуация. Может, для таких супер-пупер навороченных процессоров, как Интел, это уже и не проблема. В общем, не знаю.

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

Добавлено через 19 минут
По поводу эксперимента у меня есть мысль.

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

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

Чем мне нравится этот алгоритм, там очень много переходов - 2 вложенных друг в друга цикла, операция сравнения и переход, шагание по массиву. То что он совершенно не эффективен как алгоритм сортировки (там n^2/2 проверок) - это и не важно. Главное, что в нём очень большое раздолье для такого творчества и самомодификации можно делать очень близкие. Более умного и профессионального теста на эту тему я придумать не могу.

Но нужно, чтоб и ты немного в этом деле поучаствовал, если оно тебе интересно, конечно.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
24.12.2016, 14:14
Цитата Сообщение от JohnyWalker Посмотреть сообщение
Только вот тебе не кажется, что базовые принципы программирования и основы функционирования компьютера с тех пор мало изменились? Всё та же программа как набор машинных инструкций, те же опкоды, биты признаков и операнды, образующие формат машинной команды
Это не базовые принципы программирования, а базовые принципы реализации аппаратуры. Принципы программирования, возможно, и не поменялись (хотя это вопрос спорный), а вот само программирование поменялось. Раньше не задумывались о переносимости, не задумывались о том, что софт надо развивать и сопровождать многие года, все или почти все программы писались под конкретную аппаратуру, переход с одной машины на другую даже в рамках одного семейства вызывал проблемы. Не были по нормальному развиты системы программирования. Словом, была кустарщина. Можешь ради интереса посмотреть на то, как программирует в наши дни старшее поколение. Невооружённым глазом виден стиль программирования, оставшийся со времён, когда люди программировали на ассемблере - когда приходится писать труднопонимаемый код ради экономии пары байтов. Для реализации качественного софта в наши дни совсем не нужны подходы к программированию, которые использовались в 50-х годах. И уж тем более не нужна самомодификация кода

Цитата Сообщение от JohnyWalker Посмотреть сообщение
А основные идеи и принципы мало изменились с тех пор
Дело ведь не в принципах. С одними и теми же принципами можно написать одноразовый гавнокод, а можно и программу, которая будет жить и развиваться десятки лет. Вот этот момент изменился. Толком к этому изменению послужил окружающий мир, а вовсе не языки программирования, технологии или что-то ещё. Просто компьютеры стали тем, что есть чуть ли не в каждом чайнике, а не монстрами ценой в миллион долларов для избранных

Цитата Сообщение от JohnyWalker Посмотреть сообщение
В ней нет ничего противоестественного и маргинального
Вопрос не в противоестественности, а в нужности. Самомодификация кода для машины с 8 килобайтами памяти - скорее всего жизненно необходимая вещь. Самомодификация кода в машине с 8 гигабайтами памяти - чистой воды баловство. Пока не было активного переноса софта с машины на машину, в природе не было такого явления как вирусы. Когда вирусы появились, то возможность самомодификации кода стала одной из дыр в системе безопасности

Цитата Сообщение от JohnyWalker Посмотреть сообщение
А раз им пользуются, значит, не исключено, что Intel допускает модификацию близлежащими командами друг друга безо всяких ограничений (хотя я этого не утверждаю!)
Я тоже не утверждаю обратное, ибо этого не знаю. Я всего лишь предполагаю. Но я не разбираюсь в интеловской системе команд, у меня нет ни времени не желания проводить эксперимент, хотя посмотреть на результаты чужого эксперимента было бы интересно. Но, как я уже говорил, у меня нет полного понимания того, как эксперимент проводить правильно. И дело не в алгоритме, по которому должен работать софт, а в нехватке знаний о том, как работает конвейер. При незнании влёгкую можно поставить эксперимент, который как бы подтвердит идею, но окажется, что эксперимент был неправильный. Типа того, что проверяем утверждение "все числа чётные". Проверяем числа 2, 4, 6, 8, ..., убеждаемся, что все они чётные, распространяем результат эксперимента на всю вселенную и делаем (некорректный) вывод о том, что все числа чётные

Цитата Сообщение от JohnyWalker Посмотреть сообщение
call/ct - имелось в виду call/ret?
ct - control transfer. Термин для операции передачи управления, я просто не знаю, как она на интеле называется. Переходы бывают статические (по заранее известному адресу) и динамические (по значений в регистре). Так вот для безусловного динамического перехода (или call'а) не имеет смысл строить какие-то технологии, аналогичные предсказателю перехода, потому что невозможно заранее попытаться исполнить код, адрес которого ещё не известен. И вот динамический переход может быть естественным барьером против механизма out-of-order, который обеспечит то, что сначала самомодифицируемый код окажется в памяти гарантированно раньше того момента, когда он попадёт в конвейер. И именно этот момент отличает код работы с вложенными функциями от варианта N1-N2. Опять-таки это размышления, а не факт

Цитата Сообщение от JohnyWalker Посмотреть сообщение
Но нужно, чтоб и ты немного в этом деле поучаствовал, если оно тебе интересно, конечно
Советами-то я готов поучаствовать, но я интеловский ассемблер слабо понимаю. И, как уже выше писал, затрудняюсь сказать, как нужно правильно ставить эксперимент, чтобы не сделать неверных выводов
1
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
25.12.2016, 06:48  [ТС]
Цитата Сообщение от Evg Посмотреть сообщение
Это не базовые принципы программирования, а базовые принципы реализации аппаратуры. Принципы программирования, возможно, и не поменялись (хотя это вопрос спорный), а вот само программирование поменялось. Раньше не задумывались о переносимости, не задумывались о том, что софт надо развивать и сопровождать многие года, все или почти все программы писались под конкретную аппаратуру, переход с одной машины на другую даже в рамках одного семейства вызывал проблемы. Не были по нормальному развиты системы программирования. Словом, была кустарщина.
Это всё правильно, и с этим я согласен. Но все эти вещи — наличие качественных и богатых по своим возможностям библиотек, отладчики, профилировщики, удобные IDE, существование языков и библиотек, делающих перенос программы с одной системы на другую несложной задачей, — это, скорее, результат развития технологий программирования и вычислительных машин, но не сами идеи и базовые принципы, заложенные в их основу. Это всё же побочный эффект этих идей и результат их внедрения. Он ОЧЕНЬ важен для развития всей компьютерной отрасли, но он не фундаментален. На фундаментальном уровне процессор сейчас выполняет ту же работу, что он выполнял, допустим, в 1953 году (хотя сложность его электроники и количество оптимизаций несопоставимы с тем, что использовалось в те годы). Было небольшое отклонение от этих идей в 70-ые годы — стековые машины, ЛИСП-машины, но они почему-то не прижились и все вернулись в итоге к прежнему. На фундаментальном уровне революцией в области языков программирования было создание Бэкусом и Науром простого формализма, позволявшего описать любой язык как набор простых и понятных правил и написать по ним транслятор. Алгол-60, Лисп, Симула-67 (она, кстати, в своё время не была оценена и не прижилась, была забыта, к идеям ООП вернулись спустя 20 - 25 лет) — вот, пожалуй, предшественники большинства современных языков. На фундаментальном уровне отличия незначительны. Код на Алголе-60, кстати, очень узнаваем и понятен современному программисту, это почти современный язык (правда, в нём по-моему ещё нет структур — struct языка Си или record Паскаля). Оформление кода, его модульность — это следствие развития и чрезвычайного усложнения программ, роста их количества, но это не фундаментальное изобретение в области программирования.

Поэтому когда ты говоришь о невозможности применения принципов программирования 50-ых — 60-ых годов (особенно на машинном уровне), я с этим не согласен. Принципы-то в общих чертах сохранились прежними. А то что уровень вычислительных машин, сложность и разнообразие программ, интерфейсы пользователя изменились до невообразимости, с этим никак нельзя поспорить.

Добавлено через 33 минуты
Цитата Сообщение от Evg Посмотреть сообщение
Вопрос не в противоестественности, а в нужности. Самомодификация кода для машины с 8 килобайтами памяти - скорее всего жизненно необходимая вещь. Самомодификация кода в машине с 8 гигабайтами памяти - чистой воды баловство.
Я и не говорю о том, что этим всем необходимо пользоваться. Сам бы я не хотел писать самоизменяющийся код без действительно острой необходимости. Слишком сложно и тонко, да и не очень нужно. Но если такая возможность предусмотрена логикой вычислительной машины и принципами, заложенными в неё ещё во времена фон Неймана, она должна быть сохранена в полном объёме.

Пока не было активного переноса софта с машины на машину, в природе не было такого явления как вирусы. Когда вирусы появились, то возможность самомодификации кода стала одной из дыр в системе безопасности
Ну, хакеры были, есть и будут, я к этому явлению отношусь совершенно спокойно Защищать вычислительную систему нужно совсем другими способами. Потом этот приём — вставка кода через переполнение буфера, — как мне кажется, отнюдь не единственный и не основной в арсенале хакера. Мы ведь всегда можем средствами ОС запретить страницы с кодом изменять, а страницы с данными исполнять. И если мы вдруг вообще запретим всякую самомодификацию кода средствами процессора или операционной системы, вряд ли это отпугнёт хакеров.

Добавлено через 2 часа 28 минут
Цитата Сообщение от Evg Посмотреть сообщение
Можешь ради интереса посмотреть на то, как программирует в наши дни старшее поколение. Невооружённым глазом виден стиль программирования, оставшийся со времён, когда люди программировали на ассемблере - когда приходится писать труднопонимаемый код ради экономии пары байтов.
А что — с удовольствием бы посмотрел. У тебя есть примеры программ или их фрагментов, написанных этими стариками? Поглядел бы с интересом. Кстати, для меня вопрос оформления исходного текста никогда не был значимым. То что согласно нынешним стандартом называется говнокодом, может с алгоритмической стороны быть безупречной программой, не содержащей ошибок и быстро исполняющейся. Я понимаю, что вопрос оформления кода, именования переменных, функций и типов программы, придание им многословной и человекопонятной формы довольно остро встаёт в больших проектах — ядро ОС, большая пользовательская программа (хоть тот же MS Office). Но я на эти вещи смотрю просто и смеюсь надо всеми вашими корпоративными стандартами кодирования

Вопрос не в противоестественности, а в нужности. Самомодификация кода для машины с 8 килобайтами памяти - скорее всего жизненно необходимая вещь. Самомодификация кода в машине с 8 гигабайтами памяти - чистой воды баловство.
В общем, постараюсь выразить свои взгляды в такой форме.
Идеальной и правильной вычислительной машиной является такой компьютер, который выполняет все свои операции последовательно, одна за другой. При этом каждая такая операция является атомарной, одна операция не влияет на другую. Никакого одновременного и параллельного исполнения различных команд в таком идеализированном процессоре нет — каждая команда выбирается из памяти, расшифровывается, считывает данные, исполняет над ними какие-то действия и записывает результат в память или в регистры. Только после того, как он полностью закончил выполнение первой команды, он может приступить к выполнению следующей команды, и описанный выше цикл обработки команды полностью повторится. Садясь за компьютер, программист может смело воображать, что перед ним находится именно такая идеализированная вычислительная машина, в которой нет ни конвейера, ни внеочередного исполнения, ни кэша (даже простейшего одноуровневого), ни необходимости синхронизировать все эти кеши, если он работает с многопроцессорной системой (допустим, перед ним бытовой компьютер-многоядерник или даже сервер). Кстати, многопроцессорный компьютер вполне вписывается в эту идеализированную схему. Нет в процессоре этого компьютера и никакой суперскалярности, позволяющей параллельно обрабатывать несколько различных команд из последовательного потока. Есть последовательная обработка команд одна за одной. Пока одна команда выполняется, другая не трогается и даже не считывается из памяти.

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

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

Но мы-то отлично понимаем, что если реализовать процессор в таком виде буквально, на физическом уровне, он будет работать страшно медленно. Это слишком простой и примитивный подход. Поэтому мы применяем к нашему процессору кучу оптимизаций — конвейер, переименование регистров для борьбы с так называемыми ложными зависимостями по данным, внеочередное исполнение команд, распараллеливаем изначально последовательный поток команд по нескольким АЛУ, что называется суперскалярностью, при этом умный процессор может переставлять команды в буфере как угодно, если это позволит разом загрузить больше конвейеров и АЛУ. И это хорошо и правильно. Кэш-память просто необходима, ибо работа с внешней оперативной памятью имеет массу физических ограничений по времени.

Важно другое. В ходе всех этих сложных и хитроумных оптимизаций возникнет куча побочных эффектов. Один из них (и я чувствую, далеко не единственный) мы с тобой обсуждали. Так вот задача создателей процессора все эти побочные эффекты вычислить, понять и устранить. Если какие-то из них оказались незамеченными в одной модели (а такое бывает), в последующих моделях от них нужно избавиться. Кэши, кстати, и их синхронизация в многопроцессорной системе тоже должны быть полностью автоматизированы — никакой необходимости в их ручной синхронизации программистом, составляющим код, быть не должно.

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

Он должен, конечно, знать, что все эти приёмы очень сильно сказываются на быстродействие одной и той же, но по-разному составленной программы. Имеет значение, в каком порядке расположены команды, т. к. от этого будет зависеть, насколько полно процессор сможет загрузить свои устройства. Очень важен порядок обращения к памяти, ибо если программа будет дёргать данные по совершенно разным адресам в произвольном порядке, не стремясь медленно переходить от одних данных к другим, преимущества кэша будут потеряны — в худшем случае скорость выполнения может упасть в 100 раз (но это еще надо очень потрудиться, чтобы такое действительно произошло). Но он должен быть твёрдо уверен в одном, что любая даже самая странная и идиотская программа у него всё равно заработает. Пусть и со страшной потерей производительности, но отработает она правильно. Правильно, это значит так, как отработала бы она на идеальном простейшем последовательном процессоре, который я описал выше (без конвейера, суперскалярности и кэшей). А такой процессор позволяет любые трюки, в т. ч. трюк с самоизменением программы.

Если же процессор не работает таким образом, т. е. его производитель допускает существование побочных эффектов, связанных с оптимизацией его устройства, то это всё уже грязь и извращение. Значит, производитель знает о существовании всех этих побочных явлений, но не хочет тратить время, силы и деньги на борьбу с ними. Это просто плохой производитель.

Ну а по поводу самомодификации кода. Это не хорошо и не плохо. Есть ли в этом сейчас большая нужда? Вряд ли. Поиграть с этим можно. Хотя ты мне нашёл пример, когда даже создатели компилятора gcc, вполне серьёзной софтины, почему-то прибегли к этому приёму (к генерации кода во время выполнения). Но если такая возможность следует из логики работы вычислительной машины (принципа хранимой программы), значит она должна быть реализована в полном объёме и безо всяких ограничений. А уж пользоваться этим или нет — это воля программиста, как ему заблагорассудится
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
25.12.2016, 06:48

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

Написать программу калькулятор, выполняющую арифметические действия над целыми и вещественными числами
Напишите программу «Калькулятор», выполняющую арифметические действия над целыми и вещественными числами.

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

Арифметические операции над числами с плавающей запятой
Помогите, пожалуйста, нужно сделать на Паскале Заранее Спасибо!!!

Длинная арифметика: арифметические операции над числами
Срочно нужны исходники (функции): 1. Перевод обычного числа в длинное (массив, строка , вектор кто с чем работает) 2. Нахождение суммы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru