Skip to content

Commit c114152

Browse files
authored
[CIR] Add support for binary arithmetic operations on complex numbers (#730)
This PR adds support for complex number binary arithmetic operations. Specifically, CIRGen for the following operations on complex numbers are added: - Binary arithmetic operations: add, sub, mul, and div. - ~Unary arithmetic operations: plus, minus, and conjugate.~ A new operation `cir.complex.binop` is added to represent binary operations on complex number operands. The new operation contains complex-number-specific attributes for correct lowering to LLVM IR. ~I'll add LLVM IR lowering in later PRs as this PR is already large enough.~ This PR also includes LLVM IR lowering support. It introduces two new operations `cir.complex.real` and `cir.complex.imag` to aid for LLVM IR lowering. These two new operations extract the real and imaginary part of a complex value, which is useful during lowering prepare, during which phase the complex arithmetic operations are broken down to a series of scalar arithmetic operations on the real and imaginary parts of the complex operands.
1 parent dc9f39d commit c114152

File tree

11 files changed

+1650
-25
lines changed

11 files changed

+1650
-25
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
145145
return create<mlir::cir::CmpOp>(loc, getBoolTy(), kind, lhs, rhs);
146146
}
147147

148+
mlir::Value createIsNaN(mlir::Location loc, mlir::Value operand) {
149+
return createCompare(loc, mlir::cir::CmpOpKind::ne, operand, operand);
150+
}
151+
152+
mlir::Value createUnaryOp(mlir::Location loc, mlir::cir::UnaryOpKind kind,
153+
mlir::Value operand) {
154+
return create<mlir::cir::UnaryOp>(loc, kind, operand);
155+
}
156+
148157
mlir::Value createBinop(mlir::Value lhs, mlir::cir::BinOpKind kind,
149158
const llvm::APInt &rhs) {
150159
return create<mlir::cir::BinOp>(
@@ -158,6 +167,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
158167
rhs);
159168
}
160169

170+
mlir::Value createBinop(mlir::Location loc, mlir::Value lhs,
171+
mlir::cir::BinOpKind kind, mlir::Value rhs) {
172+
return create<mlir::cir::BinOp>(loc, lhs.getType(), kind, lhs, rhs);
173+
}
174+
161175
mlir::Value createShift(mlir::Value lhs, const llvm::APInt &rhs,
162176
bool isShiftLeft) {
163177
return create<mlir::cir::ShiftOp>(
@@ -195,6 +209,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
195209
return createBinop(lhs, mlir::cir::BinOpKind::And, rhs);
196210
}
197211

212+
mlir::Value createAnd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) {
213+
return createBinop(loc, lhs, mlir::cir::BinOpKind::And, rhs);
214+
}
215+
198216
mlir::Value createOr(mlir::Value lhs, llvm::APInt rhs) {
199217
auto val = getConstAPInt(lhs.getLoc(), lhs.getType(), rhs);
200218
return createBinop(lhs, mlir::cir::BinOpKind::Or, val);
@@ -226,6 +244,60 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
226244
return createBinop(lhs, mlir::cir::BinOpKind::Mul, val);
227245
}
228246

247+
mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real,
248+
mlir::Value imag) {
249+
auto resultComplexTy =
250+
mlir::cir::ComplexType::get(getContext(), real.getType());
251+
return create<mlir::cir::ComplexCreateOp>(loc, resultComplexTy, real, imag);
252+
}
253+
254+
mlir::Value createComplexReal(mlir::Location loc, mlir::Value operand) {
255+
auto operandTy = mlir::cast<mlir::cir::ComplexType>(operand.getType());
256+
return create<mlir::cir::ComplexRealOp>(loc, operandTy.getElementTy(),
257+
operand);
258+
}
259+
260+
mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand) {
261+
auto operandTy = mlir::cast<mlir::cir::ComplexType>(operand.getType());
262+
return create<mlir::cir::ComplexImagOp>(loc, operandTy.getElementTy(),
263+
operand);
264+
}
265+
266+
mlir::Value createComplexBinOp(mlir::Location loc, mlir::Value lhs,
267+
mlir::cir::ComplexBinOpKind kind,
268+
mlir::Value rhs,
269+
mlir::cir::ComplexRangeKind range,
270+
bool promoted) {
271+
return create<mlir::cir::ComplexBinOp>(loc, kind, lhs, rhs, range,
272+
promoted);
273+
}
274+
275+
mlir::Value createComplexAdd(mlir::Location loc, mlir::Value lhs,
276+
mlir::Value rhs) {
277+
return createBinop(loc, lhs, mlir::cir::BinOpKind::Add, rhs);
278+
}
279+
280+
mlir::Value createComplexSub(mlir::Location loc, mlir::Value lhs,
281+
mlir::Value rhs) {
282+
return createBinop(loc, lhs, mlir::cir::BinOpKind::Sub, rhs);
283+
}
284+
285+
mlir::Value createComplexMul(mlir::Location loc, mlir::Value lhs,
286+
mlir::Value rhs,
287+
mlir::cir::ComplexRangeKind range,
288+
bool promoted) {
289+
return createComplexBinOp(loc, lhs, mlir::cir::ComplexBinOpKind::Mul, rhs,
290+
range, promoted);
291+
}
292+
293+
mlir::Value createComplexDiv(mlir::Location loc, mlir::Value lhs,
294+
mlir::Value rhs,
295+
mlir::cir::ComplexRangeKind range,
296+
bool promoted) {
297+
return createComplexBinOp(loc, lhs, mlir::cir::ComplexBinOpKind::Div, rhs,
298+
range, promoted);
299+
}
300+
229301
mlir::cir::StoreOp createStore(mlir::Location loc, mlir::Value val,
230302
mlir::Value dst, bool _volatile = false,
231303
::mlir::IntegerAttr align = {},

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,12 +1210,64 @@ def ComplexCreateOp : CIR_Op<"complex.create", [Pure, SameTypeOperands]> {
12101210
let hasVerifier = 1;
12111211
}
12121212

1213+
//===----------------------------------------------------------------------===//
1214+
// ComplexRealOp and ComplexImagOp
1215+
//===----------------------------------------------------------------------===//
1216+
1217+
def ComplexRealOp : CIR_Op<"complex.real", [Pure]> {
1218+
let summary = "Extract the real part of a complex value";
1219+
let description = [{
1220+
`cir.complex.real` operation takes an operand of `!cir.complex` type and
1221+
yields the real part of it.
1222+
1223+
Example:
1224+
1225+
```mlir
1226+
%1 = cir.complex.real %0 : !cir.complex<!cir.float> -> !cir.float
1227+
```
1228+
}];
1229+
1230+
let results = (outs CIR_AnyIntOrFloat:$result);
1231+
let arguments = (ins CIR_ComplexType:$operand);
1232+
1233+
let assemblyFormat = [{
1234+
$operand `:` qualified(type($operand)) `->` qualified(type($result))
1235+
attr-dict
1236+
}];
1237+
1238+
let hasVerifier = 1;
1239+
}
1240+
1241+
def ComplexImagOp : CIR_Op<"complex.imag", [Pure]> {
1242+
let summary = "Extract the imaginary part of a complex value";
1243+
let description = [{
1244+
`cir.complex.imag` operation takes an operand of `!cir.complex` type and
1245+
yields the imaginary part of it.
1246+
1247+
Example:
1248+
1249+
```mlir
1250+
%1 = cir.complex.imag %0 : !cir.complex<!cir.float> -> !cir.float
1251+
```
1252+
}];
1253+
1254+
let results = (outs CIR_AnyIntOrFloat:$result);
1255+
let arguments = (ins CIR_ComplexType:$operand);
1256+
1257+
let assemblyFormat = [{
1258+
$operand `:` qualified(type($operand)) `->` qualified(type($result))
1259+
attr-dict
1260+
}];
1261+
1262+
let hasVerifier = 1;
1263+
}
1264+
12131265
//===----------------------------------------------------------------------===//
12141266
// ComplexRealPtrOp and ComplexImagPtrOp
12151267
//===----------------------------------------------------------------------===//
12161268

12171269
def ComplexRealPtrOp : CIR_Op<"complex.real_ptr", [Pure]> {
1218-
let summary = "Extract the real part of a complex value";
1270+
let summary = "Derive a pointer to the real part of a complex value";
12191271
let description = [{
12201272
`cir.complex.real_ptr` operation takes a pointer operand that points to a
12211273
complex value of type `!cir.complex` and yields a pointer to the real part
@@ -1240,7 +1292,7 @@ def ComplexRealPtrOp : CIR_Op<"complex.real_ptr", [Pure]> {
12401292
}
12411293

12421294
def ComplexImagPtrOp : CIR_Op<"complex.imag_ptr", [Pure]> {
1243-
let summary = "Extract the imaginary part of a complex value";
1295+
let summary = "Derive a pointer to the imaginary part of a complex value";
12441296
let description = [{
12451297
`cir.complex.imag_ptr` operation takes a pointer operand that points to a
12461298
complex value of type `!cir.complex` and yields a pointer to the imaginary
@@ -1264,6 +1316,68 @@ def ComplexImagPtrOp : CIR_Op<"complex.imag_ptr", [Pure]> {
12641316
let hasVerifier = 1;
12651317
}
12661318

1319+
//===----------------------------------------------------------------------===//
1320+
// ComplexBinOp
1321+
//===----------------------------------------------------------------------===//
1322+
1323+
def ComplexBinOpKind : I32EnumAttr<
1324+
"ComplexBinOpKind",
1325+
"complex number binary operation kind",
1326+
[BinOpKind_Mul, BinOpKind_Div]> {
1327+
let cppNamespace = "::mlir::cir";
1328+
}
1329+
1330+
def ComplexRangeKind_Full : I32EnumAttrCase<"Full", 1, "full">;
1331+
def ComplexRangeKind_Improved : I32EnumAttrCase<"Improved", 2, "improved">;
1332+
def ComplexRangeKind_Promoted : I32EnumAttrCase<"Promoted", 3, "promoted">;
1333+
def ComplexRangeKind_Basic : I32EnumAttrCase<"Basic", 4, "basic">;
1334+
def ComplexRangeKind_None : I32EnumAttrCase<"None", 5, "none">;
1335+
1336+
def ComplexRangeKind : I32EnumAttr<
1337+
"ComplexRangeKind",
1338+
"complex multiplication and division implementation",
1339+
[ComplexRangeKind_Full, ComplexRangeKind_Improved,
1340+
ComplexRangeKind_Promoted, ComplexRangeKind_Basic,
1341+
ComplexRangeKind_None]> {
1342+
let cppNamespace = "::mlir::cir";
1343+
}
1344+
1345+
def ComplexBinOp : CIR_Op<"complex.binop",
1346+
[Pure, SameTypeOperands, SameOperandsAndResultType]> {
1347+
let summary = "Binary operations on operands of complex type";
1348+
let description = [{
1349+
The `cir.complex.binop` operation represents a binary operation on operands
1350+
of C complex type (e.g. `float _Complex`). The operation can only represent
1351+
binary multiplication or division on complex numbers; other binary
1352+
operations, such as addition and subtraction, are represented by the
1353+
`cir.binop` operation.
1354+
1355+
The operation requires two input operands and has one result. The types of
1356+
all the operands and the result should be of the same `!cir.complex` type.
1357+
1358+
The operation also takes a `range` attribute that specifies the complex
1359+
range of the binary operation.
1360+
1361+
Examples:
1362+
1363+
```mlir
1364+
%2 = cir.complex.binop add %0, %1 : !cir.complex<!cir.float>
1365+
%2 = cir.complex.binop mul %0, %1 : !cir.complex<!cir.float>
1366+
```
1367+
}];
1368+
1369+
let results = (outs CIR_ComplexType:$result);
1370+
let arguments = (ins Arg<ComplexBinOpKind, "operation kind">:$kind,
1371+
CIR_ComplexType:$lhs, CIR_ComplexType:$rhs,
1372+
Arg<ComplexRangeKind, "complex range kind">:$range,
1373+
UnitAttr:$promoted);
1374+
1375+
let assemblyFormat = [{
1376+
$kind $lhs `,` $rhs `range` `(` $range `)` (`promoted` $promoted^)?
1377+
`:` qualified(type($lhs)) attr-dict
1378+
}];
1379+
}
1380+
12671381
//===----------------------------------------------------------------------===//
12681382
// BitsOp
12691383
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -758,13 +758,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
758758
return create<mlir::cir::GetMemberOp>(loc, result, base, name, index);
759759
}
760760

761-
mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real,
762-
mlir::Value imag) {
763-
auto resultComplexTy =
764-
mlir::cir::ComplexType::get(getContext(), real.getType());
765-
return create<mlir::cir::ComplexCreateOp>(loc, resultComplexTy, real, imag);
766-
}
767-
768761
/// Create a cir.complex.real_ptr operation that derives a pointer to the real
769762
/// part of the complex value pointed to by the specified pointer value.
770763
mlir::Value createRealPtr(mlir::Location loc, mlir::Value value) {

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,8 +2512,10 @@ LValue CIRGenFunction::buildLValue(const Expr *E) {
25122512
QualType Ty = E->getType();
25132513
if (const AtomicType *AT = Ty->getAs<AtomicType>())
25142514
assert(0 && "not yet implemented");
2515-
assert(!Ty->isAnyComplexType() && "complex types not implemented");
2516-
return buildCompoundAssignmentLValue(cast<CompoundAssignOperator>(E));
2515+
if (!Ty->isAnyComplexType())
2516+
return buildCompoundAssignmentLValue(cast<CompoundAssignOperator>(E));
2517+
return buildComplexCompoundAssignmentLValue(
2518+
cast<CompoundAssignOperator>(E));
25172519
}
25182520
case Expr::CallExprClass:
25192521
case Expr::CXXMemberCallExprClass:

0 commit comments

Comments
 (0)