Как стать автором
Поиск
Написать публикацию
Обновить
10.25

Компиляторы *

Из исходного кода в машинный

Сначала показывать
Порог рейтинга
Уровень сложности

Как увеличить стек FPU

Время на прочтение6 мин
Количество просмотров4.2K

Что-то не получаются у меня заголовки статей. Потому, что на вопрос «Как увеличить стек FPU?» очевидно же следует прямой и ясный ответ – да никак. Это же аппаратное устройство. Даже если бы и удалось увеличить его стек – тогда пришлось бы переделывать систему команд, рассчитанную на адресацию только 8 регистров ST0-ST7 Да и зачем его увеличивать? Для большинства выражений он и так очень глубокий, прямо-таки бездонный. Стоп. Я забегаю вперед. Ведь статью могут читать и те, кто никогда не разбирался с командами процессора на низком уровне. Поэтому начну с самого начала.

Стоп. Я забегаю вперед. Ведь статью могут читать и те, кто никогда не разбирался с командами процессора на низком уровне. Поэтому начну с самого начала.

Итак, FPU (Float Point Unit) – устройство в процессоре x86 для вычислений чисел с «плавающей точкой» в формате IEEE-754. Когда-то это была отдельная микросхема 8087 с названием «сопроцессор». Работала параллельно с основным процессором 8086 и даже была команда WAIT, которая останавливала программу и дожидалась конца выполнения очередной долгой команды сопроцессора. Я еще помню времена, когда у нас в отделе на несколько ПК был лишь один сопроцессор, мы его выковыривали отверткой и переустанавливали на тот ПК, на котором проводились большие вычисления. С появлением процессора 80486 FPU переселилось внутрь его кристалла, и проблема ушла. Кстати, команда WAIT осталась, но работает теперь не так. Впрочем, все это присказка. Главное – у FPU есть собственный стек на восемь патронов, поэтому проводить вычисления очень удобно, а для адресации в командах FPU любого объекта в этом стеке достаточно трех бит в коде каждой такой команды.

Читать далее

Подсчёт ссылок не так прост, как кажется: опыт языка Umka

Время на прочтение6 мин
Количество просмотров4.3K

Подсчёт ссылок обычно предлагается как самый простой способ автоматического управления памятью в языках программирования. Он избавляет программиста от необходимости вставлять в свой код free(), delete и тому подобное, следить за висячими указателями и утечками памяти. Принцип действительно звучит очень просто: каждый выделенный в куче блок памяти наделяется счётчиком ссылок на него. Каждая переменная, через которую можно добраться до этого блока, олицетворяет одну ссылку. Блок освобождается тогда, когда счётчик ссылок доходит до нуля.

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

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

Читать далее

Почему я прекратил разработку своего старого языка программирования и создал новый

Время на прочтение21 мин
Количество просмотров8.6K

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

Приветствую, читатель. В одной из своих прошлых статей (Как я 12 лет создавал свой ЯП и компилятор к нему) я рассказал о том, как я создавал свой язык программирования, продолжая его развитие и уже почти выпустив версию 0.2, я понял, что это не тот язык, на котором я хочу писать и я начал обдумывание нового языка. Поскольку это мой где-то 8-й по счёту язык программирования, я задумался "А в чём причина того, что я создал уже столько языков, но никак не могу получить тот, который мне подходит?".

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

Читать далее

Использование библиотеки LLVM для генерации кода регулярных выражений

Время на прочтение20 мин
Количество просмотров4.7K

Введение


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


Возникает вопрос — как можно ускорить построение конечных автоматов по регулярным выражениям? У меня как-то раз возникла идея, как это можно сделать — для этого можно применить настоящий компилятор, который может генерировать максимально эффективный машинный код. Эту идею я решил опробовать на практике, используя библиотеку LLVM как компилятор/оптимизатор для регулярных выражений. Что из этого вышло, будет изложено ниже в данной статье.

Читать дальше →

Написание парсера DBML на PHP

Время на прочтение11 мин
Количество просмотров4.7K

Иногда возникает задача парсинга произвольного DSL для дальнейшей работы с ним на уровне PHP кода. И я хочу поделиться опытом решения этой приблемы с помощью регулярок с примерами.

Читать далее

DSP-процессоры: назначение и особенности

Время на прочтение14 мин
Количество просмотров35K

Большинство из нас в повседневной жизни постоянно сталкивается с различными компьютерными системами: процессорами общего назначения (general-purpose, в основном x86) в ноутбуках и рабочих станциях, их мощными многоядерными версиями в датацентрах, мобильными процессорами в телефонах, многочисленными контроллерами в бытовой технике и на транспорте. Но помимо всех упомянутых вариантов есть ещё одно важное, хотя и редко упоминаемое семейство: цифровые сигнальные процессоры, чаще именуемые Digital Signal Processors или просто DSP.

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

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

Читать далее

Как увеличить ресурсы в десять раз

Время на прочтение6 мин
Количество просмотров5.4K

Прошу прощения за заголовок, похожий на желтые СМИ, и странный эпиграф, который я объясню ниже. Речь пойдет не о том, как увеличить скорость процессора или емкость диска на порядок, а всего лишь о разновидности данных, которые могут быть включены в исполняемый модуль формата EXE. Эти данные, на мой взгляд, не совсем удачно названы (или же зря буквально переведены) как «ресурсы».

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

В эпоху Windows EXE-формат еще усложнился, и закономерно появилась возможность хранить в нем как неотъемлемую часть не только команды и простые данные, но и, например, картинки или элементы интерактивного диалога. В самом деле, если Ваша программа рисует красивый курсор в виде какой-нибудь стрелочки «выточенной из стали», неудобно же таскать вместе с программой еще и отдельный файл с изображением этой стрелки. Гораздо удобнее поместить изображение прямо внутрь EXE-файла, указав, что это не просто картинка, а именно курсор. Кстати, при создании ярлыка программы, Windows ищет в ресурсах EXE-файла элемент типа «иконка» и высвечивает его как значок ярлыка по умолчанию.

Читать далее

Когда код это данные

Время на прочтение8 мин
Количество просмотров11K
«Представь, что люди как бы находятся в подземном жилище наподобие пещеры, где во всю её длину тянется широкий просвет. С малых лет у них на ногах и на шее оковы, так что людям не двинуться с места, и видят они только то, что у них прямо перед глазами, ибо повернуть голову они не могут из-за этих оков.»

© Платон «Государство», книга 7: Миф О Пещере
Время от времени мне пишут с просьбой помочь в написании кода, который меняет код (далее кодмод, от слов код и модификация - изменение) и сегодня я расскажу об этом нехитром процессе в новом формате, вдохновлённом диалогами Платона, он будет содержать вопросы обратившегося ко мне человека по поводу линтера нового поколения, и мои развёрнутые ответы.

Забегая вперед скажу, что результатом общения стал loader ESTrace, который при запуске может показать что-то вроде:


Но об этом позже, а сейчас:
Следим за функциями

Разработка стековой виртуальной машины и компилятора под неё (часть III)

Время на прочтение8 мин
Количество просмотров6.8K

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

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

На сегодняшний день, наиболее знакомое мне Соглашение о вызове (calling convention), регулирующее правила передачи аргументов функции, очистки стека после вызова, а также логика хранения локальных переменных - это C declaration (cdecl, x86/64) и pascal. Попробую применить эти знания с небольшими модификациями, а именно без прямого доступа программы к регистрам виртуальной машины (она же всё таки стековая, а не регистровая). Итак, логика будет следующая.

Читать далее

Rust 1.53.0: IntoIterator для массивов, "|" в шаблонах, Unicode-идентификаторы, поддержка имени HEAD-ветки в Cargo

Время на прочтение4 мин
Количество просмотров4.1K

Команда Rust рада сообщить о выпуске новой версии — 1.53.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.


Если вы установили предыдущую версию Rust средствами rustup, то для обновления до версии 1.53.0 вам достаточно выполнить следующую команду:


rustup update stable

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


Что было стабилизировано в 1.53.0


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

Читать дальше →

«Компилятор всё оптимизирует»? Ну уж нет

Время на прочтение11 мин
Количество просмотров13K
Многие программисты считают, что компиляторы — это волшебные «чёрные ящики», на вход в которые можно подать хаотичный код, а на выходе получить красивый оптимизированный двоичный файл. Доморощенные философы часто начинают рассуждать о том, какие фишки языка или флаги компилятора следует использовать, чтобы раскрыть всю мощь магии компилятора. Если вы когда-нибудь видели кодовую базу GCC, то и в самом деле могли поверить, что он выполняет какие-то волшебные оптимизации, пришедшие к нам из иных миров.

Тем не менее, если вы проанализируете результаты работы компиляторов, то узнаете, что они не очень-то хорошо справляются с оптимизацией вашего кода. Не потому, что пишущие их люди не знают, как генерировать эффективные команды, а просто потому, что компиляторы способны принимать решения только в очень малой части пространства задач. [В своём докладе Data Oriented Design (2014 год) Майк Эктон сообщил, что в проанализированном фрагменте кода компилятор теоретически может оптимизировать лишь 10% задачи, а 90% он оптимизировать не имеет никакой возможности. Если бы вам интересно было узнать больше о памяти, то стоит прочитать статью What every programmer should know about memory. Если вам любопытно, какое количество тактов тратят конкретные команды процессора, то изучите таблицы команд процессоров]

Чтобы понять, почему волшебные оптимизации компилятора не ускорят ваше ПО, нужно вернуться назад во времени, к той эпохе, когда по Земле ещё бродили динозавры, а процессоры были чрезвычайно медленными. На графике ниже показаны относительные производительности процессоров и памяти в разные годы (1980-2010 гг.). [Информация взята из статьи Pitfalls of object oriented programming Тони Альбрехта (2009 год), слайд 17. Также можно посмотреть его видео
(2017 год) на ту же тему.]

Читать дальше →

История портирования Reindexer'а – как покорить Эльбрус за 11 дней

Время на прочтение9 мин
Количество просмотров7.3K

Всем привет! На связи Антон Баширов, разработчик из ИТ-кластера «Ростелекома». Импортозамещение набирает обороты, а российский софт всё глубже проникает в нашу повседневную ИТ-шную сущность бытия. Процессоры Эльбрус и Байкал становятся более востребованными, комьюнити расширяется, но мысли о необходимости портировать весь наш любимый технологический стек на неизведанную архитектуру E2K звучат страшнее рассказов про горящий в пламени production-кластер.

Работая в команде по внедрению Эльбрусов, мне довелось в прямом и переносном смысле пощупать наши отечественные процессоры, поэтому я хочу поделиться полученным опытом, рассказать о том, какой болевой порог нужен, чтобы выдержать портирование NoSql native базы данных и не сойти с ума, а еще познакомить разработчиков с миром Эльбруса и его обитателями.

Читать далее

Сборка ядра Linux с LTO оптимизацией

Время на прочтение25 мин
Количество просмотров34K


Технический прогресс не стоит на месте, появляются новые компьютерные архитектуры, компиляторы становятся умнее и генерируют более быстрый машинный код. Современные задачи требуют все более креативного и эффективного решения. В данной статье пойдет речь, на мой взгляд, про один из самых прогрессивных тулчейнов LLVM и компиляторы на его основе Clang и Clang++, для языков программирования С и C++ соответственно. Хоть GCC — конкурент Clang, может агрессивнее оптимизировать циклы и рекурсию, Clang дает на выходе более корректный машинный код, и чаще всего не ломает поведение приложений. Плюс оптимизация программ не заканчивается только оптимизацией циклов, поэтому Clang местами дает лучшую производительность. В GCC же за счет переоптимизации вероятность получить unpredictable behavior значительно выше. По этой причине на многих ресурсах не рекомендуют использовать -O3 и LTO(Link Time Optimization) оптимизации для сборки программ. Плюс в случае агрессивной оптимизации, размер исполняемых файлов может сильно увеличиться и программы на практике будут работать даже медленнее. Поэтому мы остановились на Clang не просто так и опции компиляции -O3 и LTO работают в нем более корректно. Плюс современные компиляторы более зрелые, и сейчас уже нет тех детских болячек переоптимизации и LTO.
Узнать подробности

Ближайшие события

Sparkplug — неоптимизирующий компилятор JavaScript в подробностях

Время на прочтение10 мин
Количество просмотров5.4K

Создать компилятор JS с высокой производительностью означает сделать больше, чем разработать сильно оптимизированный компилятор, например TurboFan, особенно это касается коротких сессий, к примеру, загрузки сайта или инструментов командной строки, когда большая часть работы выполняется до того, как оптимизирующий компилятор получит хотя бы шанс на оптимизацию, не говоря уже о том, чтобы располагать временем на оптимизацию. Как решить эту проблему? К старту курса о Frontend-разработке делимся переводом статьи о Sparkplug — свече зажигания под капотом Chrome 91.

Читать далее

C++20: удивить линкер четырьмя строчками кода

Время на прочтение4 мин
Количество просмотров13K

Представьте себе, что вы студент, изучающий современные фичи C++. И вам дали задачу по теме concepts/constraints. У преподавателя, конечно, есть референсное решение "как правильно", но для вас оно неочевидно, и вы навертели гору довольно запутанного кода, который всё равно не работает. (И вы дописываете и дописываете всё новые перегрузки и специализации шаблонов, покрывая всё новые и новые претензии компилятора).

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

Сперва преподаватель (то есть, я) минимизировал код вот до такого: https://gcc.godbolt.org/z/TaMTWqc1T

Читать далее

С++23 WIP: онлайн-встреча международного комитета по C++

Время на прочтение4 мин
Количество просмотров15K
Вчера прошла встреча рабочей группы ISO C++, добавляли фичи в C++23, исправляли C++20. Мы участвуем в работе комитета, поэтому сегодня поделюсь с вами свежими новостями о развитии стандарта.



Должен заметить, что международный комитет в онлайне работает совсем уж неторопливо… Настолько неторопливо, что на февральской встрече из полезного приняли только std::to_underlying() — функцию, преобразовывающую значение enum к нижележащему целочисленному типу:

enum class ABCD : std::uint64_t { A = 0x1012, B = 0x405324, };

constexpr std::uint64_t value = std::to_underlying(ABCD::A); 

В этот раз дело пошло веселее

Разработка стековой виртуальной машины и компилятора под неё (часть II)

Время на прочтение5 мин
Количество просмотров8.1K

В первой части Разработка стековой виртуальной машины и компилятора под неё (часть I) сделал свою элементарную стековую виртуальную машину, которая умеет работать со стеком, делать арифметику с целыми числами со знаком, условные переходы и вызовы функций с возвратом. Но так как целью было создать не только виртуальную машину, но и компилятор C подобного языка, пришло время сделать первые шаги в сторону компиляции. Опыта никакого. Буду действовать по разумению.

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

Читать далее

Разработка стековой виртуальной машины и компилятора под неё (часть I)

Время на прочтение8 мин
Количество просмотров16K

Так сложилось, что за последние 18 лет, не приходилось писать на C/C++. На работе использовалась Java, да и ввиду должностей деятельность больше была связана с предпринимательством - переговоры, корпоративные продажи, выстраивание производственных операций и структурирование инвестиционных сделок. Захотелось в свободное от работы время восстановить навыки, размять часть мозга которую не напрягал все 18 лет и, естественно, начать с самых основ. Осталось придумать себе задачу.

В универе преподаватели, молодость которых приходилась на 70-80е годы, до объектно-ориентированного программирования убивались по теме разработке собственных языков (интерпретаторов, компиляторов) под предметные области. Всё это казалось мне невостребованным "старьём", но появление новых языков за последнее десятилетие (Go, Kotlin и множества других) повысили мой интерес к этой теме.

Решил в качестве хобби написать 32-bit стековую виртуальную машину и компилятор C подобного языка под неё, чтобы восстановить базовые навыки. Такая классическая Computer Science задачка для заполнения вечеров с пивом. Как предприниматель, я четко понимаю, что она никому не нужна, но такая практика нужна мне для эстетического инженерного удовольствия. Плюс когда об этом рассказываешь сам понимаешь глубже. С целью и мотивами определился. Начнём.

Так как это виртуальная машина, мне нужно определиться с её характеристиками:

CPU: 32-bitный набор команд, так как машина стековая, в основном операнды команд храним в стеке, из регистров только IP (Instruction Pointer) и SP (Stack Pointer), пока работаем с целыми числами со знаком (__int32), позже добавим остальные типы данных.

RAM: пусть памяти пока будет 65536 ячеек по 32-bit`а. Которую организуем просто. С нижних адресов в верх будут идти код (code/text) и данные (data, heap), а с верхних адресов вниз будет расти стек (stack). Дёшево и сердито.

Читать далее

Необычная концепция синтаксиса языка программирования

Время на прочтение8 мин
Количество просмотров9.1K


Хочу представить на обсуждение читателей немного необычный концепт языка программирования, в котором отсутствует проблема, присущая практически всем промышленным языкам — постоянное увеличение сложности синтаксиса языка из-за его естественного развития по мере выхода новых версий и добавления новых фич. Эта проблема описана в материале Простое сложное программирование и Какая «идеальная» цель развития у языков программирования?

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

Особенности языка:

  • Низкий порог входа и естественное ограничение уровня сложности в течение длительного времени даже при условии постоянного развития самого языка.
  • Возможность писать код программы как в декларативной, так и в императивной парадигме с использованием процедурного, модульного и объектно-ориентированного подхода.

Ну и в соответствии с собственным наблюдением Хабр — ума палата, буду рад любым комментариям и предложениям, которые помогут протестировать или улучшить предлагаемое решение.
Читать дальше →

M/o/Vfuscator2, безумный компилятор

Время на прочтение4 мин
Количество просмотров8K


Однажды один умный чувак (Кристофер Домас) читал статью другого умного чувака (Стивена Долана) про удивительную особенность архитектуры x86. Стивен ругал её за избыточность и утверждал, что набор инструкций можно сократить до одной лишь mov, потому что она Тюринг-полная. Если бы Стивен не был таким умным, в его словах можно было бы усомниться, но у Кристофера загорелись глаза: проработав двадцать лет с x86, он не слышал ни о чём подобном, и ему страшно захотелось написать компилятор, который бы переводил весь код в наборы одних лишь mov-инструкций. Так родились M/o/Vfuscator и M/o/Vfuscator2, наглядно иллюстрирующие ненормальное программирование.
Читать дальше →

Вклад авторов