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
