Disallow some vector ops and fix integral vector identity comparisons

Bail out on unsupported vector ops (not checking the rhs type for binops
though) in the frontend instead of causing LLVM errors.

Also precompute the target's critical section size once at startup, like
DMD.
This commit is contained in:
Martin Kinkelin 2019-05-24 23:08:58 +02:00
parent 8dd4f5f893
commit a44c78fb88
6 changed files with 189 additions and 117 deletions

View file

@ -103,30 +103,22 @@ version (IN_LLVM)
// implemented in gen/target.cpp:
void _init(ref const Param params);
// unused: void deinitialize();
uint alignsize(Type type);
uint fieldalign(Type type);
uint critsecsize();
uint critsecsize()
{
if (criticalSectionSize == 0)
{
import dmd.errors;
error(Loc.initial, "Unknown critical section size");
fatal();
}
return criticalSectionSize;
}
Type va_listType();
int isVectorTypeSupported(int sz, Type type);
bool isVectorOpSupported(Type type, TOK op, Type t2 = null);
const(char)* toCppMangle(Dsymbol s)
{
if (isTargetWindowsMSVC())
return toCppMangleMSVC(s);
else
return toCppMangleItanium(s);
}
const(char)* cppTypeInfoMangle(ClassDeclaration cd)
{
if (isTargetWindowsMSVC())
return cppTypeInfoMangleMSVC(cd);
else
return cppTypeInfoMangleItanium(cd);
}
const(char)* cppTypeMangle(Type t);
}
else // !IN_LLVM
{
@ -362,6 +354,7 @@ else // !IN_LLVM
assert(0);
}
}
} // !IN_LLVM
/**
* Checks whether the target supports a vector type.
@ -376,7 +369,8 @@ else // !IN_LLVM
*/
extern (C++) int isVectorTypeSupported(int sz, Type type)
{
if (!global.params.is64bit && !global.params.isOSX)
// LDC_FIXME: Is it possible to query the LLVM target about supported vectors?
if (!IN_LLVM && !global.params.is64bit && !global.params.isOSX)
return 1; // not supported
switch (type.ty)
{
@ -395,7 +389,7 @@ else // !IN_LLVM
default:
return 2; // wrong base type
}
if (sz != 16 && !(global.params.cpu >= CPU.avx && sz == 32))
if (!IN_LLVM && sz != 16 && !(global.params.cpu >= CPU.avx && sz == 32))
return 3; // wrong size
return 0;
}
@ -409,7 +403,7 @@ else // !IN_LLVM
* Returns:
* true if the operation is supported or type is not a vector
*/
extern (C++) bool isVectorOpSupported(Type type, ubyte op, Type t2 = null)
extern (C++) bool isVectorOpSupported(Type type, TOK op, Type t2 = null)
{
import dmd.tokens;
@ -417,6 +411,11 @@ else // !IN_LLVM
return true; // not a vector op
auto tvec = cast(TypeVector) type;
// LDC_FIXME:
// Most of the binops only work with `t2` being the same IR type as `tvec`
// (LLVM restriction). We'd need to be more strict here and/or convert
// the rhs to a matching type during codegen (e.g., promote scalars to
// vectors).
bool supported;
switch (op)
{
@ -424,12 +423,24 @@ else // !IN_LLVM
supported = tvec.isscalar();
break;
version (IN_LLVM)
{
case TOK.lessThan, TOK.greaterThan, TOK.lessOrEqual, TOK.greaterOrEqual:
supported = false;
break;
case TOK.equal, TOK.notEqual, TOK.identity, TOK.notIdentity:
supported = true;
break;
}
else
{
case TOK.lessThan, TOK.greaterThan, TOK.lessOrEqual, TOK.greaterOrEqual, TOK.equal, TOK.notEqual, TOK.identity, TOK.notIdentity:
supported = false;
break;
}
case TOK.leftShift, TOK.leftShiftAssign, TOK.rightShift, TOK.rightShiftAssign, TOK.unsignedRightShift, TOK.unsignedRightShiftAssign:
supported = false;
supported = IN_LLVM && tvec.isintegral();
break;
case TOK.add, TOK.addAssign, TOK.min, TOK.minAssign:
@ -437,6 +448,12 @@ else // !IN_LLVM
break;
case TOK.mul, TOK.mulAssign:
version (IN_LLVM)
{
supported = tvec.isscalar();
}
else
{
// only floats and short[8]/ushort[8] (PMULLW)
if (tvec.isfloating() || tvec.elementType().size(Loc.initial) == 2 ||
// int[4]/uint[4] with SSE4.1 (PMULLD)
@ -444,14 +461,15 @@ else // !IN_LLVM
supported = true;
else
supported = false;
}
break;
case TOK.div, TOK.divAssign:
supported = tvec.isfloating();
supported = IN_LLVM ? tvec.isscalar() : tvec.isfloating();
break;
case TOK.mod, TOK.modAssign:
supported = false;
supported = IN_LLVM && tvec.isscalar();
break;
case TOK.and, TOK.andAssign, TOK.or, TOK.orAssign, TOK.xor, TOK.xorAssign:
@ -487,12 +505,22 @@ else // !IN_LLVM
*/
extern (C++) const(char)* toCppMangle(Dsymbol s)
{
version (IN_LLVM)
{
if (isTargetWindowsMSVC())
return toCppMangleMSVC(s);
else
return toCppMangleItanium(s);
}
else
{
static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.DragonFlyBSD || TARGET.Solaris)
return toCppMangleItanium(s);
else static if (TARGET.Windows)
return toCppMangleMSVC(s);
else
static assert(0, "fix this");
}
}
/**
@ -504,12 +532,22 @@ else // !IN_LLVM
*/
extern (C++) const(char)* cppTypeInfoMangle(ClassDeclaration cd)
{
version (IN_LLVM)
{
if (isTargetWindowsMSVC())
return cppTypeInfoMangleMSVC(cd);
else
return cppTypeInfoMangleItanium(cd);
}
else
{
static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD)
return cppTypeInfoMangleItanium(cd);
else static if (TARGET.Windows)
return cppTypeInfoMangleMSVC(cd);
else
static assert(0, "fix this");
}
}
/**
@ -520,11 +558,17 @@ else // !IN_LLVM
* string if type is mangled specially on target
* null if unhandled
*/
version (IN_LLVM)
{
extern (C++) const(char)* cppTypeMangle(Type t);
}
else
{
extern (C++) const(char)* cppTypeMangle(Type t)
{
return null;
}
} // !IN_LLVM
}
/**
* Get the type that will really be used for passing the given argument

View file

@ -384,7 +384,7 @@ LLValue *DtoBinFloatsEquals(Loc &loc, DValue *lhs, DValue *rhs, TOK op) {
LLValue *mergeVectorEquals(LLValue *resultsVector, TOK op) {
// `resultsVector` is a vector of i1 values, the pair-wise results.
// Bitcast to an integer and checks the bits via additional integer
// Bitcast to an integer and check the bits via additional integer
// comparison.
const auto sizeInBits = getTypeBitSize(resultsVector->getType());
LLType *integerType = LLType::getIntNTy(gIR->context(), sizeInBits);

View file

@ -31,6 +31,63 @@ TypeTuple *toArgTypes(Type *t);
// in dmd/argtypes_sysv_x64.d:
TypeTuple *toArgTypes_sysv_x64(Type *t);
namespace {
/******************************
* Return size of alias Mutex in druntime/src/rt/monitor_.d, or, more precisely,
* the size of the native critical section as 2nd field in struct
* D_CRITICAL_SECTION (after a pointer). D_CRITICAL_SECTION is pointer-size
* aligned, so the returned field size is a multiple of pointer-size.
*/
unsigned getCriticalSectionSize(const Param &params) {
const bool is64bit = params.is64bit;
// Windows: sizeof(CRITICAL_SECTION)
if (params.isWindows)
return is64bit ? 40 : 24;
// POSIX: sizeof(pthread_mutex_t)
// based on druntime/src/core/sys/posix/sys/types.d
const auto &triple = *params.targetTriple;
const auto arch = triple.getArch();
switch (triple.getOS()) {
case llvm::Triple::Linux:
if (triple.getEnvironment() == llvm::Triple::Android) {
// 32-bit integer rounded up to pointer size
return gDataLayout->getPointerSize();
}
if (arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be)
return 48;
return is64bit ? 40 : 24;
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
return is64bit ? 64 : 44;
case llvm::Triple::NetBSD:
return is64bit ? 48 : 28;
case llvm::Triple::FreeBSD:
case llvm::Triple::OpenBSD:
case llvm::Triple::DragonFly:
return gDataLayout->getPointerSize();
case llvm::Triple::Solaris:
return 24;
default:
break;
}
#ifndef _MSC_VER
unsigned hostSize = sizeof(pthread_mutex_t);
warning(Loc(), "Assuming critical section size = %u bytes", hostSize);
return hostSize;
#else
return 0;
#endif
}
} // anonymous namespace
void Target::_init(const Param &params) {
CTFloat::initialize();
@ -39,26 +96,23 @@ void Target::_init(const Param &params) {
RealProperties._init();
const auto &triple = *params.targetTriple;
const bool isMSVC = triple.isWindowsMSVCEnvironment();
llvm::Type *const real = DtoType(Type::basic[Tfloat80]);
ptrsize = gDataLayout->getPointerSize();
llvm::Type *const real = DtoType(Type::basic[Tfloat80]);
realsize = gDataLayout->getTypeAllocSize(real);
realpad = realsize - gDataLayout->getTypeStoreSize(real);
realalignsize = gDataLayout->getABITypeAlignment(real);
// according to DMD, only for MSVC++:
reverseCppOverloads = triple.isWindowsMSVCEnvironment();
cppExceptions = true;
c_longsize =
global.params.is64bit && !triple.isWindowsMSVCEnvironment() ? 8 : 4;
c_long_doublesize = realsize;
classinfosize = 0; // unused
maxStaticDataSize = std::numeric_limits<unsigned long long>::max();
twoDtorInVtable = !triple.isWindowsMSVCEnvironment();
c_longsize = global.params.is64bit && !isMSVC ? 8 : 4;
c_long_doublesize = realsize;
criticalSectionSize = getCriticalSectionSize(params);
reverseCppOverloads = isMSVC; // according to DMD, only for MSVC++
cppExceptions = true;
twoDtorInVtable = !isMSVC;
// Finalize RealProperties for the target's `real` type.
@ -134,83 +188,8 @@ unsigned Target::alignsize(Type *type) {
*/
unsigned Target::fieldalign(Type *type) { return DtoAlignment(type); }
/******************************
* Return size of alias Mutex in druntime/src/rt/monitor_.d, or, more precisely,
* the size of the native critical section as 2nd field in struct
* D_CRITICAL_SECTION (after a pointer). D_CRITICAL_SECTION is pointer-size
* aligned, so the returned field size is a multiple of pointer-size.
*/
unsigned Target::critsecsize() {
const bool is64bit = global.params.is64bit;
// Windows: sizeof(CRITICAL_SECTION)
if (global.params.isWindows)
return is64bit ? 40 : 24;
// POSIX: sizeof(pthread_mutex_t)
// based on druntime/src/core/sys/posix/sys/types.d
const auto &triple = *global.params.targetTriple;
const auto arch = triple.getArch();
switch (triple.getOS()) {
case llvm::Triple::Linux:
if (triple.getEnvironment() == llvm::Triple::Android)
return ptrsize; // 32-bit integer rounded up to pointer size
if (arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be)
return 48;
return is64bit ? 40 : 24;
case llvm::Triple::MacOSX:
return is64bit ? 64 : 44;
case llvm::Triple::NetBSD:
return is64bit ? 48 : 28;
case llvm::Triple::FreeBSD:
case llvm::Triple::OpenBSD:
case llvm::Triple::DragonFly:
return ptrsize;
case llvm::Triple::Solaris:
return 24;
default:
break;
}
#ifndef _MSC_VER
unsigned hostSize = sizeof(pthread_mutex_t);
warning(Loc(), "Assuming critical section size = %u bytes", hostSize);
return hostSize;
#else
error(Loc(), "Unknown critical section size");
fatal();
return 0;
#endif
}
Type *Target::va_listType() { return gABI->vaListType(); }
/******************************
* Check if the given type is supported for this target
* 0: supported
* 1: not supported
* 2: wrong size
* 3: wrong base type
*/
int Target::isVectorTypeSupported(int sz, Type *type) {
// FIXME: Is it possible to query the LLVM target about supported vectors?
return 0;
}
/******************************
* Checks whether the target supports operation `op` for vectors of type `type`.
* For binary ops `t2` is the type of the 2nd operand.
*/
bool Target::isVectorOpSupported(Type *type, TOK op, Type *t2) {
// FIXME
return true;
}
/**
* Gets vendor-specific type mangling for C++ ABI.
* Params:

View file

@ -1974,6 +1974,10 @@ public:
assert(lv->getType() == rv->getType());
eval = (e->op == TOKidentity) ? p->ir->CreateICmpEQ(lv, rv)
: p->ir->CreateICmpNE(lv, rv);
if (t1->ty == Tvector) {
eval = mergeVectorEquals(eval,
e->op == TOKidentity ? TOKequal : TOKnotequal);
}
}
result = new DImValue(e->type, eval);
}

View file

@ -0,0 +1,45 @@
// REQUIRES: atleast_llvm500
// RUN: %ldc -run %s
import core.simd;
void main()
{
static void testGenericOps(T)()
{
const T v = [ 1, -2, 3, -4 ];
assert(-v == [ -1, 2, -3, 4 ]);
assert(+v == v);
T v2 = v;
assert(v2 == v && !(v2 != v));
assert(v2 is v && !(v2 !is v));
v2[0] = 0;
assert(v2 != v && !(v2 == v));
assert(v2 !is v && !(v2 is v));
assert(v + v == [ 2, -4, 6, -8 ]);
assert(v - v == T(0));
assert(v * v == [ 1, 4, 9, 16 ]);
assert(v / v == T(1));
assert(v % T(3) == [ 1, -2, 0, -1 ]);
}
testGenericOps!float4();
testGenericOps!int4();
const float4 nan = float.nan;
assert(nan != nan && !(nan == nan));
assert(nan is nan && !(nan !is nan));
const int4 i = [ 1, 2, 3, 4 ];
assert(i << i == [ 2, 8, 24, 64 ]);
const int4 a = [ 0b1, 0b10, 0b101, 0b100011 ];
const int4 b = 0b110;
assert((a & b) == [ 0, 0b10, 0b100, 0b10 ]);
assert((a | b) == [ 0b111, 0b110, 0b111, 0b100111 ]);
assert((a ^ b) == [ 0b111, 0b100, 0b11, 0b100101 ]);
assert(~a == [ ~0b1, ~0b10, ~0b101, ~0b100011 ]);
}

@ -1 +1 @@
Subproject commit 4db6a6265a36ba49dd06a3fe2f272b634b6cec6e
Subproject commit bc1f35b522f5194d8b3b5afb462cc528a324fd17