https://github.com/jzakharovnv updated 
https://github.com/llvm/llvm-project/pull/232071

>From 8821ccdd30e5dd3b75caaac605d55c0b69091fc3 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <[email protected]>
Date: Wed, 14 Oct 2026 12:54:57 -0700
Subject: [PATCH 1/5] [HLSL][SPIRV] Implement SV_IsFrontFace

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 34 +++++++++++++++----
 clang/lib/Sema/SemaHLSL.cpp                   |  9 ++++-
 .../semantics/SV_IsFrontFace.ps.hlsl          | 20 +++++++++++
 .../Semantics/isfrontface.output.hlsl         |  8 +++++
 .../SemaHLSL/Semantics/isfrontface.ps.hlsl    | 31 +++++++++++++++++
 .../SemaHLSL/Semantics/isfrontface.vs.hlsl    |  7 ++++
 7 files changed, 102 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/isfrontface.output.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
 create mode 100644 clang/test/SemaHLSL/Semantics/isfrontface.vs.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 94fe19a6636f72..a9d32f7d2590be 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13796,7 +13796,7 @@ def err_hlsl_semantic_invalid_type
             "%select{scalar|scalar or vector of up to %2 components}1 of "
             "%select{16 or 32 bit integer|32 bit integer|"
             "16 or 32 bit floating-point|16 or 32 bit unsigned integer|"
-            "32 bit unsigned integer}3 type (was %4)">;
+            "32 bit unsigned integer|bool}3 type (was %4)">;
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 35a31942c3b591..f486259e171b13 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1094,15 +1094,20 @@ static Value *buildVectorInput(IRBuilder<> &B, Function 
*F, llvm::Type *Ty) {
 }
 
 static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV,
-                                      unsigned BuiltIn) {
+                                      unsigned BuiltIn, bool NeedsFlat = 
false) {
   LLVMContext &Ctx = GV->getContext();
   IRBuilder<> B(*GV->getParent());
-  MDNode *Operands = MDNode::get(
+  // Emit all decorations as a single spirv.Decorations node.
+  SmallVector<Metadata *, 2> Decorations;
+  Decorations.push_back(MDNode::get(
       Ctx,
       {ConstantAsMetadata::get(B.getInt32(/* Spirv::Decoration::BuiltIn */ 
11)),
-       ConstantAsMetadata::get(B.getInt32(BuiltIn))});
-  MDNode *Decoration = MDNode::get(Ctx, {Operands});
-  GV->addMetadata("spirv.Decorations", *Decoration);
+       ConstantAsMetadata::get(B.getInt32(BuiltIn))}));
+  if (NeedsFlat)
+    Decorations.push_back(
+        MDNode::get(Ctx, {ConstantAsMetadata::get(
+                             B.getInt32(/* Spirv::Decoration::Flat */ 14))}));
+  GV->addMetadata("spirv.Decorations", *MDNode::get(Ctx, Decorations));
 }
 
 static void addLocationDecoration(llvm::GlobalVariable *GV, unsigned Location) 
{
@@ -1137,13 +1142,14 @@ static bool inputRequiresFlatDecoration(llvm::Type *Ty) 
{
 
 static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
                                            llvm::Type *Ty, const Twine &Name,
-                                           unsigned BuiltInID) {
+                                           unsigned BuiltInID,
+                                           bool NeedsFlat = false) {
   auto *GV = new llvm::GlobalVariable(
       M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
       /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
       llvm::GlobalVariable::GeneralDynamicTLSModel,
       /* AddressSpace */ 7, /* isExternallyInitialized= */ true);
-  addSPIRVBuiltinDecoration(GV, BuiltInID);
+  addSPIRVBuiltinDecoration(GV, BuiltInID, NeedsFlat);
   GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
   return B.CreateLoad(Ty, GV);
 }
@@ -1566,6 +1572,20 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
       return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
                                       Signature);
     break;
+  case llvm::dxbc::PSV::SemanticKind::IsFrontFace:
+    assert(Stage == llvm::Triple::Pixel &&
+           "SV_IsFrontFace is in an unavailable stage and should have been "
+           "diagnosed by Sema");
+    if (CGM.getTarget().getTriple().isSPIRV())
+      // Non-float pixel inputs cannot be interpolated and must be Flat.
+      return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
+                                    Semantic->getAttrName()->getName(),
+                                    /* BuiltIn::FrontFacing */ 17,
+                                    inputRequiresFlatDecoration(Type));
+    if (CGM.getTarget().getTriple().isDXIL())
+      return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
+                                      Signature);
+    break;
   default:
     break;
   }
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 4fe7ed151e9180..24155ce76b3030 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1004,6 +1004,8 @@ static bool isVkPipelineBuiltin(const ASTContext 
&AstContext, FunctionDecl *FD,
     return true;
   case SemanticKind::InstanceID:
     return ST == llvm::Triple::Vertex && IsInput;
+  case SemanticKind::IsFrontFace:
+    return ST == llvm::Triple::Pixel && IsInput;
   default:
     return false;
   }
@@ -1381,6 +1383,12 @@ void SemaHLSL::diagnoseSemanticType(const Decl *D,
           << /* 16 or 32 bit unsigned integer / 32 bit unsigned integer */
           (IsSPIRV ? 4 : 3) << DeclaredTy;
     return;
+  case SemanticKind::IsFrontFace:
+    if (!T->isBooleanType())
+      Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
+          << A->getAttrName() << /* scalar */ 0 << 1 << /* bool */ 5
+          << DeclaredTy;
+    return;
   default:
     // Other semantics only have the general signature type restrictions.
     break;
@@ -2186,7 +2194,6 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
   case SemanticKind::PrimitiveID:
   case SemanticKind::GSInstanceID:
   case SemanticKind::SampleIndex:
-  case SemanticKind::IsFrontFace:
   case SemanticKind::Coverage:
   case SemanticKind::InnerCoverage:
   case SemanticKind::Depth:
diff --git a/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl 
b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
new file mode 100644
index 00000000000000..73e989601cb251
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefix=CHECK-DXIL
+
+// CHECK-SPIRV: @SV_IsFrontFace = external hidden thread_local addrspace(7) 
externally_initialized constant i1, !spirv.Decorations ![[#MD_0:]]
+
+// CHECK: define void @main() {{.*}} {
+float4 main(bool ff : SV_IsFrontFace) : SV_Target {
+  // CHECK-SPIRV: %[[FF:.*]] = load i1, ptr addrspace(7) @SV_IsFrontFace
+  // CHECK-SPIRV: %[[R:.*]] = call spir_func <4 x float> @_Z4mainb(i1 %[[FF]])
+
+  // CHECK-DXIL: %[[INPUT:.*]] = call i32 @llvm.dx.load.input.i32(i32 0, i32 
0, i8 0, i32 poison)
+  // CHECK-DXIL: %[[BOOL:.*]] = icmp ne i32 %[[INPUT]], 0
+  // CHECK-DXIL: %[[R:.*]] = call <4 x float> @_Z4mainb(i1 %[[BOOL]])
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+// Two decorations in one node: BuiltIn FrontFacing (17) and Flat (14).
+// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]], ![[#MD_2:]]}
+// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 17}
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{i32 14}
diff --git a/clang/test/SemaHLSL/Semantics/isfrontface.output.hlsl 
b/clang/test/SemaHLSL/Semantics/isfrontface.output.hlsl
new file mode 100644
index 00000000000000..91379e44e03512
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/isfrontface.output.hlsl
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel 
-finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header 
-x hlsl -verify -o - %s
+
+float4 main(out bool ff : SV_IsFrontFace) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' is not supported in pixel 
shader outputs}}
+  ff = true; // Written, so the parameter is used.
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
diff --git a/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl 
b/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
new file mode 100644
index 00000000000000..b2e92ff4938060
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header 
-x hlsl -verify -o - %s
+
+[shader("pixel")]
+float4 bad_uint(uint ff : SV_IsFrontFace) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'uint' (aka 'unsigned int'))}}
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+[shader("pixel")]
+float4 bad_float(float ff : SV_IsFrontFace) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'float')}}
+  return (ff.x > 0.f) ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+[shader("pixel")]
+float4 bad_vec(bool3 ff : SV_IsFrontFace) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'bool3' (aka 'vector<bool, 3>'))}}
+  return ff.x ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+[shader("pixel")]
+float4 bad_index(bool ff : SV_IsFrontFace1) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' does not allow indexing}}
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+[shader("pixel")]
+float4 ok(bool ff : SV_IsFrontFace) : SV_Target {
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
diff --git a/clang/test/SemaHLSL/Semantics/isfrontface.vs.hlsl 
b/clang/test/SemaHLSL/Semantics/isfrontface.vs.hlsl
new file mode 100644
index 00000000000000..d3474838dc6040
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/isfrontface.vs.hlsl
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex 
-finclude-default-header -x hlsl -verify -o - %s
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-vertex -finclude-default-header 
-x hlsl -verify -o - %s
+
+float4 main(bool ff : SV_IsFrontFace) : SV_Position {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' is not supported in vertex 
shader inputs}}
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}

>From b47e7374c95fbb2c7f5af8eac87df088fc793f08 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <[email protected]>
Date: Wed, 14 Oct 2026 13:14:23 -0700
Subject: [PATCH 2/5] formatting

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f486259e171b13..92dd5aae038905 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1094,7 +1094,8 @@ static Value *buildVectorInput(IRBuilder<> &B, Function 
*F, llvm::Type *Ty) {
 }
 
 static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV,
-                                      unsigned BuiltIn, bool NeedsFlat = 
false) {
+                                      unsigned BuiltIn,
+                                      bool NeedsFlat = false) {
   LLVMContext &Ctx = GV->getContext();
   IRBuilder<> B(*GV->getParent());
   // Emit all decorations as a single spirv.Decorations node.
@@ -1578,10 +1579,9 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
            "diagnosed by Sema");
     if (CGM.getTarget().getTriple().isSPIRV())
       // Non-float pixel inputs cannot be interpolated and must be Flat.
-      return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
-                                    Semantic->getAttrName()->getName(),
-                                    /* BuiltIn::FrontFacing */ 17,
-                                    inputRequiresFlatDecoration(Type));
+      return createSPIRVBuiltinLoad(
+          B, CGM.getModule(), Type, Semantic->getAttrName()->getName(),
+          /* BuiltIn::FrontFacing */ 17, inputRequiresFlatDecoration(Type));
     if (CGM.getTarget().getTriple().isDXIL())
       return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
                                       Signature);

>From 39d60ddd05ac374305be058678239873cbb64845 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <[email protected]>
Date: Wed, 14 Oct 2026 14:21:43 -0700
Subject: [PATCH 3/5] fix codegen test

---
 clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl 
b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
index 73e989601cb251..7065fa5ab50ace 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.ps.hlsl
@@ -3,7 +3,8 @@
 
 // CHECK-SPIRV: @SV_IsFrontFace = external hidden thread_local addrspace(7) 
externally_initialized constant i1, !spirv.Decorations ![[#MD_0:]]
 
-// CHECK: define void @main() {{.*}} {
+// CHECK-SPIRV: define void @main() {{.*}} {
+// CHECK-DXIL:  define void @main() {{.*}} {
 float4 main(bool ff : SV_IsFrontFace) : SV_Target {
   // CHECK-SPIRV: %[[FF:.*]] = load i1, ptr addrspace(7) @SV_IsFrontFace
   // CHECK-SPIRV: %[[R:.*]] = call spir_func <4 x float> @_Z4mainb(i1 %[[FF]])

>From 8eaf42c750a430bd21a669c465560eed1299ce8e Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <[email protected]>
Date: Fri, 16 Oct 2026 14:13:48 -0700
Subject: [PATCH 4/5] review: fixed check divergence

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++-
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 14 +++++++++----
 clang/lib/Sema/SemaHLSL.cpp                   |  9 +++++---
 .../semantics/SV_IsFrontFace.uint.ps.hlsl     | 21 +++++++++++++++++++
 .../SemaHLSL/Semantics/isfrontface.ps.hlsl    | 15 +++++++++----
 5 files changed, 50 insertions(+), 12 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.uint.ps.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a9d32f7d2590be..937d8aeabe191d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13796,7 +13796,8 @@ def err_hlsl_semantic_invalid_type
             "%select{scalar|scalar or vector of up to %2 components}1 of "
             "%select{16 or 32 bit integer|32 bit integer|"
             "16 or 32 bit floating-point|16 or 32 bit unsigned integer|"
-            "32 bit unsigned integer|bool}3 type (was %4)">;
+            "32 bit unsigned integer|bool or 32 bit unsigned integer}3 type "
+            "(was %4)">;
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 92dd5aae038905..b5ad9ac49775ea 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1577,11 +1577,17 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     assert(Stage == llvm::Triple::Pixel &&
            "SV_IsFrontFace is in an unavailable stage and should have been "
            "diagnosed by Sema");
-    if (CGM.getTarget().getTriple().isSPIRV())
+    if (CGM.getTarget().getTriple().isSPIRV()) {
+      // The FrontFacing BuiltIn is always a bool in SPIR-V, whatever type the
+      // entry point declares. Load it as bool, then convert below.
+      llvm::Type *BoolTy = B.getInt1Ty();
       // Non-float pixel inputs cannot be interpolated and must be Flat.
-      return createSPIRVBuiltinLoad(
-          B, CGM.getModule(), Type, Semantic->getAttrName()->getName(),
-          /* BuiltIn::FrontFacing */ 17, inputRequiresFlatDecoration(Type));
+      llvm::Value *FF = createSPIRVBuiltinLoad(
+          B, CGM.getModule(), BoolTy, Semantic->getAttrName()->getName(),
+          /* BuiltIn::FrontFacing */ 17, /* NeedsFlat= */ true);
+      // Convert to the declared parameter type (eg uint).
+      return Type == BoolTy ? FF : B.CreateZExt(FF, Type);
+    }
     if (CGM.getTarget().getTriple().isDXIL())
       return emitDXILUserSemanticLoad(B, Type, Decl, Semantic, Index,
                                       Signature);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 24155ce76b3030..522894ee9a2cc6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1384,10 +1384,13 @@ void SemaHLSL::diagnoseSemanticType(const Decl *D,
           (IsSPIRV ? 4 : 3) << DeclaredTy;
     return;
   case SemanticKind::IsFrontFace:
-    if (!T->isBooleanType())
+    // DXC accepts bool and uint. The SPIR-V FrontFacing variable is always
+    // bool, so CodeGen converts it to the declared type.
+    if (!T->isBooleanType() &&
+        !(T->isUnsignedIntegerType() && isIntElementOfWidth(Ctx, T, 32)))
       Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)
-          << A->getAttrName() << /* scalar */ 0 << 1 << /* bool */ 5
-          << DeclaredTy;
+          << A->getAttrName() << /* scalar */ 0 << 1
+          << /* bool or 32 bit unsigned integer */ 5 << DeclaredTy;
     return;
   default:
     // Other semantics only have the general signature type restrictions.
diff --git a/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.uint.ps.hlsl 
b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.uint.ps.hlsl
new file mode 100644
index 00000000000000..c23ff46b176708
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_IsFrontFace.uint.ps.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefix=CHECK-DXIL
+
+// The SPIR-V FrontFacing variable stays bool even when the parameter is uint.
+// CHECK-SPIRV: @SV_IsFrontFace = external hidden thread_local addrspace(7) 
externally_initialized constant i1, !spirv.Decorations ![[#MD_0:]]
+
+// CHECK-SPIRV: define void @main() {{.*}} {
+// CHECK-DXIL:  define void @main() {{.*}} {
+float4 main(uint ff : SV_IsFrontFace) : SV_Target {
+  // CHECK-SPIRV: %[[FF:.*]] = load i1, ptr addrspace(7) @SV_IsFrontFace
+  // CHECK-SPIRV: %[[U:.*]] = zext i1 %[[FF]] to i32
+  // CHECK-SPIRV: %[[R:.*]] = call spir_func <4 x float> @_Z4mainj(i32 %[[U]])
+
+  // CHECK-DXIL: %[[INPUT:.*]] = call i32 @llvm.dx.load.input.i32(i32 0, i32 
0, i8 0, i32 poison)
+  // CHECK-DXIL: %[[R:.*]] = call <4 x float> @_Z4mainj(i32 %[[INPUT]])
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]], ![[#MD_2:]]}
+// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 17}
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{i32 14}
diff --git a/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl 
b/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
index b2e92ff4938060..698185f27e095e 100644
--- a/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
+++ b/clang/test/SemaHLSL/Semantics/isfrontface.ps.hlsl
@@ -1,21 +1,28 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library 
-finclude-default-header -x hlsl -verify -o - %s
 // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header 
-x hlsl -verify -o - %s
 
+// DXC accepts both bool and uint. The SPIR-V FrontFacing variable is always
+// bool, and CodeGen converts it to the declared type.
 [shader("pixel")]
-float4 bad_uint(uint ff : SV_IsFrontFace) : SV_Target {
-// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'uint' (aka 'unsigned int'))}}
+float4 ok_uint(uint ff : SV_IsFrontFace) : SV_Target {
+  return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
+}
+
+[shader("pixel")]
+float4 bad_int(int ff : SV_IsFrontFace) : SV_Target {
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool or 
32 bit unsigned integer type (was 'int')}}
   return ff ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
 }
 
 [shader("pixel")]
 float4 bad_float(float ff : SV_IsFrontFace) : SV_Target {
-// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'float')}}
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool or 
32 bit unsigned integer type (was 'float')}}
   return (ff.x > 0.f) ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
 }
 
 [shader("pixel")]
 float4 bad_vec(bool3 ff : SV_IsFrontFace) : SV_Target {
-// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type 
(was 'bool3' (aka 'vector<bool, 3>'))}}
+// expected-error@-1 {{semantic 'SV_IsFrontFace' must be a scalar of bool or 
32 bit unsigned integer type (was 'bool3' (aka 'vector<bool, 3>'))}}
   return ff.x ? float4(1, 0, 0, 1) : float4(0, 0, 1, 1);
 }
 

>From ec6edd66b05054e13d7f480de3843d2f2c83d398 Mon Sep 17 00:00:00 2001
From: Jonathan Zakharov <[email protected]>
Date: Fri, 16 Oct 2026 14:52:40 -0700
Subject: [PATCH 5/5] [HLSL] Accept uint for SV_IsFrontFace, convert from bool
 FrontFacing in SPIR-V

---
 clang/lib/Sema/SemaHLSL.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 522894ee9a2cc6..4362c99f2f1098 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1384,8 +1384,8 @@ void SemaHLSL::diagnoseSemanticType(const Decl *D,
           (IsSPIRV ? 4 : 3) << DeclaredTy;
     return;
   case SemanticKind::IsFrontFace:
-    // DXC accepts bool and uint. The SPIR-V FrontFacing variable is always
-    // bool, so CodeGen converts it to the declared type.
+    // Accept bool or uint; SPIR-V FrontFacing BuiltIn is always a bool
+    // and CodeGen converts it to the declared type.
     if (!T->isBooleanType() &&
         !(T->isUnsignedIntegerType() && isIntElementOfWidth(Ctx, T, 32)))
       Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type)

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

Reply via email to