mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-07 19:36:06 +03:00
Limited support for arbitrary target reals
While parsing of floating-point literals and CTFE still operate with the host LDC's real type, compile-time reals can in principle be emitted in arbitrary precision via LLVM software conversion, therefore paving the way for cross-compilation to all targets. The representable constants are still limited by the compile-time real_t precision. E.g., LDC on Windows with its 64-bit reals can't hold and emit an 80-bit `real.max` when cross-compiling to a non-Windows x86(_64) target; the compile-time value will silently overflow to infinity and later be emitted as 80-bit infinity. LDC on AArch64 with its 128-bit quad-precision reals on the other hand can hold and emit reals for all targets, making it a universal cross-compiler with quad-precision compile-time reals in hardware. We don't use the strange 2x64-bit PPC double-double format (see `getRealType()` in `ir/irtype.cpp`), but would more or less support it (the type properties (max, min_normal...) still need to be determined; LLVM isn't sure about those either...).
This commit is contained in:
parent
c051d8d829
commit
0e71a760ae
7 changed files with 115 additions and 60 deletions
|
@ -14,11 +14,17 @@
|
|||
|
||||
// Type used by the front-end for compile-time reals
|
||||
#if IN_LLVM && _MSC_VER
|
||||
// Make sure LDC built with MSVC uses double-precision compile-time reals,
|
||||
// independent of whether it was built with DMD (80-bit reals) or LDC.
|
||||
typedef double real_t;
|
||||
#else
|
||||
typedef longdouble real_t;
|
||||
#endif
|
||||
|
||||
#if IN_LLVM
|
||||
namespace llvm { class APFloat; }
|
||||
#endif
|
||||
|
||||
// Compile-time floating-point helper
|
||||
struct CTFloat
|
||||
{
|
||||
|
@ -42,6 +48,9 @@ struct CTFloat
|
|||
static real_t ceil(real_t x);
|
||||
static real_t trunc(real_t x);
|
||||
static real_t round(real_t x);
|
||||
|
||||
// implemented in gen/ctfloat.cpp
|
||||
static void toAPFloat(real_t src, llvm::APFloat &dst);
|
||||
#endif
|
||||
|
||||
static bool isIdentical(real_t a, real_t b);
|
||||
|
|
|
@ -42,7 +42,7 @@ LLType *DtoComplexBaseType(Type *t) {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LLConstant *DtoConstComplex(Type *_ty, longdouble re, longdouble im) {
|
||||
LLConstant *DtoConstComplex(Type *_ty, real_t re, real_t im) {
|
||||
Type *base = nullptr;
|
||||
switch (_ty->toBasetype()->ty) {
|
||||
default:
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#define LDC_GEN_COMPLEX_H
|
||||
|
||||
#include "tokens.h"
|
||||
#include "longdouble.h"
|
||||
#include "dvalue.h"
|
||||
|
||||
struct Loc;
|
||||
|
@ -30,7 +29,7 @@ class Value;
|
|||
llvm::StructType *DtoComplexType(Type *t);
|
||||
llvm::Type *DtoComplexBaseType(Type *t);
|
||||
|
||||
llvm::Constant *DtoConstComplex(Type *t, longdouble re, longdouble im);
|
||||
llvm::Constant *DtoConstComplex(Type *t, real_t re, real_t im);
|
||||
|
||||
llvm::Constant *DtoComplexShuffleMask(unsigned a, unsigned b);
|
||||
|
||||
|
|
48
gen/ctfloat.cpp
Normal file
48
gen/ctfloat.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//===-- ctfloat.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// LDC – the LLVM D compiler
|
||||
//
|
||||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||||
// file for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ctfloat.h"
|
||||
#include "gen/llvm.h"
|
||||
|
||||
using llvm::APFloat;
|
||||
|
||||
void CTFloat::toAPFloat(const real_t src, APFloat &dst) {
|
||||
if (sizeof(real_t) == 8) {
|
||||
dst = APFloat(static_cast<double>(src));
|
||||
return;
|
||||
}
|
||||
|
||||
assert(sizeof(real_t) > 8 && "real_t < 64 bits?");
|
||||
|
||||
union {
|
||||
real_t fp;
|
||||
uint64_t bits[(sizeof(real_t) + 7) / 8];
|
||||
} u;
|
||||
u.fp = src;
|
||||
|
||||
#if LDC_LLVM_VER >= 400
|
||||
const auto &x87DoubleExtended = APFloat::x87DoubleExtended();
|
||||
const auto &IEEEquad = APFloat::IEEEquad();
|
||||
const auto &PPCDoubleDouble = APFloat::PPCDoubleDouble();
|
||||
#else
|
||||
const auto &x87DoubleExtended = APFloat::x87DoubleExtended;
|
||||
const auto &IEEEquad = APFloat::IEEEquad;
|
||||
const auto &PPCDoubleDouble = APFloat::PPCDoubleDouble;
|
||||
#endif
|
||||
|
||||
#if __i386__ || __x86_64__
|
||||
dst = APFloat(x87DoubleExtended, APInt(80, 2, u.bits));
|
||||
#elif __aarch64__
|
||||
dst = APFloat(IEEEquad, APInt(128, 2, u.bits));
|
||||
#elif __ppc__ || __ppc64__
|
||||
dst = APFloat(PPCDoubleDouble, APInt(128, 2, u.bits));
|
||||
#else
|
||||
llvm_unreachable("Unknown host real_t type for compile-time reals");
|
||||
#endif
|
||||
}
|
|
@ -56,29 +56,18 @@ void Target::_init() {
|
|||
c_long_doublesize = realsize;
|
||||
classinfosize = 0; // unused
|
||||
|
||||
const auto pTargetRealSemantics = &real->getFltSemantics();
|
||||
const auto targetRealSemantics = &real->getFltSemantics();
|
||||
#if LDC_LLVM_VER >= 400
|
||||
const auto x87DoubleExtended = &APFloat::x87DoubleExtended();
|
||||
const auto IEEEdouble = &APFloat::IEEEdouble();
|
||||
const auto PPCDoubleDouble = &APFloat::PPCDoubleDouble();
|
||||
const auto x87DoubleExtended = &APFloat::x87DoubleExtended();
|
||||
const auto IEEEquad = &APFloat::IEEEquad();
|
||||
#else
|
||||
const auto x87DoubleExtended = &APFloat::x87DoubleExtended;
|
||||
const auto IEEEdouble = &APFloat::IEEEdouble;
|
||||
const auto PPCDoubleDouble = &APFloat::PPCDoubleDouble;
|
||||
const auto x87DoubleExtended = &APFloat::x87DoubleExtended;
|
||||
const auto IEEEquad = &APFloat::IEEEquad;
|
||||
#endif
|
||||
|
||||
if (pTargetRealSemantics == x87DoubleExtended) {
|
||||
real_max = CTFloat::parse("0x1.fffffffffffffffep+16383");
|
||||
real_min_normal = CTFloat::parse("0x1p-16382");
|
||||
real_epsilon = CTFloat::parse("0x1p-63");
|
||||
RealProperties::dig = 18;
|
||||
RealProperties::mant_dig = 64;
|
||||
RealProperties::max_exp = 16384;
|
||||
RealProperties::min_exp = -16381;
|
||||
RealProperties::max_10_exp = 4932;
|
||||
RealProperties::min_10_exp = -4932;
|
||||
} else if (pTargetRealSemantics == IEEEdouble ||
|
||||
pTargetRealSemantics == PPCDoubleDouble) {
|
||||
if (targetRealSemantics == IEEEdouble) {
|
||||
real_max = CTFloat::parse("0x1.fffffffffffffp+1023");
|
||||
real_min_normal = CTFloat::parse("0x1p-1022");
|
||||
real_epsilon = CTFloat::parse("0x1p-52");
|
||||
|
@ -88,7 +77,30 @@ void Target::_init() {
|
|||
RealProperties::min_exp = -1021;
|
||||
RealProperties::max_10_exp = 308;
|
||||
RealProperties::min_10_exp = -307;
|
||||
} else if (targetRealSemantics == x87DoubleExtended) {
|
||||
real_max = CTFloat::parse("0x1.fffffffffffffffep+16383");
|
||||
real_min_normal = CTFloat::parse("0x1p-16382");
|
||||
real_epsilon = CTFloat::parse("0x1p-63");
|
||||
RealProperties::dig = 18;
|
||||
RealProperties::mant_dig = 64;
|
||||
RealProperties::max_exp = 16384;
|
||||
RealProperties::min_exp = -16381;
|
||||
RealProperties::max_10_exp = 4932;
|
||||
RealProperties::min_10_exp = -4931;
|
||||
} else if (targetRealSemantics == IEEEquad) {
|
||||
// FIXME: hex constants
|
||||
real_max = CTFloat::parse("1.18973149535723176508575932662800702e+4932");
|
||||
real_min_normal =
|
||||
CTFloat::parse("3.36210314311209350626267781732175260e-4932");
|
||||
real_epsilon = CTFloat::parse("1.92592994438723585305597794258492732e-34");
|
||||
RealProperties::dig = 33;
|
||||
RealProperties::mant_dig = 113;
|
||||
RealProperties::max_exp = 16384;
|
||||
RealProperties::min_exp = -16381;
|
||||
RealProperties::max_10_exp = 4932;
|
||||
RealProperties::min_10_exp = -4931;
|
||||
} else {
|
||||
// rely on host compiler
|
||||
real_max = RealProperties::host_max();
|
||||
real_min_normal = RealProperties::host_min_normal();
|
||||
real_epsilon = RealProperties::host_epsilon();
|
||||
|
|
|
@ -411,51 +411,38 @@ llvm::ConstantInt *DtoConstUbyte(unsigned char i) {
|
|||
return LLConstantInt::get(LLType::getInt8Ty(gIR->context()), i, false);
|
||||
}
|
||||
|
||||
LLConstant *DtoConstFP(Type *t, longdouble value) {
|
||||
#if LDC_LLVM_VER >= 400
|
||||
auto &x87DoubleExtended = APFloat::x87DoubleExtended();
|
||||
auto &IEEEquad = APFloat::IEEEquad();
|
||||
auto &PPCDoubleDouble = APFloat::PPCDoubleDouble();
|
||||
#else
|
||||
auto &x87DoubleExtended = APFloat::x87DoubleExtended;
|
||||
auto &IEEEquad = APFloat::IEEEquad;
|
||||
auto &PPCDoubleDouble = APFloat::PPCDoubleDouble;
|
||||
#endif
|
||||
|
||||
LLConstant *DtoConstFP(Type *t, const real_t value) {
|
||||
LLType *llty = DtoType(t);
|
||||
assert(llty->isFloatingPointTy());
|
||||
|
||||
if (llty == LLType::getFloatTy(gIR->context()) ||
|
||||
llty == LLType::getDoubleTy(gIR->context())) {
|
||||
return LLConstantFP::get(llty, value);
|
||||
}
|
||||
if (llty == LLType::getX86_FP80Ty(gIR->context())) {
|
||||
uint64_t bits[] = {0, 0};
|
||||
bits[0] = *reinterpret_cast<uint64_t *>(&value);
|
||||
bits[1] =
|
||||
*reinterpret_cast<uint16_t *>(reinterpret_cast<uint64_t *>(&value) + 1);
|
||||
assert(sizeof(real_t) >= 8 && "real_t < 64 bits?");
|
||||
|
||||
if (llty->isFloatTy()) {
|
||||
// let host narrow to single-precision target
|
||||
return LLConstantFP::get(gIR->context(),
|
||||
APFloat(x87DoubleExtended, APInt(80, 2, bits)));
|
||||
}
|
||||
if (llty == LLType::getFP128Ty(gIR->context())) {
|
||||
union {
|
||||
longdouble ld;
|
||||
uint64_t bits[2];
|
||||
} t;
|
||||
t.ld = value;
|
||||
return LLConstantFP::get(gIR->context(),
|
||||
APFloat(IEEEquad, APInt(128, 2, t.bits)));
|
||||
}
|
||||
if (llty == LLType::getPPC_FP128Ty(gIR->context())) {
|
||||
uint64_t bits[] = {0, 0};
|
||||
bits[0] = *reinterpret_cast<uint64_t *>(&value);
|
||||
bits[1] =
|
||||
*reinterpret_cast<uint16_t *>(reinterpret_cast<uint64_t *>(&value) + 1);
|
||||
return LLConstantFP::get(gIR->context(),
|
||||
APFloat(PPCDoubleDouble, APInt(128, 2, bits)));
|
||||
APFloat(static_cast<float>(value)));
|
||||
}
|
||||
|
||||
llvm_unreachable("Unknown floating point type encountered");
|
||||
if (llty->isDoubleTy()) {
|
||||
// let host (potentially) narrow to double-precision target
|
||||
return LLConstantFP::get(gIR->context(),
|
||||
APFloat(static_cast<double>(value)));
|
||||
}
|
||||
|
||||
// host real_t => target real
|
||||
|
||||
// 1) represent host real_t as llvm::APFloat
|
||||
const auto &targetRealSemantics = llty->getFltSemantics();
|
||||
APFloat v(targetRealSemantics, APFloat::uninitialized);
|
||||
CTFloat::toAPFloat(value, v);
|
||||
|
||||
// 2) convert to target real
|
||||
if (&v.getSemantics() != &targetRealSemantics) {
|
||||
bool ignored;
|
||||
v.convert(targetRealSemantics, APFloat::rmNearestTiesToEven, &ignored);
|
||||
}
|
||||
|
||||
return LLConstantFP::get(gIR->context(), v);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -90,7 +90,7 @@ LLConstantInt *DtoConstSize_t(uint64_t);
|
|||
LLConstantInt *DtoConstUint(unsigned i);
|
||||
LLConstantInt *DtoConstInt(int i);
|
||||
LLConstantInt *DtoConstUbyte(unsigned char i);
|
||||
LLConstant *DtoConstFP(Type *t, longdouble value);
|
||||
LLConstant *DtoConstFP(Type *t, real_t value);
|
||||
|
||||
LLConstant *DtoConstString(const char *);
|
||||
LLConstant *DtoConstBool(bool);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue