Skip to content

Commit 5196a91

Browse files
authored
[HLSL] Run availability diagnostic on exported functions (#97352)
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
1 parent 622df0e commit 5196a91

File tree

4 files changed

+181
-22
lines changed

4 files changed

+181
-22
lines changed

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -671,30 +671,58 @@ void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD,
671671

672672
void DiagnoseHLSLAvailability::RunOnTranslationUnit(
673673
const TranslationUnitDecl *TU) {
674+
674675
// Iterate over all shader entry functions and library exports, and for those
675676
// that have a body (definiton), run diag scan on each, setting appropriate
676677
// shader environment context based on whether it is a shader entry function
677-
// or an exported function.
678-
for (auto &D : TU->decls()) {
679-
const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
680-
if (!FD || !FD->isThisDeclarationADefinition())
681-
continue;
678+
// or an exported function. Exported functions can be in namespaces and in
679+
// export declarations so we need to scan those declaration contexts as well.
680+
llvm::SmallVector<const DeclContext *, 8> DeclContextsToScan;
681+
DeclContextsToScan.push_back(TU);
682+
683+
while (!DeclContextsToScan.empty()) {
684+
const DeclContext *DC = DeclContextsToScan.pop_back_val();
685+
for (auto &D : DC->decls()) {
686+
// do not scan implicit declaration generated by the implementation
687+
if (D->isImplicit())
688+
continue;
689+
690+
// for namespace or export declaration add the context to the list to be
691+
// scanned later
692+
if (llvm::dyn_cast<NamespaceDecl>(D) || llvm::dyn_cast<ExportDecl>(D)) {
693+
DeclContextsToScan.push_back(llvm::dyn_cast<DeclContext>(D));
694+
continue;
695+
}
682696

683-
// shader entry point
684-
auto ShaderAttr = FD->getAttr<HLSLShaderAttr>();
685-
if (ShaderAttr) {
686-
SetShaderStageContext(ShaderAttr->getType());
687-
RunOnFunction(FD);
688-
continue;
689-
}
690-
// exported library function with definition
691-
// FIXME: tracking issue #92073
692-
#if 0
693-
if (FD->getFormalLinkage() == Linkage::External) {
694-
SetUnknownShaderStageContext();
695-
RunOnFunction(FD);
697+
// skip over other decls or function decls without body
698+
const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(D);
699+
if (!FD || !FD->isThisDeclarationADefinition())
700+
continue;
701+
702+
// shader entry point
703+
if (HLSLShaderAttr *ShaderAttr = FD->getAttr<HLSLShaderAttr>()) {
704+
SetShaderStageContext(ShaderAttr->getType());
705+
RunOnFunction(FD);
706+
continue;
707+
}
708+
// exported library function
709+
// FIXME: replace this loop with external linkage check once issue #92071
710+
// is resolved
711+
bool isExport = FD->isInExportDeclContext();
712+
if (!isExport) {
713+
for (const auto *Redecl : FD->redecls()) {
714+
if (Redecl->isInExportDeclContext()) {
715+
isExport = true;
716+
break;
717+
}
718+
}
719+
}
720+
if (isExport) {
721+
SetUnknownShaderStageContext();
722+
RunOnFunction(FD);
723+
continue;
724+
}
696725
}
697-
#endif
698726
}
699727
}
700728

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

713740
// Decl was already scanned
714741
const unsigned ScannedStages = GetScannedStages(FD);

clang/test/SemaHLSL/Availability/avail-diag-default-lib.hlsl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,55 @@ class MyClass
110110
}
111111
};
112112

113+
// Exported function without body, not used
114+
export void exportedFunctionUnused(float f);
115+
116+
// Exported function with body, without export, not used
117+
void exportedFunctionUnused(float f) {
118+
// expected-error@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
119+
// 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}}
120+
float A = fx(f); // #exportedFunctionUnused_fx_call
121+
122+
// API with shader-stage-specific availability in unused exported library function
123+
// - no errors expected because the actual shader stage this function
124+
// will be used in not known at this time
125+
float B = fy(f);
126+
float C = fz(f);
127+
}
128+
129+
// Exported function with body - called from main() which is a compute shader entry point
130+
export void exportedFunctionUsed(float f) {
131+
// expected-error@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
132+
// 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}}
133+
float A = fx(f); // #exportedFunctionUsed_fx_call
134+
135+
// expected-error@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
136+
// 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}}
137+
float B = fy(f); // #exportedFunctionUsed_fy_call
138+
139+
// expected-error@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
140+
// 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}}
141+
float C = fz(f); // #exportedFunctionUsed_fz_call
142+
}
143+
144+
namespace A {
145+
namespace B {
146+
export {
147+
void exportedFunctionInNS(float x) {
148+
// expected-error@#exportedFunctionInNS_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
149+
// 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}}
150+
float A = fx(x); // #exportedFunctionInNS_fx_call
151+
152+
// API with shader-stage-specific availability in exported library function
153+
// - no errors expected because the actual shader stage this function
154+
// will be used in not known at this time
155+
float B = fy(x);
156+
float C = fz(x);
157+
}
158+
}
159+
}
160+
}
161+
113162
// Shader entry point without body
114163
[shader("compute")]
115164
[numthreads(4,1,1)]
@@ -126,5 +175,6 @@ float main() {
126175
float c = C.makeF();
127176
float d = test((float)1.0);
128177
float e = test((half)1.0);
178+
exportedFunctionUsed(1.0f);
129179
return a * b * c;
130180
}

clang/test/SemaHLSL/Availability/avail-diag-relaxed-lib.hlsl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ class MyClass
110110
}
111111
};
112112

113+
// Exported function without body, not used
114+
export void exportedFunctionUnused(float f);
115+
116+
// Exported function with body, without export, not used
117+
void exportedFunctionUnused(float f) {
118+
// expected-warning@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
119+
// 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}}
120+
float A = fx(f); // #exportedFunctionUnused_fx_call
121+
122+
// API with shader-stage-specific availability in unused exported library function
123+
// - no errors expected because the actual shader stage this function
124+
// will be used in not known at this time
125+
float B = fy(f);
126+
float C = fz(f);
127+
}
128+
129+
// Exported function with body - called from main() which is a compute shader entry point
130+
export void exportedFunctionUsed(float f) {
131+
// expected-warning@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
132+
// 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}}
133+
float A = fx(f); // #exportedFunctionUsed_fx_call
134+
135+
// expected-warning@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
136+
// 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}}
137+
float B = fy(f); // #exportedFunctionUsed_fy_call
138+
139+
// expected-warning@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
140+
// 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}}
141+
float C = fz(f); // #exportedFunctionUsed_fz_call
142+
}
143+
113144
// Shader entry point without body
114145
[shader("compute")]
115146
[numthreads(4,1,1)]
@@ -126,5 +157,6 @@ float main() {
126157
float c = C.makeF();
127158
float d = test((float)1.0);
128159
float e = test((half)1.0);
160+
exportedFunctionUsed(1.0f);
129161
return a * b * c;
130162
}

clang/test/SemaHLSL/Availability/avail-diag-strict-lib.hlsl

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,55 @@ class MyClass
129129
}
130130
};
131131

132+
// Exported function without body, not used
133+
export void exportedFunctionUnused(float f);
134+
135+
// Exported function with body, without export, not used
136+
void exportedFunctionUnused(float f) {
137+
// expected-error@#exportedFunctionUnused_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
138+
// 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}}
139+
float A = fx(f); // #exportedFunctionUnused_fx_call
140+
141+
// API with shader-stage-specific availability in unused exported library function
142+
// - no errors expected because the actual shader stage this function
143+
// will be used in not known at this time
144+
float B = fy(f);
145+
float C = fz(f);
146+
}
147+
148+
// Exported function with body - called from main() which is a compute shader entry point
149+
export void exportedFunctionUsed(float f) {
150+
// expected-error@#exportedFunctionUsed_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
151+
// 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}}
152+
float A = fx(f); // #exportedFunctionUsed_fx_call
153+
154+
// expected-error@#exportedFunctionUsed_fy_call {{'fy' is only available in compute environment on Shader Model 6.5 or newer}}
155+
// 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}}
156+
float B = fy(f); // #exportedFunctionUsed_fy_call
157+
158+
// expected-error@#exportedFunctionUsed_fz_call {{'fz' is unavailable}}
159+
// 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}}
160+
float C = fz(f); // #exportedFunctionUsed_fz_call
161+
}
162+
163+
namespace A {
164+
namespace B {
165+
export {
166+
void exportedFunctionInNS(float x) {
167+
// expected-error@#exportedFunctionInNS_fx_call {{'fx' is only available on Shader Model 6.5 or newer}}
168+
// 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}}
169+
float A = fx(x); // #exportedFunctionInNS_fx_call
170+
171+
// API with shader-stage-specific availability in exported library function
172+
// - no errors expected because the actual shader stage this function
173+
// will be used in not known at this time
174+
float B = fy(x);
175+
float C = fz(x);
176+
}
177+
}
178+
}
179+
}
180+
132181
[shader("compute")]
133182
[numthreads(4,1,1)]
134183
float main() {
@@ -138,5 +187,6 @@ float main() {
138187
float c = C.makeF();
139188
float d = test((float)1.0);
140189
float e = test((half)1.0);
190+
exportedFunctionUsed(1.0f);
141191
return a * b * c;
142-
}
192+
}

0 commit comments

Comments
 (0)