Эльбрус/llvm
LLVM
В современных версиях ОС Эльбрус имеется LLVM 9 (начиная с OSL 7.2 -- и llvm13), который может собирать код не только для платформ AMDGPU и с недавнего времени NVPTX (nVidia), но и для e2k. К примеру, для ОС Эльбрус 6.0.1:
molchan_i@yukari ~ $ head -n1 /etc/mcst_version 6.0.1 molchan_i@yukari ~ $ llvm-config --version 9.0.1 molchan_i@yukari ~ $ llvm-config --targets-built Elbrus AMDGPU NVPTX
clang
В состав ОС Эльбрус также входит clang как экспериментальная альтернатива lcc, который также в некотором смысле умеет собирать ПО под Эльбрус. На данный момент он не считается доведённым до финала разработки и может быть использован на собственный страх и риск пользователя, но в некоторых случаях может оказаться предпочтителен (например, telegram-desktop 3.2 не удалось собрать lcc 1.25, в отличие от clang 9.0.1; при этом версию 4.3 собрали lcc 1.26).
Для примера - сборка архиватора plzip:
1. Выкачиваем исходники и распаковываем их:
wget http://download.savannah.gnu.org/releases/lzip/lzlib/lzlib-1.12.tar.gz wget http://download.savannah.gnu.org/releases/lzip/plzip/plzip-1.9.tar.gz tar xf plzip-1.9.tar.gz tar xf lzlib-1.12.tar.gz
2. Создаём тестовый файл (заполненный рандомными значениями)
dd if=/dev/urandom of=testfile count=1k bs=1k
3.1. Сборка (-O2):
cd lzlib-1.12 ./configure CC=clang make -j4 cd ../plzip-1.9 ./configure CXX=clang++ CXXFLAGS="-O2 -I../lzlib-1.12" LDFLAGS=-L../lzlib-1.12 make -j8
3.2. Запуск (-O2):
molchan_i@yukari ~/clang-demo/plzip-1.9 $ time ./plzip -fvk9 ../testfile ../testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m2,165s user 0m2,071s sys 0m0,101s
Потом делаем cd ..
.
4.1. Сборка (-O4):
cd lzlib-1.12 ./configure CC=clang CFLAGS="-O4" make -j4 cd ../plzip-1.9 ./configure CXX=clang++ CXXFLAGS="-O4 -I../lzlib-1.12" LDFLAGS=-L../lzlib-1.12 make -j8
4.2. Запуск (-O4):
molchan_i@yukari ~/clang-demo/plzip-1.9 $ time ./plzip -fvk9 ../testfile ../testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m2,390s user 0m2,308s sys 0m0,091s
Потом делаем cd ..
.
Вывод: указание оптимизаций не влияет на быстродействие (результат на -O4 оказался даже хуже).
Для сравнения, тот же plzip, но собранный без указания CC=clang
и CXX=clang++
(то есть, штатным lcc):
В режиме -O2:
molchan_i@yukari ~/clang-demo/plzip-1.9-lcc $ time ./plzip -fvk9 ../testfile ../testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m0,811s user 0m0,743s sys 0m0,070s
В режиме -O4:
molchan_i@yukari ~/clang-demo/plzip-1.9-lcc $ time ./plzip -fvk9 ../testfile ../testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m0,692s user 0m0,598s sys 0m0,101s
Видно, что из коробки без настроек lcc даёт код, работающий быстрее в 2,7-3,5 раза, но когда что-то не собирается lcc (но при этом собирается clang-ом) - это хотя бы может дать возможность собрать и запустить что-то, что хочется запустить на эльбрусе.
llvm-cbe
Была попытка для сравнения использовать C-backend для LLVM - llvm-cbe.
Суть в том, что если штатный компилятор не может собрать исходный код (например, он ломается на этом коде, либо вообще нет компилятора для этого языка), мы преобразовываем исходный код в LLVM-IR, его с помощью llvm-cbe - в С-код (который сгенерён из LLVM-представления), а потом получившийся код собираем уже штатным С-компилятором.
Это выглядит примерно так:
clang -S -emit-llvm -g входной_файл.c -o файл_llvm_представления.ll # Здесь может быть любой компилятор, генерирующий LLVM-IR-выхлоп (.ll-файл) llvm-cbe файл_llvm_представления.ll # Из *.ll будет создан *.cbe.c gcc -c -o выходной_объектный_файл.o файл_llvm_представления.cbe.c
На Intel-машине выяснено, что нынешний вариант умеет собираться только под LLVM 8, 9, 10 и 11 (6 и 7 слишком старые). Изначально он умел только в LLVM 10 и 11, но после принятия PR #131 LLVM 8-9 также починились. К счастью, на Эльбрусе есть LLVM 9.0.1, так что нам этого достаточно.
1. Выкачиваем исходники:
git clone https://github.com/JuliaComputing/llvm-cbe cd llvm-cbe
2. Собираем для каждого из возможных LLVM и прогоняем unit-тесты
for i in 8 9 10 11 do echo -e "\n\e[1;33m************** $i ***************\n\e[0m" mkdir -p build-$i pushd build-$i cmake .. -DLLVM_DIR=/usr/lib/llvm-$i/cmake -DLLVM_INCLUDE_TESTS=1 && make -j16 llvm-cbe CBEUnitTests pushd unittests ./CWriterTest popd popd done
3. Пробуем собрать и выполнить один из примеров (в ./main.cbe нужно будет ввести количество элементов и собственно сами элементы для сортировки):
for i in 8 9 10 11 do echo -e "\n\e[1;33m************** $i ***************\n\e[0m" pushd build-$i clang-$i -S -emit-llvm -g ../test/selectionsort/main.c tools/llvm-cbe/llvm-cbe main.ll gcc-10 -Wno-builtin-declaration-mismatch -o main.cbe main.cbe.c ./main.cbe rm -f main.ll main.cbe main.cbe.c popd done
4. Попытка собрать lzlib и plzip
for i in 8 9 10 11 do echo -e "\n\e[1;33m************** $i ***************\n\e[0m" pushd build-$i # Собираем объектник lzlib: clang-$i -S -emit-llvm -g ../lzlib-1.12/lzlib.c -I../lzlib-1.12 -o lzlib.ll tools/llvm-cbe/llvm-cbe lzlib.ll gcc-10 -Wno-builtin-declaration-mismatch -c -o lzlib.o lzlib.cbe.c # Собираем plzip (вручную): for src in arg_parser lzip_index list compress dec_stdout dec_stream decompress main do # Увы, из-за issue-в, указанных ниже, собрать через llvm-cbe нельзя - собираем с помощью gcc # clang++-$i -S -emit-llvm -g -I../plzip-1.9 -I../lzlib-1.12 -DPROGVERSION='"1.9-llvm-cbe"' -o $src.ll ../plzip-1.9/$src.cc # tools/llvm-cbe/llvm-cbe $src.ll # gcc-10 -Wno-builtin-declaration-mismatch -Wno-address-of-packed-member -c -o $src.o $src.cbe.c gcc -I./plzip-1.9 -I../lzlib-1.12 -DPROGVERSION='"1.9-llvm-cbe"' -c -o $src.o ../plzip-1.9/$src.cc done # Линкуемся с в том числе lzlib.o: g++ arg_parser.o lzip_index.o list.o compress.o dec_stdout.o dec_stream.o decompress.o main.o lzlib.o -lpthread -o plzip ./plzip -kv ../testfile -o testfile-$i.lz ./plzip -tv testfile-$i.lz popd done
Из за нескольких проблем (issue 132, 133, 134, 138) llvm-cbe не может собрать всё целиком, поэтому для исходников plzip приходится использовать обычный gcc/lcc.
Видим, что все собранные plzip-ы отработали нормально.
5. И наконец, с учётом полученной информации, собираем под эльбрус. LCC не понимает комментария препроцессора "line 0", поэтому в CBE-выхлопе его надо заменять:
git clone https://github.com/JuliaComputing/llvm-cbe cd llvm-cbe mkdir -p build cd build cmake .. && make -j16 llvm-cbe wget http://download.savannah.gnu.org/releases/lzip/lzlib/lzlib-1.12.tar.gz wget http://download.savannah.gnu.org/releases/lzip/plzip/plzip-1.9.tar.gz tar xf plzip-1.9.tar.gz tar xf lzlib-1.12.tar.gz dd if=/dev/urandom of=testfile count=1k bs=1k export OPT=-O2 clang -S -emit-llvm -g lzlib-1.12/lzlib.c -Ilzlib-1.12 -o lzlib.ll tools/llvm-cbe/llvm-cbe lzlib.ll sed -i 's/#line 0/#line 1/' lzlib.cbe.c lcc $OPT -c -o lzlib.o lzlib.cbe.c for src in arg_parser lzip_index list compress dec_stdout dec_stream decompress main do l++ $OPT -Iplzip-1.9 -Ilzlib-1.12 -DPROGVERSION='"1.9-llvm-cbe"' -c -o $src.o plzip-1.9/$src.cc done l++ $OPT arg_parser.o lzip_index.o list.o compress.o dec_stdout.o dec_stream.o decompress.o main.o lzlib.o -lpthread -o plzip time ./plzip -fvk9 testfile
Результаты (-O2):
molchan_i@yukari ~/clang-demo/llvm-cbe/build $ time ./plzip -fvk9 testfile testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m1,412s user 0m1,338s sys 0m0,081s
Результаты (-O4; для этого заменяем export OPT=-O2
на export OPT=-O4
):
molchan_i@yukari ~/clang-demo/llvm-cbe/build $ time ./plzip -fvk9 testfile testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m1,385s user 0m1,293s sys 0m0,081s
Результаты (без указания оптимизаций; для этого заменяем export OPT=-O2
на export OPT=
):
molchan_i@yukari ~/clang-demo/llvm-cbe/build $ time ./plzip -fvk9 testfile testfile: 0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062887 out. real 0m7,569s user 0m7,517s sys 0m0,062s
Внезапно, с оптимизациями получилось даже быстрее, чем если собирать нативным clang-ом. А вот без оптимизаций результат в 10 раз медленнее нативного lcc.