[clang] 6464066 - [CIR] Upstream CreateOp for ComplexType with folder (#143192)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 14 04:26:06 PDT 2025
Author: Amr Hesham
Date: 2025-06-14T13:26:03+02:00
New Revision: 64640667871990e4d73ae6221b9c4f05d0b36ea6
URL: https://github.com/llvm/llvm-project/commit/64640667871990e4d73ae6221b9c4f05d0b36ea6
DIFF: https://github.com/llvm/llvm-project/commit/64640667871990e4d73ae6221b9c4f05d0b36ea6.diff
LOG: [CIR] Upstream CreateOp for ComplexType with folder (#143192)
This change adds support for the create op for ComplexType with folder
and support for empty init list
https://github.com/llvm/llvm-project/issues/141365
Added:
clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
clang/test/CIR/Transforms/complex-create-fold.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/Dialect/IR/CIRTypes.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenBuilder.h
clang/lib/CIR/CodeGen/CIRGenDecl.cpp
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
clang/lib/CIR/CodeGen/CMakeLists.txt
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
clang/test/CIR/CodeGen/complex.cpp
Removed:
################################################################################
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/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 194153caa9271..bd36d228578b7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2353,4 +2353,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.double -> !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 225e9ec89a827..13ddc77835fbc 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -251,7 +251,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 adf7cb77f1a5d..e38faba83b80c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -360,6 +360,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 1941b5066edb4..afbe92aded804 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -499,7 +499,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,
+ /*isInit*/ true);
return;
}
case cir::TEK_Aggregate:
@@ -593,8 +599,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 42d0c78013f57..2e43f10be132c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1690,3 +1690,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..2ffe75a388e98
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
@@ -0,0 +1,79 @@
+#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()) {}
+
+ /// 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) {
+ 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(loc, 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");
+ QualType complexElemTy =
+ e->getType()->castAs<clang::ComplexType>()->getElementType();
+ mlir::Type complexElemLLVMTy = cgf.convertType(complexElemTy);
+ mlir::TypedAttr defaultValue = builder.getZeroInitAttr(complexElemLLVMTy);
+ auto complexAttr = cir::ConstComplexAttr::get(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 318d3fbf3f9e1..de6ef2a69faf1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -338,6 +338,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.
@@ -860,6 +862,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);
@@ -961,6 +967,9 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage);
+ 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 217609687eabc..385bea066c61c 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -19,6 +19,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 a685253b7d821..5578d4f5825a9 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1748,6 +1748,33 @@ 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);
+ return cir::ConstComplexAttr::get(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 6f8a64ce0251e..20c634d6c66f6 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,8 +141,8 @@ void CIRCanonicalizePass::runOnOperation() {
// Many operations are here to perform a manual `fold` in
// applyOpPatternsGreedily.
if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp,
- VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp,
- VecTernaryOp>(op))
+ ComplexCreateOp, VecCreateOp, VecExtractOp, VecShuffleOp,
+ VecShuffleDynamicOp, VecTernaryOp>(op))
ops.push_back(op);
});
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 619e113202c9a..6a4e4e4a7df3b 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -905,7 +905,32 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
getTypeConverter()));
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();
}
@@ -1810,7 +1835,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMVecSplatOpLowering,
CIRToLLVMVecShuffleOpLowering,
CIRToLLVMVecShuffleDynamicOpLowering,
- CIRToLLVMVecTernaryOpLowering
+ CIRToLLVMVecTernaryOpLowering,
+ CIRToLLVMComplexCreateOpLowering
// clang-format on
>(converter, patterns.getContext());
@@ -2096,6 +2122,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 2eda568c84bdb..a809818063547 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -408,6 +408,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..d193b9f32efbc 100644
--- a/clang/test/CIR/CodeGen/complex.cpp
+++ b/clang/test/CIR/CodeGen/complex.cpp
@@ -27,3 +27,152 @@ 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: %[[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
+
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: }
+
+}
More information about the cfe-commits
mailing list