Обновить
39.78

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

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

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

Стилистический Анализатор: Синхронизация порядка объявлений и определений функций

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров1.2K

У нас в организации есть обязательное правило оформления исходников, которое звучит так:

Порядок объявления С-функций должен совпадать с порядком определения С-функций.

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

Читать далее

Синтаксический анализатор — модифицированный Shunting Yard

Уровень сложностиСредний
Время на прочтение3 мин
Количество просмотров1.4K

Алгоритм синтаксического анализа кода на основе Shunting Yard

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

Читать далее

Реверс-инжиниринг  GDB для работы с Pwndbg

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

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

Вот почему этим летом, работая в Trail of Bits, я расширил Pwndbg — плагин для GDB. Поддерживает его мой наставник Доминик Чарнота. Я добавил в инструмент две фичи, благодаря которым практическая отладка урезанных бинарников сближается с аналогичной работой, знакомой нам из работы с отладчиком в IDE. Теперь в Pwndbg интегрирован инструмент Binary Ninja, позволяющий лучше выяснять специфику GDB+Pwndbg, а также выводить дамп структур Go, чтобы отлаживать бинарники Go стало удобнее.

Читать далее

Rust 1.81.0: Error, новая реализация сортировки, #[expect(lint)]

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров3.1K

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


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


$ rustup update stable

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


Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать канал beta (rustup default beta) или nightly (rustup default nightly). Пожалуйста, сообщайте обо всех встреченных вами ошибках.

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

Что значит инициализировать int в C++?

Уровень сложностиСредний
Время на прочтение10 мин
Количество просмотров16K

Недавно я получил по почте от Сэма Джонсона этот вопрос. Вот слегка отредактированное письмо Сэма:

«Возьмём для примера этот код в локальной области видимости функции:

int a;

a = 5;

Многие люди считают, что инициализация происходит в строке 1, потому что веб-сайты наподобие cppreference дают такое определение: "Инициализация переменной предоставляет его начальное значение на момент создания".

Однако я убеждён, что инициализация происходит в строке 2, потому что [в разных хороших книгах по C++] инициализация определяется как первое существенное значение, попадающее в переменную.

Можете ли вы сказать, какая строка считается инициализацией?»

Отличный вопрос. На Cppreference написано правильно, и для всех классовых типов ответ прост: объект инициализируется в строке 1 вызовом его стандартного конструктора.

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

Короткий ответ: вполне допустимо говорить, что переменная получает своё исходное значение в строке 2. Но заметьте, что я намеренно не сказал «Объект инициализируется в строке 2», к тому же и код, и этот ответ обходят молчанием более важный вопрос: «Ну ладно, а что, если код между строками 1 и 2 попробует считать значение объекта?»

Читать далее

Вам следует написать новый (ужасный) язык программирования

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров15K

Каждый разработчик ПО использует язык программирования, и обычно даже несколько. Лишь немногие из нас создают языки программирования. Это логично, ведь работу, которую мы выполняем, чаще всего можно сделать при помощи уже существующих языков. И совершенствованием этих языков уже занимаются другие люди, а мы можем сосредоточиться на нашей непосредственной задаче.

Но из-за этого мы теряем возможность научиться чему-то новому. Я нашла это новое, когда создала язык, руководствуясь глупым принципом: поток управления должен осуществляться через исключения и ничего больше. Я создала его как шутку, но неожиданно для себя в процессе разработки получила новые знания.
Читать дальше →

Как сделать анализатор кода за два дня

Уровень сложностиСредний
Время на прочтение23 мин
Количество просмотров10K

Статический анализ — это очень мощный инструмент, позволяющий следить за качеством кода. Предлагаем вместе попробовать написать простой Lua анализатор на Java, чтобы понять, как устроены статические анализаторы кода внутри.

Читать далее

Я на дереве сижу, препроцессинг провожу

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров2.2K

Согласно описанию,


Tree-sitter — это инструмент для генерации синтаксических анализаторов и библиотека инкрементного синтаксического анализа. Он может создавать конкретное синтаксическое дерево для исходного файла и эффективно обновлять синтаксическое дерево по мере редактирования исходного файла.

Но как Tree-sitter справляется с языками, в которых необходима стадия препроцессинга?

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

Как поделить не деля или оптимизация деления компиляторам(и)

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров11K

Если вы никогда не пробовали смотреть как код на C++ разворачивается компилятором в код Assembly – вас ждёт много сюрпризов, причём, не нужно смотреть какой-то замудренный исходный код полный templates или других сложных конструкций: рассмотрите следущий snippet:

Смотреть код

Как устроены условные точки останова

Время на прочтение9 мин
Количество просмотров3.7K
Условные точки останова (conditional breakpoints) – исключительно полезный инструмент. Но всем известно, насколько они замедляют работу кода, так, что из-за этого некоторые даже бросают ими пользоваться. В Visual Studio в своё время удалось значительно улучшить ситуацию с ними, что не помешало пользователю @ryanjfleury высмеивать их крайнюю медлительность. Но даже у raddbg уходит около 2 секунд на выполнение 10000 итераций простого цикла, если внутри него расставлены точки останова. Для сравнения: без точек останова тот же самый цикл выполняется менее чем за 1 мс. Почему же так чертовски медленно?

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

Обратите внимание: в этой статье речь идёт об отладчиках, работающих с нативным кодом – например, GDB, LLDB, Visual Studio C++. Отладчики для управляемых и скриптовых языков работают примерно так же, но могут отличаться детали реализации.
Читать дальше →

Rust 1.80.0: типы LazyCell, LazyLock, проверяемые имена и значения cfg, эксклюзивные диапазоны в шаблонах

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров3.2K

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


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


$ rustup update stable

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


Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать канал beta (rustup default beta) или nightly (rustup default nightly). Пожалуйста, сообщайте обо всех встреченных вами ошибках.

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

Как Проинициализировать Микроконтроллер [часть 2]

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров4.4K

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

В этом тексте я написал как автоматически выявить правильную последовательность инициализации

Читать далее

Квантовый искусственный интеллект: пересечение квантовых вычислений и машинного обучения

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров2.4K

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

Читать далее

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

Компилятор за выходные: наконец-то ассемблер

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров9.1K

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

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

Читать далее

Побеждаем компилятор в скорости при помощи ассемблера

Уровень сложностиСредний
Время на прочтение13 мин
Количество просмотров9K

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

Тем не менее, иногда до нас доносятся слухи.

Написанные в древних томах, подслушанные в тайных покоях, начертанные на стенах разрушенных храмов, упоминаемые в загадочных текстах, они рисуют чёткую картину:

Компиляторы плохо справляются с генерацией кода для интерпретаторов, и можно превзойти их, написав интерпретатор на языке ассемблера.
Читать дальше →

У меня нет конструктора, но я должен инициализироваться

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

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

Главная ошибка была в том, что ты в это вообще ввязался — в этом никаких сомнений.

Ещё когда я впервые взялся проходить курс по C++ несколько лет назад, меня учили, что, если я не предоставлю собственного конструктора, то компилятор сам подберёт ему замену — своего рода конструкторы, действующие по умолчанию. Я решил подробнее в этом разобраться, особенно меня волновали случаи, которые выглядят примерно так:

Читать далее

UB или не UB – вот в чём вопрос: как gcc и clang обрабатывают статически известное неопределённое поведение

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

Недавно у нас в команде зашла дискуссия о неопределённом поведении (UB) в C. Напомню для тех, кто не знает: если мы пишем такой код, эффект от выполнения которого (и события в процессе его выполнения) строго не определён в спецификации языка, то возникает неопределённое поведение. Таким образом, встретив такой код, компилятор может действовать по собственному усмотрению, и нет никаких гарантий, что выполнение этого кода пойдёт по предсказуемому пути. Следовательно, нужно избегать неопределённого поведения любой ценой, поскольку мало того, что оно может приводить к глюкам программы, но и часто становится источником уязвимостей и угрозой безопасности. Примеры кода, в котором проявляется неопределённое поведение: выход за границы массива при его индексировании, целочисленное переполнение, деление на ноль, разыменование указателя на null [1].

Компиляторы нередко пользуются неопределённой семантикой языка, чтобы делать те или иные допущения о программе. Например, если написать что-то вроде int x = y/z, компилятор может предположить, что z не может быть равно нулю, так как деление на ноль приводит к неопределённому поведению, а программист явно не собирался писать такой код. На основе этой информации он может попытаться далее оптимизировать программу так:

Читать далее

Насекомое 13 лет сидит в вашем компиляторе и не собирается оттуда вылезать

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров10K

Представим, что у вас идеальный проект. Таски пилятся, компилятор компилирует, статические анализаторы анализируют, релизы релизятся. В какой‑то момент вы принимаете волевое решение открыть древний файл, в который никто не залезал уже много лет, и видите, что он в кодировке Windows-1251. При том, что весь проект уже давно перешёл на UTF-8. «Непорядок!» — думаете вы, и лёгким движением руки меняете кодировку. На следующий день на вашем тестовом сервере случается локальный апокалипсис. Думаете, такого не может быть? Тогда предлагаю это обсудить.

Читать далее

Компиляторные оптимизации сложны, так как компиляторы забывчивы

Уровень сложностиСложный
Время на прочтение10 мин
Количество просмотров6.9K

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

Читать далее

А что если исходные коды программ хранить в бинарном формате?

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

Эта статья — просто идея, не судите строго.


TLDR: предлагаю рассмотреть хранение исходных кодов программ в некоем бинарном формате вместо голого текста.


Компилятор и IDE


Как примерно работает компилятор: сначала происходит лексический анализ, т.е. разбиение исходного кода на токены. Потом происходит синтаксический анализ — полученные токены объединяются в синтаксическое дерево. Потом семантический анализ: вывод типов данных, проверка видимости переменных, и т.д.


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


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


Т.е. сам текст программы нужен только человеку на этапе ввода информации. Потому что ему для понимания происходящего AST-дерево не подойдёт.


Но что если хранить исходный код по-другому?

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

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