Author: Andy Kaylor
Date: 2025-04-02T15:48:55-07:00
New Revision: c57b9c233a87f37e034445596ed09260cc6b23f5

URL: 
https://github.com/llvm/llvm-project/commit/c57b9c233a87f37e034445596ed09260cc6b23f5
DIFF: 
https://github.com/llvm/llvm-project/commit/c57b9c233a87f37e034445596ed09260cc6b23f5.diff

LOG: [CIR] Generate the nsw flag correctly for unary ops (#133815)

A previous checkin used a workaround to generate the nsw flag where
needed for unary ops. This change upstreams a subsequent change that was
made in the incubator to generate the flag correctly.

Added: 
    clang/test/CIR/IR/unary.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
    clang/test/CIR/CodeGen/unary.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 3965372755685..c17abfd752a1a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -697,17 +697,24 @@ def UnaryOp : CIR_Op<"unary", [Pure, 
SameOperandsAndResultType]> {
     It requires one input operand and has one result, both types
     should be the same.
 
+    If the `nsw` (no signed wrap) attribute is present, the result is poison if
+    signed overflow occurs.
+
     ```mlir
     %7 = cir.unary(inc, %1) : i32 -> i32
-    %8 = cir.unary(dec, %2) : i32 -> i32
+    %8 = cir.unary(dec, %2) nsw : i32 -> i32
     ```
   }];
 
   let results = (outs CIR_AnyType:$result);
-  let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind, 
Arg<CIR_AnyType>:$input);
+  let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind,
+                   Arg<CIR_AnyType>:$input,
+                   UnitAttr:$no_signed_wrap);
 
   let assemblyFormat = [{
-      `(` $kind `,` $input `)` `:` type($input) `,` type($result) attr-dict
+      `(` $kind `,` $input `)`
+      (`nsw` $no_signed_wrap^)?
+      `:` type($input) `,` type($result) attr-dict 
   }];
 
   let hasVerifier = 1;
@@ -961,9 +968,21 @@ def BinOp : CIR_Op<"binop", [Pure,
     It requires two input operands and has one result, all types
     should be the same.
 
+    If the `nsw` (no signed wrap) or `nuw` (no unsigned wrap) attributes are
+    present, the result is poison if signed or unsigned overflow occurs
+    (respectively).
+
+    If the `sat` (saturated) attribute is present, the result is clamped to
+    the maximum value representatable by the type if it would otherwise
+    exceed that value and is clamped to the minimum representable value if
+    it would otherwise be below that value.
+
     ```mlir
-    %7 = cir.binop(add, %1, %2) : !s32i
-    %7 = cir.binop(mul, %1, %2) : !u8i
+    %5 = cir.binop(add, %1, %2) : !s32i
+    %6 = cir.binop(mul, %1, %2) : !u8i
+    %7 = cir.binop(add, %1, %2) nsw : !s32i
+    %8 = cir.binop(add, %3, %4) nuw : !u32i
+    %9 = cir.binop(add, %1, %2) sat : !s32i
     ```
   }];
 

diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 3a102d90aba8f..23bf826d19a69 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -76,7 +76,6 @@ struct MissingFeatures {
   static bool opScopeCleanupRegion() { return false; }
 
   // Unary operator handling
-  static bool opUnarySignedOverflow() { return false; }
   static bool opUnaryPromotionType() { return false; }
 
   // Clang early optimizations or things defered to LLVM lowering.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 2cf92dfbf3a5b..5ac1dc1052c2e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -374,7 +374,7 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
         cir::UnaryOpKind kind =
             e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
         // NOTE(CIR): clang calls CreateAdd but folds this to a unary op
-        value = emitUnaryOp(e, kind, input);
+        value = emitUnaryOp(e, kind, input, /*nsw=*/false);
       }
     } else if (isa<PointerType>(type)) {
       cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec pointer");
@@ -429,19 +429,17 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *e,
                                                  mlir::Value inVal,
                                                  bool isInc) {
-    assert(!cir::MissingFeatures::opUnarySignedOverflow());
     cir::UnaryOpKind kind =
         e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
     switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
     case LangOptions::SOB_Defined:
-      return emitUnaryOp(e, kind, inVal);
+      return emitUnaryOp(e, kind, inVal, /*nsw=*/false);
     case LangOptions::SOB_Undefined:
       assert(!cir::MissingFeatures::sanitizers());
-      return emitUnaryOp(e, kind, inVal);
-      break;
+      return emitUnaryOp(e, kind, inVal, /*nsw=*/true);
     case LangOptions::SOB_Trapping:
       if (!e->canOverflow())
-        return emitUnaryOp(e, kind, inVal);
+        return emitUnaryOp(e, kind, inVal, /*nsw=*/true);
       cgf.cgm.errorNYI(e->getSourceRange(), "inc/def overflow SOB_Trapping");
       return {};
     }
@@ -473,18 +471,19 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
     assert(!cir::MissingFeatures::opUnaryPromotionType());
     mlir::Value operand = Visit(e->getSubExpr());
 
-    assert(!cir::MissingFeatures::opUnarySignedOverflow());
+    bool nsw =
+        kind == cir::UnaryOpKind::Minus && e->getType()->isSignedIntegerType();
 
     // NOTE: LLVM codegen will lower this directly to either a FNeg
     // or a Sub instruction.  In CIR this will be handled later in LowerToLLVM.
-    return emitUnaryOp(e, kind, operand);
+    return emitUnaryOp(e, kind, operand, nsw);
   }
 
   mlir::Value emitUnaryOp(const UnaryOperator *e, cir::UnaryOpKind kind,
-                          mlir::Value input) {
+                          mlir::Value input, bool nsw = false) {
     return builder.create<cir::UnaryOp>(
         cgf.getLoc(e->getSourceRange().getBegin()), input.getType(), kind,
-        input);
+        input, nsw);
   }
 
   mlir::Value VisitUnaryNot(const UnaryOperator *e) {

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index b19be53947f99..48dc09d151dcf 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -860,14 +860,8 @@ mlir::LogicalResult 
CIRToLLVMUnaryOpLowering::matchAndRewrite(
   // Integer unary operations: + - ~ ++ --
   if (mlir::isa<cir::IntType>(elementType)) {
     mlir::LLVM::IntegerOverflowFlags maybeNSW =
-        mlir::LLVM::IntegerOverflowFlags::none;
-    if (mlir::dyn_cast<cir::IntType>(elementType).isSigned()) {
-      assert(!cir::MissingFeatures::opUnarySignedOverflow());
-      // TODO: For now, assume signed overflow is undefined. We'll need to add
-      // an attribute to the unary op to control this.
-      maybeNSW = mlir::LLVM::IntegerOverflowFlags::nsw;
-    }
-
+        op.getNoSignedWrap() ? mlir::LLVM::IntegerOverflowFlags::nsw
+                             : mlir::LLVM::IntegerOverflowFlags::none;
     switch (op.getKind()) {
     case cir::UnaryOpKind::Inc: {
       assert(!isVector && "++ not allowed on vector types");

diff  --git a/clang/test/CIR/CodeGen/unary.cpp 
b/clang/test/CIR/CodeGen/unary.cpp
index 3e041e14ce177..ca47c1068e08d 100644
--- a/clang/test/CIR/CodeGen/unary.cpp
+++ b/clang/test/CIR/CodeGen/unary.cpp
@@ -83,7 +83,7 @@ int inc0() {
 // CHECK:   %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
 // CHECK:   cir.store %[[ATMP]], %[[A]] : !s32i
 // CHECK:   %[[INPUT:.*]] = cir.load %[[A]]
-// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]])
+// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]]) nsw
 // CHECK:   cir.store %[[INCREMENTED]], %[[A]]
 // CHECK:   %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
 
@@ -111,8 +111,8 @@ int dec0() {
 // CHECK:   %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
 // CHECK:   cir.store %[[ATMP]], %[[A]] : !s32i
 // CHECK:   %[[INPUT:.*]] = cir.load %[[A]]
-// CHECK:   %[[INCREMENTED:.*]] = cir.unary(dec, %[[INPUT]])
-// CHECK:   cir.store %[[INCREMENTED]], %[[A]]
+// CHECK:   %[[DECREMENTED:.*]] = cir.unary(dec, %[[INPUT]]) nsw
+// CHECK:   cir.store %[[DECREMENTED]], %[[A]]
 // CHECK:   %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
 
 // LLVM: define i32 @dec0()
@@ -139,7 +139,7 @@ int inc1() {
 // CHECK:   %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
 // CHECK:   cir.store %[[ATMP]], %[[A]] : !s32i
 // CHECK:   %[[INPUT:.*]] = cir.load %[[A]]
-// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]])
+// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]]) nsw
 // CHECK:   cir.store %[[INCREMENTED]], %[[A]]
 // CHECK:   %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
 
@@ -167,8 +167,8 @@ int dec1() {
 // CHECK:   %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
 // CHECK:   cir.store %[[ATMP]], %[[A]] : !s32i
 // CHECK:   %[[INPUT:.*]] = cir.load %[[A]]
-// CHECK:   %[[INCREMENTED:.*]] = cir.unary(dec, %[[INPUT]])
-// CHECK:   cir.store %[[INCREMENTED]], %[[A]]
+// CHECK:   %[[DECREMENTED:.*]] = cir.unary(dec, %[[INPUT]]) nsw
+// CHECK:   cir.store %[[DECREMENTED]], %[[A]]
 // CHECK:   %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
 
 // LLVM: define i32 @dec1()
@@ -197,7 +197,7 @@ int inc2() {
 // CHECK:   %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
 // CHECK:   cir.store %[[ATMP]], %[[A]] : !s32i
 // CHECK:   %[[ATOB:.*]] = cir.load %[[A]]
-// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[ATOB]])
+// CHECK:   %[[INCREMENTED:.*]] = cir.unary(inc, %[[ATOB]]) nsw
 // CHECK:   cir.store %[[INCREMENTED]], %[[A]]
 // CHECK:   cir.store %[[ATOB]], %[[B]]
 // CHECK:   %[[B_TO_OUTPUT:.*]] = cir.load %[[B]]
@@ -405,3 +405,22 @@ float fpPostInc2() {
 // OGCG:   store float %[[A_INC]], ptr %[[A]], align 4
 // OGCG:   store float %[[A_LOAD]], ptr %[[B]], align 4
 // OGCG:   %[[B_TO_OUTPUT:.*]] = load float, ptr %[[B]], align 4
+
+void chars(char c) {
+// CHECK: cir.func @chars
+
+  int c1 = +c;
+  // CHECK: %[[PROMO:.*]] = cir.cast(integral, %{{.+}} : !s8i), !s32i
+  // CHECK: cir.unary(plus, %[[PROMO]]) : !s32i, !s32i
+  int c2 = -c;
+  // CHECK: %[[PROMO:.*]] = cir.cast(integral, %{{.+}} : !s8i), !s32i
+  // CHECK: cir.unary(minus, %[[PROMO]]) nsw : !s32i, !s32i
+
+  // Chars can go through some integer promotion codegen paths even when not 
promoted.
+  // These should not have nsw attributes because the intermediate promotion 
makes the
+  // overflow defined behavior.
+  ++c; // CHECK: cir.unary(inc, %{{.+}}) : !s8i, !s8i
+  --c; // CHECK: cir.unary(dec, %{{.+}}) : !s8i, !s8i
+  c++; // CHECK: cir.unary(inc, %{{.+}}) : !s8i, !s8i
+  c--; // CHECK: cir.unary(dec, %{{.+}}) : !s8i, !s8i
+}

diff  --git a/clang/test/CIR/IR/unary.cir b/clang/test/CIR/IR/unary.cir
new file mode 100644
index 0000000000000..f01121adc106e
--- /dev/null
+++ b/clang/test/CIR/IR/unary.cir
@@ -0,0 +1,50 @@
+// RUN: cir-opt %s | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+!s64i = !cir.int<s, 64>
+!u32i = !cir.int<u, 32>
+!u64i = !cir.int<u, 64>
+
+module {
+  cir.func @test_unary_unsigned() {
+    %0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a"] {alignment = 4 : i64}
+    %1 = cir.load %0 : !cir.ptr<!u32i>, !u32i
+    %2 = cir.unary(plus, %1) : !u32i, !u32i
+    %3 = cir.unary(minus, %1) : !u32i, !u32i
+    %4 = cir.unary(not, %1) : !u32i, !u32i
+    %5 = cir.unary(inc, %1) : !u32i, !u32i
+    %6 = cir.unary(dec, %1) : !u32i, !u32i
+    cir.return
+  }
+// CHECK: cir.func @test_unary_unsigned() {
+// CHECK:   %0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a"] {alignment = 4 : i64}
+// CHECK:   %1 = cir.load %0 : !cir.ptr<!u32i>, !u32i
+// CHECK:   %2 = cir.unary(plus, %1) : !u32i, !u32i
+// CHECK:   %3 = cir.unary(minus, %1) : !u32i, !u32i
+// CHECK:   %4 = cir.unary(not, %1) : !u32i, !u32i
+// CHECK:   %5 = cir.unary(inc, %1) : !u32i, !u32i
+// CHECK:   %6 = cir.unary(dec, %1) : !u32i, !u32i
+// CHECK:   cir.return
+// CHECK: }
+
+  cir.func @test_unary_signed() {
+    %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
+    %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+    %2 = cir.unary(plus, %1) : !s32i, !s32i
+    %3 = cir.unary(minus, %1) nsw : !s32i, !s32i
+    %4 = cir.unary(not, %1) : !s32i, !s32i
+    %5 = cir.unary(inc, %1) nsw : !s32i, !s32i
+    %6 = cir.unary(dec, %1) nsw : !s32i, !s32i
+    cir.return
+  }
+// CHECK: cir.func @test_unary_signed() {
+// CHECK:   %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
+// CHECK:   %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+// CHECK:   %2 = cir.unary(plus, %1) : !s32i, !s32i
+// CHECK:   %3 = cir.unary(minus, %1) nsw : !s32i, !s32i
+// CHECK:   %4 = cir.unary(not, %1) : !s32i, !s32i
+// CHECK:   %5 = cir.unary(inc, %1) nsw : !s32i, !s32i
+// CHECK:   %6 = cir.unary(dec, %1) nsw : !s32i, !s32i
+// CHECK:   cir.return
+// CHECK: }
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to