early-access version 2836
This commit is contained in:
parent
0e7aef7e36
commit
2a9883730d
@ -1,7 +1,7 @@
|
|||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 2835.
|
This is the source code for early-access 2836.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
2
externals/dynarmic/.gitignore
vendored
2
externals/dynarmic/.gitignore
vendored
@ -1,6 +1,8 @@
|
|||||||
# Built files
|
# Built files
|
||||||
build/
|
build/
|
||||||
build-*/
|
build-*/
|
||||||
|
cmake-build-*/
|
||||||
|
.idea/
|
||||||
docs/Doxygen/
|
docs/Doxygen/
|
||||||
# Generated files
|
# Generated files
|
||||||
src/dynarmic/backend/x64/mig/
|
src/dynarmic/backend/x64/mig/
|
||||||
|
2
externals/dynarmic/CMakeLists.txt
vendored
2
externals/dynarmic/CMakeLists.txt
vendored
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.8)
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(dynarmic LANGUAGES C CXX ASM VERSION 6.1.1)
|
project(dynarmic LANGUAGES C CXX ASM VERSION 6.2.0)
|
||||||
|
|
||||||
# Determine if we're built as a subproject (using add_subdirectory)
|
# Determine if we're built as a subproject (using add_subdirectory)
|
||||||
# or if this is the master project.
|
# or if this is the master project.
|
||||||
|
31
externals/dynarmic/README.md
vendored
31
externals/dynarmic/README.md
vendored
@ -267,21 +267,30 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
```
|
```
|
||||||
|
|
||||||
### mp
|
### mcl & oaknut
|
||||||
|
|
||||||
```
|
```
|
||||||
Copyright (C) 2017 MerryMage
|
MIT License
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for
|
Copyright (c) 2022 merryhime <https://mary.rs>
|
||||||
any purpose with or without fee is hereby granted.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
in the Software without restriction, including without limitation the rights
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
furnished to do so, subject to the following conditions:
|
||||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
```
|
```
|
||||||
|
|
||||||
### robin-map
|
### robin-map
|
||||||
|
@ -3,36 +3,38 @@ on: [push, pull_request]
|
|||||||
jobs:
|
jobs:
|
||||||
test_on_ubuntu:
|
test_on_ubuntu:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Build on ${{ matrix.distro }} ${{ matrix.arch }}
|
name: g++-10
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- arch: aarch64
|
|
||||||
distro: ubuntu_latest
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout oaknut repo
|
||||||
- uses: uraimo/run-on-arch-action@v2
|
uses: actions/checkout@v3
|
||||||
name: Build and Test
|
|
||||||
id: build
|
|
||||||
with:
|
|
||||||
arch: ${{ matrix.arch }}
|
|
||||||
distro: ${{ matrix.distro }}
|
|
||||||
shell: /bin/bash
|
|
||||||
|
|
||||||
install: |
|
- name: Install dependencies
|
||||||
apt-get update -q -y
|
run: >
|
||||||
apt-get install -q -y make cmake g++ git
|
sudo apt-get install -q -y
|
||||||
|
gcc-10-aarch64-linux-gnu
|
||||||
|
g++-10-aarch64-linux-gnu
|
||||||
|
ninja-build
|
||||||
|
qemu-user
|
||||||
|
|
||||||
pushd /tmp
|
- name: Checkout Catch2 v3 repo
|
||||||
git clone https://github.com/catchorg/Catch2.git
|
run: git clone https://github.com/catchorg/Catch2.git externals/catch
|
||||||
cd Catch2
|
|
||||||
cmake -Bbuild -H. -DBUILD_TESTING=OFF
|
|
||||||
cmake --build build/ --target install
|
|
||||||
popd
|
|
||||||
|
|
||||||
run: |
|
- name: Configure CMake
|
||||||
cmake -Bbuild -H.
|
env:
|
||||||
cmake --build build
|
CC: aarch64-linux-gnu-gcc-10
|
||||||
./build/oaknut-tests
|
CXX: aarch64-linux-gnu-g++-10
|
||||||
|
run: >
|
||||||
|
cmake
|
||||||
|
-B ${{github.workspace}}/build
|
||||||
|
-H.
|
||||||
|
-GNinja
|
||||||
|
-DDYNARMIC_USE_BUNDLED_CATCH=ON
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ${{github.workspace}}/build
|
||||||
|
run: ninja
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
working-directory: ${{github.workspace}}/build
|
||||||
|
run: qemu-aarch64 -L /usr/aarch64-linux-gnu ./oaknut-tests -d yes
|
||||||
|
@ -39,7 +39,11 @@ target_compile_features(oaknut INTERFACE cxx_std_20)
|
|||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
if (MASTER_PROJECT)
|
if (MASTER_PROJECT)
|
||||||
|
if (DYNARMIC_USE_BUNDLED_CATCH)
|
||||||
|
add_subdirectory(externals/catch)
|
||||||
|
else()
|
||||||
find_package(Catch2 3 REQUIRED)
|
find_package(Catch2 3 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(oaknut-tests
|
add_executable(oaknut-tests
|
||||||
tests/basic.cpp
|
tests/basic.cpp
|
||||||
@ -49,9 +53,4 @@ if (MASTER_PROJECT)
|
|||||||
target_include_directories(oaknut-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
target_include_directories(oaknut-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||||
target_link_libraries(oaknut-tests PRIVATE Catch2::Catch2WithMain merry::oaknut)
|
target_link_libraries(oaknut-tests PRIVATE Catch2::Catch2WithMain merry::oaknut)
|
||||||
target_compile_options(oaknut-tests PRIVATE -Wall -Wextra -Wcast-qual -pedantic -pedantic-errors -Wfatal-errors -Wno-missing-braces)
|
target_compile_options(oaknut-tests PRIVATE -Wall -Wextra -Wcast-qual -pedantic -pedantic-errors -Wfatal-errors -Wno-missing-braces)
|
||||||
|
|
||||||
include(CTest)
|
|
||||||
include(Catch)
|
|
||||||
catch_discover_tests(oaknut-tests)
|
|
||||||
enable_testing()
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
@ -114,9 +114,6 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
|||||||
// Start emitting.
|
// Start emitting.
|
||||||
code.align();
|
code.align();
|
||||||
const u8* const entrypoint = code.getCurr();
|
const u8* const entrypoint = code.getCurr();
|
||||||
code.SwitchToFarCode();
|
|
||||||
const u8* const entrypoint_far = code.getCurr();
|
|
||||||
code.SwitchToNearCode();
|
|
||||||
|
|
||||||
EmitCondPrelude(ctx);
|
EmitCondPrelude(ctx);
|
||||||
|
|
||||||
@ -155,6 +152,11 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
|||||||
EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||||
code.int3();
|
code.int3();
|
||||||
|
|
||||||
|
for (auto& deferred_emit : ctx.deferred_emits) {
|
||||||
|
deferred_emit();
|
||||||
|
}
|
||||||
|
code.int3();
|
||||||
|
|
||||||
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
|
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
|
||||||
|
|
||||||
const A32::LocationDescriptor descriptor{block.Location()};
|
const A32::LocationDescriptor descriptor{block.Location()};
|
||||||
@ -163,7 +165,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
|||||||
const auto range = boost::icl::discrete_interval<u32>::closed(descriptor.PC(), end_location.PC() - 1);
|
const auto range = boost::icl::discrete_interval<u32>::closed(descriptor.PC(), end_location.PC() - 1);
|
||||||
block_ranges.AddRange(range, descriptor);
|
block_ranges.AddRange(range, descriptor);
|
||||||
|
|
||||||
return RegisterBlock(descriptor, entrypoint, entrypoint_far, size);
|
return RegisterBlock(descriptor, entrypoint, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::ClearCache() {
|
void A32EmitX64::ClearCache() {
|
||||||
@ -1168,16 +1170,9 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Xbyak::Label dest;
|
|
||||||
code.jmp(dest, Xbyak::CodeGenerator::T_NEAR);
|
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
|
||||||
code.align(16);
|
|
||||||
code.L(dest);
|
|
||||||
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
PushRSBHelper(rax, rbx, terminal.next);
|
PushRSBHelper(rax, rbx, terminal.next);
|
||||||
code.ForceReturnFromRunCode();
|
code.ForceReturnFromRunCode();
|
||||||
code.SwitchToNearCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
|
@ -110,6 +110,7 @@ protected:
|
|||||||
FakeCall FastmemCallback(u64 rip);
|
FakeCall FastmemCallback(u64 rip);
|
||||||
|
|
||||||
// Memory access helpers
|
// Memory access helpers
|
||||||
|
void EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end = nullptr);
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void EmitMemoryRead(A32EmitContext& ctx, IR::Inst* inst);
|
void EmitMemoryRead(A32EmitContext& ctx, IR::Inst* inst);
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
|
@ -235,4 +235,25 @@ void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end) {
|
||||||
|
if (!conf.check_halt_on_memory_access) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Xbyak::Label skip;
|
||||||
|
|
||||||
|
const A32::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
||||||
|
|
||||||
|
code.test(dword[r15 + offsetof(A32JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
||||||
|
if (end) {
|
||||||
|
code.jz(*end, code.T_NEAR);
|
||||||
|
} else {
|
||||||
|
code.jz(skip, code.T_NEAR);
|
||||||
|
}
|
||||||
|
EmitSetUpperLocationDescriptor(current_location, ctx.Location());
|
||||||
|
code.mov(dword[r15 + offsetof(A32JitState, Reg) + sizeof(u32) * 15], current_location.PC());
|
||||||
|
code.ForceReturnFromRunCode();
|
||||||
|
code.L(skip);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::X64
|
} // namespace Dynarmic::Backend::X64
|
||||||
|
@ -60,7 +60,7 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
|
|||||||
|
|
||||||
struct Jit::Impl {
|
struct Jit::Impl {
|
||||||
Impl(Jit* jit, A32::UserConfig conf)
|
Impl(Jit* jit, A32::UserConfig conf)
|
||||||
: block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, conf.far_code_offset, GenRCP(conf))
|
: block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||||
, emitter(block_of_code, conf, jit)
|
, emitter(block_of_code, conf, jit)
|
||||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||||
, conf(std::move(conf))
|
, conf(std::move(conf))
|
||||||
@ -171,10 +171,9 @@ private:
|
|||||||
PerformCacheInvalidation();
|
PerformCacheInvalidation();
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks,
|
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||||
{conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions, conf.check_halt_on_memory_access});
|
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::PolyfillPass(ir_block, polyfill_options);
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
||||||
Optimization::A32GetSetElimination(ir_block);
|
Optimization::A32GetSetElimination(ir_block);
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
Optimization::DeadCodeElimination(ir_block);
|
||||||
}
|
}
|
||||||
|
@ -85,9 +85,6 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
|
|||||||
// Start emitting.
|
// Start emitting.
|
||||||
code.align();
|
code.align();
|
||||||
const u8* const entrypoint = code.getCurr();
|
const u8* const entrypoint = code.getCurr();
|
||||||
code.SwitchToFarCode();
|
|
||||||
const u8* const entrypoint_far = code.getCurr();
|
|
||||||
code.SwitchToNearCode();
|
|
||||||
|
|
||||||
ASSERT(block.GetCondition() == IR::Cond::AL);
|
ASSERT(block.GetCondition() == IR::Cond::AL);
|
||||||
|
|
||||||
@ -126,6 +123,11 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
|
|||||||
EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||||
code.int3();
|
code.int3();
|
||||||
|
|
||||||
|
for (auto& deferred_emit : ctx.deferred_emits) {
|
||||||
|
deferred_emit();
|
||||||
|
}
|
||||||
|
code.int3();
|
||||||
|
|
||||||
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
|
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
|
||||||
|
|
||||||
const A64::LocationDescriptor descriptor{block.Location()};
|
const A64::LocationDescriptor descriptor{block.Location()};
|
||||||
@ -134,7 +136,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
|
|||||||
const auto range = boost::icl::discrete_interval<u64>::closed(descriptor.PC(), end_location.PC() - 1);
|
const auto range = boost::icl::discrete_interval<u64>::closed(descriptor.PC(), end_location.PC() - 1);
|
||||||
block_ranges.AddRange(range, descriptor);
|
block_ranges.AddRange(range, descriptor);
|
||||||
|
|
||||||
return RegisterBlock(descriptor, entrypoint, entrypoint_far, size);
|
return RegisterBlock(descriptor, entrypoint, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::ClearCache() {
|
void A64EmitX64::ClearCache() {
|
||||||
|
@ -108,6 +108,7 @@ protected:
|
|||||||
FakeCall FastmemCallback(u64 rip);
|
FakeCall FastmemCallback(u64 rip);
|
||||||
|
|
||||||
// Memory access helpers
|
// Memory access helpers
|
||||||
|
void EmitCheckMemoryAbort(A64EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end = nullptr);
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void EmitMemoryRead(A64EmitContext& ctx, IR::Inst* inst);
|
void EmitMemoryRead(A64EmitContext& ctx, IR::Inst* inst);
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
|
@ -407,4 +407,25 @@ void A64EmitX64::EmitA64ExclusiveWriteMemory128(A64EmitContext& ctx, IR::Inst* i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A64EmitX64::EmitCheckMemoryAbort(A64EmitContext&, IR::Inst* inst, Xbyak::Label* end) {
|
||||||
|
if (!conf.check_halt_on_memory_access) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Xbyak::Label skip;
|
||||||
|
|
||||||
|
const A64::LocationDescriptor current_location{IR::LocationDescriptor{inst->GetArg(0).GetU64()}};
|
||||||
|
|
||||||
|
code.test(dword[r15 + offsetof(A64JitState, halt_reason)], static_cast<u32>(HaltReason::MemoryAbort));
|
||||||
|
if (end) {
|
||||||
|
code.jz(*end, code.T_NEAR);
|
||||||
|
} else {
|
||||||
|
code.jz(skip, code.T_NEAR);
|
||||||
|
}
|
||||||
|
code.mov(rax, current_location.PC());
|
||||||
|
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
|
||||||
|
code.ForceReturnFromRunCode();
|
||||||
|
code.L(skip);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::X64
|
} // namespace Dynarmic::Backend::X64
|
||||||
|
@ -58,7 +58,7 @@ struct Jit::Impl final {
|
|||||||
public:
|
public:
|
||||||
Impl(Jit* jit, UserConfig conf)
|
Impl(Jit* jit, UserConfig conf)
|
||||||
: conf(conf)
|
: conf(conf)
|
||||||
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, conf.far_code_offset, GenRCP(conf))
|
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||||
, emitter(block_of_code, conf, jit)
|
, emitter(block_of_code, conf, jit)
|
||||||
, polyfill_options(GenPolyfillOptions(block_of_code)) {
|
, polyfill_options(GenPolyfillOptions(block_of_code)) {
|
||||||
ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
|
ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
|
||||||
@ -269,10 +269,10 @@ private:
|
|||||||
// JIT Compile
|
// JIT Compile
|
||||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||||
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, get_code,
|
||||||
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct, conf.hook_hint_instructions, conf.check_halt_on_memory_access});
|
{conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||||
Optimization::PolyfillPass(ir_block, polyfill_options);
|
Optimization::PolyfillPass(ir_block, polyfill_options);
|
||||||
Optimization::A64CallbackConfigPass(ir_block, conf);
|
Optimization::A64CallbackConfigPass(ir_block, conf);
|
||||||
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
if (conf.HasOptimization(OptimizationFlag::GetSetElimination) && !conf.check_halt_on_memory_access) {
|
||||||
Optimization::A64GetSetElimination(ir_block);
|
Optimization::A64GetSetElimination(ir_block);
|
||||||
Optimization::DeadCodeElimination(ir_block);
|
Optimization::DeadCodeElimination(ir_block);
|
||||||
}
|
}
|
||||||
|
@ -185,22 +185,19 @@ HostFeature GetHostFeatures() {
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, size_t far_code_offset, std::function<void(BlockOfCode&)> rcp)
|
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
|
||||||
: Xbyak::CodeGenerator(total_code_size, nullptr, &s_allocator)
|
: Xbyak::CodeGenerator(total_code_size, nullptr, &s_allocator)
|
||||||
, cb(std::move(cb))
|
, cb(std::move(cb))
|
||||||
, jsi(jsi)
|
, jsi(jsi)
|
||||||
, far_code_offset(far_code_offset)
|
|
||||||
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
, constant_pool(*this, CONSTANT_POOL_SIZE)
|
||||||
, host_features(GetHostFeatures()) {
|
, host_features(GetHostFeatures()) {
|
||||||
ASSERT(total_code_size > far_code_offset);
|
|
||||||
EnableWriting();
|
EnableWriting();
|
||||||
GenRunCode(rcp);
|
GenRunCode(rcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::PreludeComplete() {
|
void BlockOfCode::PreludeComplete() {
|
||||||
prelude_complete = true;
|
prelude_complete = true;
|
||||||
near_code_begin = getCurr();
|
code_begin = getCurr();
|
||||||
far_code_begin = getCurr() + far_code_offset;
|
|
||||||
ClearCache();
|
ClearCache();
|
||||||
DisableWriting();
|
DisableWriting();
|
||||||
}
|
}
|
||||||
@ -219,21 +216,15 @@ void BlockOfCode::DisableWriting() {
|
|||||||
|
|
||||||
void BlockOfCode::ClearCache() {
|
void BlockOfCode::ClearCache() {
|
||||||
ASSERT(prelude_complete);
|
ASSERT(prelude_complete);
|
||||||
in_far_code = false;
|
SetCodePtr(code_begin);
|
||||||
near_code_ptr = near_code_begin;
|
|
||||||
far_code_ptr = far_code_begin;
|
|
||||||
SetCodePtr(near_code_begin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BlockOfCode::SpaceRemaining() const {
|
size_t BlockOfCode::SpaceRemaining() const {
|
||||||
ASSERT(prelude_complete);
|
ASSERT(prelude_complete);
|
||||||
const u8* current_near_ptr = in_far_code ? reinterpret_cast<const u8*>(near_code_ptr) : getCurr<const u8*>();
|
const u8* current_ptr = getCurr<const u8*>();
|
||||||
const u8* current_far_ptr = in_far_code ? getCurr<const u8*>() : reinterpret_cast<const u8*>(far_code_ptr);
|
if (current_ptr >= &top_[maxSize_])
|
||||||
if (current_near_ptr >= far_code_begin)
|
|
||||||
return 0;
|
return 0;
|
||||||
if (current_far_ptr >= &top_[maxSize_])
|
return &top_[maxSize_] - current_ptr;
|
||||||
return 0;
|
|
||||||
return std::min(reinterpret_cast<const u8*>(far_code_begin) - current_near_ptr, &top_[maxSize_] - current_far_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const {
|
HaltReason BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const {
|
||||||
@ -406,26 +397,8 @@ Xbyak::Address BlockOfCode::XmmConst(const Xbyak::AddressFrame& frame, u64 lower
|
|||||||
return constant_pool.GetConstant(frame, lower, upper);
|
return constant_pool.GetConstant(frame, lower, upper);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockOfCode::SwitchToFarCode() {
|
|
||||||
ASSERT(prelude_complete);
|
|
||||||
ASSERT(!in_far_code);
|
|
||||||
in_far_code = true;
|
|
||||||
near_code_ptr = getCurr();
|
|
||||||
SetCodePtr(far_code_ptr);
|
|
||||||
|
|
||||||
ASSERT_MSG(near_code_ptr < far_code_begin, "Near code has overwritten far code!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlockOfCode::SwitchToNearCode() {
|
|
||||||
ASSERT(prelude_complete);
|
|
||||||
ASSERT(in_far_code);
|
|
||||||
in_far_code = false;
|
|
||||||
far_code_ptr = getCurr();
|
|
||||||
SetCodePtr(near_code_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
CodePtr BlockOfCode::GetCodeBegin() const {
|
CodePtr BlockOfCode::GetCodeBegin() const {
|
||||||
return near_code_begin;
|
return code_begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BlockOfCode::GetTotalCodeSize() const {
|
size_t BlockOfCode::GetTotalCodeSize() const {
|
||||||
|
@ -36,7 +36,7 @@ struct RunCodeCallbacks {
|
|||||||
|
|
||||||
class BlockOfCode final : public Xbyak::CodeGenerator {
|
class BlockOfCode final : public Xbyak::CodeGenerator {
|
||||||
public:
|
public:
|
||||||
BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, size_t far_code_offset, std::function<void(BlockOfCode&)> rcp);
|
BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp);
|
||||||
BlockOfCode(const BlockOfCode&) = delete;
|
BlockOfCode(const BlockOfCode&) = delete;
|
||||||
|
|
||||||
/// Call when external emitters have finished emitting their preludes.
|
/// Call when external emitters have finished emitting their preludes.
|
||||||
@ -49,7 +49,7 @@ public:
|
|||||||
|
|
||||||
/// Clears this block of code and resets code pointer to beginning.
|
/// Clears this block of code and resets code pointer to beginning.
|
||||||
void ClearCache();
|
void ClearCache();
|
||||||
/// Calculates how much space is remaining to use. This is the minimum of near code and far code.
|
/// Calculates how much space is remaining to use.
|
||||||
size_t SpaceRemaining() const;
|
size_t SpaceRemaining() const;
|
||||||
|
|
||||||
/// Runs emulated code from code_ptr.
|
/// Runs emulated code from code_ptr.
|
||||||
@ -125,11 +125,6 @@ public:
|
|||||||
mcl::bit::replicate_element<u64>(esize, value));
|
mcl::bit::replicate_element<u64>(esize, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Far code sits far away from the near code. Execution remains primarily in near code.
|
|
||||||
/// "Cold" / Rarely executed instructions sit in far code, so the CPU doesn't fetch them unless necessary.
|
|
||||||
void SwitchToFarCode();
|
|
||||||
void SwitchToNearCode();
|
|
||||||
|
|
||||||
CodePtr GetCodeBegin() const;
|
CodePtr GetCodeBegin() const;
|
||||||
size_t GetTotalCodeSize() const;
|
size_t GetTotalCodeSize() const;
|
||||||
|
|
||||||
@ -180,18 +175,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
RunCodeCallbacks cb;
|
RunCodeCallbacks cb;
|
||||||
JitStateInfo jsi;
|
JitStateInfo jsi;
|
||||||
size_t far_code_offset;
|
|
||||||
|
|
||||||
bool prelude_complete = false;
|
bool prelude_complete = false;
|
||||||
CodePtr near_code_begin = nullptr;
|
CodePtr code_begin = nullptr;
|
||||||
CodePtr far_code_begin = nullptr;
|
|
||||||
|
|
||||||
ConstantPool constant_pool;
|
ConstantPool constant_pool;
|
||||||
|
|
||||||
bool in_far_code = false;
|
|
||||||
CodePtr near_code_ptr;
|
|
||||||
CodePtr far_code_ptr;
|
|
||||||
|
|
||||||
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
|
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
|
||||||
RunCodeFuncType run_code = nullptr;
|
RunCodeFuncType run_code = nullptr;
|
||||||
RunCodeFuncType step_code = nullptr;
|
RunCodeFuncType step_code = nullptr;
|
||||||
|
@ -32,6 +32,8 @@ using namespace Xbyak::util;
|
|||||||
EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
|
EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
|
||||||
: reg_alloc(reg_alloc), block(block) {}
|
: reg_alloc(reg_alloc), block(block) {}
|
||||||
|
|
||||||
|
EmitContext::~EmitContext() = default;
|
||||||
|
|
||||||
size_t EmitContext::GetInstOffset(IR::Inst* inst) const {
|
size_t EmitContext::GetInstOffset(IR::Inst* inst) const {
|
||||||
return static_cast<size_t>(std::distance(block.begin(), IR::Block::iterator(inst)));
|
return static_cast<size_t>(std::distance(block.begin(), IR::Block::iterator(inst)));
|
||||||
}
|
}
|
||||||
@ -274,11 +276,8 @@ Xbyak::Label EmitX64::EmitCond(IR::Cond cond) {
|
|||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& descriptor, CodePtr entrypoint, CodePtr entrypoint_far, size_t size) {
|
EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& descriptor, CodePtr entrypoint, size_t size) {
|
||||||
PerfMapRegister(entrypoint, code.getCurr(), LocationDescriptorToFriendlyName(descriptor));
|
PerfMapRegister(entrypoint, code.getCurr(), LocationDescriptorToFriendlyName(descriptor));
|
||||||
code.SwitchToFarCode();
|
|
||||||
PerfMapRegister(entrypoint_far, code.getCurr(), LocationDescriptorToFriendlyName(descriptor) + "_far");
|
|
||||||
code.SwitchToNearCode();
|
|
||||||
Patch(descriptor, entrypoint);
|
Patch(descriptor, entrypoint);
|
||||||
|
|
||||||
BlockDescriptor block_desc{entrypoint, size};
|
BlockDescriptor block_desc{entrypoint, size};
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -14,6 +16,7 @@
|
|||||||
#include <mcl/bitsizeof.hpp>
|
#include <mcl/bitsizeof.hpp>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
#include <tsl/robin_set.h>
|
#include <tsl/robin_set.h>
|
||||||
|
#include <xbyak/xbyak.h>
|
||||||
#include <xbyak/xbyak_util.h>
|
#include <xbyak/xbyak_util.h>
|
||||||
|
|
||||||
#include "dynarmic/backend/x64/exception_handler.h"
|
#include "dynarmic/backend/x64/exception_handler.h"
|
||||||
@ -48,6 +51,7 @@ using HalfVectorArray = std::array<T, A64FullVectorWidth::value / mcl::bitsizeof
|
|||||||
|
|
||||||
struct EmitContext {
|
struct EmitContext {
|
||||||
EmitContext(RegAlloc& reg_alloc, IR::Block& block);
|
EmitContext(RegAlloc& reg_alloc, IR::Block& block);
|
||||||
|
virtual ~EmitContext();
|
||||||
|
|
||||||
size_t GetInstOffset(IR::Inst* inst) const;
|
size_t GetInstOffset(IR::Inst* inst) const;
|
||||||
void EraseInstruction(IR::Inst* inst);
|
void EraseInstruction(IR::Inst* inst);
|
||||||
@ -58,8 +62,16 @@ struct EmitContext {
|
|||||||
|
|
||||||
RegAlloc& reg_alloc;
|
RegAlloc& reg_alloc;
|
||||||
IR::Block& block;
|
IR::Block& block;
|
||||||
|
|
||||||
|
std::vector<std::function<void()>> deferred_emits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using SharedLabel = std::shared_ptr<Xbyak::Label>;
|
||||||
|
|
||||||
|
inline SharedLabel GenSharedLabel() {
|
||||||
|
return std::make_shared<Xbyak::Label>();
|
||||||
|
}
|
||||||
|
|
||||||
class EmitX64 {
|
class EmitX64 {
|
||||||
public:
|
public:
|
||||||
struct BlockDescriptor {
|
struct BlockDescriptor {
|
||||||
@ -93,7 +105,7 @@ protected:
|
|||||||
virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0;
|
virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0;
|
||||||
void EmitAddCycles(size_t cycles);
|
void EmitAddCycles(size_t cycles);
|
||||||
Xbyak::Label EmitCond(IR::Cond cond);
|
Xbyak::Label EmitCond(IR::Cond cond);
|
||||||
BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, CodePtr entrypoint_far, size_t size);
|
BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size);
|
||||||
void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target);
|
void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target);
|
||||||
|
|
||||||
// Terminal instruction emitters
|
// Terminal instruction emitters
|
||||||
|
@ -152,18 +152,18 @@ void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
Xbyak::Label ProcessNaN(BlockOfCode& code, Xbyak::Xmm a) {
|
SharedLabel ProcessNaN(BlockOfCode& code, EmitContext& ctx, Xbyak::Xmm a) {
|
||||||
Xbyak::Label nan, end;
|
SharedLabel nan = GenSharedLabel(), end = GenSharedLabel();
|
||||||
|
|
||||||
FCODE(ucomis)(a, a);
|
FCODE(ucomis)(a, a);
|
||||||
code.jp(nan, code.T_NEAR);
|
code.jp(*nan, code.T_NEAR);
|
||||||
code.SwitchToFarCode();
|
|
||||||
code.L(nan);
|
|
||||||
|
|
||||||
|
ctx.deferred_emits.emplace_back([=, &code] {
|
||||||
|
code.L(*nan);
|
||||||
code.orps(a, code.XmmBConst<fsize>(xword, fsize == 32 ? 0x00400000 : 0x0008'0000'0000'0000));
|
code.orps(a, code.XmmBConst<fsize>(xword, fsize == 32 ? 0x00400000 : 0x0008'0000'0000'0000));
|
||||||
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
});
|
||||||
|
|
||||||
code.jmp(end, code.T_NEAR);
|
|
||||||
code.SwitchToNearCode();
|
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,12 +268,12 @@ template<size_t fsize, typename Function>
|
|||||||
void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
|
void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
Xbyak::Label end;
|
SharedLabel end = GenSharedLabel();
|
||||||
|
|
||||||
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
if (!ctx.FPCR().DN() && !ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
|
if (!ctx.FPCR().DN() && !ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
|
||||||
end = ProcessNaN<fsize>(code, result);
|
end = ProcessNaN<fsize>(code, ctx, result);
|
||||||
}
|
}
|
||||||
if constexpr (std::is_member_function_pointer_v<Function>) {
|
if constexpr (std::is_member_function_pointer_v<Function>) {
|
||||||
(code.*fn)(result, result);
|
(code.*fn)(result, result);
|
||||||
@ -287,7 +287,7 @@ void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
|
|||||||
} else {
|
} else {
|
||||||
PostProcessNaN<fsize>(code, result, ctx.reg_alloc.ScratchXmm());
|
PostProcessNaN<fsize>(code, result, ctx.reg_alloc.ScratchXmm());
|
||||||
}
|
}
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
@ -321,7 +321,7 @@ void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn)
|
|||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
Xbyak::Label end, nan, op_are_nans;
|
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
|
||||||
|
|
||||||
code.movaps(result, op1);
|
code.movaps(result, op1);
|
||||||
if constexpr (std::is_member_function_pointer_v<Function>) {
|
if constexpr (std::is_member_function_pointer_v<Function>) {
|
||||||
@ -330,19 +330,21 @@ void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn)
|
|||||||
fn(result, op2);
|
fn(result, op2);
|
||||||
}
|
}
|
||||||
FCODE(ucomis)(result, result);
|
FCODE(ucomis)(result, result);
|
||||||
code.jp(nan, code.T_NEAR);
|
code.jp(*nan, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code] {
|
||||||
code.L(nan);
|
Xbyak::Label op_are_nans;
|
||||||
|
|
||||||
|
code.L(*nan);
|
||||||
FCODE(ucomis)(op1, op2);
|
FCODE(ucomis)(op1, op2);
|
||||||
code.jp(op_are_nans);
|
code.jp(op_are_nans);
|
||||||
// Here we must return a positive NaN, because the indefinite value on x86 is a negative NaN!
|
// Here we must return a positive NaN, because the indefinite value on x86 is a negative NaN!
|
||||||
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::DefaultNaN()));
|
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::DefaultNaN()));
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.L(op_are_nans);
|
code.L(op_are_nans);
|
||||||
EmitPostProcessNaNs<fsize>(code, result, op1, op2, tmp, end);
|
EmitPostProcessNaNs<fsize>(code, result, op1, op2, tmp, *end);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
@ -428,39 +430,39 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
|
|
||||||
DenormalsAreZero<fsize>(code, ctx, {result, operand});
|
DenormalsAreZero<fsize>(code, ctx, {result, operand});
|
||||||
|
|
||||||
Xbyak::Label equal, end, nan;
|
SharedLabel equal = GenSharedLabel(), end = GenSharedLabel();
|
||||||
|
|
||||||
FCODE(ucomis)(result, operand);
|
FCODE(ucomis)(result, operand);
|
||||||
code.jz(equal, code.T_NEAR);
|
code.jz(*equal, code.T_NEAR);
|
||||||
if constexpr (is_max) {
|
if constexpr (is_max) {
|
||||||
FCODE(maxs)(result, operand);
|
FCODE(maxs)(result, operand);
|
||||||
} else {
|
} else {
|
||||||
FCODE(mins)(result, operand);
|
FCODE(mins)(result, operand);
|
||||||
}
|
}
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
|
Xbyak::Label nan;
|
||||||
|
|
||||||
code.L(equal);
|
code.L(*equal);
|
||||||
code.jp(nan);
|
code.jp(nan);
|
||||||
if constexpr (is_max) {
|
if constexpr (is_max) {
|
||||||
code.andps(result, operand);
|
code.andps(result, operand);
|
||||||
} else {
|
} else {
|
||||||
code.orps(result, operand);
|
code.orps(result, operand);
|
||||||
}
|
}
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
|
|
||||||
code.L(nan);
|
code.L(nan);
|
||||||
if (ctx.FPCR().DN()) {
|
if (ctx.FPCR().DN()) {
|
||||||
code.movaps(result, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_nan : f64_nan));
|
code.movaps(result, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_nan : f64_nan));
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
} else {
|
} else {
|
||||||
code.movaps(tmp, result);
|
code.movaps(tmp, result);
|
||||||
FCODE(adds)(result, operand);
|
FCODE(adds)(result, operand);
|
||||||
EmitPostProcessNaNs<fsize>(code, result, tmp, operand, gpr_scratch, end);
|
EmitPostProcessNaNs<fsize>(code, result, tmp, operand, gpr_scratch, *end);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
code.SwitchToNearCode();
|
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
@ -469,7 +471,6 @@ template<size_t fsize, bool is_max>
|
|||||||
static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
using FPT = mcl::unsigned_integer_of_size<fsize>;
|
using FPT = mcl::unsigned_integer_of_size<fsize>;
|
||||||
constexpr FPT default_nan = FP::FPInfo<FPT>::DefaultNaN();
|
constexpr FPT default_nan = FP::FPInfo<FPT>::DefaultNaN();
|
||||||
constexpr u8 mantissa_msb_bit = static_cast<u8>(FP::FPInfo<FPT>::explicit_mantissa_width - 1);
|
|
||||||
|
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
@ -492,7 +493,7 @@ static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
Xbyak::Reg tmp = ctx.reg_alloc.ScratchGpr();
|
Xbyak::Reg tmp = ctx.reg_alloc.ScratchGpr();
|
||||||
tmp.setBit(fsize);
|
tmp.setBit(fsize);
|
||||||
|
|
||||||
const auto move_to_tmp = [&](const Xbyak::Xmm& xmm) {
|
const auto move_to_tmp = [=, &code](const Xbyak::Xmm& xmm) {
|
||||||
if constexpr (fsize == 32) {
|
if constexpr (fsize == 32) {
|
||||||
code.movd(tmp.cvt32(), xmm);
|
code.movd(tmp.cvt32(), xmm);
|
||||||
} else {
|
} else {
|
||||||
@ -500,28 +501,30 @@ static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Xbyak::Label end, z, nan, op2_is_nan, snan, maybe_both_nan, normal;
|
SharedLabel end = GenSharedLabel(), z = GenSharedLabel();
|
||||||
|
|
||||||
FCODE(ucomis)(op1, op2);
|
FCODE(ucomis)(op1, op2);
|
||||||
code.jz(z, code.T_NEAR);
|
code.jz(*z, code.T_NEAR);
|
||||||
code.L(normal);
|
|
||||||
if constexpr (is_max) {
|
if constexpr (is_max) {
|
||||||
FCODE(maxs)(op2, op1);
|
FCODE(maxs)(op2, op1);
|
||||||
} else {
|
} else {
|
||||||
FCODE(mins)(op2, op1);
|
FCODE(mins)(op2, op1);
|
||||||
}
|
}
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
|
Xbyak::Label nan, op2_is_nan, snan, maybe_both_nan;
|
||||||
|
|
||||||
code.L(z);
|
constexpr u8 mantissa_msb_bit = static_cast<u8>(FP::FPInfo<FPT>::explicit_mantissa_width - 1);
|
||||||
|
|
||||||
|
code.L(*z);
|
||||||
code.jp(nan);
|
code.jp(nan);
|
||||||
if constexpr (is_max) {
|
if constexpr (is_max) {
|
||||||
code.andps(op2, op1);
|
code.andps(op2, op1);
|
||||||
} else {
|
} else {
|
||||||
code.orps(op2, op1);
|
code.orps(op2, op1);
|
||||||
}
|
}
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
|
|
||||||
// NaN requirements:
|
// NaN requirements:
|
||||||
// op1 op2 result
|
// op1 op2 result
|
||||||
@ -542,17 +545,17 @@ static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
if (ctx.FPCR().DN()) {
|
if (ctx.FPCR().DN()) {
|
||||||
code.L(snan);
|
code.L(snan);
|
||||||
code.movaps(op2, code.XmmBConst<fsize>(xword, default_nan));
|
code.movaps(op2, code.XmmBConst<fsize>(xword, default_nan));
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
} else {
|
} else {
|
||||||
code.movaps(op2, op1);
|
code.movaps(op2, op1);
|
||||||
code.L(snan);
|
code.L(snan);
|
||||||
code.orps(op2, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::mantissa_msb));
|
code.orps(op2, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::mantissa_msb));
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(maybe_both_nan);
|
code.L(maybe_both_nan);
|
||||||
FCODE(ucomis)(op2, op2);
|
FCODE(ucomis)(op2, op2);
|
||||||
code.jnp(end, code.T_NEAR);
|
code.jnp(*end, code.T_NEAR);
|
||||||
if (ctx.FPCR().DN()) {
|
if (ctx.FPCR().DN()) {
|
||||||
code.jmp(snan);
|
code.jmp(snan);
|
||||||
} else {
|
} else {
|
||||||
@ -560,7 +563,7 @@ static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.bt(tmp.cvt64(), mantissa_msb_bit);
|
code.bt(tmp.cvt64(), mantissa_msb_bit);
|
||||||
code.jnc(snan);
|
code.jnc(snan);
|
||||||
code.movaps(op2, op1);
|
code.movaps(op2, op1);
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
}
|
}
|
||||||
|
|
||||||
// op2 is NaN
|
// op2 is NaN
|
||||||
@ -569,9 +572,8 @@ static void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.bt(tmp, mantissa_msb_bit);
|
code.bt(tmp, mantissa_msb_bit);
|
||||||
code.jnc(snan);
|
code.jnc(snan);
|
||||||
code.movaps(op2, op1);
|
code.movaps(op2, op1);
|
||||||
code.jmp(end);
|
code.jmp(*end);
|
||||||
|
});
|
||||||
code.SwitchToNearCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, op2);
|
ctx.reg_alloc.DefineValue(inst, op2);
|
||||||
@ -636,7 +638,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::FMA)) {
|
if (code.HasHostFeature(HostFeature::FMA)) {
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
@ -650,11 +652,11 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
code.movaps(tmp, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_non_sign_mask : f64_non_sign_mask));
|
code.movaps(tmp, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_non_sign_mask : f64_non_sign_mask));
|
||||||
code.andps(tmp, result);
|
code.andps(tmp, result);
|
||||||
FCODE(ucomis)(tmp, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_smallest_normal : f64_smallest_normal));
|
FCODE(ucomis)(tmp, code.XmmBConst<fsize>(xword, fsize == 32 ? f32_smallest_normal : f64_smallest_normal));
|
||||||
code.jz(fallback, code.T_NEAR);
|
code.jz(*fallback, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
|
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
@ -676,8 +678,8 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
|
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
@ -735,7 +737,7 @@ static void EmitFPMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Reg64 tmp = do_default_nan ? INVALID_REG : ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 tmp = do_default_nan ? INVALID_REG : ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
Xbyak::Label end, nan, op_are_nans;
|
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
FCODE(vmuls)(result, op1, op2);
|
FCODE(vmuls)(result, op1, op2);
|
||||||
@ -744,11 +746,13 @@ static void EmitFPMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
FCODE(muls)(result, op2);
|
FCODE(muls)(result, op2);
|
||||||
}
|
}
|
||||||
FCODE(ucomis)(result, result);
|
FCODE(ucomis)(result, result);
|
||||||
code.jp(nan, code.T_NEAR);
|
code.jp(*nan, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code] {
|
||||||
code.L(nan);
|
Xbyak::Label op_are_nans;
|
||||||
|
|
||||||
|
code.L(*nan);
|
||||||
FCODE(ucomis)(op1, op2);
|
FCODE(ucomis)(op1, op2);
|
||||||
code.jp(op_are_nans);
|
code.jp(op_are_nans);
|
||||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||||
@ -759,15 +763,15 @@ static void EmitFPMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
}
|
}
|
||||||
code.andps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::sign_mask));
|
code.andps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::sign_mask));
|
||||||
code.orps(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 2>()));
|
code.orps(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 2>()));
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.L(op_are_nans);
|
code.L(op_are_nans);
|
||||||
if (do_default_nan) {
|
if (do_default_nan) {
|
||||||
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::DefaultNaN()));
|
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPInfo<FPT>::DefaultNaN()));
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
EmitPostProcessNaNs<fsize>(code, result, op1, op2, tmp, end);
|
EmitPostProcessNaNs<fsize>(code, result, op1, op2, tmp, *end);
|
||||||
}
|
}
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
@ -871,7 +875,7 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::FMA)) {
|
if (code.HasHostFeature(HostFeature::FMA)) {
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
@ -880,11 +884,11 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 2>()));
|
code.movaps(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 2>()));
|
||||||
FCODE(vfnmadd231s)(result, operand1, operand2);
|
FCODE(vfnmadd231s)(result, operand1, operand2);
|
||||||
FCODE(ucomis)(result, result);
|
FCODE(ucomis)(result, result);
|
||||||
code.jp(fallback, code.T_NEAR);
|
code.jp(*fallback, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
|
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
@ -897,8 +901,8 @@ static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
|
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
@ -1034,8 +1038,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
const Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm();
|
||||||
[[maybe_unused]] const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
[[maybe_unused]] const Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
Xbyak::Label fallback, bad_values, end, default_nan;
|
SharedLabel bad_values = GenSharedLabel(), end = GenSharedLabel();
|
||||||
bool needs_fallback = false;
|
|
||||||
|
|
||||||
code.movaps(value, operand);
|
code.movaps(value, operand);
|
||||||
|
|
||||||
@ -1045,7 +1048,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
|
|
||||||
// Detect NaNs, negatives, zeros, denormals and infinities
|
// Detect NaNs, negatives, zeros, denormals and infinities
|
||||||
FCODE(ucomis)(value, code.XmmBConst<fsize>(xword, FPT(1) << FP::FPInfo<FPT>::explicit_mantissa_width));
|
FCODE(ucomis)(value, code.XmmBConst<fsize>(xword, FPT(1) << FP::FPInfo<FPT>::explicit_mantissa_width));
|
||||||
code.jna(bad_values, code.T_NEAR);
|
code.jna(*bad_values, code.T_NEAR);
|
||||||
|
|
||||||
FCODE(sqrts)(value, value);
|
FCODE(sqrts)(value, value);
|
||||||
ICODE(mov)(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 1>()));
|
ICODE(mov)(result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, 0, 1>()));
|
||||||
@ -1054,11 +1057,13 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
ICODE(padd)(result, code.XmmBConst<fsize>(xword, fsize == 32 ? 0x00004000 : 0x0000'0800'0000'0000));
|
ICODE(padd)(result, code.XmmBConst<fsize>(xword, fsize == 32 ? 0x00004000 : 0x0000'0800'0000'0000));
|
||||||
code.pand(result, xmm0);
|
code.pand(result, xmm0);
|
||||||
|
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
|
Xbyak::Label fallback, default_nan;
|
||||||
|
bool needs_fallback = false;
|
||||||
|
|
||||||
code.L(bad_values);
|
code.L(*bad_values);
|
||||||
if constexpr (fsize == 32) {
|
if constexpr (fsize == 32) {
|
||||||
code.movd(tmp, operand);
|
code.movd(tmp, operand);
|
||||||
|
|
||||||
@ -1080,18 +1085,18 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
|
|
||||||
if (ctx.FPCR().DN()) {
|
if (ctx.FPCR().DN()) {
|
||||||
code.ucomiss(result, result);
|
code.ucomiss(result, result);
|
||||||
code.jnp(end, code.T_NEAR);
|
code.jnp(*end, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
// FZ ? (a >= 0x80800000 && a <= 0xFF800000) : (a >= 0x80000001 && a <= 0xFF800000)
|
// FZ ? (a >= 0x80800000 && a <= 0xFF800000) : (a >= 0x80000001 && a <= 0xFF800000)
|
||||||
// !FZ path takes into account the subtraction by one from the earlier block
|
// !FZ path takes into account the subtraction by one from the earlier block
|
||||||
code.add(tmp, ctx.FPCR().FZ() ? 0x7F800000 : 0x80000000);
|
code.add(tmp, ctx.FPCR().FZ() ? 0x7F800000 : 0x80000000);
|
||||||
code.cmp(tmp, ctx.FPCR().FZ() ? 0x7F000001 : 0x7F800000);
|
code.cmp(tmp, ctx.FPCR().FZ() ? 0x7F000001 : 0x7F800000);
|
||||||
code.jnb(end, code.T_NEAR);
|
code.jnb(*end, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(default_nan);
|
code.L(default_nan);
|
||||||
code.movd(result, code.XmmBConst<32>(xword, 0x7FC00000));
|
code.movd(result, code.XmmBConst<32>(xword, 0x7FC00000));
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
Xbyak::Label nan, zero;
|
Xbyak::Label nan, zero;
|
||||||
|
|
||||||
@ -1114,7 +1119,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.jmp(fallback);
|
code.jmp(fallback);
|
||||||
} else {
|
} else {
|
||||||
// result = 0
|
// result = 0
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(zero);
|
code.L(zero);
|
||||||
@ -1124,7 +1129,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.movaps(result, value);
|
code.movaps(result, value);
|
||||||
code.por(result, code.XmmBConst<64>(xword, 0x7FF0'0000'0000'0000));
|
code.por(result, code.XmmBConst<64>(xword, 0x7FF0'0000'0000'0000));
|
||||||
}
|
}
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
|
||||||
code.L(nan);
|
code.L(nan);
|
||||||
if (!ctx.FPCR().DN()) {
|
if (!ctx.FPCR().DN()) {
|
||||||
@ -1134,12 +1139,12 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.movaps(result, operand);
|
code.movaps(result, operand);
|
||||||
code.por(result, code.XmmBConst<64>(xword, 0x0008'0000'0000'0000));
|
code.por(result, code.XmmBConst<64>(xword, 0x0008'0000'0000'0000));
|
||||||
}
|
}
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(default_nan);
|
code.L(default_nan);
|
||||||
code.movq(result, code.XmmBConst<64>(xword, 0x7FF8'0000'0000'0000));
|
code.movq(result, code.XmmBConst<64>(xword, 0x7FF8'0000'0000'0000));
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(fallback);
|
code.L(fallback);
|
||||||
@ -1153,10 +1158,9 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||||||
code.movq(result, rax);
|
code.movq(result, rax);
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
code.SwitchToNearCode();
|
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
} else {
|
} else {
|
||||||
@ -1201,7 +1205,7 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (code.HasHostFeature(HostFeature::FMA | HostFeature::AVX)) {
|
if (code.HasHostFeature(HostFeature::FMA | HostFeature::AVX)) {
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
@ -1220,13 +1224,13 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
code.cmp(tmp.cvt16(), fsize == 32 ? 0x7f00 : 0x7fe0);
|
code.cmp(tmp.cvt16(), fsize == 32 ? 0x7f00 : 0x7fe0);
|
||||||
ctx.reg_alloc.Release(tmp);
|
ctx.reg_alloc.Release(tmp);
|
||||||
|
|
||||||
code.jae(fallback, code.T_NEAR);
|
code.jae(*fallback, code.T_NEAR);
|
||||||
|
|
||||||
FCODE(vmuls)(result, result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, -1, 1>()));
|
FCODE(vmuls)(result, result, code.XmmBConst<fsize>(xword, FP::FPValue<FPT, false, -1, 1>()));
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
|
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
@ -1239,8 +1243,8 @@ static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
|||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
|
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
@ -1528,22 +1532,22 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
if constexpr (isize == 64) {
|
if constexpr (isize == 64) {
|
||||||
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
Xbyak::Label saturate_max, end;
|
|
||||||
|
|
||||||
if (!unsigned_) {
|
if (!unsigned_) {
|
||||||
|
SharedLabel saturate_max = GenSharedLabel(), end = GenSharedLabel();
|
||||||
|
|
||||||
ZeroIfNaN<64>(code, src, scratch);
|
ZeroIfNaN<64>(code, src, scratch);
|
||||||
|
|
||||||
code.movsd(scratch, code.XmmBConst<64>(xword, f64_max_s64_lim));
|
code.movsd(scratch, code.XmmBConst<64>(xword, f64_max_s64_lim));
|
||||||
code.comisd(scratch, src);
|
code.comisd(scratch, src);
|
||||||
code.jna(saturate_max, code.T_NEAR);
|
code.jna(*saturate_max, code.T_NEAR);
|
||||||
code.cvttsd2si(result, src); // 64 bit gpr
|
code.cvttsd2si(result, src); // 64 bit gpr
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code] {
|
||||||
code.L(saturate_max);
|
code.L(*saturate_max);
|
||||||
code.mov(result, 0x7FFF'FFFF'FFFF'FFFF);
|
code.mov(result, 0x7FFF'FFFF'FFFF'FFFF);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
} else {
|
} else {
|
||||||
Xbyak::Label below_max;
|
Xbyak::Label below_max;
|
||||||
|
|
||||||
|
@ -52,26 +52,27 @@ FakeCall AxxEmitX64::FastmemCallback(u64 rip_) {
|
|||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const bool ordered = IsOrdered(args[1].GetImmediateAccType());
|
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
||||||
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
||||||
|
|
||||||
if (!conf.page_table && !fastmem_marker) {
|
if (!conf.page_table && !fastmem_marker) {
|
||||||
// Neither fastmem nor page table: Use callbacks
|
// Neither fastmem nor page table: Use callbacks
|
||||||
if constexpr (bitsize == 128) {
|
if constexpr (bitsize == 128) {
|
||||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
ctx.reg_alloc.HostCall(nullptr, {}, args[1]);
|
||||||
if (ordered) {
|
if (ordered) {
|
||||||
code.mfence();
|
code.mfence();
|
||||||
}
|
}
|
||||||
code.CallFunction(memory_read_128);
|
code.CallFunction(memory_read_128);
|
||||||
ctx.reg_alloc.DefineValue(inst, xmm1);
|
ctx.reg_alloc.DefineValue(inst, xmm1);
|
||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
ctx.reg_alloc.HostCall(inst, {}, args[1]);
|
||||||
if (ordered) {
|
if (ordered) {
|
||||||
code.mfence();
|
code.mfence();
|
||||||
}
|
}
|
||||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||||
}
|
}
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,20 +84,24 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
|
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]);
|
||||||
const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx();
|
const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx();
|
||||||
|
|
||||||
const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
|
const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
|
||||||
|
|
||||||
Xbyak::Label abort, end;
|
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
|
||||||
bool require_abort_handling = false;
|
|
||||||
|
|
||||||
if (fastmem_marker) {
|
if (fastmem_marker) {
|
||||||
// Use fastmem
|
// Use fastmem
|
||||||
const auto src_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling);
|
bool require_abort_handling;
|
||||||
|
const auto src_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
|
||||||
|
|
||||||
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
||||||
|
|
||||||
|
ctx.deferred_emits.emplace_back([=, this, &ctx] {
|
||||||
|
code.L(*abort);
|
||||||
|
code.call(wrapped_fn);
|
||||||
|
|
||||||
fastmem_patch_info.emplace(
|
fastmem_patch_info.emplace(
|
||||||
mcl::bit_cast<u64>(location),
|
mcl::bit_cast<u64>(location),
|
||||||
FastmemPatchInfo{
|
FastmemPatchInfo{
|
||||||
@ -105,22 +110,24 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
*fastmem_marker,
|
*fastmem_marker,
|
||||||
conf.recompile_on_fastmem_failure,
|
conf.recompile_on_fastmem_failure,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst, end.get());
|
||||||
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Use page table
|
// Use page table
|
||||||
ASSERT(conf.page_table);
|
ASSERT(conf.page_table);
|
||||||
const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, abort, vaddr);
|
const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
|
||||||
require_abort_handling = true;
|
|
||||||
EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
||||||
}
|
|
||||||
code.L(end);
|
|
||||||
|
|
||||||
if (require_abort_handling) {
|
ctx.deferred_emits.emplace_back([=, this, &ctx] {
|
||||||
code.SwitchToFarCode();
|
code.L(*abort);
|
||||||
code.L(abort);
|
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
code.jmp(end, code.T_NEAR);
|
EmitCheckMemoryAbort(ctx, inst, end.get());
|
||||||
code.SwitchToNearCode();
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
code.L(*end);
|
||||||
|
|
||||||
if constexpr (bitsize == 128) {
|
if constexpr (bitsize == 128) {
|
||||||
ctx.reg_alloc.DefineValue(inst, Xbyak::Xmm{value_idx});
|
ctx.reg_alloc.DefineValue(inst, Xbyak::Xmm{value_idx});
|
||||||
@ -132,24 +139,25 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
const bool ordered = IsOrdered(args[3].GetImmediateAccType());
|
||||||
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
||||||
|
|
||||||
if (!conf.page_table && !fastmem_marker) {
|
if (!conf.page_table && !fastmem_marker) {
|
||||||
// Neither fastmem nor page table: Use callbacks
|
// Neither fastmem nor page table: Use callbacks
|
||||||
if constexpr (bitsize == 128) {
|
if constexpr (bitsize == 128) {
|
||||||
ctx.reg_alloc.Use(args[0], ABI_PARAM2);
|
ctx.reg_alloc.Use(args[1], ABI_PARAM2);
|
||||||
ctx.reg_alloc.Use(args[1], HostLoc::XMM1);
|
ctx.reg_alloc.Use(args[2], HostLoc::XMM1);
|
||||||
ctx.reg_alloc.EndOfAllocScope();
|
ctx.reg_alloc.EndOfAllocScope();
|
||||||
ctx.reg_alloc.HostCall(nullptr);
|
ctx.reg_alloc.HostCall(nullptr);
|
||||||
code.CallFunction(memory_write_128);
|
code.CallFunction(memory_write_128);
|
||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
|
ctx.reg_alloc.HostCall(nullptr, {}, args[1], args[2]);
|
||||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||||
}
|
}
|
||||||
if (ordered) {
|
if (ordered) {
|
||||||
code.mfence();
|
code.mfence();
|
||||||
}
|
}
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,22 +169,26 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
|
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]);
|
||||||
const int value_idx = bitsize == 128
|
const int value_idx = bitsize == 128
|
||||||
? ctx.reg_alloc.UseXmm(args[1]).getIdx()
|
? ctx.reg_alloc.UseXmm(args[2]).getIdx()
|
||||||
: (ordered ? ctx.reg_alloc.UseScratchGpr(args[1]).getIdx() : ctx.reg_alloc.UseGpr(args[1]).getIdx());
|
: (ordered ? ctx.reg_alloc.UseScratchGpr(args[2]).getIdx() : ctx.reg_alloc.UseGpr(args[2]).getIdx());
|
||||||
|
|
||||||
const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
|
const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
|
||||||
|
|
||||||
Xbyak::Label abort, end;
|
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
|
||||||
bool require_abort_handling = false;
|
|
||||||
|
|
||||||
if (fastmem_marker) {
|
if (fastmem_marker) {
|
||||||
// Use fastmem
|
// Use fastmem
|
||||||
const auto dest_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling);
|
bool require_abort_handling;
|
||||||
|
const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
|
||||||
|
|
||||||
const auto location = EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
|
const auto location = EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
|
||||||
|
|
||||||
|
ctx.deferred_emits.emplace_back([=, this, &ctx] {
|
||||||
|
code.L(*abort);
|
||||||
|
code.call(wrapped_fn);
|
||||||
|
|
||||||
fastmem_patch_info.emplace(
|
fastmem_patch_info.emplace(
|
||||||
mcl::bit_cast<u64>(location),
|
mcl::bit_cast<u64>(location),
|
||||||
FastmemPatchInfo{
|
FastmemPatchInfo{
|
||||||
@ -185,34 +197,36 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
*fastmem_marker,
|
*fastmem_marker,
|
||||||
conf.recompile_on_fastmem_failure,
|
conf.recompile_on_fastmem_failure,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst, end.get());
|
||||||
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Use page table
|
// Use page table
|
||||||
ASSERT(conf.page_table);
|
ASSERT(conf.page_table);
|
||||||
const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, abort, vaddr);
|
const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
|
||||||
require_abort_handling = true;
|
|
||||||
EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
|
EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
|
||||||
}
|
|
||||||
code.L(end);
|
|
||||||
|
|
||||||
if (require_abort_handling) {
|
ctx.deferred_emits.emplace_back([=, this, &ctx] {
|
||||||
code.SwitchToFarCode();
|
code.L(*abort);
|
||||||
code.L(abort);
|
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
code.jmp(end, code.T_NEAR);
|
EmitCheckMemoryAbort(ctx, inst, end.get());
|
||||||
code.SwitchToNearCode();
|
code.jmp(*end, code.T_NEAR);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
code.L(*end);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||||
ASSERT(conf.global_monitor != nullptr);
|
ASSERT(conf.global_monitor != nullptr);
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const bool ordered = IsOrdered(args[1].GetImmediateAccType());
|
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
||||||
|
|
||||||
if constexpr (bitsize != 128) {
|
if constexpr (bitsize != 128) {
|
||||||
using T = mcl::unsigned_integer_of_size<bitsize>;
|
using T = mcl::unsigned_integer_of_size<bitsize>;
|
||||||
|
|
||||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
ctx.reg_alloc.HostCall(inst, {}, args[1]);
|
||||||
|
|
||||||
code.mov(code.byte[r15 + offsetof(AxxJitState, exclusive_state)], u8(1));
|
code.mov(code.byte[r15 + offsetof(AxxJitState, exclusive_state)], u8(1));
|
||||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||||
@ -228,7 +242,7 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||||
ctx.reg_alloc.Use(args[0], ABI_PARAM2);
|
ctx.reg_alloc.Use(args[1], ABI_PARAM2);
|
||||||
ctx.reg_alloc.EndOfAllocScope();
|
ctx.reg_alloc.EndOfAllocScope();
|
||||||
ctx.reg_alloc.HostCall(nullptr);
|
ctx.reg_alloc.HostCall(nullptr);
|
||||||
|
|
||||||
@ -250,19 +264,21 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||||
ASSERT(conf.global_monitor != nullptr);
|
ASSERT(conf.global_monitor != nullptr);
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
const bool ordered = IsOrdered(args[3].GetImmediateAccType());
|
||||||
|
|
||||||
if constexpr (bitsize != 128) {
|
if constexpr (bitsize != 128) {
|
||||||
ctx.reg_alloc.HostCall(inst, {}, args[0], args[1]);
|
ctx.reg_alloc.HostCall(inst, {}, args[1], args[2]);
|
||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.Use(args[0], ABI_PARAM2);
|
ctx.reg_alloc.Use(args[1], ABI_PARAM2);
|
||||||
ctx.reg_alloc.Use(args[1], HostLoc::XMM1);
|
ctx.reg_alloc.Use(args[2], HostLoc::XMM1);
|
||||||
ctx.reg_alloc.EndOfAllocScope();
|
ctx.reg_alloc.EndOfAllocScope();
|
||||||
ctx.reg_alloc.HostCall(inst);
|
ctx.reg_alloc.HostCall(inst);
|
||||||
}
|
}
|
||||||
@ -308,6 +324,8 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
ctx.reg_alloc.ReleaseStackSpace(16 + ABI_SHADOW_SPACE);
|
ctx.reg_alloc.ReleaseStackSpace(16 + ABI_SHADOW_SPACE);
|
||||||
}
|
}
|
||||||
code.L(end);
|
code.L(end);
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
@ -329,7 +347,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
|
|||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
|
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]);
|
||||||
const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx();
|
const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm().getIdx() : ctx.reg_alloc.ScratchGpr().getIdx();
|
||||||
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
||||||
const Xbyak::Reg64 tmp2 = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 tmp2 = ctx.reg_alloc.ScratchGpr();
|
||||||
@ -344,10 +362,10 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
|
|||||||
|
|
||||||
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
||||||
if (fastmem_marker) {
|
if (fastmem_marker) {
|
||||||
Xbyak::Label abort, end;
|
SharedLabel abort = GenSharedLabel(), end = GenSharedLabel();
|
||||||
bool require_abort_handling = false;
|
bool require_abort_handling = false;
|
||||||
|
|
||||||
const auto src_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling);
|
const auto src_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
|
||||||
|
|
||||||
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
|
||||||
|
|
||||||
@ -360,14 +378,14 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
|
|||||||
conf.recompile_on_exclusive_fastmem_failure,
|
conf.recompile_on_exclusive_fastmem_failure,
|
||||||
});
|
});
|
||||||
|
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
if (require_abort_handling) {
|
if (require_abort_handling) {
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, this] {
|
||||||
code.L(abort);
|
code.L(*abort);
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
@ -383,6 +401,8 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
|
|||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.DefineValue(inst, Xbyak::Reg64{value_idx});
|
ctx.reg_alloc.DefineValue(inst, Xbyak::Reg64{value_idx});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t bitsize, auto callback>
|
template<std::size_t bitsize, auto callback>
|
||||||
@ -402,13 +422,13 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RBX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RBX);
|
||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RCX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RCX);
|
||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RDX);
|
||||||
return ctx.reg_alloc.UseXmm(args[1]);
|
return ctx.reg_alloc.UseXmm(args[2]);
|
||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.ScratchGpr(HostLoc::RAX);
|
ctx.reg_alloc.ScratchGpr(HostLoc::RAX);
|
||||||
return ctx.reg_alloc.UseGpr(args[1]);
|
return ctx.reg_alloc.UseGpr(args[2]);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
|
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[1]);
|
||||||
const Xbyak::Reg32 status = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 status = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
@ -416,14 +436,14 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
|
|
||||||
EmitExclusiveLock(code, conf, tmp, eax);
|
EmitExclusiveLock(code, conf, tmp, eax);
|
||||||
|
|
||||||
Xbyak::Label end;
|
SharedLabel end = GenSharedLabel();
|
||||||
|
|
||||||
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
|
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
|
||||||
code.mov(status, u32(1));
|
code.mov(status, u32(1));
|
||||||
code.cmp(code.byte[r15 + offsetof(AxxJitState, exclusive_state)], u8(0));
|
code.cmp(code.byte[r15 + offsetof(AxxJitState, exclusive_state)], u8(0));
|
||||||
code.je(end, code.T_NEAR);
|
code.je(*end, code.T_NEAR);
|
||||||
code.cmp(qword[tmp], vaddr);
|
code.cmp(qword[tmp], vaddr);
|
||||||
code.jne(end, code.T_NEAR);
|
code.jne(*end, code.T_NEAR);
|
||||||
|
|
||||||
EmitExclusiveTestAndClear(code, conf, vaddr, tmp, rax);
|
EmitExclusiveTestAndClear(code, conf, vaddr, tmp, rax);
|
||||||
|
|
||||||
@ -448,10 +468,10 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
|
|
||||||
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
const auto fastmem_marker = ShouldFastmem(ctx, inst);
|
||||||
if (fastmem_marker) {
|
if (fastmem_marker) {
|
||||||
Xbyak::Label abort;
|
SharedLabel abort = GenSharedLabel();
|
||||||
bool require_abort_handling = false;
|
bool require_abort_handling = false;
|
||||||
|
|
||||||
const auto dest_ptr = EmitFastmemVAddr(code, ctx, abort, vaddr, require_abort_handling, tmp);
|
const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling, tmp);
|
||||||
|
|
||||||
const auto location = code.getCurr();
|
const auto location = code.getCurr();
|
||||||
|
|
||||||
@ -483,8 +503,8 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
|
|
||||||
code.setnz(status.cvt8());
|
code.setnz(status.cvt8());
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, this] {
|
||||||
code.L(abort);
|
code.L(*abort);
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
|
|
||||||
fastmem_patch_info.emplace(
|
fastmem_patch_info.emplace(
|
||||||
@ -499,8 +519,8 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
code.cmp(al, 0);
|
code.cmp(al, 0);
|
||||||
code.setz(status.cvt8());
|
code.setz(status.cvt8());
|
||||||
code.movzx(status.cvt32(), status.cvt8());
|
code.movzx(status.cvt32(), status.cvt8());
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
} else {
|
} else {
|
||||||
code.call(wrapped_fn);
|
code.call(wrapped_fn);
|
||||||
code.cmp(al, 0);
|
code.cmp(al, 0);
|
||||||
@ -508,11 +528,13 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
|
|||||||
code.movzx(status.cvt32(), status.cvt8());
|
code.movzx(status.cvt32(), status.cvt8());
|
||||||
}
|
}
|
||||||
|
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
EmitExclusiveUnlock(code, conf, tmp, eax);
|
EmitExclusiveUnlock(code, conf, tmp, eax);
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, status);
|
ctx.reg_alloc.DefineValue(inst, status);
|
||||||
|
|
||||||
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef AxxEmitX64
|
#undef AxxEmitX64
|
||||||
|
@ -53,19 +53,19 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
|
|||||||
|
|
||||||
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask;
|
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask;
|
||||||
|
|
||||||
Xbyak::Label detect_boundary, resume;
|
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
|
||||||
|
|
||||||
code.jnz(detect_boundary, code.T_NEAR);
|
code.jnz(*detect_boundary, code.T_NEAR);
|
||||||
code.L(resume);
|
code.L(*resume);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code] {
|
||||||
code.L(detect_boundary);
|
code.L(*detect_boundary);
|
||||||
code.mov(tmp, vaddr);
|
code.mov(tmp, vaddr);
|
||||||
code.and_(tmp, page_align_mask);
|
code.and_(tmp, page_align_mask);
|
||||||
code.cmp(tmp, page_align_mask);
|
code.cmp(tmp, page_align_mask);
|
||||||
code.jne(resume, code.T_NEAR);
|
code.jne(*resume, code.T_NEAR);
|
||||||
// NOTE: We expect to fallthrough into abort code here.
|
// NOTE: We expect to fallthrough into abort code here.
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename EmitContext>
|
template<typename EmitContext>
|
||||||
|
@ -112,14 +112,13 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std::
|
|||||||
code.cmp(bitmask, 0);
|
code.cmp(bitmask, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Xbyak::Label end;
|
SharedLabel end = GenSharedLabel(), nan = GenSharedLabel();
|
||||||
Xbyak::Label nan;
|
|
||||||
|
|
||||||
code.jnz(nan, code.T_NEAR);
|
code.jnz(*nan, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(nan);
|
code.L(*nan);
|
||||||
|
|
||||||
const Xbyak::Xmm result = xmms[0];
|
const Xbyak::Xmm result = xmms[0];
|
||||||
|
|
||||||
@ -127,7 +126,7 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std::
|
|||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
|
|
||||||
const size_t stack_space = xmms.size() * 16;
|
const size_t stack_space = xmms.size() * 16;
|
||||||
code.sub(rsp, stack_space + ABI_SHADOW_SPACE);
|
code.sub(rsp, static_cast<u32>(stack_space + ABI_SHADOW_SPACE));
|
||||||
for (size_t i = 0; i < xmms.size(); ++i) {
|
for (size_t i = 0; i < xmms.size(); ++i) {
|
||||||
code.movaps(xword[rsp + ABI_SHADOW_SPACE + i * 16], xmms[i]);
|
code.movaps(xword[rsp + ABI_SHADOW_SPACE + i * 16], xmms[i]);
|
||||||
}
|
}
|
||||||
@ -137,11 +136,11 @@ void HandleNaNs(BlockOfCode& code, EmitContext& ctx, bool fpcr_controlled, std::
|
|||||||
code.CallFunction(nan_handler);
|
code.CallFunction(nan_handler);
|
||||||
|
|
||||||
code.movaps(result, xword[rsp + ABI_SHADOW_SPACE + 0 * 16]);
|
code.movaps(result, xword[rsp + ABI_SHADOW_SPACE + 0 * 16]);
|
||||||
code.add(rsp, stack_space + ABI_SHADOW_SPACE);
|
code.add(rsp, static_cast<u32>(stack_space + ABI_SHADOW_SPACE));
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t fsize>
|
template<size_t fsize>
|
||||||
@ -1117,7 +1116,7 @@ void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
const Xbyak::Xmm xmm_c = ctx.reg_alloc.UseXmm(args[2]);
|
const Xbyak::Xmm xmm_c = ctx.reg_alloc.UseXmm(args[2]);
|
||||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||||
code.movaps(result, xmm_a);
|
code.movaps(result, xmm_a);
|
||||||
@ -1127,19 +1126,19 @@ void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||||||
code.andnps(tmp, result);
|
code.andnps(tmp, result);
|
||||||
FCODE(vcmpeq_uqp)(tmp, tmp, GetSmallestNormalVector<fsize>(code));
|
FCODE(vcmpeq_uqp)(tmp, tmp, GetSmallestNormalVector<fsize>(code));
|
||||||
code.vptest(tmp, tmp);
|
code.vptest(tmp, tmp);
|
||||||
code.jnz(fallback, code.T_NEAR);
|
code.jnz(*fallback, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
});
|
});
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
EmitFourOpFallbackWithoutRegAlloc(code, ctx, result, xmm_a, xmm_b, xmm_c, fallback_fn, fpcr_controlled);
|
EmitFourOpFallbackWithoutRegAlloc(code, ctx, result, xmm_a, xmm_b, xmm_c, fallback_fn, fpcr_controlled);
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
@ -1377,7 +1376,7 @@ static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
|||||||
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||||
code.movaps(result, GetVectorOf<fsize, false, 0, 2>(code));
|
code.movaps(result, GetVectorOf<fsize, false, 0, 2>(code));
|
||||||
@ -1385,19 +1384,19 @@ static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
|||||||
|
|
||||||
FCODE(vcmpunordp)(tmp, result, result);
|
FCODE(vcmpunordp)(tmp, result, result);
|
||||||
code.vptest(tmp, tmp);
|
code.vptest(tmp, tmp);
|
||||||
code.jnz(fallback, code.T_NEAR);
|
code.jnz(*fallback, code.T_NEAR);
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
});
|
});
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled);
|
EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled);
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
@ -1591,7 +1590,7 @@ static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
|||||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
const Xbyak::Xmm mask = ctx.reg_alloc.ScratchXmm();
|
const Xbyak::Xmm mask = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
Xbyak::Label end, fallback;
|
SharedLabel end = GenSharedLabel(), fallback = GenSharedLabel();
|
||||||
|
|
||||||
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
|
||||||
code.vmovaps(result, GetVectorOf<fsize, false, 0, 3>(code));
|
code.vmovaps(result, GetVectorOf<fsize, false, 0, 3>(code));
|
||||||
@ -1602,21 +1601,21 @@ static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
|||||||
FCODE(vandp)(tmp, result, mask);
|
FCODE(vandp)(tmp, result, mask);
|
||||||
ICODE(vpcmpeq)(tmp, tmp, mask);
|
ICODE(vpcmpeq)(tmp, tmp, mask);
|
||||||
code.ptest(tmp, tmp);
|
code.ptest(tmp, tmp);
|
||||||
code.jnz(fallback, code.T_NEAR);
|
code.jnz(*fallback, code.T_NEAR);
|
||||||
|
|
||||||
FCODE(vmulp)(result, result, GetVectorOf<fsize, false, -1, 1>(code));
|
FCODE(vmulp)(result, result, GetVectorOf<fsize, false, -1, 1>(code));
|
||||||
code.L(end);
|
code.L(*end);
|
||||||
});
|
});
|
||||||
|
|
||||||
code.SwitchToFarCode();
|
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
|
||||||
code.L(fallback);
|
code.L(*fallback);
|
||||||
code.sub(rsp, 8);
|
code.sub(rsp, 8);
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled);
|
EmitThreeOpFallbackWithoutRegAlloc(code, ctx, result, operand1, operand2, fallback_fn, fpcr_controlled);
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
ABI_PopCallerSaveRegistersAndAdjustStackExcept(code, HostLocXmmIdx(result.getIdx()));
|
||||||
code.add(rsp, 8);
|
code.add(rsp, 8);
|
||||||
code.jmp(end, code.T_NEAR);
|
code.jmp(*end, code.T_NEAR);
|
||||||
code.SwitchToNearCode();
|
});
|
||||||
|
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
return;
|
return;
|
||||||
|
@ -69,6 +69,10 @@ std::string DisassembleAArch32([[maybe_unused]] bool is_thumb, [[maybe_unused]]
|
|||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (length) {
|
while (length) {
|
||||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(instructions), length, pc, buffer, sizeof(buffer));
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(instructions), length, pc, buffer, sizeof(buffer));
|
||||||
|
const char* const disassembled = inst_size > 0 ? buffer : "<invalid instruction>";
|
||||||
|
|
||||||
|
if (inst_size == 0)
|
||||||
|
inst_size = is_thumb ? 2 : 4;
|
||||||
|
|
||||||
result += fmt::format("{:08x} ", pc);
|
result += fmt::format("{:08x} ", pc);
|
||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
@ -78,11 +82,9 @@ std::string DisassembleAArch32([[maybe_unused]] bool is_thumb, [[maybe_unused]]
|
|||||||
result += " ";
|
result += " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result += inst_size > 0 ? buffer : "<invalid instruction>";
|
result += disassembled;
|
||||||
result += '\n';
|
result += '\n';
|
||||||
|
|
||||||
if (inst_size == 0)
|
|
||||||
inst_size = is_thumb ? 2 : 4;
|
|
||||||
if (length <= inst_size)
|
if (length <= inst_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -111,7 +113,8 @@ std::string DisassembleAArch64([[maybe_unused]] u32 instruction, [[maybe_unused]
|
|||||||
|
|
||||||
char buffer[80];
|
char buffer[80];
|
||||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
|
||||||
result = inst_size > 0 ? buffer : "<invalid instruction>";
|
result = fmt::format("{:016x} {:08x} ", pc, instruction);
|
||||||
|
result += inst_size > 0 ? buffer : "<invalid instruction>";
|
||||||
result += '\n';
|
result += '\n';
|
||||||
|
|
||||||
LLVMDisasmDispose(llvm_ctx);
|
LLVMDisasmDispose(llvm_ctx);
|
||||||
|
@ -245,40 +245,40 @@ IR::UAny IREmitter::ReadMemory(size_t bitsize, const IR::U32& vaddr, IR::AccType
|
|||||||
}
|
}
|
||||||
|
|
||||||
IR::U8 IREmitter::ReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U8 IREmitter::ReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U8>(Opcode::A32ReadMemory8, vaddr, IR::Value{acc_type});
|
return Inst<IR::U8>(Opcode::A32ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U16 IREmitter::ReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U16 IREmitter::ReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U16>(Opcode::A32ReadMemory16, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U16>(Opcode::A32ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
return current_location.EFlag() ? ByteReverseHalf(value) : value;
|
return current_location.EFlag() ? ByteReverseHalf(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U32 IREmitter::ReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U32>(Opcode::A32ReadMemory32, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U32>(Opcode::A32ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
return current_location.EFlag() ? ByteReverseWord(value) : value;
|
return current_location.EFlag() ? ByteReverseWord(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U64 IREmitter::ReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U64 IREmitter::ReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U64>(Opcode::A32ReadMemory64, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U64>(Opcode::A32ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
return current_location.EFlag() ? ByteReverseDual(value) : value;
|
return current_location.EFlag() ? ByteReverseDual(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U8>(Opcode::A32ExclusiveReadMemory8, vaddr, IR::Value{acc_type});
|
return Inst<IR::U8>(Opcode::A32ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U16>(Opcode::A32ExclusiveReadMemory16, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U16>(Opcode::A32ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
return current_location.EFlag() ? ByteReverseHalf(value) : value;
|
return current_location.EFlag() ? ByteReverseHalf(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U32>(Opcode::A32ExclusiveReadMemory32, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U32>(Opcode::A32ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
return current_location.EFlag() ? ByteReverseWord(value) : value;
|
return current_location.EFlag() ? ByteReverseWord(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<IR::U32, IR::U32> IREmitter::ExclusiveReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) {
|
std::pair<IR::U32, IR::U32> IREmitter::ExclusiveReadMemory64(const IR::U32& vaddr, IR::AccType acc_type) {
|
||||||
const auto value = Inst<IR::U64>(Opcode::A32ExclusiveReadMemory64, vaddr, IR::Value{acc_type});
|
const auto value = Inst<IR::U64>(Opcode::A32ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
const auto lo = LeastSignificantWord(value);
|
const auto lo = LeastSignificantWord(value);
|
||||||
const auto hi = MostSignificantWord(value).result;
|
const auto hi = MostSignificantWord(value).result;
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
@ -303,55 +303,55 @@ void IREmitter::WriteMemory(size_t bitsize, const IR::U32& vaddr, const IR::UAny
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A32WriteMemory8, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto v = ByteReverseHalf(value);
|
const auto v = ByteReverseHalf(value);
|
||||||
Inst(Opcode::A32WriteMemory16, vaddr, v, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
Inst(Opcode::A32WriteMemory16, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto v = ByteReverseWord(value);
|
const auto v = ByteReverseWord(value);
|
||||||
Inst(Opcode::A32WriteMemory32, vaddr, v, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
Inst(Opcode::A32WriteMemory32, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory64(const IR::U32& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory64(const IR::U32& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto v = ByteReverseDual(value);
|
const auto v = ByteReverseDual(value);
|
||||||
Inst(Opcode::A32WriteMemory64, vaddr, v, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
Inst(Opcode::A32WriteMemory64, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A32WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U32& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory8, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U32& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto v = ByteReverseHalf(value);
|
const auto v = ByteReverseHalf(value);
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, vaddr, v, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U32& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto v = ByteReverseWord(value);
|
const auto v = ByteReverseWord(value);
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, vaddr, v, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, v, IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,9 +359,9 @@ IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U32& vaddr, const IR::U32& v
|
|||||||
if (current_location.EFlag()) {
|
if (current_location.EFlag()) {
|
||||||
const auto vlo = ByteReverseWord(value_lo);
|
const auto vlo = ByteReverseWord(value_lo);
|
||||||
const auto vhi = ByteReverseWord(value_hi);
|
const auto vhi = ByteReverseWord(value_hi);
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(vlo, vhi), IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, Pack2x32To1x64(vlo, vhi), IR::Value{acc_type});
|
||||||
} else {
|
} else {
|
||||||
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(value_lo, value_hi), IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, Pack2x32To1x64(value_lo, value_hi), IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,4 +439,8 @@ void IREmitter::CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer,
|
|||||||
Inst(Opcode::A32CoprocStoreWords, IR::Value(coproc_info), address);
|
Inst(Opcode::A32CoprocStoreWords, IR::Value(coproc_info), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::U64 IREmitter::ImmCurrentLocationDescriptor() {
|
||||||
|
return Imm64(IR::LocationDescriptor{current_location}.Value());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -110,6 +110,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
enum ArchVersion arch_version;
|
enum ArchVersion arch_version;
|
||||||
|
IR::U64 ImmCurrentLocationDescriptor();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -29,12 +29,6 @@ struct TranslationOptions {
|
|||||||
/// If this is false, we treat the instruction as a NOP.
|
/// If this is false, we treat the instruction as a NOP.
|
||||||
/// If this is true, we emit an ExceptionRaised instruction.
|
/// If this is true, we emit an ExceptionRaised instruction.
|
||||||
bool hook_hint_instructions = true;
|
bool hook_hint_instructions = true;
|
||||||
|
|
||||||
/// This changes what IR we emit when we translate a memory instruction.
|
|
||||||
/// If this is false, memory accesses are not considered terminal.
|
|
||||||
/// If this is true, memory access are considered terminal. This allows
|
|
||||||
/// accurately emulating protection fault handlers.
|
|
||||||
bool check_halt_on_memory_access = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,15 +53,6 @@ bool TranslatorVisitor::RaiseException(Exception exception) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::MemoryInstructionContinues() {
|
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(static_cast<s32>(current_instruction_size))});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::UAny TranslatorVisitor::I(size_t bitsize, u64 value) {
|
IR::UAny TranslatorVisitor::I(size_t bitsize, u64 value) {
|
||||||
switch (bitsize) {
|
switch (bitsize) {
|
||||||
case 8:
|
case 8:
|
||||||
|
@ -41,7 +41,6 @@ struct TranslatorVisitor final {
|
|||||||
bool UndefinedInstruction();
|
bool UndefinedInstruction();
|
||||||
bool DecodeError();
|
bool DecodeError();
|
||||||
bool RaiseException(Exception exception);
|
bool RaiseException(Exception exception);
|
||||||
bool MemoryInstructionContinues();
|
|
||||||
|
|
||||||
struct ImmAndCarry {
|
struct ImmAndCarry {
|
||||||
u32 imm32;
|
u32 imm32;
|
||||||
|
@ -119,7 +119,7 @@ bool TranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
|
bool TranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
|
||||||
@ -176,7 +176,7 @@ bool TranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, size_t sz, bool T, bool a, Reg m) {
|
bool TranslatorVisitor::v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, size_t sz, bool T, bool a, Reg m) {
|
||||||
@ -241,7 +241,7 @@ bool TranslatorVisitor::v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, si
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
|
bool TranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
|
||||||
@ -305,7 +305,7 @@ bool TranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
|
bool TranslatorVisitor::v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
|
||||||
@ -370,6 +370,6 @@ bool TranslatorVisitor::v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -83,7 +83,7 @@ bool TranslatorVisitor::arm_LDR_lit(Cond cond, bool U, Reg t, Imm<12> imm12) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDR <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDR <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -120,7 +120,7 @@ bool TranslatorVisitor::arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDR <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDR <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -150,7 +150,7 @@ bool TranslatorVisitor::arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRB <Rt>, [PC, #+/-<imm>]
|
// LDRB <Rt>, [PC, #+/-<imm>]
|
||||||
@ -170,7 +170,7 @@ bool TranslatorVisitor::arm_LDRB_lit(Cond cond, bool U, Reg t, Imm<12> imm12) {
|
|||||||
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRB <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDRB <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -199,7 +199,7 @@ bool TranslatorVisitor::arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDRB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -223,7 +223,7 @@ bool TranslatorVisitor::arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRD <Rt>, <Rt2>, [PC, #+/-<imm>]
|
// LDRD <Rt>, <Rt2>, [PC, #+/-<imm>]
|
||||||
@ -257,7 +257,7 @@ bool TranslatorVisitor::arm_LDRD_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm
|
|||||||
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
||||||
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
||||||
}
|
}
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRD <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDRD <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -303,7 +303,7 @@ bool TranslatorVisitor::arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
||||||
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
||||||
}
|
}
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRD <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDRD <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -343,7 +343,7 @@ bool TranslatorVisitor::arm_LDRD_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
ir.SetRegister(t, ir.LeastSignificantWord(data));
|
||||||
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
ir.SetRegister(t2, ir.MostSignificantWord(data).result);
|
||||||
}
|
}
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [PC, #-/+<imm>]
|
// LDRH <Rt>, [PC, #-/+<imm>]
|
||||||
@ -368,7 +368,7 @@ bool TranslatorVisitor::arm_LDRH_lit(Cond cond, bool P, bool U, bool W, Reg t, I
|
|||||||
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDRH <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -397,7 +397,7 @@ bool TranslatorVisitor::arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDRH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -421,7 +421,7 @@ bool TranslatorVisitor::arm_LDRH_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSB <Rt>, [PC, #+/-<imm>]
|
// LDRSB <Rt>, [PC, #+/-<imm>]
|
||||||
@ -442,7 +442,7 @@ bool TranslatorVisitor::arm_LDRSB_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Im
|
|||||||
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSB <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDRSB <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -471,7 +471,7 @@ bool TranslatorVisitor::arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n,
|
|||||||
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDRSB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -495,7 +495,7 @@ bool TranslatorVisitor::arm_LDRSB_reg(Cond cond, bool P, bool U, bool W, Reg n,
|
|||||||
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSH <Rt>, [PC, #-/+<imm>]
|
// LDRSH <Rt>, [PC, #-/+<imm>]
|
||||||
@ -515,7 +515,7 @@ bool TranslatorVisitor::arm_LDRSH_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Im
|
|||||||
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSH <Rt>, [<Rn>, #+/-<imm>]{!}
|
// LDRSH <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -544,7 +544,7 @@ bool TranslatorVisitor::arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n,
|
|||||||
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// LDRSH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -568,7 +568,7 @@ bool TranslatorVisitor::arm_LDRSH_reg(Cond cond, bool P, bool U, bool W, Reg n,
|
|||||||
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STR <Rt>, [<Rn>, #+/-<imm>]{!}
|
// STR <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -585,7 +585,7 @@ bool TranslatorVisitor::arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Re
|
|||||||
const auto offset = ir.Imm32(imm12.ZeroExtend());
|
const auto offset = ir.Imm32(imm12.ZeroExtend());
|
||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::NORMAL);
|
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STR <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// STR <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -606,7 +606,7 @@ bool TranslatorVisitor::arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Re
|
|||||||
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
|
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
|
||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::NORMAL);
|
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRB <Rt>, [<Rn>, #+/-<imm>]{!}
|
// STRB <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -627,7 +627,7 @@ bool TranslatorVisitor::arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto offset = ir.Imm32(imm12.ZeroExtend());
|
const auto offset = ir.Imm32(imm12.ZeroExtend());
|
||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::NORMAL);
|
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// STRB <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -648,7 +648,7 @@ bool TranslatorVisitor::arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
|
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
|
||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::NORMAL);
|
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRD <Rt>, [<Rn>, #+/-<imm>]{!}
|
// STRD <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -686,7 +686,7 @@ bool TranslatorVisitor::arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
|
|
||||||
// NOTE: If alignment is exactly off by 4, each word is an atomic access.
|
// NOTE: If alignment is exactly off by 4, each word is an atomic access.
|
||||||
ir.WriteMemory64(address, data, IR::AccType::ATOMIC);
|
ir.WriteMemory64(address, data, IR::AccType::ATOMIC);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRD <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// STRD <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -723,7 +723,7 @@ bool TranslatorVisitor::arm_STRD_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
|
|
||||||
// NOTE: If alignment is exactly off by 4, each word is an atomic access.
|
// NOTE: If alignment is exactly off by 4, each word is an atomic access.
|
||||||
ir.WriteMemory64(address, data, IR::AccType::ATOMIC);
|
ir.WriteMemory64(address, data, IR::AccType::ATOMIC);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRH <Rt>, [<Rn>, #+/-<imm>]{!}
|
// STRH <Rt>, [<Rn>, #+/-<imm>]{!}
|
||||||
@ -746,7 +746,7 @@ bool TranslatorVisitor::arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
|
|
||||||
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::NORMAL);
|
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
// STRH <Rt>, [<Rn>, #+/-<Rm>]{!}
|
||||||
@ -768,31 +768,29 @@ bool TranslatorVisitor::arm_STRH_reg(Cond cond, bool P, bool U, bool W, Reg n, R
|
|||||||
const auto address = GetAddress(ir, P, U, W, n, offset);
|
const auto address = GetAddress(ir, P, U, W, n, offset);
|
||||||
|
|
||||||
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::NORMAL);
|
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LDMHelper(TranslatorVisitor& v, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
|
static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
|
||||||
auto address = start_address;
|
auto address = start_address;
|
||||||
for (size_t i = 0; i <= 14; i++) {
|
for (size_t i = 0; i <= 14; i++) {
|
||||||
if (mcl::bit::get_bit(i, list)) {
|
if (mcl::bit::get_bit(i, list)) {
|
||||||
v.ir.SetRegister(static_cast<Reg>(i), v.ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
ir.SetRegister(static_cast<Reg>(i), ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
address = v.ir.Add(address, v.ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (W && !mcl::bit::get_bit(RegNumber(n), list)) {
|
if (W && !mcl::bit::get_bit(RegNumber(n), list)) {
|
||||||
v.ir.SetRegister(n, writeback_address);
|
ir.SetRegister(n, writeback_address);
|
||||||
}
|
}
|
||||||
if (mcl::bit::get_bit<15>(list)) {
|
if (mcl::bit::get_bit<15>(list)) {
|
||||||
v.ir.LoadWritePC(v.ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
ir.LoadWritePC(ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
if (v.options.check_halt_on_memory_access)
|
if (n == Reg::R13)
|
||||||
v.ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
ir.SetTerm(IR::Term::PopRSBHint{});
|
||||||
else if (n == Reg::R13)
|
|
||||||
v.ir.SetTerm(IR::Term::PopRSBHint{});
|
|
||||||
else
|
else
|
||||||
v.ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDM <Rn>{!}, <reg_list>
|
// LDM <Rn>{!}, <reg_list>
|
||||||
@ -810,7 +808,7 @@ bool TranslatorVisitor::arm_LDM(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.GetRegister(n);
|
const auto start_address = ir.GetRegister(n);
|
||||||
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4)));
|
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4)));
|
||||||
return LDMHelper(*this, W, n, list, start_address, writeback_address);
|
return LDMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDMDA <Rn>{!}, <reg_list>
|
// LDMDA <Rn>{!}, <reg_list>
|
||||||
@ -828,7 +826,7 @@ bool TranslatorVisitor::arm_LDMDA(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4)));
|
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4)));
|
||||||
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
|
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
|
||||||
return LDMHelper(*this, W, n, list, start_address, writeback_address);
|
return LDMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDMDB <Rn>{!}, <reg_list>
|
// LDMDB <Rn>{!}, <reg_list>
|
||||||
@ -846,7 +844,7 @@ bool TranslatorVisitor::arm_LDMDB(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
||||||
const auto writeback_address = start_address;
|
const auto writeback_address = start_address;
|
||||||
return LDMHelper(*this, W, n, list, start_address, writeback_address);
|
return LDMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDMIB <Rn>{!}, <reg_list>
|
// LDMIB <Rn>{!}, <reg_list>
|
||||||
@ -864,7 +862,7 @@ bool TranslatorVisitor::arm_LDMIB(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
|
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
|
||||||
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
||||||
return LDMHelper(*this, W, n, list, start_address, writeback_address);
|
return LDMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::arm_LDM_usr() {
|
bool TranslatorVisitor::arm_LDM_usr() {
|
||||||
@ -875,21 +873,21 @@ bool TranslatorVisitor::arm_LDM_eret() {
|
|||||||
return InterpretThisInstruction();
|
return InterpretThisInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool STMHelper(TranslatorVisitor& v, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
|
static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
|
||||||
auto address = start_address;
|
auto address = start_address;
|
||||||
for (size_t i = 0; i <= 14; i++) {
|
for (size_t i = 0; i <= 14; i++) {
|
||||||
if (mcl::bit::get_bit(i, list)) {
|
if (mcl::bit::get_bit(i, list)) {
|
||||||
v.ir.WriteMemory32(address, v.ir.GetRegister(static_cast<Reg>(i)), IR::AccType::ATOMIC);
|
ir.WriteMemory32(address, ir.GetRegister(static_cast<Reg>(i)), IR::AccType::ATOMIC);
|
||||||
address = v.ir.Add(address, v.ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (W) {
|
if (W) {
|
||||||
v.ir.SetRegister(n, writeback_address);
|
ir.SetRegister(n, writeback_address);
|
||||||
}
|
}
|
||||||
if (mcl::bit::get_bit<15>(list)) {
|
if (mcl::bit::get_bit<15>(list)) {
|
||||||
v.ir.WriteMemory32(address, v.ir.Imm32(v.ir.PC()), IR::AccType::ATOMIC);
|
ir.WriteMemory32(address, ir.Imm32(ir.PC()), IR::AccType::ATOMIC);
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STM <Rn>{!}, <reg_list>
|
// STM <Rn>{!}, <reg_list>
|
||||||
@ -904,7 +902,7 @@ bool TranslatorVisitor::arm_STM(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.GetRegister(n);
|
const auto start_address = ir.GetRegister(n);
|
||||||
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4)));
|
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(mcl::bit::count_ones(list) * 4)));
|
||||||
return STMHelper(*this, W, n, list, start_address, writeback_address);
|
return STMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STMDA <Rn>{!}, <reg_list>
|
// STMDA <Rn>{!}, <reg_list>
|
||||||
@ -919,7 +917,7 @@ bool TranslatorVisitor::arm_STMDA(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4)));
|
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list) - 4)));
|
||||||
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
|
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
|
||||||
return STMHelper(*this, W, n, list, start_address, writeback_address);
|
return STMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STMDB <Rn>{!}, <reg_list>
|
// STMDB <Rn>{!}, <reg_list>
|
||||||
@ -934,7 +932,7 @@ bool TranslatorVisitor::arm_STMDB(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
||||||
const auto writeback_address = start_address;
|
const auto writeback_address = start_address;
|
||||||
return STMHelper(*this, W, n, list, start_address, writeback_address);
|
return STMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// STMIB <Rn>{!}, <reg_list>
|
// STMIB <Rn>{!}, <reg_list>
|
||||||
@ -949,7 +947,7 @@ bool TranslatorVisitor::arm_STMIB(Cond cond, bool W, Reg n, RegList list) {
|
|||||||
|
|
||||||
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
|
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
|
||||||
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * mcl::bit::count_ones(list))));
|
||||||
return STMHelper(*this, W, n, list, start_address, writeback_address);
|
return STMHelper(ir, W, n, list, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::arm_STM_usr() {
|
bool TranslatorVisitor::arm_STM_usr() {
|
||||||
|
@ -29,7 +29,7 @@ bool TranslatorVisitor::arm_SWP(Cond cond, Reg n, Reg t, Reg t2) {
|
|||||||
ir.WriteMemory32(ir.GetRegister(n), ir.GetRegister(t2), IR::AccType::SWAP);
|
ir.WriteMemory32(ir.GetRegister(n), ir.GetRegister(t2), IR::AccType::SWAP);
|
||||||
// TODO: Alignment check
|
// TODO: Alignment check
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SWPB<c> <Rt>, <Rt2>, [<Rn>]
|
// SWPB<c> <Rt>, <Rt2>, [<Rn>]
|
||||||
@ -48,7 +48,7 @@ bool TranslatorVisitor::arm_SWPB(Cond cond, Reg n, Reg t, Reg t2) {
|
|||||||
ir.WriteMemory8(ir.GetRegister(n), ir.LeastSignificantByte(ir.GetRegister(t2)), IR::AccType::SWAP);
|
ir.WriteMemory8(ir.GetRegister(n), ir.LeastSignificantByte(ir.GetRegister(t2)), IR::AccType::SWAP);
|
||||||
// TODO: Alignment check
|
// TODO: Alignment check
|
||||||
ir.SetRegister(t, ir.ZeroExtendByteToWord(data));
|
ir.SetRegister(t, ir.ZeroExtendByteToWord(data));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDA<c> <Rt>, [<Rn>]
|
// LDA<c> <Rt>, [<Rn>]
|
||||||
@ -63,7 +63,7 @@ bool TranslatorVisitor::arm_LDA(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ReadMemory32(address, IR::AccType::ORDERED));
|
ir.SetRegister(t, ir.ReadMemory32(address, IR::AccType::ORDERED));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
// LDAB<c> <Rt>, [<Rn>]
|
// LDAB<c> <Rt>, [<Rn>]
|
||||||
bool TranslatorVisitor::arm_LDAB(Cond cond, Reg n, Reg t) {
|
bool TranslatorVisitor::arm_LDAB(Cond cond, Reg n, Reg t) {
|
||||||
@ -77,7 +77,7 @@ bool TranslatorVisitor::arm_LDAB(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory8(address, IR::AccType::ORDERED)));
|
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory8(address, IR::AccType::ORDERED)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
// LDAH<c> <Rt>, [<Rn>]
|
// LDAH<c> <Rt>, [<Rn>]
|
||||||
bool TranslatorVisitor::arm_LDAH(Cond cond, Reg n, Reg t) {
|
bool TranslatorVisitor::arm_LDAH(Cond cond, Reg n, Reg t) {
|
||||||
@ -91,7 +91,7 @@ bool TranslatorVisitor::arm_LDAH(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory16(address, IR::AccType::ORDERED)));
|
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory16(address, IR::AccType::ORDERED)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAEX<c> <Rt>, [<Rn>]
|
// LDAEX<c> <Rt>, [<Rn>]
|
||||||
@ -106,7 +106,7 @@ bool TranslatorVisitor::arm_LDAEX(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ExclusiveReadMemory32(address, IR::AccType::ORDERED));
|
ir.SetRegister(t, ir.ExclusiveReadMemory32(address, IR::AccType::ORDERED));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAEXB<c> <Rt>, [<Rn>]
|
// LDAEXB<c> <Rt>, [<Rn>]
|
||||||
@ -121,7 +121,7 @@ bool TranslatorVisitor::arm_LDAEXB(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ORDERED)));
|
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ORDERED)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAEXD<c> <Rt>, <Rt2>, [<Rn>]
|
// LDAEXD<c> <Rt>, <Rt2>, [<Rn>]
|
||||||
@ -139,7 +139,7 @@ bool TranslatorVisitor::arm_LDAEXD(Cond cond, Reg n, Reg t) {
|
|||||||
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
||||||
ir.SetRegister(t, lo);
|
ir.SetRegister(t, lo);
|
||||||
ir.SetRegister(t + 1, hi);
|
ir.SetRegister(t + 1, hi);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAEXH<c> <Rt>, [<Rn>]
|
// LDAEXH<c> <Rt>, [<Rn>]
|
||||||
@ -154,7 +154,7 @@ bool TranslatorVisitor::arm_LDAEXH(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ORDERED)));
|
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ORDERED)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STL<c> <Rt>, [<Rn>]
|
// STL<c> <Rt>, [<Rn>]
|
||||||
@ -169,7 +169,7 @@ bool TranslatorVisitor::arm_STL(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::ORDERED);
|
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::ORDERED);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STLB<c> <Rt>, [<Rn>]
|
// STLB<c> <Rt>, [<Rn>]
|
||||||
@ -184,7 +184,7 @@ bool TranslatorVisitor::arm_STLB(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::ORDERED);
|
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)), IR::AccType::ORDERED);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STLH<c> <Rd>, <Rt>, [<Rn>]
|
// STLH<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -199,7 +199,7 @@ bool TranslatorVisitor::arm_STLH(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::ORDERED);
|
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)), IR::AccType::ORDERED);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STLEXB<c> <Rd>, <Rt>, [<Rn>]
|
// STLEXB<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -220,7 +220,7 @@ bool TranslatorVisitor::arm_STLEXB(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ORDERED);
|
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ORDERED);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
// STLEXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
|
// STLEXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
|
||||||
bool TranslatorVisitor::arm_STLEXD(Cond cond, Reg n, Reg d, Reg t) {
|
bool TranslatorVisitor::arm_STLEXD(Cond cond, Reg n, Reg d, Reg t) {
|
||||||
@ -242,7 +242,7 @@ bool TranslatorVisitor::arm_STLEXD(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value_hi = ir.GetRegister(t2);
|
const auto value_hi = ir.GetRegister(t2);
|
||||||
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ORDERED);
|
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ORDERED);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STLEXH<c> <Rd>, <Rt>, [<Rn>]
|
// STLEXH<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -263,7 +263,7 @@ bool TranslatorVisitor::arm_STLEXH(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ORDERED);
|
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ORDERED);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STLEX<c> <Rd>, <Rt>, [<Rn>]
|
// STLEX<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -284,7 +284,7 @@ bool TranslatorVisitor::arm_STLEX(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.GetRegister(t);
|
const auto value = ir.GetRegister(t);
|
||||||
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ORDERED);
|
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ORDERED);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDREX<c> <Rt>, [<Rn>]
|
// LDREX<c> <Rt>, [<Rn>]
|
||||||
@ -299,7 +299,7 @@ bool TranslatorVisitor::arm_LDREX(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ExclusiveReadMemory32(address, IR::AccType::ATOMIC));
|
ir.SetRegister(t, ir.ExclusiveReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDREXB<c> <Rt>, [<Rn>]
|
// LDREXB<c> <Rt>, [<Rn>]
|
||||||
@ -314,7 +314,7 @@ bool TranslatorVisitor::arm_LDREXB(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ATOMIC)));
|
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ATOMIC)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDREXD<c> <Rt>, <Rt2>, [<Rn>]
|
// LDREXD<c> <Rt>, <Rt2>, [<Rn>]
|
||||||
@ -332,7 +332,7 @@ bool TranslatorVisitor::arm_LDREXD(Cond cond, Reg n, Reg t) {
|
|||||||
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
||||||
ir.SetRegister(t, lo);
|
ir.SetRegister(t, lo);
|
||||||
ir.SetRegister(t + 1, hi);
|
ir.SetRegister(t + 1, hi);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDREXH<c> <Rt>, [<Rn>]
|
// LDREXH<c> <Rt>, [<Rn>]
|
||||||
@ -347,7 +347,7 @@ bool TranslatorVisitor::arm_LDREXH(Cond cond, Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ATOMIC)));
|
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ATOMIC)));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STREX<c> <Rd>, <Rt>, [<Rn>]
|
// STREX<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -368,7 +368,7 @@ bool TranslatorVisitor::arm_STREX(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.GetRegister(t);
|
const auto value = ir.GetRegister(t);
|
||||||
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STREXB<c> <Rd>, <Rt>, [<Rn>]
|
// STREXB<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -389,7 +389,7 @@ bool TranslatorVisitor::arm_STREXB(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STREXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
|
// STREXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
|
||||||
@ -412,7 +412,7 @@ bool TranslatorVisitor::arm_STREXD(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value_hi = ir.GetRegister(t2);
|
const auto value_hi = ir.GetRegister(t2);
|
||||||
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STREXH<c> <Rd>, <Rt>, [<Rn>]
|
// STREXH<c> <Rd>, <Rt>, [<Rn>]
|
||||||
@ -433,7 +433,7 @@ bool TranslatorVisitor::arm_STREXH(Cond cond, Reg n, Reg d, Reg t) {
|
|||||||
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -449,7 +449,7 @@ bool TranslatorVisitor::thumb16_LDR_literal(Reg t, Imm<8> imm8) {
|
|||||||
const auto data = ir.ReadMemory32(ir.Imm32(address), IR::AccType::NORMAL);
|
const auto data = ir.ReadMemory32(ir.Imm32(address), IR::AccType::NORMAL);
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STR <Rt>, [<Rn>, <Rm>]
|
// STR <Rt>, [<Rn>, <Rm>]
|
||||||
@ -459,7 +459,7 @@ bool TranslatorVisitor::thumb16_STR_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.GetRegister(t);
|
const auto data = ir.GetRegister(t);
|
||||||
|
|
||||||
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRH <Rt>, [<Rn>, <Rm>]
|
// STRH <Rt>, [<Rn>, <Rm>]
|
||||||
@ -469,7 +469,7 @@ bool TranslatorVisitor::thumb16_STRH_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
|
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
|
||||||
|
|
||||||
ir.WriteMemory16(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory16(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRB <Rt>, [<Rn>, <Rm>]
|
// STRB <Rt>, [<Rn>, <Rm>]
|
||||||
@ -479,7 +479,7 @@ bool TranslatorVisitor::thumb16_STRB_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.LeastSignificantByte(ir.GetRegister(t));
|
const auto data = ir.LeastSignificantByte(ir.GetRegister(t));
|
||||||
|
|
||||||
ir.WriteMemory8(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory8(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRSB <Rt>, [<Rn>, <Rm>]
|
// LDRSB <Rt>, [<Rn>, <Rm>]
|
||||||
@ -489,7 +489,7 @@ bool TranslatorVisitor::thumb16_LDRSB_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDR <Rt>, [<Rn>, <Rm>]
|
// LDR <Rt>, [<Rn>, <Rm>]
|
||||||
@ -499,7 +499,7 @@ bool TranslatorVisitor::thumb16_LDR_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [<Rn>, <Rm>]
|
// LDRH <Rt>, [<Rn>, <Rm>]
|
||||||
@ -509,7 +509,7 @@ bool TranslatorVisitor::thumb16_LDRH_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRB <Rt>, [<Rn>, <Rm>]
|
// LDRB <Rt>, [<Rn>, <Rm>]
|
||||||
@ -519,7 +519,7 @@ bool TranslatorVisitor::thumb16_LDRB_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [<Rn>, <Rm>]
|
// LDRH <Rt>, [<Rn>, <Rm>]
|
||||||
@ -529,7 +529,7 @@ bool TranslatorVisitor::thumb16_LDRSH_reg(Reg m, Reg n, Reg t) {
|
|||||||
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STR <Rt>, [<Rn>, #<imm>]
|
// STR <Rt>, [<Rn>, #<imm>]
|
||||||
@ -540,7 +540,7 @@ bool TranslatorVisitor::thumb16_STR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
|
|||||||
const auto data = ir.GetRegister(t);
|
const auto data = ir.GetRegister(t);
|
||||||
|
|
||||||
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDR <Rt>, [<Rn>, #<imm>]
|
// LDR <Rt>, [<Rn>, #<imm>]
|
||||||
@ -551,7 +551,7 @@ bool TranslatorVisitor::thumb16_LDR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
|
|||||||
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRB <Rt>, [<Rn>, #<imm>]
|
// STRB <Rt>, [<Rn>, #<imm>]
|
||||||
@ -573,7 +573,7 @@ bool TranslatorVisitor::thumb16_LDRB_imm(Imm<5> imm5, Reg n, Reg t) {
|
|||||||
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STRH <Rt>, [<Rn>, #<imm5>]
|
// STRH <Rt>, [<Rn>, #<imm5>]
|
||||||
@ -583,7 +583,7 @@ bool TranslatorVisitor::thumb16_STRH_imm(Imm<5> imm5, Reg n, Reg t) {
|
|||||||
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
|
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
|
||||||
|
|
||||||
ir.WriteMemory16(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory16(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDRH <Rt>, [<Rn>, #<imm5>]
|
// LDRH <Rt>, [<Rn>, #<imm5>]
|
||||||
@ -593,7 +593,7 @@ bool TranslatorVisitor::thumb16_LDRH_imm(Imm<5> imm5, Reg n, Reg t) {
|
|||||||
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// STR <Rt>, [<Rn>, #<imm>]
|
// STR <Rt>, [<Rn>, #<imm>]
|
||||||
@ -605,7 +605,7 @@ bool TranslatorVisitor::thumb16_STR_imm_t2(Reg t, Imm<8> imm8) {
|
|||||||
const auto data = ir.GetRegister(t);
|
const auto data = ir.GetRegister(t);
|
||||||
|
|
||||||
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
ir.WriteMemory32(address, data, IR::AccType::NORMAL);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDR <Rt>, [<Rn>, #<imm>]
|
// LDR <Rt>, [<Rn>, #<imm>]
|
||||||
@ -617,7 +617,7 @@ bool TranslatorVisitor::thumb16_LDR_imm_t2(Reg t, Imm<8> imm8) {
|
|||||||
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
const auto data = ir.ReadMemory32(address, IR::AccType::NORMAL);
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADR <Rd>, <label>
|
// ADR <Rd>, <label>
|
||||||
@ -775,7 +775,7 @@ bool TranslatorVisitor::thumb16_PUSH(bool M, RegList reg_list) {
|
|||||||
|
|
||||||
ir.SetRegister(Reg::SP, final_address);
|
ir.SetRegister(Reg::SP, final_address);
|
||||||
// TODO(optimization): Possible location for an RSB push.
|
// TODO(optimization): Possible location for an RSB push.
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POP <reg_list>
|
// POP <reg_list>
|
||||||
@ -804,15 +804,11 @@ bool TranslatorVisitor::thumb16_POP(bool P, RegList reg_list) {
|
|||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
address = ir.Add(address, ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
ir.SetRegister(Reg::SP, address);
|
ir.SetRegister(Reg::SP, address);
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::PopRSBHint{});
|
ir.SetTerm(IR::Term::PopRSBHint{});
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
ir.SetRegister(Reg::SP, address);
|
ir.SetRegister(Reg::SP, address);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,7 +887,7 @@ bool TranslatorVisitor::thumb16_STMIA(Reg n, RegList reg_list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(n, address);
|
ir.SetRegister(n, address);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDM <Rn>!, <reg_list>
|
// LDM <Rn>!, <reg_list>
|
||||||
@ -914,7 +910,7 @@ bool TranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) {
|
|||||||
if (write_back) {
|
if (write_back) {
|
||||||
ir.SetRegister(n, address);
|
ir.SetRegister(n, address);
|
||||||
}
|
}
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CB{N}Z <Rn>, <label>
|
// CB{N}Z <Rn>, <label>
|
||||||
|
@ -34,7 +34,7 @@ static bool LoadByteLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12,
|
|||||||
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory8(v.ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory8(v.ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
v.ir.SetRegister(t, data);
|
v.ir.SetRegister(t, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) {
|
static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) {
|
||||||
@ -49,7 +49,7 @@ static bool LoadByteRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re
|
|||||||
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory8(address, IR::AccType::NORMAL));
|
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory8(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
v.ir.SetRegister(t, data);
|
v.ir.SetRegister(t, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) {
|
static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) {
|
||||||
@ -64,7 +64,7 @@ static bool LoadByteImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U
|
|||||||
if (W) {
|
if (W) {
|
||||||
v.ir.SetRegister(n, offset_address);
|
v.ir.SetRegister(n, offset_address);
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_PLD_lit(bool /*U*/, Imm<12> /*imm12*/) {
|
bool TranslatorVisitor::thumb32_PLD_lit(bool /*U*/, Imm<12> /*imm12*/) {
|
||||||
|
@ -16,7 +16,7 @@ static bool LoadHalfLiteral(TranslatorVisitor& v, bool U, Reg t, Imm<12> imm12,
|
|||||||
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory16(v.ir.Imm32(address), IR::AccType::NORMAL));
|
const auto data = (v.ir.*ext_fn)(v.ir.ReadMemory16(v.ir.Imm32(address), IR::AccType::NORMAL));
|
||||||
|
|
||||||
v.ir.SetRegister(t, data);
|
v.ir.SetRegister(t, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) {
|
static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Reg m, ExtensionFunction ext_fn) {
|
||||||
@ -31,7 +31,7 @@ static bool LoadHalfRegister(TranslatorVisitor& v, Reg n, Reg t, Imm<2> imm2, Re
|
|||||||
const IR::U32 data = (v.ir.*ext_fn)(v.ir.ReadMemory16(address, IR::AccType::NORMAL));
|
const IR::U32 data = (v.ir.*ext_fn)(v.ir.ReadMemory16(address, IR::AccType::NORMAL));
|
||||||
|
|
||||||
v.ir.SetRegister(t, data);
|
v.ir.SetRegister(t, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) {
|
static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U, bool W, Imm<12> imm12, ExtensionFunction ext_fn) {
|
||||||
@ -48,7 +48,7 @@ static bool LoadHalfImmediate(TranslatorVisitor& v, Reg n, Reg t, bool P, bool U
|
|||||||
}
|
}
|
||||||
|
|
||||||
v.ir.SetRegister(t, data);
|
v.ir.SetRegister(t, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDRH_lit(bool U, Reg t, Imm<12> imm12) {
|
bool TranslatorVisitor::thumb32_LDRH_lit(bool U, Reg t, Imm<12> imm12) {
|
||||||
|
@ -36,11 +36,7 @@ static bool TableBranch(TranslatorVisitor& v, Reg n, Reg m, bool half) {
|
|||||||
|
|
||||||
v.ir.UpdateUpperLocationDescriptor();
|
v.ir.UpdateUpperLocationDescriptor();
|
||||||
v.ir.BranchWritePC(branch_value);
|
v.ir.BranchWritePC(branch_value);
|
||||||
if (v.options.check_halt_on_memory_access) {
|
|
||||||
v.ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
|
||||||
} else {
|
|
||||||
v.ir.SetTerm(IR::Term::FastDispatchHint{});
|
v.ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +68,7 @@ static bool LoadDualImmediate(TranslatorVisitor& v, bool P, bool U, bool W, Reg
|
|||||||
if (W) {
|
if (W) {
|
||||||
v.ir.SetRegister(n, offset_address);
|
v.ir.SetRegister(n, offset_address);
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadDualLiteral(TranslatorVisitor& v, bool U, bool W, Reg t, Reg t2, Imm<8> imm8) {
|
static bool LoadDualLiteral(TranslatorVisitor& v, bool U, bool W, Reg t, Reg t2, Imm<8> imm8) {
|
||||||
@ -98,7 +94,7 @@ static bool LoadDualLiteral(TranslatorVisitor& v, bool U, bool W, Reg t, Reg t2,
|
|||||||
v.ir.SetRegister(t2, v.ir.MostSignificantWord(data).result);
|
v.ir.SetRegister(t2, v.ir.MostSignificantWord(data).result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool StoreDual(TranslatorVisitor& v, bool P, bool U, bool W, Reg n, Reg t, Reg t2, Imm<8> imm8) {
|
static bool StoreDual(TranslatorVisitor& v, bool P, bool U, bool W, Reg n, Reg t, Reg t2, Imm<8> imm8) {
|
||||||
@ -127,7 +123,7 @@ static bool StoreDual(TranslatorVisitor& v, bool P, bool U, bool W, Reg n, Reg t
|
|||||||
if (W) {
|
if (W) {
|
||||||
v.ir.SetRegister(n, offset_address);
|
v.ir.SetRegister(n, offset_address);
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDA(Reg n, Reg t) {
|
bool TranslatorVisitor::thumb32_LDA(Reg n, Reg t) {
|
||||||
@ -173,7 +169,7 @@ bool TranslatorVisitor::thumb32_LDREX(Reg n, Reg t, Imm<8> imm8) {
|
|||||||
const auto value = ir.ExclusiveReadMemory32(address, IR::AccType::ATOMIC);
|
const auto value = ir.ExclusiveReadMemory32(address, IR::AccType::ATOMIC);
|
||||||
|
|
||||||
ir.SetRegister(t, value);
|
ir.SetRegister(t, value);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDREXB(Reg n, Reg t) {
|
bool TranslatorVisitor::thumb32_LDREXB(Reg n, Reg t) {
|
||||||
@ -185,7 +181,7 @@ bool TranslatorVisitor::thumb32_LDREXB(Reg n, Reg t) {
|
|||||||
const auto value = ir.ZeroExtendToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ATOMIC));
|
const auto value = ir.ZeroExtendToWord(ir.ExclusiveReadMemory8(address, IR::AccType::ATOMIC));
|
||||||
|
|
||||||
ir.SetRegister(t, value);
|
ir.SetRegister(t, value);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDREXD(Reg n, Reg t, Reg t2) {
|
bool TranslatorVisitor::thumb32_LDREXD(Reg n, Reg t, Reg t2) {
|
||||||
@ -199,7 +195,7 @@ bool TranslatorVisitor::thumb32_LDREXD(Reg n, Reg t, Reg t2) {
|
|||||||
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
|
||||||
ir.SetRegister(t, lo);
|
ir.SetRegister(t, lo);
|
||||||
ir.SetRegister(t2, hi);
|
ir.SetRegister(t2, hi);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDREXH(Reg n, Reg t) {
|
bool TranslatorVisitor::thumb32_LDREXH(Reg n, Reg t) {
|
||||||
@ -211,7 +207,7 @@ bool TranslatorVisitor::thumb32_LDREXH(Reg n, Reg t) {
|
|||||||
const auto value = ir.ZeroExtendToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ATOMIC));
|
const auto value = ir.ZeroExtendToWord(ir.ExclusiveReadMemory16(address, IR::AccType::ATOMIC));
|
||||||
|
|
||||||
ir.SetRegister(t, value);
|
ir.SetRegister(t, value);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STL(Reg n, Reg t) {
|
bool TranslatorVisitor::thumb32_STL(Reg n, Reg t) {
|
||||||
@ -221,7 +217,7 @@ bool TranslatorVisitor::thumb32_STL(Reg n, Reg t) {
|
|||||||
|
|
||||||
const auto address = ir.GetRegister(n);
|
const auto address = ir.GetRegister(n);
|
||||||
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::ORDERED);
|
ir.WriteMemory32(address, ir.GetRegister(t), IR::AccType::ORDERED);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STREX(Reg n, Reg t, Reg d, Imm<8> imm8) {
|
bool TranslatorVisitor::thumb32_STREX(Reg n, Reg t, Reg d, Imm<8> imm8) {
|
||||||
@ -236,7 +232,7 @@ bool TranslatorVisitor::thumb32_STREX(Reg n, Reg t, Reg d, Imm<8> imm8) {
|
|||||||
const auto value = ir.GetRegister(t);
|
const auto value = ir.GetRegister(t);
|
||||||
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory32(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STREXB(Reg n, Reg t, Reg d) {
|
bool TranslatorVisitor::thumb32_STREXB(Reg n, Reg t, Reg d) {
|
||||||
@ -251,7 +247,7 @@ bool TranslatorVisitor::thumb32_STREXB(Reg n, Reg t, Reg d) {
|
|||||||
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory8(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STREXD(Reg n, Reg t, Reg t2, Reg d) {
|
bool TranslatorVisitor::thumb32_STREXD(Reg n, Reg t, Reg t2, Reg d) {
|
||||||
@ -267,7 +263,7 @@ bool TranslatorVisitor::thumb32_STREXD(Reg n, Reg t, Reg t2, Reg d) {
|
|||||||
const auto value_hi = ir.GetRegister(t2);
|
const auto value_hi = ir.GetRegister(t2);
|
||||||
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STREXH(Reg n, Reg t, Reg d) {
|
bool TranslatorVisitor::thumb32_STREXH(Reg n, Reg t, Reg d) {
|
||||||
@ -282,7 +278,7 @@ bool TranslatorVisitor::thumb32_STREXH(Reg n, Reg t, Reg d) {
|
|||||||
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
|
||||||
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ATOMIC);
|
const auto passed = ir.ExclusiveWriteMemory16(address, value, IR::AccType::ATOMIC);
|
||||||
ir.SetRegister(d, passed);
|
ir.SetRegister(d, passed);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_TBB(Reg n, Reg m) {
|
bool TranslatorVisitor::thumb32_TBB(Reg n, Reg m) {
|
||||||
|
@ -12,44 +12,42 @@ static bool ITBlockCheck(const A32::IREmitter& ir) {
|
|||||||
return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock();
|
return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LDMHelper(TranslatorVisitor& v, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) {
|
static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) {
|
||||||
auto address = start_address;
|
auto address = start_address;
|
||||||
for (size_t i = 0; i <= 14; i++) {
|
for (size_t i = 0; i <= 14; i++) {
|
||||||
if (mcl::bit::get_bit(i, list)) {
|
if (mcl::bit::get_bit(i, list)) {
|
||||||
v.ir.SetRegister(static_cast<Reg>(i), v.ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
ir.SetRegister(static_cast<Reg>(i), ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
address = v.ir.Add(address, v.ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (W && !mcl::bit::get_bit(RegNumber(n), list)) {
|
if (W && !mcl::bit::get_bit(RegNumber(n), list)) {
|
||||||
v.ir.SetRegister(n, writeback_address);
|
ir.SetRegister(n, writeback_address);
|
||||||
}
|
}
|
||||||
if (mcl::bit::get_bit<15>(list)) {
|
if (mcl::bit::get_bit<15>(list)) {
|
||||||
v.ir.UpdateUpperLocationDescriptor();
|
ir.UpdateUpperLocationDescriptor();
|
||||||
v.ir.LoadWritePC(v.ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
ir.LoadWritePC(ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
if (v.options.check_halt_on_memory_access) {
|
if (n == Reg::R13) {
|
||||||
v.ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
ir.SetTerm(IR::Term::PopRSBHint{});
|
||||||
} else if (n == Reg::R13) {
|
|
||||||
v.ir.SetTerm(IR::Term::PopRSBHint{});
|
|
||||||
} else {
|
} else {
|
||||||
v.ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool STMHelper(TranslatorVisitor& v, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) {
|
static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, const IR::U32& start_address, const IR::U32& writeback_address) {
|
||||||
auto address = start_address;
|
auto address = start_address;
|
||||||
for (size_t i = 0; i <= 14; i++) {
|
for (size_t i = 0; i <= 14; i++) {
|
||||||
if (mcl::bit::get_bit(i, list)) {
|
if (mcl::bit::get_bit(i, list)) {
|
||||||
v.ir.WriteMemory32(address, v.ir.GetRegister(static_cast<Reg>(i)), IR::AccType::ATOMIC);
|
ir.WriteMemory32(address, ir.GetRegister(static_cast<Reg>(i)), IR::AccType::ATOMIC);
|
||||||
address = v.ir.Add(address, v.ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (W) {
|
if (W) {
|
||||||
v.ir.SetRegister(n, writeback_address);
|
ir.SetRegister(n, writeback_address);
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) {
|
bool TranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) {
|
||||||
@ -74,7 +72,7 @@ bool TranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) {
|
|||||||
|
|
||||||
// Start address is the same as the writeback address.
|
// Start address is the same as the writeback address.
|
||||||
const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs));
|
const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs));
|
||||||
return LDMHelper(*this, W, n, regs_imm, start_address, start_address);
|
return LDMHelper(ir, W, n, regs_imm, start_address, start_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list) {
|
bool TranslatorVisitor::thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list) {
|
||||||
@ -99,7 +97,7 @@ bool TranslatorVisitor::thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list) {
|
|||||||
|
|
||||||
const auto start_address = ir.GetRegister(n);
|
const auto start_address = ir.GetRegister(n);
|
||||||
const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4));
|
const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4));
|
||||||
return LDMHelper(*this, W, n, regs_imm, start_address, writeback_address);
|
return LDMHelper(ir, W, n, regs_imm, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_POP(Imm<16> reg_list) {
|
bool TranslatorVisitor::thumb32_POP(Imm<16> reg_list) {
|
||||||
@ -126,7 +124,7 @@ bool TranslatorVisitor::thumb32_STMIA(bool W, Reg n, Imm<15> reg_list) {
|
|||||||
|
|
||||||
const auto start_address = ir.GetRegister(n);
|
const auto start_address = ir.GetRegister(n);
|
||||||
const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4));
|
const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4));
|
||||||
return STMHelper(*this, W, n, regs_imm, start_address, writeback_address);
|
return STMHelper(ir, W, n, regs_imm, start_address, writeback_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_STMDB(bool W, Reg n, Imm<15> reg_list) {
|
bool TranslatorVisitor::thumb32_STMDB(bool W, Reg n, Imm<15> reg_list) {
|
||||||
@ -145,7 +143,7 @@ bool TranslatorVisitor::thumb32_STMDB(bool W, Reg n, Imm<15> reg_list) {
|
|||||||
|
|
||||||
// Start address is the same as the writeback address.
|
// Start address is the same as the writeback address.
|
||||||
const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs));
|
const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs));
|
||||||
return STMHelper(*this, W, n, regs_imm, start_address, start_address);
|
return STMHelper(ir, W, n, regs_imm, start_address, start_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -23,16 +23,12 @@ bool TranslatorVisitor::thumb32_LDR_lit(bool U, Reg t, Imm<12> imm12) {
|
|||||||
if (t == Reg::PC) {
|
if (t == Reg::PC) {
|
||||||
ir.UpdateUpperLocationDescriptor();
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, Imm<8> imm8) {
|
bool TranslatorVisitor::thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, Imm<8> imm8) {
|
||||||
@ -62,9 +58,7 @@ bool TranslatorVisitor::thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, I
|
|||||||
ir.UpdateUpperLocationDescriptor();
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
|
|
||||||
if (options.check_halt_on_memory_access) {
|
if (!P && W && n == Reg::R13) {
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
|
||||||
} else if (!P && W && n == Reg::R13) {
|
|
||||||
ir.SetTerm(IR::Term::PopRSBHint{});
|
ir.SetTerm(IR::Term::PopRSBHint{});
|
||||||
} else {
|
} else {
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
@ -74,7 +68,7 @@ bool TranslatorVisitor::thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDR_imm12(Reg n, Reg t, Imm<12> imm12) {
|
bool TranslatorVisitor::thumb32_LDR_imm12(Reg n, Reg t, Imm<12> imm12) {
|
||||||
@ -90,16 +84,12 @@ bool TranslatorVisitor::thumb32_LDR_imm12(Reg n, Reg t, Imm<12> imm12) {
|
|||||||
if (t == Reg::PC) {
|
if (t == Reg::PC) {
|
||||||
ir.UpdateUpperLocationDescriptor();
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDR_reg(Reg n, Reg t, Imm<2> imm2, Reg m) {
|
bool TranslatorVisitor::thumb32_LDR_reg(Reg n, Reg t, Imm<2> imm2, Reg m) {
|
||||||
@ -119,16 +109,12 @@ bool TranslatorVisitor::thumb32_LDR_reg(Reg n, Reg t, Imm<2> imm2, Reg m) {
|
|||||||
if (t == Reg::PC) {
|
if (t == Reg::PC) {
|
||||||
ir.UpdateUpperLocationDescriptor();
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
|
||||||
} else {
|
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir.SetRegister(t, data);
|
ir.SetRegister(t, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::thumb32_LDRT(Reg n, Reg t, Imm<8> imm8) {
|
bool TranslatorVisitor::thumb32_LDRT(Reg n, Reg t, Imm<8> imm8) {
|
||||||
|
@ -1201,7 +1201,7 @@ bool TranslatorVisitor::vfp_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm<8> i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VPUSH.{F32,F64} <list>
|
// VPUSH.{F32,F64} <list>
|
||||||
@ -1242,7 +1242,7 @@ bool TranslatorVisitor::vfp_VPUSH(Cond cond, bool D, size_t Vd, bool sz, Imm<8>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VLDR<c> <Dd>, [<Rn>{, #+/-<imm>}]
|
// VLDR<c> <Dd>, [<Rn>{, #+/-<imm>}]
|
||||||
@ -1268,7 +1268,7 @@ bool TranslatorVisitor::vfp_VLDR(Cond cond, bool U, bool D, Reg n, size_t Vd, bo
|
|||||||
ir.SetExtendedRegister(d, ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
ir.SetExtendedRegister(d, ir.ReadMemory32(address, IR::AccType::ATOMIC));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VSTR<c> <Dd>, [<Rn>{, #+/-<imm>}]
|
// VSTR<c> <Dd>, [<Rn>{, #+/-<imm>}]
|
||||||
@ -1295,7 +1295,7 @@ bool TranslatorVisitor::vfp_VSTR(Cond cond, bool U, bool D, Reg n, size_t Vd, bo
|
|||||||
ir.WriteMemory32(address, ir.GetExtendedRegister(d), IR::AccType::ATOMIC);
|
ir.WriteMemory32(address, ir.GetExtendedRegister(d), IR::AccType::ATOMIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VSTM{mode}<c> <Rn>{!}, <list of double registers>
|
// VSTM{mode}<c> <Rn>{!}, <list of double registers>
|
||||||
@ -1347,7 +1347,7 @@ bool TranslatorVisitor::vfp_VSTM_a1(Cond cond, bool p, bool u, bool D, bool w, R
|
|||||||
address = ir.Add(address, ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VSTM{mode}<c> <Rn>{!}, <list of single registers>
|
// VSTM{mode}<c> <Rn>{!}, <list of single registers>
|
||||||
@ -1390,7 +1390,7 @@ bool TranslatorVisitor::vfp_VSTM_a2(Cond cond, bool p, bool u, bool D, bool w, R
|
|||||||
address = ir.Add(address, ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VLDM{mode}<c> <Rn>{!}, <list of double registers>
|
// VLDM{mode}<c> <Rn>{!}, <list of double registers>
|
||||||
@ -1440,7 +1440,7 @@ bool TranslatorVisitor::vfp_VLDM_a1(Cond cond, bool p, bool u, bool D, bool w, R
|
|||||||
ir.SetExtendedRegister(d + i, ir.Pack2x32To1x64(word1, word2));
|
ir.SetExtendedRegister(d + i, ir.Pack2x32To1x64(word1, word2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VLDM{mode}<c> <Rn>{!}, <list of single registers>
|
// VLDM{mode}<c> <Rn>{!}, <list of single registers>
|
||||||
@ -1483,7 +1483,7 @@ bool TranslatorVisitor::vfp_VLDM_a2(Cond cond, bool p, bool u, bool D, bool w, R
|
|||||||
ir.SetExtendedRegister(d + i, word);
|
ir.SetExtendedRegister(d + i, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A32
|
} // namespace Dynarmic::A32
|
||||||
|
@ -107,83 +107,83 @@ void IREmitter::ClearExclusive() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U8>(Opcode::A64ReadMemory8, vaddr, IR::Value{acc_type});
|
return Inst<IR::U8>(Opcode::A64ReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U16>(Opcode::A64ReadMemory16, vaddr, IR::Value{acc_type});
|
return Inst<IR::U16>(Opcode::A64ReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ReadMemory32, vaddr, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U64>(Opcode::A64ReadMemory64, vaddr, IR::Value{acc_type});
|
return Inst<IR::U64>(Opcode::A64ReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U128>(Opcode::A64ReadMemory128, vaddr, IR::Value{acc_type});
|
return Inst<IR::U128>(Opcode::A64ReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U8>(Opcode::A64ExclusiveReadMemory8, vaddr, IR::Value{acc_type});
|
return Inst<IR::U8>(Opcode::A64ExclusiveReadMemory8, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U16>(Opcode::A64ExclusiveReadMemory16, vaddr, IR::Value{acc_type});
|
return Inst<IR::U16>(Opcode::A64ExclusiveReadMemory16, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveReadMemory32, vaddr, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveReadMemory32, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U64>(Opcode::A64ExclusiveReadMemory64, vaddr, IR::Value{acc_type});
|
return Inst<IR::U64>(Opcode::A64ExclusiveReadMemory64, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) {
|
IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr, IR::AccType acc_type) {
|
||||||
return Inst<IR::U128>(Opcode::A64ExclusiveReadMemory128, vaddr, IR::Value{acc_type});
|
return Inst<IR::U128>(Opcode::A64ExclusiveReadMemory128, ImmCurrentLocationDescriptor(), vaddr, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A64WriteMemory8, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A64WriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A64WriteMemory16, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A64WriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A64WriteMemory32, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A64WriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A64WriteMemory64, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A64WriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) {
|
void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) {
|
||||||
Inst(Opcode::A64WriteMemory128, vaddr, value, IR::Value{acc_type});
|
Inst(Opcode::A64WriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory8, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory8, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory16, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory16, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory32, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory32, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory64, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory64, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) {
|
IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value, IR::AccType acc_type) {
|
||||||
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory128, vaddr, value, IR::Value{acc_type});
|
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory128, ImmCurrentLocationDescriptor(), vaddr, value, IR::Value{acc_type});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::GetW(Reg reg) {
|
IR::U32 IREmitter::GetW(Reg reg) {
|
||||||
@ -262,4 +262,8 @@ void IREmitter::SetPC(const IR::U64& value) {
|
|||||||
Inst(Opcode::A64SetPC, value);
|
Inst(Opcode::A64SetPC, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::U64 IREmitter::ImmCurrentLocationDescriptor() {
|
||||||
|
return Imm64(IR::LocationDescriptor{*current_location}.Value());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A64
|
} // namespace Dynarmic::A64
|
||||||
|
@ -95,6 +95,9 @@ public:
|
|||||||
void SetFPCR(const IR::U32& value);
|
void SetFPCR(const IR::U32& value);
|
||||||
void SetFPSR(const IR::U32& value);
|
void SetFPSR(const IR::U32& value);
|
||||||
void SetPC(const IR::U64& value);
|
void SetPC(const IR::U64& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IR::U64 ImmCurrentLocationDescriptor();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Dynarmic::A64
|
} // namespace Dynarmic::A64
|
||||||
|
@ -35,12 +35,6 @@ struct TranslationOptions {
|
|||||||
/// If this is false, we treat the instruction as a NOP.
|
/// If this is false, we treat the instruction as a NOP.
|
||||||
/// If this is true, we emit an ExceptionRaised instruction.
|
/// If this is true, we emit an ExceptionRaised instruction.
|
||||||
bool hook_hint_instructions = true;
|
bool hook_hint_instructions = true;
|
||||||
|
|
||||||
/// This changes what IR we emit when we translate a memory instruction.
|
|
||||||
/// If this is false, memory accesses are not considered terminal.
|
|
||||||
/// If this is true, memory access are considered terminal. This allows
|
|
||||||
/// accurately emulating protection fault handlers.
|
|
||||||
bool check_halt_on_memory_access = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,15 +41,6 @@ bool TranslatorVisitor::RaiseException(Exception exception) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::MemoryInstructionContinues() {
|
|
||||||
if (options.check_halt_on_memory_access) {
|
|
||||||
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->AdvancePC(4)});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<TranslatorVisitor::BitMasks> TranslatorVisitor::DecodeBitMasks(bool immN, Imm<6> imms, Imm<6> immr, bool immediate) {
|
std::optional<TranslatorVisitor::BitMasks> TranslatorVisitor::DecodeBitMasks(bool immN, Imm<6> imms, Imm<6> immr, bool immediate) {
|
||||||
const int len = mcl::bit::highest_set_bit((immN ? 1 << 6 : 0) | (imms.ZeroExtend() ^ 0b111111));
|
const int len = mcl::bit::highest_set_bit((immN ? 1 << 6 : 0) | (imms.ZeroExtend() ^ 0b111111));
|
||||||
if (len < 1) {
|
if (len < 1) {
|
||||||
|
@ -30,7 +30,6 @@ struct TranslatorVisitor final {
|
|||||||
bool ReservedValue();
|
bool ReservedValue();
|
||||||
bool UnallocatedEncoding();
|
bool UnallocatedEncoding();
|
||||||
bool RaiseException(Exception exception);
|
bool RaiseException(Exception exception);
|
||||||
bool MemoryInstructionContinues();
|
|
||||||
|
|
||||||
struct BitMasks {
|
struct BitMasks {
|
||||||
u64 wmask, tmask;
|
u64 wmask, tmask;
|
||||||
|
@ -72,7 +72,7 @@ static bool ExclusiveSharedDecodeAndOperation(TranslatorVisitor& v, bool pair, s
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
|
bool TranslatorVisitor::STXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
|
||||||
@ -175,7 +175,7 @@ static bool OrderedSharedDecodeAndOperation(TranslatorVisitor& v, size_t size, b
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
bool TranslatorVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||||
|
@ -15,7 +15,7 @@ bool TranslatorVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
|
|||||||
const auto data = Mem(ir.Imm64(address), size, IR::AccType::NORMAL);
|
const auto data = Mem(ir.Imm64(address), size, IR::AccType::NORMAL);
|
||||||
|
|
||||||
X(8 * size, Rt, data);
|
X(8 * size, Rt, data);
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
bool TranslatorVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
||||||
@ -33,7 +33,7 @@ bool TranslatorVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
|||||||
} else {
|
} else {
|
||||||
V(128, Vt, ir.ZeroExtendToQuad(data));
|
V(128, Vt, ir.ZeroExtendToQuad(data));
|
||||||
}
|
}
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::LDRSW_lit(Imm<19> imm19, Reg Rt) {
|
bool TranslatorVisitor::LDRSW_lit(Imm<19> imm19, Reg Rt) {
|
||||||
@ -42,7 +42,7 @@ bool TranslatorVisitor::LDRSW_lit(Imm<19> imm19, Reg Rt) {
|
|||||||
const auto data = Mem(ir.Imm64(address), 4, IR::AccType::NORMAL);
|
const auto data = Mem(ir.Imm64(address), 4, IR::AccType::NORMAL);
|
||||||
|
|
||||||
X(64, Rt, ir.SignExtendWordToLong(data));
|
X(64, Rt, ir.SignExtendWordToLong(data));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::PRFM_lit(Imm<19> /*imm19*/, Imm<5> /*prfop*/) {
|
bool TranslatorVisitor::PRFM_lit(Imm<19> /*imm19*/, Imm<5> /*prfop*/) {
|
||||||
|
@ -104,7 +104,7 @@ static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
|
bool TranslatorVisitor::STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
|
||||||
|
@ -72,7 +72,7 @@ static bool LoadStoreRegisterImmediate(TranslatorVisitor& v, bool wback, bool po
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, Reg Rt) {
|
bool TranslatorVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, Reg Rt) {
|
||||||
@ -165,7 +165,7 @@ static bool LoadStoreSIMD(TranslatorVisitor& v, bool wback, bool postindex, size
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
|
bool TranslatorVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
|
||||||
|
@ -78,7 +78,7 @@ bool TranslatorVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
|
bool TranslatorVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
|
||||||
@ -148,7 +148,7 @@ bool TranslatorVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wbac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::A64
|
} // namespace Dynarmic::A64
|
||||||
|
@ -70,7 +70,7 @@ static bool RegSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 s
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
|
bool TranslatorVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
|
||||||
@ -128,7 +128,7 @@ static bool VecSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 s
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
|
bool TranslatorVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
|
||||||
|
@ -22,7 +22,7 @@ static bool StoreRegister(TranslatorVisitor& v, const size_t datasize, const Imm
|
|||||||
|
|
||||||
const IR::UAny data = v.X(datasize, Rt);
|
const IR::UAny data = v.X(datasize, Rt);
|
||||||
v.Mem(address, datasize / 8, acctype, data);
|
v.Mem(address, datasize / 8, acctype, data);
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadRegister(TranslatorVisitor& v, const size_t datasize, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
|
static bool LoadRegister(TranslatorVisitor& v, const size_t datasize, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
|
||||||
@ -42,7 +42,7 @@ static bool LoadRegister(TranslatorVisitor& v, const size_t datasize, const Imm<
|
|||||||
// max is used to zeroextend < 32 to 32, and > 32 to 64
|
// max is used to zeroextend < 32 to 32, and > 32 to 64
|
||||||
const size_t extended_size = std::max<size_t>(32, datasize);
|
const size_t extended_size = std::max<size_t>(32, datasize);
|
||||||
v.X(extended_size, Rt, v.ZeroExtend(data, extended_size));
|
v.X(extended_size, Rt, v.ZeroExtend(data, extended_size));
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LoadRegisterSigned(TranslatorVisitor& v, const size_t datasize, const Imm<2> opc, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
|
static bool LoadRegisterSigned(TranslatorVisitor& v, const size_t datasize, const Imm<2> opc, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
|
||||||
@ -90,7 +90,7 @@ static bool LoadRegisterSigned(TranslatorVisitor& v, const size_t datasize, cons
|
|||||||
// Prefetch(address, Rt);
|
// Prefetch(address, Rt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::STTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
|
bool TranslatorVisitor::STTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
|
||||||
@ -144,6 +144,6 @@ bool TranslatorVisitor::LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) {
|
|||||||
|
|
||||||
const IR::UAny data = Mem(address, 4, acctype);
|
const IR::UAny data = Mem(address, 4, acctype);
|
||||||
X(64, Rt, SignExtend(data, 64));
|
X(64, Rt, SignExtend(data, 64));
|
||||||
return MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace Dynarmic::A64
|
} // namespace Dynarmic::A64
|
||||||
|
@ -98,7 +98,7 @@ static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.MemoryInstructionContinues();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TranslatorVisitor::LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
|
bool TranslatorVisitor::LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
|
||||||
|
@ -229,9 +229,6 @@ struct UserConfig {
|
|||||||
// Minimum size is about 8MiB. Maximum size is about 2GiB. Maximum size is limited by
|
// Minimum size is about 8MiB. Maximum size is about 2GiB. Maximum size is limited by
|
||||||
// the maximum length of a x64 jump.
|
// the maximum length of a x64 jump.
|
||||||
size_t code_cache_size = 256 * 1024 * 1024; // bytes
|
size_t code_cache_size = 256 * 1024 * 1024; // bytes
|
||||||
// Determines the relative size of the near and far code caches. Must be smaller than
|
|
||||||
// code_cache_size.
|
|
||||||
size_t far_code_offset = 200 * 1024 * 1024; // bytes
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace A32
|
} // namespace A32
|
||||||
|
@ -288,9 +288,6 @@ struct UserConfig {
|
|||||||
// Minimum size is about 8MiB. Maximum size is about 2GiB. Maximum size is limited by
|
// Minimum size is about 8MiB. Maximum size is about 2GiB. Maximum size is limited by
|
||||||
// the maximum length of a x64 jump.
|
// the maximum length of a x64 jump.
|
||||||
size_t code_cache_size = 256 * 1024 * 1024; // bytes
|
size_t code_cache_size = 256 * 1024 * 1024; // bytes
|
||||||
// Determines the relative size of the near and far code caches. Must be smaller than
|
|
||||||
// code_cache_size.
|
|
||||||
size_t far_code_offset = 200 * 1024 * 1024; // bytes
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace A64
|
} // namespace A64
|
||||||
|
@ -12,6 +12,7 @@ namespace Dynarmic {
|
|||||||
enum class HaltReason : std::uint32_t {
|
enum class HaltReason : std::uint32_t {
|
||||||
Step = 0x00000001,
|
Step = 0x00000001,
|
||||||
CacheInvalidation = 0x00000002,
|
CacheInvalidation = 0x00000002,
|
||||||
|
MemoryAbort = 0x00000004,
|
||||||
UserDefined1 = 0x01000000,
|
UserDefined1 = 0x01000000,
|
||||||
UserDefined2 = 0x02000000,
|
UserDefined2 = 0x02000000,
|
||||||
UserDefined3 = 0x04000000,
|
UserDefined3 = 0x04000000,
|
||||||
|
72
externals/dynarmic/src/dynarmic/ir/opcodes.inc
vendored
72
externals/dynarmic/src/dynarmic/ir/opcodes.inc
vendored
@ -688,45 +688,45 @@ OPCODE(FPVectorToUnsignedFixed64, U128, U128
|
|||||||
|
|
||||||
// A32 Memory access
|
// A32 Memory access
|
||||||
A32OPC(ClearExclusive, Void, )
|
A32OPC(ClearExclusive, Void, )
|
||||||
A32OPC(ReadMemory8, U8, U32, AccType )
|
A32OPC(ReadMemory8, U8, U64, U32, AccType )
|
||||||
A32OPC(ReadMemory16, U16, U32, AccType )
|
A32OPC(ReadMemory16, U16, U64, U32, AccType )
|
||||||
A32OPC(ReadMemory32, U32, U32, AccType )
|
A32OPC(ReadMemory32, U32, U64, U32, AccType )
|
||||||
A32OPC(ReadMemory64, U64, U32, AccType )
|
A32OPC(ReadMemory64, U64, U64, U32, AccType )
|
||||||
A32OPC(ExclusiveReadMemory8, U8, U32, AccType )
|
A32OPC(ExclusiveReadMemory8, U8, U64, U32, AccType )
|
||||||
A32OPC(ExclusiveReadMemory16, U16, U32, AccType )
|
A32OPC(ExclusiveReadMemory16, U16, U64, U32, AccType )
|
||||||
A32OPC(ExclusiveReadMemory32, U32, U32, AccType )
|
A32OPC(ExclusiveReadMemory32, U32, U64, U32, AccType )
|
||||||
A32OPC(ExclusiveReadMemory64, U64, U32, AccType )
|
A32OPC(ExclusiveReadMemory64, U64, U64, U32, AccType )
|
||||||
A32OPC(WriteMemory8, Void, U32, U8, AccType )
|
A32OPC(WriteMemory8, Void, U64, U32, U8, AccType )
|
||||||
A32OPC(WriteMemory16, Void, U32, U16, AccType )
|
A32OPC(WriteMemory16, Void, U64, U32, U16, AccType )
|
||||||
A32OPC(WriteMemory32, Void, U32, U32, AccType )
|
A32OPC(WriteMemory32, Void, U64, U32, U32, AccType )
|
||||||
A32OPC(WriteMemory64, Void, U32, U64, AccType )
|
A32OPC(WriteMemory64, Void, U64, U32, U64, AccType )
|
||||||
A32OPC(ExclusiveWriteMemory8, U32, U32, U8, AccType )
|
A32OPC(ExclusiveWriteMemory8, U32, U64, U32, U8, AccType )
|
||||||
A32OPC(ExclusiveWriteMemory16, U32, U32, U16, AccType )
|
A32OPC(ExclusiveWriteMemory16, U32, U64, U32, U16, AccType )
|
||||||
A32OPC(ExclusiveWriteMemory32, U32, U32, U32, AccType )
|
A32OPC(ExclusiveWriteMemory32, U32, U64, U32, U32, AccType )
|
||||||
A32OPC(ExclusiveWriteMemory64, U32, U32, U64, AccType )
|
A32OPC(ExclusiveWriteMemory64, U32, U64, U32, U64, AccType )
|
||||||
|
|
||||||
// A64 Memory access
|
// A64 Memory access
|
||||||
A64OPC(ClearExclusive, Void, )
|
A64OPC(ClearExclusive, Void, )
|
||||||
A64OPC(ReadMemory8, U8, U64, AccType )
|
A64OPC(ReadMemory8, U8, U64, U64, AccType )
|
||||||
A64OPC(ReadMemory16, U16, U64, AccType )
|
A64OPC(ReadMemory16, U16, U64, U64, AccType )
|
||||||
A64OPC(ReadMemory32, U32, U64, AccType )
|
A64OPC(ReadMemory32, U32, U64, U64, AccType )
|
||||||
A64OPC(ReadMemory64, U64, U64, AccType )
|
A64OPC(ReadMemory64, U64, U64, U64, AccType )
|
||||||
A64OPC(ReadMemory128, U128, U64, AccType )
|
A64OPC(ReadMemory128, U128, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveReadMemory8, U8, U64, AccType )
|
A64OPC(ExclusiveReadMemory8, U8, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveReadMemory16, U16, U64, AccType )
|
A64OPC(ExclusiveReadMemory16, U16, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveReadMemory32, U32, U64, AccType )
|
A64OPC(ExclusiveReadMemory32, U32, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveReadMemory64, U64, U64, AccType )
|
A64OPC(ExclusiveReadMemory64, U64, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveReadMemory128, U128, U64, AccType )
|
A64OPC(ExclusiveReadMemory128, U128, U64, U64, AccType )
|
||||||
A64OPC(WriteMemory8, Void, U64, U8, AccType )
|
A64OPC(WriteMemory8, Void, U64, U64, U8, AccType )
|
||||||
A64OPC(WriteMemory16, Void, U64, U16, AccType )
|
A64OPC(WriteMemory16, Void, U64, U64, U16, AccType )
|
||||||
A64OPC(WriteMemory32, Void, U64, U32, AccType )
|
A64OPC(WriteMemory32, Void, U64, U64, U32, AccType )
|
||||||
A64OPC(WriteMemory64, Void, U64, U64, AccType )
|
A64OPC(WriteMemory64, Void, U64, U64, U64, AccType )
|
||||||
A64OPC(WriteMemory128, Void, U64, U128, AccType )
|
A64OPC(WriteMemory128, Void, U64, U64, U128, AccType )
|
||||||
A64OPC(ExclusiveWriteMemory8, U32, U64, U8, AccType )
|
A64OPC(ExclusiveWriteMemory8, U32, U64, U64, U8, AccType )
|
||||||
A64OPC(ExclusiveWriteMemory16, U32, U64, U16, AccType )
|
A64OPC(ExclusiveWriteMemory16, U32, U64, U64, U16, AccType )
|
||||||
A64OPC(ExclusiveWriteMemory32, U32, U64, U32, AccType )
|
A64OPC(ExclusiveWriteMemory32, U32, U64, U64, U32, AccType )
|
||||||
A64OPC(ExclusiveWriteMemory64, U32, U64, U64, AccType )
|
A64OPC(ExclusiveWriteMemory64, U32, U64, U64, U64, AccType )
|
||||||
A64OPC(ExclusiveWriteMemory128, U32, U64, U128, AccType )
|
A64OPC(ExclusiveWriteMemory128, U32, U64, U64, U128, AccType )
|
||||||
|
|
||||||
// Coprocessor
|
// Coprocessor
|
||||||
A32OPC(CoprocInternalOperation, Void, CoprocInfo )
|
A32OPC(CoprocInternalOperation, Void, CoprocInfo )
|
||||||
|
@ -25,7 +25,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(0).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
@ -37,7 +37,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(0).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
@ -49,7 +49,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(0).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
@ -61,7 +61,7 @@ void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 vaddr = inst.GetArg(0).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
|
3
externals/dynarmic/tests/A32/fuzz_arm.cpp
vendored
3
externals/dynarmic/tests/A32/fuzz_arm.cpp
vendored
@ -398,9 +398,8 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uni.GetRegisters()[15] > jit.Regs()[15]) {
|
if (uni.GetRegisters()[15] > jit.Regs()[15]) {
|
||||||
const u32 final_pc = jit.Regs()[15];
|
|
||||||
int trials = 0;
|
int trials = 0;
|
||||||
while (final_pc >= initial_pc && final_pc < expected_end_pc && trials++ < 100) {
|
while (jit.Regs()[15] >= initial_pc && jit.Regs()[15] < expected_end_pc && trials++ < 100 && uni.GetRegisters()[15] != jit.Regs()[15]) {
|
||||||
fmt::print("Warning: Possible unicorn overrrun, attempt recovery\n");
|
fmt::print("Warning: Possible unicorn overrrun, attempt recovery\n");
|
||||||
jit.Step();
|
jit.Step();
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@ CubebSink::CubebSink(std::string_view target_device_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cubeb_get_max_channel_count(ctx, &device_channels);
|
cubeb_get_max_channel_count(ctx, &device_channels);
|
||||||
device_channels = std::clamp(device_channels, 2U, 6U);
|
device_channels = device_channels >= 6U ? 6U : 2U;
|
||||||
}
|
}
|
||||||
|
|
||||||
CubebSink::~CubebSink() {
|
CubebSink::~CubebSink() {
|
||||||
|
@ -18,14 +18,16 @@
|
|||||||
/// Helper macros to insert unused bytes or words to properly align structs. These values will be
|
/// Helper macros to insert unused bytes or words to properly align structs. These values will be
|
||||||
/// zero-initialized.
|
/// zero-initialized.
|
||||||
#define INSERT_PADDING_BYTES(num_bytes) \
|
#define INSERT_PADDING_BYTES(num_bytes) \
|
||||||
std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
|
[[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
|
||||||
#define INSERT_PADDING_WORDS(num_words) \
|
#define INSERT_PADDING_WORDS(num_words) \
|
||||||
std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
|
[[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
|
||||||
|
|
||||||
/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
|
/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
|
||||||
/// This keeps the structure trivial to construct.
|
/// This keeps the structure trivial to construct.
|
||||||
#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
|
#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \
|
||||||
#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
|
[[maybe_unused]] std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
|
||||||
|
#define INSERT_PADDING_WORDS_NOINIT(num_words) \
|
||||||
|
[[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
|
||||||
|
@ -147,7 +147,6 @@ void ARM_Interface::Run() {
|
|||||||
|
|
||||||
// Notify the debugger and go to sleep if a watchpoint was hit.
|
// Notify the debugger and go to sleep if a watchpoint was hit.
|
||||||
if (Has(hr, watchpoint)) {
|
if (Has(hr, watchpoint)) {
|
||||||
RewindBreakpointInstruction();
|
|
||||||
if (system.DebuggerEnabled()) {
|
if (system.DebuggerEnabled()) {
|
||||||
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ public:
|
|||||||
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||||
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||||
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
|
||||||
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::UserDefined5;
|
static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
|
||||||
static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
|
static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -162,7 +162,7 @@ public:
|
|||||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||||
if (match) {
|
if (match) {
|
||||||
parent.halted_watchpoint = match;
|
parent.halted_watchpoint = match;
|
||||||
ReturnException(parent.jit.load()->Regs()[15], ARM_Interface::watchpoint);
|
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +211,6 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
|||||||
|
|
||||||
// Code cache size
|
// Code cache size
|
||||||
config.code_cache_size = 512_MiB;
|
config.code_cache_size = 512_MiB;
|
||||||
config.far_code_offset = 400_MiB;
|
|
||||||
|
|
||||||
// Allow memory fault handling to work
|
// Allow memory fault handling to work
|
||||||
if (system.DebuggerEnabled()) {
|
if (system.DebuggerEnabled()) {
|
||||||
@ -222,7 +221,6 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
|||||||
if (!page_table) {
|
if (!page_table) {
|
||||||
// Don't waste too much memory on null_jit
|
// Don't waste too much memory on null_jit
|
||||||
config.code_cache_size = 8_MiB;
|
config.code_cache_size = 8_MiB;
|
||||||
config.far_code_offset = 4_MiB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe optimizations
|
// Safe optimizations
|
||||||
|
@ -205,7 +205,7 @@ public:
|
|||||||
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
const auto match{parent.MatchingWatchpoint(addr, size, type)};
|
||||||
if (match) {
|
if (match) {
|
||||||
parent.halted_watchpoint = match;
|
parent.halted_watchpoint = match;
|
||||||
ReturnException(parent.jit.load()->GetPC(), ARM_Interface::watchpoint);
|
parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +271,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
|||||||
|
|
||||||
// Code cache size
|
// Code cache size
|
||||||
config.code_cache_size = 512_MiB;
|
config.code_cache_size = 512_MiB;
|
||||||
config.far_code_offset = 400_MiB;
|
|
||||||
|
|
||||||
// Allow memory fault handling to work
|
// Allow memory fault handling to work
|
||||||
if (system.DebuggerEnabled()) {
|
if (system.DebuggerEnabled()) {
|
||||||
@ -282,7 +281,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
|||||||
if (!page_table) {
|
if (!page_table) {
|
||||||
// Don't waste too much memory on null_jit
|
// Don't waste too much memory on null_jit
|
||||||
config.code_cache_size = 8_MiB;
|
config.code_cache_size = 8_MiB;
|
||||||
config.far_code_offset = 4_MiB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe optimizations
|
// Safe optimizations
|
||||||
|
@ -30,19 +30,19 @@ public:
|
|||||||
explicit KCodeMemory(KernelCore& kernel_);
|
explicit KCodeMemory(KernelCore& kernel_);
|
||||||
|
|
||||||
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
|
Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
|
||||||
void Finalize();
|
void Finalize() override;
|
||||||
|
|
||||||
Result Map(VAddr address, size_t size);
|
Result Map(VAddr address, size_t size);
|
||||||
Result Unmap(VAddr address, size_t size);
|
Result Unmap(VAddr address, size_t size);
|
||||||
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
|
Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
|
||||||
Result UnmapFromOwner(VAddr address, size_t size);
|
Result UnmapFromOwner(VAddr address, size_t size);
|
||||||
|
|
||||||
bool IsInitialized() const {
|
bool IsInitialized() const override {
|
||||||
return m_is_initialized;
|
return m_is_initialized;
|
||||||
}
|
}
|
||||||
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
|
||||||
|
|
||||||
KProcess* GetOwner() const {
|
KProcess* GetOwner() const override {
|
||||||
return m_owner;
|
return m_owner;
|
||||||
}
|
}
|
||||||
VAddr GetSourceAddress() const {
|
VAddr GetSourceAddress() const {
|
||||||
|
@ -642,6 +642,10 @@ void AppletMessageQueue::RequestExit() {
|
|||||||
PushMessage(AppletMessage::Exit);
|
PushMessage(AppletMessage::Exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppletMessageQueue::RequestResume() {
|
||||||
|
PushMessage(AppletMessage::Resume);
|
||||||
|
}
|
||||||
|
|
||||||
void AppletMessageQueue::FocusStateChanged() {
|
void AppletMessageQueue::FocusStateChanged() {
|
||||||
PushMessage(AppletMessage::FocusStateChanged);
|
PushMessage(AppletMessage::FocusStateChanged);
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ public:
|
|||||||
AppletMessage PopMessage();
|
AppletMessage PopMessage();
|
||||||
std::size_t GetMessageCount() const;
|
std::size_t GetMessageCount() const;
|
||||||
void RequestExit();
|
void RequestExit();
|
||||||
|
void RequestResume();
|
||||||
void FocusStateChanged();
|
void FocusStateChanged();
|
||||||
void OperationModeChanged();
|
void OperationModeChanged();
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ enum class TransactionId {
|
|||||||
|
|
||||||
class IBinder {
|
class IBinder {
|
||||||
public:
|
public:
|
||||||
|
virtual ~IBinder() = default;
|
||||||
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
|
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
|
||||||
u32 flags) = 0;
|
u32 flags) = 0;
|
||||||
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
|
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
|
||||||
|
@ -30,8 +30,6 @@ add_executable(yuzu
|
|||||||
applets/qt_web_browser_scripts.h
|
applets/qt_web_browser_scripts.h
|
||||||
bootmanager.cpp
|
bootmanager.cpp
|
||||||
bootmanager.h
|
bootmanager.h
|
||||||
check_vulkan.cpp
|
|
||||||
check_vulkan.h
|
|
||||||
compatdb.ui
|
compatdb.ui
|
||||||
compatibility_list.cpp
|
compatibility_list.cpp
|
||||||
compatibility_list.h
|
compatibility_list.h
|
||||||
@ -155,6 +153,8 @@ add_executable(yuzu
|
|||||||
main.cpp
|
main.cpp
|
||||||
main.h
|
main.h
|
||||||
main.ui
|
main.ui
|
||||||
|
startup_checks.cpp
|
||||||
|
startup_checks.h
|
||||||
uisettings.cpp
|
uisettings.cpp
|
||||||
uisettings.h
|
uisettings.h
|
||||||
util/controller_navigation.cpp
|
util/controller_navigation.cpp
|
||||||
|
@ -683,12 +683,6 @@ void Config::ReadRendererValues() {
|
|||||||
ReadGlobalSetting(Settings::values.bg_green);
|
ReadGlobalSetting(Settings::values.bg_green);
|
||||||
ReadGlobalSetting(Settings::values.bg_blue);
|
ReadGlobalSetting(Settings::values.bg_blue);
|
||||||
|
|
||||||
if (!global && UISettings::values.has_broken_vulkan &&
|
|
||||||
Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
|
|
||||||
!Settings::values.renderer_backend.UsingGlobal()) {
|
|
||||||
Settings::values.renderer_backend.SetGlobal(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global) {
|
if (global) {
|
||||||
ReadBasicSetting(Settings::values.renderer_debug);
|
ReadBasicSetting(Settings::values.renderer_debug);
|
||||||
ReadBasicSetting(Settings::values.renderer_shader_feedback);
|
ReadBasicSetting(Settings::values.renderer_shader_feedback);
|
||||||
@ -808,7 +802,6 @@ void Config::ReadUIValues() {
|
|||||||
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
||||||
ReadBasicSetting(UISettings::values.mute_when_in_background);
|
ReadBasicSetting(UISettings::values.mute_when_in_background);
|
||||||
ReadBasicSetting(UISettings::values.hide_mouse);
|
ReadBasicSetting(UISettings::values.hide_mouse);
|
||||||
ReadBasicSetting(UISettings::values.has_broken_vulkan);
|
|
||||||
ReadBasicSetting(UISettings::values.disable_web_applet);
|
ReadBasicSetting(UISettings::values.disable_web_applet);
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
@ -1357,7 +1350,6 @@ void Config::SaveUIValues() {
|
|||||||
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
||||||
WriteBasicSetting(UISettings::values.mute_when_in_background);
|
WriteBasicSetting(UISettings::values.mute_when_in_background);
|
||||||
WriteBasicSetting(UISettings::values.hide_mouse);
|
WriteBasicSetting(UISettings::values.hide_mouse);
|
||||||
WriteBasicSetting(UISettings::values.has_broken_vulkan);
|
|
||||||
WriteBasicSetting(UISettings::values.disable_web_applet);
|
WriteBasicSetting(UISettings::values.disable_web_applet);
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
@ -58,24 +58,9 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
|
|||||||
UpdateBackgroundColorButton(new_bg_color);
|
UpdateBackgroundColorButton(new_bg_color);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
|
ui->api->setEnabled(!UISettings::values.has_broken_vulkan);
|
||||||
UISettings::values.has_broken_vulkan = false;
|
ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan ||
|
||||||
|
Settings::IsConfiguringGlobal());
|
||||||
if (RetrieveVulkanDevices()) {
|
|
||||||
ui->api->setEnabled(true);
|
|
||||||
ui->button_check_vulkan->hide();
|
|
||||||
|
|
||||||
for (const auto& device : vulkan_devices) {
|
|
||||||
ui->device->addItem(device);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
UISettings::values.has_broken_vulkan = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
|
|
||||||
ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
|
|
||||||
|
|
||||||
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
|
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
|
||||||
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
|
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
|
||||||
}
|
}
|
||||||
@ -315,7 +300,7 @@ void ConfigureGraphics::UpdateAPILayout() {
|
|||||||
vulkan_device = Settings::values.vulkan_device.GetValue(true);
|
vulkan_device = Settings::values.vulkan_device.GetValue(true);
|
||||||
shader_backend = Settings::values.shader_backend.GetValue(true);
|
shader_backend = Settings::values.shader_backend.GetValue(true);
|
||||||
ui->device_widget->setEnabled(false);
|
ui->device_widget->setEnabled(false);
|
||||||
ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
|
ui->backend_widget->setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
vulkan_device = Settings::values.vulkan_device.GetValue();
|
vulkan_device = Settings::values.vulkan_device.GetValue();
|
||||||
shader_backend = Settings::values.shader_backend.GetValue();
|
shader_backend = Settings::values.shader_backend.GetValue();
|
||||||
@ -337,9 +322,9 @@ void ConfigureGraphics::UpdateAPILayout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigureGraphics::RetrieveVulkanDevices() try {
|
void ConfigureGraphics::RetrieveVulkanDevices() try {
|
||||||
if (UISettings::values.has_broken_vulkan) {
|
if (UISettings::values.has_broken_vulkan) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Vulkan;
|
using namespace Vulkan;
|
||||||
@ -355,11 +340,8 @@ bool ConfigureGraphics::RetrieveVulkanDevices() try {
|
|||||||
const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
|
const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
|
||||||
vulkan_devices.push_back(QString::fromStdString(name));
|
vulkan_devices.push_back(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (const Vulkan::vk::Exception& exception) {
|
} catch (const Vulkan::vk::Exception& exception) {
|
||||||
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
|
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
||||||
@ -440,11 +422,4 @@ void ConfigureGraphics::SetupPerGameUI() {
|
|||||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||||
ConfigurationShared::InsertGlobalItem(
|
ConfigurationShared::InsertGlobalItem(
|
||||||
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
|
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
|
||||||
|
|
||||||
if (UISettings::values.has_broken_vulkan) {
|
|
||||||
ui->backend_widget->setEnabled(true);
|
|
||||||
ConfigurationShared::SetColoredComboBox(
|
|
||||||
ui->backend, ui->backend_widget,
|
|
||||||
static_cast<int>(Settings::values.shader_backend.GetValue(true)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ private:
|
|||||||
void UpdateDeviceSelection(int device);
|
void UpdateDeviceSelection(int device);
|
||||||
void UpdateShaderBackendSelection(int backend);
|
void UpdateShaderBackendSelection(int backend);
|
||||||
|
|
||||||
bool RetrieveVulkanDevices();
|
void RetrieveVulkanDevices();
|
||||||
|
|
||||||
void SetupPerGameUI();
|
void SetupPerGameUI();
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>471</width>
|
<width>541</width>
|
||||||
<height>759</height>
|
<height>759</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -574,13 +574,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="button_check_vulkan">
|
|
||||||
<property name="text">
|
|
||||||
<string>Check for Working Vulkan</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -116,7 +116,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#include "video_core/shader_notify.h"
|
#include "video_core/shader_notify.h"
|
||||||
#include "yuzu/about_dialog.h"
|
#include "yuzu/about_dialog.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
#include "yuzu/check_vulkan.h"
|
|
||||||
#include "yuzu/compatdb.h"
|
#include "yuzu/compatdb.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
#include "yuzu/configuration/config.h"
|
#include "yuzu/configuration/config.h"
|
||||||
@ -132,6 +131,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#include "yuzu/install_dialog.h"
|
#include "yuzu/install_dialog.h"
|
||||||
#include "yuzu/loading_screen.h"
|
#include "yuzu/loading_screen.h"
|
||||||
#include "yuzu/main.h"
|
#include "yuzu/main.h"
|
||||||
|
#include "yuzu/startup_checks.h"
|
||||||
#include "yuzu/uisettings.h"
|
#include "yuzu/uisettings.h"
|
||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
@ -253,7 +253,7 @@ static QString PrettyProductName() {
|
|||||||
return QSysInfo::prettyProductName();
|
return QSysInfo::prettyProductName();
|
||||||
}
|
}
|
||||||
|
|
||||||
GMainWindow::GMainWindow()
|
GMainWindow::GMainWindow(bool has_broken_vulkan)
|
||||||
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
||||||
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
||||||
config{std::make_unique<Config>(*system)},
|
config{std::make_unique<Config>(*system)},
|
||||||
@ -353,17 +353,15 @@ GMainWindow::GMainWindow()
|
|||||||
|
|
||||||
MigrateConfigFiles();
|
MigrateConfigFiles();
|
||||||
|
|
||||||
if (!CheckVulkan()) {
|
if (has_broken_vulkan) {
|
||||||
config->Save();
|
UISettings::values.has_broken_vulkan = true;
|
||||||
|
|
||||||
QMessageBox::warning(
|
QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
|
||||||
this, tr("Broken Vulkan Installation Detected"),
|
tr("Vulkan initialization failed during boot.<br><br>Click <a "
|
||||||
tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
|
|
||||||
"href='https://yuzu-emu.org/wiki/faq/"
|
"href='https://yuzu-emu.org/wiki/faq/"
|
||||||
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
|
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
|
||||||
"instructions to fix the issue</a>."));
|
"here for instructions to fix the issue</a>."));
|
||||||
}
|
|
||||||
if (UISettings::values.has_broken_vulkan) {
|
|
||||||
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
|
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
|
||||||
|
|
||||||
renderer_status_button->setDisabled(true);
|
renderer_status_button->setDisabled(true);
|
||||||
@ -2577,6 +2575,7 @@ void GMainWindow::OnPauseContinueGame() {
|
|||||||
if (emu_thread->IsRunning()) {
|
if (emu_thread->IsRunning()) {
|
||||||
OnPauseGame();
|
OnPauseGame();
|
||||||
} else {
|
} else {
|
||||||
|
RequestGameResume();
|
||||||
OnStartGame();
|
OnStartGame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3757,6 +3756,21 @@ void GMainWindow::RequestGameExit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::RequestGameResume() {
|
||||||
|
auto& sm{system->ServiceManager()};
|
||||||
|
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||||
|
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||||
|
|
||||||
|
if (applet_oe != nullptr) {
|
||||||
|
applet_oe->GetMessageQueue()->RequestResume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applet_ae != nullptr) {
|
||||||
|
applet_ae->GetMessageQueue()->RequestResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::filterBarSetChecked(bool state) {
|
void GMainWindow::filterBarSetChecked(bool state) {
|
||||||
ui->action_Show_Filter_Bar->setChecked(state);
|
ui->action_Show_Filter_Bar->setChecked(state);
|
||||||
emit(OnToggleFilterBar());
|
emit(OnToggleFilterBar());
|
||||||
@ -3858,6 +3872,11 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
bool has_broken_vulkan = false;
|
||||||
|
if (StartupChecks(argv[0], &has_broken_vulkan)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Common::DetachedTasks detached_tasks;
|
Common::DetachedTasks detached_tasks;
|
||||||
MicroProfileOnThreadCreate("Frontend");
|
MicroProfileOnThreadCreate("Frontend");
|
||||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||||
@ -3897,7 +3916,7 @@ int main(int argc, char* argv[]) {
|
|||||||
// generating shaders
|
// generating shaders
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
|
|
||||||
GMainWindow main_window{};
|
GMainWindow main_window{has_broken_vulkan};
|
||||||
// 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();
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ class GMainWindow : public QMainWindow {
|
|||||||
public:
|
public:
|
||||||
void filterBarSetChecked(bool state);
|
void filterBarSetChecked(bool state);
|
||||||
void UpdateUITheme();
|
void UpdateUITheme();
|
||||||
explicit GMainWindow();
|
explicit GMainWindow(bool has_broken_vulkan);
|
||||||
~GMainWindow() override;
|
~GMainWindow() override;
|
||||||
|
|
||||||
bool DropAction(QDropEvent* event);
|
bool DropAction(QDropEvent* event);
|
||||||
@ -244,6 +244,7 @@ private:
|
|||||||
bool ConfirmChangeGame();
|
bool ConfirmChangeGame();
|
||||||
bool ConfirmForceLockedExit();
|
bool ConfirmForceLockedExit();
|
||||||
void RequestGameExit();
|
void RequestGameExit();
|
||||||
|
void RequestGameResume();
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
136
src/yuzu/startup_checks.cpp
Executable file
136
src/yuzu/startup_checks.cpp
Executable file
@ -0,0 +1,136 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <cstring> // for memset, strncpy
|
||||||
|
#include <processthreadsapi.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(YUZU_UNIX)
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include "video_core/vulkan_common/vulkan_instance.h"
|
||||||
|
#include "video_core/vulkan_common/vulkan_library.h"
|
||||||
|
#include "yuzu/startup_checks.h"
|
||||||
|
|
||||||
|
void CheckVulkan() {
|
||||||
|
// Just start the Vulkan loader, this will crash if something is wrong
|
||||||
|
try {
|
||||||
|
Vulkan::vk::InstanceDispatch dld;
|
||||||
|
const Common::DynamicLibrary library = Vulkan::OpenLibrary();
|
||||||
|
const Vulkan::vk::Instance instance =
|
||||||
|
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
|
||||||
|
|
||||||
|
} catch (const Vulkan::vk::Exception& exception) {
|
||||||
|
std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Check environment variable to see if we are the child
|
||||||
|
char variable_contents[8];
|
||||||
|
const DWORD startup_check_var =
|
||||||
|
GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
|
||||||
|
if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
|
||||||
|
CheckVulkan();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the startup variable for child processes
|
||||||
|
const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
|
||||||
|
if (!env_var_set) {
|
||||||
|
std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
|
||||||
|
STARTUP_CHECK_ENV_VAR, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
std::memset(&process_info, '\0', sizeof(process_info));
|
||||||
|
|
||||||
|
if (!SpawnChild(arg0, &process_info)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the processs exits and get exit code from it
|
||||||
|
WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||||
|
DWORD exit_code = STILL_ACTIVE;
|
||||||
|
const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
|
||||||
|
if (err == 0) {
|
||||||
|
std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vulkan is broken if the child crashed (return value is not zero)
|
||||||
|
*has_broken_vulkan = (exit_code != 0);
|
||||||
|
|
||||||
|
if (CloseHandle(process_info.hProcess) == 0) {
|
||||||
|
std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
if (CloseHandle(process_info.hThread) == 0) {
|
||||||
|
std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
|
||||||
|
std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n",
|
||||||
|
STARTUP_CHECK_ENV_VAR, GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(YUZU_UNIX)
|
||||||
|
const pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
CheckVulkan();
|
||||||
|
return true;
|
||||||
|
} else if (pid == -1) {
|
||||||
|
const int err = errno;
|
||||||
|
std::fprintf(stderr, "fork failed with error %d\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get exit code from child process
|
||||||
|
int status;
|
||||||
|
const int r_val = wait(&status);
|
||||||
|
if (r_val == -1) {
|
||||||
|
const int err = errno;
|
||||||
|
std::fprintf(stderr, "wait failed with error %d\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Vulkan is broken if the child crashed (return value is not zero)
|
||||||
|
*has_broken_vulkan = (status != 0);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
|
||||||
|
STARTUPINFOA startup_info;
|
||||||
|
|
||||||
|
std::memset(&startup_info, '\0', sizeof(startup_info));
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
|
||||||
|
char p_name[255];
|
||||||
|
std::strncpy(p_name, arg0, 255);
|
||||||
|
|
||||||
|
const bool process_created = CreateProcessA(nullptr, // lpApplicationName
|
||||||
|
p_name, // lpCommandLine
|
||||||
|
nullptr, // lpProcessAttributes
|
||||||
|
nullptr, // lpThreadAttributes
|
||||||
|
false, // bInheritHandles
|
||||||
|
0, // dwCreationFlags
|
||||||
|
nullptr, // lpEnvironment
|
||||||
|
nullptr, // lpCurrentDirectory
|
||||||
|
&startup_info, // lpStartupInfo
|
||||||
|
pi // lpProcessInformation
|
||||||
|
);
|
||||||
|
if (!process_created) {
|
||||||
|
std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
17
src/yuzu/startup_checks.h
Executable file
17
src/yuzu/startup_checks.h
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
|
||||||
|
|
||||||
|
void CheckVulkan();
|
||||||
|
bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
|
||||||
|
#endif
|
@ -78,7 +78,7 @@ struct Values {
|
|||||||
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
|
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
|
||||||
Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
|
Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
|
||||||
// Set when Vulkan is known to crash the application
|
// Set when Vulkan is known to crash the application
|
||||||
Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
|
bool has_broken_vulkan = false;
|
||||||
|
|
||||||
Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};
|
Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user