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

>From 9ae5374f695d6f72f9844f241a79656b3488c202 Mon Sep 17 00:00:00 2001
From: xlauko <[email protected]>
Date: Mon, 4 May 2026 11:13:16 +0200
Subject: [PATCH] [CIR] Replace nsw/nuw unit attrs with OverflowFlags BitEnum

Combine the separate `no_signed_wrap` and `no_unsigned_wrap` unit
properties on arithmetic ops into a single `OverflowFlags` BitEnum
(`nsw`, `nuw`). This allows combined flags to be written as
`nsw|nuw` in assembly, replaces the per-flag verification traits
with a single `OverflowFlagsRequireIntType` predicate, and folds
the two `HasAtMostOneOfAttrs` checks into one
`SatExclusiveWithOverflowFlags` predicate.

The bit layout matches `mlir::LLVM::IntegerOverflowFlags`, so
lowering casts the value directly and asserts the layout via
static_assert.

Updates IncOp/DecOp/MinusOp builders, CIRGenExprScalar, and
LowerItaniumCXXABI to the new API. Adds round-trip and
verification tests in clang/test/CIR/IR/.
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      | 30 +++++---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 14 ++++
 .../clang/CIR/Dialect/IR/CIREnumAttr.td       | 15 ++++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 73 +++++++++++--------
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  8 +-
 .../TargetLowering/LowerItaniumCXXABI.cpp     |  8 +-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 22 ++++--
 clang/test/CIR/IR/invalid-overflow.cir        | 39 ++++++++++
 clang/test/CIR/IR/overflow-flags.cir          | 66 +++++++++++++++++
 9 files changed, 217 insertions(+), 58 deletions(-)
 create mode 100644 clang/test/CIR/IR/invalid-overflow.cir
 create mode 100644 clang/test/CIR/IR/overflow-flags.cir

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 646fc7eb3c226..d3cc5a551fd27 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -61,6 +61,15 @@ constexpr bool testFlag(OverflowBehavior ob, 
OverflowBehavior flag) {
   return (ob & flag) != OverflowBehavior::None;
 }
 
+inline OverflowFlags toOverflowFlags(OverflowBehavior ob) {
+  auto 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:
@@ -279,18 +288,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   }
 
   mlir::Value createInc(mlir::Location loc, mlir::Value input,
-                        bool nsw = false) {
-    return cir::IncOp::create(*this, loc, input, nsw);
+                        cir::OverflowFlags flags = cir::OverflowFlags::none) {
+    return cir::IncOp::create(*this, loc, input, flags);
   }
 
   mlir::Value createDec(mlir::Location loc, mlir::Value input,
-                        bool nsw = false) {
-    return cir::DecOp::create(*this, loc, input, nsw);
+                        cir::OverflowFlags flags = cir::OverflowFlags::none) {
+    return cir::DecOp::create(*this, loc, input, flags);
   }
 
   mlir::Value createMinus(mlir::Location loc, mlir::Value input,
-                          bool nsw = false) {
-    return cir::MinusOp::create(*this, loc, input, nsw);
+                          cir::OverflowFlags flags = cir::OverflowFlags::none) 
{
+    return cir::MinusOp::create(*this, loc, input, flags);
   }
 
   mlir::TypedAttr getConstPtrAttr(mlir::Type type, int64_t value) {
@@ -629,8 +638,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs,
                         OverflowBehavior ob = OverflowBehavior::None) {
     auto op = cir::MulOp::create(*this, loc, lhs, rhs);
-    op.setNoUnsignedWrap(testFlag(ob, OverflowBehavior::NoUnsignedWrap));
-    op.setNoSignedWrap(testFlag(ob, OverflowBehavior::NoSignedWrap));
+    op.setFlags(toOverflowFlags(ob));
     return op;
   }
   mlir::Value createNSWMul(mlir::Location loc, mlir::Value lhs,
@@ -645,8 +653,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.setFlags(toOverflowFlags(ob));
     op.setSaturated(testFlag(ob, OverflowBehavior::Saturated));
     return op;
   }
@@ -664,8 +671,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.setFlags(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 1520999e3f85f..703d75bb2c14c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -110,6 +110,20 @@ def CIR_SourceLanguageAttr : 
CIR_EnumAttr<CIR_SourceLanguage, "lang"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// OverflowFlagsAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_OFnone : I32BitEnumCaseNone<"none">;
+def CIR_OFnsw  : I32BitEnumCaseBit<"nsw", 0>;
+def CIR_OFnuw  : I32BitEnumCaseBit<"nuw", 1>;
+
+def CIR_OverflowFlags : CIR_I32BitEnum<
+    "OverflowFlags", "integer arithmetic overflow flags",
+    [CIR_OFnone, CIR_OFnsw, CIR_OFnuw]>;
+
+def CIR_OverflowFlagsProp : CIR_BitEnumProp<CIR_OverflowFlags>;
+
 
//===----------------------------------------------------------------------===//
 // ArgPassingKind + RecordLayoutAttr
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td 
b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td
index 1de6ffdc08d72..78f74ec8393ae 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIREnumAttr.td
@@ -31,6 +31,21 @@ class CIR_EnumAttr<EnumAttrInfo info, string name = "", 
list<Trait> traits = []>
   let assemblyFormat = "`<` $value `>`";
 }
 
+class CIR_I32BitEnum<string name, string summary,
+                    list<BitEnumCaseBase> cases>
+    : I32BitEnum<name, summary, cases> {
+  let cppNamespace = "::cir";
+  let separator = "|";
+  let printBitEnumPrimaryGroups = 1;
+}
+
+// CIR property wrapping a BitEnum. Storage is inline (Property, not 
Attribute);
+// the auto-generated FieldParser parses bare `flag1|flag2` syntax.
+class CIR_BitEnumProp<BitEnum info, string defaultVal = info.cppType # 
"::none">
+    : EnumProp<info> {
+  let defaultValue = defaultVal;
+}
+
 class CIR_DefaultValuedEnumParameter<EnumAttrInfo info, string value = "">
     : EnumParameter<info> {
   let defaultValue = value;
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 46b23c32b1a98..4ad247a8dba68 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -130,10 +130,22 @@ class FlagRequiresIntType<string flag> : PredOpTrait<
       CPred<"::mlir::isa<::cir::IntType>(this->getResult().getType())">]>
 >;
 
-def NSWFlagIntOnly : FlagRequiresIntType<"no_signed_wrap">;
-def NUWFlagIntOnly : FlagRequiresIntType<"no_unsigned_wrap">;
 def SatFlagIntOnly : FlagRequiresIntType<"saturated">;
 
+// Requires that overflow flags imply an integer result type.
+def OverflowFlagsRequireIntType : PredOpTrait<
+  "overflow flags require an integer result type",
+  Or<[CPred<"this->getFlags() == ::cir::OverflowFlags::none">,
+      CPred<"::mlir::isa<::cir::IntType>(this->getResult().getType())">]>
+>;
+
+// Requires that the saturated flag is mutually exclusive with overflow flags.
+def SatExclusiveWithOverflowFlags : PredOpTrait<
+  "sat is mutually exclusive with nsw/nuw overflow flags",
+  CPred<"!this->getSaturated() || "
+        "this->getFlags() == ::cir::OverflowFlags::none">
+>;
+
 
//===----------------------------------------------------------------------===//
 // CastOp
 
//===----------------------------------------------------------------------===//
@@ -1831,12 +1843,12 @@ class CIR_UnaryOpWithOverflowFlag<string mnemonic, Type 
type,
                                    list<Trait> traits = []>
     : CIR_UnaryOp<mnemonic, type, traits>
 {
-  let append traits = [NSWFlagIntOnly];
+  let append traits = [OverflowFlagsRequireIntType];
 
-  let append arguments = (ins UnitProp:$no_signed_wrap);
+  let append arguments = (ins CIR_OverflowFlagsProp:$flags);
 
   let prepend assemblyFormat = [{
-    (`nsw` $no_signed_wrap^)?
+    ($flags^)?
   }];
 }
 
@@ -2383,16 +2395,12 @@ class CIR_BinaryOp<string mnemonic, Type type, 
list<Trait> traits = []>
 class CIR_BinaryOpWithOverflowFlags<string mnemonic, Type type>
     : CIR_BinaryOp<mnemonic, type>
 {
-  let append traits = [Pure, NSWFlagIntOnly, NUWFlagIntOnly];
+  let append traits = [Pure, OverflowFlagsRequireIntType];
 
-  let append arguments = (ins
-    UnitProp:$no_signed_wrap,
-    UnitProp:$no_unsigned_wrap
-  );
+  let append arguments = (ins CIR_OverflowFlagsProp:$flags);
 
   let prepend assemblyFormat = [{
-    (`nsw` $no_signed_wrap^)?
-    (`nuw` $no_unsigned_wrap^)?
+    ($flags^)?
   }];
 }
 
@@ -2400,11 +2408,7 @@ class CIR_BinaryOpWithOverflowFlags<string mnemonic, 
Type type>
 class CIR_SaturatableBinaryOp<string mnemonic, Type type>
     : CIR_BinaryOpWithOverflowFlags<mnemonic, type>
 {
-  let append traits = [
-    SatFlagIntOnly,
-    HasAtMostOneOfAttrs<["saturated", "no_signed_wrap"]>,
-    HasAtMostOneOfAttrs<["saturated", "no_unsigned_wrap"]>
-  ];
+  let append traits = [SatFlagIntOnly, SatExclusiveWithOverflowFlags];
 
   let append arguments = (ins UnitProp:$saturated);
 
@@ -2426,19 +2430,21 @@ def CIR_AddOp : CIR_SaturatableBinaryOp<"add", 
CIR_AnyArithType> {
     operands. Both operands and the result must have the same type.
 
     For integer types, 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 clamps the result to the type's representable range instead of
-    wrapping. The `nsw`/`nuw` flags and `sat` are mutually exclusive.
-    
+    unsigned wrap) overflow flags indicate that the result is poison if signed
+    or unsigned overflow occurs, respectively. Combined flags are written as
+    `nsw|nuw`. The optional `sat` (saturated) attribute clamps the result to
+    the type's representable range instead of wrapping. The `nsw`/`nuw` flags
+    and `sat` are mutually exclusive.
+
     Example:
 
     ```
     %0 = cir.add %a, %b : !s32i
     %1 = cir.add nsw %a, %b : !s32i
     %2 = cir.add nuw %a, %b : !u32i
-    %3 = cir.add sat %a, %b : !s32i
-    %4 = cir.add %a, %b : !cir.float
+    %3 = cir.add nsw|nuw %a, %b : !s32i
+    %4 = cir.add sat %a, %b : !s32i
+    %5 = cir.add %a, %b : !cir.float
     ```
   }];
 }
@@ -2454,11 +2460,12 @@ def CIR_SubOp : CIR_SaturatableBinaryOp<"sub", 
CIR_AnyArithType> {
     operands. Both operands and the result must have the same type.
 
     For integer types, 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 clamps the result to the type's representable range. The
-    `nsw`/`nuw` flags and `sat` are mutually exclusive.
-    
+    unsigned wrap) overflow flags indicate that the result is poison if signed
+    or unsigned overflow occurs, respectively. Combined flags are written as
+    `nsw|nuw`. The optional `sat` (saturated) attribute clamps the result to
+    the type's representable range. The `nsw`/`nuw` flags and `sat` are
+    mutually exclusive.
+
     Example:
 
     ```
@@ -2483,8 +2490,9 @@ def CIR_MulOp : CIR_BinaryOpWithOverflowFlags<"mul", 
CIR_AnyArithType> {
     operands. Both operands and the result must have the same type.
 
     For integer types, 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.
+    unsigned wrap) overflow flags indicate that the result is poison if signed
+    or unsigned overflow occurs, respectively. Combined flags are written as
+    `nsw|nuw`.
 
     Example:
 
@@ -2492,7 +2500,8 @@ def CIR_MulOp : CIR_BinaryOpWithOverflowFlags<"mul", 
CIR_AnyArithType> {
     %0 = cir.mul %a, %b : !s32i
     %1 = cir.mul nsw %a, %b : !s32i
     %2 = cir.mul nuw %a, %b : !u32i
-    %3 = cir.mul %a, %b : !cir.float
+    %3 = cir.mul nsw|nuw %a, %b : !s32i
+    %4 = cir.mul %a, %b : !cir.float
     ```
   }];
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 92b7156f3a3a8..07a160b4aa3e0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -796,16 +796,18 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
 
     // NOTE: LLVM codegen will lower this directly to either a FNeg
     // or a Sub instruction.  In CIR this will be handled later in LowerToLLVM.
+    auto flags = nsw ? cir::OverflowFlags::nsw : cir::OverflowFlags::none;
     return builder.createOrFold<cir::MinusOp>(
-        cgf.getLoc(e->getSourceRange().getBegin()), operand, nsw);
+        cgf.getLoc(e->getSourceRange().getBegin()), operand, flags);
   }
 
   mlir::Value emitIncOrDec(const UnaryOperator *e, mlir::Value input,
                            bool nsw = false) {
     mlir::Location loc = cgf.getLoc(e->getSourceRange().getBegin());
+    auto flags = nsw ? cir::OverflowFlags::nsw : cir::OverflowFlags::none;
     return e->isIncrementOp()
-               ? builder.createOrFold<cir::IncOp>(loc, input, nsw)
-               : builder.createOrFold<cir::DecOp>(loc, input, nsw);
+               ? builder.createOrFold<cir::IncOp>(loc, input, flags)
+               : builder.createOrFold<cir::DecOp>(loc, input, flags);
   }
 
   mlir::Value VisitUnaryNot(const UnaryOperator *e) {
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 8769975bdc948..cc2d39116181f 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -432,11 +432,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.setFlags(cir::OverflowFlags::nsw);
     adjustedPtr = subOp;
   } else {
     auto addOp = cir::AddOp::create(builder, loc, ty, loweredSrc, offsetValue);
-    addOp.setNoSignedWrap(true);
+    addOp.setFlags(cir::OverflowFlags::nsw);
     adjustedPtr = addOp;
   }
 
@@ -477,12 +477,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.setFlags(cir::OverflowFlags::nsw);
     adjustedAdjField = subOp;
   } else {
     auto addOp = cir::AddOp::create(builder, op->getLoc(), ptrdiffCIRTy,
                                     adjField, offsetValue);
-    addOp.setNoSignedWrap(true);
+    addOp.setFlags(cir::OverflowFlags::nsw);
     adjustedAdjField = addOp;
   }
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e17c7a209db6b..614a534dce232 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2582,7 +2582,8 @@ lowerIncDecOp(CIROp op, typename CIROp::Adaptor adaptor,
   mlir::Location loc = op.getLoc();
 
   if (mlir::isa<cir::IntType>(elementType)) {
-    auto maybeNSW = nswFlag(op.getNoSignedWrap());
+    auto maybeNSW =
+        nswFlag(bitEnumContainsAll(op.getFlags(), cir::OverflowFlags::nsw));
     auto one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 1);
     rewriter.replaceOpWithNewOp<LLVMIntOp>(op, adaptor.getInput(), one,
                                            maybeNSW);
@@ -2621,7 +2622,8 @@ mlir::LogicalResult 
CIRToLLVMMinusOpLowering::matchAndRewrite(
   mlir::Location loc = op.getLoc();
 
   if (mlir::isa<cir::IntType>(elementType)) {
-    auto maybeNSW = nswFlag(op.getNoSignedWrap());
+    auto maybeNSW =
+        nswFlag(bitEnumContainsAll(op.getFlags(), cir::OverflowFlags::nsw));
     mlir::Value zero;
     if (isVector)
       zero = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmType);
@@ -2683,11 +2685,17 @@ 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;
+  // cir::OverflowFlags and mlir::LLVM::IntegerOverflowFlags share the same
+  // bit positions (nsw=0, nuw=1) so the underlying values map directly.
+  static_assert(
+      static_cast<uint32_t>(cir::OverflowFlags::nsw) ==
+          static_cast<uint32_t>(mlir::LLVM::IntegerOverflowFlags::nsw),
+      "nsw bit position mismatch between cir and llvm dialects");
+  static_assert(
+      static_cast<uint32_t>(cir::OverflowFlags::nuw) ==
+          static_cast<uint32_t>(mlir::LLVM::IntegerOverflowFlags::nuw),
+      "nuw bit position mismatch between cir and llvm dialects");
+  return static_cast<mlir::LLVM::IntegerOverflowFlags>(op.getFlags());
 }
 
 /// Lower an arithmetic op that supports saturation, overflow flags, and an FP
diff --git a/clang/test/CIR/IR/invalid-overflow.cir 
b/clang/test/CIR/IR/invalid-overflow.cir
new file mode 100644
index 0000000000000..e92dd8b0f6a43
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-overflow.cir
@@ -0,0 +1,39 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+
+cir.func @nsw_on_float(%a: !cir.float, %b: !cir.float) {
+  // expected-error @below {{overflow flags require an integer result type}}
+  %0 = cir.add nsw %a, %b : !cir.float
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @nuw_on_float(%a: !cir.float, %b: !cir.float) {
+  // expected-error @below {{overflow flags require an integer result type}}
+  %0 = cir.mul nuw %a, %b : !cir.float
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @sat_with_nsw(%a: !s32i, %b: !s32i) {
+  // expected-error @below {{sat is mutually exclusive with nsw/nuw overflow 
flags}}
+  %0 = cir.add sat nsw %a, %b : !s32i
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @sat_with_nuw(%a: !s32i, %b: !s32i) {
+  // expected-error @below {{sat is mutually exclusive with nsw/nuw overflow 
flags}}
+  %0 = cir.sub sat nuw %a, %b : !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 0000000000000..78ef1969e9f59
--- /dev/null
+++ b/clang/test/CIR/IR/overflow-flags.cir
@@ -0,0 +1,66 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
+module {
+  // Round-trip every BitEnum state on cir.add: none (default, elided),
+  // nsw alone, nuw alone, both flags combined, plus saturated.
+  cir.func @test_add_flags(%a: !s32i, %b: !s32i, %c: !u32i, %d: !u32i) {
+    %0 = cir.add %a, %b : !s32i
+    %1 = cir.add nsw %a, %b : !s32i
+    %2 = cir.add nuw %c, %d : !u32i
+    %3 = cir.add nsw|nuw %a, %b : !s32i
+    %4 = cir.add sat %a, %b : !s32i
+    cir.return
+  }
+  // CHECK-LABEL: cir.func{{.*}} @test_add_flags
+  // CHECK:         cir.add %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.add nsw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.add nuw %{{.+}}, %{{.+}} : !u32i
+  // CHECK:         cir.add nsw|nuw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.add sat %{{.+}}, %{{.+}} : !s32i
+
+  cir.func @test_sub_flags(%a: !s32i, %b: !s32i) {
+    %0 = cir.sub %a, %b : !s32i
+    %1 = cir.sub nsw %a, %b : !s32i
+    %2 = cir.sub nsw|nuw %a, %b : !s32i
+    %3 = cir.sub sat %a, %b : !s32i
+    cir.return
+  }
+  // CHECK-LABEL: cir.func{{.*}} @test_sub_flags
+  // CHECK:         cir.sub %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.sub nsw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.sub nsw|nuw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.sub sat %{{.+}}, %{{.+}} : !s32i
+
+  cir.func @test_mul_flags(%a: !s32i, %b: !s32i) {
+    %0 = cir.mul %a, %b : !s32i
+    %1 = cir.mul nsw %a, %b : !s32i
+    %2 = cir.mul nuw %a, %b : !s32i
+    %3 = cir.mul nsw|nuw %a, %b : !s32i
+    cir.return
+  }
+  // CHECK-LABEL: cir.func{{.*}} @test_mul_flags
+  // CHECK:         cir.mul %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.mul nsw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.mul nuw %{{.+}}, %{{.+}} : !s32i
+  // CHECK:         cir.mul nsw|nuw %{{.+}}, %{{.+}} : !s32i
+
+  cir.func @test_unary_flags(%a: !s32i) {
+    %0 = cir.minus %a : !s32i
+    %1 = cir.minus nsw %a : !s32i
+    %2 = cir.inc %a : !s32i
+    %3 = cir.inc nsw %a : !s32i
+    %4 = cir.dec %a : !s32i
+    %5 = cir.dec nsw %a : !s32i
+    cir.return
+  }
+  // CHECK-LABEL: cir.func{{.*}} @test_unary_flags
+  // CHECK:         cir.minus %{{.+}} : !s32i
+  // CHECK:         cir.minus nsw %{{.+}} : !s32i
+  // CHECK:         cir.inc %{{.+}} : !s32i
+  // CHECK:         cir.inc nsw %{{.+}} : !s32i
+  // CHECK:         cir.dec %{{.+}} : !s32i
+  // CHECK:         cir.dec nsw %{{.+}} : !s32i
+}

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

Reply via email to