Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Самомодифицирующийся код в экосистеме Cortex-M.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2
jcxz
Есть мысль использовать самомодифицирующийся код для оптимизации решения одной задачи.
Именно самомодифицирующийся - программа строит часть самой себя по некоему алгоритму, а не просто копирует куски кода из одной области памяти в другую.
И вот я что-то не смог вспомнить чтобы здесь на форуме вообще когда-то поднималась эта тема.
Кто-то вообще использует такой код на ARM-ах в своих проектах? Просто интересно... rolleyes.gif

PS: И пожалуйста - без религиозной нетерпимости.
aaarrr
Цитата(jcxz @ Jun 29 2018, 01:54) *
И вот я что-то не смог вспомнить чтобы здесь на форуме вообще когда-то поднималась эта тема.
Кто-то вообще использует такой код на ARM-ах в своих проектах? Просто интересно... rolleyes.gif

Просто тема в принципе не частая. А так - почему бы и нет, если позволяет эффективно решить задачу.
AVI-crak
Цитата(jcxz @ Jun 29 2018, 04:54) *
Просто интересно... rolleyes.gif

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

Но выход есть всегда.
На Си, Си++, и выше - задать точную последовательность применения мнемоник ядра практически невозможно. Однако это можно сделать на асме.
Весь проект придётся делить на две части: до и после. Причём компилировать придётся то-же отдельно. Весьма утомительное удовольствие.

Если нет задачи защиты информации - этим рукоблудием лучше не заниматься.
aaarrr
Цитата(AVI-crak @ Jun 29 2018, 02:23) *
На Си, Си++, и выше - задать точную последовательность применения мнемоник ядра практически невозможно. Однако это можно сделать на асме.

Ужасы какие. ТС вроде как собирался генерировать часть кода программно с нуля, а не строить новую программу из частей исходной. Термин "самомодифицирующийся", возможно, не самый удачный, но предполагает обычно именно это.
AVI-crak
Не предполагает.
Любая перетасовка программного кода - есть алгоритм. Ничего сложного, местами даже интересно, в плюсах вообще всё прозрачно получается.
Самомодифицирующийся код - это когда цель не перемещение, а изменение программного кода. Добавление, удаление, модификация известного кода в зависимости от заложенного в него алгоритма. То-есть там появляются команды и данные которых не было в проекте изначально.
aaarrr
Давайте сначала:

Цитата(AVI-crak @ Jun 29 2018, 02:23) *
Всё дело в том что сама функция изменения физического и логического поведения кода на кортексах работает из коробки.

О какой магической функции речь? И что есть физическое и логическое поведение кода?

Цитата(AVI-crak @ Jun 29 2018, 02:23) *
Создавая собственный свод правил для определений поведения кода в режиме реального времени исполнения...

Свод правил для определений поведения - это как?

Цитата(AVI-crak @ Jun 29 2018, 03:11) *
Любая перетасовка программного кода - есть алгоритм. Ничего сложного, местами даже интересно, в плюсах вообще всё прозрачно получается.

Плюса-то здесь каким боком?
Николай Семёнович
Цитата(AVI-crak @ Jun 29 2018, 02:23) *
Начнём с того что для успешной компиляции такого кода - необходимы пляски с бубном.

Это ещё почему?
Компилятор вообще по барабану: он не знает, что за программу (какого назначения) он компилит.


Цитата(aaarrr @ Jun 29 2018, 02:31) *
Ужасы какие. ТС вроде как собирался генерировать часть кода программно с нуля, а не строить новую программу из частей исходной. Термин "самомодифицирующийся", возможно, не самый удачный, но предполагает обычно именно это.

Да банально заменить во флеше JMP +10 на JMP +20 в процедуре - вот уже самомодифицирующийся код.
Делал так.

Зачем так делать? Почему бы не сделать команду условного перехода, которая будет делать переход в зависимости от некоего доп. флажка?

Из экономии тактов и битов
halfdoom
Цитата(jcxz @ Jun 29 2018, 01:54) *
Есть мысль использовать самомодифицирующийся код для оптимизации решения одной задачи.


Никто не запрещает. Если хорошо владеете темой, то единственные грабли - это кэш.

Цитата(jcxz @ Jun 29 2018, 01:54) *
И вот я что-то не смог вспомнить чтобы здесь на форуме вообще когда-то поднималась эта тема.


Нынче памяти много, проще вызывать готовые оптимизированные куски.
jcxz
Цитата(AVI-crak @ Jun 29 2018, 02:23) *
Начнём с того что для успешной компиляции такого кода - необходимы пляски с бубном.

Какая компиляция? Я же написал: код будет строить моя программа (на этом же Cortex-M). Т.е. - она сразу будет создавать функцию в машинных кодах (если так понятнее). Которая после будет вызываться.

Цитата(AVI-crak @ Jun 29 2018, 02:23) *
На Си, Си++, и выше - задать точную последовательность применения мнемоник ядра практически невозможно.

Не понял - в чём сложность? Алгоритм создания кода вполне себе тривиальный.

Цитата(AVI-crak @ Jun 29 2018, 03:11) *
Добавление, удаление, модификация известного кода в зависимости от заложенного в него алгоритма. То-есть там появляются команды и данные которых не было в проекте изначально.

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

Цитата(Николай Семёнович @ Jun 29 2018, 06:22) *
Из экономии тактов и битов

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

Цитата(halfdoom @ Jun 29 2018, 06:29) *
Никто не запрещает. Если хорошо владеете темой, то единственные грабли - это кэш.

Кеш мы победим. rolleyes.gif

Цитата(halfdoom @ Jun 29 2018, 06:29) *
Нынче памяти много, проще вызывать готовые оптимизированные куски.

Тогда таких кусков придётся написать 2^64 (это не операция XOR). smile3009.gif Я же говорю - классическими методами не получается придумать оптимального решения.
AVI-crak
Цитата(aaarrr @ Jun 29 2018, 06:32) *
О какой магической функции речь? И что есть физическое и логическое поведение кода?

Cortex, да и почти все arm чипы - используют внеочередное исполнение кода. Он может загружать адреса для двух функций одновременно. Просчитывать данные которые будут применяться где-то внутри функции до её реального вызова. Размазывать циклы на всю портянку. Обращать переменные в другое значение чем было назначено в пользовательской программе -без потери целостности алгоритма, и ещё много много чего. Это их главная фишка.
Фактически код в отладчике выполняется по строкам программы, а в реальности прыгает как бешеный сайгак.

Попытка воткнуть модификацию кода без предварительной изоляции всего куска - обычно приводит быстрому и окончательному сбою.

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

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







Цитата(Николай Семёнович @ Jun 29 2018, 09:22) *
Компилятор вообще по барабану: он не знает, что за программу (какого назначения) он компилит.

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


Цитата(jcxz @ Jun 29 2018, 10:20) *
Какая компиляция? Я же написал: код будет строить моя программа (на этом же Cortex-M). Т.е. - она сразу будет создавать функцию в машинных кодах (если так понятнее). Которая после будет вызываться.


Кхм, это наверное слишком глупо, но всё-же.
Флешка - это именно та флешка что вставляется в разъём компьютера?
jcxz
Цитата(AVI-crak @ Jun 29 2018, 07:36) *
Cortex, да и почти все arm чипы - используют внеочередное исполнение кода. Он может загружать адреса для двух функций одновременно. Просчитывать данные которые будут применяться где-то внутри функции до её реального вызова. Размазывать циклы на всю портянку. Обращать переменные в другое значение чем было назначено в пользовательской программе -без потери целостности алгоритма, и ещё много много чего. Это их главная фишка.

Тем не менее - количество чтений/записей из/в память которые нужно выполнить всё равно не уменьшится, менее чем требующееся по алгоритму, при любом оптимизаторе.
А перевод на самомодифицирующийся код сокращает это количество многократно.
Уж что-что, а ассемблер для Cortex-M я знаю неплохо. И пока не сделали такой оптимизатор, который напишет код лучше меня. rolleyes.gif

Цитата(AVI-crak @ Jun 29 2018, 07:47) *
Флешка - это именно та флешка что вставляется в разъём компьютера?

Вы о чём? wacko.gif
AlexandrY
Цитата(jcxz @ Jun 29 2018, 01:54) *
PS: И пожалуйста - без религиозной нетерпимости.

Обещать не могу. biggrin.gif

А загрузчики считаются самомодифицирующимся кодом?
Или самомодифицирующийся - это код который вот именно сам себя модифицирует, т.е. инструкция STR вот прямо по своему адресу что-то и пишет и сама себя таким образом затирает.
jcxz
Цитата(AlexandrY @ Jun 29 2018, 08:47) *
Или самомодифицирующийся - это код который вот именно сам себя модифицирует, т.е. инструкция STR вот прямо по своему адресу что-то и пишет и сама себя таким образом затирает.

Я уже выше написал: будет функция, которая по некоторым условиям пишет другую функцию. По командам. В маш.кодах. Команды записываемые в тело создаваемой функции (и их последовательность) зависят от аргументов первой функции.

PS: Вот уже сколько откликов получил, а на собственно единственный вопрос, поставленный в первом сообщении, так никто и не ответил. И видимо забыли все в чём был вопрос..... sad.gif
Вопрос был собственно в том: использовал кто-нить из здесь присутствующих самомодифицирующийся код (именно такой код, который или сам своё тело правит или строит другой код (а не просто копирует из одного места в другое))? Именно на платформе ARM в embedded?
И, если использовали, то для какой задачи?
AlexandrY
Цитата(jcxz @ Jun 29 2018, 09:56) *
Я уже выше написал: будет функция, которая по некоторым условиям пишет другую функцию. По командам. В маш.кодах. Команды записываемые в тело создаваемой функции (и их последовательность) зависят от аргументов первой функции.

Тогда я не согласен с названием "самомодифицирующийся", у меня загрузчики тоже загружают разные не то что функции, а целые программы(и да в машинных кодах, разумеется) в зависимости от аргументов.
jcxz
Цитата(AlexandrY @ Jun 29 2018, 10:00) *
Тогда я не согласен с названием "самомодифицирующийся", у меня загрузчики тоже загружают разные не то что функции, а целые программы(и да в машинных кодах, разумеется) в зависимости от аргументов.

Ну хорошо - придумайте своё название. rolleyes.gif Не в терминах суть.
AlexandrY
Цитата(jcxz @ Jun 29 2018, 10:03) *
Ну хорошо - придумайте своё название. rolleyes.gif Не в терминах суть.

Динамически генерируемый код - вот так будет лучше, на мой взгляд.
Отладка такого кода будет адской.

Нет, я лучше возьму i.MX RT и закрою тему одним махом.

Кста, кто придумает для RT лучшую аппликацию получит плату с RT бесплатно.
Может там ваша динамика и прокатит. biggrin.gif
aaarrr
Цитата(AVI-crak @ Jun 29 2018, 07:47) *
Cortex, да и почти все arm чипы - используют внеочередное исполнение кода. Он может загружать адреса для двух функций одновременно. Просчитывать данные которые будут применяться где-то внутри функции до её реального вызова. Размазывать циклы на всю портянку. Обращать переменные в другое значение чем было назначено в пользовательской программе -без потери целостности алгоритма, и ещё много много чего. Это их главная фишка.
Фактически код в отладчике выполняется по строкам программы, а в реальности прыгает как бешеный сайгак.

Вообще-то тема про Cortex-M, а не старшие Cortex-A. И даже на последних это нисколько не мешает писать самомодифицирующийся код: сбросили кэш, установили барьеры - и вперед.
Serge V Iz
Использовал хранимые в ОЗУ участки кода, которые загружались извне в виде обезличенных произвольных массивов данных в ходе работы системы, а в местах обращений к ним трактовались как подпрограммы с заранее известным адресом точки входа. А что ему может помешать так работать?
AVR
Цитата(jcxz @ Jun 29 2018, 01:54) *
Есть мысль использовать самомодифицирующийся код для оптимизации решения одной задачи.

Оптимизация по скорости? А то я хотел microPython предложить, для best-in-class супер простой самомодификации кода.
RadiatoR
Вопрос к ТС - а каков предполагаемый размер генерируемой функции? Может имеет смысл просто в RAM зарезервировать буфер и в нем генерировать код. Если функция генератор уже создана, то можно смело пробовать...
_4afc_
Мой опыт показывает что оптимизация по скорости самомодифицирующимся кодом неэффективна.
Потому что очевидно, что такой код будет находится в ОЗУ.
А в Cortex-M3,M4 у меня код быстрее выполнялся из flash тк у Atmel там 128 битная шина доступа на частоте более 25МГц...
Т.е. даже при наличии кеша данные или код из flash обрабатывались быстрее чем из озу.
А поскольку флеша пихают теперь в кристалы просто дикое количество - то необходимости что-то копировать в ОЗУ нет, разве что для снижения потребления.

Что касается ускорения - проще расписать циклы.

Есть ещё TDM для ускорения и на старых ARM - режим FIQ со своим набором регистров.

Единственный оправданный вариант применения самогенерирующегося кода - некий софтпроцессор.
Т.е. программа в МК зашита давно, но иногда удалённо в него прилетает новая функция и он её выполняет.
VladislavS
_4afc_, не стоит быть столь категоричным. Если говорить в общем, а задачу ТС поставил именно "а поговорить", то этих кортексов в природе хоть попой кушай и у всех разные скоростные характеристики блоков памяти. Рассуждать где что разместить можно только после того как будет произнесено вслух название чипа.
jcxz
Цитата(Serge V Iz @ Jun 29 2018, 10:35) *
Использовал хранимые в ОЗУ участки кода, которые загружались извне в виде обезличенных произвольных массивов данных в ходе работы системы, а в местах обращений к ним трактовались как подпрограммы с заранее известным адресом точки входа. А что ему может помешать так работать?

Это называется "оверлеи". Пользовал такое на LPC4370, в котором нет флешь, ОЗУ не так уж много, и были оверлеи (редко выполняемые), подгружаемые из внешней SPI-флешь в один и тот же буфер в ОЗУ.
Тут несколько иная задача. AlexandrY уже придумал более точное название: "Динамически генерируемый код".

Цитата(AVR @ Jun 29 2018, 10:43) *
Оптимизация по скорости? А то я хотел microPython предложить, для best-in-class супер простой самомодификации кода.

По скорости. Ну если у меня оптимизация на таком уровне (генерация маш.кода), то это совсем другой порядок скоростей, чем смогут всякие явы и питоны laughing.gif
Kabdim
Имхо сейчас время такое что проще взять более мощный чип. Вообще полиморфы родились как способ обдурить антивирус. Разработчики, которым нужна подобная оптимизация (без криминала) обычно делают N *.dll'ек, которые загружаются исходя и потребностей.
Так что на мой взгляд толстый проц с большим кол-вом флеша и с помощью темплейтов скомпилировать все комбинации кода.
Впрочем подозреваю что идея о самопишущемся коде это скорее спортивный интерес. В этом случае надо просто делать и получать удовольствие.
jcxz
Цитата(RadiatoR @ Jun 29 2018, 10:54) *
Вопрос к ТС - а каков предполагаемый размер генерируемой функции? Может имеет смысл просто в RAM зарезервировать буфер и в нем генерировать код. Если функция генератор уже создана, то можно смело пробовать...

Так и есть. Размер функции переменный, в зависимости от заданных входных аргументов: от одной команды BX LR (пустой список) до примерно полусотни команд.
Генератор сейчас в процессе создания cool.gif

Цитата(_4afc_ @ Jun 29 2018, 11:23) *
А в Cortex-M3,M4 у меня код быстрее выполнялся из flash тк у Atmel там 128 битная шина доступа на частоте более 25МГц...

А теперь возьмите калькулятор в руки и посчитайте: 128/8*25=400МБ/сек; т.е. - если CPU работает на хотя-бы 100МГц тактовой, то 4-байтовые чтения из 0-wait-state ОЗУ уже дадут такую скорость. Так что на частотах >100МГц выполнение из ОЗУ у Вас должно быть быстрее. Конечно тут нужно ещё учесть влияние кеша FLASH и пересечение prefetch-ей с обращениям за данными по этой же шине (можно вынести такой код в отдельный регион SRAM с отдельной шиной для скорости).

Но у меня МК с шиной к FLASH программ == 256 бит, кешем FLASH и тактовой 144МГц. Но зато есть регион PSRAM (program SRAM) с отдельной шиной к нему от ядра. И существенной разницы в скорости выполнения между FLASH и PSRAM я не замечаю.

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

Цитата(_4afc_ @ Jun 29 2018, 11:23) *
Т.е. даже при наличии кеша данные или код из flash обрабатывались быстрее чем из озу.
А поскольку флеша пихают теперь в кристалы просто дикое количество - то необходимости что-то копировать в ОЗУ нет, разве что для снижения потребления.

Ещё раз внимательнее перечитайте мои посты: я уже много раз написал, что говорю не о копировании заранее подготовленных кусков (оверлеев) в ОЗУ, а о динамической генерации кода.

Цитата(VladislavS @ Jun 29 2018, 11:38) *
Рассуждать где что разместить можно только после того как будет произнесено вслух название чипа.

XMC4700 rolleyes.gif
_4afc_
Цитата(jcxz @ Jun 29 2018, 13:08) *
А теперь возьмите калькулятор в руки и посчитайте: 128/8*25=400МБ/сек; т.е. - если CPU работает на хотя-бы 100МГц тактовой, то 4-байтовые чтения из 0-wait-state ОЗУ уже дадут такую скорость. Так что на частотах >100МГц выполнение из ОЗУ у Вас должно быть быстрее. Конечно тут нужно ещё учесть влияние кеша FLASH и пересечение prefetch-ей с обращениям за данными по этой же шине (можно вынести такой код в отдельный регион SRAM с отдельной шиной для скорости).

Но у меня МК с шиной к FLASH программ == 256 бит, кешем FLASH и тактовой 144МГц. Но зато есть регион PSRAM (program SRAM) с отдельной шиной к нему от ядра. И существенной разницы в скорости выполнения между FLASH и PSRAM я не замечаю.

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

У меня для ускорения, часть алгоритма была заранее посчитана и использовались таблицы поиска (LUT).
Так вот, наибольшей скорости я добился при нахождении LUT во FLASH, а кода в ОЗУ в виде расписанных циклов.

В микроконтроллерах Атмела wait-state устанавливается на всю систему памяти и на FLASH и на ОЗУ (хотя теперь про это не пишут)
Поэтому для того чтобы выполнять программу с 0-wait-state из ОЗУ - надо сгенерить весь исполняемый код в ОЗУ и
на 100МГц во флеш даже носа не показывать, даже ради таблицы векторов.

При этом у Атмела невозможно налету поменять коэффициент деления тактовой, любое изменение частоты только через перезапуск PLL который ожидаеш на медленных RC генераторах. B вообще PLL настолько кривой - что его лучше не трогать. PSRAM - наверно выход, но PSRAM и MMU както бедно документированы.

Вероятно генерация (даже распаковка из внешней FLASH), при отключенной внутренней, в небольшое PSRAM на максимальной частоте кусков кода при 0-wait-state - это хорошая экономия ОЗУ под буфера данных...
Arlleex
Интересная тема для меня, тема СМК. Ни разу не применял, однако небольшие размышления в этой части заставили признать, что в практике программирование были моменты, где удобно было бы иметь самомодификацию. Приведу пример, где бы я использовал СМК (опять же, в реальной практике была уйма таких задач).
Пусть есть некий цикл, но внутри этого цикла подряд идут условно выполняемые команды:
Код
while(условие)
{
    if(условие 1)
        инструкция 1;

    if(условие 2)
        инструкция 2;

    if(условие 3)
        инструкция 3;

    ...

    if(условие N)
        инструкция N;
}

Так вот, условие 1, условие 2, условие 3, ..., условие N являются флаговыми переменными и определяются где-то выше по коду. В итоге ассемблерный код будет содержать условные операции выполнения и переходы, если инструкции не представляются в машинных кодах до 4 реальных инструкций для Cortex-M. Это долго, на это тратятся такты процессора, сбрасывается конвейер инструкций... в общем, одни минусы. В приложениях с хитрой математикой или логикой, которую надо быстро просчитать, было бы удобно использовать СМК в следующем виде:
1. Просчитать все условные флаги.
2. На основании этих флагов сформировать СМК-последовательность инструкций без условных инструкций.
3. Отдать эту последовательность на выполнение.

В итоге существенно сократится объем ассемблерных инструкций, с учетом того, что некоторые условные инструкции идут по 2-3 такта, а после модификации останутся только "нужные" инструкции. Останется только условие продолжения цикла и нужные команды, без этих паршивых проверок. Я ж, надеюсь, никому святую тайну не открыл? biggrin.gif Повторюсь, это лишь мои хотелки, было много ситуаций где хотелось бы сейчас это реализовать, но оно работает да и зачем туда лезть, тем более там, где во времени я сильно не ограничен...
RadiatoR
Цитата(Arlleex @ Jun 29 2018, 16:08) *
2. На основании этих флагов сформировать СМК-последовательность инструкций без условных инструкций.

Так тут будет тот же
if()...if()...if()...
a123-flex
Цитата(jcxz @ Jun 29 2018, 01:54) *
Есть мысль использовать самомодифицирующийся код для оптимизации решения одной задачи.
Именно самомодифицирующийся - программа строит часть самой себя по некоему алгоритму, а не просто копирует куски кода из одной области памяти в другую.
И вот я что-то не смог вспомнить чтобы здесь на форуме вообще когда-то поднималась эта тема.
Кто-то вообще использует такой код на ARM-ах в своих проектах? Просто интересно... rolleyes.gif

Может Вам будет проще в контроллере python поднять ? Или любой другой интерпретатор...
twix
Цитата(a123-flex @ Jun 29 2018, 14:27) *
Может Вам будет проще в контроллере python поднять ? Или любой другой интерпретатор...

действительно, интерпретатор намного эффективнее и проще в реализации.
a123-flex
Цитата(AVR @ Jun 29 2018, 10:43) *
Оптимизация по скорости? А то я хотел microPython предложить, для best-in-class супер простой самомодификации кода.

уже предложили. только заметил.

Цитата(jcxz @ Jun 29 2018, 11:43) *
По скорости. Ну если у меня оптимизация на таком уровне (генерация маш.кода), то это совсем другой порядок скоростей, чем смогут всякие явы и питоны laughing.gif

мда. присоединюсь про другое железо. Явно Allwinner просится
Николай Семёнович
Цитата(AlexandrY @ Jun 29 2018, 10:00) *
Тогда я не согласен с названием "самомодифицирующийся", у меня загрузчики тоже загружают разные не то что функции, а целые программы(и да в машинных кодах, разумеется) в зависимости от аргументов.

Попытаюсь объяснить (как я это понял), что подразумевать под "самомодифицирующимся кодом" топикстартер.

Не.

Не буду. Боюсь всё равно не поймёте
А разжигать холивар не хочу

Впрочем, скажу кое-что.
Ваш загрузчик просто подгружает целые законченные куски кода, а у топикстартера программа меняет отдельные команды своего собственного кода
adnega
Цитата(Arlleex @ Jun 29 2018, 16:08) *
1. Просчитать все условные флаги.
2. На основании этих флагов сформировать СМК-последовательность инструкций без условных инструкций.
3. Отдать эту последовательность на выполнение.

Простите, а пункты 1 и 2 не требуют тактов разве?
Serge V Iz
Кстати, а какие есть основания предполагать, что руками сгенерированный код будет лучше? Можно сэкономить на знании внешне заданных ограничений на входные данные, не выражаемых стандартными типами общеупотребительных ЯВУ? Специфический характер ветвлений, такой, что ручное управление выборкой инструкций (порядком размещения в памяти) значительно повышает эффективность выполнения? А то современные трансляторы своей железной не устающей и не ошибающейся головой как раз громоздкие конструкции оптимизируют, как правило, лучше человека...
Arlleex
Цитата(RadiatoR @ Jun 29 2018, 16:23) *
Так тут будет тот же
if()...if()...if()...

Нет, не будет.

Цитата(adnega @ Jun 29 2018, 19:03) *
Простите, а пункты 1 и 2 не требуют тактов разве?

Возможно, Вы и коллега выше не совсем поняли что я имею в виду. Смотрите. Рассмотрим первый случай - когда в цикле непосредственные команды проверки условия
Код
while(условие)
{
    if(условие 1)
        инструкция 1;
    if(условие 2)
        инструкция 2;
    if(условие 3)
        инструкция 3;
}

Как видно, будут всегда выполняться инструкции, реализующие условия проверки внутри if(...).
Если применить СМК, и вычислить условия в if(...)-ах до цикла, то в ОЗУ построится, допустим, следующий код (примем, что условия 1 и 3 истинны)
Код
while(условие)
{
    инструкция 1;
    инструкция 3;
}

Или же, если все условия истинны, то
Код
while(условие)
{
    инструкция 1;
    инструкция 2;
    инструкция 3;
}

Или же, если первое условие истинно, то
Код
while(условие)
{
    инструкция 1;
}

Как видно, в любом случае нет больше в цикле инструкций проверки условий в if(...)-ах (а они же ведь по сути Bxx в ассемблерном представлении) (да и куда лучше вовсе не проверять заранее ложные условия по списку), а выполняются только нужные инструкции сразу, как будто условных конструкций и нет. А п. 1 и 2, конечно, требуют тактов, только для моего случая п. 1. требуется выполнить только 1 раз для всех итераций цикла, на этапе составления списка инструкций СМК.
Надеюсь, моя мысль будет так понятнее.
adnega
Цитата(Arlleex @ Jun 29 2018, 19:18) *
Надеюсь, моя мысль будет так понятнее.

Условия в рантайме меняются или нет?
Если нет, то компилятор сам выкинет заведомо ложные и исполнит заведомо истинные условия.
А если они меняются в рантайме, то и ваш генератор СМК должен все эти условия проверить,
сгенерить год и исполнить его.
По моему, даже если вы выиграете на исполнении СМК, то скорее всего проиграете на подготовке СМК.
jcxz
Цитата(Arlleex @ Jun 29 2018, 16:08) *
Приведу пример, где бы я использовал СМК (опять же, в реальной практике была уйма таких задач).
Пусть есть некий цикл, но внутри этого цикла подряд идут условно выполняемые команды:
Код
while(условие)
{
    if(условие 1)
        инструкция 1;

    if(условие 2)
        инструкция 2;

    if(условие 3)
        инструкция 3;

    ...

    if(условие N)
        инструкция N;
}

Потрясающе!!!! Вы угадали прямо в точку! Именно такой алгоритм я и хочу переделать на СМК! Потрясающая проницательность! 08.gif

Конкретней:
у меня сейчас есть участок кода примерно такой:
char *s;
if (map & 1 << 0) копирование 16 или 32 бит в *s с его постинкрементом на соотв. величину из источника, адрес которого берётся из таблицы для данной битовой позиции карты
if (map & 1 << 1) копирование 16 или 32 бит в *s с его постинкрементом на соотв. величину из источника, адрес которого берётся из таблицы для данной битовой позиции карты
if (map & 1 << 2) копирование 16 или 32 бит в *s с его постинкрементом на соотв. величину из источника, адрес которого берётся из таблицы для данной битовой позиции карты
...
if (map & 1 << 11) копирование 16 или 32 бит в *s с его постинкрементом на соотв. величину из источника, адрес которого берётся из таблицы для данной битовой позиции карты
всего 12 таких операторов.
Вобщем-то 12 таких шагов не напрягают. Но! - есть желание увеличить размер битовой карты до 64 бит, соответственно и кол-во if-ов будет уже очень большим.
А всё это вызывается с довольно неплохой частотой из ISR.
В то же время одновременно установленными может быть не более некоторого числа бит в карте (примерно около 10 максимум). Но всё равно сколько бит установлено в карте - всё равно будет выполняться вся эта портянка кода - это самое неприятное. Т.е. - даже когда эта функция выключена (битовая карта map==0), то процессор всё равно будет тратить кучу времени на выполнение всех этих 64 шагов. При статическом построении алгоритма.
Вот тут то и пришла в голову идея при каждом изменении map строить динамически код. map изменяется очень редко - не чаще раза в секунду.

PS: Вобщем генератор уже написан, осталось отладить sm.gif

Цитата(twix @ Jun 29 2018, 17:35) *
действительно, интерпретатор намного эффективнее и проще в реализации.

Уже в N-раз повторяю: РАЗГОВОР О СКОРОСТИ ВЫПОЛНЕНИЯ КОДА!!! Причём тут интерпретатор???

Цитата(a123-flex @ Jun 29 2018, 18:05) *
мда. присоединюсь про другое железо. Явно Allwinner просится

Блин! причём тут allwinner, если у меня МК заточенный своей периферией под конкретную задачу - motor control. И куча периферии задействована. Эта функция - лишь небольшая сервисная часть.
Вы этот allwiner вначале хотя-бы автомотивный найдите. не говоря уже о куче другой специфичной периферии....
Arlleex
Цитата(adnega @ Jun 29 2018, 19:35) *
Условия в рантайме меняются или нет?

Конечно меняются. Это обычные переменные, которые взводятся/сбрасываются где-то выше по коду или вовсе в других его частях.
Цитата(adnega @ Jun 29 2018, 19:35) *
А если они меняются в рантайме, то и ваш генератор СМК должен все эти условия проверить, сгенерить год и исполнить его.

Так выигрыш то огромен, если список таких условных ветвей достаточно большой. Даже из 10 условных команд, в случае, если эти команды нужно выполнить все (худший случай с точки зрения затраченного времени выполнения), понадобится 20 тактов (при условии, что все команды однотактные и условно выполняемая инструкция одна) без СМК. А применив СМК - цикл сократится до 10 тактов (без накладных расходов на проверку условия цикла). В лучшем случае, если только 1 условие верно, нужно выполнить только 1 инструкцию вместо 20 (10 связок "команда сравнения - команда самого условно выполняемого действия") выигрыш в 20 раз! Это ли не здорово?
Цитата(adnega @ Jun 29 2018, 19:35) *
По моему, даже если вы выиграете на исполнении СМК, то скорее всего проиграете на подготовке СМК.

Достаточно написать перед этим интересующие участки на ассемблере, скомпилировать их и видеть в бинарнике готовые инструкции в виде машинных слов. Easy же rolleyes.gif
Цитата
Потрясающе!!!! Вы угадали прямо в точку! Именно такой алгоритм я и хочу переделать на СМК! Потрясающая проницательность!

Я просто телепат cool.gif
jcxz
Цитата(Николай Семёнович @ Jun 29 2018, 19:00) *
Ваш загрузчик просто подгружает целые законченные куски кода, а у топикстартера программа меняет отдельные команды своего собственного кода

Вот именно. Даже полностью строит процедуру. Вот такой примерно должен быть результат (код) работы генератора:
CODE
;Назначение регистров в динамически генерируемой функции.
dstReg EQU c_R0 ;указатель на целевой буфер для записи (вх.аргумент)
tblReg EQU c_R1 ;указатель на таблицу адресов переменных (вх.аргумент)
tmpReg0 EQU c_R2 ;рабочий регистр 0
tmpReg1 EQU c_R3 ;рабочий регистр 1
;сама функция
;f0 LDR tmpReg0, [tblReg, #bit1*4]; где bit1 - номер первого единичного бита в мл.слове бит-карты
;f0 LDR tmpReg1, [tblReg, #bit2*4]; где bit2 - номер второго единичного бита в мл.слове бит-карты
;f1 LDR/LDRH tmpReg0, [tmpReg0]
;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH
;f0 LDR tmpReg0, [tblReg, #bit3*4]; где bit3 - номер 3-го единичного бита в мл.слове бит-карты
;f1 LDR/LDRH tmpReg1, [tmpReg1]
;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH
;f0 LDR tmpReg1, [tblReg, #bit4*4]; где bit4 - номер 4-го единичного бита в мл.слове бит-карты
;f1 LDR/LDRH tmpReg0, [tmpReg0]
;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH
; ...
;f3 ADDS tblReg, tblReg, #32*4; вставляется при переходе к ст.слову бит-карты если в нём есть единицы
;f0 LDR tmpReg0, [tblReg, #bit1*4]; где bit1 - номер 1-го единичного бита в ст.слове бит-карты
;f1 LDR/LDRH tmpReg1, [tmpReg1]
;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH
;f0 LDR tmpReg1, [tblReg, #bit2*4]; где bit2 - номер 2-го единичного бита в ст.слове бит-карты
;f1 LDR/LDRH tmpReg0, [tmpReg0]
;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH
; ...
;f1 LDR/LDRH tmpReg1, [tmpReg1]
;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH
;f4 BX LR

LDR или LDRH использовать - определяется весом бита, заданным в таблице. Генератор формирует код глядя в эту таблицу.

Цитата(Arlleex @ Jun 29 2018, 19:18) *
Как видно, в любом случае нет больше в цикле инструкций проверки условий в if(...)-ах (а они же ведь по сути Bxx в ассемблерном представлении) (да и куда лучше вовсе не проверять заранее ложные условия по списку), а выполняются только нужные инструкции сразу, как будто условных конструкций и нет. А п. 1 и 2, конечно, требуют тактов, только для моего случая п. 1. требуется выполнить только 1 раз для всех итераций цикла, на этапе составления списка инструкций СМК.
Надеюсь, моя мысль будет так понятнее.

Блин, коллега, позвольте пожать Вашу руку! Вы - похоже почти единственный человек на этом форуме сходу понявший все нюансы!!! rolleyes.gif

Цитата(Arlleex @ Jun 29 2018, 19:56) *
Я просто телепат cool.gif

Как будто заглянули в мои исходники! cool.gif
Arlleex
Цитата(jcxz @ Jun 29 2018, 20:11) *
Блин, коллега, позвольте пожать Вашу руку! Вы - похоже почти единственный человек на этом форуме сходу понявший все нюансы!!! rolleyes.gif

О результатах сообщите, пожалуйста, удалось/не удалось внедрить, буду держать на всякий случай идею такую. Я довольно плотно поработал с битовой графикой на LCD/TFT дисплеях и организовывал меню разной степени вложенности для пользователей устройств с тачскрином/кнопками и экраном. Там таких ситуаций, где флажки надо опрашивать и определять отрисовки, да на каждом шагу по 50 biggrin.gif
jcxz
Цитата(Arlleex @ Jun 29 2018, 20:18) *
О результатах сообщите, пожалуйста, удалось/не удалось внедрить, буду держать на всякий случай идею такую. Я довольно плотно поработал с битовой графикой на LCD/TFT дисплеях и организовывал меню разной степени вложенности для пользователей устройств с тачскрином/кнопками и экраном. Там таких ситуаций, где флажки надо опрашивать и определять отрисовки, да на каждом шагу по 50 biggrin.gif

Ок. Генератор написал, но отлаживать буду завтра - поздно уже. Как заработает - напишу.
У меня этот код предназначен для записи текущих значений переменных в поток. Карта выбранных для записи переменных приходит по интерфейсу связи и может меняться в процессе работы иногда, не прерывая процесса. Всего в программе много десятков таких переменных, которые хотелось бы писать, но писать все сразу - не хватит пропускной способности канала.
Переменные пока только 16- и 32-битные (целочисленные и float). Каждая битовая позиция в карте закреплена жёстко за какой-то переменной. И есть таблица указателей на эти переменные.
И это только - сервисная задача. Её выполнение не должно мешать выполнению основных рабочих задач МК, которых достаточно много и тяжёлых. Но в процессе работы нужно следить за всеми этими переменными.
Вот примерно так.
Arlleex
Цитата(jcxz @ Jun 29 2018, 20:23) *
Всего в программе много десятков таких переменных, которые хотелось бы писать, но писать все сразу - не хватит пропускной способности канала.

Идея понятна, идея здравая, сам делаю постоянно такие связки в проектах на МК + CPU (в одной плате). В сервисных структурах обмена указываю, какие переменные в нужном ответном массиве ожидаю в следующей посылке. Идея такая же, только у меня там пока что индусский код (но небольшой), а не СМК.
Кстати, видел как еще СМК спасает именно заменой порождающего кода особенно в математических операциях, где цикличные действия зависят от знака некой величины: в таком случае инструкции add заменяется sub и копируется в ОЗУ на выполнение. Ну и куча сопутствующих примеров.
ИМХО, главное в порыве радости весь проект не перелопатить на СМК - проклянешь себя через год, взглянув на код smile3046.gif biggrin.gif
AlexandrY
Цитата(jcxz @ Jun 29 2018, 19:56) *
...
if (map & 1 << 11) копирование 16 или 32 бит в *s с его постинкрементом на соотв. величину из источника, адрес которого берётся из таблицы для данной битовой позиции карты
всего 12 таких операторов.

А чем DMA не подошёл?
Цитата(Arlleex @ Jun 29 2018, 20:36) *
ИМХО, главное в порыве радости весь проект не перелопатить на СМК - проклянешь себя через год, взглянув на код smile3046.gif biggrin.gif

А для меня лично загадка что вы называете словом СМК biggrin.gif
kolobok0
Цитата(Arlleex @ Jun 29 2018, 16:08) *
Интересная тема для меня, тема СМК. Ни разу не применял,...


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

(круглый)
Arlleex
Цитата(AlexandrY @ Jun 29 2018, 20:44) *
А для меня лично загадка что вы называете словом СМК biggrin.gif

СамоМодифицирующийся Код sm.gif

Цитата(kolobok0 @ Jun 29 2018, 20:46) *
Добро пожаловать в мир ОО восприятия... ну а аля виртуальные методы - делаются на любом языке(почти) на раз-два...тем более на сях...

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

Здорово, не так ли? cool.gif Ну а если серьезно, то я придерживаюсь принципа: нужно придерживаться максимально золотой границы между читабельностью кода и его функциональной смысловой нагрузкой. Уж выделить 0.0001% кода для СМК, особенно, если он там уж очень гладко ложится, есть комильфо.
adnega
Цитата(jcxz @ Jun 29 2018, 20:23) *
Как заработает - напишу.

Проверил в железе - работает (Cortex-M0).

CODE
#include "stdafx.h"
#include "conio.h"
#include "windows.h"

#define IS_ALIGN (1)
#define VAR_NUM (32)

//-----------------------------------------------------------------------------
// enum eVAR_TYPE
//-----------------------------------------------------------------------------
enum eVAR_TYPE
{
VAR_NONE = 0,
VAR_DWORD,
VAR_WORD,
VAR_BYTE,
};

//-----------------------------------------------------------------------------
// typedef struct sVAR
//-----------------------------------------------------------------------------
typedef struct sVAR
{
union
{
DWORD data_dw;
WORD data_w;
BYTE data_b;
};
enum eVAR_TYPE type;
} sVAR;

volatile sVAR var[VAR_NUM] =
{
{0x12345678, VAR_DWORD},
{0xABCD, VAR_WORD},
{0x11223344, VAR_DWORD},
{0x55, VAR_BYTE},
{0x00, VAR_BYTE},
{0x66, VAR_BYTE},
{0x77, VAR_BYTE},
{0x88, VAR_BYTE},
{0x99, VAR_BYTE},
{0xAA, VAR_BYTE},
{0xBB, VAR_BYTE},
{0xCC, VAR_BYTE},
{0xDD, VAR_BYTE},
{0xEE, VAR_BYTE},
{0xFF, VAR_BYTE},
{0x98765432, VAR_DWORD},
};

BYTE exec_ram[1024];

//-----------------------------------------------------------------------------
// void set_new_mask(const DWORD mask)
//-----------------------------------------------------------------------------
void set_new_mask(const DWORD mask)
{
int offset;
int last_var = 0;
int s_pos = 0;
int exec_ram_pos = 0;

printf("// r0 = *s, r1 = var\n");

// PUSH {r2, lr}
exec_ram[exec_ram_pos++] = 0x04;
exec_ram[exec_ram_pos++] = 0xB5;

printf("B5 04 - push {r2, pc}\n");

for(int i = 0; i < VAR_NUM; i++)
{
if(mask & (1 << i))
{
switch(var[i].type)
{
case VAR_DWORD:
printf("// DWORD[%d]\n", i);

offset = (i - last_var) * 8;
last_var = i;

while(offset > 128)
{
offset -= 128;
// ADD r1, #128
exec_ram[exec_ram_pos++] = 128;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 80 - add r1, #128\n");
}

if(offset)
{
// ADD r1, #offset
exec_ram[exec_ram_pos++] = offset;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 %02X - add r1, #%d\n", offset, offset);
}

if(IS_ALIGN && (s_pos & 1))
{
// byte align

// LDRB r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 0A - ldrb r2, [r1, #0]\n");

// STRB r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 02 - strb r2, [r0, #0]\n");

// LDRB r2, [r1, #1]
exec_ram[exec_ram_pos++] = 0x4A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 4A - ldrb r2, [r1, #1]\n");

// STRB r2, [r0, #1]
exec_ram[exec_ram_pos++] = 0x42;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 42 - strb r2, [r0, #1]\n");

// LDRB r2, [r1, #2]
exec_ram[exec_ram_pos++] = 0x8A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 8A - ldrb r2, [r1, #2]\n");

// STRB r2, [r0, #2]
exec_ram[exec_ram_pos++] = 0x82;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 82 - strb r2, [r0, #2]\n");

// LDRB r2, [r1, #3]
exec_ram[exec_ram_pos++] = 0xCA;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 CA - ldrb r2, [r1, #3]\n");

// STRB r2, [r0, #3]
exec_ram[exec_ram_pos++] = 0xC2;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 C2 - strb r2, [r0, #3]\n");
}
else if(IS_ALIGN && (s_pos & 2))
{
// word align

// LDRH r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x88;

printf("88 0A - ldrh r2, [r1, #0]\n");

// STRH r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x80;

printf("80 02 - strh r2, [r0, #0]\n");

// LDRH r2, [r1, #2]
exec_ram[exec_ram_pos++] = 0x4A;
exec_ram[exec_ram_pos++] = 0x88;

printf("88 4A - ldrh r2, [r1, #2]\n");

// STRH r2, [r0, #2]
exec_ram[exec_ram_pos++] = 0x42;
exec_ram[exec_ram_pos++] = 0x80;

printf("80 42 - strh r2, [r0, #2]\n");
}
else
{
// dword align

// LDR r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x68;

printf("68 0A - ldr r2, [r1, #0]\n");

// STR r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x60;

printf("60 02 - str r2, [r0, #0]\n");
}

// ADDS r0, #4
exec_ram[exec_ram_pos++] = 0x04;
exec_ram[exec_ram_pos++] = 0x30;

printf("30 04 - adds r0, #4\n");

s_pos += 4;
break;

case VAR_WORD:
printf("// WORD[%d]\n", i);

offset = (i - last_var) * 8;
last_var = i;

while(offset > 128)
{
offset -= 128;
// ADD r1, #128
exec_ram[exec_ram_pos++] = 128;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 80 - add r1, #128\n");
}

if(offset)
{
// ADD r1, #offset
exec_ram[exec_ram_pos++] = offset;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 %02X - add r1, #%d\n", offset, offset);
}

if(IS_ALIGN && (s_pos & 1))
{
// byte align

// LDRB r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 0A - ldrb r2, [r1, #0]\n");

// STRB r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 02 - strb r2, [r0, #0]\n");

// LDRB r2, [r1, #1]
exec_ram[exec_ram_pos++] = 0x4A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 4A - ldrb r2, [r1, #1]\n");

// STRB r2, [r0, #1]
exec_ram[exec_ram_pos++] = 0x42;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 42 - strb r2, [r0, #1]\n");
}
else
{
// word align

// LDRH r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x88;

printf("88 0A - ldrh r2, [r1, #0]\n");

// STRH r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x80;

printf("80 02 - strh r2, [r0, #0]\n");
}

// ADDS r0, #2
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x30;

printf("30 02 - adds r0, #2\n");

s_pos += 2;
break;

case VAR_BYTE:
printf("// BYTE[%d]\n", i);

offset = (i - last_var) * 8;
last_var = i;

while(offset > 128)
{
offset -= 128;
// ADD r1, #128
exec_ram[exec_ram_pos++] = 128;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 80 - add r1, #128\n\r");
}

if(offset)
{
// ADD r1, #offset
exec_ram[exec_ram_pos++] = offset;
exec_ram[exec_ram_pos++] = 0x31;

printf("31 %02X - add r1, #%d\n", offset, offset);
}

// LDRB r2, [r1, #0]
exec_ram[exec_ram_pos++] = 0x0A;
exec_ram[exec_ram_pos++] = 0x78;

printf("78 0A - ldrb r2, [r1, #0]\n");

// STRB r2, [r0, #0]
exec_ram[exec_ram_pos++] = 0x02;
exec_ram[exec_ram_pos++] = 0x70;

printf("70 02 - strb r2, [r0, #0]\n");

// ADDS r0, #1
exec_ram[exec_ram_pos++] = 0x01;
exec_ram[exec_ram_pos++] = 0x30;

printf("30 01 - adds r0, #1\n");

s_pos += 1;
break;
}
}
}
printf("// EXIT\n");

// POP {r2, pc}
exec_ram[exec_ram_pos++] = 0x04;
exec_ram[exec_ram_pos++] = 0xBD;

printf("BD 04 - pop {r2, pc}\n");

#if 0
printf("SMC:\n");
for(int i = 0; i < exec_ram_pos / 2; i++)
printf("%02X %02X\n", exec_ram[i * 2 + 1], exec_ram[i * 2 + 0]);
#endif
}

int _tmain(int argc, _TCHAR* argv[])
{
set_new_mask(0xc7);
_getch();
return 0;
}




СМК:
CODE
// r0 = *s, r1 = var
B5 04 - push {r2, pc}
// DWORD[0]
68 0A - ldr r2, [r1, #0]
60 02 - str r2, [r0, #0]
30 04 - adds r0, #4
// WORD[1]
31 08 - add r1, #8
88 0A - ldrh r2, [r1, #0]
80 02 - strh r2, [r0, #0]
30 02 - adds r0, #2
// DWORD[2]
31 08 - add r1, #8
88 0A - ldrh r2, [r1, #0]
80 02 - strh r2, [r0, #0]
88 4A - ldrh r2, [r1, #2]
80 42 - strh r2, [r0, #2]
30 04 - adds r0, #4
// BYTE[6]
31 20 - add r1, #32
78 0A - ldrb r2, [r1, #0]
70 02 - strb r2, [r0, #0]
30 01 - adds r0, #1
// BYTE[7]
31 08 - add r1, #8
78 0A - ldrb r2, [r1, #0]
70 02 - strb r2, [r0, #0]
30 01 - adds r0, #1
// EXIT
BD 04 - pop {r2, pc}


Результат на Cortex-M0
Цитата
RESULT[20000F44]: 78 56 34 12 CD AB 44 33 22 11 77 88
jcxz
Цитата(adnega @ Jun 30 2018, 00:21) *
Проверил в железе - работает (Cortex-M0).

мммм... неправильно laughing.gif
См. моё сообщение #39 - какой должен быть результат работы.
К тому же: a) const в аргументе set_new_mask() - лишнее; б) R2,LR в вашей функции - не нужно сохранять, см. соглашения вызова компилятора; в) даже по Вашему алгоритму копирования - очень неоптимально, количество команд в 2 раза больше, чем можно было бы (для Cortex-M4).
adnega
Цитата(jcxz @ Jun 30 2018, 03:22) *
мммм... неправильно

А что именно неправильно?
Есть таблица переменных состоящая из значений и типов.
Есть маска, где каждый бит связан с соответствующим элементом таблицы.
Есть функция-генератор, создающая в рантайме код, при выполнении которого
в выходной поток копируются данные из таблицы с учетом длины и заданной маски.
Для заданного var, RESULT (отработал СМК) такой ожидаете?
Если нет, то где ошибка?

Цитата(jcxz @ Jun 30 2018, 03:22) *
a) const в аргументе set_new_mask() - лишнее;

Ну, функция set_new_mask не должна менять mask, поэтому и const. Или я вас не понял.

Цитата(jcxz @ Jun 30 2018, 03:22) *
б) R2,LR в вашей функции - не нужно сохранять, см. соглашения вызова компилятора;

Вы же супер-скорость хотели. Выкидывайте push и pop - и вот вам готовый участок кода, дающий результат,
но не забудьте проинициализировать r0 и r1.
Если нужна именно как функция, то достаточно одного "bx lr" в конце.

Цитата(jcxz @ Jun 30 2018, 03:22) *
в) даже по Вашему алгоритму копирования - очень неоптимально, количество команд в 2 раза больше, чем можно было бы (для Cortex-M4).

Какие именно команды лишние? Можно IS_ALIGN обнулить, тогда генератор будет работать с невыровненными данными.
Попробую сегодня избавиться от лишних adds.
jcxz
Цитата(adnega @ Jun 30 2018, 09:41) *
А что именно неправильно?
Есть таблица переменных состоящая из значений и типов.

Вы же ассемблер понимаете? Тогда посмотрите на требуемый результат (код) в сообщении #39 и поймёте в чём разница.
Нет таблицы переменных, есть таблица указателей на переменные. Так как эти переменные разбросаны по всей программе и объединить их в одну область памяти (структуру) нельзя. Они находятся в разных обособленных службах программы, и там и должны оставаться. А адресация их идёт через таблицу указателей. Я писал об этом. И в примере результата это чётко видно.
Если си-исходник будет понятней, то:
CODE
enum {N = <максимум 64>};
enum {TYP_s16, TYP_u16, TYP_s32, TYP_u32, TYP_float, TYP_n};
struct {
u8 main:3;
u8 misc:5;
} const isTyp[N] = {{TYP_s32, ...}, {TYP_u16, ...}, ...};
u8 isTypSize[TYP_n] = {2, 2, 4, 4, 4};
char *psrc;
char *pdst;
if (map & 1 << 0) {
psrc = varPtrs[0];
if (isTypSize[isTyp[0].main]) == 4) {
*(u32 *)pdst = *(u32 *)psrc;
pdst += 4;
} else {
*(u16 *)pdst = *(u16 *)psrc;
pdst += 2;
}
}
if (map & 1 << 1) {
psrc = varPtrs[1];
if (isTypSize[isTyp[1].main]) == 4) {
*(u32 *)pdst = *(u32 *)psrc;
pdst += 4;
} else {
*(u16 *)pdst = *(u16 *)psrc;
pdst += 2;
}
}
//и так далее... так сейчас в си-исходнике сделано. Цель СМК - построить оптимальный код выкинув кучу ненужных операций из этого алгоритма по текущей карте map, до следующего её изменения.


Цитата(adnega @ Jun 30 2018, 09:41) *
Какие именно команды лишние? Можно IS_ALIGN обнулить, тогда генератор будет работать с невыровненными данными.
Попробую сегодня избавиться от лишних adds.

У меня Cortex-M4. Он поддерживает команды с пост- и пре- модификациями адреса. Например: LDR R0, [R1], #4 тогда ваш код сократится в 2 раза.
Да я уже вчера всё сделал. И даже больше. Мой генератор ещё и перемежение команд поддерживает для исключения штрафов между командами LDR. rolleyes.gif
И я его на асме написал. Хотя это просто из спортивного интереса. cool.gif
adnega
Цитата(jcxz @ Jun 30 2018, 10:21) *
Да я уже вчера всё сделал

Покажите пожалуйста выход генератора для случая DWORD, WORD, DWORD, BYTE, BYTE в виде asm-инструкций.
У меня с таблицей переменных и невыровненными данным получилось так
CODE
// r0 = *s, r1 = var
// DWORD[0]
68 0A - ldr r2, [r1, #0]
60 02 - str r2, [r0, #0]
// WORD[1]
89 0A - ldrh r2, [r1, #8]
80 82 - strh r2, [r0, #4]
// DWORD[2]
8A 0A - ldrh r2, [r1, #16]
80 C2 - strh r2, [r0, #6]
8A 4A - ldrh r2, [r1, #18]
81 02 - strh r2, [r0, #8]
// BYTE[6]
31 30 - add r1, #48
78 0A - ldrb r2, [r1, #0]
72 82 - strb r2, [r0, #10]
// BYTE[7]
7A 0A - ldrb r2, [r1, #8]
72 C2 - strb r2, [r0, #11]
30 0C - adds r0, #12
47 70 - bx lr


Кста, а есть у кого-нить справочник машинных кодов для Cortex-M всех семейств?
Не путать со справочником asm-команд, который найти не составляет труда.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.