[clang] [CIR] Lower elementwise saturating add/sub builtins (PR #203112)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 22 14:06:18 PDT 2026
https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/203112
>From 1dc5a8355a5ad173417f5ef77164d0a6b09e7c59 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Wed, 10 Jun 2026 14:55:12 -0700
Subject: [PATCH 1/3] [CIR] Lower elementwise saturating add/sub builtins
__builtin_elementwise_add_sat and __builtin_elementwise_sub_sat were
sitting in the errorBuiltinNYI batch in emitBuiltinExpr, so any use hit
"unimplemented builtin call". This blocks C++26 std::add_sat/std::sub_sat
(libc++ <__numeric/saturation_arithmetic.h>), which lower straight onto
these builtins.
Lower them the same way classic CodeGen does (CGBuiltin.cpp): evaluate
both operands, take the element type when the argument is a vector, and
pick the signed or unsigned saturating intrinsic from the element's
signedness -- sadd.sat/uadd.sat for add, ssub.sat/usub.sat for sub --
emitted via emitIntrinsicCallOp with the operand type as the result type.
The cases stay ahead of the elementwise_max/min group so the ordering
still follows classic CodeGen.
Scalar and vector, signed and unsigned coverage is added to
builtins-elementwise.c, checking the CIR cir.call_llvm_intrinsic and the
lowered @llvm.{s,u}{add,sub}.sat calls against both the CIR and classic
-emit-llvm paths.
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 19 +++++-
.../CodeGenBuiltins/builtins-elementwise.c | 65 +++++++++++++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index e6de3954a8c69..6dfbd5c735750 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1674,7 +1674,24 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::ValueRange{a, b, c}));
}
case Builtin::BI__builtin_elementwise_add_sat:
- case Builtin::BI__builtin_elementwise_sub_sat:
+ case Builtin::BI__builtin_elementwise_sub_sat: {
+ mlir::Location loc = getLoc(e->getExprLoc());
+ mlir::Value op0 = emitScalarExpr(e->getArg(0));
+ mlir::Value op1 = emitScalarExpr(e->getArg(1));
+ QualType ty = e->getArg(0)->getType();
+ if (const auto *vecTy = ty->getAs<clang::VectorType>())
+ ty = vecTy->getElementType();
+ assert(ty->isIntegerType() &&
+ "elementwise saturating builtins require integer operands");
+ bool isSigned = ty->isSignedIntegerType();
+ llvm::StringRef name;
+ if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_elementwise_add_sat)
+ name = isSigned ? "sadd.sat" : "uadd.sat";
+ else
+ name = isSigned ? "ssub.sat" : "usub.sat";
+ return RValue::get(builder.emitIntrinsicCallOp(loc, name, op0.getType(),
+ mlir::ValueRange{op0, op1}));
+ }
case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min:
case Builtin::BI__builtin_elementwise_maxnum:
diff --git a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
index c04739d737632..eef16addd392c 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
@@ -7,6 +7,7 @@
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-ogcg.ll %s
typedef int vint4 __attribute__((ext_vector_type(4)));
+typedef unsigned int vuint4 __attribute__((ext_vector_type(4)));
typedef short vshort8 __attribute__((ext_vector_type(8)));
typedef float vfloat4 __attribute__((ext_vector_type(4)));
typedef double vdouble4 __attribute__((ext_vector_type(4)));
@@ -509,3 +510,67 @@ void test_builtin_elementwise_fshr(long long int i1, long long int i2,
// LLVM: call <4 x i32> @llvm.fshr.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}}, <4 x i32> %{{.*}})
vu1 = __builtin_elementwise_fshr(vu1, vu2, vu3);
}
+
+void test_builtin_elementwise_add_sat(int i1, int i2, unsigned u1, unsigned u2,
+ short s1, short s2, vint4 vi1, vint4 vi2,
+ vuint4 vu1, vuint4 vu2, vshort8 vs1,
+ vshort8 vs2) {
+ // CIR-LABEL: test_builtin_elementwise_add_sat
+ // LLVM-LABEL: test_builtin_elementwise_add_sat
+
+ // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!s32i, !s32i) -> !s32i
+ // LLVM: call i32 @llvm.sadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+ i1 = __builtin_elementwise_add_sat(i1, i2);
+
+ // CIR: cir.call_llvm_intrinsic "uadd.sat" %{{.*}}, %{{.*}} : (!u32i, !u32i) -> !u32i
+ // LLVM: call i32 @llvm.uadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+ u1 = __builtin_elementwise_add_sat(u1, u2);
+
+ // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!s16i, !s16i) -> !s16i
+ // LLVM: call i16 @llvm.sadd.sat.i16(i16 %{{.*}}, i16 %{{.*}})
+ s1 = __builtin_elementwise_add_sat(s1, s2);
+
+ // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>) -> !cir.vector<4 x !s32i>
+ // LLVM: call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
+ vi1 = __builtin_elementwise_add_sat(vi1, vi2);
+
+ // CIR: cir.call_llvm_intrinsic "uadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !u32i>, !cir.vector<4 x !u32i>) -> !cir.vector<4 x !u32i>
+ // LLVM: call <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
+ vu1 = __builtin_elementwise_add_sat(vu1, vu2);
+
+ // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<8 x !s16i>, !cir.vector<8 x !s16i>) -> !cir.vector<8 x !s16i>
+ // LLVM: call <8 x i16> @llvm.sadd.sat.v8i16(<8 x i16> %{{.*}}, <8 x i16> %{{.*}})
+ vs1 = __builtin_elementwise_add_sat(vs1, vs2);
+}
+
+void test_builtin_elementwise_sub_sat(int i1, int i2, unsigned u1, unsigned u2,
+ short s1, short s2, vint4 vi1, vint4 vi2,
+ vuint4 vu1, vuint4 vu2, vshort8 vs1,
+ vshort8 vs2) {
+ // CIR-LABEL: test_builtin_elementwise_sub_sat
+ // LLVM-LABEL: test_builtin_elementwise_sub_sat
+
+ // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!s32i, !s32i) -> !s32i
+ // LLVM: call i32 @llvm.ssub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+ i1 = __builtin_elementwise_sub_sat(i1, i2);
+
+ // CIR: cir.call_llvm_intrinsic "usub.sat" %{{.*}}, %{{.*}} : (!u32i, !u32i) -> !u32i
+ // LLVM: call i32 @llvm.usub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+ u1 = __builtin_elementwise_sub_sat(u1, u2);
+
+ // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!s16i, !s16i) -> !s16i
+ // LLVM: call i16 @llvm.ssub.sat.i16(i16 %{{.*}}, i16 %{{.*}})
+ s1 = __builtin_elementwise_sub_sat(s1, s2);
+
+ // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>) -> !cir.vector<4 x !s32i>
+ // LLVM: call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
+ vi1 = __builtin_elementwise_sub_sat(vi1, vi2);
+
+ // CIR: cir.call_llvm_intrinsic "usub.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !u32i>, !cir.vector<4 x !u32i>) -> !cir.vector<4 x !u32i>
+ // LLVM: call <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
+ vu1 = __builtin_elementwise_sub_sat(vu1, vu2);
+
+ // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!cir.vector<8 x !s16i>, !cir.vector<8 x !s16i>) -> !cir.vector<8 x !s16i>
+ // LLVM: call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> %{{.*}}, <8 x i16> %{{.*}})
+ vs1 = __builtin_elementwise_sub_sat(vs1, vs2);
+}
>From 4335d0ee220cb1c6124fd8d73f07a5fb326d379e Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Mon, 15 Jun 2026 13:52:14 -0700
Subject: [PATCH 2/3] [CIR] Model elementwise sat builtins as saturated
cir.add/sub
Route __builtin_elementwise_add_sat/sub_sat through createAdd/createSub
with OverflowBehavior::Saturated instead of emitting the llvm.*.sat
intrinsics directly. The saturated cir.add/cir.sub lower to the same
intrinsics, choosing signed vs unsigned from the operand type.
cir.add/cir.sub do not model i1 arithmetic, so a bool operand (scalar
or ext-vector) is reported with errorNYI before emitScalarExpr rather
than crashing the op verifier (or the NYI bool-vector load).
Addresses review feedback on PR #203112.
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 32 +++++++++++--------
.../builtins-elementwise-bool-nyi.c | 14 ++++++++
.../CodeGenBuiltins/builtins-elementwise.c | 24 +++++++-------
3 files changed, 45 insertions(+), 25 deletions(-)
create mode 100644 clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 6dfbd5c735750..6c3bae04a8bb3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1675,22 +1675,28 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
case Builtin::BI__builtin_elementwise_add_sat:
case Builtin::BI__builtin_elementwise_sub_sat: {
+ // cir.add/cir.sub do not model i1 arithmetic, so a bool-element
+ // saturating add/sub is not representable through the saturated op.
+ // Check the AST element type and bail before emitScalarExpr: an
+ // ext-vector-of-bool operand would otherwise hit the NYI bool-vector
+ // load, which returns a null value and would crash op0.getType().
+ QualType argTy = e->getArg(0)->getType();
+ if (const auto *vecTy = argTy->getAs<clang::VectorType>())
+ argTy = vecTy->getElementType();
+ if (argTy->isBooleanType()) {
+ cgm.errorNYI(e->getSourceRange(),
+ "saturating add/sub on a boolean operand");
+ return RValue::get(nullptr);
+ }
mlir::Location loc = getLoc(e->getExprLoc());
mlir::Value op0 = emitScalarExpr(e->getArg(0));
mlir::Value op1 = emitScalarExpr(e->getArg(1));
- QualType ty = e->getArg(0)->getType();
- if (const auto *vecTy = ty->getAs<clang::VectorType>())
- ty = vecTy->getElementType();
- assert(ty->isIntegerType() &&
- "elementwise saturating builtins require integer operands");
- bool isSigned = ty->isSignedIntegerType();
- llvm::StringRef name;
- if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_elementwise_add_sat)
- name = isSigned ? "sadd.sat" : "uadd.sat";
- else
- name = isSigned ? "ssub.sat" : "usub.sat";
- return RValue::get(builder.emitIntrinsicCallOp(loc, name, op0.getType(),
- mlir::ValueRange{op0, op1}));
+ mlir::Value val =
+ builtinIDIfNoAsmLabel == Builtin::BI__builtin_elementwise_add_sat
+ ? builder.createAdd(loc, op0, op1, cir::OverflowBehavior::Saturated)
+ : builder.createSub(loc, op0, op1,
+ cir::OverflowBehavior::Saturated);
+ return RValue::get(val);
}
case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min:
diff --git a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c
new file mode 100644
index 0000000000000..933a1298ae5b9
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -verify -emit-cir -o -
+
+typedef _Bool vbool4 __attribute__((ext_vector_type(4)));
+
+void test_bool_sat(_Bool a, _Bool b, vbool4 va, vbool4 vb) {
+ // expected-error at +1 {{ClangIR code gen Not Yet Implemented: saturating add/sub on a boolean operand}}
+ (void)__builtin_elementwise_add_sat(a, b);
+ // expected-error at +1 {{ClangIR code gen Not Yet Implemented: saturating add/sub on a boolean operand}}
+ (void)__builtin_elementwise_sub_sat(a, b);
+ // expected-error at +1 {{ClangIR code gen Not Yet Implemented: saturating add/sub on a boolean operand}}
+ (void)__builtin_elementwise_add_sat(va, vb);
+ // expected-error at +1 {{ClangIR code gen Not Yet Implemented: saturating add/sub on a boolean operand}}
+ (void)__builtin_elementwise_sub_sat(va, vb);
+}
diff --git a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
index eef16addd392c..42525e6744190 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c
@@ -518,27 +518,27 @@ void test_builtin_elementwise_add_sat(int i1, int i2, unsigned u1, unsigned u2,
// CIR-LABEL: test_builtin_elementwise_add_sat
// LLVM-LABEL: test_builtin_elementwise_add_sat
- // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!s32i, !s32i) -> !s32i
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !s32i
// LLVM: call i32 @llvm.sadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
i1 = __builtin_elementwise_add_sat(i1, i2);
- // CIR: cir.call_llvm_intrinsic "uadd.sat" %{{.*}}, %{{.*}} : (!u32i, !u32i) -> !u32i
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !u32i
// LLVM: call i32 @llvm.uadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
u1 = __builtin_elementwise_add_sat(u1, u2);
- // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!s16i, !s16i) -> !s16i
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !s16i
// LLVM: call i16 @llvm.sadd.sat.i16(i16 %{{.*}}, i16 %{{.*}})
s1 = __builtin_elementwise_add_sat(s1, s2);
- // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>) -> !cir.vector<4 x !s32i>
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !cir.vector<4 x !s32i>
// LLVM: call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
vi1 = __builtin_elementwise_add_sat(vi1, vi2);
- // CIR: cir.call_llvm_intrinsic "uadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !u32i>, !cir.vector<4 x !u32i>) -> !cir.vector<4 x !u32i>
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !cir.vector<4 x !u32i>
// LLVM: call <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
vu1 = __builtin_elementwise_add_sat(vu1, vu2);
- // CIR: cir.call_llvm_intrinsic "sadd.sat" %{{.*}}, %{{.*}} : (!cir.vector<8 x !s16i>, !cir.vector<8 x !s16i>) -> !cir.vector<8 x !s16i>
+ // CIR: cir.add sat %{{.*}}, %{{.*}} : !cir.vector<8 x !s16i>
// LLVM: call <8 x i16> @llvm.sadd.sat.v8i16(<8 x i16> %{{.*}}, <8 x i16> %{{.*}})
vs1 = __builtin_elementwise_add_sat(vs1, vs2);
}
@@ -550,27 +550,27 @@ void test_builtin_elementwise_sub_sat(int i1, int i2, unsigned u1, unsigned u2,
// CIR-LABEL: test_builtin_elementwise_sub_sat
// LLVM-LABEL: test_builtin_elementwise_sub_sat
- // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!s32i, !s32i) -> !s32i
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !s32i
// LLVM: call i32 @llvm.ssub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
i1 = __builtin_elementwise_sub_sat(i1, i2);
- // CIR: cir.call_llvm_intrinsic "usub.sat" %{{.*}}, %{{.*}} : (!u32i, !u32i) -> !u32i
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !u32i
// LLVM: call i32 @llvm.usub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
u1 = __builtin_elementwise_sub_sat(u1, u2);
- // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!s16i, !s16i) -> !s16i
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !s16i
// LLVM: call i16 @llvm.ssub.sat.i16(i16 %{{.*}}, i16 %{{.*}})
s1 = __builtin_elementwise_sub_sat(s1, s2);
- // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !s32i>, !cir.vector<4 x !s32i>) -> !cir.vector<4 x !s32i>
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !cir.vector<4 x !s32i>
// LLVM: call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
vi1 = __builtin_elementwise_sub_sat(vi1, vi2);
- // CIR: cir.call_llvm_intrinsic "usub.sat" %{{.*}}, %{{.*}} : (!cir.vector<4 x !u32i>, !cir.vector<4 x !u32i>) -> !cir.vector<4 x !u32i>
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !cir.vector<4 x !u32i>
// LLVM: call <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %{{.*}}, <4 x i32> %{{.*}})
vu1 = __builtin_elementwise_sub_sat(vu1, vu2);
- // CIR: cir.call_llvm_intrinsic "ssub.sat" %{{.*}}, %{{.*}} : (!cir.vector<8 x !s16i>, !cir.vector<8 x !s16i>) -> !cir.vector<8 x !s16i>
+ // CIR: cir.sub sat %{{.*}}, %{{.*}} : !cir.vector<8 x !s16i>
// LLVM: call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> %{{.*}}, <8 x i16> %{{.*}})
vs1 = __builtin_elementwise_sub_sat(vs1, vs2);
}
>From ce005041cd9204e71152417282415c898a2125a9 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Mon, 22 Jun 2026 14:06:06 -0700
Subject: [PATCH 3/3] [CIR] Simplify bool guard for elementwise saturating
add/sub
Drop the manual VectorType unwrap in the elementwise add_sat/sub_sat case:
the bool-operand bail only needs the argument's own type, so check
isBooleanType() || isExtVectorBoolType() directly. After emitting the
operands, assert the value is integer or vector-of-integer, mirroring the
int-or-int-vector assertion classic CodeGen uses on the saturating builtins.
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 6c3bae04a8bb3..0fb35e2c16037 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1677,13 +1677,11 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_elementwise_sub_sat: {
// cir.add/cir.sub do not model i1 arithmetic, so a bool-element
// saturating add/sub is not representable through the saturated op.
- // Check the AST element type and bail before emitScalarExpr: an
- // ext-vector-of-bool operand would otherwise hit the NYI bool-vector
- // load, which returns a null value and would crash op0.getType().
+ // Bail before emitScalarExpr: an ext-vector-of-bool operand would
+ // otherwise hit the NYI bool-vector load, which returns a null value
+ // and would crash op0.getType().
QualType argTy = e->getArg(0)->getType();
- if (const auto *vecTy = argTy->getAs<clang::VectorType>())
- argTy = vecTy->getElementType();
- if (argTy->isBooleanType()) {
+ if (argTy->isBooleanType() || argTy->isExtVectorBoolType()) {
cgm.errorNYI(e->getSourceRange(),
"saturating add/sub on a boolean operand");
return RValue::get(nullptr);
@@ -1691,6 +1689,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
mlir::Location loc = getLoc(e->getExprLoc());
mlir::Value op0 = emitScalarExpr(e->getArg(0));
mlir::Value op1 = emitScalarExpr(e->getArg(1));
+ assert(cir::isIntOrVectorOfIntType(op0.getType()) &&
+ "elementwise saturating add/sub requires integer operands");
mlir::Value val =
builtinIDIfNoAsmLabel == Builtin::BI__builtin_elementwise_add_sat
? builder.createAdd(loc, op0, op1, cir::OverflowBehavior::Saturated)
More information about the cfe-commits
mailing list