diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 75fcdb4d360b..f30086af49ac 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1146,10 +1146,14 @@ def UnaryOp : CIR_Op<"unary", [Pure, SameOperandsAndResultType]> { }]; let results = (outs CIR_AnyType:$result); - let arguments = (ins Arg:$kind, Arg:$input); + let arguments = (ins Arg:$kind, + Arg:$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; diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index de92f0f19778..d9dc455cf374 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -467,7 +467,7 @@ class ScalarExprEmitter : public StmtVisitor { auto 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); } // Next most common: pointer increment. } else if (const PointerType *ptr = type->getAs()) { @@ -580,22 +580,20 @@ class ScalarExprEmitter : public StmtVisitor { mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *E, mlir::Value InVal, bool IsInc) { - // NOTE(CIR): The SignedOverflowBehavior is attached to the global ModuleOp - // and the nsw behavior is handled during lowering. auto 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: if (!CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) - return emitUnaryOp(E, Kind, InVal); + return emitUnaryOp(E, Kind, InVal, /*nsw=*/true); llvm_unreachable( "inc/dec overflow behavior SOB_Undefined not implemented yet"); break; case LangOptions::SOB_Trapping: if (!E->canOverflow()) - return emitUnaryOp(E, Kind, InVal); + return emitUnaryOp(E, Kind, InVal, /*nsw=*/true); llvm_unreachable( "inc/dec overflow behavior SOB_Trapping not implemented yet"); break; @@ -661,7 +659,8 @@ class ScalarExprEmitter : public StmtVisitor { // 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, cir::UnaryOpKind::Minus, operand); + return emitUnaryOp(E, cir::UnaryOpKind::Minus, operand, + /*nsw=*/E->getType()->isSignedIntegerType()); } mlir::Value VisitUnaryNot(const UnaryOperator *E) { @@ -684,10 +683,10 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value emitUnaryOp(const UnaryOperator *E, cir::UnaryOpKind kind, - mlir::Value input) { + mlir::Value input, bool nsw = false) { return Builder.create( CGF.getLoc(E->getSourceRange().getBegin()), input.getType(), kind, - input); + input, nsw); } // C++ diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 4d56e16db784..c014bab10d3e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2553,21 +2553,24 @@ mlir::LogicalResult CIRToLLVMUnaryOpLowering::matchAndRewrite( // Integer unary operations: + - ~ ++ -- if (mlir::isa(elementType)) { + auto overflowFlags = op.getNoSignedWrap() + ? mlir::LLVM::IntegerOverflowFlags::nsw + : mlir::LLVM::IntegerOverflowFlags::none; switch (op.getKind()) { case cir::UnaryOpKind::Inc: { assert(!IsVector && "++ not allowed on vector types"); auto One = rewriter.create( loc, llvmType, mlir::IntegerAttr::get(llvmType, 1)); - rewriter.replaceOpWithNewOp(op, llvmType, - adaptor.getInput(), One); + rewriter.replaceOpWithNewOp( + op, llvmType, adaptor.getInput(), One, overflowFlags); return mlir::success(); } case cir::UnaryOpKind::Dec: { assert(!IsVector && "-- not allowed on vector types"); auto One = rewriter.create( loc, llvmType, mlir::IntegerAttr::get(llvmType, 1)); - rewriter.replaceOpWithNewOp(op, llvmType, - adaptor.getInput(), One); + rewriter.replaceOpWithNewOp( + op, llvmType, adaptor.getInput(), One, overflowFlags); return mlir::success(); } case cir::UnaryOpKind::Plus: { @@ -2581,8 +2584,8 @@ mlir::LogicalResult CIRToLLVMUnaryOpLowering::matchAndRewrite( else Zero = rewriter.create( loc, llvmType, mlir::IntegerAttr::get(llvmType, 0)); - rewriter.replaceOpWithNewOp(op, llvmType, Zero, - adaptor.getInput()); + rewriter.replaceOpWithNewOp( + op, llvmType, Zero, adaptor.getInput(), overflowFlags); return mlir::success(); } case cir::UnaryOpKind::Not: { diff --git a/clang/test/CIR/CodeGen/bitfields.c b/clang/test/CIR/CodeGen/bitfields.c index cd8e88f5c4c9..39511a3401cf 100644 --- a/clang/test/CIR/CodeGen/bitfields.c +++ b/clang/test/CIR/CodeGen/bitfields.c @@ -89,7 +89,7 @@ int load_field(S* s) { // CHECK: cir.func {{.*@unOp}} // CHECK: [[TMP0:%.*]] = cir.get_member {{.*}}[1] {name = "d"} : !cir.ptr -> !cir.ptr> // CHECK: [[TMP1:%.*]] = cir.get_bitfield(#bfi_d, [[TMP0]] : !cir.ptr>) -> !s32i -// CHECK: [[TMP2:%.*]] = cir.unary(inc, [[TMP1]]) : !s32i, !s32i +// CHECK: [[TMP2:%.*]] = cir.unary(inc, [[TMP1]]) nsw : !s32i, !s32i // CHECK: cir.set_bitfield(#bfi_d, [[TMP0]] : !cir.ptr>, [[TMP2]] : !s32i) void unOp(S* s) { s->d++; diff --git a/clang/test/CIR/CodeGen/int128.cpp b/clang/test/CIR/CodeGen/int128.cpp index 97539e4317c9..5244b9d2fa62 100644 --- a/clang/test/CIR/CodeGen/int128.cpp +++ b/clang/test/CIR/CodeGen/int128.cpp @@ -26,8 +26,8 @@ unsigned __int128 test2(unsigned __int128 x) { // LLVM-LABEL: @_Z11unary_arithn __int128 unary_arith(__int128 x) { return ++x; - // CHECK: %{{.+}} = cir.unary(inc, %{{.+}}) : !s128i, !s128i - // LLVM: %{{.+}} = add i128 %{{.+}}, 1 + // CHECK: %{{.+}} = cir.unary(inc, %{{.+}}) nsw : !s128i, !s128i + // LLVM: %{{.+}} = add nsw i128 %{{.+}}, 1 } // CHECK-LABEL: @_Z12binary_arithnn diff --git a/clang/test/CIR/CodeGen/static-vars.c b/clang/test/CIR/CodeGen/static-vars.c index 140f4e6052f6..5cc2158059cc 100644 --- a/clang/test/CIR/CodeGen/static-vars.c +++ b/clang/test/CIR/CodeGen/static-vars.c @@ -24,7 +24,7 @@ void func1(void) { j++; // CHECK-DAG: %[[#V2:]] = cir.get_global @func1.j : !cir.ptr // CHECK-DAG: %[[#V3:]] = cir.load %[[#V2]] : !cir.ptr, !s32i - // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) : !s32i, !s32i + // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) nsw : !s32i, !s32i // CHECK-DAG: cir.store %[[#V4]], %[[#V2]] : !s32i, !cir.ptr } diff --git a/clang/test/CIR/CodeGen/static-vars.cpp b/clang/test/CIR/CodeGen/static-vars.cpp index c8d1ff5ed439..300629cacf01 100644 --- a/clang/test/CIR/CodeGen/static-vars.cpp +++ b/clang/test/CIR/CodeGen/static-vars.cpp @@ -26,7 +26,7 @@ void func1(void) { j++; // CHECK-DAG: %[[#V2:]] = cir.get_global @_ZZ5func1vE1j : !cir.ptr // CHECK-DAG: %[[#V3:]] = cir.load %[[#V2]] : !cir.ptr, !s32i - // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) : !s32i, !s32i + // CHECK-DAG: %[[#V4:]] = cir.unary(inc, %[[#V3]]) nsw : !s32i, !s32i // CHECK-DAG: cir.store %[[#V4]], %[[#V2]] : !s32i, !cir.ptr } diff --git a/clang/test/CIR/CodeGen/throw.cpp b/clang/test/CIR/CodeGen/throw.cpp index dcaf2ce11f58..88dfe4284295 100644 --- a/clang/test/CIR/CodeGen/throw.cpp +++ b/clang/test/CIR/CodeGen/throw.cpp @@ -52,7 +52,7 @@ void refoo1() { // CIR: } catch [type #cir.all { // CIR: %[[V3:.*]] = cir.catch_param -> !cir.ptr // CIR: %[[V4:.*]] = cir.load %[[V0]] : !cir.ptr, !s32i -// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) : !s32i, !s32i +// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) nsw : !s32i, !s32i // CIR: cir.store %[[V5]], %[[V0]] : !s32i, !cir.ptr // CIR: cir.yield // CIR: }] @@ -91,7 +91,7 @@ void refoo1() { // LLVM: %[[V16:.*]] = phi ptr [ %[[V9]], %[[B7]] ], [ %[[V13]], %[[B11]] ] // LLVM: %[[V17:.*]] = call ptr @__cxa_begin_catch(ptr %[[V16]]) // LLVM: %[[V18:.*]] = load i32, ptr %[[V2]], align 4 -// LLVM: %[[V19:.*]] = add i32 %[[V18]], 1 +// LLVM: %[[V19:.*]] = add nsw i32 %[[V18]], 1 // LLVM: store i32 %[[V19]], ptr %[[V2]], align 4 // LLVM: call void @__cxa_end_catch() @@ -136,7 +136,7 @@ void refoo2() { // CIR: cir.yield // CIR: } step { // CIR: %[[V5:.*]] = cir.load %[[V3]] : !cir.ptr, !s32i -// CIR: %[[V6:.*]] = cir.unary(inc, %[[V5]]) : !s32i, !s32i +// CIR: %[[V6:.*]] = cir.unary(inc, %[[V5]]) nsw : !s32i, !s32i // CIR: cir.store %[[V6]], %[[V3]] : !s32i, !cir.ptr // CIR: cir.yield // CIR: } @@ -146,7 +146,7 @@ void refoo2() { // CIR: } catch [type #cir.all { // CIR: %[[V3:.*]] = cir.catch_param -> !cir.ptr // CIR: %[[V4:.*]] = cir.load %[[V0]] : !cir.ptr, !s32i -// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) : !s32i, !s32i +// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) nsw : !s32i, !s32i // CIR: cir.store %[[V5]], %[[V0]] : !s32i, !cir.ptr // CIR: cir.yield // CIR: }] @@ -166,7 +166,7 @@ void refoo2() { // LLVM: br label %[[B16:.*]] // LLVM: [[B16]]: // LLVM: %[[V17]] = load i32, ptr {{.*}}, align 4 -// LLVM: %[[V18]] = add i32 %[[V17]], 1 +// LLVM: %[[V18]] = add nsw i32 %[[V17]], 1 // LLVM: store i32 %[[V18]], ptr {{.*}}, align 4 // LLVM: br label {{.*}} // LLVM: %[[B19:.*]] @@ -198,7 +198,7 @@ void refoo2() { // LLVM: %[[V35:.*]] = phi ptr [ %[[V32]], %[[B30]] ], [ %[[V24]], %[[B22]] ], [ %[[V28]], %[[B26]] ] // LLVM: %[[V36:.*]] = call ptr @__cxa_begin_catch(ptr %[[V35]]) // LLVM: %[[V37:.*]] = load i32, ptr {{.*}}, align 4 -// LLVM: %[[V38:.*]] = add i32 %[[V37]], 1 +// LLVM: %[[V38:.*]] = add nsw i32 %[[V37]], 1 // LLVM: store i32 %[[V38]], ptr {{.*}}, align 4 // LLVM: call void @__cxa_end_catch() // LLVM: br label {{.*}} @@ -228,7 +228,7 @@ void refoo3() { // CIR: } catch [type #cir.all { // CIR: %[[V3:.*]] = cir.catch_param -> !cir.ptr // CIR: %[[V4:.*]] = cir.load %[[V0]] : !cir.ptr, !s32i -// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) : !s32i, !s32i +// CIR: %[[V5:.*]] = cir.unary(inc, %[[V4]]) nsw : !s32i, !s32i // CIR: cir.store %[[V5]], %[[V0]] : !s32i, !cir.ptr // CIR: cir.yield // CIR: }] @@ -261,7 +261,7 @@ void refoo3() { // LLVM: %[[V17:.*]] = phi ptr [ %[[V14]], %[[B12]] ], [ %[[V10]], %[[B8]] ] // LLVM: %[[V18:.*]] = call ptr @__cxa_begin_catch(ptr %[[V17]]) // LLVM: %[[V19:.*]] = load i32, ptr {{.*}}, align 4 -// LLVM: %[[V20:.*]] = add i32 %[[V19]], 1 +// LLVM: %[[V20:.*]] = add nsw i32 %[[V19]], 1 // LLVM: store i32 %[[V20]], ptr {{.*}}, align 4 // LLVM: call void @__cxa_end_catch() // LLVM: br label %[[B21]] diff --git a/clang/test/CIR/CodeGen/try-catch.cpp b/clang/test/CIR/CodeGen/try-catch.cpp index 570017f83553..80297dd5c1b5 100644 --- a/clang/test/CIR/CodeGen/try-catch.cpp +++ b/clang/test/CIR/CodeGen/try-catch.cpp @@ -144,7 +144,7 @@ void tc6() { // CHECK: cir.return // CHECK: ^bb1: // no predecessors // CHECK: %[[V2:.*]] = cir.load {{.*}} : !cir.ptr, !s32i -// CHECK: %[[V3:.*]] = cir.unary(inc, %[[V2]]) : !s32i, !s32i +// CHECK: %[[V3:.*]] = cir.unary(inc, %[[V2]]) nsw : !s32i, !s32i // CHECK: cir.store %[[V3]], {{.*}} : !s32i, !cir.ptr // CHECK: cir.yield // CHECK: } diff --git a/clang/test/CIR/CodeGen/unary.cpp b/clang/test/CIR/CodeGen/unary.cpp index 986e9b2dcedc..d86f9908e5b7 100644 --- a/clang/test/CIR/CodeGen/unary.cpp +++ b/clang/test/CIR/CodeGen/unary.cpp @@ -49,7 +49,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]] // CHECK: cir.store %[[#A_TO_OUTPUT]], %[[#RET]] @@ -68,7 +68,7 @@ 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: %[[#INCREMENTED:]] = cir.unary(dec, %[[#INPUT]]) nsw // CHECK: cir.store %[[#INCREMENTED]], %[[#A]] // CHECK: %[[#A_TO_OUTPUT:]] = cir.load %[[#A]] // CHECK: cir.store %[[#A_TO_OUTPUT]], %[[#RET]] @@ -88,7 +88,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]] // CHECK: cir.store %[[#A_TO_OUTPUT]], %[[#RET]] @@ -107,7 +107,7 @@ 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: %[[#INCREMENTED:]] = cir.unary(dec, %[[#INPUT]]) nsw // CHECK: cir.store %[[#INCREMENTED]], %[[#A]] // CHECK: %[[#A_TO_OUTPUT:]] = cir.load %[[#A]] // CHECK: cir.store %[[#A_TO_OUTPUT]], %[[#RET]] @@ -128,7 +128,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]] @@ -218,7 +218,7 @@ void chars(char c) { // CHECK: cir.unary(plus, %[[#PROMO]]) : !s32i, !s32i int c2 = -c; // CHECK: %[[#PROMO:]] = cir.cast(integral, %{{.+}} : !s8i), !s32i - // CHECK: cir.unary(minus, %[[#PROMO]]) : !s32i, !s32i + // CHECK: cir.unary(minus, %[[#PROMO]]) nsw : !s32i, !s32i // Chars can go through some integer promotion codegen paths even when not promoted. ++c; // CHECK: cir.unary(inc, %10) : !s8i, !s8i diff --git a/clang/test/CIR/Lowering/switch-while.c b/clang/test/CIR/Lowering/switch-while.c index 078f0045716c..9123d5532960 100644 --- a/clang/test/CIR/Lowering/switch-while.c +++ b/clang/test/CIR/Lowering/switch-while.c @@ -42,7 +42,7 @@ int f(int a, int cond) { // CHECK: br label %[[LOOP_HEADER:.+]] // // CHECK: [[LOOP_HEADER]]: -// CHECK: add i32 %{{.*}}, 1 +// CHECK: add nsw i32 %{{.*}}, 1 // CHECK: br label %[[DEFAULT_BB:.+]] // // CHECK: [[DEFAULT_BB]]: @@ -61,7 +61,7 @@ int f(int a, int cond) { // CHECK: add nsw i32 %[[V1]], %[[V2]] // // CHECK: [[TWO_BB]]: -// CHECK: add i32 %{{.*}}, 1 +// CHECK: add nsw i32 %{{.*}}, 1 // CHECK: br label %[[FALLTHOUGH_BB:.+]] // // CHECK: [[FALLTHOUGH_BB]]: diff --git a/clang/test/CIR/Transforms/mem2reg.c b/clang/test/CIR/Transforms/mem2reg.c index b60d9eb0d1e9..6e0ad9a4e529 100644 --- a/clang/test/CIR/Transforms/mem2reg.c +++ b/clang/test/CIR/Transforms/mem2reg.c @@ -57,7 +57,7 @@ void alloca_in_loop(int* ar, int n) { // BEFORE: cir.yield // BEFORE: } step { // BEFORE: %4 = cir.load %2 : !cir.ptr, !s32i -// BEFORE: %5 = cir.unary(inc, %4) : !s32i, !s32i +// BEFORE: %5 = cir.unary(inc, %4) nsw : !s32i, !s32i // BEFORE: cir.store %5, %2 : !s32i, !cir.ptr // BEFORE: cir.yield // BEFORE: } @@ -82,7 +82,7 @@ void alloca_in_loop(int* ar, int n) { // MEM2REG: ^bb5: // pred: ^bb4 // MEM2REG: cir.br ^bb6 // MEM2REG: ^bb6: // pred: ^bb5 -// MEM2REG: %5 = cir.unary(inc, %1) : !s32i, !s32i +// MEM2REG: %5 = cir.unary(inc, %1) nsw : !s32i, !s32i // MEM2REG: cir.br ^bb2(%5 : !s32i) // MEM2REG: ^bb7: // pred: ^bb2 // MEM2REG: cir.br ^bb8