| 526 | } |
| 527 | |
| 528 | uint64_t Arm64JITCore::ExitFunctionLink(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) { |
| 529 | auto Thread = Frame->Thread; |
| 530 | bool TFSet = Thread->CurrentFrame->State.flags[X86State::RFLAG_TF_RAW_LOC]; |
| 531 | uintptr_t HostCode {}; |
| 532 | auto GuestRip = Record->GuestRIP; |
| 533 | |
| 534 | if (TFSet) { |
| 535 | // If TF is set, the cache must be skipped as different code needs to be generated. |
| 536 | Frame->State.rip = GuestRip; |
| 537 | return Frame->Pointers.Common.DispatcherLoopTop; |
| 538 | } else { |
| 539 | { |
| 540 | // Guard the LookupCache lock with the code invalidation mutex, to avoid issues with forking |
| 541 | auto lk_inval = GuardSignalDeferringSection<std::shared_lock>(static_cast<Context::ContextImpl*>(Thread->CTX)->CodeInvalidationMutex, Thread); |
| 542 | HostCode = Thread->LookupCache->FindBlock(GuestRip); |
| 543 | } |
| 544 | if (!HostCode) { |
| 545 | // Hold a reference to the code buffer, to avoid linking unmapped code if compilation triggers a recreation. |
| 546 | auto CodeBuffer = static_cast<Arm64JITCore*>(Thread->CPUBackend.get())->CurrentCodeBuffer; |
| 547 | HostCode = static_cast<Context::ContextImpl*>(Thread->CTX)->CompileBlock(Frame, GuestRip, 0); |
| 548 | if (Thread->LookupCache->Shared != CodeBuffer->LookupCache.get()) { |
| 549 | return HostCode; |
| 550 | } |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | // See ExitFunction in BranchOps.cpp for an assembly level view of the handled cases. |
| 555 | uintptr_t JumpThunkStartAddress = reinterpret_cast<uintptr_t>(Record) - 0x10; |
| 556 | uintptr_t CallerAddress = JumpThunkStartAddress + Record->CallerOffset; |
| 557 | auto BranchOffset = HostCode / 4 - CallerAddress / 4; |
| 558 | |
| 559 | uint32_t ExpectedKnownCallMarkerInst = 0; |
| 560 | ARMEmitter::Emitter ExpectedKnownCallMarkerEmit(reinterpret_cast<uint8_t*>(&ExpectedKnownCallMarkerInst), 4); |
| 561 | ExpectedKnownCallMarkerEmit.adr(TMP1, 0xC); |
| 562 | |
| 563 | // Guard the LookupCache lock with the code invalidation mutex, to avoid issues with forking |
| 564 | auto lk_inval = GuardSignalDeferringSection<std::shared_lock>(static_cast<Context::ContextImpl*>(Thread->CTX)->CodeInvalidationMutex, Thread); |
| 565 | |
| 566 | // Lock here is necessary to prevent simultaneous linking and delinking |
| 567 | auto lk = Thread->LookupCache->AcquireLock(); |
| 568 | |
| 569 | // For non-calls, this would extend into the block's code, however that's fine as an out-of-range adr would never |
| 570 | // be generated avoiding any false positives. |
| 571 | uintptr_t KnownCallMarkerAddr = CallerAddress - 0x8; |
| 572 | uint32_t KnownCallMarkerInst = *reinterpret_cast<uint32_t*>(KnownCallMarkerAddr); |
| 573 | if (ARMEmitter::Emitter::IsInt26(BranchOffset)) { |
| 574 | // Directly patch the callsite with the appropriate branch instruction. |
| 575 | uint32_t BranchInst = 0; |
| 576 | ARMEmitter::Emitter BranchEmit(reinterpret_cast<uint8_t*>(&BranchInst), 4); |
| 577 | |
| 578 | if (KnownCallMarkerInst == ExpectedKnownCallMarkerInst) { |
| 579 | BranchEmit.bl(BranchOffset); |
| 580 | Thread->LookupCache->AddBlockLink(GuestRip, Record, [](FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) { |
| 581 | DirectBlockDelinker(Frame, Record, true); |
| 582 | }); |
| 583 | } else { |
| 584 | BranchEmit.b(BranchOffset); |
| 585 | Thread->LookupCache->AddBlockLink(GuestRip, Record, [](FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) { |
nothing calls this directly
no test coverage detected