Skip to content

Commit d6d26db

Browse files
committed
[Sema] Requires nointerpolation on GetAttributeAtVertex param #0
This commit adds new sema checks to restrict the scope of the feature: the `nointerpolation` attribute is now required on all parameters used in GetAttributeAtVertex, and doesn't propagage magically to parent/child functions. Those issues are mostly caused by the feature which is badly designed at the language/spec level. The SPIR-V side crashes during validation, which is a bit unfortunate as the error message is not trivial to understand. For this reason, I moved some restriction further up. As for DXIL, we leave it unchanged, as DXIL validation will catch those cases with a good-enough error message. The current accepted cases this now blocks were mostly broken later in the codegen, so this should be an acceptable 'regression'. Related to #6384, #6220 Fixed #6383, #6382 Signed-off-by: Nathan Gauër <[email protected]>
1 parent 8de82c9 commit d6d26db

13 files changed

+208
-8
lines changed

tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7732,6 +7732,8 @@ def warn_hlsl_implicit_vector_truncation : Warning<
77327732
InGroup<Conversion>, DefaultWarn;
77337733
def err_hlsl_nointerpolation_and_linear : Error<
77347734
"nointerpolation cannot be used with any other interpolation mode specifier">;
7735+
def err_hlsl_parameter_requires_attribute : Error<
7736+
"parameter %0 of %1 must have a a '%2' attribute">;
77357737
def warn_hlsl_duplicate_specifier : Warning<
77367738
"duplicate HLSL specifier %0">,
77377739
InGroup<IgnoredAttributes>, DefaultWarn;

tools/clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8828,6 +8828,8 @@ class Sema {
88288828
bool AllowOnePastEnd=true, bool IndexNegated=false);
88298829
// HLSL Change Starts - checking array subscript access to vector or matrix member
88308830
void CheckHLSLArrayAccess(const Expr *expr);
8831+
bool CheckHLSLIntrinsicCall(FunctionDecl *FDecl, CallExpr *TheCall);
8832+
bool CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall);
88318833
// HLSL Change ends
88328834
void CheckArrayAccess(const Expr *E);
88338835
// Used to grab the relevant information from a FormatAttr and a

tools/clang/lib/Sema/SemaChecking.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
522522

523523
TheCall->setType(Context.VoidPtrTy);
524524
break;
525-
526525
}
527526

528527
// Since the target specific builtins for each arch overlap, only check those

tools/clang/lib/Sema/SemaExpr.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5288,7 +5288,10 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
52885288
if (FDecl) {
52895289
if (CheckFunctionCall(FDecl, TheCall, Proto))
52905290
return ExprError();
5291-
5291+
if (CheckHLSLFunctionCall(FDecl, TheCall))
5292+
return ExprError();
5293+
if (hlsl::IsIntrinsicOp(FDecl) && CheckHLSLIntrinsicCall(FDecl, TheCall))
5294+
return ExprError();
52925295
if (BuiltinID)
52935296
return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
52945297
} else if (NDecl) {

tools/clang/lib/Sema/SemaHLSL.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15176,6 +15176,83 @@ QualType Sema::getHLSLDefaultSpecialization(TemplateDecl *Decl) {
1517615176
return QualType();
1517715177
}
1517815178

15179+
static bool isRelatedDeclMarkedNointerpolation(Expr *E) {
15180+
if (!E)
15181+
return false;
15182+
E = E->IgnoreCasts();
15183+
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
15184+
return DRE->getDecl()->hasAttr<HLSLNoInterpolationAttr>();
15185+
15186+
if (auto *ME = dyn_cast<MemberExpr>(E))
15187+
return ME->getMemberDecl()->hasAttr<HLSLNoInterpolationAttr>() ||
15188+
isRelatedDeclMarkedNointerpolation(ME->getBase());
15189+
15190+
if (auto *HVE = dyn_cast<HLSLVectorElementExpr>(E))
15191+
return isRelatedDeclMarkedNointerpolation(HVE->getBase());
15192+
15193+
if (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
15194+
return isRelatedDeclMarkedNointerpolation(ASE->getBase());
15195+
15196+
return false;
15197+
}
15198+
15199+
static bool CheckIntrinsicGetAttributeAtVertex(Sema *S, FunctionDecl *FDecl,
15200+
CallExpr *TheCall) {
15201+
assert(TheCall->getNumArgs() > 0);
15202+
auto argument = TheCall->getArg(0)->IgnoreCasts();
15203+
15204+
if (!isRelatedDeclMarkedNointerpolation(argument)) {
15205+
S->Diag(argument->getExprLoc(), diag::err_hlsl_parameter_requires_attribute)
15206+
<< 0 << FDecl->getName() << "nointerpolation";
15207+
return true;
15208+
}
15209+
15210+
return false;
15211+
}
15212+
15213+
bool Sema::CheckHLSLIntrinsicCall(FunctionDecl *FDecl, CallExpr *TheCall) {
15214+
auto attr = FDecl->getAttr<HLSLIntrinsicAttr>();
15215+
15216+
switch (hlsl::IntrinsicOp(attr->getOpcode())) {
15217+
case hlsl::IntrinsicOp::IOP_GetAttributeAtVertex:
15218+
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
15219+
// to limit the scope, and fail gracefully in some cases.
15220+
if (!getLangOpts().SPIRV)
15221+
return false;
15222+
if (FDecl->getName() != "GetAttributeAtVertex")
15223+
return false;
15224+
return CheckIntrinsicGetAttributeAtVertex(this, FDecl, TheCall);
15225+
default:
15226+
break;
15227+
}
15228+
15229+
return false;
15230+
}
15231+
15232+
bool Sema::CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
15233+
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
15234+
// to limit the scope, and fail gracefully in some cases.
15235+
if (!getLangOpts().SPIRV)
15236+
return false;
15237+
15238+
bool error = false;
15239+
for (unsigned i = 0; i < FDecl->getNumParams(); i++) {
15240+
assert(i < TheCall->getNumArgs());
15241+
15242+
if (!FDecl->getParamDecl(i)->hasAttr<HLSLNoInterpolationAttr>())
15243+
continue;
15244+
15245+
if (!isRelatedDeclMarkedNointerpolation(TheCall->getArg(i))) {
15246+
Diag(TheCall->getArg(i)->getExprLoc(),
15247+
diag::err_hlsl_parameter_requires_attribute)
15248+
<< i << FDecl->getName() << "nointerpolation";
15249+
error = true;
15250+
}
15251+
}
15252+
15253+
return error;
15254+
}
15255+
1517915256
namespace hlsl {
1518015257

1518115258
static bool nodeInputIsCompatible(DXIL::NodeIOKind IOType,

tools/clang/test/CodeGenSPIRV/intrinsics.get-attribute-at-vertex.s.funcParam.hlsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ struct S {
44
float4 a : COLOR;
55
};
66

7-
float compute(float4 a) {
7+
float compute(nointerpolation float4 a) {
88
return GetAttributeAtVertex(a, 2)[0];
99
}
1010

tools/clang/test/CodeGenSPIRV/intrinsics.get-attribute-at-vertex.s.funcParam.neg.hlsl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
// RUN: not %dxc -T ps_6_1 -E main %s -spirv 2>&1 | FileCheck %s
1+
// RUN: %dxc -T ps_6_1 -E main %s -spirv -verify
22

33
struct S {
44
float4 a : COLOR;
55
};
66

7-
float compute(float4 a) {
7+
float compute(nointerpolation float4 a) {
88
return GetAttributeAtVertex(a, 2)[0];
99
}
1010

1111
float4 main(nointerpolation S s, float4 b : COLOR2) : SV_TARGET
1212
{
13-
return float4(0, 0, compute(b), compute(s.a));
13+
return float4(0,
14+
compute(b), // expected-error{{parameter 0 of compute must have a a 'nointerpolation' attribute}}
15+
compute(b), // expected-error{{parameter 0 of compute must have a a 'nointerpolation' attribute}}
16+
compute(s.a));
1417
}
15-
16-
// CHECK: error: Function 'compute' could only use noninterpolated variable as input.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: dxc -T ps_6_2 -E main %s -verify
2+
3+
// expected-no-diagnostics
4+
struct S1 {
5+
nointerpolation float3 f1;
6+
};
7+
8+
struct S2 {
9+
float3 f1;
10+
};
11+
12+
struct S3 {
13+
float3 f1[3];
14+
};
15+
16+
struct S4 {
17+
S1 f1;
18+
};
19+
20+
struct S5 {
21+
S1 f1[2];
22+
};
23+
24+
float4 main(nointerpolation float3 a : A,
25+
S1 s1 : B,
26+
nointerpolation S2 s2 : C,
27+
nointerpolation S3 s3 : D,
28+
S4 s4 : E,
29+
S5 s5 : F
30+
) : SV_Target
31+
{
32+
float v1 = GetAttributeAtVertex(a, 0)[0];
33+
float v2 = GetAttributeAtVertex(a.x, 0);
34+
float v3 = GetAttributeAtVertex(s1.f1, 0)[0];
35+
float v4 = GetAttributeAtVertex(s2.f1, 0)[0];
36+
float v5 = GetAttributeAtVertex(s3.f1[1], 0)[0];
37+
float v6 = GetAttributeAtVertex(s4.f1.f1, 0)[0];
38+
float v7 = GetAttributeAtVertex(s5.f1[1].f1, 0)[0];
39+
40+
return float4(v1, v2, v3, v4) + float4(v5, v6, v7.xx);
41+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: dxc -T ps_6_2 -E main %s -verify
2+
3+
// expected-no-diagnostics
4+
float compute(nointerpolation float3 value) {
5+
return GetAttributeAtVertex(value, 0)[0];
6+
}
7+
8+
float middle(nointerpolation float3 value) {
9+
return compute(value);
10+
}
11+
12+
float4 main(nointerpolation float3 a : A) : SV_Target
13+
{
14+
float v1 = middle(a);
15+
return float4(v1.xxxx);
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: not %dxc -T ps_6_2 -E main %s 2>&1 | FileCheck %s
2+
3+
float compute(nointerpolation float3 value) {
4+
return GetAttributeAtVertex(value, 0)[0];
5+
}
6+
7+
float4 main(float3 a : A) : SV_Target
8+
{
9+
// CHECK: error: Attribute A must have nointerpolation mode in order to use GetAttributeAtVertex function.
10+
float v1 = compute(a);
11+
return float4(v1.xxxx);
12+
}

0 commit comments

Comments
 (0)