| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| .. title:: clang-tidy - cert-arr39-c | ||
| .. meta:: | ||
| :http-equiv=refresh: 5;URL=../bugprone/sizeof-expression.html | ||
|
|
||
| cert-arr39-c | ||
| ============ | ||
|
|
||
| The `cert-arr39-c` check is an alias, please see | ||
| :doc:`bugprone-sizeof-expression <../bugprone/sizeof-expression>` | ||
| for more information. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,7 +103,7 @@ Options | |
|
|
||
| int i = -42; | ||
| unsigned int u = 0xffffffff; | ||
| printf("%u %d\n", i, u); | ||
|
|
||
| would be converted to: | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // RUN: %check_clang_tidy -std=c11-or-later %s bugprone-sizeof-expression %t | ||
|
|
||
| #define alignof(type_name) _Alignof(type_name) | ||
| extern void sink(const void *P); | ||
|
|
||
| enum { BufferSize = 1024 }; | ||
|
|
||
| struct S { | ||
| long A, B, C; | ||
| }; | ||
|
|
||
| void bad4d(void) { | ||
| struct S Buffer[BufferSize]; | ||
|
|
||
| struct S *P = &Buffer[0]; | ||
| struct S *Q = P; | ||
| while (Q < P + alignof(Buffer)) { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'alignof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator [bugprone-sizeof-expression] | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}} | ||
| sink(Q++); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,372 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-sizeof-expression %t | ||
|
|
||
| #define offsetof(type, member) __builtin_offsetof(type, member) | ||
|
|
||
| typedef __SIZE_TYPE__ size_t; | ||
| typedef __WCHAR_TYPE__ wchar_t; | ||
|
|
||
| extern void *memset(void *Dest, int Ch, size_t Count); | ||
| extern size_t strlen(const char *Str); | ||
| extern size_t wcslen(const wchar_t *Str); | ||
| extern char *strcpy(char *Dest, const char *Src); | ||
| extern wchar_t *wcscpy(wchar_t *Dest, const wchar_t *Src); | ||
| extern int scanf(const char *Format, ...); | ||
| extern int wscanf(const wchar_t *Format, ...); | ||
|
|
||
| extern void sink(const void *P); | ||
|
|
||
| enum { BufferSize = 1024 }; | ||
|
|
||
| void bad1a(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + sizeof(Buffer)) { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator [bugprone-sizeof-expression] | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}} | ||
| *Q++ = 0; | ||
| } | ||
| } | ||
|
|
||
| void bad1b(void) { | ||
| typedef int Integer; | ||
| Integer Buffer[BufferSize]; | ||
|
|
||
| Integer *P = &Buffer[0]; | ||
| Integer *Q = P; | ||
| while (Q < P + sizeof(Buffer)) { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(Integer)' == {{[0-9]+}} | ||
| *Q++ = 0; | ||
| } | ||
| } | ||
|
|
||
| void good1(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q++ = 0; | ||
| } | ||
| } | ||
|
|
||
| void bad2(void) { | ||
| int Buffer[BufferSize]; | ||
| int *P = Buffer; | ||
|
|
||
| while (P < Buffer + sizeof(Buffer)) { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:21: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}} | ||
| *P++ = 0; | ||
| } | ||
| } | ||
|
|
||
| void good2(void) { | ||
| int Buffer[BufferSize]; | ||
| int *P = Buffer; | ||
|
|
||
| while (P < Buffer + BufferSize) { | ||
| *P++ = 0; | ||
| } | ||
| } | ||
|
|
||
| struct S { | ||
| long A, B, C; | ||
| }; | ||
|
|
||
| void bad3a(struct S *S) { | ||
| const size_t Offset = offsetof(struct S, B); | ||
| struct S *P = S; | ||
|
|
||
| // This is not captureable by Tidy because the size/offset expression is | ||
| // not a direct child of the pointer arithmetics. | ||
| memset(P + Offset, 0, sizeof(struct S) - Offset); | ||
| } | ||
|
|
||
| void good3a(struct S *S) { | ||
| const size_t Offset = offsetof(struct S, B); | ||
| char *P = (char*)S; | ||
|
|
||
| // This is not captureable by Tidy because the size/offset expression is | ||
| // not a direct child of the pointer arithmetics. | ||
| memset(P + Offset, 0, sizeof(struct S) - Offset); | ||
| } | ||
|
|
||
| void bad3b(struct S *S) { | ||
| memset(S + offsetof(struct S, B), 0, | ||
| sizeof(struct S) - offsetof(struct S, B)); | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: suspicious usage of 'offsetof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-3]]:12: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}} | ||
| } | ||
|
|
||
| void good3b(struct S *S) { | ||
| char *P = (char*)S; | ||
| memset(P + offsetof(struct S, B), 0, | ||
| sizeof(struct S) - offsetof(struct S, B)); | ||
| } | ||
|
|
||
| void bad3c(void) { | ||
| struct S Buffer[BufferSize]; | ||
|
|
||
| struct S *P = &Buffer[0]; | ||
| struct S *Q = P; | ||
| while (Q < P + sizeof(Buffer)) { | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:16: note: '+' in pointer arithmetic internally scales with 'sizeof(struct S)' == {{[0-9]+}} | ||
| sink(Q++); | ||
| } | ||
| } | ||
|
|
||
| void bad4(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q += sizeof(*Q); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+=' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:7: note: '+=' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}} | ||
| } | ||
| } | ||
|
|
||
| void silenced4(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q += sizeof(*Q); | ||
| } | ||
| } | ||
|
|
||
| void good4(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q += 1; | ||
| } | ||
| } | ||
|
|
||
| void good5aa(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q += ( sizeof(Buffer) / sizeof(Buffer[0]) ); | ||
| } | ||
| } | ||
|
|
||
| void good5ab(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q = Q + ( sizeof(Buffer) / sizeof(Buffer[0]) ); | ||
| } | ||
| } | ||
|
|
||
| void good5ba(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q -= ( sizeof(Buffer) / sizeof(Buffer[0]) ); | ||
| } | ||
| } | ||
|
|
||
| void good5bb(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q = Q - ( sizeof(Buffer) / sizeof(Buffer[0]) ); | ||
| } | ||
| } | ||
|
|
||
| void bad6(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q = Q + sizeof(*Q); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:11: note: '+' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}} | ||
| } | ||
| } | ||
|
|
||
| void silenced6(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q = Q + sizeof(*Q); | ||
| } | ||
| } | ||
|
|
||
| void good6(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| *Q = 0; | ||
| Q = Q + 1; | ||
| } | ||
| } | ||
|
|
||
| void silenced7(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| const char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| sink(Q); | ||
| Q = Q + sizeof(*Q); | ||
| } | ||
| } | ||
|
|
||
| void good7(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| const char *Q = P; | ||
| while (Q < P + BufferSize) { | ||
| sink(Q); | ||
| Q = Q + 1; | ||
| } | ||
| } | ||
|
|
||
| void bad8(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P; | ||
| while (Q >= P) { | ||
| *Q = 0; | ||
| Q = Q - sizeof(*Q); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '-' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:11: note: '-' in pointer arithmetic internally scales with 'sizeof(int)' == {{[0-9]+}} | ||
| } | ||
| } | ||
|
|
||
| void silenced8(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q >= P) { | ||
| *Q = 0; | ||
| Q = Q - sizeof(*Q); | ||
| } | ||
| } | ||
|
|
||
| void good8(void) { | ||
| char Buffer[BufferSize]; | ||
|
|
||
| char *P = &Buffer[0]; | ||
| char *Q = P; | ||
| while (Q >= P) { | ||
| *Q = 0; | ||
| Q = Q - 1; | ||
| } | ||
| } | ||
|
|
||
| void good9(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = P + BufferSize; | ||
| int N = Q - P; | ||
| while (N >= 0) { | ||
| Q[N] = 0; | ||
| N = N - 1; | ||
| } | ||
| } | ||
|
|
||
| void good10(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = Buffer + BufferSize; | ||
| int I = sizeof(*P) - sizeof(*Q); | ||
|
|
||
| sink(&I); | ||
| } | ||
|
|
||
| void good11(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| int *Q = Buffer + BufferSize; | ||
| int I = sizeof(Q) - sizeof(*P); | ||
|
|
||
| sink(&I); | ||
| } | ||
|
|
||
| void bad12(void) { | ||
| wchar_t Message[BufferSize]; | ||
| wcscpy(Message, L"Message: "); | ||
| wscanf(L"%s", Message + wcslen(Message) * sizeof(wchar_t)); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious usage of 'sizeof(...)' in pointer arithmetic; this scaled value will be scaled again by the '+' operator | ||
| // CHECK-MESSAGES: :[[@LINE-2]]:25: note: '+' in pointer arithmetic internally scales with 'sizeof(wchar_t)' == {{[0-9]+}} | ||
| } | ||
|
|
||
| void silenced12(void) { | ||
| char Message[BufferSize]; | ||
| strcpy(Message, "Message: "); | ||
| scanf("%s", Message + strlen(Message) * sizeof(char)); | ||
| } | ||
|
|
||
| void nomatch12(void) { | ||
| char Message[BufferSize]; | ||
| strcpy(Message, "Message: "); | ||
| scanf("%s", Message + strlen(Message)); | ||
| } | ||
|
|
||
| void good12(void) { | ||
| wchar_t Message[BufferSize]; | ||
| wcscpy(Message, L"Message: "); | ||
| wscanf(L"%s", Message + wcslen(Message)); | ||
| } | ||
|
|
||
| void good13(void) { | ||
| int Buffer[BufferSize]; | ||
|
|
||
| int *P = &Buffer[0]; | ||
| while (P < (Buffer + sizeof(Buffer) / sizeof(int))) { | ||
| // NO-WARNING: Calculating the element count of the buffer here, which is | ||
| // safe with this idiom (as long as the types don't change). | ||
| ++P; | ||
| } | ||
|
|
||
| while (P < (Buffer + sizeof(Buffer) / sizeof(Buffer[0]))) { | ||
| // NO-WARNING: Calculating the element count of the buffer here, which is | ||
| // safe with this idiom. | ||
| ++P; | ||
| } | ||
|
|
||
| while (P < (Buffer + sizeof(Buffer) / sizeof(*P))) { | ||
| // NO-WARNING: Calculating the element count of the buffer here, which is | ||
| // safe with this idiom. | ||
| ++P; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected %s | ||
| // RUN: %clang_cc1 -verify=both,ref %s | ||
|
|
||
|
|
||
| // both-no-diagnostics | ||
|
|
||
| extern const int E; | ||
| constexpr int getE() { | ||
| return E; | ||
| } | ||
| const int E = 10; | ||
| static_assert(getE() == 10); | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,NOINLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,NOINLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -O0 %s | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -O0 %s | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,INLINE | ||
|
|
||
| // Tests that implicit constructor calls for user classes will always be inlined. | ||
|
|
||
| struct Weed { | ||
| Weed() {Count += 1;} | ||
| [[maybe_unused]] void pull() {Count--;} | ||
| static int weedCount() { return Count; } | ||
| private: | ||
| static int Count; | ||
|
|
||
| } YardWeeds; | ||
|
|
||
| int Weed::Count = 1; // It begins. . . | ||
|
|
||
| struct Kitty { | ||
| unsigned burrsInFur; | ||
|
|
||
| Kitty() { | ||
| burrsInFur = 0; | ||
| } | ||
|
|
||
| void wanderInYard(int hours) { | ||
| burrsInFur = hours*Weed::weedCount()/8; | ||
| } | ||
|
|
||
| void lick() { | ||
| if(burrsInFur) { | ||
| burrsInFur--; | ||
| Weed w; | ||
| } | ||
| } | ||
|
|
||
| } Nion; | ||
|
|
||
| void NionsDay(int hours) { | ||
| static Kitty Nion; | ||
| Nion.wanderInYard(hours); | ||
| while(Nion.burrsInFur) Nion.lick(); | ||
| } | ||
|
|
||
| // CHECK: define void @main() | ||
| // CHECK-NEXT: entry: | ||
| // Verify constructor is emitted | ||
| // NOINLINE-NEXT: call void @_GLOBAL__sub_I_inline_constructors.hlsl() | ||
| // NOINLINE-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() | ||
| // NOINLINE-NEXT: call void @"?main@@YAXI@Z"(i32 %0) | ||
| // Verify inlining leaves only calls to "llvm." intrinsics | ||
| // INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} | ||
| // CHECK: ret void | ||
| [shader("compute")] | ||
| [numthreads(1,1,1)] | ||
| void main(unsigned GI : SV_GroupIndex) { | ||
| NionsDay(10); | ||
| } | ||
|
|
||
|
|
||
| // CHECK: define void @rainyMain() | ||
| // CHECK-NEXT: entry: | ||
| // Verify constructor is emitted | ||
| // NOINLINE-NEXT: call void @_GLOBAL__sub_I_inline_constructors.hlsl() | ||
| // NOINLINE-NEXT: call void @"?rainyMain@@YAXXZ"() | ||
| // Verify inlining leaves only calls to "llvm." intrinsics | ||
| // INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} | ||
| // CHECK: ret void | ||
| [shader("compute")] | ||
| [numthreads(1,1,1)] | ||
| void rainyMain() { | ||
| NionsDay(1); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -O0 -o - | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -O0 -o - | FileCheck %s --check-prefixes=CHECK,INLINE | ||
| // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,INLINE | ||
|
|
||
| // Tests that user functions will always be inlined. | ||
| // This includes exported functions and mangled entry point implementation functions. | ||
| // The unmangled entry functions must not be alwaysinlined. | ||
|
|
||
| #define MAX 100 | ||
|
|
||
| float nums[MAX]; | ||
|
|
||
| // Verify that all functions have the alwaysinline attribute | ||
| // NOINLINE: Function Attrs: alwaysinline | ||
| // NOINLINE: define void @"?swap@@YAXY0GE@III@Z"(ptr noundef byval([100 x i32]) align 4 %Buf, i32 noundef %ix1, i32 noundef %ix2) [[IntAttr:\#[0-9]+]] | ||
| // NOINLINE: ret void | ||
| // Swap the values of Buf at indices ix1 and ix2 | ||
| void swap(unsigned Buf[MAX], unsigned ix1, unsigned ix2) { | ||
| float tmp = Buf[ix1]; | ||
| Buf[ix1] = Buf[ix2]; | ||
| Buf[ix2] = tmp; | ||
| } | ||
|
|
||
| // NOINLINE: Function Attrs: alwaysinline | ||
| // NOINLINE: define void @"?BubbleSort@@YAXY0GE@II@Z"(ptr noundef byval([100 x i32]) align 4 %Buf, i32 noundef %size) [[IntAttr]] | ||
| // NOINLINE: ret void | ||
| // Inefficiently sort Buf in place | ||
| void BubbleSort(unsigned Buf[MAX], unsigned size) { | ||
| bool swapped = true; | ||
| while (swapped) { | ||
| swapped = false; | ||
| for (unsigned i = 1; i < size; i++) { | ||
| if (Buf[i] < Buf[i-1]) { | ||
| swap(Buf, i, i-1); | ||
| swapped = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Note ExtAttr is the inlined export set of attribs | ||
| // CHECK: Function Attrs: alwaysinline | ||
| // CHECK: define noundef i32 @"?RemoveDupes@@YAIY0GE@II@Z"(ptr {{[a-z_ ]*}}noundef byval([100 x i32]) align 4 %Buf, i32 noundef %size) {{[a-z_ ]*}}[[ExtAttr:\#[0-9]+]] | ||
| // CHECK: ret i32 | ||
| // Sort Buf and remove any duplicate values | ||
| // returns the number of values left | ||
| export | ||
| unsigned RemoveDupes(unsigned Buf[MAX], unsigned size) { | ||
| BubbleSort(Buf, size); | ||
| unsigned insertPt = 0; | ||
| for (unsigned i = 1; i < size; i++) { | ||
| if (Buf[i] == Buf[i-1]) | ||
| insertPt++; | ||
| else | ||
| Buf[insertPt] = Buf[i]; | ||
| } | ||
| return insertPt; | ||
| } | ||
|
|
||
|
|
||
| RWBuffer<unsigned> Indices; | ||
|
|
||
| // The mangled version of main only remains without inlining | ||
| // because it has internal linkage from the start | ||
| // Note main functions get the norecurse attrib, which IntAttr reflects | ||
| // NOINLINE: Function Attrs: alwaysinline | ||
| // NOINLINE: define internal void @"?main@@YAXI@Z"(i32 noundef %GI) [[IntAttr]] | ||
| // NOINLINE: ret void | ||
|
|
||
| // The unmangled version is not inlined, EntryAttr reflects that | ||
| // CHECK: Function Attrs: {{.*}}noinline | ||
| // CHECK: define void @main() {{[a-z_ ]*}}[[EntryAttr:\#[0-9]+]] | ||
| // Make sure function calls are inlined when AlwaysInline is run | ||
| // This only leaves calls to llvm. intrinsics | ||
| // INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} | ||
| // CHECK: ret void | ||
|
|
||
| [numthreads(1,1,1)] | ||
| [shader("compute")] | ||
| void main(unsigned int GI : SV_GroupIndex) { | ||
| unsigned tmpIndices[MAX]; | ||
| if (GI > MAX) return; | ||
| for (unsigned i = 1; i < GI; i++) | ||
| tmpIndices[i] = Indices[i]; | ||
| RemoveDupes(tmpIndices, GI); | ||
| for (unsigned i = 1; i < GI; i++) | ||
| tmpIndices[i] = Indices[i]; | ||
| } | ||
|
|
||
| // The mangled version of main only remains without inlining | ||
| // because it has internal linkage from the start | ||
| // Note main functions get the norecurse attrib, which IntAttr reflects | ||
| // NOINLINE: Function Attrs: alwaysinline | ||
| // NOINLINE: define internal void @"?main10@@YAXXZ"() [[IntAttr]] | ||
| // NOINLINE: ret void | ||
|
|
||
| // The unmangled version is not inlined, EntryAttr reflects that | ||
| // CHECK: Function Attrs: {{.*}}noinline | ||
| // CHECK: define void @main10() {{[a-z_ ]*}}[[EntryAttr]] | ||
| // Make sure function calls are inlined when AlwaysInline is run | ||
| // This only leaves calls to llvm. intrinsics | ||
| // INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} | ||
| // CHECK: ret void | ||
|
|
||
| [numthreads(1,1,1)] | ||
| [shader("compute")] | ||
| void main10() { | ||
| main(10); | ||
| } | ||
|
|
||
| // NOINLINE: attributes [[IntAttr]] = {{.*}} alwaysinline | ||
| // CHECK: attributes [[ExtAttr]] = {{.*}} alwaysinline | ||
| // CHECK: attributes [[EntryAttr]] = {{.*}} noinline |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,67 @@ | ||
| /// PS4 clang emits warnings when SDK headers (<SDKROOT>/target/include/) or | ||
| /// libraries (<SDKROOT>/target/lib/) are missing, unless the user takes control | ||
| /// of search paths, when corresponding existence checks are skipped. | ||
| /// | ||
| /// User control of header search is assumed if `--sysroot`, `-isysroot`, | ||
| /// `-nostdinc` or `-nostdlibinc` is supplied. User control of library search | ||
| /// is assumed if `--sysroot` is supplied. | ||
| /// | ||
| /// Warnings are emitted if a specified `-isysroot` or `--sysroot` does not | ||
| /// exist. | ||
| /// | ||
| /// The default <SDKROOT> for both headers and libraries is taken from the | ||
| /// SCE_ORBIS_SDK_DIR environment variable. | ||
|
|
||
| // RUN: echo "-### -Winvalid-or-nonexistent-directory -target x86_64-scei-ps4" > %t.rsp | ||
|
|
||
| /// If SDK headers and/or libraries are found, associated warnings are absent. | ||
| // RUN: rm -rf %t.inconly && mkdir -p %t.inconly/target/include | ||
| // RUN: env SCE_ORBIS_SDK_DIR=%t.inconly %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-LIBS,NO-WARN %s | ||
|
|
||
| // RUN: rm -rf %t.libonly && mkdir -p %t.libonly/target/lib | ||
| // RUN: env SCE_ORBIS_SDK_DIR=%t.libonly %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
|
|
||
| // RUN: rm -rf %t.both && mkdir -p %t.both/target/lib && mkdir %t.both/target/include | ||
| // RUN: env SCE_ORBIS_SDK_DIR=%t.both %clang @%t.rsp %s 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
|
|
||
| /// In the following invocations, SCE_ORBIS_SDK_DIR is set to an existing | ||
| /// location where SDK headers and libraries are absent. | ||
|
|
||
| /// When compiling and linking, we should see a warnings about both missing | ||
| /// headers and libraries. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,WARN-SYS-LIBS,NO-WARN %s | ||
|
|
||
| /// If `-c`, `-S`, `-E` or `-emit-ast` is supplied, the existence check for SDK | ||
| /// libraries is skipped because no linking will be performed. We only expect | ||
| /// warnings about missing headers. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -c 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -S 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -E 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -emit-ast 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
|
|
||
| /// If the user takes control of include paths, the existence check for headers | ||
| /// is not performed. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -c -nostdinc 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -c -nostdlibinc 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -c -isysroot . 2>&1 | FileCheck -check-prefixes=NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -c --sysroot=. 2>&1 | FileCheck -check-prefixes=NO-WARN %s | ||
|
|
||
| /// --sysroot disables the existence check for libraries and headers. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s --sysroot=. 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
|
|
||
| /// -isysroot overrides --sysroot for header search, but not library search. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -isysroot . --sysroot=.. 2>&1 | FileCheck -check-prefixes=ISYSTEM,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s --sysroot=.. -isysroot . 2>&1 | FileCheck -check-prefixes=ISYSTEM,NO-WARN %s | ||
|
|
||
| /// Warnings are emitted if non-existent --sysroot/-isysroot are supplied. | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s --sysroot=foo -isysroot . 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s -isysroot foo --sysroot=. 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,NO-WARN %s | ||
| // RUN: env SCE_ORBIS_SDK_DIR=.. %clang @%t.rsp %s --sysroot=foo -isysroot bar 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,WARN-SYSROOT2,NO-WARN %s | ||
|
|
||
| // NO-WARN-NOT: {{warning:|error:}} | ||
| // WARN-SYS-LIBS: warning: unable to find PS4 system libraries directory | ||
| // WARN-SYS-HEADERS: warning: unable to find PS4 system headers directory | ||
| // WARN-SYSROOT: warning: no such sysroot directory: 'foo' | ||
| // WARN-SYSROOT2: warning: no such sysroot directory: 'bar' | ||
| // NO-WARN-NOT: {{warning:|error:}} | ||
| // ISYSTEM: "-cc1"{{.*}}"-internal-externc-isystem" "./target/include" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,44 +1,69 @@ | ||
| /// (Essentially identical to ps4-sdk-root.c except for the target.) | ||
|
|
||
| /// PS5 clang emits warnings when SDK headers (<SDKROOT>/target/include/) or | ||
| /// libraries (<SDKROOT>/target/lib/) are missing, unless the user takes control | ||
| /// of search paths, when corresponding existence checks are skipped. | ||
| /// | ||
| /// User control of header search is assumed if `--sysroot`, `-isysroot`, | ||
| /// `-nostdinc` or `-nostdlibinc` is supplied. User control of library search | ||
| /// is assumed if `--sysroot` is supplied. | ||
| /// | ||
| /// Warnings are emitted if a specified `-isysroot` or `--sysroot` does not | ||
| /// exist. | ||
| /// | ||
| /// The default <SDKROOT> for both headers and libraries is taken from the | ||
| /// SCE_PROSPERO_SDK_DIR environment variable. | ||
|
|
||
| // RUN: echo "-### -Winvalid-or-nonexistent-directory -target x86_64-sie-ps5" > %t.rsp | ||
|
|
||
| /// If SDK headers and/or libraries are found, associated warnings are absent. | ||
| // RUN: rm -rf %t.inconly && mkdir -p %t.inconly/target/include | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=%t.inconly %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-LIBS,NO-WARN %s | ||
|
|
||
| // RUN: rm -rf %t.libonly && mkdir -p %t.libonly/target/lib | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=%t.libonly %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
|
|
||
| // RUN: rm -rf %t.both && mkdir -p %t.both/target/lib && mkdir %t.both/target/include | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=%t.both %clang @%t.rsp %s 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
|
|
||
| /// In the following invocations, SCE_PROSPERO_SDK_DIR is set to an existing | ||
| /// location where SDK headers and libraries are absent. | ||
|
|
||
| /// When compiling and linking, we should see a warnings about both missing | ||
| /// headers and libraries. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,WARN-SYS-LIBS,NO-WARN %s | ||
|
|
||
| /// If `-c`, `-S`, `-E` or `-emit-ast` is supplied, the existence check for SDK | ||
| /// libraries is skipped because no linking will be performed. We only expect | ||
| /// warnings about missing headers. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -c 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -S 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -E 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -emit-ast 2>&1 | FileCheck -check-prefixes=WARN-SYS-HEADERS,NO-WARN %s | ||
|
|
||
| /// If the user takes control of include paths, the existence check for headers | ||
| /// is not performed. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -c -nostdinc 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -c -nostdlibinc 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -c -isysroot . 2>&1 | FileCheck -check-prefixes=NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -c --sysroot=. 2>&1 | FileCheck -check-prefixes=NO-WARN %s | ||
|
|
||
| /// --sysroot disables the existence check for libraries and headers. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s --sysroot=. 2>&1 | FileCheck -check-prefix=NO-WARN %s | ||
|
|
||
| /// -isysroot overrides --sysroot for header search, but not library search. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -isysroot . --sysroot=.. 2>&1 | FileCheck -check-prefixes=ISYSTEM,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s --sysroot=.. -isysroot . 2>&1 | FileCheck -check-prefixes=ISYSTEM,NO-WARN %s | ||
|
|
||
| /// Warnings are emitted if non-existent --sysroot/-isysroot are supplied. | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s --sysroot=foo -isysroot . 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s -isysroot foo --sysroot=. 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,NO-WARN %s | ||
| // RUN: env SCE_PROSPERO_SDK_DIR=.. %clang @%t.rsp %s --sysroot=foo -isysroot bar 2>&1 | FileCheck -check-prefixes=WARN-SYSROOT,WARN-SYSROOT2,NO-WARN %s | ||
|
|
||
| // NO-WARN-NOT: {{warning:|error:}} | ||
| // WARN-SYS-LIBS: warning: unable to find PS5 system libraries directory | ||
| // WARN-SYS-HEADERS: warning: unable to find PS5 system headers directory | ||
| // WARN-SYSROOT: warning: no such sysroot directory: 'foo' | ||
| // WARN-SYSROOT2: warning: no such sysroot directory: 'bar' | ||
| // NO-WARN-NOT: {{warning:|error:}} | ||
| // ISYSTEM: "-cc1"{{.*}}"-internal-externc-isystem" "./target/include" |