double
ppc_estimate_reciprocal(double v)
{
   auto bits = get_float_bits(v);

   if (bits.mantissa == 0 && bits.exponent == 0) {
      return std::copysign(std::numeric_limits<double>::infinity(), v);
   }

   if (bits.exponent == bits.exponent_max) {
      if (bits.mantissa == 0) {
         return std::copysign(0.0, v);
      }
      return static_cast<float>(v);
   }

   if (bits.exponent < 895) {
      return std::copysign(std::numeric_limits<float>::max(), v);
   }

   if (bits.exponent > 1149) {
      return std::copysign(0.0, v);
   }

   int idx = (int)(bits.mantissa >> 37);
   bits.exponent = 0x7FD - bits.exponent;
   bits.mantissa = (int64_t)(fres_expected_base[idx / 1024] - (fres_expected_dec[idx / 1024] * (idx % 1024) + 1) / 2) << 29;
   return bits.v;
}
static void
mergeGeneric(cpu::Core *state, Instruction instr)
{
   float d0, d1;

   if (flags & MergeValue0) {
      if (!is_signalling_nan(state->fpr[instr.frA].paired1)) {
         d0 = static_cast<float>(state->fpr[instr.frA].paired1);
      } else {
         d0 = truncate_double(state->fpr[instr.frA].paired1);
      }
   } else {
      if (!is_signalling_nan(state->fpr[instr.frA].paired0)) {
         d0 = static_cast<float>(state->fpr[instr.frA].paired0);
      } else {
         d0 = truncate_double(state->fpr[instr.frA].paired0);
      }
   }

   // When inserting a double-precision value into slot 1, the value is
   // truncated rather than rounded.
   double d1_double;
   if (flags & MergeValue1) {
      d1_double = state->fpr[instr.frB].paired1;
   } else {
      d1_double = state->fpr[instr.frB].paired0;
   }
   auto d1_bits = get_float_bits(d1_double);
   if (d1_bits.exponent >= 1151 && d1_bits.exponent < 2047) {
      d1 = std::numeric_limits<float>::max();
   } else {
      d1 = truncate_double(d1_double);
   }

   state->fpr[instr.frD].paired0 = extend_float(d0);
   state->fpr[instr.frD].paired1 = extend_float(d1);

   // Don't leak any exceptions (inexact, overflow etc.) to later instructions.
   std::feclearexcept(FE_ALL_EXCEPT);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Reciprocal Square Root
static void
ps_rsqrte(cpu::Core *state, Instruction instr)
{
   const double b0 = state->fpr[instr.frB].paired0;
   const double b1 = state->fpr[instr.frB].paired1;

   const bool vxsnan0 = is_signalling_nan(b0);
   const bool vxsnan1 = is_signalling_nan(b1);
   const bool vxsqrt0 = !vxsnan0 && std::signbit(b0) && !is_zero(b0);
   const bool vxsqrt1 = !vxsnan1 && std::signbit(b1) && !is_zero(b1);
   const bool zx0 = is_zero(b0);
   const bool zx1 = is_zero(b1);

   const uint32_t oldFPSCR = state->fpscr.value;
   state->fpscr.vxsnan |= vxsnan0 || vxsnan1;
   state->fpscr.vxsqrt |= vxsqrt0 || vxsqrt1;
   state->fpscr.zx |= zx0 || zx1;

   double d0, d1;
   bool write = true;
   if (((vxsnan0 || vxsqrt0) && state->fpscr.ve) || (zx0 && state->fpscr.ze)) {
      write = false;
   } else {
      d0 = ppc_estimate_reciprocal_root(b0);
      updateFPRF(state, d0);
   }
   if (((vxsnan1 || vxsqrt1) && state->fpscr.ve) || (zx1 && state->fpscr.ze)) {
      write = false;
   } else {
      d1 = ppc_estimate_reciprocal_root(b1);
   }

   if (write) {
      // ps_rsqrte behaves strangely when the result's magnitude is out of
      // range: ps0 keeps its double-precision exponent, while ps1 appears
      // to get an arbitrary value from the floating-point circuitry.  The
      // details of how ps1's exponent is affected are unknown, but the
      // logic below works for double-precision inputs 0x7FE...FFF (maximum
      // normal) and 0x000...001 (minimum denormal).

      auto bits0 = get_float_bits(d0);
      bits0.mantissa &= UINT64_C(0xFFFFFE0000000);
      state->fpr[instr.frD].paired0 = bits0.v;

      auto bits1 = get_float_bits(d1);
      if (bits1.exponent == 0) {
         // Leave as zero (reciprocal square root can never be a denormal).
      } else if (bits1.exponent < 1151) {
         int8_t exponent8 = (bits1.exponent - 1023) & 0xFF;
         bits1.exponent = 1023 + exponent8;
      } else if (bits1.exponent < 2047) {
         bits1.exponent = 1022;
      }
      bits1.mantissa &= UINT64_C(0xFFFFFE0000000);
      state->fpr[instr.frD].paired1 = bits1.v;
   }

   updateFPSCR(state, oldFPSCR);
   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
Ejemplo n.º 4
0
bool runTests(const std::string &path)
{
   uint32_t testsFailed = 0, testsPassed = 0;
   uint32_t baseAddress = mem::MEM2Base;

   Instruction bclr = encodeInstruction(InstructionID::bclr);
   bclr.bo = 0x1f;
   mem::write(baseAddress + 4, bclr.value);

   fs::FileSystem filesystem;
   fs::FolderEntry entry;
   fs::HostPath base = path;
   filesystem.mountHostFolder("/tests", base, fs::Permissions::Read);
   auto folder = filesystem.openFolder("/tests");

   while (folder->read(entry)) {
      std::ifstream file(base.join(entry.name).path(), std::ifstream::in | std::ifstream::binary);
      cereal::BinaryInputArchive cerealInput(file);
      TestFile testFile;

      // Parse test file with cereal
      testFile.name = entry.name;
      cerealInput(testFile);

      // Run tests
      gLog->info("Checking {}", testFile.name);

      for (auto &test : testFile.tests) {
         bool failed = false;

         if (!TEST_FMADDSUB) {
            auto data = espresso::decodeInstruction(test.instr);
            switch (data->id) {
            case InstructionID::fmadd:
            case InstructionID::fmadds:
            case InstructionID::fmsub:
            case InstructionID::fmsubs:
            case InstructionID::fnmadd:
            case InstructionID::fnmadds:
            case InstructionID::fnmsub:
            case InstructionID::fnmsubs:
               failed = true;
               break;
            }
            if (failed) {
               continue;
            }
         }

         // Setup core state from test input
         cpu::CoreRegs *state = cpu::this_core::state();
         memset(state, 0, sizeof(cpu::CoreRegs));
         state->cia = 0;
         state->nia = baseAddress;
         state->xer = test.input.xer;
         state->cr = test.input.cr;
         state->fpscr = test.input.fpscr;
         state->ctr = test.input.ctr;

         for (auto i = 0; i < 4; ++i) {
            state->gpr[i + 3] = test.input.gpr[i];
            state->fpr[i + 1].paired0 = test.input.fr[i];
         }

         // Execute test
         mem::write(baseAddress, test.instr.value);
         cpu::jit::clearCache();
         cpu::this_core::executeSub();

         // Check XER (all bits)
         if (state->xer.value != test.output.xer.value) {
            gLog->error("Test failed, xer expected {:08X} found {:08X}", test.output.xer.value, state->xer.value);
            failed = true;
         }

         // Check Condition Register (all bits)
         if (state->cr.value != test.output.cr.value) {
            gLog->error("Test failed, cr expected {:08X} found {:08X}", test.output.cr.value, state->cr.value);
            failed = true;
         }

         // Check FPSCR
         if (TEST_FPSCR) {
            if (!TEST_FPSCR_FR) {
               state->fpscr.fr = 0;
               test.output.fpscr.fr = 0;
            }

            if (!TEST_FPSCR_UX) {
               state->fpscr.ux = 0;
               test.output.fpscr.ux = 0;
            }

            auto state_fpscr = state->fpscr.value;
            auto test_fpscr = test.output.fpscr.value;

            if (state_fpscr != test_fpscr) {
               gLog->error("Test failed, fpscr {:08X} found {:08X}", test.output.fpscr.value, state->fpscr.value);
               compareFPSCR(test.input.fpscr, state->fpscr, test.output.fpscr);
               failed = true;
            }
         }

         // Check CTR
         if (state->ctr != test.output.ctr) {
            gLog->error("Test failed, ctr expected {:08X} found {:08X}", test.output.ctr, state->ctr);
            failed = true;
         }

         // Check all GPR
         for (auto i = 0; i < 4; ++i) {
            auto reg = i + hwtest::GPR_BASE;
            auto value = state->gpr[reg];
            auto expected = test.output.gpr[i];

            if (value != expected) {
               gLog->error("Test failed, r{} expected {:08X} found {:08X}", reg, expected, value);
               failed = true;
            }
         }

         // Check all FPR
         for (auto i = 0; i < 4; ++i) {
            auto reg = i + hwtest::FPR_BASE;
            auto value = state->fpr[reg].value;
            auto expected = test.output.fr[i];

            if (!is_nan(value) && !is_nan(expected) && !is_infinity(value) && !is_infinity(expected)) {
               double dval = value / expected;

               if (dval < 0.999 || dval > 1.001) {
                  gLog->error("Test failed, f{} expected {:16f} found {:16f}", reg, expected, value);
                  failed = true;
               }
            } else {
               if (is_nan(value) && is_nan(expected)) {
                  auto bits = get_float_bits(value);
                  bits.sign = get_float_bits(expected).sign;
                  value = bits.v;
               }

               if (bit_cast<uint64_t>(value) != bit_cast<uint64_t>(expected)) {
                  gLog->error("Test failed, f{} expected {:16X} found {:16X}", reg, bit_cast<uint64_t>(expected), bit_cast<uint64_t>(value));
                  failed = true;
               }
            }
         }

         if (failed) {
            Disassembly dis;

            // Print disassembly
            disassemble(test.instr, dis, baseAddress);
            gLog->debug(dis.text);

            // Print all test fields
            gLog->debug("{:08x}            Input         Hardware           Interp", test.instr.value);

            for (auto field : dis.instruction->read) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            for (auto field : dis.instruction->write) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            for (auto field : dis.instruction->flags) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            gLog->debug("");
            ++testsFailed;
         } else {
            ++testsPassed;
         }
      }
   }

   gLog->info("Passed: {}, Failed: {}", testsPassed, testsFailed);
   return true;
}
Ejemplo n.º 5
0
int runTests(const std::string &path)
{
   uint32_t testsFailed = 0, testsPassed = 0;
   auto baseAddress = cpu::VirtualAddress { 0x02000000u };
   auto basePhysicalAddress = cpu::PhysicalAddress { 0x50000000u };
   auto codeSize = 2048u;

   cpu::allocateVirtualAddress(baseAddress, codeSize);
   cpu::mapMemory(baseAddress, basePhysicalAddress, codeSize, cpu::MapPermission::ReadWrite);

   Instruction bclr = encodeInstruction(InstructionID::bclr);
   bclr.bo = 0x1f;
   mem::write(baseAddress.getAddress() + 4, bclr.value);

   auto ec = std::error_code { };
   for (auto itr = std::filesystem::directory_iterator { "/tests", ec }; itr != end(itr); ++itr) {
      std::ifstream file(itr->path().string(), std::ifstream::in | std::ifstream::binary);
      cereal::BinaryInputArchive cerealInput(file);
      TestFile testFile;

      // Parse test file with cereal
      testFile.name = itr->path().filename().string();
      cerealInput(testFile);

      // Run tests
      for (auto &test : testFile.tests) {
         bool failed = false;

         if (!TEST_FMADDSUB) {
            auto data = espresso::decodeInstruction(test.instr);
            switch (data->id) {
            case InstructionID::fmadd:
            case InstructionID::fmadds:
            case InstructionID::fmsub:
            case InstructionID::fmsubs:
            case InstructionID::fnmadd:
            case InstructionID::fnmadds:
            case InstructionID::fnmsub:
            case InstructionID::fnmsubs:
               failed = true;
               break;
            }
            if (failed) {
               continue;
            }
         }

         // Setup core state from test input
         cpu::CoreRegs *state = cpu::this_core::state();
         memset(state, 0, sizeof(cpu::CoreRegs));
         state->cia = 0;
         state->nia = baseAddress.getAddress();
         state->xer = test.input.xer;
         state->cr = test.input.cr;
         state->fpscr = test.input.fpscr;
         state->ctr = test.input.ctr;

         for (auto i = 0; i < 4; ++i) {
            state->gpr[i + 3] = test.input.gpr[i];
            state->fpr[i + 1].paired0 = test.input.fr[i];
         }

         // Execute test
         mem::write(baseAddress.getAddress(), test.instr.value);
         cpu::clearInstructionCache();
         cpu::this_core::executeSub();

         // Check XER (all bits)
         if (state->xer.value != test.output.xer.value) {
            gLog->error("Test failed, xer expected {:08X} found {:08X}", test.output.xer.value, state->xer.value);
            failed = true;
         }

         // Check Condition Register (all bits)
         if (state->cr.value != test.output.cr.value) {
            gLog->error("Test failed, cr expected {:08X} found {:08X}", test.output.cr.value, state->cr.value);
            failed = true;
         }

         // Check FPSCR
         if (TEST_FPSCR) {
            if (!TEST_FPSCR_FR) {
               state->fpscr.fr = 0;
               test.output.fpscr.fr = 0;
            }

            if (!TEST_FPSCR_UX) {
               state->fpscr.ux = 0;
               test.output.fpscr.ux = 0;
            }

            auto state_fpscr = state->fpscr.value;
            auto test_fpscr = test.output.fpscr.value;

            if (state_fpscr != test_fpscr) {
               gLog->error("Test failed, fpscr {:08X} found {:08X}", test.output.fpscr.value, state->fpscr.value);
               compareFPSCR(test.input.fpscr, state->fpscr, test.output.fpscr);
               failed = true;
            }
         }

         // Check CTR
         if (state->ctr != test.output.ctr) {
            gLog->error("Test failed, ctr expected {:08X} found {:08X}", test.output.ctr, state->ctr);
            failed = true;
         }

         // Check all GPR
         for (auto i = 0; i < 4; ++i) {
            auto reg = i + hwtest::GPR_BASE;
            auto value = state->gpr[reg];
            auto expected = test.output.gpr[i];

            if (value != expected) {
               gLog->error("Test failed, r{} expected {:08X} found {:08X}", reg, expected, value);
               failed = true;
            }
         }

         // Check all FPR
         for (auto i = 0; i < 4; ++i) {
            auto reg = i + hwtest::FPR_BASE;
            auto value = state->fpr[reg].value;
            auto expected = test.output.fr[i];

            if (!is_nan(value) && !is_nan(expected) && !is_infinity(value) && !is_infinity(expected)) {
               double dval = value / expected;

               if (dval < 0.999 || dval > 1.001) {
                  gLog->error("Test failed, f{} expected {:16f} found {:16f}", reg, expected, value);
                  failed = true;
               }
            } else {
               if (is_nan(value) && is_nan(expected)) {
                  auto bits = get_float_bits(value);
                  bits.sign = get_float_bits(expected).sign;
                  value = bits.v;
               }

               if (bit_cast<uint64_t>(value) != bit_cast<uint64_t>(expected)) {
                  gLog->error("Test failed, f{} expected {:16X} found {:16X}", reg, bit_cast<uint64_t>(expected), bit_cast<uint64_t>(value));
                  failed = true;
               }
            }
         }

         if (failed) {
            Disassembly dis;

            // Print disassembly
            disassemble(test.instr, dis, baseAddress.getAddress());
            gLog->debug(dis.text);

            // Print all test fields
            gLog->debug("{:08x}            Input         Hardware           Interp", test.instr.value);

            for (auto field : dis.instruction->read) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            for (auto field : dis.instruction->write) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            for (auto field : dis.instruction->flags) {
               printTestField(field, test.instr, &test.input, &test.output, state);
            }

            gLog->debug("");
            ++testsFailed;
         } else {
            ++testsPassed;
         }
      }
   }

   if (testsFailed) {
      gLog->error("Failed {} of {} tests.", testsFailed, testsFailed + testsPassed);
   }

   return testsFailed;
}