[clang] [CIR] Upstream __imag__ for ComplexType (PR #144262)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 24 10:11:45 PDT 2025
https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/144262
>From e3a6c2e2dcb8c8673f1971484a9c404543b8431d Mon Sep 17 00:00:00 2001
From: AmrDeveloper <amr96 at programmer.net>
Date: Sun, 15 Jun 2025 14:45:12 +0200
Subject: [PATCH 1/2] [CIR] Upstream __imag__ for ComplexType
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 29 ++++++++++
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 5 ++
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 23 ++++++++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 ++++++
.../Dialect/Transforms/CIRCanonicalize.cpp | 4 +-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 +++-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 ++++
clang/test/CIR/CodeGen/complex.cpp | 56 +++++++++++++++++++
.../test/CIR/Transforms/complex-imag-fold.cir | 23 ++++++++
9 files changed, 177 insertions(+), 3 deletions(-)
create mode 100644 clang/test/CIR/Transforms/complex-imag-fold.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 852d3aa131148..b8e2676ab3136 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2424,6 +2424,35 @@ def ComplexCreateOp : CIR_Op<"complex.create", [Pure, SameTypeOperands]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// ComplexImagOp
+//===----------------------------------------------------------------------===//
+
+def ComplexImagOp : CIR_Op<"complex.imag", [Pure]> {
+ let summary = "Extract the imaginary part of a complex value";
+ let description = [{
+ `cir.complex.imag` operation takes an operand of `!cir.complex` type and
+ yields the imaginary part of it.
+
+ Example:
+
+ ```mlir
+ %1 = cir.complex.imag %0 : !cir.complex<!cir.float> -> !cir.float
+ ```
+ }];
+
+ let results = (outs CIR_AnyIntOrFloatType:$result);
+ let arguments = (ins CIR_ComplexType:$operand);
+
+ let assemblyFormat = [{
+ $operand `:` qualified(type($operand)) `->` qualified(type($result))
+ attr-dict
+ }];
+
+ let hasVerifier = 1;
+ let hasFolder = 1;
+}
+
//===----------------------------------------------------------------------===//
// Assume Operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index e38faba83b80c..28809f6ac93a4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -366,6 +366,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return create<cir::ComplexCreateOp>(loc, resultComplexTy, real, imag);
}
+ mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand) {
+ auto operandTy = mlir::cast<cir::ComplexType>(operand.getType());
+ return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
+ }
+
/// Create a cir.ptr_stride operation to get access to an array element.
/// \p idx is the index of the element to access, \p shouldDecay is true if
/// the result should decay to a pointer to the element type.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 8d0db5cd0a1e5..f7c845fce7670 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -603,6 +603,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
mlir::Value VisitUnaryLNot(const UnaryOperator *e);
+ mlir::Value VisitUnaryImag(const UnaryOperator *e);
+
mlir::Value VisitCXXThisExpr(CXXThisExpr *te) { return cgf.loadCXXThis(); }
/// Emit a conversion from the specified type to the specified destination
@@ -1891,6 +1893,27 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *e) {
return maybePromoteBoolResult(boolVal, cgf.convertType(e->getType()));
}
+mlir::Value ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *e) {
+ // TODO(cir): handle scalar promotion.
+ Expr *op = e->getSubExpr();
+ if (op->getType()->isAnyComplexType()) {
+ // If it's an l-value, load through the appropriate subobject l-value.
+ // Note that we have to ask E because Op might be an l-value that
+ // this won't work for, e.g. an Obj-C property.
+ if (e->isGLValue()) {
+ mlir::Location loc = cgf.getLoc(e->getExprLoc());
+ mlir::Value complex = cgf.emitComplexExpr(op);
+ return cgf.builder.createComplexImag(loc, complex);
+ }
+
+ // Otherwise, calculate and project.
+ cgf.cgm.errorNYI(e->getSourceRange(),
+ "VisitUnaryImag calculate and project");
+ }
+
+ return Visit(op);
+}
+
/// Return the size or alignment of the type of argument of the sizeof
/// expression as an integer.
mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 16248059c4975..9d3d480322991 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1933,6 +1933,24 @@ OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) {
return cir::ConstComplexAttr::get(realAttr, imagAttr);
}
+//===----------------------------------------------------------------------===//
+// ComplexImagOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::ComplexImagOp::verify() {
+ if (getType() != getOperand().getType().getElementType()) {
+ emitOpError() << "cir.complex.imag result type does not match operand type";
+ return failure();
+ }
+ return success();
+}
+
+OpFoldResult cir::ComplexImagOp::fold(FoldAdaptor adaptor) {
+ auto complex =
+ mlir::cast_if_present<cir::ConstComplexAttr>(adaptor.getOperand());
+ return complex ? complex.getImag() : nullptr;
+}
+
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
index f07e234e5e84c..9df7c399fee55 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
@@ -141,8 +141,8 @@ void CIRCanonicalizePass::runOnOperation() {
// Many operations are here to perform a manual `fold` in
// applyOpPatternsGreedily.
if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp,
- ComplexCreateOp, VecCmpOp, VecCreateOp, VecExtractOp, VecShuffleOp,
- VecShuffleDynamicOp, VecTernaryOp>(op))
+ ComplexCreateOp, ComplexImagOp, VecCmpOp, VecCreateOp, VecExtractOp,
+ VecShuffleOp, VecShuffleDynamicOp, VecTernaryOp>(op))
ops.push_back(op);
});
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5f41e340e2474..c6fb12cf79059 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1886,7 +1886,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMVecShuffleOpLowering,
CIRToLLVMVecShuffleDynamicOpLowering,
CIRToLLVMVecTernaryOpLowering,
- CIRToLLVMComplexCreateOpLowering
+ CIRToLLVMComplexCreateOpLowering,
+ CIRToLLVMComplexImagOpLowering
// clang-format on
>(converter, patterns.getContext());
@@ -2190,6 +2191,15 @@ mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite(
+ cir::ComplexImagOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
+ rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
+ op, resultLLVMTy, adaptor.getOperand(), llvm::ArrayRef<std::int64_t>{1});
+ return mlir::success();
+}
+
std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
return std::make_unique<ConvertCIRToLLVMPass>();
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index ae7247332c668..650344802b679 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -433,6 +433,16 @@ class CIRToLLVMComplexCreateOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMComplexImagOpLowering
+ : public mlir::OpConversionPattern<cir::ComplexImagOp> {
+public:
+ using mlir::OpConversionPattern<cir::ComplexImagOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::ComplexImagOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
} // namespace direct
} // namespace cir
diff --git a/clang/test/CIR/CodeGen/complex.cpp b/clang/test/CIR/CodeGen/complex.cpp
index cfeed345b4f11..84aaa85ba13a9 100644
--- a/clang/test/CIR/CodeGen/complex.cpp
+++ b/clang/test/CIR/CodeGen/complex.cpp
@@ -216,6 +216,29 @@ void foo9(double a, double b) {
// OGCG: store double %[[TMP_A]], ptr %[[C_REAL_PTR]], align 8
// OGCG: store double %[[TMP_B]], ptr %[[C_IMAG_PTR]], align 8
+void foo13() {
+ double _Complex c;
+ double imag = __imag__ c;
+}
+
+// CIR: %[[COMPLEX:.*]] = cir.alloca !cir.complex<!cir.double>, !cir.ptr<!cir.complex<!cir.double>>, ["c"]
+// CIR: %[[INIT:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["imag", init]
+// CIR: %[[TMP:.*]] = cir.load{{.*}} %[[COMPLEX]] : !cir.ptr<!cir.complex<!cir.double>>, !cir.complex<!cir.double>
+// CIR: %[[IMAG:.*]] = cir.complex.imag %[[TMP]] : !cir.complex<!cir.double> -> !cir.double
+// CIR: cir.store{{.*}} %[[IMAG]], %[[INIT]] : !cir.double, !cir.ptr<!cir.double>
+
+// LLVM: %[[COMPLEX:.*]] = alloca { double, double }, i64 1, align 8
+// LLVM: %[[INIT:.*]] = alloca double, i64 1, align 8
+// LLVM: %[[TMP:.*]] = load { double, double }, ptr %[[COMPLEX]], align 8
+// LLVM: %[[IMAG:.*]] = extractvalue { double, double } %[[TMP]], 1
+// LLVM: store double %[[IMAG]], ptr %[[INIT]], align 8
+
+// OGCG: %[[COMPLEX:.*]] = alloca { double, double }, align 8
+// OGCG: %[[INIT:.*]] = alloca double, align 8
+// OGCG: %[[IMAG:.*]] = getelementptr inbounds nuw { double, double }, ptr %[[COMPLEX]], i32 0, i32 1
+// OGCG: %[[TMP:.*]] = load double, ptr %[[IMAG]], align 8
+// OGCG: store double %[[TMP]], ptr %[[INIT]], align 8
+
void foo14() {
int _Complex c = 2i;
}
@@ -256,3 +279,36 @@ void foo15() {
// OGCG: %[[B_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 1
// OGCG: store i32 %[[A_REAL]], ptr %[[B_REAL_PTR]], align 4
// OGCG: store i32 %[[A_IMAG]], ptr %[[B_IMAG_PTR]], align 4
+
+int foo16(int _Complex a, int _Complex b) {
+ return __imag__ a + __imag__ b;
+}
+
+// CIR: %[[RET:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR: %[[COMPLEX_A:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: %[[A_IMAG:.*]] = cir.complex.imag %[[COMPLEX_A]] : !cir.complex<!s32i> -> !s32i
+// CIR: %[[COMPLEX_B:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: %[[B_IMAG:.*]] = cir.complex.imag %[[COMPLEX_B]] : !cir.complex<!s32i> -> !s32i
+// CIR: %[[ADD:.*]] = cir.binop(add, %[[A_IMAG]], %[[B_IMAG]]) nsw : !s32i
+// CIR: cir.store %[[ADD]], %[[RET]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[TMP:.*]] = cir.load %[[RET]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.return %[[TMP]] : !s32i
+
+// LLVM: %[[RET:.*]] = alloca i32, i64 1, align 4
+// LLVM: %[[COMPLEX_A:.*]] = load { i32, i32 }, ptr {{.*}}, align 4
+// LLVM: %[[A_IMAG:.*]] = extractvalue { i32, i32 } %[[COMPLEX_A]], 1
+// LLVM: %[[COMPLEX_B:.*]] = load { i32, i32 }, ptr {{.*}}, align 4
+// LLVM: %[[B_IMAG:.*]] = extractvalue { i32, i32 } %[[COMPLEX_B]], 1
+// LLVM: %[[ADD:.*]] = add nsw i32 %[[A_IMAG]], %[[B_IMAG]]
+// LLVM: store i32 %[[ADD]], ptr %[[RET]], align 4
+// LLVM: %[[TMP:.*]] = load i32, ptr %[[RET]], align 4
+// LLVM: ret i32 %[[TMP]]
+
+// OGCG: %[[COMPLEX_A:.*]] = alloca { i32, i32 }, align 4
+// OGCG: %[[COMPLEX_B:.*]] = alloca { i32, i32 }, align 4
+// OGCG: %[[A_IMAG:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_A]], i32 0, i32 1
+// OGCG: %[[TMP_A:.*]] = load i32, ptr %[[A_IMAG]], align 4
+// OGCG: %[[B_IMAG:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 1
+// OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_IMAG]], align 4
+// OGCG: %[[ADD:.*]] = add nsw i32 %[[TMP_A]], %[[TMP_B]]
+// OGCG: ret i32 %[[ADD]]
diff --git a/clang/test/CIR/Transforms/complex-imag-fold.cir b/clang/test/CIR/Transforms/complex-imag-fold.cir
new file mode 100644
index 0000000000000..0d9a4e43142a3
--- /dev/null
+++ b/clang/test/CIR/Transforms/complex-imag-fold.cir
@@ -0,0 +1,23 @@
+// RUN: cir-opt %s -cir-canonicalize -o - | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+ cir.func @fold_complex_imag_test() -> !s32i {
+ %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+ %2 = cir.const #cir.const_complex<#cir.int<1> : !s32i, #cir.int<2> : !s32i> : !cir.complex<!s32i>
+ %4 = cir.complex.imag %2 : !cir.complex<!s32i> -> !s32i
+ cir.store %4, %0 : !s32i, !cir.ptr<!s32i>
+ %5 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+ cir.return %5 : !s32i
+ }
+
+ // CHECK: cir.func @fold_complex_imag_test() -> !s32i {
+ // CHECK: %[[RET:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+ // CHECK: %[[IMAG:.*]] = cir.const #cir.int<2> : !s32i
+ // CHECK: cir.store %[[IMAG]], %[[RET]] : !s32i, !cir.ptr<!s32i>
+ // CHECK: %[[TMP:.]] = cir.load %[[RET]] : !cir.ptr<!s32i>, !s32i
+ // CHECK: cir.return %[[TMP]] : !s32i
+ // CHECK: }
+
+}
>From d868df37ea21f25ba5ab8438f6ebd6de098e98c5 Mon Sep 17 00:00:00 2001
From: AmrDeveloper <amr96 at programmer.net>
Date: Tue, 24 Jun 2025 19:11:22 +0200
Subject: [PATCH 2/2] Add test for verifier
---
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 2 +-
clang/test/CIR/IR/invalid-complex.cir | 12 ++++++++++++
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index f7c845fce7670..d8c98cb97aacd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1898,7 +1898,7 @@ mlir::Value ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *e) {
Expr *op = e->getSubExpr();
if (op->getType()->isAnyComplexType()) {
// If it's an l-value, load through the appropriate subobject l-value.
- // Note that we have to ask E because Op might be an l-value that
+ // Note that we have to ask `e` because `op` might be an l-value that
// this won't work for, e.g. an Obj-C property.
if (e->isGLValue()) {
mlir::Location loc = cgf.getLoc(e->getExprLoc());
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9d3d480322991..48e89137b1c84 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1939,7 +1939,7 @@ OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) {
LogicalResult cir::ComplexImagOp::verify() {
if (getType() != getOperand().getType().getElementType()) {
- emitOpError() << "cir.complex.imag result type does not match operand type";
+ emitOpError() << ": result type does not match operand type";
return failure();
}
return success();
diff --git a/clang/test/CIR/IR/invalid-complex.cir b/clang/test/CIR/IR/invalid-complex.cir
index 8c6d890579321..4c5eb71544ecf 100644
--- a/clang/test/CIR/IR/invalid-complex.cir
+++ b/clang/test/CIR/IR/invalid-complex.cir
@@ -21,3 +21,15 @@ module {
cir.global external @ci2 = #cir.const_complex<#cir.int<1> : !s32i, #cir.int<2> : !s64i> : !cir.complex<!s32i>
}
+
+// -----
+
+module {
+ cir.func @complex_imag_invalid_result_type() -> !cir.double {
+ %0 = cir.alloca !cir.complex<!cir.double>, !cir.ptr<!cir.complex<!cir.double>>, ["c"]
+ %2 = cir.load align(8) %0 : !cir.ptr<!cir.complex<!cir.double>>, !cir.complex<!cir.double>
+ // expected-error @below {{result type does not match operand type}}
+ %3 = cir.complex.imag %2 : !cir.complex<!cir.double> -> !cir.float
+ cir.return
+ }
+}
More information about the cfe-commits
mailing list