[clang] [CIR] Support type promotion for Scalar unary plus & minus ops (PR #158486)

Amr Hesham via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 14 07:24:43 PDT 2025


https://github.com/AmrDeveloper created https://github.com/llvm/llvm-project/pull/158486

Support type promotion for Scalar unary plus & minus ops

>From 3f4e35ee085cdf8f451daf68fe1fe9305a7486dd Mon Sep 17 00:00:00 2001
From: AmrDeveloper <amr96 at programmer.net>
Date: Sun, 14 Sep 2025 15:18:48 +0200
Subject: [PATCH] [CIR] Support type promotion for Scalar unary plus & minus
 ops

---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 45 ++++++++++-----
 clang/test/CIR/CodeGen/unary.cpp           | 66 ++++++++++++++++++++++
 2 files changed, 98 insertions(+), 13 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 2261e24fe44c2..34438bd7a7732 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -621,19 +621,27 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitUnaryPlus(const UnaryOperator *e) {
-    return emitUnaryPlusOrMinus(e, cir::UnaryOpKind::Plus);
+    QualType promotionType = getPromotionType(e->getSubExpr()->getType());
+    mlir::Value result =
+        emitUnaryPlusOrMinus(e, cir::UnaryOpKind::Plus, promotionType);
+    if (result && !promotionType.isNull())
+      return emitUnPromotedValue(result, e->getType());
+    return result;
   }
 
   mlir::Value VisitUnaryMinus(const UnaryOperator *e) {
-    return emitUnaryPlusOrMinus(e, cir::UnaryOpKind::Minus);
+    QualType promotionType = getPromotionType(e->getSubExpr()->getType());
+    mlir::Value result =
+        emitUnaryPlusOrMinus(e, cir::UnaryOpKind::Minus, promotionType);
+    if (result && !promotionType.isNull())
+      return emitUnPromotedValue(result, e->getType());
+    return result;
   }
 
   mlir::Value emitUnaryPlusOrMinus(const UnaryOperator *e,
-                                   cir::UnaryOpKind kind) {
+                                   cir::UnaryOpKind kind,
+                                   QualType promotionType) {
     ignoreResultAssign = false;
-
-    QualType promotionType = getPromotionType(e->getSubExpr()->getType());
-
     mlir::Value operand;
     if (!promotionType.isNull())
       operand = cgf.emitPromotedScalarExpr(e->getSubExpr(), promotionType);
@@ -645,10 +653,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
 
     // NOTE: LLVM codegen will lower this directly to either a FNeg
     // or a Sub instruction.  In CIR this will be handled later in LowerToLLVM.
-    mlir::Value result = emitUnaryOp(e, kind, operand, nsw);
-    if (result && !promotionType.isNull())
-      return emitUnPromotedValue(result, e->getType());
-    return result;
+    return emitUnaryOp(e, kind, operand, nsw);
   }
 
   mlir::Value emitUnaryOp(const UnaryOperator *e, cir::UnaryOpKind kind,
@@ -1239,9 +1244,23 @@ mlir::Value ScalarExprEmitter::emitPromoted(const Expr *e,
     default:
       break;
     }
-  } else if (isa<UnaryOperator>(e)) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "unary operators");
-    return {};
+  } else if (const auto *uo = dyn_cast<UnaryOperator>(e)) {
+    switch (uo->getOpcode()) {
+    case UO_Imag:
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "ScalarExprEmitter::emitPromoted unary imag");
+      return {};
+    case UO_Real:
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "ScalarExprEmitter::emitPromoted unary real");
+      return {};
+    case UO_Minus:
+      return emitUnaryPlusOrMinus(uo, cir::UnaryOpKind::Minus, promotionType);
+    case UO_Plus:
+      return emitUnaryPlusOrMinus(uo, cir::UnaryOpKind::Plus, promotionType);
+    default:
+      break;
+    }
   }
   mlir::Value result = Visit(const_cast<Expr *>(e));
   if (result) {
diff --git a/clang/test/CIR/CodeGen/unary.cpp b/clang/test/CIR/CodeGen/unary.cpp
index a7c946eaffd03..c37524bc8b2c9 100644
--- a/clang/test/CIR/CodeGen/unary.cpp
+++ b/clang/test/CIR/CodeGen/unary.cpp
@@ -556,3 +556,69 @@ void test_logical_not() {
 // OGCG:   %[[D_NOT:.*]] = xor i1 %[[D_BOOL]], true
 // OGCG:   %[[D_CAST:.*]] = zext i1 %[[D_NOT]] to i8
 // OGCG:   store i8 %[[D_CAST]], ptr %[[B_ADDR]], align 1
+
+void f16NestedUPlus() {
+  _Float16 a;
+  _Float16 b = +(+a);
+}
+
+// CHECK: cir.func{{.*}} @_Z14f16NestedUPlusv()
+// CHECK:  %[[A_ADDR:.*]] = cir.alloca !cir.f16, !cir.ptr<!cir.f16>, ["a"]
+// CHECK:  %[[B_ADDR:.*]] = cir.alloca !cir.f16, !cir.ptr<!cir.f16>, ["b", init]
+// CHECK:  %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.f16>, !cir.f16
+// CHECK:  %[[A_F32:.*]] = cir.cast(floating, %[[TMP_A]] : !cir.f16), !cir.float
+// CHECK:  %[[A_PLUS:.*]] = cir.unary(plus, %[[A_F32]]) : !cir.float, !cir.float
+// CHECK:  %[[RESULT_F32:.*]] = cir.unary(plus, %[[A_PLUS]]) : !cir.float, !cir.float
+// CHECK:  %[[RESULT:.*]] = cir.cast(floating, %[[RESULT_F32]] : !cir.float), !cir.f16
+// CHECK:  cir.store{{.*}} %[[RESULT]], %[[B_ADDR]] : !cir.f16, !cir.ptr<!cir.f16>
+
+// LLVM: define{{.*}} void @_Z14f16NestedUPlusv()
+// LLVM:  %[[A_ADDR:.*]] = alloca half, i64 1, align 2
+// LLVM:  %[[B_ADDR:.*]] = alloca half, i64 1, align 2
+// LLVM:  %[[TMP_A:.*]] = load half, ptr %[[A_ADDR]], align 2
+// LLVM:  %[[RESULT_F32:.*]] = fpext half %[[TMP_A]] to float
+// LLVM:  %[[RESULT:.*]] = fptrunc float %[[RESULT_F32]] to half
+// LLVM:  store half %[[RESULT]], ptr %[[B_ADDR]], align 2
+
+// OGCG: define{{.*}} void @_Z14f16NestedUPlusv()
+// OGCG:  %[[A_ADDR:.*]] = alloca half, align 2
+// OGCG:  %[[B_ADDR:.*]] = alloca half, align 2
+// OGCG:  %[[TMP_A:.*]] = load half, ptr %[[A_ADDR]], align 2
+// OGCG:  %[[RESULT_F32:.*]] = fpext half %[[TMP_A]] to float
+// OGCG:  %[[RESULT:.*]] = fptrunc float %[[RESULT_F32]] to half
+// OGCG:  store half %[[RESULT]], ptr %[[B_ADDR]], align 2
+
+void f16NestedUMinus() {
+  _Float16 a;
+  _Float16 b = -(-a);
+}
+
+// CHECK: cir.func{{.*}} @_Z15f16NestedUMinusv()
+// CHECK:  %[[A_ADDR:.*]] = cir.alloca !cir.f16, !cir.ptr<!cir.f16>, ["a"]
+// CHECK:  %[[B_ADDR:.*]] = cir.alloca !cir.f16, !cir.ptr<!cir.f16>, ["b", init]
+// CHECK:  %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.f16>, !cir.f16
+// CHECK:  %[[A_F32:.*]] = cir.cast(floating, %[[TMP_A]] : !cir.f16), !cir.float
+// CHECK:  %[[A_MINUS:.*]] = cir.unary(minus, %[[A_F32]]) : !cir.float, !cir.float
+// CHECK:  %[[RESULT_F32:.*]] = cir.unary(minus, %[[A_MINUS]]) : !cir.float, !cir.float
+// CHECK:  %[[RESULT:.*]] = cir.cast(floating, %[[RESULT_F32]] : !cir.float), !cir.f16
+// CHECK:  cir.store{{.*}} %[[RESULT]], %[[B_ADDR]] : !cir.f16, !cir.ptr<!cir.f16>
+
+// LLVM: define{{.*}} void @_Z15f16NestedUMinusv()
+// LLVM:  %[[A_ADDR:.*]] = alloca half, i64 1, align 2
+// LLVM:  %[[B_ADDR:.*]] = alloca half, i64 1, align 2
+// LLVM:  %[[TMP_A:.*]] = load half, ptr %[[A_ADDR]], align 2
+// LLVM:  %[[A_F32:.*]] = fpext half %[[TMP_A]] to float
+// LLVM:  %[[A_MINUS:.*]] = fneg float %[[A_F32]]
+// LLVM:  %[[RESULT_F32:.*]] = fneg float %[[A_MINUS]]
+// LLVM:  %[[RESULT:.*]] = fptrunc float %[[RESULT_F32]] to half
+// LLVM:  store half %[[RESULT]], ptr %[[B_ADDR]], align 2
+
+// OGCG: define{{.*}} void @_Z15f16NestedUMinusv()
+// OGCG:  %[[A_ADDR:.*]] = alloca half, align 2
+// OGCG:  %[[B_ADDR:.*]] = alloca half, align 2
+// OGCG:  %[[TMP_A:.*]] = load half, ptr %[[A_ADDR]], align 2
+// OGCG:  %[[A_F32:.*]] = fpext half %[[TMP_A]] to float
+// OGCG:  %[[A_MINUS:.*]] = fneg float %[[A_F32]]
+// OGCG:  %[[RESULT_F32:.*]] = fneg float %[[A_MINUS]]
+// OGCG:  %[[RESULT:.*]] = fptrunc float %[[RESULT_F32]] to half
+// OGCG:  store half %[[RESULT]], ptr %[[B_ADDR]], align 2



More information about the cfe-commits mailing list