Как стать автором
Обновить

Комментарии 26

Для камрадов, кому интересно программирование на x86 asm'e с нуля: www.programminggroundup.blogspot.fi/ Книга написана в ключе «Обучаемся программированию с ассемблером в качестве первого языка».
спасибо за ссылку, но там тоже «КМБ», правда применительно к posix/linux.

Ну вот, о чем и шла речь — обратите внимание на последнюю строку:

#Initialize GNOME libraries
pushl 12(%ebp)     #argv
pushl 8 (%ebp)     #argc
pushl $app_version
pushl $app_id
call  gnome_init
addl  $16, %esp   #recover the stack
Пост сумбурен и не понятен. Автор в отрыве не только от ассемблера на 15 лет, но и от программирования в целом, видимо. Улучшение ассемблера — это C (C++ — не то).
P.S. Написал вывод флага РФ в 26 байт, кто меньше?
Через 13h?
а не подскажете, какой будет размер программы, которая делает всего три шага, приведенных ниже?
— получает 2 вещественных числа из консоли (вводит пользователь);
— перемножает их;
— выводит результат в консоль;
Никаких проверок, форматирования, использования инлайн ассемблера и прочего.
Очень серьезно зависит от компилятора и режимов компиляции.
я бы добавил и «от возможностей линковщика». И это не смотря на то, что уже лет 20 как вещественная арифметика встроена в x86 серверные и десктопные процессоры
Source в студию. И ключи компиляции.
Вы можете объяснить, пожалуйста, зачем нужно программирование на ассемблере в современных десктопных или мобильных операционных системах? За исключением вырожденных случаев вроде глубоко системных платформеннозависимых вещей, кодеков и супербыстрых вычислений (хотя и здесь можно поспорить насчёт необходимости ассемблера), ну и, может быть, обучения?

Гораздо важнее скорость разработки, простота поддержки и портируемость приложений. И по всем этим параметрам ассемблер очень сильно проигрывает, особенно в том, что касается портируемости.

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

А что насчёт многопоточности? В языках высокого уровня очень много абстракций, облегачющих написание concurrent-кода, вроде акторов и STM. Лично мне довольно слабо представляется, как организовывать сложный многопоточный код в отсутствии нормальной высокоуровневой модели памяти. Акторы на ассемблере? Хм…

Ваше отношение к типизации тоже не очень понятно. Как мне кажется, вы смешиваете понятия типизации и валидации:
То есть очевидное нарушение системного подхода – как будто разработчики языков высокого уровня рассматривали свои системы без учета взаимодействия с внешним миром. В итоге, программируя на типизированном языке разработчик должен предсматривать все возможные виды «неправильных» входных данных, и искать способы обхода неопределенностей. И вот тут на сцену выходят монструозные системы поддержки регулярных выражений, обработки исключительных ситуаций, сигнатуры методов/процедур для разных типов значений и прочая прочая генерация костылей.

Что значит — предусматривать все возможные виды «неправильных» входных данных? Вы имеете в виду валидацию, то есть, проверку содержимого, вроде того, представляет ли строка адрес электронной почты? Но это понятие не тождественно типизации. Более того, наоборот, типизация помогает валидации данных, потому что типизация ограничивает множество значений конкретной переменной. Валидация данных необходима в любом языке, независимо от системы типов.

Я при этом не касаюсь разработки для встраиваемых устройств. Там использование ассемблера полностью оправдано. Но там и нет описанных вами «проблем», или, по крайней мере, они себя не проявляют.
> Гораздо важнее скорость разработки, простота поддержки и портируемость приложений.

Да, к сожалению красоту, изящество, быстроту и оптимизацию кода уже давно отменили… :( Программирование перестало быть искусством. :(
Поглядите код ядра linux, и поймёте что сииииильно ошибаетесь.
Вы можете объяснить, пожалуйста, зачем нужно программирование на ассемблере в современных десктопных или мобильных операционных системах?
Можно тот же самый вопрос задать про С++, VB, F#, Haskel. Да, С условно можно считать портируемым языком (хотя тот же Python, или Java «портируемы» еще как), пока дело не касается низкоуровнего программирования. Например нам понадобится что-то прочитать из порта ввода-вывода. Тут вся «портируемость» летит высоко и быстро. Или же Вы имеете ввиду, что просто вызов «библиотечной» функции решит проблемы? Так на чем писать эту самую библиотечную функцию? Если на С, то о какой портируемости идет речь, если переносите функционал с архитектуры, где фактически нет портов ввода выводы (они замаплены на адреса памяти) на архитектуру, где они есть?

Гораздо важнее скорость разработки, простота поддержки и портируемость приложений.
Ассемблер проигрывает в «портируемости» между архитектурами, но не между ОС. Что касается скорости разработки, так проигрышь получается по причинам, описанным выше — борьба со «специфическим» использованием архитектуры языками высокого уровня.

Также не очень понятен ваш скепсис в отношении компиляторов. Неужели вы считаете, что вы сами лучше будете знать, как именно и где лучше применить, скажем, какую-нибудь из инструкций расширения SSSE3?
Насчет SSSE. Там нет такого уж кошмара, вопрос лишь в подготовке данных. Сможете сами сделать — отлично, не сможете, нестрашно, есть, например, Фортран. А про «низкоуровневую модель памяти» я не понял. Что это?

Что значит — предусматривать все возможные виды «неправильных» входных данных? Вы имеете в виду валидацию, то есть, проверку содержимого, вроде того, представляет ли строка адрес электронной почты? Но это понятие не тождественно типизации. Более того, наоборот, типизация помогает валидации данных, потому что типизация ограничивает множество значений конкретной переменной. Валидация данных необходима в любом языке, независимо от системы типов.
Так типизация и решает задачу валидации данных. Чтобы быть уверенным, что вы умножаете вещественное число, а не строку. Но при этом, кроме удобства для компилятора, вносит дополнительные сложности как для программиста, так и для пользователя.
Как пример, у нас есть некий телефонный справочник. Номер телефон это что? Целое число или строка? При отсутствии типизации мы может обрабатывать его и как число (для поиска и сравнения так будет быстрее) и как строку — для хранения номера в том виде, в каком его ввел пользователь. Будь то +7-909-78956234 или же (909)78.956.2.34. Неважно, какой смысл в это вкладывал тот, кто его (номер) вводил в том или ином виде. В случае типизации мы вынуждены заранее ограничивать пользователя в формате вводимых данных, а потом еще и исправлять все это, если формат изменился, или же стали использоваться 2 разных формата для сотовых и стационарных телефонов.
Можно тот же самый вопрос задать про С++, VB, F#, Haskel.

Нет, нельзя. Все эти языки так или иначе позволяют писать кроссплатформенные программы с высокой скоростью разработки и достаточным уровнем корректности.
хотя тот же Python, или Java «портируемы» еще как

Не понимаю вашего сарказма. Python и Java являются кроссплатформенными языками, и программы на них именно что портируемы.
пока дело не касается низкоуровнего программирования

Заметьте, я про это сказал в своём комментарии. Если необходимо писать программу на таком низком уровне, что нужно учитывать способ работы с портами ввода-вывода, то ассемблер будет нужен. Хотя, на мой взгляд, здесь и C можно обойтись, потому что, скорее всего, под ту архитектуру, которая вам нужна, уже кто-нибудь написал соответствующую обёртку. Либо, если это не так и если понадобится работать с этой архитектурой долго, можно стать первопроходцем и что-то подобное написать самостоятельно.
Ассемблер проигрывает в «портируемости» между архитектурами, но не между ОС.

Вы хоть раз программировали на ассемблере под ОС, отличной от Windows? Я да. Разница в программировании на чистом ассемблере, без библиотек вроде libc, огромна. По Windows вам придётся вызывать функции Windows API. Под линуксом вам придётся дёргать системные вызовы с помощью int $0x80. Естественно, набор этих функций абсолютно разный. Работа с памятью тоже немного другая — форматы бинарников разные. Поэтому если вы хотите перенести программу из одной ОС в другую, даже если они работают на одной архитектуре, вам придётся её переписывать.
Насчет SSSE. Там нет такого уж кошмара, вопрос лишь в подготовке данных.

Я не нашёл точных данных, но если судить по толстенному мануалу от Intel, то вместе со всеми расширениями (MMX, MMX+, EMMX, 3DNow, 3DNow+, SSE, SSE2, SSE3, SSSE3, SSE4, FMA) я думаю что не сильно ошибусь, если скажу, что их около полутора тысяч. Вы хотите сказать, что сможете их применять правильно и тогда, когда нужно? Вы очень круты, если так.
Сможете сами сделать — отлично, не сможете, нестрашно, есть, например, Фортран.

Вы же против высокоуровневых языков, нет?
А про «низкоуровневую модель памяти» я не понял. Что это?

Это то, как процессор работает с памятью.
Так типизация и решает задачу валидации данных.

Нет. Типизация может помочь при валидации, но основная задача типизации — это обеспечение корректности программы, то, что вы описали как «быть уверенным, что вы умножаете вещественное число, а не строку». Это не является валидацией. Безусловно, наличие типизации является колоссальным плюсом с точки зрения компилятора. И да, типизация может внести сложности для программиста — просто потому, что программист не сможет скомпилировать программу, которая содержит ошибки типов. Статическая или динамическая типизация — весьма холиварная тема. Лично я считаю, что статическая типизация есть благо, по разным причинам — начиная от обеспечения минимальной корректности программы и до упрощения создания мощных IDE. Однако типизация ни в коем случае не составляет сложности для пользователя программы. Неужели вы думаете, что если нет типизации, пользователь автоматически сможет вводить всё, что захочет, в том формате, в котором захочет, и ваша программа это автоматически проглотит? Пользователю глубоко наплевать, на чём написана программа, главное, что её можно запустить и поработать с ней.
Как пример, у нас есть некий телефонный справочник. Номер телефон это что? Целое число или строка? При отсутствии типизации мы может обрабатывать его и как число (для поиска и сравнения так будет быстрее) и как строку — для хранения номера в том виде, в каком его ввел пользователь.

Если честно, я буду очень рад, если вы продемонстрируете эту магию. Лично я не представляю, как с помощью ассемблера (да и не только ассемблера, в принципе — даже слабая типизация как в JS имеет свои границы) можно произвольные данные автоматически рассматривать в произвольном виде. Пожалуйста, продемонстрируйте, как вы будете обрабатывать +7-909-78956234 и как число, и как строку. Желательно, на вашем любимом ассемблере.
Нет, нельзя. Все эти языки так или иначе позволяют писать кроссплатформенные программы с высокой скоростью разработки и достаточным уровнем корректности.
VB, например, с кроссплатформенностью не сильно дружит. F# тоже не далеко ушел. Да даже С++ есть далеко не для всех архитектур и операционок. Тут спорить можно до хрипоты, но нам это ничего не даст.

Не понимаю вашего сарказма. Python и Java являются кроссплатформенными языками, и программы на них именно что портируемы.
Они не «портируемы», они «кроссплатформенны» в силу своей интерпретируемости (как бы это сейчас не называлось).

Хотя, на мой взгляд, здесь и C можно обойтись, потому что, скорее всего, под ту архитектуру, которая вам нужна, уже кто-нибудь написал соответствующую обёртку.
Отличное замечание. То есть мы сравниваем не языки, а наличие/отсутствие библиотек? Тут конечно, наличие библиотеки всегда лучше ее отсутствия. При этом, «почемуто», априори считается что программист на ассемблере не должен пользоваться сторонними библиотеками вообще, а Сшник — только и и обязан что их применять. В статье и говорится, что в силу неизвестных мне причин, библиотеки используют крайне спорные методы передачи параметров, которые, в дополнении ко всему, еще и не очень удобны при использовании асемблера.

Вы же против высокоуровневых языков, нет?
Почему если человеку нравится программировать на ассемблере, то это обзательно должен быть «красноглазый ретроград»? Я за любые языки программирования. Но это не повод плодить и оправдывать «корявые» решения что на С, что на ассемблере, что на любой другом языке.

Статическая или динамическая типизация — весьма холиварная тема. Лично я считаю, что статическая типизация есть благо, по разным причинам — начиная от обеспечения минимальной корректности программы и до упрощения создания мощных IDE. Однако типизация ни в коем случае не составляет сложности для пользователя программы.
К сожалению Вы наверно меня не поняли. Я всего лишь говорю, что преждевременная типизация, равно как и преждевременная оптимизация — это больше недостаток, чем преимущество. И дело не только в удобстве пользователя (как например, странность, почему в результате умножения переменной (с гарантированно вещественным значением) на целое значение «куда-то» пропадает дробная часть, это из SQL), но и в эффективности программы. Когда для вычисления A * B может быть использована и целочисленная (быстрая), так и вещественная (медленная) арифметика, в зависимости от фактических значений этих переменных на момент вычисления.

Пожалуйста, продемонстрируйте, как вы будете обрабатывать +7-909-78956234 и как число, и как строку. Желательно, на вашем любимом ассемблере.
А что тут демонстрировать? При преобразовании значения (например для поиска) — игнорируем все символы, кроме цифр. При этому исходное значение в строком виде не изменяем. В чем сложность то?

VB, например, с кроссплатформенностью не сильно дружит. F# тоже не далеко ушел. Да даже С++ есть далеко не для всех архитектур и операционок. Тут спорить можно до хрипоты, но нам это ничего не даст.

И, тем не менее, программу, написанную на F# или, например, на VB.NET можно запустить как под виндой (x86_64), так и на мобильнике (ARM). А если ещё вспомнить Mono, который работает на линуксе, и вспомнить количество платформ, поддерживаемых линуксом… Для C++ это справедливо в ещё большей степени. И дело здесь не в «степени» кроссплатформенности, а вообще в её наличии или, хотя бы, потенциальной возможности. Практически любой широкоиспользуемый язык высокого уровня является, по крайней мере, потенциально кроссплатформенным. Ассемблер — нет, просто в силу самого его определения.
Отличное замечание. То есть мы сравниваем не языки, а наличие/отсутствие библиотек? Тут конечно, наличие библиотеки всегда лучше ее отсутствия. При этом, «почемуто», априори считается что программист на ассемблере не должен пользоваться сторонними библиотеками вообще, а Сшник — только и и обязан что их применять. В статье и говорится, что в силу неизвестных мне причин, библиотеки используют крайне спорные методы передачи параметров, которые, в дополнении ко всему, еще и не очень удобны при использовании асемблера.

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

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

Почему если человеку нравится программировать на ассемблере, то это обзательно должен быть «красноглазый ретроград»? Я за любые языки программирования. Но это не повод плодить и оправдывать «корявые» решения что на С, что на ассемблере, что на любой другом языке.

I see your point. Сорри.

Я всего лишь говорю, что преждевременная типизация, равно как и преждевременная оптимизация — это больше недостаток, чем преимущество. И дело не только в удобстве пользователя (как например, странность, почему в результате умножения переменной (с гарантированно вещественным значением) на целое значение «куда-то» пропадает дробная часть, это из SQL), но и в эффективности программы. Когда для вычисления A * B может быть использована и целочисленная (быстрая), так и вещественная (медленная) арифметика, в зависимости от фактических значений этих переменных на момент вычисления.

Извините, но то, что вы описали, есть динамическая типизация. SQL является языком с динамической слабой (тут могу ошибаться) типизацией. В языках со строгой типизацией вы такого никогда не сможете получить. Из ваших слов я вижу, что вы — сторонник динамической типизации, даже если и сами этого не осознаёте. Ну что же, ваше право. Это действительно очень холиварная тема, и я не хочу начинать здесь спор на эту тему.

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

Сложность в том, что простым «игнорированием» всех символов, кроме цифр, вы не получите число. Мне показалось, что это должно быть очевидным вам, как любителю ассемблера. "+7-909-78956234" — это строка, скорее всего, массив байт в кодировке ASCII. Игнорированием отдельных символов вы получите тоже строку, а не число. Чтобы получить число, вам придётся совершить какое-то количество операций умножения на степени десятки. Именно на это я и хотел указать. Просто так, забесплатно, вы в принципе не сможете рассматривать произвольные данные в произвольном виде. Типизация — это фундаментальное свойство представления данных в компьютере, и вы от этого никуда не денетесь.

Кстати, я ещё подумал насчёт предложенной вами концепции передачи параметров. Где будет находиться память под вашу структуру? На стеке? Это ничем не будет отличаться от уже существующих calling conventions. Ну за исключением битовой маски, но я, если честно, совершенно не вижу в ней смысла. В куче? Это будет, мягко говоря, дорого. Операции работы с кучей в современных ОС дорогостоящи, потому что ОС должны бороться с фрагментацией, и выделение куска памяти в куче может занять довольно много времени. В памяти для статических переменных? А что делать с рекурсивными вызовами и с многопоточностью? Нет, стек является совершенно естественным способом хранения и передачи параметров в подпрограммы, и в любом соглашении о вызовах на любом процессоре, даже если это соглашение включает в себя передачу параметров через регистры, так или иначе используется стек. См. здесь.
В статье об ассемблер не хватает картинок с няшными котиками. Говорю как человек, который с 2003 года участвует в разработке ОС реализуемой преимущественно на ассемблере.
Forth теперь функциональный язык программирования, а Smalltalk — не объектно-ориентированный? TELL ME MOAR.
Хочется спросить, где был бы сейчас Linux, будь он написан на ассемблере для x86 в свое время?
И где теперь написанная на ассемблере OS/360 от IBM, после работы над которой Брукс написал свою знаменитую книгу «Мифический человеко-месяц»?
Мой ответ на вопрос «как надо программировать на ассемблере» на данный момент звучит так: программируйте на С во всех случаях, кроме написания компилятора С для совершенно новой архитектуры.
Я безмерно уважаю разработчиков ColibriOS за их труд и статьи, но, по моему мнению, коммерческая разработка на ассемблере сейчас сродни коммерческой разработке на Whitespace, с тем же примерно уровнем сложности и соотношением затрат к результату.
ColibriOS

На самом деле вы их не фига не уважаете. Если бы уважали, то написали бы правильно название ОС. Так что меньше пафоса.
Да никакого пафоса, обыкновенная опечатка, которую уже не поправить.
проблема не в ассемблере, как таковом (кстати OS/390 вполне себе живет и здравствует в виде z/OS, причем до сих пор ассемблер там очень активно используется), а в том, что очень сложно найти рекомендации или подходы к проектированию ПО, с переложением на ассемблер. Особенность использования процессора со специализированными регистрами не рассматривается вообще. Модульность (особенности ее реализации, применительно к x86) упоминается для виду. Как будто знание мнемоник команд само по себе должно вдохнуть в программиста навыки «ведущего инженера».
Если система на ассемблере спроектирована и реализована грамотно, то ее поддерживать и развивать, как минимум, не сложнее, чем то же самое, реализованное на языках высокого уровня. А от «спагетти-кода» ни С, ни С++ не избавляют.
кроме написания компилятора С для совершенно новой архитектуры.

компилятор C для совершенно новой архитектуры лучше писать на C. Генерировать он будет код целевой платформы. А работать (первое время) на любой удобной разработчику платформе. А вот после того, как этот компилятор сможет скомпилировать самого себя под целевую платформу, тогда, компиляцию C программ можно переносить на новую платформу.
Противоречивый пост. Есть интересные идеи, как будто бы подсмотренные у VLIW «Эльбруса» — если склероз мне не изменяет, то именно в нём используется раздельные стеки для аргументов и для адресов возврата. Хотя могу и ошибаться, поскольку систему команд «Эльбруса» в глаза не видел.

Что касается передачи структур вместо аргументов, то посмотрите как это сделано в С++ — при вызове нестатических методов класса, первый («нулевой») параметр и есть указатель на класс. Т.е. Ваша идея созвучна тому, как это реализовано в компиляторах С++. Кстати, можно и нужно раскритиковать предложенный Вами запрет на изменение полей структур — копирование структуры практически всегда будет менее трудоёмкой операцией, чем выделение памяти для новой структуры. Причём, если предагаете чтобы функция/процедура возвращала новую структуру, являющуюся копией входной структуры, но с изменённым полем/полями, то копирования не избежать.

Ещё момент по поводу передачи параметров функции. На архитектурах x86 32-х битный режим постепенно уходит в прошлое, а в 64-х битном режиме регистры активно используются для передачи параметров — en.wikipedia.org/wiki/X86_calling_conventions — обратите внимание на «Microsoft x64 calling convention» и «System V AMD64 ABI». Например, следующий код
fprintf(stderr, "Operation fail. Error code %d\n", errno)
на x86-64 не будет использовать стек для передачи параметров — все три аргумента поместятся в регистры.

Вообще, в наше время программировать на «чистом» ассемблере для архитектуры x86 особого смысла нет — во первых, оптимизаторы компиляторов весьма совершенны. Во вторых, не факт, что ручная оптмиизация кода под определённую модель процессора будет настолько же хороша для другой модели процессора. Вспомните хотя бы проблемы, которые не так давно решали разработчики игр, создавая критический по быстродействию код сразу для нескольких моделей процессоров.

Это было сообщение автору статьи, а чтобы не возникло ощущения, что я противник ассемблера, то приведу области где его использование действительно необходимо:
  • на устройствах с ограниченным количеством памяти, где нужно считать каждый байт
  • в системах с жестким реалтаймом, где приходится считать каждый такт
  • разработчикам операционных систем, компиляторов и системных библиотек (пример — функции setjmp/longjmp можно реализовать только на ассемблере)
  • ручная оптимизация многократо вызываемых функций в критичном ко времени исполнения коде

Наконец, знание ассемблера очень хорошо помогает при отладке — разработчику важно понимать код, который сгенерировал компилятор — objdump -S yourobjfile.o | less в помощь.

Два независимых стека у процессоров было давно и эту «фичу» активно использовал Форт.

Я предложил первый параметр использовать как маску значимых параметров. То есть, после увеличение количества передаваемых параметров старые модули/программы так и будут заполнять только известные им поля. О чем вызываемая подпрограмма может узнать из первого поля (битовой маски заполненых полей). А более новые программы смогут использовать «всю мощь» функционала обновленной подпрограммы.

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

Да, Вы привели fastCall метод вызова. Он отлично работает, пока у вас не больше 10-12 параметров. После чего начинаются свистопляски, а где же хранить это значения, потому что регистры нам нужны для работы, а их (регистров) всего 16? Или вдруг понадобилось добавить 17й параметр? Здравствуй стек. Если бы регистров было 32 (как у Power) тогда да, достаточно сложно представить ситуацию, когда «с перепугу» понадобится параметра передавать 33 параметра. Но даже при увеличение количества параметров с 5 до 6ти приходится или менять все точки вызовов, или указывать, что это другая функция. Иначе как быть с «неконтролируемым» значением в 6м регистре, о котором на вызывающей стороне ничего не известно?

Ну почему, когда слышат «ассемблер» все сразу хватаются за «оптимизацию»? Это не самоцель при программировании на ассемблере. Эффективность складывается не только из «супер быстрого куска кода», но и из общей логики работы ПО, в режимах, удобных для системы. тут и меньший расход памяти неизвестно на что, и отсутствие излишней нагрузки на исполнительные устройства процессора (что экономит немного электричества). Да в конце концов — удовольствие от работы. Вам нравится писать на С, мне приятно писать на ассемблере. Я не вижу ничего в этом плохого. :)
Я предложил первый параметр использовать как маску значимых параметров. То есть, после увеличение количества передаваемых параметров старые модули/программы так и будут заполнять только известные им поля.

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

После чего начинаются свистопляски, а где же хранить это значения, потому что регистры нам нужны для работы, а их (регистров) всего 16? Или вдруг понадобилось добавить 17й параметр? Здравствуй стек.


Не факт. В случае 17 параметров, наверное, оптимальнее будет положить их в структуру и передавать указатель на неё. Причём, это будет справедливо как для языков высокого уровня, так и для ассемблера — Вы ведь сами утверждаете, что регистров не хватит. А в случае рекрурсии получится ещё и хорошая экономия — меньше работы со стеком.

Кстати, Ваша идея с первым параметром не так уж и нова. Много раз видел структуры данных, где первое поле описывает размер структуры и одновременно является версией протокола. Например, в Windows DDK и в формате TrueType шрифтов такое встречается нередко. В аккурат так, как Вы описываете — если размер, к примеру, 10 байт, то это первая версия и используются только поля этой структуры, а, скажем, значение 16 байт — это расширенная версия с дополнительными параметрами, где первые 10 байт соответствуют «старой» структуре, а новые 6 байт — расширение. В этом случае логика работы обработчика определяется как раз первым байтом, при этом новые версии библиотек свободно работают как со старыми форматами, так и с новыми. Возможно, в некоторых ситуациях возможна и обратная совместимость, если допустить что новый формат является надмножеством старого.

«тут и меньший расход памяти неизвестно на что»

Ну почему же неизвестно на что? Очень даже известно до каждого байта — C Run-Time Library, libc и, опционально, отладочная информация. Это для статически слинкованых бинарников. Так же многие линкёры любят добавлять информацию в исполняемый файл о себе любимом — версию ОС и версию компилятора. В случае использования функций из DLL всё немого сложнее.
В случае С++ к вышесказанному добавляются таблицы для вызовов конструкторов и деструкторов для статических объектов и код для обработки исключений. Так и растёт исполняемый файл. Но фишка в том, что практически все современные компиляторы дают нам возможность рулить тем, что и как генерировать. Можно отключить массу «ненужных» фич и получить на выходе чистый «незамутнённый» ассемблерный код.

отсутствие излишней нагрузки на исполнительные устройства процессора (что экономит немного электричества).


Это весьма спорный момент. Потянет на целое обсуждение.

Да в конце концов — удовольствие от работы. Вам нравится писать на С, мне приятно писать на ассемблере. Я не вижу ничего в этом плохого. :)

О да! О вкусах не спорят. Вот по этому пункту было бы глупо возражать.

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

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

Не факт. В случае 17 параметров, наверное, оптимальнее будет положить их в структуру и передавать указатель на неё. Причём, это будет справедливо как для языков высокого уровня, так и для ассемблера — Вы ведь сами утверждаете, что регистров не хватит. А в случае рекрурсии получится ещё и хорошая экономия — меньше работы со стеком.
Именно! Однако fastcall продразумевает только расширение в стек. Программист сам должен выбирать иные варианты передачи параметров.

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

По остальным пунктам полностью согласен :)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории