LINUX.ORG.RU

Оптимизация в clang

 , ,


0

3

Есть файл foo.c:

#include <stdio.h>

static inline void foo(){
	static _Bool firstTime = 1;
	if (firstTime){
		puts("First time!");
		firstTime = 0;
	} else {
		puts("Once again.");
	}
}

int main(){
	foo();
	foo();
	return 0;
}
после выполнения clang -O3 -S -emit-llvm foo.c -o foo.ll, создаётся файл foo.ll:
; ModuleID = 'foo.c'
source_filename = "foo.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@foo.firstTime = internal unnamed_addr global i1 false, align 1
@.str = private unnamed_addr constant [12 x i8] c"First time!\00", align 1
@.str.1 = private unnamed_addr constant [12 x i8] c"Once again.\00", align 1

; Function Attrs: nounwind sspstrong uwtable
define i32 @main() local_unnamed_addr #0 {
  %1 = load i1, i1* @foo.firstTime, align 1
  br i1 %1, label %4, label %2

; <label>:2:                                      ; preds = %0
  %3 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i64 0, i64 0)) #2
  store i1 true, i1* @foo.firstTime, align 1
  br label %9

; <label>:4:                                      ; preds = %0
  %5 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0)) #2
  %6 = load i1, i1* @foo.firstTime, align 1
  br i1 %6, label %9, label %7

; <label>:7:                                      ; preds = %4
  %8 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i64 0, i64 0)) #2
  store i1 true, i1* @foo.firstTime, align 1
  br label %11

; <label>:9:                                      ; preds = %2, %4
  %10 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0)) #2
  br label %11

; <label>:11:                                     ; preds = %7, %9
  ret i32 0
}

; Function Attrs: nounwind
declare i32 @puts(i8* nocapture readonly) local_unnamed_addr #1

attributes #0 = { nounwind sspstrong uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{!"clang version 6.0.0 (tags/RELEASE_600/final)"}
Как сделать так чтобы, в файле foo.ll было что-то вроде следующего:
; ModuleID = 'foo.c'
source_filename = "foo.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@.str = private unnamed_addr constant [12 x i8] c"First time!\00", align 1
@.str.1 = private unnamed_addr constant [12 x i8] c"Once again.\00", align 1

; Function Attrs: nounwind sspstrong uwtable
define i32 @main() local_unnamed_addr #0 {
  %1 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i64 0, i64 0))
  %2 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0))
  ret i32 0
}

; Function Attrs: nounwind
declare i32 @puts(i8* nocapture readonly) local_unnamed_addr #1

attributes #0 = { nounwind sspstrong uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{!"clang version 6.0.0 (tags/RELEASE_600/final)"}

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

#include <stdio.h>

static inline void foo(_Bool* firstTime) {
	if (*firstTime) {
		puts("First time!");
		*firstTime = 0;
	} else {
		puts("Once again.");
	}
}

int main() {
	_Bool firstTime = 1;
	foo(&firstTime);
	foo(&firstTime);
	return 0;
}
Int64 ★★ ()
Ответ на: комментарий от anonymous

Функция main «чисто случайно» является точкой входа в программу. На его месте может быть любая другая функция, из которой в случайном порядке может вызываться функции main, foo и другие случайные функции. Попробуй угадай где первый раз вызовется foo.

anonymous ()
Ответ на: комментарий от Int64

Это было бы решением но, к сожалению, в реальном приложении функция foo вызывается из разных функций, но все они в одном файле вместе с функцией main и порядок вызова известен, как в моём примере.

кто знает, где ты еще ее используешь.

компилято прекрасно знает, что я использую её только в функции main.

Taetricus ()
Последнее исправление: Taetricus (всего исправлений: 1)
Ответ на: комментарий от utf8nowhere

Это можно оптимизировать только на этапе линковки (привет lto), когда линковщик знает, что это исполнимый бинарь с единственной точкой входа main. Хотя даже в этом случае бинарь можно загрузить вручную, полчить адреса функций main и foo (если они экспортируемые) и дергать их как захочется.

anonymous ()
Ответ на: комментарий от utf8nowhere

Единица трансляции - это не программа. В программу можно объединить несколько единиц трансляции, в которых можно стандартными средствами с++ вызвать foo до вызова main.

anonymous ()