-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[CIR] Implement AddOp for ComplexType #147578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Amr Hesham (AmrDeveloper) ChangesThis change adds support for AddOp for ComplexType Full diff: https://github.com/llvm/llvm-project/pull/147578.diff 6 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6529f1386599c..802cda0a1a2e3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2521,6 +2521,32 @@ def ComplexImagOp : CIR_Op<"complex.imag", [Pure]> {
let hasFolder = 1;
}
+//===----------------------------------------------------------------------===//
+// ComplexAddOp
+//===----------------------------------------------------------------------===//
+
+def ComplexAddOp : CIR_Op<"complex.add", [Pure, SameOperandsAndResultType]> {
+ let summary = "Complex addition";
+ let description = [{
+ The `cir.complex.add` operation takes two complex numbers and returns
+ their sum.
+
+ Example:
+
+ ```mlir
+ %2 = cir.complex.add %0, %1 -> !cir.complex<!cir.float>
+ ```
+ }];
+
+ let arguments = (ins CIR_ComplexType:$lhs, CIR_ComplexType:$rhs);
+
+ let results = (outs CIR_ComplexType:$result);
+
+ let assemblyFormat = [{
+ $lhs `,` $rhs `->` qualified(type($result)) attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Bit Manipulation Operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
index 84fad959ebf49..6001b0a51121d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp
@@ -57,6 +57,55 @@ class ComplexExprEmitter : public StmtVisitor<ComplexExprEmitter, mlir::Value> {
mlir::Value
VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *e);
mlir::Value VisitUnaryDeref(const Expr *e);
+
+ struct BinOpInfo {
+ mlir::Location loc;
+ mlir::Value lhs{};
+ mlir::Value rhs{};
+ QualType ty{}; // Computation Type.
+ FPOptions fpFeatures{};
+ };
+
+ BinOpInfo emitBinOps(const BinaryOperator *e,
+ QualType promotionTy = QualType());
+
+ mlir::Value emitPromoted(const Expr *e, QualType promotionTy);
+
+ mlir::Value emitPromotedComplexOperand(const Expr *e, QualType promotionTy);
+
+ mlir::Value emitBinAdd(const BinOpInfo &op);
+
+ QualType getPromotionType(QualType ty, bool isDivOpCode = false) {
+ if (auto *complexTy = ty->getAs<ComplexType>()) {
+ QualType elementTy = complexTy->getElementType();
+ if (isDivOpCode && elementTy->isFloatingType() &&
+ cgf.getLangOpts().getComplexRange() ==
+ LangOptions::ComplexRangeKind::CX_Promoted) {
+ cgf.cgm.errorNYI("HigherPrecisionTypeForComplexArithmetic");
+ return QualType();
+ }
+
+ if (elementTy.UseExcessPrecision(cgf.getContext()))
+ return cgf.getContext().getComplexType(cgf.getContext().FloatTy);
+ }
+
+ if (ty.UseExcessPrecision(cgf.getContext()))
+ return cgf.getContext().FloatTy;
+ return QualType();
+ }
+
+#define HANDLEBINOP(OP) \
+ mlir::Value VisitBin##OP(const BinaryOperator *e) { \
+ QualType promotionTy = getPromotionType( \
+ e->getType(), e->getOpcode() == BinaryOperatorKind::BO_Div); \
+ mlir::Value result = emitBin##OP(emitBinOps(e, promotionTy)); \
+ if (!promotionTy.isNull()) \
+ cgf.cgm.errorNYI("Binop emitUnPromotedValue"); \
+ return result; \
+ }
+
+ HANDLEBINOP(Add)
+#undef HANDLEBINOP
};
} // namespace
@@ -291,6 +340,58 @@ mlir::Value ComplexExprEmitter::VisitUnaryDeref(const Expr *e) {
return emitLoadOfLValue(e);
}
+mlir::Value ComplexExprEmitter::emitPromoted(const Expr *e,
+ QualType promotionTy) {
+ e = e->IgnoreParens();
+ if (const auto *bo = dyn_cast<BinaryOperator>(e)) {
+ switch (bo->getOpcode()) {
+#define HANDLE_BINOP(OP) \
+ case BO_##OP: \
+ return emitBin##OP(emitBinOps(bo, promotionTy));
+ HANDLE_BINOP(Add)
+#undef HANDLE_BINOP
+ default:
+ break;
+ }
+ } else if (isa<UnaryOperator>(e)) {
+ cgf.cgm.errorNYI("emitPromoted UnaryOperator");
+ return {};
+ }
+
+ mlir::Value result = Visit(const_cast<Expr *>(e));
+ if (!promotionTy.isNull())
+ cgf.cgm.errorNYI("emitPromoted emitPromotedValue");
+
+ return result;
+}
+
+mlir::Value
+ComplexExprEmitter::emitPromotedComplexOperand(const Expr *e,
+ QualType promotionTy) {
+ if (e->getType()->isAnyComplexType()) {
+ if (!promotionTy.isNull())
+ return cgf.emitPromotedComplexExpr(e, promotionTy);
+ return Visit(const_cast<Expr *>(e));
+ }
+
+ cgf.cgm.errorNYI("emitPromotedComplexOperand non-complex type");
+ return {};
+}
+
+ComplexExprEmitter::BinOpInfo
+ComplexExprEmitter::emitBinOps(const BinaryOperator *e, QualType promotionTy) {
+ BinOpInfo binOpInfo{cgf.getLoc(e->getExprLoc())};
+ binOpInfo.lhs = emitPromotedComplexOperand(e->getLHS(), promotionTy);
+ binOpInfo.rhs = emitPromotedComplexOperand(e->getRHS(), promotionTy);
+ binOpInfo.ty = promotionTy.isNull() ? e->getType() : promotionTy;
+ binOpInfo.fpFeatures = e->getFPFeaturesInEffect(cgf.getLangOpts());
+ return binOpInfo;
+}
+
+mlir::Value ComplexExprEmitter::emitBinAdd(const BinOpInfo &op) {
+ return builder.create<cir::ComplexAddOp>(op.loc, op.lhs, op.rhs);
+}
+
LValue CIRGenFunction::emitComplexAssignmentLValue(const BinaryOperator *e) {
assert(e->getOpcode() == BO_Assign && "Expected assign op");
@@ -313,3 +414,8 @@ void CIRGenFunction::emitStoreOfComplex(mlir::Location loc, mlir::Value v,
LValue dest, bool isInit) {
ComplexExprEmitter(*this).emitStoreOfComplex(loc, v, dest, isInit);
}
+
+mlir::Value CIRGenFunction::emitPromotedComplexExpr(const Expr *e,
+ QualType promotionType) {
+ return ComplexExprEmitter(*this).emitPromoted(e, promotionType);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 76353bae68e21..f9a4c3f7bbab5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -886,6 +886,8 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitInitializerForField(clang::FieldDecl *field, LValue lhs,
clang::Expr *init);
+ mlir::Value emitPromotedComplexExpr(const Expr *e, QualType promotionType);
+
mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
/// Emit the computation of the specified expression of scalar type.
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index af307f6ad673d..8674880bf4a5d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2048,6 +2048,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMBrOpLowering,
CIRToLLVMCallOpLowering,
CIRToLLVMCmpOpLowering,
+ CIRToLLVMComplexAddOpLowering,
CIRToLLVMComplexCreateOpLowering,
CIRToLLVMComplexImagOpLowering,
CIRToLLVMComplexRealOpLowering,
@@ -2357,6 +2358,54 @@ mlir::LogicalResult CIRToLLVMVecTernaryOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMComplexAddOpLowering::matchAndRewrite(
+ cir::ComplexAddOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Value lhs = adaptor.getLhs();
+ mlir::Value rhs = adaptor.getRhs();
+ mlir::Location loc = op.getLoc();
+
+ auto complexType = mlir::cast<cir::ComplexType>(op.getLhs().getType());
+ mlir::Type complexElemTy =
+ getTypeConverter()->convertType(complexType.getElementType());
+ auto lhsReal =
+ rewriter.create<mlir::LLVM::ExtractValueOp>(loc, complexElemTy, lhs, 0);
+ auto lhsImag =
+ rewriter.create<mlir::LLVM::ExtractValueOp>(loc, complexElemTy, lhs, 1);
+ auto rhsReal =
+ rewriter.create<mlir::LLVM::ExtractValueOp>(loc, complexElemTy, rhs, 0);
+ auto rhsImag =
+ rewriter.create<mlir::LLVM::ExtractValueOp>(loc, complexElemTy, rhs, 1);
+
+ mlir::Value newReal;
+ mlir::Value newImag;
+ if (complexElemTy.isInteger()) {
+ newReal = rewriter.create<mlir::LLVM::AddOp>(loc, complexElemTy, lhsReal,
+ rhsReal);
+ newImag = rewriter.create<mlir::LLVM::AddOp>(loc, complexElemTy, lhsImag,
+ rhsImag);
+ } else {
+ newReal = rewriter.create<mlir::LLVM::FAddOp>(loc, complexElemTy, lhsReal,
+ rhsReal);
+ newImag = rewriter.create<mlir::LLVM::FAddOp>(loc, complexElemTy, lhsImag,
+ rhsImag);
+ }
+
+ 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, newReal, 0);
+
+ auto complex = rewriter.create<mlir::LLVM::InsertValueOp>(
+ op->getLoc(), realComplex, newImag, 1);
+
+ rewriter.replaceOp(op, complex);
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite(
cir::ComplexCreateOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d9fb91066317b..a41cf3f50812e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -523,6 +523,16 @@ class CIRToLLVMGetBitfieldOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMComplexAddOpLowering
+ : public mlir::OpConversionPattern<cir::ComplexAddOp> {
+public:
+ using mlir::OpConversionPattern<cir::ComplexAddOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::ComplexAddOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
} // namespace direct
} // namespace cir
diff --git a/clang/test/CIR/CodeGen/complex-arithmetic.cpp b/clang/test/CIR/CodeGen/complex-arithmetic.cpp
new file mode 100644
index 0000000000000..b549330e769d8
--- /dev/null
+++ b/clang/test/CIR/CodeGen/complex-arithmetic.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void foo() {
+ int _Complex a;
+ int _Complex b;
+ int _Complex c = a + b;
+}
+
+// CIR: %[[COMPLEX_A:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["a"]
+// CIR: %[[COMPLEX_B:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["b"]
+// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[COMPLEX_A]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[COMPLEX_B]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: %[[ADD:.*]] = cir.complex.add %[[TMP_A]], %[[TMP_B]] -> !cir.complex<!s32i>
+
+// LLVM: %[[COMPLEX_A:.*]] = alloca { i32, i32 }, i64 1, align 4
+// LLVM: %[[COMPLEX_B:.*]] = alloca { i32, i32 }, i64 1, align 4
+// LLVM: %[[TMP_A:.*]] = load { i32, i32 }, ptr %[[COMPLEX_A]], align 4
+// LLVM: %[[TMP_B:.*]] = load { i32, i32 }, ptr %[[COMPLEX_B]], align 4
+// LLVM: %[[A_REAL:.*]] = extractvalue { i32, i32 } %[[TMP_A]], 0
+// LLVM: %[[A_IMAG:.*]] = extractvalue { i32, i32 } %[[TMP_A]], 1
+// LLVM: %[[B_REAL:.*]] = extractvalue { i32, i32 } %[[TMP_B]], 0
+// LLVM: %[[B_IMAG:.*]] = extractvalue { i32, i32 } %[[TMP_B]], 1
+// LLVM: %[[ADD_REAL:.*]] = add i32 %[[A_REAL]], %[[B_REAL]]
+// LLVM: %[[ADD_IMAG:.*]] = add i32 %[[A_IMAG]], %[[B_IMAG]]
+// LLVM: %[[RESULT:.*]] = insertvalue { i32, i32 } undef, i32 %[[ADD_REAL]], 0
+// LLVM: %[[RESULT_2:.*]] = insertvalue { i32, i32 } %[[RESULT]], i32 %[[ADD_IMAG]], 1
+
+// OGCG: %[[COMPLEX_A:.*]] = alloca { i32, i32 }, align 4
+// OGCG: %[[COMPLEX_B:.*]] = alloca { i32, i32 }, align 4
+// OGCG: %[[RESULT:.*]] = alloca { i32, i32 }, align 4
+// OGCG: %[[A_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_A]], i32 0, i32 0
+// OGCG: %[[A_REAL:.*]] = load i32, ptr %[[A_REAL_PTR]], align 4
+// OGCG: %[[A_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_A]], i32 0, i32 1
+// OGCG: %[[A_IMAG:.*]] = load i32, ptr %[[A_IMAG_PTR]], align 4
+// OGCG: %[[B_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 0
+// OGCG: %[[B_REAL:.*]] = load i32, ptr %[[B_REAL_PTR]], align 4
+// OGCG: %[[B_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[COMPLEX_B]], i32 0, i32 1
+// OGCG: %[[B_IMAG:.*]] = load i32, ptr %[[B_IMAG_PTR]], align 4
+// OGCG: %[[ADD_REAL:.*]] = add i32 %[[A_REAL]], %[[B_REAL]]
+// OGCG: %[[ADD_IMAG:.*]] = add i32 %[[A_IMAG]], %[[B_IMAG]]
+// OGCG: %[[RESULT_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[RESULT]], i32 0, i32 0
+// OGCG: %[[RESULT_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[RESULT]], i32 0, i32 1
+// OGCG: store i32 %[[ADD_REAL]], ptr %[[RESULT_REAL_PTR]], align 4
+// OGCG: store i32 %[[ADD_IMAG]], ptr %[[RESULT_IMAG_PTR]], align 4
+
+void foo2() {
+ float _Complex a;
+ float _Complex b;
+ float _Complex c = a + b;
+}
+
+// CIR: %[[COMPLEX_A:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["a"]
+// CIR: %[[COMPLEX_B:.*]] = cir.alloca !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>, ["b"]
+// CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[COMPLEX_A]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
+// CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[COMPLEX_B]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
+// CIR: %[[ADD:.*]] = cir.complex.add %[[TMP_A]], %[[TMP_B]] -> !cir.complex<!cir.float>
+
+// LLVM: %[[COMPLEX_A:.*]] = alloca { float, float }, i64 1, align 4
+// LLVM: %[[COMPLEX_B:.*]] = alloca { float, float }, i64 1, align 4
+// LLVM: %[[TMP_A:.*]] = load { float, float }, ptr %[[COMPLEX_A]], align 4
+// LLVM: %[[TMP_B:.*]] = load { float, float }, ptr %[[COMPLEX_B]], align 4
+// LLVM: %[[A_REAL:.*]] = extractvalue { float, float } %[[TMP_A]], 0
+// LLVM: %[[A_IMAG:.*]] = extractvalue { float, float } %[[TMP_A]], 1
+// LLVM: %[[B_REAL:.*]] = extractvalue { float, float } %[[TMP_B]], 0
+// LLVM: %[[B_IMAG:.*]] = extractvalue { float, float } %[[TMP_B]], 1
+// LLVM: %[[ADD_REAL:.*]] = fadd float %[[A_REAL]], %[[B_REAL]]
+// LLVM: %[[ADD_IMAG:.*]] = fadd float %[[A_IMAG]], %[[B_IMAG]]
+// LLVM: %[[RESULT:.*]] = insertvalue { float, float } undef, float %[[ADD_REAL]], 0
+// LLVM: %[[RESULT_2:.*]] = insertvalue { float, float } %[[RESULT]], float %[[ADD_IMAG]], 1
+
+// OGCG: %[[COMPLEX_A:.*]] = alloca { float, float }, align 4
+// OGCG: %[[COMPLEX_B:.*]] = alloca { float, float }, align 4
+// OGCG: %[[RESULT:.*]] = alloca { float, float }, align 4
+// OGCG: %[[A_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX_A]], i32 0, i32 0
+// OGCG: %[[A_REAL:.*]] = load float, ptr %[[A_REAL_PTR]], align 4
+// OGCG: %[[A_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX_A]], i32 0, i32 1
+// OGCG: %[[A_IMAG:.*]] = load float, ptr %[[A_IMAG_PTR]], align 4
+// OGCG: %[[B_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX_B]], i32 0, i32 0
+// OGCG: %[[B_REAL:.*]] = load float, ptr %[[B_REAL_PTR]], align 4
+// OGCG: %[[B_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[COMPLEX_B]], i32 0, i32 1
+// OGCG: %[[B_IMAG:.*]] = load float, ptr %[[B_IMAG_PTR]], align 4
+// OGCG: %[[ADD_REAL:.*]] = fadd float %[[A_REAL]], %[[B_REAL]]
+// OGCG: %[[ADD_IMAG:.*]] = fadd float %[[A_IMAG]], %[[B_IMAG]]
+// OGCG: %[[RESULT_REAL_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[RESULT]], i32 0, i32 0
+// OGCG: %[[RESULT_IMAG_PTR:.*]] = getelementptr inbounds nuw { float, float }, ptr %[[RESULT]], i32 0, i32 1
+// OGCG: store float %[[ADD_REAL]], ptr %[[RESULT_REAL_PTR]], align 4
+// OGCG: store float %[[ADD_IMAG]], ptr %[[RESULT_IMAG_PTR]], align 4
|
|
``` | ||
}]; | ||
|
||
let arguments = (ins CIR_ComplexType:$lhs, CIR_ComplexType:$rhs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we allow attaching integer overflow attributes to this operation? It looks like classic codegen doesn't do that, but I can't see why it shouldn't. This doesn't need to be done for this PR, but it's something we should think about.
// ComplexAddOp | ||
//===----------------------------------------------------------------------===// | ||
|
||
def ComplexAddOp : CIR_Op<"complex.add", [Pure, SameOperandsAndResultType]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have separate operations for add/sub/mul/div or combine them into a single complex.binop
? The strongest reason I can see for having them separate is that the 'complex' dialect has separate operations. On the other hand, the 'math' dialect has separate operations for add/sub/mul/div, but we still combined them in CIR. We should probably handle both the same way, and I'm inclined toward separate operations. I'm not sure why the decision was made to combine them for cir.binop
.
@bcardosolopes What is your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am in favor of separate operations too, as it will simplify future optimizations, like specification of commutativity etc.
Sidenote: I would not incline to complex dialect that much, as it might get deprecated, since there is no maintainer still. See: https://discourse.llvm.org/t/mlir-project-maintainers/87189
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why the decision was made to combine them for cir.binop. ... bcardosolopes What is your opinion?
I kept them combined for simplicity given all uses so far don't really require them to be separate (and lowering is still as simple as if they were separate). In hindsight using tablegen class for binop would have had similar effect.
Whenever it makes sense to split off cir.binop (commutativity spec, etc as mentioned by @xlauko, or just plain consistency), I'm in favor. In that spirit, I'd say let's keep complex
separate to mirror the dialect.
Sidenote: I would not incline to complex dialect that much, as it might get deprecated, since there is no maintainer still. See: https://discourse.llvm.org/t/mlir-project-maintainers/87189
I had the same impression when I read this post. @andykaylor do any of your colleagues (or you) could be interested in maintaining the complex dialect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll ask around. I feel like it is definitely useful, but I don't know who is actually using it.
Example: | ||
|
||
```mlir | ||
%2 = cir.complex.add %0, %1 -> !cir.complex<!cir.float> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%2 = cir.complex.add %0, %1 -> !cir.complex<!cir.float> | |
%2 = cir.complex.add %0, %1 : !cir.complex<!cir.float> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had discussion about return type assembly format this monday.
I would like to unify return types to be represented as -> return_type
.
Though maybe in case the type applies to entire operation, like in this case, :
form is better and can be nice distinguisher?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like in this case, : form is better and can be nice distinguisher?
+1 here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggested this change for consistency with what the CIR ASM style guide says. I think that's consistent with what's been said here, :
for SameOperandsAndResultType
or AllTypesMatch
and ->
for return values otherwise.
let results = (outs CIR_ComplexType:$result); | ||
|
||
let assemblyFormat = [{ | ||
$lhs `,` $rhs `->` qualified(type($result)) attr-dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$lhs `,` $rhs `->` qualified(type($result)) attr-dict | |
$lhs `,` $rhs `:` qualified(type($result)) attr-dict |
} | ||
|
||
mlir::Value ComplexExprEmitter::emitBinAdd(const BinOpInfo &op) { | ||
return builder.create<cir::ComplexAddOp>(op.loc, op.lhs, op.rhs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll eventually want a way to attach fast-math flags or set exception handling and rounding mode constraints if this is a floating-point operation. Can you add missing features here for those?
newImag = rewriter.create<mlir::LLVM::AddOp>(loc, complexElemTy, lhsImag, | ||
rhsImag); | ||
} else { | ||
newReal = rewriter.create<mlir::LLVM::FAddOp>(loc, complexElemTy, lhsReal, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs fast-math and constrained FP math missing features.
mlir::Type complexLLVMTy = | ||
getTypeConverter()->convertType(op.getResult().getType()); | ||
auto initialComplex = | ||
rewriter.create<mlir::LLVM::UndefOp>(op->getLoc(), complexLLVMTy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rewriter.create<mlir::LLVM::UndefOp>(op->getLoc(), complexLLVMTy); | |
rewriter.create<mlir::LLVM::PoisonOp>(op->getLoc(), complexLLVMTy); |
void foo() { | ||
int _Complex a; | ||
int _Complex b; | ||
int _Complex c = a + b; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test case with a compound addition (d = (a + b) + c
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm with minor nit
auto complex = rewriter.create<mlir::LLVM::InsertValueOp>( | ||
op->getLoc(), realComplex, newImag, 1); | ||
|
||
rewriter.replaceOp(op, complex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use replaceOpWithNewOp
here.
This change adds support for AddOp for ComplexType
#141365