[CIR] Omit nsw/nuw on integer vector binops#199123
Conversation
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.
|
@llvm/pr-subscribers-clangir @llvm/pr-subscribers-clang Author: adams381 ChangesCIRGen was attaching Classic CodeGen never enters the signed-overflow block for vector Regression test Full diff: https://github.com/llvm/llvm-project/pull/199123.diff 2 Files Affected:
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>
|
| // 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 |
There was a problem hiding this comment.
Why is there separate check-lines for OGCG?
CIRGen was attaching
nswtocir.addon!cir.vectorinteger typesbecause the signed-overflow path keys off
compType(the element type,still
!s32i), while the verifier only allowsnsw/nuwon scalar!cir.intresults. That mismatch showed up 144 times in the libcxxCIR sweep on
std::experimental::simd—experimental/__simd/vec_ext.hincrement/decrement (
__data + 1/__data - 1).Classic CodeGen never enters the signed-overflow block for vector
computation types; CIR now skips the scalar
nsw/nuwpath when theMLIR operand is an integer vector, and uses
getAs<VectorType>()forcompTypeso typedef-wrapped GCC vectors resolve the element typecorrectly.
Regression test
vector-binop-overflow.cppchecks scalar add still getsnswand vector add/sub/mul do not (CIR + LLVM + OGCG).