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