51 changes: 51 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,57 @@ class CodeGenModule : public CodeGenTypeCache {
MustTailCallUndefinedGlobals.insert(Global);
}

bool shouldZeroInitPadding() const {
// In C23 (N3096) $6.7.10:
// """
// If any object is initialized with an empty iniitializer, then it is
// subject to default initialization:
// - if it is an aggregate, every member is initialized (recursively)
// according to these rules, and any padding is initialized to zero bits;
// - if it is a union, the first named member is initialized (recursively)
// according to these rules, and any padding is initialized to zero bits.
//
// If the aggregate or union contains elements or members that are
// aggregates or unions, these rules apply recursively to the subaggregates
// or contained unions.
//
// If there are fewer initializers in a brace-enclosed list than there are
// elements or members of an aggregate, or fewer characters in a string
// literal used to initialize an array of known size than there are elements
// in the array, the remainder of the aggregate is subject to default
// initialization.
// """
//
// From my understanding, the standard is ambiguous in the following two
// areas:
// 1. For a union type with empty initializer, if the first named member is
// not the largest member, then the bytes comes after the first named member
// but before padding are left unspecified. An example is:
// union U { int a; long long b;};
// union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left
// unspecified.
//
// 2. It only mentions padding for empty initializer, but doesn't mention
// padding for a non empty initialization list. And if the aggregation or
// union contains elements or members that are aggregates or unions, and
// some are non empty initializers, while others are empty initiailizers,
// the padding initialization is unclear. An example is:
// struct S1 { int a; long long b; };
// struct S2 { char c; struct S1 s1; };
// // The values for paddings between s2.c and s2.s1.a, between s2.s1.a
// and s2.s1.b are unclear.
// struct S2 s2 = { 'c' };
//
// Here we choose to zero initiailize left bytes of a union type. Because
// projects like the Linux kernel are relying on this behavior. If we don't
// explicitly zero initialize them, the undef values can be optimized to
// return gabage data. We also choose to zero initialize paddings for
// aggregates and unions, no matter they are initialized by empty
// initializers or non empty initializers. This can provide a consistent
// behavior. So projects like the Linux kernel can rely on it.
return !getLangOpts().CPlusPlus;
}

private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ struct et7 {
52,
};

// CHECK: @yv7 ={{.*}} global %struct.et7 { [0 x float] zeroinitializer, i8 52 }
// CHECK: @yv7 ={{.*}} global { [0 x float], i8, [3 x i8] } { [0 x float] zeroinitializer, i8 52, [3 x i8] zeroinitializer }
4 changes: 2 additions & 2 deletions clang/test/CodeGen/2008-08-07-AlignPadding1.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ struct gc_generation {

#define GEN_HEAD(n) (&generations[n].head)

// The idea is that there are 6 undefs in this structure initializer to cover
// The idea is that there are 6 zeroinitializers in this structure initializer to cover
// the padding between elements.
// CHECK: @generations ={{.*}} global [3 x %struct.gc_generation] [%struct.gc_generation { %union._gc_head { %struct.anon { ptr @generations, ptr @generations, i64 0 }, [8 x i8] undef }, i32 700, i32 0, [8 x i8] undef }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 48), ptr getelementptr (i8, ptr @generations, i64 48), i64 0 }, [8 x i8] undef }, i32 10, i32 0, [8 x i8] undef }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 96), ptr getelementptr (i8, ptr @generations, i64 96), i64 0 }, [8 x i8] undef }, i32 10, i32 0, [8 x i8] undef }]
// CHECK: @generations ={{.*}} global [3 x %struct.gc_generation] [%struct.gc_generation { %union._gc_head { %struct.anon { ptr @generations, ptr @generations, i64 0 }, [8 x i8] zeroinitializer }, i32 700, i32 0, [8 x i8] zeroinitializer }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 48), ptr getelementptr (i8, ptr @generations, i64 48), i64 0 }, [8 x i8] zeroinitializer }, i32 10, i32 0, [8 x i8] zeroinitializer }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 96), ptr getelementptr (i8, ptr @generations, i64 96), i64 0 }, [8 x i8] zeroinitializer }, i32 10, i32 0, [8 x i8] zeroinitializer }]
/* linked lists of container objects */
struct gc_generation generations[3] = {
/* PyGC_Head, threshold, count */
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/2009-06-14-anonymous-union-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ struct sysfs_dirent {
};
struct sysfs_dirent sysfs_root = { {}, 16877 };

// CHECK: @sysfs_root = {{.*}}global %struct.sysfs_dirent { %union.anon zeroinitializer, i16 16877 }
// CHECK: @sysfs_root = {{.*}}global { %union.anon, i16, [2 x i8] } { %union.anon zeroinitializer, i16 16877, [2 x i8] zeroinitializer }

struct Foo {
union { struct empty {} x; };
Expand All @@ -16,4 +16,4 @@ struct Foo {
struct Foo foo = { {}, 16877 };

// EMPTY: @foo = {{.*}}global %struct.Foo { i16 16877 }
// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] undef, i16 16877 }
// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] zeroinitializer, i16 16877 }
12 changes: 5 additions & 7 deletions clang/test/CodeGen/64bit-swiftcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

// CHECK-DAG: %struct.atomic_padded = type { { %struct.packed, [7 x i8] } }
// CHECK-DAG: %struct.packed = type <{ i64, i8 }>
//
// CHECK: [[STRUCT2_RESULT:@.*]] = private {{.*}} constant [[STRUCT2_TYPE:%.*]] { i32 0, i8 0, i8 undef, i8 0, i32 0, i32 0 }

/*****************************************************************************/
/****************************** PARAMETER ABIS *******************************/
Expand Down Expand Up @@ -162,8 +160,8 @@ typedef struct {
} struct_2;
TEST(struct_2);
// CHECK-LABEL: define{{.*}} swiftcc { i64, i64 } @return_struct_2() {{.*}}{
// CHECK: [[RET:%.*]] = alloca [[STRUCT2_TYPE]], align 4
// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[RET]], {{.*}}[[STRUCT2_RESULT]]
// CHECK: [[RET:%.*]] = alloca [[STRUCT2:%.*]], align 4
// CHECK: call void @llvm.memset
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 1
Expand All @@ -173,15 +171,15 @@ TEST(struct_2);
// CHECK: ret { i64, i64 } [[R1]]
// CHECK: }
// CHECK-LABEL: define{{.*}} swiftcc void @take_struct_2(i64 %0, i64 %1) {{.*}}{
// CHECK: [[V:%.*]] = alloca [[STRUCT:%.*]], align 4
// CHECK: [[V:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 1
// CHECK: store i64 %1, ptr [[GEP1]], align 4
// CHECK: ret void
// CHECK: }
// CHECK-LABEL: define{{.*}} void @test_struct_2() {{.*}} {
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2_TYPE]], align 4
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[CALL:%.*]] = call swiftcc { i64, i64 } @return_struct_2()
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw {{.*}} [[TMP]], i32 0, i32 0
// CHECK: [[T0:%.*]] = extractvalue { i64, i64 } [[CALL]], 0
Expand Down Expand Up @@ -254,7 +252,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define{{.*}} swiftcc i64 @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[UNION:%.*]], align 8
// CHECK: call void @llvm.memcpy{{.*}}(ptr align 8 [[RET]]
// CHECK: call void @llvm.memset{{.*}}(ptr align 8 [[RET]]
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw { i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[R0:%.*]] = load i64, ptr [[GEP]], align 8
// CHECK: ret i64 [[R0]]
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/arm-swiftcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ typedef struct {
TEST(struct_2);
// CHECK-LABEL: define{{.*}} @return_struct_2()
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4
// CHECK: @llvm.memcpy
// CHECK: @llvm.memset
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG:{ i32, i32, float, float }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align 4
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG]], ptr [[RET]], i32 0, i32 1
Expand Down Expand Up @@ -274,7 +274,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define{{.*}} @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align {{(4|8)}}
// CHECK: @llvm.memcpy
// CHECK: @llvm.memset
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG:{ i32, i32 }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align {{(4|8)}}
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG]], ptr [[RET]], i32 0, i32 1
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/const-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ void g30(void) {
int : 1;
int x;
} a = {};
// CHECK: @g30.a = internal global %struct.anon.1 <{ i8 undef, i32 0 }>, align 1
// CHECK: @g30.a = internal global %struct.anon.1 zeroinitializer, align 1
#pragma pack()
}

Expand All @@ -182,7 +182,7 @@ void g31(void) {
short z;
} a = {23122, -12312731, -312};
#pragma pack()
// CHECK: @g31.a = internal global %struct.anon.2 { i16 23122, i32 -12312731, i16 -312 }, align 4
// CHECK: @g31.a = internal global { i16, [2 x i8], i32, i16, [2 x i8] } { i16 23122, [2 x i8] zeroinitializer, i32 -12312731, i16 -312, [2 x i8] zeroinitializer }, align 4
}

// Clang should evaluate this in constant context, so floating point mode should
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

// CHECK: @test1.x = internal constant [12 x i32] [i32 1
// CHECK: @__const.test2.x = private unnamed_addr constant [13 x i32] [i32 1,
// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] undef }
// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] zeroinitializer }
// CHECK: @test5y = {{(dso_local )?}}global { double } { double 7.300000e+0{{[0]*}}1 }

// CHECK: @__const.test6.x = private unnamed_addr constant %struct.SelectDest { i8 1, i8 2, i32 3, i32 0 }
// CHECK: @__const.test6.x = private unnamed_addr constant { i8, i8, [2 x i8], i32, i32 } { i8 1, i8 2, [2 x i8] zeroinitializer, i32 3, i32 0 }

// CHECK: @test7 = {{(dso_local )?}}global [2 x %struct.test7s] [%struct.test7s { i32 1, i32 2 }, %struct.test7s { i32 4, i32 0 }]

Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGen/designated-initializers.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct foo {
// CHECK: @u ={{.*}} global %union.anon zeroinitializer
union { int i; float f; } u = { };

// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } { i32 0, [4 x i8] undef }
// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } zeroinitializer
union { int i; double f; } u2 = { };

// CHECK: @u3 ={{.*}} global %union.anon.1 zeroinitializer
Expand Down Expand Up @@ -62,22 +62,22 @@ struct overwrite_string_struct2 {
char L[6];
int M;
} overwrite_string2[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
// CHECK: [6 x i8] c"fox\00\00\00", i32 1
// CHECK: [6 x i8] c"fox\00\00\00", [2 x i8] zeroinitializer, i32 1
struct overwrite_string_struct3 {
char L[3];
int M;
} overwrite_string3[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
// CHECK: [3 x i8] c"fox", i32 1
// CHECK: [3 x i8] c"fox", i8 0, i32 1
struct overwrite_string_struct4 {
char L[3];
int M;
} overwrite_string4[] = { { { "foobar" }, 1 }, [0].L[2] = 'x'};
// CHECK: [3 x i8] c"fox", i32 1
// CHECK: [3 x i8] c"fox", i8 0, i32 1
struct overwrite_string_struct5 {
char L[6];
int M;
} overwrite_string5[] = { { { "foo" }, 1 }, [0].L[4] = 'y'};
// CHECK: [6 x i8] c"foo\00y\00", i32 1
// CHECK: [6 x i8] c"foo\00y\00", [2 x i8] zeroinitializer, i32 1


// CHECK: @u1 = {{.*}} { i32 65535 }
Expand Down Expand Up @@ -138,7 +138,7 @@ union_16644_t union_16644_instance_4[2] =
[1].b[1] = 4
};

// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] undef, i32 123 }
// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] zeroinitializer, i32 123 }
struct leading_anon_bitfield { int : 32; int n; } lab = { .n = 123 };

struct Base {
Expand Down
18 changes: 9 additions & 9 deletions clang/test/CodeGen/ext-int.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
unsigned _BitInt(1) GlobSize1 = 0;
// CHECK: @GlobSize1 = {{.*}}global i8 0

// CHECK64: @__const.foo.A = private unnamed_addr constant { i32, [4 x i8], <{ i8, [23 x i8] }> } { i32 1, [4 x i8] undef, <{ i8, [23 x i8] }> <{ i8 -86, [23 x i8] zeroinitializer }> }, align 8
// CHECK64: @__const.foo.A = private unnamed_addr constant { i32, [4 x i8], <{ i8, [23 x i8] }> } { i32 1, [4 x i8] zeroinitializer, <{ i8, [23 x i8] }> <{ i8 -86, [23 x i8] zeroinitializer }> }, align 8
// @BigGlob = global [40 x i8] c"\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF", align 8
// CHECK64: @f.p = internal global <{ i8, i8, [22 x i8] }> <{ i8 16, i8 39, [22 x i8] zeroinitializer }>, align 8

Expand Down Expand Up @@ -91,8 +91,8 @@ int foo(int a) {
// CHECK64: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 2
// WIN32: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 2
// LIN32: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 1
// CHECK: %0 = load i32, ptr %a.addr, align 4
// CHECK: %conv = sext i32 %0 to i129
// CHECK: %[[V1:.+]] = load i32, ptr %a.addr, align 4
// CHECK: %conv = sext i32 %[[V1]] to i129
// CHECK64: storedv = sext i129 %conv to i192
// WIN32: storedv = sext i129 %conv to i192
// LIN32: storedv = sext i129 %conv to i160
Expand All @@ -102,12 +102,12 @@ int foo(int a) {
// CHECK64: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 2
// WIN32: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 2
// LIN32: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 1
// CHECK64: %1 = load i192, ptr %B3, align 8
// WIN32: %1 = load i192, ptr %B3, align 8
// LIN32: %1 = load i160, ptr %B3, align 4
// CHECK64: %loadedv = trunc i192 %1 to i129
// WIN32: %loadedv = trunc i192 %1 to i129
// LIN32: %loadedv = trunc i160 %1 to i129
// CHECK64: %[[V2:.+]] = load i192, ptr %B3, align 8
// WIN32: %[[V2:.+]] = load i192, ptr %B3, align 8
// LIN32: %[[V2:.+]] = load i160, ptr %B3, align 4
// CHECK64: %loadedv = trunc i192 %[[V2]] to i129
// WIN32: %loadedv = trunc i192 %[[V2]] to i129
// LIN32: %loadedv = trunc i160 %[[V2]] to i129
// CHECK: %conv4 = trunc i129 %loadedv to i32
struct S1 A = {1, 170};
struct S1 B = {1, a};
Expand Down
24 changes: 12 additions & 12 deletions clang/test/CodeGen/flexible-array-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ struct { int y[]; } b1 = { { 14, 16 } };

// sizeof(c) == 8, so this global should be at least 8 bytes.
struct { int x; char c; char y[]; } c = { 1, 2, { 13, 15 } };
// CHECK: @c ={{.*}} global { i32, i8, [2 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F" }
// CHECK: @c ={{.*}} global { i32, i8, [2 x i8], i8 } { i32 1, i8 2, [2 x i8] c"\0D\0F", i8 0 }

// sizeof(d) == 8, so this global should be at least 8 bytes.
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } d = { 1, 2, { 13, 15 } };
// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 undef }>,
// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 0 }>,

// This global needs 9 bytes to hold all the flexible array members.
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } e = { 1, 2, { 13, 15, 17, 19 } };
Expand Down Expand Up @@ -55,21 +55,21 @@ struct { int a; union { int b; short x[]; }; int c; int d; } hf = {1, 2, {}, 3};

// First member is the potential flexible array, initialization requires braces.
struct { int a; union { short x; int b; }; int c; int d; } i = {1, 2, {}, 3};
// CHECK: @i = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 0, i32 3 }
// CHECK: @i = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] zeroinitializer }, i32 0, i32 3 }
struct { int a; union { short x[0]; int b; }; int c; int d; } i0 = {1, {}, 2, 3};
// CHECK: @i0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 2, i32 3 }
// CHECK: @i0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } zeroinitializer, i32 2, i32 3 }
struct { int a; union { short x[1]; int b; }; int c; int d; } i1 = {1, {2}, {}, 3};
// CHECK: @i1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 0, i32 3 }
// CHECK: @i1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] zeroinitializer }, i32 0, i32 3 }
struct { int a; union { short x[]; int b; }; int c; int d; } i_f = {4, {}, {}, 6};
// CHECK: @i_f = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 4, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 0, i32 6 }
// CHECK: @i_f = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 4, { [0 x i16], [4 x i8] } zeroinitializer, i32 0, i32 6 }

// Named initializers; order doesn't matter.
struct { int a; union { int b; short x; }; int c; int d; } hn = {.a = 1, .x = 2, .c = 3};
// CHECK: @hn = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 3, i32 0 }
// CHECK: @hn = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] zeroinitializer }, i32 3, i32 0 }
struct { int a; union { int b; short x[0]; }; int c; int d; } hn0 = {.a = 1, .x = {2}, .c = 3};
// CHECK: @hn0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 3, i32 0 }
// CHECK: @hn0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } zeroinitializer, i32 3, i32 0 }
struct { int a; union { int b; short x[1]; }; int c; int d; } hn1 = {.a = 1, .x = {2}, .c = 3};
// CHECK: @hn1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 3, i32 0 }
// CHECK: @hn1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] zeroinitializer }, i32 3, i32 0 }

struct { char a[]; } empty_struct = {};
// CHECK: @empty_struct ={{.*}} global %struct.anon{{.*}} zeroinitializer, align 1
Expand All @@ -96,10 +96,10 @@ union { char a[]; } only_in_union0 = {0};
// CHECK: @only_in_union0 = global { [1 x i8] } zeroinitializer, align 1

union { char a[]; int b; } first_in_union = {};
// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } { [0 x i8] zeroinitializer, [4 x i8] undef }, align 4
// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } zeroinitializer, align 4

union { char a[]; int b; } first_in_union0 = {0};
// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } { [1 x i8] zeroinitializer, [3 x i8] undef }, align 4
// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } zeroinitializer, align 4

union { char a[]; int b; } first_in_union123 = { {1, 2, 3} };
// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 undef }, align 4
// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 0 }, align 4
2 changes: 1 addition & 1 deletion clang/test/CodeGen/global-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct ManyFields {
int f;
};

// CHECK: global %struct.ManyFields { i32 1, i32 2, i32 0, i8 0, i32 0, i32 0 }
// CHECK: global { i32, i32, i32, i8, [3 x i8], i32, i32 } { i32 1, i32 2, i32 0, i8 0, [3 x i8] zeroinitializer, i32 0, i32 0 }
struct ManyFields FewInits = {1, 2};


Expand Down
19 changes: 0 additions & 19 deletions clang/test/CodeGen/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,25 +187,6 @@ void nonzeroMemsetf64(void) {
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 68, i32 56, i1 false)
}

void nonzeroPaddedUnionMemset(void) {
union U { char c; int i; };
union U arr[9] = { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, };
// CHECK-LABEL: @nonzeroPaddedUnionMemset(
// CHECK-NOT: store
// CHECK-NOT: memcpy
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 36, i1 false)
}

void nonzeroNestedMemset(void) {
union U { char c; int i; };
struct S { union U u; short i; };
struct S arr[5] = { { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, };
// CHECK-LABEL: @nonzeroNestedMemset(
// CHECK-NOT: store
// CHECK-NOT: memcpy
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 40, i1 false)
}

// PR9257
struct test11S {
int A[10];
Expand Down
275 changes: 275 additions & 0 deletions clang/test/CodeGen/linux-kernel-struct-union-initializer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
// expected-no-diagnostics

union U1 {
int x;
char y[16];
};

struct S1 {
int x;
union U1 y;
};

union U2 {
int x;
char y[16];
} __attribute__((__aligned__(32)));

struct S2 {
int x;
long long y;
char z[8];
} __attribute__((__aligned__(32)));

struct S3 {
char x;
unsigned char y : 4;
unsigned char z : 7;
} __attribute__((packed));

union U1 global_u1 = {};

union U1 global_u2 = {3};

union U1 global_u2_from_cast = (union U1)3;

struct S1 global_s1 = {};

struct S1 global_s2 = {
.x = 3,
};

struct S1 global_s3 = {.x = 3, .y = {.x = 6}};

const union U1 global_const_u1 = {4};
struct S1 global_s3_from_const_u1 = {.y = global_const_u1};

union U2 global_u3 = {};

struct S2 global_s4 = {};

struct S2 global_s5 = {.x = 1};

struct S3 global_s6 = {101, 15, 123};

// Test empty initializer for union.
//.
// CHECK: @global_u1 = global %union.U1 zeroinitializer, align 4
// CHECK: @global_u2 = global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
// CHECK: @global_u2_from_cast = global { i32, [12 x i8] } { i32 3, [12 x i8] zeroinitializer }, align 4
// CHECK: @global_s1 = global %struct.S1 zeroinitializer, align 4
// CHECK: @global_s2 = global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
// CHECK: @global_s3 = global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
// CHECK: @global_const_u1 = constant %union.U1 { i32 4, [12 x i8] zeroinitializer }, align 4
// CHECK: @global_s3_from_const_u1 = global %struct.S1 { i32 0, %union.U1 { i32 4, [12 x i8] zeroinitializer } }, align 4
// CHECK: @global_u3 = global %union.U2 zeroinitializer, align 32
// CHECK: @global_s4 = global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
// CHECK: @global_s5 = global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
// CHECK: @global_s6 = global { i8, i8, i8 } { i8 101, i8 -65, i8 7 }, align 1
// CHECK: @test2.a = internal global %union.U1 zeroinitializer, align 4
// CHECK: @__const.test3.a = private unnamed_addr constant %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
// CHECK: @test4.a = internal global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
// CHECK: @test6.s = internal global %struct.S1 zeroinitializer, align 4
// CHECK: @__const.test7.s = private unnamed_addr constant %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
// CHECK: @test8.s = internal global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
// CHECK: @__const.test9.s = private unnamed_addr constant %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
// CHECK: @test10.s = internal global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
// CHECK: @test12.a = internal global %union.U2 zeroinitializer, align 32
// CHECK: @test14.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
// CHECK: @__const.test15.s = private unnamed_addr constant { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
// CHECK: @test16.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
//.
// CHECK-LABEL: define dso_local void @test1(
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 16, i1 false)
// CHECK-NEXT: ret void
//
void test1() {
union U1 a = {};
}

// Test empty initializer for union. Use static variable.
// CHECK-LABEL: define dso_local void @test2(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test2() {
static union U1 a = {};
}

// Test only initializing a small field for union.
// CHECK-LABEL: define dso_local void @test3(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A]], ptr align 4 @__const.test3.a, i64 16, i1 false)
// CHECK-NEXT: ret void
//
void test3() {
union U1 a = {3};
}

// Test only initializing a small field for union. Use static variable.
// CHECK-LABEL: define dso_local void @test4(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test4() {
static union U1 a = {3};
}

// Test union in struct. Use empty initializer for the struct.
// CHECK-LABEL: define dso_local void @test5(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[S]], i8 0, i64 20, i1 false)
// CHECK-NEXT: ret void
//
void test5() {
struct S1 s = {};
}

// Test union in struct. Use empty initializer for the struct. Use static variable.
// CHECK-LABEL: define dso_local void @test6(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test6() {
static struct S1 s = {};
}

// Test union in struct. Initialize other fields of the struct.
// CHECK-LABEL: define dso_local void @test7(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S]], ptr align 4 @__const.test7.s, i64 20, i1 false)
// CHECK-NEXT: ret void
//
void test7() {
struct S1 s = {
.x = 3,
};
}

// Test union in struct. Initialize other fields of the struct. Use static variable.
// CHECK-LABEL: define dso_local void @test8(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test8() {
static struct S1 s = {
.x = 3,
};
}

// Test union in struct. Initialize a small field for union.
// CHECK-LABEL: define dso_local void @test9(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S]], ptr align 4 @__const.test9.s, i64 20, i1 false)
// CHECK-NEXT: ret void
//
void test9() {
struct S1 s = {.x = 3,
.y = {
.x = 6,
}};
}

// Test union in struct. Initialize a small field for union. Use static variable.
// CHECK-LABEL: define dso_local void @test10(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test10() {
static struct S1 s = {.x = 3,
.y = {
.x = 6,
}};
}

// Test empty initializer for union with padding.
// CHECK-LABEL: define dso_local void @test11(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U2:%.*]], align 32
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 32 [[A]], i8 0, i64 32, i1 false)
// CHECK-NEXT: ret void
//
void test11() {
union U2 a = {};
}

// Test empty initializer for union with padding. Use static variable.
// CHECK-LABEL: define dso_local void @test12(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test12() {
static union U2 a = {};
}

// Test empty initializer for struct with padding.
// CHECK-LABEL: define dso_local void @test13(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 32
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 32 [[S]], i8 0, i64 32, i1 false)
// CHECK-NEXT: ret void
//
void test13() {
struct S2 s = {};
}

// Test empty initializer for struct with padding. Use static variable.
// CHECK-LABEL: define dso_local void @test14(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test14() {
static struct S2 s = {};
}

// Test partial initialization for struct with padding.
// CHECK-LABEL: define dso_local void @test15(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 32
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 32 [[S]], ptr align 32 @__const.test15.s, i64 32, i1 false)
// CHECK-NEXT: ret void
//
void test15() {
struct S2 s = {.x = 1};
}

// Test partial initialization for struct with padding. Use static variable.
// CHECK-LABEL: define dso_local void @test16(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void test16() {
static struct S2 s = {.x = 1};
}
//.
// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
// CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
//.
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
//.
181 changes: 181 additions & 0 deletions clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
// expected-no-diagnostics

union U1 {
int x;
char y[5];
};

struct S1 {
int x;
long long y;
};

struct S2 {
unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1
unsigned char : 2; // next 2 bits (in 1st byte) are blocked out as unused
unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd
unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte
int i;
};

struct S3 {
int x;
} __attribute__((__aligned__(8)));

struct S4 {
int a;
union U1 b;
};

struct S5 {
char x;
unsigned char y : 4;
unsigned char z : 7;
} __attribute__((packed));

// Test non-const initializer for union with padding.
// CHECK-LABEL: define dso_local void @test1(
// CHECK-SAME: i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// CHECK-NEXT: store i32 [[TMP0]], ptr [[A]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[A]], i64 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false)
// CHECK-NEXT: ret void
//
void test1(int x) {
union U1 a = {x};
}

// Test non-const initializer for struct with padding.
// CHECK-LABEL: define dso_local void @test2(
// CHECK-SAME: i64 noundef [[Y:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[Y_ADDR:%.*]] = alloca i64, align 8
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 8
// CHECK-NEXT: store i64 [[Y]], ptr [[Y_ADDR]], align 8
// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_S1]], ptr [[S]], i32 0, i32 0
// CHECK-NEXT: store i32 0, ptr [[X]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[S]], i64 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP0]], i8 0, i64 4, i1 false)
// CHECK-NEXT: [[Y1:%.*]] = getelementptr inbounds nuw [[STRUCT_S1]], ptr [[S]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[Y_ADDR]], align 8
// CHECK-NEXT: store i64 [[TMP1]], ptr [[Y1]], align 8
// CHECK-NEXT: ret void
//
void test2(long long y) {
struct S1 s = {.y = y};
}

// Test non-const initializer for struct with padding and bit fields.
// CHECK-LABEL: define dso_local void @test3(
// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 4
// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
// CHECK-NEXT: store i16 0, ptr [[S]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
// CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[TMP0]] to i16
// CHECK-NEXT: [[BF_LOAD:%.*]] = load i16, ptr [[S]], align 4
// CHECK-NEXT: [[BF_VALUE:%.*]] = and i16 [[TMP1]], 7
// CHECK-NEXT: [[BF_CLEAR:%.*]] = and i16 [[BF_LOAD]], -8
// CHECK-NEXT: [[BF_SET:%.*]] = or i16 [[BF_CLEAR]], [[BF_VALUE]]
// CHECK-NEXT: store i16 [[BF_SET]], ptr [[S]], align 4
// CHECK-NEXT: [[BF_LOAD1:%.*]] = load i16, ptr [[S]], align 4
// CHECK-NEXT: [[BF_CLEAR2:%.*]] = and i16 [[BF_LOAD1]], -16129
// CHECK-NEXT: [[BF_SET3:%.*]] = or i16 [[BF_CLEAR2]], 0
// CHECK-NEXT: store i16 [[BF_SET3]], ptr [[S]], align 4
// CHECK-NEXT: [[BF_LOAD4:%.*]] = load i16, ptr [[S]], align 4
// CHECK-NEXT: [[BF_CLEAR5:%.*]] = and i16 [[BF_LOAD4]], 16383
// CHECK-NEXT: [[BF_SET6:%.*]] = or i16 [[BF_CLEAR5]], 0
// CHECK-NEXT: store i16 [[BF_SET6]], ptr [[S]], align 4
// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[S]], i64 2
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 2 [[TMP2]], i8 0, i64 2, i1 false)
// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds nuw [[STRUCT_S2]], ptr [[S]], i32 0, i32 1
// CHECK-NEXT: store i32 0, ptr [[I]], align 4
// CHECK-NEXT: ret void
//
void test3(unsigned char b) {
struct S2 s = {.b1 = b};
}

// Test non-const initializer for struct with padding at the end of the struct.
// CHECK-LABEL: define dso_local void @test4(
// CHECK-SAME: i32 noundef [[X:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S3:%.*]], align 8
// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
// CHECK-NEXT: [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_S3]], ptr [[S]], i32 0, i32 0
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
// CHECK-NEXT: store i32 [[TMP0]], ptr [[X1]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false)
// CHECK-NEXT: ret void
//
void test4(int x) {
struct S3 s = {x};
}

// Test non-const initializer for union in struct.
// CHECK-LABEL: define dso_local void @test5(
// CHECK-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S4:%.*]], align 4
// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
// CHECK-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
// CHECK-NEXT: [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_S4]], ptr [[S]], i32 0, i32 0
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-NEXT: store i32 [[TMP0]], ptr [[A1]], align 4
// CHECK-NEXT: [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_S4]], ptr [[S]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
// CHECK-NEXT: store i32 [[TMP1]], ptr [[B2]], align 4
// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[B2]], i64 4
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false)
// CHECK-NEXT: ret void
//
void test5(int a, int b) {
struct S4 s = {a, {b}};
}

// CHECK-LABEL: define dso_local void @test6(
// CHECK-SAME: i8 noundef signext [[X:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i8, align 1
// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S5:%.*]], align 1
// CHECK-NEXT: store i8 [[X]], ptr [[X_ADDR]], align 1
// CHECK-NEXT: [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_S5]], ptr [[S]], i32 0, i32 0
// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[X_ADDR]], align 1
// CHECK-NEXT: store i8 [[TMP0]], ptr [[X1]], align 1
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 1
// CHECK-NEXT: store i16 0, ptr [[TMP1]], align 1
// CHECK-NEXT: [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_S5]], ptr [[S]], i32 0, i32 1
// CHECK-NEXT: [[BF_LOAD:%.*]] = load i16, ptr [[Y]], align 1
// CHECK-NEXT: [[BF_CLEAR:%.*]] = and i16 [[BF_LOAD]], -16
// CHECK-NEXT: [[BF_SET:%.*]] = or i16 [[BF_CLEAR]], 0
// CHECK-NEXT: store i16 [[BF_SET]], ptr [[Y]], align 1
// CHECK-NEXT: [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_S5]], ptr [[S]], i32 0, i32 1
// CHECK-NEXT: [[BF_LOAD2:%.*]] = load i16, ptr [[Z]], align 1
// CHECK-NEXT: [[BF_CLEAR3:%.*]] = and i16 [[BF_LOAD2]], -2033
// CHECK-NEXT: [[BF_SET4:%.*]] = or i16 [[BF_CLEAR3]], 0
// CHECK-NEXT: store i16 [[BF_SET4]], ptr [[Z]], align 1
// CHECK-NEXT: ret void
//
void test6(char x) {
struct S5 s = {.x = x};
}
//.
// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
// CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
//.
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
//.
9 changes: 3 additions & 6 deletions clang/test/CodeGen/mingw-long-double.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@ struct {
char c;
long double ldb;
} agggregate_LD = {};
// GNU32: %struct.anon = type { i8, x86_fp80 }
// GNU32: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 4
// GNU64: %struct.anon = type { i8, x86_fp80 }
// GNU64: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 16
// MSC64: %struct.anon = type { i8, double }
// MSC64: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 8
// GNU32: @agggregate_LD = dso_local global { i8, [3 x i8], x86_fp80 } zeroinitializer, align 4
// GNU64: @agggregate_LD = dso_local global { i8, [15 x i8], x86_fp80 } zeroinitializer, align 16
// MSC64: @agggregate_LD = dso_local global { i8, [7 x i8], double } zeroinitializer, align 8

long double dataLD = 1.0L;
// GNU32: @dataLD = dso_local global x86_fp80 0xK3FFF8000000000000000, align 4
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/mms-bitfields.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ union HEADER {
struct Inner variable = { 1,0,1, 21 };
union HEADER hdr = {{1,2,3,4}};

// CHECK: @variable ={{.*}} global { i8, [3 x i8], i8, i8, i8, i8 } { i8 5, [3 x i8] undef, i8 21, i8 0, i8 0, i8 0 }, align 1
// CHECK: @hdr ={{.*}} global { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } } { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } { i8 8, i8 0, [2 x i8] undef, i8 2, i8 0, i8 0, i8 3, i8 4, [3 x i8] undef } }, align 1
// CHECK: @variable ={{.*}} global { i8, [3 x i8], i8, i8, i8, i8 } { i8 5, [3 x i8] zeroinitializer, i8 21, i8 0, i8 0, i8 0 }, align 1
// CHECK: @hdr ={{.*}} global { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } } { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } { i8 8, i8 0, [2 x i8] zeroinitializer, i8 2, i8 0, i8 0, i8 3, i8 4, [3 x i8] zeroinitializer } }, align 1
4 changes: 2 additions & 2 deletions clang/test/CodeGen/union-init2.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// RUN: %clang_cc1 -x c++ %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefixes=CHECK-CXX

// Make sure we generate something sane instead of a ptrtoint
// CHECK: @r, [4 x i8] undef
// CHECK: @r, [4 x i8] zeroinitializer
union x {long long b;union x* a;} r = {.a = &r};


// CHECK: global { [3 x i8], [5 x i8] } { [3 x i8] zeroinitializer, [5 x i8] undef }
// CHECK: global { [3 x i8], [5 x i8] } zeroinitializer
union z {
char a[3];
long long b;
Expand Down
12 changes: 5 additions & 7 deletions clang/test/CodeGen/windows-swiftcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#define ERROR __attribute__((swift_error_result))
#define CONTEXT __attribute__((swift_context))

// CHECK: [[STRUCT2_RESULT:@.*]] = private {{.*}} constant [[STRUCT2_TYPE:%.*]] { i32 0, i8 0, i8 undef, i8 0, i32 0, i32 0 }

/*****************************************************************************/
/****************************** PARAMETER ABIS *******************************/
/*****************************************************************************/
Expand Down Expand Up @@ -142,8 +140,8 @@ typedef struct {
} struct_2;
TEST(struct_2);
// CHECK-LABEL: define dso_local swiftcc { i64, i64 } @return_struct_2() {{.*}}{
// CHECK: [[RET:%.*]] = alloca [[STRUCT2_TYPE]], align 4
// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[RET]], {{.*}}[[STRUCT2_RESULT]]
// CHECK: [[RET:%.*]] = alloca [[STRUCT2:%.*]], align 4
// CHECK: call void @llvm.memset
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 1
Expand All @@ -153,15 +151,15 @@ TEST(struct_2);
// CHECK: ret { i64, i64 } [[R1]]
// CHECK: }
// CHECK-LABEL: define dso_local swiftcc void @take_struct_2(i64 %0, i64 %1) {{.*}}{
// CHECK: [[V:%.*]] = alloca [[STRUCT:%.*]], align 4
// CHECK: [[V:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 1
// CHECK: store i64 %1, ptr [[GEP1]], align 4
// CHECK: ret void
// CHECK: }
// CHECK-LABEL: define dso_local void @test_struct_2() {{.*}} {
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2_TYPE]], align 4
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[CALL:%.*]] = call swiftcc { i64, i64 } @return_struct_2()
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw {{.*}} [[TMP]], i32 0, i32 0
// CHECK: [[T0:%.*]] = extractvalue { i64, i64 } [[CALL]], 0
Expand Down Expand Up @@ -234,7 +232,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define dso_local swiftcc i64 @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[UNION:%.*]], align 8
// CHECK: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} [[RET]]
// CHECK: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} [[RET]]
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw { i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[R0:%.*]] = load i64, ptr [[GEP]], align 8
// CHECK: ret i64 [[R0]]
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenObjC/designated-initializers.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
char L[3];
int M;
} overwrite_string[] = { { { @encode(void**) }, 1 }, [0].L[1] = 'x'};
// CHECK: [3 x i8] c"^xv", i32 1
// CHECK: [3 x i8] c"^xv", i8 0, i32 1
22 changes: 17 additions & 5 deletions libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,13 @@ function(_get_common_compile_options output_var flags)
list(APPEND compile_options "-Wconversion")
list(APPEND compile_options "-Wno-sign-conversion")
# Silence this warning because _Complex is a part of C99.
list(APPEND compile_options "-Wno-c99-extensions")
list(APPEND compile_options "-Wno-pedantic")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND compile_options "-fext-numeric-literals")
list(APPEND compile_options "-Wno-pedantic")
else()
list(APPEND compile_options "-Wno-c99-extensions")
list(APPEND compile_options "-Wno-gnu-imaginary-constant")
endif()
list(APPEND compile_options "-Wimplicit-fallthrough")
list(APPEND compile_options "-Wwrite-strings")
list(APPEND compile_options "-Wextra-semi")
Expand Down Expand Up @@ -210,7 +215,7 @@ function(_get_common_test_compile_options output_var c_test flags)
list(APPEND compile_options "-fno-exceptions")
list(APPEND compile_options "-fno-unwind-tables")
list(APPEND compile_options "-fno-asynchronous-unwind-tables")
if(NOT ${c_test})
if(NOT c_test)
list(APPEND compile_options "-fno-rtti")
endif()
endif()
Expand All @@ -231,8 +236,15 @@ function(_get_common_test_compile_options output_var c_test flags)
# list(APPEND compile_options "-Wwrite-strings")
# list(APPEND compile_options "-Wextra-semi")
# Silence this warning because _Complex is a part of C99.
list(APPEND compile_options "-Wno-c99-extensions")
list(APPEND compile_options "-Wno-pedantic")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(NOT c_test)
list(APPEND compile_options "-fext-numeric-literals")
list(APPEND compile_options "-Wno-pedantic")
endif()
else()
list(APPEND compile_options "-Wno-c99-extensions")
list(APPEND compile_options "-Wno-gnu-imaginary-constant")
endif()
# if(NOT CMAKE_COMPILER_IS_GNUCXX)
# list(APPEND compile_options "-Wnewline-eof")
# list(APPEND compile_options "-Wnonportable-system-include-path")
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/API/SBCommandReturnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class LLDB_API SBCommandReturnObject {
const char *GetOutput();

const char *GetError();
SBStructuredData GetErrorData();

#ifndef SWIG
LLDB_DEPRECATED_FIXME("Use PutOutput(SBFile) or PutOutput(FileSP)",
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/API/SBStructuredData.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef LLDB_API_SBSTRUCTUREDDATA_H
#define LLDB_API_SBSTRUCTUREDDATA_H

#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBModule.h"
#include "lldb/API/SBScriptObject.h"
Expand Down Expand Up @@ -110,6 +111,7 @@ class SBStructuredData {

protected:
friend class SBAttachInfo;
friend class SBCommandReturnObject;
friend class SBLaunchInfo;
friend class SBDebugger;
friend class SBTarget;
Expand Down
13 changes: 10 additions & 3 deletions lldb/include/lldb/Interpreter/CommandReturnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StreamTee.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-private.h"

#include "llvm/ADT/StringRef.h"
Expand All @@ -31,7 +32,7 @@ class CommandReturnObject {
~CommandReturnObject() = default;

/// Format any inline diagnostics with an indentation of \c indent.
llvm::StringRef GetInlineDiagnosticString(unsigned indent);
std::string GetInlineDiagnosticString(unsigned indent);

llvm::StringRef GetOutputString() {
lldb::StreamSP stream_sp(m_out_stream.GetStreamAtIndex(eStreamStringIndex));
Expand All @@ -40,7 +41,13 @@ class CommandReturnObject {
return llvm::StringRef();
}

llvm::StringRef GetErrorString();
/// Return the errors as a string.
///
/// If \c with_diagnostics is true, all diagnostics are also
/// rendered into the string. Otherwise the expectation is that they
/// are fetched with \ref GetInlineDiagnosticString().
std::string GetErrorString(bool with_diagnostics = true);
StructuredData::ObjectSP GetErrorData();

Stream &GetOutputStream() {
// Make sure we at least have our normal string stream output stream
Expand Down Expand Up @@ -168,7 +175,6 @@ class CommandReturnObject {
StreamTee m_out_stream;
StreamTee m_err_stream;
std::vector<DiagnosticDetail> m_diagnostics;
StreamString m_diag_stream;
std::optional<uint16_t> m_diagnostic_indent;

lldb::ReturnStatus m_status = lldb::eReturnStatusStarted;
Expand All @@ -178,6 +184,7 @@ class CommandReturnObject {

/// If true, then the input handle from the debugger will be hooked up.
bool m_interactive = true;
bool m_colors;
};

} // namespace lldb_private
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/API/SBCommandReturnObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "lldb/API/SBError.h"
#include "lldb/API/SBFile.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStructuredData.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Instrumentation.h"
Expand Down Expand Up @@ -96,6 +98,15 @@ const char *SBCommandReturnObject::GetError() {
return output.AsCString(/*value_if_empty*/ "");
}

SBStructuredData SBCommandReturnObject::GetErrorData() {
LLDB_INSTRUMENT_VA(this);

StructuredData::ObjectSP data(ref().GetErrorData());
SBStructuredData sb_data;
sb_data.m_impl_up->SetObjectSP(data);
return sb_data;
}

size_t SBCommandReturnObject::GetOutputSize() {
LLDB_INSTRUMENT_VA(this);

Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Commands/CommandObjectDWIMPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
// Record the position of the expression in the command.
std::optional<uint16_t> indent;
if (fixed_expression.empty()) {
size_t pos = m_original_command.find(expr);
size_t pos = m_original_command.rfind(expr);
if (pos != llvm::StringRef::npos)
indent = pos;
}
Expand Down
42 changes: 13 additions & 29 deletions lldb/source/Commands/CommandObjectExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,35 +485,8 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,

result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
// Retrieve the diagnostics.
std::vector<DiagnosticDetail> details;
llvm::consumeError(llvm::handleErrors(
result_valobj_sp->GetError().ToError(),
[&](DiagnosticError &error) { details = error.GetDetails(); }));
// Find the position of the expression in the command.
std::optional<uint16_t> expr_pos;
size_t nchar = m_original_command.find(expr);
if (nchar != std::string::npos)
expr_pos = nchar + GetDebugger().GetPrompt().size();

if (!details.empty()) {
bool show_inline =
GetDebugger().GetShowInlineDiagnostics() && !expr.contains('\n');
RenderDiagnosticDetails(error_stream, expr_pos, show_inline, details);
} else {
const char *error_cstr = result_valobj_sp->GetError().AsCString();
llvm::StringRef error(error_cstr);
if (!error.empty()) {
if (!error.starts_with("error:"))
error_stream << "error: ";
error_stream << error;
if (!error.ends_with('\n'))
error_stream.EOL();
} else {
error_stream << "error: unknown error\n";
}
}
result.SetStatus(eReturnStatusFailed);
result.SetError(result_valobj_sp->GetError().ToError());
}
}
} else {
Expand All @@ -533,10 +506,13 @@ void CommandObjectExpression::IOHandlerInputComplete(IOHandler &io_handler,
CommandReturnObject return_obj(
GetCommandInterpreter().GetDebugger().GetUseColor());
EvaluateExpression(line.c_str(), *output_sp, *error_sp, return_obj);

if (output_sp)
output_sp->Flush();
if (error_sp)
if (error_sp) {
*error_sp << return_obj.GetErrorString();
error_sp->Flush();
}
}

bool CommandObjectExpression::IOHandlerIsInputComplete(IOHandler &io_handler,
Expand Down Expand Up @@ -679,6 +655,14 @@ void CommandObjectExpression::DoExecute(llvm::StringRef command,
}
}

// Previously the indent was set up for diagnosing command line
// parsing errors. Now point it to the expression.
std::optional<uint16_t> indent;
size_t pos = m_original_command.rfind(expr);
if (pos != llvm::StringRef::npos)
indent = pos;
result.SetDiagnosticIndent(indent);

Target &target = GetTarget();
if (EvaluateExpression(expr, result.GetOutputStream(),
result.GetErrorStream(), result)) {
Expand Down
4 changes: 1 addition & 3 deletions lldb/source/Commands/CommandObjectTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3583,12 +3583,10 @@ class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed {
addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target);
if (abi)
start_addr = abi->FixCodeAddress(start_addr);
range.GetBaseAddress().SetLoadAddress(start_addr, target);

FuncUnwindersSP func_unwinders_sp(
sc.module_sp->GetUnwindTable()
.GetUncachedFuncUnwindersContainingAddress(range.GetBaseAddress(),
sc));
.GetUncachedFuncUnwindersContainingAddress(start_addr, sc));
if (!func_unwinders_sp)
continue;

Expand Down
23 changes: 11 additions & 12 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2636,20 +2636,18 @@ void CommandInterpreter::HandleCommands(const StringList &commands,
}

if (!success || !tmp_result.Succeeded()) {
llvm::StringRef error_msg = tmp_result.GetErrorString();
std::string error_msg = tmp_result.GetErrorString();
if (error_msg.empty())
error_msg = "<unknown error>.\n";
if (options.GetStopOnError()) {
result.AppendErrorWithFormat(
"Aborting reading of commands after command #%" PRIu64
": '%s' failed with %s",
(uint64_t)idx, cmd, error_msg.str().c_str());
result.AppendErrorWithFormatv("Aborting reading of commands after "
"command #{0}: '{1}' failed with {2}",
(uint64_t)idx, cmd, error_msg);
m_debugger.SetAsyncExecution(old_async_execution);
return;
} else if (options.GetPrintResults()) {
result.AppendMessageWithFormat(
"Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd,
error_msg.str().c_str());
result.AppendMessageWithFormatv("Command #{0} '{1}' failed with {2}",
(uint64_t)idx + 1, cmd, error_msg);
}
}

Expand Down Expand Up @@ -3187,11 +3185,12 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) ||
io_handler.GetFlags().Test(eHandleCommandFlagPrintErrors)) {
// Display any inline diagnostics first.
if (!result.GetImmediateErrorStream() &&
GetDebugger().GetShowInlineDiagnostics()) {
const bool inline_diagnostics = !result.GetImmediateErrorStream() &&
GetDebugger().GetShowInlineDiagnostics();
if (inline_diagnostics) {
unsigned prompt_len = m_debugger.GetPrompt().size();
if (auto indent = result.GetDiagnosticIndent()) {
llvm::StringRef diags =
std::string diags =
result.GetInlineDiagnosticString(prompt_len + *indent);
PrintCommandOutput(io_handler, diags, true);
}
Expand All @@ -3207,7 +3206,7 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,

// Now emit the command error text from the command we just executed.
if (!result.GetImmediateErrorStream()) {
llvm::StringRef error = result.GetErrorString();
std::string error = result.GetErrorString(!inline_diagnostics);
PrintCommandOutput(io_handler, error, false);
}
}
Expand Down
84 changes: 67 additions & 17 deletions lldb/source/Interpreter/CommandReturnObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s) {
}

CommandReturnObject::CommandReturnObject(bool colors)
: m_out_stream(colors), m_err_stream(colors), m_diag_stream(colors) {}
: m_out_stream(colors), m_err_stream(colors), m_colors(colors) {}

void CommandReturnObject::AppendErrorWithFormat(const char *format, ...) {
SetStatus(eReturnStatusFailed);
Expand Down Expand Up @@ -123,30 +123,79 @@ void CommandReturnObject::SetError(llvm::Error error) {
}
}

llvm::StringRef
CommandReturnObject::GetInlineDiagnosticString(unsigned indent) {
RenderDiagnosticDetails(m_diag_stream, indent, true, m_diagnostics);
std::string CommandReturnObject::GetInlineDiagnosticString(unsigned indent) {
StreamString diag_stream(m_colors);
RenderDiagnosticDetails(diag_stream, indent, true, m_diagnostics);
// Duplex the diagnostics to the secondary stream (but not inlined).
if (auto stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex))
if (auto stream_sp = m_err_stream.GetStreamAtIndex(eImmediateStreamIndex))
RenderDiagnosticDetails(*stream_sp, std::nullopt, false, m_diagnostics);

// Clear them so GetErrorData() doesn't render them again.
m_diagnostics.clear();
return m_diag_stream.GetString();
return diag_stream.GetString().str();
}

llvm::StringRef CommandReturnObject::GetErrorString() {
// Diagnostics haven't been fetched; render them now (not inlined).
if (!m_diagnostics.empty()) {
RenderDiagnosticDetails(GetErrorStream(), std::nullopt, false,
m_diagnostics);
m_diagnostics.clear();
}
std::string CommandReturnObject::GetErrorString(bool with_diagnostics) {
StreamString stream(m_colors);
if (with_diagnostics)
RenderDiagnosticDetails(stream, std::nullopt, false, m_diagnostics);

lldb::StreamSP stream_sp(m_err_stream.GetStreamAtIndex(eStreamStringIndex));
if (stream_sp)
return std::static_pointer_cast<StreamString>(stream_sp)->GetString();
return llvm::StringRef();
stream << std::static_pointer_cast<StreamString>(stream_sp)->GetString();
return stream.GetString().str();
}

StructuredData::ObjectSP CommandReturnObject::GetErrorData() {
auto make_array = []() { return std::make_unique<StructuredData::Array>(); };
auto make_bool = [](bool b) {
return std::make_unique<StructuredData::Boolean>(b);
};
auto make_dict = []() {
return std::make_unique<StructuredData::Dictionary>();
};
auto make_int = [](unsigned i) {
return std::make_unique<StructuredData::UnsignedInteger>(i);
};
auto make_string = [](llvm::StringRef s) {
return std::make_unique<StructuredData::String>(s);
};
auto dict_up = make_dict();
dict_up->AddItem("version", make_int(1));
auto array_up = make_array();
for (const DiagnosticDetail &diag : m_diagnostics) {
auto detail_up = make_dict();
if (auto &sloc = diag.source_location) {
auto sloc_up = make_dict();
sloc_up->AddItem("file", make_string(sloc->file.GetPath()));
sloc_up->AddItem("line", make_int(sloc->line));
sloc_up->AddItem("length", make_int(sloc->length));
sloc_up->AddItem("hidden", make_bool(sloc->hidden));
sloc_up->AddItem("in_user_input", make_bool(sloc->in_user_input));
detail_up->AddItem("source_location", std::move(sloc_up));
}
llvm::StringRef severity = "unknown";
switch (diag.severity) {
case lldb::eSeverityError:
severity = "error";
break;
case lldb::eSeverityWarning:
severity = "warning";
break;
case lldb::eSeverityInfo:
severity = "note";
break;
}
detail_up->AddItem("severity", make_string(severity));
detail_up->AddItem("message", make_string(diag.message));
detail_up->AddItem("rendered", make_string(diag.rendered));
array_up->AddItem(std::move(detail_up));
}
dict_up->AddItem("details", std::move(array_up));
if (auto stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex)) {
auto text = std::static_pointer_cast<StreamString>(stream_sp)->GetString();
if (!text.empty())
dict_up->AddItem("text", make_string(text));
}
return dict_up;
}

// Similar to AppendError, but do not prepend 'Status: ' to message, and don't
Expand Down Expand Up @@ -179,6 +228,7 @@ void CommandReturnObject::Clear() {
stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex);
if (stream_sp)
static_cast<StreamString *>(stream_sp.get())->Clear();
m_diagnostics.clear();
m_status = eReturnStatusStarted;
m_did_change_process_state = false;
m_suppress_immediate_output = false;
Expand Down
12 changes: 6 additions & 6 deletions lldb/source/Symbol/UnwindTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ UnwindTable::GetAddressRange(const Address &addr, const SymbolContext &sc) {
m_object_file_unwind_up->GetAddressRange(addr, range))
return range;

// Check the symbol context
if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
false, range) &&
range.GetBaseAddress().IsValid())
return range;

// Does the eh_frame unwind info has a function bounds for this addr?
if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
return range;
Expand All @@ -107,12 +113,6 @@ UnwindTable::GetAddressRange(const Address &addr, const SymbolContext &sc) {
if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
return range;

// Check the symbol context
if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
false, range) &&
range.GetBaseAddress().IsValid())
return range;

return std::nullopt;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,53 +184,44 @@ def test_source_locations_from_objc_modules(self):
# the first argument are probably stable enough that this test can check for them.
self.assertIn("void NSLog(NSString *format", value.GetError().GetCString())

def test_command_expr_formatting(self):
"""Test that the source and caret positions LLDB prints are correct"""
def test_command_expr_sbdata(self):
"""Test the structured diagnostics data"""
self.build()

(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "// Break here", self.main_source_spec
)
frame = thread.GetFrameAtIndex(0)
self.expect("settings set show-inline-diagnostics true")

def check(input_ref):
self.expect(input_ref[0], error=True, substrs=input_ref[1:])

check(
[
"expression -- a+b",
" ^ ^",
" | error: use of undeclared identifier 'b'",
" error: use of undeclared identifier 'a'",
]
)

check(
[
"expr -- a",
" ^",
" error: use of undeclared identifier 'a'",
]
)
check(
[
"expr -i 0 -o 0 -- a",
" ^",
" error: use of undeclared identifier 'a'",
]
)

self.expect(
"expression --top-level -- template<typename T> T FOO(T x) { return x/2;}"
)
check(
[
'expression -- FOO("")',
" ^",
" note: in instantiation of function template specialization 'FOO<const char *>' requested here",
"error: <user expression",
"invalid operands to binary expression",
]
)
check(["expression --\na\n+\nb", "error: <user", "a", "error: <user", "b"])
interp = self.dbg.GetCommandInterpreter()
cro = lldb.SBCommandReturnObject()
interp.HandleCommand("expression -- a+b", cro)

diags = cro.GetErrorData()
# Version.
version = diags.GetValueForKey("version")
self.assertEqual(version.GetIntegerValue(), 1)

details = diags.GetValueForKey("details")

# Detail 1/2: undeclared 'a'
diag = details.GetItemAtIndex(0)

severity = diag.GetValueForKey("severity")
message = diag.GetValueForKey("message")
rendered = diag.GetValueForKey("rendered")
sloc = diag.GetValueForKey("source_location")
filename = sloc.GetValueForKey("file")
hidden = sloc.GetValueForKey("hidden")
in_user_input = sloc.GetValueForKey("in_user_input")

self.assertEqual(str(severity), "error")
self.assertIn("undeclared identifier 'a'", str(message))
# The rendered string should contain the source file.
self.assertIn("user expression", str(rendered))
self.assertIn("user expression", str(filename))
self.assertFalse(hidden.GetBooleanValue())
self.assertTrue(in_user_input.GetBooleanValue())

# Detail 1/2: undeclared 'b'
diag = details.GetItemAtIndex(1)
message = diag.GetValueForKey("message")
self.assertIn("undeclared identifier 'b'", str(message))
6 changes: 6 additions & 0 deletions lldb/test/Shell/Commands/Inputs/multiline-expr.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
expression --
a
+
b

quit
32 changes: 32 additions & 0 deletions lldb/test/Shell/Commands/command-expr-diagnostics.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RUN: echo quit | %lldb -o "expression a+b" \
# RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK1
# (lldb) expression a+b
# CHECK1:{{^ \^ \^}}
# CHECK1: {{^ | error: use of undeclared identifier 'b'}}
# CHECK1: {{^ error: use of undeclared identifier 'a'}}

# RUN: echo quit | %lldb -o "expr a" \
# RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK2
# (lldb) expr a
# CHECK2:{{^ \^}}

# RUN: echo quit | %lldb -o "expr -i 0 -o 0 -- a" \
# RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK3
# (lldb) expr -i 0 -o 0 -- a
# CHECK3:{{^ \^}}
# CHECK3: {{^ error: use of undeclared identifier 'a'}}

# RUN: echo "int main(){return 0;}">%t.c
# RUN: %clang_host %t.c -o %t.exe
# RUN: echo quit | %lldb %t.exe -o "b main" -o r -o \
# RUN: "expr --top-level -- template<typename T> T FOO(T x) { return x/2;}" -o \
# RUN: "expression -- FOO(\"\")" 2>&1 | FileCheck %s --check-prefix=CHECK4
# (lldb) expression -- FOO("")
# CHECK4:{{^ \^}}
# CHECK4: {{^ note: in instantiation of function template}}
# CHECK4: error: <user expression

# RUN: echo expression --\na\n+\nb
# RUN: cat %S/Inputs/multiline-expr.txt | %lldb 2>&1 | FileCheck %s --strict-whitespace --check-prefix=CHECK5
# CHECK5: error: <user{{.*}}a
# CHECK5: error: <user{{.*}}b
256 changes: 0 additions & 256 deletions lldb/test/Shell/Unwind/Inputs/basic-block-sections-with-dwarf.s

This file was deleted.

26 changes: 0 additions & 26 deletions lldb/test/Shell/Unwind/Inputs/linux-x86_64.yaml

This file was deleted.

65 changes: 0 additions & 65 deletions lldb/test/Shell/Unwind/basic-block-sections-with-dwarf-static.test

This file was deleted.

23 changes: 0 additions & 23 deletions lldb/test/Shell/Unwind/basic-block-sections-with-dwarf.test

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,25 @@ class DGNode {
// TODO: Use a PointerIntPair for SubclassID and I.
/// For isa/dyn_cast etc.
DGNodeID SubclassID;
/// The number of unscheduled successors.
unsigned UnscheduledSuccs = 0;
/// This is true if this node has been scheduled.
bool Scheduled = false;

DGNode(Instruction *I, DGNodeID ID) : I(I), SubclassID(ID) {}
friend class MemDGNode; // For constructor.
friend class MemDGNode; // For constructor.
friend class DependencyGraph; // For UnscheduledSuccs

public:
DGNode(Instruction *I) : I(I), SubclassID(DGNodeID::DGNode) {
assert(!isMemDepNodeCandidate(I) && "Expected Non-Mem instruction, ");
}
DGNode(const DGNode &Other) = delete;
virtual ~DGNode() = default;
/// \Returns the number of unscheduled successors.
unsigned getNumUnscheduledSuccs() const { return UnscheduledSuccs; }
/// \Returns true if this node has been scheduled.
bool scheduled() const { return Scheduled; }
/// \Returns true if this is before \p Other in program order.
bool comesBefore(const DGNode *Other) { return I->comesBefore(Other->I); }
using iterator = PredIterator;
Expand Down Expand Up @@ -215,8 +224,16 @@ class MemDGNode final : public DGNode {
MemDGNode *getPrevNode() const { return PrevMemN; }
/// \Returns the next Mem DGNode in instruction order.
MemDGNode *getNextNode() const { return NextMemN; }
/// Adds the mem dependency edge PredN->this.
void addMemPred(MemDGNode *PredN) { MemPreds.insert(PredN); }
/// Adds the mem dependency edge PredN->this. This also increments the
/// UnscheduledSuccs counter of the predecessor if this node has not been
/// scheduled.
void addMemPred(MemDGNode *PredN) {
auto Inserted = MemPreds.insert(PredN).second;
assert(Inserted && "PredN already exists!");
if (!Scheduled) {
++PredN->UnscheduledSuccs;
}
}
/// \Returns true if there is a memory dependency N->this.
bool hasMemPred(DGNode *N) const {
if (auto *MN = dyn_cast<MemDGNode>(N))
Expand Down Expand Up @@ -284,6 +301,10 @@ class DependencyGraph {
/// \p DstN.
void scanAndAddDeps(MemDGNode &DstN, const Interval<MemDGNode> &SrcScanRange);

/// Sets the UnscheduledSuccs of all DGNodes in \p NewInterval based on
/// def-use edges.
void setDefUseUnscheduledSuccs(const Interval<Instruction> &NewInterval);

/// Create DAG nodes for instrs in \p NewInterval and update the MemNode
/// chain.
void createNewNodes(const Interval<Instruction> &NewInterval);
Expand Down
20 changes: 10 additions & 10 deletions llvm/lib/Target/RISCV/RISCVInstrInfoC.td
Original file line number Diff line number Diff line change
Expand Up @@ -707,23 +707,23 @@ def C_SLLI64_HINT : RVInst16CI<0b000, 0b10, (outs GPR:$rd_wb), (ins GPR:$rd),
let Inst{12} = 0;
}

def C_SRLI64_HINT : RVInst16CI<0b100, 0b01, (outs GPRC:$rd_wb),
(ins GPRC:$rd),
"c.srli64", "$rd">,
def C_SRLI64_HINT : RVInst16CB<0b100, 0b01, (outs GPRC:$rs1_wb),
(ins GPRC:$rs1),
"c.srli64", "$rs1">,
Sched<[WriteShiftImm, ReadShiftImm]> {
let Constraints = "$rd = $rd_wb";
let Constraints = "$rs1 = $rs1_wb";
let Inst{6-2} = 0;
let Inst{11-10} = 0;
let Inst{11-10} = 0b00;
let Inst{12} = 0;
}

def C_SRAI64_HINT : RVInst16CI<0b100, 0b01, (outs GPRC:$rd_wb),
(ins GPRC:$rd),
"c.srai64", "$rd">,
def C_SRAI64_HINT : RVInst16CB<0b100, 0b01, (outs GPRC:$rs1_wb),
(ins GPRC:$rs1),
"c.srai64", "$rs1">,
Sched<[WriteShiftImm, ReadShiftImm]> {
let Constraints = "$rd = $rd_wb";
let Constraints = "$rs1 = $rs1_wb";
let Inst{6-2} = 0;
let Inst{11-10} = 1;
let Inst{11-10} = 0b01;
let Inst{12} = 0;
}

Expand Down
70 changes: 60 additions & 10 deletions llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,17 @@ bool PredIterator::operator==(const PredIterator &Other) const {
}

#ifndef NDEBUG
void DGNode::print(raw_ostream &OS, bool PrintDeps) const { I->dumpOS(OS); }
void DGNode::dump() const {
print(dbgs());
dbgs() << "\n";
void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
OS << *I << " USuccs:" << UnscheduledSuccs << "\n";
}
void DGNode::dump() const { print(dbgs()); }
void MemDGNode::print(raw_ostream &OS, bool PrintDeps) const {
I->dumpOS(OS);
DGNode::print(OS, false);
if (PrintDeps) {
// Print memory preds.
static constexpr const unsigned Indent = 4;
for (auto *Pred : MemPreds) {
OS.indent(Indent) << "<-";
Pred->print(OS, false);
OS << "\n";
}
for (auto *Pred : MemPreds)
OS.indent(Indent) << "<-" << *Pred->getInstruction() << "\n";
}
}
#endif // NDEBUG
Expand Down Expand Up @@ -215,6 +211,58 @@ void DependencyGraph::scanAndAddDeps(MemDGNode &DstN,
}
}

void DependencyGraph::setDefUseUnscheduledSuccs(
const Interval<Instruction> &NewInterval) {
// +---+
// | | Def
// | | |
// | | v
// | | Use
// +---+
// Set the intra-interval counters in NewInterval.
for (Instruction &I : NewInterval) {
for (Value *Op : I.operands()) {
auto *OpI = dyn_cast<Instruction>(Op);
if (OpI == nullptr)
continue;
if (!NewInterval.contains(OpI))
continue;
auto *OpN = getNode(OpI);
if (OpN == nullptr)
continue;
++OpN->UnscheduledSuccs;
}
}

// Now handle the cross-interval edges.
bool NewIsAbove = DAGInterval.empty() || NewInterval.comesBefore(DAGInterval);
const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
// +---+
// |Top|
// | | Def
// +---+ |
// | | v
// |Bot| Use
// | |
// +---+
// Walk over all instructions in "BotInterval" and update the counter
// of operands that are in "TopInterval".
for (Instruction &BotI : BotInterval) {
for (Value *Op : BotI.operands()) {
auto *OpI = dyn_cast<Instruction>(Op);
if (OpI == nullptr)
continue;
if (!TopInterval.contains(OpI))
continue;
auto *OpN = getNode(OpI);
if (OpN == nullptr)
continue;
++OpN->UnscheduledSuccs;
}
}
}

void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
// Create Nodes only for the new sections of the DAG.
DGNode *LastN = getOrCreateNode(NewInterval.top());
Expand Down Expand Up @@ -260,6 +308,8 @@ void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
}
#endif // NDEBUG
}

setDefUseUnscheduledSuccs(NewInterval);
}

Interval<Instruction> DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
Expand Down
92 changes: 92 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/ARM32/vararg-arm32.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "E-m:m-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array. The first argument is stored at position 4, since it's right
; justified.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 4) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check multiple fixed arguments.
declare i32 @foo2(i32 %g1, i32 %g2, ...)
define i32 @bar2() {
%1 = call i32 (i32, i32, ...) @foo2(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; CHECK-LABEL: @bar2
; CHECK: store i64 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)

; CHECK: declare void @__msan_maybe_warning_1(i8 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_1(i8 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_2(i16 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_2(i16 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_4(i32 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_4(i32 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_8(i64 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_8(i64 signext, ptr, i32 signext)
92 changes: 92 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/Mips32/vararg-mips.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "E-m:m-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array. The first argument is stored at position 4, since it's right
; justified.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 4) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check multiple fixed arguments.
declare i32 @foo2(i32 %g1, i32 %g2, ...)
define i32 @bar2() {
%1 = call i32 (i32, i32, ...) @foo2(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; CHECK-LABEL: @bar2
; CHECK: store i64 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)

; CHECK: declare void @__msan_maybe_warning_1(i8 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_1(i8 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_2(i16 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_2(i16 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_4(i32 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_4(i32 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_8(i64 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_8(i64 signext, ptr, i32 signext)
Loading