[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