https://github.com/xlauko updated 
https://github.com/llvm/llvm-project/pull/195618

>From 42d08145d25c2a70814513ab2eb8f0e38a6b329b Mon Sep 17 00:00:00 2001
From: Henrich Lauko <[email protected]>
Date: Tue, 27 Oct 2026 12:52:51 +0000
Subject: [PATCH] [CIR] Store nsw/nuw as an OverflowFlags bit enum

cir.add, cir.sub and cir.mul carried nsw and nuw as two separate unit
properties, and lowering mapped them to LLVM with an early return that
picked nuw over nsw. An op with both flags therefore lowered to
`overflow<nuw>` and lost nsw.

Replace the two properties with a single OverflowFlags bit-enum property
whose layout matches mlir::LLVM::IntegerOverflowFlags, so lowering casts
it directly and keeps both flags. The assembly keeps the bare `nsw` and
`nuw` keywords (now accepted in either order, printed as `nsw nuw`), so
existing CIR in the custom assembly form is unchanged. The builder helper
that converts OverflowBehavior documents that Saturated is set separately,
and createMul asserts it is not requested.

Assisted-by: Claude Code (Claude Opus 5.5).
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      | 23 ++++++--
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 15 +++++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 39 ++++++-------
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 33 ++++++++++-
 .../TargetLowering/LowerItaniumCXXABI.cpp     |  8 +--
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 ++--
 clang/test/CIR/IR/invalid-overflow-flags.cir  | 49 +++++++++++++++++
 clang/test/CIR/IR/overflow-flags.cir          | 55 +++++++++++++++++++
 clang/test/CIR/Lowering/overflow-flags.cir    | 54 ++++++++++++++++++
 9 files changed, 252 insertions(+), 36 deletions(-)
 create mode 100644 clang/test/CIR/IR/invalid-overflow-flags.cir
 create mode 100644 clang/test/CIR/IR/overflow-flags.cir
 create mode 100644 clang/test/CIR/Lowering/overflow-flags.cir

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 489ed57695d299..5a1409a5a755b6 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -62,6 +62,18 @@ constexpr bool testFlag(OverflowBehavior ob, 
OverflowBehavior flag) {
   return (ob & flag) != OverflowBehavior::None;
 }
 
+/// Returns the nsw/nuw bits of \p ob as overflow flags. Saturated is not an
+/// overflow flag: the ops that support it carry it as a separate `saturated`
+/// property, which createAdd and createSub set on their own.
+inline OverflowFlags toOverflowFlags(OverflowBehavior ob) {
+  OverflowFlags flags = OverflowFlags::none;
+  if (testFlag(ob, OverflowBehavior::NoSignedWrap))
+    flags = flags | OverflowFlags::nsw;
+  if (testFlag(ob, OverflowBehavior::NoUnsignedWrap))
+    flags = flags | OverflowFlags::nuw;
+  return flags;
+}
+
 class CIRBaseBuilderTy : public mlir::OpBuilder {
 
 public:
@@ -855,9 +867,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
 
   mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
                         OverflowBehavior ob = OverflowBehavior::None) {
+    assert(!testFlag(ob, OverflowBehavior::Saturated) &&
+           "cir.mul has no saturated form");
     auto op = cir::MulOp::create(*this, loc, lhs, rhs);
-    op.setNoUnsignedWrap(testFlag(ob, OverflowBehavior::NoUnsignedWrap));
-    op.setNoSignedWrap(testFlag(ob, OverflowBehavior::NoSignedWrap));
+    op.setOverflowFlags(toOverflowFlags(ob));
     return op;
   }
   mlir::Value createNSWMul(mlir::Location loc, mlir::Value lhs,
@@ -872,8 +885,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   mlir::Value createSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
                         OverflowBehavior ob = OverflowBehavior::None) {
     auto op = cir::SubOp::create(*this, loc, lhs, rhs);
-    op.setNoUnsignedWrap(testFlag(ob, OverflowBehavior::NoUnsignedWrap));
-    op.setNoSignedWrap(testFlag(ob, OverflowBehavior::NoSignedWrap));
+    op.setOverflowFlags(toOverflowFlags(ob));
     op.setSaturated(testFlag(ob, OverflowBehavior::Saturated));
     return op;
   }
@@ -891,8 +903,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
                         OverflowBehavior ob = OverflowBehavior::None) {
     auto op = cir::AddOp::create(*this, loc, lhs, rhs);
-    op.setNoUnsignedWrap(testFlag(ob, OverflowBehavior::NoUnsignedWrap));
-    op.setNoSignedWrap(testFlag(ob, OverflowBehavior::NoSignedWrap));
+    op.setOverflowFlags(toOverflowFlags(ob));
     op.setSaturated(testFlag(ob, OverflowBehavior::Saturated));
     return op;
   }
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index e04ac4bd9b8b84..55c84fdc317c02 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1121,6 +1121,21 @@ def CIR_FastMathFlags : CIR_I32BitEnum<
 
 def CIR_FastMathFlagsAttr : CIR_EnumAttr<CIR_FastMathFlags, "fastmath">;
 
+//===----------------------------------------------------------------------===//
+// OverflowFlags
+//===----------------------------------------------------------------------===//
+
+// The bit layout matches mlir::LLVM::IntegerOverflowFlags so that lowering can
+// cast between the two.
+def CIR_OFnone : I32BitEnumCaseNone<"none">;
+def CIR_OFnsw  : I32BitEnumCaseBit<"nsw", 0>;
+def CIR_OFnuw  : I32BitEnumCaseBit<"nuw", 1>;
+
+def CIR_OverflowFlags : CIR_I32BitEnum<
+    "OverflowFlags", "integer overflow flags", [
+  CIR_OFnone, CIR_OFnsw, CIR_OFnuw
+]>;
+
 
//===----------------------------------------------------------------------===//
 // GlobalViewAttr
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bd762aae3e1d7e..20b25f46bc817e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2831,6 +2831,11 @@ class CIR_BinaryOp<string mnemonic, Type type, 
list<Trait> traits = [],
   }];
 }
 
+// Integer overflow flags (nsw/nuw) stored as a single bit enum. They are
+// printed as bare `nsw` and `nuw` keywords, as in LLVM IR.
+defvar CIR_OverflowFlagsProp =
+    DefaultValuedProp<EnumProp<CIR_OverflowFlags>, "cir::OverflowFlags::none">;
+
 // Base class for binary ops that support integer overflow flags (nsw/nuw)
 // and saturated arithmetic.
 class CIR_BinaryOpWithOverflowFlags<string mnemonic, Type type,
@@ -2839,14 +2844,12 @@ class CIR_BinaryOpWithOverflowFlags<string mnemonic, 
Type type,
 {
   let arguments = (ins
     type:$lhs, type:$rhs,
-    UnitProp:$no_signed_wrap,
-    UnitProp:$no_unsigned_wrap,
+    CIR_OverflowFlagsProp:$overflow_flags,
     UnitProp:$saturated
   );
 
   let assemblyFormat = [{
-    (`nsw` $no_signed_wrap^)?
-    (`nuw` $no_unsigned_wrap^)?
+    custom<OverflowFlags>($overflow_flags)
     (`sat` $saturated^)?
     $lhs `,` $rhs `:` type($lhs) attr-dict
   }];
@@ -2866,9 +2869,9 @@ def CIR_AddOp
     operands and the result must have the same integer, vector-of-integer or
     matrix-of-integer type.
 
-    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) unit
-    attributes indicate that the result is poison if signed or unsigned
-    overflow occurs, respectively. The optional `sat` (saturated) attribute
+    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) flags
+    indicate that the result is poison if signed or unsigned overflow occurs,
+    respectively; both may be given. The optional `sat` (saturated) flag
     clamps the result to the type's representable range instead of wrapping.
     The `nsw`/`nuw` flags and `sat` are mutually exclusive.
 
@@ -2877,7 +2880,7 @@ def CIR_AddOp
     ```
     %0 = cir.add %a, %b : !s32i
     %1 = cir.add nsw %a, %b : !s32i
-    %2 = cir.add nuw %a, %b : !u32i
+    %2 = cir.add nsw nuw %a, %b : !s32i
     %3 = cir.add sat %a, %b : !s32i
     %4 = cir.add %va, %vb : !cir.vector<4 x !s32i>
     %5 = cir.add %ma, %mb : !cir.matrix<3 x 3 x !s32i>
@@ -2897,9 +2900,9 @@ def CIR_SubOp
     operands and the result must have the same integer, vector-of-integer or
     matrix-of-integer type.
 
-    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) unit
-    attributes indicate that the result is poison if signed or unsigned
-    overflow occurs, respectively. The optional `sat` (saturated) attribute
+    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) flags
+    indicate that the result is poison if signed or unsigned overflow occurs,
+    respectively; both may be given. The optional `sat` (saturated) flag
     clamps the result to the type's representable range. The `nsw`/`nuw`
     flags and `sat` are mutually exclusive.
 
@@ -2926,16 +2929,16 @@ def CIR_MulOp : CIR_BinaryOp<"mul", 
CIR_AnyIntOrVecOrMatrixOfIntType> {
     operands and the result must have the same integer or vector-of-integer
     or matrix-of-integer type.
 
-    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) unit
-    attributes indicate that the result is poison if signed or unsigned
-    overflow occurs, respectively.
+    The optional `nsw` (no signed wrap) and `nuw` (no unsigned wrap) flags
+    indicate that the result is poison if signed or unsigned overflow occurs,
+    respectively; both may be given.
 
     Example:
 
     ```
     %0 = cir.mul %a, %b : !s32i
     %1 = cir.mul nsw %a, %b : !s32i
-    %2 = cir.mul nuw %a, %b : !u32i
+    %2 = cir.mul nsw nuw %a, %b : !u32i
     %3 = cir.mul %va, %vb : !cir.vector<4 x !s32i>
     %4 = cir.mul %ma, %mb : !cir.matrix<3 x 3 x !s32i>
     ```
@@ -2944,13 +2947,11 @@ def CIR_MulOp : CIR_BinaryOp<"mul", 
CIR_AnyIntOrVecOrMatrixOfIntType> {
   let arguments = (ins
     CIR_AnyIntOrVecOrMatrixOfIntType:$lhs,
     CIR_AnyIntOrVecOrMatrixOfIntType:$rhs,
-    UnitProp:$no_signed_wrap,
-    UnitProp:$no_unsigned_wrap
+    CIR_OverflowFlagsProp:$overflow_flags
   );
 
   let assemblyFormat = [{
-    (`nsw` $no_signed_wrap^)?
-    (`nuw` $no_unsigned_wrap^)?
+    custom<OverflowFlags>($overflow_flags)
     $lhs `,` $rhs `:` type($lhs) attr-dict
   }];
 }
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 5fecc433d05ae8..56957d0ada399d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -504,6 +504,35 @@ static void 
printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer,
                       /*printBlockTerminators=*/!omitRegionTerm(region));
 }
 
+// Parses the overflow flags as optional bare `nsw` and `nuw` keywords, in any
+// order and each at most once.
+static mlir::ParseResult parseOverflowFlags(mlir::OpAsmParser &parser,
+                                            cir::OverflowFlags &flags) {
+  flags = cir::OverflowFlags::none;
+  while (true) {
+    llvm::SMLoc loc = parser.getCurrentLocation();
+    cir::OverflowFlags flag;
+    if (succeeded(parser.parseOptionalKeyword("nsw")))
+      flag = cir::OverflowFlags::nsw;
+    else if (succeeded(parser.parseOptionalKeyword("nuw")))
+      flag = cir::OverflowFlags::nuw;
+    else
+      return success();
+    if (cir::bitEnumContainsAll(flags, flag))
+      return parser.emitError(loc, "duplicate '")
+             << cir::stringifyOverflowFlags(flag) << "' flag";
+    flags = flags | flag;
+  }
+}
+
+static void printOverflowFlags(mlir::OpAsmPrinter &printer, mlir::Operation *,
+                               cir::OverflowFlags flags) {
+  if (cir::bitEnumContainsAll(flags, cir::OverflowFlags::nsw))
+    printer << " nsw";
+  if (cir::bitEnumContainsAll(flags, cir::OverflowFlags::nuw))
+    printer << " nuw";
+}
+
 mlir::OptionalParseResult
 parseGlobalMemorySpace(mlir::AsmParser &p,
                        mlir::ptr::MemorySpaceAttrInterface &attr);
@@ -3428,14 +3457,14 @@ mlir::LogicalResult cir::FuncOp::verify() {
 // sat needs to be verified.
 
 LogicalResult cir::AddOp::verify() {
-  if (getSaturated() && (getNoSignedWrap() || getNoUnsignedWrap()))
+  if (getSaturated() && getOverflowFlags() != cir::OverflowFlags::none)
     return emitOpError()
            << "the nsw/nuw flags and the saturated flag are mutually 
exclusive";
   return mlir::success();
 }
 
 LogicalResult cir::SubOp::verify() {
-  if (getSaturated() && (getNoSignedWrap() || getNoUnsignedWrap()))
+  if (getSaturated() && getOverflowFlags() != cir::OverflowFlags::none)
     return emitOpError()
            << "the nsw/nuw flags and the saturated flag are mutually 
exclusive";
   return mlir::success();
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 962cdd3782adad..c6a8814a617bf8 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -480,11 +480,11 @@ static mlir::Value lowerDataMemberCast(mlir::Operation 
*op,
   mlir::Value adjustedPtr;
   if (isDerivedToBase) {
     auto subOp = cir::SubOp::create(builder, loc, ty, loweredSrc, offsetValue);
-    subOp.setNoSignedWrap(true);
+    subOp.setOverflowFlags(cir::OverflowFlags::nsw);
     adjustedPtr = subOp;
   } else {
     auto addOp = cir::AddOp::create(builder, loc, ty, loweredSrc, offsetValue);
-    addOp.setNoSignedWrap(true);
+    addOp.setOverflowFlags(cir::OverflowFlags::nsw);
     adjustedPtr = addOp;
   }
 
@@ -531,12 +531,12 @@ static mlir::Value lowerMethodCast(mlir::Operation *op, 
mlir::Value loweredSrc,
   if (isDerivedToBase) {
     auto subOp = cir::SubOp::create(builder, op->getLoc(), ptrdiffCIRTy,
                                     adjField, offsetValue);
-    subOp.setNoSignedWrap(true);
+    subOp.setOverflowFlags(cir::OverflowFlags::nsw);
     adjustedAdjField = subOp;
   } else {
     auto addOp = cir::AddOp::create(builder, op->getLoc(), ptrdiffCIRTy,
                                     adjField, offsetValue);
-    addOp.setNoSignedWrap(true);
+    addOp.setOverflowFlags(cir::OverflowFlags::nsw);
     adjustedAdjField = addOp;
   }
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 39ecb6fa7c746c..2576e2fbc0daff 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3768,11 +3768,13 @@ static bool isIntTypeUnsigned(mlir::Type type) {
 
 template <typename BinOp>
 static mlir::LLVM::IntegerOverflowFlags intOverflowFlag(BinOp op) {
-  if (op.getNoUnsignedWrap())
-    return mlir::LLVM::IntegerOverflowFlags::nuw;
-  if (op.getNoSignedWrap())
-    return mlir::LLVM::IntegerOverflowFlags::nsw;
-  return mlir::LLVM::IntegerOverflowFlags::none;
+  static_assert(
+      llvm::to_underlying(cir::OverflowFlags::nsw) ==
+              llvm::to_underlying(mlir::LLVM::IntegerOverflowFlags::nsw) &&
+          llvm::to_underlying(cir::OverflowFlags::nuw) ==
+              llvm::to_underlying(mlir::LLVM::IntegerOverflowFlags::nuw),
+      "cir::OverflowFlags must match mlir::LLVM::IntegerOverflowFlags");
+  return static_cast<mlir::LLVM::IntegerOverflowFlags>(op.getOverflowFlags());
 }
 
 /// Lower an arithmetic op that supports saturation, overflow flags, and an FP
diff --git a/clang/test/CIR/IR/invalid-overflow-flags.cir 
b/clang/test/CIR/IR/invalid-overflow-flags.cir
new file mode 100644
index 00000000000000..56487bcc814e92
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-overflow-flags.cir
@@ -0,0 +1,49 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+
+cir.func @duplicate_nsw(%a: !s32i, %b: !s32i) {
+  // expected-error@+1 {{duplicate 'nsw' flag}}
+  %0 = cir.add nsw nsw %a, %b : !s32i
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @duplicate_nuw(%a: !s32i, %b: !s32i) {
+  // expected-error@+1 {{duplicate 'nuw' flag}}
+  %0 = cir.mul nuw nsw nuw %a, %b : !s32i
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @add_nsw_sat(%a: !s32i, %b: !s32i) {
+  // expected-error@+1 {{the nsw/nuw flags and the saturated flag are mutually 
exclusive}}
+  %0 = cir.add nsw sat %a, %b : !s32i
+  cir.return
+}
+
+// -----
+
+!u32i = !cir.int<u, 32>
+
+cir.func @sub_nuw_sat(%a: !u32i, %b: !u32i) {
+  // expected-error@+1 {{the nsw/nuw flags and the saturated flag are mutually 
exclusive}}
+  %0 = cir.sub nuw sat %a, %b : !u32i
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @unknown_bit(%a: !s32i, %b: !s32i) {
+  // expected-error@+1 {{property 'overflow_flags' failed to satisfy 
constraint: integer overflow flags}}
+  %0 = "cir.add"(%a, %b) <{overflow_flags = 4 : i32, saturated = false}> : 
(!s32i, !s32i) -> !s32i
+  cir.return
+}
diff --git a/clang/test/CIR/IR/overflow-flags.cir 
b/clang/test/CIR/IR/overflow-flags.cir
new file mode 100644
index 00000000000000..69e2c50f5bdbe1
--- /dev/null
+++ b/clang/test/CIR/IR/overflow-flags.cir
@@ -0,0 +1,55 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s -mlir-print-op-generic | FileCheck %s --check-prefix=GENERIC
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
+module {
+  // CHECK-LABEL: cir.func @add
+  cir.func @add(%a: !s32i, %b: !s32i) {
+    // CHECK: cir.add %{{.*}}, %{{.*}} : !s32i
+    %0 = cir.add %a, %b : !s32i
+    // CHECK: cir.add nsw %{{.*}}, %{{.*}} : !s32i
+    %1 = cir.add nsw %a, %b : !s32i
+    // CHECK: cir.add nuw %{{.*}}, %{{.*}} : !s32i
+    %2 = cir.add nuw %a, %b : !s32i
+    // CHECK: cir.add nsw nuw %{{.*}}, %{{.*}} : !s32i
+    %3 = cir.add nsw nuw %a, %b : !s32i
+    // CHECK: cir.add nsw nuw %{{.*}}, %{{.*}} : !s32i
+    %4 = cir.add nuw nsw %a, %b : !s32i
+    // CHECK: cir.add sat %{{.*}}, %{{.*}} : !s32i
+    %5 = cir.add sat %a, %b : !s32i
+    cir.return
+  }
+
+  // CHECK-LABEL: cir.func @sub
+  cir.func @sub(%a: !u32i, %b: !u32i) {
+    // CHECK: cir.sub nuw %{{.*}}, %{{.*}} : !u32i
+    %0 = cir.sub nuw %a, %b : !u32i
+    // CHECK: cir.sub nsw nuw %{{.*}}, %{{.*}} : !u32i
+    %1 = cir.sub nuw nsw %a, %b : !u32i
+    // CHECK: cir.sub sat %{{.*}}, %{{.*}} : !u32i
+    %2 = cir.sub sat %a, %b : !u32i
+    cir.return
+  }
+
+  // CHECK-LABEL: cir.func @mul
+  cir.func @mul(%a: !s32i, %b: !s32i) {
+    // CHECK: cir.mul %{{.*}}, %{{.*}} : !s32i
+    %0 = cir.mul %a, %b : !s32i
+    // CHECK: cir.mul nsw %{{.*}}, %{{.*}} : !s32i
+    %1 = cir.mul nsw %a, %b : !s32i
+    // CHECK: cir.mul nsw nuw %{{.*}}, %{{.*}} : !s32i
+    %2 = cir.mul nuw nsw %a, %b : !s32i
+    cir.return
+  }
+}
+
+// The flags are stored as a single bit-enum property.
+// GENERIC-LABEL: sym_name = "add"
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 0 : i32, saturated 
= false}>
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 1 : i32, saturated 
= false}>
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 2 : i32, saturated 
= false}>
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 3 : i32, saturated 
= false}>
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 3 : i32, saturated 
= false}>
+// GENERIC: "cir.add"(%{{.*}}, %{{.*}}) <{overflow_flags = 0 : i32, saturated}>
diff --git a/clang/test/CIR/Lowering/overflow-flags.cir 
b/clang/test/CIR/Lowering/overflow-flags.cir
new file mode 100644
index 00000000000000..8b648c7f8397e6
--- /dev/null
+++ b/clang/test/CIR/Lowering/overflow-flags.cir
@@ -0,0 +1,54 @@
+// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR
+// RUN: cir-translate %s -cir-to-llvmir --target x86_64-unknown-linux-gnu 
--disable-cc-lowering | FileCheck %s -check-prefix=LLVM
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
+// Both flags must survive lowering together.
+
+module {
+  cir.func @add(%a: !s32i, %b: !s32i) -> !s32i {
+    %0 = cir.add nsw nuw %a, %b : !s32i
+    cir.return %0 : !s32i
+  }
+  // MLIR-LABEL: llvm.func @add
+  // MLIR: llvm.add %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+  // LLVM-LABEL: define {{.*}}@add(
+  // LLVM: add nuw nsw i32
+
+  cir.func @sub(%a: !u32i, %b: !u32i) -> !u32i {
+    %0 = cir.sub nsw nuw %a, %b : !u32i
+    cir.return %0 : !u32i
+  }
+  // MLIR-LABEL: llvm.func @sub
+  // MLIR: llvm.sub %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+  // LLVM-LABEL: define {{.*}}@sub(
+  // LLVM: sub nuw nsw i32
+
+  cir.func @mul(%a: !s32i, %b: !s32i) -> !s32i {
+    %0 = cir.mul nsw nuw %a, %b : !s32i
+    cir.return %0 : !s32i
+  }
+  // MLIR-LABEL: llvm.func @mul
+  // MLIR: llvm.mul %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+  // LLVM-LABEL: define {{.*}}@mul(
+  // LLVM: mul nuw nsw i32
+
+  cir.func @vec_add(%a: !cir.vector<4 x !s32i>, %b: !cir.vector<4 x !s32i>) -> 
!cir.vector<4 x !s32i> {
+    %0 = cir.add nsw nuw %a, %b : !cir.vector<4 x !s32i>
+    cir.return %0 : !cir.vector<4 x !s32i>
+  }
+  // MLIR-LABEL: llvm.func @vec_add
+  // MLIR: llvm.add %{{.*}}, %{{.*}} overflow<nsw, nuw> : vector<4xi32>
+  // LLVM-LABEL: define {{.*}}@vec_add(
+  // LLVM: add nuw nsw <4 x i32>
+
+  cir.func @matrix_mul(%a: !cir.matrix<2 x 3 x !s32i>, %b: !cir.matrix<2 x 3 x 
!s32i>) -> !cir.matrix<2 x 3 x !s32i> {
+    %0 = cir.mul nsw nuw %a, %b : !cir.matrix<2 x 3 x !s32i>
+    cir.return %0 : !cir.matrix<2 x 3 x !s32i>
+  }
+  // MLIR-LABEL: llvm.func @matrix_mul
+  // MLIR: llvm.mul %{{.*}}, %{{.*}} overflow<nsw, nuw> : vector<6xi32>
+  // LLVM-LABEL: define {{.*}}@matrix_mul(
+  // LLVM: mul nuw nsw <6 x i32>
+}

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

Reply via email to