[clang] [CIR] Use declarative TableGen constraints for overflow flag verification (PR #195476)

Henrich Lauko via cfe-commits cfe-commits at lists.llvm.org
Sat May 2 11:50:51 PDT 2026


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

>From 34197a96d7249993ac87473badd557c7688d6e19 Mon Sep 17 00:00:00 2001
From: xlauko <xlauko at mail.muni.cz>
Date: Sat, 2 May 2026 20:33:36 +0200
Subject: [PATCH] [CIR] Use declarative TableGen constraints for overflow flag
 verification

Replace hand-written C++ verifiers with PredOpTrait-based constraints
(FlagRequiresIntType, HasAtMostOneOfAttrs). Introduce CIR_SaturatableBinaryOp
base class and use append/prepend ODS directives to compose arguments, format,
and traits across the op hierarchy. Fix HasAtMostOneOfAttrsPred to use
accessor methods instead of dollar-sign references. Add Commutative trait
to AddOp and MulOp.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td | 85 ++++++++++++--------
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp      | 39 ---------
 2 files changed, 50 insertions(+), 74 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 97d623ba5e6d9..7a450d3267f7c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -114,12 +114,20 @@ class CIR_Op<string mnemonic, list<Trait> traits = []> :
 //===----------------------------------------------------------------------===//
 
 class HasAtMostOneOfAttrsPred<list<string> names> :
-  CPred<!foldl("0", names, acc, name,  acc # " + (" # name # " ? 1 : 0)")
+  CPred<!foldl("0", names, acc, name,
+    acc # " + (this->get" # snakeCaseToCamelCase<name>.ret # "() ? 1 : 0)")
         # " <= 1">;
 
 class HasAtMostOneOfAttrs<list<string> names> : PredOpTrait<
   "has only one of the optional attributes: " # !interleave(names, ", "),
-  HasAtMostOneOfAttrsPred<!foreach(name, names, "$" # name)>
+  HasAtMostOneOfAttrsPred<names>
+>;
+
+// Requires that a boolean flag attribute implies integer result type.
+class FlagRequiresIntType<string flag> : PredOpTrait<
+  "only operations on integer values may have the " # flag # " flag",
+  Or<[Neg<CPred<"this->get" # snakeCaseToCamelCase<flag>.ret # "()">>,
+      CPred<"::mlir::isa<::cir::IntType>(this->getResult().getType())">]>
 >;
 
 //===----------------------------------------------------------------------===//
@@ -1819,11 +1827,12 @@ class CIR_UnaryOpWithOverflowFlag<string mnemonic, Type type,
                                    list<Trait> traits = []>
     : CIR_UnaryOp<mnemonic, type, traits>
 {
-  let arguments = (ins type:$input, UnitProp:$no_signed_wrap);
+  let append traits = [NSWFlagIntOnly];
 
-  let assemblyFormat = [{
+  let append arguments = (ins UnitProp:$no_signed_wrap);
+
+  let prepend assemblyFormat = [{
     (`nsw` $no_signed_wrap^)?
-    $input `:` type($input) attr-dict
   }];
 }
 
@@ -2366,34 +2375,52 @@ class CIR_BinaryOp<string mnemonic, Type type, list<Trait> traits = []>
   }];
 }
 
-// Base class for binary ops that support integer overflow flags (nsw/nuw)
-// and saturated arithmetic.
-class CIR_BinaryOpWithOverflowFlags<string mnemonic, Type type,
-                                    list<Trait> traits = []>
-    : CIR_BinaryOp<mnemonic, type, !listconcat([Pure], traits)>
+def NSWFlagIntOnly : FlagRequiresIntType<"no_signed_wrap">;
+def NUWFlagIntOnly : FlagRequiresIntType<"no_unsigned_wrap">;
+
+// Base class for binary ops that support integer overflow flags (nsw/nuw).
+class CIR_BinaryOpWithOverflowFlags<string mnemonic, Type type>
+    : CIR_BinaryOp<mnemonic, type>
 {
-  let arguments = (ins
-    type:$lhs, type:$rhs,
+  let append traits = [Pure, NSWFlagIntOnly, NUWFlagIntOnly];
+
+  let append arguments = (ins
     UnitProp:$no_signed_wrap,
-    UnitProp:$no_unsigned_wrap,
-    UnitProp:$saturated
+    UnitProp:$no_unsigned_wrap
   );
 
-  let assemblyFormat = [{
+  let prepend assemblyFormat = [{
     (`nsw` $no_signed_wrap^)?
     (`nuw` $no_unsigned_wrap^)?
-    (`sat` $saturated^)?
-    $lhs `,` $rhs `:` type($lhs) attr-dict
   }];
+}
 
-  let hasVerifier = 1;
+def SatFlagIntOnly : FlagRequiresIntType<"saturated">;
+
+// Extends CIR_BinaryOpWithOverflowFlags with saturated arithmetic support.
+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 arguments = (ins UnitProp:$saturated);
+
+  let prepend assemblyFormat = [{
+    (`sat` $saturated^)?
+  }];
 }
 
 //===----------------------------------------------------------------------===//
 // AddOp
 //===----------------------------------------------------------------------===//
 
-def CIR_AddOp : CIR_BinaryOpWithOverflowFlags<"add", CIR_AnyArithType> {
+def CIR_AddOp : CIR_SaturatableBinaryOp<"add", CIR_AnyArithType> {
+  let append traits = [Commutative];
+
   let summary = "Integer or floating-point addition";
   let description = [{
     The `cir.add` operation performs addition on integer or floating-point
@@ -2421,7 +2448,7 @@ def CIR_AddOp : CIR_BinaryOpWithOverflowFlags<"add", CIR_AnyArithType> {
 // SubOp
 //===----------------------------------------------------------------------===//
 
-def CIR_SubOp : CIR_BinaryOpWithOverflowFlags<"sub", CIR_AnyArithType> {
+def CIR_SubOp : CIR_SaturatableBinaryOp<"sub", CIR_AnyArithType> {
   let summary = "Integer or floating-point subtraction";
   let description = [{
     The `cir.sub` operation performs subtraction on integer or floating-point
@@ -2448,7 +2475,9 @@ def CIR_SubOp : CIR_BinaryOpWithOverflowFlags<"sub", CIR_AnyArithType> {
 // MulOp
 //===----------------------------------------------------------------------===//
 
-def CIR_MulOp : CIR_BinaryOp<"mul", CIR_AnyArithType> {
+def CIR_MulOp : CIR_BinaryOpWithOverflowFlags<"mul", CIR_AnyArithType> {
+  let append traits = [Commutative];
+
   let summary = "Integer or floating-point multiplication";
   let description = [{
     The `cir.mul` operation performs multiplication on integer or floating-point
@@ -2467,20 +2496,6 @@ def CIR_MulOp : CIR_BinaryOp<"mul", CIR_AnyArithType> {
     %3 = cir.mul %a, %b : !cir.float
     ```
   }];
-  
-  let arguments = (ins
-    CIR_AnyArithType:$lhs, CIR_AnyArithType:$rhs,
-    UnitProp:$no_signed_wrap,
-    UnitProp:$no_unsigned_wrap
-  );
-
-  let assemblyFormat = [{
-    (`nsw` $no_signed_wrap^)?
-    (`nuw` $no_unsigned_wrap^)?
-    $lhs `,` $rhs `:` type($lhs) attr-dict
-  }];
-
-  let hasVerifier = 1;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 7386819d8fce9..05aff42f7c148 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2708,45 +2708,6 @@ mlir::LogicalResult cir::FuncOp::verify() {
   return success();
 }
 
-//===----------------------------------------------------------------------===//
-// AddOp / SubOp / MulOp
-//===----------------------------------------------------------------------===//
-
-static LogicalResult verifyBinaryOverflowOp(mlir::Operation *op,
-                                            bool noSignedWrap,
-                                            bool noUnsignedWrap, bool saturated,
-                                            bool hasSat) {
-  bool noWrap = noSignedWrap || noUnsignedWrap;
-  if (!isa<cir::IntType>(op->getResultTypes()[0]) && noWrap)
-    return op->emitError()
-           << "only operations on integer values may have nsw/nuw flags";
-  if (hasSat && saturated && !isa<cir::IntType>(op->getResultTypes()[0]))
-    return op->emitError()
-           << "only operations on integer values may have sat flag";
-  if (hasSat && noWrap && saturated)
-    return op->emitError()
-           << "the nsw/nuw flags and the saturated flag are mutually exclusive";
-  return mlir::success();
-}
-
-LogicalResult cir::AddOp::verify() {
-  return verifyBinaryOverflowOp(getOperation(), getNoSignedWrap(),
-                                getNoUnsignedWrap(), getSaturated(),
-                                /*hasSat=*/true);
-}
-
-LogicalResult cir::SubOp::verify() {
-  return verifyBinaryOverflowOp(getOperation(), getNoSignedWrap(),
-                                getNoUnsignedWrap(), getSaturated(),
-                                /*hasSat=*/true);
-}
-
-LogicalResult cir::MulOp::verify() {
-  return verifyBinaryOverflowOp(getOperation(), getNoSignedWrap(),
-                                getNoUnsignedWrap(), /*saturated=*/false,
-                                /*hasSat=*/false);
-}
-
 //===----------------------------------------------------------------------===//
 // TernaryOp
 //===----------------------------------------------------------------------===//



More information about the cfe-commits mailing list