Skip to content

Commit 2dc3b50

Browse files
authored
[HLSL] Apply NoRecurse attrib to all HLSL functions (#105907)
Previously, functions named "main" got the NoRecurse attribute consistent with the behavior of C++, which HLSL largely follows. However, standard recursion is not allowed in HLSL, so all functions should really have this attribute. This doesn't prevent recursion, but rather signals that these functions aren't expected to recurse. Practically, this was done so that entry point functions named "main" would have all have the same attributes as otherwise identical entry points with other names. This required small changes to the this assignment tests because they no longer generate so many attribute sets since more of them match. related to #105244 but done to simplify testing for #89806
1 parent 1783924 commit 2dc3b50

File tree

4 files changed

+104
-7
lines changed

4 files changed

+104
-7
lines changed

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,13 +1064,17 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
10641064
// OpenCL C 2.0 v2.2-11 s6.9.i:
10651065
// Recursion is not supported.
10661066
//
1067+
// HLSL
1068+
// Recursion is not supported.
1069+
//
10671070
// SYCL v1.2.1 s3.10:
10681071
// kernels cannot include RTTI information, exception classes,
10691072
// recursive code, virtual functions or make use of C++ libraries that
10701073
// are not compiled for the device.
1071-
if (FD && ((getLangOpts().CPlusPlus && FD->isMain()) ||
1072-
getLangOpts().OpenCL || getLangOpts().SYCLIsDevice ||
1073-
(getLangOpts().CUDA && FD->hasAttr<CUDAGlobalAttr>())))
1074+
if (FD &&
1075+
((getLangOpts().CPlusPlus && FD->isMain()) || getLangOpts().OpenCL ||
1076+
getLangOpts().HLSL || getLangOpts().SYCLIsDevice ||
1077+
(getLangOpts().CUDA && FD->hasAttr<CUDAGlobalAttr>())))
10741078
Fn->addFnAttr(llvm::Attribute::NoRecurse);
10751079

10761080
llvm::RoundingMode RM = getLangOpts().getDefaultRoundingMode();
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %clang_cc1 -x hlsl -triple dxil-pc-shadermodel6.3-library -finclude-default-header %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
2+
// RUN: %clang_cc1 -x hlsl -triple dxil-pc-shadermodel6.0-compute -finclude-default-header %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
3+
4+
// Verify that a few different function types all get the NoRecurse attribute
5+
6+
#define MAX 100
7+
8+
struct Node {
9+
uint value;
10+
uint key;
11+
uint left, right;
12+
};
13+
14+
// CHECK: Function Attrs:{{.*}}norecurse
15+
// CHECK: define noundef i32 @"?Find@@YAIY0GE@UNode@@I@Z"(ptr noundef byval([100 x %struct.Node]) align 4 %SortedTree, i32 noundef %key) [[IntAttr:\#[0-9]+]]
16+
// CHECK: ret i32
17+
// Find and return value corresponding to key in the SortedTree
18+
uint Find(Node SortedTree[MAX], uint key) {
19+
uint nix = 0; // head
20+
while(true) {
21+
if (nix < 0)
22+
return 0.0; // Not found
23+
Node n = SortedTree[nix];
24+
if (n.key == key)
25+
return n.value;
26+
if (key < n.key)
27+
nix = n.left;
28+
else
29+
nix = n.right;
30+
}
31+
}
32+
33+
// CHECK: Function Attrs:{{.*}}norecurse
34+
// CHECK: define noundef i1 @"?InitTree@@YA_NY0GE@UNode@@V?$RWBuffer@T?$__vector@I$03@__clang@@@hlsl@@I@Z"(ptr noundef byval([100 x %struct.Node]) align 4 %tree, ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %encodedTree, i32 noundef %maxDepth) [[ExtAttr:\#[0-9]+]]
35+
// CHECK: ret i1
36+
// Initialize tree with given buffer
37+
// Imagine the inout works
38+
export
39+
bool InitTree(/*inout*/ Node tree[MAX], RWBuffer<uint4> encodedTree, uint maxDepth) {
40+
uint size = pow(2.f, maxDepth) - 1;
41+
if (size > MAX) return false;
42+
for (uint i = 1; i < size; i++) {
43+
tree[i].value = encodedTree[i].x;
44+
tree[i].key = encodedTree[i].y;
45+
tree[i].left = encodedTree[i].z;
46+
tree[i].right = encodedTree[i].w;
47+
}
48+
return true;
49+
}
50+
51+
RWBuffer<uint4> gTree;
52+
53+
// Mangled entry points are internal
54+
// CHECK: Function Attrs:{{.*}}norecurse
55+
// CHECK: define internal void @"?main@@YAXI@Z"(i32 noundef %GI) [[IntAttr]]
56+
// CHECK: ret void
57+
58+
// Canonical entry points are external and shader attributed
59+
// CHECK: Function Attrs:{{.*}}norecurse
60+
// CHECK: define void @main() [[EntryAttr:\#[0-9]+]]
61+
// CHECK: ret void
62+
63+
[numthreads(1,1,1)]
64+
[shader("compute")]
65+
void main(uint GI : SV_GroupIndex) {
66+
Node haystack[MAX];
67+
uint needle = 0;
68+
if (InitTree(haystack, gTree, GI))
69+
needle = Find(haystack, needle);
70+
}
71+
72+
// Mangled entry points are internal
73+
// CHECK: Function Attrs:{{.*}}norecurse
74+
// CHECK: define internal void @"?defaultMain@@YAXXZ"() [[IntAttr]]
75+
// CHECK: ret void
76+
77+
// Canonical entry points are external and shader attributed
78+
// CHECK: Function Attrs:{{.*}}norecurse
79+
// CHECK: define void @defaultMain() [[EntryAttr]]
80+
// CHECK: ret void
81+
82+
[numthreads(1,1,1)]
83+
[shader("compute")]
84+
void defaultMain() {
85+
Node haystack[MAX];
86+
uint needle = 0;
87+
if (InitTree(haystack, gTree, 4))
88+
needle = Find(haystack, needle);
89+
}
90+
91+
// CHECK: attributes [[IntAttr]] = {{.*}} norecurse
92+
// CHECK: attributes [[ExtAttr]] = {{.*}} norecurse
93+
// CHECK: attributes [[EntryAttr]] = {{.*}} norecurse

clang/test/CodeGenHLSL/this-assignment-overload.hlsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void main() {
2525
}
2626

2727
// This test makes a probably safe assumption that HLSL 202x includes operator overloading for assignment operators.
28-
// CHECK: define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #2 align 2 {
28+
// CHECK: define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 align 2 {
2929
// CHECK-NEXT:entry:
3030
// CHECK-NEXT:%this.addr = alloca ptr, align 4
3131
// CHECK-NEXT:%Another = alloca %struct.Pair, align 4
@@ -42,7 +42,7 @@ void main() {
4242
// CHECK-NEXT:%0 = load i32, ptr %First2, align 4
4343
// CHECK-NEXT:ret i32 %0
4444

45-
// CHECK: define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #2 align 2 {
45+
// CHECK: define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 align 2 {
4646
// CHECK-NEXT:entry:
4747
// CHECK-NEXT:%this.addr = alloca ptr, align 4
4848
// CHECK-NEXT:%agg.tmp = alloca %struct.Pair, align 4

clang/test/CodeGenHLSL/this-assignment.hlsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void main() {
2424
}
2525

2626
// This tests reference like implicit this in HLSL
27-
// CHECK: define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #3 align 2 {
27+
// CHECK: define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 align 2 {
2828
// CHECK-NEXT:entry:
2929
// CHECK-NEXT:%this.addr = alloca ptr, align 4
3030
// CHECK-NEXT:%Another = alloca %struct.Pair, align 4
@@ -34,7 +34,7 @@ void main() {
3434
// CHECK-NEXT:call void @llvm.memcpy.p0.p0.i32(ptr align 4 %this1, ptr align 4 %Another, i32 8, i1 false)
3535
// CHECK-NEXT:%First = getelementptr inbounds nuw %struct.Pair, ptr %this1, i32 0, i32 0
3636

37-
// CHECK: define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #3 align 2 {
37+
// CHECK: define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 align 2 {
3838
// CHECK-NEXT:entry:
3939
// CHECK-NEXT:%this.addr = alloca ptr, align 4
4040
// CHECK-NEXT:%ref.tmp = alloca %struct.Pair, align 4

0 commit comments

Comments
 (0)