2014-12-16 13 views
6

Beim Erstellen eines Projekts mit LLVM werden einige Funktionsaufrufe durch intrinsische Funktionen ersetzt. Wird der Austausch durch das Front-End (z. B. Clang) oder das LLVM-Back-End abgeschlossen?LLVM-Eigenfunktionen

Diskussionen im Internet zeigen, dass der Ersatz von intrinsischen Funktionen mit Optimierungsoptionen zusammenhängt. Also, wenn es keine Optimierungsmöglichkeit gibt, dann wird kein intrinsischer Ersatz passieren? Oder tatsächlich, es gibt einige Ersatzfunktionen für intrinsische Funktionen, die nicht deaktiviert werden können?

Wenn es eine Methode gibt, um alle intrinsischen Funktionen zu deaktivieren, wie soll ich das tun?

Antwort

12

Kommt drauf an. In Code geschriebene Intrinsics werden direkt über das Frontend ausgegeben. Intrinsics wie llvm.memset werden während der Optimierung auf IR-Ebene in den Code eingeführt (unabhängig davon, ob das Frontend oder das Backend diese Optimierungen durchführt).

Hier ist eine (ziemlich dumm) Beispiel:

int main(int argc, char** argv) 
{ 
     int a[8]; 

     for (int i = 0; i != 8; ++i) 
       a[i] = 0; 

     for (int i = 7; i >= 0; --i) 
       a[i] = a[i+1] + argc; 

     return a[0]; 
} 

Zusammengestellt mit Klirren 3.5 (Klirren -S -emit-llvm) erhalten Sie folgende IR ohne intrinsics erhalten:

; Function Attrs: nounwind uwtable 
define i32 @main(i32 %argc, i8** %argv) #0 { 
    %1 = alloca i32, align 4 
    %2 = alloca i32, align 4 
    %3 = alloca i8**, align 8 
    %a = alloca [8 x i32], align 16 
    %i = alloca i32, align 4 
    %i1 = alloca i32, align 4 
    store i32 0, i32* %1 
    store i32 %argc, i32* %2, align 4 
    store i8** %argv, i8*** %3, align 8 
    store i32 0, i32* %i, align 4 
    br label %4 

; <label>:4          ; preds = %11, %0 
    %5 = load i32* %i, align 4 
    %6 = icmp ne i32 %5, 8 
    br i1 %6, label %7, label %14 

; <label>:7          ; preds = %4 
    %8 = load i32* %i, align 4 
    %9 = sext i32 %8 to i64 
    %10 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %9 
    store i32 0, i32* %10, align 4 
    br label %11 

; <label>:11          ; preds = %7 
    %12 = load i32* %i, align 4 
    %13 = add nsw i32 %12, 1 
    store i32 %13, i32* %i, align 4 
    br label %4 

; <label>:14          ; preds = %4 
    store i32 7, i32* %i1, align 4 
    br label %15 

; <label>:15          ; preds = %29, %14 
    %16 = load i32* %i1, align 4 
    %17 = icmp sge i32 %16, 0 
    br i1 %17, label %18, label %32 

; <label>:18          ; preds = %15 
    %19 = load i32* %i1, align 4 
    %20 = add nsw i32 %19, 1 
    %21 = sext i32 %20 to i64 
    %22 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %21 
    %23 = load i32* %22, align 4 
    %24 = load i32* %2, align 4 
    %25 = add nsw i32 %23, %24 
    %26 = load i32* %i1, align 4 
    %27 = sext i32 %26 to i64 
    %28 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 %27 
    store i32 %25, i32* %28, align 4 
    br label %29 

; <label>:29          ; preds = %18 
    %30 = load i32* %i1, align 4 
    %31 = add nsw i32 %30, -1 
    store i32 %31, i32* %i1, align 4 
    br label %15 

; <label>:32          ; preds = %15 
    %33 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0 
    %34 = load i32* %33, align 4 
    ret i32 %34 
} 

zusammengestellt wieder mit clang -emit-llvm -O1 werden Sie sehen:

; Function Attrs: nounwind readnone uwtable 
define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 { 
.preheader: 
    %a = alloca [8 x i32], align 16 
    %a6 = bitcast [8 x i32]* %a to i8* 
    call void @llvm.memset.p0i8.i64(i8* %a6, i8 0, i64 32, i32 4, i1 false) 
    br label %0 

; <label>:0          ; preds = %.preheader, %0 
    %indvars.iv = phi i64 [ 7, %.preheader ], [ %indvars.iv.next, %0 ] 
    %1 = add nsw i64 %indvars.iv, 1 
    %2 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 %1 
    %3 = load i32* %2, align 4, !tbaa !1 
    %4 = add nsw i32 %3, %argc 
    %5 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 %indvars.iv 
    store i32 %4, i32* %5, align 4, !tbaa !1 
    %indvars.iv.next = add nsw i64 %indvars.iv, -1 
    %6 = trunc i64 %indvars.iv to i32 
    %7 = icmp sgt i32 %6, 0 
    br i1 %7, label %0, label %8 

; <label>:8          ; preds = %0 
    %9 = getelementptr inbounds [8 x i32]* %a, i64 0, i64 0 
    %10 = load i32* %9, align 16, !tbaa !1 
    ret i32 %10 
} 

Die Initialisierung Schleife durch th ersetzt wurde e llvm.memset intrinsisch. Das Back-End ist frei, das intrinsische zu handhaben, wie es will, aber gewöhnlich wird llvm.memset auf einen libc-Bibliotheksaufruf gesenkt.

Um Ihre erste Frage zu beantworten: Ja, wenn Sie Ihren Code nicht optimieren, dann erhalten Sie keine intrinsics in Ihrem IR.

Um zu verhindern, dass intrinsics in Ihrem Code eingeführt wird, müssen Sie nur den Optimierungsdurchlauf Ihres IR finden und ihn nicht ausführen. Hier ist eine weitere Frage, wie, um herauszufinden, was Pass auf der IR getan werden: Where to find the optimization sequence for clang -OX?

für -O1 erhalten wir:

Prune-eh -inline-Cost-Inline--functionattrs somit sehr passgenau -sroa -domtree -early-cse -lazy-wert-info -jump-threading -korreliert-propagation -simplifycfg -instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops -loop-vereinfachen -lcssa -loop-rotieren -licm -loop-unswitch - instcombine -scalar-evolution -lcssa -indvars -loop-idiom -loop-löschung -loop-entroll -memdep -memcpyopt -sccp -instcombine -lazy-value-info -jump-threading -korreliert-propagation -domtree -memdep -dse - adce -simplifycfg -incombined -barrier -domtree -loops -loop-vereinfachen -lcssa -Zweig-prob -Block-Freq -scalar-evolution -loop-vectorize -instcombine -simplifycfg -Streifen-dead-Prototypen -verify

Eine wilde Vermutung: instcombine ist Einführung der llvm.memset. Ich betreibe die Pässe ohne instcombine und entscheiden auf dem nicht optimierten IR und erhalten diese:

; Function Attrs: nounwind readnone uwtable 
define i32 @main(i32 %argc, i8** %argv) #0 { 
    %a = alloca [8 x i32], align 16 
    %1 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 8 
    %2 = load i32* %1, align 4 
    %3 = add nsw i32 %2, %argc 
    %4 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 7 
    store i32 %3, i32* %4, align 4 
    %5 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 7 
    %6 = load i32* %5, align 4 
    %7 = add nsw i32 %6, %argc 
    %8 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 6 
    store i32 %7, i32* %8, align 4 
    %9 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 6 
    %10 = load i32* %9, align 4 
    %11 = add nsw i32 %10, %argc 
    %12 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 5 
    store i32 %11, i32* %12, align 4 
    %13 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 5 
    %14 = load i32* %13, align 4 
    %15 = add nsw i32 %14, %argc 
    %16 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 4 
    store i32 %15, i32* %16, align 4 
    %17 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 4 
    %18 = load i32* %17, align 4 
    %19 = add nsw i32 %18, %argc 
    %20 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 3 
    store i32 %19, i32* %20, align 4 
    %21 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 3 
    %22 = load i32* %21, align 4 
    %23 = add nsw i32 %22, %argc 
    %24 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 2 
    store i32 %23, i32* %24, align 4 
    %25 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 2 
    %26 = load i32* %25, align 4 
    %27 = add nsw i32 %26, %argc 
    %28 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 1 
    store i32 %27, i32* %28, align 4 
    %29 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 1 
    %30 = load i32* %29, align 4 
    %31 = add nsw i32 %30, %argc 
    %32 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0 
    store i32 %31, i32* %32, align 4 
    %33 = getelementptr inbounds [8 x i32]* %a, i32 0, i64 0 
    %34 = load i32* %33, align 4 
    ret i32 %34 
} 

Keine Anweisungen. So verhindern Sie (zumindest die memset) intrinsics in Ihrem Code nicht Instcombine auf Ihrem IR. Instcombine ist jedoch ein mächtiger Opt-Pass, der den Code wirklich verkürzt.

Jetzt haben Sie zwei Möglichkeiten:

  1. nicht verwenden opt Pässe, die Spezifika einführen
  2. Ihre eigene llvm opt Pass schreiben, die Spezifika zurück, was auch immer verwandelt konnten sie mit einem Lauf ersetzt werden, nach der Optimierung und vor dem Back-End beginnt zu arbeiten

Ich hoffe, das hilft Ihnen irgendwie. Prost!

+0

Die Antwort ist ziemlich aufschlussreich und danke! – Junxzm

+0

clang 5 erzeugt intrinsics sogar mit -O0 –