Author: adams381
Date: 2026-06-23T10:56:45-05:00
New Revision: 9acc93f96af24fd3b3598f3cc51520aaff6e688c

URL: 
https://github.com/llvm/llvm-project/commit/9acc93f96af24fd3b3598f3cc51520aaff6e688c
DIFF: 
https://github.com/llvm/llvm-project/commit/9acc93f96af24fd3b3598f3cc51520aaff6e688c.diff

LOG: [CIR] Lower elementwise saturating add/sub builtins (#203112)

`__builtin_elementwise_add_sat` and `__builtin_elementwise_sub_sat` were
still in the `errorBuiltinNYI` batch in `emitBuiltinExpr`, so any use
hit "unimplemented builtin call". That blocks C++26
`std::add_sat`/`std::sub_sat` (libc++
`<__numeric/saturation_arithmetic.h>`), which lower directly onto these
builtins.

This lowers them the way classic CodeGen does in `CGBuiltin.cpp`: select
the signed or unsigned saturating intrinsic from the operand's element
type (`sadd.sat`/`uadd.sat` for add, `ssub.sat`/`usub.sat` for sub).

Test coverage in `builtins-elementwise.c` exercises signed and unsigned,
scalar and vector, at i32 and i16 widths, checking the CIR
`cir.call_llvm_intrinsic` and the lowered `@llvm.{s,u}{add,sub}.sat`
calls on both the CIR and classic `-emit-llvm` paths.

Added: 
    clang/test/CIR/CodeGenBuiltins/builtins-elementwise-bool-nyi.c

Modified: 
    clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
    clang/test/CIR/CodeGenBuiltins/builtins-elementwise.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 4fb7ffc13a2ce..e206353aac2c9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1695,7 +1695,30 @@ 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: {
+    // cir.add/cir.sub do not model i1 arithmetic, so a bool-element
+    // saturating add/sub is not representable through the saturated op.
+    // 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 (argTy->isBooleanType() || argTy->isExtVectorBoolType()) {
+      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));
+    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)
+            : builder.createSub(loc, op0, op1,
+                                cir::OverflowBehavior::Saturated);
+    return RValue::get(val);
+  }
   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-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@+1 {{ClangIR code gen Not Yet Implemented: saturating 
add/sub on a boolean operand}}
+  (void)__builtin_elementwise_add_sat(a, b);
+  // expected-error@+1 {{ClangIR code gen Not Yet Implemented: saturating 
add/sub on a boolean operand}}
+  (void)__builtin_elementwise_sub_sat(a, b);
+  // expected-error@+1 {{ClangIR code gen Not Yet Implemented: saturating 
add/sub on a boolean operand}}
+  (void)__builtin_elementwise_add_sat(va, vb);
+  // expected-error@+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 c04739d737632..42525e6744190 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.add sat %{{.*}}, %{{.*}} : !s32i
+  // LLVM: call i32 @llvm.sadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+  i1 = __builtin_elementwise_add_sat(i1, i2);
+
+  // CIR: cir.add sat %{{.*}}, %{{.*}} : !u32i
+  // LLVM: call i32 @llvm.uadd.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+  u1 = __builtin_elementwise_add_sat(u1, u2);
+
+  // CIR: cir.add sat %{{.*}}, %{{.*}} : !s16i
+  // LLVM: call i16 @llvm.sadd.sat.i16(i16 %{{.*}}, i16 %{{.*}})
+  s1 = __builtin_elementwise_add_sat(s1, s2);
+
+  // 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.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.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);
+}
+
+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.sub sat %{{.*}}, %{{.*}} : !s32i
+  // LLVM: call i32 @llvm.ssub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+  i1 = __builtin_elementwise_sub_sat(i1, i2);
+
+  // CIR: cir.sub sat %{{.*}}, %{{.*}} : !u32i
+  // LLVM: call i32 @llvm.usub.sat.i32(i32 %{{.*}}, i32 %{{.*}})
+  u1 = __builtin_elementwise_sub_sat(u1, u2);
+
+  // CIR: cir.sub sat %{{.*}}, %{{.*}} : !s16i
+  // LLVM: call i16 @llvm.ssub.sat.i16(i16 %{{.*}}, i16 %{{.*}})
+  s1 = __builtin_elementwise_sub_sat(s1, s2);
+
+  // 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.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.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);
+}


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

Reply via email to