Skip to content

[HLSL] Run availability diagnostic on exported functions #97352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 2, 2024
Merged
69 changes: 48 additions & 21 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,30 +671,58 @@ void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD,

void DiagnoseHLSLAvailability::RunOnTranslationUnit(
const TranslationUnitDecl *TU) {

// Iterate over all shader entry functions and library exports, and for those
// that have a body (definiton), run diag scan on each, setting appropriate
// shader environment context based on whether it is a shader entry function
// or an exported function.
for (auto &D : TU->decls()) {
const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
if (!FD || !FD->isThisDeclarationADefinition())
continue;
// or an exported function. Exported functions can be in namespaces and in
// export declarations so we need to scan those declaration contexts as well.
llvm::SmallVector<const DeclContext *, 8> DeclContextsToScan;
DeclContextsToScan.push_back(TU);

while (!DeclContextsToScan.empty()) {
const DeclContext *DC = DeclContextsToScan.pop_back_val();
for (auto &D : DC->decls()) {
// do not scan implicit declaration generated by the implementation
if (D->isImplicit())
continue;

// for namespace or export declaration add the context to the list to be
// scanned later
if (llvm::dyn_cast<NamespaceDecl>(D) || llvm::dyn_cast<ExportDecl>(D)) {
DeclContextsToScan.push_back(llvm::dyn_cast<DeclContext>(D));
continue;
}

// shader entry point
auto ShaderAttr = FD->getAttr<HLSLShaderAttr>();
if (ShaderAttr) {
SetShaderStageContext(ShaderAttr->getType());
RunOnFunction(FD);
continue;
}
// exported library function with definition
// FIXME: tracking issue #92073
#if 0
if (FD->getFormalLinkage() == Linkage::External) {
SetUnknownShaderStageContext();
RunOnFunction(FD);
// skip over other decls or function decls without body
const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
if (!FD || !FD->isThisDeclarationADefinition())
continue;

// shader entry point
if (HLSLShaderAttr *ShaderAttr = FD->getAttr<HLSLShaderAttr>()) {
SetShaderStageContext(ShaderAttr->getType());
RunOnFunction(FD);
continue;
}
// exported library function
// FIXME: replace this loop with external linkage check once issue #92071
// is resolved
bool isExport = FD->isInExportDeclContext();
if (!isExport) {
for (const auto *Redecl : FD->redecls()) {
if (Redecl->isInExportDeclContext()) {
isExport = true;
break;
}
}
}
if (isExport) {
SetUnknownShaderStageContext();
RunOnFunction(FD);
continue;
}
}
#endif
}
}

Expand All @@ -707,8 +735,7 @@ void DiagnoseHLSLAvailability::RunOnFunction(const FunctionDecl *FD) {
// For any CallExpr found during the traversal add it's callee to the top of
// the stack to be processed next. Functions already processed are stored in
// ScannedDecls.
const FunctionDecl *FD = DeclsToScan.back();
DeclsToScan.pop_back();
const FunctionDecl *FD = DeclsToScan.pop_back_val();

// Decl was already scanned
const unsigned ScannedStages = GetScannedStages(FD);
Expand Down
50 changes: 50 additions & 0 deletions clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,55 @@ class MyClass
}
};

// Exported function without body, not used
export void exportedFunctionUnused(float f);

// Exported function with body, without export, not used
void exportedFunctionUnused(float f) {
// expected-error@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUnused_fx_call

// API with shader-stage-specific availability in unused exported library function
// - no errors expected because the actual shader stage this function
// will be used in not known at this time
float B = fy(f);
float C = fz(f);
}

// Exported function with body - called from main() which is a compute shader entry point
export void exportedFunctionUsed(float f) {
// expected-error@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUsed_fx_call

// expected-error@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
// expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
float B = fy(f); // #exportedFunctionUsed_fy_call

// expected-error@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
// expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
float C = fz(f); // #exportedFunctionUsed_fz_call
}

namespace A {
namespace B {
export {
void exportedFunctionInNS(float x) {
// expected-error@#exportedFunctionInNS_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(x); // #exportedFunctionInNS_fx_call

// API with shader-stage-specific availability in exported library function
// - no errors expected because the actual shader stage this function
// will be used in not known at this time
float B = fy(x);
float C = fz(x);
}
}
}
}

// Shader entry point without body
[shader("compute")]
[numthreads(4,1,1)]
Expand All @@ -126,5 +175,6 @@ float main() {
float c = C.makeF();
float d = test((float)1.0);
float e = test((half)1.0);
exportedFunctionUsed(1.0f);
return a * b * c;
}
32 changes: 32 additions & 0 deletions clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ class MyClass
}
};

// Exported function without body, not used
export void exportedFunctionUnused(float f);

// Exported function with body, without export, not used
void exportedFunctionUnused(float f) {
// expected-warning@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUnused_fx_call

// API with shader-stage-specific availability in unused exported library function
// - no errors expected because the actual shader stage this function
// will be used in not known at this time
float B = fy(f);
float C = fz(f);
}

// Exported function with body - called from main() which is a compute shader entry point
export void exportedFunctionUsed(float f) {
// expected-warning@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUsed_fx_call

// expected-warning@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
// expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
float B = fy(f); // #exportedFunctionUsed_fy_call

// expected-warning@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
// expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
float C = fz(f); // #exportedFunctionUsed_fz_call
}

// Shader entry point without body
[shader("compute")]
[numthreads(4,1,1)]
Expand All @@ -126,5 +157,6 @@ float main() {
float c = C.makeF();
float d = test((float)1.0);
float e = test((half)1.0);
exportedFunctionUsed(1.0f);
return a * b * c;
}
52 changes: 51 additions & 1 deletion clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,55 @@ class MyClass
}
};

// Exported function without body, not used
export void exportedFunctionUnused(float f);

// Exported function with body, without export, not used
void exportedFunctionUnused(float f) {
// expected-error@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUnused_fx_call

// API with shader-stage-specific availability in unused exported library function
// - no errors expected because the actual shader stage this function
// will be used in not known at this time
float B = fy(f);
float C = fz(f);
}

// Exported function with body - called from main() which is a compute shader entry point
export void exportedFunctionUsed(float f) {
// expected-error@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(f); // #exportedFunctionUsed_fx_call

// expected-error@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
// expected-note@#fy {{'fy' has been marked as being introduced in Shader Model 6.5 in compute environment here, but the deployment target is Shader Model 6.0 compute environment}}
float B = fy(f); // #exportedFunctionUsed_fy_call

// expected-error@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
// expected-note@#fz {{'fz' has been marked as being introduced in Shader Model 6.5 in mesh environment here, but the deployment target is Shader Model 6.0 compute environment}}
float C = fz(f); // #exportedFunctionUsed_fz_call
}

namespace A {
namespace B {
export {
void exportedFunctionInNS(float x) {
// expected-error@#exportedFunctionInNS_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
// expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}}
float A = fx(x); // #exportedFunctionInNS_fx_call

// API with shader-stage-specific availability in exported library function
// - no errors expected because the actual shader stage this function
// will be used in not known at this time
float B = fy(x);
float C = fz(x);
}
}
}
}

[shader("compute")]
[numthreads(4,1,1)]
float main() {
Expand All @@ -138,5 +187,6 @@ float main() {
float c = C.makeF();
float d = test((float)1.0);
float e = test((half)1.0);
exportedFunctionUsed(1.0f);
return a * b * c;
}
}
Loading