Skip to content

Commit 8be5654

Browse files
committed
[HLSL] Parameter modifier parsing and AST
This change implements parsing for HLSL's parameter modifier keywords `in`, `out` and `inout`. Because HLSL doesn't support references or pointers, these keywords are used to allow parameters to be passed in and out of functions. This change only implements the parsing and AST support. In the HLSL ASTs we represent `out` and `inout` parameters as references, and we implement the semantics of by-value passing during IR generation. In HLSL parameters marked `out` and `inout` are ambiguous in function declarations, and `in`, `out` and `inout` may be ambiguous at call sites. This means a function may be defined as `fn(in T)` and `fn(inout T)` or `fn(out T)`, but not `fn(inout T)` and `fn(out T)`. If a funciton `fn` is declared with `in` and `inout` or `out` arguments, the call will be ambiguous the same as a C++ call would be ambiguous given declarations `fn(T)` and `fn(T&)`.
1 parent 0e42df4 commit 8be5654

File tree

13 files changed

+227
-1
lines changed

13 files changed

+227
-1
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4229,6 +4229,18 @@ def HLSLGroupSharedAddressSpace : TypeAttr {
42294229
let Documentation = [HLSLGroupSharedAddressSpaceDocs];
42304230
}
42314231

4232+
def HLSLParamModifier : TypeAttr {
4233+
let Spellings = [CustomKeyword<"in">, CustomKeyword<"inout">, CustomKeyword<"out">];
4234+
let Accessors = [Accessor<"isIn", [CustomKeyword<"in">]>,
4235+
Accessor<"isInOut", [CustomKeyword<"inout">]>,
4236+
Accessor<"isOut", [CustomKeyword<"out">]>,
4237+
Accessor<"isAnyOut", [CustomKeyword<"out">, CustomKeyword<"inout">]>,
4238+
Accessor<"isAnyIn", [CustomKeyword<"in">, CustomKeyword<"inout">]>];
4239+
let Subjects = SubjectList<[ParmVar]>;
4240+
let Documentation = [HLSLParamQualifierDocs];
4241+
let Args = [DefaultBoolArgument<"MergedSpelling", /*default*/0, /*fake*/1>];
4242+
}
4243+
42324244
def RandomizeLayout : InheritableAttr {
42334245
let Spellings = [GCC<"randomize_layout">];
42344246
let Subjects = SubjectList<[Record]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7061,6 +7061,25 @@ The full documentation is available here: https://learn.microsoft.com/en-us/wind
70617061
}];
70627062
}
70637063

7064+
def HLSLParamQualifierDocs : Documentation {
7065+
let Category = DocCatVariable;
7066+
let Content = [{
7067+
HLSL function parameters are passed by value. Parameter declarations support
7068+
three qualifiers to denote parameter passing behavior. The three qualifiers are
7069+
`in`, `out` and `inout`.
7070+
7071+
Parameters annotated with `in` or with no annotation are passed by value from
7072+
the caller to the callee.
7073+
7074+
Parameters annotated with `out` are written to the argument after the callee
7075+
returns (Note: arguments values passed into `out` parameters _are not_ copied
7076+
into the callee).
7077+
7078+
Parameters annotated with `inout` are copied into the callee via a temporary,
7079+
and copied back to the argument after the callee returns.
7080+
}];
7081+
}
7082+
70647083
def AnnotateTypeDocs : Documentation {
70657084
let Category = DocCatType;
70667085
let Heading = "annotate_type";

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11989,6 +11989,7 @@ def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numt
1198911989
def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">;
1199011990
def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">;
1199111991
def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">;
11992+
def err_hlsl_duplicate_parameter_modifier : Error<"duplicate parameter modifier %0">;
1199211993
def err_hlsl_missing_semantic_annotation : Error<
1199311994
"semantic annotations must be present for all parameters of an entry "
1199411995
"function or patch constant function">;
@@ -12004,6 +12005,9 @@ def err_hlsl_pointers_unsupported : Error<
1200412005
def err_hlsl_operator_unsupported : Error<
1200512006
"the '%select{&|*|->}0' operator is unsupported in HLSL">;
1200612007

12008+
def err_hlsl_param_qualifier_mismatch :
12009+
Error<"conflicting parameter qualifier %0 on parameter %1">;
12010+
1200712011
// Layout randomization diagnostics.
1200812012
def err_non_designated_init_used : Error<
1200912013
"a randomized struct can only be initialized with a designated initializer">;

clang/include/clang/Basic/TokenKinds.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,9 @@ KEYWORD(__noinline__ , KEYCUDA)
626626
KEYWORD(cbuffer , KEYHLSL)
627627
KEYWORD(tbuffer , KEYHLSL)
628628
KEYWORD(groupshared , KEYHLSL)
629+
KEYWORD(in , KEYHLSL)
630+
KEYWORD(inout , KEYHLSL)
631+
KEYWORD(out , KEYHLSL)
629632

630633
// OpenMP Type Traits
631634
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL)

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3732,6 +3732,9 @@ class Sema final {
37323732
int X, int Y, int Z);
37333733
HLSLShaderAttr *mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL,
37343734
HLSLShaderAttr::ShaderType ShaderType);
3735+
HLSLParamModifierAttr *
3736+
mergeHLSLParamModifierAttr(Decl *D, const AttributeCommonInfo &AL,
3737+
HLSLParamModifierAttr::Spelling Spelling);
37353738

37363739
void mergeDeclAttributes(NamedDecl *New, Decl *Old,
37373740
AvailabilityMergeKind AMK = AMK_Redeclaration);

clang/lib/AST/TypePrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
18941894
case attr::ArmMveStrictPolymorphism:
18951895
OS << "__clang_arm_mve_strict_polymorphism";
18961896
break;
1897+
1898+
// Nothing to print for this attribute.
1899+
case attr::HLSLParamModifier:
1900+
break;
18971901
}
18981902
OS << "))";
18991903
}

clang/lib/Parse/ParseDecl.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4528,6 +4528,9 @@ void Parser::ParseDeclarationSpecifiers(
45284528
break;
45294529

45304530
case tok::kw_groupshared:
4531+
case tok::kw_in:
4532+
case tok::kw_inout:
4533+
case tok::kw_out:
45314534
// NOTE: ParseHLSLQualifiers will consume the qualifier token.
45324535
ParseHLSLQualifiers(DS.getAttributes());
45334536
continue;
@@ -5559,7 +5562,6 @@ bool Parser::isTypeSpecifierQualifier() {
55595562
case tok::kw___read_write:
55605563
case tok::kw___write_only:
55615564
case tok::kw___funcref:
5562-
case tok::kw_groupshared:
55635565
return true;
55645566

55655567
case tok::kw_private:
@@ -5568,6 +5570,13 @@ bool Parser::isTypeSpecifierQualifier() {
55685570
// C11 _Atomic
55695571
case tok::kw__Atomic:
55705572
return true;
5573+
5574+
// HLSL type qualifiers
5575+
case tok::kw_groupshared:
5576+
case tok::kw_in:
5577+
case tok::kw_inout:
5578+
case tok::kw_out:
5579+
return getLangOpts().HLSL;
55715580
}
55725581
}
55735582

@@ -6067,6 +6076,9 @@ void Parser::ParseTypeQualifierListOpt(
60676076
break;
60686077

60696078
case tok::kw_groupshared:
6079+
case tok::kw_in:
6080+
case tok::kw_inout:
6081+
case tok::kw_out:
60706082
// NOTE: ParseHLSLQualifiers will consume the qualifier token.
60716083
ParseHLSLQualifiers(DS.getAttributes());
60726084
continue;

clang/lib/Parse/ParseTentative.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,9 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
15291529

15301530
// HLSL address space qualifiers
15311531
case tok::kw_groupshared:
1532+
case tok::kw_in:
1533+
case tok::kw_inout:
1534+
case tok::kw_out:
15321535

15331536
// GNU
15341537
case tok::kw_restrict:

clang/lib/Sema/SemaDecl.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3368,6 +3368,26 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
33683368
diag::note_carries_dependency_missing_first_decl) << 1/*Param*/;
33693369
}
33703370

3371+
// HLSL parameter declarations for inout and out must match between
3372+
// declarations. In HLSL inout and out are ambiguous at the call site, but
3373+
// have different calling behavior, so you cannot overload a method based on a
3374+
// difference between inout and out annotations.
3375+
if (S.getLangOpts().HLSL) {
3376+
const auto *NDAttr = newDecl->getAttr<HLSLParamModifierAttr>();
3377+
const auto *ODAttr = oldDecl->getAttr<HLSLParamModifierAttr>();
3378+
// We don't need to cover the case where one declaration doesn't have an
3379+
// attribute. The only possible case there is if one declaration has an `in`
3380+
// attribute and the other declaration has no attribute. This case is
3381+
// allowed since parameters are `in` by default.
3382+
if (NDAttr && ODAttr &&
3383+
NDAttr->getSpellingListIndex() != ODAttr->getSpellingListIndex()) {
3384+
S.Diag(newDecl->getLocation(), diag::err_hlsl_param_qualifier_mismatch)
3385+
<< NDAttr << newDecl;
3386+
S.Diag(oldDecl->getLocation(), diag::note_previous_declaration_as)
3387+
<< ODAttr;
3388+
}
3389+
}
3390+
33713391
if (!oldDecl->hasAttrs())
33723392
return;
33733393

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7306,6 +7306,36 @@ static void handleHLSLResourceBindingAttr(Sema &S, Decl *D,
73067306
D->addAttr(NewAttr);
73077307
}
73087308

7309+
static void handleHLSLParamModifierAttr(Sema &S, Decl *D,
7310+
const ParsedAttr &AL) {
7311+
HLSLParamModifierAttr *NewAttr = S.mergeHLSLParamModifierAttr(
7312+
D, AL,
7313+
static_cast<HLSLParamModifierAttr::Spelling>(AL.getSemanticSpelling()));
7314+
if (NewAttr)
7315+
D->addAttr(NewAttr);
7316+
}
7317+
7318+
HLSLParamModifierAttr *
7319+
Sema::mergeHLSLParamModifierAttr(Decl *D, const AttributeCommonInfo &AL,
7320+
HLSLParamModifierAttr::Spelling Spelling) {
7321+
// We can only merge an `in` attribute with an `out` attribute. All other
7322+
// combinations of duplicated attributes are ill-formed.
7323+
if (HLSLParamModifierAttr *PA = D->getAttr<HLSLParamModifierAttr>()) {
7324+
if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) ||
7325+
(PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) {
7326+
D->dropAttr<HLSLParamModifierAttr>();
7327+
SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()};
7328+
return HLSLParamModifierAttr::Create(
7329+
Context, /*MergedSpelling=*/true, AdjustedRange,
7330+
HLSLParamModifierAttr::Keyword_inout);
7331+
}
7332+
Diag(AL.getLoc(), diag::err_hlsl_duplicate_parameter_modifier) << AL;
7333+
Diag(PA->getLocation(), diag::note_conflicting_attribute);
7334+
return nullptr;
7335+
}
7336+
return HLSLParamModifierAttr::Create(Context, AL);
7337+
}
7338+
73097339
static void handleMSInheritanceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
73107340
if (!S.LangOpts.CPlusPlus) {
73117341
S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang)
@@ -9456,6 +9486,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
94569486
case ParsedAttr::AT_HLSLResourceBinding:
94579487
handleHLSLResourceBindingAttr(S, D, AL);
94589488
break;
9489+
case ParsedAttr::AT_HLSLParamModifier:
9490+
handleHLSLParamModifierAttr(S, D, AL);
9491+
break;
94599492

94609493
case ParsedAttr::AT_AbiTag:
94619494
handleAbiTagAttr(S, D, AL);

0 commit comments

Comments
 (0)