Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
- ldc2.conf: `%%ldcversion%%` placeholder added, allowing to refer to version-specific directories.

#### Platform support
- Supports LLVM 15 - 19.
- Initial compiler and runtime support for ppc64 and ppc64le systems that use IEEE 754R 128-bit floating-point as the default 128-bit floating-point format. (#4833)

#### Bug fixes
- Building multi-file D applications with control-flow protection will no longer cause LDC to throw an internal compiler error. (#4828)
Expand Down
4 changes: 0 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,6 @@ if( CMAKE_COMPILER_IS_GNUCXX
AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.6.0" )
append("-mminimal-toc" LDC_CXXFLAGS)
endif()
# Do not use doubledouble on ppc
if( CMAKE_SYSTEM_PROCESSOR MATCHES "ppc|powerpc")
append("-mlong-double-64" LDC_CXXFLAGS)
endif()
if(UNIX)
append("-DLDC_POSIX" LDC_CXXFLAGS)
endif()
Expand Down
6 changes: 4 additions & 2 deletions driver/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,10 @@ void registerPredefinedTargetVersions() {
VersionCondition::addPredefinedGlobalIdent("PPC64");
registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat");
if (triple.getOS() == llvm::Triple::Linux) {
VersionCondition::addPredefinedGlobalIdent(
triple.getArch() == llvm::Triple::ppc64 ? "ELFv1" : "ELFv2");
const llvm::SmallVector<llvm::StringRef> features{};
const std::string abi = getABI(triple, features);
VersionCondition::addPredefinedGlobalIdent(abi == "elfv1" ? "ELFv1"
: "ELFv2");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://github.com/dlang/dmd/blob/e082ce247afa6cf41c552ec57db2e95c3f7c0dac/druntime/src/etc/valgrind/valgrind.h#L154-L159, little-endian uses v2, big-endian v1.

That's a bad detection: big-endian ppc can use ELFv2, and little-endian ppc can also use ELFv1: https://godbolt.org/z/s5eceeqxs.

}
break;
case llvm::Triple::arm:
Expand Down
4 changes: 2 additions & 2 deletions gen/abi/ppc64le.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// The ABI implementation used for 64 bit little-endian PowerPC targets.
//
// The PowerOpen 64bit ELF v2 ABI can be found here:
// https://members.openpowerfoundation.org/document/dl/576
// https://files.openpower.foundation/s/cfA2oFPXbbZwEBK/download/64biteflv2abi-v1.5.pdf
//===----------------------------------------------------------------------===//

#include "gen/abi/abi.h"
Expand Down Expand Up @@ -51,7 +51,7 @@ struct PPC64LETargetABI : TargetABI {
} else {
compositeToArray64.applyTo(arg);
}
} else if (ty->isintegral()) {
} else if (ty->isintegral() && !ty->isTypeVector()) {
arg.attrs.addAttribute(ty->isunsigned() ? LLAttribute::ZExt
: LLAttribute::SExt);
}
Expand Down
17 changes: 17 additions & 0 deletions gen/modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,23 @@ void addModuleFlags(llvm::Module &m) {
opts::fCFProtection == opts::CFProtectionType::Full) {
m.setModuleFlag(ModuleMinFlag, "cf-protection-branch", ConstantOneMetadata);
}

// Target specific flags
const auto ModuleErrFlag = llvm::Module::Error;
switch (global.params.targetTriple->getArch()) {
case llvm::Triple::ppc64:
case llvm::Triple::ppc64le:
if (target.RealProperties.mant_dig == 113) {
const auto ConstantIEEE128String = llvm::MDString::get(gIR->context(), "ieeequad");
m.setModuleFlag(ModuleErrFlag, "float-abi", ConstantIEEE128String);
} else if (target.RealProperties.mant_dig == 106) {
const auto ConstantIBM128String = llvm::MDString::get(gIR->context(), "doubledouble");
m.setModuleFlag(ModuleErrFlag, "float-abi", ConstantIBM128String);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it this is just informational, or is this supported by some toolchains?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it this is just informational, or is this supported by some toolchains?

I see Clang emit this information, I am guessing this is to avoid mixing compilation units with different float ABIs when doing LTO.

}
break;
default:
break;
}
}

} // anonymous namespace
Expand Down
85 changes: 81 additions & 4 deletions gen/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,34 @@
using namespace dmd;
using llvm::APFloat;

enum class RealPrecision : uint8_t { Default, Double, Quad, DoubleDouble };
static llvm::cl::opt<RealPrecision, false> realPrecision{
"real-precision",
llvm::cl::ZeroOrMore,
llvm::cl::Hidden,
llvm::cl::init(RealPrecision::Default),
llvm::cl::desc("Override the precision of the `real` type"),
llvm::cl::values(clEnumValN(RealPrecision::Double, "double",
"Use double precision (64-bit)"),
clEnumValN(RealPrecision::Quad, "quad",
"Use IEEE quad precision (128-bit)"),
clEnumValN(RealPrecision::DoubleDouble, "doubledouble",
"Use IBM double double precision (128-bit)"))};

namespace {
// Returns the LL type to be used for D `real` (C `long double`).
llvm::Type *getRealType(const llvm::Triple &triple) {
using llvm::Triple;

auto &ctx = getGlobalContext();

// If user specified double or quad precision, use it unconditionally.
if (realPrecision == RealPrecision::Double) {
return LLType::getDoubleTy(ctx);
} else if (realPrecision == RealPrecision::Quad) {
return LLType::getFP128Ty(ctx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested if this will blow up x86 builds?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Android x86_64 is already using it, so I guess that's fine. - This is an advanced option (hence hidden), for pros only - druntime and Phobos need to match too etc. (no problem for simple betterC stuff in wasm, just need to compile everything with the override). If LLVM can't cope with this format for a target's codegen, it'll error out. [I'm a fan of giving users full control and letting them explore the compiler/LLVM limits.]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Plus there are some compiler assumptions wrt. real precision, in existing TargetABI implementations - as the real precision wasn't configurable so far.]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[I'm a fan of giving users full control and letting them explore the compiler/LLVM limits.]

I see. Then that's fine by me. [Although LLVM is quite fragile when operating beyond its comfort zone]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RealPrecision::DoubleDouble should be handled here too (i.e. the user has set it explicitly)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RealPrecision::DoubleDouble should be handled here too (i.e. the user has set it explicitly)

Right, and for all other architectures, it should throw an error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RealPrecision::DoubleDouble should be handled here too (i.e. the user has set it explicitly)

That I wanted to avoid though. I originally only wanted to expose the 2 standard IEEE formats as overrides, but unfortunately we need a way to make a native-PPC64 build with default IEEE quad ABI still cross-compile to the old doubledouble ABI. (I still don't wanna expose x87 if we can help it - although admittedly it could be useful for MSVC targets, to unlock the x87 real, which the compiler itself could use as real_t - bye-bye dmd.root.longdouble...)

But yeah, we can bail out as a compromise if this option is used for non-ppc64 targets.

}

// Android: x86 targets follow ARM, with emulated quad precision for x64
if (triple.getEnvironment() == llvm::Triple::Android) {
return triple.isArch64Bit() ? LLType::getFP128Ty(ctx)
Expand Down Expand Up @@ -64,9 +85,34 @@ llvm::Type *getRealType(const llvm::Triple &triple) {
case Triple::wasm64:
return LLType::getFP128Ty(ctx);

case Triple::ppc64:
case Triple::ppc64le:
if (realPrecision == RealPrecision::DoubleDouble) {
return LLType::getPPC_FP128Ty(ctx);
}
if (triple.isMusl()) { // Musl uses double
return LLType::getDoubleTy(ctx);
}
#if defined(__linux__) && defined(__powerpc64__)
// for a PowerPC64 Linux build:
// default to the C++ host compiler's `long double` ABI
switch (std::numeric_limits<long double>::digits) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use:

Suggested change
switch (std::numeric_limits<long double>::digits) {
switch (__LDBL_MANT_DIG__) {

This one is defined by the compiler directly and I have tested to contain correct information.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine by me. - We should probably only forward the host precision when targeting a Linux ppc64 target, not e.g. default to IEEE quad when cross-compiling to a ppc64 FreeBSD target.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when changed to the compiler macro, could this be #if sequence such that failure happens during compilation rather than at runtime?

case 113:
return LLType::getFP128Ty(ctx);
case 106:
return LLType::getPPC_FP128Ty(ctx);
case 53:
return LLType::getDoubleTy(ctx);
default:
llvm_unreachable("Unexpected host C++ 'long double' precision for a "
"PowerPC64 target!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps print the actual __LDBL_MANT_DIG__ number in the diagnostic? (will help troubleshooting when this error triggers)

}
#endif
return LLType::getPPC_FP128Ty(ctx);

default:
// 64-bit double precision for all other targets
// FIXME: PowerPC, SystemZ, ...
// FIXME: SystemZ, ...
return LLType::getDoubleTy(ctx);
}
}
Expand Down Expand Up @@ -156,6 +202,7 @@ void Target::_init(const Param &params) {
const auto IEEEdouble = &APFloat::IEEEdouble();
const auto x87DoubleExtended = &APFloat::x87DoubleExtended();
const auto IEEEquad = &APFloat::IEEEquad();
const auto PPCDoubleDouble = &APFloat::PPCDoubleDouble();
bool isOutOfRange = false;

RealProperties.nan = CTFloat::nan;
Expand Down Expand Up @@ -197,6 +244,18 @@ void Target::_init(const Param &params) {
RealProperties.min_exp = -16381;
RealProperties.max_10_exp = 4932;
RealProperties.min_10_exp = -4931;
} else if (targetRealSemantics == PPCDoubleDouble) {
RealProperties.max =
CTFloat::parse("0x1.fffffffffffff7ffffffffffff8p1023", isOutOfRange);
RealProperties.min_normal = CTFloat::parse("0x1p-969", isOutOfRange);
RealProperties.epsilon =
CTFloat::parse("0x0.000000000000000000000000008p-969", isOutOfRange);
RealProperties.dig = 31;
RealProperties.mant_dig = 106;
RealProperties.max_exp = 1024;
RealProperties.min_exp = -968;
RealProperties.max_10_exp = 308;
RealProperties.min_10_exp = -291;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised by this being quite a bit greater than double.min_10_exp (-307). How did you get these numbers? Querying GDC or the C++ compiler?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised by this being quite a bit greater than double.min_10_exp (-307). How did you get these numbers? Querying GDC or the C++ compiler?

Yes, the numbers are obtained from GCC/Clang. IBM stated due to the peculiarity of the type, those values are best-effort approximations.

} else {
// leave initialized with host real_t values
warning(Loc(), "unknown properties for target `real` type, relying on D "
Expand Down Expand Up @@ -237,11 +296,29 @@ Type *Target::va_listType(const Loc &loc, Scope *sc) {
const char *TargetCPP::typeMangle(Type *t) {
if (t->ty == TY::Tfloat80) {
const auto &triple = *global.params.targetTriple;

// `long double` on Android/x64 is __float128 and mangled as `g`
bool isAndroidX64 = triple.getEnvironment() == llvm::Triple::Android &&
triple.getArch() == llvm::Triple::x86_64;
return isAndroidX64 ? "g" : "e";
if (triple.getEnvironment() == llvm::Triple::Android &&
triple.getArch() == llvm::Triple::x86_64 &&
target.RealProperties.mant_dig == 113) {
return "g";
};

if (triple.getArch() == llvm::Triple::ppc64 ||
triple.getArch() == llvm::Triple::ppc64le) {
if (target.RealProperties.mant_dig == 113 &&
triple.getEnvironment() == llvm::Triple::GNU) {
return "u9__ieee128";
}
if (target.RealProperties.mant_dig == 106) {
// IBM long double
return "g";
}
}

return "e";
}

return nullptr;
}

Expand Down
4 changes: 2 additions & 2 deletions gen/toir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1858,8 +1858,8 @@ class ToElemVisitor : public Visitor {

LLValue *b = DtoRVal(DtoCast(e->loc, u, Type::tbool));

LLConstant *zero = DtoConstBool(false);
b = p->ir->CreateICmpEQ(b, zero);
LLConstant *one = DtoConstBool(true);
b = p->ir->CreateXor(b, one);

result = zextBool(b, e->type);
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/druntime/src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ version (LDC)
version (D_LP64)
{
version (PPC64)
enum has128BitCAS = false;
enum has128BitCAS = real.mant_dig == 113;
else
enum has128BitCAS = true;
}
Expand Down
5 changes: 5 additions & 0 deletions runtime/druntime/src/core/stdc/config.d
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,8 @@ package(core) template muslRedirTime64Mangle(string name, string redirectedName)
else
enum muslRedirTime64Mangle = name;
}

version (PPC64)
enum PPCUseIEEE128 = real.mant_dig == 113;
else
enum PPCUseIEEE128 = false;
Loading
Loading