https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/143192
>From 0ee92db03198e2364ade53e5c0bbd0f844fe634f Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Fri, 6 Jun 2025 20:56:49 +0200 Subject: [PATCH 1/3] [CIR] Upstream CreateOp for ComplexType with folder --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 32 ++++++++ .../include/clang/CIR/Dialect/IR/CIRTypes.td | 3 +- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenBuilder.h | 6 ++ clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 12 ++- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 11 +++ clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp | 81 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 9 +++ clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 28 +++++++ .../Dialect/Transforms/CIRCanonicalize.cpp | 3 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 48 ++++++++++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 +++ clang/test/CIR/CodeGen/complex.cpp | 56 +++++++++++++ .../CIR/Transforms/complex-create-fold.cir | 30 +++++++ 15 files changed, 322 insertions(+), 9 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp create mode 100644 clang/test/CIR/Transforms/complex-create-fold.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bd847731193ab..5f72545ea020d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2226,4 +2226,36 @@ def VecTernaryOp : CIR_Op<"vec.ternary", let hasVerifier = 1; } +//===----------------------------------------------------------------------===// +// ComplexCreateOp +//===----------------------------------------------------------------------===// + +def ComplexCreateOp : CIR_Op<"complex.create", [Pure, SameTypeOperands]> { + let summary = "Create a complex value from its real and imaginary parts"; + let description = [{ + The `cir.complex.create` operation takes two operands that represent the + real and imaginary part of a complex number, and yields the complex number. + + ```mlir + %0 = cir.const #cir.fp<1.000000e+00> : !cir.double + %1 = cir.const #cir.fp<2.000000e+00> : !cir.double + %2 = cir.complex.create %0, %1 : !cir.complex<!cir.double> + ``` + }]; + + let results = (outs CIR_ComplexType:$result); + let arguments = (ins + CIR_AnyIntOrFloatType:$real, + CIR_AnyIntOrFloatType:$imag + ); + + let assemblyFormat = [{ + $real `,` $imag + `:` qualified(type($real)) `->` qualified(type($result)) attr-dict + }]; + + let hasVerifier = 1; + let hasFolder = 1; +} + #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index fb96976075130..41d7d725a09e0 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -600,7 +600,8 @@ def CIRRecordType : Type< def CIR_AnyType : AnyTypeOf<[ CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_VectorType, CIR_IntType, - CIR_AnyFloatType, CIR_PointerType, CIR_FuncType, CIR_RecordType + CIR_AnyFloatType, CIR_PointerType, CIR_FuncType, CIR_RecordType, + CIR_ComplexType ]>; #endif // MLIR_CIR_DIALECT_CIR_TYPES diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index f1e0c15d41f64..473e4dc7a81a4 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -233,7 +233,6 @@ struct MissingFeatures { // Future CIR operations static bool awaitOp() { return false; } static bool callOp() { return false; } - static bool complexCreateOp() { return false; } static bool complexImagOp() { return false; } static bool complexRealOp() { return false; } static bool ifOp() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 5f33ae1af35ee..308b51be6c30b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -323,6 +323,12 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return CIRBaseBuilderTy::createStore(loc, val, dst.getPointer(), align); } + mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real, + mlir::Value imag) { + auto resultComplexTy = cir::ComplexType::get(real.getType()); + return create<cir::ComplexCreateOp>(loc, resultComplexTy, real, imag); + } + /// 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/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 80b0172090aa3..3e2c96c5aaeaf 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -255,7 +255,13 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, emitScalarInit(init, getLoc(d->getSourceRange()), lvalue); return; case cir::TEK_Complex: { - cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: complex type"); + mlir::Value complex = emitComplexExpr(init); + if (capturedByInit) + cgm.errorNYI(init->getSourceRange(), + "emitExprAsInit: complex type captured by init"); + mlir::Location loc = getLoc(init->getExprLoc()); + emitStoreOfComplex(loc, complex, lvalue, + /*init*/ true); return; } case cir::TEK_Aggregate: @@ -344,8 +350,8 @@ void CIRGenFunction::emitDecl(const Decl &d) { // None of these decls require codegen support. return; - case Decl::Enum: // enum X; - case Decl::Record: // struct/union/class X; + case Decl::Enum: // enum X; + case Decl::Record: // struct/union/class X; case Decl::CXXRecord: // struct/union/class X; [C++] case Decl::NamespaceAlias: case Decl::Using: // using X; [C++] diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1175fdc0be2cf..a8f927befcb75 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1495,3 +1495,14 @@ cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize) .getDefiningOp()); } + +/// An LValue is a candidate for having its loads and stores be made atomic if +/// we are operating under /volatile:ms *and* the LValue itself is volatile and +/// performing such an operation can be performed without a libcall. +bool CIRGenFunction::isLValueSuitableForInlineAtomic(LValue lv) { + if (!cgm.getLangOpts().MSVolatile) + return false; + + cgm.errorNYI("LValueSuitableForInlineAtomic LangOpts MSVolatile"); + return false; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp new file mode 100644 index 0000000000000..e7eaebac01341 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -0,0 +1,81 @@ +#include "CIRGenBuilder.h" +#include "CIRGenFunction.h" + +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace clang::CIRGen; + +namespace { +class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> { + CIRGenFunction &cgf; + CIRGenBuilderTy &builder; + +public: + explicit ComplexExprEmitter(CIRGenFunction &cgf) + : cgf(cgf), builder(cgf.getBuilder()) {} + + /// EmitStoreOfComplex - Store the specified real/imag parts into the + /// specified value pointer. + void emitStoreOfComplex(mlir::Location loc, mlir::Value val, LValue lv, + bool isInit); + + mlir::Value VisitInitListExpr(InitListExpr *e); +}; + +} // namespace + +static const ComplexType *getComplexType(QualType type) { + type = type.getCanonicalType(); + if (const ComplexType *comp = dyn_cast<ComplexType>(type)) + return comp; + return cast<ComplexType>(cast<AtomicType>(type)->getValueType()); +} + +void ComplexExprEmitter::emitStoreOfComplex(mlir::Location loc, mlir::Value val, + LValue lv, bool isInit) { + if (lv.getType()->isAtomicType() || + (!isInit && cgf.isLValueSuitableForInlineAtomic(lv))) { + cgf.cgm.errorNYI("StoreOfComplex with Atomic LV"); + return; + } + + const Address destAddr = lv.getAddress(); + builder.createStore(loc, val, destAddr); +} + +mlir::Value ComplexExprEmitter::VisitInitListExpr(InitListExpr *e) { + if (e->getNumInits() == 2) { + mlir::Value real = cgf.emitScalarExpr(e->getInit(0)); + mlir::Value imag = cgf.emitScalarExpr(e->getInit(1)); + return builder.createComplexCreate(cgf.getLoc(e->getExprLoc()), real, imag); + } + + if (e->getNumInits() == 1) { + cgf.cgm.errorNYI("Create Complex with InitList with size 1"); + return {}; + } + + assert(e->getNumInits() == 0 && "Unexpected number of inits"); + mlir::Location loc = cgf.getLoc(e->getExprLoc()); + QualType complexElemTy = + e->getType()->castAs<clang::ComplexType>()->getElementType(); + mlir::Type complexElemLLVMTy = cgf.convertType(complexElemTy); + mlir::TypedAttr defaultValue = builder.getZeroInitAttr(complexElemLLVMTy); + auto complexTy = cir::ComplexType::get(complexElemLLVMTy); + auto complexAttr = + cir::ConstComplexAttr::get(complexTy, defaultValue, defaultValue); + return builder.create<cir::ConstantOp>(loc, complexAttr); +} + +mlir::Value CIRGenFunction::emitComplexExpr(const Expr *e) { + assert(e && getComplexType(e->getType()) && + "Invalid complex expression to emit"); + + return ComplexExprEmitter(*this).Visit(const_cast<Expr *>(e)); +} + +void CIRGenFunction::emitStoreOfComplex(mlir::Location loc, mlir::Value v, + LValue dest, bool isInit) { + ComplexExprEmitter(*this).emitStoreOfComplex(loc, v, dest, isInit); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index ee014adc961be..e17119cf34e98 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -327,6 +327,8 @@ class CIRGenFunction : public CIRGenTypeCache { PrototypeWrapper(const clang::ObjCMethodDecl *md) : p(md) {} }; + bool isLValueSuitableForInlineAtomic(LValue lv); + /// An abstract representation of regular/ObjC call/message targets. class AbstractCallee { /// The function declaration of the callee. @@ -763,6 +765,10 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitForStmt(const clang::ForStmt &s); + /// Emit the computation of the specified expression of complex type, + /// returning the result. + mlir::Value emitComplexExpr(const Expr *e); + void emitCompoundStmt(const clang::CompoundStmt &s); void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s); @@ -849,6 +855,9 @@ class CIRGenFunction : public CIRGenTypeCache { void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit = false); + void emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, + bool isInit); + void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, bool isInit = false, bool isNontemporal = false); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 185a0e10547af..63226a313c599 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangCIR CIRGenDeclOpenACC.cpp CIRGenExpr.cpp CIRGenExprAggregate.cpp + CIRGenExprComplex.cpp CIRGenExprConstant.cpp CIRGenExprScalar.cpp CIRGenFunction.cpp diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index d7999f59bd021..00e963aa056ba 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1641,6 +1641,34 @@ LogicalResult cir::VecTernaryOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// ComplexCreateOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::ComplexCreateOp::verify() { + if (getType().getElementType() != getReal().getType()) { + emitOpError() + << "operand type of cir.complex.create does not match its result type"; + return failure(); + } + + return success(); +} + +OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) { + mlir::Attribute real = adaptor.getReal(); + mlir::Attribute imag = adaptor.getImag(); + if (!real || !imag) + return {}; + + // When both of real and imag are constants, we can fold the operation into an + // `#cir.const_complex` operation. + auto realAttr = mlir::cast<mlir::TypedAttr>(real); + auto imagAttr = mlir::cast<mlir::TypedAttr>(imag); + auto complexTy = cir::ComplexType::get(realAttr.getType()); + return cir::ConstComplexAttr::get(complexTy, realAttr, imagAttr); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index 7d03e374c27e8..7abb9f1ebdb0f 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -134,14 +134,13 @@ void CIRCanonicalizePass::runOnOperation() { getOperation()->walk([&](Operation *op) { assert(!cir::MissingFeatures::switchOp()); assert(!cir::MissingFeatures::tryOp()); - assert(!cir::MissingFeatures::complexCreateOp()); assert(!cir::MissingFeatures::complexRealOp()); assert(!cir::MissingFeatures::complexImagOp()); assert(!cir::MissingFeatures::callOp()); // CastOp, UnaryOp, VecExtractOp and VecShuffleDynamicOp are here to perform // a manual `fold` in applyOpPatternsGreedily. if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp, - VecExtractOp, VecShuffleDynamicOp>(op)) + VecExtractOp, VecShuffleDynamicOp, ComplexCreateOp>(op)) ops.push_back(op); }); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 977c8912c1d11..6ce5b67f5e4f9 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -869,7 +869,32 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( rewriter.eraseOp(op); return mlir::success(); } - } else { + } else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) { + auto complexAttr = mlir::cast<cir::ConstComplexAttr>(op.getValue()); + mlir::Type complexElemTy = complexTy.getElementType(); + mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy); + + mlir::Attribute components[2]; + if (mlir::isa<cir::IntType>(complexElemTy)) { + components[0] = rewriter.getIntegerAttr( + complexElemLLVMTy, + mlir::cast<cir::IntAttr>(complexAttr.getReal()).getValue()); + components[1] = rewriter.getIntegerAttr( + complexElemLLVMTy, + mlir::cast<cir::IntAttr>(complexAttr.getImag()).getValue()); + } else { + components[0] = rewriter.getFloatAttr( + complexElemLLVMTy, + mlir::cast<cir::FPAttr>(complexAttr.getReal()).getValue()); + components[1] = rewriter.getFloatAttr( + complexElemLLVMTy, + mlir::cast<cir::FPAttr>(complexAttr.getImag()).getValue()); + } + + attr = rewriter.getArrayAttr(components); + } + + else { return op.emitError() << "unsupported constant type " << op.getType(); } @@ -1771,7 +1796,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMVecInsertOpLowering, CIRToLLVMVecCmpOpLowering, CIRToLLVMVecShuffleDynamicOpLowering, - CIRToLLVMVecTernaryOpLowering + CIRToLLVMVecTernaryOpLowering, + CIRToLLVMComplexCreateOpLowering // clang-format on >(converter, patterns.getContext()); @@ -1990,6 +2016,24 @@ mlir::LogicalResult CIRToLLVMVecTernaryOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite( + cir::ComplexCreateOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type complexLLVMTy = + getTypeConverter()->convertType(op.getResult().getType()); + auto initialComplex = + rewriter.create<mlir::LLVM::UndefOp>(op->getLoc(), complexLLVMTy); + + auto realComplex = rewriter.create<mlir::LLVM::InsertValueOp>( + op->getLoc(), initialComplex, adaptor.getReal(), 0); + + auto complex = rewriter.create<mlir::LLVM::InsertValueOp>( + op->getLoc(), realComplex, adaptor.getImag(), 1); + + rewriter.replaceOp(op, complex); + 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 d1efa4e181a07..b572fb6a9dfe4 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -378,6 +378,16 @@ class CIRToLLVMVecTernaryOpLowering mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMComplexCreateOpLowering + : public mlir::OpConversionPattern<cir::ComplexCreateOp> { +public: + using mlir::OpConversionPattern<cir::ComplexCreateOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::ComplexCreateOp 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 6fa7bca3749cf..3702f809b14f6 100644 --- a/clang/test/CIR/CodeGen/complex.cpp +++ b/clang/test/CIR/CodeGen/complex.cpp @@ -27,3 +27,59 @@ float _Complex cf2 = { 1.0f, 2.0f }; // OGCG: {{.*}} = global { float, float } zeroinitializer, align 4 // OGCG: {{.*}} = global { i32, i32 } { i32 1, i32 2 }, align 4 // OGCG: {{.*}} = global { float, float } { float 1.000000e+00, float 2.000000e+00 }, align 4 + +void foo() { int _Complex c = {}; } + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] +// CIR: %[[COMPLEX:.*]] = cir.const #cir.const_complex<#cir.int<0> : !s32i, #cir.int<0> : !s32i> : !cir.complex<!s32i> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + +// LLVM: %[[INIT:.*]] = alloca { i32, i32 }, i64 1, align 4 +// LLVM: store { i32, i32 } zeroinitializer, ptr %[[INIT]], align 4 + +// OGCG: %[[COMPLEX:.*]] = alloca { i32, i32 }, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store i32 0, ptr %[[C_REAL_PTR]], align 4 +// OGCG: store i32 0, ptr %[[C_IMAG_PTR]], align 4 + +void foo2() { int _Complex c = {1, 2}; } + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] +// CIR: %[[COMPLEX:.*]] = cir.const #cir.const_complex<#cir.int<1> : !s32i, #cir.int<2> : !s32i> : !cir.complex<!s32i> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + +// LLVM: %[[INIT:.*]] = alloca { i32, i32 }, i64 1, align 4 +// LLVM: store { i32, i32 } { i32 1, i32 2 }, ptr %[[INIT]], align 4 + +// OGCG: %[[COMPLEX:.*]] = alloca { i32, i32 }, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store i32 1, ptr %[[C_REAL_PTR]], align 4 +// OGCG: store i32 2, ptr %[[C_IMAG_PTR]], align 4 + +void foo3() { + int a; + int b; + int _Complex c = {a, b}; +} + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] +// CIR: %[[TMP_A:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i> +// CIR: %[[TMP_B:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i> +// CIR: %[[COMPLEX:.*]] = cir.complex.create %[[TMP_A]], %[[TMP_B]] : !s32i -> !cir.complex<!s32i> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + +// LLVM: %[[INIT:.*]] = alloca { i32, i32 }, i64 1, align 4 +// LLVM: %[[TMP_A:.*]] = load i32, ptr {{.*}}, align 4 +// LLVM: %[[TMP_B:.*]] = load i32, ptr {{.*}}, align 4 +// LLVM: %[[TMP:.*]] = insertvalue { i32, i32 } undef, i32 %[[TMP_A]], 0 +// LLVM: %[[TMP_2:.*]] = insertvalue { i32, i32 } %[[TMP]], i32 %[[TMP_B]], 1 +// LLVM: store { i32, i32 } %[[TMP_2]], ptr %[[INIT]], align 4 + +// OGCG: %[[REAL_VAL:.*]] = load i32, ptr {{.*}}, align 4 +// OGCG: %[[IMAG_VAL:.*]] = load i32, ptr {{.*}}, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store i32 %[[REAL_VAL]], ptr %[[C_REAL_PTR]], align 4 +// OGCG: store i32 %[[IMAG_VAL]], ptr %[[C_IMAG_PTR]], align 4 diff --git a/clang/test/CIR/Transforms/complex-create-fold.cir b/clang/test/CIR/Transforms/complex-create-fold.cir new file mode 100644 index 0000000000000..5d9d22112c8b7 --- /dev/null +++ b/clang/test/CIR/Transforms/complex-create-fold.cir @@ -0,0 +1,30 @@ +// RUN: cir-opt %s -cir-canonicalize -o - | FileCheck %s + +!s32i = !cir.int<s, 32> + +module { + cir.func @fold_complex_create_test() -> !cir.complex<!s32i> { + %0 = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["__retval"] + %1 = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] + %2 = cir.const #cir.int<1> : !s32i + %3 = cir.const #cir.int<2> : !s32i + %4 = cir.complex.create %2, %3 : !s32i -> !cir.complex<!s32i> + cir.store align(4) %4, %1 : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + %5 = cir.load align(4) %1 : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i> + cir.store align(4) %5, %0 : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + %6 = cir.load %0 : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i> + cir.return %6 : !cir.complex<!s32i> + } + +// CHECK: cir.func @fold_complex_create_test() -> !cir.complex<!s32i> { +// CHECK: %[[RET:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["__retval"] +// CHECK: %[[INIT:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] +// CHECK: %[[COMPLEX:.*]] = cir.const #cir.const_complex<#cir.int<1> : !s32i, #cir.int<2> : !s32i> : !cir.complex<!s32i> +// CHECK: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> +// CHECK: %[[TMP:.*]] = cir.load{{.*}} %[[INIT]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i> +// CHECK: cir.store{{.*}} %[[TMP:.*]], %[[RET]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> +// CHECK: %[[TMP_2:.*]] = cir.load %[[RET]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i> +// CHECK: cir.return %[[TMP_2]] : !cir.complex<!s32i> +// CHECK: } + +} >From f175574c731de8e8ab86d0460cd2bb4b5f37f346 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Fri, 6 Jun 2025 23:18:35 +0200 Subject: [PATCH 2/3] Add more test cases --- clang/test/CIR/CodeGen/complex.cpp | 93 ++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/clang/test/CIR/CodeGen/complex.cpp b/clang/test/CIR/CodeGen/complex.cpp index 3702f809b14f6..d193b9f32efbc 100644 --- a/clang/test/CIR/CodeGen/complex.cpp +++ b/clang/test/CIR/CodeGen/complex.cpp @@ -77,9 +77,102 @@ void foo3() { // LLVM: %[[TMP_2:.*]] = insertvalue { i32, i32 } %[[TMP]], i32 %[[TMP_B]], 1 // LLVM: store { i32, i32 } %[[TMP_2]], ptr %[[INIT]], align 4 +// OGCG: %[[COMPLEX:.*]] = alloca { i32, i32 }, align 4 // OGCG: %[[REAL_VAL:.*]] = load i32, ptr {{.*}}, align 4 // OGCG: %[[IMAG_VAL:.*]] = load i32, ptr {{.*}}, align 4 // OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 0 // OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1 // OGCG: store i32 %[[REAL_VAL]], ptr %[[C_REAL_PTR]], align 4 // OGCG: store i32 %[[IMAG_VAL]], ptr %[[C_IMAG_PTR]], align 4 + +void foo4() { + int a; + int _Complex c = {1, a}; +} + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["c", init] +// CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s32i +// CIR: %[[TMP_A:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i>, !s32i +// CIR: %[[COMPLEX:.*]] = cir.complex.create %[[CONST_1]], %[[TMP_A]] : !s32i -> !cir.complex<!s32i> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>> + +// LLVM: %[[INIT:.*]] = alloca { i32, i32 }, i64 1, align 4 +// LLVM: %[[TMP_A:.*]] = load i32, ptr {{.*}}, align 4 +// LLVM: %[[COMPLEX:.*]] = insertvalue { i32, i32 } { i32 1, i32 undef }, i32 %[[TMP_A]], 1 +// LLVM: store { i32, i32 } %[[COMPLEX]], ptr %[[INIT]], align 4 + +// OGCG: %[[COMPLEX:.*]] = alloca { i32, i32 }, align 4 +// OGCG: %[[TMP_A:.*]] = load i32, ptr {{.*}}, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store i32 1, ptr %[[C_REAL_PTR]], align 4 +// OGCG: store i32 %[[TMP_A]], ptr %[[C_IMAG_PTR]], align 4 + +void foo5() { + float _Complex c = {1.0f, 2.0f}; +} + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["c", init] +// CIR: %[[COMPLEX:.*]] = cir.const #cir.const_complex<#cir.fp<1.000000e+00> : !cir.float, #cir.fp<2.000000e+00> : !cir.float> : !cir.complex<!cir.float> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> + +// LLVM: %[[INIT:.*]] = alloca { float, float }, i64 1, align 4 +// LLVM: store { float, float } { float 1.000000e+00, float 2.000000e+00 }, ptr %[[INIT]], align 4 + +// OGCG: %[[COMPLEX]] = alloca { float, float }, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store float 1.000000e+00, ptr %[[C_REAL_PTR]], align 4 +// OGCG: store float 2.000000e+00, ptr %[[C_IMAG_PTR]], align 4 + +void foo6() { + float a; + float b; + float _Complex c = {a, b}; +} + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["c", init] +// CIR: %[[TMP_A:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[TMP_B:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[COMPLEX:.*]] = cir.complex.create %[[TMP_A]], %[[TMP_B]] : !cir.float -> !cir.complex<!cir.float> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> + +// LLVM: %[[COMPLEX:.*]] = alloca { float, float }, i64 1, align 4 +// LLVM: %[[TMP_A:.*]] = load float, ptr {{.*}}, align 4 +// LLVM: %[[TMP_B:.*]] = load float, ptr {{.*}}, align 4 +// LLVM: %[[TMP:.*]] = insertvalue { float, float } undef, float %[[TMP_A]], 0 +// LLVM: %[[TMP_2:.*]] = insertvalue { float, float } %[[TMP]], float %[[TMP_B]], 1 +// LLVM: store { float, float } %[[TMP_2]], ptr %[[COMPLEX]], align 4 + +// OGCG: %[[COMPLEX]] = alloca { float, float }, align 4 +// OGCG: %[[TMP_A:.*]] = load float, ptr {{.*}}, align 4 +// OGCG: %[[TMP_B:.*]] = load float, ptr {{.*}}, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store float %[[TMP_A]], ptr %[[C_REAL_PTR]], align 4 +// OGCG: store float %[[TMP_B]], ptr %[[C_IMAG_PTR]], align 4 + +void foo7() { + float a; + float _Complex c = {a, 2.0f}; +} + +// CIR: %[[INIT:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["c", init] +// CIR: %[[TMP_A:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.float>, !cir.float +// CIR: %[[CONST_2F:.*]] = cir.const #cir.fp<2.000000e+00> : !cir.float +// CIR: %[[COMPLEX:.*]] = cir.complex.create %[[TMP_A]], %[[CONST_2F]] : !cir.float -> !cir.complex<!cir.float> +// CIR: cir.store{{.*}} %[[COMPLEX]], %[[INIT]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> + +// LLVM: %[[COMPLEX:.*]] = alloca { float, float }, i64 1, align 4 +// LLVM: %[[TMP_A:.*]] = load float, ptr {{.*}}, align 4 +// LLVM: %[[TMP:.*]] = insertvalue { float, float } undef, float %[[TMP_A]], 0 +// LLVM: %[[TMP_2:.*]] = insertvalue { float, float } %[[TMP]], float 2.000000e+00, 1 +// LLVM: store { float, float } %[[TMP_2]], ptr %[[COMPLEX]], align 4 + +// OGCG: %[[COMPLEX:.*]] = alloca { float, float }, align 4 +// OGCG: %[[TMP_A:.*]] = load float, ptr {{.*}}, align 4 +// OGCG: %[[C_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 0 +// OGCG: %[[C_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX]], i32 0, i32 1 +// OGCG: store float %[[TMP_A]], ptr %[[C_REAL_PTR]], align 4 +// OGCG: store float 2.000000e+00, ptr %[[C_IMAG_PTR]], align 4 + >From ffe4b9245ce551a19eb792aad864da414afcfbb7 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Sat, 7 Jun 2025 13:55:47 +0200 Subject: [PATCH 3/3] Fix CIR format in description --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 5f72545ea020d..10e44cf34a686 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2239,7 +2239,7 @@ def ComplexCreateOp : CIR_Op<"complex.create", [Pure, SameTypeOperands]> { ```mlir %0 = cir.const #cir.fp<1.000000e+00> : !cir.double %1 = cir.const #cir.fp<2.000000e+00> : !cir.double - %2 = cir.complex.create %0, %1 : !cir.complex<!cir.double> + %2 = cir.complex.create %0, %1 : !cir.double -> !cir.complex<!cir.double> ``` }]; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits