diff --git a/llvm/test/Transforms/SROA/alloca-struct.ll b/llvm/test/Transforms/SROA/alloca-struct.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SROA/alloca-struct.ll @@ -0,0 +1,191 @@ +; RUN: opt < %s -sroa -S | FileCheck %s +; RUN: opt < %s -passes=sroa -S | FileCheck %s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.RetValIntChar = type { i32, i8 } +%struct.RetValTwoInts = type { i32, i32 } +%struct.RetValOneIntTwoChar = type { i32, i8 } + +; Tests that a struct of type {i32, i8} is scalarized by SROA. +; FIXME: SROA should skip scalarization since there is no scalar access. +; Currently scalarization happens due to the mismatch of allocated size +; and the actual structure size. +define dso_local i64 @test_struct_of_int_char(i1 zeroext %test, i64 ()* %p) #0 { +; CHECK-LABEL: @test_struct_of_int_char( +; CHECK-NEXT: entry: +; COM: Check that registers are used and alloca instructions are eliminated. +; CHECK-NOT: alloca +; CHECK: if.then: +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: if.end: +; CHECK-NEXT: call i64 [[P:%.*]]() +; CHECK: br label [[RETURN]] +; CHECK: return: +; COM: Check there are more than one PHI nodes to select scalarized values. +; CHECK-COUNT-3: phi +; CHECK: ret i64 +; +entry: + %retval = alloca %struct.RetValIntChar, align 4 + %test.addr = alloca i8, align 1 + %p.addr = alloca i64 ()*, align 8 + %frombool = zext i1 %test to i8 + store i8 %frombool, i8* %test.addr, align 1, !tbaa !3 + store i64 ()* %p, i64 ()** %p.addr, align 8, !tbaa !7 + %0 = load i8, i8* %test.addr, align 1, !tbaa !3, !range !9 + %tobool = trunc i8 %0 to i1 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + %x = getelementptr inbounds %struct.RetValIntChar, %struct.RetValIntChar* %retval, i32 0, i32 0 + store i32 0, i32* %x, align 4, !tbaa !10 + %y = getelementptr inbounds %struct.RetValIntChar, %struct.RetValIntChar* %retval, i32 0, i32 1 + store i8 0, i8* %y, align 4, !tbaa !13 + br label %return + +if.end: ; preds = %entry + %1 = load i64 ()*, i64 ()** %p.addr, align 8, !tbaa !7 + %call = call i64 %1() + %2 = bitcast %struct.RetValIntChar* %retval to i64* + store i64 %call, i64* %2, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %3 = bitcast %struct.RetValIntChar* %retval to i64* + %4 = load i64, i64* %3, align 4 + ret i64 %4 +} + +; Test that the alloca of struct{int, int} will be scalarized by SROA. +define dso_local i64 @test_struct_of_two_int(i1 zeroext %test, i64 ()* %p) #0 { +; CHECK-LABEL: @test_struct_of_two_int( +; CHECK-NEXT: entry: +; CHECK-NOT: alloca +; CHECK: if.then: +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: if.end: +; CHECK-NEXT: call i64 +; CHECK: return: +; COM: Check that there are more than one PHI nodes to select the scalarized values. +; CHECK-COUNT-2: phi +; CHECK: ret i64 +; +entry: + %retval = alloca %struct.RetValTwoInts, align 4 + %test.addr = alloca i8, align 1 + %p.addr = alloca i64 ()*, align 8 + %frombool = zext i1 %test to i8 + store i8 %frombool, i8* %test.addr, align 1, !tbaa !3 + store i64 ()* %p, i64 ()** %p.addr, align 8, !tbaa !7 + %0 = load i8, i8* %test.addr, align 1, !tbaa !3, !range !9 + %tobool = trunc i8 %0 to i1 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + %x = getelementptr inbounds %struct.RetValTwoInts, %struct.RetValTwoInts* %retval, i32 0, i32 0 + store i32 0, i32* %x, align 4, !tbaa !14 + %y = getelementptr inbounds %struct.RetValTwoInts, %struct.RetValTwoInts* %retval, i32 0, i32 1 + store i32 0, i32* %y, align 4, !tbaa !16 + br label %return + +if.end: ; preds = %entry + %1 = load i64 ()*, i64 ()** %p.addr, align 8, !tbaa !7 + %call = call i64 %1() + %2 = bitcast %struct.RetValTwoInts* %retval to i64* + store i64 %call, i64* %2, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %3 = bitcast %struct.RetValTwoInts* %retval to i64* + %4 = load i64, i64* %3, align 4 + ret i64 %4 +} + +; Tests that allocated struct type is scalarized when non-constant values are +; stored into its fields. +define dso_local i64 @test_one_field_has_runtime_value(i1 zeroext %test, i64 ()* %p) #0 { +; CHECK-LABEL: @test_one_field_has_runtime_value( +; CHECK-NEXT: entry: +; CHECK-NOT: alloca +; CHECK: call void @srand +; CHECK: if.then: +; CHECK-NEXT: call i32 @rand() +; CHECK-NEXT: br label +; CHECK: if.end: +; CHECK-NEXT: call i64 +; CHECK: return: +; CHECK-COUNT-2: phi i32 +; CHECK: ret i64 +; +entry: + %retval = alloca %struct.RetValTwoInts, align 4 + %test.addr = alloca i8, align 1 + %p.addr = alloca i64 ()*, align 8 + %frombool = zext i1 %test to i8 + store i8 %frombool, i8* %test.addr, align 1, !tbaa !3 + store i64 ()* %p, i64 ()** %p.addr, align 8, !tbaa !7 + %call = call i64 @time(i64* null) #2 + %conv = trunc i64 %call to i32 + call void @srand(i32 %conv) #2 + %0 = load i8, i8* %test.addr, align 1, !tbaa !3, !range !9 + %tobool = trunc i8 %0 to i1 + br i1 %tobool, label %if.then, label %if.end + +if.then: ; preds = %entry + %x = getelementptr inbounds %struct.RetValTwoInts, %struct.RetValTwoInts* %retval, i32 0, i32 0 + %call1 = call i32 @rand() #2 + store i32 %call1, i32* %x, align 4, !tbaa !14 + %y = getelementptr inbounds %struct.RetValTwoInts, %struct.RetValTwoInts* %retval, i32 0, i32 1 + store i32 1, i32* %y, align 4, !tbaa !16 + br label %return + +if.end: ; preds = %entry + %1 = load i64 ()*, i64 ()** %p.addr, align 8, !tbaa !7 + %call2 = call i64 %1() + %2 = bitcast %struct.RetValTwoInts* %retval to i64* + store i64 %call2, i64* %2, align 4 + br label %return + +return: ; preds = %if.end, %if.then + %3 = bitcast %struct.RetValTwoInts* %retval to i64* + %4 = load i64, i64* %3, align 4 + ret i64 %4 +} + +; Function Attrs: nounwind +declare dso_local void @srand(i32) #1 + +; Function Attrs: nounwind +declare dso_local i64 @time(i64*) #1 + +; Function Attrs: nounwind +declare dso_local i32 @rand() #1 + +attributes #0 = { mustprogress uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { nounwind } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 738734f655d3a13dbc2a2dd851a7e907ddcf3fb8)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"bool", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C++ TBAA"} +!7 = !{!8, !8, i64 0} +!8 = !{!"any pointer", !5, i64 0} +!9 = !{i8 0, i8 2} +!10 = !{!11, !12, i64 0} +!11 = !{!"_ZTS13RetValIntChar", !12, i64 0, !5, i64 4} +!12 = !{!"int", !5, i64 0} +!13 = !{!11, !5, i64 4} +!14 = !{!15, !12, i64 0} +!15 = !{!"_ZTS13RetValTwoInts", !12, i64 0, !12, i64 4} +!16 = !{!15, !12, i64 4} +!17 = !{!18, !12, i64 0} +!18 = !{!"_ZTS19RetValOneIntTwoChar", !12, i64 0, !5, i64 4} +!19 = !{!18, !5, i64 4}