[clang] [CIR] Upstream initial support for unary op (PR #131369)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 14 11:53:40 PDT 2025
================
@@ -92,6 +92,222 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitCastExpr(CastExpr *E);
+ // Unary Operators.
+ mlir::Value VisitUnaryPostDec(const UnaryOperator *e) {
+ LValue lv = cgf.emitLValue(e->getSubExpr());
+ return emitScalarPrePostIncDec(e, lv, false, false);
+ }
+ mlir::Value VisitUnaryPostInc(const UnaryOperator *e) {
+ LValue lv = cgf.emitLValue(e->getSubExpr());
+ return emitScalarPrePostIncDec(e, lv, true, false);
+ }
+ mlir::Value VisitUnaryPreDec(const UnaryOperator *e) {
+ LValue lv = cgf.emitLValue(e->getSubExpr());
+ return emitScalarPrePostIncDec(e, lv, false, true);
+ }
+ mlir::Value VisitUnaryPreInc(const UnaryOperator *e) {
+ LValue lv = cgf.emitLValue(e->getSubExpr());
+ return emitScalarPrePostIncDec(e, lv, true, true);
+ }
+ mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv,
+ bool isInc, bool isPre) {
+ if (cgf.getLangOpts().OpenMP)
+ cgf.cgm.errorNYI(e->getSourceRange(), "inc/dec OpenMP");
+
+ QualType type = e->getSubExpr()->getType();
+
+ mlir::Value value;
+ mlir::Value input;
+
+ if (type->getAs<AtomicType>()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Atomic inc/dec");
+ // TODO(cir): This is not correct, but it will produce reasonable code
+ // until atomic operations are implemented.
+ value = cgf.emitLoadOfLValue(lv, e->getExprLoc()).getScalarVal();
+ input = value;
+ } else {
+ value = cgf.emitLoadOfLValue(lv, e->getExprLoc()).getScalarVal();
+ input = value;
+ }
+
+ // NOTE: When possible, more frequent cases are handled first.
+
+ // Special case of integer increment that we have to check first: bool++.
+ // Due to promotion rules, we get:
+ // bool++ -> bool = bool + 1
+ // -> bool = (int)bool + 1
+ // -> bool = ((int)bool + 1 != 0)
+ // An interesting aspect of this is that increment is always true.
+ // Decrement does not have this property.
+ if (isInc && type->isBooleanType()) {
+ value = builder.create<cir::ConstantOp>(cgf.getLoc(e->getExprLoc()),
+ cgf.convertType(type),
+ builder.getCIRBoolAttr(true));
+ } else if (type->isIntegerType()) {
+ QualType promotedType;
+ bool canPerformLossyDemotionCheck = false;
+ if (cgf.getContext().isPromotableIntegerType(type)) {
+ promotedType = cgf.getContext().getPromotedIntegerType(type);
+ assert(promotedType != type && "Shouldn't promote to the same type.");
+ canPerformLossyDemotionCheck = true;
+ canPerformLossyDemotionCheck &=
+ cgf.getContext().getCanonicalType(type) !=
+ cgf.getContext().getCanonicalType(promotedType);
+ canPerformLossyDemotionCheck &=
+ type->isIntegerType() && promotedType->isIntegerType();
+
+ // TODO(cir): Currently, we store bitwidths in CIR types only for
+ // integers. This might also be required for other types.
+ auto srcCirTy = mlir::dyn_cast<cir::IntType>(cgf.convertType(type));
+ auto promotedCirTy =
+ mlir::dyn_cast<cir::IntType>(cgf.convertType(type));
+ assert(srcCirTy && promotedCirTy && "Expected integer type");
+
+ assert(
+ (!canPerformLossyDemotionCheck ||
+ type->isSignedIntegerOrEnumerationType() ||
+ promotedType->isSignedIntegerOrEnumerationType() ||
+ srcCirTy.getWidth() == promotedCirTy.getWidth()) &&
+ "The following check expects that if we do promotion to different "
+ "underlying canonical type, at least one of the types (either "
+ "base or promoted) will be signed, or the bitwidths will match.");
+ }
+
+ assert(!cir::MissingFeatures::sanitizers());
+ if (e->canOverflow() && type->isSignedIntegerOrEnumerationType()) {
+ value = emitIncDecConsiderOverflowBehavior(e, value, isInc);
+ } else {
+ cir::UnaryOpKind 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);
+ }
+ } else if (const PointerType *ptr = type->getAs<PointerType>()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec pointer");
+ return {};
+ } else if (type->isVectorType()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec vector");
+ return {};
+ } else if (type->isRealFloatingType()) {
+ assert(!cir::MissingFeatures::CGFPOptionsRAII());
+
+ if (type->isHalfType() &&
+ !cgf.getContext().getLangOpts().NativeHalfType) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec half");
+ return {};
+ }
+
+ if (mlir::isa<cir::SingleType, cir::DoubleType>(value.getType())) {
+ // Create the inc/dec operation.
+ // NOTE(CIR): clang calls CreateAdd but folds this to a unary op
+ cir::UnaryOpKind kind =
+ (isInc ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec);
+ value = emitUnaryOp(e, kind, value);
+ } else {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fp type");
+ return {};
+ }
+ } else if (type->isFixedPointType()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fixed point");
+ return {};
+ } else {
+ assert(type->castAs<ObjCObjectPointerType>());
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec ObjectiveC pointer");
+ return {};
+ }
+
+ CIRGenFunction::SourceLocRAIIObject sourceloc{
+ cgf, cgf.getLoc(e->getSourceRange())};
+
+ // Store the updated result through the lvalue
+ if (lv.isBitField()) {
+ cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec bitfield");
+ return {};
+ } else {
+ cgf.emitStoreThroughLValue(RValue::get(value), lv);
+ }
+
+ // If this is a postinc, return the value read from memory, otherwise use
+ // the updated value.
+ return isPre ? value : input;
+ }
+
+ mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *e,
+ mlir::Value inVal,
+ bool isInc) {
+ assert(!cir::MissingFeatures::opUnarySignedOverflow());
+ cir::UnaryOpKind kind =
+ e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec;
+ switch (cgf.getLangOpts().getSignedOverflowBehavior()) {
+ case LangOptions::SOB_Defined:
+ return emitUnaryOp(e, kind, inVal);
+ case LangOptions::SOB_Undefined:
+ assert(!cir::MissingFeatures::sanitizers());
+ return emitUnaryOp(e, kind, inVal);
+ break;
+ case LangOptions::SOB_Trapping:
+ if (!e->canOverflow())
+ return emitUnaryOp(e, kind, inVal);
+ cgf.cgm.errorNYI(e->getSourceRange(), "inc/def overflow SOB_Trapping");
+ return {};
+ }
+ llvm_unreachable("Unexpected signed overflow behavior kind");
+ }
+
+ mlir::Value VisitUnaryPlus(const UnaryOperator *e,
+ QualType promotionType = QualType()) {
+ if (!promotionType.isNull())
+ cgf.cgm.errorNYI(e->getSourceRange(), "VisitUnaryPlus: promotionType");
+ assert(!cir::MissingFeatures::opUnaryPromotionType());
+ mlir::Value result = VisitPlus(e);
+ return result;
+ }
+
+ mlir::Value VisitPlus(const UnaryOperator *e) {
+ // This differs from gcc, though, most likely due to a bug in gcc.
+ ignoreResultAssign = false;
+
+ assert(!cir::MissingFeatures::opUnaryPromotionType());
+ mlir::Value operand = Visit(e->getSubExpr());
+
+ return emitUnaryOp(e, cir::UnaryOpKind::Plus, operand);
+ }
+
+ mlir::Value VisitUnaryMinus(const UnaryOperator *e,
+ QualType promotionType = QualType()) {
+ if (!promotionType.isNull())
+ cgf.cgm.errorNYI(e->getSourceRange(), "VisitUnaryMinus: promotionType");
+ assert(!cir::MissingFeatures::opUnaryPromotionType());
+ mlir::Value result = VisitMinus(e);
+ return result;
+ }
+
+ mlir::Value VisitMinus(const UnaryOperator *e) {
----------------
andykaylor wrote:
I think VisitUnaryPlus/VisitUnaryMinus have to be separate because they are called via DISPATCH macros in the visitor base class. VisitPlus/VisitMinus, on the other hand, look like they would be, but I checked and they are not, so those should be at least renamed, and I'll consider whether they can be combined.
https://github.com/llvm/llvm-project/pull/131369
More information about the cfe-commits
mailing list