[clang] [HLSL] Fix crash when comparing two half vectors (PR #214332)
Kaitlin Peng via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 16:52:27 PDT 2026
https://github.com/kmpeng updated https://github.com/llvm/llvm-project/pull/214332
>From 51e1a3eae51ca0a0a5034782a5fe02234650e912 Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Wed, 5 Aug 2026 12:59:14 -0700
Subject: [PATCH 1/2] fix conversion bug and add tests
---
clang/lib/Sema/SemaExpr.cpp | 7 ++
.../Operators/half-vector-comparisons.hlsl | 70 +++++++++++++++++++
.../Operators/half-vector-comparisons.hlsl | 63 +++++++++++++++++
3 files changed, 140 insertions(+)
create mode 100644 clang/test/CodeGenHLSL/Operators/half-vector-comparisons.hlsl
create mode 100644 clang/test/SemaHLSL/Operators/half-vector-comparisons.hlsl
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 59b8c9b60663c..e18be2a03bd99 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15753,6 +15753,13 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
ConvertHalfVec =
needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get());
+ // Skip the vector conversion when the result is not a half/short vector (e.g.
+ // HLSL comparisons).
+ if (ConvertHalfVec && ResultTy->isVectorType() &&
+ !isVector(ResultTy, Context.HalfTy) &&
+ !isVector(ResultTy, Context.ShortTy))
+ ConvertHalfVec = false;
+
// Check for array bounds violations for both sides of the BinaryOperator
CheckArrayAccess(LHS.get());
CheckArrayAccess(RHS.get());
diff --git a/clang/test/CodeGenHLSL/Operators/half-vector-comparisons.hlsl b/clang/test/CodeGenHLSL/Operators/half-vector-comparisons.hlsl
new file mode 100644
index 0000000000000..c289f372f4542
--- /dev/null
+++ b/clang/test/CodeGenHLSL/Operators/half-vector-comparisons.hlsl
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NATIVE_HALF
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF
+
+// Regression test for issue llvm/llvm-project#213814
+
+// CHECK-LABEL: test_lt
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} olt <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} olt <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_lt(half4 a, half4 b) {
+ return a < b;
+}
+
+// CHECK-LABEL: test_le
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} ole <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} ole <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_le(half4 a, half4 b) {
+ return a <= b;
+}
+
+// CHECK-LABEL: test_gt
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} ogt <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} ogt <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_gt(half4 a, half4 b) {
+ return a > b;
+}
+
+// CHECK-LABEL: test_ge
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} oge <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} oge <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_ge(half4 a, half4 b) {
+ return a >= b;
+}
+
+// CHECK-LABEL: test_eq
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} oeq <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} oeq <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_eq(half4 a, half4 b) {
+ return a == b;
+}
+
+// CHECK-LABEL: test_ne
+// NATIVE_HALF: [[CMP:%.*]] = fcmp {{.*}} une <4 x half>
+// NATIVE_HALF-NEXT: [[SEXT:%.*]] = sext <4 x i1> [[CMP]] to <4 x i16>
+// NATIVE_HALF-NEXT: [[RET:%.*]] = sext <4 x i16> [[SEXT]] to <4 x i32>
+// NO_HALF: [[CMP:%.*]] = fcmp {{.*}} une <4 x float>
+// NO_HALF-NEXT: [[RET:%.*]] = sext <4 x i1> [[CMP]] to <4 x i32>
+// CHECK-NEXT: ret <4 x i32> [[RET]]
+int4 test_ne(half4 a, half4 b) {
+ return a != b;
+}
diff --git a/clang/test/SemaHLSL/Operators/half-vector-comparisons.hlsl b/clang/test/SemaHLSL/Operators/half-vector-comparisons.hlsl
new file mode 100644
index 0000000000000..f3be50ef6fc62
--- /dev/null
+++ b/clang/test/SemaHLSL/Operators/half-vector-comparisons.hlsl
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -ast-dump -ast-dump-filter=test | FileCheck %s
+
+// Regression test for issue llvm/llvm-project#213814
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_lt 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '<'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_lt(half4 a, half4 b) {
+ return a < b;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_le 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '<='
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_le(half4 a, half4 b) {
+ return a <= b;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_gt 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_gt(half4 a, half4 b) {
+ return a > b;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_ge 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '>='
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_ge(half4 a, half4 b) {
+ return a >= b;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_eq 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '=='
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_eq(half4 a, half4 b) {
+ return a == b;
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_ne 'int4 (half4, half4)'
+// CHECK: BinaryOperator {{.*}} 'vector<int, 4>' '!='
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'a' 'half4':'vector<half, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half4':'vector<half, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'b' 'half4':'vector<half, 4>'
+int4 test_ne(half4 a, half4 b) {
+ return a != b;
+}
>From 2433d8f47c0cbc017fd5cea8302180aae92d9184 Mon Sep 17 00:00:00 2001
From: kmpeng <kaitlinpeng at microsoft.com>
Date: Wed, 5 Aug 2026 16:52:13 -0700
Subject: [PATCH 2/2] move fix into `needsConversionOfHalfVec`
---
clang/lib/Sema/SemaExpr.cpp | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e18be2a03bd99..3065c7ea23870 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15502,10 +15502,17 @@ static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS,
/// Returns true if conversion between vectors of halfs and vectors of floats
/// is needed.
static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx,
- Expr *E0, Expr *E1 = nullptr) {
+ QualType ResultTy, Expr *E0,
+ Expr *E1 = nullptr) {
if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType)
return false;
+ // The conversion truncates the result to a half/short vector, so it shouldn't
+ // apply when the result is not that type (e.g. HLSL comparisons).
+ if (ResultTy->isVectorType() && !isVector(ResultTy, Ctx.HalfTy) &&
+ !isVector(ResultTy, Ctx.ShortTy))
+ return false;
+
auto HasVectorOfHalfType = [&Ctx](Expr *E) {
QualType Ty = E->IgnoreImplicit()->getType();
@@ -15750,15 +15757,8 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
(Opc == BO_Comma || isVector(RHS.get()->getType(), Context.HalfTy) ==
isVector(LHS.get()->getType(), Context.HalfTy)) &&
"both sides are half vectors or neither sides are");
- ConvertHalfVec =
- needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get());
-
- // Skip the vector conversion when the result is not a half/short vector (e.g.
- // HLSL comparisons).
- if (ConvertHalfVec && ResultTy->isVectorType() &&
- !isVector(ResultTy, Context.HalfTy) &&
- !isVector(ResultTy, Context.ShortTy))
- ConvertHalfVec = false;
+ ConvertHalfVec = needsConversionOfHalfVec(ConvertHalfVec, Context, ResultTy,
+ LHS.get(), RHS.get());
// Check for array bounds violations for both sides of the BinaryOperator
CheckArrayAccess(LHS.get());
@@ -16311,7 +16311,8 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
// float vector and truncating the result back to a half vector. For now,
// we do this only when HalfArgsAndReturns is set (that is, when the
// target is arm or arm64).
- ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get());
+ ConvertHalfVec = needsConversionOfHalfVec(
+ true, Context, Input.get()->getType(), Input.get());
// If the operand is a half vector, promote it to a float vector.
if (ConvertHalfVec)
More information about the cfe-commits
mailing list