[Validation] Add validation and tests for invalid system values in incompatible node launch type (#6108)

#5827 first requires validation at the DXIL level. The validation will
check for functions that have incompatible node launch types, and
intrinsics that represent certain system values.
This PR implements this validation and adds some tests that exercise
this validation.
Fixes #6104
This commit is contained in:
Joshua Batista 2024-01-24 12:45:19 -08:00 коммит произвёл GitHub
Родитель 79a290e719
Коммит 02afa578c1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 191 добавлений и 3 удалений

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

@ -3134,6 +3134,7 @@ INSTR.SAMPLERMODEFORSAMPLEC sample_c_*/gather_c instructions requi
INSTR.SIGNATUREOPERATIONNOTINENTRY Dxil operation for input output signature must be in entryPoints.
INSTR.STATUS Resource status should only be used by CheckAccessFullyMapped.
INSTR.STRUCTBITCAST Bitcast on struct types is not allowed.
INSTR.SVCONFLICTINGLAUNCHMODE Input system values are compatible with node shader launch mode.
INSTR.TEXTUREOFFSET offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7.
INSTR.TGSMRACECOND Race condition writing to shared memory detected, consider making this write conditional.
INSTR.UNDEFINEDVALUEFORUAVSTORE Assignment of undefined values to UAV.

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

@ -2134,6 +2134,19 @@ static void ValidateBarrierFlagArg(ValidationContext &ValCtx, CallInst *CI,
}
}
std::string GetLaunchTypeStr(DXIL::NodeLaunchType LT) {
switch (LT) {
case DXIL::NodeLaunchType::Broadcasting:
return "Broadcasting";
case DXIL::NodeLaunchType::Coalescing:
return "Coalescing";
case DXIL::NodeLaunchType::Thread:
return "Thread";
default:
return "Invalid";
}
}
static void ValidateDxilOperationCallInProfile(CallInst *CI,
DXIL::OpCode opcode,
const ShaderModel *pSM,
@ -2141,10 +2154,15 @@ static void ValidateDxilOperationCallInProfile(CallInst *CI,
DXIL::ShaderKind shaderKind =
pSM ? pSM->GetKind() : DXIL::ShaderKind::Invalid;
llvm::Function *F = CI->getParent()->getParent();
DXIL::NodeLaunchType nodeLaunchType = DXIL::NodeLaunchType::Invalid;
if (DXIL::ShaderKind::Library == shaderKind) {
if (ValCtx.DxilMod.HasDxilFunctionProps(F))
if (ValCtx.DxilMod.HasDxilFunctionProps(F)) {
DxilEntryProps &entryProps = ValCtx.DxilMod.GetDxilEntryProps(F);
shaderKind = ValCtx.DxilMod.GetDxilFunctionProps(F).shaderKind;
else if (ValCtx.DxilMod.IsPatchConstantShader(F))
if (shaderKind == DXIL::ShaderKind::Node)
nodeLaunchType = entryProps.props.Node.LaunchType;
} else if (ValCtx.DxilMod.IsPatchConstantShader(F))
shaderKind = DXIL::ShaderKind::Hull;
}
@ -2346,6 +2364,65 @@ static void ValidateDxilOperationCallInProfile(CallInst *CI,
{"CreateHandle", "Shader model 6.5 and below"});
}
break;
case DXIL::OpCode::ThreadId: // SV_DispatchThreadID
if (shaderKind != DXIL::ShaderKind::Node) {
break;
}
if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting)
break;
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrSVConflictingLaunchMode,
{"ThreadId", "SV_DispatchThreadID", GetLaunchTypeStr(nodeLaunchType)});
break;
case DXIL::OpCode::GroupId: // SV_GroupId
if (shaderKind != DXIL::ShaderKind::Node) {
break;
}
if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting)
break;
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrSVConflictingLaunchMode,
{"GroupId", "SV_GroupId", GetLaunchTypeStr(nodeLaunchType)});
break;
case DXIL::OpCode::ThreadIdInGroup: // SV_GroupThreadID
if (shaderKind != DXIL::ShaderKind::Node) {
break;
}
if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting ||
nodeLaunchType == DXIL::NodeLaunchType::Coalescing)
break;
ValCtx.EmitInstrFormatError(CI,
ValidationRule::InstrSVConflictingLaunchMode,
{"ThreadIdInGroup", "SV_GroupThreadID",
GetLaunchTypeStr(nodeLaunchType)});
break;
case DXIL::OpCode::FlattenedThreadIdInGroup: // SV_GroupIndex
if (shaderKind != DXIL::ShaderKind::Node) {
break;
}
if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting ||
nodeLaunchType == DXIL::NodeLaunchType::Coalescing)
break;
ValCtx.EmitInstrFormatError(CI,
ValidationRule::InstrSVConflictingLaunchMode,
{"FlattenedThreadIdInGroup", "SV_GroupIndex",
GetLaunchTypeStr(nodeLaunchType)});
break;
default:
// TODO: make sure every opcode is checked.
// Skip opcodes don't need special check.
@ -3520,7 +3597,6 @@ static void ValidateFunction(Function &F, ValidationContext &ValCtx) {
{std::to_string(arg.getArgNo()), F.getName()});
};
// Validate parameter type.
unsigned numArgs = 0;
for (auto &arg : F.args()) {
Type *argTy = arg.getType();

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

@ -0,0 +1,51 @@
; RUN: %dxv %s | FileCheck %s
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"
; CHECK: Function: entry3: error: Call to DXIL intrinsic ThreadId (SV_DispatchThreadID) is not allowed in node shader launch type Coalescing
; CHECK: note: at '%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)' in block '#0' of function 'entry3'.
; CHECK: Function: entry3: error: Call to DXIL intrinsic GroupId (SV_GroupId) is not allowed in node shader launch type Coalescing
; CHECK: note: at '%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)' in block '#0' of function 'entry3'.
define void @entry3() {
%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)
%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)
%3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1)
%4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)
ret void
}
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.typeAnnotations = !{!4}
!dx.entryPoints = !{!8, !9}
!0 = !{!"dxc(private) 1.7.0.14317 (main, e3c311409675)"}
!1 = !{i32 1, i32 3}
!2 = !{i32 1, i32 8}
!3 = !{!"lib", i32 6, i32 3}
!4 = !{i32 1, void ()* @entry3, !5}
!5 = !{!6}
!6 = !{i32 0, !7, !7}
!7 = !{}
!8 = !{null, !"", null, null, null}
!9 = !{void ()* @entry3, !"entry3", null, null, !10}
!10 = !{i32 8, i32 15, i32 13, i32 2, i32 15, !11, i32 16, i32 -1, i32 4, !12, i32 5, !13}
!11 = !{!"entry3", i32 0}
!12 = !{i32 1, i32 1, i32 1}
!13 = !{i32 0}
; Function Attrs: nounwind readnone
declare i32 @dx.op.threadId.i32(i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @dx.op.groupId.i32(i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @dx.op.threadIdInGroup.i32(i32, i32) #0
; Function Attrs: nounwind readnone
declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0

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

@ -0,0 +1,55 @@
; RUN: %dxv %s | FileCheck %s
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"
; CHECK: Function: entry: error: Call to DXIL intrinsic ThreadId (SV_DispatchThreadID) is not allowed in node shader launch type Thread
; CHECK: note: at '%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)' in block '#0' of function 'entry'.
; CHECK: Function: entry: error: Call to DXIL intrinsic GroupId (SV_GroupId) is not allowed in node shader launch type Thread
; CHECK: note: at '%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)' in block '#0' of function 'entry'.
; CHECK: Function: entry: error: Call to DXIL intrinsic ThreadIdInGroup (SV_GroupThreadID) is not allowed in node shader launch type Thread
; CHECK: note: at '%3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1)' in block '#0' of function 'entry'.
; CHECK: Function: entry: error: Call to DXIL intrinsic FlattenedThreadIdInGroup (SV_GroupIndex) is not allowed in node shader launch type Thread
; CHECK: note: at '%4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)' in block '#0' of function 'entry'.
define void @entry() {
%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)
%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)
%3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1)
%4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)
ret void
}
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.typeAnnotations = !{!4}
!dx.entryPoints = !{!8, !9}
!0 = !{!"dxc(private) 1.7.0.14317 (main, e3c311409675)"}
!1 = !{i32 1, i32 3}
!2 = !{i32 1, i32 8}
!3 = !{!"lib", i32 6, i32 3}
!4 = !{i32 1, void ()* @entry, !5}
!5 = !{!6}
!6 = !{i32 0, !7, !7}
!7 = !{}
!8 = !{null, !"", null, null, null}
!9 = !{void ()* @entry, !"entry", null, null, !10}
!10 = !{i32 8, i32 15, i32 13, i32 3, i32 15, !11, i32 16, i32 -1, i32 4, !12, i32 5, !13}
!11 = !{!"entry", i32 0}
!12 = !{i32 1, i32 1, i32 1}
!13 = !{i32 0}
; Function Attrs: nounwind readnone
declare i32 @dx.op.threadId.i32(i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @dx.op.groupId.i32(i32, i32) #1
; Function Attrs: nounwind readnone
declare i32 @dx.op.threadIdInGroup.i32(i32, i32) #0
; Function Attrs: nounwind readnone
declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0

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

@ -7559,6 +7559,11 @@ class db_dxil(object):
self.add_valrule(
"Instr.AtomicIntrinNonUAV", "Non-UAV destination to atomic intrinsic."
)
self.add_valrule_msg(
"Instr.SVConflictingLaunchMode",
"Input system values are compatible with node shader launch mode.",
"Call to DXIL intrinsic %0 (%1) is not allowed in node shader launch type %2"
)
self.add_valrule("Instr.AtomicConst", "Constant destination to atomic.")
# Work-Graphs