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

Conversation

hekota
Copy link
Member

@hekota hekota commented Jul 1, 2024

Implements availability diagnostic on export functions.

For shader libraries the HLSL availability diagnostic should run on all entry points and export functions. Now that the export keyword is implemented (#96823), we can detect which functions are exported and run the diagnostic on them.

Exported functions can be nested in namespaces and in export declarations so we need to scan not just the current translation unit but also namespace and export declarations contexts.

Fixes #92073

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Jul 1, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 1, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-hlsl

Author: Helena Kotas (hekota)

Changes

Implements availability diagnostic on export functions.

For shader libraries the HLSL availability diagnostic should run on all entry points and export functions. Now that the export keyword is implemented, we can detect which functions are exported and run the diagnostic on them.

Exported functions can be nested in namespaces and in export declarations so we need to scan not just the current translation unit but also namespace and export declarations contexts.

Fixes #92073


Full diff: https://github.com/llvm/llvm-project/pull/97352.diff

4 Files Affected:

  • (modified) clang/lib/Sema/SemaHLSL.cpp (+37-21)
  • (modified) clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl (+50)
  • (modified) clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl (+32)
  • (modified) clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl (+51-1)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index eebe17a5b4bf7..d22e6e8915067 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -671,30 +671,47 @@ 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
+      if (FD->isInExportDeclContext()) {
+        SetUnknownShaderStageContext();
+        RunOnFunction(FD);
+        continue;
+      }
     }
-#endif
   }
 }
 
@@ -707,8 +724,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);
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
index 515e4c5f9df03..19b06832c2218 100644
--- a/clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
+++ b/clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl
@@ -110,6 +110,55 @@ class MyClass
   }
 };
 
+// Exported function without body
+export void exportedFunctionUnused(float f);
+
+// Exported function with body - not used
+export 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)]
@@ -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;
 }
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
index 6bd20450f8bfa..33d6e4816dda8 100644
--- a/clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
+++ b/clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl
@@ -110,6 +110,37 @@ class MyClass
   }
 };
 
+// Exported function without body - not used
+export void exportedFunctionUnused(float f);
+
+// Exported function with body - not used
+export 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)]
@@ -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;
 }
diff --git a/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
index 4c9675051e570..cea3c33080d27 100644
--- a/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
+++ b/clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl
@@ -129,6 +129,55 @@ class MyClass
   }
 };
 
+// Exported function without body - not used
+export void exportedFunctionUnused(float f);
+
+// Exported function with body - not used
+export 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() {
@@ -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;
-}
\ No newline at end of file
+}

Copy link

github-actions bot commented Jul 1, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@hekota hekota merged commit 5196a91 into llvm:main Jul 2, 2024
7 checks passed
lravenclaw pushed a commit to lravenclaw/llvm-project that referenced this pull request Jul 3, 2024
Implements availability diagnostic on `export` functions. 

For shader libraries the HLSL availability diagnostic should run on all
entry points and export functions. Now that the `export` keyword is
implemented (llvm#96823), we can detect which functions are
exported and run the diagnostic on them.

Exported functions can be nested in namespaces and in export
declarations so we need to scan not just the current translation unit
but also namespace and export declarations contexts.

Fixes llvm#92073
kbluck pushed a commit to kbluck/llvm-project that referenced this pull request Jul 6, 2024
Implements availability diagnostic on `export` functions. 

For shader libraries the HLSL availability diagnostic should run on all
entry points and export functions. Now that the `export` keyword is
implemented (llvm#96823), we can detect which functions are
exported and run the diagnostic on them.

Exported functions can be nested in namespaces and in export
declarations so we need to scan not just the current translation unit
but also namespace and export declarations contexts.

Fixes llvm#92073
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[HLSL] Enable HLSL availability diagnostic on exported library functions and add tests
4 participants