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

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

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

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

Программирование ARM на Pascal

Время на прочтение7 мин
Количество просмотров24K
Однажды, вдруг совершенно неожиданно и без объявления войны, появилась идея. И требовалось для этого написать и запрограммировать кристалл STM32.

А собственно в чем проблемам? stm32vldiscovery лежала на полке и дожидалась своего часа, программирование знаю и частенько пишу “на заказ”. С железом дружу хорошо.

Первым делом возник вопрос “на чем писать”? Сред программирования много, но язык только “Си”. Без вариантов. Ассемблер не рассматриваю в принципе. Светодиодом помигать можно, но что-то сложнее требует огромных трудозатрат.

Но я не знаю Си! Вообще. Всю жизнь писал только на Pascal/Delphi. Учить язык? Вы пробовали учить язык, когда вам более 40 лет возраста? Когда работа, семья и минимум свободного времени. Когда ум уже не так остр, как в молодости. Да и затевать все это ради одного проект смысла не более, чем учиться на права и покупать машину ради поездки в булочную в соседнем доме.

Выходом послужил найденный “mikroPascal PRO for ARM” от MikroElektronika. Если честно, я уже работал с “mikroPascal PRO for PIC” на пике популярности PIC чипов. Впечатления остались не очень хорошие. Компилятор “со странностями”, оболочка тоже не отличалась стабильностью и дружественным интерфейсом.

Тем более интересно было посмотреть, что изменилось за эти годы и в какую сторону.

И так, что мы имеем на руках:
  • Плату stm32f4discovery;
  • mikroPascal PRO for ARM с лицензионном ключем (взято у товарища. потом придется вернуть). Без ключа — ограничение в 2 КВ на размер кода;
  • Инженер, которого в ВУЗе учили исключительно Pascal.

Задача: освоить программирование микроконтроллера без единой строчки Си кода.

Итак приступим…
Читать дальше →

Сделаем GCC C++ для AVR и Arduino лучше?

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


Привет хабраплюсплюсовцам!

Хочу разобрать проблему компилятора avr-g++, из-за которой в разных дискуссиях про AVR и Arduino звучит «С++ — это не для микроконтроллеров, C++ жрёт память, C++ генерирует раздутый код — пишите на голом C, а лучше на ASM».

Для начала давайте разберёмся, в чём же преимущество C++ перед C. Концепций, которые добавляет C++ много, но самая значимая и самая эксплуатируемая — это поддержка ООП. Что такое ООП?
  • Инкапсуляция
  • Наследование
  • Полиморфизм


Использование первых двух пунктов в C++ «бесплатно». Никакого преимущества программа на чистом C перед программой на C++ с инкапсуляцией и наследованием не имеет. Картина меняется, когда мы подключаем к действу полиморфизм. Полиморфизм бывает разным: compile-time, link-time, run-time. Я говорю о классическом run-time, т.е. о виртуальных функциях. Как только в своих классах вы начинаете добавлять виртуальные методы, чудесным образом растёт потребление как Flash-памяти, так и SRAM.

Почему так происходит и, что с этим можно было бы сделать, расскажу под катом.
Читать дальше →

«Ра-а-авняйсь, смирно!». Выравниваем данные

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


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

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

Новое предупреждение о неверном вычислении размера массива в gcc 5.1

Время на прочтение2 мин
Количество просмотров15K
Хорошие новости для пользователей gcc — при использовании gcc 5.1 и выше им будет проще быстро находить вот такую распространенную ошибку вычисления размера массива, объявленного как параметр функции:

void something( char arr[100] )
{
    // this loop is broken
    for( size_t index = 0; index < sizeof(arr)/sizeof(arr[0]); index++ ) {
       //WHATEVER
   }
}

Хотя параметр и объявлен как массив известного размера, с точки зрения компиляторов C и C++ это указатель типа char*, поэтому sizeof(arr) даст то же значение, что и sizeof(char*) – скорее всего, 4 или 8. Цикл, скорее всего, будет работать не так, как ожидалось.
Другой вариант:
void something( char encryptionKey[9000] )
{
   // WHATEVER, PROFIT

  // this call is broken
  SecureZeroMemory( encryptionKey, sizeof(encryptionKey)); // erase the key
}

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

Чтобы найти такой код было проще, в gcc 5.1 и новее на такой код выдается предупреждение и оно включено по умолчанию.
Читать дальше →

Прокачиваем JavaScript с помощью TurboFan

Время на прочтение2 мин
Количество просмотров15K
Привет, Хабр! Что-то давно мы не говорили о Google Chrome и простых веб-технология. Давайте это исправим.



Как вам всем, наверное, известно, экосистема JavaScript развивается сразу в нескольких направлениях. Среди основных изменений можно выделить, например, прогресс в основных стандартах языка — недавнее закрепление норм ECMAScript 2015. Кроме таких серьёзных изменений, развивается язык и во множестве маленьких экспериментов — например — Strong Mode.

Само собой, для обеспечения растущих потребностей и поддержки новых технологий необходим новый, гибкий динамический (just-in-time) компилятор, и мы усердно работали над ним для нашего JavaScript-движка V8.
Читать дальше →

Сказ о том, как «цифирь» не сошлась

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


Некоторое время назад я писал про то, как получать воспроизводимые результаты и какие сложности с этим связаны. Также подробно рассказал про модели, позволяющие контролировать работу с числами с плавающей точкой в компиляторе и отдельно уточнил, что, если мы используем какие-либо библиотеки или стандарты, то должны позаботится, чтобы нужные флаги были указаны и для них. И вот совсем недавно я натолкнулся на интересную проблемку, связанную именно с воспроизводимостью результатов при работе с OpenMP.

Что такое воспроизводимость? Да всё просто – мы хотим получать одну и ту же «хорошую цифирь» от запуска к запуску, потому что для нас это важно. Это критично во многих областях, где сейчас активно используются параллельные вычисления.

Итак, как вы помните, для машинных вычислений существенную роль играет порядок суммирования, и, если у нас имеются циклы, распараллеленные с помощью любой технологии, то неизбежно возникнет проблема воспроизводимости результатов, потому что никто не знает в каком порядке будет проводиться суммирование, и на сколько «кусков» будет разбит наш исходный цикл. В частности это проявляется при использовании OpenMP в редукциях.
Читать дальше →

Хвостовая рекурсия в C++ с использованием 64-битных переменных — Часть 2

Время на прочтение5 мин
Количество просмотров10K
В прошлой заметке я рассказывал о проблемах с рекурсией в функции Фибоначчи при использовании 64-битных переменных в качестве ее аргументов и компиляции кода с помощью Microsoft Visual C++. Выяснилось, что компилятор включает хвостовую рекурсию, когда используются 32-битные переменные, но не делает этого при переходе к 64-битным. На всякий случай напомню, что хвостовая рекурсия – это оптимизация, производимая компилятором, при которой некоторые виды хвостовых вызовов преобразуются в безусловные переходы. Узнать больше о хвостовой рекурсии.
Читать дальше →

Хвостовая рекурсия в C++ с использованием 64-битных переменных — Часть 1

Время на прочтение3 мин
Количество просмотров19K
В этот раз я хочу поделиться с вами одной проблемой, с которой столкнулся, когда решил сравнить итерационные и рекурсивные функции в C++. Между рекурсией и итерацией есть несколько отличий: о них подробно рассказано в этой стать. В языках общего назначения вроде Java, C или Python рекурсия довольно затратна по сравнению с итерацией из-за необходимости каждый раз выделять новый стековый фрэйм. В C/C++ эти затраты можно легко устранить, указав оптимизирующему компилятору использовать хвостовую рекурсию, при которой определенные типы рекурсии (а точнее, определенные виды хвостовых вызовов) преобразуются в команды безусловного перехода. Для осуществления такого преобразования необходимо, чтобы самым последним действием функции перед возвратом значения был вызов еще одной функции (в данном случае себя самой). При таком сценарии безусловный переход к началу второй подпрограммы безопасен. Главный недостаток рекурсивных алгоритмов в императивных языках заключается в том, что в них не всегда возможно иметь хвостовые вызовы, т.е. выделение места для адреса функции (и связанных с ней переменных, например, структур) на стеке при каждом вызове. При слишком большой глубине рекурсии это может привести к переполнению стека из-за ограничения на его максимальный размер, который обычно на порядки меньше объема оперативной памяти.
Читать дальше →

Jancy — скриптовый язык для системных/сетевых программистов

Время на прочтение14 мин
Количество просмотров19K
jancyЗачем вообще создавать новый язык программирования? Их уже существует невероятное количество — по моему твёрдому убеждению, значительно больше, чем надо. И наверняка далеко не последнюю роль в данном положении вещей играет то, что создание компиляторов — это невероятно увлекательный процесс. С поправкой на арбузы и свиные хрящики — это вообще одна из самых «вкусных» работ, о которых может мечтать увлечённый программист.

Непередаваемо здоровским является цветочно-конфетный период — первый этап изучения теории компиляторов по толстым умным книжкам, и — тут же! — её применения на практике, в своём собственном языке. Даже печальная перспектива того, что создатель языка вполне может оказаться его единственным пользователем, не способна перевесить радость творчества и остановить сферического-в-вакууме компиляторного Кулибина. Разумеется, если удовлетворение собственного интереса является не только важной, но и единственной движущей силой всего процесса — вышеописанная перспектива с неизбежностью будет воплощена в жизнь. Но даже если это и НЕ единственная причина создания нового языка — перспектива стать одиноким пользователем своего творения всё равно имеет шансы реализоваться.
Читать дальше →

Оберон умер, да здравствует Оберон! Часть 1. Некоторые любят поактивней

Время на прочтение5 мин
Количество просмотров27K
Языкам программирования семейства Оберон не суждено было прорваться в мейнстрим, хотя они и оставили заметный след в IT-индустрии. Однако, и операционные системы, написанные на этих языках (являясь одновременно и программными каркасами различных решений и средами разработки), и сами языки программирования используются в учебной, исследовательской и промышленной сферах и по сей день, понуждая к творчеству и экспериментам, развиваясь и впитывая новые веянья индустрии и влияя на неё.

Этой обзорной статьёй я открываю серию статей, посвящённых языку Активный Оберон и операционной системе A2, написанной на этом языке.

Итак, встречайте — Активный Оберон


Первая публикация по Активному Оберону появилась в 1997 году, но понятно, что язык и его реализация появились несколько раньше. За эти годы произошло много изменений в языке, переработана среда времени выполнения, написана операционная система A2…
Читать дальше →

Метапрограммирование: какое оно есть и каким должно быть

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

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

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

Метапрограммирование реализовано в той или иной мере в очень разных языках; если не рассматривать экзотические и близкие к ним языки, то самым известным примером метапрограммирования является С++ с его системой шаблонов. Из «новых» языков можно рассмотреть D и Nim. Одна из самых удачных попыток реализации метапрограммирования — язык Nemerle. Собственно, на примере этой четверки мы и рассмотрим сабж.

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

Циклические контейнеры в Objective-C

Время на прочтение2 мин
Количество просмотров7.8K
Некоторое время назад я написал этот код:

NSMutableArray *environments = [NSMutableArray new];
for (NSString *key in [dictionary allKeys]) {
    XCCEnvironment *environment = [[XCCEnvironment alloc] initWithName:key
                                                            parameters:dictionary[key]];
    [environments addObject:environments];
}
return environments;

Заметили проблему? Я — нет.
Читать дальше →

Изменения в Visual C++

Время на прочтение12 мин
Количество просмотров36K
Когда вы захотите обновить версию Visual C++ компилятора (например, перейти с Visual Studio с 2013 на 2015), будет не лишним узнать, почему вы можете столкнуться с тем, что код, который прежде успешно компилировался и выполнялся, теперь будет вызывать ошибки компиляции и/или ошибки времени выполнения.
Эти проблемы могут быть вызваны многочисленными изменениями компилятора для соответствия стандарту С++, изменениями в сигнатурах функций или изменениями расположения объектов в памяти.
Узнать подробнее

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

Оптимизируем шаг за шагом с компилятором Intel C++

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


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

Итак, весь тернистый путь компиляции и оптимизации нашего приложения можно разбить на 7 шагов. Пошагали!
Читать дальше →

Новый инструмент анализа SIMD программ — Vectorization Advisor

Время на прочтение6 мин
Количество просмотров6.3K
В блоге компании опубликовано уже немало постов, посвященных векторизации, вот, например, довольно обстоятельный обзор принципов автовекторизации. С каждым выходом новых процессоров Intel тема становится все более актуальной для достижения максимальной производительности приложения. В этом посте я расскажу о Vectorization Advisor, который входит в знакомый многим Intel Advisor XE и позволяет решить множество проблем векторизации кода. Однако сначала о том, зачем это нужно.
Читать дальше →

Разбор естественного языка: под капотом

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


API синтаксического анализатора


Продолжаю свой предыдущий пост. Время сфокусироваться на деталях внутреннего устройства синтаксического анализатора. В качестве языка реализации я выбрал Go, поскольку хотел малой ценой получить параллельный (в смысле, использующий все доступные ядра CPU) производительный инструмент, без погружения в низкоуровневую пучину C++.

Полученный код предоставляет следующий API:
type Attribute struct {
    Name   string
    Value  string
}

type ParseMatch struct {
    Text            string
    Nonterminal     string
    Rule            string
    Attributes      []Attribute
    Submatches      []ParseMatch
    Hypotheses      []string
    HypothesisCount uint
}

func Parse(text, nonterminal string, hypotheses_limit uint) []ParseMatch

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

Итак, функция Parse берёт text — текст для разбора, nonterminal — название нетерминала (например, «sentence»), а также максимальное число выдвигаемых гипотез hypotheses_limit (об этом чуть ниже). Параметр nonterminal может быть пустым. В этом случае тексту будет сопоставляться лексический терминал, найденный в морфологической базе.

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

LLILC — транслятор MSIL в байткод LLVM от Microsoft

Время на прочтение1 мин
Количество просмотров13K
Сегодня сотрудник Microsoft анонсировал проект LLILC — новый проект для трансляции MSIL в байткод LLVM, предназначенный пока главным образом для инфраструктуры CoreCLR. В ближайшее время он может быть использован для JIT-компиляции, а в дальнейшем и для формирования прекомпилированных сборок (Ahead-of-Time) средствами .NET Native.

Несмотря на то, что в CoreCLR уже есть свой JIT, планируется расширить поддержку различных платформ за счёт LLVM. Новый JIT использует тот же набор внутренних API, что и RyuJIT и бесшовно его заменяет. Таким образом новый JIT позволит .NET-коду выполняться на всех поддерживаемых LLVM-платформах, на которые можно портировать CoreCLR.
Читать дальше →

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

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


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

  • Морфологический — анализ словоформ и их характеристик (число, падеж, и т.д.);
  • Синтаксический — выделение структуры предложения (отношения между словами);
  • Семантический — выделение смысла исходя из «модели мира»;

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

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

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

Генерация кода во время исполнения или «Пишем свой JIT-компилятор»

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

Современные компиляторы очень хорошо умеют оптимизировать код. Они удаляют никогда не выполняющиеся условные переходы, вычисляют константные выражения, избавляются от бессмысленных арифметических действий (умножение на 1, сложение с 0). Они оперируют данными, известными на момент компиляции.
В момент выполнения информации об обрабатываемых данных гораздо больше. На её основании можно выполнить дополнительные оптимизации и ускорить работу программы.
Оптимизированный для частного случая алгоритм всегда работает быстрее универсального (по крайней мере, не медленнее).
Что если для каждого набора входных данных генерировать оптимальный для обработки этих данных алгоритм?
Очевидно, часть времени выполнения уйдёт на оптимизацию, но если оптимизированный код выполняется часто, затраты окупятся с лихвой.
Как же технически это сделать? Довольно просто — в программу включается мини-компилятор, генерирующий необходимый код. Идея не нова, технология называется “компиляция времени исполнения” или JIT-компиляция. Ключевую роль JIT-компиляция играет в виртуальных машинах и интерпретаторах языков программирования. Часто используемые участки кода (или байт-кода) преобразуются в машинные команды, что позволяет сильно повысить производительность.
Java, Python, C#, JavaScript, Flash ActionScript — неполный (совсем неполный) список языков, в которых это используется. Я предлагаю решить конкретную задачу с использованием этой технологии и посмотреть, что получится.
Читать дальше →

«Нежданчики» языка Фортран

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

Многие из нас, обучаясь программированию ещё в университетах или дома, делали это на языках С/С++. Конечно, всё зависит от времени, в которое начиналось наше знакомство с языками программирования. Скажем, кто-то начинал с Фортрана, другие — с Basic’a или Delphi, но стоит признать, что доля начавших свой тернистый путь программиста с С/С++ наибольшая. К чему я всё это? Когда перед нами стоит задача изучить новый язык и написать на нём код, мы часто основываемся на том, как бы я это написал на своём «базовом» языке. Сузим вопрос — если нужно написать что-то на Фортране, то мы вспоминаем, как бы это было реализовано на С и делаем по аналогии. Очередной раз столкнувшись с тонкостью языка, которая привела к абсолютно неработающему алгоритму и большой проблеме, эскалированной мне, я решил отыскать как можно больше нюансов языка Фортран (Fortran 90/95), по сравнению с С, с которыми столкнулся лично. Это своего рода «нежданчики», которые ты явно не планировал увидеть, а они бац – и всплыли!
Конечно, речь не пойдёт о синтаксисе — в каждом языке он свой. Я попробую рассказать о глобальных вещах, способных изменить всё «с ног на голову». Поехали!
Читать дальше →

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