Так что периодически я пробую LLVM, так как у меня есть эта теория, она должна превосходить GNU. И тогда это, к сожалению, нет.
Часть теории связана с его способностью связывать модули / объекты вместе и затем оптимизировать, где обычно происходит оптимизация для каждого файла / объекта.
Вместо использования обобщенного c один, я вижу, как построить для заданного c целевого объекта по умолчанию
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-10.0.0
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS='clang;lld' -DCMAKE_CROSSCOMPILING=True -DCMAKE_INSTALL_PREFIX=/opt/llvm/llvm10armv6m -DLLVM_DEFAULT_TARGET_TRIPLE=armv6m-none-eabi -DLLVM_TARGET_ARCH=ARM -DLLVM_TARGETS_TO_BUILD=ARM -G "Unix Makefiles" ../llvm
make -j 8
make -j 4
make
sudo make install
и тестовых файлов
test. c
unsigned int one ( void )
{
return(1);
}
unsigned int two ( void );
unsigned int testone ( void )
{
return(one());
}
unsigned int testtwo ( void )
{
return(two());
}
два. c
unsigned int two ( void )
{
return(2);
}
basi c run
clang -O2 -fomit-frame-pointer -c test.c -o test.o
llvm-objdump -D test.o
00000000 one:
0: 01 20 movs r0, #1
2: 70 47 bx lr
00000004 testone:
4: 01 20 movs r0, #1
6: 70 47 bx lr
00000008 testtwo:
8: 80 b5 push {r7, lr}
a: ff f7 fe ff bl #-4
e: 80 bd pop {r7, pc}
, как и следовало ожидать, one () был встроен в testone ().
Желание также сделать testwo () встроенным.
clang -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
llc both.bc -o both.s
cat both.s
opt -O2 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
дает
testone:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl one
pop {r7, pc}
testtwo:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl two
pop {r7, pc}
и
testone:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl one
pop {r7, pc}
testtwo:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl two
pop {r7, pc}
, что хуже.
opt -std-link-opts both.bc -o both.opt.bc
то же самое, не лучше
Теперь это работает
clang -O2 -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -O2 -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
opt -O2 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
testone:
.fnstart
@ %bb.0: @ %entry
movs r0, #1
bx lr
testtwo:
.fnstart
@ %bb.0: @ %entry
movs r0, #2
bx lr
Можно было бы подумать, что неоптимизация частей даст больше мяса для оптимизации всего пережевывания. Да? Хотя это указывает на иное.
clang -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
opt -O3 both.bc -o both.opt.bc
llc both.opt.bc -o both.opt.s
cat both.opt.s
testone:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl one
movs r0, #1
pop {r7, pc}
testtwo:
.fnstart
@ %bb.0: @ %entry
.save {r7, lr}
push {r7, lr}
bl two
movs r0, #2
pop {r7, pc}
-O3 тоже не помогает, и этот вывод настолько же плох, что вызывает функцию И вставляет ее. Что там происходит?!
llvm-dis both.opt.bc
cat both.opt.ll
; ModuleID = 'both.opt.bc'
source_filename = "llvm-link"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv6m-none-unknown-eabi"
; Function Attrs: noinline nounwind optnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
%call = call i32 @one()
ret i32 1
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @testtwo() local_unnamed_addr #0 {
entry:
%call = call i32 @two()
ret i32 2
}
; Function Attrs: noinline nounwind optnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
ret i32 2
}
Как отменить это?
clang -O2 -fomit-frame-pointer -c -emit-llvm test.c -o test.bc
clang -O2 -fomit-frame-pointer -c -emit-llvm two.c -o two.bc
llvm-link test.bc two.bc -o both.bc
llvm-dis both.bc
cat both.ll
opt -O3 both.bc -o both.opt.bc
llvm-dis both.opt.bc
cat both.opt.ll
дает
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: nounwind
define dso_local i32 @testtwo() local_unnamed_addr #1 {
entry:
%call = tail call i32 @two() #2
ret i32 %call
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
ret i32 2
}
и
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testtwo() local_unnamed_addr #0 {
entry:
ret i32 2
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @two() local_unnamed_addr #0 {
entry:
ret i32 2
}
Так это правильно, что вы должны применять оптимизации везде, на уровне файлов / объектов, чтобы оптимизировать уровень проекта?
И тогда возникает вопрос о хвостовом вызове или листе, и т. Д. * Оптимизация 1089 *, если не что иное, testtwo: даже в первом случае
clang -O2 -fomit-frame-pointer -c test.c -o test.o
может просто переходить к two () и не устанавливать кадр стека, не делая ничего из этого. Или это большой палец? b не могу добраться?
one:
0: b8 01 00 00 00 movl $1, %eax
5: c3 retq
testone:
10: b8 01 00 00 00 movl $1, %eax
15: c3 retq
testtwo:
20: e9 00 00 00 00 jmp 0 <testtwo+5>
В GNU компоновщик исправляет любые проблемы с ветками или проблемы с батутами
arm-none-eabi-gcc -c -O2 -mcpu=cortex-m0 test.c -o test.o
arm-none-eabi-objdump -D test.o
00000000 <one>:
0: 2001 movs r0, #1
2: 4770 bx lr
00000004 <testone>:
4: 2001 movs r0, #1
6: 4770 bx lr
00000008 <testtwo>:
8: b510 push {r4, lr}
a: f7ff fffe bl 0 <two>
e: bd10 pop {r4, pc}
Хорошо, я исправлен ...
clang --version
clang version 10.0.0 (https://github.com/llvm/llvm-project.git d32170dbd5b0d54436537b6b75beaf44324e0c28)
Target: armv6m-none-unknown-eabi
Thread model: posix
InstalledDir: /opt/llvm/llvm10armv6m/bin
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Полагаю, вопрос в том, хочет ли кто-нибудь выполнить оптимизацию на уровне проекта, используя llvm-link и opt, является ли оптимизация каждого отдельного элемента обязательной или есть опция командной строки, которую я пропускаю. Не интересует спецификация компилятора c атрибутов, которые go в самом исходном коде, не хочет, чтобы код был заражен ни спецификой g cc, ни llvm.
После g cc 5.xx код получил более раздутые надеялись, что у llvm будет шанс, но всякий раз, когда я пытаюсь это сделать (в проектах не только 10 строк кода), g cc заканчивается меньшим количеством выполненных инструкций и / или меньшим количеством обращений к памяти, et c, et c. Для простых демонстрационных функций, подобных приведенным выше, за некоторыми исключениями они выдают одинаковый / эквивалентный результат.
Есть ли что-то, еще один инструмент или опции командной строки, которые мне не хватает, чтобы получить больше от clang / llvm?
Это слишком тривиально пример для инструмента?
РЕДАКТИРОВАТЬ на основе ответа
clang -c start.s -o start.o
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
clang -O2 -flto=thin -fomit-frame-pointer -c two.c
ld.lld start.o test.o two.o -o test.elf
llvm-objdump -D test.elf
000110fc testtwo:
110fc: 02 20 movs r0, #2
110fe: 70 47 bx lr
00011100 two:
11100: 02 20 movs r0, #2
11102: 70 47 bx lr
, поэтому избавление от -emit-llvm и использование lto в основном дает желаемый результат.
Просмотр разборки b c
clang -O2 -flto=thin -fomit-frame-pointer -c test.c
llvm-dis test.o
cat test.o.ll
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @one() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: norecurse nounwind readnone
define dso_local i32 @testone() local_unnamed_addr #0 {
entry:
ret i32 1
}
; Function Attrs: nounwind
define dso_local i32 @testtwo() local_unnamed_addr #1 {
entry:
%call = tail call i32 @two() #3
ret i32 %call
}
включает / добавляет хвостовой вызов. Мне очень не нравится использовать компилятор / оболочку в качестве компоновщика (для встроенных проектов, которые имеют свой собственный bootstrap и скрипт компоновщика), использование llvm-ldd было нелегко выяснить или вообще невозможно выяснить, но ld.lld также поддерживает tlo вещи, так что получилось.