Skip to content

[CIR] Omit nsw/nuw on integer vector binops#199123

Open
adams381 wants to merge 1 commit into
llvm:mainfrom
adams381:cir-vector-binop-overflow
Open

[CIR] Omit nsw/nuw on integer vector binops#199123
adams381 wants to merge 1 commit into
llvm:mainfrom
adams381:cir-vector-binop-overflow

Conversation

@adams381
Copy link
Copy Markdown
Contributor

CIRGen was attaching nsw to cir.add on !cir.vector integer types
because the signed-overflow path keys off compType (the element type,
still !s32i), while the verifier only allows nsw/nuw on scalar
!cir.int results. That mismatch showed up 144 times in the libcxx
CIR sweep on std::experimental::simdexperimental/__simd/vec_ext.h
increment/decrement (__data + 1 / __data - 1).

Classic CodeGen never enters the signed-overflow block for vector
computation types; CIR now skips the scalar nsw/nuw path when the
MLIR operand is an integer vector, and uses getAs<VectorType>() for
compType so typedef-wrapped GCC vectors resolve the element type
correctly.

Regression test vector-binop-overflow.cpp checks scalar add still gets
nsw and vector add/sub/mul do not (CIR + LLVM + OGCG).

Signed-overflow flags apply only to scalar CIR integer ops.  Using the
vector element type for the signed-overflow check produced cir.add nsw on
vector types, which the CIR verifier rejects and blocked libcxx
experimental simd (vec_ext.h increment/decrement).

Skip the scalar nsw/nuw path when the MLIR operand is an integer vector,
and resolve vector element types with getAs<VectorType>() so typedef-wrapped
GCC vectors get the right compType.
@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels May 21, 2026
@llvmorg-github-actions
Copy link
Copy Markdown

llvmorg-github-actions Bot commented May 21, 2026

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: adams381

Changes

CIRGen was attaching nsw to cir.add on !cir.vector integer types
because the signed-overflow path keys off compType (the element type,
still !s32i), while the verifier only allows nsw/nuw on scalar
!cir.int results. That mismatch showed up 144 times in the libcxx
CIR sweep on std::experimental::simdexperimental/__simd/vec_ext.h
increment/decrement (__data + 1 / __data - 1).

Classic CodeGen never enters the signed-overflow block for vector
computation types; CIR now skips the scalar nsw/nuw path when the
MLIR operand is an integer vector, and uses getAs&lt;VectorType&gt;() for
compType so typedef-wrapped GCC vectors resolve the element type
correctly.

Regression test vector-binop-overflow.cpp checks scalar add still gets
nsw and vector add/sub/mul do not (CIR + LLVM + OGCG).


Full diff: https://github.com/llvm/llvm-project/pull/199123.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+13-6)
  • (added) clang/test/CIR/CodeGen/vector-binop-overflow.cpp (+49)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 5609bd848f230..f89585e43b511 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1060,9 +1060,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
     else
       result.fullType = e->getType();
     result.compType = result.fullType;
-    if (const auto *vecType = dyn_cast_or_null<VectorType>(result.fullType)) {
+    if (const auto *vecType = result.fullType->getAs<VectorType>())
       result.compType = vecType->getElementType();
-    }
     result.opcode = e->getOpcode();
     result.loc = e->getSourceRange();
     // TODO(cir): Result.FPFeatures
@@ -1557,7 +1556,7 @@ LValue ScalarExprEmitter::emitCompoundAssignLValue(
 
   opInfo.fullType = promotionTypeCR;
   opInfo.compType = opInfo.fullType;
-  if (const auto *vecType = dyn_cast_or_null<VectorType>(opInfo.fullType))
+  if (const auto *vecType = opInfo.fullType->getAs<VectorType>())
     opInfo.compType = vecType->getElementType();
   opInfo.opcode = e->getOpcode();
   opInfo.fpFeatures = e->getFPFeaturesInEffect(cgf.getLangOpts());
@@ -1933,9 +1932,15 @@ static mlir::Value emitPointerArithmetic(CIRGenFunction &cgf,
                                   pointer.getType(), pointer, index);
 }
 
+static bool isIntegerVectorBinOp(mlir::Type ty) {
+  auto vecTy = mlir::dyn_cast<cir::VectorType>(ty);
+  return vecTy && mlir::isa<cir::IntType>(vecTy.getElementType());
+}
+
 mlir::Value ScalarExprEmitter::emitMul(const BinOpInfo &ops) {
   const mlir::Location loc = cgf.getLoc(ops.loc);
-  if (ops.compType->isSignedIntegerOrEnumerationType()) {
+  if (!isIntegerVectorBinOp(ops.lhs.getType()) &&
+      ops.compType->isSignedIntegerOrEnumerationType()) {
     switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
     case LangOptions::SOB_Defined:
       if (!cgf.sanOpts.has(SanitizerKind::SignedIntegerOverflow))
@@ -1990,7 +1995,8 @@ mlir::Value ScalarExprEmitter::emitAdd(const BinOpInfo &ops) {
     return emitPointerArithmetic(cgf, ops, /*isSubtraction=*/false);
 
   const mlir::Location loc = cgf.getLoc(ops.loc);
-  if (ops.compType->isSignedIntegerOrEnumerationType()) {
+  if (!isIntegerVectorBinOp(ops.lhs.getType()) &&
+      ops.compType->isSignedIntegerOrEnumerationType()) {
     switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
     case LangOptions::SOB_Defined:
       if (!cgf.sanOpts.has(SanitizerKind::SignedIntegerOverflow))
@@ -2035,7 +2041,8 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo &ops) {
   const mlir::Location loc = cgf.getLoc(ops.loc);
   // The LHS is always a pointer if either side is.
   if (!mlir::isa<cir::PointerType>(ops.lhs.getType())) {
-    if (ops.compType->isSignedIntegerOrEnumerationType()) {
+    if (!isIntegerVectorBinOp(ops.lhs.getType()) &&
+        ops.compType->isSignedIntegerOrEnumerationType()) {
       switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
       case LangOptions::SOB_Defined: {
         if (!cgf.sanOpts.has(SanitizerKind::SignedIntegerOverflow))
diff --git a/clang/test/CIR/CodeGen/vector-binop-overflow.cpp b/clang/test/CIR/CodeGen/vector-binop-overflow.cpp
new file mode 100644
index 0000000000000..7b020f85d388c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/vector-binop-overflow.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+typedef int vi4 __attribute__((vector_size(16)));
+
+struct SimdStorage {
+  int __data __attribute__((vector_size(16)));
+};
+
+int scalar_add(int a, int b) { return a + b; }
+
+void vector_binops(vi4 a, vi4 b, SimdStorage &s) {
+  vi4 c = a + b;
+  vi4 d = a - b;
+  vi4 e = a * b;
+  s.__data = s.__data + 1;
+  s.__data = s.__data - 1;
+}
+
+// Scalar signed add keeps nsw.
+// CIR: cir.add nsw
+// LLVM: add nsw i32
+// OGCG: add nsw i32
+
+// Vector integer binops must not use nsw/nuw (CIR verifier rejects them).
+// CIR: cir.add %{{.*}}, %{{.*}} : !cir.vector<4 x !s32i>
+// CIR-NOT: cir.add{{.*}}nsw{{.*}}!cir.vector
+// CIR: cir.sub %{{.*}}, %{{.*}} : !cir.vector<4 x !s32i>
+// CIR-NOT: cir.sub{{.*}}nsw{{.*}}!cir.vector
+// CIR: cir.mul %{{.*}}, %{{.*}} : !cir.vector<4 x !s32i>
+// CIR-NOT: cir.mul{{.*}}nsw{{.*}}!cir.vector
+
+// LLVM: add <4 x i32>
+// LLVM-NOT: add nsw <4 x i32>
+// LLVM: sub <4 x i32>
+// LLVM-NOT: sub nsw <4 x i32>
+// LLVM: mul <4 x i32>
+// LLVM-NOT: mul nsw <4 x i32>
+
+// OGCG: add <4 x i32>
+// OGCG-NOT: add nsw <4 x i32>
+// OGCG: sub <4 x i32>
+// OGCG-NOT: sub nsw <4 x i32>
+// OGCG: mul <4 x i32>
+// OGCG-NOT: mul nsw <4 x i32>

@adams381 adams381 requested a review from erichkeane May 21, 2026 22:05
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there separate check-lines for OGCG?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants