понедельник, 23 июля 2018 г.

[C++] Что такое warning C4291 и как с ним бороться

Самой яркой особенностью языка программирования C++ перед привычной мне Java является необходимость обеспечивать ручное управление памятью и на этом пути разработчика поджидает множество интересных особенностей. Например, если мы переопределяем оператор new, снабдив его нужными исключительно нам аргументами (так называемая class-specific placement allocation functions), то необходимо подобным же образом переопределить и оператор delete (имеется ввиду, что нужно определить оператор delete, имеющий сигнатуру void operator delete(void *ptr, user-defined-args...), иначе при возникновении исключительной ситуации в конструкторе класса будет не понятно какой оператор delete следует вызывать, чтобы освободить уже выделенную для создаваемого объекта данного класса память. Произойдет утечка памяти.

Предупреждение компилятора C4291


К счастью компилятор MSVC информирует разработчика об опасности, выбрасывая исключение C4291 no matching operator delete found; memory will not be freed if initialization throws an exception (код должен компилироваться с флагами компиляции /EHsc /W1). Например:

[1/2] Building CXX object src\memory\CMakeFiles\placement-new-delete.dir\PlacementNewDelete.cpp.obj
..\src\memory\PlacementNewDelete.cpp(67): warning C4291: 'void *MyClassA::operator new(size_t,MyAllocator &)': no matching operator delete found; memory will not be freed if initialization throws an exception
..\src\memory\PlacementNewDelete.cpp(32): note: see declaration of 'MyClassA::operator new'
[2/2] Linking CXX executable src\memory\placement-new-delete.exe

Подробно предупреждение компилятора C4291 описано в одноименной статье на MSDN.

Проблема заключается в том, что реализовать соответствующий оператор delete может быть не так то и просто. Как нетрудно заметить, оператор new в любом случае принимает аргумент size - размер требуемой памяти в байтах, оператор же delete такого параметра не принимает, а он может использоваться при выделении памяти и быть необходим, чтобы вернуть ее операционной системе.

Использование аллокатора для размещения объектов в памяти


Рассмотрим такой паттерн: использование для выделения памяти стороннего аллокатора, реализующего два метода: allocate и deallocate, каждый из которых принимает на вход параметр size_t size. Такой код довольно часто встречается в реализации JIT-компилятора в наборе компонентов для построения виртуальных машин Eclipse OMR, из чего я сделал вывод, что паттерн довольно распространен.

вторник, 17 июля 2018 г.

IBM открывает себя: WebSphere с открытым исходным кодом на JVM с открытым исходным кодом

Давно не писал заметки в блог, но есть новость и ей хочется поделиться: мой первый (и, я очень надеюсь - не последний), вклад в продукт с открытым исходным кодом Eclipse OpenJ9 был принят сообществом. Это - повод, а рассказать в данной заметке я хочу о проектах с открытым кодом, вышедших за последние годы из недр корпорации IBM. Возможно некоторые из них будут интересны вам лично или вашему бизнесу.

  • OpenLiberty - открытый сервер приложений Java EE, на базе него собирается WebSphere Liberty Profile. В качестве системы сборки используется Gradle (тесно интегрированная с bndtools, т.к. проект целиком и полностью основан на OSGi).

  • OpenJ9 - JVM с открытым исходным кодом, построенная на базе библиотеки классов из OpenJDK и другого проекта - OMR, содержащего кросс-платформенные компоненты для построения надежной и высокопроизводительной среды исполнения (т.е. на базе OMR можно построить не только JVM, но и среду исполнения для Python, Ruby или любого другого языка программирования). Какие это компоненты? Сборщик мусора, JIT-компилятор, библиотека общих кросс-платформенных функций, профайлер и т.д.

OpenJ9 и OMR открыты под эгидой сообщества Eclipse Foundation.

Теперь несколько слов о том, как собрать OpenJ9. Стоит сказать большое спасибо разработчикам за том, что они опубликовали довольно подробную инструкцию по сборке проекта с использованием вспомогательного Docker-образа и утилиты Make. Инструкция для JDK 8 может быть найдена по ссылке: Build_Instructions_V8.md, а для JDK 9 - Build_Istructions_V9.md. Обе инструкции подразумевают создание вспомогательного Docker-контейнера, содердащего компилятор C++, линковщик и все необходимые утилиты, библиотеки и заголовочные файлы. Т.е. можно не "захламлять" основное рабочее окружение если, например, разработка на C++ не является вашей основной деятельностью, а вам просто нужна свежая сборка JVM для тестов или экспериментов.

среда, 23 мая 2018 г.

Собираем свой собственный пасс компилятора под Windows как часть проекта LLVM

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

1. Нужно скопировать каталог с исходными кодами своих пассов (буду называть его LLVMExperimentPasses) в lib/Transforms (здесь и далее пути даны относительно корня каталога с исходными кодами LLVM, предполагается, что вы уже его зачекаутили, например с GitHub-зеркала).

2. Добавить команду add_subdirectory(LLVMExperimentPasses) в файл lib/Transforms/CMakeLists.txt.

3. Для каждого созданного пасса добавить функцию с сигнатурой initialize${ИМЯ ПАССА}Pass(PassRegistry &); в заголовочный файл include/llvm/InitializePasses.h. Также следует добавить туда функцию с сигнатурой void initializeLLVMExperimentPasses(PassRegistry &);. Для пасса FunctionArgumentCount следует добавить в заголовочный файл такие строки:

// my experiment passes
void initializeLLVMExperimentPasses(PassRegistry &);
void initializeFunctionArgumentCountPass(PassRegistry &);
} // end namespace llvm
(функция обязательно должна быть определена внутри пространства имен llvm).

4. Добавить библиотеку LLVMExperimentPasses в список LLVM_LINK_COMPONENTS в файле tools/opt/CMakeLists.txt:

set(LLVM_LINK_COMPONENTS
    ${LLVM_TARGETS_TO_BUILD}
    AggressiveInstCombine
    ...
    ExperimentPasses
)
Важно: Библиотека перечисляется как ExperimentPasses, а не LLVMExperimentPasses.

5. Зарегистрировать пассы в исходном коде утилиты opt, добавив вызов функции initializeLLVMExperimentPasses в функцию main данной утилиты (в файле tools/opt/opt.cpp):

// Initialize passes
PassRegistry &Registry = *PassRegistry::getPassRegistry();
initializeCore(Registry);
...
initializeLLVMExperimentPasses(Registry);
// For codegen passes, only passes that do IR to IR transformation are
// supported.
6. Пересобрать LLVM (здесь YOUR_LLVM_BUILDTREE - каталог, в котором вы собираете вашу копию LLVM) и установить новые библиотеки и утилиты:

cd YOUR_LLVM_BUILDTREE

cmake -DCMAKE_CXX_COMPILER=YOUR_FAVOURITE_COMPILER -DCMAKE_C_COMPILER=YOUR_FAVOURITE_COMPILER -DCMAKE_LINKER=YOUR_FAVOURITE_LINKER .. -G"Ninja"

cmake --build .

cmake --build . --target install
Все, пассы готовы к использованию. Например, пасс FunctionArgumentCount из моего примера будет зарегистрирован под именем fnargcnt и может быть вызван с помощью утилиты opt:

opt.exe -fnargcnt < sum.bc > sum-out.bc

Понравилось сообщение - подпишитесь на блог