https://github.com/Alexander-Johnston updated 
https://github.com/llvm/llvm-project/pull/164831

>From 658a18417d52b6ba5739dabfd9acac9808cac1c1 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Thu, 23 Oct 2025 15:12:23 +0100
Subject: [PATCH 01/12] [HLSL] Implement ddx/ddy_coarse intrinsics

---
 clang/include/clang/Basic/Builtins.td         | 12 +++
 clang/include/clang/Basic/BuiltinsSPIRVVK.td  |  2 +
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          | 18 +++++
 clang/lib/CodeGen/CGHLSLRuntime.h             |  2 +
 clang/lib/CodeGen/TargetBuiltins/SPIR.cpp     | 12 +++
 .../lib/Headers/hlsl/hlsl_intrinsic_helpers.h | 16 ++++
 clang/lib/Headers/hlsl/hlsl_intrinsics.h      | 76 +++++++++++++++++++
 clang/lib/Sema/SemaHLSL.cpp                   |  4 +-
 clang/lib/Sema/SemaSPIRV.cpp                  | 26 +++++++
 .../builtins/ddx-coarse-builtin.hlsl          | 17 +++++
 .../test/CodeGenHLSL/builtins/ddx-coarse.hlsl | 61 +++++++++++++++
 .../builtins/ddy-coarse-builtin.hlsl          | 17 +++++
 .../test/CodeGenHLSL/builtins/ddy-coarse.hlsl | 61 +++++++++++++++
 .../SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl  | 23 ++++++
 .../SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl  | 21 +++++
 .../SemaSPIRV/BuiltIns/ddx-coarse-errors.c    | 29 +++++++
 .../SemaSPIRV/BuiltIns/ddy-coarse-errors.c    | 29 +++++++
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |  2 +
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  2 +
 llvm/lib/Target/DirectX/DXIL.td               | 18 +++++
 .../DirectX/DirectXTargetTransformInfo.cpp    |  2 +
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 15 +++-
 .../test/CodeGen/DirectX/ddx_coarse-errors.ll | 15 ++++
 llvm/test/CodeGen/DirectX/ddx_coarse.ll       | 41 ++++++++++
 .../test/CodeGen/DirectX/ddy_coarse-errors.ll | 15 ++++
 llvm/test/CodeGen/DirectX/ddy_coarse.ll       | 41 ++++++++++
 .../SPIRV/hlsl-intrinsics/ddx_coarse.ll       | 43 +++++++++++
 .../SPIRV/hlsl-intrinsics/ddy_coarse.ll       | 43 +++++++++++
 28 files changed, 661 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
 create mode 100644 clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
 create mode 100644 clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c
 create mode 100644 llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
 create mode 100644 llvm/test/CodeGen/DirectX/ddx_coarse.ll
 create mode 100644 llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
 create mode 100644 llvm/test/CodeGen/DirectX/ddy_coarse.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index d4a3e34a43c53..c9c52296ec0dd 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5247,6 +5247,18 @@ def HLSLF16ToF32 : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void(...)";
 }
 
+def HLSLDdxCoarse : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_elementwise_ddx_coarse"];
+  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
+def HLSLDdyCoarse : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_elementwise_ddy_coarse"];
+  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
 // Builtins for XRay.
 def XRayCustomEvent : Builtin {
   let Spellings = ["__xray_customevent"];
diff --git a/clang/include/clang/Basic/BuiltinsSPIRVVK.td 
b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
index 5dc3c7588cd2a..96e7c7b11fdfd 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRVVK.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
@@ -12,3 +12,5 @@ include "clang/Basic/BuiltinsSPIRVBase.td"
 def reflect : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
 def faceforward : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
 def refract : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
+def ddx_coarse : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
+def ddy_coarse : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp 
b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index b6928ce7d9c44..12d9a98915ce3 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -924,6 +924,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned 
BuiltinID,
     return EmitRuntimeCall(
         Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID));
   }
+  case Builtin::BI__builtin_hlsl_elementwise_ddx_coarse: {
+    Value *Op0 = EmitScalarExpr(E->getArg(0));
+    if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+      llvm_unreachable("ddx_coarse operand must have a float representation");
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getDdxCoarseIntrinsic();
+    return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
+                                   ArrayRef<Value *>{Op0}, nullptr,
+                                   "hlsl.ddx.coarse");
+  }
+  case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse: {
+    Value *Op0 = EmitScalarExpr(E->getArg(0));
+    if (!E->getArg(0)->getType()->hasFloatingRepresentation())
+      llvm_unreachable("ddy_coarse operand must have a float representation");
+    Intrinsic::ID ID = CGM.getHLSLRuntime().getDdyCoarseIntrinsic();
+    return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
+                                   ArrayRef<Value *>{Op0}, nullptr,
+                                   "hlsl.ddy.coarse");
+  }
   case Builtin::BI__builtin_get_spirv_spec_constant_bool:
   case Builtin::BI__builtin_get_spirv_spec_constant_short:
   case Builtin::BI__builtin_get_spirv_spec_constant_ushort:
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 9d31714ab8606..2b1ae7f5a6527 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -136,6 +136,8 @@ class CGHLSLRuntime {
   GENERATE_HLSL_INTRINSIC_FUNCTION(GroupMemoryBarrierWithGroupSync,
                                    group_memory_barrier_with_group_sync)
   GENERATE_HLSL_INTRINSIC_FUNCTION(GetDimensionsX, resource_getdimensions_x)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(DdxCoarse, ddx_coarse)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(DdyCoarse, ddy_coarse)
 
   
//===----------------------------------------------------------------------===//
   // End of reserved area for HLSL intrinsic getters.
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp 
b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 243aad8bf7083..12937fe8c0d10 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -151,6 +151,18 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned 
BuiltinID,
         Intrinsic::spv_global_offset,
         ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
         "spv.global.offset");
+  case SPIRV::BI__builtin_spirv_ddx_coarse:
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/getTypes().ConvertType(E->getType()),
+        Intrinsic::spv_ddx_coarse,
+        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
+        "spv.ddx.coarse");
+  case SPIRV::BI__builtin_spirv_ddy_coarse:
+    return Builder.CreateIntrinsic(
+        /*ReturnType=*/getTypes().ConvertType(E->getType()),
+        Intrinsic::spv_ddy_coarse,
+        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
+        "spv.ddy.coarse");
   }
   return nullptr;
 }
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 3d8fe7ea701a6..13dabd065ed4a 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -160,6 +160,22 @@ constexpr K firstbithigh_impl(T X) {
   return FBH;
 }
 
+template <typename T> constexpr T ddx_coarse_impl(T value) {
+#if (__has_builtin(__builtin_spirv_ddx_coarse))
+  return __builtin_spirv_ddx_coarse(value);
+#else
+  return __builtin_hlsl_elementwise_ddx_coarse(value);
+#endif
+}
+
+template <typename T> constexpr T ddy_coarse_impl(T value) {
+#if (__has_builtin(__builtin_spirv_ddy_coarse))
+  return __builtin_spirv_ddy_coarse(value);
+#else
+  return __builtin_hlsl_elementwise_ddy_coarse(value);
+#endif
+}
+
 } // namespace __detail
 } // namespace hlsl
 
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 33ed14328ee8a..aad0091f3c115 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -666,5 +666,81 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min,
   return __detail::smoothstep_vec_impl(Min, Max, X);
 }
 
+//===----------------------------------------------------------------------===//
+// ddx_coarse builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddx_coarse(T value)
+/// \brief Computes a low precision partial derivative with respect to the
+/// screen-space x-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the low
+/// prevision partial derivative of the input value.
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<half, T>::value, T>
+ddx_coarse(T value) {
+  return __detail::ddx_coarse_impl(value);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
+ddx_coarse(T value) {
+  return __detail::ddx_coarse_impl(value);
+}
+
+template <int L>
+const inline __detail::HLSL_FIXED_VECTOR<half, L>
+ddx_coarse(__detail::HLSL_FIXED_VECTOR<half, L> value) {
+  return __detail::ddx_coarse_impl(value);
+}
+
+template <int L>
+const inline __detail::HLSL_FIXED_VECTOR<float, L>
+ddx_coarse(__detail::HLSL_FIXED_VECTOR<float, L> value) {
+  return __detail::ddx_coarse_impl(value);
+}
+
+//===----------------------------------------------------------------------===//
+// ddy_coarse builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddy_coarse(T value)
+/// \brief Computes a low precision partial derivative with respect to the
+/// screen-space y-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the low
+/// prevision partial derivative of the input value.
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<half, T>::value, T>
+ddy_coarse(T value) {
+  return __detail::ddy_coarse_impl(value);
+}
+
+template <typename T>
+const inline __detail::enable_if_t<
+    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
+ddy_coarse(T value) {
+  return __detail::ddy_coarse_impl(value);
+}
+
+template <int L>
+const inline __detail::HLSL_FIXED_VECTOR<half, L>
+ddy_coarse(__detail::HLSL_FIXED_VECTOR<half, L> value) {
+  return __detail::ddy_coarse_impl(value);
+}
+
+template <int L>
+const inline __detail::HLSL_FIXED_VECTOR<float, L>
+ddy_coarse(__detail::HLSL_FIXED_VECTOR<float, L> value) {
+  return __detail::ddy_coarse_impl(value);
+}
+
 } // namespace hlsl
 #endif //_HLSL_HLSL_INTRINSICS_H_
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e95fe16e6cb6c..6c698388b58c9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3242,7 +3242,9 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned 
BuiltinID, CallExpr *TheCall) {
   case Builtin::BI__builtin_hlsl_elementwise_degrees:
   case Builtin::BI__builtin_hlsl_elementwise_radians:
   case Builtin::BI__builtin_hlsl_elementwise_rsqrt:
-  case Builtin::BI__builtin_hlsl_elementwise_frac: {
+  case Builtin::BI__builtin_hlsl_elementwise_frac:
+  case Builtin::BI__builtin_hlsl_elementwise_ddx_coarse:
+  case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse: {
     if (SemaRef.checkArgCount(TheCall, 1))
       return true;
     if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index c8ea0d09c4081..0e2bb1f3c1900 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -46,6 +46,19 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr 
*TheCall) {
   return false;
 }
 
+static bool CheckAllArgTypesAreCorrect(
+    Sema *S, CallExpr *TheCall,
+    llvm::function_ref<bool(Sema *S, SourceLocation Loc, int ArgOrdinal,
+                            clang::QualType PassedType)>
+        Check) {
+  for (unsigned I = 0; I < TheCall->getNumArgs(); ++I) {
+    Expr *Arg = TheCall->getArg(I);
+    if (Check(S, Arg->getBeginLoc(), I + 1, Arg->getType()))
+      return true;
+  }
+  return false;
+}
+
 static bool CheckAllArgTypesAreCorrect(
     Sema *S, CallExpr *TheCall,
     llvm::ArrayRef<
@@ -360,6 +373,19 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const 
TargetInfo &TI,
   case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
     return checkGenericCastToPtr(SemaRef, TheCall);
   }
+  case SPIRV::BI__builtin_spirv_ddx_coarse:
+  case SPIRV::BI__builtin_spirv_ddy_coarse: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+
+    if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
+                                   CheckFloatOrHalfRepresentation))
+      return true;
+
+    QualType RetTy = TheCall->getArg(0)->getType();
+    TheCall->setType(RetTy);
+    break;
+  }
   }
   return false;
 }
diff --git a/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
new file mode 100644
index 0000000000000..36d9d5969a59b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+// CHECK: define hidden noundef nofpclass(nan inf) half @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddx.coarse.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddx.coarse
+half test_f16_ddx_coarse(half val) {
+    return __builtin_hlsl_elementwise_ddx_coarse(val);
+}
+
+// CHECK: define hidden noundef nofpclass(nan inf) float @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddx.coarse.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddx.coarse
+float test_f32_ddx_coarse(float val) {
+    return __builtin_hlsl_elementwise_ddx_coarse(val);
+}
\ No newline at end of file
diff --git a/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
new file mode 100644
index 0000000000000..aedcb9b6e08cd
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+using hlsl::ddx_coarse;
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) half @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddx.coarse.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddx.coarse
+half test_f16_ddx_coarse(half val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x half> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <2 x half> 
@llvm.dx.ddx.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddx.coarse
+half2 test_f16_ddx_coarse2(half2 val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x half> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <3 x half> 
@llvm.dx.ddx.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddx.coarse
+half3 test_f16_ddx_coarse3(half3 val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x half> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <4 x half> 
@llvm.dx.ddx.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddx.coarse
+half4 test_f16_ddx_coarse4(half4 val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) float @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddx.coarse.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddx.coarse
+float test_f32_ddx_coarse(float val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x float> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <2 x float> 
@llvm.dx.ddx.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddx.coarse
+float2 test_f32_ddx_coarse2(float2 val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x float> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.dx.ddx.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddx.coarse
+float3 test_f32_ddx_coarse3(float3 val) {
+    return ddx_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @
+// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <4 x float> 
@llvm.dx.ddx.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddx.coarse
+float4 test_f32_ddx_coarse4(float4 val) {
+    return ddx_coarse(val);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
new file mode 100644
index 0000000000000..5f69699ff7bd6
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+// CHECK: define hidden noundef nofpclass(nan inf) half @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddy.coarse.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddy.coarse
+half test_f16_ddy_coarse(half val) {
+    return __builtin_hlsl_elementwise_ddy_coarse(val);
+}
+
+// CHECK: define hidden noundef nofpclass(nan inf) float @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddy.coarse.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddy.coarse
+float test_f32_ddy_coarse(float val) {
+    return __builtin_hlsl_elementwise_ddy_coarse(val);
+}
\ No newline at end of file
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
new file mode 100644
index 0000000000000..5428930d519ab
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK
+
+using hlsl::ddy_coarse;
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) half @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddy.coarse.f16(half %{{.*}})
+// CHECK: ret half %hlsl.ddy.coarse
+half test_f16_ddy_coarse(half val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x half> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <2 x half> 
@llvm.dx.ddy.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK: ret <2 x half> %hlsl.ddy.coarse
+half2 test_f16_ddy_coarse2(half2 val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x half> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <3 x half> 
@llvm.dx.ddy.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK: ret <3 x half> %hlsl.ddy.coarse
+half3 test_f16_ddy_coarse3(half3 val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x half> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <4 x half> 
@llvm.dx.ddy.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK: ret <4 x half> %hlsl.ddy.coarse
+half4 test_f16_ddy_coarse4(half4 val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) float @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddy.coarse.f32(float %{{.*}})
+// CHECK: ret float %hlsl.ddy.coarse
+float test_f32_ddy_coarse(float val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x float> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <2 x float> 
@llvm.dx.ddy.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK: ret <2 x float> %hlsl.ddy.coarse
+float2 test_f32_ddy_coarse2(float2 val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x float> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.dx.ddy.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK: ret <3 x float> %hlsl.ddy.coarse
+float3 test_f32_ddy_coarse3(float3 val) {
+    return ddy_coarse(val);
+}
+
+// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @
+// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <4 x float> 
@llvm.dx.ddy.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK: ret <4 x float> %hlsl.ddy.coarse
+float4 test_f32_ddy_coarse4(float4 val) {
+    return ddy_coarse(val);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
new file mode 100644
index 0000000000000..48b717a4af11f
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s 
-fnative-half-type -verify
+
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+float no_arg() {
+  return __builtin_hlsl_elementwise_ddx_coarse();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float too_many_args(float val) {
+  return __builtin_hlsl_elementwise_ddx_coarse(val, val);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float test_integer_scalar_input(int val) {
+  return __builtin_hlsl_elementwise_ddx_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+  return __builtin_hlsl_elementwise_ddx_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
new file mode 100644
index 0000000000000..c9e9d17757f25
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s 
-fnative-half-type -verify
+
+float no_arg() {
+  return __builtin_hlsl_elementwise_ddy_coarse();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float too_many_args(float val) {
+  return __builtin_hlsl_elementwise_ddy_coarse(val, val);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float test_integer_scalar_input(int val) {
+  return __builtin_hlsl_elementwise_ddy_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+  return __builtin_hlsl_elementwise_ddy_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
+}
\ No newline at end of file
diff --git a/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
new file mode 100644
index 0000000000000..d4f37100f6ce2
--- /dev/null
+++ b/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
@@ -0,0 +1,29 @@
+/// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify
+
+typedef _Float16 half;
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+float no_arg() {
+  return __builtin_spirv_ddx_coarse();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float too_many_args(float val) {
+  return __builtin_spirv_ddx_coarse(val, val);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float mismatched_return(float2 val) {
+  return __builtin_spirv_ddx_coarse(val);
+  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
+}
+
+float test_integer_scalar_input(int val) {
+  return __builtin_spirv_ddx_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+  return __builtin_spirv_ddx_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
+}
\ No newline at end of file
diff --git a/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c
new file mode 100644
index 0000000000000..fc8c32f998ffd
--- /dev/null
+++ b/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c
@@ -0,0 +1,29 @@
+/// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify
+
+typedef _Float16 half;
+typedef float float2 __attribute__((ext_vector_type(2)));
+
+float no_arg() {
+  return __builtin_spirv_ddy_coarse();
+  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
+}
+
+float too_many_args(float val) {
+  return __builtin_spirv_ddy_coarse(val, val);
+  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
+}
+
+float mismatched_return(float2 val) {
+  return __builtin_spirv_ddy_coarse(val);
+  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
+}
+
+float test_integer_scalar_input(int val) {
+  return __builtin_spirv_ddy_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
+}
+
+double test_double_scalar_input(double val) {
+  return __builtin_spirv_ddy_coarse(val);
+  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
+}
\ No newline at end of file
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index d7db935ee07f1..5a4cc776b26a5 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -170,6 +170,8 @@ def int_dx_splitdouble : 
DefaultAttrsIntrinsic<[llvm_anyint_ty, LLVMMatchType<0>
     [LLVMScalarOrSameVectorWidth<0, llvm_double_ty>], [IntrNoMem]>;
 def int_dx_radians : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
 def int_dx_discard : DefaultAttrsIntrinsic<[], [llvm_i1_ty], []>;
+def int_dx_ddx_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
+def int_dx_ddy_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
 def int_dx_firstbituhigh : 
DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], 
[llvm_anyint_ty], [IntrNoMem]>;
 def int_dx_firstbitshigh : 
DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], 
[llvm_anyint_ty], [IntrNoMem]>;
 def int_dx_firstbitlow : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, 
llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td 
b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index f39c6cda2c579..2f7c25550a0cc 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -134,6 +134,8 @@ def int_spv_rsqrt : 
DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
   def int_spv_group_memory_barrier_with_group_sync
       : DefaultAttrsIntrinsic<[], [], [IntrConvergent]>;
   def int_spv_discard : DefaultAttrsIntrinsic<[], [], []>;
+  def int_spv_ddx_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
+  def int_spv_ddy_coarse : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_sclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
   def int_spv_nclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], 
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 67437f6969b27..8b2866260e9c9 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -930,6 +930,24 @@ def Discard : DXILOp<82, discard> {
   let stages = [Stages<DXIL1_0, [pixel]>];
 }
 
+def DerivCoarseX : DXILOp<83, unary> {
+  let Doc = "computes the rate of change per stamp in x direction";
+  let intrinsics = [IntrinSelect<int_dx_ddx_coarse>];
+  let arguments = [OverloadTy];
+  let result = OverloadTy;
+  let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy]>];
+  let stages = [Stages<DXIL1_0, [library, pixel]>];
+}
+
+def DerivCoarseY : DXILOp<84, unary> {
+  let Doc = "computes the rate of change per stamp in y direction";
+  let intrinsics = [IntrinSelect<int_dx_ddy_coarse>];
+  let arguments = [OverloadTy];
+  let result = OverloadTy;
+  let overloads = [Overloads<DXIL1_0, [HalfTy, FloatTy]>];
+  let stages = [Stages<DXIL1_0, [library, pixel]>];
+}
+
 def ThreadId : DXILOp<93, threadId> {
   let Doc = "Reads the thread ID";
   let intrinsics = [IntrinSelect<int_dx_thread_id>];
diff --git a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp 
b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
index 6cacbf6564db2..a755dd522969d 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp
@@ -64,6 +64,8 @@ bool DirectXTTIImpl::isTargetIntrinsicTriviallyScalarizable(
   case Intrinsic::dx_wave_reduce_usum:
   case Intrinsic::dx_imad:
   case Intrinsic::dx_umad:
+  case Intrinsic::dx_ddx_coarse:
+  case Intrinsic::dx_ddy_coarse:
     return true;
   default:
     return false;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index fc87288a4a212..ea87edaeef6da 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3528,7 +3528,20 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register 
ResVReg,
   case Intrinsic::spv_unpackhalf2x16: {
     return selectExtInst(ResVReg, ResType, I, GL::UnpackHalf2x16);
   }
-
+  case Intrinsic::spv_ddx_coarse: {
+    return BuildMI(*I.getParent(), I, I.getDebugLoc(),
+                   TII.get(SPIRV::OpDPdxCoarse))
+        .addDef(ResVReg)
+        .addUse(GR.getSPIRVTypeID(ResType))
+        .addUse(I.getOperand(2).getReg());
+  }
+  case Intrinsic::spv_ddy_coarse: {
+    return BuildMI(*I.getParent(), I, I.getDebugLoc(),
+                   TII.get(SPIRV::OpDPdyCoarse))
+        .addDef(ResVReg)
+        .addUse(GR.getSPIRVTypeID(ResType))
+        .addUse(I.getOperand(2).getReg());
+  }
   default: {
     std::string DiagMsg;
     raw_string_ostream OS(DiagMsg);
diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll 
b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
new file mode 100644
index 0000000000000..2cf0d1db27efe
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
@@ -0,0 +1,15 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 
2>&1 | FileCheck %s
+
+; DXIL operation ddx.coarse does not support double overload type
+; CHECK: in function ddx.coarse
+; CHECK-SAME: Cannot create DerivCoarseX operation: Invalid overload type
+
+; Function Attrs: noinline nounwind optnone
+define noundef double @ddx.coarse_double(double noundef %a) #0 {
+entry:
+  %a.addr = alloca double, align 8
+  store double %a, ptr %a.addr, align 8
+  %0 = load double, ptr %a.addr, align 8
+  %dx.ddx.coarse = call double @llvm.dx.ddx.coarse.f64(double %0)
+  ret double %dx.ddx.coarse
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
new file mode 100644
index 0000000000000..bb4512a7a4341
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
@@ -0,0 +1,41 @@
+; RUN: opt -S  -scalarizer -dxil-op-lower 
-mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; Make sure dxil operation function calls for fwidth are generated for 
half/float and matching vectors
+
+define noundef half @deriv_coarse_x_half(half noundef %a) {
+; CHECK: call half @dx.op.unary.f16(i32 83, half %{{.*}})
+entry:
+  %dx.ddx.coarse = call half @llvm.dx.ddx.coarse.f16(half %a)
+  ret half %dx.ddx.coarse
+}
+
+define noundef float @deriv_coarse_x_float(float noundef %a) {
+; CHECK: call float @dx.op.unary.f32(i32 83, float %{{.*}})
+entry:
+  %dx.ddx.coarse = call float @llvm.dx.ddx.coarse.f32(float %a)
+  ret float %dx.ddx.coarse
+}
+
+define noundef <4 x float> @deriv_coarse_x_float4(<4 x float> noundef %a) {
+; CHECK: [[ee0:%.*]] = extractelement <4 x float> %a, i64 0
+; CHECK: [[ie0:%.*]] = call float @dx.op.unary.f32(i32 83, float [[ee0]])
+; CHECK: [[ee1:%.*]] = extractelement <4 x float> %a, i64 1
+; CHECK: [[ie1:%.*]] = call float @dx.op.unary.f32(i32 83, float [[ee1]])
+; CHECK: [[ee2:%.*]] = extractelement <4 x float> %a, i64 2
+; CHECK: [[ie2:%.*]] = call float @dx.op.unary.f32(i32 83, float [[ee2]])
+; CHECK: [[ee3:%.*]] = extractelement <4 x float> %a, i64 3
+; CHECK: [[ie3:%.*]] = call float @dx.op.unary.f32(i32 83, float [[ee3]])
+; CHECK: insertelement <4 x float> poison, float [[ie0]], i64 0
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie1]], i64 1
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie2]], i64 2
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie3]], i64 3
+; CHECK: ret <4 x float> %{{.*}}
+entry:
+  %dx.ddx.coarse = call <4 x float> @llvm.dx.ddx.coarse.v4f32(<4 x float> %a)
+  ret <4 x float> %dx.ddx.coarse
+}
+
+declare half @llvm.dx.ddx.coarse.f16(half)
+declare float @llvm.dx.ddx.coarse.f32(float)
+declare <4 x float> @llvm.dx.ddx.coarse.v4f32(<4 x float>)
+
diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll 
b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
new file mode 100644
index 0000000000000..400969f21713c
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
@@ -0,0 +1,15 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 
2>&1 | FileCheck %s
+
+; DXIL operation ddy.coarse does not support double overload type
+; CHECK: in function ddy.coarse
+; CHECK-SAME: Cannot create DerivCoarseY operation: Invalid overload type
+
+; Function Attrs: noinline nounwind optnone
+define noundef double @ddy.coarse_double(double noundef %a) #0 {
+entry:
+  %a.addr = alloca double, align 8
+  store double %a, ptr %a.addr, align 8
+  %0 = load double, ptr %a.addr, align 8
+  %dx.ddy.coarse = call double @llvm.dx.ddy.coarse.f64(double %0)
+  ret double %dx.ddy.coarse
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
new file mode 100644
index 0000000000000..7321fc1a80975
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
@@ -0,0 +1,41 @@
+; RUN: opt -S  -scalarizer -dxil-op-lower 
-mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; Make sure dxil operation function calls for fwidth are generated for 
half/float and matching vectors
+
+define noundef half @deriv_coarse_y_half(half noundef %a) {
+; CHECK: call half @dx.op.unary.f16(i32 84, half %{{.*}})
+entry:
+  %dx.ddy.coarse = call half @llvm.dx.ddy.coarse.f16(half %a)
+  ret half %dx.ddy.coarse
+}
+
+define noundef float @deriv_coarse_y_float(float noundef %a) {
+; CHECK: call float @dx.op.unary.f32(i32 84, float %{{.*}})
+entry:
+  %dx.ddy.coarse = call float @llvm.dx.ddy.coarse.f32(float %a)
+  ret float %dx.ddy.coarse
+}
+
+define noundef <4 x float> @deriv_coarse_y_float4(<4 x float> noundef %a) {
+; CHECK: [[ee0:%.*]] = extractelement <4 x float> %a, i64 0
+; CHECK: [[ie0:%.*]] = call float @dx.op.unary.f32(i32 84, float [[ee0]])
+; CHECK: [[ee1:%.*]] = extractelement <4 x float> %a, i64 1
+; CHECK: [[ie1:%.*]] = call float @dx.op.unary.f32(i32 84, float [[ee1]])
+; CHECK: [[ee2:%.*]] = extractelement <4 x float> %a, i64 2
+; CHECK: [[ie2:%.*]] = call float @dx.op.unary.f32(i32 84, float [[ee2]])
+; CHECK: [[ee3:%.*]] = extractelement <4 x float> %a, i64 3
+; CHECK: [[ie3:%.*]] = call float @dx.op.unary.f32(i32 84, float [[ee3]])
+; CHECK: insertelement <4 x float> poison, float [[ie0]], i64 0
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie1]], i64 1
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie2]], i64 2
+; CHECK: insertelement <4 x float> %{{.*}}, float [[ie3]], i64 3
+; CHECK: ret <4 x float> %{{.*}}
+entry:
+  %dx.ddy.coarse = call <4 x float> @llvm.dx.ddy.coarse.v4f32(<4 x float> %a)
+  ret <4 x float> %dx.ddy.coarse
+}
+
+declare half @llvm.dx.ddy.coarse.f16(half)
+declare float @llvm.dx.ddy.coarse.f32(float)
+declare <4 x float> @llvm.dx.ddy.coarse.v4f32(<4 x float>)
+
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
new file mode 100644
index 0000000000000..019c7b75ffe66
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
@@ -0,0 +1,43 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+
+define noundef float @ddx_coarse_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdxCoarse %[[#float_32]] %[[#float_32_arg]]
+  %elt.ddx.coarse = call float @llvm.spv.ddx.coarse.f32(float %a)
+  ret float %elt.ddx.coarse
+}
+
+define noundef half @ddx_coarse_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#]] = OpDPdxCoarse %[[#float_16]] %[[#float_16_arg]]
+  %elt.ddx.coarse = call half @llvm.spv.ddx.coarse.f16(half %a)
+  ret half %elt.ddx.coarse
+}
+
+define noundef <4 x float> @ddx_coarse_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdxCoarse %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+  %elt.ddx.coarse = call <4 x float> @llvm.spv.ddx.coarse.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.ddx.coarse
+}
+
+define noundef <4 x half> @ddx_coarse_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#]] = OpDPdxCoarse %[[#vec4_float_16]] %[[#vec4_float_16_arg]]
+  %elt.ddx.coarse = call <4 x half> @llvm.spv.ddx.coarse.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.ddx.coarse
+}
+
+declare float @llvm.spv.ddx.coarse.f32(float)
+declare half @llvm.spv.ddx.coarse.f16(half)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
new file mode 100644
index 0000000000000..87e03f55c260b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
@@ -0,0 +1,43 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+
+define noundef float @ddy_coarse_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpDPdyCoarse %[[#float_32]] %[[#float_32_arg]]
+  %elt.ddy.coarse = call float @llvm.spv.ddy.coarse.f32(float %a)
+  ret float %elt.ddy.coarse
+}
+
+define noundef half @ddy_coarse_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#]] = OpDPdyCoarse %[[#float_16]] %[[#float_16_arg]]
+  %elt.ddy.coarse = call half @llvm.spv.ddy.coarse.f16(half %a)
+  ret half %elt.ddy.coarse
+}
+
+define noundef <4 x float> @ddy_coarse_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpDPdyCoarse %[[#vec4_float_32]] %[[#vec4_float_32_arg]]
+  %elt.ddy.coarse = call <4 x float> @llvm.spv.ddy.coarse.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.ddy.coarse
+}
+
+define noundef <4 x half> @ddy_coarse_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#]] = OpDPdyCoarse %[[#vec4_float_16]] %[[#vec4_float_16_arg]]
+  %elt.ddy.coarse = call <4 x half> @llvm.spv.ddy.coarse.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.ddy.coarse
+}
+
+declare float @llvm.spv.ddy.coarse.f32(float)
+declare half @llvm.spv.ddy.coarse.f16(half)
\ No newline at end of file

>From 236c7f7445f810da282d29b0fc717bba43b9e731 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Thu, 23 Oct 2025 16:49:28 +0100
Subject: [PATCH 02/12] Revert unneeded SPIR-V target changes

---
 clang/include/clang/Basic/BuiltinsSPIRVVK.td  |  2 --
 clang/lib/CodeGen/TargetBuiltins/SPIR.cpp     | 12 --------
 clang/lib/Sema/SemaSPIRV.cpp                  | 26 -----------------
 .../SemaSPIRV/BuiltIns/ddx-coarse-errors.c    | 29 -------------------
 .../SemaSPIRV/BuiltIns/ddy-coarse-errors.c    | 29 -------------------
 5 files changed, 98 deletions(-)
 delete mode 100644 clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
 delete mode 100644 clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c

diff --git a/clang/include/clang/Basic/BuiltinsSPIRVVK.td 
b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
index 96e7c7b11fdfd..5dc3c7588cd2a 100644
--- a/clang/include/clang/Basic/BuiltinsSPIRVVK.td
+++ b/clang/include/clang/Basic/BuiltinsSPIRVVK.td
@@ -12,5 +12,3 @@ include "clang/Basic/BuiltinsSPIRVBase.td"
 def reflect : SPIRVBuiltin<"void(...)", [NoThrow, Const]>;
 def faceforward : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
 def refract : SPIRVBuiltin<"void(...)", [NoThrow, Const, CustomTypeChecking]>;
-def ddx_coarse : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
-def ddy_coarse : SPIRVBuiltin<"void(...)", [NoThrow, Const, 
CustomTypeChecking]>;
diff --git a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp 
b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
index 12937fe8c0d10..243aad8bf7083 100644
--- a/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
@@ -151,18 +151,6 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned 
BuiltinID,
         Intrinsic::spv_global_offset,
         ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
         "spv.global.offset");
-  case SPIRV::BI__builtin_spirv_ddx_coarse:
-    return Builder.CreateIntrinsic(
-        /*ReturnType=*/getTypes().ConvertType(E->getType()),
-        Intrinsic::spv_ddx_coarse,
-        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
-        "spv.ddx.coarse");
-  case SPIRV::BI__builtin_spirv_ddy_coarse:
-    return Builder.CreateIntrinsic(
-        /*ReturnType=*/getTypes().ConvertType(E->getType()),
-        Intrinsic::spv_ddy_coarse,
-        ArrayRef<Value *>{EmitScalarExpr(E->getArg(0))}, nullptr,
-        "spv.ddy.coarse");
   }
   return nullptr;
 }
diff --git a/clang/lib/Sema/SemaSPIRV.cpp b/clang/lib/Sema/SemaSPIRV.cpp
index 0e2bb1f3c1900..c8ea0d09c4081 100644
--- a/clang/lib/Sema/SemaSPIRV.cpp
+++ b/clang/lib/Sema/SemaSPIRV.cpp
@@ -46,19 +46,6 @@ static bool CheckAllArgsHaveSameType(Sema *S, CallExpr 
*TheCall) {
   return false;
 }
 
-static bool CheckAllArgTypesAreCorrect(
-    Sema *S, CallExpr *TheCall,
-    llvm::function_ref<bool(Sema *S, SourceLocation Loc, int ArgOrdinal,
-                            clang::QualType PassedType)>
-        Check) {
-  for (unsigned I = 0; I < TheCall->getNumArgs(); ++I) {
-    Expr *Arg = TheCall->getArg(I);
-    if (Check(S, Arg->getBeginLoc(), I + 1, Arg->getType()))
-      return true;
-  }
-  return false;
-}
-
 static bool CheckAllArgTypesAreCorrect(
     Sema *S, CallExpr *TheCall,
     llvm::ArrayRef<
@@ -373,19 +360,6 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(const 
TargetInfo &TI,
   case SPIRV::BI__builtin_spirv_generic_cast_to_ptr_explicit: {
     return checkGenericCastToPtr(SemaRef, TheCall);
   }
-  case SPIRV::BI__builtin_spirv_ddx_coarse:
-  case SPIRV::BI__builtin_spirv_ddy_coarse: {
-    if (SemaRef.checkArgCount(TheCall, 1))
-      return true;
-
-    if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
-                                   CheckFloatOrHalfRepresentation))
-      return true;
-
-    QualType RetTy = TheCall->getArg(0)->getType();
-    TheCall->setType(RetTy);
-    break;
-  }
   }
   return false;
 }
diff --git a/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
deleted file mode 100644
index d4f37100f6ce2..0000000000000
--- a/clang/test/SemaSPIRV/BuiltIns/ddx-coarse-errors.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify
-
-typedef _Float16 half;
-typedef float float2 __attribute__((ext_vector_type(2)));
-
-float no_arg() {
-  return __builtin_spirv_ddx_coarse();
-  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
-}
-
-float too_many_args(float val) {
-  return __builtin_spirv_ddx_coarse(val, val);
-  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
-}
-
-float mismatched_return(float2 val) {
-  return __builtin_spirv_ddx_coarse(val);
-  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
-}
-
-float test_integer_scalar_input(int val) {
-  return __builtin_spirv_ddx_coarse(val);
-  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
-}
-
-double test_double_scalar_input(double val) {
-  return __builtin_spirv_ddx_coarse(val);
-  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
-}
\ No newline at end of file
diff --git a/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c 
b/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c
deleted file mode 100644
index fc8c32f998ffd..0000000000000
--- a/clang/test/SemaSPIRV/BuiltIns/ddy-coarse-errors.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/// RUN: %clang_cc1 %s -triple spirv-pc-vulkan-compute -verify
-
-typedef _Float16 half;
-typedef float float2 __attribute__((ext_vector_type(2)));
-
-float no_arg() {
-  return __builtin_spirv_ddy_coarse();
-  // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
-}
-
-float too_many_args(float val) {
-  return __builtin_spirv_ddy_coarse(val, val);
-  // expected-error@-1 {{too many arguments to function call, expected 1, have 
2}}
-}
-
-float mismatched_return(float2 val) {
-  return __builtin_spirv_ddy_coarse(val);
-  // expected-error@-1 {{returning 'float2' (vector of 2 'float' values) from 
a function with incompatible result type 'float'}}
-}
-
-float test_integer_scalar_input(int val) {
-  return __builtin_spirv_ddy_coarse(val);
-  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'int')}}
-}
-
-double test_double_scalar_input(double val) {
-  return __builtin_spirv_ddy_coarse(val);
-  // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
-}
\ No newline at end of file

>From 9fdda3505efbb2f57c7ed2765b337b6bcff5c75a Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Thu, 23 Oct 2025 17:59:54 +0100
Subject: [PATCH 03/12] Change hlsl ddx/y_coarse intrinsic to alias

---
 .../lib/Headers/hlsl/hlsl_alias_intrinsics.h  | 68 +++++++++++++++++
 .../lib/Headers/hlsl/hlsl_intrinsic_helpers.h | 16 ----
 clang/lib/Headers/hlsl/hlsl_intrinsics.h      | 76 -------------------
 3 files changed, 68 insertions(+), 92 deletions(-)

diff --git a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
index 2e2703de18cb1..38b95ee90736a 100644
--- a/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
@@ -2946,5 +2946,73 @@ float4 radians(float4);
 _HLSL_BUILTIN_ALIAS(__builtin_hlsl_group_memory_barrier_with_group_sync)
 __attribute__((convergent)) void GroupMemoryBarrierWithGroupSync(void);
 
+//===----------------------------------------------------------------------===//
+// ddx_coarse builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddx_coarse(T value)
+/// \brief Computes a low precision partial derivative with respect to the
+/// screen-space x-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the low
+/// prevision partial derivative of the input value.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+half ddx_coarse(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+half2 ddx_coarse(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+half3 ddx_coarse(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+half4 ddx_coarse(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+float ddx_coarse(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+float2 ddx_coarse(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+float3 ddx_coarse(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddx_coarse)
+float4 ddx_coarse(float4);
+
+//===----------------------------------------------------------------------===//
+// ddy_coarse builtin
+//===----------------------------------------------------------------------===//
+
+/// \fn T ddy_coarse(T value)
+/// \brief Computes a low precision partial derivative with respect to the
+/// screen-space y-coordinate.
+/// \param value The input value.
+///
+/// The return value is a floating point scalar or vector containing the low
+/// prevision partial derivative of the input value.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+half ddy_coarse(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+half2 ddy_coarse(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+half3 ddy_coarse(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+half4 ddy_coarse(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+float ddy_coarse(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+float2 ddy_coarse(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+float3 ddy_coarse(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_ddy_coarse)
+float4 ddy_coarse(float4);
+
 } // namespace hlsl
 #endif //_HLSL_HLSL_ALIAS_INTRINSICS_H_
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
index 13dabd065ed4a..3d8fe7ea701a6 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
@@ -160,22 +160,6 @@ constexpr K firstbithigh_impl(T X) {
   return FBH;
 }
 
-template <typename T> constexpr T ddx_coarse_impl(T value) {
-#if (__has_builtin(__builtin_spirv_ddx_coarse))
-  return __builtin_spirv_ddx_coarse(value);
-#else
-  return __builtin_hlsl_elementwise_ddx_coarse(value);
-#endif
-}
-
-template <typename T> constexpr T ddy_coarse_impl(T value) {
-#if (__has_builtin(__builtin_spirv_ddy_coarse))
-  return __builtin_spirv_ddy_coarse(value);
-#else
-  return __builtin_hlsl_elementwise_ddy_coarse(value);
-#endif
-}
-
 } // namespace __detail
 } // namespace hlsl
 
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h 
b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index aad0091f3c115..33ed14328ee8a 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -666,81 +666,5 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min,
   return __detail::smoothstep_vec_impl(Min, Max, X);
 }
 
-//===----------------------------------------------------------------------===//
-// ddx_coarse builtin
-//===----------------------------------------------------------------------===//
-
-/// \fn T ddx_coarse(T value)
-/// \brief Computes a low precision partial derivative with respect to the
-/// screen-space x-coordinate.
-/// \param value The input value.
-///
-/// The return value is a floating point scalar or vector containing the low
-/// prevision partial derivative of the input value.
-
-template <typename T>
-const inline __detail::enable_if_t<
-    __detail::is_arithmetic<T>::Value && __detail::is_same<half, T>::value, T>
-ddx_coarse(T value) {
-  return __detail::ddx_coarse_impl(value);
-}
-
-template <typename T>
-const inline __detail::enable_if_t<
-    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
-ddx_coarse(T value) {
-  return __detail::ddx_coarse_impl(value);
-}
-
-template <int L>
-const inline __detail::HLSL_FIXED_VECTOR<half, L>
-ddx_coarse(__detail::HLSL_FIXED_VECTOR<half, L> value) {
-  return __detail::ddx_coarse_impl(value);
-}
-
-template <int L>
-const inline __detail::HLSL_FIXED_VECTOR<float, L>
-ddx_coarse(__detail::HLSL_FIXED_VECTOR<float, L> value) {
-  return __detail::ddx_coarse_impl(value);
-}
-
-//===----------------------------------------------------------------------===//
-// ddy_coarse builtin
-//===----------------------------------------------------------------------===//
-
-/// \fn T ddy_coarse(T value)
-/// \brief Computes a low precision partial derivative with respect to the
-/// screen-space y-coordinate.
-/// \param value The input value.
-///
-/// The return value is a floating point scalar or vector containing the low
-/// prevision partial derivative of the input value.
-
-template <typename T>
-const inline __detail::enable_if_t<
-    __detail::is_arithmetic<T>::Value && __detail::is_same<half, T>::value, T>
-ddy_coarse(T value) {
-  return __detail::ddy_coarse_impl(value);
-}
-
-template <typename T>
-const inline __detail::enable_if_t<
-    __detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
-ddy_coarse(T value) {
-  return __detail::ddy_coarse_impl(value);
-}
-
-template <int L>
-const inline __detail::HLSL_FIXED_VECTOR<half, L>
-ddy_coarse(__detail::HLSL_FIXED_VECTOR<half, L> value) {
-  return __detail::ddy_coarse_impl(value);
-}
-
-template <int L>
-const inline __detail::HLSL_FIXED_VECTOR<float, L>
-ddy_coarse(__detail::HLSL_FIXED_VECTOR<float, L> value) {
-  return __detail::ddy_coarse_impl(value);
-}
-
 } // namespace hlsl
 #endif //_HLSL_HLSL_INTRINSICS_H_

>From 0890e4db85a80a9b81321cc030d0eb603a9abde4 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Thu, 23 Oct 2025 18:00:34 +0100
Subject: [PATCH 04/12] Update ddx/y_coarse tests

---
 .../builtins/ddx-coarse-builtin.hlsl          | 19 ++++--
 .../test/CodeGenHLSL/builtins/ddx-coarse.hlsl | 61 +++++++++++++------
 .../builtins/ddy-coarse-builtin.hlsl          | 19 ++++--
 .../test/CodeGenHLSL/builtins/ddy-coarse.hlsl | 61 +++++++++++++------
 .../SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl  |  2 +-
 .../SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl  |  2 +-
 llvm/test/CodeGen/DirectX/ddx_coarse.ll       |  2 +-
 llvm/test/CodeGen/DirectX/ddy_coarse.ll       |  2 +-
 8 files changed, 118 insertions(+), 50 deletions(-)

diff --git a/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
index 36d9d5969a59b..01216eefadba2 100644
--- a/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ddx-coarse-builtin.hlsl
@@ -1,17 +1,26 @@
 // RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
 // RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
 // RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-compute  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
 
-// CHECK: define hidden noundef nofpclass(nan inf) half @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddx.coarse.f16(half %{{.*}})
+// CHECK-LABEL: half @_Z19test_f16_ddx_coarseDh
+// CHECK: %hlsl.ddx.coarse = call {{.*}} half @llvm.dx.ddx.coarse.f16(half 
%{{.*}})
 // CHECK: ret half %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: half @_Z19test_f16_ddx_coarseDh
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} half 
@llvm.spv.ddx.coarse.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddx.coarse
 half test_f16_ddx_coarse(half val) {
     return __builtin_hlsl_elementwise_ddx_coarse(val);
 }
 
-// CHECK: define hidden noundef nofpclass(nan inf) float @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddx.coarse.f32(float %{{.*}})
+// CHECK-LABEL: float @_Z19test_f32_ddx_coarsef
+// CHECK: %hlsl.ddx.coarse = call {{.*}} float @llvm.dx.ddx.coarse.f32(float 
%{{.*}})
 // CHECK: ret float %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: float @_Z19test_f32_ddx_coarsef
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} float 
@llvm.spv.ddx.coarse.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddx.coarse
 float test_f32_ddx_coarse(float val) {
     return __builtin_hlsl_elementwise_ddx_coarse(val);
-}
\ No newline at end of file
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
index aedcb9b6e08cd..c200d4715629e 100644
--- a/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ddx-coarse.hlsl
@@ -1,61 +1,86 @@
 // RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
 // RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
 // RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-compute  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
 
-using hlsl::ddx_coarse;
-
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) half @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddx.coarse.f16(half %{{.*}})
+// CHECK-LABEL: half @_Z19test_f16_ddx_coarseDh
+// CHECK: %hlsl.ddx.coarse = call {{.*}} half @llvm.dx.ddx.coarse.f16(half 
%{{.*}})
 // CHECK: ret half %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: half @_Z19test_f16_ddx_coarseDh
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} half 
@llvm.spv.ddx.coarse.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddx.coarse
 half test_f16_ddx_coarse(half val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x half> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <2 x half> 
@llvm.dx.ddx.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK-LABEL: <2 x half> @_Z20test_f16_ddx_coarse2Dv2_Dh
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x half> 
@llvm.dx.ddx.coarse.v2f16(<2 x half> %{{.*}})
 // CHECK: ret <2 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <2 x half> @_Z20test_f16_ddx_coarse2Dv2_Dh
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <2 x half> 
@llvm.spv.ddx.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %hlsl.ddx.coarse
 half2 test_f16_ddx_coarse2(half2 val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x half> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <3 x half> 
@llvm.dx.ddx.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK-LABEL: <3 x half> @_Z20test_f16_ddx_coarse3Dv3_Dh
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x half> 
@llvm.dx.ddx.coarse.v3f16(<3 x half> %{{.*}})
 // CHECK: ret <3 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <3 x half> @_Z20test_f16_ddx_coarse3Dv3_Dh
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <3 x half> 
@llvm.spv.ddx.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %hlsl.ddx.coarse
 half3 test_f16_ddx_coarse3(half3 val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x half> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <4 x half> 
@llvm.dx.ddx.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK-LABEL: <4 x half> @_Z20test_f16_ddx_coarse4Dv4_Dh
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x half> 
@llvm.dx.ddx.coarse.v4f16(<4 x half> %{{.*}})
 // CHECK: ret <4 x half> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <4 x half> @_Z20test_f16_ddx_coarse4Dv4_Dh
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <4 x half> 
@llvm.spv.ddx.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %hlsl.ddx.coarse
 half4 test_f16_ddx_coarse4(half4 val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) float @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddx.coarse.f32(float %{{.*}})
+// CHECK-LABEL: float @_Z19test_f32_ddx_coarsef
+// CHECK: %hlsl.ddx.coarse = call {{.*}} float @llvm.dx.ddx.coarse.f32(float 
%{{.*}})
 // CHECK: ret float %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: float @_Z19test_f32_ddx_coarsef
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} float 
@llvm.spv.ddx.coarse.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddx.coarse
 float test_f32_ddx_coarse(float val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x float> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <2 x float> 
@llvm.dx.ddx.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK-LABEL: <2 x float> @_Z20test_f32_ddx_coarse2Dv2_f
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <2 x float> 
@llvm.dx.ddx.coarse.v2f32(<2 x float> %{{.*}})
 // CHECK: ret <2 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <2 x float> @_Z20test_f32_ddx_coarse2Dv2_f
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <2 x float> 
@llvm.spv.ddx.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %hlsl.ddx.coarse
 float2 test_f32_ddx_coarse2(float2 val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x float> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.dx.ddx.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK-LABEL: <3 x float> @_Z20test_f32_ddx_coarse3Dv3_f
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <3 x float> 
@llvm.dx.ddx.coarse.v3f32(<3 x float> %{{.*}})
 // CHECK: ret <3 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <3 x float> @_Z20test_f32_ddx_coarse3Dv3_f
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <3 x float> 
@llvm.spv.ddx.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %hlsl.ddx.coarse
 float3 test_f32_ddx_coarse3(float3 val) {
     return ddx_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @
-// CHECK: %hlsl.ddx.coarse = call reassoc nnan ninf nsz arcp afn <4 x float> 
@llvm.dx.ddx.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK-LABEL: <4 x float> @_Z20test_f32_ddx_coarse4Dv4_f
+// CHECK: %hlsl.ddx.coarse = call {{.*}} <4 x float> 
@llvm.dx.ddx.coarse.v4f32(<4 x float> %{{.*}})
 // CHECK: ret <4 x float> %hlsl.ddx.coarse
+// CHECK-LABEL-SPIRV: <4 x float> @_Z20test_f32_ddx_coarse4Dv4_f
+// CHECK-SPIRV: %hlsl.ddx.coarse = call {{.*}} <4 x float> 
@llvm.spv.ddx.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %hlsl.ddx.coarse
 float4 test_f32_ddx_coarse4(float4 val) {
     return ddx_coarse(val);
 }
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
index 5f69699ff7bd6..2967deb75031f 100644
--- a/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ddy-coarse-builtin.hlsl
@@ -1,17 +1,26 @@
 // RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
 // RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
 // RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-compute  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
 
-// CHECK: define hidden noundef nofpclass(nan inf) half @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddy.coarse.f16(half %{{.*}})
+// CHECK-LABEL: half @_Z19test_f16_ddy_coarseDh
+// CHECK: %hlsl.ddy.coarse = call {{.*}} half @llvm.dx.ddy.coarse.f16(half 
%{{.*}})
 // CHECK: ret half %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: half @_Z19test_f16_ddy_coarseDh
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} half 
@llvm.spv.ddy.coarse.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddy.coarse
 half test_f16_ddy_coarse(half val) {
     return __builtin_hlsl_elementwise_ddy_coarse(val);
 }
 
-// CHECK: define hidden noundef nofpclass(nan inf) float @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddy.coarse.f32(float %{{.*}})
+// CHECK-LABEL: float @_Z19test_f32_ddy_coarsef
+// CHECK: %hlsl.ddy.coarse = call {{.*}} float @llvm.dx.ddy.coarse.f32(float 
%{{.*}})
 // CHECK: ret float %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: float @_Z19test_f32_ddy_coarsef
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} float 
@llvm.spv.ddy.coarse.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddy.coarse
 float test_f32_ddy_coarse(float val) {
     return __builtin_hlsl_elementwise_ddy_coarse(val);
-}
\ No newline at end of file
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl 
b/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
index 5428930d519ab..faa972a1be326 100644
--- a/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ddy-coarse.hlsl
@@ -1,61 +1,86 @@
 // RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
dxil-pc-shadermodel6.3-library %s \
 // RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
 // RUN:  FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -finclude-default-header  -x hlsl  -triple 
spirv-pc-vulkan-compute  %s \
+// RUN:  -emit-llvm -disable-llvm-passes -fnative-half-type -o - | \
+// RUN:  FileCheck %s --check-prefixes=CHECK-SPIRV
 
-using hlsl::ddy_coarse;
-
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) half @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn half 
@llvm.dx.ddy.coarse.f16(half %{{.*}})
+// CHECK-LABEL: half @_Z19test_f16_ddy_coarseDh
+// CHECK: %hlsl.ddy.coarse = call {{.*}} half @llvm.dx.ddy.coarse.f16(half 
%{{.*}})
 // CHECK: ret half %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: half @_Z19test_f16_ddy_coarseDh
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} half 
@llvm.spv.ddy.coarse.f16(half %{{.*}})
+// CHECK-SPIRV: ret half %hlsl.ddy.coarse
 half test_f16_ddy_coarse(half val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x half> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <2 x half> 
@llvm.dx.ddy.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK-LABEL: <2 x half> @_Z20test_f16_ddy_coarse2Dv2_Dh
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x half> 
@llvm.dx.ddy.coarse.v2f16(<2 x half> %{{.*}})
 // CHECK: ret <2 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <2 x half> @_Z20test_f16_ddy_coarse2Dv2_Dh
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <2 x half> 
@llvm.spv.ddy.coarse.v2f16(<2 x half> %{{.*}})
+// CHECK-SPIRV: ret <2 x half> %hlsl.ddy.coarse
 half2 test_f16_ddy_coarse2(half2 val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x half> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <3 x half> 
@llvm.dx.ddy.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK-LABEL: <3 x half> @_Z20test_f16_ddy_coarse3Dv3_Dh
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x half> 
@llvm.dx.ddy.coarse.v3f16(<3 x half> %{{.*}})
 // CHECK: ret <3 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <3 x half> @_Z20test_f16_ddy_coarse3Dv3_Dh
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <3 x half> 
@llvm.spv.ddy.coarse.v3f16(<3 x half> %{{.*}})
+// CHECK-SPIRV: ret <3 x half> %hlsl.ddy.coarse
 half3 test_f16_ddy_coarse3(half3 val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x half> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <4 x half> 
@llvm.dx.ddy.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK-LABEL: <4 x half> @_Z20test_f16_ddy_coarse4Dv4_Dh
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x half> 
@llvm.dx.ddy.coarse.v4f16(<4 x half> %{{.*}})
 // CHECK: ret <4 x half> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <4 x half> @_Z20test_f16_ddy_coarse4Dv4_Dh
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <4 x half> 
@llvm.spv.ddy.coarse.v4f16(<4 x half> %{{.*}})
+// CHECK-SPIRV: ret <4 x half> %hlsl.ddy.coarse
 half4 test_f16_ddy_coarse4(half4 val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) float @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn float 
@llvm.dx.ddy.coarse.f32(float %{{.*}})
+// CHECK-LABEL: float @_Z19test_f32_ddy_coarsef
+// CHECK: %hlsl.ddy.coarse = call {{.*}} float @llvm.dx.ddy.coarse.f32(float 
%{{.*}})
 // CHECK: ret float %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: float @_Z19test_f32_ddy_coarsef
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} float 
@llvm.spv.ddy.coarse.f32(float %{{.*}})
+// CHECK-SPIRV: ret float %hlsl.ddy.coarse
 float test_f32_ddy_coarse(float val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <2 x float> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <2 x float> 
@llvm.dx.ddy.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK-LABEL: <2 x float> @_Z20test_f32_ddy_coarse2Dv2_f
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <2 x float> 
@llvm.dx.ddy.coarse.v2f32(<2 x float> %{{.*}})
 // CHECK: ret <2 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <2 x float> @_Z20test_f32_ddy_coarse2Dv2_f
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <2 x float> 
@llvm.spv.ddy.coarse.v2f32(<2 x float> %{{.*}})
+// CHECK-SPIRV: ret <2 x float> %hlsl.ddy.coarse
 float2 test_f32_ddy_coarse2(float2 val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <3 x float> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <3 x float> 
@llvm.dx.ddy.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK-LABEL: <3 x float> @_Z20test_f32_ddy_coarse3Dv3_f
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <3 x float> 
@llvm.dx.ddy.coarse.v3f32(<3 x float> %{{.*}})
 // CHECK: ret <3 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <3 x float> @_Z20test_f32_ddy_coarse3Dv3_f
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <3 x float> 
@llvm.spv.ddy.coarse.v3f32(<3 x float> %{{.*}})
+// CHECK-SPIRV: ret <3 x float> %hlsl.ddy.coarse
 float3 test_f32_ddy_coarse3(float3 val) {
     return ddy_coarse(val);
 }
 
-// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @
-// CHECK: %hlsl.ddy.coarse = call reassoc nnan ninf nsz arcp afn <4 x float> 
@llvm.dx.ddy.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK-LABEL: <4 x float> @_Z20test_f32_ddy_coarse4Dv4_f
+// CHECK: %hlsl.ddy.coarse = call {{.*}} <4 x float> 
@llvm.dx.ddy.coarse.v4f32(<4 x float> %{{.*}})
 // CHECK: ret <4 x float> %hlsl.ddy.coarse
+// CHECK-LABEL-SPIRV: <4 x float> @_Z20test_f32_ddy_coarse4Dv4_f
+// CHECK-SPIRV: %hlsl.ddy.coarse = call {{.*}} <4 x float> 
@llvm.spv.ddy.coarse.v4f32(<4 x float> %{{.*}})
+// CHECK-SPIRV: ret <4 x float> %hlsl.ddy.coarse
 float4 test_f32_ddy_coarse4(float4 val) {
     return ddy_coarse(val);
 }
diff --git a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
index 48b717a4af11f..6bb7d5c0f9a2c 100644
--- a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
@@ -20,4 +20,4 @@ float test_integer_scalar_input(int val) {
 double test_double_scalar_input(double val) {
   return __builtin_hlsl_elementwise_ddx_coarse(val);
   // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
index c9e9d17757f25..fe01ec0f4c4b0 100644
--- a/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
@@ -18,4 +18,4 @@ float test_integer_scalar_input(int val) {
 double test_double_scalar_input(double val) {
   return __builtin_hlsl_elementwise_ddy_coarse(val);
   // expected-error@-1 {{1st argument must be a scalar or vector of 16 or 32 
bit floating-point types (was 'double')}}
-}
\ No newline at end of file
+}
diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
index bb4512a7a4341..986f7a57fd5ea 100644
--- a/llvm/test/CodeGen/DirectX/ddx_coarse.ll
+++ b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
@@ -1,6 +1,6 @@
 ; RUN: opt -S  -scalarizer -dxil-op-lower 
-mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
 
-; Make sure dxil operation function calls for fwidth are generated for 
half/float and matching vectors
+; Make sure dxil operation function calls for ddx_coarse are generated for 
half/float and matching vectors
 
 define noundef half @deriv_coarse_x_half(half noundef %a) {
 ; CHECK: call half @dx.op.unary.f16(i32 83, half %{{.*}})
diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
index 7321fc1a80975..24c6d6c9d745a 100644
--- a/llvm/test/CodeGen/DirectX/ddy_coarse.ll
+++ b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
@@ -1,6 +1,6 @@
 ; RUN: opt -S  -scalarizer -dxil-op-lower 
-mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
 
-; Make sure dxil operation function calls for fwidth are generated for 
half/float and matching vectors
+; Make sure dxil operation function calls for ddy_coarse are generated for 
half/float and matching vectors
 
 define noundef half @deriv_coarse_y_half(half noundef %a) {
 ; CHECK: call half @dx.op.unary.f16(i32 84, half %{{.*}})

>From 3c1060c95c569a0d03d6275afa45192e0862bee1 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Sat, 25 Oct 2025 15:38:04 +0100
Subject: [PATCH 05/12] [SPIRV] Don't add FP Encoding to OpTypeFloat by default

Using getOrCreateSPIRVFloatType during instruction selection would
result in FP Encoding 0 being added to the OpTypeFloat. This maps
to the Khronos extension BFloat16KHR, which is undesired default
behaviour. Instead no FP Encoding should be added by default to
an OpTypeFloat in this case.
---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp 
b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 47022b3f89a8b..76fd834fd7219 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -1697,11 +1697,16 @@ SPIRVType 
*SPIRVGlobalRegistry::getOrCreateSPIRVType(unsigned BitWidth,
   MachineIRBuilder MIRBuilder(DepMBB, DepMBB.getFirstNonPHI());
   const MachineInstr *NewMI =
       createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
-        return BuildMI(MIRBuilder.getMBB(), *MIRBuilder.getInsertPt(),
-                       MIRBuilder.getDL(), TII.get(SPIRVOPcode))
-            .addDef(createTypeVReg(CurMF->getRegInfo()))
-            .addImm(BitWidth)
-            .addImm(0);
+        auto NewTypeMI = BuildMI(MIRBuilder.getMBB(), 
*MIRBuilder.getInsertPt(),
+                                 MIRBuilder.getDL(), TII.get(SPIRVOPcode))
+                             .addDef(createTypeVReg(CurMF->getRegInfo()))
+                             .addImm(BitWidth);
+        // Don't add Encoding to FP type
+        if (!Ty->isFloatTy()) {
+          return NewTypeMI.addImm(0);
+        } else {
+          return NewTypeMI;
+        }
       });
   add(Ty, false, NewMI);
   return finishCreatingSPIRVType(Ty, NewMI);

>From afb4ad4ab0862404ba54413f1b8850263fdaeb3c Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Sat, 25 Oct 2025 15:56:29 +0100
Subject: [PATCH 06/12] Wrap OpDPdx/yCoarse with OpFConvert where needed.

HLSL allows half types as arg and result to dd/y_coarse. This is not
allowed in SPIRV, so to manage this we must detect these cases and wrap
the OpDPd instructions in OpFConvert from half -> float -> half.
---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 67 ++++++++++++++++---
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp |  9 ++-
 .../SPIRV/hlsl-intrinsics/ddx_coarse.ll       |  8 ++-
 .../SPIRV/hlsl-intrinsics/ddy_coarse.ll       |  8 ++-
 4 files changed, 77 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ea87edaeef6da..a91206cbbcf73 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -328,6 +328,8 @@ class SPIRVInstructionSelector : public InstructionSelector 
{
                            MachineInstr &I) const;
   bool selectFrexp(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
+  bool selectDpdCoarse(Register ResVReg, const SPIRVType *ResType,
+                       MachineInstr &I, const unsigned DPdOpCode) const;
   // Utilities
   std::pair<Register, bool>
   buildI32Constant(uint32_t Val, MachineInstr &I,
@@ -3140,6 +3142,59 @@ bool SPIRVInstructionSelector::wrapIntoSpecConstantOp(
   return Result;
 }
 
+bool SPIRVInstructionSelector::selectDpdCoarse(Register ResVReg,
+                                               const SPIRVType *ResType,
+                                               MachineInstr &I,
+                                               const unsigned DPdOpCode) const 
{
+  // If the arg/result types are half then we need to wrap the instr in
+  // conversions to float
+  // This case occurs because a half arg/result is legal in HLSL but not spirv.
+  Register SrcReg = I.getOperand(2).getReg();
+  SPIRVType *SrcType = GR.getSPIRVTypeForVReg(SrcReg);
+  unsigned BitWidth = std::min(GR.getScalarOrVectorBitWidth(SrcType),
+                               GR.getScalarOrVectorBitWidth(ResType));
+  if (BitWidth == 32) {
+    return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DPdOpCode))
+        .addDef(ResVReg)
+        .addUse(GR.getSPIRVTypeID(ResType))
+        .addUse(I.getOperand(2).getReg());
+  } else {
+    MachineIRBuilder MIRBuilder(I);
+    unsigned componentCount = GR.getScalarOrVectorComponentCount(SrcType);
+    SPIRVType *Float32Ty = GR.getOrCreateSPIRVFloatType(32, I, TII);
+    SPIRVType *F32ConvertTy;
+    if (componentCount == 1) {
+      F32ConvertTy = Float32Ty;
+    } else {
+      F32ConvertTy = GR.getOrCreateSPIRVVectorType(Float32Ty, componentCount,
+                                                   MIRBuilder, false);
+    }
+
+    const TargetRegisterClass *RegClass = GR.getRegClass(SrcType);
+    Register ConvertToVReg = MRI->createVirtualRegister(RegClass);
+    Register DpdOpVReg = MRI->createVirtualRegister(RegClass);
+
+    bool Result =
+        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
+            .addDef(ConvertToVReg)
+            .addUse(GR.getSPIRVTypeID(F32ConvertTy))
+            .addUse(SrcReg)
+            .constrainAllUses(TII, TRI, RBI);
+    Result &= BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DPdOpCode))
+                  .addDef(DpdOpVReg)
+                  .addUse(GR.getSPIRVTypeID(F32ConvertTy))
+                  .addUse(ConvertToVReg)
+                  .constrainAllUses(TII, TRI, RBI);
+    Result &=
+        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
+            .addDef(ResVReg)
+            .addUse(GR.getSPIRVTypeID(ResType))
+            .addUse(DpdOpVReg)
+            .constrainAllUses(TII, TRI, RBI);
+    return Result;
+  }
+}
+
 bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
                                                const SPIRVType *ResType,
                                                MachineInstr &I) const {
@@ -3529,18 +3584,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register 
ResVReg,
     return selectExtInst(ResVReg, ResType, I, GL::UnpackHalf2x16);
   }
   case Intrinsic::spv_ddx_coarse: {
-    return BuildMI(*I.getParent(), I, I.getDebugLoc(),
-                   TII.get(SPIRV::OpDPdxCoarse))
-        .addDef(ResVReg)
-        .addUse(GR.getSPIRVTypeID(ResType))
-        .addUse(I.getOperand(2).getReg());
+    return selectDpdCoarse(ResVReg, ResType, I, SPIRV::OpDPdxCoarse);
   }
   case Intrinsic::spv_ddy_coarse: {
-    return BuildMI(*I.getParent(), I, I.getDebugLoc(),
-                   TII.get(SPIRV::OpDPdyCoarse))
-        .addDef(ResVReg)
-        .addUse(GR.getSPIRVTypeID(ResType))
-        .addUse(I.getOperand(2).getReg());
+    return selectDpdCoarse(ResVReg, ResType, I, SPIRV::OpDPdyCoarse);
   }
   default: {
     std::string DiagMsg;
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp 
b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index b8cd9c1358f00..bd754d17694b8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -934,7 +934,8 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
                     Capability::UniformBufferArrayDynamicIndexing,
                     Capability::SampledImageArrayDynamicIndexing,
                     Capability::StorageBufferArrayDynamicIndexing,
-                    Capability::StorageImageArrayDynamicIndexing});
+                    Capability::StorageImageArrayDynamicIndexing,
+                    Capability::DerivativeControl});
 
   // Became core in Vulkan 1.2
   if (ST.isAtLeastSPIRVVer(VersionTuple(1, 5))) {
@@ -2148,6 +2149,12 @@ void addInstrRequirements(const MachineInstr &MI,
     }
     break;
   }
+  case SPIRV::OpDPdxCoarse:
+  case SPIRV::OpDPdyCoarse: {
+    Reqs.addCapability(SPIRV::Capability::DerivativeControl);
+    break;
+  }
+
   default:
     break;
   }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
index 019c7b75ffe66..1d88dac855bf5 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
@@ -18,7 +18,9 @@ entry:
 define noundef half @ddx_coarse_half(half noundef %a) {
 entry:
 ; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
-; CHECK: %[[#]] = OpDPdxCoarse %[[#float_16]] %[[#float_16_arg]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#coarse:]] = OpDPdxCoarse %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#coarse]]
   %elt.ddx.coarse = call half @llvm.spv.ddx.coarse.f16(half %a)
   ret half %elt.ddx.coarse
 }
@@ -34,7 +36,9 @@ entry:
 define noundef <4 x half> @ddx_coarse_half_vector(<4 x half> noundef %a) {
 entry:
 ; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
-; CHECK: %[[#]] = OpDPdxCoarse %[[#vec4_float_16]] %[[#vec4_float_16_arg]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#vec4_float_32:]] 
%[[#vec4_float_16_arg]]
+; CHECK: %[[#coarse:]] = OpDPdxCoarse %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#coarse]]
   %elt.ddx.coarse = call <4 x half> @llvm.spv.ddx.coarse.v4f16(<4 x half> %a)
   ret <4 x half> %elt.ddx.coarse
 }
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
index 87e03f55c260b..68105e03bd02a 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
@@ -18,7 +18,9 @@ entry:
 define noundef half @ddy_coarse_half(half noundef %a) {
 entry:
 ; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
-; CHECK: %[[#]] = OpDPdyCoarse %[[#float_16]] %[[#float_16_arg]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#float_32:]] %[[#float_16_arg]]
+; CHECK: %[[#coarse:]] = OpDPdyCoarse %[[#float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#float_16]] %[[#coarse]]
   %elt.ddy.coarse = call half @llvm.spv.ddy.coarse.f16(half %a)
   ret half %elt.ddy.coarse
 }
@@ -34,7 +36,9 @@ entry:
 define noundef <4 x half> @ddy_coarse_half_vector(<4 x half> noundef %a) {
 entry:
 ; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
-; CHECK: %[[#]] = OpDPdyCoarse %[[#vec4_float_16]] %[[#vec4_float_16_arg]]
+; CHECK: %[[#converted:]] = OpFConvert %[[#vec4_float_32:]] 
%[[#vec4_float_16_arg]]
+; CHECK: %[[#coarse:]] = OpDPdyCoarse %[[#vec4_float_32]] %[[#converted]]
+; CHECK: %[[#]] = OpFConvert %[[#vec4_float_16]] %[[#coarse]]
   %elt.ddy.coarse = call <4 x half> @llvm.spv.ddy.coarse.v4f16(<4 x half> %a)
   ret <4 x half> %elt.ddy.coarse
 }

>From 579c83d8a1884ce95c98b86e8b5f575e0ed69274 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Fri, 7 Nov 2025 17:40:48 +0000
Subject: [PATCH 07/12] [SPIRV] ensure OpDpdx/yCoarse aren't produced in opencl

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp |  7 +++++++
 llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll | 13 +++++++++++++
 llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll | 13 +++++++++++++
 3 files changed, 33 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index a91206cbbcf73..fa9a697ff011e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3146,6 +3146,13 @@ bool SPIRVInstructionSelector::selectDpdCoarse(Register 
ResVReg,
                                                const SPIRVType *ResType,
                                                MachineInstr &I,
                                                const unsigned DPdOpCode) const 
{
+  if (!STI.isShader()) {
+    std::string DiagMsg;
+    raw_string_ostream OS(DiagMsg);
+    I.print(OS, true, false, false, false);
+    DiagMsg += " is only supported in shaders.\n";
+    report_fatal_error(DiagMsg.c_str(), false);
+  }
   // If the arg/result types are half then we need to wrap the instr in
   // conversions to float
   // This case occurs because a half arg/result is legal in HLSL but not spirv.
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
new file mode 100644
index 0000000000000..c3cfd67397b64
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
@@ -0,0 +1,13 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.ddx.coarse), 
%{{.*}} is only supported in shaders.
+
+define noundef float @ddx_coarse(float noundef %a) {
+entry:
+  %spv.ddx.coarse = call float @llvm.spv.ddx.coarse.f32(float %a)
+  ret float %spv.ddx.coarse
+}
+
+declare float @llvm.spv.ddx.coarse.f32(float)
+
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll
new file mode 100644
index 0000000000000..e18eb9f7bfca7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll
@@ -0,0 +1,13 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s 
-o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.ddy.coarse), 
%{{.*}} is only supported in shaders.
+
+define noundef float @ddy_coarse(float noundef %a) {
+entry:
+  %spv.ddy.coarse = call float @llvm.spv.ddy.coarse.f32(float %a)
+  ret float %spv.ddy.coarse
+}
+
+declare float @llvm.spv.ddy.coarse.f32(float)
+

>From 2cfaedba78a6be03ca6b1272cf5db09c6d7c9bb0 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Tue, 11 Nov 2025 18:25:45 +0000
Subject: [PATCH 08/12] ddx/y_coarse test cleanup

---
 clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl   | 2 --
 llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll        | 2 +-
 llvm/test/CodeGen/DirectX/ddx_coarse.ll               | 1 -
 llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll        | 2 +-
 llvm/test/CodeGen/DirectX/ddy_coarse.ll               | 1 -
 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll | 4 ++--
 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll | 4 ++--
 llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll    | 1 -
 llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll    | 1 -
 9 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
index 6bb7d5c0f9a2c..f1097bd5bc657 100644
--- a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
@@ -1,7 +1,5 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s 
-fnative-half-type -verify
 
-typedef float float2 __attribute__((ext_vector_type(2)));
-
 float no_arg() {
   return __builtin_hlsl_elementwise_ddx_coarse();
   // expected-error@-1 {{too few arguments to function call, expected 1, have 
0}}
diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll 
b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
index 2cf0d1db27efe..0679eec31cec1 100644
--- a/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
+++ b/llvm/test/CodeGen/DirectX/ddx_coarse-errors.ll
@@ -12,4 +12,4 @@ entry:
   %0 = load double, ptr %a.addr, align 8
   %dx.ddx.coarse = call double @llvm.dx.ddx.coarse.f64(double %0)
   ret double %dx.ddx.coarse
-}
\ No newline at end of file
+}
diff --git a/llvm/test/CodeGen/DirectX/ddx_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
index 986f7a57fd5ea..f6ea031273263 100644
--- a/llvm/test/CodeGen/DirectX/ddx_coarse.ll
+++ b/llvm/test/CodeGen/DirectX/ddx_coarse.ll
@@ -38,4 +38,3 @@ entry:
 declare half @llvm.dx.ddx.coarse.f16(half)
 declare float @llvm.dx.ddx.coarse.f32(float)
 declare <4 x float> @llvm.dx.ddx.coarse.v4f32(<4 x float>)
-
diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll 
b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
index 400969f21713c..df8e3eb0f7e0b 100644
--- a/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
+++ b/llvm/test/CodeGen/DirectX/ddy_coarse-errors.ll
@@ -12,4 +12,4 @@ entry:
   %0 = load double, ptr %a.addr, align 8
   %dx.ddy.coarse = call double @llvm.dx.ddy.coarse.f64(double %0)
   ret double %dx.ddy.coarse
-}
\ No newline at end of file
+}
diff --git a/llvm/test/CodeGen/DirectX/ddy_coarse.ll 
b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
index 24c6d6c9d745a..e3337022e1b01 100644
--- a/llvm/test/CodeGen/DirectX/ddy_coarse.ll
+++ b/llvm/test/CodeGen/DirectX/ddy_coarse.ll
@@ -38,4 +38,3 @@ entry:
 declare half @llvm.dx.ddy.coarse.f16(half)
 declare float @llvm.dx.ddy.coarse.f32(float)
 declare <4 x float> @llvm.dx.ddy.coarse.v4f32(<4 x float>)
-
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
index 1d88dac855bf5..478acb53701ea 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddx_coarse.ll
@@ -1,5 +1,5 @@
 ; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val --target-env spv1.4 %}
 
 ; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
 ; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
@@ -44,4 +44,4 @@ entry:
 }
 
 declare float @llvm.spv.ddx.coarse.f32(float)
-declare half @llvm.spv.ddx.coarse.f16(half)
\ No newline at end of file
+declare half @llvm.spv.ddx.coarse.f16(half)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll 
b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
index 68105e03bd02a..8ad67cb644aa7 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/ddy_coarse.ll
@@ -1,5 +1,5 @@
 ; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | 
FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 
-filetype=obj | spirv-val --target-env spv1.4 %}
 
 ; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
 ; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
@@ -44,4 +44,4 @@ entry:
 }
 
 declare float @llvm.spv.ddy.coarse.f32(float)
-declare half @llvm.spv.ddy.coarse.f16(half)
\ No newline at end of file
+declare half @llvm.spv.ddy.coarse.f16(half)
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
index c3cfd67397b64..e93c1d1ba4d36 100644
--- a/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddx_coarse-error.ll
@@ -10,4 +10,3 @@ entry:
 }
 
 declare float @llvm.spv.ddx.coarse.f32(float)
-
diff --git a/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll 
b/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll
index e18eb9f7bfca7..aa71a395d8680 100644
--- a/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll
+++ b/llvm/test/CodeGen/SPIRV/opencl/ddy_coarse-error.ll
@@ -10,4 +10,3 @@ entry:
 }
 
 declare float @llvm.spv.ddy.coarse.f32(float)
-

>From 985e7b2268ff0189d237af4b2a7884a3f22286ce Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Tue, 11 Nov 2025 18:27:48 +0000
Subject: [PATCH 09/12] [SPIRV] Add todo to update instruction check when
 possible

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index fa9a697ff011e..556f011fc7beb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3146,6 +3146,8 @@ bool SPIRVInstructionSelector::selectDpdCoarse(Register 
ResVReg,
                                                const SPIRVType *ResType,
                                                MachineInstr &I,
                                                const unsigned DPdOpCode) const 
{
+  // TODO: This should check specifically for Fragment Execution Model, but STI
+  // doesn't provide that information yet.
   if (!STI.isShader()) {
     std::string DiagMsg;
     raw_string_ostream OS(DiagMsg);

>From 0e37b75d2c9e5314aaa4c25e2273a848a13de102 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Tue, 11 Nov 2025 20:18:06 +0000
Subject: [PATCH 10/12] Add issue number to TODO

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 556f011fc7beb..9ff83cec5792e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3147,7 +3147,7 @@ bool SPIRVInstructionSelector::selectDpdCoarse(Register 
ResVReg,
                                                MachineInstr &I,
                                                const unsigned DPdOpCode) const 
{
   // TODO: This should check specifically for Fragment Execution Model, but STI
-  // doesn't provide that information yet.
+  // doesn't provide that information yet. See #167562
   if (!STI.isShader()) {
     std::string DiagMsg;
     raw_string_ostream OS(DiagMsg);

>From 57af4cd6f4219f6e9b2d6d1f2af30c2dc5ee92ea Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Mon, 17 Nov 2025 14:55:04 +0000
Subject: [PATCH 11/12] Add spirv run lines to dd_coarse Sema tests

---
 clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl | 1 +
 clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl | 1 +
 2 files changed, 2 insertions(+)

diff --git a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
index f1097bd5bc657..ebad1cc6826d8 100644
--- a/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ddx-coarse-errors.hlsl
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s 
-fnative-half-type -verify
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library %s 
-fnative-half-type -verify
 
 float no_arg() {
   return __builtin_hlsl_elementwise_ddx_coarse();
diff --git a/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
index fe01ec0f4c4b0..9cc23665882c8 100644
--- a/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ddy-coarse-errors.hlsl
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library %s 
-fnative-half-type -verify
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library %s 
-fnative-half-type -verify
 
 float no_arg() {
   return __builtin_hlsl_elementwise_ddy_coarse();

>From 5c13759bbf7490a1aaebc9389ea4e108d72c0154 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <[email protected]>
Date: Mon, 17 Nov 2025 15:20:09 +0000
Subject: [PATCH 12/12] [SPIRV] Cleanup SPIRVInstructionSelector with new
 helper

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 84 ++++++++++---------
 1 file changed, 43 insertions(+), 41 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp 
b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9ff83cec5792e..0653b4eb9dfe2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -373,6 +373,7 @@ class SPIRVInstructionSelector : public InstructionSelector 
{
   bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType,
                                 GIntrinsic &HandleDef, MachineInstr &Pos) 
const;
   void decorateUsesAsNonUniform(Register &NonUniformReg) const;
+  void errorIfInstrOutsideShader(MachineInstr &I) const;
 };
 
 bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) {
@@ -3148,13 +3149,8 @@ bool SPIRVInstructionSelector::selectDpdCoarse(Register 
ResVReg,
                                                const unsigned DPdOpCode) const 
{
   // TODO: This should check specifically for Fragment Execution Model, but STI
   // doesn't provide that information yet. See #167562
-  if (!STI.isShader()) {
-    std::string DiagMsg;
-    raw_string_ostream OS(DiagMsg);
-    I.print(OS, true, false, false, false);
-    DiagMsg += " is only supported in shaders.\n";
-    report_fatal_error(DiagMsg.c_str(), false);
-  }
+  errorIfInstrOutsideShader(I);
+
   // If the arg/result types are half then we need to wrap the instr in
   // conversions to float
   // This case occurs because a half arg/result is legal in HLSL but not spirv.
@@ -3162,46 +3158,41 @@ bool SPIRVInstructionSelector::selectDpdCoarse(Register 
ResVReg,
   SPIRVType *SrcType = GR.getSPIRVTypeForVReg(SrcReg);
   unsigned BitWidth = std::min(GR.getScalarOrVectorBitWidth(SrcType),
                                GR.getScalarOrVectorBitWidth(ResType));
-  if (BitWidth == 32) {
+  if (BitWidth == 32)
     return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DPdOpCode))
         .addDef(ResVReg)
         .addUse(GR.getSPIRVTypeID(ResType))
         .addUse(I.getOperand(2).getReg());
-  } else {
-    MachineIRBuilder MIRBuilder(I);
-    unsigned componentCount = GR.getScalarOrVectorComponentCount(SrcType);
-    SPIRVType *Float32Ty = GR.getOrCreateSPIRVFloatType(32, I, TII);
-    SPIRVType *F32ConvertTy;
-    if (componentCount == 1) {
-      F32ConvertTy = Float32Ty;
-    } else {
-      F32ConvertTy = GR.getOrCreateSPIRVVectorType(Float32Ty, componentCount,
-                                                   MIRBuilder, false);
-    }
 
-    const TargetRegisterClass *RegClass = GR.getRegClass(SrcType);
-    Register ConvertToVReg = MRI->createVirtualRegister(RegClass);
-    Register DpdOpVReg = MRI->createVirtualRegister(RegClass);
+  MachineIRBuilder MIRBuilder(I);
+  unsigned componentCount = GR.getScalarOrVectorComponentCount(SrcType);
+  SPIRVType *F32ConvertTy = GR.getOrCreateSPIRVFloatType(32, I, TII);
+  if (componentCount != 1)
+    F32ConvertTy = GR.getOrCreateSPIRVVectorType(F32ConvertTy, componentCount,
+                                                 MIRBuilder, false);
 
-    bool Result =
-        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
-            .addDef(ConvertToVReg)
-            .addUse(GR.getSPIRVTypeID(F32ConvertTy))
-            .addUse(SrcReg)
-            .constrainAllUses(TII, TRI, RBI);
-    Result &= BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DPdOpCode))
-                  .addDef(DpdOpVReg)
-                  .addUse(GR.getSPIRVTypeID(F32ConvertTy))
-                  .addUse(ConvertToVReg)
-                  .constrainAllUses(TII, TRI, RBI);
-    Result &=
-        BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
-            .addDef(ResVReg)
-            .addUse(GR.getSPIRVTypeID(ResType))
-            .addUse(DpdOpVReg)
-            .constrainAllUses(TII, TRI, RBI);
-    return Result;
-  }
+  const TargetRegisterClass *RegClass = GR.getRegClass(SrcType);
+  Register ConvertToVReg = MRI->createVirtualRegister(RegClass);
+  Register DpdOpVReg = MRI->createVirtualRegister(RegClass);
+
+  bool Result =
+      BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
+          .addDef(ConvertToVReg)
+          .addUse(GR.getSPIRVTypeID(F32ConvertTy))
+          .addUse(SrcReg)
+          .constrainAllUses(TII, TRI, RBI);
+  Result &= BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(DPdOpCode))
+                .addDef(DpdOpVReg)
+                .addUse(GR.getSPIRVTypeID(F32ConvertTy))
+                .addUse(ConvertToVReg)
+                .constrainAllUses(TII, TRI, RBI);
+  Result &=
+      BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpFConvert))
+          .addDef(ResVReg)
+          .addUse(GR.getSPIRVTypeID(ResType))
+          .addUse(DpdOpVReg)
+          .constrainAllUses(TII, TRI, RBI);
+  return Result;
 }
 
 bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
@@ -4763,6 +4754,17 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
       .constrainAllUses(TII, TRI, RBI);
 }
 
+void SPIRVInstructionSelector::errorIfInstrOutsideShader(
+    MachineInstr &I) const {
+  if (!STI.isShader()) {
+    std::string DiagMsg;
+    raw_string_ostream OS(DiagMsg);
+    I.print(OS, true, false, false, false);
+    DiagMsg += " is only supported in shaders.\n";
+    report_fatal_error(DiagMsg.c_str(), false);
+  }
+}
+
 namespace llvm {
 InstructionSelector *
 createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,

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

Reply via email to