Эльбрус/llvm

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

LLVM

В современных версиях ОС Эльбрус имеется LLVM 9, который может собирать код не только для платформ 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, который также умеет собирать ПО под Эльбрус.

Для примера - сборка архиватора 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. Сборка:

   cd lzlib-1.12
   ./configure CC=clang
   make -j4
   cd ../plzip-1.9
   ./configure CXX=clang++ CXXFLAGS=-I../lzlib-1.12 LDFLAGS=-L../lzlib-1.12
   make -j8

4. Запуск:

   molchan_i@yukari ~/clang-demo/plzip-1.9 $ time ./plzip -fvk9 ../testfile
     ../testfile:  0.987:1, 101.37% ratio, -1.37% saved, 1048576 in, 1062911 out.
   
   real    0m2,287s
   user    0m2,280s
   sys     0m0,010s

Для сравнения, тот же plzip, но собранный без указания CC=clang и CXX=clang++ (то есть, штатным lcc):

   molchan_i@yukari ~/clang-demo/plzip-1.9-lcc $ time ./plzip -fvk9 ../testfile
     ../testfile:  0.987:1, 101.37% ratio, -1.37% saved, 1048576 in, 1062911 out.
   
   real    0m0,823s
   user    0m0,800s
   sys     0m0,030s

Видно, что из коробки без настроек lcc даёт код, работающий быстрее в 2,5-3 раза, но когда что-то не собирается 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.

Видим, что все собранные plzip-ы отработали нормально.

4. И наконец, с учётом полученной информации, собираем под эльбрус. 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
   
   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 -c -o lzlib.o lzlib.cbe.c
   
   for src in arg_parser lzip_index list compress dec_stdout dec_stream decompress main
   do
       l++ -Iplzip-1.9 -Ilzlib-1.12 -DPROGVERSION='"1.9-llvm-cbe"' -c -o $src.o plzip-1.9/$src.cc
   done
   
   l++ 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

Результаты:

   molchan_i@yukari ~/clang-demo/llvm-cbe/build $ time ./plzip -fvk9 testfile
     testfile:  0.987:1, 101.36% ratio, -1.36% saved, 1048576 in, 1062838 out.
   
   real    0m8,134s
   user    0m8,080s
   sys     0m0,060s

Что ж, в 10 раз медленнее lcc, но иногда другого выхода нет.