@@ -94,6 +94,222 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
9494
9595 mlir::Value VisitUnaryExprOrTypeTraitExpr (const UnaryExprOrTypeTraitExpr *e);
9696
97+ // Unary Operators.
98+ mlir::Value VisitUnaryPostDec (const UnaryOperator *e) {
99+ LValue lv = cgf.emitLValue (e->getSubExpr ());
100+ return emitScalarPrePostIncDec (e, lv, false , false );
101+ }
102+ mlir::Value VisitUnaryPostInc (const UnaryOperator *e) {
103+ LValue lv = cgf.emitLValue (e->getSubExpr ());
104+ return emitScalarPrePostIncDec (e, lv, true , false );
105+ }
106+ mlir::Value VisitUnaryPreDec (const UnaryOperator *e) {
107+ LValue lv = cgf.emitLValue (e->getSubExpr ());
108+ return emitScalarPrePostIncDec (e, lv, false , true );
109+ }
110+ mlir::Value VisitUnaryPreInc (const UnaryOperator *e) {
111+ LValue lv = cgf.emitLValue (e->getSubExpr ());
112+ return emitScalarPrePostIncDec (e, lv, true , true );
113+ }
114+ mlir::Value emitScalarPrePostIncDec (const UnaryOperator *e, LValue lv,
115+ bool isInc, bool isPre) {
116+ if (cgf.getLangOpts ().OpenMP )
117+ cgf.cgm .errorNYI (e->getSourceRange (), " inc/dec OpenMP" );
118+
119+ QualType type = e->getSubExpr ()->getType ();
120+
121+ mlir::Value value;
122+ mlir::Value input;
123+
124+ if (type->getAs <AtomicType>()) {
125+ cgf.cgm .errorNYI (e->getSourceRange (), " Atomic inc/dec" );
126+ // TODO(cir): This is not correct, but it will produce reasonable code
127+ // until atomic operations are implemented.
128+ value = cgf.emitLoadOfLValue (lv, e->getExprLoc ()).getScalarVal ();
129+ input = value;
130+ } else {
131+ value = cgf.emitLoadOfLValue (lv, e->getExprLoc ()).getScalarVal ();
132+ input = value;
133+ }
134+
135+ // NOTE: When possible, more frequent cases are handled first.
136+
137+ // Special case of integer increment that we have to check first: bool++.
138+ // Due to promotion rules, we get:
139+ // bool++ -> bool = bool + 1
140+ // -> bool = (int)bool + 1
141+ // -> bool = ((int)bool + 1 != 0)
142+ // An interesting aspect of this is that increment is always true.
143+ // Decrement does not have this property.
144+ if (isInc && type->isBooleanType ()) {
145+ value = builder.create <cir::ConstantOp>(cgf.getLoc (e->getExprLoc ()),
146+ cgf.convertType (type),
147+ builder.getCIRBoolAttr (true ));
148+ } else if (type->isIntegerType ()) {
149+ QualType promotedType;
150+ bool canPerformLossyDemotionCheck = false ;
151+ if (cgf.getContext ().isPromotableIntegerType (type)) {
152+ promotedType = cgf.getContext ().getPromotedIntegerType (type);
153+ assert (promotedType != type && " Shouldn't promote to the same type." );
154+ canPerformLossyDemotionCheck = true ;
155+ canPerformLossyDemotionCheck &=
156+ cgf.getContext ().getCanonicalType (type) !=
157+ cgf.getContext ().getCanonicalType (promotedType);
158+ canPerformLossyDemotionCheck &=
159+ type->isIntegerType () && promotedType->isIntegerType ();
160+
161+ // TODO(cir): Currently, we store bitwidths in CIR types only for
162+ // integers. This might also be required for other types.
163+ auto srcCirTy = mlir::dyn_cast<cir::IntType>(cgf.convertType (type));
164+ auto promotedCirTy =
165+ mlir::dyn_cast<cir::IntType>(cgf.convertType (type));
166+ assert (srcCirTy && promotedCirTy && " Expected integer type" );
167+
168+ assert (
169+ (!canPerformLossyDemotionCheck ||
170+ type->isSignedIntegerOrEnumerationType () ||
171+ promotedType->isSignedIntegerOrEnumerationType () ||
172+ srcCirTy.getWidth () == promotedCirTy.getWidth ()) &&
173+ " The following check expects that if we do promotion to different "
174+ " underlying canonical type, at least one of the types (either "
175+ " base or promoted) will be signed, or the bitwidths will match." );
176+ }
177+
178+ assert (!cir::MissingFeatures::sanitizers ());
179+ if (e->canOverflow () && type->isSignedIntegerOrEnumerationType ()) {
180+ value = emitIncDecConsiderOverflowBehavior (e, value, isInc);
181+ } else {
182+ cir::UnaryOpKind kind =
183+ e->isIncrementOp () ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
184+ // NOTE(CIR): clang calls CreateAdd but folds this to a unary op
185+ value = emitUnaryOp (e, kind, input);
186+ }
187+ } else if (const PointerType *ptr = type->getAs <PointerType>()) {
188+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec pointer" );
189+ return {};
190+ } else if (type->isVectorType ()) {
191+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec vector" );
192+ return {};
193+ } else if (type->isRealFloatingType ()) {
194+ assert (!cir::MissingFeatures::CGFPOptionsRAII ());
195+
196+ if (type->isHalfType () &&
197+ !cgf.getContext ().getLangOpts ().NativeHalfType ) {
198+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec half" );
199+ return {};
200+ }
201+
202+ if (mlir::isa<cir::SingleType, cir::DoubleType>(value.getType ())) {
203+ // Create the inc/dec operation.
204+ // NOTE(CIR): clang calls CreateAdd but folds this to a unary op
205+ cir::UnaryOpKind kind =
206+ (isInc ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec);
207+ value = emitUnaryOp (e, kind, value);
208+ } else {
209+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec other fp type" );
210+ return {};
211+ }
212+ } else if (type->isFixedPointType ()) {
213+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec other fixed point" );
214+ return {};
215+ } else {
216+ assert (type->castAs <ObjCObjectPointerType>());
217+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec ObjectiveC pointer" );
218+ return {};
219+ }
220+
221+ CIRGenFunction::SourceLocRAIIObject sourceloc{
222+ cgf, cgf.getLoc (e->getSourceRange ())};
223+
224+ // Store the updated result through the lvalue
225+ if (lv.isBitField ()) {
226+ cgf.cgm .errorNYI (e->getSourceRange (), " Unary inc/dec bitfield" );
227+ return {};
228+ } else {
229+ cgf.emitStoreThroughLValue (RValue::get (value), lv);
230+ }
231+
232+ // If this is a postinc, return the value read from memory, otherwise use
233+ // the updated value.
234+ return isPre ? value : input;
235+ }
236+
237+ mlir::Value emitIncDecConsiderOverflowBehavior (const UnaryOperator *e,
238+ mlir::Value inVal,
239+ bool isInc) {
240+ assert (!cir::MissingFeatures::opUnarySignedOverflow ());
241+ cir::UnaryOpKind kind =
242+ e->isIncrementOp () ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
243+ switch (cgf.getLangOpts ().getSignedOverflowBehavior ()) {
244+ case LangOptions::SOB_Defined:
245+ return emitUnaryOp (e, kind, inVal);
246+ case LangOptions::SOB_Undefined:
247+ assert (!cir::MissingFeatures::sanitizers ());
248+ return emitUnaryOp (e, kind, inVal);
249+ break ;
250+ case LangOptions::SOB_Trapping:
251+ if (!e->canOverflow ())
252+ return emitUnaryOp (e, kind, inVal);
253+ cgf.cgm .errorNYI (e->getSourceRange (), " inc/def overflow SOB_Trapping" );
254+ return {};
255+ }
256+ llvm_unreachable (" Unexpected signed overflow behavior kind" );
257+ }
258+
259+ mlir::Value VisitUnaryPlus (const UnaryOperator *e,
260+ QualType promotionType = QualType()) {
261+ if (!promotionType.isNull ())
262+ cgf.cgm .errorNYI (e->getSourceRange (), " VisitUnaryPlus: promotionType" );
263+ assert (!cir::MissingFeatures::opUnaryPromotionType ());
264+ mlir::Value result = VisitPlus (e);
265+ return result;
266+ }
267+
268+ mlir::Value VisitPlus (const UnaryOperator *e) {
269+ // This differs from gcc, though, most likely due to a bug in gcc.
270+ ignoreResultAssign = false ;
271+
272+ assert (!cir::MissingFeatures::opUnaryPromotionType ());
273+ mlir::Value operand = Visit (e->getSubExpr ());
274+
275+ return emitUnaryOp (e, cir::UnaryOpKind::Plus, operand);
276+ }
277+
278+ mlir::Value VisitUnaryMinus (const UnaryOperator *e,
279+ QualType promotionType = QualType()) {
280+ if (!promotionType.isNull ())
281+ cgf.cgm .errorNYI (e->getSourceRange (), " VisitUnaryMinus: promotionType" );
282+ assert (!cir::MissingFeatures::opUnaryPromotionType ());
283+ mlir::Value result = VisitMinus (e);
284+ return result;
285+ }
286+
287+ mlir::Value VisitMinus (const UnaryOperator *e) {
288+ ignoreResultAssign = false ;
289+
290+ assert (!cir::MissingFeatures::opUnaryPromotionType ());
291+ mlir::Value operand = Visit (e->getSubExpr ());
292+
293+ assert (!cir::MissingFeatures::opUnarySignedOverflow ());
294+
295+ // NOTE: LLVM codegen will lower this directly to either a FNeg
296+ // or a Sub instruction. In CIR this will be handled later in LowerToLLVM.
297+ return emitUnaryOp (e, cir::UnaryOpKind::Minus, operand);
298+ }
299+
300+ mlir::Value emitUnaryOp (const UnaryOperator *e, cir::UnaryOpKind kind,
301+ mlir::Value input) {
302+ return builder.create <cir::UnaryOp>(
303+ cgf.getLoc (e->getSourceRange ().getBegin ()), input.getType (), kind,
304+ input);
305+ }
306+
307+ mlir::Value VisitUnaryNot (const UnaryOperator *e) {
308+ ignoreResultAssign = false ;
309+ mlir::Value op = Visit (e->getSubExpr ());
310+ return emitUnaryOp (e, cir::UnaryOpKind::Not, op);
311+ }
312+
97313 // / Emit a conversion from the specified type to the specified destination
98314 // / type, both of which are CIR scalar types.
99315 // / TODO: do we need ScalarConversionOpts here? Should be done in another
@@ -188,3 +404,10 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
188404 loc, builder.getAttr <cir::IntAttr>(
189405 cgf.cgm .UInt64Ty , e->EvaluateKnownConstInt (cgf.getContext ())));
190406}
407+
408+ mlir::Value CIRGenFunction::emitScalarPrePostIncDec (const UnaryOperator *E,
409+ LValue LV, bool isInc,
410+ bool isPre) {
411+ return ScalarExprEmitter (*this , builder)
412+ .emitScalarPrePostIncDec (E, LV, isInc, isPre);
413+ }
0 commit comments