[SimplifyLibcalls] Replace locked IO with unlocked IO

Summary: If file stream arg is not captured and source is fopen, we could replace IO calls by unlocked IO ("_unlocked" function variants) to gain better speed,

Reviewers: efriedma, RKSimon, spatel, sanjoy, hfinkel, majnemer, lebedev.ri, rja

Reviewed By: rja

Subscribers: rja, srhines, efriedma, lebedev.ri, llvm-commits

Differential Revision: https://reviews.llvm.org/D45736

llvm-svn: 332452
diff --git a/llvm/test/Transforms/InstCombine/unlocked-stdio.ll b/llvm/test/Transforms/InstCombine/unlocked-stdio.ll
new file mode 100644
index 0000000..fa5a9fa
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/unlocked-stdio.ll
@@ -0,0 +1,197 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -instcombine -S -mtriple=x86_64-unknown-linux-gnu | FileCheck %s
+
+%struct._IO_FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct._IO_FILE*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] }
+%struct._IO_marker = type { %struct._IO_marker*, %struct._IO_FILE*, i32 }
+
+@.str = private unnamed_addr constant [5 x i8] c"file\00", align 1
+@.str.1 = private unnamed_addr constant [2 x i8] c"w\00", align 1
+@.str.2 = private unnamed_addr constant [4 x i8] c"str\00", align 1
+@global_file = common global %struct._IO_FILE* null, align 8
+
+define void @external_fgetc_test(%struct._IO_FILE* %f) {
+; CHECK-LABEL: @external_fgetc_test(
+; CHECK-NEXT:    [[CALL:%.*]] = call i32 @fgetc(%struct._IO_FILE* [[F:%.*]])
+; CHECK-NEXT:    ret void
+;
+  %call = call i32 @fgetc(%struct._IO_FILE* %f)
+  ret void
+}
+
+declare i32 @fgetc(%struct._IO_FILE* nocapture) #0
+
+define void @external_fgetc_test2() {
+; CHECK-LABEL: @external_fgetc_test2(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[FPUTC_UNLOCKED:%.*]] = call i32 @fputc_unlocked(i32 99, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %call1 = call i32 @fputc(i32 99, %struct._IO_FILE* %call)
+  ret void
+}
+
+declare %struct._IO_FILE* @fopen(i8*, i8*)
+declare i32 @fputc(i32, %struct._IO_FILE* nocapture) #0
+
+define internal void @fgetc_test() {
+; CHECK-LABEL: @fgetc_test(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[FGETC_UNLOCKED:%.*]] = call i32 @fgetc_unlocked(%struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %call1 = call i32 @fgetc(%struct._IO_FILE* %call)
+  ret void
+}
+
+define void @external_fgetc_internal_test() {
+; CHECK-LABEL: @external_fgetc_internal_test(
+; CHECK-NEXT:    call void @fgetc_test()
+; CHECK-NEXT:    ret void
+;
+  call void @fgetc_test()
+  ret void
+}
+
+define internal void @fwrite_test() {
+; CHECK-LABEL: @fwrite_test(
+; CHECK-NEXT:    [[S:%.*]] = alloca [10 x i8], align 1
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* [[S]], i64 0, i64 0
+; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8* nonnull [[ARRAYDECAY]], i64 10, i64 10, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %s = alloca [10 x i8], align 1
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %s, i64 0, i64 0
+  %call1 = call i64 @fwrite(i8* nonnull %arraydecay, i64 10, i64 10, %struct._IO_FILE* %call)
+  ret void
+}
+
+define internal void @fread_test() {
+; CHECK-LABEL: @fread_test(
+; CHECK-NEXT:    [[S:%.*]] = alloca [10 x i8], align 1
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* [[S]], i64 0, i64 0
+; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fread_unlocked(i8* nonnull [[ARRAYDECAY]], i64 10, i64 10, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %s = alloca [10 x i8], align 1
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %s, i64 0, i64 0
+  %call1 = call i64 @fread(i8* nonnull %arraydecay, i64 10, i64 10, %struct._IO_FILE* %call)
+  ret void
+}
+
+define internal void @fputs_test() {
+; CHECK-LABEL: @fputs_test(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %call1 = call i32 @fputs(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), %struct._IO_FILE* %call)
+  ret void
+}
+
+define internal void @fgets_test() {
+; CHECK-LABEL: @fgets_test(
+; CHECK-NEXT:    [[BUF:%.*]] = alloca [10 x i8], align 1
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], [10 x i8]* [[BUF]], i64 0, i64 0
+; CHECK-NEXT:    [[FGETS_UNLOCKED:%.*]] = call i32 @fgets_unlocked(i8* nonnull [[ARRAYDECAY]], i32 10, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %buf = alloca [10 x i8], align 1
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %buf, i64 0, i64 0
+  %call1 = call i8* @fgets(i8* nonnull %arraydecay, i32 10, %struct._IO_FILE* %call)
+  ret void
+}
+
+define internal void @fputc_test() {
+; CHECK-LABEL: @fputc_test(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[FPUTC_UNLOCKED:%.*]] = call i32 @fputc_unlocked(i32 99, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+  %call1 = call i32 @fputc(i32 99, %struct._IO_FILE* %call)
+  ret void
+}
+
+define i32 @main() {
+; CHECK-LABEL: @main(
+; CHECK-NEXT:    call void @fwrite_test()
+; CHECK-NEXT:    call void @fread_test()
+; CHECK-NEXT:    call void @fputs_test()
+; CHECK-NEXT:    call void @fgets_test()
+; CHECK-NEXT:    call void @fputc_test()
+; CHECK-NEXT:    call void @fgetc_test()
+; CHECK-NEXT:    ret i32 0
+;
+  call void @fwrite_test()
+  call void @fread_test()
+  call void @fputs_test()
+  call void @fgets_test()
+  call void @fputc_test()
+  call void @fgetc_test()
+  ret i32 0
+}
+
+declare i32 @fclose(%struct._IO_FILE* nocapture)
+
+define void @test_with_fclose() {
+; CHECK-LABEL: @test_with_fclose(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0)) #2
+  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
+  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
+  ret void
+}
+
+declare void @modify_file(%struct._IO_FILE*)
+
+define void @test_captured_by_function(){
+; CHECK-LABEL: @test_captured_by_function(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    call void @modify_file(%struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    [[CALL1:%.*]] = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0)) #2
+  call void @modify_file(%struct._IO_FILE* %call) #2
+  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
+  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
+  ret void
+}
+
+define void @test_captured_by_global_value() {
+; CHECK-LABEL: @test_captured_by_global_value(
+; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
+; CHECK-NEXT:    [[DOTCAST:%.*]] = ptrtoint %struct._IO_FILE* [[CALL]] to i64
+; CHECK-NEXT:    store i64 [[DOTCAST]], i64* bitcast (%struct._IO_FILE** @global_file to i64*), align 8
+; CHECK-NEXT:    [[CALL1:%.*]] = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE* [[CALL]])
+; CHECK-NEXT:    ret void
+;
+  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0)) #2
+  %.cast = ptrtoint %struct._IO_FILE* %call to i64
+  store i64 %.cast, i64* bitcast (%struct._IO_FILE** @global_file to i64*), align 8
+  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
+  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
+  ret void
+}
+
+
+
+declare i64 @fwrite(i8* nocapture, i64, i64, %struct._IO_FILE* nocapture) #0
+declare i64 @fread(i8* nocapture, i64, i64, %struct._IO_FILE* nocapture) #0
+declare i32 @fputs(i8* nocapture readonly, %struct._IO_FILE* nocapture) #0
+declare i8* @fgets(i8*, i32, %struct._IO_FILE* nocapture) #0