early-access version 1995

main
pineappleEA 2021-08-16 13:42:12 +02:00
parent 1c11ae4a45
commit 66b92b0ba8
95 changed files with 18941 additions and 675 deletions

View File

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1994. This is the source code for early-access 1995.
## Legal Notice ## Legal Notice

View File

@ -7,7 +7,9 @@ include(DownloadExternals)
# xbyak # xbyak
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
add_library(xbyak INTERFACE) add_library(xbyak INTERFACE)
target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif() endif()
@ -19,6 +21,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
if (ARCHITECTURE_x86_64) if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF) set(DYNARMIC_TESTS OFF)
set(DYNARMIC_NO_BUNDLED_FMT ON) set(DYNARMIC_NO_BUNDLED_FMT ON)
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
add_subdirectory(dynarmic) add_subdirectory(dynarmic)
endif() endif()

View File

@ -3,4 +3,4 @@ build/
build-*/ build-*/
docs/Doxygen/ docs/Doxygen/
# Generated files # Generated files
src/backend/x64/mig/ src/dynarmic/backend/x64/mig/

View File

@ -12,6 +12,7 @@ endif()
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON) option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" OFF) option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" OFF)
option(DYNARMIC_FATAL_ERRORS "Errors are fatal" OFF) option(DYNARMIC_FATAL_ERRORS "Errors are fatal" OFF)
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_TESTS "Build tests" ${MASTER_PROJECT}) option(DYNARMIC_TESTS "Build tests" ${MASTER_PROJECT})
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF) option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF) option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF)
@ -27,7 +28,7 @@ if (NOT CMAKE_BUILD_TYPE)
endif() endif()
# Set hard requirements for C++ # Set hard requirements for C++
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)

View File

@ -5,14 +5,14 @@ support for other versions of the ARM architecture, having a interpreter mode, a
for other architectures. for other architectures.
Users of this library interact with it primarily through the interface provided in Users of this library interact with it primarily through the interface provided in
[`include/dynarmic`](../include/dynarmic). Users specify how dynarmic's CPU core interacts with [`src/dynarmic/interface`](../src/dynarmic/interface). Users specify how dynarmic's CPU core interacts with
the rest of their system providing an implementation of the relevant `UserCallbacks` interface. the rest of their system providing an implementation of the relevant `UserCallbacks` interface.
Users setup the CPU state using member functions of `Jit`, then call `Jit::Execute` to start CPU Users setup the CPU state using member functions of `Jit`, then call `Jit::Execute` to start CPU
execution. The callbacks defined on `UserCallbacks` may be called from dynamically generated code, execution. The callbacks defined on `UserCallbacks` may be called from dynamically generated code,
so users of the library should not depend on the stack being in a walkable state for unwinding. so users of the library should not depend on the stack being in a walkable state for unwinding.
* A32: [`Jit`](../include/dynarmic/A32/a32.h), [`UserCallbacks`](../include/dynarmic/A32/config.h) * A32: [`Jit`](../src/dynarmic/interface/A32/a32.h), [`UserCallbacks`](../src/dynarmic/interface/A32/config.h)
* A64: [`Jit`](../include/dynarmic/A64/a64.h), [`UserCallbacks`](../include/dynarmic/A64/config.h) * A64: [`Jit`](../src/dynarmic/interface/A64/a64.h), [`UserCallbacks`](../src/dynarmic/interface/A64/config.h)
Dynarmic reads instructions from memory by calling `UserCallbacks::MemoryReadCode`. These Dynarmic reads instructions from memory by calling `UserCallbacks::MemoryReadCode`. These
instructions then pass through several stages: instructions then pass through several stages:
@ -26,19 +26,19 @@ instructions then pass through several stages:
Using the A32 frontend with the x64 backend as an example: Using the A32 frontend with the x64 backend as an example:
* Decoding is done by [double dispatch](https://en.wikipedia.org/wiki/Visitor_pattern) in * Decoding is done by [double dispatch](https://en.wikipedia.org/wiki/Visitor_pattern) in
[`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/frontend/A32/decoder/). [`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/dynarmic/frontend/A32/decoder/).
* Translation is done by the visitors in `src/frontend/A32/translate/translate_{arm,thumb}.cpp`. * Translation is done by the visitors in [`src/dynarmic/frontend/A32/translate/translate_{arm,thumb}.cpp`](../src/dynarmic/frontend/A32/translate/).
The function [`Translate`](../src/frontend/A32/translate/translate.h) takes a starting memory location, The function [`Translate`](../src/dynarmic/frontend/A32/translate/translate.h) takes a starting memory location,
some CPU state, and memory reader callback and returns a basic block of IR. some CPU state, and memory reader callback and returns a basic block of IR.
* The IR can be found under [`src/frontend/ir/`](../src/frontend/ir/). * The IR can be found under [`src/frontend/ir/`](../src/dynarmic/ir/).
* Optimizations can be found under [`src/ir_opt/`](../src/ir_opt/). * Optimizations can be found under [`src/ir_opt/`](../src/dynarmic/ir/opt/).
* Emission is done by `EmitX64` which can be found in `src/backend_x64/emit_x64.{h,cpp}`. * Emission is done by `EmitX64` which can be found in [`src/dynarmic/backend/x64/emit_x64.{h,cpp}`](../src/dynarmic/backend/x64/).
* Execution is performed by calling `BlockOfCode::RunCode` in `src/backend_x64/block_of_code.{h,cpp}`. * Execution is performed by calling `BlockOfCode::RunCode` in [`src/dynarmic/backend/x64/block_of_code.{h,cpp}`](../src/dynarmic/backend/x64/).
## Decoder ## Decoder
The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant
instruction table. Here is an example line from [`arm.h`](../src/frontend/A32/decoder/arm.h): instruction table. Here is an example line from [`arm.h`](../src/dynarmic/frontend/A32/decoder/arm.h):
INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv")
@ -61,7 +61,7 @@ error results.
## Translator ## Translator
The translator is a visitor that uses the decoder to decode instructions. The translator generates IR code with the The translator is a visitor that uses the decoder to decode instructions. The translator generates IR code with the
help of the [`IREmitter` class](../src/frontend/ir/ir_emitter.h). An example of a translation function follows: help of the [`IREmitter` class](../src/dynarmic/ir/ir_emitter.h). An example of a translation function follows:
bool ArmTranslatorVisitor::arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) { bool ArmTranslatorVisitor::arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
u32 imm32 = ArmExpandImm(rotate, imm8); u32 imm32 = ArmExpandImm(rotate, imm8);
@ -107,7 +107,7 @@ function analyser in the medium-term future.
Dynarmic's intermediate representation is typed. Each microinstruction may take zero or more arguments and may Dynarmic's intermediate representation is typed. Each microinstruction may take zero or more arguments and may
return zero or more arguments. A subset of the microinstructions available is documented below. return zero or more arguments. A subset of the microinstructions available is documented below.
A complete list of microinstructions can be found in [src/frontend/ir/opcodes.inc](../src/frontend/ir/opcodes.inc). A complete list of microinstructions can be found in [src/dynarmic/ir/opcodes.inc](../src/dynarmic/ir/opcodes.inc).
The below lists some commonly used microinstructions. The below lists some commonly used microinstructions.

View File

@ -5,7 +5,7 @@
# catch # catch
add_library(catch INTERFACE) add_library(catch INTERFACE)
target_include_directories(catch INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/catch>) target_include_directories(catch INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/catch/include>)
# fmt # fmt
@ -36,7 +36,9 @@ endif()
if (NOT TARGET xbyak) if (NOT TARGET xbyak)
if (ARCHITECTURE STREQUAL "x86" OR ARCHITECTURE STREQUAL "x86_64") if (ARCHITECTURE STREQUAL "x86" OR ARCHITECTURE STREQUAL "x86_64")
add_library(xbyak INTERFACE) add_library(xbyak INTERFACE)
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif() endif()
endif() endif()

File diff suppressed because it is too large Load Diff

View File

@ -103,6 +103,7 @@ Type traits not in the standard library.
* `mp::parameter_list`: Get a typelist of the parameter types * `mp::parameter_list`: Get a typelist of the parameter types
* `mp::get_parameter`: Get the type of a parameter by index * `mp::get_parameter`: Get the type of a parameter by index
* `mp::equivalent_function_type`: Get an equivalent function type (for MFPs this does not include the class) * `mp::equivalent_function_type`: Get an equivalent function type (for MFPs this does not include the class)
* `mp::equivalent_function_type_with_class`: Get an equivalent function type with explicit `this` argument (MFPs only)
* `mp::return_type`: Return type of the function * `mp::return_type`: Return type of the function
* `mp::class_type`: Only valid for member function pointer types. Gets the class the member function is associated with. * `mp::class_type`: Only valid for member function pointer types. Gets the class the member function is associated with.

View File

@ -36,11 +36,15 @@ struct function_info<R(*)(As...)> : function_info<R(As...)> {};
template<class C, class R, class... As> template<class C, class R, class... As>
struct function_info<R(C::*)(As...)> : function_info<R(As...)> { struct function_info<R(C::*)(As...)> : function_info<R(As...)> {
using class_type = C; using class_type = C;
using equivalent_function_type_with_class = R(C*, As...);
}; };
template<class C, class R, class... As> template<class C, class R, class... As>
struct function_info<R(C::*)(As...) const> : function_info<R(As...)> { struct function_info<R(C::*)(As...) const> : function_info<R(As...)> {
using class_type = C; using class_type = C;
using equivalent_function_type_with_class = R(C*, As...);
}; };
template<class F> template<class F>
@ -55,6 +59,9 @@ using get_parameter = typename function_info<F>::template parameter<I>::type;
template<class F> template<class F>
using equivalent_function_type = typename function_info<F>::equivalent_function_type; using equivalent_function_type = typename function_info<F>::equivalent_function_type;
template<class F>
using equivalent_function_type_with_class = typename function_info<F>::equivalent_function_type_with_class;
template<class F> template<class F>
using return_type = typename function_info<F>::return_type; using return_type = typename function_info<F>::return_type;

View File

@ -381,6 +381,9 @@ endif()
if (DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT) if (DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=1) target_compile_definitions(dynarmic PRIVATE DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=1)
endif() endif()
if (DYNARMIC_IGNORE_ASSERTS)
target_compile_definitions(dynarmic PRIVATE DYNARMIC_IGNORE_ASSERTS=1)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(dynarmic PRIVATE FMT_USE_WINDOWS_H=0) target_compile_definitions(dynarmic PRIVATE FMT_USE_WINDOWS_H=0)
endif() endif()

View File

@ -629,18 +629,10 @@ static void EmitSetFlag(BlockOfCode& code, A32EmitContext& ctx, IR::Inst* inst,
} }
} }
void A32EmitX64::EmitA32GetNFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitGetFlag(code, ctx, inst, NZCV::x64_n_flag_bit);
}
void A32EmitX64::EmitA32SetNFlag(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32SetNFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitSetFlag(code, ctx, inst, NZCV::x64_n_flag_bit); EmitSetFlag(code, ctx, inst, NZCV::x64_n_flag_bit);
} }
void A32EmitX64::EmitA32GetZFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitGetFlag(code, ctx, inst, NZCV::x64_z_flag_bit);
}
void A32EmitX64::EmitA32SetZFlag(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32SetZFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitSetFlag(code, ctx, inst, NZCV::x64_z_flag_bit); EmitSetFlag(code, ctx, inst, NZCV::x64_z_flag_bit);
} }
@ -653,10 +645,6 @@ void A32EmitX64::EmitA32SetCFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitSetFlag(code, ctx, inst, NZCV::x64_c_flag_bit); EmitSetFlag(code, ctx, inst, NZCV::x64_c_flag_bit);
} }
void A32EmitX64::EmitA32GetVFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitGetFlag(code, ctx, inst, NZCV::x64_v_flag_bit);
}
void A32EmitX64::EmitA32SetVFlag(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32SetVFlag(A32EmitContext& ctx, IR::Inst* inst) {
EmitSetFlag(code, ctx, inst, NZCV::x64_v_flag_bit); EmitSetFlag(code, ctx, inst, NZCV::x64_v_flag_bit);
} }

View File

@ -18,8 +18,8 @@
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/cast_util.h" #include "dynarmic/common/cast_util.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
#include "dynarmic/common/llvm_disassemble.h"
#include "dynarmic/common/scope_exit.h" #include "dynarmic/common/scope_exit.h"
#include "dynarmic/common/x64_disassemble.h"
#include "dynarmic/frontend/A32/translate/translate.h" #include "dynarmic/frontend/A32/translate/translate.h"
#include "dynarmic/interface/A32/a32.h" #include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/context.h" #include "dynarmic/interface/A32/context.h"
@ -91,13 +91,6 @@ struct Jit::Impl {
jit_state.exclusive_state = 0; jit_state.exclusive_state = 0;
} }
std::string Disassemble(const IR::LocationDescriptor& descriptor) {
auto block = GetBasicBlock(descriptor);
std::string result = fmt::format("address: {}\nsize: {} bytes\n", block.entrypoint, block.size);
result += Common::DisassembleX64(block.entrypoint, reinterpret_cast<const char*>(block.entrypoint) + block.size);
return result;
}
void PerformCacheInvalidation() { void PerformCacheInvalidation() {
if (invalidate_entire_cache) { if (invalidate_entire_cache) {
jit_state.ResetRSB(); jit_state.ResetRSB();
@ -324,8 +317,9 @@ void Jit::LoadContext(const Context& ctx) {
impl->jit_state.TransferJitState(ctx.impl->jit_state, reset_rsb); impl->jit_state.TransferJitState(ctx.impl->jit_state, reset_rsb);
} }
std::string Jit::Disassemble() const { void Jit::DumpDisassembly() const {
return Common::DisassembleX64(impl->block_of_code.GetCodeBegin(), impl->block_of_code.getCurr()); const size_t size = (const char*)impl->block_of_code.getCurr() - (const char*)impl->block_of_code.GetCodeBegin();
Common::DumpDisassembledX64(impl->block_of_code.GetCodeBegin(), size);
} }
} // namespace Dynarmic::A32 } // namespace Dynarmic::A32

View File

@ -7,8 +7,6 @@
#include <array> #include <array>
#include <xbyak.h>
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
namespace Dynarmic::Backend::X64 { namespace Dynarmic::Backend::X64 {

View File

@ -14,8 +14,8 @@
#include "dynarmic/backend/x64/devirtualize.h" #include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/llvm_disassemble.h"
#include "dynarmic/common/scope_exit.h" #include "dynarmic/common/scope_exit.h"
#include "dynarmic/common/x64_disassemble.h"
#include "dynarmic/frontend/A64/translate/translate.h" #include "dynarmic/frontend/A64/translate/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"
@ -199,8 +199,9 @@ public:
return is_executing; return is_executing;
} }
std::string Disassemble() const { void DumpDisassembly() const {
return Common::DisassembleX64(block_of_code.GetCodeBegin(), block_of_code.getCurr()); const size_t size = (const char*)block_of_code.getCurr() - (const char*)block_of_code.GetCodeBegin();
Common::DumpDisassembledX64(block_of_code.GetCodeBegin(), size);
} }
private: private:
@ -397,8 +398,8 @@ bool Jit::IsExecuting() const {
return impl->IsExecuting(); return impl->IsExecuting();
} }
std::string Jit::Disassemble() const { void Jit::DumpDisassembly() const {
return impl->Disassemble(); return impl->DumpDisassembly();
} }
} // namespace Dynarmic::A64 } // namespace Dynarmic::A64

View File

@ -7,8 +7,6 @@
#include <array> #include <array>
#include <xbyak.h>
#include "dynarmic/backend/x64/nzcv_util.h" #include "dynarmic/backend/x64/nzcv_util.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
#include "dynarmic/frontend/A64/location_descriptor.h" #include "dynarmic/frontend/A64/location_descriptor.h"

View File

@ -8,7 +8,7 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/block_of_code.h" #include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -15,7 +15,7 @@
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/a32_jitstate.h" #include "dynarmic/backend/x64/a32_jitstate.h"
#include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/abi.h"
@ -258,8 +258,6 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
jmp(ABI_PARAM2); jmp(ABI_PARAM2);
align();
// Dispatcher loop // Dispatcher loop
Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited; Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited;

View File

@ -10,8 +10,8 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include <xbyak_util.h> #include <xbyak/xbyak_util.h>
#include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/callback.h" #include "dynarmic/backend/x64/callback.h"

View File

@ -8,7 +8,7 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -8,7 +8,7 @@
#include <map> #include <map>
#include <tuple> #include <tuple>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -13,7 +13,7 @@
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include <tsl/robin_set.h> #include <tsl/robin_set.h>
#include <xbyak_util.h> #include <xbyak/xbyak_util.h>
#include "dynarmic/backend/x64/exception_handler.h" #include "dynarmic/backend/x64/exception_handler.h"
#include "dynarmic/backend/x64/reg_alloc.h" #include "dynarmic/backend/x64/reg_alloc.h"

View File

@ -1283,6 +1283,72 @@ void EmitX64::EmitAnd64(EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
} }
void EmitX64::EmitAndNot32(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!args[0].IsImmediate() && !args[1].IsImmediate() && code.HasHostFeature(HostFeature::BMI1)) {
Xbyak::Reg32 op_a = ctx.reg_alloc.UseGpr(args[0]).cvt32();
Xbyak::Reg32 op_b = ctx.reg_alloc.UseGpr(args[1]).cvt32();
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
code.andn(result, op_b, op_a);
ctx.reg_alloc.DefineValue(inst, result);
return;
}
Xbyak::Reg32 result;
if (args[1].IsImmediate()) {
result = ctx.reg_alloc.ScratchGpr().cvt32();
code.mov(result, u32(~args[1].GetImmediateU32()));
} else {
result = ctx.reg_alloc.UseScratchGpr(args[1]).cvt32();
code.not_(result);
}
if (args[0].IsImmediate()) {
const u32 op_arg = args[0].GetImmediateU32();
code.and_(result, op_arg);
} else {
OpArg op_arg = ctx.reg_alloc.UseOpArg(args[0]);
op_arg.setBit(32);
code.and_(result, *op_arg);
}
ctx.reg_alloc.DefineValue(inst, result);
}
void EmitX64::EmitAndNot64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!args[0].IsImmediate() && !args[1].IsImmediate() && code.HasHostFeature(HostFeature::BMI1)) {
Xbyak::Reg64 op_a = ctx.reg_alloc.UseGpr(args[0]);
Xbyak::Reg64 op_b = ctx.reg_alloc.UseGpr(args[1]);
Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr();
code.andn(result, op_b, op_a);
ctx.reg_alloc.DefineValue(inst, result);
return;
}
Xbyak::Reg64 result;
if (args[1].IsImmediate()) {
result = ctx.reg_alloc.ScratchGpr();
code.mov(result, ~args[1].GetImmediateU64());
} else {
result = ctx.reg_alloc.UseScratchGpr(args[1]);
code.not_(result);
}
if (args[0].FitsInImmediateS32()) {
const u32 op_arg = u32(args[0].GetImmediateS32());
code.and_(result, op_arg);
} else {
OpArg op_arg = ctx.reg_alloc.UseOpArg(args[0]);
op_arg.setBit(64);
code.and_(result, *op_arg);
}
ctx.reg_alloc.DefineValue(inst, result);
}
void EmitX64::EmitEor32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitEor32(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);

View File

@ -766,12 +766,16 @@ static void EmitFPRecipEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
if constexpr (fsize == 32) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.rcpss(result, operand); FCODE(vrcp14s)(result, operand, operand);
} else { } else {
code.cvtsd2ss(result, operand); if constexpr (fsize == 32) {
code.rcpss(result, result); code.rcpss(result, operand);
code.cvtss2sd(result, result); } else {
code.cvtsd2ss(result, operand);
code.rcpss(result, result);
code.cvtss2sd(result, result);
}
} }
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
@ -984,20 +988,22 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
if constexpr (fsize == 32) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.rsqrtss(result, operand); FCODE(vrsqrt14s)(result, operand, operand);
} else { } else {
code.cvtsd2ss(result, operand); if constexpr (fsize == 32) {
code.rsqrtss(result, result); code.rsqrtss(result, operand);
code.cvtss2sd(result, result); } else {
code.cvtsd2ss(result, operand);
code.rsqrtss(result, result);
code.cvtss2sd(result, result);
}
} }
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
return; return;
} }
// TODO: VRSQRT14SS implementation (AVX512F)
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);

View File

@ -165,7 +165,7 @@ void EmitX64::EmitVectorGetElement8(EmitContext& ctx, IR::Inst* inst) {
if (code.HasHostFeature(HostFeature::SSE41)) { if (code.HasHostFeature(HostFeature::SSE41)) {
code.pextrb(dest, source, index); code.pextrb(dest, source, index);
} else { } else {
code.pextrw(dest, source, index / 2); code.pextrw(dest, source, u8(index / 2));
if (index % 2 == 1) { if (index % 2 == 1) {
code.shr(dest, 8); code.shr(dest, 8);
} else { } else {
@ -439,6 +439,17 @@ void EmitX64::EmitVectorAnd(EmitContext& ctx, IR::Inst* inst) {
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::pand); EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::pand);
} }
void EmitX64::EmitVectorAndNot(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm xmm_a = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm xmm_b = ctx.reg_alloc.UseScratchXmm(args[1]);
code.pandn(xmm_b, xmm_a);
ctx.reg_alloc.DefineValue(inst, xmm_b);
}
static void ArithmeticShiftRightByte(EmitContext& ctx, BlockOfCode& code, const Xbyak::Xmm& result, u8 shift_amount) { static void ArithmeticShiftRightByte(EmitContext& ctx, BlockOfCode& code, const Xbyak::Xmm& result, u8 shift_amount) {
if (code.HasHostFeature(HostFeature::GFNI)) { if (code.HasHostFeature(HostFeature::GFNI)) {
const u64 shift_matrix = shift_amount < 8 const u64 shift_matrix = shift_amount < 8
@ -741,6 +752,148 @@ void EmitX64::EmitVectorBroadcast64(EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(inst, a); ctx.reg_alloc.DefineValue(inst, a);
} }
void EmitX64::EmitVectorBroadcastElementLower8(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 16);
if (index > 0) {
code.psrldq(a, index);
}
if (code.HasHostFeature(HostFeature::AVX2)) {
code.vpbroadcastb(a, a);
code.vmovq(a, a);
} else if (code.HasHostFeature(HostFeature::SSSE3)) {
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
code.pxor(tmp, tmp);
code.pshufb(a, tmp);
code.movq(a, a);
} else {
code.punpcklbw(a, a);
code.pshuflw(a, a, 0);
}
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElementLower16(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 8);
if (index > 0) {
code.psrldq(a, u8(index * 2));
}
code.pshuflw(a, a, 0);
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElementLower32(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 4);
if (index > 0) {
code.psrldq(a, u8(index * 4));
}
code.pshuflw(a, a, 0b01'00'01'00);
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElement8(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 16);
if (index > 0) {
code.psrldq(a, index);
}
if (code.HasHostFeature(HostFeature::AVX2)) {
code.vpbroadcastb(a, a);
} else if (code.HasHostFeature(HostFeature::SSSE3)) {
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
code.pxor(tmp, tmp);
code.pshufb(a, tmp);
} else {
code.punpcklbw(a, a);
code.pshuflw(a, a, 0);
code.punpcklqdq(a, a);
}
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElement16(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 8);
if (index == 0 && code.HasHostFeature(HostFeature::AVX2)) {
code.vpbroadcastw(a, a);
ctx.reg_alloc.DefineValue(inst, a);
return;
}
if (index < 4) {
code.pshuflw(a, a, Common::Replicate<u8>(index, 2));
code.punpcklqdq(a, a);
} else {
code.pshufhw(a, a, Common::Replicate<u8>(u8(index - 4), 2));
code.punpckhqdq(a, a);
}
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElement32(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 4);
code.pshufd(a, a, Common::Replicate<u8>(index, 2));
ctx.reg_alloc.DefineValue(inst, a);
}
void EmitX64::EmitVectorBroadcastElement64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm a = ctx.reg_alloc.UseScratchXmm(args[0]);
ASSERT(args[1].IsImmediate());
const u8 index = args[1].GetImmediateU8();
ASSERT(index < 2);
if (code.HasHostFeature(HostFeature::AVX)) {
code.vpermilpd(a, a, Common::Replicate<u8>(index, 1));
} else {
if (index == 0) {
code.punpcklqdq(a, a);
} else {
code.punpckhqdq(a, a);
}
}
ctx.reg_alloc.DefineValue(inst, a);
}
template<typename T> template<typename T>
static void EmitVectorCountLeadingZeros(VectorArray<T>& result, const VectorArray<T>& data) { static void EmitVectorCountLeadingZeros(VectorArray<T>& result, const VectorArray<T>& data) {
for (size_t i = 0; i < result.size(); i++) { for (size_t i = 0; i < result.size(); i++) {

View File

@ -1288,12 +1288,16 @@ static void EmitRecipEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* ins
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
if constexpr (fsize == 32) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.rcpps(result, operand); FCODE(vrcp14p)(result, operand);
} else { } else {
code.cvtpd2ps(result, operand); if constexpr (fsize == 32) {
code.rcpps(result, result); code.rcpps(result, operand);
code.cvtps2pd(result, result); } else {
code.cvtpd2ps(result, operand);
code.rcpps(result, result);
code.cvtps2pd(result, result);
}
} }
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
@ -1502,12 +1506,16 @@ static void EmitRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* ins
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]); const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(); const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
if constexpr (fsize == 32) { if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.rsqrtps(result, operand); FCODE(vrsqrt14p)(result, operand);
} else { } else {
code.cvtpd2ps(result, operand); if constexpr (fsize == 32) {
code.rsqrtps(result, result); code.rsqrtps(result, operand);
code.cvtps2pd(result, result); } else {
code.cvtpd2ps(result, operand);
code.rsqrtps(result, result);
code.cvtps2pd(result, result);
}
} }
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
@ -1707,8 +1715,6 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const auto rounding = static_cast<FP::RoundingMode>(inst->GetArg(2).GetU8()); const auto rounding = static_cast<FP::RoundingMode>(inst->GetArg(2).GetU8());
[[maybe_unused]] const bool fpcr_controlled = inst->GetArg(3).GetU1(); [[maybe_unused]] const bool fpcr_controlled = inst->GetArg(3).GetU1();
// TODO: AVX512 implementation
if constexpr (fsize != 16) { if constexpr (fsize != 16) {
if (code.HasHostFeature(HostFeature::SSE41) && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero) { if (code.HasHostFeature(HostFeature::SSE41) && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -1737,17 +1743,21 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
if constexpr (fsize == 32) { if constexpr (fsize == 32) {
code.cvttps2dq(src, src); code.cvttps2dq(src, src);
} else { } else {
const Xbyak::Reg64 hi = ctx.reg_alloc.ScratchGpr(); if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
const Xbyak::Reg64 lo = ctx.reg_alloc.ScratchGpr(); code.vcvttpd2qq(src, src);
} else {
const Xbyak::Reg64 hi = ctx.reg_alloc.ScratchGpr();
const Xbyak::Reg64 lo = ctx.reg_alloc.ScratchGpr();
code.cvttsd2si(lo, src); code.cvttsd2si(lo, src);
code.punpckhqdq(src, src); code.punpckhqdq(src, src);
code.cvttsd2si(hi, src); code.cvttsd2si(hi, src);
code.movq(src, lo); code.movq(src, lo);
code.pinsrq(src, hi, 1); code.pinsrq(src, hi, 1);
ctx.reg_alloc.Release(hi); ctx.reg_alloc.Release(hi);
ctx.reg_alloc.Release(lo); ctx.reg_alloc.Release(lo);
}
} }
}; };
@ -1765,29 +1775,43 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
[[maybe_unused]] constexpr u64 float_upper_limit_unsigned = fsize == 32 ? 0x4f800000 : 0x43f0000000000000; [[maybe_unused]] constexpr u64 float_upper_limit_unsigned = fsize == 32 ? 0x4f800000 : 0x43f0000000000000;
if constexpr (unsigned_) { if constexpr (unsigned_) {
// Zero is minimum if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
code.xorps(xmm0, xmm0); // Mask positive values
FCODE(cmplep)(xmm0, src); code.xorps(xmm0, xmm0);
FCODE(andp)(src, xmm0); FCODE(vcmpp)(k1, src, xmm0, Cmp::GreaterEqual_OQ);
// Will we exceed unsigned range? // Convert positive values to unsigned integers, write 0 anywhere else
const Xbyak::Xmm exceed_unsigned = ctx.reg_alloc.ScratchXmm(); // vcvttp*2u*q already saturates out-of-range values to (0xFFFF...)
code.movaps(exceed_unsigned, GetVectorOf<fsize, float_upper_limit_unsigned>(code)); if constexpr (fsize == 32) {
FCODE(cmplep)(exceed_unsigned, src); code.vcvttps2udq(src | k1 | T_z, src);
} else {
code.vcvttpd2uqq(src | k1 | T_z, src);
}
} else {
// Zero is minimum
code.xorps(xmm0, xmm0);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(src, xmm0);
// Will be exceed signed range? // Will we exceed unsigned range?
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(); const Xbyak::Xmm exceed_unsigned = ctx.reg_alloc.ScratchXmm();
code.movaps(tmp, GetVectorOf<fsize, float_upper_limit_signed>(code)); code.movaps(exceed_unsigned, GetVectorOf<fsize, float_upper_limit_unsigned>(code));
code.movaps(xmm0, tmp); FCODE(cmplep)(exceed_unsigned, src);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(tmp, xmm0);
FCODE(subp)(src, tmp);
perform_conversion(src);
ICODE(psll)(xmm0, static_cast<u8>(fsize - 1));
FCODE(orp)(src, xmm0);
// Saturate to max // Will be exceed signed range?
FCODE(orp)(src, exceed_unsigned); const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
code.movaps(tmp, GetVectorOf<fsize, float_upper_limit_signed>(code));
code.movaps(xmm0, tmp);
FCODE(cmplep)(xmm0, src);
FCODE(andp)(tmp, xmm0);
FCODE(subp)(src, tmp);
perform_conversion(src);
ICODE(psll)(xmm0, static_cast<u8>(fsize - 1));
FCODE(orp)(src, xmm0);
// Saturate to max
FCODE(orp)(src, exceed_unsigned);
}
} else { } else {
constexpr u64 integer_max = static_cast<FPT>(std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max()); constexpr u64 integer_max = static_cast<FPT>(std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max());

View File

@ -5,7 +5,7 @@
#include "dynarmic/backend/x64/hostloc.h" #include "dynarmic/backend/x64/hostloc.h"
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/stack_layout.h" #include "dynarmic/backend/x64/stack_layout.h"

View File

@ -4,7 +4,7 @@
*/ */
#pragma once #pragma once
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -5,7 +5,7 @@
#pragma once #pragma once
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"

View File

@ -10,7 +10,7 @@
#include <utility> #include <utility>
#include <fmt/ostream.h> #include <fmt/ostream.h>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/stack_layout.h" #include "dynarmic/backend/x64/stack_layout.h"

View File

@ -11,7 +11,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/block_of_code.h" #include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/backend/x64/hostloc.h" #include "dynarmic/backend/x64/hostloc.h"

View File

@ -203,7 +203,7 @@ constexpr T Replicate(T value, size_t element_size) {
ASSERT_MSG(BitSize<T>() % element_size == 0, "bitsize of T not divisible by element_size"); ASSERT_MSG(BitSize<T>() % element_size == 0, "bitsize of T not divisible by element_size");
if (element_size == BitSize<T>()) if (element_size == BitSize<T>())
return value; return value;
return Replicate(value | (value << element_size), element_size * 2); return Replicate<T>(T(value | value << element_size), element_size * 2);
} }
template<typename T> template<typename T>

View File

@ -13,6 +13,7 @@
#endif #endif
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
#include "dynarmic/common/llvm_disassemble.h" #include "dynarmic/common/llvm_disassemble.h"
@ -48,7 +49,7 @@ std::string DisassembleX64(const void* begin, const void* end) {
LLVMDisasmDispose(llvm_ctx); LLVMDisasmDispose(llvm_ctx);
#else #else
result += fmt::format("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n"); result += fmt::format("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n");
result += fmt::format("start: {:016x}, end: {:016x}\n", begin, end); result += fmt::format("start: {:016x}, end: {:016x}\n", BitCast<u64>(begin), BitCast<u64>(end));
#endif #endif
return result; return result;

View File

@ -1364,12 +1364,12 @@ public:
std::string vfp_VMOV_from_i16(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<1> i2) { std::string vfp_VMOV_from_i16(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<1> i2) {
const size_t index = concatenate(i1, i2).ZeroExtend(); const size_t index = concatenate(i1, i2).ZeroExtend();
return fmt::format("vmov{}.{}16 {}[{}], {}", CondToString(cond), FPRegStr(true, Vd, D), index, t); return fmt::format("vmov{}.16 {}[{}], {}", CondToString(cond), FPRegStr(true, Vd, D), index, t);
} }
std::string vfp_VMOV_from_i8(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<2> i2) { std::string vfp_VMOV_from_i8(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<2> i2) {
const size_t index = concatenate(i1, i2).ZeroExtend(); const size_t index = concatenate(i1, i2).ZeroExtend();
return fmt::format("vmov{}.{}8 {}[{}], {}", CondToString(cond), FPRegStr(true, Vd, D), index, t); return fmt::format("vmov{}.8 {}[{}], {}", CondToString(cond), FPRegStr(true, Vd, D), index, t);
} }
std::string vfp_VMOV_to_i32(Cond cond, Imm<1> i, size_t Vn, Reg t, bool N) { std::string vfp_VMOV_to_i32(Cond cond, Imm<1> i, size_t Vn, Reg t, bool N) {

View File

@ -32,6 +32,7 @@ bool IsConditionPassed(TranslatorVisitor& v, IR::Cond cond) {
if (cond == IR::Cond::NV) { if (cond == IR::Cond::NV) {
// NV conditional is obsolete // NV conditional is obsolete
v.cond_state = ConditionalState::Break;
v.RaiseException(Exception::UnpredictableInstruction); v.RaiseException(Exception::UnpredictableInstruction);
return false; return false;
} }

View File

@ -80,8 +80,7 @@ bool TranslatorVisitor::asimd_VDUP_scalar(bool D, Imm<4> imm4, size_t Vd, bool Q
const auto m = ToVector(false, Vm, M); const auto m = ToVector(false, Vm, M);
const auto reg_m = ir.GetVector(m); const auto reg_m = ir.GetVector(m);
const auto scalar = ir.VectorGetElement(esize, reg_m, index); const auto result = ir.VectorBroadcastElement(esize, reg_m, index);
const auto result = ir.VectorBroadcast(esize, scalar);
ir.SetVector(d, result); ir.SetVector(d, result);
return true; return true;

View File

@ -318,7 +318,7 @@ bool TranslatorVisitor::asimd_VAND_reg(bool D, size_t Vn, size_t Vd, bool N, boo
bool TranslatorVisitor::asimd_VBIC_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) { bool TranslatorVisitor::asimd_VBIC_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) { return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorAnd(reg_n, ir.VectorNot(reg_m)); return ir.VectorAndNot(reg_n, reg_m);
}); });
} }
@ -342,19 +342,19 @@ bool TranslatorVisitor::asimd_VEOR_reg(bool D, size_t Vn, size_t Vd, bool N, boo
bool TranslatorVisitor::asimd_VBSL(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) { bool TranslatorVisitor::asimd_VBSL(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) { return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_n, reg_d), ir.VectorAnd(reg_m, ir.VectorNot(reg_d))); return ir.VectorOr(ir.VectorAnd(reg_n, reg_d), ir.VectorAndNot(reg_m, reg_d));
}); });
} }
bool TranslatorVisitor::asimd_VBIT(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) { bool TranslatorVisitor::asimd_VBIT(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) { return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_n, reg_m), ir.VectorAnd(reg_d, ir.VectorNot(reg_m))); return ir.VectorOr(ir.VectorAnd(reg_n, reg_m), ir.VectorAndNot(reg_d, reg_m));
}); });
} }
bool TranslatorVisitor::asimd_VBIF(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) { bool TranslatorVisitor::asimd_VBIF(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) { return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_d, reg_m), ir.VectorAnd(reg_n, ir.VectorNot(reg_m))); return ir.VectorOr(ir.VectorAnd(reg_d, reg_m), ir.VectorAndNot(reg_n, reg_m));
}); });
} }

View File

@ -46,9 +46,8 @@ bool ScalarMultiply(TranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn,
const auto n = ToVector(Q, Vn, N); const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm); const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto reg_n = v.ir.GetVector(n); const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.VectorBroadcast(esize, scalar); const auto reg_m = v.ir.VectorBroadcastElement(esize, v.ir.GetVector(m), index);
const auto addend = F ? v.ir.FPVectorMul(esize, reg_n, reg_m, false) const auto addend = F ? v.ir.FPVectorMul(esize, reg_n, reg_m, false)
: v.ir.VectorMultiply(esize, reg_n, reg_m); : v.ir.VectorMultiply(esize, reg_n, reg_m);
const auto result = [&] { const auto result = [&] {
@ -125,9 +124,8 @@ bool ScalarMultiplyReturnHigh(TranslatorVisitor& v, bool Q, bool D, size_t sz, s
const auto n = ToVector(Q, Vn, N); const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm); const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto reg_n = v.ir.GetVector(n); const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.VectorBroadcast(esize, scalar); const auto reg_m = v.ir.VectorBroadcastElement(esize, v.ir.GetVector(m), index);
const auto result = [&] { const auto result = [&] {
const auto tmp = v.ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m); const auto tmp = v.ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m);
@ -177,9 +175,8 @@ bool TranslatorVisitor::asimd_VQDMULL_scalar(bool D, size_t sz, size_t Vn, size_
const auto n = ToVector(false, Vn, N); const auto n = ToVector(false, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm); const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = ir.VectorGetElement(esize, ir.GetVector(m), index);
const auto reg_n = ir.GetVector(n); const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.VectorBroadcast(esize, scalar); const auto reg_m = ir.VectorBroadcastElement(esize, ir.GetVector(m), index);
const auto result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, reg_n, reg_m); const auto result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, reg_n, reg_m);
ir.SetVector(d, result); ir.SetVector(d, result);

View File

@ -177,7 +177,7 @@ bool TranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool
const auto shifted = ir.VectorLogicalShiftRight(esize, reg_m, static_cast<u8>(shift_amount)); const auto shifted = ir.VectorLogicalShiftRight(esize, reg_m, static_cast<u8>(shift_amount));
const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask)); const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const auto result = ir.VectorOr(ir.VectorAnd(reg_d, ir.VectorNot(mask_vec)), shifted); const auto result = ir.VectorOr(ir.VectorAndNot(reg_d, mask_vec), shifted);
ir.SetVector(d, result); ir.SetVector(d, result);
return true; return true;
@ -203,7 +203,7 @@ bool TranslatorVisitor::asimd_VSLI(bool D, size_t imm6, size_t Vd, bool L, bool
const auto shifted = ir.VectorLogicalShiftLeft(esize, reg_m, static_cast<u8>(shift_amount)); const auto shifted = ir.VectorLogicalShiftLeft(esize, reg_m, static_cast<u8>(shift_amount));
const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask)); const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const auto result = ir.VectorOr(ir.VectorAnd(reg_d, ir.VectorNot(mask_vec)), shifted); const auto result = ir.VectorOr(ir.VectorAndNot(reg_d, mask_vec), shifted);
ir.SetVector(d, result); ir.SetVector(d, result);
return true; return true;

View File

@ -250,7 +250,7 @@ bool TranslatorVisitor::arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate,
} }
const auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag()); const auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.Imm32(imm_carry.imm32))); const auto result = ir.AndNot(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
if (d == Reg::PC) { if (d == Reg::PC) {
if (S) { if (S) {
// This is UNPREDICTABLE when in user-mode. // This is UNPREDICTABLE when in user-mode.
@ -280,7 +280,7 @@ bool TranslatorVisitor::arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5
const auto carry_in = ir.GetCFlag(); const auto carry_in = ir.GetCFlag();
const auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in); const auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
const auto result = ir.And(ir.GetRegister(n), ir.Not(shifted.result)); const auto result = ir.AndNot(ir.GetRegister(n), shifted.result);
if (d == Reg::PC) { if (d == Reg::PC) {
if (S) { if (S) {
// This is UNPREDICTABLE when in user-mode. // This is UNPREDICTABLE when in user-mode.
@ -315,7 +315,7 @@ bool TranslatorVisitor::arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, Shif
const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s)); const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
const auto carry_in = ir.GetCFlag(); const auto carry_in = ir.GetCFlag();
const auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in); const auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
const auto result = ir.And(ir.GetRegister(n), ir.Not(shifted.result)); const auto result = ir.AndNot(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {

View File

@ -356,7 +356,7 @@ bool TranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) {
bool TranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) { bool TranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
const Reg d = d_n; const Reg d = d_n;
const Reg n = d_n; const Reg n = d_n;
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m))); const auto result = ir.AndNot(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) { if (!ir.current_location.IT().IsInITBlock()) {

View File

@ -45,7 +45,7 @@ bool TranslatorVisitor::thumb32_BIC_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Re
} }
const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.Imm32(imm_carry.imm32))); const auto result = ir.AndNot(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {

View File

@ -45,7 +45,7 @@ bool TranslatorVisitor::thumb32_BIC_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2
} }
const auto shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag()); const auto shifted = EmitImmShift(ir.GetRegister(m), type, imm3, imm2, ir.GetCFlag());
const auto result = ir.And(ir.GetRegister(n), ir.Not(shifted.result)); const auto result = ir.AndNot(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result); ir.SetRegister(d, result);
if (S) { if (S) {
ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetNFlag(ir.MostSignificantBit(result));

View File

@ -128,8 +128,8 @@ bool TranslatorVisitor::BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Re
const u8 shift_amount = imm6.ZeroExtend<u8>(); const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn); const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount))); const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2); const auto result = ir.AndNot(operand1, operand2);
X(datasize, Rd, result); X(datasize, Rd, result);
return true; return true;
@ -225,8 +225,8 @@ bool TranslatorVisitor::BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn,
const u8 shift_amount = imm6.ZeroExtend<u8>(); const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn); const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount))); const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2); const auto result = ir.AndNot(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result)); ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result); X(datasize, Rd, result);

View File

@ -41,8 +41,7 @@ bool TranslatorVisitor::DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64; const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(idxdsize, Vn); const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny element = ir.VectorGetElement(esize, operand, index); const IR::U128 result = Q ? ir.VectorBroadcastElement(esize, operand, index) : ir.VectorBroadcastElementLower(esize, operand, index);
const IR::U128 result = Q ? ir.VectorBroadcast(esize, element) : ir.VectorBroadcastLower(esize, element);
V(datasize, Vd, result); V(datasize, Vd, result);
return true; return true;
} }

View File

@ -23,7 +23,7 @@ bool TranslatorVisitor::BCAX(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 m = ir.GetQ(Vm); const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn); const IR::U128 n = ir.GetQ(Vn);
const IR::U128 result = ir.VectorEor(n, ir.VectorAnd(m, ir.VectorNot(a))); const IR::U128 result = ir.VectorEor(n, ir.VectorAndNot(m, a));
ir.SetQ(Vd, result); ir.SetQ(Vd, result);
return true; return true;

View File

@ -65,7 +65,7 @@ bool SM3TT2(TranslatorVisitor& v, Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd, SM3TTVari
return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d)); return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d));
} }
const IR::U32 tmp1 = v.ir.And(top_d, before_top_d); const IR::U32 tmp1 = v.ir.And(top_d, before_top_d);
const IR::U32 tmp2 = v.ir.And(v.ir.Not(top_d), after_low_d); const IR::U32 tmp2 = v.ir.AndNot(after_low_d, top_d);
return v.ir.Or(tmp1, tmp2); return v.ir.Or(tmp1, tmp2);
}(); }();
const IR::U32 final_tt2 = v.ir.Add(tt2, v.ir.Add(low_d, v.ir.Add(top_n, wj))); const IR::U32 final_tt2 = v.ir.Add(tt2, v.ir.Add(low_d, v.ir.Add(top_n, wj)));

View File

@ -156,7 +156,7 @@ bool ShiftAndInsert(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec
return v.ir.LogicalShiftLeft(operand1, v.ir.Imm8(shift_amount)); return v.ir.LogicalShiftLeft(operand1, v.ir.Imm8(shift_amount));
}(); }();
const IR::U64 result = v.ir.Or(v.ir.And(operand2, v.ir.Not(v.ir.Imm64(mask))), shifted); const IR::U64 result = v.ir.Or(v.ir.AndNot(operand2, v.ir.Imm64(mask)), shifted);
v.V_scalar(esize, Vd, result); v.V_scalar(esize, Vd, result);
return true; return true;
} }

View File

@ -143,8 +143,8 @@ bool TranslatorVisitor::SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> V
const auto [index, Vm] = Combine(size, H, L, M, Vmlo); const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index); const IR::U128 operand2 = V(128, Vm);
const IR::U128 broadcast = ir.VectorBroadcast(esize, operand2); const IR::U128 broadcast = ir.VectorBroadcastElement(esize, operand2, index);
const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, broadcast); const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, broadcast);
const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1))); const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
@ -161,8 +161,8 @@ bool TranslatorVisitor::SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vm
const auto [index, Vm] = Combine(size, H, L, M, Vmlo); const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0)); const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index); const IR::U128 operand2 = V(128, Vm);
const IR::U128 broadcast = ir.VectorBroadcast(esize, operand2); const IR::U128 broadcast = ir.VectorBroadcastElement(esize, operand2, index);
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, broadcast); const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, broadcast);
V(128, Vd, result); V(128, Vd, result);

View File

@ -50,7 +50,7 @@ IR::U128 SHA512Hash(IREmitter& ir, Vec Vm, Vec Vn, Vec Vd, SHA512HashPart part)
const IR::U64 tmp1 = ir.And(a, b); const IR::U64 tmp1 = ir.And(a, b);
if (part == SHA512HashPart::Part1) { if (part == SHA512HashPart::Part1) {
const IR::U64 tmp2 = ir.And(ir.Not(a), c); const IR::U64 tmp2 = ir.AndNot(c, a);
return ir.Eor(tmp1, tmp2); return ir.Eor(tmp1, tmp2);
} }

View File

@ -350,7 +350,7 @@ bool TranslatorVisitor::SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd)
const IR::U128 shifted = ir.VectorLogicalShiftRight(esize, operand1, shift_amount); const IR::U128 shifted = ir.VectorLogicalShiftRight(esize, operand1, shift_amount);
const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask)); const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted); const IR::U128 result = ir.VectorOr(ir.VectorAndNot(operand2, mask_vec), shifted);
V(datasize, Vd, result); V(datasize, Vd, result);
return true; return true;
@ -376,7 +376,7 @@ bool TranslatorVisitor::SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd)
const IR::U128 shifted = ir.VectorLogicalShiftLeft(esize, operand1, shift_amount); const IR::U128 shifted = ir.VectorLogicalShiftLeft(esize, operand1, shift_amount);
const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask)); const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted); const IR::U128 result = ir.VectorOr(ir.VectorAndNot(operand2, mask_vec), shifted);
V(datasize, Vd, result); V(datasize, Vd, result);
return true; return true;

View File

@ -773,7 +773,7 @@ bool TranslatorVisitor::BIC_asimd_reg(bool Q, Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm); const IR::U128 operand2 = V(datasize, Vm);
IR::U128 result = ir.VectorAnd(operand1, ir.VectorNot(operand2)); IR::U128 result = ir.VectorAndNot(operand1, operand2);
if (datasize == 64) { if (datasize == 64) {
result = ir.VectorZeroUpper(result); result = ir.VectorZeroUpper(result);
} }

View File

@ -36,7 +36,7 @@ bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<
const size_t datasize = Q ? 128 : 64; const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = v.ir.VectorBroadcast(esize, v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index)); const IR::U128 operand2 = v.ir.VectorBroadcastElement(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand3 = v.V(datasize, Vd); const IR::U128 operand3 = v.V(datasize, Vd);
IR::U128 result = v.ir.VectorMultiply(esize, operand1, operand2); IR::U128 result = v.ir.VectorMultiply(esize, operand1, operand2);
@ -64,9 +64,8 @@ bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1>
const size_t esize = sz ? 64 : 32; const size_t esize = sz ? 64 : 32;
const size_t datasize = Q ? 128 : 64; const size_t datasize = Q ? 128 : 64;
const IR::UAny element2 = v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = Q ? v.ir.VectorBroadcast(esize, element2) : v.ir.VectorBroadcastLower(esize, element2); const IR::U128 operand2 = Q ? v.ir.VectorBroadcastElement(esize, v.V(idxdsize, Vm), index) : v.ir.VectorBroadcastElementLower(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand3 = v.V(datasize, Vd); const IR::U128 operand3 = v.V(datasize, Vd);
const IR::U128 result = [&] { const IR::U128 result = [&] {
@ -93,9 +92,8 @@ bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Im
const size_t esize = 16; const size_t esize = 16;
const size_t datasize = Q ? 128 : 64; const size_t datasize = Q ? 128 : 64;
const IR::UAny element2 = v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand1 = v.V(datasize, Vn); const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = Q ? v.ir.VectorBroadcast(esize, element2) : v.ir.VectorBroadcastLower(esize, element2); const IR::U128 operand2 = Q ? v.ir.VectorBroadcastElement(esize, v.V(idxdsize, Vm), index) : v.ir.VectorBroadcastElementLower(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand3 = v.V(datasize, Vd); const IR::U128 operand3 = v.V(datasize, Vd);
// TODO: We currently don't implement half-precision paths for // TODO: We currently don't implement half-precision paths for
@ -179,7 +177,7 @@ bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M,
const IR::U128 operand1 = v.Vpart(datasize, Vn, Q); const IR::U128 operand1 = v.Vpart(datasize, Vn, Q);
const IR::U128 operand2 = v.V(idxsize, Vm); const IR::U128 operand2 = v.V(idxsize, Vm);
const IR::U128 index_vector = v.ir.VectorBroadcast(esize, v.ir.VectorGetElement(esize, operand2, index)); const IR::U128 index_vector = v.ir.VectorBroadcastElement(esize, operand2, index);
const IR::U128 result = [&] { const IR::U128 result = [&] {
const auto [extended_op1, extended_index] = extend_operands(operand1, index_vector); const auto [extended_op1, extended_index] = extend_operands(operand1, index_vector);
@ -349,7 +347,7 @@ bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I
const IR::U128 operand1 = Vpart(datasize, Vn, part); const IR::U128 operand1 = Vpart(datasize, Vn, part);
const IR::U128 operand2 = V(idxsize, Vm); const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index)); const IR::U128 index_vector = ir.VectorBroadcastElement(esize, operand2, index);
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, index_vector); const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, index_vector);
V(128, Vd, result); V(128, Vd, result);
@ -368,7 +366,7 @@ bool TranslatorVisitor::SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, I
const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(idxsize, Vm); const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index)); const IR::U128 index_vector = ir.VectorBroadcastElement(esize, operand2, index);
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector).upper; const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector).upper;
V(datasize, Vd, result); V(datasize, Vd, result);
@ -387,7 +385,7 @@ bool TranslatorVisitor::SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M,
const IR::U128 operand1 = V(datasize, Vn); const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(idxsize, Vm); const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index)); const IR::U128 index_vector = ir.VectorBroadcastElement(esize, operand2, index);
const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector); const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector);
const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1))); const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));

View File

@ -15,7 +15,7 @@ bool TranslatorVisitor::AXFlag() {
const IR::U32 v = ir.And(nzcv, ir.Imm32(0x10000000)); const IR::U32 v = ir.And(nzcv, ir.Imm32(0x10000000));
const IR::U32 new_z = ir.Or(ir.LogicalShiftLeft(v, ir.Imm8(2)), z); const IR::U32 new_z = ir.Or(ir.LogicalShiftLeft(v, ir.Imm8(2)), z);
const IR::U32 new_c = ir.And(ir.And(c, ir.Not(ir.LogicalShiftLeft(v, ir.Imm8(1)))), ir.Imm32(0x20000000)); const IR::U32 new_c = ir.And(ir.AndNot(c, ir.LogicalShiftLeft(v, ir.Imm8(1))), ir.Imm32(0x20000000));
ir.SetNZCVRaw(ir.Or(new_z, new_c)); ir.SetNZCVRaw(ir.Or(new_z, new_c));
return true; return true;
@ -27,8 +27,8 @@ bool TranslatorVisitor::XAFlag() {
const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000)); const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000));
const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000)); const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000));
const IR::U32 not_z = ir.And(ir.Not(z), ir.Imm32(0x40000000)); const IR::U32 not_z = ir.AndNot(ir.Imm32(0x40000000), z);
const IR::U32 not_c = ir.And(ir.Not(c), ir.Imm32(0x20000000)); const IR::U32 not_c = ir.AndNot(ir.Imm32(0x20000000), c);
const IR::U32 new_n = ir.And(ir.LogicalShiftLeft(not_c, ir.Imm8(2)), const IR::U32 new_n = ir.And(ir.LogicalShiftLeft(not_c, ir.Imm8(2)),
ir.LogicalShiftLeft(not_z, ir.Imm8(1))); ir.LogicalShiftLeft(not_z, ir.Imm8(1)));

View File

@ -104,7 +104,10 @@ struct detail {
} }
} }
#ifndef DYNARMIC_IGNORE_ASSERTS
// Avoids a MSVC ICE.
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
#endif
return std::make_tuple(masks, shifts); return std::make_tuple(masks, shifts);
} }

View File

@ -88,11 +88,8 @@ public:
return is_executing; return is_executing;
} }
/** /// Debugging: Dump a disassembly all compiled code to the console.
* Debugging: Disassemble all of compiled code. void DumpDisassembly() const;
* @return A string containing disassembly of all host machine code produced.
*/
std::string Disassemble() const;
private: private:
bool is_executing = false; bool is_executing = false;

View File

@ -114,11 +114,8 @@ public:
*/ */
bool IsExecuting() const; bool IsExecuting() const;
/** /// Debugging: Dump a disassembly all of compiled code to the console.
* Debugging: Disassemble all of compiled code. void DumpDisassembly() const;
* @return A string containing disassembly of all host machine code produced.
*/
std::string Disassemble() const;
private: private:
struct Impl; struct Impl;

View File

@ -317,6 +317,15 @@ U32U64 IREmitter::And(const U32U64& a, const U32U64& b) {
} }
} }
U32U64 IREmitter::AndNot(const U32U64& a, const U32U64& b) {
ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) {
return Inst<U32>(Opcode::AndNot32, a, b);
} else {
return Inst<U64>(Opcode::AndNot64, a, b);
}
}
U32U64 IREmitter::Eor(const U32U64& a, const U32U64& b) { U32U64 IREmitter::Eor(const U32U64& a, const U32U64& b) {
ASSERT(a.GetType() == b.GetType()); ASSERT(a.GetType() == b.GetType());
if (a.GetType() == Type::U32) { if (a.GetType() == Type::U32) {
@ -958,6 +967,10 @@ U128 IREmitter::VectorAnd(const U128& a, const U128& b) {
return Inst<U128>(Opcode::VectorAnd, a, b); return Inst<U128>(Opcode::VectorAnd, a, b);
} }
U128 IREmitter::VectorAndNot(const U128& a, const U128& b) {
return Inst<U128>(Opcode::VectorAndNot, a, b);
}
U128 IREmitter::VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) { U128 IREmitter::VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount) {
switch (esize) { switch (esize) {
case 8: case 8:
@ -1012,6 +1025,34 @@ U128 IREmitter::VectorBroadcast(size_t esize, const UAny& a) {
UNREACHABLE(); UNREACHABLE();
} }
U128 IREmitter::VectorBroadcastElementLower(size_t esize, const U128& a, size_t index) {
ASSERT_MSG(esize * index < 128, "Invalid index");
switch (esize) {
case 8:
return Inst<U128>(Opcode::VectorBroadcastElementLower8, a, u8(index));
case 16:
return Inst<U128>(Opcode::VectorBroadcastElementLower16, a, u8(index));
case 32:
return Inst<U128>(Opcode::VectorBroadcastElementLower32, a, u8(index));
}
UNREACHABLE();
}
U128 IREmitter::VectorBroadcastElement(size_t esize, const U128& a, size_t index) {
ASSERT_MSG(esize * index < 128, "Invalid index");
switch (esize) {
case 8:
return Inst<U128>(Opcode::VectorBroadcastElement8, a, u8(index));
case 16:
return Inst<U128>(Opcode::VectorBroadcastElement16, a, u8(index));
case 32:
return Inst<U128>(Opcode::VectorBroadcastElement32, a, u8(index));
case 64:
return Inst<U128>(Opcode::VectorBroadcastElement64, a, u8(index));
}
UNREACHABLE();
}
U128 IREmitter::VectorCountLeadingZeros(size_t esize, const U128& a) { U128 IREmitter::VectorCountLeadingZeros(size_t esize, const U128& a) {
switch (esize) { switch (esize) {
case 8: case 8:

View File

@ -144,6 +144,7 @@ public:
U32U64 UnsignedDiv(const U32U64& a, const U32U64& b); U32U64 UnsignedDiv(const U32U64& a, const U32U64& b);
U32U64 SignedDiv(const U32U64& a, const U32U64& b); U32U64 SignedDiv(const U32U64& a, const U32U64& b);
U32U64 And(const U32U64& a, const U32U64& b); U32U64 And(const U32U64& a, const U32U64& b);
U32U64 AndNot(const U32U64& a, const U32U64& b);
U32U64 Eor(const U32U64& a, const U32U64& b); U32U64 Eor(const U32U64& a, const U32U64& b);
U32U64 Or(const U32U64& a, const U32U64& b); U32U64 Or(const U32U64& a, const U32U64& b);
U32U64 Not(const U32U64& a); U32U64 Not(const U32U64& a);
@ -240,10 +241,13 @@ public:
U128 VectorAbs(size_t esize, const U128& a); U128 VectorAbs(size_t esize, const U128& a);
U128 VectorAdd(size_t esize, const U128& a, const U128& b); U128 VectorAdd(size_t esize, const U128& a, const U128& b);
U128 VectorAnd(const U128& a, const U128& b); U128 VectorAnd(const U128& a, const U128& b);
U128 VectorAndNot(const U128& a, const U128& b);
U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount); U128 VectorArithmeticShiftRight(size_t esize, const U128& a, u8 shift_amount);
U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b); U128 VectorArithmeticVShift(size_t esize, const U128& a, const U128& b);
U128 VectorBroadcast(size_t esize, const UAny& a); U128 VectorBroadcast(size_t esize, const UAny& a);
U128 VectorBroadcastLower(size_t esize, const UAny& a); U128 VectorBroadcastLower(size_t esize, const UAny& a);
U128 VectorBroadcastElement(size_t esize, const U128& a, size_t index);
U128 VectorBroadcastElementLower(size_t esize, const U128& a, size_t index);
U128 VectorCountLeadingZeros(size_t esize, const U128& a); U128 VectorCountLeadingZeros(size_t esize, const U128& a);
U128 VectorEor(const U128& a, const U128& b); U128 VectorEor(const U128& a, const U128& b);
U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b); U128 VectorDeinterleaveEven(size_t esize, const U128& a, const U128& b);

View File

@ -155,10 +155,7 @@ bool Inst::IsMemoryReadOrWrite() const {
bool Inst::ReadsFromCPSR() const { bool Inst::ReadsFromCPSR() const {
switch (op) { switch (op) {
case Opcode::A32GetCpsr: case Opcode::A32GetCpsr:
case Opcode::A32GetNFlag:
case Opcode::A32GetZFlag:
case Opcode::A32GetCFlag: case Opcode::A32GetCFlag:
case Opcode::A32GetVFlag:
case Opcode::A32GetGEFlags: case Opcode::A32GetGEFlags:
case Opcode::A32UpdateUpperLocationDescriptor: case Opcode::A32UpdateUpperLocationDescriptor:
case Opcode::A64GetCFlag: case Opcode::A64GetCFlag:
@ -566,6 +563,8 @@ bool Inst::MayGetNZCVFromOp() const {
case Opcode::Sub64: case Opcode::Sub64:
case Opcode::And32: case Opcode::And32:
case Opcode::And64: case Opcode::And64:
case Opcode::AndNot32:
case Opcode::AndNot64:
case Opcode::Eor32: case Opcode::Eor32:
case Opcode::Eor64: case Opcode::Eor64:
case Opcode::Or32: case Opcode::Or32:

View File

@ -1,3 +1,5 @@
// clang-format off
// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ... // opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ...
OPCODE(Void, Void, ) OPCODE(Void, Void, )
@ -20,13 +22,10 @@ A32OPC(SetCpsr, Void, U32
A32OPC(SetCpsrNZCV, Void, NZCV ) A32OPC(SetCpsrNZCV, Void, NZCV )
A32OPC(SetCpsrNZCVRaw, Void, U32 ) A32OPC(SetCpsrNZCVRaw, Void, U32 )
A32OPC(SetCpsrNZCVQ, Void, U32 ) A32OPC(SetCpsrNZCVQ, Void, U32 )
A32OPC(GetNFlag, U1, )
A32OPC(SetNFlag, Void, U1 ) A32OPC(SetNFlag, Void, U1 )
A32OPC(GetZFlag, U1, )
A32OPC(SetZFlag, Void, U1 ) A32OPC(SetZFlag, Void, U1 )
A32OPC(GetCFlag, U1, ) A32OPC(GetCFlag, U1, )
A32OPC(SetCFlag, Void, U1 ) A32OPC(SetCFlag, Void, U1 )
A32OPC(GetVFlag, U1, )
A32OPC(SetVFlag, Void, U1 ) A32OPC(SetVFlag, Void, U1 )
A32OPC(OrQFlag, Void, U1 ) A32OPC(OrQFlag, Void, U1 )
A32OPC(GetGEFlags, U32, ) A32OPC(GetGEFlags, U32, )
@ -141,6 +140,8 @@ OPCODE(SignedDiv32, U32, U32,
OPCODE(SignedDiv64, U64, U64, U64 ) OPCODE(SignedDiv64, U64, U64, U64 )
OPCODE(And32, U32, U32, U32 ) OPCODE(And32, U32, U32, U32 )
OPCODE(And64, U64, U64, U64 ) OPCODE(And64, U64, U64, U64 )
OPCODE(AndNot32, U32, U32, U32 )
OPCODE(AndNot64, U64, U64, U64 )
OPCODE(Eor32, U32, U32, U32 ) OPCODE(Eor32, U32, U32, U32 )
OPCODE(Eor64, U64, U64, U64 ) OPCODE(Eor64, U64, U64, U64 )
OPCODE(Or32, U32, U32, U32 ) OPCODE(Or32, U32, U32, U32 )
@ -289,6 +290,7 @@ OPCODE(VectorAdd16, U128, U128
OPCODE(VectorAdd32, U128, U128, U128 ) OPCODE(VectorAdd32, U128, U128, U128 )
OPCODE(VectorAdd64, U128, U128, U128 ) OPCODE(VectorAdd64, U128, U128, U128 )
OPCODE(VectorAnd, U128, U128, U128 ) OPCODE(VectorAnd, U128, U128, U128 )
OPCODE(VectorAndNot, U128, U128, U128 )
OPCODE(VectorArithmeticShiftRight8, U128, U128, U8 ) OPCODE(VectorArithmeticShiftRight8, U128, U128, U8 )
OPCODE(VectorArithmeticShiftRight16, U128, U128, U8 ) OPCODE(VectorArithmeticShiftRight16, U128, U128, U8 )
OPCODE(VectorArithmeticShiftRight32, U128, U128, U8 ) OPCODE(VectorArithmeticShiftRight32, U128, U128, U8 )
@ -304,6 +306,13 @@ OPCODE(VectorBroadcast8, U128, U8
OPCODE(VectorBroadcast16, U128, U16 ) OPCODE(VectorBroadcast16, U128, U16 )
OPCODE(VectorBroadcast32, U128, U32 ) OPCODE(VectorBroadcast32, U128, U32 )
OPCODE(VectorBroadcast64, U128, U64 ) OPCODE(VectorBroadcast64, U128, U64 )
OPCODE(VectorBroadcastElementLower8, U128, U128, U8 )
OPCODE(VectorBroadcastElementLower16, U128, U128, U8 )
OPCODE(VectorBroadcastElementLower32, U128, U128, U8 )
OPCODE(VectorBroadcastElement8, U128, U128, U8 )
OPCODE(VectorBroadcastElement16, U128, U128, U8 )
OPCODE(VectorBroadcastElement32, U128, U128, U8 )
OPCODE(VectorBroadcastElement64, U128, U128, U8 )
OPCODE(VectorCountLeadingZeros8, U128, U128 ) OPCODE(VectorCountLeadingZeros8, U128, U128 )
OPCODE(VectorCountLeadingZeros16, U128, U128 ) OPCODE(VectorCountLeadingZeros16, U128, U128 )
OPCODE(VectorCountLeadingZeros32, U128, U128 ) OPCODE(VectorCountLeadingZeros32, U128, U128 )
@ -718,3 +727,5 @@ A32OPC(CoprocGetOneWord, U32, Copr
A32OPC(CoprocGetTwoWords, U64, CoprocInfo ) A32OPC(CoprocGetTwoWords, U64, CoprocInfo )
A32OPC(CoprocLoadWords, Void, CoprocInfo, U32 ) A32OPC(CoprocLoadWords, Void, CoprocInfo, U32 )
A32OPC(CoprocStoreWords, Void, CoprocInfo, U32 ) A32OPC(CoprocStoreWords, Void, CoprocInfo, U32 )
// clang-format on

View File

@ -170,18 +170,10 @@ void A32GetSetElimination(IR::Block& block) {
do_set(cpsr_info.n, inst->GetArg(0), inst); do_set(cpsr_info.n, inst->GetArg(0), inst);
break; break;
} }
case IR::Opcode::A32GetNFlag: {
do_get(cpsr_info.n, inst);
break;
}
case IR::Opcode::A32SetZFlag: { case IR::Opcode::A32SetZFlag: {
do_set(cpsr_info.z, inst->GetArg(0), inst); do_set(cpsr_info.z, inst->GetArg(0), inst);
break; break;
} }
case IR::Opcode::A32GetZFlag: {
do_get(cpsr_info.z, inst);
break;
}
case IR::Opcode::A32SetCFlag: { case IR::Opcode::A32SetCFlag: {
do_set(cpsr_info.c, inst->GetArg(0), inst); do_set(cpsr_info.c, inst->GetArg(0), inst);
break; break;
@ -194,10 +186,6 @@ void A32GetSetElimination(IR::Block& block) {
do_set(cpsr_info.v, inst->GetArg(0), inst); do_set(cpsr_info.v, inst->GetArg(0), inst);
break; break;
} }
case IR::Opcode::A32GetVFlag: {
do_get(cpsr_info.v, inst);
break;
}
case IR::Opcode::A32SetGEFlags: { case IR::Opcode::A32SetGEFlags: {
do_set(cpsr_info.ge, inst->GetArg(0), inst); do_set(cpsr_info.ge, inst->GetArg(0), inst);
break; break;

View File

@ -10,7 +10,7 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../fuzz_util.h" #include "../fuzz_util.h"
#include "../rand_int.h" #include "../rand_int.h"
@ -372,7 +372,7 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit,
fmt::print("\n"); fmt::print("\n");
fmt::print("x86_64:\n"); fmt::print("x86_64:\n");
fmt::print("{}\n", jit.Disassemble()); jit.DumpDisassembly();
fmt::print("Interrupts:\n"); fmt::print("Interrupts:\n");
for (const auto& i : uni_env.interrupts) { for (const auto& i : uni_env.interrupts) {

View File

@ -12,7 +12,7 @@
#include <string_view> #include <string_view>
#include <tuple> #include <tuple>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../rand_int.h" #include "../rand_int.h"
#include "../unicorn_emu/a32_unicorn.h" #include "../unicorn_emu/a32_unicorn.h"
@ -183,7 +183,8 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
Optimization::VerificationPass(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%s", jit.Disassemble().c_str()); printf("\n\nx86_64:\n");
jit.DumpDisassembly();
num_insts += ir_block.CycleCount(); num_insts += ir_block.CycleCount();
} }

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include "dynarmic/frontend/A32/disassembler/disassembler.h" #include "dynarmic/frontend/A32/disassembler/disassembler.h"

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include "./testenv.h" #include "./testenv.h"
#include "dynarmic/frontend/A32/location_descriptor.h" #include "dynarmic/frontend/A32/location_descriptor.h"

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include "./testenv.h" #include "./testenv.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include "./testenv.h" #include "./testenv.h"
#include "dynarmic/common/fp/fpsr.h" #include "dynarmic/common/fp/fpsr.h"
@ -675,6 +675,57 @@ TEST_CASE("A64: FMADD", "[a64]") {
REQUIRE(jit.GetVector(10) == Vector{0x3f059921bf0dbfff, 0x0000000000000000}); REQUIRE(jit.GetVector(10) == Vector{0x3f059921bf0dbfff, 0x0000000000000000});
} }
TEST_CASE("A64: FMLA.4S(lane)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4f8f11c0); // FMLA.4S V0, V14, V15[0]
env.code_mem.emplace_back(0x4faf11c1); // FMLA.4S V1, V14, V15[1]
env.code_mem.emplace_back(0x4f8f19c2); // FMLA.4S V2, V14, V15[2]
env.code_mem.emplace_back(0x4faf19c3); // FMLA.4S V3, V14, V15[3]
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(0, {0x3ff00000'3ff00000, 0x00000000'00000000});
jit.SetVector(1, {0x3ff00000'3ff00000, 0x00000000'00000000});
jit.SetVector(2, {0x3ff00000'3ff00000, 0x00000000'00000000});
jit.SetVector(3, {0x3ff00000'3ff00000, 0x00000000'00000000});
jit.SetVector(14, {0x3ff00000'3ff00000, 0x3ff00000'3ff00000});
jit.SetVector(15, {0x3ff00000'40000000, 0x40400000'40800000});
env.ticks_left = 5;
jit.Run();
REQUIRE(jit.GetVector(0) == Vector{0x40b4000040b40000, 0x4070000040700000});
REQUIRE(jit.GetVector(1) == Vector{0x40ac800040ac8000, 0x4061000040610000});
REQUIRE(jit.GetVector(2) == Vector{0x4116000041160000, 0x40f0000040f00000});
REQUIRE(jit.GetVector(3) == Vector{0x40f0000040f00000, 0x40b4000040b40000});
}
TEST_CASE("A64: FMUL.4S(lane)", "[a64]") {
A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}};
env.code_mem.emplace_back(0x4f8f91c0); // FMUL.4S V0, V14, V15[0]
env.code_mem.emplace_back(0x4faf91c1); // FMUL.4S V1, V14, V15[1]
env.code_mem.emplace_back(0x4f8f99c2); // FMUL.4S V2, V14, V15[2]
env.code_mem.emplace_back(0x4faf99c3); // FMUL.4S V3, V14, V15[3]
env.code_mem.emplace_back(0x14000000); // B .
jit.SetPC(0);
jit.SetVector(14, {0x3ff00000'3ff00000, 0x3ff00000'3ff00000});
jit.SetVector(15, {0x3ff00000'40000000, 0x40400000'40800000});
env.ticks_left = 5;
jit.Run();
REQUIRE(jit.GetVector(0) == Vector{0x4070000040700000, 0x4070000040700000});
REQUIRE(jit.GetVector(1) == Vector{0x4061000040610000, 0x4061000040610000});
REQUIRE(jit.GetVector(2) == Vector{0x40f0000040f00000, 0x40f0000040f00000});
REQUIRE(jit.GetVector(3) == Vector{0x40b4000040b40000, 0x40b4000040b40000});
}
TEST_CASE("A64: FMLA.4S (denormal)", "[a64]") { TEST_CASE("A64: FMLA.4S (denormal)", "[a64]") {
A64TestEnv env; A64TestEnv env;
A64::Jit jit{A64::UserConfig{&env}}; A64::Jit jit{A64::UserConfig{&env}};

View File

@ -8,7 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../fuzz_util.h" #include "../fuzz_util.h"
#include "../rand_int.h" #include "../rand_int.h"
@ -232,7 +232,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
} }
const auto uni_vecs = uni.GetVectors(); const auto uni_vecs = uni.GetVectors();
for (size_t i = 0; i < vecs.size(); ++i) { for (size_t i = 0; i < vecs.size(); ++i) {
fmt::print("{:3s}: {}{} {}{} {}\n", A64::VecToString(static_cast<A64::Vec>(i)), fmt::print("{:3s}: {:016x}{:016x} {:016x}{:016x} {}\n", A64::VecToString(static_cast<A64::Vec>(i)),
uni_vecs[i][1], uni_vecs[i][0], uni_vecs[i][1], uni_vecs[i][0],
jit.GetVectors()[i][1], jit.GetVectors()[i][0], jit.GetVectors()[i][1], jit.GetVectors()[i][0],
uni_vecs[i] != jit.GetVectors()[i] ? "*" : ""); uni_vecs[i] != jit.GetVectors()[i] ? "*" : "");
@ -276,7 +276,7 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
fmt::print("{}\n", IR::DumpBlock(ir_block)); fmt::print("{}\n", IR::DumpBlock(ir_block));
fmt::print("x86_64:\n"); fmt::print("x86_64:\n");
fmt::print("{}\n", jit.Disassemble()); jit.DumpDisassembly();
fmt::print("Interrupts:\n"); fmt::print("Interrupts:\n");
for (auto& i : uni_env.interrupts) { for (auto& i : uni_env.interrupts) {

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include "./testenv.h" #include "./testenv.h"
#include "dynarmic/interface/A64/a64.h" #include "dynarmic/interface/A64/a64.h"

View File

@ -5,7 +5,7 @@
#include <array> #include <array>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../rand_int.h" #include "../rand_int.h"
#include "../unicorn_emu/a64_unicorn.h" #include "../unicorn_emu/a64_unicorn.h"

View File

@ -6,8 +6,8 @@
#include <array> #include <array>
#include <utility> #include <utility>
#include <catch.hpp> #include <catch2/catch.hpp>
#include <xbyak_util.h> #include <xbyak/xbyak_util.h>
TEST_CASE("Host CPU supports", "[a64]") { TEST_CASE("Host CPU supports", "[a64]") {
Xbyak::util::Cpu cpu_info; Xbyak::util::Cpu cpu_info;

View File

@ -7,7 +7,7 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/frontend/A32/decoder/asimd.h" #include "dynarmic/frontend/A32/decoder/asimd.h"

View File

@ -6,7 +6,7 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../rand_int.h" #include "../rand_int.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -6,7 +6,7 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../rand_int.h" #include "../rand_int.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -6,7 +6,7 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <catch.hpp> #include <catch2/catch.hpp>
#include "../rand_int.h" #include "../rand_int.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -4,4 +4,4 @@
*/ */
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch.hpp> #include <catch2/catch.hpp>

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#include <catch.hpp> #include <catch2/catch.hpp>
#include <fmt/printf.h> #include <fmt/printf.h>
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"

View File

@ -2,13 +2,9 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <climits> #include <climits>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -16,28 +12,173 @@
#include <windows.h> // For OutputDebugStringW #include <windows.h> // For OutputDebugStringW
#endif #endif
#include "common/assert.h"
#include "common/fs/file.h" #include "common/fs/file.h"
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/literals.h" #include "common/literals.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/logging/text_formatter.h" #include "common/logging/text_formatter.h"
#include "common/settings.h" #include "common/settings.h"
#ifdef _WIN32
#include "common/string_util.h" #include "common/string_util.h"
#endif
#include "common/threadsafe_queue.h" #include "common/threadsafe_queue.h"
namespace Common::Log { namespace Common::Log {
namespace {
/**
* Interface for logging backends.
*/
class Backend {
public:
virtual ~Backend() = default;
virtual void Write(const Entry& entry) = 0;
virtual void EnableForStacktrace() = 0;
virtual void Flush() = 0;
};
/**
* Backend that writes to stderr and with color
*/
class ColorConsoleBackend final : public Backend {
public:
explicit ColorConsoleBackend() = default;
~ColorConsoleBackend() override = default;
void Write(const Entry& entry) override {
if (enabled.load(std::memory_order_relaxed)) {
PrintColoredMessage(entry);
}
}
void Flush() override {
// stderr shouldn't be buffered
}
void EnableForStacktrace() override {
enabled = true;
}
void SetEnabled(bool enabled_) {
enabled = enabled_;
}
private:
std::atomic_bool enabled{false};
};
/**
* Backend that writes to a file passed into the constructor
*/
class FileBackend final : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename));
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
FS::FileType::TextFile);
}
~FileBackend() override = default;
void Write(const Entry& entry) override {
if (!enabled) {
return;
}
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) {
// Stop writing after the write limit is exceeded.
// Don't close the file so we can print a stacktrace if necessary
enabled = false;
}
file->Flush();
}
}
void Flush() override {
file->Flush();
}
void EnableForStacktrace() override {
enabled = true;
bytes_written = 0;
}
private:
std::unique_ptr<FS::IOFile> file;
bool enabled = true;
std::size_t bytes_written = 0;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend final : public Backend {
public:
explicit DebuggerBackend() = default;
~DebuggerBackend() override = default;
void Write(const Entry& entry) override {
#ifdef _WIN32
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
}
void Flush() override {}
void EnableForStacktrace() override {}
};
bool initialization_in_progress_suppress_logging = false;
/** /**
* Static state as a singleton. * Static state as a singleton.
*/ */
class Impl { class Impl {
public: public:
static Impl& Instance() { static Impl& Instance() {
static Impl backend; if (!instance) {
return backend; abort();
}
return *instance;
}
static void Initialize() {
if (instance) {
abort();
}
using namespace Common::FS;
initialization_in_progress_suppress_logging = true;
const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
void(CreateDir(log_dir));
Filter filter;
filter.ParseFilterString(Settings::values.log_filter.GetValue());
instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
Deleter);
initialization_in_progress_suppress_logging = false;
} }
Impl(const Impl&) = delete; Impl(const Impl&) = delete;
@ -46,74 +187,54 @@ public:
Impl(Impl&&) = delete; Impl(Impl&&) = delete;
Impl& operator=(Impl&&) = delete; Impl& operator=(Impl&&) = delete;
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, std::string message) {
message_queue.Push(
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
}
void AddBackend(std::unique_ptr<Backend> backend) {
std::lock_guard lock{writing_mutex};
backends.push_back(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
std::lock_guard lock{writing_mutex};
std::erase_if(backends, [&backend_name](const auto& backend) {
return backend_name == backend->GetName();
});
}
const Filter& GetGlobalFilter() const {
return filter;
}
void SetGlobalFilter(const Filter& f) { void SetGlobalFilter(const Filter& f) {
filter = f; filter = f;
} }
Backend* GetBackend(std::string_view backend_name) { void SetColorConsoleBackendEnabled(bool enabled) {
const auto it = color_console_backend.SetEnabled(enabled);
std::find_if(backends.begin(), backends.end(), }
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
if (it == backends.end()) void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
return nullptr; const char* function, std::string message) {
return it->get(); if (!filter.CheckMessage(log_class, log_level))
return;
const Entry& entry =
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
message_queue.Push(entry);
} }
private: private:
Impl() { Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
backend_thread = std::thread([&] { : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
Entry entry; Common::SetCurrentThreadName("yuzu:Log");
auto write_logs = [&](Entry& e) { Entry entry;
std::lock_guard lock{writing_mutex}; const auto write_logs = [this, &entry]() {
for (const auto& backend : backends) { ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
backend->Write(e); };
} while (true) {
}; entry = message_queue.PopWait();
while (true) { if (entry.final_entry) {
entry = message_queue.PopWait(); break;
if (entry.final_entry) { }
break; write_logs();
} }
write_logs(entry); // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
} // case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a while (max_logs_to_write-- && message_queue.Pop(entry)) {
// case where a system is repeatedly spamming logs even on close. write_logs();
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; }
int logs_written = 0; })} {}
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
write_logs(entry);
}
});
}
~Impl() { ~Impl() {
Entry entry; StopBackendThread();
entry.final_entry = true; }
message_queue.Push(entry);
void StopBackendThread() {
Entry stop_entry{};
stop_entry.final_entry = true;
message_queue.Push(stop_entry);
backend_thread.join(); backend_thread.join();
} }
@ -135,100 +256,51 @@ private:
}; };
} }
std::mutex writing_mutex; void ForEachBackend(auto lambda) {
std::thread backend_thread; lambda(static_cast<Backend&>(debugger_backend));
std::vector<std::unique_ptr<Backend>> backends; lambda(static_cast<Backend&>(color_console_backend));
MPSCQueue<Entry> message_queue; lambda(static_cast<Backend&>(file_backend));
}
static void Deleter(Impl* ptr) {
delete ptr;
}
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
Filter filter; Filter filter;
DebuggerBackend debugger_backend{};
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
std::thread backend_thread;
MPSCQueue<Entry> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
}; };
} // namespace
ConsoleBackend::~ConsoleBackend() = default; void Initialize() {
Impl::Initialize();
void ConsoleBackend::Write(const Entry& entry) {
PrintMessage(entry);
} }
ColorConsoleBackend::~ColorConsoleBackend() = default; void DisableLoggingInTests() {
initialization_in_progress_suppress_logging = true;
void ColorConsoleBackend::Write(const Entry& entry) {
PrintColoredMessage(entry);
}
FileBackend::FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
FS::RemoveFile(old_filename);
void(FS::RenameFile(filename, old_filename));
file =
std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
FileBackend::~FileBackend() = default;
void FileBackend::Write(const Entry& entry) {
if (!file->IsOpen()) {
return;
}
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
const bool write_limit_exceeded =
bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
(bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
// Close the file after the write limit is exceeded.
if (write_limit_exceeded) {
file->Close();
return;
}
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file->Flush();
}
}
DebuggerBackend::~DebuggerBackend() = default;
void DebuggerBackend::Write(const Entry& entry) {
#ifdef _WIN32
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
} }
void SetGlobalFilter(const Filter& filter) { void SetGlobalFilter(const Filter& filter) {
Impl::Instance().SetGlobalFilter(filter); Impl::Instance().SetGlobalFilter(filter);
} }
void AddBackend(std::unique_ptr<Backend> backend) { void SetColorConsoleBackendEnabled(bool enabled) {
Impl::Instance().AddBackend(std::move(backend)); Impl::Instance().SetColorConsoleBackendEnabled(enabled);
}
void RemoveBackend(std::string_view backend_name) {
Impl::Instance().RemoveBackend(backend_name);
}
Backend* GetBackend(std::string_view backend_name) {
return Impl::Instance().GetBackend(backend_name);
} }
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format, unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) { const fmt::format_args& args) {
auto& instance = Impl::Instance(); if (!initialization_in_progress_suppress_logging) {
const auto& filter = instance.GetGlobalFilter(); Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
if (!filter.CheckMessage(log_class, log_level)) fmt::vformat(format, args));
return; }
instance.PushEntry(log_class, log_level, filename, line_num, function,
fmt::vformat(format, args));
} }
} // namespace Common::Log } // namespace Common::Log

View File

@ -5,120 +5,21 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Common::FS {
class IOFile;
}
namespace Common::Log { namespace Common::Log {
class Filter; class Filter;
/** /// Initializes the logging system. This should be the first thing called in main.
* Interface for logging backends. As loggers can be created and removed at runtime, this can be void Initialize();
* used by a frontend for adding a custom logging backend as needed
*/
class Backend {
public:
virtual ~Backend() = default;
virtual void SetFilter(const Filter& new_filter) { void DisableLoggingInTests();
filter = new_filter;
}
virtual const char* GetName() const = 0;
virtual void Write(const Entry& entry) = 0;
private:
Filter filter;
};
/** /**
* Backend that writes to stderr without any color commands * The global filter will prevent any messages from even being processed if they are filtered.
*/
class ConsoleBackend : public Backend {
public:
~ConsoleBackend() override;
static const char* Name() {
return "console";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
/**
* Backend that writes to stderr and with color
*/
class ColorConsoleBackend : public Backend {
public:
~ColorConsoleBackend() override;
static const char* Name() {
return "color_console";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
/**
* Backend that writes to a file passed into the constructor
*/
class FileBackend : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename);
~FileBackend() override;
static const char* Name() {
return "file";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
private:
std::unique_ptr<FS::IOFile> file;
std::size_t bytes_written = 0;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend : public Backend {
public:
~DebuggerBackend() override;
static const char* Name() {
return "debugger";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(std::string_view backend_name);
/**
* The global filter will prevent any messages from even being processed if they are filtered. Each
* backend can have a filter, but if the level is lower than the global filter, the backend will
* never get the message
*/ */
void SetGlobalFilter(const Filter& filter); void SetGlobalFilter(const Filter& filter);
} // namespace Common::Log
void SetColorConsoleBackendEnabled(bool enabled);
} // namespace Common::Log

View File

@ -4,175 +4,242 @@
#pragma once #pragma once
// a simple lockless thread-safe,
// single reader, single writer queue
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <cstddef> #include <iostream>
#include <mutex> #include <mutex>
#include <utility> #include <optional>
namespace Common { namespace Common {
/// a more foolproof multiple reader, multiple writer queue
template <typename T> template <typename T>
class SPSCQueue { class MPMCQueue {
#define ABORT() \
do { \
std::cerr << __FILE__ " ERR " << __LINE__ << std::endl; \
abort(); \
} while (0)
public: public:
SPSCQueue() { ~MPMCQueue() {
write_ptr = read_ptr = new ElementPtr(); Clear();
} if (waiting || head || tail) {
~SPSCQueue() { // Remove all the ABORT() after 1 month merged without problems
// this will empty out the whole queue ABORT();
delete read_ptr; }
}
[[nodiscard]] std::size_t Size() const {
return size.load();
}
[[nodiscard]] bool Empty() const {
return Size() == 0;
}
[[nodiscard]] T& Front() const {
return read_ptr->current;
} }
template <typename Arg> template <typename Arg>
void Push(Arg&& t) { void Push(Arg&& t) {
// create the element, add it to the queue Node* const node = new Node(std::forward<Arg>(t));
write_ptr->current = std::forward<Arg>(t); if (!node || node == PLACEHOLDER) {
// set the next pointer to a new element ptr ABORT();
// then advance the write pointer }
ElementPtr* new_ptr = new ElementPtr(); while (true) {
write_ptr->next.store(new_ptr, std::memory_order_release); if (Node* const previous = tail.load(ACQUIRE)) {
write_ptr = new_ptr; if (Node* exchange = nullptr;
++size; !previous->next.compare_exchange_weak(exchange, node, ACQ_REL)) {
continue;
// cv_mutex must be held or else there will be a missed wakeup if the other thread is in the }
// line before cv.wait if (tail.exchange(node, ACQ_REL) != previous) {
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. ABORT();
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. }
std::lock_guard lock{cv_mutex}; } else {
cv.notify_one(); if (Node* exchange = nullptr;
} !tail.compare_exchange_weak(exchange, node, ACQ_REL)) {
continue;
void Pop() { }
--size; for (Node* exchange = nullptr;
!head.compare_exchange_weak(exchange, node, ACQ_REL);)
ElementPtr* tmpptr = read_ptr; ;
// advance the read pointer }
read_ptr = tmpptr->next.load(); break;
// set the next element to nullptr to stop the recursive deletion }
tmpptr->next.store(nullptr); if (waiting.load(ACQUIRE)) {
delete tmpptr; // this also deletes the element std::lock_guard lock{mutex};
} condition.notify_one();
bool Pop(T& t) {
if (Empty())
return false;
--size;
ElementPtr* tmpptr = read_ptr;
read_ptr = tmpptr->next.load(std::memory_order_acquire);
t = std::move(tmpptr->current);
tmpptr->next.store(nullptr);
delete tmpptr;
return true;
}
void Wait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
cv.wait(lock, [this]() { return !Empty(); });
} }
} }
bool Pop(T& t) {
return PopImpl<false>(t);
}
T PopWait() { T PopWait() {
Wait();
T t; T t;
Pop(t); if (!PopImpl<true>(t)) {
ABORT();
}
return t; return t;
} }
// not thread-safe void Wait() {
if (head.load(ACQUIRE)) {
return;
}
static_cast<void>(waiting.fetch_add(1, ACQ_REL));
std::unique_lock lock{mutex};
while (true) {
if (head.load(ACQUIRE)) {
break;
}
condition.wait(lock);
}
if (!waiting.fetch_sub(1, ACQ_REL)) {
ABORT();
}
}
void Clear() { void Clear() {
size.store(0); while (true) {
delete read_ptr; Node* const last = tail.load(ACQUIRE);
write_ptr = read_ptr = new ElementPtr(); if (!last) {
return;
}
if (Node* exchange = nullptr;
!last->next.compare_exchange_weak(exchange, PLACEHOLDER, ACQ_REL)) {
continue;
}
if (tail.exchange(nullptr, ACQ_REL) != last) {
ABORT();
}
Node* node = head.exchange(nullptr, ACQ_REL);
while (node && node != PLACEHOLDER) {
Node* next = node->next.load(ACQUIRE);
delete node;
node = next;
}
return;
}
} }
private: private:
// stores a pointer to element template <bool WAIT>
// and a pointer to the next ElementPtr bool PopImpl(T& t) {
class ElementPtr { std::optional<std::unique_lock<std::mutex>> lock{std::nullopt};
public: while (true) {
ElementPtr() {} Node* const node = head.load(ACQUIRE);
~ElementPtr() { if (!node) {
ElementPtr* next_ptr = next.load(); if constexpr (!WAIT) {
return false;
if (next_ptr) }
delete next_ptr; if (!lock) {
static_cast<void>(waiting.fetch_add(1, ACQ_REL));
lock = std::unique_lock{mutex};
continue;
}
condition.wait(*lock);
continue;
}
Node* const next = node->next.load(ACQUIRE);
if (next) {
if (next == PLACEHOLDER) {
continue;
}
if (Node* exchange = node; !head.compare_exchange_weak(exchange, next, ACQ_REL)) {
continue;
}
} else {
if (Node* exchange = nullptr;
!node->next.compare_exchange_weak(exchange, PLACEHOLDER, ACQ_REL)) {
continue;
}
if (tail.exchange(nullptr, ACQ_REL) != node) {
ABORT();
}
if (head.exchange(nullptr, ACQ_REL) != node) {
ABORT();
}
}
t = std::move(node->value);
delete node;
if (lock) {
if (!waiting.fetch_sub(1, ACQ_REL)) {
ABORT();
}
}
return true;
} }
}
T current; struct Node {
std::atomic<ElementPtr*> next{nullptr}; template <typename Arg>
explicit Node(Arg&& t) : value{std::forward<Arg>(t)} {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const T value;
std::atomic<Node*> next{nullptr};
}; };
ElementPtr* write_ptr; // We only need to avoid SEQ_CST on X86
ElementPtr* read_ptr; // We can add RELAXED later if we port to ARM and it's too slow
std::atomic_size_t size{0}; static constexpr auto ACQUIRE = std::memory_order_acquire;
std::mutex cv_mutex; static constexpr auto ACQ_REL = std::memory_order_acq_rel;
std::condition_variable cv; static inline const auto PLACEHOLDER = reinterpret_cast<Node*>(1);
std::atomic<Node*> head{nullptr};
std::atomic<Node*> tail{nullptr};
std::atomic_size_t waiting{0};
std::condition_variable condition{};
std::mutex mutex{};
#undef ABORT
}; };
// a simple thread-safe, /// a simple lockless thread-safe,
// single reader, multiple writer queue /// single reader, single writer queue
template <typename T> template <typename T>
class MPSCQueue { class /*[[deprecated("Transition to MPMCQueue")]]*/ SPSCQueue {
public: public:
[[nodiscard]] std::size_t Size() const {
return spsc_queue.Size();
}
[[nodiscard]] bool Empty() const {
return spsc_queue.Empty();
}
[[nodiscard]] T& Front() const {
return spsc_queue.Front();
}
template <typename Arg> template <typename Arg>
void Push(Arg&& t) { void Push(Arg&& t) {
std::lock_guard lock{write_lock}; queue.Push(std::forward<Arg>(t));
spsc_queue.Push(t);
}
void Pop() {
return spsc_queue.Pop();
} }
bool Pop(T& t) { bool Pop(T& t) {
return spsc_queue.Pop(t); return queue.Pop(t);
} }
void Wait() { void Wait() {
spsc_queue.Wait(); queue.Wait();
} }
T PopWait() { T PopWait() {
return spsc_queue.PopWait(); return queue.PopWait();
} }
// not thread-safe
void Clear() { void Clear() {
spsc_queue.Clear(); queue.Clear();
} }
private: private:
SPSCQueue<T> spsc_queue; MPMCQueue<T> queue{};
std::mutex write_lock; };
/// a simple thread-safe,
/// single reader, multiple writer queue
template <typename T>
class /*[[deprecated("Transition to MPMCQueue")]]*/ MPSCQueue {
public:
template <typename Arg>
void Push(Arg&& t) {
queue.Push(std::forward<Arg>(t));
}
bool Pop(T& t) {
return queue.Pop(t);
}
T PopWait() {
return queue.PopWait();
}
private:
MPMCQueue<T> queue{};
}; };
} // namespace Common } // namespace Common

View File

@ -6,7 +6,7 @@
#include <bitset> #include <bitset>
#include <initializer_list> #include <initializer_list>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "common/assert.h" #include "common/assert.h"
namespace Common::X64 { namespace Common::X64 {

View File

@ -5,7 +5,7 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "common/x64/xbyak_abi.h" #include "common/x64/xbyak_abi.h"
namespace Common::X64 { namespace Common::X64 {

View File

@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
} // Anonymous namespace } // Anonymous namespace
/*static*/ System System::s_instance;
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) { const std::string& path) {
// To account for split 00+01+etc files. // To account for split 00+01+etc files.
@ -425,6 +423,13 @@ struct System::Impl {
System::System() : impl{std::make_unique<Impl>(*this)} {} System::System() : impl{std::make_unique<Impl>(*this)} {}
System::~System() = default; System::~System() = default;
void System::InitializeGlobalInstance() {
if (s_instance) {
abort();
}
s_instance = std::unique_ptr<System>(new System);
}
CpuManager& System::GetCpuManager() { CpuManager& System::GetCpuManager() {
return impl->cpu_manager; return impl->cpu_manager;
} }

View File

@ -121,9 +121,14 @@ public:
* @returns Reference to the instance of the System singleton class. * @returns Reference to the instance of the System singleton class.
*/ */
[[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
return s_instance; if (!s_instance) {
abort();
}
return *s_instance;
} }
static void InitializeGlobalInstance();
/// Enumeration representing the return values of the System Initialize and Load process. /// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 { enum class ResultStatus : u32 {
Success, ///< Succeeded Success, ///< Succeeded
@ -393,7 +398,7 @@ private:
struct Impl; struct Impl;
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
static System s_instance; inline static std::unique_ptr<System> s_instance{};
}; };
} // namespace Core } // namespace Core

View File

@ -261,20 +261,23 @@ struct KernelCore::Impl {
current_process = process; current_process = process;
} }
/// Creates a new host thread ID, should only be called by GetHostThreadId static inline thread_local u32 host_thread_id = UINT32_MAX;
u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
if (core_id) { /// Gets the host thread ID for the caller, allocating a new one if this is the first time
u32 GetHostThreadId(std::size_t core_id) {
if (static_cast<s32>(host_thread_id) < 0) {
// The first for slots are reserved for CPU core threads // The first for slots are reserved for CPU core threads
ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES); ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
return static_cast<u32>(*core_id); host_thread_id = static_cast<u32>(core_id);
} else {
return next_host_thread_id++;
} }
return host_thread_id;
} }
/// Gets the host thread ID for the caller, allocating a new one if this is the first time /// Gets the host thread ID for the caller, allocating a new one if this is the first time
u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) { u32 GetHostThreadId() {
const thread_local auto host_thread_id{AllocateHostThreadId(core_id)}; if (static_cast<s32>(host_thread_id) < 0) {
host_thread_id = next_host_thread_id++;
}
return host_thread_id; return host_thread_id;
} }

View File

@ -11,6 +11,7 @@
#include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/network/network.h" #include "core/network/network.h"
#include "core/network/network_interface.h"
namespace Service::NIFM { namespace Service::NIFM {
@ -357,16 +358,10 @@ private:
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size."); "IpConfigInfo has incorrect size.");
auto ipv4 = Network::GetHostIPv4Address(); IpConfigInfo ip_config_info{
if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
const IpConfigInfo ip_config_info{
.ip_address_setting{ .ip_address_setting{
.is_automatic{true}, .is_automatic{true},
.current_address{*ipv4}, .current_address{0, 0, 0, 0},
.subnet_mask{255, 255, 255, 0}, .subnet_mask{255, 255, 255, 0},
.gateway{192, 168, 1, 1}, .gateway{192, 168, 1, 1},
}, },
@ -377,6 +372,19 @@ private:
}, },
}; };
const auto iface = Network::GetSelectedNetworkInterface();
if (iface) {
ip_config_info.ip_address_setting =
IpAddressSetting{.is_automatic{true},
.current_address{Network::TranslateIPv4(iface->ip_address)},
.subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
.gateway{Network::TranslateIPv4(iface->gateway)}};
} else {
LOG_ERROR(Service_NIFM,
"Couldn't get host network configuration info, using default values");
}
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushRaw<IpConfigInfo>(ip_config_info); rb.PushRaw<IpConfigInfo>(ip_config_info);

View File

@ -50,11 +50,6 @@ void Finalize() {
WSACleanup(); WSACleanup();
} }
constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b;
return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result; sockaddr_in result;
@ -141,12 +136,6 @@ void Initialize() {}
void Finalize() {} void Finalize() {}
constexpr IPv4Address TranslateIPv4(in_addr addr) {
const u32 bytes = addr.s_addr;
return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) { sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result; sockaddr_in result;

View File

@ -11,6 +11,12 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#ifdef _WIN32
#include <winsock2.h>
#elif YUZU_UNIX
#include <netinet/in.h>
#endif
namespace Network { namespace Network {
class Socket; class Socket;
@ -93,6 +99,19 @@ public:
~NetworkInstance(); ~NetworkInstance();
}; };
#ifdef _WIN32
constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b;
return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
}
#elif YUZU_UNIX
constexpr IPv4Address TranslateIPv4(in_addr addr) {
const u32 bytes = addr.s_addr;
return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
}
#endif
/// @brief Returns host's IPv4 address /// @brief Returns host's IPv4 address
/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
std::optional<IPv4Address> GetHostIPv4Address(); std::optional<IPv4Address> GetHostIPv4Address();

View File

@ -2,11 +2,15 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector> #include <vector>
#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/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/network/network_interface.h" #include "core/network/network_interface.h"
@ -29,8 +33,9 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
// retry up to 5 times // retry up to 5 times
for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, ret = GetAdaptersAddresses(
nullptr, adapter_addresses.data(), &buf_size); AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
nullptr, adapter_addresses.data(), &buf_size);
if (ret == ERROR_BUFFER_OVERFLOW) { if (ret == ERROR_BUFFER_OVERFLOW) {
adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
@ -57,9 +62,26 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
*current_address->FirstUnicastAddress->Address.lpSockaddr) *current_address->FirstUnicastAddress->Address.lpSockaddr)
.sin_addr; .sin_addr;
ULONG mask = 0;
if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
&mask) != NO_ERROR) {
LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
continue;
}
struct in_addr gateway = {.S_un{.S_addr{0}}};
if (current_address->FirstGatewayAddress != nullptr &&
current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
gateway = Common::BitCast<struct sockaddr_in>(
*current_address->FirstGatewayAddress->Address.lpSockaddr)
.sin_addr;
}
result.push_back(NetworkInterface{ result.push_back(NetworkInterface{
.name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
.ip_address{ip_addr}}); .ip_address{ip_addr},
.subnet_mask = in_addr{.S_un{.S_addr{mask}}},
.gateway = gateway});
} }
return result; return result;
@ -83,7 +105,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
} }
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) { if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
continue; continue;
} }
@ -95,9 +117,59 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
continue; continue;
} }
std::uint32_t gateway{0};
std::ifstream file{"/proc/net/route"};
if (file.is_open()) {
// ignore header
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
bool gateway_found = false;
for (std::string line; std::getline(file, line);) {
std::istringstream iss{line};
std::string iface_name{};
iss >> iface_name;
if (iface_name != ifa->ifa_name) {
continue;
}
iss >> std::hex;
std::uint32_t dest{0};
iss >> dest;
if (dest != 0) {
// not the default route
continue;
}
iss >> gateway;
std::uint16_t flags{0};
iss >> flags;
// flag RTF_GATEWAY (defined in <linux/route.h>)
if ((flags & 0x2) == 0) {
continue;
}
gateway_found = true;
break;
}
if (!gateway_found) {
gateway = 0;
}
} else {
LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
}
result.push_back(NetworkInterface{ result.push_back(NetworkInterface{
.name{ifa->ifa_name}, .name{ifa->ifa_name},
.ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}}); .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
.subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
.gateway{in_addr{.s_addr = gateway}}});
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
@ -107,4 +179,25 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif #endif
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
if (network_interfaces.size() == 0) {
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
return {};
}
const auto res =
std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
return iface.name == selected_network_interface;
});
if (res != network_interfaces.end()) {
return *res;
} else {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return {};
}
}
} // namespace Network } // namespace Network

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -18,8 +19,11 @@ namespace Network {
struct NetworkInterface { struct NetworkInterface {
std::string name; std::string name;
struct in_addr ip_address; struct in_addr ip_address;
struct in_addr subnet_mask;
struct in_addr gateway;
}; };
std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
std::optional<NetworkInterface> GetSelectedNetworkInterface();
} // namespace Network } // namespace Network

View File

@ -4,11 +4,13 @@
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <math.h> #include <math.h>
#include "common/logging/backend.h"
#include "common/param_package.h" #include "common/param_package.h"
namespace Common { namespace Common {
TEST_CASE("ParamPackage", "[common]") { TEST_CASE("ParamPackage", "[common]") {
Common::Log::DisableLoggingInTests();
ParamPackage original{ ParamPackage original{
{"abc", "xyz"}, {"abc", "xyz"},
{"def", "42"}, {"def", "42"},

View File

@ -6,7 +6,7 @@
#include <array> #include <array>
#include <bitset> #include <bitset>
#include <xbyak.h> #include <xbyak/xbyak.h>
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/x64/xbyak_abi.h" #include "common/x64/xbyak_abi.h"

View File

@ -21,6 +21,7 @@ void ToggleConsole() {
console_shown = UISettings::values.show_console.GetValue(); console_shown = UISettings::values.show_console.GetValue();
} }
using namespace Common::Log;
#if defined(_WIN32) && !defined(_DEBUG) #if defined(_WIN32) && !defined(_DEBUG)
FILE* temp; FILE* temp;
if (UISettings::values.show_console) { if (UISettings::values.show_console) {
@ -29,24 +30,20 @@ void ToggleConsole() {
freopen_s(&temp, "CONIN$", "r", stdin); freopen_s(&temp, "CONIN$", "r", stdin);
freopen_s(&temp, "CONOUT$", "w", stdout); freopen_s(&temp, "CONOUT$", "w", stdout);
freopen_s(&temp, "CONOUT$", "w", stderr); freopen_s(&temp, "CONOUT$", "w", stderr);
Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); SetColorConsoleBackendEnabled(true);
} }
} else { } else {
if (FreeConsole()) { if (FreeConsole()) {
// In order to close the console, we have to also detach the streams on it. // In order to close the console, we have to also detach the streams on it.
// Just redirect them to NUL if there is no console window // Just redirect them to NUL if there is no console window
Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); SetColorConsoleBackendEnabled(false);
freopen_s(&temp, "NUL", "r", stdin); freopen_s(&temp, "NUL", "r", stdin);
freopen_s(&temp, "NUL", "w", stdout); freopen_s(&temp, "NUL", "w", stdout);
freopen_s(&temp, "NUL", "w", stderr); freopen_s(&temp, "NUL", "w", stderr);
} }
} }
#else #else
if (UISettings::values.show_console) { SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
} else {
Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
}
#endif #endif
} }
} // namespace Debugger } // namespace Debugger

View File

@ -177,21 +177,6 @@ void GMainWindow::ShowTelemetryCallout() {
const int GMainWindow::max_recent_files_item; const int GMainWindow::max_recent_files_item;
static void InitializeLogging() {
using namespace Common;
Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
Log::SetGlobalFilter(log_filter);
const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
void(FS::CreateDir(log_dir));
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
}
static void RemoveCachedContents() { static void RemoveCachedContents() {
const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
const auto offline_fonts = cache_dir / "fonts"; const auto offline_fonts = cache_dir / "fonts";
@ -209,8 +194,6 @@ GMainWindow::GMainWindow()
: input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} { provider{std::make_unique<FileSys::ManualContentProvider>()} {
InitializeLogging();
LoadTranslation(); LoadTranslation();
setAcceptDrops(true); setAcceptDrops(true);
@ -3463,6 +3446,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#endif #endif
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
Common::Log::Initialize();
Common::DetachedTasks detached_tasks; Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend"); MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); }); SCOPE_EXIT({ MicroProfileShutdown(); });
@ -3502,6 +3486,7 @@ int main(int argc, char* argv[]) {
// generating shaders // generating shaders
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
Core::System::InitializeGlobalInstance();
GMainWindow main_window; GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter // After settings have been loaded by GMainWindow, apply the filter
main_window.show(); main_window.show();

View File

@ -74,31 +74,14 @@ static void PrintVersion() {
std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
} }
static void InitializeLogging() {
using namespace Common;
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
void(FS::CreateDir(log_dir));
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
}
/// Application entry point /// Application entry point
int main(int argc, char** argv) { int main(int argc, char** argv) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::DetachedTasks detached_tasks; Common::DetachedTasks detached_tasks;
Config config; Config config;
int option_index = 0; int option_index = 0;
InitializeLogging();
#ifdef _WIN32 #ifdef _WIN32
int argc_w; int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@ -163,6 +146,7 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
Core::System::InitializeGlobalInstance();
auto& system{Core::System::GetInstance()}; auto& system{Core::System::GetInstance()};
InputCommon::InputSubsystem input_subsystem; InputCommon::InputSubsystem input_subsystem;