Skip to content

Commit 27b87b7

Browse files
authored
[SPIR-V][Sema] Requires nointerpolation on GetAttributeAtVertex param #0 (#6453)
This commit adds new sema checks for SPIR-V 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. The current accepted cases this now blocks were mostly broken later in the codegen, so this should be an acceptable 'regression'. Related to #6220 Fixes #6384, #6383, #6382 --------- Signed-off-by: Nathan Gauër <[email protected]>
1 parent 06e706c commit 27b87b7

14 files changed

+219
-9
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7735,6 +7735,8 @@ def warn_hlsl_implicit_vector_truncation : Warning<
77357735
InGroup<Conversion>, DefaultWarn;
77367736
def err_hlsl_nointerpolation_and_linear : Error<
77377737
"nointerpolation cannot be used with any other interpolation mode specifier">;
7738+
def err_hlsl_parameter_requires_attribute : Error<
7739+
"parameter %0 of %1 must have a '%2' attribute">;
77387740
def warn_hlsl_duplicate_specifier : Warning<
77397741
"duplicate HLSL specifier %0">,
77407742
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5288,7 +5288,8 @@ 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();
52925293
if (BuiltinID)
52935294
return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
52945295
} else if (NDecl) {

tools/clang/lib/Sema/SemaHLSL.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15184,6 +15184,88 @@ QualType Sema::getHLSLDefaultSpecialization(TemplateDecl *Decl) {
1518415184
return QualType();
1518515185
}
1518615186

15187+
static bool isRelatedDeclMarkedNointerpolation(Expr *E) {
15188+
if (!E)
15189+
return false;
15190+
E = E->IgnoreCasts();
15191+
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
15192+
return DRE->getDecl()->hasAttr<HLSLNoInterpolationAttr>();
15193+
15194+
if (auto *ME = dyn_cast<MemberExpr>(E))
15195+
return ME->getMemberDecl()->hasAttr<HLSLNoInterpolationAttr>() ||
15196+
isRelatedDeclMarkedNointerpolation(ME->getBase());
15197+
15198+
if (auto *HVE = dyn_cast<HLSLVectorElementExpr>(E))
15199+
return isRelatedDeclMarkedNointerpolation(HVE->getBase());
15200+
15201+
if (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
15202+
return isRelatedDeclMarkedNointerpolation(ASE->getBase());
15203+
15204+
return false;
15205+
}
15206+
15207+
static bool CheckIntrinsicGetAttributeAtVertex(Sema *S, FunctionDecl *FDecl,
15208+
CallExpr *TheCall) {
15209+
assert(TheCall->getNumArgs() > 0);
15210+
auto argument = TheCall->getArg(0)->IgnoreCasts();
15211+
15212+
if (!isRelatedDeclMarkedNointerpolation(argument)) {
15213+
S->Diag(argument->getExprLoc(), diag::err_hlsl_parameter_requires_attribute)
15214+
<< 0 << FDecl->getName() << "nointerpolation";
15215+
return true;
15216+
}
15217+
15218+
return false;
15219+
}
15220+
15221+
bool Sema::CheckHLSLIntrinsicCall(FunctionDecl *FDecl, CallExpr *TheCall) {
15222+
auto attr = FDecl->getAttr<HLSLIntrinsicAttr>();
15223+
15224+
switch (hlsl::IntrinsicOp(attr->getOpcode())) {
15225+
case hlsl::IntrinsicOp::IOP_GetAttributeAtVertex:
15226+
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
15227+
// to limit the scope, and fail gracefully in some cases.
15228+
if (!getLangOpts().SPIRV)
15229+
return false;
15230+
// This should never happen for SPIR-V. But on the DXIL side, extension can
15231+
// be added by inserting new intrinsics, meaning opcodes can collide with
15232+
// existing ones. See the ExtensionTest.EvalAttributeCollision test.
15233+
assert(FDecl->getName() == "GetAttributeAtVertex");
15234+
return CheckIntrinsicGetAttributeAtVertex(this, FDecl, TheCall);
15235+
default:
15236+
break;
15237+
}
15238+
15239+
return false;
15240+
}
15241+
15242+
bool Sema::CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
15243+
if (hlsl::IsIntrinsicOp(FDecl) && CheckHLSLIntrinsicCall(FDecl, TheCall))
15244+
return true;
15245+
15246+
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
15247+
// to limit the scope, and fail gracefully in some cases.
15248+
if (!getLangOpts().SPIRV)
15249+
return false;
15250+
15251+
bool error = false;
15252+
for (unsigned i = 0; i < FDecl->getNumParams(); i++) {
15253+
assert(i < TheCall->getNumArgs());
15254+
15255+
if (!FDecl->getParamDecl(i)->hasAttr<HLSLNoInterpolationAttr>())
15256+
continue;
15257+
15258+
if (!isRelatedDeclMarkedNointerpolation(TheCall->getArg(i))) {
15259+
Diag(TheCall->getArg(i)->getExprLoc(),
15260+
diag::err_hlsl_parameter_requires_attribute)
15261+
<< i << FDecl->getName() << "nointerpolation";
15262+
error = true;
15263+
}
15264+
}
15265+
15266+
return error;
15267+
}
15268+
1518715269
namespace hlsl {
1518815270

1518915271
static bool nodeInputIsCompatible(DXIL::NodeIOKind IOType,

tools/clang/test/CodeGenSPIRV/intrinsics.get-attribute-at-vertex.cbuf.var.hlsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: dxc -T ps_6_2 -E main -spirv %s | FileCheck %s
1+
// RUN: %dxc -T ps_6_2 -E main -spirv %s | FileCheck %s
22

33
// CHECK: OpDecorate %in_var_A PerVertexKHR
44
// CHECK-DAG: %type_constants = OpTypeStruct %uint

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 'nointerpolation' attribute}}
15+
compute(b), // expected-error{{parameter 0 of compute must have a 'nointerpolation' attribute}}
16+
compute(s.a));
1417
}
15-
16-
// CHECK: error: Function 'compute' could only use noninterpolated variable as input.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
float compute(float3 value) {
25+
return GetAttributeAtVertex(value, 0)[0];
26+
}
27+
28+
float4 main(nointerpolation float3 a : A,
29+
S1 s1 : B,
30+
nointerpolation S2 s2 : C,
31+
nointerpolation S3 s3 : D,
32+
S4 s4 : E,
33+
S5 s5 : F
34+
) : SV_Target
35+
{
36+
float v1 = GetAttributeAtVertex(a, 0)[0];
37+
float v2 = GetAttributeAtVertex(a.x, 0);
38+
float v3 = GetAttributeAtVertex(s1.f1, 0)[0];
39+
float v4 = GetAttributeAtVertex(s2.f1, 0)[0];
40+
float v5 = GetAttributeAtVertex(s3.f1[1], 0)[0];
41+
float v6 = GetAttributeAtVertex(s4.f1.f1, 0)[0];
42+
float v7 = GetAttributeAtVertex(s5.f1[1].f1, 0)[0];
43+
float v8 = compute(s1.f1);
44+
45+
return float4(v1, v2, v3, v4) + float4(v5, v6, v7, v8);
46+
}
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+
}

0 commit comments

Comments
 (0)