186 changes: 186 additions & 0 deletions dmd2/root/longdouble.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,194 @@
#define __LONG_DOUBLE_H__

#include <stdio.h>

#if USE_OSX_TARGET_REAL
/* Support limited cross-compiling on OS X to target real type.
This is not a general solution and probably won't work on other OSes,
instead just enough to support a single cross compiler on X86 Mac targeting
OS X variants - MacOS, iOS (ARM) 64-bit real or iOS sim (X86) 80-bit real.
There is a better solution coming in LDC and this can all get stripped once
that is fully available.
Code here and sprinkled about uses typedef longdouble to represent D real.
Normally it is set to long double but here we override to a limited Real
value type that does just enough to satisfy needs of dmd code without much
further code change. #if USE_OSX_TARGET_REAL is wrapping places where code
was changed to make it easier to find and disable when this needs to be
backed out. Some dmd code uses the float.h LDBL_ constants for real
attributes, so those are overriden here too.
Real just wraps a long double but based on `targetReal64` may convert to
double precision in ctor. That way it never holds more precision than the
target. Once a Real is constructed, it always has target's precision.
Single operations are done with long double but result is converted again
if needed. The key is for CTFE to be close to what real target would
compute.
dmd uses sizeof(longdouble) or sizeof(real_t) in some places to determine
buffer sizes which means oversized when 64-bit real, but only used for
print buffers, so no worries. There are other places when address is
taken, but we ensure that Real bitwise is a valid long double.
Implementation for Real is in port.c because this is temporary.
*/

#include <assert.h>
#include <math.h>
#include <memory.h>

struct Real // to used to D struct for value :-)
{
private:
// Only member
long double val;

// Assumption that has x87 80-bit rep for long double
static const size_t size = 10;
static const size_t pad = 6;

// Set if target real is 64-bits
static bool targetReal64;
static bool initialized;

public:
// Must be called before any other use with 'useReal64' true for targets like
// ARM real == double, or false for X86.
static void init(bool useReal64);

static bool useReal64()
{
assert(initialized); // kick out earlier arrivals
return targetReal64;
}

Real() : val(0) {}

Real(long double x)
{
if (useReal64())
val = (double)x;
else
val = x;
}

Real operator=(Real x)
{
val = x.val;
return *this;
}

// bit for bit comparison
bool bitsmatch(Real x) const
{
return memcmp(&val, &x.val, size) == 0;
}

// Could zero out pad of long double type when someone wants raw bits.
//
// Note 1: Can make private to find all the address takers - makes compiler
// error.
//
// Note 2: Don't think this is really needed - I was fooled by existing
// issue discussed in pull #770, so this operator doesn't fix it. But I
// think last 6 pad bytes should be undefined. Expecting zero is wrong.
//
// https://github.com/ldc-developers/ldc/pull/770
/*
Real* operator&()
{
memset(reinterpret_cast<char*>(&val) + size, 0, pad);
return this;
}
*/

// provide enough conversions used by rest of dmd code and make explict to
// avoid ambiguities
explicit operator bool() const {return val != 0;}
explicit operator signed char() const {return val;}
explicit operator unsigned char() const {return val;}
explicit operator short() const {return val;}
explicit operator unsigned short() const {return val;}
explicit operator int() const {return val;}
explicit operator unsigned int() const {return val;}
explicit operator long() const {return val;}
explicit operator unsigned long() const {return val;}
explicit operator long long() const {return val;}
explicit operator unsigned long long() const {return val;}
explicit operator float() const {return val;}
explicit operator double() const {return val;}
explicit operator long double() const {return val;}

// provide operators used by rest of dmd code
bool operator<(Real x) const {return val < x.val;}
bool operator<=(Real x) const {return val <= x.val;}
bool operator>(Real x) const {return val > x.val;}
bool operator>=(Real x) const {return val >= x.val;}
bool operator==(Real x) const {return val == x.val;}
bool operator!=(Real x) const {return val != x.val;}
Real operator+(Real x) const {return val + x.val;}
Real operator-(Real x) const {return val - x.val;}
Real operator-() const {return -val;}
Real operator*(Real x) const {return val * x.val;}
Real operator/(Real x) const {return val / x.val;}

// provide the math functions used by dmd code
friend Real sinl(Real x) {return sinl(x.val);}
friend Real cosl(Real x) {return cosl(x.val);}
friend Real tanl(Real x) {return tanl(x.val);}
friend Real fabsl(Real x) {return fabsl(x.val);}
friend Real sqrtl(Real x) {return sqrtl(x.val);}
friend Real logl(Real x) {return logl(x.val);}
friend Real fminl(Real x, Real y) {return fminl(x.val,y.val);}
friend Real fmaxl(Real x, Real y) {return fmaxl(x.val,y.val);}
friend Real floor(Real x) {return floorl(x.val);}
friend Real ceil(Real x) {return ceill(x.val);}
friend Real trunc(Real x) {return truncl(x.val);}
friend Real round(Real x) {return roundl(x.val);}

static int ldbl_dig() {return useReal64() ? __DBL_DIG__ : __LDBL_DIG__;}
static Real ldbl_max() {return useReal64() ? __DBL_MAX__ : __LDBL_MAX__;}
static Real ldbl_min() {return useReal64() ? __DBL_MIN__ : __LDBL_MIN__;}
static Real ldbl_epsilon() {return useReal64() ? __DBL_EPSILON__ : __LDBL_EPSILON__;}
static int ldbl_mant_dig() {return useReal64() ? __DBL_MANT_DIG__ : __LDBL_MANT_DIG__;}
static int ldbl_max_exp() {return useReal64() ? __DBL_MAX_EXP__ : __LDBL_MAX_EXP__;}
static int ldbl_min_exp() {return useReal64() ? __DBL_MIN_EXP__ : __LDBL_MIN_EXP__;}
static int ldbl_max_10_exp() {return useReal64() ? __DBL_MAX_10_EXP__ : __LDBL_MAX_10_EXP__;}
static int ldbl_min_10_exp() {return useReal64() ? __DBL_MIN_10_EXP__ : __LDBL_MIN_10_EXP__;}
};


typedef Real longdouble;
// volatile should not be needed with Real
typedef Real volatile_longdouble;

// undo what the compiler and float.h say for LDBL. By including float.h
// first, we ensure include order elsewhere won't make a difference.
#include <float.h>
#undef LDBL_DIG
#define LDBL_DIG Real::ldbl_dig()
#undef LDBL_MAX
#define LDBL_MAX Real::ldbl_max()
#undef LDBL_MIN
#define LDBL_MIN Real::ldbl_min()
#undef LDBL_EPSILON
#define LDBL_EPSILON Real::ldbl_epsilon()
#undef LDBL_MANT_DIG
#define LDBL_MANT_DIG Real::ldbl_mant_dig()
#undef LDBL_MAX_EXP
#define LDBL_MAX_EXP Real::ldbl_max_exp()
#undef LDBL_MIN_EXP
#define LDBL_MIN_EXP Real::ldbl_min_exp()
#undef LDBL_MAX_10_EXP
#define LDBL_MAX_10_EXP Real::ldbl_max_10_exp()
#undef LDBL_MIN_10_EXP
#define LDBL_MIN_10_EXP Real::ldbl_min_10_exp()

#else // use native long double (what ever it might be)
typedef long double longdouble;
typedef volatile long double volatile_longdouble;
#endif

// also used from within C code, so use a #define rather than a template
// template<typename T> longdouble ldouble(T x) { return (longdouble) x; }
Expand Down
66 changes: 59 additions & 7 deletions dmd2/root/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,13 +679,22 @@ longdouble Port::snan;

static double zero = 0;
double Port::infinity = 1 / zero;
#if USE_OSX_TARGET_REAL
longdouble Port::ldbl_infinity;
#else
longdouble Port::ldbl_infinity = 1 / zero;
#endif

double Port::dbl_max = 1.7976931348623157e308;
double Port::dbl_min = 5e-324;

#if USE_OSX_TARGET_REAL
longdouble Port::ldbl_max;
#else
longdouble Port::ldbl_max = LDBL_MAX;
#endif

#if __i386 || __x86_64__
#if (__i386 || __x86_64__) && !USE_OSX_TARGET_REAL
bool Port::yl2x_supported = true;
bool Port::yl2xp1_supported = true;
#else
Expand All @@ -698,14 +707,39 @@ struct PortInitializer
PortInitializer();
};

#if USE_OSX_TARGET_REAL

bool Real::initialized;
bool Real::targetReal64;

void Real::init(bool useReal64)
{
if (!initialized)
{
// Target set, safe to use Real
targetReal64 = useReal64;
initialized = true;
Port::ldbl_infinity = 1 / zero;
Port::ldbl_max = LDBL_MAX;

static PortInitializer portinitializer;
}
}

#else // !USE_OSX_TARGET_REAL
static PortInitializer portinitializer;
#endif

PortInitializer::PortInitializer()
{
#if IN_LLVM

// Derive LLVM APFloat::fltSemantics from native format
#if LDBL_MANT_DIG == 53
#if USE_OSX_TARGET_REAL
// OSX_TARGET_REAL approach uses extended precision and coverts to double as
// needed by longdouble class when targeting 64-bit real.
#define FLT_SEMANTIC llvm::APFloat::x87DoubleExtended
#elif LDBL_MANT_DIG == 53
#define FLT_SEMANTIC llvm::APFloat::IEEEdouble
#elif LDBL_MANT_DIG == 64
#define FLT_SEMANTIC llvm::APFloat::x87DoubleExtended
Expand All @@ -718,9 +752,8 @@ PortInitializer::PortInitializer()
#endif

Port::nan = *reinterpret_cast<const double*>(llvm::APFloat::getNaN(llvm::APFloat::IEEEdouble).bitcastToAPInt().getRawData());
Port::ldbl_nan = *reinterpret_cast<const long double*>(llvm::APFloat::getNaN(FLT_SEMANTIC).bitcastToAPInt().getRawData());
Port::snan = *reinterpret_cast<const long double*>(llvm::APFloat::getSNaN(FLT_SEMANTIC).bitcastToAPInt().getRawData());

Port::ldbl_nan = *reinterpret_cast<const longdouble*>(llvm::APFloat::getNaN(FLT_SEMANTIC).bitcastToAPInt().getRawData());
Port::snan = *reinterpret_cast<const longdouble*>(llvm::APFloat::getSNaN(FLT_SEMANTIC).bitcastToAPInt().getRawData());
#else
union
{ unsigned int ui[2];
Expand Down Expand Up @@ -772,7 +805,9 @@ int Port::isNan(double r)

int Port::isNan(longdouble r)
{
#if __APPLE__
#if USE_OSX_TARGET_REAL
return isnan((long double)r);
#elif __APPLE__
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
return __inline_isnanl(r);
#else
Expand Down Expand Up @@ -814,15 +849,28 @@ int Port::isInfinity(double r)
#endif
}

#if USE_OSX_TARGET_REAL
int Port::isInfinity(longdouble r)
{
return isinf((long double)r);
}
#endif

longdouble Port::sqrt(longdouble x)
{
#if USE_OSX_TARGET_REAL
return sqrtl(x);
#else
return std::sqrt(x);
#endif
}

longdouble Port::fmodl(longdouble x, longdouble y)
{
#if __FreeBSD__ && __FreeBSD_version < 800000 || __OpenBSD__ || __NetBSD__ || __DragonFly__
return ::fmod(x, y); // hack for now, fix later
#elif USE_OSX_TARGET_REAL
return ::fmodl((long double)x, (long double)y);
#else
return std::fmod(x, y);
#endif
Expand All @@ -833,10 +881,14 @@ int Port::fequal(longdouble x, longdouble y)
/* In some cases, the REALPAD bytes get garbage in them,
* so be sure and ignore them.
*/
#if USE_OSX_TARGET_REAL
return x.bitsmatch(y);
#else
return memcmp(&x, &y, Target::realsize - Target::realpad) == 0;
#endif
}

#if __i386 || __x86_64__
#if (__i386 || __x86_64__) && !USE_OSX_TARGET_REAL
void Port::yl2x_impl(longdouble* x, longdouble* y, longdouble* res)
{
__asm__ volatile("fyl2x": "=t" (*res): "u" (*y), "0" (*x) : "st(1)" );
Expand Down
3 changes: 3 additions & 0 deletions dmd2/root/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ struct Port
static int isSignallingNan(longdouble);

static int isInfinity(double);
#if USE_OSX_TARGET_REAL
static int isInfinity(longdouble);
#endif

static longdouble fmodl(longdouble x, longdouble y);
static longdouble sqrt(longdouble x);
Expand Down
30 changes: 28 additions & 2 deletions driver/cl_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ static cl::opt<bool, true>
vgc("vgc", cl::desc("list all gc allocations including hidden ones"),
cl::ZeroOrMore, cl::location(global.params.vgc));

static cl::opt<bool, true>
verboseTls("vtls",
cl::desc("list TLS variables (useful if -disable-tls used)"),
cl::ZeroOrMore, cl::location(global.params.vtls));

static cl::opt<bool, true> verbose_cg("v-cg", cl::desc("Verbose codegen"),
cl::ZeroOrMore,
cl::location(global.params.verbose_cg));
Expand Down Expand Up @@ -245,8 +250,18 @@ cl::opt<std::string>
moduleDepsFile("deps", cl::desc("Write module dependencies to filename"),
cl::value_desc("filename"));

cl::opt<std::string> mArch("march",
cl::desc("Architecture to generate code for:"));
// Provide a clang-like "-arch" for iOS targets. It is used by the OS X based
// tools to specify just the <arch><sub> part of the triple
// <arch><sub>-<vendor>-<sys>-<abi>. This differs from -march below that
// implements an llc-like "-march" which selects a target name from the LLVM
// TargetRegistery.
cl::opt<std::string> iosArch(
"arch",
cl::desc("Specify the iOS architecture to build for (like clang -arch):"));

cl::opt<std::string>
mArch("march",
cl::desc("Architecture to generate code for (like llc -march):"));

cl::opt<bool> m32bits("m32", cl::desc("32 bit target"), cl::ZeroOrMore);

Expand Down Expand Up @@ -274,10 +289,14 @@ cl::opt<std::string>

cl::opt<llvm::Reloc::Model> mRelocModel(
"relocation-model", cl::desc("Relocation model"),
#if LDC_LLVM_VER < 309
cl::init(llvm::Reloc::Default),
#endif
cl::values(
#if LDC_LLVM_VER < 309
clEnumValN(llvm::Reloc::Default, "default",
"Target default relocation model"),
#endif
clEnumValN(llvm::Reloc::Static, "static", "Non-relocatable code"),
clEnumValN(llvm::Reloc::PIC_, "pic",
"Fully relocatable, position independent code"),
Expand Down Expand Up @@ -410,6 +429,13 @@ cl::opt<unsigned char, true, CoverageParser> coverageAnalysis(
"minimum required coverage)"),
cl::location(global.params.covPercent), cl::ValueOptional, cl::init(127));

// Useful if target OS does not have TLS or threads, or perhaps you are
// writing an OS.
cl::opt<bool, true> disableTls(
"disable-tls",
cl::desc("Disable thread local storage (variables become __gshared)"),
cl::location(global.params.disableTls), cl::init(false));

static cl::extrahelp footer(
"\n"
"-d-debug can also be specified without options, in which case it enables "
Expand Down
2 changes: 2 additions & 0 deletions driver/cl_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern cl::opt<std::string> hdrFile;
extern cl::list<std::string> versions;
extern cl::opt<std::string> moduleDepsFile;

extern cl::opt<std::string> iosArch;
extern cl::opt<std::string> mArch;
extern cl::opt<bool> m32bits;
extern cl::opt<bool> m64bits;
Expand All @@ -72,6 +73,7 @@ extern cl::opt<FloatABI::Type> mFloatABI;
extern cl::opt<bool, true> singleObj;
extern cl::opt<bool> linkonceTemplates;
extern cl::opt<bool> disableLinkerStripDead;
extern cl::opt<bool, true> disableTls;

extern cl::opt<BOUNDSCHECK> boundsCheck;
extern bool nonSafeBoundsChecks;
Expand Down
17 changes: 13 additions & 4 deletions driver/configfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#include <string>
#if _WIN32
#define WIN32_LEAN_AND_MEAN
#include "llvm/Support/ConvertUTF.h"
#include <windows.h>
#include <shlobj.h>
#include <tchar.h>
// Prevent name clash with LLVM
#undef GetCurrentDirectory
#endif
Expand Down Expand Up @@ -55,16 +57,23 @@ static bool ReadPathFromRegistry(llvm::SmallString<128> &p) {
HKEY hkey;
bool res = false;
// FIXME: Version number should be a define.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\ldc-developers\\LDC\\0.11.0",
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\ldc-developers\\LDC\\0.11.0"),
NULL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
DWORD length;
if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, NULL, &length) ==
if (RegGetValue(hkey, NULL, _T("Path"), RRF_RT_REG_SZ, NULL, NULL, &length) ==
ERROR_SUCCESS) {
char *data = static_cast<char *>(_alloca(length));
if (RegGetValue(hkey, NULL, "Path", RRF_RT_REG_SZ, NULL, data, &length) ==
TCHAR *data = static_cast<TCHAR *>(_alloca(length * sizeof(TCHAR)));
if (RegGetValue(hkey, NULL, _T("Path"), RRF_RT_REG_SZ, NULL, data, &length) ==
ERROR_SUCCESS) {
#if UNICODE
std::string out;
res = llvm::convertUTF16ToUTF8String(
llvm::ArrayRef<UTF16>(reinterpret_cast<UTF16 *>(data), length), out);
p = out;
#else
p = std::string(data);
res = true;
#endif
}
}
RegCloseKey(hkey);
Expand Down
10 changes: 4 additions & 6 deletions driver/ldmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,9 @@ Usage:\n\
-vdmd print the command used to invoke the underlying compiler\n\
--version print compiler version and exit\n\
-version=level compile in version code >= level\n\
-version=ident compile in version code identified by ident\n"
#if 0
" -vtls list all variables going into thread local storage\n"
#endif
" -vgc list all gc allocations including hidden ones\n\
-version=ident compile in version code identified by ident\n\
-vtls list all variables going into thread local storage\n\
-vgc list all gc allocations including hidden ones\n\
-verrors=num limit the number of error messages (0 means unlimited)\n\
-w enable warnings\n\
-wi enable informational warnings\n\
Expand Down Expand Up @@ -845,7 +843,7 @@ void buildCommandLine(std::vector<const char *> &r, const Params &p) {
r.push_back("-vgc");
}
if (p.logTlsUse) {
warning("-vtls not yet supported by LDC.");
r.push_back("-vtls");
}
if (p.errorLimitSet) {
r.push_back(concat("-verrors=", p.errorLimit));
Expand Down
20 changes: 19 additions & 1 deletion driver/linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "llvm/Support/Path.h"
#if _WIN32
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ConvertUTF.h"
#include <Windows.h>
#endif

Expand Down Expand Up @@ -181,6 +182,13 @@ static int linkObjToBinaryGcc(bool sharedLib, bool fullyStatic) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
args.push_back("-ldl");
#if MACOSX_DEAD_STRIP
// Experiment, but early trials had some weird unittest failures
// possibly due to this
if (!opts::disableLinkerStripDead) {
args.push_back("-Wl,-dead_strip");
}
#endif
// fallthrough
case llvm::Triple::FreeBSD:
case llvm::Triple::NetBSD:
Expand Down Expand Up @@ -242,6 +250,8 @@ static int linkObjToBinaryGcc(bool sharedLib, bool fullyStatic) {
switch (global.params.targetTriple.getArch()) {
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
#if LDC_LLVM_VER == 305
Expand Down Expand Up @@ -348,9 +358,17 @@ int executeAndWait(const char *commandLine) {

DWORD exitCode;

#if UNICODE
std::wstring wcommandLine;
if (!llvm::ConvertUTF8toWide(commandLine, wcommandLine))
return -3;
auto cmdline = const_cast<wchar_t *>(wcommandLine.data());
#else
auto cmdline = const_cast<char *>(commandLine);
#endif
// according to MSDN, only CreateProcessW (unicode) may modify the passed
// command line
if (!CreateProcess(NULL, const_cast<char *>(commandLine), NULL, NULL, TRUE, 0,
if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0,
NULL, NULL, &si, &pi)) {
exitCode = -1;
} else {
Expand Down
116 changes: 107 additions & 9 deletions driver/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ static cl::opt<bool> staticFlag(
"Create a statically linked binary, including all system dependencies"),
cl::ZeroOrMore);

#if LDC_LLVM_VER >= 309
static inline llvm::Optional<llvm::Reloc::Model> getRelocModel() {
if (mRelocModel.getNumOccurrences()) {
llvm::Reloc::Model R = mRelocModel;
return R;
}
return llvm::None;
}
#else
static inline llvm::Reloc::Model getRelocModel() {
return mRelocModel;
}
#endif

void printVersion() {
printf("LDC - the LLVM D compiler (%s):\n", global.ldc_version);
printf(" based on DMD %s and LLVM %s\n", global.version,
Expand All @@ -113,7 +127,7 @@ void printVersion() {
printf(" compiled with address sanitizer enabled\n");
#endif
#endif
printf(" Default target: %s\n", llvm::sys::getDefaultTargetTriple().c_str());
printf(" Default target: %s\n", ldc::getDefaultTriple().c_str());
std::string CPU = llvm::sys::getHostCPUName();
if (CPU == "generic") {
CPU = "(unknown)";
Expand Down Expand Up @@ -505,7 +519,11 @@ static void parseCommandLine(int argc, char **argv, Strings &sourceFiles,
error(Loc(), "-lib and -shared switches cannot be used together");
}

#if LDC_LLVM_VER >= 309
if (createSharedLib && !mRelocModel.getNumOccurrences()) {
#else
if (createSharedLib && mRelocModel == llvm::Reloc::Default) {
#endif
mRelocModel = llvm::Reloc::PIC_;
}

Expand Down Expand Up @@ -553,7 +571,9 @@ static void initializePasses() {
#if LDC_LLVM_VER < 308
initializeIPA(Registry);
#endif
#if LDC_LLVM_VER >= 306
#if LDC_LLVM_VER >= 400
initializeRewriteSymbolsLegacyPassPass(Registry);
#elif LDC_LLVM_VER >= 306
initializeRewriteSymbolsPass(Registry);
#endif
#if LDC_LLVM_VER >= 307
Expand Down Expand Up @@ -588,7 +608,9 @@ static void registerPredefinedFloatABI(const char *soft, const char *hard,
// Use target floating point unit instead of s/w float routines
#if LDC_LLVM_VER >= 307
// FIXME: This is a semantic change!
bool useFPU = gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard;
//bool useFPU = gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard;
// hardcode for iOS softfp, but breaks soft
bool useFPU = true;
#else
bool useFPU = !gTargetMachine->Options.UseSoftFloat;
#endif
Expand Down Expand Up @@ -645,6 +667,7 @@ static void registerPredefinedTargetVersions() {
registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP");
break;
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
VersionCondition::addPredefinedGlobalIdent("ARM");
VersionCondition::addPredefinedGlobalIdent(
"Thumb"); // For backwards compatibility.
Expand Down Expand Up @@ -753,6 +776,20 @@ static void registerPredefinedTargetVersions() {
VersionCondition::addPredefinedGlobalIdent("Haiku");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::IOS:
VersionCondition::addPredefinedGlobalIdent("iOS");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
#if LDC_LLVM_VER >= 308
case llvm::Triple::WatchOS:
VersionCondition::addPredefinedGlobalIdent("WatchOS");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::TvOS:
VersionCondition::addPredefinedGlobalIdent("TVOS");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
#endif
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
VersionCondition::addPredefinedGlobalIdent("OSX");
Expand Down Expand Up @@ -820,6 +857,10 @@ static void registerPredefinedVersions() {
VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks");
}

if (global.params.disableTls) {
VersionCondition::addPredefinedGlobalIdent("NoThreadLocalStorage");
}

registerPredefinedTargetVersions();

// Pass sanitizer arguments to linker. Requires clang.
Expand Down Expand Up @@ -904,9 +945,26 @@ static void emitJson(Modules &modules) {
}
}

static bool validiOSArch(const std::string &iosArch) {
// TODO: should be renamed as validDarwinArch
return (iosArch == "i386" ||
iosArch == "x86_64" ||
iosArch == "armv6" ||
iosArch == "armv7" ||
iosArch == "armv7s" ||
#if LDC_LLVM_VER >= 308
iosArch == "armv7k" ||
#endif
iosArch == "arm64");
}

int main(int argc, char **argv) {
// stack trace on signals
#if LDC_LLVM_VER >= 309
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
#else
llvm::sys::PrintStackTraceOnErrorSignal();
#endif

exe_path::initialize(argv[0], reinterpret_cast<void *>(main));

Expand Down Expand Up @@ -939,10 +997,25 @@ int main(int argc, char **argv) {
}

// Set up the TargetMachine.
if (!iosArch.empty()) {
#ifndef IPHONEOS_DEFAULT_TRIPLE
error(Loc(), "-arch only available when default target is iOS");
#endif
if (!validiOSArch(iosArch)) {
error(Loc(), "-arch %s is not available for iOS", iosArch.c_str());
}

if (!mArch.empty() || !mTargetTriple.empty()) {
error(Loc(),
"-arch switch cannot be used with -march and -mtriple switches");
}
}

ExplicitBitness::Type bitness = ExplicitBitness::None;
if ((m32bits || m64bits) && (!mArch.empty() || !mTargetTriple.empty())) {
error(Loc(), "-m32 and -m64 switches cannot be used together with -march "
"and -mtriple switches");
if ((m32bits || m64bits) &&
(!iosArch.empty() || !mArch.empty() || !mTargetTriple.empty())) {
error(Loc(), "-m32 and -m64 switches cannot be used together with -arch, "
"-march, and -mtriple switches");
}

if (m32bits) {
Expand All @@ -960,8 +1033,9 @@ int main(int argc, char **argv) {
}

gTargetMachine = createTargetMachine(
mTargetTriple, mArch, mCPU, mAttrs, bitness, mFloatABI, mRelocModel,
mCodeModel, codeGenOptLevel(), disableFpElim, disableLinkerStripDead);
iosArch, mTargetTriple, mArch, mCPU, mAttrs, bitness, mFloatABI,
getRelocModel(), mCodeModel, codeGenOptLevel(), disableFpElim,
disableLinkerStripDead);

#if LDC_LLVM_VER >= 308
static llvm::DataLayout DL = gTargetMachine->createDataLayout();
Expand Down Expand Up @@ -997,7 +1071,31 @@ int main(int argc, char **argv) {
global.lib_ext = "a";
}

// Initialization
// Initialization
#if USE_OSX_TARGET_REAL
// TODO: could make abi specify size of real. For now, decided here for
// assuming OSX.
switch (global.params.targetTriple.getArch()) {
case llvm::Triple::x86:
case llvm::Triple::x86_64:
Real::init(false);
break;
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
Real::init(true);
break;
default:
error(Loc(), "cross compiling for '%s' not yet supported",
global.params.targetTriple.str().c_str());
fatal();
break;
}

#endif
Lexer::initLexer();
Type::init();
Id::initialize();
Expand Down
161 changes: 111 additions & 50 deletions driver/targetmachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ MipsABI::Type getMipsABI() {
#endif
if (dl.getPointerSizeInBits() == 64)
return MipsABI::N64;
#if LDC_LLVM_VER >= 309
else if (dl.getLargestLegalIntTypeSizeInBits() == 64)
#else
else if (dl.getLargestLegalIntTypeSize() == 64)
#endif
return MipsABI::N32;
else
return MipsABI::O32;
Expand Down Expand Up @@ -162,59 +166,17 @@ static std::string getX86TargetCPU(const llvm::Triple &triple) {
}

static std::string getARMTargetCPU(const llvm::Triple &triple) {
const char *result = llvm::StringSwitch<const char *>(triple.getArchName())
.Cases("armv2", "armv2a", "arm2")
.Case("armv3", "arm6")
.Case("armv3m", "arm7m")
.Case("armv4", "strongarm")
.Case("armv4t", "arm7tdmi")
.Cases("armv5", "armv5t", "arm10tdmi")
.Cases("armv5e", "armv5te", "arm1026ejs")
.Case("armv5tej", "arm926ej-s")
.Cases("armv6", "armv6k", "arm1136jf-s")
.Case("armv6j", "arm1136j-s")
.Cases("armv6z", "armv6zk", "arm1176jzf-s")
.Case("armv6t2", "arm1156t2-s")
.Cases("armv6m", "armv6-m", "cortex-m0")
.Cases("armv7", "armv7a", "armv7-a", "cortex-a8")
.Cases("armv7l", "armv7-l", "cortex-a8")
.Cases("armv7f", "armv7-f", "cortex-a9-mp")
.Cases("armv7s", "armv7-s", "swift")
.Cases("armv7r", "armv7-r", "cortex-r4")
.Cases("armv7m", "armv7-m", "cortex-m3")
.Cases("armv7em", "armv7e-m", "cortex-m4")
.Cases("armv8", "armv8a", "armv8-a", "cortex-a53")
.Case("ep9312", "ep9312")
.Case("iwmmxt", "iwmmxt")
.Case("xscale", "xscale")
// If all else failed, return the most base CPU with
// thumb interworking
// supported by LLVM.
.Default(nullptr);

if (result) {
return result;
}

return (triple.getEnvironment() == llvm::Triple::GNUEABIHF) ? "arm1176jzf-s"
: "arm7tdmi";
return triple.getARMCPUForArch();
// Note: Previous version was copy+paste from clang Tools.cpp, but was
// missing thumbs.
}

/// Returns the LLVM name of the target CPU to use given the provided
/// -mcpu argument and target triple.
static std::string getTargetCPU(const std::string &cpu,
const llvm::Triple &triple) {
if (!cpu.empty()) {
if (cpu != "native") {
return cpu;
}

// FIXME: Reject attempts to use -mcpu=native unless the target matches
// the host.
std::string hostCPU = llvm::sys::getHostCPUName();
if (!hostCPU.empty() && hostCPU != "generic") {
return hostCPU;
}
return cpu;
}

switch (triple.getArch()) {
Expand All @@ -226,11 +188,14 @@ static std::string getTargetCPU(const std::string &cpu,
case llvm::Triple::x86_64:
return getX86TargetCPU(triple);
case llvm::Triple::arm:
case llvm::Triple::thumb:
return getARMTargetCPU(triple);
// TODO: for AArch64?
}
}

static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) {
// From clang Tools.cpp
return llvm::StringSwitch<const char *>(CPU)
.Case("strongarm", "v4")
.Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t")
Expand All @@ -244,7 +209,12 @@ static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) {
.Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6")
.Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6")
.Cases("arm1156t2-s", "arm1156t2f-s", "v6t2")
#if LDC_LLVM_VER >= 308
.Cases("cortex-a5", "cortex-a8", "v7")
.Case("cortex-a7", "v7k") // watchOS uses, based on ARMTargetParser.def
#else
.Cases("cortex-a5", "cortex-a7", "cortex-a8", "v7")
#endif
.Cases("cortex-a9", "cortex-a12", "cortex-a15", "v7")
.Cases("cortex-r4", "cortex-r5", "v7r")
.Case("cortex-m0", "v6m")
Expand All @@ -257,11 +227,34 @@ static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) {
.Default("");
}

static void convertiOSTriple(llvm::Triple &triple, const std::string &cpu) {
// Need to convert armv7, etc to thumbv7.
// See clang Darwin::ComputeEffectiveClangTriple which calls
// ToolChain::ComputeLLVMTriple to see how triple is translated based on
// arch.
switch (triple.getArch()) {
default:
break;
case llvm::Triple::arm:
case llvm::Triple::thumb: {
llvm::StringRef suffix = getLLVMArchSuffixForARM(getTargetCPU(cpu, triple));
if (suffix.startswith("v6m") || suffix.startswith("v7m") ||
suffix.startswith("v7em") ||
(suffix.startswith("v7") && triple.isOSBinFormatMachO())) {
triple.setArchName("thumb" + suffix.str());
}
}
}
}

static FloatABI::Type getARMFloatABI(const llvm::Triple &triple,
const char *llvmArchSuffix) {
switch (triple.getOS()) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
#if LDC_LLVM_VER >= 308
case llvm::Triple::TvOS:
#endif
case llvm::Triple::IOS: {
// Darwin defaults to "softfp" for v6 and v7.
if (llvm::StringRef(llvmArchSuffix).startswith("v6") ||
Expand All @@ -271,6 +264,11 @@ static FloatABI::Type getARMFloatABI(const llvm::Triple &triple,
return FloatABI::Soft;
}

#if LDC_LLVM_VER >= 308
case llvm::Triple::WatchOS:
return FloatABI::Hard;
#endif

case llvm::Triple::FreeBSD:
// FreeBSD defaults to soft float
return FloatABI::Soft;
Expand Down Expand Up @@ -418,27 +416,61 @@ const llvm::Target *lookupTarget(const std::string &arch, llvm::Triple &triple,
return target;
}

std::string ldc::getDefaultTriple() {
// Default triple if nothing else specified.
//
// llvm configure --target doesn't accept ios for the operating system
// like i386-apple-ios. Can specify i386-apple-darwin, but that is
// assumed to be macosx. Clang handles this by also looking at
// -mios_simulator_version_min or -miphoneos_version_min to decide on the
// OS. We handle it by making our own default.
#ifdef IPHONEOS_DEFAULT_TRIPLE
return "i386-apple-ios";
#else
return llvm::sys::getDefaultTargetTriple();
#endif
}

llvm::TargetMachine *createTargetMachine(
std::string iosArch, // if set, targetTriple and arch not
std::string targetTriple, std::string arch, std::string cpu,
std::vector<std::string> attrs, ExplicitBitness::Type bitness,
FloatABI::Type floatABI, llvm::Reloc::Model relocModel,
FloatABI::Type floatABI,
#if LDC_LLVM_VER >= 309
llvm::Optional<llvm::Reloc::Model> relocModel,
#else
llvm::Reloc::Model relocModel,
#endif
llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel,
bool noFramePointerElim, bool noLinkerStripDead) {
if (!cpu.empty() && cpu == "native") {
// FIXME: Reject attempts to use -mcpu=native unless the target matches
// the host.
std::string hostCPU = llvm::sys::getHostCPUName();
if (!hostCPU.empty() && hostCPU != "generic") {
cpu = hostCPU;
}
}

// Determine target triple. If the user didn't explicitly specify one, use
// the one set at LLVM configure time.
llvm::Triple triple;
if (targetTriple.empty()) {
triple = llvm::Triple(llvm::sys::getDefaultTargetTriple());
triple = llvm::Triple(ldc::getDefaultTriple());

// We only support OSX, so darwin should really be macosx.
if (triple.getOS() == llvm::Triple::Darwin) {
triple.setOS(llvm::Triple::MacOSX);
}

if (!iosArch.empty()) {
triple.setArchName(iosArch);
convertiOSTriple(triple, cpu);
}
// Handle -m32/-m64.
if (sizeof(void *) == 4 && bitness == ExplicitBitness::M64) {
else if (bitness == ExplicitBitness::M64) {
triple = triple.get64BitArchVariant();
} else if (sizeof(void *) == 8 && bitness == ExplicitBitness::M32) {
} else if (bitness == ExplicitBitness::M32) {
triple = triple.get32BitArchVariant();
}
} else {
Expand Down Expand Up @@ -478,6 +510,26 @@ llvm::TargetMachine *createTargetMachine(
features.AddFeature(attr);
}

// neon instructions sometimes misaligned so disable when optimizing. Not
// sure of exact conditions but happens with llvm 3.5.1 with optimization
// turned on for thumb. Check this out again to see if it still applies
// with llvm 3.6
// TODO: resolve this
if (triple.isiOS() && triple.getArch() == llvm::Triple::thumb &&
codeGenOptLevel != llvm::CodeGenOpt::None) {
// -neon, unless explicity specified
bool neonAttr = false;
for (unsigned i = 0; i < attrs.size() && !neonAttr; ++i) {
if (attrs[i].find("neon") != std::string::npos) {
neonAttr = true;
}
}

if (!neonAttr) {
features.AddFeature("-neon");
}
}

// With an empty CPU string, LLVM will default to the host CPU, which is
// usually not what we want (expected behavior from other compilers is
// to default to "generic").
Expand All @@ -499,14 +551,22 @@ llvm::TargetMachine *createTargetMachine(
}
}

if (global.params.verbose) {
fprintf(global.stdmsg, "targeting '%s' (CPU '%s' with features '%s')\n",
triple.str().c_str(), cpu.c_str(), features.getString().c_str());
}
if (Logger::enabled()) {
Logger::println("Targeting '%s' (CPU '%s' with features '%s')",
triple.str().c_str(), cpu.c_str(),
features.getString().c_str());
}

// Handle cases where LLVM picks wrong default relocModel
#if LDC_LLVM_VER >= 309
if (!relocModel.hasValue()) {
#else
if (relocModel == llvm::Reloc::Default) {
#endif
if (triple.isOSDarwin()) {
// Darwin defaults to PIC (and as of 10.7.5/LLVM 3.1-3.3, TLS use leads
// to crashes for non-PIC code). LLVM doesn't handle this.
Expand Down Expand Up @@ -537,6 +597,7 @@ llvm::TargetMachine *createTargetMachine(
case llvm::Triple::thumb:
floatABI = getARMFloatABI(triple, getLLVMArchSuffixForARM(cpu));
break;
// TODO: something for AArch64?
}
}

Expand Down
19 changes: 16 additions & 3 deletions driver/targetmachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#ifndef LDC_DRIVER_TARGET_H
#define LDC_DRIVER_TARGET_H

#if LDC_LLVM_VER >= 309
#include "llvm/ADT/Optional.h"
#endif
#include "llvm/Support/CodeGen.h"
#include <string>
#include <vector>
Expand All @@ -31,6 +34,11 @@ namespace MipsABI {
enum Type { Unknown, O32, N32, N64, EABI };
}

namespace ldc {
// Get the default triple, normally configured into LLVM
std::string getDefaultTriple();
}

namespace llvm {
class TargetMachine;
}
Expand All @@ -42,9 +50,14 @@ class TargetMachine;
* Does not depend on any global state.
*/
llvm::TargetMachine *createTargetMachine(
std::string targetTriple, std::string arch, std::string cpu,
std::vector<std::string> attrs, ExplicitBitness::Type bitness,
FloatABI::Type floatABI, llvm::Reloc::Model relocModel,
std::string iosArch, std::string targetTriple, std::string arch,
std::string cpu, std::vector<std::string> attrs,
ExplicitBitness::Type bitness, FloatABI::Type floatABI,
#if LDC_LLVM_VER >= 309
llvm::Optional<llvm::Reloc::Model> relocModel,
#else
llvm::Reloc::Model relocModel,
#endif
llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel,
bool noFramePointerElim, bool noLinkerStripDead);

Expand Down
15 changes: 8 additions & 7 deletions gen/abi-aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ struct AArch64TargetABI : TargetABI {

Type *rt = tf->next->toBasetype();

// FIXME
if (tf->linkage == LINKd)
return rt->ty == Tsarray || rt->ty == Tstruct;
if (!isPOD(rt))
return true;

return rt->ty == Tsarray ||
(rt->ty == Tstruct && rt->size() > 16 && !isHFA((TypeStruct *)rt));
return passByVal(rt);
}

bool passByVal(Type *t) override {
Expand All @@ -60,8 +58,11 @@ struct AArch64TargetABI : TargetABI {
for (auto arg : fty.args) {
if (!arg->byref)
rewriteArgument(fty, *arg);
else if (passByVal(arg->type))
arg->attrs.remove(LLAttribute::ByVal);
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) {
fty.reverseParams = true;
}
}

Expand Down
12 changes: 6 additions & 6 deletions gen/abi-arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-arm.h"
#include "llvm/Target/TargetMachine.h"

struct ArmTargetABI : TargetABI {
HFAToArray hfaToArray;
Expand All @@ -31,14 +32,13 @@ struct ArmTargetABI : TargetABI {
return false;
Type *rt = tf->next->toBasetype();

// For extern(D), always return structs by arg because of problem with
// non-POD structs (failure in std.algorithm.move when struct has a ctor).
// TODO: figure out what the problem is
if (tf->linkage == LINKd)
return rt->ty == Tsarray || rt->ty == Tstruct;
if (!isPOD(rt))
return true;

return rt->ty == Tsarray ||
(rt->ty == Tstruct && rt->size() > 4 && !isHFA((TypeStruct *)rt));
(rt->ty == Tstruct && rt->size() > 4 &&
(gTargetMachine->Options.FloatABIType == llvm::FloatABI::Soft ||
!isHFA((TypeStruct *)rt)));
}

bool passByVal(Type *t) override {
Expand Down
178 changes: 178 additions & 0 deletions gen/abi-ios-arm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//===-- abi-ios-arm.cpp ---------------------------------------------------===//
//
// LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//

/*
The iOS ARM ABI is based on a variant of the older APCS:
http://infocenter.arm.com/help/topic/com.arm.doc.dui0041c/DUI0041C.pdf
It is highly confusing because the iOS docummentation explicitly refers to
the AAPCS in armv6 section, but clang source and LLVM mail lists says
otherwise.
https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Introduction/Introduction.html
*/

#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-ios-arm.h"

// local stuff
namespace {

bool isStructIntegerLike(TypeStruct *t) {
// To be integer-like, all fields must be addressed at offset 0
// (e.g. union or bit-fields) and must be integral type, pointer (we
// extend to D pointer-ish types like class ref or AA), or another
// integer-like struct. clang's isIntegerLikeType() in TargetInfo.cpp
// does something similar.

VarDeclarations fields = t->sym->fields;
for (size_t i = 0; i < fields.dim; ++i) {
if (fields[i]->offset != 0)
return false;

Type *ft = fields[i]->type;
if (!(ft->isintegral() || ft->ty == Tpointer || ft->ty == Tclass ||
ft->ty == Taarray ||
(ft->ty == Tstruct && isStructIntegerLike((TypeStruct *)ft))))
return false;
}

return true;
}

bool isStructSimple(TypeStruct *t) {
// Is this struct simple? From APCS 10.3.3 "a structure is considered
// integer-like if its size is less than or equal to one word, and the
// offset of each of its addressable subfields is zero. An integer-like
// structured result is considered simple and is returned in register a1
// [that is r0]."
return (t->Type::size() <= 4 && isStructIntegerLike(t));
}

// Hacked in to do ARM APCS byval rewrites like clang with correct alignment.
// This is based on something similar in abi-x86-64.cpp
struct ImplicitByvalRewrite : ABIRewrite {
LLValue *get(Type *dty, LLValue *v) override {
return DtoLoad(v, ".ImplicitByvalRewrite_getResult");
}

void getL(Type *dty, LLValue *v, LLValue *lval) override {
DtoMemCpy(lval, v);
}

LLValue *put(DValue *v) override {
// if v alignment is good enough (ACPS iOS says 4-byte alignment), can use
// as is, otherwise need to make a copy. Note that clang also makes a copy
// if v is locatd in a different address space, which we are ignoring here.
Type *dty = v->getType();
const unsigned align = DtoAlignment(dty);
if (align >= 4) {
return getAddressOf(v);
}

LLValue *originalPointer = v->getRVal();
LLType *type = originalPointer->getType()->getPointerElementType();
LLValue *copyForCallee =
DtoRawAlloca(type, 4, ".ImplicitByvalRewrite_putResult");
DtoMemCpy(copyForCallee, originalPointer);
return copyForCallee;
}

LLType *type(Type *dty, LLType *t) override { return DtoPtrToType(dty); }
};

} // end local stuff

struct IOSArmTargetABI : TargetABI {
CompositeToArray32 compositeToArray32;
ImplicitByvalRewrite byvalRewrite;

bool returnInArg(TypeFunction *tf) override {
// Return composites in an arg, however APCS 10.3.3 says simple
// integer-like structs should be returned in r0. Doesn't apply to
// non-POD structs.
if (tf->isref)
return false;

Type *rt = tf->next->toBasetype();
if (!isPOD(rt))
return true;

return ((rt->ty == Tstruct && !isStructSimple((TypeStruct *)rt)) ||
rt->ty == Tsarray);
}

bool passByVal(Type *t) override {
// APCS does not use an indirect arg to pass aggregates, however
// clang uses byval for types > 64-bytes, then llvm backend
// converts back to non-byval. Without this special handling the
// optimzer generates bad code (e.g. std.random unittest crash).

// TODO: Using below as is in abi-arm wasn't setting up proper alignment
// for iOS (needs to be byval with align 4). Revised to use
// ImplicitByvalRewrite instead. Can clean this up after determining if
// abi-arm.cpp is ok or needs this too.
#if 0
t = t->toBasetype();
return ((t->ty == Tsarray || t->ty == Tstruct) && t->size() > 64);
#else
return false;
#endif

// the codegen is horrible for arrays passed by value - tries to do
// copy without a loop for huge arrays. Would be better if byval
// could be used, but then there is the optimizer problem. Maybe can
// figure that out?
#if 0
TY ty = t->toBasetype()->ty;
return ty == Tsarray;
#endif
}

void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
for (auto arg : fty.args) {
if (!arg->byref)
rewriteArgument(fty, *arg);
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) {
fty.reverseParams = true;
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
// structs and arrays need rewrite as i32 arrays. This keeps data layout
// unchanged when passed in registers r0-r3 and is necessary to match C ABI
// for struct passing. Without out this rewrite, each field or array
// element is passed in own register. For example: char[4] now all fits in
// r0, where before it consumed r0-r3.
Type *ty = arg.type->toBasetype();

// TODO: want to also rewrite Tsarray as i32 arrays, but sometimes
// llvm selects an aligned ldrd instruction even though the ptr is
// unaligned (e.g. walking through members of array char[5][]).
// if (ty->ty == Tstruct || ty->ty == Tsarray)
if (ty->ty == Tstruct) {
if (ty->size() > 64) {
arg.rewrite = &byvalRewrite;
arg.ltype = arg.ltype->getPointerTo();
arg.attrs.addByVal(4);
} else {
arg.rewrite = &compositeToArray32;
arg.ltype = compositeToArray32.type(arg.type, arg.ltype);
}
}
}
};

TargetABI *getIOSArmTargetABI() { return new IOSArmTargetABI; }
21 changes: 21 additions & 0 deletions gen/abi-ios-arm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- gen/abi-ios-arm.h - iOS ARM ABI description -------------*- C++ -*-===//
//
// LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for iOS ARM targets.
//
//===----------------------------------------------------------------------===//

#ifndef LDC_GEN_ABI_IOS_ARM_H
#define LDC_GEN_ABI_IOS_ARM_H

struct TargetABI;

TargetABI *getIOSArmTargetABI();

#endif
156 changes: 156 additions & 0 deletions gen/abi-ios-arm64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//===-- abi-ios-arm64.cpp -------------------------------------------------===//
//
// LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The Procedure Call Standard can be found here:
// https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
//
// and here:
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
//
//===----------------------------------------------------------------------===//

#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-ios-arm64.h"

namespace {
// Rewrites a composite as an integer of the same size.
struct CompositeToInt : ABIRewrite {
LLValue *get(Type *dty, LLValue *v) {
Logger::println("rewriting integer -> %s", dty->toChars());
LLValue *mem = DtoAlloca(dty, ".int_to_composite");
DtoStore(v, DtoBitCast(mem, getPtrToType(v->getType())));
return DtoLoad(mem);
}

void getL(Type *dty, LLValue *v, LLValue *lval) {
Logger::println("rewriting integer -> %s", dty->toChars());
DtoStore(v, DtoBitCast(lval, getPtrToType(v->getType())));
}

LLValue *put(DValue *dv) {
Type *dty = dv->getType();
Logger::println("rewriting %s -> integer", dty->toChars());
LLType *t = LLIntegerType::get(gIR->context(), dty->size() * 8);
return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t)));
}

LLType *type(Type *t, LLType *) {
size_t sz = t->size() * 8;
return LLIntegerType::get(gIR->context(), sz);
}
};
} // end local stuff

struct IOSArm64TargetABI : TargetABI {
CompositeToArray64 compositeToArray64;
HFAToArray hfaToArray;
IntegerRewrite integerRewrite;
// IntegerRewrite doesn't do i128, so bring back CompositeToInt
CompositeToInt compositeToInt;
ExplicitByvalRewrite byvalRewrite;

IOSArm64TargetABI() : byvalRewrite(1) {}

bool returnInArg(TypeFunction *tf) override {
if (tf->isref)
return false;

// Should be same rule as passByValue for args
Type *rt = tf->next->toBasetype();

// When AAPCS64 returns a struct in registers, struct padding may be
// undefined which causes a problem for bit comparisons. Punt for now on
// using C-ABI for D here.
if (tf->linkage == LINKd && rt->ty == Tstruct) {
return true;
}

// return aggregates > 16 bytes in arg, except HFAs
// TODO: Tsarrays can be HFAs too, consider revising.
return rt->size() > 16 &&
(rt->ty == Tsarray || (rt->ty == Tstruct && !isHFA((TypeStruct *)rt)));
}

bool passByVal(Type *t) override {
// byval in backend is not used for this target
return false;
}

void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
// Value struct returns should be rewritten as an int type to generate
// correct register usage. HFA struct returns don't normally need to
// be rewritten (clang does not rewrite), but D unions don't seem to
// match C unions when first member is not largest (maybe that is a
// bug?), so rewrite HFAs anyway.
//
// note: sret functions change ret type to void so this won't trigger
// for those
Type *retTy = fty.ret->type->toBasetype();
if (!fty.ret->byref && retTy->ty == Tstruct) {
if (isHFA((TypeStruct *)retTy)) {
fty.ret->rewrite = &hfaToArray;
fty.ret->ltype = hfaToArray.type(fty.ret->type, fty.ret->ltype);
} else {
fty.ret->rewrite = &compositeToInt;
fty.ret->ltype = compositeToInt.type(fty.ret->type, fty.ret->ltype);
}
}

for (auto arg : fty.args) {
if (!arg->byref)
rewriteArgument(fty, *arg);
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
Type *ty = arg.type->toBasetype();
if (ty->ty == Tstruct || ty->ty == Tsarray) {
if (ty->ty == Tstruct && isHFA((TypeStruct *)ty)) {
arg.rewrite = &hfaToArray;
arg.ltype = hfaToArray.type(arg.type, arg.ltype);
} else if (ty->size() > 16) {
arg.rewrite = &byvalRewrite;
arg.ltype = arg.ltype->getPointerTo();
} else {
arg.rewrite = &compositeToArray64;
arg.ltype = compositeToArray64.type(arg.type, arg.ltype);
}
}
}

// TODO: revisit with an ABI test to see if we need to do the byvalRewrite
// as above. Need an abi test
void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args) override {
for (unsigned i = 0; i < args.size(); ++i) {
IrFuncTyArg &arg = *args[i];
if (!arg.byref) // don't rewrite ByVal arguments
{
// LLVM CallingConv::C promotes a varag float to double.
// extern(D) wants it to remain a float. I am not sure if
// this is an LLVM bug or just behavior not encountered in C
// where all vararg floats are promoted to double by the
// frontend (backend never sees).
switch (arg.type->toBasetype()->ty) {
case Tfloat32:
case Timaginary32:
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
break;
default:
rewriteArgument(fty, arg);
break;
}
}
}
}
};

// The public getter for abi.cpp
TargetABI *getIOSArm64TargetABI() { return new IOSArm64TargetABI(); }
21 changes: 21 additions & 0 deletions gen/abi-ios-arm64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- gen/abi-ios-arm64.h - iOS ARM64 ABI description ---------*- C++ -*-===//
//
// LDC - the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for iOS ARM64 (AArch64) targets.
//
//===----------------------------------------------------------------------===//

#ifndef LDC_GEN_ABI_IOS_ARM64_H
#define LDC_GEN_ABI_IOS_ARM64_H

struct TargetABI;

TargetABI *getIOSArm64TargetABI();

#endif
15 changes: 14 additions & 1 deletion gen/abi-mips64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ struct MIPS64TargetABI : TargetABI {
return false;
}

Type *rt = tf->next->toBasetype();

if (!isPOD(rt))
return true;

// Return structs and static arrays on the stack. The latter is needed
// because otherwise LLVM tries to actually return the array in a number
// of physical registers, which leads, depending on the target, to
// either horrendous codegen or backend crashes.
Type *rt = tf->next->toBasetype();
return (rt->ty == Tstruct || rt->ty == Tsarray);
}

Expand All @@ -44,11 +48,20 @@ struct MIPS64TargetABI : TargetABI {
}

void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
if (!fty.ret->byref) {
rewriteArgument(fty, *fty.ret);
}

for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) {
fty.reverseParams = true;
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
Expand Down
64 changes: 37 additions & 27 deletions gen/abi-ppc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
#include "gen/tollvm.h"

struct PPCTargetABI : TargetABI {
ExplicitByvalRewrite byvalRewrite;
CompositeToArray32 compositeToArray32;
CompositeToArray64 compositeToArray64;
IntegerRewrite integerRewrite;
const bool Is64Bit;

Expand All @@ -39,55 +40,64 @@ struct PPCTargetABI : TargetABI {
return false;
}

// Return structs and static arrays on the stack. The latter is needed
// because otherwise LLVM tries to actually return the array in a number
// of physical registers, which leads, depending on the target, to
// either horrendous codegen or backend crashes.
Type *rt = tf->next->toBasetype();
return (rt->ty == Tstruct || rt->ty == Tsarray);

// The ABI specifies that aggregates of size 8 bytes or less are
// returned in r3/r4 (ppc) or in r3 (ppc64). Looking at the IR
// generated by clang this seems not to be implemented. Regardless
// of size, the aggregate is always returned as sret.
return rt->ty == Tsarray || rt->ty == Tstruct;
}

bool passByVal(Type *t) override {
TY ty = t->toBasetype()->ty;
return ty == Tstruct || ty == Tsarray;
// On ppc, aggregates are always passed as an indirect value.
// On ppc64, they are always passed by value. However, clang
// used byval for type > 64 bytes.
t = t->toBasetype();
return (t->ty == Tsarray || t->ty == Tstruct) &&
(!Is64Bit || t->size() > 64);
}

void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
// EXPLICIT PARAMETERS
// return value
if (!fty.ret->byref) {
rewriteArgument(fty, *fty.ret);
}

// explicit parameters
for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) {
fty.reverseParams = true;
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
Type *ty = arg.type->toBasetype();

if (ty->ty == Tstruct || ty->ty == Tsarray) {
if (canRewriteAsInt(ty, Is64Bit)) {
if (!IntegerRewrite::isObsoleteFor(arg.ltype)) {
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
}
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
} else {
// these types are passed byval:
// the caller allocates a copy and then passes a pointer to the copy
arg.rewrite = &byvalRewrite;
arg.ltype = byvalRewrite.type(arg.type, arg.ltype);

// the copy is treated as a local variable of the callee
// hence add the NoAlias and NoCapture attributes
arg.attrs.clear()
.add(LLAttribute::NoAlias)
.add(LLAttribute::NoCapture)
.addAlignment(byvalRewrite.alignment(arg.type));
if (Is64Bit) {
arg.rewrite = &compositeToArray64;
arg.ltype = compositeToArray64.type(arg.type, arg.ltype);
} else {
arg.rewrite = &compositeToArray32;
arg.ltype = compositeToArray32.type(arg.type, arg.ltype);
}
}
} else if (ty->isintegral()) {
arg.attrs.add(ty->isunsigned() ? LLAttribute::ZExt : LLAttribute::SExt);
}
}
};

// The public getter for abi.cpp
TargetABI *getPPCTargetABI(bool Is64Bit) {
return new PPCTargetABI(Is64Bit);
}
TargetABI *getPPCTargetABI(bool Is64Bit) { return new PPCTargetABI(Is64Bit); }
43 changes: 13 additions & 30 deletions gen/abi-ppc64le.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,10 @@ struct PPC64LETargetABI : TargetABI {

Type *rt = tf->next->toBasetype();

// FIXME: The return value of this function translates
// to RETstack or RETregs in function retStyle(), which
// directly influences if NRVO is possible or not
// (false -> RETregs -> nrvo_can = false). Depending on
// NRVO, the postblit constructor is called or not.
// Thus using the rules of the C ABI here (as mandated by
// the D specification) leads to crashes.
if (tf->linkage == LINKd)
return rt->ty == Tsarray || rt->ty == Tstruct;
if (!isPOD(rt))
return true;

return rt->ty == Tsarray || (rt->ty == Tstruct && rt->size() > 16 &&
!isHFA((TypeStruct *)rt, nullptr, 8));
return passByVal(rt);
}

bool passByVal(Type *t) override {
Expand All @@ -56,33 +48,23 @@ struct PPC64LETargetABI : TargetABI {
}

void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
// RETURN VALUE
// return value
Type *retTy = fty.ret->type->toBasetype();
if (!fty.ret->byref) {
if (retTy->ty == Tstruct || retTy->ty == Tsarray) {
if (retTy->ty == Tstruct &&
isHFA((TypeStruct *)retTy, &fty.ret->ltype, 8)) {
fty.ret->rewrite = &hfaToArray;
fty.ret->ltype = hfaToArray.type(fty.ret->type, fty.ret->ltype);
} else if (canRewriteAsInt(retTy, true)) {
fty.ret->rewrite = &integerRewrite;
fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype);
} else {
fty.ret->rewrite = &compositeToArray64;
fty.ret->ltype =
compositeToArray64.type(fty.ret->type, fty.ret->ltype);
}
} else if (retTy->isintegral())
fty.ret->attrs.add(retTy->isunsigned() ? LLAttribute::ZExt
: LLAttribute::SExt);
rewriteArgument(fty, *fty.ret);
}

// EXPLICIT PARAMETERS
// explicit parameters
for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) {
fty.reverseParams = true;
}
}

void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
Expand All @@ -98,8 +80,9 @@ struct PPC64LETargetABI : TargetABI {
arg.rewrite = &compositeToArray64;
arg.ltype = compositeToArray64.type(arg.type, arg.ltype);
}
} else if (ty->isintegral())
} else if (ty->isintegral()) {
arg.attrs.add(ty->isunsigned() ? LLAttribute::ZExt : LLAttribute::SExt);
}
}
};

Expand Down
2 changes: 1 addition & 1 deletion gen/abi-x86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct X86TargetABI : TargetABI {
IntegerRewrite integerRewrite;

X86TargetABI()
: isOSX(global.params.targetTriple.isMacOSX()),
: isOSX(global.params.targetTriple.isOSDarwin()),
isMSVC(global.params.targetTriple.isWindowsMSVCEnvironment()) {
using llvm::Triple;
auto os = global.params.targetTriple.getOS();
Expand Down
6 changes: 6 additions & 0 deletions gen/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "id.h"
#include "gen/abi-generic.h"
#include "gen/abi-aarch64.h"
#include "gen/abi-ios-arm.h"
#include "gen/abi-ios-arm64.h"
#include "gen/abi-arm.h"
#include "gen/abi-mips64.h"
#include "gen/abi-ppc.h"
Expand Down Expand Up @@ -350,11 +352,15 @@ TargetABI *TargetABI::getTarget() {
#endif
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
if (global.params.targetTriple.isiOS())
return getIOSArm64TargetABI();
return getAArch64TargetABI();
case llvm::Triple::arm:
case llvm::Triple::armeb:
case llvm::Triple::thumb:
case llvm::Triple::thumbeb:
if (global.params.targetTriple.isiOS())
return getIOSArmTargetABI();
return getArmTargetABI();
default:
Logger::cout() << "WARNING: Unknown ABI, guessing...\n";
Expand Down
4 changes: 4 additions & 0 deletions gen/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,11 @@ void initializeArrayLiteral(IRState *p, ArrayLiteralExp *ale, LLValue *dstMem) {
auto gvar = new llvm::GlobalVariable(gIR->module, constarr->getType(),
true, LLGlobalValue::InternalLinkage,
constarr, ".arrayliteral");
#if LDC_LLVM_VER >= 309
gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
gvar->setUnnamedAddr(true);
#endif
DtoMemCpy(dstMem, gvar,
DtoConstSize_t(getTypeAllocSize(constarr->getType())));
}
Expand Down
7 changes: 3 additions & 4 deletions gen/asm-x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -2310,11 +2310,10 @@ struct AsmProcessor {
return false;
}

// OSX and 32-bit Windows need an extra leading underscore when mangling a
// symbol name.
// darwin (OSX/iOS) and 32-bit Windows need an extra leading underscore when
// mangling a symbol name.
static bool prependExtraUnderscore() {
return global.params.targetTriple.getOS() == llvm::Triple::MacOSX ||
global.params.targetTriple.getOS() == llvm::Triple::Darwin ||
return global.params.targetTriple.isOSDarwin() ||
(global.params.targetTriple.isOSWindows() &&
global.params.targetTriple.isArch32Bit());
}
Expand Down
5 changes: 5 additions & 0 deletions gen/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@ void DtoDeclareFunction(FuncDeclaration *fdecl) {
}
}

// dano hack to make sure it happens. May only need for WatchOS exception
// handling.
// TODO: find the best place for this, and whether it should only be for
// WatchOS or others
func->addFnAttr("no-frame-pointer-elim", "true");
applyFuncDeclUDAs(fdecl, func);

// main
Expand Down
30 changes: 23 additions & 7 deletions gen/llvmhelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,8 @@ void DtoResolveVariable(VarDeclaration *vd) {

llvm::GlobalVariable *gvar =
getOrCreateGlobal(vd->loc, gIR->module, DtoMemType(vd->type), isLLConst,
linkage, nullptr, llName, vd->isThreadlocal());
linkage, nullptr, llName, vd->isThreadlocal(),
vd->toChars());
getIrGlobal(vd)->value = gvar;

// Set the alignment and use the target pointer size as lower bound.
Expand Down Expand Up @@ -1642,12 +1643,11 @@ llvm::Constant *DtoConstSymbolAddress(Loc &loc, Declaration *decl) {
llvm_unreachable("Taking constant address not implemented.");
}

llvm::GlobalVariable *getOrCreateGlobal(Loc &loc, llvm::Module &module,
llvm::Type *type, bool isConstant,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *init,
llvm::StringRef name,
bool isThreadLocal) {
llvm::GlobalVariable *
getOrCreateGlobal(Loc &loc, llvm::Module &module, llvm::Type *type,
bool isConstant, llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *init, llvm::StringRef name,
bool isThreadLocal, const char *prettyName) {
llvm::GlobalVariable *existing = module.getGlobalVariable(name, true);
if (existing) {
if (existing->getType()->getElementType() != type) {
Expand All @@ -1659,6 +1659,22 @@ llvm::GlobalVariable *getOrCreateGlobal(Loc &loc, llvm::Module &module,
return existing;
}

if (isThreadLocal && global.params.vtls) {
char *p = loc.toChars();
fprintf(global.stdmsg, "%s: %s is thread local%s\n", p ? p : "",
prettyName ? prettyName : name.str().c_str(),
global.params.disableTls ? " (but TLS is disabled)" : "");
if (p) {
mem.xfree(p);
}
}

// disable thread locals if requested. This is useful if target
// OS/runtime does not support thread locals or threads
if (global.params.disableTls) {
isThreadLocal = false;
}

// Use a command line option for the thread model.
// On PPC there is only local-exec available - in this case just ignore the
// command line.
Expand Down
11 changes: 5 additions & 6 deletions gen/llvmhelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,11 @@ LLConstant *toConstantArray(LLType *ct, LLArrayType *at, T *str, size_t len,
///
/// Necessary to support multiple declarations with the same mangled name, as
/// can be the case due to pragma(mangle).
llvm::GlobalVariable *getOrCreateGlobal(Loc &loc, llvm::Module &module,
llvm::Type *type, bool isConstant,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *init,
llvm::StringRef name,
bool isThreadLocal = false);
llvm::GlobalVariable *
getOrCreateGlobal(Loc &loc, llvm::Module &module, llvm::Type *type,
bool isConstant, llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *init, llvm::StringRef name,
bool isThreadLocal = false, const char *prettyName = NULL);

FuncDeclaration *getParentFunc(Dsymbol *sym, bool stopOnStatic);

Expand Down
3 changes: 1 addition & 2 deletions gen/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
#ifndef LDC_GEN_LOGGER_H
#define LDC_GEN_LOGGER_H

#include <iosfwd>
#include <iostream>
#include <ostream>

namespace llvm {
class Type;
Expand Down
23 changes: 23 additions & 0 deletions gen/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,27 @@ static void addCoverageAnalysisInitializer(Module *m) {

static void genModuleInfo(Module *m, bool emitFullModuleInfo);

static void genFakeBitCode(IRState *p) {
// TODO: disabled because LLVM 3.9.1 EmitSpecialLLVMGlobal says these are
// "unknown special variable", detected because of AppendingLinkage.
// will need to resolve for watchOS later
#if 0
// make bitcode sections without any real contents. This seems to satisfy
// the linker
llvm::StringRef s("", 0);
LLConstant* init = llvm::ConstantDataArray::getString(p->context(), s, true);
auto llvmBitcode = new llvm::GlobalVariable(
p->module, init->getType(), true, llvm::GlobalValue::AppendingLinkage,
init, "llvm.embedded.module");
llvmBitcode->setSection("__LLVM,__bitcode");
auto llvmCmdline = new llvm::GlobalVariable(
p->module, init->getType(), true, llvm::GlobalValue::AppendingLinkage,
init, "llvm.cmdline");
llvmCmdline->setSection("__LLVM,__cmdline");
#endif
}


void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) {
assert(!irs->dmodule &&
"irs->module not null, codegen already in progress?!");
Expand All @@ -742,6 +763,8 @@ void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) {
fatal();
}

genFakeBitCode(irs);

// Skip emission of all the additional module metadata if requested by the
// user.
if (!m->noModuleInfo) {
Expand Down
10 changes: 4 additions & 6 deletions gen/naked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,11 @@ void DtoDefineNakedFunction(FuncDeclaration *fd) {
std::ostringstream tmpstr;

bool const isWin = global.params.targetTriple.isOSWindows();
bool const isOSX =
(global.params.targetTriple.getOS() == llvm::Triple::Darwin ||
global.params.targetTriple.getOS() == llvm::Triple::MacOSX);
bool const isDarwin = global.params.targetTriple.isOSDarwin();

// osx is different
// darwin (osx and iOS - includes x86 iphonesimulator) is different
// also mangling has an extra underscore prefixed
if (isOSX) {
if (isDarwin) {
std::string section = "text";
bool weak = false;
if (DtoIsTemplateInstance(fd)) {
Expand Down Expand Up @@ -225,7 +223,7 @@ void DtoDefineNakedFunction(FuncDeclaration *fd) {

// emit size after body
// llvm does this on linux, but not on osx or Win
if (!(isWin || isOSX)) {
if (!(isWin || isDarwin)) {
asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl
<< std::endl;
}
Expand Down
8 changes: 8 additions & 0 deletions gen/target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,19 @@ Expression *Target::paintAsType(Expression *e, Type *type) {
break;

case Tfloat32:
#if USE_OSX_TARGET_REAL
u.float32value = (float)e->toReal();
#else
u.float32value = e->toReal();
#endif
break;

case Tfloat64:
#if USE_OSX_TARGET_REAL
u.float64value = (double)e->toReal();
#else
u.float64value = e->toReal();
#endif
break;

default:
Expand Down
13 changes: 13 additions & 0 deletions gen/toconstelem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,13 @@ class ToConstElemVisitor : public Visitor {
//////////////////////////////////////////////////////////////////////////////

void visit(RealExp *e) override {
#if USE_OSX_TARGET_REAL
IF_LOG Logger::print("RealExp::toConstElem: %s @ %s | %La\n", e->toChars(),
e->type->toChars(), (long double)e->value);
#else
IF_LOG Logger::print("RealExp::toConstElem: %s @ %s | %La\n", e->toChars(),
e->type->toChars(), e->value);
#endif
LOG_SCOPE;
Type *t = e->type->toBasetype();
result = DtoConstFP(t, e->value);
Expand Down Expand Up @@ -204,7 +209,11 @@ class ToConstElemVisitor : public Visitor {
llvm::GlobalValue::PrivateLinkage;
gvar = new llvm::GlobalVariable(gIR->module, _init->getType(), true,
_linkage, _init, ".str");
#if LDC_LLVM_VER >= 309
gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
gvar->setUnnamedAddr(true);
#endif
(*stringLiteralCache)[key] = gvar;
}

Expand Down Expand Up @@ -568,7 +577,11 @@ class ToConstElemVisitor : public Visitor {
auto gvar = new llvm::GlobalVariable(
gIR->module, initval->getType(), canBeConst,
llvm::GlobalValue::InternalLinkage, initval, ".dynarrayStorage");
#if LDC_LLVM_VER >= 309
gvar->setUnnamedAddr(canBeConst ? llvm::GlobalValue::UnnamedAddr::Global : llvm::GlobalValue::UnnamedAddr::None);
#else
gvar->setUnnamedAddr(canBeConst);
#endif
llvm::Constant *store = DtoBitCast(gvar, getPtrToType(arrtype));

if (bt->ty == Tpointer) {
Expand Down
4 changes: 4 additions & 0 deletions gen/toir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,11 @@ class ToElemVisitor : public Visitor {
}
gvar = new llvm::GlobalVariable(gIR->module, at, true, _linkage, _init,
".str");
#if LDC_LLVM_VER >= 309
gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
gvar->setUnnamedAddr(true);
#endif
(*stringLiteralCache)[key] = gvar;
}

Expand Down
15 changes: 15 additions & 0 deletions gen/tollvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@
bool DtoIsInMemoryOnly(Type *type) {
Type *typ = type->toBasetype();
TY t = typ->ty;
#if 0
// TODO: look into fixing this. May be solution for passing structs by value.
return (t == Tsarray);
#else
return (t == Tstruct || t == Tsarray);
#endif
}

RET retStyle(TypeFunction *tf) {
Expand Down Expand Up @@ -420,7 +425,11 @@ LLConstant *DtoConstFP(Type *t, longdouble value) {

if (llty == LLType::getFloatTy(gIR->context()) ||
llty == LLType::getDoubleTy(gIR->context())) {
#if USE_OSX_TARGET_REAL
return LLConstantFP::get(llty, (double)value);
#else
return LLConstantFP::get(llty, value);
#endif
}
if (llty == LLType::getX86_FP80Ty(gIR->context())) {
uint64_t bits[] = {0, 0};
Expand All @@ -430,6 +439,7 @@ LLConstant *DtoConstFP(Type *t, longdouble value) {
return LLConstantFP::get(gIR->context(), APFloat(APFloat::x87DoubleExtended,
APInt(80, 2, bits)));
}
#if !USE_OSX_TARGET_REAL
if (llty == LLType::getFP128Ty(gIR->context())) {
union {
longdouble ld;
Expand All @@ -447,6 +457,7 @@ LLConstant *DtoConstFP(Type *t, longdouble value) {
return LLConstantFP::get(
gIR->context(), APFloat(APFloat::PPCDoubleDouble, APInt(128, 2, bits)));
}
#endif

llvm_unreachable("Unknown floating point type encountered");
}
Expand All @@ -465,7 +476,11 @@ LLConstant *DtoConstString(const char *str) {
gvar = new llvm::GlobalVariable(gIR->module, init->getType(), true,
llvm::GlobalValue::PrivateLinkage, init,
".str");
#if LDC_LLVM_VER >= 309
gvar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
gvar->setUnnamedAddr(true);
#endif
gIR->stringLiteral1ByteCache[s] = gvar;
}
LLConstant *idxs[] = {DtoConstUint(0), DtoConstUint(0)};
Expand Down
8 changes: 8 additions & 0 deletions gen/typinf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,15 @@ class LLVMDefineVisitor : public Visitor {
C = DtoConstString(
static_cast<const char *>(defaultval->toStringExp()->string));
} else if (memtype->isfloating()) {
#if USE_OSX_TARGET_REAL
if (Real::useReal64()) {
C = LLConstantFP::get(memty, (double)defaultval->toReal());
} else {
C = LLConstantFP::get(memty, (long double)defaultval->toReal());
}
#else
C = LLConstantFP::get(memty, defaultval->toReal());
#endif
} else {
llvm_unreachable("Unsupported type");
}
Expand Down
4 changes: 4 additions & 0 deletions ir/irclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,11 @@ llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,

// Thunks themselves don't have an identity, only the target
// function has.
#if LDC_LLVM_VER >= 309
thunk->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
thunk->setUnnamedAddr(true);
#endif

// create entry and end blocks
llvm::BasicBlock *beginbb =
Expand Down
11 changes: 9 additions & 2 deletions ir/irtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ llvm::Type *getReal80Type(llvm::LLVMContext &ctx) {
;

// only x86 has 80bit float - but no support with MS C Runtime!
if (anyX86 && !global.params.targetTriple.isWindowsMSVCEnvironment()) {
if (anyX86 &&
#if USE_OSX_TARGET_REAL
// Note: This is here for completeness but only makes sense if
// libc/libm are also compiled for double precision.
!Real::useReal64() &&
#endif
!global.params.targetTriple.isWindowsMSVCEnvironment()) {
return llvm::Type::getX86_FP80Ty(ctx);
}

if (anyAarch64) {
// iOS and Darwin variants use 64-bit long double
if (anyAarch64 && !global.params.targetTriple.isOSDarwin()) {
return llvm::Type::getFP128Ty(ctx);
}

Expand Down
151 changes: 147 additions & 4 deletions runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ cmake_minimum_required(VERSION 2.6)

set(DMDFE_VERSION ${D_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION})

set(IPHONEOS_ARCHS "" CACHE STRING "Build libraries for these iPhoneOS architectures")
set(WITH_CURL ON CACHE BOOL "Build phobos with std.net.curl")
set(MULTILIB OFF CACHE BOOL "Build both 32/64 bit runtime libraries")
set(BUILD_BC_LIBS OFF CACHE BOOL "Build the runtime as LLVM bitcode libraries")
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/d CACHE PATH "Path to install D modules to")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Whether to build the runtime as a shared library")
set(TARGET_C_FLAGS "" CACHE STRING "Runtime build flags for C code")
set(D_FLAGS -w CACHE STRING "Runtime build flags, separated by ;")
set(D_FLAGS_DEBUG -g;-link-debuglib CACHE STRING "Runtime build flags (debug libraries), separated by ;")
set(D_FLAGS_RELEASE -O3;-release CACHE STRING "Runtime build flags (release libraries), separated by ;")
Expand All @@ -36,6 +39,12 @@ else()
set(MULTILIB_SUFFIX 64)
endif()

# zlib needs HAVE_UNISTD_H on 64-bit iOS
check_include_file(unistd.h HAVE_UNISTD_H)
if(HAVE_UNISTD_H)
append("-DHAVE_UNISTD_H" CMAKE_C_FLAGS)
endif()

if(BUILD_SHARED_LIBS)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
message(FATAL_ERROR "Shared libraries (BUILD_SHARED_LIBS) are only supported on Linux for the time being.")
Expand Down Expand Up @@ -146,6 +155,10 @@ if(PHOBOS2_DIR)
file(GLOB_RECURSE PHOBOS2_D_INTERNAL ${PHOBOS2_DIR}/std/internal/*.d)
file(GLOB PHOBOS2_D_C ${PHOBOS2_DIR}/std/c/*.d)
file(GLOB PHOBOS2_ETC ${PHOBOS2_DIR}/etc/c/*.d)
if(NOT WITH_CURL)
list(REMOVE_ITEM PHOBOS2_D_NET ${PHOBOS2_DIR}/std/net/curl.d)
list(REMOVE_ITEM PHOBOS2_ETC ${PHOBOS2_DIR}/etc/c/curl.d)
endif()
if(APPLE)
file(GLOB PHOBOS2_D_C_SYS ${PHOBOS2_DIR}/std/c/osx/*.d)
elseif(UNIX)
Expand Down Expand Up @@ -477,22 +490,57 @@ endmacro()
macro(build_runtime_variants d_flags c_flags ld_flags path_suffix outlist_targets)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_RELEASE}"
"${c_flags}"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
""
"${path_suffix}"
${outlist_targets}
)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_DEBUG}"
"${c_flags}"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
"-debug"
"${path_suffix}"
${outlist_targets}
)
endmacro()

macro(build_runtime_and_tests d_flags c_flags ld_flags path_suffix outlist_targets outlist_test_targets)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_RELEASE}"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
""
"${path_suffix}"
${outlist_targets}
)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_DEBUG}"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
"-debug"
"${path_suffix}"
${outlist_targets}
)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_RELEASE};-unittest"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
"-unittest"
"${path_suffix}"
${outlist_test_targets}
)
build_runtime(
"${d_flags};${D_FLAGS};${D_FLAGS_DEBUG};-unittest"
"${c_flags} ${TARGET_C_FLAGS}"
"${ld_flags}"
"-unittest-debug"
"${path_suffix}"
${outlist_test_targets}
)
endmacro()

#
# Set up build targets.
#
Expand All @@ -509,7 +557,94 @@ if(BUILD_SHARED_LIBS)
else()
set(OSX_LIBEXT "a")
endif()
if(MULTILIB)

if(IPHONEOS_ARCHS)
# "all" is synonym for all vetted iOS archs
if(IPHONEOS_ARCHS STREQUAL "all")
set(IPHONEOS_ARCHS i386 x86_64 armv7 armv7s arm64)
endif()

macro(findsdk sdkname sdkpath)
execute_process(COMMAND xcrun --sdk ${sdkname} --show-sdk-path
RESULT_VARIABLE exitcode
OUTPUT_VARIABLE ${sdkpath}
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT exitcode EQUAL 0)
message(FATAL_ERROR "Can't find SDK '${sdkname}'")
endif()
endmacro()

findsdk(iphoneos dev_sdk)
findsdk(iphonesimulator sim_sdk)
# Need to unset these for newer cmake to override host detection
# and adding macosx flags
unset(CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG)
unset(CMAKE_C_SYSROOT_FLAG)
set(dev_cflags "-isysroot ${dev_sdk} -miphoneos-version-min=5.1")
set(sim_cflags "-isysroot ${sim_sdk} -mios-simulator-version-min=5.1")
set(libs)
set(testlibs)
set(unilibs)
list(LENGTH IPHONEOS_ARCHS numarchs)

foreach(arch ${IPHONEOS_ARCHS})
if(numarchs EQUAL 1)
set(libsuffix)
else()
set(libsuffix ${arch})
endif()
if(arch MATCHES "armv6|armv7|armv7s|arm64")
# Need WIP_FloatPrecIssue for some math unittests
build_runtime_and_tests("-arch;${arch};-d-version=WIP_FloatPrecIssue"
"-arch ${arch} ${dev_cflags} ${RT_CFLAGS}"
"-arch ${arch} ${dev_cflags} ${LD_FLAGS}"
"${libsuffix}" libs testlibs)
elseif(arch MATCHES "i386|x86_64")
build_runtime_and_tests("-arch;${arch}"
"-arch ${arch} ${sim_cflags} ${RT_CFLAGS}"
"-arch ${arch} ${sim_cflags} ${LD_FLAGS}"
"${libsuffix}" libs testlibs)
else()
message(FATAL_ERROR "Unknown arch '${arch}'")
endif()

# first time through, harvest name of universal lib
if(NOT unilibs)
string(REPLACE "_${arch}" "" LIBS_TO_INSTALL "${libs}")
string(REPLACE "_${arch}" "" unilibs "${testlibs}")
list(APPEND unilibs "${LIBS_TO_INSTALL}")
endif()
endforeach()

set_target_properties(${testlibs} PROPERTIES
EXCLUDE_FROM_ALL ON
EXCLUDE_FROM_DEFAULT_BUILD ON)

# maybe want to copy when numarch is 1 to prevent rebuilding libs
# when working on one arch at a time.
if(numarchs GREATER 1)
foreach(lib ${unilibs})
set(depends)
set(libarchs)
foreach(arch ${IPHONEOS_ARCHS})
list(APPEND depends "${lib}_${arch}")
list(APPEND libarchs
${CMAKE_BINARY_DIR}/lib${arch}/lib${lib}.${OSX_LIBEXT})
endforeach()
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/lib${lib}.${OSX_LIBEXT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}
COMMAND "lipo"
ARGS ${libarchs} -create -output ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/lib${lib}.${OSX_LIBEXT}
DEPENDS ${depends})
if(lib MATCHES "-unittest")
add_custom_target(${lib} DEPENDS ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/lib${lib}.${OSX_LIBEXT})
else()
add_custom_target(${lib} ALL DEPENDS ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX}/lib${lib}.${OSX_LIBEXT})
endif()
endforeach()
endif()
elseif(MULTILIB)
if(APPLE)
# On OS X, build a "fat" library.

Expand Down Expand Up @@ -705,13 +840,21 @@ macro(build_test_runner name_suffix d_flags c_flags)
endif()
endif()
endmacro()

# output_path was last left to an arch specific lib by build_runtime, need to
# change so all lib casms ends up in correct lib dir for linking
if(APPLE AND MULTILIB)
set(output_path ${CMAKE_BINARY_DIR}/lib${LIB_SUFFIX})
endif()

if(NOT IPHONEOS_ARCHS)
build_test_runner("" "${D_FLAGS_RELEASE}" "")
build_test_runner("-debug" "${D_FLAGS_DEBUG}" "")
if(MULTILIB AND ${HOST_BITNESS} EQUAL 64)
build_test_runner("-32" "${D_FLAGS_RELEASE};-m32" "-m32")
build_test_runner("-debug-32" "${D_FLAGS_DEBUG};-m32" "-m32")
endif()

endif()
# Add the druntime/Phobos test runner invocations for all the different modules.

macro(file_to_module_name file_name out_module_name)
Expand Down
2 changes: 1 addition & 1 deletion runtime/druntime
Submodule druntime updated 99 files
+3 −0 benchmark/gcbench/vdparser.extra/vdc/versions.d
+14 −0 mak/COPY
+15 −0 mak/MANIFEST
+24 −1 src/core/atomic.d
+329 −0 src/core/checkedint.d
+9 −1 src/core/demangle.d
+13 −5 src/core/runtime.d
+10 −1 src/core/stdc/config.d
+10 −1 src/core/stdc/errno.d
+147 −5 src/core/stdc/fenv.d
+10 −1 src/core/stdc/locale.d
+93 −0 src/core/stdc/math.d
+15 −3 src/core/stdc/stdarg.d
+6 −2 src/core/stdc/stdint.d
+13 −4 src/core/stdc/stdio.d
+10 −1 src/core/stdc/stdlib.d
+10 −1 src/core/stdc/string.d
+36 −3 src/core/stdc/time.d
+18 −9 src/core/sync/semaphore.d
+25 −0 src/core/sys/darwin/execinfo.d
+36 −0 src/core/sys/darwin/mach/dyld.d
+31 −0 src/core/sys/darwin/mach/getsect.d
+82 −0 src/core/sys/darwin/mach/kern_return.d
+106 −0 src/core/sys/darwin/mach/loader.d
+47 −0 src/core/sys/darwin/mach/port.d
+66 −0 src/core/sys/darwin/mach/semaphore.d
+220 −0 src/core/sys/darwin/mach/thread_act.d
+60 −0 src/core/sys/darwin/pthread.d
+25 −0 src/core/sys/darwin/sys/cdefs.d
+139 −0 src/core/sys/darwin/sys/event.d
+111 −0 src/core/sys/darwin/sys/mman.d
+1 −7 src/core/sys/osx/execinfo.d
+1 −13 src/core/sys/osx/mach/dyld.d
+1 −8 src/core/sys/osx/mach/getsect.d
+1 −58 src/core/sys/osx/mach/kern_return.d
+1 −83 src/core/sys/osx/mach/loader.d
+1 −12 src/core/sys/osx/mach/port.d
+1 −42 src/core/sys/osx/mach/semaphore.d
+1 −112 src/core/sys/osx/mach/thread_act.d
+1 −35 src/core/sys/osx/pthread.d
+1 −9 src/core/sys/osx/sys/cdefs.d
+1 −115 src/core/sys/osx/sys/event.d
+1 −95 src/core/sys/osx/sys/mman.d
+15 −0 src/core/sys/osx/sys/stat_ino32.d
+11 −2 src/core/sys/posix/arpa/inet.d
+114 −9 src/core/sys/posix/dirent.d
+10 −1 src/core/sys/posix/dlfcn.d
+10 −1 src/core/sys/posix/fcntl.d
+12 −3 src/core/sys/posix/grp.d
+10 −1 src/core/sys/posix/net/if_.d
+10 −1 src/core/sys/posix/netdb.d
+12 −3 src/core/sys/posix/netinet/in_.d
+10 −1 src/core/sys/posix/netinet/tcp.d
+10 −1 src/core/sys/posix/poll.d
+20 −11 src/core/sys/posix/pthread.d
+12 −3 src/core/sys/posix/pwd.d
+12 −3 src/core/sys/posix/sched.d
+12 −2 src/core/sys/posix/semaphore.d
+19 −16 src/core/sys/posix/signal.d
+10 −1 src/core/sys/posix/stdio.d
+12 −3 src/core/sys/posix/stdlib.d
+10 −1 src/core/sys/posix/sys/ioctl.d
+10 −1 src/core/sys/posix/sys/ipc.d
+17 −8 src/core/sys/posix/sys/mman.d
+12 −3 src/core/sys/posix/sys/resource.d
+25 −1 src/core/sys/posix/sys/select.d
+10 −1 src/core/sys/posix/sys/shm.d
+12 −3 src/core/sys/posix/sys/socket.d
+98 −30 src/core/sys/posix/sys/stat.d
+10 −1 src/core/sys/posix/sys/time.d
+18 −11 src/core/sys/posix/sys/types.d
+10 −1 src/core/sys/posix/sys/uio.d
+10 −1 src/core/sys/posix/sys/un.d
+10 −1 src/core/sys/posix/sys/utsname.d
+11 −2 src/core/sys/posix/sys/wait.d
+10 −1 src/core/sys/posix/syslog.d
+11 −2 src/core/sys/posix/termios.d
+14 −5 src/core/sys/posix/time.d
+13 −4 src/core/sys/posix/unistd.d
+10 −1 src/core/sys/posix/utime.d
+108 −31 src/core/thread.d
+16 −2 src/core/threadasm.S
+19 −10 src/core/time.d
+16 −2 src/gc/os.d
+1 −1 src/ldc/arrayinit.d
+4 −0 src/ldc/eh/common.d
+22 −0 src/ldc/eh/libunwind.d
+1 −1 src/ldc/intrinsics.di
+14 −0 src/ldc/osx_tls.c
+163 −0 src/ldc/xyzzy.d
+10 −1 src/rt/alloca.d
+10 −1 src/rt/deh_win64_posix.d
+10 −1 src/rt/qsort.d
+10 −1 src/rt/sections.d
+6 −1 src/rt/sections_elf_shared.d
+21 −7 src/rt/sections_ldc.d
+14 −5 src/rt/sections_osx.d
+34 −0 win32.mak
+34 −0 win64.mak
2 changes: 1 addition & 1 deletion runtime/phobos
2 changes: 1 addition & 1 deletion tests/d2/dmd-testsuite
Submodule dmd-testsuite updated 2 files
+42 −0 runnable/testtypeid.d
+5 −2 runnable/xtest46.d
15 changes: 14 additions & 1 deletion utils/FileCheck-3.9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ InputFilename("input-file", cl::desc("File to check (defaults to stdin)"),
static cl::list<std::string>
CheckPrefixes("check-prefix",
cl::desc("Prefix to use from check file (defaults to 'CHECK')"));
static cl::alias CheckPrefixesAlias(
"check-prefixes", cl::aliasopt(CheckPrefixes), cl::CommaSeparated,
cl::NotHidden,
cl::desc(
"Alias for -check-prefix permitting multiple comma separated values"));

static cl::opt<bool>
NoCanonicalizeWhiteSpace("strict-whitespace",
Expand Down Expand Up @@ -1298,8 +1303,15 @@ static void AddCheckPrefixIfNeeded() {
CheckPrefixes.push_back("CHECK");
}

static void DumpCommandLine(int argc, char **argv) {
errs() << "FileCheck command line: ";
for (int I = 0; I < argc; I++)
errs() << " " << argv[I];
errs() << "\n";
}

int main(int argc, char **argv) {
sys::PrintStackTraceOnErrorSignal();
sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
cl::ParseCommandLineOptions(argc, argv);

Expand Down Expand Up @@ -1331,6 +1343,7 @@ int main(int argc, char **argv) {

if (File->getBufferSize() == 0 && !AllowEmptyInput) {
errs() << "FileCheck error: '" << InputFilename << "' is empty.\n";
DumpCommandLine(argc, argv);
return 2;
}

Expand Down
1,416 changes: 1,416 additions & 0 deletions utils/FileCheck-4.0.cpp

Large diffs are not rendered by default.