mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-29 14:40:40 +03:00
508 lines
15 KiB
C++
508 lines
15 KiB
C++
//===-- complex.cpp -------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/complex.h"
|
||
|
||
#include "dmd/declaration.h"
|
||
#include "dmd/errors.h"
|
||
#include "dmd/mtype.h"
|
||
#include "gen/binops.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/tollvm.h"
|
||
|
||
using namespace dmd;
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
llvm::StructType *DtoComplexType(Type *type) {
|
||
Type *t = type->toBasetype();
|
||
LLType *base = DtoComplexBaseType(t);
|
||
LLType *types[] = {base, base};
|
||
return llvm::StructType::get(gIR->context(), types, false);
|
||
}
|
||
|
||
LLType *DtoComplexBaseType(Type *t) {
|
||
switch (t->toBasetype()->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
return DtoType(Type::tfloat32);
|
||
case TY::Tcomplex64:
|
||
return DtoType(Type::tfloat64);
|
||
case TY::Tcomplex80:
|
||
return DtoType(Type::tfloat80);
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLConstant *DtoConstComplex(Type *_ty, real_t re, real_t im) {
|
||
Type *base = nullptr;
|
||
switch (_ty->toBasetype()->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
base = Type::tfloat32;
|
||
break;
|
||
case TY::Tcomplex64:
|
||
base = Type::tfloat64;
|
||
break;
|
||
case TY::Tcomplex80:
|
||
base = Type::tfloat80;
|
||
break;
|
||
}
|
||
|
||
LLConstant *inits[] = {DtoConstFP(base, re), DtoConstFP(base, im)};
|
||
|
||
return llvm::ConstantStruct::get(DtoComplexType(_ty),
|
||
llvm::ArrayRef<LLConstant *>(inits));
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue *DtoComplex(const Loc &loc, Type *to, DValue *val) {
|
||
LLType *complexTy = DtoType(to);
|
||
|
||
Type *baserety;
|
||
Type *baseimty;
|
||
switch (to->toBasetype()->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
baserety = Type::tfloat32;
|
||
baseimty = Type::timaginary32;
|
||
break;
|
||
case TY::Tcomplex64:
|
||
baserety = Type::tfloat64;
|
||
baseimty = Type::timaginary64;
|
||
break;
|
||
case TY::Tcomplex80:
|
||
baserety = Type::tfloat80;
|
||
baseimty = Type::timaginary80;
|
||
break;
|
||
}
|
||
|
||
LLValue *re, *im;
|
||
DtoGetComplexParts(loc, to, val, re, im);
|
||
|
||
if (!re) {
|
||
re = LLConstant::getNullValue(DtoType(baserety));
|
||
}
|
||
if (!im) {
|
||
im = LLConstant::getNullValue(DtoType(baseimty));
|
||
}
|
||
|
||
LLValue *res = DtoAggrPair(complexTy, re, im);
|
||
|
||
return new DImValue(to, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoComplexSet(LLType* ty, LLValue *c, LLValue *re, LLValue *im) {
|
||
DtoStore(re, DtoGEP(ty, c, 0u, 0));
|
||
DtoStore(im, DtoGEP(ty, c, 0, 1));
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoGetComplexParts(const Loc &loc, Type *to, DValue *val, DValue *&re,
|
||
DValue *&im) {
|
||
Type *baserety;
|
||
Type *baseimty;
|
||
switch (to->toBasetype()->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
baserety = Type::tfloat32;
|
||
baseimty = Type::timaginary32;
|
||
break;
|
||
case TY::Tcomplex64:
|
||
baserety = Type::tfloat64;
|
||
baseimty = Type::timaginary64;
|
||
break;
|
||
case TY::Tcomplex80:
|
||
baserety = Type::tfloat80;
|
||
baseimty = Type::timaginary80;
|
||
break;
|
||
}
|
||
|
||
Type *t = val->type->toBasetype();
|
||
|
||
if (t->iscomplex()) {
|
||
DValue *v = DtoCastComplex(loc, val, to);
|
||
if (to->iscomplex()) {
|
||
if (v->isLVal()) {
|
||
LLValue *reVal = DtoGEP(DtoType(v->type), DtoLVal(v), 0u, 0, ".re_part");
|
||
LLValue *imVal = DtoGEP(DtoType(v->type), DtoLVal(v), 0, 1, ".im_part");
|
||
re = new DLValue(baserety, reVal);
|
||
im = new DLValue(baseimty, imVal);
|
||
} else {
|
||
LLValue *reVal =
|
||
gIR->ir->CreateExtractValue(DtoRVal(v), 0, ".re_part");
|
||
LLValue *imVal =
|
||
gIR->ir->CreateExtractValue(DtoRVal(v), 1, ".im_part");
|
||
re = new DImValue(baserety, reVal);
|
||
im = new DImValue(baseimty, imVal);
|
||
}
|
||
} else {
|
||
DtoGetComplexParts(loc, to, v, re, im);
|
||
}
|
||
} else if (t->isimaginary()) {
|
||
re = nullptr;
|
||
im = DtoCastFloat(loc, val, baseimty);
|
||
} else if (t->isfloating()) {
|
||
re = DtoCastFloat(loc, val, baserety);
|
||
im = nullptr;
|
||
} else if (t->isintegral()) {
|
||
re = DtoCastInt(loc, val, baserety);
|
||
im = nullptr;
|
||
} else {
|
||
llvm_unreachable("Unexpected numeric type.");
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoGetComplexParts(const Loc &loc, Type *to, DValue *val, LLValue *&re,
|
||
LLValue *&im) {
|
||
DValue *dre, *dim;
|
||
DtoGetComplexParts(loc, to, val, dre, dim);
|
||
re = dre ? DtoRVal(dre) : nullptr;
|
||
im = dim ? DtoRVal(dim) : nullptr;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexAdd(const Loc &loc, Type *type, DRValue *lhs,
|
||
DRValue *rhs) {
|
||
llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im);
|
||
|
||
// add up
|
||
if (lhs_re && rhs_re) {
|
||
res_re = gIR->ir->CreateFAdd(lhs_re, rhs_re);
|
||
} else if (lhs_re) {
|
||
res_re = lhs_re;
|
||
} else { // either rhs_re or no re at all (then use any)
|
||
res_re = rhs_re;
|
||
}
|
||
|
||
if (lhs_im && rhs_im) {
|
||
res_im = gIR->ir->CreateFAdd(lhs_im, rhs_im);
|
||
} else if (lhs_im) {
|
||
res_im = lhs_im;
|
||
} else { // either rhs_im or no im at all (then use any)
|
||
res_im = rhs_im;
|
||
}
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexMin(const Loc &loc, Type *type, DRValue *lhs,
|
||
DRValue *rhs) {
|
||
llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im);
|
||
|
||
// add up
|
||
if (lhs_re && rhs_re) {
|
||
res_re = gIR->ir->CreateFSub(lhs_re, rhs_re);
|
||
} else if (lhs_re) {
|
||
res_re = lhs_re;
|
||
} else { // either rhs_re or no re at all (then use any)
|
||
res_re = gIR->ir->CreateFNeg(rhs_re, "neg");
|
||
}
|
||
|
||
if (lhs_im && rhs_im) {
|
||
res_im = gIR->ir->CreateFSub(lhs_im, rhs_im);
|
||
} else if (lhs_im) {
|
||
res_im = lhs_im;
|
||
} else { // either rhs_im or no im at all (then use any)
|
||
res_im = gIR->ir->CreateFNeg(rhs_im, "neg");
|
||
}
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexMul(const Loc &loc, Type *type, DRValue *lhs,
|
||
DRValue *rhs) {
|
||
llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im);
|
||
|
||
// mul up
|
||
llvm::Value *rere = nullptr;
|
||
llvm::Value *reim = nullptr;
|
||
llvm::Value *imre = nullptr;
|
||
llvm::Value *imim = nullptr;
|
||
|
||
if (lhs_re && rhs_re) {
|
||
rere = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere_mul");
|
||
}
|
||
if (lhs_re && rhs_im) {
|
||
reim = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim_mul");
|
||
}
|
||
if (lhs_im && rhs_re) {
|
||
imre = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre_mul");
|
||
}
|
||
if (lhs_im && rhs_im) {
|
||
imim = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim_mul");
|
||
}
|
||
|
||
if (rere && imim) {
|
||
res_re = gIR->ir->CreateFSub(rere, imim, "rere_imim_sub");
|
||
} else if (rere) {
|
||
res_re = rere;
|
||
} else if (imim) {
|
||
res_re = gIR->ir->CreateFNeg(imim, "imim_neg");
|
||
} else {
|
||
res_re = lhs_re ? rhs_re : lhs_re; // null!
|
||
}
|
||
|
||
if (reim && imre) {
|
||
res_im = gIR->ir->CreateFAdd(reim, imre, "reim_imre_add");
|
||
} else if (reim) {
|
||
res_im = reim;
|
||
} else if (imre) {
|
||
res_im = imre;
|
||
} else {
|
||
res_im = lhs_re ? rhs_im : lhs_re; // null!
|
||
}
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexDiv(const Loc &loc, Type *type, DRValue *lhs,
|
||
DRValue *rhs) {
|
||
llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im);
|
||
|
||
// if divisor is only real, division is simple
|
||
if (rhs_re && !rhs_im) {
|
||
if (lhs_re) {
|
||
res_re = gIR->ir->CreateFDiv(lhs_re, rhs_re, "re_divby_re");
|
||
} else {
|
||
res_re = lhs_re;
|
||
}
|
||
if (lhs_im) {
|
||
res_im = gIR->ir->CreateFDiv(lhs_im, rhs_re, "im_divby_re");
|
||
} else {
|
||
res_im = lhs_im;
|
||
}
|
||
}
|
||
// if divisor is only imaginary, division is simple too
|
||
else if (!rhs_re && rhs_im) {
|
||
if (lhs_re) {
|
||
res_im = gIR->ir->CreateFNeg(
|
||
gIR->ir->CreateFDiv(lhs_re, rhs_im, "re_divby_im"), "neg");
|
||
} else {
|
||
res_im = lhs_re;
|
||
}
|
||
if (lhs_im) {
|
||
res_re = gIR->ir->CreateFDiv(lhs_im, rhs_im, "im_divby_im");
|
||
} else {
|
||
res_re = lhs_im;
|
||
}
|
||
}
|
||
// full division
|
||
else {
|
||
llvm::Value *tmp1, *tmp2, *denom;
|
||
|
||
if (lhs_re && lhs_im) {
|
||
tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere");
|
||
tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim");
|
||
res_re = gIR->ir->CreateFAdd(tmp1, tmp2, "rere_plus_imim");
|
||
|
||
tmp1 = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim");
|
||
tmp2 = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre");
|
||
res_im = gIR->ir->CreateFSub(tmp2, tmp1, "imre_sub_reim");
|
||
} else if (lhs_re) {
|
||
res_re = gIR->ir->CreateFMul(lhs_re, rhs_re, "rere");
|
||
|
||
res_im = gIR->ir->CreateFMul(lhs_re, rhs_im, "reim");
|
||
res_im = gIR->ir->CreateFNeg(res_im);
|
||
} else if (lhs_im) {
|
||
res_re = gIR->ir->CreateFMul(lhs_im, rhs_im, "imim");
|
||
res_im = gIR->ir->CreateFMul(lhs_im, rhs_re, "imre");
|
||
} else {
|
||
llvm_unreachable("lhs has neither real nor imaginary part");
|
||
}
|
||
|
||
tmp1 = gIR->ir->CreateFMul(rhs_re, rhs_re, "rhs_resq");
|
||
tmp2 = gIR->ir->CreateFMul(rhs_im, rhs_im, "rhs_imsq");
|
||
denom = gIR->ir->CreateFAdd(tmp1, tmp2, "denom");
|
||
|
||
res_re = gIR->ir->CreateFDiv(res_re, denom, "res_re");
|
||
res_im = gIR->ir->CreateFDiv(res_im, denom, "res_im");
|
||
}
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexMod(const Loc &loc, Type *type, DRValue *lhs,
|
||
DRValue *rhs) {
|
||
llvm::Value *lhs_re, *lhs_im, *rhs_re, *rhs_im, *res_re, *res_im, *divisor;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, type, rhs, rhs_re, rhs_im);
|
||
|
||
// Divisor can be real or imaginary but not complex
|
||
assert((rhs_re != 0) ^ (rhs_im != 0));
|
||
|
||
divisor = rhs_re ? rhs_re : rhs_im;
|
||
res_re = lhs_re ? gIR->ir->CreateFRem(lhs_re, divisor) : lhs_re;
|
||
res_im = lhs_re ? gIR->ir->CreateFRem(lhs_im, divisor) : lhs_im;
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), res_re, res_im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DImValue *DtoComplexNeg(const Loc &loc, Type *type, DRValue *val) {
|
||
llvm::Value *a, *b, *re, *im;
|
||
|
||
// values
|
||
DtoGetComplexParts(loc, type, val, a, b);
|
||
|
||
// neg up
|
||
assert(a && b);
|
||
re = gIR->ir->CreateFNeg(a);
|
||
im = gIR->ir->CreateFNeg(b);
|
||
|
||
LLValue *res = DtoAggrPair(DtoType(type), re, im);
|
||
return new DImValue(type, res);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue *DtoComplexEquals(const Loc &loc, EXP op, DValue *lhs, DValue *rhs) {
|
||
DValue *lhs_re, *lhs_im, *rhs_re, *rhs_im;
|
||
|
||
// lhs values
|
||
DtoGetComplexParts(loc, lhs->type, lhs, lhs_re, lhs_im);
|
||
// rhs values
|
||
DtoGetComplexParts(loc, lhs->type, rhs, rhs_re, rhs_im);
|
||
|
||
// (l.re==r.re && l.im==r.im) or (l.re!=r.re || l.im!=r.im)
|
||
LLValue *b1 = DtoBinFloatsEquals(loc, lhs_re, rhs_re, op);
|
||
LLValue *b2 = DtoBinFloatsEquals(loc, lhs_im, rhs_im, op);
|
||
|
||
return (op == EXP::equal || op == EXP::identity) ? gIR->ir->CreateAnd(b1, b2)
|
||
: gIR->ir->CreateOr(b1, b2);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue *DtoCastComplex(const Loc &loc, DValue *val, Type *_to) {
|
||
Type *to = _to->toBasetype();
|
||
Type *vty = val->type->toBasetype();
|
||
if (to->iscomplex()) {
|
||
if (size(vty) == size(to)) {
|
||
return val;
|
||
}
|
||
|
||
llvm::Value *re, *im;
|
||
DtoGetComplexParts(loc, val->type, val, re, im);
|
||
LLType *toty = DtoComplexBaseType(to);
|
||
|
||
if (size(to) < size(vty)) {
|
||
re = gIR->ir->CreateFPTrunc(re, toty);
|
||
im = gIR->ir->CreateFPTrunc(im, toty);
|
||
} else {
|
||
re = gIR->ir->CreateFPExt(re, toty);
|
||
im = gIR->ir->CreateFPExt(im, toty);
|
||
}
|
||
|
||
LLValue *pair = DtoAggrPair(DtoType(_to), re, im);
|
||
return new DImValue(_to, pair);
|
||
}
|
||
if (to->isimaginary()) {
|
||
// FIXME: this loads both values, even when we only need one
|
||
LLValue *v = DtoRVal(val);
|
||
LLValue *impart = gIR->ir->CreateExtractValue(v, 1, ".im_part");
|
||
Type *extractty;
|
||
switch (vty->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
extractty = Type::timaginary32;
|
||
break;
|
||
case TY::Tcomplex64:
|
||
extractty = Type::timaginary64;
|
||
break;
|
||
case TY::Tcomplex80:
|
||
extractty = Type::timaginary80;
|
||
break;
|
||
}
|
||
auto im = new DImValue(extractty, impart);
|
||
return DtoCastFloat(loc, im, to);
|
||
}
|
||
if (to->ty == TY::Tbool) {
|
||
return new DImValue(
|
||
_to, DtoComplexEquals(loc, EXP::notEqual, val, DtoNullValue(vty)));
|
||
}
|
||
if (to->isfloating() || to->isintegral()) {
|
||
// FIXME: this loads both values, even when we only need one
|
||
LLValue *v = DtoRVal(val);
|
||
LLValue *repart = gIR->ir->CreateExtractValue(v, 0, ".re_part");
|
||
Type *extractty;
|
||
switch (vty->ty) {
|
||
default:
|
||
llvm_unreachable("Unexpected complex floating point type");
|
||
case TY::Tcomplex32:
|
||
extractty = Type::tfloat32;
|
||
break;
|
||
case TY::Tcomplex64:
|
||
extractty = Type::tfloat64;
|
||
break;
|
||
case TY::Tcomplex80:
|
||
extractty = Type::tfloat80;
|
||
break;
|
||
}
|
||
auto re = new DImValue(extractty, repart);
|
||
return DtoCastFloat(loc, re, to);
|
||
}
|
||
error(loc, "Don't know how to cast `%s` to `%s`", vty->toChars(),
|
||
to->toChars());
|
||
fatal();
|
||
}
|