in tools/clang/lib/CodeGen/CGHLSLMS.cpp [1375:2449]
void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
// Add hlsl intrinsic attr
unsigned intrinsicOpcode;
StringRef intrinsicGroup;
if (hlsl::GetIntrinsicOp(FD, intrinsicOpcode, intrinsicGroup)) {
AddHLSLIntrinsicOpcodeToFunction(F, intrinsicOpcode);
F->addFnAttr(hlsl::HLPrefix, intrinsicGroup);
StringRef lower;
if (hlsl::GetIntrinsicLowering(FD, lower))
hlsl::SetHLLowerStrategy(F, lower);
if (FD->hasAttr<HLSLWaveSensitiveAttr>())
hlsl::SetHLWaveSensitive(F);
// Don't need to add FunctionQual for intrinsic function.
return;
}
if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::FTZ) {
F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValueFtzString);
}
else if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::Preserve) {
F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValuePreserveString);
}
else if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::Any) {
F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValueAnyString);
}
// Set entry function
const std::string &entryName = m_pHLModule->GetEntryFunctionName();
bool isEntry = FD->getNameAsString() == entryName;
if (isEntry) {
Entry.Func = F;
Entry.SL = FD->getLocation();
}
DiagnosticsEngine &Diags = CGM.getDiags();
std::unique_ptr<DxilFunctionProps> funcProps =
llvm::make_unique<DxilFunctionProps>();
funcProps->shaderKind = DXIL::ShaderKind::Invalid;
bool isCS = false;
bool isGS = false;
bool isHS = false;
bool isDS = false;
bool isVS = false;
bool isPS = false;
bool isRay = false;
bool isMS = false;
bool isAS = false;
if (const HLSLShaderAttr *Attr = FD->getAttr<HLSLShaderAttr>()) {
// Stage is already validate in HandleDeclAttributeForHLSL.
// Here just check first letter (or two).
switch (Attr->getStage()[0]) {
case 'c':
switch (Attr->getStage()[1]) {
case 'o':
isCS = true;
funcProps->shaderKind = DXIL::ShaderKind::Compute;
break;
case 'l':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::ClosestHit;
break;
case 'a':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::Callable;
break;
default:
break;
}
break;
case 'v':
isVS = true;
funcProps->shaderKind = DXIL::ShaderKind::Vertex;
break;
case 'h':
isHS = true;
funcProps->shaderKind = DXIL::ShaderKind::Hull;
break;
case 'd':
isDS = true;
funcProps->shaderKind = DXIL::ShaderKind::Domain;
break;
case 'g':
isGS = true;
funcProps->shaderKind = DXIL::ShaderKind::Geometry;
break;
case 'p':
isPS = true;
funcProps->shaderKind = DXIL::ShaderKind::Pixel;
break;
case 'r':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::RayGeneration;
break;
case 'i':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::Intersection;
break;
case 'a':
switch (Attr->getStage()[1]) {
case 'm':
isAS = true;
funcProps->shaderKind = DXIL::ShaderKind::Amplification;
break;
case 'n':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::AnyHit;
break;
default:
break;
}
break;
case 'm':
switch (Attr->getStage()[1]) {
case 'e':
isMS = true;
funcProps->shaderKind = DXIL::ShaderKind::Mesh;
break;
case 'i':
isRay = true;
funcProps->shaderKind = DXIL::ShaderKind::Miss;
break;
default:
break;
}
break;
default:
break;
}
if (funcProps->shaderKind == DXIL::ShaderKind::Invalid) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "Invalid profile for shader attribute");
Diags.Report(Attr->getLocation(), DiagID);
}
if (isEntry && isRay) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "Ray function cannot be used as a global entry point");
Diags.Report(Attr->getLocation(), DiagID);
}
}
// Save patch constant function to patchConstantFunctionMap.
bool isPatchConstantFunction = false;
if (!isEntry && CGM.getContext().IsPatchConstantFunctionDecl(FD)) {
isPatchConstantFunction = true;
auto &PCI = patchConstantFunctionMap[FD->getName()];
PCI.SL = FD->getLocation();
PCI.Func = F;
++PCI.NumOverloads;
for (ParmVarDecl *parmDecl : FD->parameters()) {
QualType Ty = parmDecl->getType();
if (IsHLSLOutputPatchType(Ty)) {
funcProps->ShaderProps.HS.outputControlPoints =
GetHLSLOutputPatchCount(parmDecl->getType());
} else if (IsHLSLInputPatchType(Ty)) {
funcProps->ShaderProps.HS.inputControlPoints =
GetHLSLInputPatchCount(parmDecl->getType());
}
}
// Mark patch constant functions that cannot be linked as exports
// InternalLinkage. Patch constant functions that are actually used
// will be set back to ExternalLinkage in FinishCodeGen.
if (funcProps->ShaderProps.HS.outputControlPoints ||
funcProps->ShaderProps.HS.inputControlPoints) {
PCI.Func->setLinkage(GlobalValue::InternalLinkage);
}
funcProps->shaderKind = DXIL::ShaderKind::Hull;
}
const ShaderModel *SM = m_pHLModule->GetShaderModel();
if (isEntry) {
funcProps->shaderKind = SM->GetKind();
if (funcProps->shaderKind == DXIL::ShaderKind::Mesh) {
isMS = true;
}
else if (funcProps->shaderKind == DXIL::ShaderKind::Amplification) {
isAS = true;
}
}
// Geometry shader.
if (const HLSLMaxVertexCountAttr *Attr =
FD->getAttr<HLSLMaxVertexCountAttr>()) {
isGS = true;
funcProps->shaderKind = DXIL::ShaderKind::Geometry;
funcProps->ShaderProps.GS.maxVertexCount = Attr->getCount();
funcProps->ShaderProps.GS.inputPrimitive = DXIL::InputPrimitive::Undefined;
if (isEntry && !SM->IsGS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"attribute maxvertexcount only valid for GS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
}
if (const HLSLInstanceAttr *Attr = FD->getAttr<HLSLInstanceAttr>()) {
unsigned instanceCount = Attr->getCount();
funcProps->ShaderProps.GS.instanceCount = instanceCount;
if (isEntry && !SM->IsGS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"attribute maxvertexcount only valid for GS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
} else {
// Set default instance count.
if (isGS)
funcProps->ShaderProps.GS.instanceCount = 1;
}
// Compute shader
if (const HLSLNumThreadsAttr *Attr = FD->getAttr<HLSLNumThreadsAttr>()) {
if (isMS) {
funcProps->ShaderProps.MS.numThreads[0] = Attr->getX();
funcProps->ShaderProps.MS.numThreads[1] = Attr->getY();
funcProps->ShaderProps.MS.numThreads[2] = Attr->getZ();
} else if (isAS) {
funcProps->ShaderProps.AS.numThreads[0] = Attr->getX();
funcProps->ShaderProps.AS.numThreads[1] = Attr->getY();
funcProps->ShaderProps.AS.numThreads[2] = Attr->getZ();
} else {
isCS = true;
funcProps->shaderKind = DXIL::ShaderKind::Compute;
funcProps->ShaderProps.CS.numThreads[0] = Attr->getX();
funcProps->ShaderProps.CS.numThreads[1] = Attr->getY();
funcProps->ShaderProps.CS.numThreads[2] = Attr->getZ();
}
if (isEntry && !SM->IsCS() && !SM->IsMS() && !SM->IsAS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "attribute numthreads only valid for CS/MS/AS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
}
// Hull shader.
if (const HLSLPatchConstantFuncAttr *Attr =
FD->getAttr<HLSLPatchConstantFuncAttr>()) {
if (isEntry && !SM->IsHS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute patchconstantfunc only valid for HS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
isHS = true;
funcProps->shaderKind = DXIL::ShaderKind::Hull;
HSEntryPatchConstantFuncAttr[F] = Attr;
} else {
// TODO: This is a duplicate check. We also have this check in
// hlsl::DiagnoseTranslationUnit(clang::Sema*).
if (isEntry && SM->IsHS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"HS entry point must have the patchconstantfunc attribute");
Diags.Report(FD->getLocation(), DiagID);
return;
}
}
if (const HLSLOutputControlPointsAttr *Attr =
FD->getAttr<HLSLOutputControlPointsAttr>()) {
if (isHS) {
funcProps->ShaderProps.HS.outputControlPoints = Attr->getCount();
} else if (isEntry && !SM->IsHS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute outputcontrolpoints only valid for HS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
}
if (const HLSLPartitioningAttr *Attr = FD->getAttr<HLSLPartitioningAttr>()) {
if (isHS) {
DXIL::TessellatorPartitioning partition =
StringToPartitioning(Attr->getScheme());
funcProps->ShaderProps.HS.partition = partition;
} else if (isEntry && !SM->IsHS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Warning,
"attribute partitioning only valid for HS.");
Diags.Report(Attr->getLocation(), DiagID);
}
}
if (const HLSLOutputTopologyAttr *Attr =
FD->getAttr<HLSLOutputTopologyAttr>()) {
if (isHS) {
DXIL::TessellatorOutputPrimitive primitive =
StringToTessOutputPrimitive(Attr->getTopology());
funcProps->ShaderProps.HS.outputPrimitive = primitive;
}
else if (isMS) {
DXIL::MeshOutputTopology topology =
StringToMeshOutputTopology(Attr->getTopology());
funcProps->ShaderProps.MS.outputTopology = topology;
}
else if (isEntry && !SM->IsHS() && !SM->IsMS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Warning,
"attribute outputtopology only valid for HS and MS.");
Diags.Report(Attr->getLocation(), DiagID);
}
}
if (isHS) {
funcProps->ShaderProps.HS.maxTessFactor = DXIL::kHSMaxTessFactorUpperBound;
funcProps->ShaderProps.HS.inputControlPoints = DXIL::kHSDefaultInputControlPointCount;
}
if (const HLSLMaxTessFactorAttr *Attr =
FD->getAttr<HLSLMaxTessFactorAttr>()) {
if (isHS) {
// TODO: change getFactor to return float.
llvm::APInt intV(32, Attr->getFactor());
funcProps->ShaderProps.HS.maxTessFactor = intV.bitsToFloat();
} else if (isEntry && !SM->IsHS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"attribute maxtessfactor only valid for HS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
}
// Hull or domain shader.
if (const HLSLDomainAttr *Attr = FD->getAttr<HLSLDomainAttr>()) {
if (isEntry && !SM->IsHS() && !SM->IsDS()) {
unsigned DiagID =
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"attribute domain only valid for HS or DS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
isDS = !isHS;
if (isDS)
funcProps->shaderKind = DXIL::ShaderKind::Domain;
DXIL::TessellatorDomain domain = StringToDomain(Attr->getDomainType());
if (isHS)
funcProps->ShaderProps.HS.domain = domain;
else
funcProps->ShaderProps.DS.domain = domain;
}
// Vertex shader.
if (const HLSLClipPlanesAttr *Attr = FD->getAttr<HLSLClipPlanesAttr>()) {
if (isEntry && !SM->IsVS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "attribute clipplane only valid for VS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
isVS = true;
// The real job is done at EmitHLSLFunctionProlog where debug info is
// available. Only set shader kind here.
funcProps->shaderKind = DXIL::ShaderKind::Vertex;
}
// Pixel shader.
if (const HLSLEarlyDepthStencilAttr *Attr =
FD->getAttr<HLSLEarlyDepthStencilAttr>()) {
if (isEntry && !SM->IsPS()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute earlydepthstencil only valid for PS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
isPS = true;
funcProps->ShaderProps.PS.EarlyDepthStencil = true;
funcProps->shaderKind = DXIL::ShaderKind::Pixel;
}
if (const HLSLWaveSizeAttr *Attr = FD->getAttr<HLSLWaveSizeAttr>()) {
if (!m_pHLModule->GetShaderModel()->IsSM66Plus()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute WaveSize only valid for shader model 6.6 and higher.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
if (!isCS) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute WaveSize only valid for CS.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
if (!isEntry) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"attribute WaveSize only valid on entry point function.");
Diags.Report(Attr->getLocation(), DiagID);
return;
}
// validate that it is a power of 2 between 4 and 128
unsigned waveSize = Attr->getSize();
if (!DXIL::IsValidWaveSizeValue(waveSize)) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"WaveSize value must be between %0 and %1 and a power of 2.");
Diags.Report(Attr->getLocation(), DiagID) << DXIL::kMinWaveSize << DXIL::kMaxWaveSize;
}
funcProps->waveSize = Attr->getSize();
}
const unsigned profileAttributes = isCS + isHS + isDS + isGS + isVS + isPS + isRay + isMS + isAS;
// TODO: check this in front-end and report error.
DXASSERT(profileAttributes < 2, "profile attributes are mutual exclusive");
if (isEntry) {
switch (funcProps->shaderKind) {
case ShaderModel::Kind::Compute:
case ShaderModel::Kind::Hull:
case ShaderModel::Kind::Domain:
case ShaderModel::Kind::Geometry:
case ShaderModel::Kind::Vertex:
case ShaderModel::Kind::Pixel:
case ShaderModel::Kind::Mesh:
case ShaderModel::Kind::Amplification:
DXASSERT(funcProps->shaderKind == SM->GetKind(),
"attribute profile not match entry function profile");
break;
case ShaderModel::Kind::Library:
case ShaderModel::Kind::Invalid:
// Non-shader stage shadermodels don't have entry points.
break;
}
}
DxilFunctionAnnotation *FuncAnnotation =
m_pHLModule->AddFunctionAnnotation(F);
bool bDefaultRowMajor = m_pHLModule->GetHLOptions().bDefaultRowMajor;
// Param Info
unsigned streamIndex = 0;
unsigned inputPatchCount = 0;
unsigned outputPatchCount = 0;
unsigned ArgNo = 0;
unsigned ParmIdx = 0;
auto ArgIt = F->arg_begin();
if (const CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(FD)) {
if (MethodDecl->isInstance()) {
QualType ThisTy = MethodDecl->getThisType(FD->getASTContext());
DxilParameterAnnotation ¶mAnnotation =
FuncAnnotation->GetParameterAnnotation(ArgNo++);
++ArgIt;
// Construct annoation for this pointer.
ConstructFieldAttributedAnnotation(paramAnnotation, ThisTy,
bDefaultRowMajor);
if (MethodDecl->isConst()) {
paramAnnotation.SetParamInputQual(DxilParamInputQual::In);
} else {
paramAnnotation.SetParamInputQual(DxilParamInputQual::Inout);
}
}
}
// Ret Info
QualType retTy = FD->getReturnType();
DxilParameterAnnotation *pRetTyAnnotation = nullptr;
if (F->getReturnType()->isVoidTy() && !retTy->isVoidType()) {
// SRet.
pRetTyAnnotation = &FuncAnnotation->GetParameterAnnotation(ArgNo++);
// Save resource properties for parameters.
AddValToPropertyMap(ArgIt, retTy);
++ArgIt;
} else {
pRetTyAnnotation = &FuncAnnotation->GetRetTypeAnnotation();
}
DxilParameterAnnotation &retTyAnnotation = *pRetTyAnnotation;
// keep Undefined here, we cannot decide for struct
retTyAnnotation.SetInterpolationMode(
GetInterpMode(FD, CompType::Kind::Invalid, /*bKeepUndefined*/ true)
.GetKind());
SourceLocation retTySemanticLoc = SetSemantic(FD, retTyAnnotation);
retTyAnnotation.SetParamInputQual(DxilParamInputQual::Out);
if (isEntry) {
if (CGM.getLangOpts().EnableDX9CompatMode && retTyAnnotation.HasSemanticString()) {
RemapObsoleteSemantic(retTyAnnotation, /*isPatchConstantFunction*/ false);
}
CheckParameterAnnotation(retTySemanticLoc, retTyAnnotation,
/*isPatchConstantFunction*/ false);
}
if (isRay && !retTy->isVoidType()) {
Diags.Report(FD->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error, "return type for ray tracing shaders must be void"));
}
ConstructFieldAttributedAnnotation(retTyAnnotation, retTy, bDefaultRowMajor);
if (FD->hasAttr<HLSLPreciseAttr>())
retTyAnnotation.SetPrecise();
if (isRay) {
funcProps->ShaderProps.Ray.payloadSizeInBytes = 0;
funcProps->ShaderProps.Ray.attributeSizeInBytes = 0;
}
bool hasOutIndices = false;
bool hasOutVertices = false;
bool hasOutPrimitives = false;
bool hasInPayload = false;
bool rayShaderHaveErrors = false;
for (; ArgNo < F->arg_size(); ++ArgNo, ++ParmIdx, ++ArgIt) {
DxilParameterAnnotation ¶mAnnotation =
FuncAnnotation->GetParameterAnnotation(ArgNo);
const ParmVarDecl *parmDecl = FD->getParamDecl(ParmIdx);
QualType fieldTy = parmDecl->getType();
// Save object properties for parameters.
AddValToPropertyMap(ArgIt, fieldTy);
// if parameter type is a typedef, try to desugar it first.
if (isa<TypedefType>(fieldTy.getTypePtr()))
fieldTy = fieldTy.getDesugaredType(FD->getASTContext());
ConstructFieldAttributedAnnotation(paramAnnotation, fieldTy,
bDefaultRowMajor);
if (parmDecl->hasAttr<HLSLPreciseAttr>())
paramAnnotation.SetPrecise();
// keep Undefined here, we cannot decide for struct
InterpolationMode paramIM =
GetInterpMode(parmDecl, CompType::Kind::Invalid, KeepUndefinedTrue);
paramAnnotation.SetInterpolationMode(paramIM);
SourceLocation paramSemanticLoc = SetSemantic(parmDecl, paramAnnotation);
DxilParamInputQual dxilInputQ = DxilParamInputQual::In;
if (parmDecl->hasAttr<HLSLInOutAttr>())
dxilInputQ = DxilParamInputQual::Inout;
else if (parmDecl->hasAttr<HLSLOutAttr>())
dxilInputQ = DxilParamInputQual::Out;
if (parmDecl->hasAttr<HLSLOutAttr>() && parmDecl->hasAttr<HLSLInAttr>())
dxilInputQ = DxilParamInputQual::Inout;
if (parmDecl->hasAttr<HLSLOutAttr>() && parmDecl->hasAttr<HLSLIndicesAttr>()) {
if (hasOutIndices) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"multiple out indices parameters not allowed");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(fieldTy.getCanonicalType());
if (CAT == nullptr) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"indices output is not an constant-length array");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
unsigned count = CAT->getSize().getZExtValue();
if (count > DXIL::kMaxMSOutputPrimitiveCount) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"max primitive count should not exceed %0");
Diags.Report(parmDecl->getLocation(), DiagID) << DXIL::kMaxMSOutputPrimitiveCount;
continue;
}
if (funcProps->ShaderProps.MS.maxPrimitiveCount != 0 &&
funcProps->ShaderProps.MS.maxPrimitiveCount != count) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"max primitive count mismatch");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
// Get element type.
QualType arrayEleTy = CAT->getElementType();
if (hlsl::IsHLSLVecType(arrayEleTy)) {
QualType vecEltTy = hlsl::GetHLSLVecElementType(arrayEleTy);
if (!vecEltTy->isUnsignedIntegerType() || CGM.getContext().getTypeSize(vecEltTy) != 32) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"the element of out_indices array must be uint2 for line output or uint3 for triangle output");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
unsigned vecEltCount = hlsl::GetHLSLVecSize(arrayEleTy);
if (funcProps->ShaderProps.MS.outputTopology == DXIL::MeshOutputTopology::Line && vecEltCount != 2) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"the element of out_indices array in a mesh shader whose output topology is line must be uint2");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
if (funcProps->ShaderProps.MS.outputTopology == DXIL::MeshOutputTopology::Triangle && vecEltCount != 3) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"the element of out_indices array in a mesh shader whose output topology is triangle must be uint3");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
} else {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"the element of out_indices array must be uint2 for line output or uint3 for triangle output");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
dxilInputQ = DxilParamInputQual::OutIndices;
funcProps->ShaderProps.MS.maxPrimitiveCount = count;
hasOutIndices = true;
}
if (parmDecl->hasAttr<HLSLOutAttr>() && parmDecl->hasAttr<HLSLVerticesAttr>()) {
if (hasOutVertices) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"multiple out vertices parameters not allowed");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(fieldTy.getCanonicalType());
if (CAT == nullptr) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"vertices output is not an constant-length array");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
unsigned count = CAT->getSize().getZExtValue();
if (count > DXIL::kMaxMSOutputVertexCount) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"max vertex count should not exceed %0");
Diags.Report(parmDecl->getLocation(), DiagID) << DXIL::kMaxMSOutputVertexCount;
continue;
}
dxilInputQ = DxilParamInputQual::OutVertices;
funcProps->ShaderProps.MS.maxVertexCount = count;
hasOutVertices = true;
}
if (parmDecl->hasAttr<HLSLOutAttr>() && parmDecl->hasAttr<HLSLPrimitivesAttr>()) {
if (hasOutPrimitives) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"multiple out primitives parameters not allowed");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(fieldTy.getCanonicalType());
if (CAT == nullptr) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"primitives output is not an constant-length array");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
unsigned count = CAT->getSize().getZExtValue();
if (count > DXIL::kMaxMSOutputPrimitiveCount) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"max primitive count should not exceed %0");
Diags.Report(parmDecl->getLocation(), DiagID) << DXIL::kMaxMSOutputPrimitiveCount;
continue;
}
if (funcProps->ShaderProps.MS.maxPrimitiveCount != 0 &&
funcProps->ShaderProps.MS.maxPrimitiveCount != count) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"max primitive count mismatch");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
dxilInputQ = DxilParamInputQual::OutPrimitives;
funcProps->ShaderProps.MS.maxPrimitiveCount = count;
hasOutPrimitives = true;
}
if (parmDecl->hasAttr<HLSLInAttr>() && parmDecl->hasAttr<HLSLPayloadAttr>()) {
if (hasInPayload) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"multiple in payload parameters not allowed");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
dxilInputQ = DxilParamInputQual::InPayload;
DataLayout DL(&this->TheModule);
funcProps->ShaderProps.MS.payloadSizeInBytes = DL.getTypeAllocSize(
F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
hasInPayload = true;
}
DXIL::InputPrimitive inputPrimitive = DXIL::InputPrimitive::Undefined;
if (IsHLSLOutputPatchType(parmDecl->getType())) {
outputPatchCount++;
if (dxilInputQ != DxilParamInputQual::In) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"OutputPatch should not be out/inout parameter");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
dxilInputQ = DxilParamInputQual::OutputPatch;
if (isDS)
funcProps->ShaderProps.DS.inputControlPoints =
GetHLSLOutputPatchCount(parmDecl->getType());
} else if (IsHLSLInputPatchType(parmDecl->getType())) {
inputPatchCount++;
if (dxilInputQ != DxilParamInputQual::In) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"InputPatch should not be out/inout parameter");
Diags.Report(parmDecl->getLocation(), DiagID);
continue;
}
dxilInputQ = DxilParamInputQual::InputPatch;
if (isHS) {
funcProps->ShaderProps.HS.inputControlPoints =
GetHLSLInputPatchCount(parmDecl->getType());
} else if (isGS) {
inputPrimitive = (DXIL::InputPrimitive)(
(unsigned)DXIL::InputPrimitive::ControlPointPatch1 +
GetHLSLInputPatchCount(parmDecl->getType()) - 1);
}
} else if (IsHLSLStreamOutputType(parmDecl->getType())) {
// TODO: validation this at ASTContext::getFunctionType in
// AST/ASTContext.cpp
DXASSERT(dxilInputQ == DxilParamInputQual::Inout,
"stream output parameter must be inout");
switch (streamIndex) {
case 0:
dxilInputQ = DxilParamInputQual::OutStream0;
break;
case 1:
dxilInputQ = DxilParamInputQual::OutStream1;
break;
case 2:
dxilInputQ = DxilParamInputQual::OutStream2;
break;
case 3:
default:
// TODO: validation this at ASTContext::getFunctionType in
// AST/ASTContext.cpp
DXASSERT(streamIndex == 3, "stream number out of bound");
dxilInputQ = DxilParamInputQual::OutStream3;
break;
}
DXIL::PrimitiveTopology &streamTopology =
funcProps->ShaderProps.GS.streamPrimitiveTopologies[streamIndex];
if (IsHLSLPointStreamType(parmDecl->getType()))
streamTopology = DXIL::PrimitiveTopology::PointList;
else if (IsHLSLLineStreamType(parmDecl->getType()))
streamTopology = DXIL::PrimitiveTopology::LineStrip;
else {
DXASSERT(IsHLSLTriangleStreamType(parmDecl->getType()),
"invalid StreamType");
streamTopology = DXIL::PrimitiveTopology::TriangleStrip;
}
if (streamIndex > 0) {
bool bAllPoint =
streamTopology == DXIL::PrimitiveTopology::PointList &&
funcProps->ShaderProps.GS.streamPrimitiveTopologies[0] ==
DXIL::PrimitiveTopology::PointList;
if (!bAllPoint) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "when multiple GS output streams are "
"used they must be pointlists.");
Diags.Report(FD->getLocation(), DiagID);
}
}
streamIndex++;
}
unsigned GsInputArrayDim = 0;
if (parmDecl->hasAttr<HLSLTriangleAttr>()) {
inputPrimitive = DXIL::InputPrimitive::Triangle;
GsInputArrayDim = 3;
} else if (parmDecl->hasAttr<HLSLTriangleAdjAttr>()) {
inputPrimitive = DXIL::InputPrimitive::TriangleWithAdjacency;
GsInputArrayDim = 6;
} else if (parmDecl->hasAttr<HLSLPointAttr>()) {
inputPrimitive = DXIL::InputPrimitive::Point;
GsInputArrayDim = 1;
} else if (parmDecl->hasAttr<HLSLLineAdjAttr>()) {
inputPrimitive = DXIL::InputPrimitive::LineWithAdjacency;
GsInputArrayDim = 4;
} else if (parmDecl->hasAttr<HLSLLineAttr>()) {
inputPrimitive = DXIL::InputPrimitive::Line;
GsInputArrayDim = 2;
}
if (inputPrimitive != DXIL::InputPrimitive::Undefined) {
// Set to InputPrimitive for GS.
dxilInputQ = DxilParamInputQual::InputPrimitive;
if (funcProps->ShaderProps.GS.inputPrimitive ==
DXIL::InputPrimitive::Undefined) {
funcProps->ShaderProps.GS.inputPrimitive = inputPrimitive;
} else if (funcProps->ShaderProps.GS.inputPrimitive != inputPrimitive) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "input parameter conflicts with geometry "
"specifier of previous input parameters");
Diags.Report(parmDecl->getLocation(), DiagID);
}
}
if (GsInputArrayDim != 0) {
QualType Ty = parmDecl->getType();
if (!Ty->isConstantArrayType()) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"input types for geometry shader must be constant size arrays");
Diags.Report(parmDecl->getLocation(), DiagID);
} else {
const ConstantArrayType *CAT = cast<ConstantArrayType>(Ty);
if (CAT->getSize().getLimitedValue() != GsInputArrayDim) {
StringRef primtiveNames[] = {
"invalid", // 0
"point", // 1
"line", // 2
"triangle", // 3
"lineadj", // 4
"invalid", // 5
"triangleadj", // 6
};
DXASSERT(GsInputArrayDim < llvm::array_lengthof(primtiveNames),
"Invalid array dim");
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "array dimension for %0 must be %1");
Diags.Report(parmDecl->getLocation(), DiagID)
<< primtiveNames[GsInputArrayDim] << GsInputArrayDim;
}
}
}
// Validate Ray Tracing function parameter (some validation may be pushed into front end)
if (isRay) {
switch (funcProps->shaderKind) {
case DXIL::ShaderKind::RayGeneration:
case DXIL::ShaderKind::Intersection:
// RayGeneration and Intersection shaders are not allowed to have any input parameters
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error, "parameters are not allowed for %0 shader"))
<< (funcProps->shaderKind == DXIL::ShaderKind::RayGeneration ?
"raygeneration" : "intersection");
rayShaderHaveErrors = true;
case DXIL::ShaderKind::AnyHit:
case DXIL::ShaderKind::ClosestHit:
if (0 == ArgNo && dxilInputQ != DxilParamInputQual::Inout) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"ray payload parameter must be inout"));
rayShaderHaveErrors = true;
} else if (1 == ArgNo && dxilInputQ != DxilParamInputQual::In) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"intersection attributes parameter must be in"));
rayShaderHaveErrors = true;
} else if (ArgNo > 1) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"too many parameters, expected payload and attributes parameters only."));
rayShaderHaveErrors = true;
}
if (ArgNo < 2) {
if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"payload and attribute structures must be user defined types with only numeric contents."));
rayShaderHaveErrors = true;
} else {
DataLayout DL(&this->TheModule);
unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
if (0 == ArgNo)
funcProps->ShaderProps.Ray.payloadSizeInBytes = size;
else
funcProps->ShaderProps.Ray.attributeSizeInBytes = size;
}
}
break;
case DXIL::ShaderKind::Miss:
if (ArgNo > 0) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"only one parameter (ray payload) allowed for miss shader"));
rayShaderHaveErrors = true;
} else if (dxilInputQ != DxilParamInputQual::Inout) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"ray payload parameter must be declared inout"));
rayShaderHaveErrors = true;
}
if (ArgNo < 1) {
if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"ray payload parameter must be a user defined type with only numeric contents."));
rayShaderHaveErrors = true;
} else {
DataLayout DL(&this->TheModule);
unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
funcProps->ShaderProps.Ray.payloadSizeInBytes = size;
}
}
break;
case DXIL::ShaderKind::Callable:
if (ArgNo > 0) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"only one parameter allowed for callable shader"));
rayShaderHaveErrors = true;
} else if (dxilInputQ != DxilParamInputQual::Inout) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"callable parameter must be declared inout"));
rayShaderHaveErrors = true;
}
if (ArgNo < 1) {
if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"callable parameter must be a user defined type with only numeric contents."));
rayShaderHaveErrors = true;
} else {
DataLayout DL(&this->TheModule);
unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
funcProps->ShaderProps.Ray.paramSizeInBytes = size;
}
}
break;
}
}
paramAnnotation.SetParamInputQual(dxilInputQ);
if (isEntry) {
if (CGM.getLangOpts().EnableDX9CompatMode && paramAnnotation.HasSemanticString()) {
RemapObsoleteSemantic(paramAnnotation, /*isPatchConstantFunction*/ false);
}
CheckParameterAnnotation(paramSemanticLoc, paramAnnotation,
/*isPatchConstantFunction*/ false);
}
}
if (inputPatchCount > 1) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "may only have one InputPatch parameter");
Diags.Report(FD->getLocation(), DiagID);
}
if (outputPatchCount > 1) {
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "may only have one OutputPatch parameter");
Diags.Report(FD->getLocation(), DiagID);
}
// If Shader is a ray shader that requires parameters, make sure size is non-zero
if (isRay) {
bool bNeedsAttributes = false;
bool bNeedsPayload = false;
switch (funcProps->shaderKind) {
case DXIL::ShaderKind::AnyHit:
case DXIL::ShaderKind::ClosestHit:
bNeedsAttributes = true;
case DXIL::ShaderKind::Miss:
bNeedsPayload = true;
case DXIL::ShaderKind::Callable:
if (0 == funcProps->ShaderProps.Ray.payloadSizeInBytes) {
unsigned DiagID = bNeedsPayload ?
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"shader must include inout payload structure parameter.") :
Diags.getCustomDiagID(DiagnosticsEngine::Error,
"shader must include inout parameter structure.");
Diags.Report(FD->getLocation(), DiagID);
rayShaderHaveErrors = true;
}
}
if (bNeedsAttributes &&
0 == funcProps->ShaderProps.Ray.attributeSizeInBytes) {
Diags.Report(FD->getLocation(), Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"shader must include attributes structure parameter."));
rayShaderHaveErrors = true;
}
}
// If we encountered an error during verification of RayTracing
// shader signatures, stop here. Otherwise we risk to trigger
// unhandled behaviour, i.e., DXC crashes when the payload is
// declared as matrix<float...> type.
if(rayShaderHaveErrors)
return;
// Type annotation for parameters and return type.
{
DxilTypeSystem &dxilTypeSys = m_pHLModule->GetTypeSystem();
unsigned arrayEltSize = 0;
AddTypeAnnotation(FD->getReturnType(), dxilTypeSys, arrayEltSize);
// Type annotation for this pointer.
if (const CXXMethodDecl *MFD = dyn_cast<CXXMethodDecl>(FD)) {
const CXXRecordDecl *RD = MFD->getParent();
QualType Ty = CGM.getContext().getTypeDeclType(RD);
AddTypeAnnotation(Ty, dxilTypeSys, arrayEltSize);
}
for (const ValueDecl *param : FD->params()) {
QualType Ty = param->getType();
AddTypeAnnotation(Ty, dxilTypeSys, arrayEltSize);
}
dxilTypeSys.FinishFunctionAnnotation(*FuncAnnotation);
}
// clear isExportedEntry if not exporting entry
bool isExportedEntry = profileAttributes != 0;
if (isExportedEntry) {
// use unmangled or mangled name depending on which is used for final entry function
StringRef name = isRay ? F->getName() : FD->getName();
if (!m_ExportMap.IsExported(name)) {
isExportedEntry = false;
}
}
// Only parse root signature for entry function.
if (HLSLRootSignatureAttr *RSA = FD->getAttr<HLSLRootSignatureAttr>()) {
if (isExportedEntry || isEntry)
EmitHLSLRootSignature(RSA, F, *funcProps);
}
// Only add functionProps when exist.
if (isExportedEntry || isEntry)
m_pHLModule->AddDxilFunctionProps(F, funcProps);
if (isPatchConstantFunction)
patchConstantFunctionPropsMap[F] = std::move(funcProps);
// Save F to entry map.
if (isExportedEntry) {
if (entryFunctionMap.count(FD->getName())) {
DiagnosticsEngine &Diags = CGM.getDiags();
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"redefinition of %0");
Diags.Report(FD->getLocStart(), DiagID) << FD->getName();
}
auto &Entry = entryFunctionMap[FD->getNameAsString()];
Entry.SL = FD->getLocation();
Entry.Func= F;
}
// Add target-dependent experimental function attributes
for (const HLSLExperimentalAttr *Attr : FD->specific_attrs<HLSLExperimentalAttr>()) {
F->addFnAttr(Twine("exp-", Attr->getName()).str(), Attr->getValue());
}
m_ScopeMap[F] = ScopeInfo(F);
}