/* DingusPPC - The Experimental PowerPC Macintosh emulator Copyright (C) 2018-26 The DingusPPC Development Team (See CREDITS.MD for more details) (You may also contact divingkxt or powermax2286 on Discord) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // Replay floating-point instructions and input registers captured while BeOS // PR1 GLTeapot renders. This measures the real opcode dispatcher and handlers, // including FPSCR maintenance, without guest boot, display, or timer overhead. #include #include #include #include #include #include #include #include #include #include "bench2_data.h" #if defined(PPC_BENCHMARKS) void ppc_exception_handler(Except_Type exception_type, uint32_t srr1_bits) { power_off(po_benchmark_exception); } #endif static void replay_sample(const FpuSample& sample) { if ((ppc_state.fpscr ^ sample.fpscr) & FPSCR::RN_MASK) set_host_rounding_mode(sample.fpscr & FPSCR::RN_MASK); ppc_state.fpscr = sample.fpscr; ppc_state.cr = sample.cr; ppc_state.fpr[(sample.opcode >> 21) & 31].int64_r = sample.d; ppc_state.fpr[(sample.opcode >> 16) & 31].int64_r = sample.a; ppc_state.fpr[(sample.opcode >> 11) & 31].int64_r = sample.b; ppc_state.fpr[(sample.opcode >> 6) & 31].int64_r = sample.c; ppc_main_opcode(ppc_opcode_grabber, sample.opcode); } static uint64_t check_replay() { uint64_t hash = 1469598103934665603ULL; auto mix = [&hash](uint64_t value) { hash = (hash ^ value) * 1099511628211ULL; }; std::feclearexcept(FE_ALL_EXCEPT); for (const auto& sample : fpu_samples) { replay_sample(sample); mix(ppc_state.fpr[(sample.opcode >> 21) & 31].int64_r); mix(ppc_state.fpscr); mix(ppc_state.cr); } return hash; } int main(int argc, char** argv) { if (argc > 2 || (argc == 2 && std::strcmp(argv[1], "--check"))) { fprintf(stderr, "Usage: %s [--check]\n", argv[0]); return 2; } loguru::g_stderr_verbosity = loguru::Verbosity_ERROR; is_601 = false; initialize_ppc_opcode_table(); ppc_msr_did_change(ppc_state.msr, MSR::ME | MSR::IP | MSR::FP, false); set_host_rounding_mode(0); power_on = true; const uint64_t expected = check_replay(); constexpr size_t sample_count = sizeof(fpu_samples) / sizeof(fpu_samples[0]); printf("GLTeapot FPU replay: %zu samples, checksum %016" PRIx64 "\n", sample_count, expected); if (!power_on) return 1; if (argc == 2 && !std::strcmp(argv[1], "--check")) return 0; constexpr unsigned passes = 10000; constexpr unsigned trials = 9; // Warm the dispatcher, handlers and captured input data. for (unsigned pass = 0; pass < 100; pass++) for (const auto& sample : fpu_samples) replay_sample(sample); std::vector times; for (unsigned trial = 0; trial < trials; trial++) { std::feclearexcept(FE_ALL_EXCEPT); auto start = std::chrono::steady_clock::now(); for (unsigned pass = 0; pass < passes; pass++) for (const auto& sample : fpu_samples) replay_sample(sample); auto end = std::chrono::steady_clock::now(); if (!power_on || check_replay() != expected) { fprintf(stderr, "FPU replay changed its result during timing\n"); return 1; } double ns = std::chrono::duration(end - start).count() / (uint64_t(passes) * sample_count); times.push_back(ns); printf("Trial %u: %.3f ns/FPU instruction, %.3f million FPU instructions/s\n", trial + 1, ns, 1000.0 / ns); } std::sort(times.begin(), times.end()); double median = times[times.size() / 2]; printf("Median: %.3f ns/FPU instruction, %.3f million FPU instructions/s\n", median, 1000.0 / median); return 0; }