Compare commits
9 commits
7d1e9ab530
...
ef47188d17
Author | SHA1 | Date | |
---|---|---|---|
ef47188d17 | |||
52b524ed59 | |||
726fe33511 | |||
bf4dce8d0b | |||
45263ee7aa | |||
f19bbda517 | |||
f5bb07341a | |||
3e299dc0f5 | |||
3ac9d65cdd |
73 changed files with 2055 additions and 2867 deletions
|
@ -1,9 +1,12 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "audio_core/audio_event.h"
|
#include "audio_core/audio_event.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
constexpr u32 CurrentRevision = 16;
|
constexpr u32 CurrentRevision = 16;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
|
#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
|
||||||
#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
|
#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
|
#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
|
||||||
#include "audio_core/renderer/command/effect/reverb.h"
|
#include "audio_core/renderer/command/effect/reverb.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include "audio_core/renderer/mix/mix_context.h"
|
#include "audio_core/renderer/mix/mix_context.h"
|
||||||
#include "audio_core/renderer/splitter/splitter_context.h"
|
#include "audio_core/renderer/splitter/splitter_context.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "audio_core/renderer/voice/voice_context.h"
|
#include "audio_core/renderer/voice/voice_context.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "common/fs/fs_util.h"
|
#include "common/fs/fs_util.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace Common::FS {
|
namespace Common::FS {
|
||||||
|
|
||||||
|
|
|
@ -1,530 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
//
|
|
||||||
// TODO: remove this file when ranges are supported by all compilation targets
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <utility>
|
|
||||||
#include <version>
|
|
||||||
|
|
||||||
#ifndef __cpp_lib_ranges
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
namespace ranges {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept range = requires(T& t) {
|
|
||||||
begin(t);
|
|
||||||
end(t);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept input_range = range<T>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept output_range = range<T>;
|
|
||||||
|
|
||||||
template <range R>
|
|
||||||
using range_difference_t = ptrdiff_t;
|
|
||||||
|
|
||||||
//
|
|
||||||
// find, find_if, find_if_not
|
|
||||||
//
|
|
||||||
|
|
||||||
struct find_fn {
|
|
||||||
template <typename Iterator, typename T, typename Proj = std::identity>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
|
|
||||||
Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (std::invoke(proj, *first) == value) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T, typename Proj = std::identity>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct find_if_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (std::invoke(pred, std::invoke(proj, *first))) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct find_if_not_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (!std::invoke(pred, std::invoke(proj, *first))) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr find_fn find;
|
|
||||||
inline constexpr find_if_fn find_if;
|
|
||||||
inline constexpr find_if_not_fn find_if_not;
|
|
||||||
|
|
||||||
//
|
|
||||||
// any_of, all_of, none_of
|
|
||||||
//
|
|
||||||
|
|
||||||
struct all_of_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct any_of_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct none_of_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr any_of_fn any_of;
|
|
||||||
inline constexpr all_of_fn all_of;
|
|
||||||
inline constexpr none_of_fn none_of;
|
|
||||||
|
|
||||||
//
|
|
||||||
// count, count_if
|
|
||||||
//
|
|
||||||
|
|
||||||
struct count_fn {
|
|
||||||
template <typename Iterator, typename T, typename Proj = std::identity>
|
|
||||||
constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
|
|
||||||
Proj proj = {}) const {
|
|
||||||
ptrdiff_t counter = 0;
|
|
||||||
for (; first != last; ++first)
|
|
||||||
if (std::invoke(proj, *first) == value)
|
|
||||||
++counter;
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T, typename Proj = std::identity>
|
|
||||||
constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct count_if_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
|
|
||||||
ptrdiff_t counter = 0;
|
|
||||||
for (; first != last; ++first)
|
|
||||||
if (std::invoke(pred, std::invoke(proj, *first)))
|
|
||||||
++counter;
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr count_fn count;
|
|
||||||
inline constexpr count_if_fn count_if;
|
|
||||||
|
|
||||||
//
|
|
||||||
// transform
|
|
||||||
//
|
|
||||||
|
|
||||||
struct transform_fn {
|
|
||||||
template <typename InputIterator, typename OutputIterator, typename F,
|
|
||||||
typename Proj = std::identity>
|
|
||||||
constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
|
|
||||||
F op, Proj proj = {}) const {
|
|
||||||
for (; first1 != last1; ++first1, (void)++result) {
|
|
||||||
*result = std::invoke(op, std::invoke(proj, *first1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename OutputIterator, typename F,
|
|
||||||
typename Proj = std::identity>
|
|
||||||
constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr transform_fn transform;
|
|
||||||
|
|
||||||
//
|
|
||||||
// sort
|
|
||||||
//
|
|
||||||
|
|
||||||
struct sort_fn {
|
|
||||||
template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
|
|
||||||
constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
|
|
||||||
if (first == last)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Iterator last_iter = ranges::next(first, last);
|
|
||||||
std::sort(first, last_iter,
|
|
||||||
[&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
|
|
||||||
constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr sort_fn sort;
|
|
||||||
|
|
||||||
//
|
|
||||||
// fill
|
|
||||||
//
|
|
||||||
|
|
||||||
struct fill_fn {
|
|
||||||
template <typename T, typename OutputIterator>
|
|
||||||
constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
|
|
||||||
const T& value) const {
|
|
||||||
while (first != last) {
|
|
||||||
*first++ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, ranges::output_range R>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr fill_fn fill;
|
|
||||||
|
|
||||||
//
|
|
||||||
// for_each
|
|
||||||
//
|
|
||||||
|
|
||||||
struct for_each_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Fun>
|
|
||||||
constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
std::invoke(f, std::invoke(proj, *first));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Fun>
|
|
||||||
constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr for_each_fn for_each;
|
|
||||||
|
|
||||||
//
|
|
||||||
// min_element, max_element
|
|
||||||
//
|
|
||||||
|
|
||||||
struct min_element_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
if (first == last) {
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto smallest = first;
|
|
||||||
++first;
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
|
|
||||||
smallest = first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return smallest;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct max_element_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
if (first == last) {
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto largest = first;
|
|
||||||
++first;
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
|
|
||||||
largest = first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return largest;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr min_element_fn min_element;
|
|
||||||
inline constexpr max_element_fn max_element;
|
|
||||||
|
|
||||||
//
|
|
||||||
// replace, replace_if
|
|
||||||
//
|
|
||||||
|
|
||||||
struct replace_fn {
|
|
||||||
template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
|
|
||||||
const T2& new_value, Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (old_value == std::invoke(proj, *first)) {
|
|
||||||
*first = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
|
|
||||||
Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct replace_if_fn {
|
|
||||||
template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
|
|
||||||
Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (!!std::invoke(pred, std::invoke(proj, *first))) {
|
|
||||||
*first = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::move(first);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
|
|
||||||
Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
|
|
||||||
std::move(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr replace_fn replace;
|
|
||||||
inline constexpr replace_if_fn replace_if;
|
|
||||||
|
|
||||||
//
|
|
||||||
// copy, copy_if
|
|
||||||
//
|
|
||||||
|
|
||||||
struct copy_fn {
|
|
||||||
template <typename InputIterator, typename OutputIterator>
|
|
||||||
constexpr void operator()(InputIterator first, InputIterator last,
|
|
||||||
OutputIterator result) const {
|
|
||||||
for (; first != last; ++first, (void)++result) {
|
|
||||||
*result = *first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename OutputIterator>
|
|
||||||
constexpr void operator()(R&& r, OutputIterator result) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(result));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct copy_if_fn {
|
|
||||||
template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
|
|
||||||
typename Pred>
|
|
||||||
constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
|
|
||||||
Pred pred, Proj proj = {}) const {
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
if (std::invoke(pred, std::invoke(proj, *first))) {
|
|
||||||
*result = *first;
|
|
||||||
++result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
|
|
||||||
typename Pred>
|
|
||||||
constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
|
|
||||||
std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr copy_fn copy;
|
|
||||||
inline constexpr copy_if_fn copy_if;
|
|
||||||
|
|
||||||
//
|
|
||||||
// generate
|
|
||||||
//
|
|
||||||
|
|
||||||
struct generate_fn {
|
|
||||||
template <typename Iterator, typename F>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
|
|
||||||
for (; first != last; *first = std::invoke(gen), ++first)
|
|
||||||
;
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R, std::copy_constructible F>
|
|
||||||
requires std::invocable<F&> && ranges::output_range<R>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr generate_fn generate;
|
|
||||||
|
|
||||||
//
|
|
||||||
// lower_bound, upper_bound
|
|
||||||
//
|
|
||||||
|
|
||||||
struct lower_bound_fn {
|
|
||||||
template <typename Iterator, typename T, typename Proj = std::identity,
|
|
||||||
typename Comp = ranges::less>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
Iterator it;
|
|
||||||
std::ptrdiff_t _count, _step;
|
|
||||||
_count = std::distance(first, last);
|
|
||||||
|
|
||||||
while (_count > 0) {
|
|
||||||
it = first;
|
|
||||||
_step = _count / 2;
|
|
||||||
ranges::advance(it, _step, last);
|
|
||||||
if (comp(std::invoke(proj, *it), value)) {
|
|
||||||
first = ++it;
|
|
||||||
_count -= _step + 1;
|
|
||||||
} else {
|
|
||||||
_count = _step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T, typename Proj = std::identity,
|
|
||||||
typename Comp = ranges::less>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct upper_bound_fn {
|
|
||||||
template <typename Iterator, typename T, typename Proj = std::identity,
|
|
||||||
typename Comp = ranges::less>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
Iterator it;
|
|
||||||
std::ptrdiff_t _count, _step;
|
|
||||||
_count = std::distance(first, last);
|
|
||||||
|
|
||||||
while (_count > 0) {
|
|
||||||
it = first;
|
|
||||||
_step = _count / 2;
|
|
||||||
ranges::advance(it, _step, last);
|
|
||||||
if (!comp(value, std::invoke(proj, *it))) {
|
|
||||||
first = ++it;
|
|
||||||
_count -= _step + 1;
|
|
||||||
} else {
|
|
||||||
_count = _step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename T, typename Proj = std::identity,
|
|
||||||
typename Comp = ranges::less>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr lower_bound_fn lower_bound;
|
|
||||||
inline constexpr upper_bound_fn upper_bound;
|
|
||||||
|
|
||||||
//
|
|
||||||
// adjacent_find
|
|
||||||
//
|
|
||||||
|
|
||||||
struct adjacent_find_fn {
|
|
||||||
template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
|
|
||||||
constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
|
|
||||||
Proj proj = {}) const {
|
|
||||||
if (first == last)
|
|
||||||
return first;
|
|
||||||
auto _next = ranges::next(first);
|
|
||||||
for (; _next != last; ++_next, ++first)
|
|
||||||
if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
|
|
||||||
return first;
|
|
||||||
return _next;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <ranges::input_range R, typename Proj = std::identity,
|
|
||||||
typename Pred = ranges::equal_to>
|
|
||||||
constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
|
|
||||||
return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr adjacent_find_fn adjacent_find;
|
|
||||||
|
|
||||||
} // namespace ranges
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "core/crypto/aes_util.h"
|
#include "core/crypto/aes_util.h"
|
||||||
#include "core/crypto/ctr_encryption_layer.h"
|
#include "core/crypto/ctr_encryption_layer.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "core/hle/kernel/memory_types.h"
|
#include "core/hle/kernel/memory_types.h"
|
||||||
#include "core/hle/kernel/slab_helpers.h"
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/stb.h"
|
#include "common/stb.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
@ -647,7 +647,8 @@ public:
|
||||||
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
|
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
|
||||||
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
|
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
|
||||||
{2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"},
|
{2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"},
|
||||||
{3, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"},
|
{3, &IManagerForApplication::LoadIdTokenCacheDeprecated, "LoadIdTokenCache"},
|
||||||
|
{4, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"},
|
||||||
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
|
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
|
||||||
{136, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCache"}, // 19.0.0+
|
{136, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCache"}, // 19.0.0+
|
||||||
{150, nullptr, "CreateAuthorizationRequest"},
|
{150, nullptr, "CreateAuthorizationRequest"},
|
||||||
|
@ -683,12 +684,25 @@ private:
|
||||||
rb.PushIpcInterface(ensure_token_id);
|
rb.PushIpcInterface(ensure_token_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadIdTokenCache(HLERequestContext& ctx) {
|
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||||
|
|
||||||
ensure_token_id->LoadIdTokenCache(ctx);
|
ensure_token_id->LoadIdTokenCache(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoadIdTokenCache(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||||
|
|
||||||
|
std::vector<u8> token_data(0x100);
|
||||||
|
std::fill(token_data.begin(), token_data.end(), 0);
|
||||||
|
|
||||||
|
ctx.WriteBuffer(token_data, 0);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(static_cast<u32>(token_data.size()));
|
||||||
|
}
|
||||||
|
|
||||||
void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) {
|
void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "common/bit_cast.h"
|
#include "common/bit_cast.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/internal_network/emu_net_state.h"
|
#include "core/internal_network/emu_net_state.h"
|
||||||
|
|
|
@ -39,9 +39,6 @@ option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
|
||||||
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
|
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
|
||||||
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
|
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
|
||||||
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
|
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
|
||||||
if (NOT DEFINED DYNARMIC_FRONTENDS)
|
|
||||||
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "Selects which frontends to enable")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Default to a Release build
|
# Default to a Release build
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
include(TargetArchitectureSpecificSources)
|
include(TargetArchitectureSpecificSources)
|
||||||
|
|
||||||
add_library(dynarmic
|
add_library(dynarmic
|
||||||
|
@ -90,78 +89,59 @@ add_library(dynarmic
|
||||||
ir/opcodes.cpp
|
ir/opcodes.cpp
|
||||||
ir/opcodes.h
|
ir/opcodes.h
|
||||||
ir/opcodes.inc
|
ir/opcodes.inc
|
||||||
ir/opt/constant_propagation_pass.cpp
|
ir/opt_passes.cpp
|
||||||
ir/opt/dead_code_elimination_pass.cpp
|
ir/opt_passes.h
|
||||||
ir/opt/identity_removal_pass.cpp
|
|
||||||
ir/opt/ir_matcher.h
|
|
||||||
ir/opt/naming_pass.cpp
|
|
||||||
ir/opt/passes.h
|
|
||||||
ir/opt/polyfill_pass.cpp
|
|
||||||
ir/opt/verification_pass.cpp
|
|
||||||
ir/terminal.h
|
ir/terminal.h
|
||||||
ir/type.cpp
|
ir/type.cpp
|
||||||
ir/type.h
|
ir/type.h
|
||||||
ir/value.cpp
|
ir/value.cpp
|
||||||
ir/value.h
|
ir/value.h
|
||||||
|
# A32
|
||||||
|
frontend/A32/a32_ir_emitter.cpp
|
||||||
|
frontend/A32/a32_ir_emitter.h
|
||||||
|
frontend/A32/a32_location_descriptor.cpp
|
||||||
|
frontend/A32/a32_location_descriptor.h
|
||||||
|
frontend/A32/decoder/arm.h
|
||||||
|
frontend/A32/decoder/arm.inc
|
||||||
|
frontend/A32/decoder/asimd.h
|
||||||
|
frontend/A32/decoder/asimd.inc
|
||||||
|
frontend/A32/decoder/thumb16.h
|
||||||
|
frontend/A32/decoder/thumb16.inc
|
||||||
|
frontend/A32/decoder/thumb32.h
|
||||||
|
frontend/A32/decoder/thumb32.inc
|
||||||
|
frontend/A32/decoder/vfp.h
|
||||||
|
frontend/A32/decoder/vfp.inc
|
||||||
|
frontend/A32/disassembler/disassembler.h
|
||||||
|
frontend/A32/disassembler/disassembler_arm.cpp
|
||||||
|
frontend/A32/disassembler/disassembler_thumb.cpp
|
||||||
|
frontend/A32/FPSCR.h
|
||||||
|
frontend/A32/ITState.h
|
||||||
|
frontend/A32/PSR.h
|
||||||
|
frontend/A32/translate/a32_translate.cpp
|
||||||
|
frontend/A32/translate/a32_translate.h
|
||||||
|
frontend/A32/translate/conditional_state.cpp
|
||||||
|
frontend/A32/translate/conditional_state.h
|
||||||
|
frontend/A32/translate/translate_arm.cpp
|
||||||
|
frontend/A32/translate/translate_thumb.cpp
|
||||||
|
interface/A32/a32.h
|
||||||
|
interface/A32/arch_version.h
|
||||||
|
interface/A32/config.h
|
||||||
|
interface/A32/coprocessor.h
|
||||||
|
interface/A32/coprocessor_util.h
|
||||||
|
interface/A32/disassembler.h
|
||||||
|
# A64
|
||||||
|
frontend/A64/a64_ir_emitter.cpp
|
||||||
|
frontend/A64/a64_ir_emitter.h
|
||||||
|
frontend/A64/a64_location_descriptor.cpp
|
||||||
|
frontend/A64/a64_location_descriptor.h
|
||||||
|
frontend/A64/decoder/a64.h
|
||||||
|
frontend/A64/decoder/a64.inc
|
||||||
|
frontend/A64/translate/a64_translate.cpp
|
||||||
|
frontend/A64/translate/a64_translate.h
|
||||||
|
interface/A64/a64.h
|
||||||
|
interface/A64/config.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
frontend/A32/a32_ir_emitter.cpp
|
|
||||||
frontend/A32/a32_ir_emitter.h
|
|
||||||
frontend/A32/a32_location_descriptor.cpp
|
|
||||||
frontend/A32/a32_location_descriptor.h
|
|
||||||
frontend/A32/decoder/arm.h
|
|
||||||
frontend/A32/decoder/arm.inc
|
|
||||||
frontend/A32/decoder/asimd.h
|
|
||||||
frontend/A32/decoder/asimd.inc
|
|
||||||
frontend/A32/decoder/thumb16.h
|
|
||||||
frontend/A32/decoder/thumb16.inc
|
|
||||||
frontend/A32/decoder/thumb32.h
|
|
||||||
frontend/A32/decoder/thumb32.inc
|
|
||||||
frontend/A32/decoder/vfp.h
|
|
||||||
frontend/A32/decoder/vfp.inc
|
|
||||||
frontend/A32/disassembler/disassembler.h
|
|
||||||
frontend/A32/disassembler/disassembler_arm.cpp
|
|
||||||
frontend/A32/disassembler/disassembler_thumb.cpp
|
|
||||||
frontend/A32/FPSCR.h
|
|
||||||
frontend/A32/ITState.h
|
|
||||||
frontend/A32/PSR.h
|
|
||||||
frontend/A32/translate/a32_translate.cpp
|
|
||||||
frontend/A32/translate/a32_translate.h
|
|
||||||
frontend/A32/translate/conditional_state.cpp
|
|
||||||
frontend/A32/translate/conditional_state.h
|
|
||||||
frontend/A32/translate/translate_arm.cpp
|
|
||||||
frontend/A32/translate/translate_thumb.cpp
|
|
||||||
interface/A32/a32.h
|
|
||||||
interface/A32/arch_version.h
|
|
||||||
interface/A32/config.h
|
|
||||||
interface/A32/coprocessor.h
|
|
||||||
interface/A32/coprocessor_util.h
|
|
||||||
interface/A32/disassembler.h
|
|
||||||
ir/opt/a32_constant_memory_reads_pass.cpp
|
|
||||||
ir/opt/a32_get_set_elimination_pass.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
frontend/A64/a64_ir_emitter.cpp
|
|
||||||
frontend/A64/a64_ir_emitter.h
|
|
||||||
frontend/A64/a64_location_descriptor.cpp
|
|
||||||
frontend/A64/a64_location_descriptor.h
|
|
||||||
frontend/A64/decoder/a64.h
|
|
||||||
frontend/A64/decoder/a64.inc
|
|
||||||
frontend/A64/translate/a64_translate.cpp
|
|
||||||
frontend/A64/translate/a64_translate.h
|
|
||||||
interface/A64/a64.h
|
|
||||||
interface/A64/config.h
|
|
||||||
ir/opt/a64_callback_config_pass.cpp
|
|
||||||
ir/opt/a64_get_set_elimination_pass.cpp
|
|
||||||
ir/opt/a64_merge_interpret_blocks.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
|
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
|
||||||
# fail in dynarmic
|
# fail in dynarmic
|
||||||
|
@ -218,29 +198,21 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
common/spin_lock_x64.h
|
common/spin_lock_x64.h
|
||||||
common/x64_disassemble.cpp
|
common/x64_disassemble.cpp
|
||||||
common/x64_disassemble.h
|
common/x64_disassemble.h
|
||||||
|
# A32
|
||||||
|
backend/x64/a32_emit_x64.cpp
|
||||||
|
backend/x64/a32_emit_x64.h
|
||||||
|
backend/x64/a32_emit_x64_memory.cpp
|
||||||
|
backend/x64/a32_interface.cpp
|
||||||
|
backend/x64/a32_jitstate.cpp
|
||||||
|
backend/x64/a32_jitstate.h
|
||||||
|
# A64
|
||||||
|
backend/x64/a64_emit_x64.cpp
|
||||||
|
backend/x64/a64_emit_x64.h
|
||||||
|
backend/x64/a64_emit_x64_memory.cpp
|
||||||
|
backend/x64/a64_interface.cpp
|
||||||
|
backend/x64/a64_jitstate.cpp
|
||||||
|
backend/x64/a64_jitstate.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "x86_64"
|
|
||||||
backend/x64/a32_emit_x64.cpp
|
|
||||||
backend/x64/a32_emit_x64.h
|
|
||||||
backend/x64/a32_emit_x64_memory.cpp
|
|
||||||
backend/x64/a32_interface.cpp
|
|
||||||
backend/x64/a32_jitstate.cpp
|
|
||||||
backend/x64/a32_jitstate.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "x86_64"
|
|
||||||
backend/x64/a64_emit_x64.cpp
|
|
||||||
backend/x64/a64_emit_x64.h
|
|
||||||
backend/x64/a64_emit_x64_memory.cpp
|
|
||||||
backend/x64/a64_interface.cpp
|
|
||||||
backend/x64/a64_jitstate.cpp
|
|
||||||
backend/x64/a64_jitstate.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("arm64" IN_LIST ARCHITECTURE)
|
if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
|
@ -284,25 +256,17 @@ if ("arm64" IN_LIST ARCHITECTURE)
|
||||||
backend/arm64/verbose_debugging_output.h
|
backend/arm64/verbose_debugging_output.h
|
||||||
common/spin_lock_arm64.cpp
|
common/spin_lock_arm64.cpp
|
||||||
common/spin_lock_arm64.h
|
common/spin_lock_arm64.h
|
||||||
|
# A32
|
||||||
|
backend/arm64/a32_address_space.cpp
|
||||||
|
backend/arm64/a32_address_space.h
|
||||||
|
backend/arm64/a32_core.h
|
||||||
|
backend/arm64/a32_interface.cpp
|
||||||
|
# A64
|
||||||
|
backend/arm64/a64_address_space.cpp
|
||||||
|
backend/arm64/a64_address_space.h
|
||||||
|
backend/arm64/a64_core.h
|
||||||
|
backend/arm64/a64_interface.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "arm64"
|
|
||||||
backend/arm64/a32_address_space.cpp
|
|
||||||
backend/arm64/a32_address_space.h
|
|
||||||
backend/arm64/a32_core.h
|
|
||||||
backend/arm64/a32_interface.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_architecture_specific_sources(dynarmic "arm64"
|
|
||||||
backend/arm64/a64_address_space.cpp
|
|
||||||
backend/arm64/a64_address_space.h
|
|
||||||
backend/arm64/a64_core.h
|
|
||||||
backend/arm64/a64_interface.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("riscv" IN_LIST ARCHITECTURE)
|
if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
|
@ -331,21 +295,14 @@ if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
backend/riscv64/reg_alloc.cpp
|
backend/riscv64/reg_alloc.cpp
|
||||||
backend/riscv64/reg_alloc.h
|
backend/riscv64/reg_alloc.h
|
||||||
backend/riscv64/stack_layout.h
|
backend/riscv64/stack_layout.h
|
||||||
|
# A32
|
||||||
|
backend/riscv64/a32_address_space.cpp
|
||||||
|
backend/riscv64/a32_address_space.h
|
||||||
|
backend/riscv64/a32_core.h
|
||||||
|
backend/riscv64/a32_interface.cpp
|
||||||
|
backend/riscv64/code_block.h
|
||||||
)
|
)
|
||||||
|
message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture")
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic PRIVATE
|
|
||||||
backend/riscv64/a32_address_space.cpp
|
|
||||||
backend/riscv64/a32_address_space.h
|
|
||||||
backend/riscv64/a32_core.h
|
|
||||||
backend/riscv64/a32_interface.cpp
|
|
||||||
backend/riscv64/code_block.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -423,7 +380,7 @@ target_link_libraries(dynarmic
|
||||||
)
|
)
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
target_link_libraries(dynarmic PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(dynarmic PRIVATE Boost::headers)
|
target_link_libraries(dynarmic PRIVATE Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2022 MerryMage
|
* Copyright (c) 2022 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -16,7 +19,7 @@
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/interface/A32/config.h"
|
#include "dynarmic/interface/A32/config.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
|
@ -163,21 +166,7 @@ A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||||
|
|
||||||
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::PolyfillPass(ir_block, {});
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nzc_to_nz = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
|
|
||||||
return ir_block;
|
return ir_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2022 MerryMage
|
* Copyright (c) 2022 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -15,7 +18,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
#include "dynarmic/interface/A64/config.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
|
@ -331,22 +334,7 @@ IR::Block A64AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
|
||||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{descriptor}, get_code,
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{descriptor}, get_code,
|
||||||
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A64CallbackConfigPass(ir_block, conf);
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::MiscIROpt)) {
|
|
||||||
Optimization::A64MergeInterpretBlocksPass(ir_block, conf.callbacks);
|
|
||||||
}
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
|
|
||||||
return ir_block;
|
return ir_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2016 MerryMage
|
* Copyright (c) 2016 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -13,15 +16,15 @@ struct ExceptionHandler::Impl final {
|
||||||
ExceptionHandler::ExceptionHandler() = default;
|
ExceptionHandler::ExceptionHandler() = default;
|
||||||
ExceptionHandler::~ExceptionHandler() = default;
|
ExceptionHandler::~ExceptionHandler() = default;
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include "dynarmic/backend/exception_handler.h"
|
#include "dynarmic/backend/exception_handler.h"
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
|
|
||||||
# include "dynarmic/backend/x64/block_of_code.h"
|
# include "dynarmic/backend/x64/block_of_code.h"
|
||||||
# define mig_external extern "C"
|
# define mig_external extern "C"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
using dynarmic_thread_state_t = x86_thread_state64_t;
|
using dynarmic_thread_state_t = x86_thread_state64_t;
|
||||||
|
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
|
||||||
# include <oaknut/code_block.hpp>
|
# include <oaknut/code_block.hpp>
|
||||||
# define mig_external extern "C"
|
# define mig_external extern "C"
|
||||||
|
@ -133,7 +133,7 @@ void MachHandler::MessagePump() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
|
||||||
|
|
||||||
return KERN_SUCCESS;
|
return KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) {
|
kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) {
|
||||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||||
|
|
||||||
|
@ -269,13 +269,13 @@ private:
|
||||||
ExceptionHandler::ExceptionHandler() = default;
|
ExceptionHandler::ExceptionHandler() = default;
|
||||||
ExceptionHandler::~ExceptionHandler() = default;
|
ExceptionHandler::~ExceptionHandler() = default;
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
void ExceptionHandler::Register(X64::BlockOfCode& code) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
|
||||||
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
const u64 code_end = code_begin + code.GetTotalCodeSize();
|
||||||
impl = std::make_unique<Impl>(code_begin, code_end);
|
impl = std::make_unique<Impl>(code_begin, code_end);
|
||||||
}
|
}
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
|
||||||
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
|
||||||
const u64 code_end = code_begin + size;
|
const u64 code_end = code_begin + size;
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
#include <mcl/macro/architecture.hpp>
|
#include <mcl/macro/architecture.hpp>
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
|
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include "dynarmic/backend/arm64/mig/mach_exc_server.c"
|
# include "dynarmic/backend/arm64/mig/mach_exc_server.c"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -12,31 +12,17 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sys/mman.h>
|
|
||||||
#ifdef __APPLE__
|
|
||||||
# include <signal.h>
|
|
||||||
# include <sys/ucontext.h>
|
|
||||||
#else
|
|
||||||
# include <signal.h>
|
|
||||||
# ifndef __OpenBSD__
|
|
||||||
# include <ucontext.h>
|
|
||||||
# endif
|
|
||||||
# ifdef __sun__
|
|
||||||
# include <sys/regset.h>
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ankerl/unordered_dense.h>
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include "dynarmic/backend/exception_handler.h"
|
#include "dynarmic/backend/exception_handler.h"
|
||||||
|
#include "dynarmic/common/assert.h"
|
||||||
|
#include "dynarmic/common/context.h"
|
||||||
#include "dynarmic/common/common_types.h"
|
#include "dynarmic/common/common_types.h"
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/block_of_code.h"
|
# include "dynarmic/backend/x64/block_of_code.h"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include <oaknut/code_block.hpp>
|
# include <oaknut/code_block.hpp>
|
||||||
|
|
||||||
# include "dynarmic/backend/arm64/abi.h"
|
# include "dynarmic/backend/arm64/abi.h"
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
# include "dynarmic/backend/riscv64/code_block.h"
|
# include "dynarmic/backend/riscv64/code_block.h"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
@ -129,35 +115,8 @@ void RegisterHandler() {
|
||||||
|
|
||||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||||
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||||
#ifndef MCL_ARCHITECTURE_RISCV
|
CTX_DECLARE(raw_context);
|
||||||
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
#ifndef __OpenBSD__
|
|
||||||
auto& mctx = ucontext->uc_mcontext;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
|
||||||
# if defined(__APPLE__)
|
|
||||||
# define CTX_RIP (mctx->__ss.__rip)
|
|
||||||
# define CTX_RSP (mctx->__ss.__rsp)
|
|
||||||
# elif defined(__linux__)
|
|
||||||
# define CTX_RIP (mctx.gregs[REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.gregs[REG_RSP])
|
|
||||||
# elif defined(__FreeBSD__)
|
|
||||||
# define CTX_RIP (mctx.mc_rip)
|
|
||||||
# define CTX_RSP (mctx.mc_rsp)
|
|
||||||
# elif defined(__NetBSD__)
|
|
||||||
# define CTX_RIP (mctx.__gregs[_REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.__gregs[_REG_RSP])
|
|
||||||
# elif defined(__OpenBSD__)
|
|
||||||
# define CTX_RIP (ucontext->sc_rip)
|
|
||||||
# define CTX_RSP (ucontext->sc_rsp)
|
|
||||||
# elif defined(__sun__)
|
|
||||||
# define CTX_RIP (mctx.gregs[REG_RIP])
|
|
||||||
# define CTX_RSP (mctx.gregs[REG_RSP])
|
|
||||||
# else
|
|
||||||
# error "Unknown platform"
|
|
||||||
# endif
|
|
||||||
{
|
{
|
||||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_RIP); iter != sig_handler->code_block_infos.end()) {
|
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_RIP); iter != sig_handler->code_block_infos.end()) {
|
||||||
|
@ -169,48 +128,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# if defined(__APPLE__)
|
|
||||||
# define CTX_PC (mctx->__ss.__pc)
|
|
||||||
# define CTX_SP (mctx->__ss.__sp)
|
|
||||||
# define CTX_LR (mctx->__ss.__lr)
|
|
||||||
# define CTX_X(i) (mctx->__ss.__x[i])
|
|
||||||
# define CTX_Q(i) (mctx->__ns.__v[i])
|
|
||||||
# elif defined(__linux__)
|
|
||||||
# define CTX_PC (mctx.pc)
|
|
||||||
# define CTX_SP (mctx.sp)
|
|
||||||
# define CTX_LR (mctx.regs[30])
|
|
||||||
# define CTX_X(i) (mctx.regs[i])
|
|
||||||
# define CTX_Q(i) (fpctx->vregs[i])
|
|
||||||
[[maybe_unused]] const auto fpctx = [&mctx] {
|
|
||||||
_aarch64_ctx* header = (_aarch64_ctx*)&mctx.__reserved;
|
|
||||||
while (header->magic != FPSIMD_MAGIC) {
|
|
||||||
ASSERT(header->magic && header->size);
|
|
||||||
header = (_aarch64_ctx*)((char*)header + header->size);
|
|
||||||
}
|
|
||||||
return (fpsimd_context*)header;
|
|
||||||
}();
|
|
||||||
# elif defined(__FreeBSD__)
|
|
||||||
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
|
||||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
|
||||||
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
|
||||||
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
|
||||||
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
|
||||||
# elif defined(__NetBSD__)
|
|
||||||
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
|
||||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
|
||||||
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
|
||||||
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
|
||||||
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
|
||||||
# elif defined(__OpenBSD__)
|
|
||||||
# define CTX_PC (ucontext->sc_elr)
|
|
||||||
# define CTX_SP (ucontext->sc_sp)
|
|
||||||
# define CTX_LR (ucontext->sc_lr)
|
|
||||||
# define CTX_X(i) (ucontext->sc_x[i])
|
|
||||||
# define CTX_Q(i) (ucontext->sc_q[i])
|
|
||||||
# else
|
|
||||||
# error "Unknown platform"
|
|
||||||
# endif
|
|
||||||
{
|
{
|
||||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||||
|
@ -220,7 +138,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||||
#elif defined(MCL_ARCHITECTURE_RISCV)
|
#elif defined(ARCHITECTURE_riscv64)
|
||||||
ASSERT_FALSE("Unimplemented");
|
ASSERT_FALSE("Unimplemented");
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2023 MerryMage
|
* Copyright (c) 2023 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -5,9 +8,9 @@
|
||||||
|
|
||||||
#include <mcl/macro/architecture.hpp>
|
#include <mcl/macro/architecture.hpp>
|
||||||
|
|
||||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
|
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
|
||||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
# include "dynarmic/backend/exception_handler_generic.cpp"
|
# include "dynarmic/backend/exception_handler_generic.cpp"
|
||||||
#else
|
#else
|
||||||
# error "Invalid architecture"
|
# error "Invalid architecture"
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "dynarmic/backend/riscv64/stack_layout.h"
|
#include "dynarmic/backend/riscv64/stack_layout.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::RV64 {
|
namespace Dynarmic::Backend::RV64 {
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/location_descriptor.h"
|
#include "dynarmic/ir/location_descriptor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
|
@ -217,19 +217,7 @@ private:
|
||||||
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
||||||
|
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
return emitter.Emit(ir_block);
|
return emitter.Emit(ir_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/interface/A64/a64.h"
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::A64 {
|
namespace Dynarmic::A64 {
|
||||||
|
|
||||||
|
@ -275,21 +275,7 @@ private:
|
||||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
||||||
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||||
Optimization::A64CallbackConfigPass(ir_block, conf);
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::ConstProp)) {
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
}
|
|
||||||
if (conf.HasOptimization(OptimizationFlag::MiscIROpt)) {
|
|
||||||
Optimization::A64MergeInterpretBlocksPass(ir_block, conf.callbacks);
|
|
||||||
}
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
return emitter.Emit(ir_block).entrypoint;
|
return emitter.Emit(ir_block).entrypoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
120
src/dynarmic/src/dynarmic/common/context.h
Normal file
120
src/dynarmic/src/dynarmic/common/context.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <signal.h>
|
||||||
|
# include <sys/ucontext.h>
|
||||||
|
#else
|
||||||
|
# include <signal.h>
|
||||||
|
# ifndef __OpenBSD__
|
||||||
|
# include <ucontext.h>
|
||||||
|
# endif
|
||||||
|
# ifdef __sun__
|
||||||
|
# include <sys/regset.h>
|
||||||
|
# endif
|
||||||
|
# ifdef __linux__
|
||||||
|
# include <sys/syscall.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
# ifdef __OpenBSD__
|
||||||
|
# define CTX_DECLARE(raw_context) ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
||||||
|
# else
|
||||||
|
# define CTX_DECLARE(raw_context) \
|
||||||
|
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context); \
|
||||||
|
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext;
|
||||||
|
# endif
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
# ifdef __OpenBSD__
|
||||||
|
# define CTX_DECLARE(raw_context) ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context);
|
||||||
|
# else
|
||||||
|
# define CTX_DECLARE(raw_context) \
|
||||||
|
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(raw_context); \
|
||||||
|
[[maybe_unused]] auto& mctx = ucontext->uc_mcontext; \
|
||||||
|
[[maybe_unused]] const auto fpctx = GetFloatingPointState(mctx);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
|
# if defined(__APPLE__)
|
||||||
|
# define CTX_RIP (mctx->__ss.__rip)
|
||||||
|
# define CTX_RSP (mctx->__ss.__rsp)
|
||||||
|
# elif defined(__linux__)
|
||||||
|
# define CTX_RIP (mctx.gregs[REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.gregs[REG_RSP])
|
||||||
|
# elif defined(__FreeBSD__)
|
||||||
|
# define CTX_RIP (mctx.mc_rip)
|
||||||
|
# define CTX_RSP (mctx.mc_rsp)
|
||||||
|
# elif defined(__NetBSD__)
|
||||||
|
# define CTX_RIP (mctx.__gregs[_REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.__gregs[_REG_RSP])
|
||||||
|
# elif defined(__OpenBSD__)
|
||||||
|
# define CTX_RIP (ucontext->sc_rip)
|
||||||
|
# define CTX_RSP (ucontext->sc_rsp)
|
||||||
|
# elif defined(__sun__)
|
||||||
|
# define CTX_RIP (mctx.gregs[REG_RIP])
|
||||||
|
# define CTX_RSP (mctx.gregs[REG_RSP])
|
||||||
|
# else
|
||||||
|
# error "Unknown platform"
|
||||||
|
# endif
|
||||||
|
#elif defined(ARCHITECTURE_arm64)
|
||||||
|
# if defined(__APPLE__)
|
||||||
|
# define CTX_PC (mctx->__ss.__pc)
|
||||||
|
# define CTX_SP (mctx->__ss.__sp)
|
||||||
|
# define CTX_LR (mctx->__ss.__lr)
|
||||||
|
# define CTX_PSTATE (mctx->__ss.__cpsr)
|
||||||
|
# define CTX_X(i) (mctx->__ss.__x[i])
|
||||||
|
# define CTX_Q(i) (mctx->__ns.__v[i])
|
||||||
|
# define CTX_FPSR (mctx->__ns.__fpsr)
|
||||||
|
# define CTX_FPCR (mctx->__ns.__fpcr)
|
||||||
|
# elif defined(__linux__)
|
||||||
|
# define CTX_PC (mctx.pc)
|
||||||
|
# define CTX_SP (mctx.sp)
|
||||||
|
# define CTX_LR (mctx.regs[30])
|
||||||
|
# define CTX_PSTATE (mctx.pstate)
|
||||||
|
# define CTX_X(i) (mctx.regs[i])
|
||||||
|
# define CTX_Q(i) (fpctx->vregs[i])
|
||||||
|
# define CTX_FPSR (fpctx->fpsr)
|
||||||
|
# define CTX_FPCR (fpctx->fpcr)
|
||||||
|
# elif defined(__FreeBSD__)
|
||||||
|
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
||||||
|
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||||
|
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
||||||
|
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
||||||
|
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
||||||
|
# elif defined(__NetBSD__)
|
||||||
|
# define CTX_PC (mctx.mc_gpregs.gp_elr)
|
||||||
|
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||||
|
# define CTX_LR (mctx.mc_gpregs.gp_lr)
|
||||||
|
# define CTX_X(i) (mctx.mc_gpregs.gp_x[i])
|
||||||
|
# define CTX_Q(i) (mctx.mc_fpregs.fp_q[i])
|
||||||
|
# elif defined(__OpenBSD__)
|
||||||
|
# define CTX_PC (ucontext->sc_elr)
|
||||||
|
# define CTX_SP (ucontext->sc_sp)
|
||||||
|
# define CTX_LR (ucontext->sc_lr)
|
||||||
|
# define CTX_X(i) (ucontext->sc_x[i])
|
||||||
|
# define CTX_Q(i) (ucontext->sc_q[i])
|
||||||
|
# else
|
||||||
|
# error "Unknown platform"
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# error "unimplemented"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
#ifdef __APPLE__
|
||||||
|
inline _STRUCT_ARM_NEON_STATE64* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||||
|
return &(host_ctx->__ns);
|
||||||
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
inline fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||||
|
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
|
||||||
|
while (header->magic != FPSIMD_MAGIC)
|
||||||
|
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
|
||||||
|
return reinterpret_cast<fpsimd_context*>(header);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -1,70 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/interface/A32/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|
||||||
for (auto& inst : block) {
|
|
||||||
switch (inst.GetOpcode()) {
|
|
||||||
case IR::Opcode::A32ReadMemory8: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory16: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory32: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32ReadMemory64: {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
|
||||||
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,382 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
|
|
||||||
#include "dynarmic/frontend/A32/a32_types.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void FlagsPass(IR::Block& block) {
|
|
||||||
using Iterator = std::reverse_iterator<IR::Block::iterator>;
|
|
||||||
|
|
||||||
struct FlagInfo {
|
|
||||||
bool set_not_required = false;
|
|
||||||
bool has_value_request = false;
|
|
||||||
Iterator value_request = {};
|
|
||||||
};
|
|
||||||
struct ValuelessFlagInfo {
|
|
||||||
bool set_not_required = false;
|
|
||||||
};
|
|
||||||
ValuelessFlagInfo nzcvq;
|
|
||||||
ValuelessFlagInfo nzcv;
|
|
||||||
ValuelessFlagInfo nz;
|
|
||||||
FlagInfo c_flag;
|
|
||||||
FlagInfo ge;
|
|
||||||
|
|
||||||
auto do_set = [&](FlagInfo& info, IR::Value value, Iterator inst) {
|
|
||||||
if (info.has_value_request) {
|
|
||||||
info.value_request->ReplaceUsesWith(value);
|
|
||||||
}
|
|
||||||
info.has_value_request = false;
|
|
||||||
|
|
||||||
if (info.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
info.set_not_required = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto do_set_valueless = [&](ValuelessFlagInfo& info, Iterator inst) {
|
|
||||||
if (info.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
info.set_not_required = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto do_get = [](FlagInfo& info, Iterator inst) {
|
|
||||||
if (info.has_value_request) {
|
|
||||||
info.value_request->ReplaceUsesWith(IR::Value{&*inst});
|
|
||||||
}
|
|
||||||
info.has_value_request = true;
|
|
||||||
info.value_request = inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
|
||||||
|
|
||||||
for (auto inst = block.rbegin(); inst != block.rend(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A32GetCFlag: {
|
|
||||||
do_get(c_flag, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCV: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
ir.SetInsertionPointBefore(inst.base()); // base is one ahead
|
|
||||||
IR::U1 c = ir.GetCFlagFromNZCV(IR::NZCV{inst->GetArg(0)});
|
|
||||||
c_flag.value_request->ReplaceUsesWith(c);
|
|
||||||
c_flag.has_value_request = false;
|
|
||||||
break; // This case will be executed again because of the above
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcv, inst);
|
|
||||||
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCVRaw: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
nzcv.set_not_required = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcv, inst);
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZCVQ: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
nzcvq.set_not_required = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_set_valueless(nzcvq, inst);
|
|
||||||
|
|
||||||
nzcv = {.set_not_required = true};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
c_flag = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZ: {
|
|
||||||
do_set_valueless(nz, inst);
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetCpsrNZC: {
|
|
||||||
if (c_flag.has_value_request) {
|
|
||||||
c_flag.value_request->ReplaceUsesWith(inst->GetArg(1));
|
|
||||||
c_flag.has_value_request = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inst->GetArg(1).IsImmediate() && inst->GetArg(1).GetInstRecursive()->GetOpcode() == IR::Opcode::A32GetCFlag) {
|
|
||||||
const auto nz_value = inst->GetArg(0);
|
|
||||||
|
|
||||||
inst->Invalidate();
|
|
||||||
|
|
||||||
ir.SetInsertionPointBefore(inst.base());
|
|
||||||
ir.SetCpsrNZ(IR::NZCV{nz_value});
|
|
||||||
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
nz = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nz.set_not_required && c_flag.set_not_required) {
|
|
||||||
inst->Invalidate();
|
|
||||||
} else if (nz.set_not_required) {
|
|
||||||
inst->SetArg(0, IR::Value::EmptyNZCVImmediateMarker());
|
|
||||||
}
|
|
||||||
nz.set_not_required = true;
|
|
||||||
c_flag.set_not_required = true;
|
|
||||||
|
|
||||||
nzcv = {};
|
|
||||||
nzcvq = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetGEFlags: {
|
|
||||||
do_set(ge, inst->GetArg(0), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetGEFlags: {
|
|
||||||
do_get(ge, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetGEFlagsCompressed: {
|
|
||||||
ge = {.set_not_required = true};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32OrQFlag: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCPSR(opcode) || WritesToCPSR(opcode)) {
|
|
||||||
nzcvq = {};
|
|
||||||
nzcv = {};
|
|
||||||
nz = {};
|
|
||||||
c_flag = {};
|
|
||||||
ge = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegisterPass(IR::Block& block) {
|
|
||||||
using Iterator = IR::Block::iterator;
|
|
||||||
|
|
||||||
struct RegInfo {
|
|
||||||
IR::Value register_value;
|
|
||||||
std::optional<Iterator> last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<RegInfo, 15> reg_info;
|
|
||||||
|
|
||||||
const auto do_get = [](RegInfo& info, Iterator get_inst) {
|
|
||||||
if (info.register_value.IsEmpty()) {
|
|
||||||
info.register_value = IR::Value(&*get_inst);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
get_inst->ReplaceUsesWith(info.register_value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_set = [](RegInfo& info, IR::Value value, Iterator set_inst) {
|
|
||||||
if (info.last_set_instruction) {
|
|
||||||
(*info.last_set_instruction)->Invalidate();
|
|
||||||
}
|
|
||||||
info = {
|
|
||||||
.register_value = value,
|
|
||||||
.last_set_instruction = set_inst,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ExtValueType {
|
|
||||||
Empty,
|
|
||||||
Single,
|
|
||||||
Double,
|
|
||||||
VectorDouble,
|
|
||||||
VectorQuad,
|
|
||||||
};
|
|
||||||
struct ExtRegInfo {
|
|
||||||
ExtValueType value_type = {};
|
|
||||||
IR::Value register_value;
|
|
||||||
std::optional<Iterator> last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<ExtRegInfo, 64> ext_reg_info;
|
|
||||||
|
|
||||||
const auto do_ext_get = [](ExtValueType type, std::initializer_list<std::reference_wrapper<ExtRegInfo>> infos, Iterator get_inst) {
|
|
||||||
if (!std::all_of(infos.begin(), infos.end(), [type](const auto& info) { return info.get().value_type == type; })) {
|
|
||||||
for (auto& info : infos) {
|
|
||||||
info.get() = {
|
|
||||||
.value_type = type,
|
|
||||||
.register_value = IR::Value(&*get_inst),
|
|
||||||
.last_set_instruction = std::nullopt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
get_inst->ReplaceUsesWith(std::data(infos)[0].get().register_value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_ext_set = [](ExtValueType type, std::initializer_list<std::reference_wrapper<ExtRegInfo>> infos, IR::Value value, Iterator set_inst) {
|
|
||||||
if (std::all_of(infos.begin(), infos.end(), [type](const auto& info) { return info.get().value_type == type; })) {
|
|
||||||
if (std::data(infos)[0].get().last_set_instruction) {
|
|
||||||
(*std::data(infos)[0].get().last_set_instruction)->Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& info : infos) {
|
|
||||||
info.get() = {
|
|
||||||
.value_type = type,
|
|
||||||
.register_value = value,
|
|
||||||
.last_set_instruction = set_inst,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Location and version don't matter here.
|
|
||||||
A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}};
|
|
||||||
|
|
||||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A32GetRegister: {
|
|
||||||
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
|
||||||
ASSERT(reg != A32::Reg::PC);
|
|
||||||
const size_t reg_index = static_cast<size_t>(reg);
|
|
||||||
do_get(reg_info[reg_index], inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetRegister: {
|
|
||||||
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
|
||||||
if (reg == A32::Reg::PC) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto reg_index = static_cast<size_t>(reg);
|
|
||||||
do_set(reg_info[reg_index], inst->GetArg(1), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetExtendedRegister32: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_get(ExtValueType::Single, {ext_reg_info[reg_index]}, inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetExtendedRegister32: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_set(ExtValueType::Single, {ext_reg_info[reg_index]}, inst->GetArg(1), inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetExtendedRegister64: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_get(ExtValueType::Double,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetExtendedRegister64: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
do_ext_set(ExtValueType::Double,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst->GetArg(1),
|
|
||||||
inst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32GetVector: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
if (A32::IsDoubleExtReg(reg)) {
|
|
||||||
do_ext_get(ExtValueType::VectorDouble,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
} else {
|
|
||||||
DEBUG_ASSERT(A32::IsQuadExtReg(reg));
|
|
||||||
do_ext_get(ExtValueType::VectorQuad,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 4 + 0],
|
|
||||||
ext_reg_info[reg_index * 4 + 1],
|
|
||||||
ext_reg_info[reg_index * 4 + 2],
|
|
||||||
ext_reg_info[reg_index * 4 + 3],
|
|
||||||
},
|
|
||||||
inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A32SetVector: {
|
|
||||||
const A32::ExtReg reg = inst->GetArg(0).GetA32ExtRegRef();
|
|
||||||
const size_t reg_index = A32::RegNumber(reg);
|
|
||||||
if (A32::IsDoubleExtReg(reg)) {
|
|
||||||
ir.SetInsertionPointAfter(inst);
|
|
||||||
const IR::U128 stored_value = ir.VectorZeroUpper(IR::U128{inst->GetArg(1)});
|
|
||||||
do_ext_set(ExtValueType::VectorDouble,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 2 + 0],
|
|
||||||
ext_reg_info[reg_index * 2 + 1],
|
|
||||||
},
|
|
||||||
stored_value,
|
|
||||||
inst);
|
|
||||||
} else {
|
|
||||||
DEBUG_ASSERT(A32::IsQuadExtReg(reg));
|
|
||||||
do_ext_set(ExtValueType::VectorQuad,
|
|
||||||
{
|
|
||||||
ext_reg_info[reg_index * 4 + 0],
|
|
||||||
ext_reg_info[reg_index * 4 + 1],
|
|
||||||
ext_reg_info[reg_index * 4 + 2],
|
|
||||||
ext_reg_info[reg_index * 4 + 3],
|
|
||||||
},
|
|
||||||
inst->GetArg(1),
|
|
||||||
inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCoreRegister(opcode) || WritesToCoreRegister(opcode)) {
|
|
||||||
reg_info = {};
|
|
||||||
ext_reg_info = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions) {
|
|
||||||
FlagsPass(block);
|
|
||||||
RegisterPass(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,57 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2018 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_ir_emitter.h"
|
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf) {
|
|
||||||
if (conf.hook_data_cache_operations) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& inst : block) {
|
|
||||||
if (inst.GetOpcode() != IR::Opcode::A64DataCacheOperationRaised) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto op = static_cast<A64::DataCacheOperation>(inst.GetArg(1).GetU64());
|
|
||||||
if (op == A64::DataCacheOperation::ZeroByVA) {
|
|
||||||
A64::IREmitter ir{block};
|
|
||||||
ir.current_location = A64::LocationDescriptor{IR::LocationDescriptor{inst.GetArg(0).GetU64()}};
|
|
||||||
ir.SetInsertionPointBefore(&inst);
|
|
||||||
|
|
||||||
size_t bytes = 4 << static_cast<size_t>(conf.dczid_el0 & 0b1111);
|
|
||||||
IR::U64 addr{inst.GetArg(2)};
|
|
||||||
|
|
||||||
const IR::U128 zero_u128 = ir.ZeroExtendToQuad(ir.Imm64(0));
|
|
||||||
while (bytes >= 16) {
|
|
||||||
ir.WriteMemory128(addr, zero_u128, IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(16));
|
|
||||||
bytes -= 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (bytes >= 8) {
|
|
||||||
ir.WriteMemory64(addr, ir.Imm64(0), IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(8));
|
|
||||||
bytes -= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (bytes >= 4) {
|
|
||||||
ir.WriteMemory32(addr, ir.Imm32(0), IR::AccType::DCZVA);
|
|
||||||
addr = ir.Add(addr, ir.Imm64(4));
|
|
||||||
bytes -= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inst.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,165 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_types.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64GetSetElimination(IR::Block& block) {
|
|
||||||
using Iterator = IR::Block::iterator;
|
|
||||||
|
|
||||||
enum class TrackingType {
|
|
||||||
W,
|
|
||||||
X,
|
|
||||||
S,
|
|
||||||
D,
|
|
||||||
Q,
|
|
||||||
SP,
|
|
||||||
NZCV,
|
|
||||||
NZCVRaw,
|
|
||||||
};
|
|
||||||
struct RegisterInfo {
|
|
||||||
IR::Value register_value;
|
|
||||||
TrackingType tracking_type;
|
|
||||||
bool set_instruction_present = false;
|
|
||||||
Iterator last_set_instruction;
|
|
||||||
};
|
|
||||||
std::array<RegisterInfo, 31> reg_info;
|
|
||||||
std::array<RegisterInfo, 32> vec_info;
|
|
||||||
RegisterInfo sp_info;
|
|
||||||
RegisterInfo nzcv_info;
|
|
||||||
|
|
||||||
const auto do_set = [&block](RegisterInfo& info, IR::Value value, Iterator set_inst, TrackingType tracking_type) {
|
|
||||||
if (info.set_instruction_present) {
|
|
||||||
info.last_set_instruction->Invalidate();
|
|
||||||
block.Instructions().erase(info.last_set_instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.register_value = value;
|
|
||||||
info.tracking_type = tracking_type;
|
|
||||||
info.set_instruction_present = true;
|
|
||||||
info.last_set_instruction = set_inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto do_get = [](RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) {
|
|
||||||
const auto do_nothing = [&] {
|
|
||||||
info = {};
|
|
||||||
info.register_value = IR::Value(&*get_inst);
|
|
||||||
info.tracking_type = tracking_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (info.register_value.IsEmpty()) {
|
|
||||||
do_nothing();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.tracking_type == tracking_type) {
|
|
||||||
get_inst->ReplaceUsesWith(info.register_value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_nothing();
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto inst = block.begin(); inst != block.end(); ++inst) {
|
|
||||||
auto const opcode = inst->GetOpcode();
|
|
||||||
switch (opcode) {
|
|
||||||
case IR::Opcode::A64GetW: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_get(reg_info.at(index), inst, TrackingType::W);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetX: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_get(reg_info.at(index), inst, TrackingType::X);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetS: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::S);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetD: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::D);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetQ: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_get(vec_info.at(index), inst, TrackingType::Q);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetSP: {
|
|
||||||
do_get(sp_info, inst, TrackingType::SP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64GetNZCVRaw: {
|
|
||||||
do_get(nzcv_info, inst, TrackingType::NZCVRaw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetW: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_set(reg_info.at(index), inst->GetArg(1), inst, TrackingType::W);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetX: {
|
|
||||||
const size_t index = A64::RegNumber(inst->GetArg(0).GetA64RegRef());
|
|
||||||
do_set(reg_info.at(index), inst->GetArg(1), inst, TrackingType::X);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetS: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::S);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetD: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::D);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetQ: {
|
|
||||||
const size_t index = A64::VecNumber(inst->GetArg(0).GetA64VecRef());
|
|
||||||
do_set(vec_info.at(index), inst->GetArg(1), inst, TrackingType::Q);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetSP: {
|
|
||||||
do_set(sp_info, inst->GetArg(0), inst, TrackingType::SP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetNZCV: {
|
|
||||||
do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCV);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IR::Opcode::A64SetNZCVRaw: {
|
|
||||||
do_set(nzcv_info, inst->GetArg(0), inst, TrackingType::NZCVRaw);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (ReadsFromCPSR(opcode) || WritesToCPSR(opcode)) {
|
|
||||||
nzcv_info = {};
|
|
||||||
}
|
|
||||||
if (ReadsFromCoreRegister(opcode) || WritesToCoreRegister(opcode)) {
|
|
||||||
reg_info = {};
|
|
||||||
vec_info = {};
|
|
||||||
sp_info = {};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,57 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2018 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <boost/variant/get.hpp>
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
|
||||||
#include "dynarmic/interface/A64/config.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
|
||||||
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
|
||||||
const auto instruction = cb->MemoryReadCode(location.PC());
|
|
||||||
if (!instruction)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
IR::Block new_block{location};
|
|
||||||
A64::TranslateSingleInstruction(new_block, location, *instruction);
|
|
||||||
|
|
||||||
if (!new_block.Instructions().empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const IR::Terminal terminal = new_block.GetTerminal();
|
|
||||||
if (auto term = boost::get<IR::Term::Interpret>(&terminal)) {
|
|
||||||
return term->next == location;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
IR::Terminal terminal = block.GetTerminal();
|
|
||||||
auto term = boost::get<IR::Term::Interpret>(&terminal);
|
|
||||||
if (!term)
|
|
||||||
return;
|
|
||||||
|
|
||||||
A64::LocationDescriptor location{term->next};
|
|
||||||
size_t num_instructions = 1;
|
|
||||||
|
|
||||||
while (is_interpret_instruction(location.AdvancePC(static_cast<int>(num_instructions * 4)))) {
|
|
||||||
num_instructions++;
|
|
||||||
}
|
|
||||||
|
|
||||||
term->num_instructions = num_instructions;
|
|
||||||
block.ReplaceTerminal(terminal);
|
|
||||||
block.CycleCount() += num_instructions - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,559 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include <mcl/bit/rotate.hpp>
|
|
||||||
#include <mcl/bit/swap.hpp>
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
|
|
||||||
#include "dynarmic/common/safe_ops.h"
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/ir_emitter.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
using Op = Dynarmic::IR::Opcode;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Tiny helper to avoid the need to store based off the opcode
|
|
||||||
// bit size all over the place within folding functions.
|
|
||||||
void ReplaceUsesWith(IR::Inst& inst, bool is_32_bit, u64 value) {
|
|
||||||
if (is_32_bit) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
} else {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::Value Value(bool is_32_bit, u64 value) {
|
|
||||||
return is_32_bit ? IR::Value{static_cast<u32>(value)} : IR::Value{value};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ImmFn>
|
|
||||||
bool FoldCommutative(IR::Inst& inst, bool is_32_bit, ImmFn imm_fn) {
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
|
|
||||||
const bool is_lhs_immediate = lhs.IsImmediate();
|
|
||||||
const bool is_rhs_immediate = rhs.IsImmediate();
|
|
||||||
|
|
||||||
if (is_lhs_immediate && is_rhs_immediate) {
|
|
||||||
const u64 result = imm_fn(lhs.GetImmediateAsU64(), rhs.GetImmediateAsU64());
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_lhs_immediate && !is_rhs_immediate) {
|
|
||||||
const IR::Inst* rhs_inst = rhs.GetInstRecursive();
|
|
||||||
if (rhs_inst->GetOpcode() == inst.GetOpcode() && rhs_inst->GetArg(1).IsImmediate()) {
|
|
||||||
const u64 combined = imm_fn(lhs.GetImmediateAsU64(), rhs_inst->GetArg(1).GetImmediateAsU64());
|
|
||||||
inst.SetArg(0, rhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
} else {
|
|
||||||
// Normalize
|
|
||||||
inst.SetArg(0, rhs);
|
|
||||||
inst.SetArg(1, lhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_lhs_immediate && is_rhs_immediate) {
|
|
||||||
const IR::Inst* lhs_inst = lhs.GetInstRecursive();
|
|
||||||
if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->GetArg(1).IsImmediate()) {
|
|
||||||
const u64 combined = imm_fn(rhs.GetImmediateAsU64(), lhs_inst->GetArg(1).GetImmediateAsU64());
|
|
||||||
inst.SetArg(0, lhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldAdd(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
const auto carry = inst.GetArg(2);
|
|
||||||
|
|
||||||
if (lhs.IsImmediate() && !rhs.IsImmediate()) {
|
|
||||||
// Normalize
|
|
||||||
inst.SetArg(0, rhs);
|
|
||||||
inst.SetArg(1, lhs);
|
|
||||||
FoldAdd(inst, is_32_bit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.HasAssociatedPseudoOperation()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lhs.IsImmediate() && rhs.IsImmediate()) {
|
|
||||||
const IR::Inst* lhs_inst = lhs.GetInstRecursive();
|
|
||||||
if (lhs_inst->GetOpcode() == inst.GetOpcode() && lhs_inst->GetArg(1).IsImmediate() && lhs_inst->GetArg(2).IsImmediate()) {
|
|
||||||
const u64 combined = rhs.GetImmediateAsU64() + lhs_inst->GetArg(1).GetImmediateAsU64() + lhs_inst->GetArg(2).GetU1();
|
|
||||||
if (combined == 0) {
|
|
||||||
inst.ReplaceUsesWith(lhs_inst->GetArg(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inst.SetArg(0, lhs_inst->GetArg(0));
|
|
||||||
inst.SetArg(1, Value(is_32_bit, combined));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rhs.IsZero() && carry.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(lhs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() + rhs.GetImmediateAsU64() + carry.GetU1();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds AND operations based on the following:
|
|
||||||
///
|
|
||||||
/// 1. imm_x & imm_y -> result
|
|
||||||
/// 2. x & 0 -> 0
|
|
||||||
/// 3. 0 & y -> 0
|
|
||||||
/// 4. x & y -> y (where x has all bits set to 1)
|
|
||||||
/// 5. x & y -> x (where y has all bits set to 1)
|
|
||||||
///
|
|
||||||
void FoldAND(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a & b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
} else if (rhs.HasAllBitsSet()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds byte reversal opcodes based on the following:
|
|
||||||
///
|
|
||||||
/// 1. imm -> swap(imm)
|
|
||||||
///
|
|
||||||
void FoldByteReverse(IR::Inst& inst, Op op) {
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
|
|
||||||
if (!operand.IsImmediate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op == Op::ByteReverseWord) {
|
|
||||||
const u32 result = mcl::bit::swap_bytes_32(static_cast<u32>(operand.GetImmediateAsU64()));
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
} else if (op == Op::ByteReverseHalf) {
|
|
||||||
const u16 result = mcl::bit::swap_bytes_16(static_cast<u16>(operand.GetImmediateAsU64()));
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
} else {
|
|
||||||
const u64 result = mcl::bit::swap_bytes_64(operand.GetImmediateAsU64());
|
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Folds division operations based on the following:
|
|
||||||
///
|
|
||||||
/// 1. x / 0 -> 0 (NOTE: This is an ARM-specific behavior defined in the architecture reference manual)
|
|
||||||
/// 2. imm_x / imm_y -> result
|
|
||||||
/// 3. x / 1 -> x
|
|
||||||
///
|
|
||||||
void FoldDivide(IR::Inst& inst, bool is_32_bit, bool is_signed) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
if (lhs.IsImmediate() && rhs.IsImmediate()) {
|
|
||||||
if (is_signed) {
|
|
||||||
const s64 result = lhs.GetImmediateAsS64() / rhs.GetImmediateAsS64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, static_cast<u64>(result));
|
|
||||||
} else {
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() / rhs.GetImmediateAsU64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
} else if (rhs.IsUnsignedImmediate(1)) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{lhs});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds EOR operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x ^ imm_y -> result
|
|
||||||
// 2. x ^ 0 -> x
|
|
||||||
// 3. 0 ^ y -> y
|
|
||||||
//
|
|
||||||
void FoldEOR(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a ^ b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantByte(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u8>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantHalf(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u16>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldLeastSignificantWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldMostSignificantBit(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
inst.ReplaceUsesWith(IR::Value{(operand.GetImmediateAsU64() >> 31) != 0});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldMostSignificantWord(IR::Inst& inst) {
|
|
||||||
IR::Inst* carry_inst = inst.GetAssociatedPseudoOperation(Op::GetCarryFromOp);
|
|
||||||
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
if (carry_inst) {
|
|
||||||
carry_inst->ReplaceUsesWith(IR::Value{mcl::bit::get_bit<31>(operand.GetImmediateAsU64())});
|
|
||||||
}
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(operand.GetImmediateAsU64() >> 32)});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds multiplication operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x * imm_y -> result
|
|
||||||
// 2. x * 0 -> 0
|
|
||||||
// 3. 0 * y -> 0
|
|
||||||
// 4. x * 1 -> x
|
|
||||||
// 5. 1 * y -> y
|
|
||||||
//
|
|
||||||
void FoldMultiply(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a * b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, 0);
|
|
||||||
} else if (rhs.IsUnsignedImmediate(1)) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds NOT operations if the contained value is an immediate.
|
|
||||||
void FoldNOT(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
const auto operand = inst.GetArg(0);
|
|
||||||
|
|
||||||
if (!operand.IsImmediate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 result = ~operand.GetImmediateAsU64();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folds OR operations based on the following:
|
|
||||||
//
|
|
||||||
// 1. imm_x | imm_y -> result
|
|
||||||
// 2. x | 0 -> x
|
|
||||||
// 3. 0 | y -> y
|
|
||||||
//
|
|
||||||
void FoldOR(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (FoldCommutative(inst, is_32_bit, [](u64 a, u64 b) { return a | b; })) {
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
if (rhs.IsZero()) {
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FoldShifts(IR::Inst& inst) {
|
|
||||||
IR::Inst* carry_inst = inst.GetAssociatedPseudoOperation(Op::GetCarryFromOp);
|
|
||||||
|
|
||||||
// The 32-bit variants can contain 3 arguments, while the
|
|
||||||
// 64-bit variants only contain 2.
|
|
||||||
if (inst.NumArgs() == 3 && !carry_inst) {
|
|
||||||
inst.SetArg(2, IR::Value(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto shift_amount = inst.GetArg(1);
|
|
||||||
|
|
||||||
if (shift_amount.IsZero()) {
|
|
||||||
if (carry_inst) {
|
|
||||||
carry_inst->ReplaceUsesWith(inst.GetArg(2));
|
|
||||||
}
|
|
||||||
inst.ReplaceUsesWith(inst.GetArg(0));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.NumArgs() == 3 && shift_amount.IsImmediate() && !shift_amount.IsZero()) {
|
|
||||||
inst.SetArg(2, IR::Value(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inst.AreAllArgsImmediates() || carry_inst) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSignExtendXToWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const s64 value = inst.GetArg(0).GetImmediateAsS64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSignExtendXToLong(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const s64 value = inst.GetArg(0).GetImmediateAsS64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u64>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldSub(IR::Inst& inst, bool is_32_bit) {
|
|
||||||
if (!inst.AreAllArgsImmediates() || inst.HasAssociatedPseudoOperation()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto lhs = inst.GetArg(0);
|
|
||||||
const auto rhs = inst.GetArg(1);
|
|
||||||
const auto carry = inst.GetArg(2);
|
|
||||||
|
|
||||||
const u64 result = lhs.GetImmediateAsU64() + (~rhs.GetImmediateAsU64()) + carry.GetU1();
|
|
||||||
ReplaceUsesWith(inst, is_32_bit, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldZeroExtendXToWord(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 value = inst.GetArg(0).GetImmediateAsU64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(value)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FoldZeroExtendXToLong(IR::Inst& inst) {
|
|
||||||
if (!inst.AreAllArgsImmediates()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 value = inst.GetArg(0).GetImmediateAsU64();
|
|
||||||
inst.ReplaceUsesWith(IR::Value{value});
|
|
||||||
}
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
void ConstantPropagation(IR::Block& block) {
|
|
||||||
for (auto& inst : block) {
|
|
||||||
const auto opcode = inst.GetOpcode();
|
|
||||||
|
|
||||||
switch (opcode) {
|
|
||||||
case Op::LeastSignificantWord:
|
|
||||||
FoldLeastSignificantWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::MostSignificantWord:
|
|
||||||
FoldMostSignificantWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::LeastSignificantHalf:
|
|
||||||
FoldLeastSignificantHalf(inst);
|
|
||||||
break;
|
|
||||||
case Op::LeastSignificantByte:
|
|
||||||
FoldLeastSignificantByte(inst);
|
|
||||||
break;
|
|
||||||
case Op::MostSignificantBit:
|
|
||||||
FoldMostSignificantBit(inst);
|
|
||||||
break;
|
|
||||||
case Op::IsZero32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{inst.GetArg(0).GetU32() == 0});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::IsZero64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
inst.ReplaceUsesWith(IR::Value{inst.GetArg(0).GetU64() == 0});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeft32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::LogicalShiftLeft<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeft64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::LogicalShiftLeft<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::LogicalShiftRight<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::LogicalShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, Safe::ArithmeticShiftRight<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, Safe::ArithmeticShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRight32:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRight64:
|
|
||||||
if (FoldShifts(inst)) {
|
|
||||||
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeftMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, inst.GetArg(0).GetU32() << (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftLeftMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, inst.GetArg(0).GetU64() << (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, inst.GetArg(0).GetU32() >> (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::LogicalShiftRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, inst.GetArg(0).GetU64() >> (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, static_cast<s32>(inst.GetArg(0).GetU32()) >> (inst.GetArg(1).GetU32() & 0x1f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::ArithmeticShiftRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, static_cast<s64>(inst.GetArg(0).GetU64()) >> (inst.GetArg(1).GetU64() & 0x3f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRightMasked32:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU32()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::RotateRightMasked64:
|
|
||||||
if (inst.AreAllArgsImmediates()) {
|
|
||||||
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU64()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Op::Add32:
|
|
||||||
case Op::Add64:
|
|
||||||
FoldAdd(inst, opcode == Op::Add32);
|
|
||||||
break;
|
|
||||||
case Op::Sub32:
|
|
||||||
case Op::Sub64:
|
|
||||||
FoldSub(inst, opcode == Op::Sub32);
|
|
||||||
break;
|
|
||||||
case Op::Mul32:
|
|
||||||
case Op::Mul64:
|
|
||||||
FoldMultiply(inst, opcode == Op::Mul32);
|
|
||||||
break;
|
|
||||||
case Op::SignedDiv32:
|
|
||||||
case Op::SignedDiv64:
|
|
||||||
FoldDivide(inst, opcode == Op::SignedDiv32, true);
|
|
||||||
break;
|
|
||||||
case Op::UnsignedDiv32:
|
|
||||||
case Op::UnsignedDiv64:
|
|
||||||
FoldDivide(inst, opcode == Op::UnsignedDiv32, false);
|
|
||||||
break;
|
|
||||||
case Op::And32:
|
|
||||||
case Op::And64:
|
|
||||||
FoldAND(inst, opcode == Op::And32);
|
|
||||||
break;
|
|
||||||
case Op::Eor32:
|
|
||||||
case Op::Eor64:
|
|
||||||
FoldEOR(inst, opcode == Op::Eor32);
|
|
||||||
break;
|
|
||||||
case Op::Or32:
|
|
||||||
case Op::Or64:
|
|
||||||
FoldOR(inst, opcode == Op::Or32);
|
|
||||||
break;
|
|
||||||
case Op::Not32:
|
|
||||||
case Op::Not64:
|
|
||||||
FoldNOT(inst, opcode == Op::Not32);
|
|
||||||
break;
|
|
||||||
case Op::SignExtendByteToWord:
|
|
||||||
case Op::SignExtendHalfToWord:
|
|
||||||
FoldSignExtendXToWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::SignExtendByteToLong:
|
|
||||||
case Op::SignExtendHalfToLong:
|
|
||||||
case Op::SignExtendWordToLong:
|
|
||||||
FoldSignExtendXToLong(inst);
|
|
||||||
break;
|
|
||||||
case Op::ZeroExtendByteToWord:
|
|
||||||
case Op::ZeroExtendHalfToWord:
|
|
||||||
FoldZeroExtendXToWord(inst);
|
|
||||||
break;
|
|
||||||
case Op::ZeroExtendByteToLong:
|
|
||||||
case Op::ZeroExtendHalfToLong:
|
|
||||||
case Op::ZeroExtendWordToLong:
|
|
||||||
FoldZeroExtendXToLong(inst);
|
|
||||||
break;
|
|
||||||
case Op::ByteReverseWord:
|
|
||||||
case Op::ByteReverseHalf:
|
|
||||||
case Op::ByteReverseDual:
|
|
||||||
FoldByteReverse(inst, opcode);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,23 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <mcl/iterator/reverse.hpp>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void DeadCodeElimination(IR::Block& block) {
|
|
||||||
// We iterate over the instructions in reverse order.
|
|
||||||
// This is because removing an instruction reduces the number of uses for earlier instructions.
|
|
||||||
for (auto& inst : mcl::iterator::reverse(block)) {
|
|
||||||
if (!inst.HasUses() && !MayHaveSideEffects(inst.GetOpcode())) {
|
|
||||||
inst.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,44 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2020 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void IdentityRemovalPass(IR::Block& block) {
|
|
||||||
std::vector<IR::Inst*> to_invalidate;
|
|
||||||
|
|
||||||
auto iter = block.begin();
|
|
||||||
while (iter != block.end()) {
|
|
||||||
IR::Inst& inst = *iter;
|
|
||||||
|
|
||||||
const size_t num_args = inst.NumArgs();
|
|
||||||
for (size_t i = 0; i < num_args; i++) {
|
|
||||||
while (true) {
|
|
||||||
IR::Value arg = inst.GetArg(i);
|
|
||||||
if (!arg.IsIdentity())
|
|
||||||
break;
|
|
||||||
inst.SetArg(i, arg.GetInst()->GetArg(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.GetOpcode() == IR::Opcode::Identity || inst.GetOpcode() == IR::Opcode::Void) {
|
|
||||||
iter = block.Instructions().erase(inst);
|
|
||||||
to_invalidate.push_back(&inst);
|
|
||||||
} else {
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IR::Inst* inst : to_invalidate) {
|
|
||||||
inst->Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,127 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2020 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include <mp/metafunction/apply.h>
|
|
||||||
#include <mp/typelist/concat.h>
|
|
||||||
#include <mp/typelist/drop.h>
|
|
||||||
#include <mp/typelist/get.h>
|
|
||||||
#include <mp/typelist/head.h>
|
|
||||||
#include <mp/typelist/list.h>
|
|
||||||
#include <mp/typelist/prepend.h>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/value.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization::IRMatcher {
|
|
||||||
|
|
||||||
struct CaptureValue {
|
|
||||||
using ReturnType = std::tuple<IR::Value>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureInst {
|
|
||||||
using ReturnType = std::tuple<IR::Inst*>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
if (value.IsImmediate())
|
|
||||||
return std::nullopt;
|
|
||||||
return std::tuple(value.GetInstRecursive());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureUImm {
|
|
||||||
using ReturnType = std::tuple<u64>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value.GetImmediateAsU64());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CaptureSImm {
|
|
||||||
using ReturnType = std::tuple<s64>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
return std::tuple(value.GetImmediateAsS64());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<u64 Value>
|
|
||||||
struct UImm {
|
|
||||||
using ReturnType = std::tuple<>;
|
|
||||||
|
|
||||||
static std::optional<std::tuple<>> Match(IR::Value value) {
|
|
||||||
if (value.GetImmediateAsU64() == Value)
|
|
||||||
return std::tuple();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<s64 Value>
|
|
||||||
struct SImm {
|
|
||||||
using ReturnType = std::tuple<>;
|
|
||||||
|
|
||||||
static std::optional<std::tuple<>> Match(IR::Value value) {
|
|
||||||
if (value.GetImmediateAsS64() == Value)
|
|
||||||
return std::tuple();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<IR::Opcode Opcode, typename... Args>
|
|
||||||
struct Inst {
|
|
||||||
public:
|
|
||||||
using ReturnType = mp::concat<std::tuple<>, typename Args::ReturnType...>;
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(const IR::Inst& inst) {
|
|
||||||
if (inst.GetOpcode() != Opcode)
|
|
||||||
return std::nullopt;
|
|
||||||
if (inst.HasAssociatedPseudoOperation())
|
|
||||||
return std::nullopt;
|
|
||||||
return MatchArgs<0>(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::optional<ReturnType> Match(IR::Value value) {
|
|
||||||
if (value.IsImmediate())
|
|
||||||
return std::nullopt;
|
|
||||||
return Match(*value.GetInstRecursive());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<size_t I>
|
|
||||||
static auto MatchArgs(const IR::Inst& inst) -> std::optional<mp::apply<mp::concat, mp::prepend<mp::drop<I, mp::list<typename Args::ReturnType...>>, std::tuple<>>>> {
|
|
||||||
if constexpr (I >= sizeof...(Args)) {
|
|
||||||
return std::tuple();
|
|
||||||
} else {
|
|
||||||
using Arg = mp::get<I, mp::list<Args...>>;
|
|
||||||
|
|
||||||
if (const auto arg = Arg::Match(inst.GetArg(I))) {
|
|
||||||
if (const auto rest = MatchArgs<I + 1>(inst)) {
|
|
||||||
return std::tuple_cat(*arg, *rest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool IsSameInst(std::tuple<IR::Inst*, IR::Inst*> t) {
|
|
||||||
return std::get<0>(t) == std::get<1>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsSameInst(std::tuple<IR::Inst*, IR::Inst*, IR::Inst*> t) {
|
|
||||||
return std::get<0>(t) == std::get<1>(t) && std::get<0>(t) == std::get<2>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization::IRMatcher
|
|
|
@ -1,18 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2023 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void NamingPass(IR::Block& block) {
|
|
||||||
unsigned name = 1;
|
|
||||||
for (auto& inst : block) {
|
|
||||||
inst.SetName(name++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,47 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
|
||||||
struct UserCallbacks;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Dynarmic::A64 {
|
|
||||||
struct UserCallbacks;
|
|
||||||
struct UserConfig;
|
|
||||||
} // namespace Dynarmic::A64
|
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
|
||||||
class Block;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
struct PolyfillOptions {
|
|
||||||
bool sha256 = false;
|
|
||||||
bool vector_multiply_widen = false;
|
|
||||||
|
|
||||||
bool operator==(const PolyfillOptions&) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct A32GetSetEliminationOptions {
|
|
||||||
bool convert_nzc_to_nz = false;
|
|
||||||
bool convert_nz_to_nzc = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void PolyfillPass(IR::Block& block, const PolyfillOptions& opt);
|
|
||||||
void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb);
|
|
||||||
void A32GetSetElimination(IR::Block& block, A32GetSetEliminationOptions opt);
|
|
||||||
void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf);
|
|
||||||
void A64GetSetElimination(IR::Block& block);
|
|
||||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb);
|
|
||||||
void ConstantPropagation(IR::Block& block);
|
|
||||||
void DeadCodeElimination(IR::Block& block);
|
|
||||||
void IdentityRemovalPass(IR::Block& block);
|
|
||||||
void VerificationPass(const IR::Block& block);
|
|
||||||
void NamingPass(IR::Block& block);
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,218 +0,0 @@
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2022 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/ir_emitter.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void PolyfillSHA256MessageSchedule0(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
const IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
const IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
|
|
||||||
const IR::U128 t = ir.VectorExtract(x, y, 32);
|
|
||||||
|
|
||||||
IR::U128 result = ir.ZeroVector();
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
|
||||||
const IR::U32 modified_element = [&] {
|
|
||||||
const IR::U32 element = ir.VectorGetElement(32, t, i);
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(element, ir.Imm8(7));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(element, ir.Imm8(18));
|
|
||||||
const IR::U32 tmp3 = ir.LogicalShiftRight(element, ir.Imm8(3));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}();
|
|
||||||
|
|
||||||
result = ir.VectorSetElement(32, result, i, modified_element);
|
|
||||||
}
|
|
||||||
result = ir.VectorAdd(32, result, x);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PolyfillSHA256MessageSchedule1(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
const IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
const IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
const IR::U128 z = (IR::U128)inst.GetArg(2);
|
|
||||||
|
|
||||||
const IR::U128 T0 = ir.VectorExtract(y, z, 32);
|
|
||||||
|
|
||||||
const IR::U128 lower_half = [&] {
|
|
||||||
const IR::U128 T = ir.VectorRotateWholeVectorRight(z, 64);
|
|
||||||
const IR::U128 tmp1 = ir.VectorRotateRight(32, T, 17);
|
|
||||||
const IR::U128 tmp2 = ir.VectorRotateRight(32, T, 19);
|
|
||||||
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, T, 10);
|
|
||||||
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
|
|
||||||
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, x, T0));
|
|
||||||
return ir.VectorZeroUpper(tmp5);
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U64 upper_half = [&] {
|
|
||||||
const IR::U128 tmp1 = ir.VectorRotateRight(32, lower_half, 17);
|
|
||||||
const IR::U128 tmp2 = ir.VectorRotateRight(32, lower_half, 19);
|
|
||||||
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, lower_half, 10);
|
|
||||||
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
|
|
||||||
|
|
||||||
// Shuffle the top two 32-bit elements downwards [3, 2, 1, 0] -> [1, 0, 3, 2]
|
|
||||||
const IR::U128 shuffled_d = ir.VectorRotateWholeVectorRight(x, 64);
|
|
||||||
const IR::U128 shuffled_T0 = ir.VectorRotateWholeVectorRight(T0, 64);
|
|
||||||
|
|
||||||
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, shuffled_d, shuffled_T0));
|
|
||||||
return ir.VectorGetElement(64, tmp5, 0);
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U128 result = ir.VectorSetElement(64, lower_half, 1, upper_half);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAchoose(IR::IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
|
|
||||||
return ir.Eor(ir.And(ir.Eor(y, z), x), z);
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAmajority(IR::IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
|
|
||||||
return ir.Or(ir.And(x, y), ir.And(ir.Or(x, y), z));
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAhashSIGMA0(IR::IREmitter& ir, IR::U32 x) {
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(2));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(13));
|
|
||||||
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(22));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::U32 SHAhashSIGMA1(IR::IREmitter& ir, IR::U32 x) {
|
|
||||||
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(6));
|
|
||||||
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(11));
|
|
||||||
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(25));
|
|
||||||
|
|
||||||
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PolyfillSHA256Hash(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
IR::U128 x = (IR::U128)inst.GetArg(0);
|
|
||||||
IR::U128 y = (IR::U128)inst.GetArg(1);
|
|
||||||
const IR::U128 w = (IR::U128)inst.GetArg(2);
|
|
||||||
const bool part1 = inst.GetArg(3).GetU1();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
|
||||||
const IR::U32 low_x = ir.VectorGetElement(32, x, 0);
|
|
||||||
const IR::U32 after_low_x = ir.VectorGetElement(32, x, 1);
|
|
||||||
const IR::U32 before_high_x = ir.VectorGetElement(32, x, 2);
|
|
||||||
const IR::U32 high_x = ir.VectorGetElement(32, x, 3);
|
|
||||||
|
|
||||||
const IR::U32 low_y = ir.VectorGetElement(32, y, 0);
|
|
||||||
const IR::U32 after_low_y = ir.VectorGetElement(32, y, 1);
|
|
||||||
const IR::U32 before_high_y = ir.VectorGetElement(32, y, 2);
|
|
||||||
const IR::U32 high_y = ir.VectorGetElement(32, y, 3);
|
|
||||||
|
|
||||||
const IR::U32 choice = SHAchoose(ir, low_y, after_low_y, before_high_y);
|
|
||||||
const IR::U32 majority = SHAmajority(ir, low_x, after_low_x, before_high_x);
|
|
||||||
|
|
||||||
const IR::U32 t = [&] {
|
|
||||||
const IR::U32 w_element = ir.VectorGetElement(32, w, i);
|
|
||||||
const IR::U32 sig = SHAhashSIGMA1(ir, low_y);
|
|
||||||
|
|
||||||
return ir.Add(high_y, ir.Add(sig, ir.Add(choice, w_element)));
|
|
||||||
}();
|
|
||||||
|
|
||||||
const IR::U32 new_low_x = ir.Add(t, ir.Add(SHAhashSIGMA0(ir, low_x), majority));
|
|
||||||
const IR::U32 new_low_y = ir.Add(t, high_x);
|
|
||||||
|
|
||||||
// Shuffle all words left by 1 element: [3, 2, 1, 0] -> [2, 1, 0, 3]
|
|
||||||
const IR::U128 shuffled_x = ir.VectorRotateWholeVectorRight(x, 96);
|
|
||||||
const IR::U128 shuffled_y = ir.VectorRotateWholeVectorRight(y, 96);
|
|
||||||
|
|
||||||
x = ir.VectorSetElement(32, shuffled_x, 0, new_low_x);
|
|
||||||
y = ir.VectorSetElement(32, shuffled_y, 0, new_low_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(part1 ? x : y);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t esize, bool is_signed>
|
|
||||||
void PolyfillVectorMultiplyWiden(IR::IREmitter& ir, IR::Inst& inst) {
|
|
||||||
IR::U128 n = (IR::U128)inst.GetArg(0);
|
|
||||||
IR::U128 m = (IR::U128)inst.GetArg(1);
|
|
||||||
|
|
||||||
const IR::U128 wide_n = is_signed ? ir.VectorSignExtend(esize, n) : ir.VectorZeroExtend(esize, n);
|
|
||||||
const IR::U128 wide_m = is_signed ? ir.VectorSignExtend(esize, m) : ir.VectorZeroExtend(esize, m);
|
|
||||||
|
|
||||||
const IR::U128 result = ir.VectorMultiply(esize * 2, wide_n, wide_m);
|
|
||||||
|
|
||||||
inst.ReplaceUsesWith(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) {
|
|
||||||
if (polyfill == PolyfillOptions{}) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::IREmitter ir{block};
|
|
||||||
|
|
||||||
for (auto& inst : block) {
|
|
||||||
ir.SetInsertionPointBefore(&inst);
|
|
||||||
|
|
||||||
switch (inst.GetOpcode()) {
|
|
||||||
case IR::Opcode::SHA256MessageSchedule0:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256MessageSchedule0(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::SHA256MessageSchedule1:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256MessageSchedule1(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::SHA256Hash:
|
|
||||||
if (polyfill.sha256) {
|
|
||||||
PolyfillSHA256Hash(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden8:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<8, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden16:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<16, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplySignedWiden32:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<32, true>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden8:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<8, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden16:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<16, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IR::Opcode::VectorMultiplyUnsignedWiden32:
|
|
||||||
if (polyfill.vector_multiply_widen) {
|
|
||||||
PolyfillVectorMultiplyWiden<32, false>(ir, inst);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
|
@ -1,51 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
|
||||||
* Copyright (c) 2016 MerryMage
|
|
||||||
* SPDX-License-Identifier: 0BSD
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "dynarmic/common/assert.h"
|
|
||||||
#include "dynarmic/common/common_types.h"
|
|
||||||
#include <ankerl/unordered_dense.h>
|
|
||||||
|
|
||||||
#include "dynarmic/ir/basic_block.h"
|
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
|
||||||
#include "dynarmic/ir/opcodes.h"
|
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
|
||||||
#include "dynarmic/ir/type.h"
|
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
|
||||||
|
|
||||||
void VerificationPass(const IR::Block& block) {
|
|
||||||
for (const auto& inst : block) {
|
|
||||||
for (size_t i = 0; i < inst.NumArgs(); i++) {
|
|
||||||
const IR::Type t1 = inst.GetArg(i).GetType();
|
|
||||||
const IR::Type t2 = IR::GetArgTypeOf(inst.GetOpcode(), i);
|
|
||||||
if (!IR::AreTypesCompatible(t1, t2)) {
|
|
||||||
std::puts(IR::DumpBlock(block).c_str());
|
|
||||||
ASSERT_FALSE("above block failed validation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ankerl::unordered_dense::map<IR::Inst*, size_t> actual_uses;
|
|
||||||
for (const auto& inst : block) {
|
|
||||||
for (size_t i = 0; i < inst.NumArgs(); i++) {
|
|
||||||
const auto arg = inst.GetArg(i);
|
|
||||||
if (!arg.IsImmediate()) {
|
|
||||||
actual_uses[arg.GetInst()]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& pair : actual_uses) {
|
|
||||||
ASSERT(pair.first->UseCount() == pair.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Dynarmic::Optimization
|
|
1519
src/dynarmic/src/dynarmic/ir/opt_passes.cpp
Normal file
1519
src/dynarmic/src/dynarmic/ir/opt_passes.cpp
Normal file
File diff suppressed because it is too large
Load diff
37
src/dynarmic/src/dynarmic/ir/opt_passes.h
Normal file
37
src/dynarmic/src/dynarmic/ir/opt_passes.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Dynarmic::A32 {
|
||||||
|
struct UserCallbacks;
|
||||||
|
struct UserConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::A64 {
|
||||||
|
struct UserCallbacks;
|
||||||
|
struct UserConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::IR {
|
||||||
|
class Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Dynarmic::Optimization {
|
||||||
|
|
||||||
|
struct PolyfillOptions {
|
||||||
|
bool sha256 = false;
|
||||||
|
bool vector_multiply_widen = false;
|
||||||
|
|
||||||
|
bool operator==(const PolyfillOptions&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Optimize(IR::Block& block, const A32::UserConfig& conf, const Optimization::PolyfillOptions& polyfill_options);
|
||||||
|
void Optimize(IR::Block& block, const A64::UserConfig& conf, const Optimization::PolyfillOptions& polyfill_options);
|
||||||
|
|
||||||
|
} // namespace Dynarmic::Optimization
|
|
@ -29,7 +29,7 @@
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
@ -179,13 +179,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
|
||||||
while (num_insts < instructions_to_execute_count) {
|
while (num_insts < instructions_to_execute_count) {
|
||||||
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
|
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
|
||||||
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
|
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
|
||||||
Optimization::NamingPass(ir_block);
|
Optimization::Optimize(ir_block, &test_env, {});
|
||||||
Optimization::A32GetSetElimination(ir_block, {.convert_nz_to_nzc = true});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::A32ConstantMemoryReads(ir_block, &test_env);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::VerificationPass(ir_block);
|
|
||||||
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
|
printf("\n\nIR:\n%s", IR::DumpBlock(ir_block).c_str());
|
||||||
printf("\n\nx86_64:\n");
|
printf("\n\nx86_64:\n");
|
||||||
jit.DumpDisassembly();
|
jit.DumpDisassembly();
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opcodes.h"
|
#include "dynarmic/ir/opcodes.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
// Must be declared last for all necessary operator<< to be declared prior to this.
|
// Must be declared last for all necessary operator<< to be declared prior to this.
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
@ -271,17 +271,9 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
|
||||||
|
|
||||||
const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
|
const auto get_code = [&jit_env](u64 vaddr) { return jit_env.MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {});
|
IR::Block ir_block = A64::Translate({instructions_start, FP::FPCR{fpcr}}, get_code, {});
|
||||||
Optimization::A64CallbackConfigPass(ir_block, GetUserConfig(jit_env));
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("IR:\n");
|
fmt::print("IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
|
|
||||||
fmt::print("Optimized IR:\n");
|
fmt::print("Optimized IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
include(TargetArchitectureSpecificSources)
|
include(TargetArchitectureSpecificSources)
|
||||||
|
|
||||||
add_executable(dynarmic_tests
|
add_executable(dynarmic_tests
|
||||||
|
@ -6,33 +8,24 @@ add_executable(dynarmic_tests
|
||||||
fp/mantissa_util_tests.cpp
|
fp/mantissa_util_tests.cpp
|
||||||
fp/unpacked_tests.cpp
|
fp/unpacked_tests.cpp
|
||||||
rand_int.h
|
rand_int.h
|
||||||
|
# A32
|
||||||
|
A32/test_arm_disassembler.cpp
|
||||||
|
A32/test_arm_instructions.cpp
|
||||||
|
A32/test_coprocessor.cpp
|
||||||
|
A32/test_svc.cpp
|
||||||
|
A32/test_thumb_instructions.cpp
|
||||||
|
A32/testenv.h
|
||||||
|
decoder_tests.cpp
|
||||||
|
# A64
|
||||||
|
A64/a64.cpp
|
||||||
|
A64/fibonacci.cpp
|
||||||
|
A64/fp_min_max.cpp
|
||||||
|
A64/misaligned_page_table.cpp
|
||||||
|
A64/test_invalidation.cpp
|
||||||
|
A64/real_world.cpp
|
||||||
|
A64/testenv.h
|
||||||
)
|
)
|
||||||
|
target_link_libraries(dynarmic_tests PRIVATE merry::oaknut)
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic_tests PRIVATE
|
|
||||||
A32/test_arm_disassembler.cpp
|
|
||||||
A32/test_arm_instructions.cpp
|
|
||||||
A32/test_coprocessor.cpp
|
|
||||||
A32/test_svc.cpp
|
|
||||||
A32/test_thumb_instructions.cpp
|
|
||||||
A32/testenv.h
|
|
||||||
decoder_tests.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_link_libraries(dynarmic_tests PRIVATE merry::oaknut)
|
|
||||||
|
|
||||||
target_sources(dynarmic_tests PRIVATE
|
|
||||||
A64/a64.cpp
|
|
||||||
A64/fibonacci.cpp
|
|
||||||
A64/fp_min_max.cpp
|
|
||||||
A64/misaligned_page_table.cpp
|
|
||||||
A64/test_invalidation.cpp
|
|
||||||
A64/real_world.cpp
|
|
||||||
A64/testenv.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (DYNARMIC_TESTS_USE_UNICORN)
|
if (DYNARMIC_TESTS_USE_UNICORN)
|
||||||
target_link_libraries(dynarmic_tests PRIVATE Unicorn::Unicorn)
|
target_link_libraries(dynarmic_tests PRIVATE Unicorn::Unicorn)
|
||||||
|
@ -40,25 +33,17 @@ if (DYNARMIC_TESTS_USE_UNICORN)
|
||||||
target_sources(dynarmic_tests PRIVATE
|
target_sources(dynarmic_tests PRIVATE
|
||||||
fuzz_util.cpp
|
fuzz_util.cpp
|
||||||
fuzz_util.h
|
fuzz_util.h
|
||||||
|
# A32
|
||||||
|
A32/fuzz_arm.cpp
|
||||||
|
A32/fuzz_thumb.cpp
|
||||||
|
unicorn_emu/a32_unicorn.cpp
|
||||||
|
unicorn_emu/a32_unicorn.h
|
||||||
|
# A64
|
||||||
|
A64/fuzz_with_unicorn.cpp
|
||||||
|
A64/verify_unicorn.cpp
|
||||||
|
unicorn_emu/a64_unicorn.cpp
|
||||||
|
unicorn_emu/a64_unicorn.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic_tests PRIVATE
|
|
||||||
A32/fuzz_arm.cpp
|
|
||||||
A32/fuzz_thumb.cpp
|
|
||||||
unicorn_emu/a32_unicorn.cpp
|
|
||||||
unicorn_emu/a32_unicorn.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ("A64" IN_LIST DYNARMIC_FRONTENDS)
|
|
||||||
target_sources(dynarmic_tests PRIVATE
|
|
||||||
A64/fuzz_with_unicorn.cpp
|
|
||||||
A64/verify_unicorn.cpp
|
|
||||||
unicorn_emu/a64_unicorn.cpp
|
|
||||||
unicorn_emu/a64_unicorn.h
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ("riscv" IN_LIST ARCHITECTURE)
|
if ("riscv" IN_LIST ARCHITECTURE)
|
||||||
|
@ -69,9 +54,6 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||||
target_link_libraries(dynarmic_tests PRIVATE xbyak::xbyak)
|
target_link_libraries(dynarmic_tests PRIVATE xbyak::xbyak)
|
||||||
target_architecture_specific_sources(dynarmic_tests "x86_64"
|
target_architecture_specific_sources(dynarmic_tests "x86_64"
|
||||||
x64_cpu_info.cpp
|
x64_cpu_info.cpp
|
||||||
)
|
|
||||||
|
|
||||||
target_architecture_specific_sources(dynarmic_tests "x86_64"
|
|
||||||
native/preserve_xmm.cpp
|
native/preserve_xmm.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,50 +67,70 @@ endif()
|
||||||
|
|
||||||
include(CreateDirectoryGroups)
|
include(CreateDirectoryGroups)
|
||||||
|
|
||||||
if (("A32" IN_LIST DYNARMIC_FRONTENDS) AND ("A64" IN_LIST DYNARMIC_FRONTENDS))
|
#
|
||||||
add_executable(dynarmic_print_info
|
# dynarmic_print_info
|
||||||
print_info.cpp
|
#
|
||||||
)
|
add_executable(dynarmic_print_info
|
||||||
|
print_info.cpp
|
||||||
create_target_directory_groups(dynarmic_print_info)
|
)
|
||||||
|
create_target_directory_groups(dynarmic_print_info)
|
||||||
target_link_libraries(dynarmic_print_info PRIVATE dynarmic Boost::headers fmt::fmt merry::mcl)
|
target_link_libraries(dynarmic_print_info PRIVATE dynarmic fmt::fmt merry::mcl)
|
||||||
target_include_directories(dynarmic_print_info PRIVATE . ../src)
|
if (BOOST_NO_HEADERS)
|
||||||
target_compile_options(dynarmic_print_info PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_link_libraries(dynarmic_print_info PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
target_compile_definitions(dynarmic_print_info PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
else()
|
||||||
|
target_link_libraries(dynarmic_print_info PRIVATE Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
target_include_directories(dynarmic_print_info PRIVATE . ../src)
|
||||||
|
target_compile_options(dynarmic_print_info PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
|
target_compile_definitions(dynarmic_print_info PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||||
|
|
||||||
if (("A32" IN_LIST DYNARMIC_FRONTENDS) AND ("A64" IN_LIST DYNARMIC_FRONTENDS))
|
#
|
||||||
add_executable(dynarmic_test_generator
|
# dynarmic_test_generator
|
||||||
fuzz_util.cpp
|
#
|
||||||
fuzz_util.h
|
add_executable(dynarmic_test_generator
|
||||||
test_generator.cpp
|
fuzz_util.cpp
|
||||||
)
|
fuzz_util.h
|
||||||
|
test_generator.cpp
|
||||||
|
)
|
||||||
|
|
||||||
create_target_directory_groups(dynarmic_test_generator)
|
create_target_directory_groups(dynarmic_test_generator)
|
||||||
|
|
||||||
target_link_libraries(dynarmic_test_generator PRIVATE dynarmic Boost::headers fmt::fmt merry::mcl)
|
target_link_libraries(dynarmic_test_generator PRIVATE dynarmic fmt::fmt merry::mcl)
|
||||||
target_include_directories(dynarmic_test_generator PRIVATE . ../src)
|
if (BOOST_NO_HEADERS)
|
||||||
target_compile_options(dynarmic_test_generator PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_link_libraries(dynarmic_test_generator PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
target_compile_definitions(dynarmic_test_generator PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
else()
|
||||||
|
target_link_libraries(dynarmic_test_generator PRIVATE Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
target_include_directories(dynarmic_test_generator PRIVATE . ../src)
|
||||||
|
target_compile_options(dynarmic_test_generator PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
|
target_compile_definitions(dynarmic_test_generator PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||||
|
|
||||||
if (("A32" IN_LIST DYNARMIC_FRONTENDS) AND ("A64" IN_LIST DYNARMIC_FRONTENDS))
|
#
|
||||||
add_executable(dynarmic_test_reader
|
# dynarmic_test_reader
|
||||||
test_reader.cpp
|
#
|
||||||
)
|
add_executable(dynarmic_test_reader
|
||||||
|
test_reader.cpp
|
||||||
create_target_directory_groups(dynarmic_test_reader)
|
)
|
||||||
|
create_target_directory_groups(dynarmic_test_reader)
|
||||||
target_link_libraries(dynarmic_test_reader PRIVATE dynarmic Boost::headers fmt::fmt merry::mcl)
|
target_link_libraries(dynarmic_test_reader PRIVATE dynarmic fmt::fmt merry::mcl)
|
||||||
target_include_directories(dynarmic_test_reader PRIVATE . ../src)
|
if (BOOST_NO_HEADERS)
|
||||||
target_compile_options(dynarmic_test_reader PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_link_libraries(dynarmic_test_reader PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
target_compile_definitions(dynarmic_test_reader PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
else()
|
||||||
|
target_link_libraries(dynarmic_test_reader PRIVATE Boost::headers)
|
||||||
endif()
|
endif()
|
||||||
|
target_include_directories(dynarmic_test_reader PRIVATE . ../src)
|
||||||
|
target_compile_options(dynarmic_test_reader PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
|
target_compile_definitions(dynarmic_test_reader PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||||
|
|
||||||
|
#
|
||||||
create_target_directory_groups(dynarmic_tests)
|
create_target_directory_groups(dynarmic_tests)
|
||||||
|
|
||||||
target_link_libraries(dynarmic_tests PRIVATE dynarmic Boost::headers Catch2::Catch2WithMain fmt::fmt merry::mcl)
|
target_link_libraries(dynarmic_tests PRIVATE dynarmic Catch2::Catch2WithMain fmt::fmt merry::mcl)
|
||||||
|
if (BOOST_NO_HEADERS)
|
||||||
|
target_link_libraries(dynarmic_tests PRIVATE Boost::variant Boost::icl Boost::pool)
|
||||||
|
else()
|
||||||
|
target_link_libraries(dynarmic_tests PRIVATE Boost::headers)
|
||||||
|
endif()
|
||||||
target_include_directories(dynarmic_tests PRIVATE . ../src)
|
target_include_directories(dynarmic_tests PRIVATE . ../src)
|
||||||
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/interface/A32/disassembler.h"
|
#include "dynarmic/interface/A32/disassembler.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt_passes.h"
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
@ -64,18 +64,9 @@ void PrintA32Instruction(u32 instruction) {
|
||||||
IR::Block ir_block{location};
|
IR::Block ir_block{location};
|
||||||
const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction);
|
const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction);
|
||||||
fmt::print("should_continue: {}\n\n", should_continue);
|
fmt::print("should_continue: {}\n\n", should_continue);
|
||||||
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("IR:\n");
|
fmt::print("IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A32GetSetElimination(ir_block, {});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("Optimized IR:\n");
|
fmt::print("Optimized IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
}
|
}
|
||||||
|
@ -88,18 +79,9 @@ void PrintA64Instruction(u32 instruction) {
|
||||||
IR::Block ir_block{location};
|
IR::Block ir_block{location};
|
||||||
const bool should_continue = A64::TranslateSingleInstruction(ir_block, location, instruction);
|
const bool should_continue = A64::TranslateSingleInstruction(ir_block, location, instruction);
|
||||||
fmt::print("should_continue: {}\n\n", should_continue);
|
fmt::print("should_continue: {}\n\n", should_continue);
|
||||||
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("IR:\n");
|
fmt::print("IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("Optimized IR:\n");
|
fmt::print("Optimized IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
}
|
}
|
||||||
|
@ -115,18 +97,9 @@ void PrintThumbInstruction(u32 instruction) {
|
||||||
IR::Block ir_block{location};
|
IR::Block ir_block{location};
|
||||||
const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction);
|
const bool should_continue = A32::TranslateSingleInstruction(ir_block, location, instruction);
|
||||||
fmt::print("should_continue: {}\n\n", should_continue);
|
fmt::print("should_continue: {}\n\n", should_continue);
|
||||||
|
|
||||||
Optimization::NamingPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("IR:\n");
|
fmt::print("IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
|
Optimization::Optimize(ir_block, conf, {});
|
||||||
Optimization::A32GetSetElimination(ir_block, {});
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::ConstantPropagation(ir_block);
|
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
|
||||||
Optimization::IdentityRemovalPass(ir_block);
|
|
||||||
|
|
||||||
fmt::print("Optimized IR:\n");
|
fmt::print("Optimized IR:\n");
|
||||||
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
fmt::print("{}\n", IR::DumpBlock(ir_block));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <common/scope_exit.h>
|
#include <common/scope_exit.h>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "hid_core/frontend/emulated_controller.h"
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
#include "hid_core/frontend/input_converter.h"
|
#include "hid_core/frontend/input_converter.h"
|
||||||
|
@ -577,7 +577,7 @@ void EmulatedController::UnloadInput() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::EnableConfiguration() {
|
void EmulatedController::EnableConfiguration() {
|
||||||
std::scoped_lock lock{connect_mutex, npad_mutex};
|
std::unique_lock lock1{connect_mutex}, lock2{npad_mutex};
|
||||||
is_configuring = true;
|
is_configuring = true;
|
||||||
tmp_is_connected = is_connected;
|
tmp_is_connected = is_connected;
|
||||||
tmp_npad_type = npad_type;
|
tmp_npad_type = npad_type;
|
||||||
|
@ -614,19 +614,19 @@ void EmulatedController::DisableConfiguration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::EnableSystemButtons() {
|
void EmulatedController::EnableSystemButtons() {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
system_buttons_enabled = true;
|
system_buttons_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::DisableSystemButtons() {
|
void EmulatedController::DisableSystemButtons() {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
system_buttons_enabled = false;
|
system_buttons_enabled = false;
|
||||||
controller.home_button_state.raw = 0;
|
controller.home_button_state.raw = 0;
|
||||||
controller.capture_button_state.raw = 0;
|
controller.capture_button_state.raw = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::ResetSystemButtons() {
|
void EmulatedController::ResetSystemButtons() {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
controller.home_button_state.home.Assign(false);
|
controller.home_button_state.home.Assign(false);
|
||||||
controller.capture_button_state.capture.Assign(false);
|
controller.capture_button_state.capture.Assign(false);
|
||||||
}
|
}
|
||||||
|
@ -937,7 +937,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
const auto stick_value = TransformToStick(callback);
|
const auto stick_value = TransformToStick(callback);
|
||||||
|
|
||||||
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
||||||
|
@ -994,7 +994,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
const auto trigger_value = TransformToTrigger(callback);
|
const auto trigger_value = TransformToTrigger(callback);
|
||||||
|
|
||||||
// Only read trigger values that have the same uuid or are pressed once
|
// Only read trigger values that have the same uuid or are pressed once
|
||||||
|
@ -1042,7 +1042,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
auto& raw_status = controller.motion_values[index].raw_status;
|
auto& raw_status = controller.motion_values[index].raw_status;
|
||||||
auto& emulated = controller.motion_values[index].emulated;
|
auto& emulated = controller.motion_values[index].emulated;
|
||||||
|
|
||||||
|
@ -1078,7 +1078,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
controller.color_values[index] = TransformToColor(callback);
|
controller.color_values[index] = TransformToColor(callback);
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
|
@ -1129,7 +1129,7 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
controller.battery_values[index] = TransformToBattery(callback);
|
controller.battery_values[index] = TransformToBattery(callback);
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
|
@ -1194,7 +1194,7 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
controller.camera_values = TransformToCamera(callback);
|
controller.camera_values = TransformToCamera(callback);
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
|
@ -1211,7 +1211,7 @@ void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& call
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
const auto force_value = TransformToStick(callback);
|
const auto force_value = TransformToStick(callback);
|
||||||
|
|
||||||
controller.ring_analog_value = force_value.x;
|
controller.ring_analog_value = force_value.x;
|
||||||
|
@ -1227,7 +1227,7 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
controller.nfc_values = TransformToNfc(callback);
|
controller.nfc_values = TransformToNfc(callback);
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
|
@ -1662,7 +1662,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NpadStyleIndex::Fullkey:
|
case NpadStyleIndex::Fullkey:
|
||||||
|
@ -1678,7 +1678,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NpadStyleIndex::Fullkey:
|
case NpadStyleIndex::Fullkey:
|
||||||
|
@ -1718,7 +1718,7 @@ void EmulatedController::Connect(bool use_temporary_value) {
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{connect_mutex, mutex};
|
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
tmp_is_connected = true;
|
tmp_is_connected = true;
|
||||||
return;
|
return;
|
||||||
|
@ -1735,7 +1735,7 @@ void EmulatedController::Disconnect() {
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{connect_mutex, mutex};
|
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
tmp_is_connected = false;
|
tmp_is_connected = false;
|
||||||
return;
|
return;
|
||||||
|
@ -1749,23 +1749,21 @@ void EmulatedController::Disconnect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
||||||
std::scoped_lock lock{connect_mutex};
|
std::shared_lock lock{connect_mutex};
|
||||||
if (get_temporary_value && is_configuring) {
|
if (get_temporary_value && is_configuring)
|
||||||
return tmp_is_connected;
|
return tmp_is_connected;
|
||||||
}
|
|
||||||
return is_connected;
|
return is_connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
NpadIdType EmulatedController::GetNpadIdType() const {
|
NpadIdType EmulatedController::GetNpadIdType() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::shared_lock lock{mutex};
|
||||||
return npad_id_type;
|
return npad_id_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
|
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
|
||||||
std::scoped_lock lock{npad_mutex};
|
std::shared_lock lock{npad_mutex};
|
||||||
if (get_temporary_value && is_configuring) {
|
if (get_temporary_value && is_configuring)
|
||||||
return tmp_npad_type;
|
return tmp_npad_type;
|
||||||
}
|
|
||||||
return npad_type;
|
return npad_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,7 +1771,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||||
auto trigger_guard = SCOPE_GUARD {
|
auto trigger_guard = SCOPE_GUARD {
|
||||||
TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
|
TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
|
||||||
};
|
};
|
||||||
std::scoped_lock lock{mutex, npad_mutex};
|
std::unique_lock lock1{mutex}, lock2{npad_mutex};
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
if (tmp_npad_type == npad_type_) {
|
if (tmp_npad_type == npad_type_) {
|
||||||
|
@ -1819,37 +1817,37 @@ LedPattern EmulatedController::GetLedPattern() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonValues EmulatedController::GetButtonsValues() const {
|
ButtonValues EmulatedController::GetButtonsValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.button_values;
|
return controller.button_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
SticksValues EmulatedController::GetSticksValues() const {
|
SticksValues EmulatedController::GetSticksValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.stick_values;
|
return controller.stick_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
TriggerValues EmulatedController::GetTriggersValues() const {
|
TriggerValues EmulatedController::GetTriggersValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.trigger_values;
|
return controller.trigger_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerMotionValues EmulatedController::GetMotionValues() const {
|
ControllerMotionValues EmulatedController::GetMotionValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.motion_values;
|
return controller.motion_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorValues EmulatedController::GetColorsValues() const {
|
ColorValues EmulatedController::GetColorsValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.color_values;
|
return controller.color_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
BatteryValues EmulatedController::GetBatteryValues() const {
|
BatteryValues EmulatedController::GetBatteryValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.battery_values;
|
return controller.battery_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraValues EmulatedController::GetCameraValues() const {
|
CameraValues EmulatedController::GetCameraValues() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.camera_values;
|
return controller.camera_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1858,7 +1856,7 @@ RingAnalogValue EmulatedController::GetRingSensorValues() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1866,7 +1864,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1874,7 +1872,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1882,7 +1880,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1890,7 +1888,7 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalogSticks EmulatedController::GetSticks() const {
|
AnalogSticks EmulatedController::GetSticks() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -1900,7 +1898,7 @@ AnalogSticks EmulatedController::GetSticks() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
NpadGcTriggerState EmulatedController::GetTriggers() const {
|
NpadGcTriggerState EmulatedController::GetTriggers() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1913,17 +1911,17 @@ MotionState EmulatedController::GetMotions() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerColors EmulatedController::GetColors() const {
|
ControllerColors EmulatedController::GetColors() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.colors_state;
|
return controller.colors_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
BatteryLevelState EmulatedController::GetBattery() const {
|
BatteryLevelState EmulatedController::GetBattery() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.battery_state;
|
return controller.battery_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CameraState& EmulatedController::GetCamera() const {
|
const CameraState& EmulatedController::GetCamera() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.camera_state;
|
return controller.camera_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1932,7 +1930,7 @@ RingSensorForce EmulatedController::GetRingSensorForce() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
const NfcState& EmulatedController::GetNfc() const {
|
const NfcState& EmulatedController::GetNfc() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::unique_lock lock{mutex};
|
||||||
return controller.nfc_state;
|
return controller.nfc_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1946,7 +1944,7 @@ NpadColor EmulatedController::GetNpadColor(u32 color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
||||||
std::scoped_lock lock{callback_mutex};
|
std::unique_lock lock{callback_mutex};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const ControllerUpdateCallback& poller = poller_pair.second;
|
const ControllerUpdateCallback& poller = poller_pair.second;
|
||||||
if (!is_npad_service_update && poller.is_npad_service) {
|
if (!is_npad_service_update && poller.is_npad_service) {
|
||||||
|
@ -1959,13 +1957,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
|
||||||
}
|
}
|
||||||
|
|
||||||
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
|
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
|
||||||
std::scoped_lock lock{callback_mutex};
|
std::unique_lock lock{callback_mutex};
|
||||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||||
return last_callback_key++;
|
return last_callback_key++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::DeleteCallback(int key) {
|
void EmulatedController::DeleteCallback(int key) {
|
||||||
std::scoped_lock lock{callback_mutex};
|
std::unique_lock lock{callback_mutex};
|
||||||
const auto& iterator = callback_list.find(key);
|
const auto& iterator = callback_list.find(key);
|
||||||
if (iterator == callback_list.end()) {
|
if (iterator == callback_list.end()) {
|
||||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -7,6 +10,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -626,10 +630,10 @@ private:
|
||||||
StickDevices virtual_stick_devices;
|
StickDevices virtual_stick_devices;
|
||||||
ControllerMotionDevices virtual_motion_devices;
|
ControllerMotionDevices virtual_motion_devices;
|
||||||
|
|
||||||
mutable std::mutex mutex;
|
mutable std::shared_mutex mutex;
|
||||||
mutable std::mutex callback_mutex;
|
mutable std::shared_mutex callback_mutex;
|
||||||
mutable std::mutex npad_mutex;
|
mutable std::shared_mutex npad_mutex;
|
||||||
mutable std::mutex connect_mutex;
|
mutable std::shared_mutex connect_mutex;
|
||||||
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
||||||
int last_callback_key = 0;
|
int last_callback_key = 0;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -8,7 +11,7 @@
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/frontend/ir/type.h"
|
#include "shader_recompiler/frontend/ir/type.h"
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -9,7 +12,7 @@
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/exception.h"
|
#include "shader_recompiler/exception.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/decode.h"
|
#include "shader_recompiler/frontend/maxwell/decode.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/exception.h"
|
#include "shader_recompiler/exception.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/decode.h"
|
#include "shader_recompiler/frontend/maxwell/decode.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -12,7 +15,7 @@
|
||||||
|
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <boost/intrusive/list.hpp>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/environment.h"
|
#include "shader_recompiler/environment.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "common/algorithm.h"
|
#include "common/algorithm.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -14,7 +17,7 @@
|
||||||
|
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "shader_recompiler/stage.h"
|
#include "shader_recompiler/stage.h"
|
||||||
#include "video_core/renderer_opengl/gl_device.h"
|
#include "video_core/renderer_opengl/gl_device.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -7,7 +10,7 @@
|
||||||
#include "common/bit_cast.h"
|
#include "common/bit_cast.h"
|
||||||
#include "common/cityhash.h"
|
#include "common/cityhash.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/engines/draw_manager.h"
|
#include "video_core/engines/draw_manager.h"
|
||||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
|
|
||||||
#include "video_core/renderer_vulkan/present/smaa.h"
|
#include "video_core/renderer_vulkan/present/smaa.h"
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -7,7 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
||||||
#include "video_core/vulkan_common/vulkan_device.h"
|
#include "video_core/vulkan_common/vulkan_device.h"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -12,7 +15,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/environment.h"
|
#include "shader_recompiler/environment.h"
|
||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/texture_cache/formatter.h"
|
#include "video_core/texture_cache/formatter.h"
|
||||||
#include "video_core/texture_cache/image_base.h"
|
#include "video_core/texture_cache/image_base.h"
|
||||||
#include "video_core/texture_cache/image_info.h"
|
#include "video_core/texture_cache/image_info.h"
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/lru_cache.h"
|
#include "common/lru_cache.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/slot_vector.h"
|
#include "common/slot_vector.h"
|
||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/textures/astc.h"
|
#include "video_core/textures/astc.h"
|
||||||
#include "video_core/textures/workers.h"
|
#include "video_core/textures/workers.h"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
#include "video_core/transform_feedback.h"
|
#include "video_core/transform_feedback.h"
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
|
#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
|
||||||
#include "video_core/vulkan_common/vma.h"
|
#include "video_core/vulkan_common/vma.h"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -9,7 +12,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/dynamic_library.h"
|
#include "common/dynamic_library.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "video_core/vulkan_common/vulkan_instance.h"
|
#include "video_core/vulkan_common/vulkan_instance.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include <ranges>
|
||||||
#include "video_core/vulkan_common/vma.h"
|
#include "video_core/vulkan_common/vma.h"
|
||||||
#include "video_core/vulkan_common/vulkan_device.h"
|
#include "video_core/vulkan_common/vulkan_device.h"
|
||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -49,19 +51,23 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
|
/// @brief Translates pixel position to float position
|
||||||
int w, h;
|
EmuWindow_SDL2::FloatPairNonHFA EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
|
||||||
|
int w = 0, h = 0;
|
||||||
SDL_GetWindowSize(render_window, &w, &h);
|
SDL_GetWindowSize(render_window, &w, &h);
|
||||||
const float fx = static_cast<float>(touch_x) / w;
|
const float fx = float(touch_x) / w;
|
||||||
const float fy = static_cast<float>(touch_y) / h;
|
const float fy = float(touch_y) / h;
|
||||||
|
return {
|
||||||
return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
|
std::clamp<float>(fx, 0.0f, 1.0f),
|
||||||
|
std::clamp<float>(fy, 0.0f, 1.0f),
|
||||||
|
0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||||
const auto mouse_button = SDLButtonToMouseButton(button);
|
const auto mouse_button = SDLButtonToMouseButton(button);
|
||||||
if (state == SDL_PRESSED) {
|
if (state == SDL_PRESSED) {
|
||||||
const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
|
auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y);
|
||||||
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
||||||
input_subsystem->GetMouse()->PressMouseButton(mouse_button);
|
input_subsystem->GetMouse()->PressMouseButton(mouse_button);
|
||||||
input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, mouse_button);
|
input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, mouse_button);
|
||||||
|
@ -71,7 +77,7 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||||
const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
|
auto const [touch_x, touch_y, _] = MouseToTouchPos(x, y);
|
||||||
input_subsystem->GetMouse()->Move(x, y, 0, 0);
|
input_subsystem->GetMouse()->Move(x, y, 0, 0);
|
||||||
input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
|
input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
|
||||||
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
|
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
@ -43,8 +46,11 @@ protected:
|
||||||
/// Converts a SDL mouse button into MouseInput mouse button
|
/// Converts a SDL mouse button into MouseInput mouse button
|
||||||
InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
|
InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
|
||||||
|
|
||||||
/// Translates pixel position to float position
|
// Using std::pair<float,float> will invoke HFA miscompilation bug on g++10
|
||||||
std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
|
// on newer versions it emits an annoying warning, so just not use an HFA
|
||||||
|
// https://stackoverflow.com/questions/77729813/parameter-passing-for-argument-when-c17-is-enabled-changed-to-match-c14
|
||||||
|
using FloatPairNonHFA = std::tuple<float, float, char>;
|
||||||
|
FloatPairNonHFA MouseToTouchPos(s32 touch_x, s32 touch_y) const;
|
||||||
|
|
||||||
/// Called by WaitEvent when a mouse button is pressed or released
|
/// Called by WaitEvent when a mouse button is pressed or released
|
||||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue