https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/144262
>From eb25aa90dda67c3622f36b561a81f110e0a3d54f Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@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 | 5 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 +++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 ++++ clang/test/CIR/CodeGen/complex.cpp | 58 ++++++++++++++++++- .../test/CIR/Transforms/complex-imag-fold.cir | 23 ++++++++ 9 files changed, 179 insertions(+), 4 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 426173658f77d..5f24ab7816cbc 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2400,6 +2400,35 @@ def ComplexRealOp : CIR_Op<"complex.real", [Pure]> { 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 2c029fdb89baa..ac62ea7c6aa16 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -371,6 +371,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return create<cir::ComplexRealOp>(loc, operandTy.getElementType(), operand); } + 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 afe71c3bb95a1..6931692306b51 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -605,6 +605,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value VisitUnaryReal(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 @@ -1914,6 +1916,27 @@ mlir::Value ScalarExprEmitter::VisitUnaryReal(const UnaryOperator *e) { return Visit(op); } +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 ef8a99cd4ab12..65c2fd120934f 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1932,6 +1932,24 @@ OpFoldResult cir::ComplexRealOp::fold(FoldAdaptor adaptor) { return complex ? complex.getReal() : nullptr; } +//===----------------------------------------------------------------------===// +// 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 7adec3ebf9f7d..e505db50d3609 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -141,8 +141,9 @@ void CIRCanonicalizePass::runOnOperation() { // Many operations are here to perform a manual `fold` in // applyOpPatternsGreedily. if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp, - ComplexCreateOp, ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, - VecShuffleOp, VecShuffleDynamicOp, VecTernaryOp>(op)) + ComplexCreateOp, ComplexImagOp, ComplexRealOp, 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 58aaa45c1cf1e..d41afbdd0b69e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1904,7 +1904,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMVecShuffleDynamicOpLowering, CIRToLLVMVecTernaryOpLowering, CIRToLLVMComplexCreateOpLowering, - CIRToLLVMComplexRealOpLowering + CIRToLLVMComplexRealOpLowering, + CIRToLLVMComplexImagOpLowering // clang-format on >(converter, patterns.getContext()); @@ -2217,6 +2218,15 @@ mlir::LogicalResult CIRToLLVMComplexRealOpLowering::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 0e5c54f01b7dc..8502cb1ae5d9f 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -453,6 +453,16 @@ class CIRToLLVMComplexRealOpLowering 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 9ee3dfb05cb40..ad3720097a795 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 foo12() { + 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 foo13() { double _Complex c; double real = __real__ c; @@ -281,6 +304,39 @@ void foo15() { // 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]] + int foo17(int _Complex a, int _Complex b) { return __real__ a + __real__ b; } @@ -312,4 +368,4 @@ int foo17(int _Complex a, int _Complex b) { // OGCG: %[[B_REAL:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 0 // OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_REAL]], align 4 // OGCG: %[[ADD:.*]] = add nsw i32 %[[TMP_A]], %[[TMP_B]] -// OGCG: ret i32 %[[ADD]] +// OGCG: ret i32 %[[ADD]] \ No newline at end of file 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 f8d6788f5e34f6bf2abe720f1341cb5d69e89894 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@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 6931692306b51..577b45ad4ec0f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1921,7 +1921,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 65c2fd120934f..d38da65a8ec79 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1938,7 +1938,7 @@ OpFoldResult cir::ComplexRealOp::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 7937148e1cc3e..2414809f7dbca 100644 --- a/clang/test/CIR/IR/invalid-complex.cir +++ b/clang/test/CIR/IR/invalid-complex.cir @@ -33,3 +33,15 @@ module { cir.return } } + +// ----- + +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 + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits