llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Finn Plummer (inbelic)

<details>
<summary>Changes</summary>

This change adds the equivalent verification from DXC to ensure that a local 
resource is only assigned to a unique global resource.

It corresponds to the 'local resource not guaranteed to map to unique global 
resource' error demonstrated [here](https://godbolt.org/z/cK4xh1P49).

This is orthogonal, but a pre-requisite, to resolving 
https://github.com/llvm/llvm-project/issues/165288. As it limits the kinds of 
ptr/handle usages are required to be handled during `dxil-resource-access`.

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


5 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) 
- (modified) clang/include/clang/Sema/SemaHLSL.h (+15) 
- (modified) clang/lib/Sema/SemaHLSL.cpp (+70-6) 
- (added) clang/test/SemaHLSL/local_resource_binding.hlsl (+56) 
- (added) clang/test/SemaHLSL/local_resource_binding_errs.hlsl (+41) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index eb7a608f798b8..87673e944fc0d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13431,6 +13431,9 @@ def 
err_hlsl_incomplete_resource_array_in_function_param: Error<
   "incomplete resource array in a function parameter">;
 def err_hlsl_assign_to_global_resource: Error<
   "assignment to global resource variable %0 is not allowed">;
+def err_hlsl_assigning_local_resource_is_not_unique
+    : Error<"assignment to local resource %0 is not to same unique global "
+            "resource">;
 
 def err_hlsl_push_constant_unique
     : Error<"cannot have more than one push constant block">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 99d8ed137b0c2..48daebbf61c90 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -132,6 +132,14 @@ class SemaHLSL : public SemaBase {
   bool ActOnUninitializedVarDecl(VarDecl *D);
   void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
   void CheckEntryPoint(FunctionDecl *FD);
+
+  // Return an info if the expr has common global binding info; returns
+  // std::nullopt if the expr refers to non-unique global bindings, returns
+  // nullptr if it isn't refer to any global binding, otherwise it returns
+  // a reference to the global binding info.
+  std::optional<const DeclBindingInfo *> GetGlobalBinding(Expr *E);
+
+  // Return true if everything is ok; returns false if there was an error.
   bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr,
                           SourceLocation Loc);
 
@@ -230,6 +238,13 @@ class SemaHLSL : public SemaBase {
   // List of all resource bindings
   ResourceBindings Bindings;
 
+  // Map of local resource variables to their used global resource.
+  //
+  // The Binding can be a nullptr, in which case, the variable has not be
+  // initialized or assigned to yet.
+  llvm::DenseMap<const VarDecl *, const DeclBindingInfo *>
+      LocalResourceBindings;
+
   // Global declaration collected for the $Globals default constant
   // buffer which will be created at the end of the translation unit.
   llvm::SmallVector<Decl *> DefaultCBufferDecls;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index f15b274a65a53..cd00167ceb72f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4398,7 +4398,35 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
   return false;
 }
 
-// Return true if everything is ok; returns false if there was an error.
+std::optional<const DeclBindingInfo *> SemaHLSL::GetGlobalBinding(Expr *E) {
+  if (auto *Ternary = dyn_cast<ConditionalOperator>(E)) {
+    auto TrueInfo = GetGlobalBinding(Ternary->getTrueExpr());
+    auto FalseInfo = GetGlobalBinding(Ternary->getFalseExpr());
+    if (!TrueInfo || !FalseInfo)
+      return std::nullopt;
+    if (*TrueInfo != *FalseInfo)
+      return std::nullopt;
+    return TrueInfo;
+  }
+
+  if (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
+    E = ASE->getBase()->IgnoreParenImpCasts();
+
+  if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens()))
+    if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+      const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
+      if (Ty->isArrayType())
+        Ty = Ty->getArrayElementTypeNoTypeQual();
+      if (const auto *AttrResType =
+              HLSLAttributedResourceType::findHandleTypeOnResource(Ty)) {
+        ResourceClass RC = AttrResType->getAttrs().ResourceClass;
+        return Bindings.getDeclBindingInfo(VD, RC);
+      }
+    }
+
+  return nullptr;
+}
+
 bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr,
                                   Expr *RHSExpr, SourceLocation Loc) {
   assert((LHSExpr->getType()->isHLSLResourceRecord() ||
@@ -4412,14 +4440,37 @@ bool SemaHLSL::CheckResourceBinOp(BinaryOperatorKind 
Opc, Expr *LHSExpr,
   while (auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
     E = ASE->getBase()->IgnoreParenImpCasts();
 
+  auto RHSBinding = GetGlobalBinding(RHSExpr);
+  if (!RHSBinding) {
+    SemaRef.Diag(Loc, diag::err_hlsl_assigning_local_resource_is_not_unique)
+        << RHSExpr;
+    return false;
+  }
+
   // Report error if LHS is a non-static resource declared at a global scope.
   if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
     if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
-      if (VD->hasGlobalStorage() && VD->getStorageClass() != SC_Static) {
-        // assignment to global resource is not allowed
-        SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD;
-        SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
-        return false;
+      if (VD->getStorageClass() != SC_Static) {
+        if (VD->hasGlobalStorage()) {
+          // assignment to global resource is not allowed
+          SemaRef.Diag(Loc, diag::err_hlsl_assign_to_global_resource) << VD;
+          SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
+          return false;
+        }
+
+        // Ensure assignment to a non-static local resource does not conflict
+        // with previous assignments to global resources
+        const DeclBindingInfo *LHSBinding = LocalResourceBindings[VD];
+        if (!LHSBinding) {
+          // occurs when local resource was instantiated without an expression
+          LocalResourceBindings[VD] = *RHSBinding;
+        } else if (LHSBinding != *RHSBinding) {
+          SemaRef.Diag(Loc,
+                       diag::err_hlsl_assigning_local_resource_is_not_unique)
+              << RHSExpr;
+          SemaRef.Diag(VD->getLocation(), diag::note_var_declared_here) << VD;
+          return false;
+        }
       }
     }
   }
@@ -4797,6 +4848,19 @@ bool SemaHLSL::transformInitList(const InitializedEntity 
&Entity,
 }
 
 bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) {
+  // If initializing a local resource, register the binding it is using
+  if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage())
+    if (auto *InitExpr = Init) {
+      if (auto Binding = GetGlobalBinding(InitExpr)) {
+        LocalResourceBindings.insert({VDecl, *Binding});
+      } else {
+        SemaRef.Diag(Init->getBeginLoc(),
+                     diag::err_hlsl_assigning_local_resource_is_not_unique)
+            << Init;
+        return false;
+      }
+    }
+
   const HLSLVkConstantIdAttr *ConstIdAttr =
       VDecl->getAttr<HLSLVkConstantIdAttr>();
   if (!ConstIdAttr)
diff --git a/clang/test/SemaHLSL/local_resource_binding.hlsl 
b/clang/test/SemaHLSL/local_resource_binding.hlsl
new file mode 100644
index 0000000000000..3059452b49ef0
--- /dev/null
+++ b/clang/test/SemaHLSL/local_resource_binding.hlsl
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s
+
+// expected-no-diagnostics
+
+RWBuffer<int> In : register(u0);
+RWStructuredBuffer<int> Out0 : register(u1);
+RWStructuredBuffer<int> Out1 : register(u2);
+RWStructuredBuffer<int> OutArr[];
+
+cbuffer c {
+    bool cond;
+};
+
+void no_initial_assignment(int idx) {
+    RWStructuredBuffer<int> Out;
+    if (cond) {
+        Out = Out1;
+    }
+    Out[idx] = In[idx];
+}
+
+void same_assignment(int idx) {
+    RWStructuredBuffer<int> Out = Out1;
+    if (cond) {
+        Out = Out1;
+    }
+    Out[idx] = In[idx];
+}
+
+void conditional_initialization_with_index(int idx) {
+    RWStructuredBuffer<int> Out = cond ? OutArr[0] : OutArr[1];
+    Out[idx] = In[idx];
+}
+
+void conditional_assignment_with_index(int idx) {
+    RWStructuredBuffer<int> Out;
+       if (cond) {
+               Out = OutArr[0];
+       } else {
+               Out = OutArr[1];
+       }
+    Out[idx] = In[idx];
+}
+
+void reassignment(int idx) {
+    RWStructuredBuffer<int> Out = Out0;
+       if (cond) {
+               Out = Out0;
+       }
+       Out[idx] = In[idx];
+}
+
+void conditional_result_in_same(int idx) {
+    RWStructuredBuffer<int> Out = cond ? Out0 : Out0;
+       Out[idx] = In[idx];
+}
diff --git a/clang/test/SemaHLSL/local_resource_binding_errs.hlsl 
b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl
new file mode 100644
index 0000000000000..163d3c4493ca2
--- /dev/null
+++ b/clang/test/SemaHLSL/local_resource_binding_errs.hlsl
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -verify %s
+
+RWBuffer<int> In : register(u0);
+RWStructuredBuffer<int> Out0 : register(u1);
+RWStructuredBuffer<int> Out1 : register(u2);
+RWStructuredBuffer<int> OutArr[];
+
+cbuffer c {
+    bool cond;
+};
+
+void conditional_initialization(int idx) {
+    // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' 
is not to same unique global resource}}
+    RWStructuredBuffer<int> Out = cond ? Out0 : Out1;
+    Out[idx] = In[idx];
+}
+
+void branched_assignment(int idx) {
+    RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is 
declared here}}
+    if (cond) {
+        // expected-error@+1 {{assignment to local resource 'Out1' is not to 
same unique global resource}}
+        Out = Out1;
+    }
+    Out[idx] = In[idx];
+}
+
+void branched_assignment_with_array(int idx) {
+    RWStructuredBuffer<int> Out = Out0; // expected-note {{variable 'Out' is 
declared here}}
+    if (cond) {
+        // expected-error@+1 {{assignment to local resource 'OutArr[0]' is not 
to same unique global resource}}
+        Out = OutArr[0];
+    }
+    Out[idx] = In[idx];
+}
+
+void conditional_assignment(int idx) {
+    RWStructuredBuffer<int> Out;
+    // expected-error@+1 {{assignment to local resource 'cond ? Out0 : Out1' 
is not to same unique global resource}}
+    Out = cond ? Out0 : Out1;
+    Out[idx] = In[idx];
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/176793
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to