DxilValidation: add ShaderCompatInfo call graph checking (#6332)

ShaderCompatInfo identifies compatibility for functions that could be
called from an entry point.
Currently, this checking detects compatibility problems between entry
points and internal function calls that the validator otherwise misses.

This change adds a check for ShaderCompatInfo, recursing into called
functions looking for a source of conflict when not compatible with the
current entry point properties.
Errors are emitted for the entry point and at one source of each type of
conflict that ShaderCompatInfo detects.
A function is considered the source of a conflict when it has the
conflict but none of the functions called from this function have this
conflict.

Removed early exit for ShaderFlags validation when module is a library,
since these flags should be validated for libraries, and running
CollectShaderFlagsForModule fills in the ShaderCompatInfo data we need
for validation.

Also fixed tests for illegal barrier and derivative use, as well as
replacing the ignored MaxOutputRecords with the correct MaxRecords
attribute.

Fixes #6292.
This commit is contained in:
Tex Riddell 2024-02-26 15:02:28 -08:00 коммит произвёл GitHub
Родитель 1a48b86e08
Коммит 696a13a2a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
23 изменённых файлов: 1296 добавлений и 20 удалений

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

@ -3216,6 +3216,14 @@ SM.GSVALIDINPUTPRIMITIVE GS input primitive unrecognized.
SM.GSVALIDOUTPUTPRIMITIVETOPOLOGY GS output primitive topology unrecognized.
SM.HSINPUTCONTROLPOINTCOUNTRANGE HS input control point count must be [0..%0]. %1 specified.
SM.HULLPASSTHRUCONTROLPOINTCOUNTMATCH For pass thru hull shader, input control point count must match output control point count
SM.INCOMPATIBLECALLINENTRY Features used in internal function calls must be compatible with entry
SM.INCOMPATIBLEDERIVINCOMPUTESHADERMODEL Derivatives in compute-model shaders require shader model 6.6 and above
SM.INCOMPATIBLEDERIVLAUNCH Node shaders only support derivatives in broadcasting launch mode
SM.INCOMPATIBLEOPERATION Operations used in entry function must be compatible with shader stage and other properties
SM.INCOMPATIBLEREQUIRESGROUP Functions requiring groupshared memory must be called from shaders with a visible group
SM.INCOMPATIBLESHADERMODEL Functions may only use features available in the current shader model
SM.INCOMPATIBLESTAGE Functions may only use features available in the entry function's stage
SM.INCOMPATIBLETHREADGROUPDIM When derivatives are used in compute-model shaders, the thread group dimensions must be compatible
SM.INSIDETESSFACTORSIZEMATCHDOMAIN InsideTessFactor rows, columns (%0, %1) invalid for domain %2. Expected %3 rows and 1 column.
SM.INVALIDRESOURCECOMPTYPE Invalid resource return type.
SM.INVALIDRESOURCEKIND Invalid resources kind.

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

@ -174,6 +174,7 @@ struct ValidationContext {
const unsigned kLLVMLoopMDKind;
unsigned m_DxilMajor, m_DxilMinor;
ModuleSlotTracker slotTracker;
std::unique_ptr<CallGraph> pCallGraph;
ValidationContext(Module &llvmModule, Module *DebugModule,
DxilModule &dxilModule)
@ -397,6 +398,12 @@ struct ValidationContext {
EntryStatus &GetEntryStatus(Function *F) { return *entryStatusMap[F]; }
CallGraph &GetCallGraph() {
if (!pCallGraph)
pCallGraph = llvm::make_unique<CallGraph>(M);
return *pCallGraph.get();
}
DxilResourceProperties GetResourceFromVal(Value *resVal);
void EmitGlobalVariableFormatError(GlobalVariable *GV, ValidationRule rule,
@ -5386,6 +5393,216 @@ static void ValidateEntrySignatures(ValidationContext &ValCtx) {
}
}
// CompatibilityChecker is used to identify incompatibilities in an entry
// function and any functions called by that entry function.
struct CompatibilityChecker {
ValidationContext &ValCtx;
Function *EntryFn;
const DxilFunctionProps &props;
DXIL::ShaderKind shaderKind;
// These masks identify the potential conflict flags based on the entry
// function's shader kind and properties when either UsesDerivatives or
// RequiresGroup flags are set in ShaderCompatInfo.
uint32_t maskForDeriv = 0;
uint32_t maskForGroup = 0;
enum class ConflictKind : uint32_t {
Stage,
ShaderModel,
DerivLaunch,
DerivThreadGroupDim,
DerivInComputeShaderModel,
RequiresGroup,
};
enum class ConflictFlags : uint32_t {
Stage = 1 << (uint32_t)ConflictKind::Stage,
ShaderModel = 1 << (uint32_t)ConflictKind::ShaderModel,
DerivLaunch = 1 << (uint32_t)ConflictKind::DerivLaunch,
DerivThreadGroupDim = 1 << (uint32_t)ConflictKind::DerivThreadGroupDim,
DerivInComputeShaderModel =
1 << (uint32_t)ConflictKind::DerivInComputeShaderModel,
RequiresGroup = 1 << (uint32_t)ConflictKind::RequiresGroup,
};
CompatibilityChecker(ValidationContext &ValCtx, Function *EntryFn)
: ValCtx(ValCtx), EntryFn(EntryFn),
props(ValCtx.DxilMod.GetDxilEntryProps(EntryFn).props),
shaderKind(props.shaderKind) {
// Precompute potential incompatibilities based on shader stage, shader kind
// and entry attributes. These will turn into full conflicts if the entry
// point's shader flags indicate that they use relevant features.
if (!ValCtx.DxilMod.GetShaderModel()->IsSM66Plus() &&
(shaderKind == DXIL::ShaderKind::Mesh ||
shaderKind == DXIL::ShaderKind::Amplification ||
shaderKind == DXIL::ShaderKind::Compute)) {
maskForDeriv |=
static_cast<uint32_t>(ConflictFlags::DerivInComputeShaderModel);
} else if (shaderKind == DXIL::ShaderKind::Node) {
// Only broadcasting launch supports derivatives.
if (props.Node.LaunchType != DXIL::NodeLaunchType::Broadcasting)
maskForDeriv |= static_cast<uint32_t>(ConflictFlags::DerivLaunch);
// Thread launch node has no group.
if (props.Node.LaunchType == DXIL::NodeLaunchType::Thread)
maskForGroup |= static_cast<uint32_t>(ConflictFlags::RequiresGroup);
}
if (shaderKind == DXIL::ShaderKind::Mesh ||
shaderKind == DXIL::ShaderKind::Amplification ||
shaderKind == DXIL::ShaderKind::Compute ||
shaderKind == DXIL::ShaderKind::Node) {
// All compute-like stages
// Thread dimensions must be either 1D and X is multiple of 4, or 2D
// and X and Y must be multiples of 2.
if (props.numThreads[1] == 1 && props.numThreads[2] == 1) {
if ((props.numThreads[0] & 0x3) != 0)
maskForDeriv |=
static_cast<uint32_t>(ConflictFlags::DerivThreadGroupDim);
} else if ((props.numThreads[0] & 0x1) || (props.numThreads[1] & 0x1))
maskForDeriv |=
static_cast<uint32_t>(ConflictFlags::DerivThreadGroupDim);
} else {
// other stages have no group
maskForGroup |= static_cast<uint32_t>(ConflictFlags::RequiresGroup);
}
}
uint32_t
IdentifyConflict(const DxilModule::ShaderCompatInfo &compatInfo) const {
uint32_t conflictMask = 0;
// Compatibility check said this shader kind is not compatible.
if (0 == ((1 << (uint32_t)shaderKind) & compatInfo.mask))
conflictMask |= (uint32_t)ConflictFlags::Stage;
// Compatibility check said this shader model is not compatible.
if (DXIL::CompareVersions(ValCtx.DxilMod.GetShaderModel()->GetMajor(),
ValCtx.DxilMod.GetShaderModel()->GetMinor(),
compatInfo.minMajor, compatInfo.minMinor) < 0)
conflictMask |= (uint32_t)ConflictFlags::ShaderModel;
if (compatInfo.shaderFlags.GetUsesDerivatives())
conflictMask |= maskForDeriv;
if (compatInfo.shaderFlags.GetRequiresGroup())
conflictMask |= maskForGroup;
return conflictMask;
}
void Diagnose(Function *F, uint32_t conflictMask, ConflictKind conflict,
ValidationRule rule, ArrayRef<StringRef> args = {}) {
if (conflictMask & (1 << (unsigned)conflict))
ValCtx.EmitFnFormatError(F, rule, args);
}
void DiagnoseConflicts(Function *F, uint32_t conflictMask) {
// Emit a diagnostic indicating that either the entry function or a function
// called by the entry function contains a disallowed operation.
if (F == EntryFn)
ValCtx.EmitFnError(EntryFn, ValidationRule::SmIncompatibleOperation);
else
ValCtx.EmitFnError(EntryFn, ValidationRule::SmIncompatibleCallInEntry);
// Emit diagnostics for each conflict found in this function.
Diagnose(F, conflictMask, ConflictKind::Stage,
ValidationRule::SmIncompatibleStage,
{ShaderModel::GetKindName(props.shaderKind)});
Diagnose(F, conflictMask, ConflictKind::ShaderModel,
ValidationRule::SmIncompatibleShaderModel);
Diagnose(F, conflictMask, ConflictKind::DerivLaunch,
ValidationRule::SmIncompatibleDerivLaunch,
{GetLaunchTypeStr(props.Node.LaunchType)});
Diagnose(F, conflictMask, ConflictKind::DerivThreadGroupDim,
ValidationRule::SmIncompatibleThreadGroupDim,
{std::to_string(props.numThreads[0]),
std::to_string(props.numThreads[1]),
std::to_string(props.numThreads[2])});
Diagnose(F, conflictMask, ConflictKind::DerivInComputeShaderModel,
ValidationRule::SmIncompatibleDerivInComputeShaderModel);
Diagnose(F, conflictMask, ConflictKind::RequiresGroup,
ValidationRule::SmIncompatibleRequiresGroup);
}
// Visit function and all functions called by it.
// Emit diagnostics for incompatibilities found in a function when no
// functions called by that function introduced the conflict.
// In those cases, the called functions themselves will emit the diagnostic.
// Return conflict mask for this function.
uint32_t Visit(Function *F, uint32_t &remainingMask,
llvm::SmallPtrSet<Function *, 8> &visited, CallGraph &CG) {
// Recursive check looks for where a conflict is found and not present
// in functions called by the current function.
// - When a source is found, emit diagnostics and clear the conflict
// flags introduced by this function from the working mask so we don't
// report this conflict again.
// - When the remainingMask is 0, we are done.
if (remainingMask == 0)
return 0; // Nothing left to search for.
if (!visited.insert(F).second)
return 0; // Already visited.
const DxilModule::ShaderCompatInfo *compatInfo =
ValCtx.DxilMod.GetCompatInfoForFunction(F);
DXASSERT(compatInfo, "otherwise, compat info not computed in module");
if (!compatInfo)
return 0;
uint32_t maskForThisFunction = IdentifyConflict(*compatInfo);
uint32_t maskForCalls = 0;
if (CallGraphNode *CGNode = CG[F]) {
for (auto &Call : *CGNode) {
Function *called = Call.second->getFunction();
if (called->isDeclaration())
continue;
maskForCalls |= Visit(called, remainingMask, visited, CG);
if (remainingMask == 0)
return 0; // Nothing left to search for.
}
}
// Mask of incompatibilities introduced by this function.
uint32_t conflictsIntroduced =
remainingMask & maskForThisFunction & ~maskForCalls;
if (conflictsIntroduced) {
// This function introduces at least one conflict.
DiagnoseConflicts(F, conflictsIntroduced);
// Mask off diagnosed incompatibilities.
remainingMask &= ~conflictsIntroduced;
}
return maskForThisFunction;
}
void FindIncompatibleCall(const DxilModule::ShaderCompatInfo &compatInfo) {
uint32_t conflictMask = IdentifyConflict(compatInfo);
if (conflictMask == 0)
return;
CallGraph &CG = ValCtx.GetCallGraph();
llvm::SmallPtrSet<Function *, 8> visited;
Visit(EntryFn, conflictMask, visited, CG);
}
};
static void ValidateEntryCompatibility(ValidationContext &ValCtx) {
// Make sure functions called from each entry are compatible with that entry.
DxilModule &DM = ValCtx.DxilMod;
for (Function &F : DM.GetModule()->functions()) {
if (DM.HasDxilEntryProps(&F)) {
const DxilModule::ShaderCompatInfo *compatInfo =
DM.GetCompatInfoForFunction(&F);
DXASSERT(compatInfo, "otherwise, compat info not computed in module");
if (!compatInfo)
continue;
CompatibilityChecker checker(ValCtx, &F);
checker.FindIncompatibleCall(*compatInfo);
}
}
}
static void CheckPatchConstantSemantic(ValidationContext &ValCtx,
const DxilEntryProps &EntryProps,
EntryStatus &Status, Function *F) {
@ -5900,7 +6117,7 @@ CalculateCallDepth(CallGraphNode *node,
static void ValidateCallGraph(ValidationContext &ValCtx) {
// Build CallGraph.
CallGraph CG(*ValCtx.DxilMod.GetModule());
CallGraph &CG = ValCtx.GetCallGraph();
std::unordered_map<CallGraphNode *, unsigned> depthMap;
std::unordered_set<CallGraphNode *> callStack;
@ -6161,6 +6378,8 @@ HRESULT ValidateDxilModule(llvm::Module *pModule, llvm::Module *pDebugModule) {
ValidateShaderFlags(ValCtx);
ValidateEntryCompatibility(ValCtx);
ValidateEntrySignatures(ValCtx);
ValidateUninitializedOutput(ValCtx);

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

@ -15,7 +15,7 @@ EmptyNodeOutput outputNode)
[NodeDispatchGrid(1, 1, 1)]
[NumThreads(1, 1, 1)]
void node_EmptyNodeOutput(
[MaxOutputRecords(1)] EmptyNodeOutput loadStressChild
[MaxRecords(1)] EmptyNodeOutput loadStressChild
)
{
loadStressEmptyRecWorker(wrapper(loadStressChild));

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

@ -301,7 +301,7 @@ void node_RWGroupNodeInputRecords([MaxRecords(4)] RWGroupNodeInputRecords<RECORD
[NodeLaunch("thread")]
void node_RWThreadNodeInputRecord(RWThreadNodeInputRecord<RECORD> input)
{
Barrier(wrapper(input), 3);
Barrier(wrapper(input), 0);
}
// ThreadNodeInputRecord
@ -317,7 +317,7 @@ void node_RWThreadNodeInputRecord(RWThreadNodeInputRecord<RECORD> input)
[NodeLaunch("thread")]
void node_ThreadNodeInputRecord(ThreadNodeInputRecord<RECORD> input)
{
Barrier(wrapper(input), 3);
Barrier(wrapper(input), 0);
}

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

@ -60,7 +60,7 @@ void samplecmpbias() {
// RDAT: MinShaderTarget: 0x50068
[shader("compute")]
[numthreads(1,1,1)]
[numthreads(4,1,1)]
void samplecmpbias_compute(uint tidx : SV_GroupIndex) {
samplecmpbias();
}

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

@ -38,9 +38,11 @@ float2 ddy;
bool clamp;
[Shader("node")]
[NodeLaunch("thread")]
[NodeLaunch("broadcasting")]
[NodeDispatchGrid(1, 1, 1)]
[NumThreads(4, 1, 1)]
void BackwardRef(
RWThreadNodeInputRecord<rec0> InputyMcInputFace,
RWDispatchNodeInputRecord<rec0> InputyMcInputFace,
[MaxRecords(5)] NodeOutput<rec1> Output1,
[MaxRecordsSharedWith(Output1)] NodeOutput<rec1> Output2)
{

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

@ -7,7 +7,9 @@ static const int a = 7;
static const int b = 3;
[Shader("node")]
[NodeLaunch("thread")]
[NodeLaunch("broadcasting")]
[NodeDispatchGrid(1, 1, 1)]
[NumThreads(1, 1, 1)]
void node117_barrier_memoryarg()
{
// literal integer flag values
@ -67,9 +69,9 @@ void node117_barrier_memoryarg()
// Arg #1: ShaderKind Tag (8)
// Arg #2: Node (15)
// Arg #3: NodeLaunch Tag (13)
// Arg #4: thread (3)
// Arg #4: broadcasting (1)
// ...
// ------------------------------------------------------------------
// CHECK: [[ATTRS]] = !{
// CHECK-SAME: i32 8, i32 15, i32 13, i32 3
// CHECK-SAME: i32 8, i32 15, i32 13, i32 1
// CHECK-SAME: }

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

@ -37,12 +37,12 @@ void node02([MaxRecords(8)] GroupNodeInputRecords<RECORD> input)
[NodeLaunch("thread")]
void node03(RWThreadNodeInputRecord<RECORD> input)
{
Barrier(input, 3);
Barrier(input, 0);
}
// CHECK: define void @node03() {
// CHECK: [[NODE03_A:%[0-9]+]] = call %dx.types.NodeRecordHandle @dx.op.createNodeInputRecordHandle(i32 {{[0-9]+}}, i32 0) ; CreateNodeInputRecordHandle(MetadataIdx)
// CHECK: [[ANN_NODE03_A:%[0-9]+]] = call %dx.types.NodeRecordHandle @dx.op.annotateNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[NODE03_A]], %dx.types.NodeRecordInfo { i32 37, i32 4 })
// CHECK: call void @dx.op.barrierByNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[ANN_NODE03_A]], i32 3) ; BarrierByNodeRecordHandle(object,SemanticFlags)
// CHECK: call void @dx.op.barrierByNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[ANN_NODE03_A]], i32 0) ; BarrierByNodeRecordHandle(object,SemanticFlags)
[Shader("node")]
[NodeLaunch("coalescing")]
@ -74,22 +74,22 @@ void node05([MaxRecords(5)] NodeOutput<RECORD> outputs)
[Shader("node")]
[NodeLaunch("thread")]
void node06([MaxOutputRecords(5)] NodeOutput<RECORD> outputs)
void node06([MaxRecords(5)] NodeOutput<RECORD> outputs)
{
ThreadNodeOutputRecords<RECORD> outrec = outputs.GetThreadNodeOutputRecords(3);
Barrier(outrec, 3);
Barrier(outrec, 0);
}
// CHECK: define void @node06() {
// CHECK: [[NODE06_A:%[0-9]+]] = call %dx.types.NodeHandle @dx.op.createNodeOutputHandle(i32 {{[0-9]+}}, i32 0) ; CreateNodeOutputHandle(MetadataIdx)
// CHECK: [[ANN_NODE06_A:%[0-9]+]] = call %dx.types.NodeHandle @dx.op.annotateNodeHandle(i32 {{[0-9]+}}, %dx.types.NodeHandle [[NODE06_A]], %dx.types.NodeInfo { i32 6, i32 4 })
// CHECK: [[NODE06_B:%[0-9]+]] = call %dx.types.NodeRecordHandle @dx.op.allocateNodeOutputRecords(i32 {{[0-9]+}}, %dx.types.NodeHandle [[ANN_NODE06_A]], i32 3, i1 true) ; AllocateNodeOutputRecords(output,numRecords,perThread)
// CHECK: [[ANN_NODE06_B:%[0-9]+]] = call %dx.types.NodeRecordHandle @dx.op.annotateNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[NODE06_B]], %dx.types.NodeRecordInfo { i32 38, i32 4 })
// CHECK: call void @dx.op.barrierByNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[ANN_NODE06_B]], i32 3) ; BarrierByNodeRecordHandle(object,SemanticFlags)
// CHECK: call void @dx.op.barrierByNodeRecordHandle(i32 {{[0-9]+}}, %dx.types.NodeRecordHandle [[ANN_NODE06_B]], i32 0) ; BarrierByNodeRecordHandle(object,SemanticFlags)
[Shader("node")]
[NodeLaunch("coalescing")]
[NumThreads(256,1,3)]
void node07([MaxOutputRecords(5)] NodeOutput<RECORD> outputs)
void node07([MaxRecords(5)] NodeOutput<RECORD> outputs)
{
GroupNodeOutputRecords<RECORD> outrec = outputs.GetGroupNodeOutputRecords(1);
Barrier(outrec, 3);

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

@ -71,7 +71,7 @@ void node03(NodeOutput<RECORD> output3)
[Shader("node")]
[NumThreads(1024,1,1)]
[NodeLaunch("coalescing")]
void node04([MaxOutputRecords(5)] NodeOutput<RECORD> outputs4)
void node04([MaxRecords(5)] NodeOutput<RECORD> outputs4)
{
ThreadNodeOutputRecords<RECORD> outrec = outputs4.GetThreadNodeOutputRecords(1);
// CHECK: getelementptr %struct.RECORD, %struct.RECORD addrspace(6)*

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

@ -116,7 +116,7 @@ void node03(NodeOutput<RECORD> output3)
[Shader("node")]
[NumThreads(1024,1,1)]
[NodeLaunch("coalescing")]
void node04([MaxOutputRecords(5)] NodeOutput<RECORD> outputs4)
void node04([MaxRecords(5)] NodeOutput<RECORD> outputs4)
{
GroupNodeOutputRecords<RECORD> outrec = outputs4.GetGroupNodeOutputRecords(1);
// CHECK: %[[p1_0:[^ ]+]] = getelementptr inbounds %[[RECORD]], %[[RECORD]] addrspace(6)* %{{[^,]+}}, i32 0, i32 1, i32 0

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

@ -12,7 +12,7 @@ EmptyNodeOutput outputNode)
[NodeDispatchGrid(1, 1, 1)]
[NumThreads(1, 1, 1)]
void loadStressEmptyRec_1(
[MaxOutputRecords(1)] EmptyNodeOutput loadStressChild
[MaxRecords(1)] EmptyNodeOutput loadStressChild
)
{
loadStressEmptyRecWorker(loadStressChild);

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

@ -0,0 +1,60 @@
// RUN: %dxc -T lib_6_8 -Vd %s | %D3DReflect %s | %FileCheck %s -check-prefixes=RDAT
// Verifies that a Barrier requiring a visible group in a noinline function
// called by a vertex shader is correctly marked as requiring a group in RDAT.
// Validation is disabled to allow this to produce the RDAT blob for checking
// the flags, and for generating .ll tests.
// RDAT: FunctionTable[{{.*}}] = {
RWBuffer<uint> Buf : register(u0);
// RDAT-LABEL: UnmangledName: "write_value"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: 0
// RDAT: ShaderStageFlag: (Pixel | Vertex | Geometry | Hull | Domain | Compute | Library | RayGeneration | Intersection | AnyHit | ClosestHit | Miss | Callable | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void write_value(uint value) {
Buf[value] = value;
}
// RDAT-LABEL: UnmangledName: "barrier_group"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_RequiresGroup)
// RDAT: ShaderStageFlag: (Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void barrier_group() {
write_value(1);
Barrier(GROUP_SHARED_MEMORY, GROUP_SCOPE);
write_value(2);
}
// RDAT-LABEL: UnmangledName: "intermediate"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_RequiresGroup)
// RDAT: ShaderStageFlag: (Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void intermediate() {
write_value(3);
barrier_group();
write_value(4);
}
// RDAT-LABEL: UnmangledName: "main"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_RequiresGroup)
// ShaderStageFlag indicates no compatible entry type after masking for vertex.
// RDAT: ShaderStageFlag: 0
// MinShaderTarget still indicates vertex shader.
// RDAT: MinShaderTarget: 0x10060
[shader("vertex")]
void main() {
intermediate();
}

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

@ -0,0 +1,98 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Make sure function compatibility checking is done for called functions.
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}barrier_group{{.*}}: error: Function uses features incompatible with the shader stage (vs) of the entry function.
; CHECK: Function: {{.*}}barrier_group{{.*}}: error: Function requires a visible group, but is called from a shader without one.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResourceProperties = type { i32, i32 }
%"class.RWBuffer<unsigned int>" = type { i32 }
@"\01?Buf@@3V?$RWBuffer@I@@A" = external constant %dx.types.Handle, align 4
; Function to call that does nothing, so is not to blame for conflict.
; Function Attrs: noinline nounwind
define void @"\01?write_value@@YAXI@Z"(i32 %value) #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@I@@A", align 4
%2 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1)
%3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 4106, i32 261 })
call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle %3, i32 %value, i32 undef, i32 %value, i32 %value, i32 %value, i32 %value, i8 15)
ret void
}
; Function that uses a barrier requiring visible group.
; Also calls write_value, but this function is to blame for group requirement.
; Function Attrs: noinline nounwind
define void @"\01?barrier_group@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXI@Z"(i32 1)
call void @dx.op.barrierByMemoryType(i32 244, i32 2, i32 2)
call void @"\01?write_value@@YAXI@Z"(i32 2)
ret void
}
; Intermediate function that is not directly to blame for conflict.
; Function calls barrier_group, and write_value.
; Function Attrs: noinline nounwind
define void @"\01?intermediate@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXI@Z"(i32 3)
call void @"\01?barrier_group@@YAXXZ"()
call void @"\01?write_value@@YAXI@Z"(i32 4)
ret void
}
; Function Attrs: nounwind
define void @main() #1 {
call void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.i32(i32, %dx.types.Handle, i32, i32, i32, i32, i32, i32, i8) #1
; Function Attrs: noduplicate nounwind
declare void @dx.op.barrierByMemoryType(i32, i32, i32) #2
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #3
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32, %dx.types.Handle) #4
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { noduplicate nounwind }
attributes #3 = { nounwind readnone }
attributes #4 = { nounwind readonly }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.typeAnnotations = !{!7}
!dx.entryPoints = !{!14, !16}
!0 = !{!"dxc(private) 1.8.0.4482 (val-compat-calls, 055b660e4)"}
!1 = !{i32 1, i32 8}
!2 = !{!"lib", i32 6, i32 8}
!3 = !{null, !4, null, null}
!4 = !{!5}
!5 = !{i32 0, %"class.RWBuffer<unsigned int>"* bitcast (%dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@I@@A" to %"class.RWBuffer<unsigned int>"*), !"Buf", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !6}
!6 = !{i32 0, i32 5}
!7 = !{i32 1, void (i32)* @"\01?write_value@@YAXI@Z", !8, void ()* @"\01?barrier_group@@YAXXZ", !13, void ()* @"\01?intermediate@@YAXXZ", !13, void ()* @main, !13}
!8 = !{!9, !11}
!9 = !{i32 1, !10, !10}
!10 = !{}
!11 = !{i32 0, !12, !10}
!12 = !{i32 7, i32 5}
!13 = !{!9}
!14 = !{null, !"", null, !3, !15}
!15 = !{i32 0, i64 8589934592}
!16 = !{void ()* @main, !"main", null, null, !17}
!17 = !{i32 8, i32 1, i32 5, !18}
!18 = !{i32 0}

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

@ -0,0 +1,60 @@
// RUN: %dxilver 1.8 | %dxc -T lib_6_5 -Vd %s | %D3DReflect %s | %FileCheck %s -check-prefixes=RDAT
// Verifies that a Sample operation requiring derivatives in a noinline function
// called by a compute shader is correctly marked as requiring SM 6.6 in RDAT.
// Validation is disabled to allow this to produce the RDAT blob for checking
// the MinShaderTarget, and for generating .ll tests.
// RDAT: FunctionTable[{{.*}}] = {
Texture2D<float4> T : register(t0);
SamplerState S : register(s0);
RWBuffer<float4> Buf : register(u0);
// RDAT-LABEL: UnmangledName: "write_value"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: 0
// RDAT: ShaderStageFlag: (Pixel | Vertex | Geometry | Hull | Domain | Compute | Library | RayGeneration | Intersection | AnyHit | ClosestHit | Miss | Callable | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void write_value(float value) {
Buf[uint(value)] = value;
}
// RDAT-LABEL: UnmangledName: "fn_sample"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void fn_sample() {
write_value(T.Sample(S, float2(0, 0)).x);
}
// RDAT-LABEL: UnmangledName: "intermediate"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void intermediate() {
write_value(1);
fn_sample();
write_value(2);
}
// RDAT-LABEL: UnmangledName: "main"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Compute)
// MinShaderTarget indicates higher requirement.
// RDAT: MinShaderTarget: 0x50066
[numthreads(8, 8, 1)]
[shader("compute")]
void main() {
intermediate();
}

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

@ -0,0 +1,129 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Verify that Sample called from compute shader is not allowed when shader
; model is less than 6.6 when compiled to library.
; test generated from deriv-in-nested-fn-cs65.hlsl
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses derivatives in compute-model shader, which is only supported in shader model 6.6 and above.
; This next error is also emitted because when the derivative usage is combined
; with the shader stage at the entry point, it results in setting the minimum
; shader model to 6.6. It is seen as an independent conflict in a way.
; It would be difficult to mask it off without missing other conflicts that
; could have independently caused the minimum shader model to be set higher.
; CHECK: Function: main: error: Entry function performs some operation that is incompatible with the shader stage or other entry properties. See other errors for details.
; CHECK: Function: main: error: Function uses features incompatible with the shader model.
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"
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%struct.SamplerState = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%dx.types.Handle = type { i8* }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
@"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" = external constant %"class.Texture2D<vector<float, 4> >", align 4
@"\01?S@@3USamplerState@@A" = external constant %struct.SamplerState, align 4
@"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" = external constant %"class.RWBuffer<vector<float, 4> >", align 4
; Function Attrs: noinline nounwind
define void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> %value) #0 {
%1 = load %"class.RWBuffer<vector<float, 4> >", %"class.RWBuffer<vector<float, 4> >"* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A", align 4
%2 = extractelement <4 x float> %value, i32 0
%3 = fptoui float %2 to i32
%4 = call %dx.types.Handle @"dx.op.createHandleForLib.class.RWBuffer<vector<float, 4> >"(i32 160, %"class.RWBuffer<vector<float, 4> >" %1) ; CreateHandleForLib(Resource)
%5 = extractelement <4 x float> %value, i64 0
%6 = extractelement <4 x float> %value, i64 1
%7 = extractelement <4 x float> %value, i64 2
%8 = extractelement <4 x float> %value, i64 3
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %4, i32 %3, i32 undef, float %5, float %6, float %7, float %8, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = load %struct.SamplerState, %struct.SamplerState* @"\01?S@@3USamplerState@@A", align 4
%2 = load %"class.Texture2D<vector<float, 4> >", %"class.Texture2D<vector<float, 4> >"* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A", align 4
%3 = call %dx.types.Handle @"dx.op.createHandleForLib.class.Texture2D<vector<float, 4> >"(i32 160, %"class.Texture2D<vector<float, 4> >" %2) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.createHandleForLib.struct.SamplerState(i32 160, %struct.SamplerState %1) ; CreateHandleForLib(Resource)
%5 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %3, %dx.types.Handle %4, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%6 = extractvalue %dx.types.ResRet.f32 %5, 0
%7 = insertelement <4 x float> undef, float %6, i64 0
%8 = extractvalue %dx.types.ResRet.f32 %5, 1
%9 = insertelement <4 x float> %7, float %8, i64 1
%10 = extractvalue %dx.types.ResRet.f32 %5, 2
%11 = insertelement <4 x float> %9, float %10, i64 2
%12 = extractvalue %dx.types.ResRet.f32 %5, 3
%13 = insertelement <4 x float> %11, float %12, i64 3
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> %13)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?intermediate@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> <float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00>)
call void @"\01?fn_sample@@YAXXZ"()
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00>)
ret void
}
; Function Attrs: nounwind
define void @main() #1 {
call void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readonly
declare %dx.types.Handle @"dx.op.createHandleForLib.class.RWBuffer<vector<float, 4> >"(i32, %"class.RWBuffer<vector<float, 4> >") #2
; Function Attrs: nounwind readonly
declare %dx.types.Handle @"dx.op.createHandleForLib.class.Texture2D<vector<float, 4> >"(i32, %"class.Texture2D<vector<float, 4> >") #2
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandleForLib.struct.SamplerState(i32, %struct.SamplerState) #2
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.resources = !{!4}
!dx.typeAnnotations = !{!12}
!dx.entryPoints = !{!19, !20}
!0 = !{!"dxc(private) 1.8.0.4482 (val-compat-calls, 055b660e4)"}
!1 = !{i32 1, i32 5}
!2 = !{i32 1, i32 8}
!3 = !{!"lib", i32 6, i32 5}
!4 = !{!5, !8, null, !10}
!5 = !{!6}
!6 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A", !"T", i32 0, i32 0, i32 1, i32 2, i32 0, !7}
!7 = !{i32 0, i32 9}
!8 = !{!9}
!9 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A", !"Buf", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !7}
!10 = !{!11}
!11 = !{i32 0, %struct.SamplerState* @"\01?S@@3USamplerState@@A", !"S", i32 0, i32 0, i32 1, i32 0, null}
!12 = !{i32 1, void (<4 x float>)* @"\01?write_value@@YAXV?$vector@M$03@@@Z", !13, void ()* @"\01?fn_sample@@YAXXZ", !18, void ()* @"\01?intermediate@@YAXXZ", !18, void ()* @main, !18}
!13 = !{!14, !16}
!14 = !{i32 1, !15, !15}
!15 = !{}
!16 = !{i32 0, !17, !15}
!17 = !{i32 7, i32 9}
!18 = !{!14}
!19 = !{null, !"", null, !4, null}
!20 = !{void ()* @main, !"main", null, null, !21}
!21 = !{i32 8, i32 5, i32 4, !22, i32 5, !23}
!22 = !{i32 8, i32 8, i32 1}
!23 = !{i32 0}

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

@ -0,0 +1,110 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Verify that Sample called from compute shader is not allowed when thread
; group size is not compatible with derivatives.
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses derivatives in compute-model shader with NumThreads (17, 1, 1); derivatives require NumThreads to be 1D and a multiple of 4, or 2D/3D with X and Y both being a multiple of 2.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResourceProperties = type { i32, i32 }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%struct.SamplerState = type { i32 }
@"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
@"\01?S@@3USamplerState@@A" = external constant %dx.types.Handle, align 4
@"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
; Function Attrs: noinline nounwind
define void @"\01?write_value@@YAXM@Z"(float %value) #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A", align 4
%2 = fptoui float %value to i32
%3 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4106, i32 1033 }) ; AnnotateHandle(res,props) resource: RWTypedBuffer<4xF32>
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %4, i32 %2, i32 undef, float %value, float %value, float %value, float %value, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?S@@3USamplerState@@A", align 4
%2 = load %dx.types.Handle, %dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A", align 4
%3 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %2) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 2, i32 1033 }) ; AnnotateHandle(res,props) resource: Texture2D<4xF32>
%5 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 14, i32 0 }) ; AnnotateHandle(res,props) resource: SamplerState
%7 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %4, %dx.types.Handle %6, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%8 = extractvalue %dx.types.ResRet.f32 %7, 0
call void @"\01?write_value@@YAXM@Z"(float %8)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?intermediate@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXM@Z"(float 1.000000e+00)
call void @"\01?fn_sample@@YAXXZ"()
call void @"\01?write_value@@YAXM@Z"(float 2.000000e+00)
ret void
}
; Function Attrs: nounwind
define void @main() #1 {
call void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #3
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32, %dx.types.Handle) #2
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
attributes #3 = { nounwind readnone }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.resources = !{!4}
!dx.typeAnnotations = !{!12}
!dx.entryPoints = !{!19, !20}
!0 = !{!"dxc(private) 1.8.0.4484 (val-compat-calls, 9e4ee71a0)"}
!1 = !{i32 1, i32 6}
!2 = !{i32 1, i32 8}
!3 = !{!"lib", i32 6, i32 6}
!4 = !{!5, !8, null, !10}
!5 = !{!6}
!6 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" to %"class.Texture2D<vector<float, 4> >"*), !"T", i32 0, i32 0, i32 1, i32 2, i32 0, !7}
!7 = !{i32 0, i32 9}
!8 = !{!9}
!9 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" to %"class.RWBuffer<vector<float, 4> >"*), !"Buf", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !7}
!10 = !{!11}
!11 = !{i32 0, %struct.SamplerState* bitcast (%dx.types.Handle* @"\01?S@@3USamplerState@@A" to %struct.SamplerState*), !"S", i32 0, i32 0, i32 1, i32 0, null}
!12 = !{i32 1, void (float)* @"\01?write_value@@YAXM@Z", !13, void ()* @"\01?fn_sample@@YAXXZ", !18, void ()* @"\01?intermediate@@YAXXZ", !18, void ()* @main, !18}
!13 = !{!14, !16}
!14 = !{i32 1, !15, !15}
!15 = !{}
!16 = !{i32 0, !17, !15}
!17 = !{i32 7, i32 9}
!18 = !{!14}
!19 = !{null, !"", null, !4, null}
!20 = !{void ()* @main, !"main", null, null, !21}
!21 = !{i32 8, i32 5, i32 4, !22, i32 5, !23}
!22 = !{i32 17, i32 1, i32 1}
!23 = !{i32 0}

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

@ -0,0 +1,92 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Verify that Sample called from compute shader is not allowed when shader
; model is less than 6.6 when compiled to cs_6_5 non-library target.
; Opcode validation fails because for non-lib shader models, all functions are
; validated assuming the same stage. This verifies that the call graph
; validation is still applied.
; test generated from deriv-in-nested-fn-cs-lib65.hlsl with -T cs_6_5
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: opcode 'Derivatives in CS/MS/AS' should only be used in 'Shader Model 6.6+'.
; CHECK: note: at '%3 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %1, %dx.types.Handle %2, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef)' in block '#0' of function '{{.*}}fn_sample{{.*}}'.
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses derivatives in compute-model shader, which is only supported in shader model 6.6 and above.
; CHECK: Function: main: error: Entry function performs some operation that is incompatible with the shader stage or other entry properties. See other errors for details.
; CHECK: Function: main: error: Function uses features incompatible with the shader model.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%struct.SamplerState = type { i32 }
define void @main() {
call fastcc void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?intermediate@@YAXXZ"() #0 {
call fastcc void @"\01?write_value@@YAXM@Z"(float 1.000000e+00)
call fastcc void @"\01?fn_sample@@YAXXZ"()
call fastcc void @"\01?write_value@@YAXM@Z"(float 2.000000e+00)
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?write_value@@YAXM@Z"(float %x) #0 {
%1 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 0, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex)
%2 = fptoui float %x to i32
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %1, i32 %2, i32 undef, float %x, float %x, float %x, float %x, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 0, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex)
%2 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 3, i32 0, i32 0, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex)
%3 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %1, %dx.types.Handle %2, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%4 = extractvalue %dx.types.ResRet.f32 %3, 0
call fastcc void @"\01?write_value@@YAXM@Z"(float %4)
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandle(i32, i8, i32, i32, i1) #2
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.resources = !{!4}
!dx.entryPoints = !{!12}
!0 = !{!"dxc(private) 1.8.0.4482 (val-compat-calls, 055b660e4)"}
!1 = !{i32 1, i32 0}
!2 = !{i32 1, i32 8}
!3 = !{!"cs", i32 6, i32 0}
!4 = !{!5, !8, null, !10}
!5 = !{!6}
!6 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* undef, !"", i32 0, i32 0, i32 1, i32 2, i32 0, !7}
!7 = !{i32 0, i32 9}
!8 = !{!9}
!9 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* undef, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !7}
!10 = !{!11}
!11 = !{i32 0, %struct.SamplerState* undef, !"", i32 0, i32 0, i32 1, i32 0, null}
!12 = !{void ()* @main, !"main", null, !4, !13}
!13 = !{i32 4, !14}
!14 = !{i32 8, i32 8, i32 1}

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

@ -0,0 +1,93 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Verify that Sample called from compute shader is not allowed when thread
; group size is not compatible with derivatives.
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses derivatives in compute-model shader with NumThreads (8, 7, 1); derivatives require NumThreads to be 1D and a multiple of 4, or 2D/3D with X and Y both being a multiple of 2.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResBind = type { i32, i32, i32, i8 }
%dx.types.ResourceProperties = type { i32, i32 }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%struct.SamplerState = type { i32 }
define void @main() {
call fastcc void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?intermediate@@YAXXZ"() #0 {
call fastcc void @"\01?write_value@@YAXM@Z"(float 1.000000e+00)
call fastcc void @"\01?fn_sample@@YAXXZ"()
call fastcc void @"\01?write_value@@YAXM@Z"(float 2.000000e+00)
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?write_value@@YAXM@Z"(float %value) #0 {
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 1 }, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
%2 = fptoui float %value to i32
%3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 4106, i32 1033 }) ; AnnotateHandle(res,props) resource: RWTypedBuffer<4xF32>
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %3, i32 %2, i32 undef, float %value, float %value, float %value, float %value, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define internal fastcc void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind zeroinitializer, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
%2 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 0, i32 0, i8 3 }, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
%3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 2, i32 1033 }) ; AnnotateHandle(res,props) resource: Texture2D<4xF32>
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 14, i32 0 }) ; AnnotateHandle(res,props) resource: SamplerState
%5 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %3, %dx.types.Handle %4, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%6 = extractvalue %dx.types.ResRet.f32 %5, 0
call fastcc void @"\01?write_value@@YAXM@Z"(float %6)
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #3
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #3
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
attributes #3 = { nounwind readnone }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!2}
!dx.shaderModel = !{!3}
!dx.resources = !{!4}
!dx.entryPoints = !{!12}
!0 = !{!"dxc(private) 1.8.0.4484 (val-compat-calls, 9e4ee71a0)"}
!1 = !{i32 1, i32 6}
!2 = !{i32 1, i32 8}
!3 = !{!"cs", i32 6, i32 6}
!4 = !{!5, !8, null, !10}
!5 = !{!6}
!6 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* undef, !"", i32 0, i32 0, i32 1, i32 2, i32 0, !7}
!7 = !{i32 0, i32 9}
!8 = !{!9}
!9 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* undef, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !7}
!10 = !{!11}
!11 = !{i32 0, %struct.SamplerState* undef, !"", i32 0, i32 0, i32 1, i32 0, null}
!12 = !{void ()* @main, !"main", null, !4, !13}
!13 = !{i32 4, !14}
!14 = !{i32 8, i32 7, i32 1}

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

@ -0,0 +1,60 @@
// RUN: %dxilver 1.8 | %dxc -T lib_6_8 -Vd %s | %D3DReflect %s | %FileCheck %s -check-prefixes=RDAT
// Check that ShaderCompatInfo in RDAT has expected flags for scenario with
// thread launch node calling a function using derivatives (through Sample()).
// Validation is disabled to allow this to produce the RDAT blob to check.
// Used to generate deriv-in-nested-fn-node-lib68-launch.ll
// RDAT: FunctionTable[{{.*}}] = {
Texture2D<float4> T : register(t0);
SamplerState S : register(s0);
RWBuffer<float4> Buf : register(u0);
// RDAT-LABEL: UnmangledName: "write_value"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: 0
// RDAT: ShaderStageFlag: (Pixel | Vertex | Geometry | Hull | Domain | Compute | Library | RayGeneration | Intersection | AnyHit | ClosestHit | Miss | Callable | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void write_value(float value) {
Buf[uint(value)] = value;
}
// RDAT-LABEL: UnmangledName: "fn_sample"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void fn_sample() {
write_value(T.Sample(S, float2(0, 0)).x);
}
// RDAT-LABEL: UnmangledName: "intermediate"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void intermediate() {
write_value(1);
fn_sample();
write_value(2);
}
// RDAT-LABEL: UnmangledName: "main"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Node)
// RDAT: MinShaderTarget: 0xf0068
// RDAT: LaunchType: Thread
[shader("node")]
[NodeLaunch("thread")]
void main() {
intermediate();
}

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

@ -0,0 +1,111 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Sample called from node shader is not allowed with thread launch.
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function called from Thread launch node shader uses derivatives; only broadcasting launch supports derivatives.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses derivatives in compute-model shader with NumThreads (1, 1, 1); derivatives require NumThreads to be 1D and a multiple of 4, or 2D/3D with X and Y both being a multiple of 2.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResourceProperties = type { i32, i32 }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%struct.SamplerState = type { i32 }
@"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
@"\01?S@@3USamplerState@@A" = external constant %dx.types.Handle, align 4
@"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
; Function Attrs: noinline nounwind
define void @"\01?write_value@@YAXM@Z"(float %value) #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A", align 4
%2 = fptoui float %value to i32
%3 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4106, i32 1033 }) ; AnnotateHandle(res,props) resource: RWTypedBuffer<4xF32>
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %4, i32 %2, i32 undef, float %value, float %value, float %value, float %value, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?S@@3USamplerState@@A", align 4
%2 = load %dx.types.Handle, %dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A", align 4
%3 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %2) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 2, i32 1033 }) ; AnnotateHandle(res,props) resource: Texture2D<4xF32>
%5 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 14, i32 0 }) ; AnnotateHandle(res,props) resource: SamplerState
%7 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %4, %dx.types.Handle %6, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%8 = extractvalue %dx.types.ResRet.f32 %7, 0
call void @"\01?write_value@@YAXM@Z"(float %8)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?intermediate@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXM@Z"(float 1.000000e+00)
call void @"\01?fn_sample@@YAXXZ"()
call void @"\01?write_value@@YAXM@Z"(float 2.000000e+00)
ret void
}
; Function Attrs: nounwind
define void @main() #1 {
call void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #3
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32, %dx.types.Handle) #2
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
attributes #3 = { nounwind readnone }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.typeAnnotations = !{!11}
!dx.entryPoints = !{!18, !20}
!0 = !{!"dxc(private) 1.8.0.4484 (val-compat-calls, 9e4ee71a0)"}
!1 = !{i32 1, i32 8}
!2 = !{!"lib", i32 6, i32 8}
!3 = !{!4, !7, null, !9}
!4 = !{!5}
!5 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" to %"class.Texture2D<vector<float, 4> >"*), !"T", i32 0, i32 0, i32 1, i32 2, i32 0, !6}
!6 = !{i32 0, i32 9}
!7 = !{!8}
!8 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" to %"class.RWBuffer<vector<float, 4> >"*), !"Buf", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !6}
!9 = !{!10}
!10 = !{i32 0, %struct.SamplerState* bitcast (%dx.types.Handle* @"\01?S@@3USamplerState@@A" to %struct.SamplerState*), !"S", i32 0, i32 0, i32 1, i32 0, null}
!11 = !{i32 1, void (float)* @"\01?write_value@@YAXM@Z", !12, void ()* @"\01?fn_sample@@YAXXZ", !17, void ()* @"\01?intermediate@@YAXXZ", !17, void ()* @main, !17}
!12 = !{!13, !15}
!13 = !{i32 1, !14, !14}
!14 = !{}
!15 = !{i32 0, !16, !14}
!16 = !{i32 7, i32 9}
!17 = !{!13}
!18 = !{null, !"", null, !3, !19}
!19 = !{i32 0, i64 8589934592}
!20 = !{void ()* @main, !"main", null, null, !21}
!21 = !{i32 8, i32 15, i32 13, i32 3, i32 15, !22, i32 16, i32 -1, i32 4, !23, i32 5, !24}
!22 = !{!"main", i32 0}
!23 = !{i32 1, i32 1, i32 1}
!24 = !{i32 0}

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

@ -0,0 +1,61 @@
// RUN: %dxc -T lib_6_8 -Vd %s | %D3DReflect %s | %FileCheck %s -check-prefixes=RDAT
// Check that ShaderCompatInfo in RDAT has expected flags for scenario with
// verttex entry point calling a function using derivatives (through Sample()).
// Validation is disabled to allow this to produce the RDAT blob to check.
// Used to generate deriv-in-nested-fn-vs.ll
// RDAT: FunctionTable[{{.*}}] = {
Texture2D<float4> T : register(t0);
SamplerState S : register(s0);
RWBuffer<float4> Buf : register(u0);
// RDAT-LABEL: UnmangledName: "write_value"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: 0
// RDAT: ShaderStageFlag: (Pixel | Vertex | Geometry | Hull | Domain | Compute | Library | RayGeneration | Intersection | AnyHit | ClosestHit | Miss | Callable | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void write_value(float4 value) {
Buf[uint(value.x)] = value;
}
// RDAT-LABEL: UnmangledName: "fn_sample"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void fn_sample() {
write_value(T.Sample(S, float2(0, 0)));
}
// RDAT-LABEL: UnmangledName: "intermediate"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// RDAT: ShaderStageFlag: (Pixel | Compute | Library | Mesh | Amplification | Node)
// RDAT: MinShaderTarget: 0x60060
[noinline] export
void intermediate() {
write_value(1);
fn_sample();
write_value(2);
}
// RDAT-LABEL: UnmangledName: "main"
// RDAT: FeatureInfo1: 0
// RDAT: FeatureInfo2: (Opt_UsesDerivatives)
// ShaderStageFlag indicates no compatible entry type after masking for vertex.
// RDAT: ShaderStageFlag: 0
// MinShaderTarget still indicates vertex shader.
// RDAT: MinShaderTarget: 0x10060
//[numthreads(8, 8, 1)]
[shader("vertex")]
void main() {
intermediate();
}

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

@ -0,0 +1,121 @@
; RUN: %dxilver 1.8 | %dxv %s | FileCheck %s
; Verify that Sample called from vertex shader is not allowed.
; test generated from deriv-in-nested-fn-vs.hlsl
; CHECK: Function: main: error: Entry function calls one or more functions using incompatible features. See other errors for details.
; CHECK: Function: {{.*}}fn_sample{{.*}}: error: Function uses features incompatible with the shader stage (vs) of the entry function.
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"
%dx.types.Handle = type { i8* }
%dx.types.ResourceProperties = type { i32, i32 }
%dx.types.ResRet.f32 = type { float, float, float, float, i32 }
%"class.Texture2D<vector<float, 4> >" = type { <4 x float>, %"class.Texture2D<vector<float, 4> >::mips_type" }
%"class.Texture2D<vector<float, 4> >::mips_type" = type { i32 }
%"class.RWBuffer<vector<float, 4> >" = type { <4 x float> }
%struct.SamplerState = type { i32 }
@"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
@"\01?S@@3USamplerState@@A" = external constant %dx.types.Handle, align 4
@"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4
; Function Attrs: noinline nounwind
define void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> %value) #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A", align 4
%2 = extractelement <4 x float> %value, i32 0
%3 = fptoui float %2 to i32
%4 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%5 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %4, %dx.types.ResourceProperties { i32 4106, i32 1033 }) ; AnnotateHandle(res,props) resource: RWTypedBuffer<4xF32>
%6 = extractelement <4 x float> %value, i64 0
%7 = extractelement <4 x float> %value, i64 1
%8 = extractelement <4 x float> %value, i64 2
%9 = extractelement <4 x float> %value, i64 3
call void @dx.op.bufferStore.f32(i32 69, %dx.types.Handle %5, i32 %3, i32 undef, float %6, float %7, float %8, float %9, i8 15) ; BufferStore(uav,coord0,coord1,value0,value1,value2,value3,mask)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?fn_sample@@YAXXZ"() #0 {
%1 = load %dx.types.Handle, %dx.types.Handle* @"\01?S@@3USamplerState@@A", align 4
%2 = load %dx.types.Handle, %dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A", align 4
%3 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %2) ; CreateHandleForLib(Resource)
%4 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 2, i32 1033 }) ; AnnotateHandle(res,props) resource: Texture2D<4xF32>
%5 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource)
%6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 14, i32 0 }) ; AnnotateHandle(res,props) resource: SamplerState
%7 = call %dx.types.ResRet.f32 @dx.op.sample.f32(i32 60, %dx.types.Handle %4, %dx.types.Handle %6, float 0.000000e+00, float 0.000000e+00, float undef, float undef, i32 0, i32 0, i32 undef, float undef) ; Sample(srv,sampler,coord0,coord1,coord2,coord3,offset0,offset1,offset2,clamp)
%8 = extractvalue %dx.types.ResRet.f32 %7, 0
%9 = insertelement <4 x float> undef, float %8, i64 0
%10 = extractvalue %dx.types.ResRet.f32 %7, 1
%11 = insertelement <4 x float> %9, float %10, i64 1
%12 = extractvalue %dx.types.ResRet.f32 %7, 2
%13 = insertelement <4 x float> %11, float %12, i64 2
%14 = extractvalue %dx.types.ResRet.f32 %7, 3
%15 = insertelement <4 x float> %13, float %14, i64 3
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> %15)
ret void
}
; Function Attrs: noinline nounwind
define void @"\01?intermediate@@YAXXZ"() #0 {
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> <float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00>)
call void @"\01?fn_sample@@YAXXZ"()
call void @"\01?write_value@@YAXV?$vector@M$03@@@Z"(<4 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00, float 2.000000e+00>)
ret void
}
; Function Attrs: nounwind
define void @main() #1 {
call void @"\01?intermediate@@YAXXZ"()
ret void
}
; Function Attrs: nounwind
declare void @dx.op.bufferStore.f32(i32, %dx.types.Handle, i32, i32, float, float, float, float, i8) #1
; Function Attrs: nounwind readonly
declare %dx.types.ResRet.f32 @dx.op.sample.f32(i32, %dx.types.Handle, %dx.types.Handle, float, float, float, float, i32, i32, i32, float) #2
; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #3
; Function Attrs: nounwind readonly
declare %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32, %dx.types.Handle) #2
attributes #0 = { noinline nounwind }
attributes #1 = { nounwind }
attributes #2 = { nounwind readonly }
attributes #3 = { nounwind readnone }
!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.typeAnnotations = !{!11}
!dx.entryPoints = !{!18, !20}
!0 = !{!"dxc(private) 1.8.0.4482 (val-compat-calls, 055b660e4)"}
!1 = !{i32 1, i32 8}
!2 = !{!"lib", i32 6, i32 8}
!3 = !{!4, !7, null, !9}
!4 = !{!5}
!5 = !{i32 0, %"class.Texture2D<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?T@@3V?$Texture2D@V?$vector@M$03@@@@A" to %"class.Texture2D<vector<float, 4> >"*), !"T", i32 0, i32 0, i32 1, i32 2, i32 0, !6}
!6 = !{i32 0, i32 9}
!7 = !{!8}
!8 = !{i32 0, %"class.RWBuffer<vector<float, 4> >"* bitcast (%dx.types.Handle* @"\01?Buf@@3V?$RWBuffer@V?$vector@M$03@@@@A" to %"class.RWBuffer<vector<float, 4> >"*), !"Buf", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, !6}
!9 = !{!10}
!10 = !{i32 0, %struct.SamplerState* bitcast (%dx.types.Handle* @"\01?S@@3USamplerState@@A" to %struct.SamplerState*), !"S", i32 0, i32 0, i32 1, i32 0, null}
!11 = !{i32 1, void (<4 x float>)* @"\01?write_value@@YAXV?$vector@M$03@@@Z", !12, void ()* @"\01?fn_sample@@YAXXZ", !17, void ()* @"\01?intermediate@@YAXXZ", !17, void ()* @main, !17}
!12 = !{!13, !15}
!13 = !{i32 1, !14, !14}
!14 = !{}
!15 = !{i32 0, !16, !14}
!16 = !{i32 7, i32 9, i32 13, i32 4}
!17 = !{!13}
!18 = !{null, !"", null, !3, !19}
!19 = !{i32 0, i64 8589934592}
!20 = !{void ()* @main, !"main", null, null, !21}
!21 = !{i32 8, i32 1, i32 5, !22}
!22 = !{i32 0}

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

@ -7589,7 +7589,7 @@ class db_dxil(object):
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"
"Call to DXIL intrinsic %0 (%1) is not allowed in node shader launch type %2",
)
self.add_valrule("Instr.AtomicConst", "Constant destination to atomic.")
@ -8081,6 +8081,56 @@ class db_dxil(object):
"%0 node shader '%1' has incompatible input record type (should be %2)",
)
# These errors are emitted from ShaderCompatInfo validation.
# If a called function is identifiable as a potential source of the
# incompatibility, you get Sm.IncompatibleCallInEntry,
# otherwise you get Sm.IncompatibleOperation.
# You also get the specific incompatibilities found with one function
# introducing each problem.
# These may be emitted in addition to another specific operation
# validation error that identifies the root cause, but is meant to
# catch cases currently missed by other validation.
self.add_valrule_msg(
"Sm.IncompatibleCallInEntry",
"Features used in internal function calls must be compatible with entry",
"Entry function calls one or more functions using incompatible features. See other errors for details.",
)
self.add_valrule_msg(
"Sm.IncompatibleOperation",
"Operations used in entry function must be compatible with shader stage and other properties",
"Entry function performs some operation that is incompatible with the shader stage or other entry properties. See other errors for details.",
)
self.add_valrule_msg(
"Sm.IncompatibleStage",
"Functions may only use features available in the entry function's stage",
"Function uses features incompatible with the shader stage (%0) of the entry function.",
)
self.add_valrule_msg(
"Sm.IncompatibleShaderModel",
"Functions may only use features available in the current shader model",
"Function uses features incompatible with the shader model.",
)
self.add_valrule_msg(
"Sm.IncompatibleThreadGroupDim",
"When derivatives are used in compute-model shaders, the thread group dimensions must be compatible",
"Function uses derivatives in compute-model shader with NumThreads (%0, %1, %2); derivatives require NumThreads to be 1D and a multiple of 4, or 2D/3D with X and Y both being a multiple of 2.",
)
self.add_valrule_msg(
"Sm.IncompatibleDerivInComputeShaderModel",
"Derivatives in compute-model shaders require shader model 6.6 and above",
"Function uses derivatives in compute-model shader, which is only supported in shader model 6.6 and above.",
)
self.add_valrule_msg(
"Sm.IncompatibleRequiresGroup",
"Functions requiring groupshared memory must be called from shaders with a visible group",
"Function requires a visible group, but is called from a shader without one.",
)
self.add_valrule_msg(
"Sm.IncompatibleDerivLaunch",
"Node shaders only support derivatives in broadcasting launch mode",
"Function called from %0 launch node shader uses derivatives; only broadcasting launch supports derivatives.",
)
# Assign sensible category names and build up an enumeration description
cat_names = {
"CONTAINER": "Container",