-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, i think we should do that |
||
|
||
let results = (outs CIR_ComplexType:$result); | ||
|
||
let assemblyFormat = [{ | ||
$lhs `,` $rhs `:` qualified(type($result)) attr-dict | ||
}]; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Bit Manipulation Operations | ||
//===----------------------------------------------------------------------===// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,60 @@ 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) { | ||
assert(!cir::MissingFeatures::fastMathFlags()); | ||
assert(!cir::MissingFeatures::cgFPOptionsRAII()); | ||
return builder.create<cir::ComplexAddOp>(op.loc, op.lhs, op.rhs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
} | ||
|
||
LValue CIRGenFunction::emitComplexAssignmentLValue(const BinaryOperator *e) { | ||
assert(e->getOpcode() == BO_Assign && "Expected assign op"); | ||
|
||
|
@@ -313,3 +416,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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2048,6 +2048,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { | |
CIRToLLVMBrOpLowering, | ||
CIRToLLVMCallOpLowering, | ||
CIRToLLVMCmpOpLowering, | ||
CIRToLLVMComplexAddOpLowering, | ||
CIRToLLVMComplexCreateOpLowering, | ||
CIRToLLVMComplexImagOpLowering, | ||
CIRToLLVMComplexRealOpLowering, | ||
|
@@ -2357,6 +2358,55 @@ 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 { | ||
assert(!cir::MissingFeatures::fastMathFlags()); | ||
assert(!cir::MissingFeatures::fpConstraints()); | ||
newReal = rewriter.create<mlir::LLVM::FAddOp>(loc, complexElemTy, lhsReal, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also needs fast-math and constrained FP math missing features. |
||
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::PoisonOp>(op->getLoc(), complexLLVMTy); | ||
|
||
auto realComplex = rewriter.create<mlir::LLVM::InsertValueOp>( | ||
op->getLoc(), initialComplex, newReal, 0); | ||
|
||
rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(op, realComplex, | ||
newImag, 1); | ||
|
||
return mlir::success(); | ||
} | ||
|
||
mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite( | ||
cir::ComplexCreateOp op, OpAdaptor adaptor, | ||
mlir::ConversionPatternRewriter &rewriter) const { | ||
|
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 forcir.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 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.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.