yihanaa created this revision.
yihanaa added reviewers: aaron.ballman, erichkeane, MaskRay.
yihanaa added a project: clang.
Herald added a subscriber: StephenFan.
Herald added a project: All.
yihanaa requested review of this revision.
Herald added a subscriber: cfe-commits.
Add constant array support for __builtin_dump_sturct. For N-dimensional arrays,
the new dumpArray function dumps the elements of the array by generating N
layers of 'for' loops.
for example:
The struct:
struct Foo {
int x;
};
struct S {
int a[3];
int b[2][2];
struct Foo c[2];
};
The dump result:
struct S {
int[3] a = [
[0] = 1
[1] = 2
[2] = 3
]
int[2][2] b = [
[0] = [
[0] = 1
[1] = 2
]
[1] = [
[0] = 3
[1] = 4
]
]
struct Foo[2] c = [
[0] = {
int x = 1
}
[1] = {
int x = 2
}
]
}
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D122822
Files:
clang/docs/ReleaseNotes.rst
clang/lib/CodeGen/CGBuiltin.cpp
clang/test/CodeGen/dump-struct-builtin.c
Index: clang/test/CodeGen/dump-struct-builtin.c
===================================================================
--- clang/test/CodeGen/dump-struct-builtin.c
+++ clang/test/CodeGen/dump-struct-builtin.c
@@ -76,7 +76,11 @@
// CHECK: @__const.unit15.a = private unnamed_addr constant %struct.U15A { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
// CHECK-NEXT: [[STRUCT_STR_U15:@[0-9]+]] = private unnamed_addr constant [15 x i8] c"struct U15A {\0A\00", align 1
-// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" int[3] a = %p\0A\00", align 1
+// CHECK-NEXT: [[FIELD_U15:@[0-9]+]] = private unnamed_addr constant [16 x i8] c" int[3] a = \00", align 1
+// CHECK-NEXT: [[ARRAY_L_SQUARE_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_INDEX_U15:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" [%ld] = \00", align 1
+// CHECK-NEXT: [[ARRAY_ELEMENT_U15:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_R_SQUARE_U15:@[0-9]+]] = private unnamed_addr constant [7 x i8] c" ]\0A\00", align 1
// CHECK-NEXT: [[END_STRUCT_U15:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
// CHECK: @__const.unit16.a = private unnamed_addr constant %struct.U16A { i8 12 }, align 1
@@ -94,6 +98,18 @@
// CHECK-NEXT: [[FIELD_U18:@[0-9]+]] = private unnamed_addr constant [25 x i8] c" long double a = %Lf\0A\00", align 1
// CHECK-NEXT: [[END_STRUCT_U18:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
+// CHECK: @__const.unit19.a = private unnamed_addr constant %struct.T19A { {{.*}} }, align 4
+// CHECK-NEXT: [[STRUCT_STR_U19:@[0-9]+]] = private unnamed_addr constant [15 x i8] c"struct T19A {\0A\00", align 1
+// CHECK-NEXT: [[FIELD_U19:@[0-9]+]] = private unnamed_addr constant [19 x i8] c" int[2][2] b = \00", align 1
+// CHECK-NEXT: [[ARRAY_L_SQUARE_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_INDEX_U19:@[0-9]+]] = private unnamed_addr constant [17 x i8] c" [%ld] = \00", align 1
+// CHECK-NEXT: [[ARRAY_L_SQUARE2_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"[\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_INDEX2_U19:@[0-9]+]] = private unnamed_addr constant [21 x i8] c" [%ld] = \00", align 1
+// CHECK-NEXT: [[ARRAY_ELEMENT_U19:@[0-9]+]] = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_R_SQUARE2_U19:@[0-9]+]] = private unnamed_addr constant [11 x i8] c" ]\0A\00", align 1
+// CHECK-NEXT: [[ARRAY_R_SQUARE_U19:@[0-9]+]] = private unnamed_addr constant [7 x i8] c" ]\0A\00", align 1
+// CHECK-NEXT: [[END_STRUCT_U19:@[0-9]+]] = private unnamed_addr constant [3 x i8] c"}\0A\00", align 1
+
int printf(const char *fmt, ...) {
return 0;
}
@@ -107,8 +123,8 @@
.a = 12,
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U1]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U1]], i32 0, i32 0), i16 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U1]], i32 0, i32 0))
__builtin_dump_struct(&a, &printf);
@@ -124,8 +140,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U2]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i16, i16* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* [[FIELD_U2]], i32 0, i32 0), i16 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U2]], i32 0, i32 0))
__builtin_dump_struct(&a, &printf);
@@ -141,8 +157,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U3]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U3]], i32 0, i32 0), i32 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U3]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -158,8 +174,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U4]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* [[FIELD_U4]], i32 0, i32 0), i32 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U4]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -175,8 +191,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U5]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* [[FIELD_U5]], i32 0, i32 0), i64 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U5]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -192,8 +208,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U6]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* [[FIELD_U6]], i32 0, i32 0), i64 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U6]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -209,8 +225,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U7]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* [[FIELD_U7]], i32 0, i32 0), i64 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U7]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -226,8 +242,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U8]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i64, i64* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([33 x i8], [33 x i8]* [[FIELD_U8]], i32 0, i32 0), i64 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U8]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -243,8 +259,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[STRUCT_STR_U9]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i8, i8* [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[FIELD_U9]], i32 0, i32 0), i8 [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U9]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -260,8 +276,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U10]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U10]], i32 0, i32 0), i8* [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U10]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -277,8 +293,8 @@
};
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U11]], i32 0, i32 0))
- // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load i8*, i8** [[RES1]],
+ // CHECK: [[RES1:%[âaâzAâZ._0-9]+]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0
+ // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U11]], i32 0, i32 0), i8* [[LOAD1]])
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U11]], i32 0, i32 0)
__builtin_dump_struct(&a, &printf);
@@ -347,10 +363,26 @@
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U15]], i32 0, i32 0))
// CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0
- // CHECK: [[LOAD1:%.*]] = load [3 x i32], [3 x i32]* [[RES1]],
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U15]], i32 0, i32 0), [3 x i32] [[LOAD1]])
- // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U15]], i32 0, i32 0)
- __builtin_dump_struct(&a, &printf);
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* [[FIELD_U15]], i32 0, i32 0))
+ // CHECK: [[ARRAY_PTR:%.*]] = getelementptr inbounds [3 x i32], [3 x i32]* [[RES1]], i64 3
+ // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1,
+ // CHECK: store i64 0, i64* [[ALLOC_INDEX]],
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE_U15]], i32 0, i32 0))
+ // CHECK: br label %__builtin_dump_struct.array.body.1
+
+ // CHECK: __builtin_dump_struct.array.body.1:
+ // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]]
+ // CHECK: [[RES2:%.*]] = getelementptr inbounds [3 x i32], [3 x i32]* [[RES1]], i64 0, i64 [[INDEX]]
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[ARRAY_INDEX_U15]], i32 0, i32 0), i64 [[INDEX]])
+ // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES2]],
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[ARRAY_ELEMENT_U15]], i32 0, i32 0), i32 [[LOAD1]])
+ // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1
+ // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]],
+ // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 3
+ // CHECK: br i1 [[DONE]], label %__builtin_dump_struct.array.done.1, label %__builtin_dump_struct.array.body.1
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[ARRAY_R_SQUARE_U15]], i32 0, i32 0))
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U15]], i32 0, i32 0))
+ __builtin_dump_struct(&a, &printf);
}
void unit16(void) {
@@ -404,6 +436,57 @@
__builtin_dump_struct(&a, &printf);
}
+void unit19(void) {
+ struct T19A {
+ int b[2][2];
+ };
+ struct T19A a = {
+ .b = {{1, 2}, {3, 4}}
+ };
+
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[STRUCT_STR_U19]], i32 0, i32 0))
+ // CHECK: [[RES1:%.*]] = getelementptr inbounds %struct.T19A, %struct.T19A* %a, i32 0, i32 0
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[FIELD_U19]], i32 0, i32 0))
+ // CHECK: [[RES2:%.*]] = getelementptr inbounds [2 x [2 x i32]], [2 x [2 x i32]]* [[RES1]], i64 2
+ // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1,
+ // CHECK: store i64 0, i64* [[ALLOC_INDEX]],
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE_U19]], i32 0, i32 0))
+ // CHECK: br label %__builtin_dump_struct.array.body.1
+
+ // CHECK: __builtin_dump_struct.array.body.1:
+ // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]]
+ // CHECK: [[RES3:%.*]] = getelementptr inbounds [2 x [2 x i32]], [2 x [2 x i32]]* [[RES1]], i64 0, i64 [[INDEX]]
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[ARRAY_INDEX_U19]], i32 0, i32 0), i64 [[INDEX]])
+ // CHECK: [[RES4:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[RES3]], i64 2
+ // CHECK: [[ALLOC_INDEX1:%.*]] = alloca i64, i64 1,
+ // CHECK: store i64 0, i64* [[ALLOC_INDEX1]],
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[ARRAY_L_SQUARE2_U19]], i32 0, i32 0))
+ // CHECK: br label %__builtin_dump_struct.array.body.2
+
+ // CHECK: __builtin_dump_struct.array.body.2:
+ // CHECK: [[INDEX1:%.*]] = load i64, i64* [[ALLOC_INDEX1]],
+ // CHECK: [[RES5:%.*]] = getelementptr inbounds [2 x i32], [2 x i32]* [[RES3]], i64 0, i64 [[INDEX1]]
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* [[ARRAY_INDEX2_U19]], i32 0, i32 0), i64 [[INDEX1]])
+ // CHECK: [[LOAD1:%.*]] = load i32, i32* [[RES5]],
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[ARRAY_ELEMENT_U19]], i32 0, i32 0), i32 [[LOAD1]])
+ // CHECK: [[NEXT_INDEX1:%.*]] = add i64 [[INDEX1]], 1
+ // CHECK: store i64 [[NEXT_INDEX1]], i64* [[ALLOC_INDEX1]],
+ // CHECK: [[DONE1:%.*]] = icmp eq i64 [[NEXT_INDEX1]], 2
+ // CHECK: br i1 %20, label %__builtin_dump_struct.array.done.2, label %__builtin_dump_struct.array.body.2
+
+ // CHECK: __builtin_dump_struct.array.done.2:
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[ARRAY_R_SQUARE2_U19]], i32 0, i32 0))
+ // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1
+ // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]],
+ // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 2
+ // CHECK: br i1 %25, label %__builtin_dump_struct.array.done.1, label %__builtin_dump_struct.array.body.1
+
+ // CHECK: __builtin_dump_struct.array.done.1:
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[ARRAY_R_SQUARE_U19]], i32 0, i32 0))
+ // CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* [[END_STRUCT_U19]], i32 0, i32 0))
+ __builtin_dump_struct(&a, &printf);
+}
+
void test1(void) {
struct T1A {
int a;
@@ -475,8 +558,24 @@
// CHECK: [[LOAD1:%.*]] = load i32, i32* [[BC1]],
// CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]])
// CHECK: [[BC2:%.*]] = bitcast %union.anon* [[RES1]] to [4 x i8]*
- // CHECK: [[LOAD2:%.*]] = load [4 x i8], [4 x i8]* [[BC2]],
- // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]])
+ // CHECK: call i32 (i8*, ...) @printf(
+ // CHECK: [[RES2:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* [[BC2]], i64 4
+ // CHECK: [[ALLOC_INDEX:%.*]] = alloca i64, i64 1,
+ // CHECK: store i64 0, i64* [[ALLOC_INDEX]],
+ // CHECK: call i32 (i8*, ...) @printf(
+ // CHECK: br label %__builtin_dump_struct.array.body.2
+ // CHECK: __builtin_dump_struct.array.body.2:
+ // CHECK: [[INDEX:%.*]] = load i64, i64* [[ALLOC_INDEX]],
+ // CHECK: [[RES3:%.*]] = getelementptr inbounds [4 x i8], [4 x i8]* [[BC2]], i64 0, i64 [[INDEX]]
+ // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[INDEX]])
+ // CHECK: [[LOAD2:%.*]] = load i8, i8* [[RES3]],
+ // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8 [[LOAD2]])
+ // CHECK: [[NEXT_INDEX:%.*]] = add i64 [[INDEX]], 1
+ // CHECK: store i64 [[NEXT_INDEX]], i64* [[ALLOC_INDEX]],
+ // CHECK: [[DONE:%.*]] = icmp eq i64 [[NEXT_INDEX]], 4
+ // CHECK: br i1 %19, label %__builtin_dump_struct.array.done.2, label %__builtin_dump_struct.array.body.2
+ // CHECK: __builtin_dump_struct.array.done.2:
+ // CHECK: call i32 (i8*, ...) @printf(
// CHECK: call i32 (i8*, ...) @printf(
// CHECK: call i32 (i8*, ...) @printf(
__builtin_dump_struct(&a, &printf);
@@ -619,20 +718,20 @@
};
// CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[BC1:%.*]] = bitcast %struct.T8A* %a to i8*
- // CHECK: [[LOAD1:%.*]] = load i8, i8* [[BC1]],
- // CHECK: [[CLEAR1:%.*]] = and i8 [[LOAD1]], 1
- // CHECK: [[CAST1:%.*]] = zext i8 [[CLEAR1]] to i32
+ // CHECK: [[BC1:%[âaâzAâZ._0-9]+]] = bitcast %struct.T8A* %a to i8*
+ // CHECK: [[LOAD1:%[-a-zA-Z._0-9]+]] = load i8, i8* [[BC1]],
+ // CHECK: [[CLEAR1:%[-a-zA-Z._0-9]+]] = and i8 [[LOAD1]], 1
+ // CHECK: [[CAST1:%[-a-zA-Z._0-9]+]] = zext i8 [[CLEAR1]] to i32
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([29 x i8], [29 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST1]])
- // CHECK: [[BC2:%.*]] = bitcast %struct.T8A* %a to i8*
- // CHECK: [[LOAD2:%.*]] = load i8, i8* [[BC2]],
- // CHECK: [[LSHR2:%.*]] = lshr i8 [[LOAD2]], 1
- // CHECK: [[CLEAR2:%.*]] = and i8 [[LSHR2]], 7
- // CHECK: [[CAST2:%.*]] = zext i8 [[CLEAR2]] to i32
+ // CHECK: [[BC2:%[âaâzAâZ._0-9]+]] = bitcast %struct.T8A* %a to i8*
+ // CHECK: [[LOAD2:%[-a-zA-Z._0-9]+]] = load i8, i8* [[BC2]],
+ // CHECK: [[LSHR2:%[0-9a-z.]+]] = lshr i8 [[LOAD2]], 1
+ // CHECK: [[CLEAR2:%[-a-zA-Z._0-9]+]] = and i8 [[LSHR2]], 7
+ // CHECK: [[CAST2:%[-a-zA-Z._0-9]+]] = zext i8 [[CLEAR2]] to i32
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* {{.*}}, i32 0, i32 0), i32 [[CAST2]])
// CHECK: call i32 (i8*, ...) @printf(
- // CHECK: [[RES3:%.*]] = getelementptr inbounds %struct.T8A, %struct.T8A* %a, i32 0, i32 1
- // CHECK: [[LOAD3:%.*]] = load i32, i32* [[RES3]],
+ // CHECK: [[RES3:%[-a-zA-Z._0-9]+]] = getelementptr inbounds %struct.T8A, %struct.T8A* %a, i32 0, i32 1
+ // CHECK: [[LOAD3:%[-a-zA-Z._0-9]+]] = load i32, i32* [[RES3]],
// CHECK: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* {{.*}}, i32 0, i32 0), i32 [[LOAD3]])
// CHECK: call i32 (i8*, ...) @printf(
__builtin_dump_struct(&a, &printf);
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -2043,21 +2043,9 @@
return RValue::get(Overflow);
}
-static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType,
- LValue RecordLV, CharUnits Align,
- llvm::FunctionCallee Func, int Lvl) {
- ASTContext &Context = CGF.getContext();
- RecordDecl *RD = RType->castAs<RecordType>()->getDecl()->getDefinition();
- std::string Pad = std::string(Lvl * 4, ' ');
- std::string ElementPad = std::string((Lvl + 1) * 4, ' ');
-
- PrintingPolicy Policy(Context.getLangOpts());
- Policy.AnonymousTagLocations = false;
- Value *GString = CGF.Builder.CreateGlobalStringPtr(
- llvm::Twine(Pad).concat(RType.getAsString(Policy)).concat(" {\n").str());
- Value *Res = CGF.Builder.CreateCall(Func, {GString});
-
- static llvm::DenseMap<QualType, const char *> Types;
+static StringRef getDumpStructFormatSpecifier(ASTContext &Context,
+ QualType Type) {
+ static llvm::DenseMap<QualType, StringRef> Types;
if (Types.empty()) {
Types[Context.CharTy] = "%c";
Types[Context.BoolTy] = "%d";
@@ -2079,10 +2067,156 @@
Types[Context.getPointerType(Context.getConstType(Context.CharTy))] = "%s";
}
+ if (Types.find(Type) == Types.end())
+ return Types[Context.VoidPtrTy];
+ return Types[Type];
+}
+
+static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType,
+ LValue RecordLV, CharUnits Align, bool inArray,
+ llvm::FunctionCallee Func, int Lvl);
+
+static llvm::Value *dumpArray(CodeGenFunction &CGF, QualType ArrayType,
+ LValue LV, CharUnits Align,
+ llvm::FunctionCallee Func, int Lvl) {
+ assert(ArrayType->isConstantArrayType() && "required an array type");
+ auto &Builder = CGF.Builder;
+ ASTContext &Context = CGF.getContext();
+ std::string Pad = std::string(Lvl * 4, ' ');
+ std::string ElementPad = std::string((Lvl + 1) * 4, ' ');
+ std::string &LastElementPad = Pad;
+ std::string Name = "__builtin_dump_struct.array.";
+
+ auto GetConstInt = [&CGF](int64_t Val) -> llvm::ConstantInt * {
+ return llvm::ConstantInt::get(CGF.IntPtrTy, Val, true);
+ };
+
+ auto GetInstName = [&Name, &Lvl](StringRef Suffix) -> std::string {
+ return llvm::Twine(Name)
+ .concat(Suffix)
+ .concat(".")
+ .concat(llvm::Twine(Lvl))
+ .str();
+ };
+
+ const ConstantArrayType *CAT = Context.getAsConstantArrayType(ArrayType);
+ QualType ElementType = CAT->getElementType();
+ Address ArrayAddress = LV.getAddress(CGF);
+
+ CharUnits ElementAlign = ArrayAddress.getAlignment().alignmentOfArrayElement(
+ Context.getTypeSizeInChars(ArrayType));
+
+ const llvm::ArrayType *LLVMArrayType =
+ dyn_cast<llvm::ArrayType>(ArrayAddress.getElementType());
+
+ // The length of an array in elements, as well as the base element type and a
+ // properly-typed first element pointer.
+ llvm::Value *Length = GetConstInt(LLVMArrayType->getNumElements());
+
+ // Normally we have to check whether the array is zero-length.
+ bool CheckZeroLength = true;
+ // But if the array length is constant, we can suppress that.
+ if (llvm::ConstantInt *CL = dyn_cast<llvm::ConstantInt>(Length)) {
+ // ...and if it's constant zero, we can just skip the entire thing.
+ if (!CL->isZero())
+ CheckZeroLength = false;
+ }
+
+ llvm::Value *Begin = ArrayAddress.getPointer();
+ llvm::Value *End =
+ Builder.CreateInBoundsGEP(ArrayAddress.getElementType(), Begin, Length);
+
+ Address IndexAddr = CGF.CreateTempAlloca(CGF.IntPtrTy, CGF.getSizeAlign(),
+ "index", GetConstInt(1), nullptr);
+ llvm::Value *Index = Builder.CreateStore(GetConstInt(0), IndexAddr);
+
+ // The basic structure here is a do-while loop, because we don't
+ // need to check for the zero-element case.
+ llvm::BasicBlock *BodyBB = CGF.createBasicBlock(GetInstName("body"));
+ llvm::BasicBlock *DoneBB = CGF.createBasicBlock(GetInstName("done"));
+
+ if (CheckZeroLength) {
+ llvm::Value *isEmpty =
+ Builder.CreateICmpEQ(Begin, End, GetInstName("isempty"));
+ CGF.Builder.CreateCondBr(isEmpty, DoneBB, BodyBB);
+ }
+
+ llvm::Value *GString = Builder.CreateGlobalStringPtr("[\n");
+ llvm::Value *Res = Builder.CreateCall(Func, {GString});
+ llvm::Value *TmpRes = nullptr;
+
+ CGF.EmitBlock(BodyBB);
+ Index = Builder.CreateLoad(IndexAddr);
+ llvm::Value *Element = Builder.CreateInBoundsGEP(
+ ArrayAddress.getElementType(), ArrayAddress.getPointer(),
+ {CGF.CGM.getSize(CharUnits::Zero()), Index});
+ Address ElementAddr =
+ Address(Element, LLVMArrayType->getElementType(), ElementAlign);
+ LValue ElementLV = CGF.MakeAddrLValue(ElementAddr, ElementType);
+
+ GString = Builder.CreateGlobalStringPtr(
+ llvm::Twine(ElementPad).concat("[%ld] = ").str());
+ TmpRes = CGF.Builder.CreateCall(Func, {GString, Index});
+ Res = CGF.Builder.CreateAdd(Res, TmpRes);
+
+ if (ElementType->isConstantArrayType()) {
+ TmpRes =
+ dumpArray(CGF, ElementType, ElementLV, ElementAlign, Func, Lvl + 1);
+ Res = Builder.CreateAdd(Res, TmpRes);
+ } else {
+ if (ElementType->isRecordType()) {
+ TmpRes = dumpRecord(CGF, ElementType, ElementLV, ElementAlign, true, Func,
+ Lvl + 1);
+ } else {
+ StringRef Format = getDumpStructFormatSpecifier(Context, ElementType);
+ GString =
+ Builder.CreateGlobalStringPtr(llvm::Twine(Format).concat("\n").str());
+ RValue RV = CGF.EmitLoadOfLValue(ElementLV, SourceLocation());
+ TmpRes = CGF.Builder.CreateCall(Func, {GString, RV.getScalarVal()});
+ }
+ Res = CGF.Builder.CreateAdd(Res, TmpRes);
+ }
+
+ Index = Builder.CreateAdd(Index, GetConstInt(1));
+ Builder.CreateStore(Index, IndexAddr);
+
+ llvm::Value *Done = Builder.CreateICmpEQ(Index, Length);
+ Builder.CreateCondBr(Done, DoneBB, BodyBB);
+
+ CGF.EmitBlock(DoneBB);
+ GString = Builder.CreateGlobalStringPtr(
+ llvm::Twine(LastElementPad).concat("]\n").str());
+ TmpRes = Builder.CreateCall(Func, {GString});
+ Res = Builder.CreateAdd(TmpRes, Res);
+ return Res;
+}
+
+static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType,
+ LValue RecordLV, CharUnits Align, bool inArray,
+ llvm::FunctionCallee Func, int Lvl) {
+ ASTContext &Context = CGF.getContext();
+ RecordDecl *RD = RType->castAs<RecordType>()->getDecl()->getDefinition();
+ std::string Pad = std::string(Lvl * 4, ' ');
+ std::string FieldPad = std::string((Lvl + 1) * 4, ' ');
+
+ Value *GString = nullptr;
+ if (inArray) {
+ GString = CGF.Builder.CreateGlobalStringPtr("{\n");
+ } else {
+ PrintingPolicy Policy(Context.getLangOpts());
+ Policy.AnonymousTagLocations = false;
+ GString =
+ CGF.Builder.CreateGlobalStringPtr(llvm::Twine(Pad)
+ .concat(RType.getAsString(Policy))
+ .concat(" {\n")
+ .str());
+ }
+ Value *Res = CGF.Builder.CreateCall(Func, {GString});
+
for (const auto *FD : RD->fields()) {
Value *TmpRes = nullptr;
- std::string Format = llvm::Twine(ElementPad)
+ std::string Format = llvm::Twine(FieldPad)
.concat(FD->getType().getAsString())
.concat(llvm::Twine(' '))
.concat(FD->getNameAsString())
@@ -2114,15 +2248,24 @@
// We check whether we are in a recursive type
if (CanonicalType->isRecordType()) {
- TmpRes = dumpRecord(CGF, CanonicalType, FieldLV, Align, Func, Lvl + 1);
+ TmpRes =
+ dumpRecord(CGF, CanonicalType, FieldLV, Align, false, Func, Lvl + 1);
+ Res = CGF.Builder.CreateAdd(TmpRes, Res);
+ continue;
+ }
+
+ if (CanonicalType->isConstantArrayType()) {
+ GString = CGF.Builder.CreateGlobalStringPtr(
+ llvm::Twine(Format).concat(" = ").str());
+ TmpRes = CGF.Builder.CreateCall(Func, {GString});
+ Res = CGF.Builder.CreateAdd(Res, TmpRes);
+ TmpRes = dumpArray(CGF, CanonicalType, FieldLV, Align, Func, Lvl + 1);
Res = CGF.Builder.CreateAdd(TmpRes, Res);
continue;
}
// We try to determine the best format to print the current field
- const char *TypeFormat = Types.find(CanonicalType) == Types.end()
- ? Types[Context.VoidPtrTy]
- : Types[CanonicalType];
+ StringRef TypeFormat = getDumpStructFormatSpecifier(Context, CanonicalType);
GString = CGF.Builder.CreateGlobalStringPtr(llvm::Twine(Format)
.concat(" = ")
@@ -2681,7 +2824,7 @@
Value *RecordPtr = EmitScalarExpr(Arg0);
LValue RecordLV = MakeAddrLValue(RecordPtr, Arg0Type, Arg0Align);
- Value *Res = dumpRecord(*this, Arg0Type, RecordLV, Arg0Align,
+ Value *Res = dumpRecord(*this, Arg0Type, RecordLV, Arg0Align, false,
{LLVMFuncType, Func}, 0);
return RValue::get(Res);
}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -118,6 +118,7 @@
- Improve the dump format, dump both bitwidth(if its a bitfield) and field value.
- Remove anonymous tag locations.
- Beautify dump format, add indent for nested struct and struct members.
+ - Support constant array in struct and union.
New Compiler Flags
------------------
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits