[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 <brioche@google.com>
This commit is contained in:
Nathan Gauër 2024-04-03 18:17:33 +02:00 коммит произвёл GitHub
Родитель 06e706cf6e
Коммит 27b87b7593
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 219 добавлений и 9 удалений

Просмотреть файл

@ -7735,6 +7735,8 @@ def warn_hlsl_implicit_vector_truncation : Warning<
InGroup<Conversion>, DefaultWarn;
def err_hlsl_nointerpolation_and_linear : Error<
"nointerpolation cannot be used with any other interpolation mode specifier">;
def err_hlsl_parameter_requires_attribute : Error<
"parameter %0 of %1 must have a '%2' attribute">;
def warn_hlsl_duplicate_specifier : Warning<
"duplicate HLSL specifier %0">,
InGroup<IgnoredAttributes>, DefaultWarn;

Просмотреть файл

@ -8828,6 +8828,8 @@ private:
bool AllowOnePastEnd=true, bool IndexNegated=false);
// HLSL Change Starts - checking array subscript access to vector or matrix member
void CheckHLSLArrayAccess(const Expr *expr);
bool CheckHLSLIntrinsicCall(FunctionDecl *FDecl, CallExpr *TheCall);
bool CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall);
// HLSL Change ends
void CheckArrayAccess(const Expr *E);
// Used to grab the relevant information from a FormatAttr and a

Просмотреть файл

@ -522,7 +522,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
TheCall->setType(Context.VoidPtrTy);
break;
}
// Since the target specific builtins for each arch overlap, only check those

Просмотреть файл

@ -5288,7 +5288,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
if (FDecl) {
if (CheckFunctionCall(FDecl, TheCall, Proto))
return ExprError();
if (CheckHLSLFunctionCall(FDecl, TheCall))
return ExprError();
if (BuiltinID)
return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall);
} else if (NDecl) {

Просмотреть файл

@ -15184,6 +15184,88 @@ QualType Sema::getHLSLDefaultSpecialization(TemplateDecl *Decl) {
return QualType();
}
static bool isRelatedDeclMarkedNointerpolation(Expr *E) {
if (!E)
return false;
E = E->IgnoreCasts();
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl()->hasAttr<HLSLNoInterpolationAttr>();
if (auto *ME = dyn_cast<MemberExpr>(E))
return ME->getMemberDecl()->hasAttr<HLSLNoInterpolationAttr>() ||
isRelatedDeclMarkedNointerpolation(ME->getBase());
if (auto *HVE = dyn_cast<HLSLVectorElementExpr>(E))
return isRelatedDeclMarkedNointerpolation(HVE->getBase());
if (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
return isRelatedDeclMarkedNointerpolation(ASE->getBase());
return false;
}
static bool CheckIntrinsicGetAttributeAtVertex(Sema *S, FunctionDecl *FDecl,
CallExpr *TheCall) {
assert(TheCall->getNumArgs() > 0);
auto argument = TheCall->getArg(0)->IgnoreCasts();
if (!isRelatedDeclMarkedNointerpolation(argument)) {
S->Diag(argument->getExprLoc(), diag::err_hlsl_parameter_requires_attribute)
<< 0 << FDecl->getName() << "nointerpolation";
return true;
}
return false;
}
bool Sema::CheckHLSLIntrinsicCall(FunctionDecl *FDecl, CallExpr *TheCall) {
auto attr = FDecl->getAttr<HLSLIntrinsicAttr>();
switch (hlsl::IntrinsicOp(attr->getOpcode())) {
case hlsl::IntrinsicOp::IOP_GetAttributeAtVertex:
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
// to limit the scope, and fail gracefully in some cases.
if (!getLangOpts().SPIRV)
return false;
// This should never happen for SPIR-V. But on the DXIL side, extension can
// be added by inserting new intrinsics, meaning opcodes can collide with
// existing ones. See the ExtensionTest.EvalAttributeCollision test.
assert(FDecl->getName() == "GetAttributeAtVertex");
return CheckIntrinsicGetAttributeAtVertex(this, FDecl, TheCall);
default:
break;
}
return false;
}
bool Sema::CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) {
if (hlsl::IsIntrinsicOp(FDecl) && CheckHLSLIntrinsicCall(FDecl, TheCall))
return true;
// See #hlsl-specs/issues/181. Feature is broken. For SPIR-V we want
// to limit the scope, and fail gracefully in some cases.
if (!getLangOpts().SPIRV)
return false;
bool error = false;
for (unsigned i = 0; i < FDecl->getNumParams(); i++) {
assert(i < TheCall->getNumArgs());
if (!FDecl->getParamDecl(i)->hasAttr<HLSLNoInterpolationAttr>())
continue;
if (!isRelatedDeclMarkedNointerpolation(TheCall->getArg(i))) {
Diag(TheCall->getArg(i)->getExprLoc(),
diag::err_hlsl_parameter_requires_attribute)
<< i << FDecl->getName() << "nointerpolation";
error = true;
}
}
return error;
}
namespace hlsl {
static bool nodeInputIsCompatible(DXIL::NodeIOKind IOType,

Просмотреть файл

@ -1,4 +1,4 @@
// RUN: dxc -T ps_6_2 -E main -spirv %s | FileCheck %s
// RUN: %dxc -T ps_6_2 -E main -spirv %s | FileCheck %s
// CHECK: OpDecorate %in_var_A PerVertexKHR
// CHECK-DAG: %type_constants = OpTypeStruct %uint

Просмотреть файл

@ -4,7 +4,7 @@ struct S {
float4 a : COLOR;
};
float compute(float4 a) {
float compute(nointerpolation float4 a) {
return GetAttributeAtVertex(a, 2)[0];
}

Просмотреть файл

@ -1,16 +1,17 @@
// RUN: not %dxc -T ps_6_1 -E main %s -spirv 2>&1 | FileCheck %s
// RUN: %dxc -T ps_6_1 -E main %s -spirv -verify
struct S {
float4 a : COLOR;
};
float compute(float4 a) {
float compute(nointerpolation float4 a) {
return GetAttributeAtVertex(a, 2)[0];
}
float4 main(nointerpolation S s, float4 b : COLOR2) : SV_TARGET
{
return float4(0, 0, compute(b), compute(s.a));
return float4(0,
compute(b), // expected-error{{parameter 0 of compute must have a 'nointerpolation' attribute}}
compute(b), // expected-error{{parameter 0 of compute must have a 'nointerpolation' attribute}}
compute(s.a));
}
// CHECK: error: Function 'compute' could only use noninterpolated variable as input.

Просмотреть файл

@ -0,0 +1,46 @@
// RUN: %dxc -T ps_6_2 -E main %s -verify
// expected-no-diagnostics
struct S1 {
nointerpolation float3 f1;
};
struct S2 {
float3 f1;
};
struct S3 {
float3 f1[3];
};
struct S4 {
S1 f1;
};
struct S5 {
S1 f1[2];
};
float compute(float3 value) {
return GetAttributeAtVertex(value, 0)[0];
}
float4 main(nointerpolation float3 a : A,
S1 s1 : B,
nointerpolation S2 s2 : C,
nointerpolation S3 s3 : D,
S4 s4 : E,
S5 s5 : F
) : SV_Target
{
float v1 = GetAttributeAtVertex(a, 0)[0];
float v2 = GetAttributeAtVertex(a.x, 0);
float v3 = GetAttributeAtVertex(s1.f1, 0)[0];
float v4 = GetAttributeAtVertex(s2.f1, 0)[0];
float v5 = GetAttributeAtVertex(s3.f1[1], 0)[0];
float v6 = GetAttributeAtVertex(s4.f1.f1, 0)[0];
float v7 = GetAttributeAtVertex(s5.f1[1].f1, 0)[0];
float v8 = compute(s1.f1);
return float4(v1, v2, v3, v4) + float4(v5, v6, v7, v8);
}

Просмотреть файл

@ -0,0 +1,16 @@
// RUN: %dxc -T ps_6_2 -E main %s -verify
// expected-no-diagnostics
float compute(nointerpolation float3 value) {
return GetAttributeAtVertex(value, 0)[0];
}
float middle(nointerpolation float3 value) {
return compute(value);
}
float4 main(nointerpolation float3 a : A) : SV_Target
{
float v1 = middle(a);
return float4(v1.xxxx);
}

Просмотреть файл

@ -0,0 +1,12 @@
// RUN: not %dxc -T ps_6_2 -E main %s 2>&1 | FileCheck %s
float compute(nointerpolation float3 value) {
return GetAttributeAtVertex(value, 0)[0];
}
float4 main(float3 a : A) : SV_Target
{
// CHECK: error: Attribute A must have nointerpolation mode in order to use GetAttributeAtVertex function.
float v1 = compute(a);
return float4(v1.xxxx);
}

Просмотреть файл

@ -0,0 +1,12 @@
// REQUIRES: spirv
// RUN: %dxc -T ps_6_2 -E main %s -spirv -verify
float compute(nointerpolation float3 value) {
return GetAttributeAtVertex(value, 0)[0];
}
float4 main(float3 a : A) : SV_Target
{
float v1 = compute(a); /* expected-error{{parameter 0 of compute must have a 'nointerpolation' attribute}} */
return float4(v1.xxxx);
}

Просмотреть файл

@ -0,0 +1,19 @@
// RUN: not %dxc -T ps_6_2 -E main %s 2>&1 | FileCheck %s
struct S1 {
float3 f1;
};
float compute(float3 value) {
return GetAttributeAtVertex(value, 0)[0];
}
float4 main(float3 a : A, S1 s1 : B) : SV_Target
{
// CHECK-DAG: error: Attribute A must have nointerpolation mode in order to use GetAttributeAtVertex function.
// CHECK-DAG: error: Attribute B must have nointerpolation mode in order to use GetAttributeAtVertex function.
float v1 = GetAttributeAtVertex(a, 0)[0];
float v2 = GetAttributeAtVertex(s1.f1, 0)[0];
float v3 = compute(a);
return float4(v1.xx, v2, v3);
}

Просмотреть файл

@ -0,0 +1,18 @@
// REQUIRES: spirv
// RUN: %dxc -T ps_6_2 -E main %s -spirv -verify
struct S1 {
float3 f1;
};
float compute(float3 value) {
return GetAttributeAtVertex(value, 0)[0]; /* expected-error{{parameter 0 of GetAttributeAtVertex must have a 'nointerpolation' attribute}} */
}
float4 main(float3 a : A, S1 s1 : B) : SV_Target
{
float v1 = GetAttributeAtVertex(a, 0)[0]; /* expected-error{{parameter 0 of GetAttributeAtVertex must have a 'nointerpolation' attribute}} */
float v2 = GetAttributeAtVertex(s1.f1, 0)[0]; /* expected-error{{parameter 0 of GetAttributeAtVertex must have a 'nointerpolation' attribute}} */
float v3 = compute(a);
return float4(v1.xx, v2, v3);
}