From a4e16b819584b0bff204d83dbd40cf7f1c2d0c15 Mon Sep 17 00:00:00 2001 From: Tim Corringham Date: Tue, 21 Apr 2026 11:46:39 +0100 Subject: [PATCH] Add defensive checks for declToIndex entries Check the variable is in the declToIndex map before calling reportUse() for that variable or marking it as Initialized. HLSL out parameters or local variables from template instantiations may not be in the declToIndex map in the current DeclContext, which can lead to asserts when they are enabled, or the use of unitialized memory otherwise. Fixes #5293 Fixes #8310 --- .../lib/Analysis/UninitializedValues.cpp | 29 ++++++++- .../DXC/template_uninitialized_values.hlsl | 63 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tools/clang/test/DXC/template_uninitialized_values.hlsl diff --git a/tools/clang/lib/Analysis/UninitializedValues.cpp b/tools/clang/lib/Analysis/UninitializedValues.cpp index dfd689bdf3..0bcccc7e6b 100644 --- a/tools/clang/lib/Analysis/UninitializedValues.cpp +++ b/tools/clang/lib/Analysis/UninitializedValues.cpp @@ -171,6 +171,12 @@ class CFGBlockValues { return declToIndex.getHLSLOutParams(); } // HLSL Change End - Treat `out` parameters as uninitialized values. + + // HLSL Change Begin - check the variable is in the declToIndex map + bool hasValueIndex(const VarDecl *vd) { + return declToIndex.getValueIndex(vd).hasValue(); + } + // HLSL Change End - check the variable is in the declToIndex map }; } // end anonymous namespace @@ -781,10 +787,23 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { case ClassifyRefs::Ignore: break; case ClassifyRefs::Use: - reportUse(dr, cast(dr->getDecl())); + // HLSL Change Begin - check the variable is in the declToIndex map + // before calling reportUse(). HLSL out parameters or local variables from + // template instantiations may not be mapped in the current DeclContext. + if (const VarDecl *VD = cast(dr->getDecl())) { + if (vals.hasValueIndex(VD)) + reportUse(dr, VD); + } + // HLSL Change End - check the variable is in the declToIndex map break; case ClassifyRefs::Init: - vals[cast(dr->getDecl())] = Initialized; + // HLSL Change Begin - check the variable is in the declToIndex map + // before marking it Initialized. + if (const VarDecl *VD = cast(dr->getDecl())) { + if (vals.hasValueIndex(VD)) + vals[VD] = Initialized; + } + // HLSL Change End - check the variable is in the declToIndex map break; case ClassifyRefs::SelfInit: handler.handleSelfInit(cast(dr->getDecl())); @@ -795,8 +814,12 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { if (BO->getOpcode() == BO_Assign) { FindVarResult Var = findVar(BO->getLHS()); + // HLSL Change Begin - check the variable is in the declToIndex map + // before marking it Initialized. if (const VarDecl *VD = Var.getDecl()) - vals[VD] = Initialized; + if (vals.hasValueIndex(VD)) + vals[VD] = Initialized; + // HLSL Change End - check the variable is in the declToIndex map } } diff --git a/tools/clang/test/DXC/template_uninitialized_values.hlsl b/tools/clang/test/DXC/template_uninitialized_values.hlsl new file mode 100644 index 0000000000..43d334d82c --- /dev/null +++ b/tools/clang/test/DXC/template_uninitialized_values.hlsl @@ -0,0 +1,63 @@ +// RUN: dxc -E main -T cs_6_6 -HV 2021 -Wno-unused-value %s | FileCheck %s + +// Asserts in UnitializedValues.cpp were triggered by the following code +// examples. The cause was that HLSL out parameters or local variables from +// template instantiations may not be present in the declToIndex map when the +// variable's DeclContext differs from the analysis context (common in +// template instantiations). +// The fix was to add defensive checks so that if the variable isn't tracked +// it is silently ignored. + +// CHECK: define void @main() + +template +void test(R x, out uint result) { + uint repro = 0; + result = 10; +} + +[numthreads(32, 32, 1)] void main(uint2 threadId: SV_DispatchThreadID) { + uint x; + test(10, x); +} + +template +void func2(out uint var1) +{ + uint var3; + uint var4; + uint var5; + uint var6; + uint var7; + uint var8; + uint var9; + uint var10; + uint var11; + uint var12; + uint var13; + uint var14; + uint var15; + uint var16; + uint var17; + uint var18; + uint var19; + uint var20; + uint var21; + uint var22; + uint var23; + uint var24; + uint var25; + uint var26; + uint var27; + uint var28; + uint var29; + uint var30; + uint var31; + var1; +} + +void func1() +{ + uint var33; + func2(var33); +}