#include "gtest/gtest.h"
+#include <tuple>
+
using namespace rr;
int reference(int *p, int y)
delete routine;
}
+TEST(ReactorUnitTests, Unreachable)
+{
+ Routine *routine = nullptr;
+
+ {
+ Function<Int(Int)> function;
+ {
+ Int a = function.Arg<0>();
+ Int z = 4;
+
+ Return(a + z);
+
+ // Code beyond this point is unreachable but should not cause any
+ // compilation issues.
+
+ z += a;
+ }
+
+ routine = function("one");
+
+ if(routine)
+ {
+ int (*callable)(int) = (int(*)(int))routine->getEntry();
+ int result = callable(16);
+ EXPECT_EQ(result, 20);
+ }
+ }
+
+ delete routine;
+}
+
+TEST(ReactorUnitTests, VariableAddress)
+{
+ Routine *routine = nullptr;
+
+ {
+ Function<Int(Int)> function;
+ {
+ Int a = function.Arg<0>();
+ Int z = 0;
+ Pointer<Int> p = &z;
+ *p = 4;
+
+ Return(a + z);
+ }
+
+ routine = function("one");
+
+ if(routine)
+ {
+ int (*callable)(int) = (int(*)(int))routine->getEntry();
+ int result = callable(16);
+ EXPECT_EQ(result, 20);
+ }
+ }
+
+ delete routine;
+}
+
TEST(ReactorUnitTests, SubVectorLoadStore)
{
Routine *routine = nullptr;
{
Pointer<Byte> out = function.Arg<0>();
- *Pointer<Short4>(out + 8 * 0) =
- MulHigh(Short4(0x1aa, 0x2dd, 0x3ee, 0xF422),
- Short4(0x1bb, 0x2cc, 0x3ff, 0xF411));
- *Pointer<UShort4>(out + 8 * 1) =
- MulHigh(UShort4(0x1aa, 0x2dd, 0x3ee, 0xF422),
- UShort4(0x1bb, 0x2cc, 0x3ff, 0xF411));
+ *Pointer<Short4>(out + 16 * 0) =
+ MulHigh(Short4(0x01AA, 0x02DD, 0x03EE, 0xF422),
+ Short4(0x01BB, 0x02CC, 0x03FF, 0xF411));
+ *Pointer<UShort4>(out + 16 * 1) =
+ MulHigh(UShort4(0x01AA, 0x02DD, 0x03EE, 0xF422),
+ UShort4(0x01BB, 0x02CC, 0x03FF, 0xF411));
+
+ *Pointer<Int4>(out + 16 * 2) =
+ MulHigh(Int4(0x000001AA, 0x000002DD, 0xC8000000, 0xF8000000),
+ Int4(0x000001BB, 0x84000000, 0x000003EE, 0xD7000000));
+ *Pointer<UInt4>(out + 16 * 3) =
+ MulHigh(UInt4(0x000001AAu, 0x000002DDu, 0xC8000000u, 0xD8000000u),
+ UInt4(0x000001BBu, 0x84000000u, 0x000003EEu, 0xD7000000u));
+
+ *Pointer<Int4>(out + 16 * 4) =
+ MulHigh(Int4(0x7FFFFFFF, 0x7FFFFFFF, 0x80008000, 0xFFFFFFFF),
+ Int4(0x7FFFFFFF, 0x80000000, 0x80008000, 0xFFFFFFFF));
+ *Pointer<UInt4>(out + 16 * 5) =
+ MulHigh(UInt4(0x7FFFFFFFu, 0x7FFFFFFFu, 0x80008000u, 0xFFFFFFFFu),
+ UInt4(0x7FFFFFFFu, 0x80000000u, 0x80008000u, 0xFFFFFFFFu));
+
+ // (U)Short8 variants currently unimplemented.
- // (U)Short8 variants are mentioned but unimplemented
Return(0);
}
if(routine)
{
- unsigned int out[2][2];
+ unsigned int out[6][4];
memset(&out, 0, sizeof(out));
callable(&out);
EXPECT_EQ(out[0][0], 0x00080002u);
- EXPECT_EQ(out[0][1], 0x008D000fu);
+ EXPECT_EQ(out[0][1], 0x008D000Fu);
EXPECT_EQ(out[1][0], 0x00080002u);
- EXPECT_EQ(out[1][1], 0xe8C0000Fu);
+ EXPECT_EQ(out[1][1], 0xE8C0000Fu);
+
+ EXPECT_EQ(out[2][0], 0x00000000u);
+ EXPECT_EQ(out[2][1], 0xFFFFFE9Cu);
+ EXPECT_EQ(out[2][2], 0xFFFFFF23u);
+ EXPECT_EQ(out[2][3], 0x01480000u);
+
+ EXPECT_EQ(out[3][0], 0x00000000u);
+ EXPECT_EQ(out[3][1], 0x00000179u);
+ EXPECT_EQ(out[3][2], 0x00000311u);
+ EXPECT_EQ(out[3][3], 0xB5680000u);
+
+ EXPECT_EQ(out[4][0], 0x3FFFFFFFu);
+ EXPECT_EQ(out[4][1], 0xC0000000u);
+ EXPECT_EQ(out[4][2], 0x3FFF8000u);
+ EXPECT_EQ(out[4][3], 0x00000000u);
+
+ EXPECT_EQ(out[5][0], 0x3FFFFFFFu);
+ EXPECT_EQ(out[5][1], 0x3FFFFFFFu);
+ EXPECT_EQ(out[5][2], 0x40008000u);
+ EXPECT_EQ(out[5][3], 0xFFFFFFFEu);
}
}
delete routine;
}
+// Check that a complex generated function which utilizes all 8 or 16 XMM
+// registers computes the correct result.
+// (Note that due to MSC's lack of support for inline assembly in x64,
+// this test does not actually check that the register contents are
+// preserved, just that the generated function computes the correct value.
+// It's necessary to inspect the registers in a debugger to actually verify.)
+TEST(ReactorUnitTests, PreserveXMMRegisters)
+{
+ Routine *routine = nullptr;
+
+ {
+ Function<Void(Pointer<Byte>, Pointer<Byte>)> function;
+ {
+ Pointer<Byte> in = function.Arg<0>();
+ Pointer<Byte> out = function.Arg<1>();
+
+ Float4 a = *Pointer<Float4>(in + 16 * 0);
+ Float4 b = *Pointer<Float4>(in + 16 * 1);
+ Float4 c = *Pointer<Float4>(in + 16 * 2);
+ Float4 d = *Pointer<Float4>(in + 16 * 3);
+ Float4 e = *Pointer<Float4>(in + 16 * 4);
+ Float4 f = *Pointer<Float4>(in + 16 * 5);
+ Float4 g = *Pointer<Float4>(in + 16 * 6);
+ Float4 h = *Pointer<Float4>(in + 16 * 7);
+ Float4 i = *Pointer<Float4>(in + 16 * 8);
+ Float4 j = *Pointer<Float4>(in + 16 * 9);
+ Float4 k = *Pointer<Float4>(in + 16 * 10);
+ Float4 l = *Pointer<Float4>(in + 16 * 11);
+ Float4 m = *Pointer<Float4>(in + 16 * 12);
+ Float4 n = *Pointer<Float4>(in + 16 * 13);
+ Float4 o = *Pointer<Float4>(in + 16 * 14);
+ Float4 p = *Pointer<Float4>(in + 16 * 15);
+
+ Float4 ab = a + b;
+ Float4 cd = c + d;
+ Float4 ef = e + f;
+ Float4 gh = g + h;
+ Float4 ij = i + j;
+ Float4 kl = k + l;
+ Float4 mn = m + n;
+ Float4 op = o + p;
+
+ Float4 abcd = ab + cd;
+ Float4 efgh = ef + gh;
+ Float4 ijkl = ij + kl;
+ Float4 mnop = mn + op;
+
+ Float4 abcdefgh = abcd + efgh;
+ Float4 ijklmnop = ijkl + mnop;
+ Float4 sum = abcdefgh + ijklmnop;
+ *Pointer<Float4>(out) = sum;
+ Return();
+ }
+
+ routine = function("one");
+ assert(routine);
+
+ float input[64] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ -1.0f, 1.0f, -1.0f, 0.0f,
+ 1.0f, 2.0f, -2.0f, 0.0f,
+ -1.0f, 3.0f, -3.0f, 0.0f,
+ 1.0f, 4.0f, -4.0f, 0.0f,
+ -1.0f, 5.0f, -5.0f, 0.0f,
+ 1.0f, 6.0f, -6.0f, 0.0f,
+ -1.0f, 7.0f, -7.0f, 0.0f,
+ 1.0f, 8.0f, -8.0f, 0.0f,
+ -1.0f, 9.0f, -9.0f, 0.0f,
+ 1.0f, 10.0f, -10.0f, 0.0f,
+ -1.0f, 11.0f, -11.0f, 0.0f,
+ 1.0f, 12.0f, -12.0f, 0.0f,
+ -1.0f, 13.0f, -13.0f, 0.0f,
+ 1.0f, 14.0f, -14.0f, 0.0f,
+ -1.0f, 15.0f, -15.0f, 0.0f };
+
+ float result[4];
+ void (*callable)(float*, float*) = (void(*)(float*,float*))routine->getEntry();
+
+ callable(input, result);
+
+ EXPECT_EQ(result[0], 0.0f);
+ EXPECT_EQ(result[1], 120.0f);
+ EXPECT_EQ(result[2], -120.0f);
+ EXPECT_EQ(result[3], 0.0f);
+ }
+
+ delete routine;
+}
+
+template <typename T>
+class CToReactorCastTest : public ::testing::Test
+{
+public:
+ using CType = typename std::tuple_element<0, T>::type;
+ using ReactorType = typename std::tuple_element<1, T>::type;
+};
+
+using CToReactorCastTestTypes = ::testing::Types
+ < // Subset of types that can be used as arguments.
+ // std::pair<bool, Bool>, FIXME(capn): Not supported as argument type by Subzero.
+ // std::pair<uint8_t, Byte>, FIXME(capn): Not supported as argument type by Subzero.
+ // std::pair<int8_t, SByte>, FIXME(capn): Not supported as argument type by Subzero.
+ // std::pair<int16_t, Short>, FIXME(capn): Not supported as argument type by Subzero.
+ // std::pair<uint16_t, UShort>, FIXME(capn): Not supported as argument type by Subzero.
+ std::pair<int, Int>,
+ std::pair<unsigned int, UInt>,
+ std::pair<float, Float>
+ >;
+
+TYPED_TEST_CASE(CToReactorCastTest, CToReactorCastTestTypes);
+
+TYPED_TEST(CToReactorCastTest, Casts)
+{
+ using CType = typename TestFixture::CType;
+ using ReactorType = typename TestFixture::ReactorType;
+
+ Routine *routine = nullptr;
+
+ {
+ Function< Int(ReactorType) > function;
+ {
+ ReactorType a = function.template Arg<0>();
+ ReactorType b = CType{};
+ RValue<ReactorType> c = RValue<ReactorType>(CType{});
+ Bool same = (a == b) && (a == c);
+ Return(IfThenElse(same, Int(1), Int(0))); // TODO: Ability to use Bools as return values.
+ }
+
+ routine = function("one");
+
+ if(routine)
+ {
+ auto callable = (int(*)(CType))routine->getEntry();
+ CType in = {};
+ EXPECT_EQ(callable(in), 1);
+ }
+ }
+
+ delete routine;
+}
+
+template <typename T>
+class GEPTest : public ::testing::Test
+{
+public:
+ using CType = typename std::tuple_element<0, T>::type;
+ using ReactorType = typename std::tuple_element<1, T>::type;
+};
+
+using GEPTestTypes = ::testing::Types
+ <
+ std::pair<bool, Bool>,
+ std::pair<int8_t, Byte>,
+ std::pair<int8_t, SByte>,
+ std::pair<int8_t[4], Byte4>,
+ std::pair<int8_t[4], SByte4>,
+ std::pair<int8_t[8], Byte8>,
+ std::pair<int8_t[8], SByte8>,
+ std::pair<int8_t[16], Byte16>,
+ std::pair<int8_t[16], SByte16>,
+ std::pair<int16_t, Short>,
+ std::pair<int16_t, UShort>,
+ std::pair<int16_t[2], Short2>,
+ std::pair<int16_t[2], UShort2>,
+ std::pair<int16_t[4], Short4>,
+ std::pair<int16_t[4], UShort4>,
+ std::pair<int16_t[8], Short8>,
+ std::pair<int16_t[8], UShort8>,
+ std::pair<int, Int>,
+ std::pair<int, UInt>,
+ std::pair<int[2], Int2>,
+ std::pair<int[2], UInt2>,
+ std::pair<int[4], Int4>,
+ std::pair<int[4], UInt4>,
+ std::pair<int64_t, Long>,
+ std::pair<int16_t, Half>,
+ std::pair<float, Float>,
+ std::pair<float[2], Float2>,
+ std::pair<float[4], Float4>
+ >;
+
+TYPED_TEST_CASE(GEPTest, GEPTestTypes);
+
+TYPED_TEST(GEPTest, PtrOffsets)
+{
+ using CType = typename TestFixture::CType;
+ using ReactorType = typename TestFixture::ReactorType;
+
+ Routine *routine = nullptr;
+
+ {
+ Function< Pointer<ReactorType>(Pointer<ReactorType>, Int) > function;
+ {
+ Pointer<ReactorType> pointer = function.template Arg<0>();
+ Int index = function.template Arg<1>();
+ Return(&pointer[index]);
+ }
+
+ routine = function("one");
+
+ if(routine)
+ {
+ auto callable = (CType*(*)(CType*, unsigned int))routine->getEntry();
+
+ union PtrInt {
+ CType* p;
+ size_t i;
+ };
+
+ PtrInt base;
+ base.i = 0x10000;
+
+ for (int i = 0; i < 5; i++)
+ {
+ PtrInt reference;
+ reference.p = &base.p[i];
+
+ PtrInt result;
+ result.p = callable(base.p, i);
+
+ auto expect = reference.i - base.i;
+ auto got = result.i - base.i;
+
+ EXPECT_EQ(got, expect) << "i:" << i;
+ }
+ }
+ }
+
+ delete routine;
+}
+
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);