Pull to refresh
10
0
Олег Лысый @Minatych

Программист

Send message

Немного вмешаюсь.

  1. Соглашусь, что читать сообщения об ошибках в шаблонах бывает сложно, но это проявляется в сложных шаблонах. В большинстве случаев это дело привычки.

  2. Это от части заблуждение. Тем более в этом случае. Инстанцировать шаблон, скопировав ветку ast и подставив типы раз в 100 быстрее, чем парить 16 деклараций конструкторов класса с учётом всех неоднозначностей грамматики и семантики языка. Если очень хочется, можно написать явные инстанцирования, или явные специализации в cpp файле.

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

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

Первая - оно достаточно объемное для того, чтобы нельзя было сохранять его как есть (2-5 гб в среднем на каждый файд, в зависимости от исходника). Мало того, что если в проекте файлов 500 представление не поместится на диск, так и читать его в общую память будет проблематично.

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

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

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

Приведу текст из статьи как раз про эту проблему

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

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

Тоже верно) Тут только практика покажет.

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

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

Была такая идея. Можно совместить подходы)

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

Согласен, примеров маловато.

Суть в том, что оптимизатор с включённым LTO применят те же оптимизации, что и без него. Разница в том, что ему доступно больше информации.

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

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

Например, ничего не мешает ему встраивать тела функций, и потом в них раскручивать циклы, удалять ненужные проверки, или целые ветки неиспользуемого (или бессмысленного) кода. Или, например выполнять Compile Time вычисления.

Без включенного LTO это тоже будет работать, но только если все определения будут находиться в одной и той же единице трансляции.

Всё зависит от структуры ваших проектов. Если они связаны одним CMake-скриптом на верхнем уровне, то межмодульный анализ и так отработает нормально.

Если у вас 2 отдельных, несвязанных друг с другом CMake-проекта, то всё сложнее. По умолчанию мы не разбираем CMake скрипты. Вместо этого используется сгенерированные последними файлы compile_commands.json, в которых содержатся все команды компиляции. Для каждой такой команды генерируется модуль результатов семантического анализа (DFO). Но, в compile_commands.json нет команд для компоновщика. Поэтому отследить внешние для вашего проекта библиотеки оттуда не получится.

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

Что я имел в виду под ручным способом. C и C++ анализаторы PVS-Studio представлены ядром и набором вспомогательных утилит командной строки. Ядро - основная программа (PVS-Studio.exe для Windows и pvs-studio для Linux/macOS). Оно запускается в разных режимах, которые настраиваются через конфигурационные файлы. Для каждой единицы трансляции нужно протолкнуть соответствующий конфигурационный файл. Чтобы активировать межмодульный анализ, нужно в этих файлах прописать шаг, который вы выполняете. Всего их 3:

  • сбор семантической информации

  • слияние DFO модулей

  • запуск анализа с использованием объединённого DFO-файла

Помимо этого, в конфигурационных файлах прописывается и другая важная информация, например версия стандарта, платформа, препроцессор и т. д. Вручную писать их неудобно. Для этого мы и создали утилиты pvs-studio-anayzer (для Linux), или CompilerCommandsAnalyzer.exe для Windows. Их, к слову, использует плагин для CLion. А для анализа .sln используется PVS-Studio_Cmd.exe.

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

 

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

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

В теории, нет ничего сложного в том, чтобы объединить межмодульную информацию для вашего проекта и стороннего, если они в реальности не нарушают ODR. Это хорошая идея для доработки интерфейсной части анализатора, но, пока что это придётся делать вручную (через ядро pvs-studio).

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

Дело в том, что мы не используем фронтенд какого либо компилятора для трансляции C++ кода и не можем взять уже готовое AST. PVS-Studio анализатор поддерживает несколько компиляторов, в том числе для Embedded разработки с их расширениями, и нам не подойдёт какой то конкретный из них.

Information

Rating
Does not participate
Location
Тула, Тульская обл., Россия
Works in
Date of birth
Registered
Activity