[clang] [HLSL][SPIRV] Implement SV_IsFrontFace (PR #232071)
Jonathan Zakharov via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 16 14:55:37 PDT 2026
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 <jzakharov at nvidia.com>
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 at -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 at -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 at -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 at -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 at -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 at -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 <jzakharov at nvidia.com>
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 <jzakharov at nvidia.com>
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 <jzakharov at nvidia.com>
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 at -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 at -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 at -1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type (was 'float')}}
+// expected-error at -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 at -1 {{semantic 'SV_IsFrontFace' must be a scalar of bool type (was 'bool3' (aka 'vector<bool, 3>'))}}
+// expected-error at -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 <jzakharov at nvidia.com>
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)
More information about the cfe-commits
mailing list