[clang] [CIR] Upstream CreateOp for ComplexType with folder (PR #143192)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 7 04:56:08 PDT 2025
https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/143192
>From 0ee92db03198e2364ade53e5c0bbd0f844fe634f Mon Sep 17 00:00:00 2001
From: AmrDeveloper <amr96 at 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 <amr96 at 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 <amr96 at 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>
```
}];
More information about the cfe-commits
mailing list