summaryrefslogtreecommitdiffstats
path: root/src/core/core_cpu.cpp
blob: 9f856ca6e2c17778216bc751eb1be6248f009a99 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <condition_variable>
#include <mutex>

#include "common/logging/log.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/settings.h"

namespace Core {

void CpuBarrier::NotifyEnd() {
    std::unique_lock<std::mutex> lock(mutex);
    end = true;
    condition.notify_all();
}

bool CpuBarrier::Rendezvous() {
    if (!Settings::values.use_multi_core) {
        // Meaningless when running in single-core mode
        return true;
    }

    if (!end) {
        std::unique_lock<std::mutex> lock(mutex);

        --cores_waiting;
        if (!cores_waiting) {
            cores_waiting = NUM_CPU_CORES;
            condition.notify_all();
            return true;
        }

        condition.wait(lock);
        return true;
    }

    return false;
}

Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
    : cpu_barrier{cpu_barrier}, core_index{core_index} {
    if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
        arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
#else
        arm_interface = std::make_unique<ARM_Unicorn>();
        LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
    } else {
        arm_interface = std::make_unique<ARM_Unicorn>();
    }

    scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
}

Cpu::~Cpu() = default;

std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
    if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
        return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
#else
        return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
    } else {
        return nullptr; // TODO(merry): Passthrough exclusive monitor
    }
}

void Cpu::RunLoop(bool tight_loop) {
    // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
    if (!cpu_barrier.Rendezvous()) {
        // If rendezvous failed, session has been killed
        return;
    }

    // If we don't have a currently active thread then don't execute instructions,
    // instead advance to the next event and try to yield to the next thread
    if (Kernel::GetCurrentThread() == nullptr) {
        LOG_TRACE(Core, "Core-{} idling", core_index);

        if (IsMainCore()) {
            // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
            CoreTiming::Idle();
            CoreTiming::Advance();
        }

        PrepareReschedule();
    } else {
        if (IsMainCore()) {
            CoreTiming::Advance();
        }

        if (tight_loop) {
            arm_interface->Run();
        } else {
            arm_interface->Step();
        }
    }

    Reschedule();
}

void Cpu::SingleStep() {
    return RunLoop(false);
}

void Cpu::PrepareReschedule() {
    arm_interface->PrepareReschedule();
    reschedule_pending = true;
}

void Cpu::Reschedule() {
    if (!reschedule_pending) {
        return;
    }

    reschedule_pending = false;
    // Lock the global kernel mutex when we manipulate the HLE state
    std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
    scheduler->Reschedule();
}

} // namespace Core