Skip to content

Commit 2cdbe0d

Browse files
committed
[CIR][CodeGen] Add basic support for increment/decrement
1 parent 493374f commit 2cdbe0d

File tree

2 files changed

+163
-4
lines changed

2 files changed

+163
-4
lines changed

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,16 +216,113 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
216216

217217
// Unary Operators.
218218
mlir::Value VisitUnaryPostDec(const UnaryOperator *E) {
219-
llvm_unreachable("NYI");
219+
return buildScalarPrePostIncDec(E);
220220
}
221221
mlir::Value VisitUnaryPostInc(const UnaryOperator *E) {
222-
llvm_unreachable("NYI");
222+
return buildScalarPrePostIncDec(E);
223223
}
224224
mlir::Value VisitUnaryPreDec(const UnaryOperator *E) {
225-
llvm_unreachable("NYI");
225+
return buildScalarPrePostIncDec(E);
226226
}
227227
mlir::Value VisitUnaryPreInc(const UnaryOperator *E) {
228-
llvm_unreachable("NYI");
228+
return buildScalarPrePostIncDec(E);
229+
}
230+
mlir::Value buildScalarPrePostIncDec(const UnaryOperator *E) {
231+
QualType type = E->getSubExpr()->getType();
232+
233+
auto LV = CGF.buildLValue(E->getSubExpr());
234+
mlir::Value Value;
235+
mlir::Value Input;
236+
237+
if (const AtomicType *atomicTy = type->getAs<AtomicType>()) {
238+
assert(0 && "no atomics inc/dec yet");
239+
} else {
240+
Value = buildLoadOfLValue(LV, E->getExprLoc());
241+
Input = Value;
242+
}
243+
244+
// NOTE: When possible, more frequent cases are handled first.
245+
246+
// Special case of integer increment that we have to check first: bool++.
247+
// Due to promotion rules, we get:
248+
// bool++ -> bool = bool + 1
249+
// -> bool = (int)bool + 1
250+
// -> bool = ((int)bool + 1 != 0)
251+
// An interesting aspect of this is that increment is always true.
252+
// Decrement does not have this property.
253+
if (E->isIncrementOp() && type->isBooleanType()) {
254+
assert(0 && "inc simplification for booleans not implemented yet");
255+
256+
// NOTE: We likely want the code below, but loading/store booleans need to work first.
257+
// See CIRGenFunction::buildFromMemory().
258+
Value = Builder.create<mlir::cir::ConstantOp>(CGF.getLoc(E->getExprLoc()),
259+
CGF.getCIRType(type),
260+
Builder.getBoolAttr(true));
261+
} else if (type->isIntegerType()) {
262+
bool canPerformLossyDemotionCheck = false;
263+
if (type->isPromotableIntegerType()) {
264+
canPerformLossyDemotionCheck = true;
265+
assert(0 && "no promotable integer inc/dec yet");
266+
}
267+
268+
if (CGF.SanOpts.hasOneOf(
269+
SanitizerKind::ImplicitIntegerArithmeticValueChange) &&
270+
canPerformLossyDemotionCheck) {
271+
assert(0 &&
272+
"perform lossy demotion case for inc/dec not implemented yet");
273+
} else if (E->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
274+
Value = buildIncDecConsiderOverflowBehavior(E, Value);
275+
} else if (E->canOverflow() && type->isUnsignedIntegerType() &&
276+
CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) {
277+
assert(0 &&
278+
"unsigned integer overflow sanitized inc/dec not implemented");
279+
} else {
280+
auto Kind = E->isIncrementOp() ? mlir::cir::UnaryOpKind::Inc
281+
: mlir::cir::UnaryOpKind::Dec;
282+
Value = buildUnaryOp(E, Kind, Input);
283+
}
284+
} else if (const PointerType *ptr = type->getAs<PointerType>()) {
285+
assert(0 && "no pointer inc/dec yet");
286+
} else if (type->isVectorType()) {
287+
assert(0 && "no vector inc/dec yet");
288+
} else if (type->isRealFloatingType()) {
289+
assert(0 && "no float inc/dec yet");
290+
} else if (type->isFixedPointType()) {
291+
assert(0 && "no fixed point inc/dec yet");
292+
} else {
293+
assert(type->castAs<ObjCObjectPointerType>());
294+
assert(0 && "no objc pointer type inc/dec yet");
295+
}
296+
297+
CIRGenFunction::SourceLocRAIIObject sourceloc{
298+
CGF, CGF.getLoc(E->getSourceRange())};
299+
300+
if (LV.isBitField())
301+
assert(0 && "no bitfield inc/dec yet");
302+
else
303+
CGF.buildStoreThroughLValue(RValue::get(Value), LV, nullptr);
304+
305+
return E->isPrefix() ? Value : Input;
306+
}
307+
308+
mlir::Value buildIncDecConsiderOverflowBehavior(const UnaryOperator *E,
309+
mlir::Value V) {
310+
switch (CGF.getLangOpts().getSignedOverflowBehavior()) {
311+
case LangOptions::SOB_Defined: {
312+
auto Kind = E->isIncrementOp() ? mlir::cir::UnaryOpKind::Inc
313+
: mlir::cir::UnaryOpKind::Dec;
314+
return buildUnaryOp(E, Kind, V);
315+
break;
316+
}
317+
case LangOptions::SOB_Undefined:
318+
assert(0 &&
319+
"inc/dec overflow behavior SOB_Undefined not implemented yet");
320+
break;
321+
case LangOptions::SOB_Trapping:
322+
assert(0 && "inc/dec overflow behavior SOB_Trapping not implemented yet");
323+
break;
324+
}
325+
llvm_unreachable("Unknown SignedOverflowBehaviorTy");
229326
}
230327

231328
mlir::Value VisitUnaryAddrOf(const UnaryOperator *E) {
@@ -256,6 +353,13 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
256353
llvm_unreachable("NYI");
257354
}
258355

356+
mlir::Value buildUnaryOp(const UnaryOperator *E, mlir::cir::UnaryOpKind kind,
357+
mlir::Value input) {
358+
return Builder.create<mlir::cir::UnaryOp>(
359+
CGF.getLoc(E->getSourceRange().getBegin()),
360+
CGF.getCIRType(E->getType()), kind, input);
361+
}
362+
259363
// C++
260364
mlir::Value VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) {
261365
llvm_unreachable("NYI");

clang/test/CIR/CodeGen/inc-dec.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fenable-clangir -Wno-unused-value -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
unsigned id0() {
5+
unsigned a = 1;
6+
return ++a;
7+
}
8+
9+
// CHECK: cir.func @_Z3id0v() -> i32 {
10+
// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr <i32>, ["__retval", uninitialized]
11+
// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr <i32>, ["a", cinit]
12+
// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]]
13+
// CHECK: %[[#AFTER_A:]] = cir.unary(inc, %[[#BEFORE_A]])
14+
// CHECK: cir.store %[[#AFTER_A]], %[[#A]]
15+
// CHECK: cir.store %[[#AFTER_A]], %[[#RET]]
16+
17+
18+
unsigned id1() {
19+
unsigned a = 1;
20+
return --a;
21+
}
22+
23+
// CHECK: cir.func @_Z3id1v() -> i32 {
24+
// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr <i32>, ["__retval", uninitialized]
25+
// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr <i32>, ["a", cinit]
26+
// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]]
27+
// CHECK: %[[#AFTER_A:]] = cir.unary(dec, %[[#BEFORE_A]])
28+
// CHECK: cir.store %[[#AFTER_A]], %[[#A]]
29+
// CHECK: cir.store %[[#AFTER_A]], %[[#RET]]
30+
31+
unsigned id2() {
32+
unsigned a = 1;
33+
return a++;
34+
}
35+
36+
// CHECK: cir.func @_Z3id2v() -> i32 {
37+
// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr <i32>, ["__retval", uninitialized]
38+
// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr <i32>, ["a", cinit]
39+
// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]]
40+
// CHECK: %[[#AFTER_A:]] = cir.unary(inc, %[[#BEFORE_A]])
41+
// CHECK: cir.store %[[#AFTER_A]], %[[#A]]
42+
// CHECK: cir.store %[[#BEFORE_A]], %[[#RET]]
43+
44+
unsigned id3() {
45+
unsigned a = 1;
46+
return a--;
47+
}
48+
49+
// CHECK: cir.func @_Z3id3v() -> i32 {
50+
// CHECK: %[[#RET:]] = cir.alloca i32, cir.ptr <i32>, ["__retval", uninitialized]
51+
// CHECK: %[[#A:]] = cir.alloca i32, cir.ptr <i32>, ["a", cinit]
52+
// CHECK: %[[#BEFORE_A:]] = cir.load %[[#A]]
53+
// CHECK: %[[#AFTER_A:]] = cir.unary(dec, %[[#BEFORE_A]])
54+
// CHECK: cir.store %[[#AFTER_A]], %[[#A]]
55+
// CHECK: cir.store %[[#BEFORE_A]], %[[#RET]]

0 commit comments

Comments
 (0)