Skip to content

Commit 6c92c23

Browse files
committed
[HLSL] add loop unroll
- `Attr.td` - Define the HLSL loop attribute hints (unroll and loop) - `AttrDocs.td` - Add documentation for unroll and loop - `CGLoopInfo.cpp` - Add codegen for HLSL unroll that maps to clang unroll expectations - `ParseStmt.cpp` - For statements if HLSL define DeclSpecAttrs via MaybeParseMicrosoftAttributes - `SemaStmtAttr.cpp` - Add the HLSL loop unroll handeling
1 parent 493eefc commit 6c92c23

File tree

6 files changed

+208
-6
lines changed

6 files changed

+208
-6
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,6 +4114,19 @@ def LoopHint : Attr {
41144114
let HasCustomParsing = 1;
41154115
}
41164116

4117+
/// The HLSL loop attributes
4118+
def HLSLLoopHint: StmtAttr {
4119+
/// [unroll(directive)]
4120+
/// [loop]
4121+
let Spellings = [Microsoft<"unroll">, Microsoft<"loop">];
4122+
let Args = [UnsignedArgument<"directive">];
4123+
let Subjects = SubjectList<[ForStmt, WhileStmt, DoStmt],
4124+
ErrorDiag, "'for', 'while', and 'do' statements">;
4125+
let LangOpts = [HLSL];
4126+
let Documentation = [HLSLLoopHintDocs, HLSLUnrollHintDocs];
4127+
let HasCustomParsing = 1;
4128+
}
4129+
41174130
def CapturedRecord : InheritableAttr {
41184131
// This attribute has no spellings as it is only ever created implicitly.
41194132
let Spellings = [];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7342,6 +7342,53 @@ where shaders must be compiled into a library and linked at runtime.
73427342
}];
73437343
}
73447344

7345+
def HLSLLoopHintDocs : Documentation {
7346+
let Category = DocCatStmt;
7347+
let Heading = "#[loop]";
7348+
let Content = [{
7349+
The ``[loop]`` directive allows loop optimization hints to be
7350+
specified for the subsequent loop. The directive allows unrolling to
7351+
be disabled and is not compatible with [unroll(x)].
7352+
7353+
Specifying the parameter, ``[loop]``, directs the
7354+
unroller to not unroll the loop.
7355+
7356+
.. code-block:: hlsl
7357+
7358+
[loop]
7359+
for (...) {
7360+
...
7361+
}
7362+
7363+
See `hlsl loop extensions
7364+
<https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-for>`_
7365+
for details.
7366+
}];
7367+
}
7368+
7369+
def HLSLUnrollHintDocs : Documentation {
7370+
let Category = DocCatStmt;
7371+
let Heading = "[unroll(x)]";
7372+
let Content = [{
7373+
Loop unrolling optimization hints can be specified with ``[unroll(x)]``
7374+
. The attribute is placed immediately before a for, while,
7375+
or do-while.
7376+
Specifying the parameter, ``[unroll(_value_)]``, directs the
7377+
unroller to unroll the loop ``_value_`` times. Note: [unroll(x)] is not compatible with [loop].
7378+
7379+
.. code-block:: hlsl
7380+
7381+
[unroll(4)]
7382+
for (...) {
7383+
...
7384+
}
7385+
See
7386+
`hlsl loop extensions
7387+
<https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-for>`_
7388+
for details.
7389+
}];
7390+
}
7391+
73457392
def ClangRandomizeLayoutDocs : Documentation {
73467393
let Category = DocCatDecl;
73477394
let Heading = "randomize_layout, no_randomize_layout";

clang/lib/CodeGen/CGLoopInfo.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,9 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
612612
const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(Attr);
613613
const OpenCLUnrollHintAttr *OpenCLHint =
614614
dyn_cast<OpenCLUnrollHintAttr>(Attr);
615-
615+
const HLSLLoopHintAttr *HLSLLoopHint = dyn_cast<HLSLLoopHintAttr>(Attr);
616616
// Skip non loop hint attributes
617-
if (!LH && !OpenCLHint) {
617+
if (!LH && !OpenCLHint && !HLSLLoopHint) {
618618
continue;
619619
}
620620

@@ -635,6 +635,17 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
635635
Option = LoopHintAttr::UnrollCount;
636636
State = LoopHintAttr::Numeric;
637637
}
638+
} else if (HLSLLoopHint) {
639+
ValueInt = HLSLLoopHint->getDirective();
640+
if (HLSLLoopHint->getSemanticSpelling() ==
641+
HLSLLoopHintAttr::Spelling::Microsoft_unroll) {
642+
if (ValueInt == 0)
643+
State = LoopHintAttr::Enable;
644+
if (ValueInt > 0) {
645+
Option = LoopHintAttr::UnrollCount;
646+
State = LoopHintAttr::Numeric;
647+
}
648+
}
638649
} else if (LH) {
639650
auto *ValueExpr = LH->getValue();
640651
if (ValueExpr) {

clang/lib/Parse/ParseStmt.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,21 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
114114
// here because we don't want to allow arbitrary orderings.
115115
ParsedAttributes CXX11Attrs(AttrFactory);
116116
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
117-
ParsedAttributes GNUAttrs(AttrFactory);
117+
ParsedAttributes DeclSpecAttrs(AttrFactory);
118118
if (getLangOpts().OpenCL)
119-
MaybeParseGNUAttributes(GNUAttrs);
119+
MaybeParseGNUAttributes(DeclSpecAttrs);
120+
121+
if (getLangOpts().HLSL)
122+
MaybeParseMicrosoftAttributes(DeclSpecAttrs);
120123

121124
StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
122-
Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUAttrs);
125+
Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, DeclSpecAttrs);
123126
MaybeDestroyTemplateIds();
124127

125128
// Attributes that are left should all go on the statement, so concatenate the
126129
// two lists.
127130
ParsedAttributes Attrs(AttrFactory);
128-
takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs);
131+
takeAndConcatenateAttrs(CXX11Attrs, DeclSpecAttrs, Attrs);
129132

130133
assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
131134
"attributes on empty statement");

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/Basic/TargetInfo.h"
1717
#include "clang/Sema/DelayedDiagnostic.h"
1818
#include "clang/Sema/Lookup.h"
19+
#include "clang/Sema/ParsedAttr.h"
1920
#include "clang/Sema/ScopeInfo.h"
2021
#include "clang/Sema/SemaInternal.h"
2122
#include "llvm/ADT/StringExtras.h"
@@ -584,6 +585,32 @@ static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
584585
return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);
585586
}
586587

588+
static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
589+
SourceRange Range) {
590+
unsigned UnrollFactor = 0;
591+
if (A.getNumArgs() == 1) {
592+
Expr *E = A.getArgAsExpr(0);
593+
std::optional<llvm::APSInt> ArgVal;
594+
595+
if (!(ArgVal = E->getIntegerConstantExpr(S.Context))) {
596+
S.Diag(A.getLoc(), diag::err_attribute_argument_type)
597+
<< A << AANT_ArgumentIntegerConstant << E->getSourceRange();
598+
return nullptr;
599+
}
600+
601+
int Val = ArgVal->getSExtValue();
602+
if (Val <= 0) {
603+
S.Diag(A.getRange().getBegin(),
604+
diag::err_attribute_requires_positive_integer)
605+
<< A << /* positive */ 0;
606+
return nullptr;
607+
}
608+
UnrollFactor = static_cast<unsigned>(Val);
609+
}
610+
611+
return ::new (S.Context) HLSLLoopHintAttr(S.Context, A, UnrollFactor);
612+
}
613+
587614
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
588615
SourceRange Range) {
589616
if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
@@ -618,6 +645,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
618645
return handleFallThroughAttr(S, St, A, Range);
619646
case ParsedAttr::AT_LoopHint:
620647
return handleLoopHintAttr(S, St, A, Range);
648+
case ParsedAttr::AT_HLSLLoopHint:
649+
return handleHLSLLoopHintAttr(S, St, A, Range);
621650
case ParsedAttr::AT_OpenCLUnrollHint:
622651
return handleOpenCLUnrollHint(S, St, A, Range);
623652
case ParsedAttr::AT_Suppress:
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
2+
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -o - | FileCheck %s
3+
4+
/*** for ***/
5+
void for_count()
6+
{
7+
// CHECK-LABEL: for_count
8+
[unroll(8)]
9+
for( int i = 0; i < 1000; ++i);
10+
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_DISTINCT:.*]]
11+
}
12+
13+
void for_disable()
14+
{
15+
// CHECK-LABEL: for_disable
16+
[loop]
17+
for( int i = 0; i < 1000; ++i);
18+
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_DISABLE:.*]]
19+
}
20+
21+
void for_enable()
22+
{
23+
// CHECK-LABEL: for_enable
24+
[unroll]
25+
for( int i = 0; i < 1000; ++i);
26+
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_ENABLE:.*]]
27+
}
28+
29+
/*** while ***/
30+
void while_count()
31+
{
32+
// CHECK-LABEL: while_count
33+
int i = 1000;
34+
[unroll(4)]
35+
while(i-->0);
36+
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_DISTINCT:.*]]
37+
}
38+
39+
void while_disable()
40+
{
41+
// CHECK-LABEL: while_disable
42+
int i = 1000;
43+
[loop]
44+
while(i-->0);
45+
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_DISABLE:.*]]
46+
}
47+
48+
void while_enable()
49+
{
50+
// CHECK-LABEL: while_enable
51+
int i = 1000;
52+
[unroll]
53+
while(i-->0);
54+
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_ENABLE:.*]]
55+
}
56+
57+
/*** do ***/
58+
void do_count()
59+
{
60+
// CHECK-LABEL: do_count
61+
int i = 1000;
62+
[unroll(16)]
63+
do {} while(i--> 0);
64+
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_DISTINCT:.*]]
65+
}
66+
67+
void do_disable()
68+
{
69+
// CHECK-LABEL: do_disable
70+
int i = 1000;
71+
[loop]
72+
do {} while(i--> 0);
73+
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_DISABLE:.*]]
74+
}
75+
76+
void do_enable()
77+
{
78+
// CHECK-LABEL: do_enable
79+
int i = 1000;
80+
[unroll]
81+
do {} while(i--> 0);
82+
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_ENABLE:.*]]
83+
}
84+
85+
86+
// CHECK: ![[FOR_DISTINCT]] = distinct !{![[FOR_DISTINCT]], ![[FOR_COUNT:.*]]}
87+
// CHECK: ![[FOR_COUNT]] = !{!"llvm.loop.unroll.count", i32 8}
88+
// CHECK: ![[FOR_DISABLE]] = distinct !{![[FOR_DISABLE]], ![[DISABLE:.*]]}
89+
// CHECK: ![[DISABLE]] = !{!"llvm.loop.unroll.disable"}
90+
// CHECK: ![[FOR_ENABLE]] = distinct !{![[FOR_ENABLE]], ![[ENABLE:.*]]}
91+
// CHECK: ![[ENABLE]] = !{!"llvm.loop.unroll.enable"}
92+
// CHECK: ![[WHILE_DISTINCT]] = distinct !{![[WHILE_DISTINCT]], ![[WHILE_COUNT:.*]]}
93+
// CHECK: ![[WHILE_COUNT]] = !{!"llvm.loop.unroll.count", i32 4}
94+
// CHECK: ![[WHILE_DISABLE]] = distinct !{![[WHILE_DISABLE]], ![[DISABLE]]}
95+
// CHECK: ![[WHILE_ENABLE]] = distinct !{![[WHILE_ENABLE]], ![[ENABLE]]}
96+
// CHECK: ![[DO_DISTINCT]] = distinct !{![[DO_DISTINCT]], ![[DO_COUNT:.*]]}
97+
// CHECK: ![[DO_COUNT]] = !{!"llvm.loop.unroll.count", i32 16}
98+
// CHECK: ![[DO_DISABLE]] = distinct !{![[DO_DISABLE]], ![[DISABLE]]}
99+
// CHECK: ![[DO_ENABLE]] = distinct !{![[DO_ENABLE]], ![[ENABLE]]}

0 commit comments

Comments
 (0)