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