https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/143192
>From ce4df0e64a8b59411c8c94c5f65e315a76b8a371 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Fri, 6 Jun 2025 20:56:49 +0200 Subject: [PATCH 1/5] [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 | 12 +++ 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 | 4 +- .../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, 324 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 8579f7066e4fe..114ef3d6252c6 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2318,4 +2318,36 @@ def BaseClassAddrOp : CIR_Op<"base_class_addr"> { }]; } +//===----------------------------------------------------------------------===// +// 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 72d882beb2244..26737280e6354 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -237,7 +237,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 03077ee062a65..bc3311674a3b3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -335,6 +335,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 8129fe0ad7db7..dd84f634b6acd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1521,6 +1521,7 @@ cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, .getDefiningOp()); } + /// Try to emit a reference to the given value without producing it as /// an l-value. For many cases, this is just an optimization, but it avoids /// us needing to emit global copies of variables if they're named without @@ -1574,3 +1575,14 @@ mlir::Value CIRGenFunction::emitScalarConstant( } return builder.getConstant(getLoc(e->getSourceRange()), constant.getValue()); } + +/// 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 d6002c3e4d4d9..30a859dbd997e 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. @@ -805,6 +807,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); @@ -893,6 +899,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 8bfcd2773d07a..57e71204d5504 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -17,6 +17,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 bfd3a0a62a8e7..844466e70b992 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1696,6 +1696,34 @@ OpFoldResult cir::VecTernaryOp::fold(FoldAdaptor adaptor) { vecTy, mlir::ArrayAttr::get(getContext(), elements)); } +//===----------------------------------------------------------------------===// +// 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 33881c69eec5f..dba4ec18171d7 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -134,7 +134,6 @@ 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()); @@ -142,7 +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, - VecExtractOp, VecShuffleDynamicOp, VecTernaryOp>(op)) + VecExtractOp, VecShuffleDynamicOp, VecTernaryOp, 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 4fdf8f9ec2695..ce7d2cf1f4013 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -901,7 +901,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(); } @@ -1805,7 +1830,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMVecCmpOpLowering, CIRToLLVMVecShuffleOpLowering, CIRToLLVMVecShuffleDynamicOpLowering, - CIRToLLVMVecTernaryOpLowering + CIRToLLVMVecTernaryOpLowering, + CIRToLLVMComplexCreateOpLowering // clang-format on >(converter, patterns.getContext()); @@ -2041,6 +2067,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 22d8a1e7c22e0..211996ad6770d 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -398,6 +398,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 fc09c78e1f77f59429be3d8c6e2c400eeded39d0 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Fri, 6 Jun 2025 23:18:35 +0200 Subject: [PATCH 2/5] 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 c134338a298c7725a740c3e82b2c284c09eaec54 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Sat, 7 Jun 2025 13:55:47 +0200 Subject: [PATCH 3/5] 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 114ef3d6252c6..ea5710c63db0e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2331,7 +2331,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> ``` }]; >From 1dc9a2e0556c44c9c02f3cf12e3e942b80bfbd4c Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Mon, 9 Jun 2025 19:38:50 +0200 Subject: [PATCH 4/5] Address code review comment --- clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp index e7eaebac01341..cff87f9780357 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -15,7 +15,7 @@ class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> { explicit ComplexExprEmitter(CIRGenFunction &cgf) : cgf(cgf), builder(cgf.getBuilder()) {} - /// EmitStoreOfComplex - Store the specified real/imag parts into the + /// Store the specified real/imag parts into the /// specified value pointer. void emitStoreOfComplex(mlir::Location loc, mlir::Value val, LValue lv, bool isInit); >From 8d31f9cdab818af3d7d651b584647f4455630d5e Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Tue, 10 Jun 2025 19:29:57 +0200 Subject: [PATCH 5/5] Address code review comments --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 4 ++-- clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp | 8 +++----- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 3 +-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index d22d265e82425..b48f4ed461ccb 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -307,9 +307,9 @@ def ConstComplexAttr : CIR_Attr<"ConstComplex", "const_complex", ); let builders = [ - AttrBuilderWithInferredContext<(ins "cir::ComplexType":$type, - "mlir::TypedAttr":$real, + AttrBuilderWithInferredContext<(ins "mlir::TypedAttr":$real, "mlir::TypedAttr":$imag), [{ + auto type = cir::ComplexType::get(real.getType()); return $_get(type.getContext(), type, real, imag); }]>, ]; diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 3e2c96c5aaeaf..2c4df1891b1ea 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -261,7 +261,7 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, "emitExprAsInit: complex type captured by init"); mlir::Location loc = getLoc(init->getExprLoc()); emitStoreOfComplex(loc, complex, lvalue, - /*init*/ true); + /*isInit*/ true); return; } case cir::TEK_Aggregate: diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp index cff87f9780357..2ffe75a388e98 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -45,10 +45,11 @@ void ComplexExprEmitter::emitStoreOfComplex(mlir::Location loc, mlir::Value val, } mlir::Value ComplexExprEmitter::VisitInitListExpr(InitListExpr *e) { + mlir::Location loc = cgf.getLoc(e->getExprLoc()); 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); + return builder.createComplexCreate(loc, real, imag); } if (e->getNumInits() == 1) { @@ -57,14 +58,11 @@ mlir::Value ComplexExprEmitter::VisitInitListExpr(InitListExpr *e) { } 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); + auto complexAttr = cir::ConstComplexAttr::get(defaultValue, defaultValue); return builder.create<cir::ConstantOp>(loc, complexAttr); } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 844466e70b992..9a6602636ac4a 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1720,8 +1720,7 @@ OpFoldResult cir::ComplexCreateOp::fold(FoldAdaptor adaptor) { // `#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); + return cir::ConstComplexAttr::get(realAttr, imagAttr); } //===----------------------------------------------------------------------===// _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits