Skip to content

Commit fc0a132

Browse files
authored
Add missing complex ops at compile-time (* and /) (#29081)
Adds the missing `*` and `/` at compile-time for params I originally started to implement `**` for param's per #28898, but then quickly found that `*` and `/` were missing. This seems to have been left as future work from #12386. I also noticed that several other operations were missing, which I noted in #29051 - [x] paratest [Reviewed by @benharsh]
2 parents d1ddd6c + 0cec9ca commit fc0a132

14 files changed

Lines changed: 334 additions & 5 deletions

File tree

compiler/AST/checkAST.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ void checkPrimitives()
230230
case PRIM_CHECK_NIL:
231231
case PRIM_GET_REAL: // get complex real component
232232
case PRIM_GET_IMAG: // get complex imag component
233+
case PRIM_BUILD_COMPLEX: // build complex from real and imag
233234
case PRIM_QUERY: // query expression primitive
234235
case PRIM_LOCAL_CHECK: // assert that a wide ref is on this locale
235236
case PRIM_GET_END_COUNT:

compiler/AST/primitive.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ returnInfoComplexField(CallExpr* call) { // for get real/imag primitives
153153
return QualifiedType(dtUnknown);
154154
}
155155

156+
static QualifiedType
157+
returnInfoComplex(CallExpr* call) {
158+
Type *t = call->get(1)->getValType();
159+
if (t == dtReal[FLOAT_SIZE_32] || t == dtImag[FLOAT_SIZE_32]) {
160+
return QualifiedType(dtComplex[COMPLEX_SIZE_64], QUAL_VAL);
161+
} else if (t == dtReal[FLOAT_SIZE_64] || t == dtImag[FLOAT_SIZE_64]) {
162+
return QualifiedType(dtComplex[COMPLEX_SIZE_128], QUAL_VAL);
163+
} else {
164+
INT_FATAL( call, "unsupported complex size");
165+
}
166+
return QualifiedType(dtUnknown);
167+
}
168+
169+
156170
static QualifiedType
157171
returnInfoAbs(CallExpr* call) {
158172
Type *t = call->get(1)->getValType();
@@ -913,6 +927,7 @@ initPrimitive() {
913927
prim_def(PRIM_GET_REAL, "complex_get_real", returnInfoComplexField);
914928
// given a complex value, produce a reference to the imag component
915929
prim_def(PRIM_GET_IMAG, "complex_get_imag", returnInfoComplexField);
930+
prim_def(PRIM_BUILD_COMPLEX, "build_complex", returnInfoComplex);
916931
// query expression primitive
917932
prim_def(PRIM_QUERY, "query", returnInfoUnknown);
918933
prim_def(PRIM_QUERY_PARAM_FIELD, "query param field", returnInfoGetMemberRef);

compiler/resolution/postFold.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,9 @@ static Expr* postFoldPrimop(CallExpr* call) {
653653
} else if (call->isPrimitive(PRIM_GET_IMAG) == true) {
654654
FOLD_CALL1(P_prim_get_imag);
655655

656+
} else if (call->isPrimitive(PRIM_BUILD_COMPLEX) == true) {
657+
FOLD_CALL2(P_prim_build_complex);
658+
656659
} else if (call->isPrimitive(PRIM_ADD) == true) {
657660
FOLD_CALL2(P_prim_add);
658661

frontend/include/chpl/uast/prim-ops-list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ PRIMITIVE_R(NEW_WITH_ALLOCATOR, "new with allocator")
136136

137137
PRIMITIVE_G(GET_REAL, "complex_get_real")
138138
PRIMITIVE_G(GET_IMAG, "complex_get_imag")
139+
PRIMITIVE_R(BUILD_COMPLEX, "build_complex")
139140

140141
PRIMITIVE_R(QUERY, "query")
141142
PRIMITIVE_R(QUERY_PARAM_FIELD, "query param field")

frontend/lib/immediates/complex-support.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,42 @@ struct complex128 complexSqrt128(struct complex128 x) {
7777
ret.i = cimag(n);
7878
return ret;
7979
}
80+
81+
struct complex64 complexMul64(struct complex64 c1, struct complex64 c2) {
82+
float complex n1 = makeFloatComplex(c1.r, c1.i);
83+
float complex n2 = makeFloatComplex(c2.r, c2.i);
84+
float complex n = n1 * n2;
85+
struct complex64 ret;
86+
ret.r = crealf(n);
87+
ret.i = cimagf(n);
88+
return ret;
89+
}
90+
91+
struct complex128 complexMul128(struct complex128 c1, struct complex128 c2) {
92+
double complex n1 = makeDoubleComplex(c1.r, c1.i);
93+
double complex n2 = makeDoubleComplex(c2.r, c2.i);
94+
double complex n = n1 * n2;
95+
struct complex128 ret;
96+
ret.r = creal(n);
97+
ret.i = cimag(n);
98+
return ret;
99+
}
100+
101+
struct complex64 complexDiv64(struct complex64 c1, struct complex64 c2) {
102+
float complex n1 = makeFloatComplex(c1.r, c1.i);
103+
float complex n2 = makeFloatComplex(c2.r, c2.i);
104+
float complex n = n1 / n2;
105+
struct complex64 ret;
106+
ret.r = crealf(n);
107+
ret.i = cimagf(n);
108+
return ret;
109+
}
110+
struct complex128 complexDiv128(struct complex128 c1, struct complex128 c2) {
111+
double complex n1 = makeDoubleComplex(c1.r, c1.i);
112+
double complex n2 = makeDoubleComplex(c2.r, c2.i);
113+
double complex n = n1 / n2;
114+
struct complex128 ret;
115+
ret.r = creal(n);
116+
ret.i = cimag(n);
117+
return ret;
118+
}

frontend/lib/immediates/complex-support.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
This header exists to work around a problem with complex square root being
2626
inaccurate on libc++ by using C functions to do the complex square root.
2727
28+
This is also used by complex multiplication and division to avoid the same problem.
29+
2830
*/
2931

3032
struct complex64 {
@@ -39,4 +41,10 @@ struct complex128 {
3941
struct complex64 complexSqrt64(struct complex64 x);
4042
struct complex128 complexSqrt128(struct complex128 x);
4143

44+
struct complex64 complexMul64(struct complex64 c1, struct complex64 c2);
45+
struct complex128 complexMul128(struct complex128 c1, struct complex128 c2);
46+
47+
struct complex64 complexDiv64(struct complex64 c1, struct complex64 c2);
48+
struct complex128 complexDiv128(struct complex128 c1, struct complex128 c2);
49+
4250
#endif

frontend/lib/immediates/num.cpp

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,48 @@ coerce_immediate(chpl::Context* context, Immediate *from, Immediate *to) {
502502
break; \
503503
}
504504

505+
#define DO_FOLDDIV() \
506+
switch (imm->const_kind) { \
507+
case NUM_KIND_COMPLEX: \
508+
switch (imm->num_index) { \
509+
case COMPLEX_SIZE_64: \
510+
{ \
511+
imm->v_complex64 = complexDiv64(im1.v_complex64, im2.v_complex64); \
512+
break; \
513+
} \
514+
case COMPLEX_SIZE_128: \
515+
{ \
516+
imm->v_complex128 = complexDiv128(im1.v_complex128, im2.v_complex128); \
517+
break; \
518+
} \
519+
} \
520+
break; \
521+
default: \
522+
DO_FOLD(/, false, false); \
523+
break; \
524+
}
525+
526+
#define DO_FOLDMUL() \
527+
switch (imm->const_kind) { \
528+
case NUM_KIND_COMPLEX: \
529+
switch (imm->num_index) { \
530+
case COMPLEX_SIZE_64: \
531+
{ \
532+
imm->v_complex64 = complexMul64(im1.v_complex64, im2.v_complex64); \
533+
break; \
534+
} \
535+
case COMPLEX_SIZE_128: \
536+
{ \
537+
imm->v_complex128 = complexMul128(im1.v_complex128, im2.v_complex128); \
538+
break; \
539+
} \
540+
} \
541+
break; \
542+
default: \
543+
DO_FOLD(*, false, false); \
544+
break; \
545+
}
546+
505547
static void doFoldSqrt(chpl::Context* context,
506548
Immediate &im1, /* input */
507549
Immediate *imm /* output */) {
@@ -664,6 +706,27 @@ static void doFoldGetImag(chpl::Context* context,
664706
}
665707
}
666708

709+
static void doFoldBuildComplex(chpl::Context* context,
710+
Immediate &im1, /* input */
711+
Immediate &im2, /* input */
712+
Immediate *imm /* output */) {
713+
switch (imm->const_kind) {
714+
case NUM_KIND_COMPLEX:
715+
switch (imm->num_index) {
716+
case COMPLEX_SIZE_64:
717+
imm->v_complex64.r = im1.v_float32;
718+
imm->v_complex64.i = im2.v_float32;
719+
break;
720+
case COMPLEX_SIZE_128:
721+
imm->v_complex128.r = im1.v_float64;
722+
imm->v_complex128.i = im2.v_float64;
723+
break;
724+
default: CHPL_ASSERT(false && "Unhandled case in switch statement");
725+
}
726+
break;
727+
}
728+
}
729+
667730

668731

669732

@@ -1084,6 +1147,20 @@ fold_constant(chpl::Context* context, int op,
10841147
imm->num_index = im1.num_index;
10851148
}
10861149
break;
1150+
case P_prim_build_complex:
1151+
if ((im1.const_kind == NUM_KIND_REAL || im1.const_kind == NUM_KIND_IMAG) &&
1152+
(im2.const_kind == NUM_KIND_REAL || im2.const_kind == NUM_KIND_IMAG)) {
1153+
imm->const_kind = NUM_KIND_COMPLEX;
1154+
if (im1.num_index == FLOAT_SIZE_32 && im2.num_index == FLOAT_SIZE_32)
1155+
imm->num_index = COMPLEX_SIZE_64;
1156+
else if (im1.num_index == FLOAT_SIZE_64 && im2.num_index == FLOAT_SIZE_64)
1157+
imm->num_index = COMPLEX_SIZE_128;
1158+
else
1159+
CHPL_ASSERT(false && "build_complex numeric kind not supported");
1160+
} else {
1161+
CHPL_ASSERT(false && "build_complex numeric kind not supported");
1162+
}
1163+
break;
10871164
}
10881165
if (coerce.const_kind) {
10891166
coerce_immediate(context, &im1, &coerce);
@@ -1095,8 +1172,8 @@ fold_constant(chpl::Context* context, int op,
10951172
}
10961173
switch (op) {
10971174
default: CHPL_ASSERT(false && "fold constant op not supported"); break;
1098-
case P_prim_mult: DO_FOLD(*, false, false); break;
1099-
case P_prim_div: DO_FOLD(/, false, false); break;
1175+
case P_prim_mult: DO_FOLDMUL(); break;
1176+
case P_prim_div: DO_FOLDDIV(); break;
11001177
case P_prim_mod: DO_FOLDI(%); break;
11011178
case P_prim_add: DO_FOLD(+, true, false); break;
11021179
case P_prim_subtract: DO_FOLD(-, false, true); break;
@@ -1137,6 +1214,10 @@ fold_constant(chpl::Context* context, int op,
11371214
doFoldGetReal(context, im1, imm);
11381215
break;
11391216
}
1217+
case P_prim_build_complex: {
1218+
doFoldBuildComplex(context, im1, im2, imm);
1219+
break;
1220+
}
11401221
}
11411222
}
11421223

frontend/lib/immediates/prim_data.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@
5151
#define P_prim_sqrt chpl::uast::primtags::PRIM_SQRT
5252
#define P_prim_get_imag chpl::uast::primtags::PRIM_GET_IMAG
5353
#define P_prim_get_real chpl::uast::primtags::PRIM_GET_REAL
54+
#define P_prim_build_complex chpl::uast::primtags::PRIM_BUILD_COMPLEX
5455

5556
#endif

frontend/lib/resolution/prims.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,26 @@ primComplexGetComponent(Context* context, const CallInfo& ci) {
10731073
return ret;
10741074
}
10751075

1076+
/* for complex primitives */
1077+
static QualifiedType
1078+
primBuildComplex(Context* context, const CallInfo& ci) {
1079+
QualifiedType ret = QualifiedType();
1080+
1081+
if (ci.numActuals() != 2) return ret;
1082+
1083+
auto actual0R = ci.actual(0).type().type()->toRealType();
1084+
auto actual1R = ci.actual(1).type().type()->toRealType();
1085+
auto actual0I = ci.actual(0).type().type()->toImagType();
1086+
auto actual1I = ci.actual(1).type().type()->toImagType();
1087+
auto actual0BW = actual0R ? actual0R->bitwidth() : (actual0I ? actual0I->bitwidth() : 0);
1088+
auto actual1BW = actual1R ? actual1R->bitwidth() : (actual1I ? actual1I->bitwidth() : 0);
1089+
if (actual0BW != 0 && actual1BW != 0 && actual0BW == actual1BW) {
1090+
int BW = actual0BW * 2;
1091+
ret = QualifiedType(QualifiedType::REF, ComplexType::get(context, BW));
1092+
}
1093+
return ret;
1094+
}
1095+
10761096
/* for abs */
10771097
static QualifiedType
10781098
primAbsGetType(Context* context, const CallInfo& ci) {
@@ -1926,6 +1946,10 @@ CallResolutionResult resolvePrimCall(ResolutionContext* rc,
19261946
// TODO: get the real/imag component from a param complex
19271947
type = primComplexGetComponent(context, ci);
19281948
break;
1949+
/* primitives to build complex numbers */
1950+
case PRIM_BUILD_COMPLEX:
1951+
type = primBuildComplex(context, ci);
1952+
break;
19291953
/* other math primitives */
19301954
case PRIM_ABS:
19311955
type = primAbsGetType(context, ci);

modules/internal/ChapelBase.chpl

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -736,12 +736,24 @@ module ChapelBase {
736736
inline operator *(param a: imag(32), param b: imag(32)) param do return __primitive("*", -a, b) : real(32);
737737
inline operator *(param a: imag(64), param b: imag(64)) param do return __primitive("*", -a, b) : real(64);
738738

739+
inline operator *(param a: complex(64), param b: complex(64)) param do return __primitive("*", a, b);
740+
inline operator *(param a: complex(128), param b: complex(128)) param do return __primitive("*", a, b);
741+
739742
inline operator *(param a: real(32), param b: imag(32)) param do return __primitive("*", a, b : real(32)) : imag(32);
740743
inline operator *(param a: real(64), param b: imag(64)) param do return __primitive("*", a, b : real(64)) : imag(64);
741-
742744
inline operator *(param a: imag(32), param b: real(32)) param do return __primitive("*", a : real(32), b) : imag(32);
743745
inline operator *(param a: imag(64), param b: real(64)) param do return __primitive("*", a : real(64), b) : imag(64);
744746

747+
inline operator *(param a: real(32), param b: complex(64)) param do return __primitive("build_complex", a*b.re, a*b.im) : complex(64);
748+
inline operator *(param a: complex(64), param b: real(32)) param do return __primitive("build_complex", a.re*b, a.im*b) : complex(64);
749+
inline operator *(param a: real(64), param b: complex(128)) param do return __primitive("build_complex", a*b.re, a*b.im) : complex(128);
750+
inline operator *(param a: complex(128), param b: real(64)) param do return __primitive("build_complex", a.re*b, a.im*b) : complex(128);
751+
752+
inline operator *(param a: imag(32), param b: complex(64)) param do return __primitive("build_complex", -_i2r(a)*b.im, _i2r(a)*b.re) : complex(64);
753+
inline operator *(param a: complex(64), param b: imag(32)) param do return __primitive("build_complex", -a.im*_i2r(b), a.re*_i2r(b)) : complex(64);
754+
inline operator *(param a: imag(64), param b: complex(128)) param do return __primitive("build_complex", -_i2r(a)*b.im, _i2r(a)*b.re) : complex(128);
755+
inline operator *(param a: complex(128), param b: imag(64)) param do return __primitive("build_complex", -a.im*_i2r(b), a.re*_i2r(b)) : complex(128);
756+
745757
inline operator /(param a: int(8), param b: int(8)) param {
746758
if b == 0 then compilerError("Attempt to divide by zero");
747759
return __primitive("/", a, b);
@@ -782,12 +794,36 @@ module ChapelBase {
782794
inline operator /(param a: imag(32), param b: imag(32)) param do return __primitive("/", a, b) : real(32);
783795
inline operator /(param a: imag(64), param b: imag(64)) param do return __primitive("/", a, b) : real(64);
784796

797+
inline operator /(param a: complex(64), param b: complex(64)) param do return __primitive("/", a, b);
798+
inline operator /(param a: complex(128), param b: complex(128)) param do return __primitive("/", a, b);
799+
800+
785801
inline operator /(param a: real(32), param b: imag(32)) param do return __primitive("/", -a, b : real(32)) : imag(32);
786802
inline operator /(param a: real(64), param b: imag(64)) param do return __primitive("/", -a, b : real(64)) : imag(64);
787-
788803
inline operator /(param a: imag(32), param b: real(32)) param do return __primitive("/", a : real(32), b) : imag(32);
789804
inline operator /(param a: imag(64), param b: real(64)) param do return __primitive("/", a : real(64), b) : imag(64);
790805

806+
inline operator /(param a: real(32), param b: complex(64)) param {
807+
param d = abs(b);
808+
return __primitive("build_complex", (a/d)*(b.re/d), (-a/d)*(b.im/d)):complex(64);
809+
}
810+
inline operator /(param a: complex(64), param b: real(32)) param do return __primitive("build_complex", a.re/b, a.im/b) : complex(64);
811+
inline operator /(param a: real(64), param b: complex(128)) param {
812+
param d = abs(b);
813+
return __primitive("build_complex", (a/d)*(b.re/d), (-a/d)*(b.im/d)):complex(128);
814+
}
815+
inline operator /(param a: complex(128), param b: real(64)) param do return __primitive("build_complex", a.re/b, a.im/b) : complex(128);
816+
817+
inline operator /(param a: imag(32), param b: complex(64)) param {
818+
param d = abs(b);
819+
return __primitive("build_complex", (_i2r(a)/d)*(b.im/d), (_i2r(a)/d)*(b.re/d)):complex(64);
820+
}
821+
inline operator /(param a: complex(64), param b: imag(32)) param do return __primitive("build_complex", a.im/_i2r(b), -a.re/_i2r(b)) : complex(64);
822+
inline operator /(param a: imag(64), param b: complex(128)) param {
823+
param d = abs(b);
824+
return __primitive("build_complex", (_i2r(a)/d)*(b.im/d), (_i2r(a)/d)*(b.re/d)):complex(128);
825+
}
826+
inline operator /(param a: complex(128), param b: imag(64)) param do return __primitive("build_complex", a.im/_i2r(b), -a.re/_i2r(b)) : complex(128);
791827

792828
//
793829
// % on primitive types
@@ -1516,6 +1552,8 @@ module ChapelBase {
15161552
//
15171553
inline proc _i2r(a: imag(?w)) do return __primitive("cast", real(w), a);
15181554
inline proc _r2i(a: real(?w)) do return __primitive("cast", imag(w), a);
1555+
inline proc _i2r(param a: imag(?w)) param do return a:real(w);
1556+
inline proc _r2i(param a: real(?w)) param do return a:imag(w);
15191557

15201558
//
15211559
// More primitive funs

0 commit comments

Comments
 (0)