|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
|
Арифметические операции над небольшими целыми числами в процессоре SPARC11.09.2016, 14:11. Показов 5368. Ответов 57
Метки нет (Все метки)
Здравствуйте, форумчане!
Есть ли среди вас знатоки архитектуры SPARC? Если да, то просветите меня, пожалуйста, по такому вопросу. Как в процессоре SPARC выполняются простые арифметические действия над целыми числами размера меньшего, чем стандартное машинное слово этого процессора (32 или 64 бит)? Допустим, нам надо выполнить какую-то простую операцию над байтом или 16-битным полусловом. Мне хотелось бы понять, каким набором машинных инструкций эта операция над байтом или полусловом длиной в 16 бит будет воплощена? Особенно мне интересно, как осуществляется контроль за переполнением разрядной сетки в этом случае. В случае архитектуры x86 особых вопросов не возникает - как и в любой другой CISC-архитектуре код операции (поле КОП) в команде один и тот же независимо от размера данных, а собственно размер операндов задаётся специальными битами признаков внутри самой команды. Проблемы контроля переполнения данных в x86 (как, впрочем, и в других CISC-архитектурах) тоже нет. Фиксируется переполнение той разрядной сетки, которая указана в поле длины операндов. Т. е. если длина данных равняется одному байту - в регистре флагов фиксируется выход значения за границы одного байта, если операнды представляют собой 16-битные полуслова - фиксируется выход за пределы 16-битного представления целых чисел, если длина операндов равна 32-битному слову, результат должен помещаться в 32 бита (в противном случае будут установлены соответствующие флаги в регистре флагов). То же самое относится к 64-разрядным целым числам. Хотелось бы понять, как те же самые проблемы (хранения небольших целых чисел размера меньшего длины машинного слова, манипулирования этими маленькими числами, контроля над переполнением данных при арифметических операциях с их участием) решаются в архитектуре SPARC. Для примера можно было бы рассмотреть какое-то простое арифметическое действие (например, сложение или вычитание) над двумя числами размером с байт или 16-битное полуслово и привести элементарную программку, которая его выполняет. Был бы очень благодарен тем, кто мне ответит.
0
|
|
| 11.09.2016, 14:11 | |
|
Ответы с готовыми решениями:
57
Арифметические опреции над целыми числами
Арифметические операции над числами |
|
|
||||
| 22.11.2016, 20:56 | ||||
|
Не, тут речь идёт не о том, что компиляторы делают более строгими, а о том, что исправляют ошибки. Т.е. по стандарту компилятор должен был ругаться, но он не ругался. В основном это из C++ лезет
Ну а коммерческие проекты практически никто не спешит переводить на более свежие версии. Особенно на самые свежие (которые всем миром ещё не успели отладить). Intel очень долго поддерживал (а может и до сих пор поддерживает) старую версию своего компилятора для (вроде бы) Oracle. Просто потому, что переход на другую версию выльется в очень большую трату денег Добавлено через 1 минуту Добавлено через 2 минуты Согласованная сборка (обычно туда входят binutils, компилятор, glibc, хидера от linux'а) по умному называется toolchain. По запросу "как собрать кросс-toolchain" уже вываливаются полезные статьи. Но вот как называется утилита для автоматизации этого процесса - буду вспоминать Добавлено через 3 минуты Вроде бы вот это http://crosstool-ng.org/ Добавлено через 5 минут Добавлено через 59 секунд И если вдруг чего-то соберёшь, то было бы полезно поделиться информацией с другими - кому-то это тоже пригодится, а кому-то будет просто интересно
1
|
||||
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||
| 22.11.2016, 21:41 [ТС] | ||||
Есть 2 пути. 1. Делать это самому вручную. Нужно разобраться, из каких составляющих состоит gcc (их очень много), а потом каждую из них собирать со всеми нужными параметрами. Это огромная работа. 2. Воспользоваться готовой утилитой для сборки кросскомпилятора на основе gcc — crosstool-ng, в которой все эти шаги уже проработаны и запрограммированы (в виде там скриптов, сишных программ и т. п.). Но с её настройками тоже надо разбираться, и это не так просто. Правильно я суть понимаю?
Добавлено через 2 минуты P.S. Вот ещё что нашёл для Debian http://wiki.debian.org/CrossToolchains Вот ещё статья, не смотрел пока http://vovanium.ru/cifra/kross-kompiljacija
0
|
||||
|
|
||
| 22.11.2016, 21:47 | ||
|
Добавлено через 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
|
|
|
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
|
|
|
|
||
| 22.12.2016, 15:13 | ||
|
За счёт этого в процессоре 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 можно командами переходов
0
|
||||||
|
|
||||||
| 22.12.2016, 23:25 | ||||||
|
1
|
||||||
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||
| 23.12.2016, 03:44 [ТС] | ||||
Добавлено через 1 час 32 минуты ![]() Добавлено через 1 час 9 минут Evg, у меня ещё пара вопросов к тебе по SPARC'у. Первый вопрос. Допускает ли этот процессор самомодификацию кода и нормальную работу такого самомодифицирующегося кода? Я знаю, что многими "светилами" компьютерных наук, сторонниками структурного, объектно-ориентированного, правильного программирования, этот подход предан анафеме. Но я не принадлежу к числу этих "светил" и ничего плохого в этом подходе не вижу. В самом деле, ничего ужасного в самомодифицирующейся программе нет. Подход вполне логичен, разумен и не противоречит здравому смыслу и принципам вычислительной техники. Я вижу следующие сложности на этом пути. Этот подход сложноват в реализации, т. к. надо отслеживать адреса всех команд в памяти, которые мы хотим модифицировать. Нужно знать в деталях форматы машинных команд и отнюдь не на уровне ассемблера, а в двоичных кодах, в случае CISC-процессора команды могут быть достаточно сложны по устройству, т. е. надо помнить довольно большой объём информации. Подход плохо совместим с идеей языка высокого уровня, т. е. чтобы его реализовать всё с самого начала нужно делать на ассемблере. И подход очень плохо сочетается с конвейерным принципом организации процессора. Если некоторая команда оказалась модифицирована тогда, когда она уже была считана в буфер команд или, тем более, уже попала на конвейер и находилась на какой-то из его ступеней, все ступени конвейера, начиная с этой команды и позже, надо сбрасывать, очищать и загружать его по-новой. Т. е. если у нас процессор конвейерный и каждая пятая или десятая команда в процессе выполнения, допустим, меняет содержимое близлежащей команды, то все выгоды конвейерной организации процессора сводятся на нет — он работает как процессор без конвейерной структуры. Боюсь, что в случае суперскалярника всё окажется ещё хуже. Я даже не представляю себе, как суперскалярный процессор со всеми его ухищрениями — переименованием регистров в командах, перестановкой команд в потоке, разделением одного последовательного потока на несколько небольших цепочек, логически независимых, а потому способных исполниться параллельно, наличием нескольких конвейеров со своим АЛУ у каждого и блоком-диспетчером, который раскидывает команды по этим конвейерам, — обрабатывает такую ситуацию. По-моему это очень непросто. Хотя ведь Интел — суперскалярник со всеми этими штуками, а он вполне допускает самомодифицирующийся код и как-то такое событие обрабатывает, так что ошибок в вычислениях не происходит. В общем, я ничего не имею против программ с самомодифицирующимся кодом ни с какой стороны, но понимаю, что они плохо сочетаются с устройством любого современного процессора и плохо влияют на его быстродействие. Но это всё философия, а вопрос в другом. Допускают ли реально существующие экземпляры SPARC и сама эта архитектура в своём стандарте (т. е. если говорить даже не о реально существующих процессорах, а о самом стандарте, как формальном своде правил), программы, изменяющие свой собственный код? Или же в реально существующих процессорах и в стандартах SPARC (v7, v8, v9 и т. д.) это начисто запрещено? Если программа, запущенная на процессоре SPARC, всё-таки попробует изменить свой код, к чему это приведёт в действительности?
0
|
||||
|
|
||||||||
| 23.12.2016, 13:29 | ||||||||
|
Добавлено через 4 минуты Ну а на 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
|
|
|
|
|||||||||
| 23.12.2016, 15:47 | |||||||||
|
Добавлено через 29 минут В 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
|
|
|
|
|||
| 23.12.2016, 16:56 | |||
|
Такая ересь могла бы понадобиться только из соображений совместимости, в случае нормального развития никто в процессор столько лишних аппаратуры вставлять не будет, т.к. на практике кроме извращенцев оно никому не нужно. Чтобы такую хотелку реализовать, нужно уметь откатывать конвейер, как это делается в механизме предсказания переходов. Но предсказания переходов появились только в 586-м процессоре (в первом суперскаляре). До появления суперскаляра такого механизма скорее всего не было. Конвейер был уже в 086-м, в те времена откат конвейера был бы слишком жирной операцией для процессора, да и сам конвейер был свежим изобретением. Откат конвейера - это по сути борьба самим с собой, и вряд ли первый конвейер создавали так, чтобы с самим собой и бороться. Из этого можно сделать вывод, что в самых первых процессорах в общем-то невозможно было написать программу, где команда N1 модифицирует команду N2 (в предположении, что N1 и N2 исполняются друг за другом подряд). Поэтому в этом месте вопроса совместимости вроде бы как не должно возникать
0
|
|||
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||||
| 23.12.2016, 18:00 [ТС] | ||||||
0
|
||||||
|
|
|||||||
| 23.12.2016, 21:47 | |||||||
![]() Добавлено через 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 [ТС] | |||||||
Просто идея о том, что одна команда может изменить код другой или даже полностью её перезаписать, мне нравится. В ней нет ничего противоестественного и маргинального. Команды - такие же данные, они хранятся в таких же ячейках памяти, они не представляют собой что-то вечное и неизменное. В этом принципе заложена своя красота и гибкость, просто он до сих пор полностью не раскрыт. Ведь вся прелесть и сила компьютера в его гибкости и изменяемости. Так зачем же такой мощный принцип приносить в жертву и запрещать. Конвейерность и суперскалярность должна увеличивать быстродействие и производительность вычислительной системы, но не уродовать её, делая прежние приёмы программирования невозможными.
Но это в общем-то всё философствования ![]() Добавлено через 39 минут ![]() Но я не знаю, что там на самом деле. Мне очень интересно! Добавлено через 23 минуты А вот тут
Добавлено через 23 минуты Команда N1 логически исполняется раньше команды N2, и она меняет содержимое команды N2 (перезаписывает её). Но процессор на момент анализа двух этих команд этого понять не может и решает, что это две независимые команды, которые можно исполнять параллельно или даже в обратном порядке. В результате, сначала исполняется команда N2 (потому что так этого захотел процессор), она что-то пишет в регистр или даже в память, потом запускается команда N1, она перезаписывает команду N2 и обнаруживается, что команда N2 выполнила бред, поскольку на её месте должна была исполниться совсем другая команда. Ведь логически первой всё равно должна была исполниться N1, а она-то и перезаписала N2. Отследить эту ситуацию несложно, но вот что делать дальше? Возвращать всё назад? А это может оказаться невозможно, поскольку команда N2 (её изначальная, неперезаписанная, "неправильная" с точки зрения логики версия) уже безнадёжно испортила регистр или даже память. Даже не знаю, разрешима ли эта ситуация. Может, для таких супер-пупер навороченных процессоров, как Интел, это уже и не проблема. В общем, не знаю. Интересно, а можно ли так запутать суперскалярник такими приёмами с самомодификацией друг другом близлежащих команд, чтобы он запутался и сделал ошибочные вычисления. Или он со всем этим прекрасно справляется и не путается? Добавлено через 19 минут По поводу эксперимента у меня есть мысль. Надо написать какую-то простенькую маленькую совсем детскую программку, перевести её на ассемблер обычным образом, безо всяких выкрутасов. А потом эту обычную ассемблерную программку превратить в самомодифицирующуюся, причем чем больше этот трюк в ней будет использоваться, тем лучше. В качестве примера можно взять сортировку массива, причем использовать самый простой детский алгоритм - путём поиска наименьшего элемента. Ты понял, о чём я. Написать сначала эту игрушечную программу на Си, потом вручную перевести её на ассемблер обычным нормальным способом, а потом из нормальной ассемблерной программы превратить в ассемблерную самомодифицирующуюся. Чем мне нравится этот алгоритм, там очень много переходов - 2 вложенных друг в друга цикла, операция сравнения и переход, шагание по массиву. То что он совершенно не эффективен как алгоритм сортировки (там n^2/2 проверок) - это и не важно. Главное, что в нём очень большое раздолье для такого творчества и самомодификации можно делать очень близкие. Более умного и профессионального теста на эту тему я придумать не могу. Но нужно, чтоб и ты немного в этом деле поучаствовал, если оно тебе интересно, конечно.
0
|
|||||||
|
|
|||||||
| 24.12.2016, 14:14 | |||||||
|
1
|
|||||||
|
200 / 87 / 9
Регистрация: 15.11.2010
Сообщений: 472
|
||||||
| 25.12.2016, 06:48 [ТС] | ||||||
|
Поэтому когда ты говоришь о невозможности применения принципов программирования 50-ых — 60-ых годов (особенно на машинном уровне), я с этим не согласен. Принципы-то в общих чертах сохранились прежними. А то что уровень вычислительных машин, сложность и разнообразие программ, интерфейсы пользователя изменились до невообразимости, с этим никак нельзя поспорить. Добавлено через 33 минуты
Защищать вычислительную систему нужно совсем другими способами. Потом этот приём — вставка кода через переполнение буфера, — как мне кажется, отнюдь не единственный и не основной в арсенале хакера. Мы ведь всегда можем средствами ОС запретить страницы с кодом изменять, а страницы с данными исполнять. И если мы вдруг вообще запретим всякую самомодификацию кода средствами процессора или операционной системы, вряд ли это отпугнёт хакеров.Добавлено через 2 часа 28 минут ![]()
Идеальной и правильной вычислительной машиной является такой компьютер, который выполняет все свои операции последовательно, одна за другой. При этом каждая такая операция является атомарной, одна операция не влияет на другую. Никакого одновременного и параллельного исполнения различных команд в таком идеализированном процессоре нет — каждая команда выбирается из памяти, расшифровывается, считывает данные, исполняет над ними какие-то действия и записывает результат в память или в регистры. Только после того, как он полностью закончил выполнение первой команды, он может приступить к выполнению следующей команды, и описанный выше цикл обработки команды полностью повторится. Садясь за компьютер, программист может смело воображать, что перед ним находится именно такая идеализированная вычислительная машина, в которой нет ни конвейера, ни внеочередного исполнения, ни кэша (даже простейшего одноуровневого), ни необходимости синхронизировать все эти кеши, если он работает с многопроцессорной системой (допустим, перед ним бытовой компьютер-многоядерник или даже сервер). Кстати, многопроцессорный компьютер вполне вписывается в эту идеализированную схему. Нет в процессоре этого компьютера и никакой суперскалярности, позволяющей параллельно обрабатывать несколько различных команд из последовательного потока. Есть последовательная обработка команд одна за одной. Пока одна команда выполняется, другая не трогается и даже не считывается из памяти. Садясь за такой компьютер, программист может воображать, что он работает с памятью напрямую, никакой прослойки в виде кэша между процессором и памятью у него нет. Соответственно думать о том, что один процессор в своём кэше изменил какую-то переменную, а в кэш другого её изменённый образ не попал, он не должен. Ибо кэша не существует (для программиста, естественно). Чем эта схема хороша — она предсказуема, она совершенно непротиворечива, в ней нет никаких побочных эффектов (которые учесть и предсказать-то не всегда легко) вроде того, который мы с тобой обсуждали, когда написать самомодифицирующийся код, изменив одну из ближайших команд, может оказаться невозможно из-за того, что она уже считана конвейером. Но мы-то отлично понимаем, что если реализовать процессор в таком виде буквально, на физическом уровне, он будет работать страшно медленно. Это слишком простой и примитивный подход. Поэтому мы применяем к нашему процессору кучу оптимизаций — конвейер, переименование регистров для борьбы с так называемыми ложными зависимостями по данным, внеочередное исполнение команд, распараллеливаем изначально последовательный поток команд по нескольким АЛУ, что называется суперскалярностью, при этом умный процессор может переставлять команды в буфере как угодно, если это позволит разом загрузить больше конвейеров и АЛУ. И это хорошо и правильно. Кэш-память просто необходима, ибо работа с внешней оперативной памятью имеет массу физических ограничений по времени. Важно другое. В ходе всех этих сложных и хитроумных оптимизаций возникнет куча побочных эффектов. Один из них (и я чувствую, далеко не единственный) мы с тобой обсуждали. Так вот задача создателей процессора все эти побочные эффекты вычислить, понять и устранить. Если какие-то из них оказались незамеченными в одной модели (а такое бывает), в последующих моделях от них нужно избавиться. Кэши, кстати, и их синхронизация в многопроцессорной системе тоже должны быть полностью автоматизированы — никакой необходимости в их ручной синхронизации программистом, составляющим код, быть не должно. Т. е. у программиста должна создаваться иллюзия, что работает он с чисто последовательной машиной, которую я описал в самом начале, а все оптимизирующие её работу принципы и трюки никак не влияют на логику вычислений и конечный результат. Он должен, конечно, знать, что все эти приёмы очень сильно сказываются на быстродействие одной и той же, но по-разному составленной программы. Имеет значение, в каком порядке расположены команды, т. к. от этого будет зависеть, насколько полно процессор сможет загрузить свои устройства. Очень важен порядок обращения к памяти, ибо если программа будет дёргать данные по совершенно разным адресам в произвольном порядке, не стремясь медленно переходить от одних данных к другим, преимущества кэша будут потеряны — в худшем случае скорость выполнения может упасть в 100 раз (но это еще надо очень потрудиться, чтобы такое действительно произошло). Но он должен быть твёрдо уверен в одном, что любая даже самая странная и идиотская программа у него всё равно заработает. Пусть и со страшной потерей производительности, но отработает она правильно. Правильно, это значит так, как отработала бы она на идеальном простейшем последовательном процессоре, который я описал выше (без конвейера, суперскалярности и кэшей). А такой процессор позволяет любые трюки, в т. ч. трюк с самоизменением программы. Если же процессор не работает таким образом, т. е. его производитель допускает существование побочных эффектов, связанных с оптимизацией его устройства, то это всё уже грязь и извращение. Значит, производитель знает о существовании всех этих побочных явлений, но не хочет тратить время, силы и деньги на борьбу с ними. Это просто плохой производитель. Ну а по поводу самомодификации кода. Это не хорошо и не плохо. Есть ли в этом сейчас большая нужда? Вряд ли. Поиграть с этим можно. Хотя ты мне нашёл пример, когда даже создатели компилятора gcc, вполне серьёзной софтины, почему-то прибегли к этому приёму (к генерации кода во время выполнения). Но если такая возможность следует из логики работы вычислительной машины (принципа хранимой программы), значит она должна быть реализована в полном объёме и безо всяких ограничений. А уж пользоваться этим или нет — это воля программиста, как ему заблагорассудится
0
|
||||||
| 25.12.2016, 06:48 | |
|
Арифметические операции над числами Написать программу калькулятор, выполняющую арифметические действия над целыми и вещественными числами
Длинная арифметика: арифметические операции над числами Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
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, синий туман.
Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
|