Эльбрус/lcc

Материал из ALT Linux Wiki

См. тж. Руководство по эффективному программированию на платформе «Эльбрус»

lcc на e2k

Сразу оговорюсь: речь именно о родном режиме работы lcc, кроссовым (собирать для e2k, сидя на x86) мы не пользуемся.

Основная часть проблем, возникающих при сборке рассчитанного на gcc программного обеспечения сводится к тому, что lcc -- это всё же не gcc, несмотря на выставленный __GNUC__[1]; в патчах можно проверять взведённый/заполненный __LCC__[2], хотя порой даже проще прикинуться __ICC или __clang__, у которых во многом схожие ограничения -- начиная с того, что они тоже не gcc.

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

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

проблемы

фронтэнд

Надо понимать, что МЦСТ применяет в lcc сторонний фронтэнд разработки Edison Design Group (EDG), как несколько раньше делал и Intel в своём icc.

Собственно, в основном проблемы здесь -- и с новыми версиями стандартов вроде C++20 (стабилизируется lcc 1.25[3]), и с отсутствием поддержки как некоторых расширений GNU (в первую очередь вложенных функций -- nested functions, и массивов переменной длины в структуре -- variable length array in structure, VLAIS), так и ряда языков -- Objective C, D, Ada, Go -- либо конкретных опций, специфичных для gcc или других компиляторов.

Стоит отметить, что часть "проблем" на самом деле относится именно к собираемому софту и находится в нём -- просто gcc или смотрит сквозь пальцы, ограничиваясь предупреждениями, или не делает даже их, что позволяет фактическим ошибкам оставаться в коде даже с -Werror.

бэкенд

С ним бывают проблемы в основном двух типов: неверная оптимизация или сбой самого оптимизатора.

misoptimization

Обычно замечается по странным сбоям в работе программы (особенно Illegal instruction, оно же SIGILL); диагностируется по корректности работы собранного с -O0 и/или -g0 кода; исправляется в компиляторе или обходится в коде.

Без отключения оптимизаций отладочная информация будет бесполезна: lcc её построит, но она не будет ассоциирована с кодом.

Также могут пригодиться #pragma diag_suppress и #pragma diag_default.

segfault

работа /opt/mcst/lcc-home/1.23.12/e2k-v3-linux/bin/ecf_opt64 завершена по сигналу Segmentation fault (11)

При падении компилятора остаётся только вешать отчёт об ошибке в lcc.

линкер

Между сборками binutils в ОС Эльбрус[4] и ОС Альт есть существенная разница в поведении компоновщика по умолчанию (#3675): в альтовой из соображений безопасности не задана опция -Wl,--no-warn-shared-textrel, которая отключает предупреждения о создании релокаций и совместно с опцией -Wl,--fatal-warnings может привести к сбоям сборки вида:

/usr/bin/ld: CMakeFiles/KF5CoreAddons.dir/plugin/desktopfileparser.cpp.o: warning: relocation against `_ZTISt9bad_alloc' in readonly section `.gcc_except_table'.
/usr/bin/ld: warning: creating a DT_TEXTREL in a shared object.

либо

/usr/bin/ld: CMakeFiles/openbabel.dir/mcdlutil.cpp.o: предупреждение: перемещение указывает на «_ZTISt9exception» из раздела только для чтения «.gcc_except_table».
/usr/bin/ld: предупреждение: создаётся DT_TEXTREL в общем объекте.

Проверив, что компилятору (в момент получения объектного файла) передана опция -fPIC, в качестве обходной меры можно передать -Wl,--no-warn-shared-textrel явно: в некоторых версиях компилятора известна нефатальная ошибка, которая может приводить к подобным сбоям.

OpenMP

В lcc <= 1.23 доступна реализация OpenMP 2.5, начиная с lcc-1.24 реализована OpenMP 3.1 с некоторыми ограничениями (см. /opt/mcst/doc/openmp.html). Кроме того, в старших версиях компилятора есть исправление ряда ошибок и расширение функциональности, поэтому рекомендуется использовать их.

Для собираемости могут потребоваться положенное -fopenmp вместо явного -lgomp (mcst#2483) и хак в виде подстановки переменных, содержащих значение выражения, вместо выражения:

-#pragma omp parallel sections if (a > b)
+  int c = a > b;
+#pragma omp parallel sections if (c)

Наткнувшись на ошибку "omp-регион не является замкнутым", запрашивайте обновление компилятора до 1.23.20 или выше (mcst#3639).

howto

Маленький сборник проверенных на пакетах для e2k-alt-linux рецептов.

UTF-8 BOM

Проблема (#2418): строгий фронтэнд с негодованием спотыкается на трёхбайтном маркере в начале файла, указывающем, что используется кодировка UTF-8 (обычно оставлен текстовым редактором); изменение этого поведения ожидается в версии 1.24[5], а до того может понадобиться:

%ifarch %e2k
# strip UTF-8 BOM for lcc < 1.24
find -type f -print0 -name '*.cpp' -o -name '*.hpp' -o -name '*.cc' -o -name '*.h' |
      xargs -r0 sed -ri 's,^\xEF\xBB\xBF,,'
%endif

Такие пакеты при обходе проблемы в альте обычно получают подобную запись в %changelog:

- E2K: strip UTF-8 BOM for lcc < 1.24

-std=c++11

Ошибки могут быть довольно разнообразными; скажем,

"nullptr" не определен

По умолчанию в lcc 1.23 идёт -std=c++03, как и в gcc 5.5; если код подразумевает более новый стандарт без учёта этого в системе сборки -- включаем явно:

%ifarch %e2k
# -std=c++03 by default as of lcc 1.23.20
%add_optflags -std=c++11
%endif
- E2K: explicit -std=c++11

В lcc 1.24 по умолчанию -std=c++11.

-O

Некоторые пакеты указывают уровень оптимизации сверх специфицированных (-O6, -O9, -O20): gcc такое допускает, хотя реально ставит -O3, а EDG -- нет (#2266); понижаем до заданного:

%ifarch %e2k
sed -i 's/-O6/-O%_optlevel/g' configure*
%endif
- E2K: fix superfluous optimization level

Аналогично в случаях, когда гвоздиком прибит -O2, а нам с lcc надо выше.

- E2K: fix hardwired optimization level

optimize("O0")

Проблема (#4061): lcc до версии 1.24.03 воспринимает другой вариант синтаксиса таких атрибутов -- численный[6]; обход:

%ifarch %e2k
# lcc before 1.24.03 can't do that (mcst#4061)
find -type f -print0 -name '*.c' |
       xargs -r0 sed -i 's,optimize("-O3"),optimize(3),g'
%endif

В случаях вроде

__attribute__((optimize("-fno-fast-math")))

придётся переносить опцию на уровень файла или проекта.

символьные константы

Проблема (#3940): по умолчанию символьные константы в UTF-8 не будут разобраны фронтэндом:

lcc: "static_unicode_sets.h", строка 111: ошибка: слишком
          много символов в символьной
          константе
      {RUPEE_SIGN, u'₨'},
                   ^

Добавим опцию -finput-charset=utf8:

%ifarch %e2k
# lcc 1.23.12 doesn't grok u'’' by default
%add_optflags -finput-charset=utf8
%endif
- E2K: expect UTF-8 input

__builtin

В lcc 1.23 не поддерживается ряд типично ожидаемых от заявленного gcc5 builtin'ов[7], в т.ч.: __builtin_mul_overflow_p, __builtin_constant_p, __builtin_uadd_overflow, __builtin_sub_overflow, __builtin_add_overflow.

Смысл патча обычно заключается в добавлении проверки на lcc <= 1.23 -- например, для включающих gnulib проектов:

-#if 5 <= __GNUC__ && !defined __ICC
+#if 5 <= __GNUC__ && !defined __ICC && !(defined __LCC__ && __LCC__ <= 123)
...
-#elif 5 <= __GNUC__ && !defined __ICC && !__STRICT_ANSI__
+#elif 5 <= __GNUC__ && !defined __ICC && \
+         !(defined __LCC__ && __LCC__ <= 123) && !__STRICT_ANSI__

Также не поддерживается vector_shuffle (#3982 о libfreetype >= 2.9) -- пока неясно, будет ли реализация в lcc.

int128

int128_t/uint128_t поддержаны начиная с lcc 1.24 (#1802); для 1.23 и более ранних веток применяем аналогичные вышеизложенным для __builtin_* обходы либо прикидываемся 32-битной платформой с максимум 64-битными целыми, смотря по ситуации (такие в сизифе оказались довольно редки, апстрим libtommath патчик уже принял).

FP*/Decimal*

См. обсуждение на форуме:

Аппаратно поддержаны FP32 (float), FP64 (double), FP80 (long double, __float80). У FP128 (__float128) поддержка программная. Всё симметрично во всех моделях процессоров.

Для FP256 и Decimal'ов (Decimal64, Decimal128...) и вообще для какого угодно формата можно было бы сделать программную поддержку, но проблема упирается в то, что покупной фронтенд edg, на базе которого построен компилятор lcc, данные типы не поддерживает.

R_E2K_32_ABS

При столкновении с ошибкой вида

relocation truncated to fit: R_E2K_32_ABS against `.debug_info'

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

В альтовом spec-файле это может выглядеть так, если сборочная система проекта обращает внимание на CFLAGS/CXXFLAGS (или так):

# due to R_E2K_32_ABS debuginfo truncation, cf.: webkit
%remove_optflags -g
%add_optflags -g0

Для новых сборок пакетов с библиотеками полное отключение отладочной информации может быть неприятным вдвойне, т.к. на их debuginfo-части наверняка будут зависимости у других пакетов и может потребоваться каскадное отключение *-debuginfo. По возможности просто снижайте уровень (на "ругань" %remove_optflags можно не обращать внимания).

bugreport

Пишем на user@mcst.ru заявку на регистрацию в системе отслеживания ошибок (с рабочего адреса и указав серийный номер используемого "Эльбруса" либо сообщив о применении удалённого доступа).

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

Если произошёл сбой компиляции, к отчёту об ошибке стоит приложить препроцессированный исходник (-E) и строчку запуска -- например,

g++ -Wall -O2  -DNDEBUG -std=c++11 -c -I ./include/   ./core/xhtmlgenerator.cpp

...превращается в:

$ g++ -Wall -O2 -DNDEBUG -std=c++11 -I ./include/   ./core/xhtmlgenerator.cpp -E -o test.pp.cpp

Если в исходной строке запуска была указана опция -o с именем бинарного объекта, её стоит удалить.

особенности

система программирования

см. тж. mcst.ru/sdk

Для корректной работы следует придерживаться проверенных МЦСТ комбинаций Linux, glibc, lcc и binutils -- смежные версии могут работать (особенно в окрестностях точек перехода), но далее разработчики закладываются на особенности новых lcc в новых ядрах и т.д.; например, вот эта диагностика похожа на признак "перекоса" установки (получена от lcc 1.25 под Linux 4.9):

Assembler messages: Error: literals cannot be packed into a wide command
Error: cannot place literals
ядро Linux glibc lcc binutils
5.4 2.29 1.25 2.34
4.19 2.29 1.24 2.34
4.9 2.23 1.23 2.29
3.14 2.23 1.21 2.26

libcxa

lcc до 1.23 требовал явной линковки libcxa к C++-программам, иначе можно было получить один из характерных симптомов (#1811):

undefined reference to `__cxa_vec_ctor'

либо в случае подключаемых модулей, так или иначе слинкованных с C++-кодом (особенность ветки 1.21):

cannot allocate memory in static TLS block

Рекомендуемый разработчиками компилятора обход обеих проблем при невозможности перехода на lcc >= 1.23 -- принудительная линковка такой программы с -lcxa; в случае плагинов линковать требуется то, к чему они линкуются, причём "до упора" (т.е. если имеем C++-плагин к mod_php к apache, то линковать так придётся именно apache). Подчас оказывалось достаточно export LIBS=-lcxa перед запуском autoreconf и configure.

В целом же лучше перейти на 1.23+, где помимо доработок по части zero cost exceptions и поддержки стандартов внедрено множество иных улучшений и оптимизаций. Обратите внимание, что добавленные -lcxa в этом случае обязательно убрать.

Обратите внимание, что для некоторых случаев C++-кода, который написан так, чтобы линковаться gcc вместо g++[8], на 1.23 может потребоваться другой обход -- компоновка с -lsupc++ -lgcc_eh -llcc из libstdc++5-devel-static.

оптимизация

По умолчанию lcc собирает без оптимизации (-O0), что удобно для отладки. Для релизов и предназначенных для использования версий настоятельно рекомендуется -O3; при этом прыгать на четвёртый уровень бездумно не стоит, т.к. там выключен gos-solver, что может привести к сильному раздуванию кода и обратному эффекту -- понижению производительности полученного бинарника; посмотрите сперва внимательней на -fwhole.

При отладке стоит понижать уровень оптимизации до -O0 и порой отключать генерацию отладочной информации (-g0, см. выше).

-O3

Сборочные системы приложений обычно предполагают -O2, а порой вдобавок игнорируют выставленные CFLAGS/CXXFLAGS; собранные так программы могут работать на e2k медленней, чем способны при -O3.

-fwhole

Данный режим работы объединяет все модули программы в один большой модуль, что позволяет обходить ограничения классической помодульной сборки проекта и применять межпроцедурные оптимизации для процедур, находящихся в разных модулях. Режим похож на -flto у gcc и llvm, но обладает совершенно иной технической реализацией; по этой причине всё связанное с -flto (например, -fuse-linker-plugin, #4020) следует удалять из сборки под e2k.

Оптимизация очень сильная: разница между -O3 и -O3 -fwhole обычно больше, чем между -O2 и -O3. Но эту опцию нельзя просто так применять: в зависимости от ситуации нужно выбирать между -fwhole и -fwhole-shared для конкретных файлов, иногда вовсе нельзя: -fwhole можно применять для исполняемых файлов, но нельзя для динамических библиотек (-shared); для последних есть -fwhole-shared, но в ней есть смысл только совместно с -fvisibility=protected, что встречается редко. Опция обязательно должна подаваться не только на стадии компиляции, но и на финальной линковке. Несовместима с -g (#5104) и некоторыми расширениями GNU, см. документацию компилятора.

С точки зрения оптимизаций данный режим имеет большое влияние на любые межпроцедурные оптимизации: подстановка функций, анализы указателей, распространение констант, девиртуализация и т.д. Косвенным образом влияет и на внутрипроцедурные оптимизации, т.к. расширяет количество информации о коде. Для этого режима важно соблюдать ODR (One Definition Rule), т.е. не допускать наличия классов или объектов с одним именем, но различной реализацией.

Lcc-performance.jpg

версии

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

ссылки

примечания

  1. соответственно заявленной в `lcc -v` совместимой версии
  2. __LCC__ <= 123, например
  3. см. тж. вкладку "Характеристики" на mcst.ru/lcc:
    1.24 поддерживает C++11/C++14 и частично C++17;
    1.23 -- C++11 и частично C++14,
    1.21 -- частично C++11
  4. ...где "как в апстриме"
  5. lcc 1.23.17 обучен опции --ignore-utf8-bom, но её не будет в 1.24.x.
  6. это ограничение соответствующей версии фронтэнда EDG -- или символьный, или целочисленный вариант должен быть выбран при сборке компилятора; в случае lcc исторически был выбран численный
  7. исправлено в 1.24, но тот представляется gcc7, от которого ожидают ещё более новых builtin'ов, в свою очередь
  8. и которому это удаётся из-за умения GCC встраивать вызов __cxa_vec_ctor; например, libgraphite2