Skip to content

Commit c57b9c2

Browse files
authored
[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.
1 parent 3f7ca88 commit c57b9c2

File tree

6 files changed

+111
-31
lines changed

6 files changed

+111
-31
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -697,17 +697,24 @@ def UnaryOp : CIR_Op<"unary", [Pure, SameOperandsAndResultType]> {
697697
It requires one input operand and has one result, both types
698698
should be the same.
699699

700+
If the `nsw` (no signed wrap) attribute is present, the result is poison if
701+
signed overflow occurs.
702+
700703
```mlir
701704
%7 = cir.unary(inc, %1) : i32 -> i32
702-
%8 = cir.unary(dec, %2) : i32 -> i32
705+
%8 = cir.unary(dec, %2) nsw : i32 -> i32
703706
```
704707
}];
705708

706709
let results = (outs CIR_AnyType:$result);
707-
let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind, Arg<CIR_AnyType>:$input);
710+
let arguments = (ins Arg<UnaryOpKind, "unary op kind">:$kind,
711+
Arg<CIR_AnyType>:$input,
712+
UnitAttr:$no_signed_wrap);
708713

709714
let assemblyFormat = [{
710-
`(` $kind `,` $input `)` `:` type($input) `,` type($result) attr-dict
715+
`(` $kind `,` $input `)`
716+
(`nsw` $no_signed_wrap^)?
717+
`:` type($input) `,` type($result) attr-dict
711718
}];
712719

713720
let hasVerifier = 1;
@@ -961,9 +968,21 @@ def BinOp : CIR_Op<"binop", [Pure,
961968
It requires two input operands and has one result, all types
962969
should be the same.
963970

971+
If the `nsw` (no signed wrap) or `nuw` (no unsigned wrap) attributes are
972+
present, the result is poison if signed or unsigned overflow occurs
973+
(respectively).
974+
975+
If the `sat` (saturated) attribute is present, the result is clamped to
976+
the maximum value representatable by the type if it would otherwise
977+
exceed that value and is clamped to the minimum representable value if
978+
it would otherwise be below that value.
979+
964980
```mlir
965-
%7 = cir.binop(add, %1, %2) : !s32i
966-
%7 = cir.binop(mul, %1, %2) : !u8i
981+
%5 = cir.binop(add, %1, %2) : !s32i
982+
%6 = cir.binop(mul, %1, %2) : !u8i
983+
%7 = cir.binop(add, %1, %2) nsw : !s32i
984+
%8 = cir.binop(add, %3, %4) nuw : !u32i
985+
%9 = cir.binop(add, %1, %2) sat : !s32i
967986
```
968987
}];
969988

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ struct MissingFeatures {
7676
static bool opScopeCleanupRegion() { return false; }
7777

7878
// Unary operator handling
79-
static bool opUnarySignedOverflow() { return false; }
8079
static bool opUnaryPromotionType() { return false; }
8180

8281
// Clang early optimizations or things defered to LLVM lowering.

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
374374
cir::UnaryOpKind kind =
375375
e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
376376
// NOTE(CIR): clang calls CreateAdd but folds this to a unary op
377-
value = emitUnaryOp(e, kind, input);
377+
value = emitUnaryOp(e, kind, input, /*nsw=*/false);
378378
}
379379
} else if (isa<PointerType>(type)) {
380380
cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec pointer");
@@ -429,19 +429,17 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
429429
mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *e,
430430
mlir::Value inVal,
431431
bool isInc) {
432-
assert(!cir::MissingFeatures::opUnarySignedOverflow());
433432
cir::UnaryOpKind kind =
434433
e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
435434
switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
436435
case LangOptions::SOB_Defined:
437-
return emitUnaryOp(e, kind, inVal);
436+
return emitUnaryOp(e, kind, inVal, /*nsw=*/false);
438437
case LangOptions::SOB_Undefined:
439438
assert(!cir::MissingFeatures::sanitizers());
440-
return emitUnaryOp(e, kind, inVal);
441-
break;
439+
return emitUnaryOp(e, kind, inVal, /*nsw=*/true);
442440
case LangOptions::SOB_Trapping:
443441
if (!e->canOverflow())
444-
return emitUnaryOp(e, kind, inVal);
442+
return emitUnaryOp(e, kind, inVal, /*nsw=*/true);
445443
cgf.cgm.errorNYI(e->getSourceRange(), "inc/def overflow SOB_Trapping");
446444
return {};
447445
}
@@ -473,18 +471,19 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
473471
assert(!cir::MissingFeatures::opUnaryPromotionType());
474472
mlir::Value operand = Visit(e->getSubExpr());
475473

476-
assert(!cir::MissingFeatures::opUnarySignedOverflow());
474+
bool nsw =
475+
kind == cir::UnaryOpKind::Minus && e->getType()->isSignedIntegerType();
477476

478477
// NOTE: LLVM codegen will lower this directly to either a FNeg
479478
// or a Sub instruction. In CIR this will be handled later in LowerToLLVM.
480-
return emitUnaryOp(e, kind, operand);
479+
return emitUnaryOp(e, kind, operand, nsw);
481480
}
482481

483482
mlir::Value emitUnaryOp(const UnaryOperator *e, cir::UnaryOpKind kind,
484-
mlir::Value input) {
483+
mlir::Value input, bool nsw = false) {
485484
return builder.create<cir::UnaryOp>(
486485
cgf.getLoc(e->getSourceRange().getBegin()), input.getType(), kind,
487-
input);
486+
input, nsw);
488487
}
489488

490489
mlir::Value VisitUnaryNot(const UnaryOperator *e) {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -860,14 +860,8 @@ mlir::LogicalResult CIRToLLVMUnaryOpLowering::matchAndRewrite(
860860
// Integer unary operations: + - ~ ++ --
861861
if (mlir::isa<cir::IntType>(elementType)) {
862862
mlir::LLVM::IntegerOverflowFlags maybeNSW =
863-
mlir::LLVM::IntegerOverflowFlags::none;
864-
if (mlir::dyn_cast<cir::IntType>(elementType).isSigned()) {
865-
assert(!cir::MissingFeatures::opUnarySignedOverflow());
866-
// TODO: For now, assume signed overflow is undefined. We'll need to add
867-
// an attribute to the unary op to control this.
868-
maybeNSW = mlir::LLVM::IntegerOverflowFlags::nsw;
869-
}
870-
863+
op.getNoSignedWrap() ? mlir::LLVM::IntegerOverflowFlags::nsw
864+
: mlir::LLVM::IntegerOverflowFlags::none;
871865
switch (op.getKind()) {
872866
case cir::UnaryOpKind::Inc: {
873867
assert(!isVector && "++ not allowed on vector types");

clang/test/CIR/CodeGen/unary.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ int inc0() {
8383
// CHECK: %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
8484
// CHECK: cir.store %[[ATMP]], %[[A]] : !s32i
8585
// CHECK: %[[INPUT:.*]] = cir.load %[[A]]
86-
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]])
86+
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]]) nsw
8787
// CHECK: cir.store %[[INCREMENTED]], %[[A]]
8888
// CHECK: %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
8989

@@ -111,8 +111,8 @@ int dec0() {
111111
// CHECK: %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
112112
// CHECK: cir.store %[[ATMP]], %[[A]] : !s32i
113113
// CHECK: %[[INPUT:.*]] = cir.load %[[A]]
114-
// CHECK: %[[INCREMENTED:.*]] = cir.unary(dec, %[[INPUT]])
115-
// CHECK: cir.store %[[INCREMENTED]], %[[A]]
114+
// CHECK: %[[DECREMENTED:.*]] = cir.unary(dec, %[[INPUT]]) nsw
115+
// CHECK: cir.store %[[DECREMENTED]], %[[A]]
116116
// CHECK: %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
117117

118118
// LLVM: define i32 @dec0()
@@ -139,7 +139,7 @@ int inc1() {
139139
// CHECK: %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
140140
// CHECK: cir.store %[[ATMP]], %[[A]] : !s32i
141141
// CHECK: %[[INPUT:.*]] = cir.load %[[A]]
142-
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]])
142+
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[INPUT]]) nsw
143143
// CHECK: cir.store %[[INCREMENTED]], %[[A]]
144144
// CHECK: %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
145145

@@ -167,8 +167,8 @@ int dec1() {
167167
// CHECK: %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
168168
// CHECK: cir.store %[[ATMP]], %[[A]] : !s32i
169169
// CHECK: %[[INPUT:.*]] = cir.load %[[A]]
170-
// CHECK: %[[INCREMENTED:.*]] = cir.unary(dec, %[[INPUT]])
171-
// CHECK: cir.store %[[INCREMENTED]], %[[A]]
170+
// CHECK: %[[DECREMENTED:.*]] = cir.unary(dec, %[[INPUT]]) nsw
171+
// CHECK: cir.store %[[DECREMENTED]], %[[A]]
172172
// CHECK: %[[A_TO_OUTPUT:.*]] = cir.load %[[A]]
173173

174174
// LLVM: define i32 @dec1()
@@ -197,7 +197,7 @@ int inc2() {
197197
// CHECK: %[[ATMP:.*]] = cir.const #cir.int<1> : !s32i
198198
// CHECK: cir.store %[[ATMP]], %[[A]] : !s32i
199199
// CHECK: %[[ATOB:.*]] = cir.load %[[A]]
200-
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[ATOB]])
200+
// CHECK: %[[INCREMENTED:.*]] = cir.unary(inc, %[[ATOB]]) nsw
201201
// CHECK: cir.store %[[INCREMENTED]], %[[A]]
202202
// CHECK: cir.store %[[ATOB]], %[[B]]
203203
// CHECK: %[[B_TO_OUTPUT:.*]] = cir.load %[[B]]
@@ -405,3 +405,22 @@ float fpPostInc2() {
405405
// OGCG: store float %[[A_INC]], ptr %[[A]], align 4
406406
// OGCG: store float %[[A_LOAD]], ptr %[[B]], align 4
407407
// OGCG: %[[B_TO_OUTPUT:.*]] = load float, ptr %[[B]], align 4
408+
409+
void chars(char c) {
410+
// CHECK: cir.func @chars
411+
412+
int c1 = +c;
413+
// CHECK: %[[PROMO:.*]] = cir.cast(integral, %{{.+}} : !s8i), !s32i
414+
// CHECK: cir.unary(plus, %[[PROMO]]) : !s32i, !s32i
415+
int c2 = -c;
416+
// CHECK: %[[PROMO:.*]] = cir.cast(integral, %{{.+}} : !s8i), !s32i
417+
// CHECK: cir.unary(minus, %[[PROMO]]) nsw : !s32i, !s32i
418+
419+
// Chars can go through some integer promotion codegen paths even when not promoted.
420+
// These should not have nsw attributes because the intermediate promotion makes the
421+
// overflow defined behavior.
422+
++c; // CHECK: cir.unary(inc, %{{.+}}) : !s8i, !s8i
423+
--c; // CHECK: cir.unary(dec, %{{.+}}) : !s8i, !s8i
424+
c++; // CHECK: cir.unary(inc, %{{.+}}) : !s8i, !s8i
425+
c--; // CHECK: cir.unary(dec, %{{.+}}) : !s8i, !s8i
426+
}

clang/test/CIR/IR/unary.cir

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: cir-opt %s | FileCheck %s
2+
3+
!s32i = !cir.int<s, 32>
4+
!s64i = !cir.int<s, 64>
5+
!u32i = !cir.int<u, 32>
6+
!u64i = !cir.int<u, 64>
7+
8+
module {
9+
cir.func @test_unary_unsigned() {
10+
%0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a"] {alignment = 4 : i64}
11+
%1 = cir.load %0 : !cir.ptr<!u32i>, !u32i
12+
%2 = cir.unary(plus, %1) : !u32i, !u32i
13+
%3 = cir.unary(minus, %1) : !u32i, !u32i
14+
%4 = cir.unary(not, %1) : !u32i, !u32i
15+
%5 = cir.unary(inc, %1) : !u32i, !u32i
16+
%6 = cir.unary(dec, %1) : !u32i, !u32i
17+
cir.return
18+
}
19+
// CHECK: cir.func @test_unary_unsigned() {
20+
// CHECK: %0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["a"] {alignment = 4 : i64}
21+
// CHECK: %1 = cir.load %0 : !cir.ptr<!u32i>, !u32i
22+
// CHECK: %2 = cir.unary(plus, %1) : !u32i, !u32i
23+
// CHECK: %3 = cir.unary(minus, %1) : !u32i, !u32i
24+
// CHECK: %4 = cir.unary(not, %1) : !u32i, !u32i
25+
// CHECK: %5 = cir.unary(inc, %1) : !u32i, !u32i
26+
// CHECK: %6 = cir.unary(dec, %1) : !u32i, !u32i
27+
// CHECK: cir.return
28+
// CHECK: }
29+
30+
cir.func @test_unary_signed() {
31+
%0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
32+
%1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
33+
%2 = cir.unary(plus, %1) : !s32i, !s32i
34+
%3 = cir.unary(minus, %1) nsw : !s32i, !s32i
35+
%4 = cir.unary(not, %1) : !s32i, !s32i
36+
%5 = cir.unary(inc, %1) nsw : !s32i, !s32i
37+
%6 = cir.unary(dec, %1) nsw : !s32i, !s32i
38+
cir.return
39+
}
40+
// CHECK: cir.func @test_unary_signed() {
41+
// CHECK: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
42+
// CHECK: %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i
43+
// CHECK: %2 = cir.unary(plus, %1) : !s32i, !s32i
44+
// CHECK: %3 = cir.unary(minus, %1) nsw : !s32i, !s32i
45+
// CHECK: %4 = cir.unary(not, %1) : !s32i, !s32i
46+
// CHECK: %5 = cir.unary(inc, %1) nsw : !s32i, !s32i
47+
// CHECK: %6 = cir.unary(dec, %1) nsw : !s32i, !s32i
48+
// CHECK: cir.return
49+
// CHECK: }
50+
}

0 commit comments

Comments
 (0)