[clang] [HLSL] Add matrix support for `sign` (PR #235542)

David Browne via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 25 17:41:23 PDT 2026


https://github.com/djb created https://github.com/llvm/llvm-project/pull/235542

Implement matrix support for `sign` and add matrix codegen and sema tests. Also fix a sema bug by rejecting boolean matrix and vector inputs to `__builtin_hlsl_elementwise_sign`, consistent with the builtin's rejection of scalar boolean inputs. Backend tests were not added as no modifications were made to either backend.

>From 42403aa2181c781f3fe8575c56e7a5e19d72998a Mon Sep 17 00:00:00 2001
From: David Browne <djb at users.noreply.github.com>
Date: Mon, 26 Oct 2026 00:53:23 +0100
Subject: [PATCH] add matrix support for sign

Implement matrix support for sign and add matrix codegen and sema
tests. Also fix a sema bug by rejecting boolean matrix and vector
inputs to __builtin_hlsl_elementwise_sign, consistent with the
builtin's rejection of scalar boolean inputs. Backend tests were not
added as no modifications were made to either backend.
---
 clang/include/clang/Basic/HLSLIntrinsics.td   |  1 -
 clang/lib/AST/Type.cpp                        |  2 +
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          |  8 +--
 clang/lib/Sema/SemaHLSL.cpp                   | 12 ++--
 clang/test/CodeGenHLSL/builtins/sign_mat.hlsl | 60 +++++++++++++++++++
 clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl |  9 ++-
 .../SemaHLSL/BuiltIns/sign_mat-errors.hlsl    | 13 ++++
 7 files changed, 90 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/builtins/sign_mat.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/sign_mat-errors.hlsl

diff --git a/clang/include/clang/Basic/HLSLIntrinsics.td b/clang/include/clang/Basic/HLSLIntrinsics.td
index fd59e3477b8c79..583917ca4030b5 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -1559,7 +1559,6 @@ def hlsl_sign : HLSLOneArgBuiltin<"sign", "__builtin_hlsl_elementwise_sign"> {
   let ReturnType = VaryingShape<IntTy>;
   let VaryingTypes = AllNumericTypes;
   let VaryingLongVector = 1;
-  let VaryingMatDims = [];
 }
 
 // Returns the sine of the input value, Val.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4c5f2f60a90005..8cbad334ebc467 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2245,6 +2245,8 @@ bool Type::hasAutoForTrailingReturnType() const {
 bool Type::hasIntegerRepresentation() const {
   if (const auto *VT = dyn_cast<VectorType>(CanonicalType))
     return VT->getElementType()->isIntegerType();
+  if (const auto *MT = dyn_cast<MatrixType>(CanonicalType))
+    return MT->getElementType()->isIntegerType();
   if (CanonicalType->isSveVLSBuiltinType()) {
     const auto *VT = cast<BuiltinType>(CanonicalType);
     return VT->getKind() == BuiltinType::SveBool ||
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index fc422918499ed2..2281e96889a770 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1751,12 +1751,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     auto *Arg0 = E->getArg(0);
     Value *Op0 = EmitScalarExpr(Arg0);
     llvm::Type *Xty = Op0->getType();
-    llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext());
-    if (Xty->isVectorTy()) {
-      auto *XVecTy = Arg0->getType()->castAs<VectorType>();
-      retType = llvm::VectorType::get(
-          retType, ElementCount::getFixed(XVecTy->getNumElements()));
-    }
+    llvm::Type *retType = getAggregateType(
+        llvm::Type::getInt32Ty(this->getLLVMContext()), Arg0->getType());
     assert((Arg0->getType()->hasFloatingRepresentation() ||
             Arg0->getType()->hasIntegerRepresentation()) &&
            "sign operand must have a float or int representation");
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index f3fd1fb68c0727..e61d66833b7372 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3796,11 +3796,11 @@ static bool CheckNoDoubleVectors(Sema *S, SourceLocation Loc, int ArgOrdinal,
   return false;
 }
 
-static bool CheckFloatingOrIntRepresentation(Sema *S, SourceLocation Loc,
-                                             int ArgOrdinal,
-                                             clang::QualType PassedType) {
-  if (!PassedType->hasIntegerRepresentation() &&
-      !PassedType->hasFloatingRepresentation())
+static bool CheckFloatingOrNonBoolIntRepresentation(
+    Sema *S, SourceLocation Loc, int ArgOrdinal, clang::QualType PassedType) {
+  if (getScalarComponentType(PassedType)->isBooleanType() ||
+      (!PassedType->hasIntegerRepresentation() &&
+       !PassedType->hasFloatingRepresentation()))
     return S->Diag(Loc, diag::err_builtin_invalid_arg_type)
            << ArgOrdinal << /* scalar or vector of */ 5 << /* integer */ 1
            << /* fp */ 1 << PassedType;
@@ -5146,7 +5146,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
     if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
       return true;
     if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
-                                   CheckFloatingOrIntRepresentation))
+                                   CheckFloatingOrNonBoolIntRepresentation))
       return true;
     SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().IntTy);
     break;
diff --git a/clang/test/CodeGenHLSL/builtins/sign_mat.hlsl b/clang/test/CodeGenHLSL/builtins/sign_mat.hlsl
new file mode 100644
index 00000000000000..018ef3dbd6d8ae
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/sign_mat.hlsl
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type -fnative-int16-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,NATIVE_HALF \
+// RUN:   -DTARGET=dx -DFNATTRS="hidden noundef"
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF \
+// RUN:   -DTARGET=dx -DFNATTRS="hidden noundef"
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-library %s -fnative-half-type -fnative-int16-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,NATIVE_HALF \
+// RUN:   -DTARGET=spv -DFNATTRS="hidden spir_func noundef"
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-library %s -emit-llvm -disable-llvm-passes \
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF \
+// RUN:   -DTARGET=spv -DFNATTRS="hidden spir_func noundef"
+
+// CHECK-LABEL: test_sign_half2x3
+// NATIVE_HALF: call <6 x i32> @llvm.[[TARGET]].sign.v6f16(<6 x half> %{{.*}})
+// NO_HALF: call <6 x i32> @llvm.[[TARGET]].sign.v6f32(<6 x float> %{{.*}})
+int2x3 test_sign_half2x3(half2x3 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_half3x4
+// NATIVE_HALF: call <12 x i32> @llvm.[[TARGET]].sign.v12f16(<12 x half> %{{.*}})
+// NO_HALF: call <12 x i32> @llvm.[[TARGET]].sign.v12f32(<12 x float> %{{.*}})
+int3x4 test_sign_half3x4(half3x4 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_half4x4
+// NATIVE_HALF: call <16 x i32> @llvm.[[TARGET]].sign.v16f16(<16 x half> %{{.*}})
+// NO_HALF: call <16 x i32> @llvm.[[TARGET]].sign.v16f32(<16 x float> %{{.*}})
+int4x4 test_sign_half4x4(half4x4 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_int2x3
+// CHECK: call <6 x i32> @llvm.[[TARGET]].sign.v6i32(<6 x i32> %{{.*}})
+int2x3 test_sign_int2x3(int2x3 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_int3x4
+// CHECK: call <12 x i32> @llvm.[[TARGET]].sign.v12i32(<12 x i32> %{{.*}})
+int3x4 test_sign_int3x4(int3x4 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_int4x4
+// CHECK: call <16 x i32> @llvm.[[TARGET]].sign.v16i32(<16 x i32> %{{.*}})
+int4x4 test_sign_int4x4(int4x4 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_uint2x3
+// CHECK: [[CMP:%.*]] = icmp eq <6 x i32> %{{.*}}, zeroinitializer
+// CHECK: %hlsl.sign = select <6 x i1> [[CMP]], <6 x i32> zeroinitializer, <6 x i32> splat (i32 1)
+int2x3 test_sign_uint2x3(uint2x3 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_uint3x4
+// CHECK: [[CMP:%.*]] = icmp eq <12 x i32> %{{.*}}, zeroinitializer
+// CHECK: %hlsl.sign = select <12 x i1> [[CMP]], <12 x i32> zeroinitializer, <12 x i32> splat (i32 1)
+int3x4 test_sign_uint3x4(uint3x4 p0) { return sign(p0); }
+
+// CHECK-LABEL: test_sign_uint4x4
+// CHECK: [[CMP:%.*]] = icmp eq <16 x i32> %{{.*}}, zeroinitializer
+// CHECK: %hlsl.sign = select <16 x i1> [[CMP]], <16 x i32> zeroinitializer, <16 x i32> splat (i32 1)
+int4x4 test_sign_uint4x4(uint4x4 p0) { return sign(p0); }
diff --git a/clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl
index 68583d10d12876..31a6368e3f779a 100644
--- a/clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl
@@ -10,7 +10,12 @@ bool2 test_too_many_arg(float2 p0) {
   // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
 }
 
-bool builtin_bool_to_float_type_promotion(bool p1) {
+bool builtin_sign_bool(bool p1) {
   return __builtin_hlsl_elementwise_sign(p1);
-  // expected-error at -1 {passing 'bool' to parameter of incompatible type 'float'}}
+  // expected-error at -1 {{1st argument must be a vector, integer or floating-point type (was 'bool')}}
+}
+
+int2 builtin_sign_bool2(bool2 p0) {
+  return __builtin_hlsl_elementwise_sign(p0);
+  // expected-error at -1 {{1st argument must be a scalar or vector of integer or floating-point types (was 'bool2' (aka 'vector<bool, 2>'))}}
 }
diff --git a/clang/test/SemaHLSL/BuiltIns/sign_mat-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/sign_mat-errors.hlsl
new file mode 100644
index 00000000000000..5c0032b669069c
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/sign_mat-errors.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -fnative-int16-type -emit-llvm-only -disable-llvm-passes -verify
+
+int2x2 test_too_many_arg(float2x2 p0) {
+  return __builtin_hlsl_elementwise_sign(p0, p0);
+  // expected-error at -1 2 {{cannot pass object of non-trivial type 'float2x2' (aka 'matrix<float, 2, 2>') through variadic function; call will abort at runtime}}
+  // expected-error at -2 {{too many arguments to function call, expected 1, have 2}}
+}
+
+int2x2 test_bool_matrix(bool2x2 p0) {
+  return __builtin_hlsl_elementwise_sign(p0);
+  // expected-error at -1 {{cannot pass object of non-trivial type 'bool2x2' (aka 'matrix<bool, 2, 2>') through variadic function; call will abort at runtime}}
+  // expected-error at -2 {{1st argument must be a scalar or vector of integer or floating-point types (was 'bool2x2' (aka 'matrix<bool, 2, 2>'))}}
+}



More information about the cfe-commits mailing list