Exemple #1
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;
}
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;
}