2014-12-16 22 views
6

Khi xây dựng một dự án với LLVM, một số cuộc gọi chức năng sẽ được thay thế bằng các hàm nội tại. Việc thay thế có được hoàn thành bởi giao diện người dùng (ví dụ: clang) hay back-end LLVM không?Chức năng nội tại LLVM

Thảo luận qua Internet cho biết rằng thay thế chức năng nội tại có liên quan đến các tùy chọn tối ưu hóa. Vì vậy, nó có nghĩa là nếu không có tùy chọn tối ưu hóa, sau đó không có thay thế nội tại sẽ xảy ra? Hoặc trên thực tế, có một số thay thế chức năng nội tại mặc định không thể bị vô hiệu hóa?

Nếu có bất kỳ phương pháp nào để vô hiệu hóa tất cả các chức năng nội tại, tôi nên làm như thế nào?

Trả lời

12

Điều đó tùy thuộc. Nội tại được viết bằng mã được phát trực tiếp qua giao diện người dùng. Intrinsics như llvm.memset được giới thiệu với mã trong quá trình tối ưu hóa ở mức IR (eigther front-end và back-end thực hiện tối ưu hóa này).

Đây là một (khá ngu ngốc) ví dụ:

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]; 
} 

Biên soạn với kêu vang 3,5 (kêu vang -S -emit-llvm) bạn sẽ nhận được IR sau đây mà không bất kỳ intrinsics:

; 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 
} 

Biên soạn lại với clang -emit-llvm -O1 bạn sẽ thấy điều này:

; 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 
} 

Vòng lặp khởi tạo đã được thay thế bằng thứ e llvm.memset nội tại. Back-end là miễn phí để xử lý nội tại như nó muốn nhưng thường là llvm.memset được hạ xuống một cuộc gọi thư viện libc.

Để trả lời câu hỏi đầu tiên của bạn: Có, nếu bạn không tối ưu hóa mã của mình, thì bạn sẽ không nhận được nội tại trong IR của mình.

Để ngăn chặn nội tại được giới thiệu trong mã của bạn, tất cả những gì bạn phải làm là tìm đường chuyền tối ưu trên IR của bạn và không chạy nó. Dưới đây là một câu hỏi có liên quan làm thế nào để tìm hiểu những gì vượt qua được thực hiện trên IR: Where to find the optimization sequence for clang -OX?

cho -O1 chúng tôi nhận được:

mận-eh -always-inline -inline chi phí -functionattrs -sroa -domtree -early-cse -lazy-giá trị-thông tin -jump-threading -correlated-tuyên truyền -simplifycfg -instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -loop-unswitch - instcombine -scalar-evolution -lcssa -indvars -loop-idiom -loop-deletion -loop-unroll -memdep -memcpyopt -sccp -instcombine -lazy-giá trị-thông tin -jump-threading -correlated-tuyên truyền -domtree -memdep -dse - adce -simplifycfg -instcombine -barrier -domtree -loops -loop-đơn giản hóa -lcssa -branch-prob -block-freq -scalar-tiến hóa -loop-vectorize -instcombine -simplifycfg -strip-chết-nguyên mẫu -verify

Một đoán hoang dã: instcombine là giới thiệu llvm.memset. Tôi chạy qua mà không có instcombine và chọn trên IR không được tối ưu hóa và nhận được điều này:

; 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 
} 

Không có hướng dẫn. Vì vậy, để ngăn chặn (ít nhất là memset) intrinsics trong mã của bạn không chạy instcombine trên IR của bạn. Tuy nhiên, instcombine là một lựa chọn vượt trội mà thực sự rút ngắn mã.

Bây giờ bạn có hai lựa chọn:

  1. không sử dụng vượt qua opt mà giới thiệu intrinsics
  2. viết riêng llvm opt đường chuyền của bạn mà biến đổi intrinsics trở lại bất cứ điều gì họ có thể được thay thế bằng một chạy nó sau khi tối ưu hóa và trước khi back-end bắt đầu hoạt động

Tôi hy vọng điều này sẽ giúp bạn bằng cách nào đó. Chúc mừng!

+0

Câu trả lời khá sâu sắc và cảm ơn! – Junxzm

+0

clang 5 tạo nội tại ngay cả với -O0 –

Các vấn đề liên quan