Skip to content

PPU Analyzer: GPR-assisted jump table analysis#16644

Merged
elad335 merged 4 commits into
RPCS3:masterfrom
elad335:code
Feb 7, 2025
Merged

PPU Analyzer: GPR-assisted jump table analysis#16644
elad335 merged 4 commits into
RPCS3:masterfrom
elad335:code

Conversation

@elad335

@elad335 elad335 commented Jan 31, 2025

Copy link
Copy Markdown
Contributor
  • Implement GPR-assisted jump table analysis, allowing to find branch addresses using reference from CTR register.
  • Implement initial PPU in-block code analyzer.
  • Fix compilation of possible PRX file with empty relocation table.
  • Simplify address-heap analysis.
  • Bonus commit: filter (try to) embedded floats in PPU code from compilation.

Btw, if you see a new log message, "BCTR to constant destination at", after this pull request, please open an issue.

Resolves #16640
Also fixes #16112 - Final Fantasy XIII (testers commented)

Before:

1,658,357 function blocks.,
image

After:

430,231 function blocks. (nearly 75% less PPU blocks to compile!)
image

And indeed, jump table is found at 0xff00 * 4 + 0xb83b30 to 0xff39 * 4 + 0xb83b30 which are 0xBC3730 to 0xBC3814

image
image
image

@elad335 elad335 added Enhancement CPU Optimization Optimizes existing code LLVM Related to LLVM instruction decoders labels Jan 31, 2025
@elad335 elad335 force-pushed the code branch 12 times, most recently from cadfd43 to c02e38f Compare February 6, 2025 17:44
@elad335

elad335 commented Feb 6, 2025

Copy link
Copy Markdown
Contributor Author

Finished this. Maybe it would help another game, who knows.

@bigol83

bigol83 commented Feb 6, 2025

Copy link
Copy Markdown

This fixes Final Fantasy XIII PPU compilation 👍

@Darkhost1999

Darkhost1999 commented Feb 6, 2025

Copy link
Copy Markdown
Contributor

This fixes Final Fantasy XIII PPU compilation 👍

Indeed closes #16112
I'm not close to chapter 9

@elad335 elad335 force-pushed the code branch 3 times, most recently from 0d6b0c0 to 1eef084 Compare February 7, 2025 16:59
@elad335 elad335 force-pushed the code branch 4 times, most recently from 5b086dd to 67269f3 Compare February 7, 2025 17:26
@oltolm

oltolm commented Feb 8, 2025

Copy link
Copy Markdown
Contributor

I get this error when starting any game in a debug build

rpcs3.exe caused a Fast Fail at location 00007FFD08B5286E in module ucrtbase.dll with code 7 (FATAL_APP_EXIT).

AddrPC           Params
00007FFD08B5286E 000000AD00000003 000001F700000003 000000AD96DEF650  ucrtbase.dll!abort+0x4e
00007FFCC428CFF1 000000AD96DEF650 0000000003CB5D44 000000AD96DEE0AF  libc++.dll!std::__1::__vector_base_common<true>::__throw_out_of_range+0x61
00000000016E5569 000000AD96DEF640 00000000016E55C6 000001F7954B6AB8  rpcs3.exe!std::__1::vector<ppu_module<lv2_obj>::analyse(unsigned int, unsigned int, unsigned int, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::function<bool ()>)::block_local_info_t, std::__1::allocator<ppu_module<lv2_obj>::analyse(unsigned int, unsigned int, unsigned int, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigne+0x49  [C:/msys64/clang64/include/c++/v1/vector @ 1436]
  1434: _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::reference
  1435: vector<_Tp, _Allocator>::operator[](size_type __n) _NOEXCEPT {
> 1436:   _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n < size(), "vector[] index out of bounds");
  1437:   return this->__begin_[__n];
  1438: }
00000000016E5E33 000001F781FD3C30 000001F781FD3C48 000001F701E8D1C8  rpcs3.exe!ppu_module<lv2_obj>::analyse(unsigned int, unsigned int, unsigned int, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::function<bool ()>)::$_16::operator()+0x23  [C:/src/rpcs3/rpcs3/Emu/Cell/PPUAnalyser.cpp @ 1794]
  1792: for (u32 j = 0; j < block_queue.size(); [&]()
  1793: {
> 1794: if (u32 size = block_queue[j].size)
  1795: {
  1796: // Update size
00000000016E0E5D 000000AD96DF0730 000001F700000000 000000AD96DF0720  rpcs3.exe!ppu_module<lv2_obj>::analyse+0x82cd  [C:/src/rpcs3/rpcs3/Emu/Cell/PPUAnalyser.cpp @ 1792]
  1790: 
  1791: // Block loop (block_queue may grow, may be aborted via clearing)
> 1792: for (u32 j = 0; j < block_queue.size(); [&]()
  1793: {
  1794: if (u32 size = block_queue[j].size)
00000000014B4277 0000000000140009 0000000000000000 000001F7958C4490  rpcs3.exe!ppu_load_prx+0x42c7  [C:/src/rpcs3/rpcs3/Emu/Cell/PPUModule.cpp @ 1955]
  1953: prx->applied_patches = applied;
  1954: prx->is_relocatable = true;
> 1955: prx->analyse(toc, 0, end, applied, exported_funcs);
  1956: 
  1957: if (!ar && !virtual_load)
00000000014BA5C7 0000000100000000 AAAAAAFF00000000 0000000100000000  rpcs3.exe!ppu_load_exec+0x39a7  [C:/src/rpcs3/rpcs3/Emu/Cell/PPUModule.cpp @ 2594]
  2592: ppu_loader.warning("Loading library: %s", name);
  2593: 
> 2594: auto prx = ppu_load_prx(obj, false, lle_dir + name, 0, nullptr);
  2595: prx->state = PRX_STATE_STARTED;
  2596: prx->load_exports();
0000000000F7F5B8 000001F785912E40 000000B000000005 0000014000000140  rpcs3.exe!Emulator::Load+0xa7c8  [C:/src/rpcs3/rpcs3/Emu/System.cpp @ 2373]
  2371: const auto _main = ensure(g_fxo->init<main_ppu_module<lv2_obj>>());
  2372: 
> 2373: if (ppu_load_exec(ppu_exec, false, m_path, DeserialManager()))
  2374: {
  2375: if (g_cfg.core.ppu_debug && had_been_decrypted)
0000000000F74B54 000001F6B8090001 000001F6000003FF 0000000000002288  rpcs3.exe!Emulator::BootGame+0x2b4  [C:/src/rpcs3/rpcs3/Emu/System.cpp @ 1041]
  1039: ensure(!elf.empty());
  1040: m_path = elf;
> 1041: result = Load(title_id);
  1042: }
  1043: 
0000000001348FFC 000000AD96DF72C0 000001F79557F880 000001F781F4CD00  rpcs3.exe!main_window::Boot+0x13c  [C:/src/rpcs3/rpcs3/rpcs3qt/main_window.cpp @ 603]
   601: m_app_icon = gui::utils::get_app_icon_from_path(path, title_id);
   602: 
>  603: if (const auto error = Emu.BootGame(path, title_id, direct, config_mode, config_path); error != game_boot_result::no_errors)
   604: {
   605: gui_log.error("Boot failed: reason: %s, path: %s", error, path);
0000000001385D27 0000000000008234 00007FFD08AEF05B 000001F78539D270  rpcs3.exe!main_window::CreateDockWindows()::$_5::operator()+0xa7  [C:/src/rpcs3/rpcs3/rpcs3qt/main_window.cpp @ 3627]
  3625: connect(m_game_list_frame, &game_list_frame::RequestBoot, this, [this](const game_info& game, cfg_mode config_mode, const std::string& config_path, const std::string& savestate)
  3626: {
> 3627: Boot(savestate.empty() ? game->info.path : savestate, game->info.serial, false, false, config_mode, config_path);
  3628: });
  3629: 
0000000001385C7A 000001F7955E2EF0 0000000000000000 000001F6B9BD9CE0  rpcs3.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2, 3>, QtPrivate::List<std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>, void, main_window::CreateDockWindows()::$_5>::call(main_window::CreateDockWindows()::$_5&, void**)::{lambda()#1}::operator()+0x4a  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 141]
   139:         {
   140:             call_internal<R>(arg, [&] {
>  141:                 return f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
   142:             });
   143:         }
0000000001385C28 000001F6B8090000 000001F6B8090000 0000000000000000  rpcs3.exe!QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2, 3>, QtPrivate::List<std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>, void, main_window::CreateDockWindows()::$_5>::call(main_window::CreateDockWindows()::$_5&, void**)::{lambda()#1}>+0x18  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 65]
    63:         {
    64:             if constexpr (std::is_void_v<R> || std::is_void_v<std::invoke_result_t<Lambda>>) {
>   65:                 std::forward<Lambda>(fn)();
    66:             } else {
    67:                 if (args[0])
0000000001385C01 0000000000000041 000001F79558BBF0 000000AD96DF7518  rpcs3.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2, 3>, QtPrivate::List<std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>, void, main_window::CreateDockWindows()::$_5>::call+0x31  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 140]
   138:         static void call(Function &f, void **arg)
   139:         {
>  140:             call_internal<R>(arg, [&] {
   141:                 return f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
   142:             });
0000000001385BB2 000001F6B9BD9CE0 000001F7821680A0 000001F78553FE80  rpcs3.exe!QtPrivate::FunctorCallable<main_window::CreateDockWindows()::$_5, std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>::call<QtPrivate::List<std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::+0x22  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 362]
   360:         template <typename SignalArgs, typename R>
   361:         static void call(Func &f, void *, void **arg) {
>  362:             FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Func>::call(f, arg);
   363:         }
   364:     };
0000000001385B63 0000000000008234 00007FFD08AEF05B 000001F78539D270  rpcs3.exe!QtPrivate::QCallableObject<main_window::CreateDockWindows()::$_5, QtPrivate::List<std::__1::shared_ptr<gui_game_info> const&, cfg_mode, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>, void>::impl+0x93  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 572]
   570:                     FuncType::template call<Args, R>(that->object(), static_cast<typename FuncType::Object *>(r), a);
   571:                 else
>  572:                     FuncType::template call<Args, R>(that->object(), r, a);
   573:                 break;
   574:             case Compare:
00007FFC644957A8 0000000000008234 000000000000000D 000001F7821680A0  Qt6Core.dll!doActivate<false>+0x608
00000000013CD5D1 000000AD96DF7698 0000000100000001 000001F785AF7FC0  rpcs3.exe!game_list_frame::RequestBoot+0x71  [E:/build-rpcs3-clang/rpcs3/rpcs3qt/rpcs3_ui_autogen/EWIEGA46WW/moc_game_list_frame.cpp @ 462]
   460: {
   461:     void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t3))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t4))) };
>  462:     QMetaObject::activate(this, &staticMetaObject, 2, _a);
   463: }
   464: 
0000000001C19022 25846051F9A36A3F 000001F78539D270 0000000000008234  rpcs3.exe!game_list_frame::doubleClickedSlot+0xf2  [C:/src/rpcs3/rpcs3/rpcs3qt/game_list_frame.cpp @ 979]
   977: 
   978: sys_log.notice("Booting from gamelist per doubleclick...");
>  979: Q_EMIT RequestBoot(game);
   980: }
   981: 
0000000001C18D75 000001F7955E2E50 000001F7955E2B90 000001F7955CA9F0  rpcs3.exe!game_list_frame::doubleClickedSlot+0x45  [C:/src/rpcs3/rpcs3/rpcs3qt/game_list_frame.cpp @ 968]
   966: }
   967: 
>  968: doubleClickedSlot(GetGameInfoByMode(item));
   969: }
   970: 
0000000001C61600 000001F785B4EA60 0000000000000011 000001F7955CA9F0  rpcs3.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QTableWidgetItem*>, void, void (game_list_frame::*)(QTableWidgetItem*)>::call(void (game_list_frame::*)(QTableWidgetItem*), game_list_frame*, void**)::{lambda()#1}::operator()+0x80  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 152]
   150:             assertObjectType<Obj>(o);
   151:             call_internal<R>(arg, [&] {
>  152:                 return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
   153:             });
   154:         }
0000000001C61578 00000000077BBC10 0000000001C852C5 000001F7955CA9F0  rpcs3.exe!QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QTableWidgetItem*>, void, void (game_list_frame::*)(QTableWidgetItem*)>::call(void (game_list_frame::*)(QTableWidgetItem*), game_list_frame*, void**)::{lambda()#1}>+0x18  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 65]
    63:         {
    64:             if constexpr (std::is_void_v<R> || std::is_void_v<std::invoke_result_t<Lambda>>) {
>   65:                 std::forward<Lambda>(fn)();
    66:             } else {
    67:                 if (args[0])
0000000001C61556 0000000000008234 0000000000000000 0000000000000000  rpcs3.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QTableWidgetItem*>, void, void (game_list_frame::*)(QTableWidgetItem*)>::call+0x56  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 151]
   149:         {
   150:             assertObjectType<Obj>(o);
>  151:             call_internal<R>(arg, [&] {
   152:                 return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
   153:             });
0000000001C614E7 0000000000000000 0000000000000000 00000000077BBC08  rpcs3.exe!QtPrivate::FunctionPointer<void (game_list_frame::*)(QTableWidgetItem*)>::call<QtPrivate::List<QTableWidgetItem*>, void>+0x47  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 199]
   197:         template <typename SignalArgs, typename R>
   198:         static void call(Function f, Obj *o, void **arg) {
>  199:             FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg);
   200:         }
   201:     };
0000000001C61410 000001F7857824F0 000000000000003D 000000AD96DF7900  rpcs3.exe!QtPrivate::QCallableObject<void (game_list_frame::*)(QTableWidgetItem*), QtPrivate::List<QTableWidgetItem*>, void>::impl+0xb0  [C:/msys64/clang64/include/qt6/QtCore/qobjectdefs_impl.h @ 570]
   568:             case Call:
   569:                 if constexpr (std::is_member_function_pointer_v<FunctorValue>)
>  570:                     FuncType::template call<Args, R>(that->object(), static_cast<typename FuncType::Object *>(r), a);
   571:                 else
   572:                     FuncType::template call<Args, R>(that->object(), r, a);
00007FFC644957A8 0000000000000000 0000000000000000 AAAAAAAA00000000  Qt6Core.dll!doActivate<false>+0x608
00007FFC6A10DD77 0000000000000000 0000000000000000 0000000000000000  Qt6Widgets.dll!QTableWidgetPrivate::emitItemDoubleClicked+0xa7
00007FFC644957A8 000000AD96DF7BF8 000000AD00000000 0000000000000000  Qt6Core.dll!doActivate<false>+0x608
00007FFC6A0986AE 0000000000000000 0000000000000030 0000000000000020  Qt6Widgets.dll!QAbstractItemView::mouseDoubleClickEvent+0x19e
0000000001F68A9D 000001F6B8260000 000000AD0000000F 0000000000000020  rpcs3.exe!game_list::mouseDoubleClickEvent+0x4d  [C:/src/rpcs3/rpcs3/rpcs3qt/game_list.cpp @ 156]
   154: }
   155: 
>  156: QTableWidget::mouseDoubleClickEvent(ev);
   157: }
   158: 
00007FFC69DF36BC 0000000000000000 00007FFD08AEFDE6 0000000000000000  Qt6Widgets.dll!QWidget::event+0x8fc
00007FFC69E9145C 0000000000000002 00007FFC00000000 00000000000000D8  Qt6Widgets.dll!QFrame::event+0x2c
00007FFC6A096B9F 00000000000D0003 0000000000000000 000001F785B4EDA0  Qt6Widgets.dll!QAbstractItemView::viewportEvent+0x60f
00007FFC6443E7F0 8000000000000000 8000000000000000 000000AD96DF81E0  Qt6Core.dll!QCoreApplicationPrivate::sendThroughObjectEventFilters+0x100
00007FFC69D99BC7 4086600000000000 4076100000000000 0000000000000000  Qt6Widgets.dll!QApplicationPrivate::notify_helper+0x127
00007FFC69D9C467 0000000000000000 0A6642490A0A6CE5 000000AD96DF8578  Qt6Widgets.dll!QApplication::notify+0x1aa7
00007FFC6443E484 4086600000000000 4076100000000000 AAAAAAAAAAAAAAAA  Qt6Core.dll!QCoreApplication::notifyInternal2+0xa4
00007FFC69D9A1FC 000000AD96DF8640 00007FFC64A23261 000001F7856D0700  Qt6Widgets.dll!QApplicationPrivate::sendMouseEvent+0x34c
00007FFC69E0E5EF 0000000200000000 000001F782153840 000000AD96DF8B50  Qt6Widgets.dll!QWidgetWindow::handleMouseEvent+0x60f
00007FFC69E0D7CA A04330126538E911 00007FFC648EFF48 0000000000000001  Qt6Widgets.dll!QWidgetWindow::event+0x9a
00007FFC69D99BDB 0000000000000000 00007FFD08AEFDE6 0000000000000000  Qt6Widgets.dll!QApplicationPrivate::notify_helper+0x13b
00007FFC69D9AC19 000000005C3E766D 000001F785906A80 00007FFC65131AB0  Qt6Widgets.dll!QApplication::notify+0x259
00007FFC6443E484 000000AD96437000 00007FFCB8182220 0000000000030000  Qt6Core.dll!QCoreApplication::notifyInternal2+0xa4
00007FFC649B1042 000000AD96DFFB20 00000000000000A4 000001F782048CD0  Qt6Gui.dll!QGuiApplicationPrivate::processMouseEvent+0xd02
00007FFC64A20ABA 000001F781FD3C30 000000AD96DF8D00 000001F781FD3B50  Qt6Gui.dll!QWindowSystemInterface::sendWindowSystemEvents+0x1ca
00007FFC645ED4DF 000000AD96DFFB20 0000000000000080 000000AD96DFBEE0  Qt6Core.dll!QEventDispatcherWin32::processEvents+0x5f
00007FFC64D86349 000000AD96DFBEE0 0000000000000001 0000000000000000  Qt6Gui.dll!QWindowsGuiEventDispatcher::processEvents+0x19
00007FFC64448AA6 00000000044909B1 0000000000DFC843 0000000000000004  Qt6Core.dll!QEventLoop::exec+0x236
00007FFC6443EC2A 00007FFD08BD1270 0000000000000000 000001F600000002  Qt6Core.dll!QCoreApplication::exec+0x9a
0000000000E118D7 000000000000000E 000000AD96DFFB20 000001F6B8097D8F  rpcs3.exe!qMain+0x90d7  [C:/src/rpcs3/rpcs3/main.cpp @ 1491]
  1489: 
  1490: // run event loop (maybe only needed for the gui application)
> 1491: return app->exec();
  1492: }
  1493: 
000000000149B8E8 00007FFD08BD1270 000000000149AF50 0000000000000002  rpcs3.exe!main+0xd8
0000000000DD133C 0000000000000000 0000000000000000 0000000000000000  rpcs3.exe!__tmainCRTStartup+0x1bc
0000000000DD1176 0000000000000000 0000000000000000 0000000000000000  rpcs3.exe!WinMainCRTStartup+0x16
00007FFD0A1E7374 0000000000000000 0000000000000000 0000000000000000  KERNEL32.DLL!BaseThreadInitThunk+0x14
00007FFD0ACDCC91 0000000000000000 0000000000000000 0000000000000000  ntdll.dll!RtlUserThreadStart+0x21

This happens because of

block_queue.clear();
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CPU Enhancement LLVM Related to LLVM instruction decoders Optimization Optimizes existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task] PPU Analyzer: GPR-based jumptable analysis [Regression] FINAL FANTASY XIII [MRTC00003] Closes to Desktop (#15961)

4 participants