mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 07:18:33 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95759ae726 | |||
| dba3bfe74b | |||
| 6d9e4b97a6 | |||
| c14bfc3d5e | |||
| a238536d6d | |||
| 9fe8674760 | |||
| 97547657b5 | |||
| f73164b191 | |||
| 35c420582f | |||
| b2b7a34957 | |||
| d7a564b088 | |||
| 1d57d4a73d | |||
| 3af03332cc | |||
| a6e506c328 |
@@ -19,7 +19,6 @@ log.txt
|
||||
# Generated source files
|
||||
src/common/scm_rev.cpp
|
||||
dist/english_plurals/generated_en.ts
|
||||
*-toolchain.cmake
|
||||
|
||||
# Project/editor files
|
||||
*.swp
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set(CROSS_TARGET "powerpc64le" CACHE STRING "Cross-compilation target (aarch64, powerpc64le, riscv64, etc)")
|
||||
|
||||
set(CMAKE_SYSROOT /usr/${CROSS_TARGET}-unknown-linux-gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${CROSS_TARGET}-unknown-linux-gnu-g++)
|
||||
|
||||
# search programs in the host environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
# search headers and libraries in the target environment
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH /usr/${CROSS_TARGET}-unknown-linux-gnu)
|
||||
+1
-137
@@ -2,122 +2,7 @@
|
||||
|
||||
General guide for cross compiling.
|
||||
|
||||
## Gentoo
|
||||
|
||||
Gentoo's cross-compilation setup is relatively easy, provided you're already familiar with portage.
|
||||
|
||||
### Crossdev
|
||||
|
||||
First, emerge crossdev via `sudo emerge -a sys-devel/crossdev`.
|
||||
|
||||
Now, set up the environment depending on the target architecture; e.g.
|
||||
|
||||
```sh
|
||||
sudo crossdev powerpc64le
|
||||
sudo crossdev aarch64
|
||||
```
|
||||
|
||||
### QEMU
|
||||
|
||||
Installing a qemu user setup is recommended for testing. To do so, you will need the relevant USE flags:
|
||||
|
||||
```sh
|
||||
app-emulation/qemu static-user qemu_user_targets_ppc64le qemu_user_targets_aarch64
|
||||
```
|
||||
|
||||
Note that to use cross-emerged libraries, you will need to tell qemu where the sysroot is. You can do this with an alias:
|
||||
|
||||
```sh
|
||||
alias qemu-ppc64le="qemu-ppc64le -L /usr/powerpc64le-unknown-linux-gnu"
|
||||
alias qemu-aarch64="qemu-aarch64 -L /usr/aarch64-unknown-linux-gnu"
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
Some packages have broken USE flags on other architectures; you'll also need to set up python targets. In `/usr/<target>-unknown-linux-gnu/etc/portage/package.use`:
|
||||
|
||||
```sh
|
||||
>=net-misc/curl-8.16.0-r1 ssl
|
||||
|
||||
*/* PYTHON_TARGETS: python3_13 PYTHON_SINGLE_TARGET: python3_13
|
||||
*/* pam
|
||||
|
||||
sys-apps/util-linux pam su
|
||||
app-shells/bash -readline
|
||||
>=dev-libs/libpcre2-10.47 unicode
|
||||
>=x11-libs/libxkbcommon-1.12.3 X
|
||||
>=sys-libs/zlib-1.3.1-r1 minizip
|
||||
>=app-alternatives/gpg-1-r3 ssl
|
||||
>=app-crypt/gnupg-2.5.13-r2 ssl
|
||||
|
||||
dev-libs/* -introspection
|
||||
media-libs/harfbuzz -introspection
|
||||
dev-libs/quazip -qt5 qt6
|
||||
```
|
||||
|
||||
Dependencies should be about the same [as normal Gentoo](./Deps.md), but removing gamemode and renderdoc is recommended. Keep in mind that when emerging, you want to use `emerge-<target>-unknown-linux-gnu`, e.g. `emerge-powerpc64le-unknown-linux-gnu`.
|
||||
|
||||
Enable GURU in the cross environment (as root):
|
||||
|
||||
```sh
|
||||
mkdir -p /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf
|
||||
cat << EOF > /usr/powerpc64le-unknown-linux-gnu/etc/portage/repos.conf/guru.conf
|
||||
[guru]
|
||||
location = /var/db/repos/guru
|
||||
auto-sync = no
|
||||
priority = 1
|
||||
EOF
|
||||
```
|
||||
|
||||
Now emerge your dependencies:
|
||||
|
||||
```sh
|
||||
sudo emerge-powerpc64le-unknown-linux-gnu -aU app-arch/lz4 app-arch/zstd app-arch/unzip \
|
||||
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit dev-libs/oaknut \
|
||||
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||
media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||
media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \
|
||||
net-libs/enet net-libs/mbedtls \
|
||||
sys-libs/zlib \
|
||||
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt dev-cpp/catch \
|
||||
net-wireless/wireless-tools \
|
||||
dev-qt/qtbase:6 dev-libs/quazip \
|
||||
virtual/pkgconfig
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
A toolchain is provided in `CMakeModules/GentooCross.cmake`. To use it:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF
|
||||
```
|
||||
|
||||
Now build as normal:
|
||||
|
||||
```sh
|
||||
cmake --build build/ppc64 -j$(nproc)
|
||||
```
|
||||
|
||||
### Alternatively
|
||||
|
||||
Only emerge the absolute necessities:
|
||||
|
||||
```sh
|
||||
sudo emerge-powerpc64le-unknown-linux-gnu -aU media-video/ffmpeg media-libs/libsdl2 dev-qt/qtbase:6
|
||||
```
|
||||
|
||||
Then set `YUZU_USE_CPM=ON`:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build/ppc64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/GentooCross.cmake -G Ninja -DCROSS_TARGET=powerpc64le -DENABLE_OPENGL=OFF -DYUZU_USE_CPM=ON
|
||||
```
|
||||
|
||||
## ARM64
|
||||
|
||||
### Debian ARM64
|
||||
## Debian ARM64
|
||||
|
||||
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
|
||||
|
||||
@@ -203,24 +88,3 @@ And finally, run the compiled executable with QEMU!
|
||||
```sh
|
||||
qemu-aarch64 build/aarch64/bin/eden
|
||||
```
|
||||
|
||||
## PowerPC
|
||||
|
||||
This is a guide for FreeBSD users mainly.
|
||||
|
||||
Now you got a PowerPC sysroot - quickly decompress it somewhere, say `/home/user/opt/powerpc64le`. Create a toolchain file, for example `powerpc64le-toolchain.cmake`; always [consult the manual](https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports).
|
||||
|
||||
There is a script to automatically do all of this under `./tools/setup-cross-sysroot.sh`.
|
||||
|
||||
Remember to add `-mabi=elfv1` to `CFLAGS`/`CXXFLAGS` otherwise the program will crash.
|
||||
|
||||
Specify:
|
||||
|
||||
- `YUZU_USE_CPM`: Set this to `ON` so packages can be found and built if your sysroot doesn't have them.
|
||||
- `YUZU_USE_EXTERNAL_FFMPEG`: Set this to `ON` as well.
|
||||
|
||||
Then run using a program such as QEMU to emulate userland syscalls:
|
||||
|
||||
```sh
|
||||
cmake --build build-ppc64-pc-freebsd -t dynarmic_tests -- -j8 && qemu-ppc64-static -L $HOME/opt/ppc64-freebsd/sysroot ./build-ppc64-pc-freebsd/bin/dynarmic_tests
|
||||
```
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# PowerPC 64 backend
|
||||
|
||||
The ppc64 backend currently only supports the little endian variant, with big endian support being experimental for the time being. Additionally only A32 is supported (for now).
|
||||
|
||||
- Flag handling: Flags are emulated via software, while there may be some funny tricks with the CTR, I'd rather not bother - plus it's widely known that those instructions are not nice on real metal - so I would rather take the i-cache cost.
|
||||
- 128-bit atomics: No 128-bit atomic support is provided, this may cause wrong or erroneous execution in some contexts.
|
||||
|
||||
To handle endianess differences all 16/32/64-bit loads and stores to the "emulated memory" are byteswapped beforehand.
|
||||
@@ -35,7 +35,6 @@ There are no plans to support v1 or v2.
|
||||
|
||||
* x86-64
|
||||
* AArch64
|
||||
* PowerPC 64 (POWER 4 and up)
|
||||
|
||||
There are no plans to support any 32-bit architecture.
|
||||
|
||||
@@ -51,7 +50,6 @@ Documentation
|
||||
-------------
|
||||
|
||||
Design documentation can be found at [./Design.md](./Design.md).
|
||||
PowerPC design documentation can be found at [./PowerPC](./PowerPC.md).
|
||||
|
||||
|
||||
Usage Example
|
||||
|
||||
Vendored
-5
@@ -254,11 +254,6 @@ endif()
|
||||
# TZDB (Time Zone Database)
|
||||
add_subdirectory(nx_tzdb)
|
||||
|
||||
# PowerPC emitter
|
||||
if (ARCHITECTURE_powerpc64)
|
||||
add_subdirectory(powah)
|
||||
endif()
|
||||
|
||||
add_library(tz tz/tz/tz.cpp)
|
||||
target_include_directories(tz PUBLIC ./tz)
|
||||
|
||||
|
||||
+2
-2
@@ -166,7 +166,7 @@ detect_architecture_symbols(
|
||||
"_M_MRX000")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH powerpc64
|
||||
ARCH ppc64
|
||||
SYMBOLS
|
||||
"__ppc64__"
|
||||
"__powerpc64__"
|
||||
@@ -174,7 +174,7 @@ detect_architecture_symbols(
|
||||
"_M_PPC64")
|
||||
|
||||
detect_architecture_symbols(
|
||||
ARCH powerpc
|
||||
ARCH ppc
|
||||
SYMBOLS
|
||||
"__ppc__"
|
||||
"__ppc"
|
||||
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_library(powah INTERFACE)
|
||||
target_include_directories(powah INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_options(powah INTERFACE -Wno-unused-parameter)
|
||||
|
||||
add_executable(powah_tests tests.cpp)
|
||||
create_target_directory_groups(powah_tests)
|
||||
target_link_libraries(powah_tests PRIVATE Catch2::Catch2WithMain)
|
||||
Vendored
-145
@@ -1,145 +0,0 @@
|
||||
vabsdub,VX,04,1027
|
||||
vabsduh,VX,04,1091
|
||||
vabsduw,VX,04,1155
|
||||
vaddcuw,VX,04,384
|
||||
vaddfp,VX,04,10
|
||||
vaddsbs,VX,04,768
|
||||
vaddshs,VX,04,832
|
||||
vaddsws,VX,04,896
|
||||
vaddubm,VX,04,0
|
||||
vaddubs,VX,04,512
|
||||
vadduhm,VX,04,64
|
||||
vadduhs,VX,04,576
|
||||
vadduwm,VX,04,128
|
||||
vadduws,VX,04,640
|
||||
vand,VX,04,1028
|
||||
vandc,VX,04,1092
|
||||
vavgsb,VX,04,1282
|
||||
vavgsh,VX,04,1346
|
||||
vavgsw,VX,04,1410
|
||||
vavgub,VX,04,1026
|
||||
vavguh,VX,04,1090
|
||||
vavguw,VX,04,1154
|
||||
vcfsx,VDU,04842
|
||||
vcfux,VDU,04778
|
||||
vcmpbfp,VXR,04,966
|
||||
vcmpeqfp,VXR,04,198
|
||||
vcmpequb,VXR,04,6
|
||||
vcmpequh,VXR,04,70
|
||||
vcmpequw,VXR,04,134
|
||||
vcmpgefp,VXR,04,454
|
||||
vcmpgtfp,VXR,04,710
|
||||
vcmpgtsb,VXR,04,774
|
||||
vcmpgtsh,VXR,04,838
|
||||
vcmpgtsw,VXR,04,902
|
||||
vcmpgtub,VXR,04,518
|
||||
vcmpgtuh,VXR,04,582
|
||||
vcmpgtuw,VXR,04,646
|
||||
vctsxs,VDU,04970
|
||||
vctuxs,VDU,04906
|
||||
vexptefp,VY,04,394
|
||||
vlogefp,VY,04,458
|
||||
vmaddfp,VXC,46
|
||||
vmaxfp,VX,04,1034
|
||||
vmaxsb,VX,04,258
|
||||
vmaxsh,VX,04,322
|
||||
vmaxsw,VX,04,386
|
||||
vmaxub,VX,04,2
|
||||
vmaxuh,VX,04,66
|
||||
vmaxuw,VX,04,130
|
||||
vmhaddshs,VXC,32
|
||||
vmhraddshs,VXC,33
|
||||
vminfp,VX,04,1098
|
||||
vminsb,VX,04,770
|
||||
vminsh,VX,04,834
|
||||
vminsw,VX,04,898
|
||||
vminub,VX,04,514
|
||||
vminuh,VX,04,578
|
||||
vminuw,VX,04,642
|
||||
vmladduhm,VXC,34
|
||||
vmrghb,VX,04,12
|
||||
vmrghh,VX,04,76
|
||||
vmrghw,VX,04,140
|
||||
vmrglb,VX,04,268
|
||||
vmrglh,VX,04,332
|
||||
vmrglw,VX,04,396
|
||||
vmsummbm,VXC,37
|
||||
vmsumshm,VXC,40
|
||||
vmsumshs,VXC,41
|
||||
vmsumubm,VXC,36
|
||||
vmsumuhm,VXC,38
|
||||
vmsumuhs,VXC,39
|
||||
vmulesb,VX,04,776
|
||||
vmulesh,VX,04,840
|
||||
vmuleub,VX,04,520
|
||||
vmuleuh,VX,04,584
|
||||
vmulosb,VX,04,264
|
||||
vmulosh,VX,04,328
|
||||
vmuloub,VX,04,8
|
||||
vmulouh,VX,04,72
|
||||
vnmsubfp,VXC,47
|
||||
vnor,VX,04,1284
|
||||
vor,VX,04,1156
|
||||
vperm,VXC,43
|
||||
vpkpx,VX,04,782
|
||||
vpkshss,VX,04,398
|
||||
vpkshus,VX,04,270
|
||||
vpkswss,VX,04,462
|
||||
vpkswus,VX,04,334
|
||||
vpkuhum,VX,04,14
|
||||
vpkuhus,VX,04,142
|
||||
vpkuwum,VX,04,78
|
||||
vpkuwus,VX,04,206
|
||||
vrefp,VY,04,266
|
||||
vrfim,VY,04,714
|
||||
vrfin,VY,04,522
|
||||
vrfip,VY,04,650
|
||||
vrfiz,VY,04,586
|
||||
vrlb,VX,04,4
|
||||
vrlh,VX,04,68
|
||||
vrlw,VX,04,132
|
||||
vrsqrtefp,VY,04,330
|
||||
vsel,VXC,42
|
||||
vsl,VX,04,452
|
||||
vslb,VX,04,260
|
||||
vsldoi,VX,04,/ SH 44
|
||||
vslh,VX,04,324
|
||||
vslo,VX,04,1036
|
||||
vslw,VX,04,388
|
||||
vspltb,VU,04,524
|
||||
vsplth,VU,04,588
|
||||
vspltisb,VS,04,780
|
||||
vspltish,VS,04,844
|
||||
vspltisw,VS,04,908
|
||||
vspltw,VSU,04,652
|
||||
vsr,VX,04,708
|
||||
vsrab,VX,04,772
|
||||
vsrah,VX,04,836
|
||||
vsraw,VX,04,900
|
||||
vsrb,VX,04,516
|
||||
vsrh,VX,04,580
|
||||
vsro,VX,04,1100
|
||||
vsrw,VX,04,644
|
||||
vsubcuw,VX,04,1408
|
||||
vsubfp,VX,04,74
|
||||
vsubsbs,VX,04,1792
|
||||
vsubshs,VX,04,1856
|
||||
vsubsws,VX,04,1920
|
||||
vsububm,VX,04,1024
|
||||
vsububs,VX,04,1536
|
||||
vsubuhm,VX,04,1088
|
||||
vsubuhs,VX,04,1600
|
||||
vsubuwm,VX,04,1152
|
||||
vsubuws,VX,04,1664
|
||||
vsumsws,VX,04,1928
|
||||
vsum2sws,VX,04,1672
|
||||
vsum4sbs,VX,04,1800
|
||||
vsum4shs,VX,04,1608
|
||||
vsum4ubs,VX,04,1544
|
||||
vupkhpx,VY,04,846
|
||||
vupkhsb,VY,04,526
|
||||
vupkhsh,VY,04,590
|
||||
vupklpx,VY,04,974
|
||||
vupklsb,VY,04,654
|
||||
vupklsh,VY,04,718
|
||||
vxor,VX,04,122
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
$CC data2code.c -o data2code && ./data2code data.txt >powah_gen_base.hpp
|
||||
$CC --target=powerpc64le test.S -c -o test && objdump -SC test
|
||||
Vendored
-222
@@ -1,222 +0,0 @@
|
||||
add[o][.],Add,XO,31,266
|
||||
addc[o][.],Add Carrying,XO,31,10
|
||||
adde[o][.],Add Extended,XO,31,138
|
||||
addi,Add Immediate,D,14,
|
||||
addic,Add Immediate Carrying,D,12,
|
||||
addic.,Add Immediate Carrying and Record,D,13,
|
||||
addis,Add Immediate Shifted,D,15,
|
||||
addme[o][.],Add to Minus One Extended,XO,31,234
|
||||
addze[o][.],Add to Zero Extended,XO,31,202
|
||||
and[.],AND,X,31,28
|
||||
andc[.],AND with Complement,X,31,60
|
||||
andi.,AND Immediate,D,28,
|
||||
andis.,AND Immediate Shifted,D,29,
|
||||
b[l][a],Branch,I,18,
|
||||
bc[l][a],Branch Conditional,B,16,
|
||||
bcctr[l],Branch Conditional to Count Register,XL,19,528
|
||||
bclr[l],Branch Conditional Link Register,XL,19,16
|
||||
cmp,Compare,X,31,0
|
||||
cmpi,Compare Immediate,D,11,
|
||||
cmpl,Compare Logical,X,31,32
|
||||
cmpli,Compare Logical Immediate,D,10,
|
||||
cntlzd,Count Leading Zeros Doubleword,X,31,58
|
||||
cntlzw[.],Count Leading Zeros Word,X,31,26
|
||||
crand,Condition Register AND,XL,19,257
|
||||
crandc,Condition Register AND with Complement,XL,19,129
|
||||
creqv,Condition Register Equivalent,XL,19,289
|
||||
crnand,Condition Register NAND,XL,19,225
|
||||
crnor,Condition Register NOR,XL,19,33
|
||||
cror,Condition Register OR,XL,19,449
|
||||
crorc,Condition Register OR with Complement,XL,19,417
|
||||
crxor,Condition Register XOR,XL,19,193
|
||||
dcbf,Data Cache Block Flush,X,31,86
|
||||
dcbi,Data Cache Block Invalidate,X,31,470
|
||||
dcbst,Data Cache Block Store,X,31,54
|
||||
dcbt,Data Cache Block Touch,X,31,278
|
||||
dcbtst,Data Cache Block Touch for Store,X,31,246
|
||||
dcbz,Data Cache Block Set to Zero,X,31,1014
|
||||
divd,Divide Doubleword,XO,31,489
|
||||
divdu,Divide Doubleword Unsigned,XO,31,457
|
||||
divw[o][.],Divide Word,XO,31,491
|
||||
divwu[o][.],Divide Word Unsigned,XO,31,459
|
||||
eciwx,External Control in Word Indexed (opt.),X,31,310
|
||||
ecowx,External Control out Word Indexed (opt.),X,31,438
|
||||
eieio,Enforce In-order Execution of I/O,X,31,854
|
||||
eqv[.],Equivalent,X,31,284
|
||||
extsb[.],Extend Sign Byte,X,31,954
|
||||
extsh[.],Extend Sign Halfword,XO,31,922
|
||||
extsw,Extend Sign Word,X,31,986
|
||||
fabs[.],Floating Absolute Value,X,63,264
|
||||
fadd[.],Floating Add,A,63,21
|
||||
fadds[.],Floating Add Single,A,59,21
|
||||
fcfid,Floating Convert from Integer Doubleword,X,63,846
|
||||
fcmpo,Floating Compare Ordered,X,63,32
|
||||
fcmpu,Floating Compare Unordered,XL,63,0
|
||||
fctid,Floating Convert to Integer Doubleword,X,63,814
|
||||
fctidz,Floating Convert to Integer Doubleword with Round Toward Zero,X,63,815
|
||||
fctiw[.],Floating Convert to Integer Word,X,63,14
|
||||
fctiwz[.],Floating Convert to Integer Word with Round to Zero,XL,63,15
|
||||
fdiv[.],Floating Divide,A,63,18
|
||||
fdivs[.],Floating Divide Single,A,59,18
|
||||
fmadd[.],Floating Multiply-Add,A,63,29
|
||||
fmadds[.],Floating Multiply-Add Single,A,59,29
|
||||
fmr[.],Floating Move Register,X,63,72
|
||||
fmsub[.],Floating Multiply-Subtract,A,63,28
|
||||
fmsubs[.],Floating Multiply-Subtract Single,A,59,28
|
||||
fmul[.],Floating Multiply,A,63,25
|
||||
fmuls[.],Floating Multiply Single,A,59,25
|
||||
fnabs[.],Floating Negative Absolute Value,X,63,136
|
||||
fneg[.],Floating Negate,X,63,40
|
||||
fnmadd[.],Floating Negative Multiply-Add,A,63,31
|
||||
fnmadds[.],Floating Negative Multiply-Add Single,A,59,31
|
||||
fnmsub[.],Floating Negative Multiply-Subtract,A,63,30
|
||||
fnmsubs[.],Floating Negative Multiply-Subtract Single,A,59,30
|
||||
fres[.],Floating Reciprocal Estimate Single (optional),A,59,24
|
||||
frsp[.],Floating Round to Single Precision,X,63,12
|
||||
frsqrte[.],Floating Reciprocal Square Root Estimate (optional),A,63,26
|
||||
fsel[.],Floating-Point Select (optional),A,63,23
|
||||
fsub[.],Floating Subtract,A,63,20
|
||||
fsubs[.],Floating Subtract Single,A,59,20
|
||||
icbi,Instruction Cache Block Invalidate,X,31,982
|
||||
isync,Instruction Synchronize,X,19,150
|
||||
lbz,Load Byte and Zero,D,34,
|
||||
lbzu,Load Byte and Zero with Update,D,35,
|
||||
lbzux,Load Byte and Zero with Update Indexed,X,31,119
|
||||
lbzx,Load Byte and Zero Indexed,X,31,87
|
||||
ld,Load Doubleword,DS,58,0
|
||||
ldarx,Load Doubleword and Reserve Indexed,X,31,84
|
||||
ldu,Load Doubleword with Update,DS,58,1
|
||||
ldux,Load Doubleword with Update Indexed,X,31,53
|
||||
ldx,Load Doubleword Indexed,X,31,21
|
||||
lfd,Load Floating-Point Double,D,50,
|
||||
lfdu,Load Floating-Point Double with Update,D,51,
|
||||
lfdux,Load Floating-Point Double with Update Indexed,X,31,631
|
||||
lfdx,Load Floating-Point Double Indexed,X,31,599
|
||||
lfs,Load Floating-Point Single,D,48,
|
||||
lfsu,Load Floating-Point Single with Update,D,49,
|
||||
lfsux,Load Floating-Point Single with Update Indexed,X,31,567
|
||||
lfsx,Load Floating-Point Single Indexed,X,31,535
|
||||
lha,Load Half Algebraic,D,42,
|
||||
lhau,Load Half Algebraic with Update,D,43,
|
||||
lhaux,Load Half Algebraic with Update Indexed,X,31,375
|
||||
lhax,Load Half Algebraic Indexed,X,31,343
|
||||
lhbrx,Load Half Byte-Reversed Indexed,X,31,790
|
||||
lhz,Load Half and Zero,D,40,
|
||||
lhzu,Load Half and Zero with Update,D,41,
|
||||
lhzux,Load Half and Zero with Update Indexed,X,31,331
|
||||
lhzx,Load Half and Zero Indexed,X,31,279
|
||||
lmw,Load Multiple Word,D,46,
|
||||
lswi,Load String Word Immediate,X,31,597
|
||||
lswx,Load String Word Indexed,X,31,533
|
||||
lwa,Load Word Algebraic,DS,58,2
|
||||
lwarx,Load Word and Reserve Indexed,X,31,20
|
||||
lwaux,Load Word Algebraic with Update Indexed,X,31,373
|
||||
lwax,Load Word Algebraic Indexed,X,31,341
|
||||
lwbrx,Load Word Byte-Reversed Indexed,X,31,534
|
||||
lwz,Load Word and Zero,D,32,
|
||||
lwzu,Load Word with Zero Update,D,33,
|
||||
lwzux,Load Word and Zero with Update Indexed,X,31,55
|
||||
lwzx,Load Word and Zero Indexed,X,31,23
|
||||
mcrf,Move Condition Register Field,XL,19,0
|
||||
mcrfs,Move to Condition Register from FPSCR,X,63,64
|
||||
mcrxr,Move to Condition Register from XER,X,31,512
|
||||
mfcr,Move from Condition Register,X,31,19
|
||||
mffs[.],Move from FPSCR,X,63,583
|
||||
mfmsr,Move from Machine State Register,X,31,83
|
||||
mfspr,Move from Special-Purpose Register,X,31,339
|
||||
mfsr,Move from Segment Register,X,31,595
|
||||
mfsrin,Move from Segment Register Indirect,X,31,659
|
||||
mtcrf,Move to Condition Register Fields,XFX,31,144
|
||||
mtfsb0[.],Move to FPSCR Bit 0,X,63,70
|
||||
mtfsb1[.],Move to FPSCR Bit 1,X,63,38
|
||||
mtfsf[.],Move to FPSCR Fields,XFL,63,711
|
||||
mtfsfi[.],Move to FPSCR Field Immediate,X,63,134
|
||||
mtmsr,Move to Machine State Register,X,31,146
|
||||
mtspr,Move to Special-Purpose Register,X,31,467
|
||||
mtsr,Move to Segment Register,X,31,210
|
||||
mtsrin,Move to Segment Register Indirect,X,31,242
|
||||
mulhd,Multiply High Doubleword,XO,31,73
|
||||
mulhdu,Multiply High Doubleword Unsigned,XO,31,9
|
||||
mulhw[.],Multiply High Word,XO,31,75
|
||||
mulhwu[.],Multiply High Word Unsigned,XO,31,11
|
||||
mulld,Multiply Low Doubleword,XO,31,233
|
||||
mulli,Multiply Low Immediate,D,07,
|
||||
mullw[o][.],Multiply Low Word,XO,31,235
|
||||
nand[.],NAND,X,31,476
|
||||
neg[o][.],Negate,XO,31,104
|
||||
nor[.],NOR,X,31,124
|
||||
or[.],OR,X,31,444
|
||||
orc[.],OR with Complement,X,31,412
|
||||
ori,OR Immediate,D,24,
|
||||
oris,OR Immediate Shifted,D,25,
|
||||
rfi,Return from Interrupt,X,19,50
|
||||
rldcl,Rotate Left Doubleword then Clear Left,MDS,30,8
|
||||
rldcr,Rotate Left Doubleword then Clear Right,MDS,30,9
|
||||
rldic,Rotate Left Doubleword Immediate then Clear,MD,30,2
|
||||
rldicl,Rotate Left Doubleword Immediate then Clear Left,MD,30,0
|
||||
rldicr,Rotate Left Doubleword Immediate then Clear Right,MD,30,1
|
||||
rldimi,Rotate Left Doubleword Immediate then Mask Insert,MD,30,3
|
||||
rlwimi[.],Rotate Left Word Immediate then Mask Insert,M,20,
|
||||
rlwinm[.],Rotate Left Word Immediate then AND with Mask,M,21,
|
||||
rlwnm[.],Rotate Left Word then AND with Mask,M,23,
|
||||
sc,System Call,SC,17,
|
||||
si,Subtract Immediate,D,12,
|
||||
si.,Subtract Immediate and Record,D,13,
|
||||
slbia,SLB Invalidate All,X,31,498
|
||||
slbie,SLB Invalidate Entry,X,31,434
|
||||
sld,Shift Left Doubleword,X,31,27
|
||||
slw[.],Shift Left Word,X,31,24
|
||||
srad,Shift Right Algebraic Doubleword,X,31,794
|
||||
sradi,Shift Right Algebraic Doubleword Immediate,XS,31,413
|
||||
srd,Shift Right Doubleword,X,31,539
|
||||
sraw[.],Shift Right Algebraic Word,X,31,792
|
||||
srawi[.],Shift Right Algebraic Word Immediate,X,31,824
|
||||
srw[.],Shift Right Word,X,31,536
|
||||
stb,Store Byte,D,38,
|
||||
stbu,Store Byte with Update,D,39,
|
||||
stbux,Store Byte with Update Indexed,X,31,247
|
||||
stbx,Store Byte Indexed,X,31,215
|
||||
std,Store Doubleword,DS,62,0
|
||||
stdcx,Store Doubleword Conditional Indexed,X,31,214
|
||||
stdu,Store Doubleword with Update,DS,62,1
|
||||
stdux,Store Doubleword with Update Indexed,X,31,181
|
||||
stdx,Store Doubleword Indexed,X,31,149
|
||||
stfd,Store Floating-Point Double,D,54,
|
||||
stfdu,Store Floating-Point Double with Update,D,55,
|
||||
stfdux,Store Floating-Point Double with Update Indexed,X,31,759
|
||||
stfdx,Store Floating-Point Double Indexed,X,31,727
|
||||
stfiwx,Store Floating-Point as Integer Word Indexed (optional),X,31,983
|
||||
stfs,Store Floating-Point Single,D,52,
|
||||
stfsu,Store Floating-Point Single with Update,D,53,
|
||||
stfsux,Store Floating-Point Single with Update Indexed,X,31,695
|
||||
stfsx,Store Floating-Point Single Indexed,X,31,663
|
||||
sth,Store Half,D,44,
|
||||
sthbrx,Store Half Byte-Reverse Indexed,X,31,918
|
||||
sthu,Store Half with Update,D,45,
|
||||
sthux,Store Half with Update Indexed,X,31,439
|
||||
sthx,Store Half Indexed,X,31,407
|
||||
stmw,Store Multiple Word,D,47,
|
||||
stswi,Store String Word Immediate,X,31,725
|
||||
stswx,Store String Word Indexed,X,31,661
|
||||
stw,Store,D,36,
|
||||
stwbrx,Store Word Byte-Reversed Indexed,X,31,662
|
||||
stwcx.,Store Word Conditional Indexed,X,31,150
|
||||
stwu,Store Word with Update,D,37,
|
||||
stwux,Store Word with Update Indexed,X,31,183
|
||||
stwx,Store Word Indexed,X,31,151
|
||||
subf[o][.],Subtract from,XO,31,40
|
||||
subfc[o][.],Subtract from Carrying,XO,31,08
|
||||
subfe[o][.],Subtract from Extended,XO,31,136
|
||||
subfic,Subtract from Immediate Carrying,D,08,
|
||||
subfme[o][.],Subtract from Minus One Extended,XO,31,232
|
||||
subfze[o][.],Subtract from Zero Extended,XO,31,200
|
||||
sync,Synchronize,X,31,598
|
||||
td,Trap Doubleword,X,31,68
|
||||
tdi,Trap Doubleword Immediate,D,2,
|
||||
tlbie,Translation Look-aside Buffer Invalidate Entry (optional),X,31,306
|
||||
tlbsync,Translation Look-aside Buffer Synchronize (optional),X,31,566
|
||||
tw,Trap Word,X,31,04
|
||||
twi,Trap Word Immediate,D,03,
|
||||
xor[.],XOR,X,31,316
|
||||
xori,XOR Immediate,D,26,
|
||||
xoris,XOR Immediate Shift,D,27,
|
||||
Vendored
-343
@@ -1,343 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
printf(
|
||||
"// this file is autogenerated DO NOT MODIFY\n"
|
||||
"#pragma once\n"
|
||||
);
|
||||
FILE* fp = fopen(argv[1], "rt");
|
||||
if (fp) {
|
||||
char line[80];
|
||||
while (fgets(line, sizeof line, fp) != NULL) {
|
||||
bool with_o = strstr(line, "[o]"), with_d = strstr(line, "[.]");
|
||||
char* p = strchr(line, '\n'), *name = strchr(line, ','), *mem = line;
|
||||
if (p) *p = '\0';
|
||||
if (name) {
|
||||
*name++ = '\0';
|
||||
char *form = strchr(name, ',');
|
||||
if (form) {
|
||||
*form++ = '\0';
|
||||
char *opc = strchr(form, ',');
|
||||
if (opc) {
|
||||
*opc++ = '\0';
|
||||
char *sec = strchr(opc, ',');
|
||||
if (sec) {
|
||||
struct b_info { const char *s; int o; int p; } infos[] = {
|
||||
{"",1,0},
|
||||
{"LT",1,12},
|
||||
{"LE",2,4},
|
||||
{"NG",2,4},
|
||||
{"EQ",3,12},
|
||||
{"GE",1,4},
|
||||
{"NL",1,4},
|
||||
{"GT",2,12},
|
||||
{"NE",3,4},
|
||||
{"SO",4,12},
|
||||
{"UN",4,12},
|
||||
{"NS",4,4},
|
||||
{"NU",4,4},
|
||||
};
|
||||
#define OP_EXT ((i_opcode << 26) | (i_extopc << 1))
|
||||
if (strchr(mem, '[') != NULL) *strchr(mem, '[') = '\0';
|
||||
if (strchr(mem, '.') != NULL) *strchr(mem, '.') = '_';
|
||||
*sec++ = '\0';
|
||||
for (int i = 0; i < strlen(mem); ++i)
|
||||
mem[i] = toupper(mem[i]);
|
||||
int i_opcode = atoi(opc);
|
||||
int i_extopc = atoi(sec);
|
||||
//printf("// %s\n", mem);
|
||||
if (!strcmp(form, "XO")) {
|
||||
if (!strcmp(mem, "EXTSH")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, false, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sC(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, true, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, false, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sC_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, rt, ra, R0, true, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, false, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sO(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, true, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, false, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sO_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, rt, ra, rb, true, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "X")) {
|
||||
if (!strcmp(mem, "CMPL") || !strcmp(mem, "CMP")) {
|
||||
printf(
|
||||
"void %s(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, rb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(mem, "CNTLZD") || !strcmp(mem, "CNTLZW") || !strcmp(mem, "EXTSB") || !strcmp(mem, "EXTSH") || !strcmp(mem, "EXTSW")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, ra, rt, R0, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra) {"
|
||||
" emit_%s(0x%08x, ra, rt, R0, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, ra, rt, rb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, GPR const rb) {"
|
||||
" emit_%s(0x%08x, ra, rt, rb, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "I")) {
|
||||
printf(
|
||||
"void %s(Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, i, false); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
printf(
|
||||
"void %sL(Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, i, true); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(form, "B")) {
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
printf(
|
||||
"void %s%s(CPR const cr, Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, false); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
|
||||
printf(
|
||||
"void %s%sL(CPR const cr, Label const& i) {"
|
||||
" emit_reloc_%s(0x%08x, %i, cr.index + %i, i, true); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, i_opcode << 26, infos[i].p, infos[i].o - 1);
|
||||
if (!strcmp(mem, "BC")) mem[1] = '\0';
|
||||
}
|
||||
} else if (!strcmp(form, "D")) {
|
||||
if (!strcmp(mem, "CMPLI") || !strcmp(mem, "CMPI")) {
|
||||
printf(
|
||||
"void %s(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, GPR{(bf << 2) | l}, ra, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(mem, "ANDIS_") || !strcmp(mem, "ANDI_")
|
||||
|| !strcmp(mem, "ORI") || !strcmp(mem, "ORIS")
|
||||
|| !strcmp(mem, "XORI") || !strcmp(mem, "XORIS")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, ra, rt, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, rt, ra, d); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
}
|
||||
} else if (!strcmp(form, "SC")) {
|
||||
printf(
|
||||
"void %s(uint32_t lev) {"
|
||||
" emit_%s(0x%08x, lev); "
|
||||
"}\n"
|
||||
, mem, form, i_opcode << 26);
|
||||
} else if (!strcmp(form, "DS")) {
|
||||
#define OP_EXT_DS ((i_opcode << 26) | (i_extopc << 0))
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t d) {"
|
||||
" emit_%s(0x%08x, rt, ra, d >> 2); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_DS);
|
||||
#undef OP_EXT_DS
|
||||
} else if (!strcmp(form, "XS")) {
|
||||
#define OP_EXT_XS ((i_opcode << 26) | (i_extopc << 2))
|
||||
/* HUGE DIFFERENCE DO NOT REMOVE */
|
||||
printf(
|
||||
"void %s(GPR const rt, GPR const ra, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, rt, ra, sh, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_XS);
|
||||
printf(
|
||||
"void %s_(GPR const rt, GPR const ra, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, rt, ra, sh, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT_XS);
|
||||
#undef OP_EXT_XS
|
||||
} else if (!strcmp(form, "XL")) {
|
||||
if (!strcmp(mem, "BCLR")) {
|
||||
printf(
|
||||
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
if (!strcmp(mem, "BCLR")) mem[1] = '\0';
|
||||
for (int i = 1; i < 12; ++i) {
|
||||
printf(
|
||||
"void %s%sLR(CPR const cr) {"
|
||||
" emit_%s(0x%08x, %i, cr.index + %i, 0, false); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
|
||||
printf(
|
||||
"void %s%sLRL(CPR const cr) {"
|
||||
" emit_%s(0x%08x, %i, cr.index + %i, 0, true); "
|
||||
"}\n"
|
||||
, mem, infos[i].s, form, OP_EXT, infos[i].p, infos[i].o - 1);
|
||||
}
|
||||
} else if (mem[0] == 'B') {
|
||||
printf(
|
||||
"void %s(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, CPR const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s(GPR const bt, Cond const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(GPR const bt, Cond const ba, GPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, cond2offset(ba), bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(CPR const bt, CPR const ba, CPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %sL(CPR const bt, CPR const ba, CPR const bb) {"
|
||||
" emit_%s(0x%08x, bt.index, ba.index, bb.index, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "M")) {
|
||||
if (!strcmp(mem, "RLWNM")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb.index, mb, me, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, sh, mb, me, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) {"
|
||||
" emit_%s(0x%08x, ra, rs, sh, mb, me, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
} else if (!strcmp(form, "MD")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, ra, rs, mb, sh, false); "
|
||||
"}\n"
|
||||
, mem, form, (i_opcode << 26) | (i_extopc << 2));
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) {"
|
||||
" emit_%s(0x%08x, ra, rs, mb, sh, true); "
|
||||
"}\n"
|
||||
, mem, form, (i_opcode << 26) | (i_extopc << 2));
|
||||
} else if (!strcmp(form, "MDS")) {
|
||||
printf(
|
||||
"void %s(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb, mb, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) {"
|
||||
" emit_%s(0x%08x, ra, rs, rb, mb, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(form, "A")) {
|
||||
printf(
|
||||
"void %s(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
|
||||
" emit_%s(0x%08x, frt, fra, frb, frc, false); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
printf(
|
||||
"void %s_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) {"
|
||||
" emit_%s(0x%08x, frt, fra, frb, frc, true); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else if (!strcmp(form, "XFX")) {
|
||||
printf(
|
||||
"void %s(GPR const rt, uint32_t spr) {"
|
||||
" emit_%s(0x%08x, rt, spr); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
} else {
|
||||
printf(
|
||||
"void %s() {"
|
||||
" emit_%s(0x%08x); "
|
||||
"}\n"
|
||||
, mem, form, OP_EXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//printf("%s\n", line);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
Vendored
-401
@@ -1,401 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <sys/mman.h>
|
||||
#include <cstdlib>
|
||||
|
||||
//#ifndef __cpp_lib_unreachable
|
||||
namespace std {
|
||||
[[noreturn]] inline void unreachable() {
|
||||
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
|
||||
__assume(false);
|
||||
#else // GCC, Clang
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
||||
namespace powah {
|
||||
|
||||
// Symbolic conditions
|
||||
enum class Cond : uint8_t {
|
||||
LT, LE, NG, EQ, GE, NL, GT, NE, SO, UN, NS, NU
|
||||
};
|
||||
|
||||
struct GPR { uint32_t index; };
|
||||
struct FPR { uint32_t index; };
|
||||
struct VPR { uint32_t index; };
|
||||
struct CPR { uint32_t index; };
|
||||
struct Label { uint32_t index; };
|
||||
|
||||
constexpr inline GPR R0{0};
|
||||
constexpr inline GPR R1{1};
|
||||
constexpr inline GPR R2{2};
|
||||
constexpr inline GPR R3{3};
|
||||
constexpr inline GPR R4{4};
|
||||
constexpr inline GPR R5{5};
|
||||
constexpr inline GPR R6{6};
|
||||
constexpr inline GPR R7{7};
|
||||
constexpr inline GPR R8{8};
|
||||
constexpr inline GPR R9{9};
|
||||
constexpr inline GPR R10{10};
|
||||
constexpr inline GPR R11{11};
|
||||
constexpr inline GPR R12{12};
|
||||
constexpr inline GPR R13{13};
|
||||
constexpr inline GPR R14{14};
|
||||
constexpr inline GPR R15{15};
|
||||
constexpr inline GPR R16{16};
|
||||
constexpr inline GPR R17{17};
|
||||
constexpr inline GPR R18{18};
|
||||
constexpr inline GPR R19{19};
|
||||
constexpr inline GPR R20{20};
|
||||
constexpr inline GPR R21{21};
|
||||
constexpr inline GPR R22{22};
|
||||
constexpr inline GPR R23{23};
|
||||
constexpr inline GPR R24{24};
|
||||
constexpr inline GPR R25{25};
|
||||
constexpr inline GPR R26{26};
|
||||
constexpr inline GPR R27{27};
|
||||
constexpr inline GPR R28{28};
|
||||
constexpr inline GPR R29{29};
|
||||
constexpr inline GPR R30{30};
|
||||
constexpr inline GPR R31{31};
|
||||
|
||||
constexpr inline FPR FR0{0};
|
||||
constexpr inline FPR FR1{1};
|
||||
constexpr inline FPR FR2{2};
|
||||
constexpr inline FPR FR3{3};
|
||||
constexpr inline FPR FR4{4};
|
||||
constexpr inline FPR FR5{5};
|
||||
constexpr inline FPR FR6{6};
|
||||
constexpr inline FPR FR7{7};
|
||||
constexpr inline FPR FR8{8};
|
||||
constexpr inline FPR FR9{9};
|
||||
constexpr inline FPR FR10{10};
|
||||
constexpr inline FPR FR11{11};
|
||||
constexpr inline FPR FR12{12};
|
||||
constexpr inline FPR FR13{13};
|
||||
constexpr inline FPR FR14{14};
|
||||
constexpr inline FPR FR15{15};
|
||||
constexpr inline FPR FR16{16};
|
||||
constexpr inline FPR FR17{17};
|
||||
constexpr inline FPR FR18{18};
|
||||
constexpr inline FPR FR19{19};
|
||||
constexpr inline FPR FR20{20};
|
||||
constexpr inline FPR FR21{21};
|
||||
constexpr inline FPR FR22{22};
|
||||
constexpr inline FPR FR23{23};
|
||||
constexpr inline FPR FR24{24};
|
||||
constexpr inline FPR FR25{25};
|
||||
constexpr inline FPR FR26{26};
|
||||
constexpr inline FPR FR27{27};
|
||||
constexpr inline FPR FR28{28};
|
||||
constexpr inline FPR FR29{29};
|
||||
constexpr inline FPR FR30{30};
|
||||
constexpr inline FPR FR31{31};
|
||||
|
||||
// They call it CPR because when programmers see this code they-
|
||||
constexpr inline CPR CR0{0};
|
||||
constexpr inline CPR CR1{4};
|
||||
constexpr inline CPR CR2{8};
|
||||
constexpr inline CPR CR3{12};
|
||||
constexpr inline CPR CR4{16};
|
||||
constexpr inline CPR CR5{20};
|
||||
constexpr inline CPR CR6{24};
|
||||
constexpr inline CPR CR7{28};
|
||||
|
||||
constexpr uint32_t XER_SO = 32;
|
||||
constexpr uint32_t XER_OV = 33;
|
||||
constexpr uint32_t XER_CA = 34;
|
||||
|
||||
enum class RelocKind : uint8_t {
|
||||
FormB,
|
||||
FormI,
|
||||
};
|
||||
struct RelocInfo {
|
||||
uint32_t offset;
|
||||
RelocKind kind;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
Context() = default;
|
||||
Context(void* ptr, size_t size)
|
||||
: base{reinterpret_cast<uint32_t*>(ptr)}
|
||||
, offset{0}
|
||||
, size{uint32_t(size)} {
|
||||
|
||||
}
|
||||
~Context() = default;
|
||||
|
||||
std::vector<uint32_t> labels;
|
||||
std::vector<std::pair<uint32_t, RelocInfo>> relocs;
|
||||
|
||||
Label DefineLabel() {
|
||||
labels.push_back(0);
|
||||
return Label{ uint32_t(labels.size() - 1) };
|
||||
}
|
||||
|
||||
void ApplyRelocs() {
|
||||
for (auto const &[index, info] : relocs) {
|
||||
//assert(labels[index] != 0); //label must have an addr
|
||||
int32_t rel = (int32_t)labels[index] - (int32_t)info.offset;
|
||||
switch (info.kind) {
|
||||
case RelocKind::FormB:
|
||||
base[info.offset] |= bitExt(rel, 16, 14);
|
||||
break;
|
||||
case RelocKind::FormI:
|
||||
base[info.offset] |= bitExt(rel, 6, 24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
relocs.clear();
|
||||
}
|
||||
|
||||
void LABEL(Label l) {
|
||||
assert(labels[l.index] == 0);
|
||||
labels[l.index] = offset;
|
||||
}
|
||||
|
||||
static constexpr uint32_t cond2offset(Cond c) noexcept {
|
||||
switch (c) {
|
||||
case Cond::LT: return 1;
|
||||
case Cond::LE: return 2;
|
||||
case Cond::NG: return 2;
|
||||
case Cond::EQ: return 3;
|
||||
case Cond::GE: return 1;
|
||||
case Cond::NL: return 1;
|
||||
case Cond::GT: return 2;
|
||||
case Cond::NE: return 3;
|
||||
case Cond::SO: return 4;
|
||||
case Cond::UN: return 4;
|
||||
case Cond::NS: return 4;
|
||||
case Cond::NU: return 4;
|
||||
default: return 0; //hopefully icc isn't stupid
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bitExt(uint32_t value, uint32_t offs, uint32_t n) {
|
||||
uint32_t mask = (1UL << n) - 1;
|
||||
return (value & mask) << (32 - (n + offs));
|
||||
}
|
||||
void emit_XO(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool oe, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| bitExt(oe, 21, 1)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_D(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| (d & 0xffff)
|
||||
);
|
||||
}
|
||||
void emit_X(uint32_t op, GPR const rt, GPR const ra, GPR const rb, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XS(uint32_t op, GPR const rt, GPR const ra, uint32_t sh, bool rc) {
|
||||
base[offset++] = (op |
|
||||
((rt.index & 0x1f) << 16)
|
||||
| ((ra.index & 0x1f) << 21)
|
||||
| ((sh & 0x1f) << 11)
|
||||
| ((sh >> 4) & 0x02)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_reloc_I(uint32_t op, Label const& l, bool lk) {
|
||||
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormI });
|
||||
base[offset++] = (op |
|
||||
bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_reloc_B(uint32_t op, uint32_t bo, uint32_t cri, Label const& l, bool lk) {
|
||||
relocs.emplace_back(l.index, RelocInfo{ offset, RelocKind::FormB });
|
||||
base[offset++] = (op |
|
||||
bitExt(bo, 6, 5)
|
||||
| bitExt(cri, 11, 5)
|
||||
| bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XL(uint32_t op, uint32_t bt, uint32_t ba, uint32_t bb, bool lk) {
|
||||
base[offset++] = (op |
|
||||
bitExt(bt, 6, 5)
|
||||
| bitExt(ba, 11, 5)
|
||||
| bitExt(bb, 16, 5)
|
||||
| bitExt(lk, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_A(uint32_t op, FPR const frt, FPR const fra, FPR const frb, FPR const frc, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(frt.index, 6, 5)
|
||||
| bitExt(fra.index, 11, 5)
|
||||
| bitExt(frb.index, 16, 5)
|
||||
| bitExt(frc.index, 21, 5)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_DS(uint32_t op, GPR const rt, GPR const ra, uint32_t d) {
|
||||
//assert(d & 0x03 == 0);
|
||||
base[offset++] = (op |
|
||||
bitExt(rt.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(d, 16, 14)
|
||||
);
|
||||
}
|
||||
void emit_M(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me, bool rc) {
|
||||
assert(sh <= 0x3f && mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| ((sh & 0x1f) << 11)
|
||||
| ((mb & 0x1f) << 6)
|
||||
| ((me & 0x1f) << 1)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MD(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
|
||||
assert(mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MD(uint32_t op, GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, bool rc) {
|
||||
assert(sh <= 0x3f && mb <= 0x3f);
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| ((sh & 0x1f) << 11) | ((sh >> 4) & 0x02)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_MDS(uint32_t op, GPR const rs, GPR const ra, GPR const rb, uint32_t mb, bool rc) {
|
||||
base[offset++] = (op |
|
||||
bitExt(rs.index, 6, 5)
|
||||
| bitExt(ra.index, 11, 5)
|
||||
| bitExt(rb.index, 16, 5)
|
||||
| ((mb & 0x1f) << 6) | (mb & 0x20)
|
||||
| bitExt(rc, 31, 1)
|
||||
);
|
||||
}
|
||||
void emit_XFL(uint32_t op) {
|
||||
(void)op;
|
||||
std::abort();
|
||||
}
|
||||
void emit_XFX(uint32_t op, GPR const rt, uint32_t spr) {
|
||||
base[offset++] = (op |
|
||||
(rt.index & 0x1f) << 21
|
||||
| ((spr & 0xff) << 12)
|
||||
);
|
||||
}
|
||||
void emit_SC(uint32_t op, uint32_t lev) {
|
||||
(void)op;
|
||||
(void)lev;
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// Extended Memmonics, hand coded :)
|
||||
void MR(GPR const ra, GPR const rs) { OR(ra, rs, rs); }
|
||||
void NOP() { ORI(R0, R0, 0); }
|
||||
void NOT(GPR const ra, GPR const rs) { NOR(ra, rs, rs); }
|
||||
|
||||
void ROTLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, n, 0); }
|
||||
void ROTRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, 0); }
|
||||
|
||||
void ROTLWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, n, 0, 31); }
|
||||
void ROTRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, 0, 31); }
|
||||
|
||||
void ROTLW(GPR const ra, GPR const rs, GPR const rb) { RLWNM(ra, rs, rb, 0, 31); }
|
||||
void ROTLD(GPR const ra, GPR const rs, GPR const rb) { RLDCL(ra, rs, rb, 0); }
|
||||
|
||||
void EXTLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, b, n - 1); }
|
||||
void SLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, n, 63 - n); }
|
||||
void CLRLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 0, n); }
|
||||
|
||||
void EXTRDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICL(ra, rs, b + n, 64 - n); }
|
||||
void SRDI(GPR const ra, GPR const rs, uint32_t n) { RLDICL(ra, rs, 64 - n, n); }
|
||||
void CLLDI(GPR const ra, GPR const rs, uint32_t n) { RLDICR(ra, rs, 0, n); }
|
||||
void CLRSLDI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLDICR(ra, rs, n, b - n); }
|
||||
|
||||
void EXTLWI(GPR const ra, GPR const rs, uint32_t n, uint32_t b) { RLWINM(ra, rs, b, 0, n - 1); }
|
||||
void SRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 32 - n, n, 31); }
|
||||
void CLRRWI(GPR const ra, GPR const rs, uint32_t n) { RLWINM(ra, rs, 0, 0, 31 - n); }
|
||||
|
||||
void CRSET(CPR const bx) { CREQV(bx, bx, bx); }
|
||||
void CRCLR(CPR const bx) { CRXOR(bx, bx, bx); }
|
||||
void CRMOVE(CPR const bx, CPR const by) { CROR(bx, by, by); }
|
||||
void CRNOT(CPR const bx, CPR const by) { CRNOR(bx, by, by); }
|
||||
|
||||
void CMPLDI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 1, rx, v); }
|
||||
void CMPLWI(CPR const cr, GPR const rx, uint32_t v) { CMPLI(cr.index, 0, rx, v); }
|
||||
void CMPLD(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 1, rx, ry); }
|
||||
void CMPLW(CPR const cr, GPR const rx, GPR const ry) { CMPL(cr.index, 0, rx, ry); }
|
||||
void CMPLDI(GPR const rx, uint32_t v) { CMPLI(0, 1, rx, v); }
|
||||
void CMPLWI(GPR const rx, uint32_t v) { CMPLI(0, 0, rx, v); }
|
||||
void CMPLD(GPR const rx, GPR const ry) { CMPL(0, 1, rx, ry); }
|
||||
void CMPLW(GPR const rx, GPR const ry) { CMPL(0, 0, rx, ry); }
|
||||
|
||||
void CMPWI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 0, rx, si); }
|
||||
void CMPW(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 0, rx, ry); }
|
||||
void CMPDI(CPR const cr, GPR const rx, uint32_t si) { CMPI(cr.index, 1, rx, si); }
|
||||
void CMPD(CPR const cr, GPR const rx, GPR const ry) { CMP(cr.index, 1, rx, ry); }
|
||||
void CMPWI(GPR const rx, uint32_t si) { CMPI(0, 0, rx, si); }
|
||||
void CMPW(GPR const rx, GPR const ry) { CMP(0, 0, rx, ry); }
|
||||
void CMPDI(GPR const rx, uint32_t si) { CMPI(0, 1, rx, si); }
|
||||
void CMPD(GPR const rx, GPR const ry) { CMP(0, 1, rx, ry); }
|
||||
|
||||
void LI(GPR const rx, uint32_t value) { ADDI(rx, R0, value); }
|
||||
void LIS(GPR const rx, uint32_t value) { ADDIS(rx, R0, value); }
|
||||
|
||||
void MFLR(GPR const rt) { MFSPR(powah::GPR{8}, rt, powah::GPR{0}); }
|
||||
void MTLR(GPR const rt) { MTSPR(powah::GPR{8}, rt, powah::GPR{0}); }
|
||||
void BLR() { base[offset++] = 0x4e800020; } //BCLR(R0, CR0, R0);
|
||||
|
||||
void MFCTR(GPR const rt) { MFSPR(powah::GPR{9}, rt, powah::GPR{0}); }
|
||||
void MTCTR(GPR const rt) { MTSPR(powah::GPR{9}, rt, powah::GPR{0}); }
|
||||
void BCTRL() { base[offset++] = 0x4e800421; } //BCCTRL(R0, CR0, R0);
|
||||
void BCTR() { base[offset++] = 0x4e800420; } //BCCTR(R0, CR0, R0);
|
||||
|
||||
// TODO: PowerPC 11 stuff
|
||||
void ISEL(GPR const rd, GPR const ra, GPR const rb, uint32_t d) {
|
||||
(void)rd;
|
||||
(void)ra;
|
||||
(void)rb;
|
||||
(void)d;
|
||||
std::unreachable();
|
||||
}
|
||||
void ISELLT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 0); }
|
||||
void ISELGT(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 1); }
|
||||
void ISELEQ(GPR const rd, GPR const ra, GPR const rb) { ISEL(rd, ra, rb, 2); }
|
||||
|
||||
// Rawly pasted because fuck you
|
||||
#include "powah_gen_base.hpp"
|
||||
|
||||
uint32_t* base = nullptr;
|
||||
uint32_t offset = 0;
|
||||
uint32_t size = 0;
|
||||
};
|
||||
|
||||
}
|
||||
Vendored
-487
@@ -1,487 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// this file is autogenerated DO NOT MODIFY
|
||||
#pragma once
|
||||
void ADD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, false); }
|
||||
void ADDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, false); }
|
||||
void ADD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, false, true); }
|
||||
void ADDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000214, rt, ra, rb, true, true); }
|
||||
void ADDC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, false); }
|
||||
void ADDCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, false); }
|
||||
void ADDC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, false, true); }
|
||||
void ADDCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000014, rt, ra, rb, true, true); }
|
||||
void ADDE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, false); }
|
||||
void ADDEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, false); }
|
||||
void ADDE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, false, true); }
|
||||
void ADDEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000114, rt, ra, rb, true, true); }
|
||||
void ADDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x38000000, rt, ra, d); }
|
||||
void ADDIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
|
||||
void ADDIC_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
|
||||
void ADDIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x3c000000, rt, ra, d); }
|
||||
void ADDME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, false); }
|
||||
void ADDMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, false); }
|
||||
void ADDME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, false, true); }
|
||||
void ADDMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d4, rt, ra, rb, true, true); }
|
||||
void ADDZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, false); }
|
||||
void ADDZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, false); }
|
||||
void ADDZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, false, true); }
|
||||
void ADDZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000194, rt, ra, rb, true, true); }
|
||||
void AND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, false); }
|
||||
void AND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000038, ra, rt, rb, true); }
|
||||
void ANDC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, false); }
|
||||
void ANDC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000078, ra, rt, rb, true); }
|
||||
void ANDI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x70000000, ra, rt, d); }
|
||||
void ANDIS_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x74000000, ra, rt, d); }
|
||||
void B(Label const& i) { emit_reloc_I(0x48000000, i, false); }
|
||||
void BL(Label const& i) { emit_reloc_I(0x48000000, i, true); }
|
||||
void BC(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, false); }
|
||||
void BCL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 0, cr.index + 0, i, true); }
|
||||
void BLT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, false); }
|
||||
void BLTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 0, i, true); }
|
||||
void BLE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
|
||||
void BLEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
|
||||
void BNG(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, false); }
|
||||
void BNGL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 1, i, true); }
|
||||
void BEQ(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, false); }
|
||||
void BEQL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 2, i, true); }
|
||||
void BGE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
|
||||
void BGEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
|
||||
void BNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, false); }
|
||||
void BNLL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 0, i, true); }
|
||||
void BGT(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, false); }
|
||||
void BGTL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 1, i, true); }
|
||||
void BNE(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, false); }
|
||||
void BNEL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 2, i, true); }
|
||||
void BSO(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
|
||||
void BSOL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
|
||||
void BUN(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, false); }
|
||||
void BUNL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 12, cr.index + 3, i, true); }
|
||||
void BNS(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, false); }
|
||||
void BNSL(CPR const cr, Label const& i) { emit_reloc_B(0x40000000, 4, cr.index + 3, i, true); }
|
||||
void BCCTR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, false); }
|
||||
void BCCTRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, ba.index, bb.index, true); }
|
||||
void BCCTR(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, false); }
|
||||
void BCCTRL(GPR const bt, Cond const ba, GPR const bb) { emit_XL(0x4c000420, bt.index, cond2offset(ba), bb.index, true); }
|
||||
void BCLR(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, false); }
|
||||
void BCLRL(GPR const bt, CPR const ba, GPR const bb) { emit_XL(0x4c000020, bt.index, ba.index, bb.index, true); }
|
||||
void BLTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, false); }
|
||||
void BLTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 0, 0, true); }
|
||||
void BLELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
|
||||
void BLELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
|
||||
void BNGLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, false); }
|
||||
void BNGLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 1, 0, true); }
|
||||
void BEQLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, false); }
|
||||
void BEQLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 2, 0, true); }
|
||||
void BGELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
|
||||
void BGELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
|
||||
void BNLLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, false); }
|
||||
void BNLLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 0, 0, true); }
|
||||
void BGTLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, false); }
|
||||
void BGTLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 1, 0, true); }
|
||||
void BNELR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, false); }
|
||||
void BNELRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 2, 0, true); }
|
||||
void BSOLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
|
||||
void BSOLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
|
||||
void BUNLR(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, false); }
|
||||
void BUNLRL(CPR const cr) { emit_XL(0x4c000020, 12, cr.index + 3, 0, true); }
|
||||
void BNSLR(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, false); }
|
||||
void BNSLRL(CPR const cr) { emit_XL(0x4c000020, 4, cr.index + 3, 0, true); }
|
||||
void CMP(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000000, GPR{(bf << 2) | l}, ra, rb, false); }
|
||||
void CMPI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x2c000000, GPR{(bf << 2) | l}, ra, d); }
|
||||
void CMPL(uint32_t bf, uint32_t l, GPR const ra, GPR const rb) { emit_X(0x7c000040, GPR{(bf << 2) | l}, ra, rb, false); }
|
||||
void CMPLI(uint32_t bf, uint32_t l, GPR const ra, uint32_t d) { emit_D(0x28000000, GPR{(bf << 2) | l}, ra, d); }
|
||||
void CNTLZD(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, false); }
|
||||
void CNTLZD_(GPR const rt, GPR const ra) { emit_X(0x7c000074, ra, rt, R0, true); }
|
||||
void CNTLZW(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, false); }
|
||||
void CNTLZW_(GPR const rt, GPR const ra) { emit_X(0x7c000034, ra, rt, R0, true); }
|
||||
void CRAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, false); }
|
||||
void CRANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000202, bt.index, ba.index, bb.index, true); }
|
||||
void CRANDC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, false); }
|
||||
void CRANDCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000102, bt.index, ba.index, bb.index, true); }
|
||||
void CREQV(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, false); }
|
||||
void CREQVL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000242, bt.index, ba.index, bb.index, true); }
|
||||
void CRNAND(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, false); }
|
||||
void CRNANDL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c0001c2, bt.index, ba.index, bb.index, true); }
|
||||
void CRNOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, false); }
|
||||
void CRNORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000042, bt.index, ba.index, bb.index, true); }
|
||||
void CROR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, false); }
|
||||
void CRORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000382, bt.index, ba.index, bb.index, true); }
|
||||
void CRORC(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, false); }
|
||||
void CRORCL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000342, bt.index, ba.index, bb.index, true); }
|
||||
void CRXOR(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, false); }
|
||||
void CRXORL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000182, bt.index, ba.index, bb.index, true); }
|
||||
void DCBF(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, false); }
|
||||
void DCBF_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ac, ra, rt, rb, true); }
|
||||
void DCBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, false); }
|
||||
void DCBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003ac, ra, rt, rb, true); }
|
||||
void DCBST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, false); }
|
||||
void DCBST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006c, ra, rt, rb, true); }
|
||||
void DCBT(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, false); }
|
||||
void DCBT_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022c, ra, rt, rb, true); }
|
||||
void DCBTST(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, false); }
|
||||
void DCBTST_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ec, ra, rt, rb, true); }
|
||||
void DCBZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, false); }
|
||||
void DCBZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ec, ra, rt, rb, true); }
|
||||
void DIVD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, false); }
|
||||
void DIVDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, false); }
|
||||
void DIVD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, false, true); }
|
||||
void DIVDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d2, rt, ra, rb, true, true); }
|
||||
void DIVDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, false); }
|
||||
void DIVDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, false); }
|
||||
void DIVDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, false, true); }
|
||||
void DIVDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000392, rt, ra, rb, true, true); }
|
||||
void DIVW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, false); }
|
||||
void DIVWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, false); }
|
||||
void DIVW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, false, true); }
|
||||
void DIVWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0003d6, rt, ra, rb, true, true); }
|
||||
void DIVWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, false); }
|
||||
void DIVWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, false); }
|
||||
void DIVWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, false, true); }
|
||||
void DIVWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000396, rt, ra, rb, true, true); }
|
||||
void ECIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, false); }
|
||||
void ECIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00026c, ra, rt, rb, true); }
|
||||
void ECOWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, false); }
|
||||
void ECOWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036c, ra, rt, rb, true); }
|
||||
void EIEIO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, false); }
|
||||
void EIEIO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0006ac, ra, rt, rb, true); }
|
||||
void EQV(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, false); }
|
||||
void EQV_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000238, ra, rt, rb, true); }
|
||||
void EXTSB(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, false); }
|
||||
void EXTSB_(GPR const rt, GPR const ra) { emit_X(0x7c000774, ra, rt, R0, true); }
|
||||
void EXTSH(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, false); }
|
||||
void EXTSHC(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, false); }
|
||||
void EXTSH_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, false, true); }
|
||||
void EXTSHC_(GPR const rt, GPR const ra) { emit_XO(0x7c000734, rt, ra, R0, true, true); }
|
||||
void EXTSW(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, false); }
|
||||
void EXTSW_(GPR const rt, GPR const ra) { emit_X(0x7c0007b4, ra, rt, R0, true); }
|
||||
void FABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, false); }
|
||||
void FABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000210, ra, rt, rb, true); }
|
||||
void FADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, false); }
|
||||
void FADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002a, frt, fra, frb, frc, true); }
|
||||
void FADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, false); }
|
||||
void FADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00002a, frt, fra, frb, frc, true); }
|
||||
void FCFID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, false); }
|
||||
void FCFID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00069c, ra, rt, rb, true); }
|
||||
void FCMPO(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, false); }
|
||||
void FCMPO_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000040, ra, rt, rb, true); }
|
||||
void FCMPU(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, false); }
|
||||
void FCMPUL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc000000, bt.index, ba.index, bb.index, true); }
|
||||
void FCTID(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, false); }
|
||||
void FCTID_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065c, ra, rt, rb, true); }
|
||||
void FCTIDZ(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, false); }
|
||||
void FCTIDZ_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00065e, ra, rt, rb, true); }
|
||||
void FCTIW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, false); }
|
||||
void FCTIW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00001c, ra, rt, rb, true); }
|
||||
void FCTIWZ(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, false); }
|
||||
void FCTIWZL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0xfc00001e, bt.index, ba.index, bb.index, true); }
|
||||
void FDIV(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, false); }
|
||||
void FDIV_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000024, frt, fra, frb, frc, true); }
|
||||
void FDIVS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, false); }
|
||||
void FDIVS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000024, frt, fra, frb, frc, true); }
|
||||
void FMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, false); }
|
||||
void FMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003a, frt, fra, frb, frc, true); }
|
||||
void FMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, false); }
|
||||
void FMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003a, frt, fra, frb, frc, true); }
|
||||
void FMR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, false); }
|
||||
void FMR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000090, ra, rt, rb, true); }
|
||||
void FMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, false); }
|
||||
void FMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000038, frt, fra, frb, frc, true); }
|
||||
void FMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, false); }
|
||||
void FMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000038, frt, fra, frb, frc, true); }
|
||||
void FMUL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, false); }
|
||||
void FMUL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000032, frt, fra, frb, frc, true); }
|
||||
void FMULS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, false); }
|
||||
void FMULS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000032, frt, fra, frb, frc, true); }
|
||||
void FNABS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, false); }
|
||||
void FNABS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000110, ra, rt, rb, true); }
|
||||
void FNEG(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, false); }
|
||||
void FNEG_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000050, ra, rt, rb, true); }
|
||||
void FNMADD(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, false); }
|
||||
void FNMADD_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003e, frt, fra, frb, frc, true); }
|
||||
void FNMADDS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, false); }
|
||||
void FNMADDS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003e, frt, fra, frb, frc, true); }
|
||||
void FNMSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, false); }
|
||||
void FNMSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00003c, frt, fra, frb, frc, true); }
|
||||
void FNMSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, false); }
|
||||
void FNMSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec00003c, frt, fra, frb, frc, true); }
|
||||
void FRES(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, false); }
|
||||
void FRES_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000030, frt, fra, frb, frc, true); }
|
||||
void FRSP(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, false); }
|
||||
void FRSP_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000018, ra, rt, rb, true); }
|
||||
void FRSQRTE(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, false); }
|
||||
void FRSQRTE_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000034, frt, fra, frb, frc, true); }
|
||||
void FSEL(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, false); }
|
||||
void FSEL_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc00002e, frt, fra, frb, frc, true); }
|
||||
void FSUB(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, false); }
|
||||
void FSUB_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xfc000028, frt, fra, frb, frc, true); }
|
||||
void FSUBS(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, false); }
|
||||
void FSUBS_(FPR const frt, FPR const fra, FPR const frb, FPR const frc) { emit_A(0xec000028, frt, fra, frb, frc, true); }
|
||||
void ICBI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, false); }
|
||||
void ICBI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ac, ra, rt, rb, true); }
|
||||
void ISYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, false); }
|
||||
void ISYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c00012c, ra, rt, rb, true); }
|
||||
void LBZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x88000000, rt, ra, d); }
|
||||
void LBZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x8c000000, rt, ra, d); }
|
||||
void LBZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, false); }
|
||||
void LBZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ee, ra, rt, rb, true); }
|
||||
void LBZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, false); }
|
||||
void LBZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000ae, ra, rt, rb, true); }
|
||||
void LD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000000, rt, ra, d >> 2); }
|
||||
void LDARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, false); }
|
||||
void LDARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a8, ra, rt, rb, true); }
|
||||
void LDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000001, rt, ra, d >> 2); }
|
||||
void LDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, false); }
|
||||
void LDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006a, ra, rt, rb, true); }
|
||||
void LDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, false); }
|
||||
void LDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002a, ra, rt, rb, true); }
|
||||
void LFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc8000000, rt, ra, d); }
|
||||
void LFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xcc000000, rt, ra, d); }
|
||||
void LFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, false); }
|
||||
void LFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ee, ra, rt, rb, true); }
|
||||
void LFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, false); }
|
||||
void LFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ae, ra, rt, rb, true); }
|
||||
void LFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc0000000, rt, ra, d); }
|
||||
void LFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xc4000000, rt, ra, d); }
|
||||
void LFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, false); }
|
||||
void LFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046e, ra, rt, rb, true); }
|
||||
void LFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, false); }
|
||||
void LFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042e, ra, rt, rb, true); }
|
||||
void LHA(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa8000000, rt, ra, d); }
|
||||
void LHAU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xac000000, rt, ra, d); }
|
||||
void LHAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, false); }
|
||||
void LHAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ee, ra, rt, rb, true); }
|
||||
void LHAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, false); }
|
||||
void LHAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ae, ra, rt, rb, true); }
|
||||
void LHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, false); }
|
||||
void LHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00062c, ra, rt, rb, true); }
|
||||
void LHZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa0000000, rt, ra, d); }
|
||||
void LHZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xa4000000, rt, ra, d); }
|
||||
void LHZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, false); }
|
||||
void LHZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000296, ra, rt, rb, true); }
|
||||
void LHZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, false); }
|
||||
void LHZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00022e, ra, rt, rb, true); }
|
||||
void LMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb8000000, rt, ra, d); }
|
||||
void LSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, false); }
|
||||
void LSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004aa, ra, rt, rb, true); }
|
||||
void LSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, false); }
|
||||
void LSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042a, ra, rt, rb, true); }
|
||||
void LWA(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xe8000002, rt, ra, d >> 2); }
|
||||
void LWARX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, false); }
|
||||
void LWARX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000028, ra, rt, rb, true); }
|
||||
void LWAUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, false); }
|
||||
void LWAUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002ea, ra, rt, rb, true); }
|
||||
void LWAX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, false); }
|
||||
void LWAX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002aa, ra, rt, rb, true); }
|
||||
void LWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, false); }
|
||||
void LWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00042c, ra, rt, rb, true); }
|
||||
void LWZ(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x80000000, rt, ra, d); }
|
||||
void LWZU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x84000000, rt, ra, d); }
|
||||
void LWZUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, false); }
|
||||
void LWZUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00006e, ra, rt, rb, true); }
|
||||
void LWZX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, false); }
|
||||
void LWZX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00002e, ra, rt, rb, true); }
|
||||
void MCRF(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, false); }
|
||||
void MCRFL(CPR const bt, CPR const ba, CPR const bb) { emit_XL(0x4c000000, bt.index, ba.index, bb.index, true); }
|
||||
void MCRFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, false); }
|
||||
void MCRFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc000080, ra, rt, rb, true); }
|
||||
void MCRXR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, false); }
|
||||
void MCRXR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000400, ra, rt, rb, true); }
|
||||
void MFCR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, false); }
|
||||
void MFCR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000026, ra, rt, rb, true); }
|
||||
void MFFS(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, false); }
|
||||
void MFFS_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00048e, ra, rt, rb, true); }
|
||||
void MFMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, false); }
|
||||
void MFMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000a6, ra, rt, rb, true); }
|
||||
void MFSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, false); }
|
||||
void MFSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0002a6, ra, rt, rb, true); }
|
||||
void MFSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, false); }
|
||||
void MFSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004a6, ra, rt, rb, true); }
|
||||
void MFSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, false); }
|
||||
void MFSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000526, ra, rt, rb, true); }
|
||||
void MTCRF(GPR const rt, uint32_t spr) { emit_XFX(0x7c000120, rt, spr); }
|
||||
void MTFSB0(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, false); }
|
||||
void MTFSB0_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00008c, ra, rt, rb, true); }
|
||||
void MTFSB1(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, false); }
|
||||
void MTFSB1_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00004c, ra, rt, rb, true); }
|
||||
void MTFSF() { emit_XFL(0xfc00058e); }
|
||||
void MTFSFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, false); }
|
||||
void MTFSFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0xfc00010c, ra, rt, rb, true); }
|
||||
void MTMSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, false); }
|
||||
void MTMSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000124, ra, rt, rb, true); }
|
||||
void MTSPR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, false); }
|
||||
void MTSPR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003a6, ra, rt, rb, true); }
|
||||
void MTSR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, false); }
|
||||
void MTSR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001a4, ra, rt, rb, true); }
|
||||
void MTSRIN(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, false); }
|
||||
void MTSRIN_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001e4, ra, rt, rb, true); }
|
||||
void MULHD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, false); }
|
||||
void MULHDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, false); }
|
||||
void MULHD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, false, true); }
|
||||
void MULHDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000092, rt, ra, rb, true, true); }
|
||||
void MULHDU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, false); }
|
||||
void MULHDUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, false); }
|
||||
void MULHDU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, false, true); }
|
||||
void MULHDUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000012, rt, ra, rb, true, true); }
|
||||
void MULHW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, false); }
|
||||
void MULHWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, false); }
|
||||
void MULHW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, false, true); }
|
||||
void MULHWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000096, rt, ra, rb, true, true); }
|
||||
void MULHWU(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, false); }
|
||||
void MULHWUO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, false); }
|
||||
void MULHWU_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, false, true); }
|
||||
void MULHWUO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000016, rt, ra, rb, true, true); }
|
||||
void MULLD(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, false); }
|
||||
void MULLDO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, false); }
|
||||
void MULLD_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, false, true); }
|
||||
void MULLDO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d2, rt, ra, rb, true, true); }
|
||||
void MULLI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x1c000000, rt, ra, d); }
|
||||
void MULLW(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, false); }
|
||||
void MULLWO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, false); }
|
||||
void MULLW_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, false, true); }
|
||||
void MULLWO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d6, rt, ra, rb, true, true); }
|
||||
void NAND(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, false); }
|
||||
void NAND_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003b8, ra, rt, rb, true); }
|
||||
void NEG(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, false); }
|
||||
void NEGO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, false); }
|
||||
void NEG_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, false, true); }
|
||||
void NEGO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0000d0, rt, ra, rb, true, true); }
|
||||
void NOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, false); }
|
||||
void NOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0000f8, ra, rt, rb, true); }
|
||||
void OR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, false); }
|
||||
void OR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000378, ra, rt, rb, true); }
|
||||
void ORC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, false); }
|
||||
void ORC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000338, ra, rt, rb, true); }
|
||||
void ORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x60000000, ra, rt, d); }
|
||||
void ORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x64000000, ra, rt, d); }
|
||||
void RFI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, false); }
|
||||
void RFI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x4c000064, ra, rt, rb, true); }
|
||||
void RLDCL(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, false); }
|
||||
void RLDCL_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000010, ra, rs, rb, mb, true); }
|
||||
void RLDCR(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, false); }
|
||||
void RLDCR_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb) { emit_MDS(0x78000012, ra, rs, rb, mb, true); }
|
||||
void RLDIC(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, false); }
|
||||
void RLDIC_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000008, ra, rs, mb, sh, true); }
|
||||
void RLDICL(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, false); }
|
||||
void RLDICL_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000000, ra, rs, mb, sh, true); }
|
||||
void RLDICR(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, false); }
|
||||
void RLDICR_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x78000004, ra, rs, mb, sh, true); }
|
||||
void RLDIMI(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, false); }
|
||||
void RLDIMI_(GPR const rs, GPR const ra, uint32_t mb, uint32_t sh) { emit_MD(0x7800000c, ra, rs, mb, sh, true); }
|
||||
void RLWIMI(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, false); }
|
||||
void RLWIMI_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x50000000, ra, rs, sh, mb, me, true); }
|
||||
void RLWINM(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, false); }
|
||||
void RLWINM_(GPR const rs, GPR const ra, uint32_t sh, uint32_t mb, uint32_t me = 0) { emit_M(0x54000000, ra, rs, sh, mb, me, true); }
|
||||
void RLWNM(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, false); }
|
||||
void RLWNM_(GPR const rs, GPR const ra, GPR const rb, uint32_t mb, uint32_t me = 0) { emit_M(0x5c000000, ra, rs, rb.index, mb, me, true); }
|
||||
void SC(uint32_t lev) { emit_SC(0x44000000, lev); }
|
||||
void SI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x30000000, rt, ra, d); }
|
||||
void SI_(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x34000000, rt, ra, d); }
|
||||
void SLBIA(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, false); }
|
||||
void SLBIA_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0003e4, ra, rt, rb, true); }
|
||||
void SLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, false); }
|
||||
void SLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000364, ra, rt, rb, true); }
|
||||
void SLD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, false); }
|
||||
void SLD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000036, ra, rt, rb, true); }
|
||||
void SLW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, false); }
|
||||
void SLW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000030, ra, rt, rb, true); }
|
||||
void SRAD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, false); }
|
||||
void SRAD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000634, ra, rt, rb, true); }
|
||||
void SRADI(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, false); }
|
||||
void SRADI_(GPR const rt, GPR const ra, uint32_t sh) { emit_XS(0x7c000674, rt, ra, sh, true); }
|
||||
void SRD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, false); }
|
||||
void SRD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000436, ra, rt, rb, true); }
|
||||
void SRAW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, false); }
|
||||
void SRAW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000630, ra, rt, rb, true); }
|
||||
void SRAWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, false); }
|
||||
void SRAWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000670, ra, rt, rb, true); }
|
||||
void SRW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, false); }
|
||||
void SRW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000430, ra, rt, rb, true); }
|
||||
void STB(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x98000000, rt, ra, d); }
|
||||
void STBU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x9c000000, rt, ra, d); }
|
||||
void STBUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, false); }
|
||||
void STBUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ee, ra, rt, rb, true); }
|
||||
void STBX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, false); }
|
||||
void STBX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ae, ra, rt, rb, true); }
|
||||
void STD(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000000, rt, ra, d >> 2); }
|
||||
void STDCX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, false); }
|
||||
void STDCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0001ac, ra, rt, rb, true); }
|
||||
void STDU(GPR const rt, GPR const ra, uint32_t d) { emit_DS(0xf8000001, rt, ra, d >> 2); }
|
||||
void STDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, false); }
|
||||
void STDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016a, ra, rt, rb, true); }
|
||||
void STDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, false); }
|
||||
void STDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012a, ra, rt, rb, true); }
|
||||
void STFD(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd8000000, rt, ra, d); }
|
||||
void STFDU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xdc000000, rt, ra, d); }
|
||||
void STFDUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, false); }
|
||||
void STFDUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ee, ra, rt, rb, true); }
|
||||
void STFDX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, false); }
|
||||
void STFDX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005ae, ra, rt, rb, true); }
|
||||
void STFIWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, false); }
|
||||
void STFIWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0007ae, ra, rt, rb, true); }
|
||||
void STFS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd0000000, rt, ra, d); }
|
||||
void STFSU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xd4000000, rt, ra, d); }
|
||||
void STFSUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, false); }
|
||||
void STFSUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00056e, ra, rt, rb, true); }
|
||||
void STFSX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, false); }
|
||||
void STFSX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052e, ra, rt, rb, true); }
|
||||
void STH(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb0000000, rt, ra, d); }
|
||||
void STHBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, false); }
|
||||
void STHBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00072c, ra, rt, rb, true); }
|
||||
void STHU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xb4000000, rt, ra, d); }
|
||||
void STHUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, false); }
|
||||
void STHUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00036e, ra, rt, rb, true); }
|
||||
void STHX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, false); }
|
||||
void STHX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00032e, ra, rt, rb, true); }
|
||||
void STMW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0xbc000000, rt, ra, d); }
|
||||
void STSWI(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, false); }
|
||||
void STSWI_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0005aa, ra, rt, rb, true); }
|
||||
void STSWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, false); }
|
||||
void STSWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052a, ra, rt, rb, true); }
|
||||
void STW(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x90000000, rt, ra, d); }
|
||||
void STWBRX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, false); }
|
||||
void STWBRX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00052c, ra, rt, rb, true); }
|
||||
void STWCX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, false); }
|
||||
void STWCX__(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012c, ra, rt, rb, true); }
|
||||
void STWU(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x94000000, rt, ra, d); }
|
||||
void STWUX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, false); }
|
||||
void STWUX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00016e, ra, rt, rb, true); }
|
||||
void STWX(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, false); }
|
||||
void STWX_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00012e, ra, rt, rb, true); }
|
||||
void SUBF(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, false); }
|
||||
void SUBFO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, false); }
|
||||
void SUBF_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, false, true); }
|
||||
void SUBFO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000050, rt, ra, rb, true, true); }
|
||||
void SUBFC(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, false); }
|
||||
void SUBFCO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, false); }
|
||||
void SUBFC_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, false, true); }
|
||||
void SUBFCO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000010, rt, ra, rb, true, true); }
|
||||
void SUBFE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, false); }
|
||||
void SUBFEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, false); }
|
||||
void SUBFE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, false, true); }
|
||||
void SUBFEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000110, rt, ra, rb, true, true); }
|
||||
void SUBFIC(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x20000000, rt, ra, d); }
|
||||
void SUBFME(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, false); }
|
||||
void SUBFMEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, false); }
|
||||
void SUBFME_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, false, true); }
|
||||
void SUBFMEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c0001d0, rt, ra, rb, true, true); }
|
||||
void SUBFZE(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, false); }
|
||||
void SUBFZEO(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, false); }
|
||||
void SUBFZE_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, false, true); }
|
||||
void SUBFZEO_(GPR const rt, GPR const ra, GPR const rb) { emit_XO(0x7c000190, rt, ra, rb, true, true); }
|
||||
void SYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, false); }
|
||||
void SYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c0004ac, ra, rt, rb, true); }
|
||||
void TD(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, false); }
|
||||
void TD_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000088, ra, rt, rb, true); }
|
||||
void TDI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x08000000, rt, ra, d); }
|
||||
void TLBIE(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, false); }
|
||||
void TLBIE_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000264, ra, rt, rb, true); }
|
||||
void TLBSYNC(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, false); }
|
||||
void TLBSYNC_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c00046c, ra, rt, rb, true); }
|
||||
void TW(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, false); }
|
||||
void TW_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000008, ra, rt, rb, true); }
|
||||
void TWI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x0c000000, rt, ra, d); }
|
||||
void XOR(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, false); }
|
||||
void XOR_(GPR const rt, GPR const ra, GPR const rb) { emit_X(0x7c000278, ra, rt, rb, true); }
|
||||
void XORI(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x68000000, ra, rt, d); }
|
||||
void XORIS(GPR const rt, GPR const ra, uint32_t d) { emit_D(0x6c000000, ra, rt, d); }
|
||||
Vendored
-340
@@ -1,340 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <sys/mman.h>
|
||||
#include "powah_emit.hpp"
|
||||
|
||||
#define EB32(X) __bswap32(X)
|
||||
|
||||
TEST_CASE("ppc64: addi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.ADDI(powah::R2, powah::R2, 0);
|
||||
ctx.ADDIS(powah::R2, powah::R12, 0);
|
||||
REQUIRE(data[0] == EB32(0x00004238));
|
||||
REQUIRE(data[1] == EB32(0x00004c3c));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: std/mr", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.STD(powah::R26, powah::R1, 144);
|
||||
ctx.MR(powah::R26, powah::R4);
|
||||
ctx.STD(powah::R30, powah::R1, 176);
|
||||
ctx.MR(powah::R30, powah::R3);
|
||||
ctx.RLDICR(powah::R4, powah::R5, 25, 6);
|
||||
ctx.STD(powah::R28, powah::R1, 160);
|
||||
REQUIRE(data[0] == EB32(0x900041fb));
|
||||
REQUIRE(data[1] == EB32(0x78239a7c));
|
||||
REQUIRE(data[2] == EB32(0xb000c1fb));
|
||||
REQUIRE(data[3] == EB32(0x781b7e7c));
|
||||
REQUIRE(data[4] == EB32(0x84c9a478));
|
||||
REQUIRE(data[5] == EB32(0xa00081fb));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: sldi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
|
||||
ctx.SLDI(powah::R3, powah::R5, 37);
|
||||
REQUIRE(data[0] == EB32(0x862ea378));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rldicr", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.RLDICR(powah::R1, powah::R1, 0, 0);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 1, 0);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 0, 1);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 1, 1);
|
||||
ctx.RLDICR(powah::R1, powah::R1, 31, 31);
|
||||
REQUIRE(data[0] == EB32(0x04002178));
|
||||
REQUIRE(data[1] == EB32(0x04082178));
|
||||
REQUIRE(data[2] == EB32(0x44002178));
|
||||
REQUIRE(data[3] == EB32(0x44082178));
|
||||
REQUIRE(data[4] == EB32(0xc4ff2178));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: mr/or/std", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.MR(powah::R27, powah::R7);
|
||||
ctx.MR(powah::R29, powah::R6);
|
||||
ctx.OR(powah::R3, powah::R3, powah::R4);
|
||||
ctx.MR(powah::R4, powah::R28);
|
||||
ctx.CRMOVE(powah::CPR{8}, powah::CPR{1});
|
||||
ctx.MR(powah::R25, powah::R5);
|
||||
ctx.OR(powah::R3, powah::R3, powah::R26);
|
||||
ctx.STD(powah::R3, powah::R1, 56);
|
||||
ctx.MR(powah::R3, powah::R30);
|
||||
REQUIRE(data[0] == EB32(0x783bfb7c));
|
||||
REQUIRE(data[1] == EB32(0x7833dd7c));
|
||||
REQUIRE(data[2] == EB32(0x7823637c));
|
||||
REQUIRE(data[3] == EB32(0x78e3847f));
|
||||
REQUIRE(data[4] == EB32(0x820b014d));
|
||||
REQUIRE(data[5] == EB32(0x782bb97c));
|
||||
REQUIRE(data[6] == EB32(0x78d3637c));
|
||||
REQUIRE(data[7] == EB32(0x380061f8));
|
||||
REQUIRE(data[8] == EB32(0x78f3c37f));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: ld/crand+addi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.LD(powah::R4, powah::R1, 72);
|
||||
ctx.LWZ(powah::R5, powah::R1, 80);
|
||||
ctx.CRAND(powah::CPR{20}, powah::CPR{10}, powah::CPR{9});
|
||||
ctx.ADDI(powah::R6, powah::R4, 4);
|
||||
ctx.ANDIS_(powah::R4, powah::R5, 1992);
|
||||
ctx.LD(powah::R5, powah::R30, 1984);
|
||||
ctx.LBZ(powah::R3, powah::R1, 84);
|
||||
ctx.CLRLDI(powah::R26, powah::R6, 8);
|
||||
ctx.STW(powah::R4, powah::R1, 80);
|
||||
ctx.ADDI(powah::R5, powah::R5, 1);
|
||||
ctx.STD(powah::R26, powah::R1, 72);
|
||||
ctx.STD(powah::R5, powah::R30, 1984);
|
||||
REQUIRE(data[0] == EB32(0x480081e8));
|
||||
REQUIRE(data[1] == EB32(0x5000a180));
|
||||
REQUIRE(data[2] == EB32(0x024a8a4e));
|
||||
REQUIRE(data[3] == EB32(0x0400c438));
|
||||
REQUIRE(data[4] == EB32(0xc807a474));
|
||||
REQUIRE(data[5] == EB32(0xc007bee8));
|
||||
REQUIRE(data[6] == EB32(0x54006188));
|
||||
REQUIRE(data[7] == EB32(0x0002da78));
|
||||
REQUIRE(data[8] == EB32(0x50008190));
|
||||
REQUIRE(data[9] == EB32(0x0100a538));
|
||||
REQUIRE(data[10] == EB32(0x480041fb));
|
||||
REQUIRE(data[11] == EB32(0xc007bef8));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rotldi", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 16);
|
||||
ctx.ROTLDI(powah::R4, powah::R3, 8);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 8, 48);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 24);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 16, 40);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 32);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 24, 32);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 48);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 40, 16);
|
||||
ctx.ROTLDI(powah::R5, powah::R3, 56);
|
||||
ctx.RLDIMI(powah::R4, powah::R5, 48, 8);
|
||||
ctx.RLDIMI(powah::R4, powah::R3, 56, 0);
|
||||
ctx.MR(powah::R3, powah::R4);
|
||||
REQUIRE(data[0] == EB32(0x00806578));
|
||||
REQUIRE(data[1] == EB32(0x00406478));
|
||||
REQUIRE(data[2] == EB32(0x2c44a478));
|
||||
REQUIRE(data[3] == EB32(0x00c06578));
|
||||
REQUIRE(data[4] == EB32(0x2c82a478));
|
||||
REQUIRE(data[5] == EB32(0x02006578));
|
||||
REQUIRE(data[6] == EB32(0x2cc0a478));
|
||||
REQUIRE(data[7] == EB32(0x02806578));
|
||||
REQUIRE(data[8] == EB32(0x0e44a478));
|
||||
REQUIRE(data[9] == EB32(0x02c06578));
|
||||
REQUIRE(data[10] == EB32(0x0e82a478));
|
||||
REQUIRE(data[11] == EB32(0x0ec06478));
|
||||
REQUIRE(data[12] == EB32(0x7823837c));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: branch-backwards", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
powah::Label l_3 = ctx.DefineLabel();
|
||||
ctx.LABEL(l_3);
|
||||
ctx.ADD(powah::R1, powah::R2, powah::R3);
|
||||
ctx.B(l_3);
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x141a227c));
|
||||
REQUIRE(data[1] == EB32(0xfcffff4b));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: rlwimi madness", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.ROTLWI(powah::R10, powah::R3, 24);
|
||||
ctx.SRDI(powah::R9, powah::R3, 32);
|
||||
ctx.RLWIMI(powah::R10, powah::R3, 8, 8, 15);
|
||||
ctx.RLWIMI(powah::R10, powah::R3, 8, 24, 31);
|
||||
ctx.ROTLWI(powah::R3, powah::R9, 24);
|
||||
ctx.RLWIMI(powah::R3, powah::R9, 8, 8, 15);
|
||||
ctx.RLWIMI(powah::R3, powah::R9, 8, 24, 31);
|
||||
ctx.RLDIMI(powah::R3, powah::R10, 32, 0);
|
||||
REQUIRE(data[0] == EB32(0x3ec06a54));
|
||||
REQUIRE(data[1] == EB32(0x22006978));
|
||||
REQUIRE(data[2] == EB32(0x1e426a50));
|
||||
REQUIRE(data[3] == EB32(0x3e466a50));
|
||||
REQUIRE(data[4] == EB32(0x3ec02355));
|
||||
REQUIRE(data[5] == EB32(0x1e422351));
|
||||
REQUIRE(data[6] == EB32(0x3e462351));
|
||||
REQUIRE(data[7] == EB32(0x0e004379));
|
||||
}
|
||||
|
||||
/*
|
||||
0: 78 1b 68 7c mr 8, 3
|
||||
4: 38 28 83 7c and 3, 4, 5
|
||||
8: 74 00 6a 7c cntlzd 10, 3
|
||||
c: 76 06 69 7c sradi 9, 3, 32
|
||||
10: 82 d1 4a 79 rldicl 10, 10, 58, 6
|
||||
14: 00 00 29 55 rlwinm 9, 9, 0, 0, 0
|
||||
18: 64 f0 4a 79 sldi 10, 10, 30
|
||||
1c: 78 53 29 7d or 9, 9, 10
|
||||
20: 00 00 28 f9 std 9, 0(8)
|
||||
24: 20 00 80 4e blr
|
||||
*/
|
||||
TEST_CASE("ppc64: functor-2", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
ctx.MR(powah::R8, powah::R3);
|
||||
ctx.AND(powah::R3, powah::R4, powah::R5);
|
||||
ctx.CNTLZD(powah::R10, powah::R3);
|
||||
ctx.SRADI(powah::R9, powah::R3, 32);
|
||||
ctx.RLDICL(powah::R10, powah::R10, 58, 6);
|
||||
ctx.RLWINM(powah::R9, powah::R9, 0, 0, 0);
|
||||
ctx.SLDI(powah::R10, powah::R10, 30);
|
||||
ctx.OR(powah::R9, powah::R9, powah::R10);
|
||||
ctx.STD(powah::R9, powah::R8, 0);
|
||||
ctx.BLR();
|
||||
REQUIRE(data[0] == EB32(0x781b687c));
|
||||
REQUIRE(data[1] == EB32(0x3828837c));
|
||||
REQUIRE(data[2] == EB32(0x74006a7c));
|
||||
REQUIRE(data[3] == EB32(0x7606697c));
|
||||
REQUIRE(data[4] == EB32(0x82d14a79));
|
||||
REQUIRE(data[5] == EB32(0x00002955));
|
||||
REQUIRE(data[6] == EB32(0x64f04a79));
|
||||
REQUIRE(data[7] == EB32(0x7853297d));
|
||||
REQUIRE(data[8] == EB32(0x000028f9));
|
||||
REQUIRE(data[9] == EB32(0x2000804e));
|
||||
}
|
||||
|
||||
TEST_CASE("ppc64: functor-1", "[ppc64]") {
|
||||
std::vector<uint32_t> data(64);
|
||||
powah::Context ctx(data.data(), data.size());
|
||||
powah::Label l_4 = ctx.DefineLabel();
|
||||
powah::Label l_7 = ctx.DefineLabel();
|
||||
ctx.CMPLD(powah::CR0, powah::R3, powah::R4);
|
||||
ctx.MR(powah::R9, powah::R3);
|
||||
ctx.BGT(powah::CR0, l_7);
|
||||
ctx.CMPLDI(powah::CR0, powah::R3, 3781);
|
||||
ctx.LI(powah::R3, 1);
|
||||
ctx.BGTLR(powah::CR0);
|
||||
ctx.CMPLD(powah::CR0, powah::R9, powah::R5);
|
||||
ctx.BLE(powah::CR0, l_4);
|
||||
ctx.SUBF(powah::R5, powah::R9, powah::R5);
|
||||
ctx.XOR(powah::R3, powah::R5, powah::R4);
|
||||
ctx.BLR();
|
||||
ctx.LABEL(l_4);
|
||||
ctx.ADD(powah::R5, powah::R4, powah::R5);
|
||||
ctx.ADD(powah::R3, powah::R5, powah::R9);
|
||||
ctx.BLR();
|
||||
ctx.LABEL(l_7);
|
||||
ctx.ADD(powah::R3, powah::R4, powah::R5);
|
||||
ctx.BLR();
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x4020237c));
|
||||
REQUIRE(data[1] == EB32(0x781b697c));
|
||||
REQUIRE(data[2] == EB32(0x30008141));
|
||||
REQUIRE(data[3] == EB32(0xc50e2328));
|
||||
REQUIRE(data[4] == EB32(0x01006038));
|
||||
REQUIRE(data[5] == EB32(0x2000814d));
|
||||
REQUIRE(data[6] == EB32(0x4028297c));
|
||||
REQUIRE(data[7] == EB32(0x10008140));
|
||||
REQUIRE(data[8] == EB32(0x5028a97c));
|
||||
REQUIRE(data[9] == EB32(0x7822a37c));
|
||||
REQUIRE(data[10] == EB32(0x2000804e));
|
||||
REQUIRE(data[11] == EB32(0x142aa47c));
|
||||
REQUIRE(data[12] == EB32(0x144a657c));
|
||||
REQUIRE(data[13] == EB32(0x2000804e));
|
||||
REQUIRE(data[14] == EB32(0x142a647c));
|
||||
REQUIRE(data[15] == EB32(0x2000804e));
|
||||
}
|
||||
|
||||
#if defined(ARCHITECTURE_powerpc64)
|
||||
/*
|
||||
0: d619637c mullw 3, 3, 3
|
||||
4: 20006378 clrldi 3, 3, 32
|
||||
8: 2000804e blr
|
||||
*/
|
||||
TEST_CASE("ppc64: live-exec square", "[ppc64]") {
|
||||
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
REQUIRE(data != nullptr);
|
||||
|
||||
powah::Context ctx((void*)data, 4096);
|
||||
ctx.MULLW(powah::R3, powah::R3, powah::R3);
|
||||
ctx.CLRLDI(powah::R3, powah::R3, 32);
|
||||
ctx.BLR();
|
||||
REQUIRE(data[0] == EB32(0xd619637c));
|
||||
REQUIRE(data[1] == EB32(0x20006378));
|
||||
REQUIRE(data[2] == EB32(0x2000804e));
|
||||
|
||||
auto* fn = (int (*)(int))data;
|
||||
REQUIRE(fn(4) == 4 * 4);
|
||||
REQUIRE(fn(8) == 8 * 8);
|
||||
|
||||
munmap((void*)data, 4096);
|
||||
}
|
||||
|
||||
/*
|
||||
int f(int a, int b, int c) {
|
||||
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
|
||||
return a * a + b ^ c & b;
|
||||
}
|
||||
*/
|
||||
TEST_CASE("ppc64: live-exec xoralu", "[ppc64]") {
|
||||
uint32_t* data = (uint32_t*)mmap(nullptr, 4096, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
REQUIRE(data != nullptr);
|
||||
|
||||
powah::Context ctx((void*)data, 4096);
|
||||
powah::Label l_2 = ctx.DefineLabel();
|
||||
powah::Label l_3 = ctx.DefineLabel();
|
||||
ctx.CMPW(powah::R3, powah::R4);
|
||||
ctx.BGT(powah::CR0, l_2);
|
||||
ctx.ADDI(powah::R6, powah::R5, 402);
|
||||
ctx.B(l_3);
|
||||
ctx.LABEL(l_2);
|
||||
ctx.ADDI(powah::R6, powah::R4, 1);
|
||||
ctx.ADDIS(powah::R6, powah::R6, -1);
|
||||
ctx.LABEL(l_3);
|
||||
ctx.ADDI(powah::R4, powah::R4, 2);
|
||||
ctx.ADD(powah::R3, powah::R6, powah::R3);
|
||||
ctx.MULLW(powah::R6, powah::R4, powah::R5);
|
||||
ctx.MULLW(powah::R3, powah::R3, powah::R3);
|
||||
ctx.AND(powah::R5, powah::R5, powah::R6);
|
||||
ctx.ADD(powah::R3, powah::R3, powah::R4);
|
||||
ctx.AND(powah::R4, powah::R5, powah::R4);
|
||||
ctx.XOR(powah::R3, powah::R3, powah::R4);
|
||||
ctx.EXTSW(powah::R3, powah::R3);
|
||||
ctx.BLR();
|
||||
ctx.ApplyRelocs();
|
||||
REQUIRE(data[0] == EB32(0x0020037c));
|
||||
REQUIRE(data[1] == EB32(0x0c008141));
|
||||
REQUIRE(data[2] == EB32(0x9201c538));
|
||||
REQUIRE(data[3] == EB32(0x0c000048));
|
||||
REQUIRE(data[4] == EB32(0x0100c438));
|
||||
REQUIRE(data[5] == EB32(0xffffc63c));
|
||||
REQUIRE(data[6] == EB32(0x02008438));
|
||||
REQUIRE(data[7] == EB32(0x141a667c));
|
||||
REQUIRE(data[8] == EB32(0xd629c47c));
|
||||
REQUIRE(data[9] == EB32(0xd619637c));
|
||||
REQUIRE(data[10] == EB32(0x3830a57c));
|
||||
REQUIRE(data[11] == EB32(0x1422637c));
|
||||
REQUIRE(data[12] == EB32(0x3820a47c));
|
||||
REQUIRE(data[13] == EB32(0x7822637c));
|
||||
REQUIRE(data[14] == EB32(0xb407637c));
|
||||
REQUIRE(data[15] == EB32(0x2000804e));
|
||||
|
||||
auto const orig = [](int a, int b, int c) {
|
||||
a += (a > b) ? b - 0xffff : c + 402, b += 2, c &= b*c;
|
||||
return a * a + b ^ c & b;
|
||||
};
|
||||
auto* fn = (int (*)(int, int, int))data;
|
||||
REQUIRE(fn(0, 1, 2) == orig(0, 1, 2));
|
||||
REQUIRE(fn(6456, 4564, 4564561) == orig(6456, 4564, 4564561));
|
||||
|
||||
munmap((void*)data, 4096);
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
include_directories(.)
|
||||
|
||||
# Dynarmic
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64 OR ARCHITECTURE_powerpc64) AND NOT YUZU_STATIC_ROOM)
|
||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM)
|
||||
add_subdirectory(dynarmic)
|
||||
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
||||
endif()
|
||||
|
||||
+1
@@ -586,6 +586,7 @@ abstract class SettingsItem(
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
titleId = R.string.fsr_sharpness,
|
||||
descriptionId = R.string.fsr_sharpness_description,
|
||||
max = 200,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
container,
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
minValue = 0,
|
||||
maxValue = 100,
|
||||
maxValue = 200,
|
||||
units = "%"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1251,7 +1251,7 @@ if (HAS_NCE)
|
||||
target_link_libraries(core PRIVATE merry::oaknut)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_powerpc64 OR ARCHITECTURE_powerpc64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_64.cpp
|
||||
|
||||
@@ -88,25 +88,10 @@ Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
|
||||
}
|
||||
|
||||
Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
|
||||
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
|
||||
|
||||
// Ensure the display name is null-terminated
|
||||
display_name[display_name.size() - 1] = '\0';
|
||||
|
||||
// According to switchbrew, only "Default", "External", "Edid", "Internal" and "Null" are valid
|
||||
const std::array<std::string_view, 5> valid_names = {
|
||||
"Default", "External", "Edid", "Internal", "Null"
|
||||
};
|
||||
|
||||
bool valid_name = false;
|
||||
for (const auto& name : valid_names) {
|
||||
if (name == display_name.data()) {
|
||||
valid_name = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_UNLESS(valid_name, ResultOperationFailed);
|
||||
LOG_DEBUG(Service_VI, "called with display_name={}", display_name.data());
|
||||
|
||||
R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ add_library(dynarmic STATIC
|
||||
interface/halt_reason.h
|
||||
interface/exclusive_monitor.h
|
||||
interface/optimization_flags.h
|
||||
interface/halt_reason.h
|
||||
ir/acc_type.h
|
||||
ir/basic_block.cpp
|
||||
ir/basic_block.h
|
||||
@@ -140,8 +139,10 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
# Newer versions of xbyak (>= 7.25.0) have stricter checks that currently
|
||||
# fail in dynarmic
|
||||
target_compile_definitions(dynarmic PRIVATE XBYAK_STRICT_CHECK_MEM_REG_SIZE=0)
|
||||
|
||||
target_compile_definitions(dynarmic PRIVATE XBYAK_OLD_DISP_CHECK=1)
|
||||
target_link_libraries(dynarmic PRIVATE xbyak::xbyak)
|
||||
|
||||
target_architecture_specific_sources(dynarmic "x86_64"
|
||||
backend/x64/abi.cpp
|
||||
backend/x64/abi.h
|
||||
@@ -293,35 +294,6 @@ if ("riscv64" IN_LIST ARCHITECTURE)
|
||||
)
|
||||
message(WARNING "TODO: Incomplete frontend for this host architecture")
|
||||
endif()
|
||||
if ("powerpc64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(dynarmic PRIVATE powah)
|
||||
target_sources(dynarmic PRIVATE
|
||||
backend/ppc64/abi.h
|
||||
backend/ppc64/emit_context.h
|
||||
backend/ppc64/emit_ppc64_a32.cpp
|
||||
backend/ppc64/emit_ppc64_a64.cpp
|
||||
backend/ppc64/emit_ppc64_misc.cpp
|
||||
backend/ppc64/emit_ppc64_data_processing.cpp
|
||||
backend/ppc64/emit_ppc64_floating_point.cpp
|
||||
backend/ppc64/emit_ppc64_vector.cpp
|
||||
backend/ppc64/emit_ppc64.cpp
|
||||
backend/ppc64/emit_ppc64.h
|
||||
backend/ppc64/exclusive_monitor.cpp
|
||||
backend/ppc64/reg_alloc.cpp
|
||||
backend/ppc64/reg_alloc.h
|
||||
backend/ppc64/stack_layout.h
|
||||
backend/ppc64/hostloc.h
|
||||
common/spin_lock_ppc64.cpp
|
||||
common/spin_lock_ppc64.h
|
||||
# A32
|
||||
backend/ppc64/a32_core.h
|
||||
backend/ppc64/a32_interface.cpp
|
||||
# A64
|
||||
backend/ppc64/a64_core.h
|
||||
backend/ppc64/a64_interface.cpp
|
||||
backend/ppc64/code_block.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if ("loongarch64" IN_LIST ARCHITECTURE)
|
||||
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
class BlockOfCode;
|
||||
}
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
namespace oaknut {
|
||||
class CodeBlock;
|
||||
@@ -30,10 +30,6 @@ class CodeBlock;
|
||||
namespace Dynarmic::Backend::LoongArch64 {
|
||||
class CodeBlock;
|
||||
} // namespace Dynarmic::Backend::LoongArch64
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
class CodeBlock;
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -57,9 +53,6 @@ struct FakeCall {
|
||||
struct FakeCall {
|
||||
u64 call_pc;
|
||||
};
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
struct FakeCall {
|
||||
};
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -77,8 +70,6 @@ public:
|
||||
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size);
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void Register(PPC64::CodeBlock& mem, std::size_t mem_size);
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
@@ -17,21 +17,24 @@ ExceptionHandler::ExceptionHandler() = default;
|
||||
ExceptionHandler::~ExceptionHandler() = default;
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
void ExceptionHandler::Register(X64::BlockOfCode&)
|
||||
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t)
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void ExceptionHandler::Register(PPC64::CodeBlock&, std::size_t)
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
bool ExceptionHandler::SupportsFastmem() const noexcept {
|
||||
return false;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -35,8 +34,6 @@
|
||||
# include "dynarmic/backend/riscv64/code_block.h"
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
# include "dynarmic/backend/loongarch64/code_block.h"
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
# include "dynarmic/backend/ppc64/code_block.h"
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
@@ -171,7 +168,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||
@@ -233,10 +230,6 @@ void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
|
||||
void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) {
|
||||
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
|
||||
}
|
||||
#elif defined(ARCHITECTURE_powerpc64)
|
||||
void ExceptionHandler::Register(PPC64::CodeBlock& mem, std::size_t size) {
|
||||
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/ppc64/code_block.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct A32JitState {
|
||||
std::array<u32, 16> regs{};
|
||||
alignas(16) std::array<u32, 64> ext_regs{};
|
||||
u32 upper_location_descriptor;
|
||||
u32 exclusive_state = 0;
|
||||
u32 cpsr_nzcv = 0;
|
||||
u32 fpscr = 0;
|
||||
u32 check_bit = 0;
|
||||
|
||||
IR::LocationDescriptor GetLocationDescriptor() const {
|
||||
return IR::LocationDescriptor{regs[15] | (u64(upper_location_descriptor) << 32)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,250 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||
#include "dynarmic/frontend/A32/FPSCR.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/common/atomic.h"
|
||||
#include "dynarmic/ir/opt_passes.h"
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
using namespace Dynarmic::Backend::PPC64;
|
||||
using CodePtr = std::uint32_t*;
|
||||
|
||||
struct A32AddressSpace final {
|
||||
explicit A32AddressSpace(const A32::UserConfig& conf)
|
||||
: conf(conf)
|
||||
, cb(conf.code_cache_size)
|
||||
, as(cb.ptr<u8*>(), conf.code_cache_size) {
|
||||
|
||||
}
|
||||
|
||||
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
|
||||
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
|
||||
return it->second;
|
||||
|
||||
ir_block.Reset(A32::LocationDescriptor{desc});
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{desc}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
|
||||
|
||||
block_infos.insert_or_assign(desc.Value(), block_info);
|
||||
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
|
||||
return block_info.entry_point;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
block_entries.clear();
|
||||
block_infos.clear();
|
||||
}
|
||||
|
||||
EmittedBlockInfo Emit(IR::Block block) {
|
||||
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
|
||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||
.always_little_endian = conf.always_little_endian,
|
||||
.a64_variant = false
|
||||
});
|
||||
Link(block_info);
|
||||
return block_info;
|
||||
}
|
||||
|
||||
void Link(EmittedBlockInfo& block_info) {
|
||||
//UNREACHABLE();
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
|
||||
const A32::UserConfig conf;
|
||||
CodeBlock cb;
|
||||
powah::Context as;
|
||||
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
|
||||
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
|
||||
};
|
||||
|
||||
struct A32Core final {
|
||||
static HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) {
|
||||
auto const loc = thread_ctx.GetLocationDescriptor();
|
||||
auto const entry = process.GetOrEmit(loc);
|
||||
using CodeFn = HaltReason (*)(A32AddressSpace*, A32JitState*, volatile u32*, void*);
|
||||
return (CodeFn(entry))(&process, &thread_ctx, halt_reason, reinterpret_cast<void*>(&A32Core::Run));
|
||||
}
|
||||
};
|
||||
|
||||
struct Jit::Impl final {
|
||||
Impl(Jit* jit_interface, A32::UserConfig conf)
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, jit_interface(jit_interface) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = false;
|
||||
HaltReason hr = A32Core::Run(current_address_space, jit_state, &halt_reason);
|
||||
is_executing = true;
|
||||
RequestCacheInvalidation();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
// HaltReason hr = A32Core::Step(current_address_space, jit_state, &halt_reason);
|
||||
// RequestCacheInvalidation();
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u32 start_address, size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
std::array<u32, 16>& Regs() {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
const std::array<u32, 16>& Regs() const {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
std::array<u32, 64>& ExtRegs() {
|
||||
return jit_state.ext_regs;
|
||||
}
|
||||
|
||||
const std::array<u32, 64>& ExtRegs() const {
|
||||
return jit_state.ext_regs;
|
||||
}
|
||||
|
||||
u32 Cpsr() const {
|
||||
return jit_state.cpsr_nzcv;
|
||||
}
|
||||
|
||||
void SetCpsr(u32 value) {
|
||||
jit_state.cpsr_nzcv = value;
|
||||
}
|
||||
|
||||
u32 Fpscr() const {
|
||||
return jit_state.fpscr;
|
||||
}
|
||||
|
||||
void SetFpscr(u32 value) {
|
||||
jit_state.fpscr = value;
|
||||
}
|
||||
|
||||
void ClearExclusiveState() {
|
||||
jit_state.exclusive_state = false;
|
||||
}
|
||||
|
||||
private:
|
||||
void RequestCacheInvalidation() {
|
||||
// UNREACHABLE();
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
}
|
||||
|
||||
A32::UserConfig conf;
|
||||
A32JitState jit_state{};
|
||||
A32AddressSpace current_address_space;
|
||||
Jit* jit_interface;
|
||||
volatile u32 halt_reason = 0;
|
||||
bool is_executing = false;
|
||||
|
||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(this, conf)) {}
|
||||
Jit::~Jit() = default;
|
||||
|
||||
HaltReason Jit::Run() {
|
||||
return impl->Run();
|
||||
}
|
||||
|
||||
HaltReason Jit::Step() {
|
||||
return impl->Step();
|
||||
}
|
||||
|
||||
void Jit::ClearCache() {
|
||||
impl->ClearCache();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
|
||||
impl->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void Jit::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Jit::HaltExecution(HaltReason hr) {
|
||||
impl->HaltExecution(hr);
|
||||
}
|
||||
|
||||
void Jit::ClearHalt(HaltReason hr) {
|
||||
impl->ClearHalt(hr);
|
||||
}
|
||||
|
||||
std::array<u32, 16>& Jit::Regs() {
|
||||
return impl->Regs();
|
||||
}
|
||||
|
||||
const std::array<u32, 16>& Jit::Regs() const {
|
||||
return impl->Regs();
|
||||
}
|
||||
|
||||
std::array<u32, 64>& Jit::ExtRegs() {
|
||||
return impl->ExtRegs();
|
||||
}
|
||||
|
||||
const std::array<u32, 64>& Jit::ExtRegs() const {
|
||||
return impl->ExtRegs();
|
||||
}
|
||||
|
||||
u32 Jit::Cpsr() const {
|
||||
return impl->Cpsr();
|
||||
}
|
||||
|
||||
void Jit::SetCpsr(u32 value) {
|
||||
impl->SetCpsr(value);
|
||||
}
|
||||
|
||||
u32 Jit::Fpscr() const {
|
||||
return impl->Fpscr();
|
||||
}
|
||||
|
||||
void Jit::SetFpscr(u32 value) {
|
||||
impl->SetFpscr(value);
|
||||
}
|
||||
|
||||
void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A32
|
||||
@@ -1,40 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include "dynarmic/backend/ppc64/code_block.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
#include "dynarmic/interface/A64/a64.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct A64JitState {
|
||||
using ProgramCounterType = u32;
|
||||
std::array<u64, 31> regs{};
|
||||
u64 pc = 0;
|
||||
alignas(16) std::array<u64, 64> vec{};
|
||||
u64 sp = 0;
|
||||
u32 upper_location_descriptor = 0;
|
||||
u32 exclusive_state = 0;
|
||||
u32 pstate = 0;
|
||||
u32 fpcr = 0;
|
||||
u32 fpsr = 0;
|
||||
volatile u32 halt_reason = 0;
|
||||
u32 check_bit = 0;
|
||||
|
||||
IR::LocationDescriptor GetLocationDescriptor() const {
|
||||
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
||||
return IR::LocationDescriptor{pc_u64 | fpcr_u64};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,366 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cstddef>
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/common/atomic.h"
|
||||
#include "dynarmic/ir/opt_passes.h"
|
||||
#include "dynarmic/interface/A64/a64.h"
|
||||
|
||||
namespace Dynarmic::A64 {
|
||||
|
||||
using namespace Dynarmic::Backend::PPC64;
|
||||
using CodePtr = std::uint32_t*;
|
||||
|
||||
struct A64AddressSpace final {
|
||||
explicit A64AddressSpace(const A64::UserConfig& conf)
|
||||
: conf(conf)
|
||||
, cb(conf.code_cache_size)
|
||||
, as(cb.ptr<u8*>(), conf.code_cache_size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CodePtr GetOrEmit(IR::LocationDescriptor desc) {
|
||||
if (auto const it = block_entries.find(desc.Value()); it != block_entries.end())
|
||||
return it->second;
|
||||
auto const get_code = [this](u64 vaddr) {
|
||||
return conf.callbacks->MemoryReadCode(vaddr);
|
||||
};
|
||||
ir_block.Reset(A64::LocationDescriptor{desc});
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{desc}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
Optimization::Optimize(ir_block, conf, {});
|
||||
fmt::print("IR:\n{}\n", IR::DumpBlock(ir_block));
|
||||
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
|
||||
block_infos.insert_or_assign(desc.Value(), block_info);
|
||||
block_entries.insert_or_assign(desc.Value(), block_info.entry_point);
|
||||
return block_info.entry_point;
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
block_entries.clear();
|
||||
block_infos.clear();
|
||||
}
|
||||
|
||||
EmittedBlockInfo Emit(IR::Block block) {
|
||||
EmittedBlockInfo block_info = EmitPPC64(as, std::move(block), {
|
||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||
.always_little_endian = true,
|
||||
.a64_variant = true
|
||||
});
|
||||
Link(block_info);
|
||||
return block_info;
|
||||
}
|
||||
|
||||
void Link(EmittedBlockInfo& block_info) {
|
||||
// TODO(lizzie): Block linking
|
||||
// UNREACHABLE();
|
||||
}
|
||||
|
||||
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
|
||||
const A64::UserConfig conf;
|
||||
CodeBlock cb;
|
||||
powah::Context as;
|
||||
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
|
||||
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
|
||||
};
|
||||
|
||||
struct A64Core final {
|
||||
using CodeFn = HaltReason (*)(A64AddressSpace*, A64JitState*, volatile u32*, void *fn);
|
||||
static HaltReason Run(A64AddressSpace& process, A64JitState& thread_ctx, volatile u32* halt_reason) {
|
||||
HaltReason hr{};
|
||||
do {
|
||||
const auto loc = thread_ctx.GetLocationDescriptor();
|
||||
const auto entry = process.GetOrEmit(loc);
|
||||
hr = (CodeFn(entry))(&process, &thread_ctx, halt_reason, (void*)&A64Core::Run);
|
||||
} while (hr == HaltReason(0));
|
||||
return hr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Jit::Impl final {
|
||||
Impl(Jit* jit_interface, A64::UserConfig conf)
|
||||
: conf(conf)
|
||||
, current_address_space(conf)
|
||||
, jit_interface(jit_interface) {}
|
||||
|
||||
HaltReason Run() {
|
||||
ASSERT(!is_executing);
|
||||
is_executing = true;
|
||||
HaltReason hr = A64Core::Run(current_address_space, jit_state, &halt_reason);
|
||||
is_executing = false;
|
||||
RequestCacheInvalidation();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HaltReason Step() {
|
||||
// HaltReason hr = A64Core::Step(current_address_space, jit_state, &halt_reason);
|
||||
// RequestCacheInvalidation();
|
||||
return HaltReason{};
|
||||
}
|
||||
|
||||
void ClearCache() {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
invalidate_entire_cache = true;
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u64 start_address, size_t length) {
|
||||
std::unique_lock lock{invalidation_mutex};
|
||||
const auto end_address = u64(start_address + length - 1);
|
||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, end_address));
|
||||
HaltExecution(HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ASSERT(!is_executing);
|
||||
jit_state = {};
|
||||
}
|
||||
|
||||
void HaltExecution(HaltReason hr) {
|
||||
Atomic::Or(&jit_state.halt_reason, u32(hr));
|
||||
}
|
||||
|
||||
void ClearHalt(HaltReason hr) {
|
||||
Atomic::And(&jit_state.halt_reason, ~u32(hr));
|
||||
}
|
||||
|
||||
u64 GetSP() const {
|
||||
return jit_state.sp;
|
||||
}
|
||||
|
||||
void SetSP(u64 value) {
|
||||
jit_state.sp = value;
|
||||
}
|
||||
|
||||
u64 GetPC() const {
|
||||
return jit_state.pc;
|
||||
}
|
||||
|
||||
void SetPC(u64 value) {
|
||||
jit_state.pc = value;
|
||||
}
|
||||
|
||||
u64 GetRegister(size_t index) const {
|
||||
return index == 31 ? GetSP() : jit_state.regs.at(index);
|
||||
}
|
||||
|
||||
void SetRegister(size_t index, u64 value) {
|
||||
if (index == 31)
|
||||
return SetSP(value);
|
||||
jit_state.regs.at(index) = value;
|
||||
}
|
||||
|
||||
std::array<u64, 31> GetRegisters() const {
|
||||
return jit_state.regs;
|
||||
}
|
||||
|
||||
void SetRegisters(const std::array<u64, 31>& value) {
|
||||
jit_state.regs = value;
|
||||
}
|
||||
|
||||
Vector GetVector(size_t index) const {
|
||||
return {jit_state.vec.at(index * 2), jit_state.vec.at(index * 2 + 1)};
|
||||
}
|
||||
|
||||
void SetVector(size_t index, Vector value) {
|
||||
jit_state.vec.at(index * 2) = value[0];
|
||||
jit_state.vec.at(index * 2 + 1) = value[1];
|
||||
}
|
||||
|
||||
std::array<Vector, 32> GetVectors() const {
|
||||
std::array<Vector, 32> ret;
|
||||
static_assert(sizeof(ret) == sizeof(jit_state.vec));
|
||||
std::memcpy(ret.data(), jit_state.vec.data(), sizeof(jit_state.vec));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SetVectors(const std::array<Vector, 32>& value) {
|
||||
static_assert(sizeof(value) == sizeof(jit_state.vec));
|
||||
std::memcpy(jit_state.vec.data(), value.data(), sizeof(jit_state.vec));
|
||||
}
|
||||
|
||||
u32 GetFpcr() const {
|
||||
return jit_state.fpcr;
|
||||
}
|
||||
|
||||
void SetFpcr(u32 value) {
|
||||
jit_state.fpcr = value;
|
||||
}
|
||||
|
||||
u32 GetFpsr() const {
|
||||
return jit_state.fpsr;
|
||||
}
|
||||
|
||||
void SetFpsr(u32 value) {
|
||||
jit_state.fpsr = value;
|
||||
}
|
||||
|
||||
u32 GetPstate() const {
|
||||
return jit_state.pstate;
|
||||
}
|
||||
|
||||
void SetPstate(u32 value) {
|
||||
jit_state.pstate = value;
|
||||
}
|
||||
|
||||
void ClearExclusiveState() {
|
||||
jit_state.exclusive_state = 0;
|
||||
}
|
||||
|
||||
bool IsExecuting() const {
|
||||
return is_executing;
|
||||
}
|
||||
|
||||
std::string Disassemble() const {
|
||||
// const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
// auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||
// return Common::DisassemblePPC64(p, p + size);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
void RequestCacheInvalidation() {
|
||||
// UNREACHABLE();
|
||||
invalidate_entire_cache = false;
|
||||
invalid_cache_ranges.clear();
|
||||
}
|
||||
|
||||
A64::UserConfig conf;
|
||||
A64JitState jit_state{};
|
||||
A64AddressSpace current_address_space;
|
||||
Jit* jit_interface;
|
||||
volatile u32 halt_reason = 0;
|
||||
bool is_executing = false;
|
||||
|
||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||
bool invalidate_entire_cache = false;
|
||||
std::mutex invalidation_mutex;
|
||||
};
|
||||
|
||||
Jit::Jit(UserConfig conf) : impl(std::make_unique<Jit::Impl>(this, conf)) {}
|
||||
Jit::~Jit() = default;
|
||||
|
||||
HaltReason Jit::Run() {
|
||||
return impl->Run();
|
||||
}
|
||||
|
||||
HaltReason Jit::Step() {
|
||||
return impl->Step();
|
||||
}
|
||||
|
||||
void Jit::ClearCache() {
|
||||
impl->ClearCache();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCacheRange(u64 start_address, size_t length) {
|
||||
impl->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void Jit::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Jit::HaltExecution(HaltReason hr) {
|
||||
impl->HaltExecution(hr);
|
||||
}
|
||||
|
||||
void Jit::ClearHalt(HaltReason hr) {
|
||||
impl->ClearHalt(hr);
|
||||
}
|
||||
|
||||
u64 Jit::GetSP() const {
|
||||
return impl->GetSP();
|
||||
}
|
||||
|
||||
void Jit::SetSP(u64 value) {
|
||||
impl->SetSP(value);
|
||||
}
|
||||
|
||||
u64 Jit::GetPC() const {
|
||||
return impl->GetPC();
|
||||
}
|
||||
|
||||
void Jit::SetPC(u64 value) {
|
||||
impl->SetPC(value);
|
||||
}
|
||||
|
||||
u64 Jit::GetRegister(size_t index) const {
|
||||
return impl->GetRegister(index);
|
||||
}
|
||||
|
||||
void Jit::SetRegister(size_t index, u64 value) {
|
||||
impl->SetRegister(index, value);
|
||||
}
|
||||
|
||||
std::array<u64, 31> Jit::GetRegisters() const {
|
||||
return impl->GetRegisters();
|
||||
}
|
||||
|
||||
void Jit::SetRegisters(const std::array<u64, 31>& value) {
|
||||
impl->SetRegisters(value);
|
||||
}
|
||||
|
||||
Vector Jit::GetVector(size_t index) const {
|
||||
return impl->GetVector(index);
|
||||
}
|
||||
|
||||
void Jit::SetVector(size_t index, Vector value) {
|
||||
impl->SetVector(index, value);
|
||||
}
|
||||
|
||||
std::array<Vector, 32> Jit::GetVectors() const {
|
||||
return impl->GetVectors();
|
||||
}
|
||||
|
||||
void Jit::SetVectors(const std::array<Vector, 32>& value) {
|
||||
impl->SetVectors(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetFpcr() const {
|
||||
return impl->GetFpcr();
|
||||
}
|
||||
|
||||
void Jit::SetFpcr(u32 value) {
|
||||
impl->SetFpcr(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetFpsr() const {
|
||||
return impl->GetFpsr();
|
||||
}
|
||||
|
||||
void Jit::SetFpsr(u32 value) {
|
||||
impl->SetFpsr(value);
|
||||
}
|
||||
|
||||
u32 Jit::GetPstate() const {
|
||||
return impl->GetPstate();
|
||||
}
|
||||
|
||||
void Jit::SetPstate(u32 value) {
|
||||
impl->SetPstate(value);
|
||||
}
|
||||
|
||||
void Jit::ClearExclusiveState() {
|
||||
impl->ClearExclusiveState();
|
||||
}
|
||||
|
||||
bool Jit::IsExecuting() const {
|
||||
return impl->IsExecuting();
|
||||
}
|
||||
|
||||
std::string Jit::Disassemble() const {
|
||||
return impl->Disassemble();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::A64
|
||||
@@ -1,77 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
/*
|
||||
https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
|
||||
|
||||
r0 Volatile register used in function prologs
|
||||
r1 Stack frame pointer
|
||||
r2 TOC pointer
|
||||
r3 Volatile parameter and return value register
|
||||
r4-r10 Volatile registers used for function parameters
|
||||
r11 Volatile register used in calls by pointer and as an environment pointer for languages which require one
|
||||
r12 Volatile register used for exception handling and glink code
|
||||
r13 Reserved for use as system thread ID
|
||||
r14-r31 Nonvolatile registers used for local variables
|
||||
|
||||
f0 Volatile scratch register
|
||||
f1-f4 Volatile floating point parameter and return value registers
|
||||
f5-f13 Volatile floating point parameter registers
|
||||
f14-f31 Nonvolatile registers
|
||||
|
||||
LR Link register (volatile)
|
||||
CTR Loop counter register (volatile)
|
||||
XER Fixed point exception register (volatile)
|
||||
FPSCR Floating point status and control register (volatile)
|
||||
|
||||
CR0-CR1 Volatile condition code register fields
|
||||
CR2-CR4 Nonvolatile condition code register fields
|
||||
CR5-CR7 Volatile condition code register fields
|
||||
|
||||
v0-v1 Volatile scratch registers
|
||||
v2-v13 Volatile vector parameters registers
|
||||
v14-v19 Volatile scratch registers
|
||||
v20-v31 Non-volatile registers
|
||||
vrsave Non-volatile 32-bit register
|
||||
*/
|
||||
|
||||
// Jit fn signature => (AXXAddressSpace& process, AXXJitState& thread_ctx, volatile u32* halt_reason)
|
||||
constexpr powah::GPR RPROCESS = powah::R3;
|
||||
constexpr powah::GPR RJIT = powah::R4;
|
||||
constexpr powah::GPR RHALTREASON = powah::R5;
|
||||
constexpr powah::GPR RTOCPTR = powah::R6;
|
||||
// temporals
|
||||
constexpr powah::GPR RNZCV = powah::R7;
|
||||
|
||||
constexpr powah::GPR ABI_PARAM1 = powah::R3;
|
||||
constexpr powah::GPR ABI_PARAM2 = powah::R4;
|
||||
constexpr powah::GPR ABI_PARAM3 = powah::R5;
|
||||
constexpr powah::GPR ABI_PARAM4 = powah::R6;
|
||||
|
||||
// See https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#REG
|
||||
constexpr std::initializer_list<u32> GPR_ORDER{
|
||||
// r13 is thread-id
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 //non-volatile
|
||||
};
|
||||
|
||||
constexpr std::initializer_list<powah::GPR> ABI_CALLEER_SAVED{
|
||||
powah::R3, powah::R4, powah::R5, powah::R6,
|
||||
powah::R7, powah::R8, powah::R9, powah::R10,
|
||||
powah::R11, powah::R12
|
||||
};
|
||||
constexpr std::initializer_list<powah::GPR> ABI_CALLEE_SAVED{
|
||||
powah::R14, powah::R15, powah::R16, powah::R17,
|
||||
powah::R18, powah::R19, powah::R20, powah::R21,
|
||||
powah::R22, powah::R23, powah::R24, powah::R25,
|
||||
powah::R26, powah::R27, powah::R28, powah::R29,
|
||||
powah::R30, powah::R31
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <new>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
class CodeBlock {
|
||||
public:
|
||||
explicit CodeBlock(size_t size_) noexcept : size{size_} {
|
||||
block = (u8*)mmap(nullptr, size, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
//printf("block = %p, size= %zx\n", block, size);
|
||||
ASSERT(block != nullptr);
|
||||
}
|
||||
~CodeBlock() noexcept {
|
||||
if (block != nullptr)
|
||||
munmap(block, size);
|
||||
}
|
||||
template<typename T>
|
||||
T ptr() const noexcept {
|
||||
static_assert(std::is_pointer_v<T> || std::is_same_v<T, uintptr_t> || std::is_same_v<T, intptr_t>);
|
||||
return reinterpret_cast<T>(block);
|
||||
}
|
||||
protected:
|
||||
void* block = nullptr;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
class Block;
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct EmitConfig;
|
||||
|
||||
struct EmitContext {
|
||||
IR::Block& block;
|
||||
RegAlloc& reg_alloc;
|
||||
const EmitConfig& emit_conf;
|
||||
EmittedBlockInfo& ebi;
|
||||
// label used when returning :)
|
||||
powah::Label l_return;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,305 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/backend/ppc64/stack_layout.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
#include "stack_layout.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Void>(powah::Context&, EmitContext&, IR::Inst*) {}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Identity>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.MR(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Breakpoint>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CallHostFunction>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PushRSB>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCarryFromOp>(powah::Context&, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetOverflowFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetGEFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetNZCVFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (ctx.reg_alloc.IsValueLive(inst)) {
|
||||
ASSERT(false && "unimp value live");
|
||||
return;
|
||||
}
|
||||
|
||||
// All logical operations whom set (RC) are going to compute as the following:
|
||||
// 1. Rt <- logical_op (Ra, Rb)
|
||||
// 2. Compare as Signed(Rt) against 0
|
||||
// Basically, it's equivalent to say:
|
||||
//
|
||||
// add r0, r1, r2 -> add. r0, r1, r2
|
||||
// cmpli r0, 0
|
||||
//
|
||||
// CR0:
|
||||
// 0 - 0x08 - N/LT, result is negative
|
||||
// 1 - 0x04 - P/GT, result is positive
|
||||
// 2 - 0x02 - Z/EQ, result is zero
|
||||
// 3 - 0x01 - S/SO, summary overflow (carry?)
|
||||
// XER:
|
||||
// 32 - SO
|
||||
// 33 - Overflow
|
||||
// 34 - Carry
|
||||
// NZCV -> (CR0.0, CR0.2, XER.34, XER.33)
|
||||
if (false) {
|
||||
//code.MFSPR(powah::GPR{1}, tmp4, powah::R0); // From XER
|
||||
/*uint64_t nzcv(uint64_t cr0, uint64_t xer) {
|
||||
return ((xer >> 33) & 1)
|
||||
| (((xer >> 34) & 1) << 1)
|
||||
| (((cr0 >> (32 + 2)) & 1) << 2)
|
||||
| (((cr0 >> (32 + 0)) & 1) << 3);
|
||||
}*/
|
||||
// auto const tmp9 = ctx.reg_alloc.ScratchGpr();
|
||||
// auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
// code.SRDI(tmp9, tmp3, 34);
|
||||
// code.RLDICL(tmp10, tmp4, 31, 63);
|
||||
// code.SRDI(tmp3, tmp3, 32);
|
||||
// code.RLDIC(tmp9, tmp9, 2, 61);
|
||||
// code.OR(tmp9, tmp9, tmp10);
|
||||
// code.RLDIC(tmp3, tmp3, 3, 60);
|
||||
// code.SRDI(tmp4, tmp4, 34);
|
||||
// code.OR(tmp3, tmp9, tmp3);
|
||||
// code.RLDIC(tmp4, tmp4, 1, 62);
|
||||
// code.OR(tmp3, tmp3, tmp4);
|
||||
} else {
|
||||
// MFCR Fills RT 32:63, RT 0:31 left blank
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
code.MR(tmp3, PPC64::RNZCV);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetNZFromOp>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetUpperFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetLowerFromOp>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::GetCFlagFromNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// r3 -> process
|
||||
// r4 -> thread_ctx
|
||||
// r5 -> halt_reason
|
||||
// r6 -> test_thunk
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool) {
|
||||
if (ctx.emit_conf.a64_variant) {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STD(tmp, PPC64::RJIT, offsetof(A64JitState, pc));
|
||||
code.B(ctx.l_return);
|
||||
} else {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LI(tmp, terminal.next.Value());
|
||||
code.STW(tmp, PPC64::RJIT, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
EmitTerminal(code, ctx, IR::Term::LinkBlock{terminal.next}, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
powah::Label const l_else = code.DefineLabel();
|
||||
powah::Label const l_end = code.DefineLabel();
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LWZ(tmp, PPC64::RJIT, offsetof(A64JitState, check_bit));
|
||||
code.CMPLDI(tmp, 0);
|
||||
code.BEQ(powah::CR0, l_else);
|
||||
// CheckBit == 1
|
||||
EmitLeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.B(l_end);
|
||||
// CheckBit == 0
|
||||
code.LABEL(l_else);
|
||||
EmitLeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.LABEL(l_end);
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
void EmitLeafTerminal(powah::Context& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitTerminal(powah::Context& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitLeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf) {
|
||||
EmittedBlockInfo ebi;
|
||||
RegAlloc reg_alloc{code};
|
||||
EmitContext ctx{block, reg_alloc, emit_conf, ebi, code.DefineLabel()};
|
||||
size_t const stack_size = 112 + ABI_CALLEE_SAVED.size() * 8;
|
||||
auto const start_offset = code.offset;
|
||||
ebi.entry_point = &code.base[start_offset];
|
||||
if (!block.instructions.empty()) {
|
||||
code.MFLR(powah::R0);
|
||||
code.STD(powah::R0, powah::R1, 16);
|
||||
// Non-volatile saves
|
||||
std::vector<powah::GPR> gp_regs{ABI_CALLEE_SAVED};
|
||||
for (size_t i = 0; i < gp_regs.size(); ++i)
|
||||
code.STD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
|
||||
code.STDU(powah::R1, powah::R1, uint32_t(-stack_size));
|
||||
code.STD(powah::R2, powah::R1, 40);
|
||||
|
||||
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
|
||||
IR::Inst* inst = &*iter;
|
||||
switch (inst->GetOpcode()) {
|
||||
#define OPCODE(name, type, ...) \
|
||||
case IR::Opcode::name: \
|
||||
EmitIR<IR::Opcode::name>(code, ctx, inst); \
|
||||
break;
|
||||
#define A32OPC(name, type, ...) \
|
||||
case IR::Opcode::A32##name: \
|
||||
EmitIR<IR::Opcode::A32##name>(code, ctx, inst); \
|
||||
break;
|
||||
#define A64OPC(name, type, ...) \
|
||||
case IR::Opcode::A64##name: \
|
||||
EmitIR<IR::Opcode::A64##name>(code, ctx, inst); \
|
||||
break;
|
||||
#include "dynarmic/ir/opcodes.inc"
|
||||
#undef OPCODE
|
||||
#undef A32OPC
|
||||
#undef A64OPC
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// auto const cycles_to_add = block.CycleCount();
|
||||
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
|
||||
code.LABEL(ctx.l_return);
|
||||
code.ADDI(powah::R1, powah::R1, stack_size);
|
||||
for (size_t i = 0; i < gp_regs.size(); ++i)
|
||||
code.LD(gp_regs[i], powah::R1, -int32_t(gp_regs.size() - i) * 8);
|
||||
code.LD(powah::R0, powah::R1, 16);
|
||||
code.MTLR(powah::R0);
|
||||
code.LI(powah::R3, 0);
|
||||
} else {
|
||||
EmitTerminal(code, ctx, ctx.block.GetTerminal(), ctx.block.Location(), false);
|
||||
code.LABEL(ctx.l_return);
|
||||
code.LI(powah::R3, 67); //non-zero ret
|
||||
}
|
||||
code.BLR();
|
||||
code.ApplyRelocs();
|
||||
|
||||
/*
|
||||
llvm-objcopy -I binary -O elf64-powerpc --rename-section=.data=.text,code test.bin test.elf && llvm-objdump -d test.elf
|
||||
*/
|
||||
static FILE* fp = fopen("test.bin", "wb");
|
||||
fwrite(code.base, code.offset - start_offset, sizeof(uint32_t), fp);
|
||||
fflush(fp);
|
||||
|
||||
ebi.size = code.offset - start_offset;
|
||||
return ebi;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace biscuit {
|
||||
class Assembler;
|
||||
} // namespace biscuit
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
class Block;
|
||||
class Inst;
|
||||
enum class Cond;
|
||||
enum class Opcode;
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct EmittedBlockInfo {
|
||||
std::uint32_t* entry_point;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct EmitConfig {
|
||||
bool enable_cycle_counting;
|
||||
bool always_little_endian;
|
||||
bool a64_variant;
|
||||
};
|
||||
|
||||
struct EmitContext;
|
||||
|
||||
EmittedBlockInfo EmitPPC64(powah::Context& code, IR::Block block, const EmitConfig& emit_conf);
|
||||
template<IR::Opcode op>
|
||||
void EmitIR(powah::Context& code, EmitContext& ctx, IR::Inst* inst);
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,318 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/frontend/A32/a32_types.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCheckBit>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.ADDI(result, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
|
||||
code.LWZ(result, result, offsetof(A32JitState, regs));
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetVector>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetRegister>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A32Reg) {
|
||||
auto const addr = ctx.reg_alloc.ScratchGpr();
|
||||
code.ADDI(addr, PPC64::RJIT, A32::RegNumber(inst->GetArg(0).GetA32RegRef()) * sizeof(u32));
|
||||
code.STW(value, addr, offsetof(A32JitState, regs));
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetExtendedRegister64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetVector>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVRaw>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZCVQ>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZ>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetCpsrNZC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.LD(result, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(result, result, 31, 31, 31);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32OrQFlag>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlags>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetGEFlagsCompressed>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32BXWritePC>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32UpdateUpperLocationDescriptor>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CallSupervisor>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExceptionRaised>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32DataMemoryBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32InstructionSynchronizationBarrier>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscr>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32GetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32SetFpscrNZCV>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Memory
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ClearExclusive>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32WriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Coprocesor
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocLoadWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocStoreWords>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,341 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/frontend/A64/a64_types.h"
|
||||
#include "dynarmic/backend/ppc64/a64_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetCheckBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.STW(value, PPC64::RJIT, offsetof(A64JitState, check_bit));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCFlag>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCVRaw>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.STW(value, PPC64::RJIT, offsetof(A64JitState, pstate));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
// Need to account for endianess here...
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.LWZ(result, PPC64::RJIT, offs);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.LD(result, PPC64::RJIT, offs);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetW>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.RLDICL(tmp, value, 0, 32);
|
||||
code.STD(tmp, PPC64::RJIT, offs);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetX>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const value = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
if (inst->GetArg(0).GetType() == IR::Type::A64Reg) {
|
||||
auto const offs = offsetof(A64JitState, regs)
|
||||
+ A64::RegNumber(inst->GetArg(0).GetA64RegRef()) * sizeof(u64);
|
||||
code.STD(value, PPC64::RJIT, offs);
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetS>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetD>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetSP>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPCR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetFPSR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetPC>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64CallSupervisor>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExceptionRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTFRQ>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCNTPCT>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetCTR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetDCZID>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64GetTPIDRRO>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64SetTPIDR>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Memory
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ClearExclusive>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveReadMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64WriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory8>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory16>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A64ExclusiveWriteMemory128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,933 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
// uint64_t pack2x1(uint32_t lo, uint32_t hi) { return (uint64_t)lo | ((uint64_t)hi << 32); }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x32To1x64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const lo = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const hi = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.SLDI(result, hi, 32);
|
||||
code.OR(result, result, lo);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Pack2x64To1x128>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// uint64_t lsw(uint64_t a) { return (uint32_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLDICL(result, source, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t f(uint64_t a) { return (uint16_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t f(uint64_t a) { return (uint8_t)a; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LeastSignificantByte>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// uint64_t msw(uint64_t a) { return a >> 32; }
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRDI(result, source, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return ((uint32_t)a) >> 31; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MostSignificantBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 1, 31, 31);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return (uint32_t)a == 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZW(result, source);
|
||||
code.SRWI(result, result, 5);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return a == 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::IsZero64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZD(result, source);
|
||||
code.SRDI(result, result, 6);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a) { return (a & 1) != 0; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::TestBit>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (inst->GetArg(1).IsImmediate()) {
|
||||
auto const shift = inst->GetArg(1).GetImmediateAsU64();
|
||||
if (shift > 0) {
|
||||
code.RLDICL(result, source, (64 - shift - 1) & 0x3f, 63);
|
||||
}
|
||||
} else {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
struct jit {
|
||||
uint64_t t;
|
||||
uint64_t a;
|
||||
};
|
||||
uint64_t f(jit *p, uint64_t a, uint64_t b) {
|
||||
bool n = (p->a & 0b1000) != 0;
|
||||
bool z = (p->a & 0b0100) != 0;
|
||||
bool c = (p->a & 0b0010) != 0;
|
||||
bool v = (p->a & 0b0001) != 0;
|
||||
return (p->a & 0b0001) == 0 ? a : b;
|
||||
}
|
||||
*/
|
||||
static powah::GPR EmitConditionalSelectX(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const nzcv = ctx.reg_alloc.ScratchGpr();
|
||||
auto const then_ = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const else_ = ctx.reg_alloc.UseGpr(inst->GetArg(2));
|
||||
switch (inst->GetArg(0).GetCond()) {
|
||||
case IR::Cond::EQ: // Z == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 4);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::NE: // Z == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 4);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::CS: // C == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 2);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::CC: // C == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 2);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::MI: // N == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 8);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::PL: // N == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 8);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::VS: // V == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::VC: // V == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, else_, then_);
|
||||
break;
|
||||
case IR::Cond::HI: // Z == 0 && C == 1
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(nzcv, nzcv, 0, 29, 30);
|
||||
code.CMPLDI(nzcv, 2);
|
||||
code.ISELEQ(nzcv, then_, else_);
|
||||
break;
|
||||
case IR::Cond::LS: // Z == 1 || C == 0
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.RLWINM(nzcv, nzcv, 0, 29, 30);
|
||||
code.CMPLDI(nzcv, 2);
|
||||
code.ISELEQ(nzcv, else_, then_);
|
||||
break;
|
||||
case IR::Cond::GE: { // N == V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, else_, then_);
|
||||
} break;
|
||||
case IR::Cond::LT: { // N != V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
code.LWZ(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(nzcv, then_, else_);
|
||||
} break;
|
||||
case IR::Cond::GT: { // Z == 0 && N == V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
powah::Label const l_ne = code.DefineLabel();
|
||||
powah::Label const l_cc = code.DefineLabel();
|
||||
powah::Label const l_fi = code.DefineLabel();
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(tmp, nzcv, 4);
|
||||
code.BNE(powah::CR0, l_ne);
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.CMPLDI(nzcv, 0);
|
||||
code.BGT(powah::CR0, l_fi);
|
||||
code.LABEL(l_ne);
|
||||
code.MR(then_, else_);
|
||||
code.LABEL(l_cc);
|
||||
code.MR(nzcv, then_);
|
||||
code.LABEL(l_fi);
|
||||
} break;
|
||||
case IR::Cond::LE: { // Z == 1 || N != V
|
||||
auto const tmp = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp2 = ctx.reg_alloc.ScratchGpr();
|
||||
powah::Label const l_ne = code.DefineLabel();
|
||||
code.MR(tmp2, then_);
|
||||
code.LD(nzcv, PPC64::RJIT, offsetof(A32JitState, cpsr_nzcv));
|
||||
code.ANDI_(tmp, nzcv, 4);
|
||||
code.BNE(powah::CR0, l_ne);
|
||||
code.SRWI(tmp, nzcv, 3);
|
||||
code.XOR(nzcv, tmp, nzcv);
|
||||
code.ANDI_(nzcv, nzcv, 1);
|
||||
code.ISELGT(tmp2, then_, else_);
|
||||
code.LABEL(l_ne);
|
||||
code.MR(nzcv, tmp2);
|
||||
} break;
|
||||
default:
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
return nzcv;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelect64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ConditionalSelectNZCV>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = EmitConditionalSelectX(code, ctx, inst);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeft32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SLW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeft64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SLD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
uint64_t f(uint64_t a, uint64_t s) { return a >> s; }
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRAW(result, source, shift);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const shift = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SRAD(result, source, shift);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// __builtin_rotateright32
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLW(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightExtended>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SLW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftLeftMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SLD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::LogicalShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRAW(result, source, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ArithmeticShiftRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.SRAD(result, source, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRightMasked64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NEG(result, src_a, powah::R0);
|
||||
code.ROTLD(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Add32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.ADDC_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
/*
|
||||
struct jit {
|
||||
uint32_t nzcv;
|
||||
};
|
||||
uint64_t addc(jit *p, uint64_t a, uint64_t b) {
|
||||
long long unsigned int e;
|
||||
uint64_t r = __builtin_addcll(a, b, p->nzcv & 0b0010, &e);
|
||||
p->nzcv = (p->nzcv & 0b1101) | e;
|
||||
return r;
|
||||
}
|
||||
*/
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Add64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.ADDC_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Sub32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SUBFC_(result, src_b, src_a);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Sub64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.SUBFC_(result, src_b, src_a);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLWO_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Mul64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedMultiplyHigh64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.MULLDO_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVWU_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVDU_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVW_(result, src_a, src_b);
|
||||
code.EXTSW(result, result);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedDiv64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.DIVD_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp8 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = PPC64::RNZCV;
|
||||
code.RLDICL(tmp3, src_a, 0, 32); // Truncate
|
||||
code.AND(tmp3, tmp3, src_b);
|
||||
code.CNTLZD(tmp9, tmp3);
|
||||
code.RLWINM(tmp8, tmp3, 0, 0, 0);
|
||||
code.SRDI(tmp9, tmp9, 6);
|
||||
code.SLDI(tmp9, tmp9, 30);
|
||||
code.OR(tmp9, tmp9, tmp8);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::And64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = PPC64::RNZCV;
|
||||
code.AND(tmp3, src_a, src_b);
|
||||
code.CNTLZD(tmp10, tmp3);
|
||||
code.SRADI(tmp9, tmp3, 32);
|
||||
code.SRDI(tmp10, tmp10, 6);
|
||||
code.RLWINM(tmp9, tmp9, 0, 0, 0);
|
||||
code.SLDI(tmp10, tmp10, 30);
|
||||
code.OR(tmp9, tmp9, tmp10);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.NAND_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AndNot64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.NAND_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.XOR_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Eor64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.XOR_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.OR_(result, src_a, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Or64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(1));
|
||||
code.OR_(result, src_a, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// TODO(lizzie): NZVC support for NOT
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NOT(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::Not64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.NOT(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSB(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSH(result, source);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSH(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSB(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.EXTSW(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendByteToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendHalfToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLWINM(result, source, 0, 0xffff);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendWordToLong>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.RLDICL(result, source, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ZeroExtendLongToQuad>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// __builtin_bswap32
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseWord>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRW(result, source);
|
||||
//code.RLDICL(result, result, 0, 32);
|
||||
} else {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
code.ROTLWI(result, source, 24);
|
||||
code.RLWIMI(result, source, 8, 8, 15);
|
||||
code.RLWIMI(result, source, 8, 24, 31);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
}
|
||||
|
||||
// __builtin_bswap64
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseHalf>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRH(result, source);
|
||||
code.RLWINM(result, source, 0, 0xff);
|
||||
} else {
|
||||
code.ROTLWI(result, source, 24);
|
||||
code.RLWIMI(result, source, 8, 0, 23);
|
||||
code.CLRLDI(result, result, 48);
|
||||
}
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ByteReverseDual>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
if (false) {
|
||||
//code.BRD(result, source);
|
||||
} else {
|
||||
auto const tmp10 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp9 = ctx.reg_alloc.ScratchGpr();
|
||||
auto const tmp3 = ctx.reg_alloc.ScratchGpr();
|
||||
code.MR(tmp3, source);
|
||||
code.ROTLWI(tmp10, tmp3, 24);
|
||||
code.SRDI(tmp9, tmp3, 32);
|
||||
code.RLWIMI(tmp10, tmp3, 8, 8, 15);
|
||||
code.RLWIMI(tmp10, tmp3, 8, 24, 31);
|
||||
code.ROTLWI(tmp3, tmp9, 24);
|
||||
code.RLWIMI(tmp3, tmp9, 8, 8, 15);
|
||||
code.RLWIMI(tmp3, tmp9, 8, 24, 31);
|
||||
code.RLDIMI(tmp3, tmp10, 32, 0);
|
||||
ctx.reg_alloc.DefineValue(inst, tmp3);
|
||||
}
|
||||
}
|
||||
|
||||
// __builtin_clz
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZW(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
// __builtin_clz
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CountLeadingZeros64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const source = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CNTLZD(result, source);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ExtractRegister64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::ReplicateBit64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLW(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MaxUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLD(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPW(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinSigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPD(powah::CR0, result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned32>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLW(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
code.RLDICL(result, result, 0, 32);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::MinUnsigned64>(powah::Context& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto const result = ctx.reg_alloc.ScratchGpr();
|
||||
auto const src_a = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
auto const src_b = ctx.reg_alloc.UseGpr(inst->GetArg(0));
|
||||
code.CMPLD(result, src_a);
|
||||
code.ISELGT(result, result, src_b);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,458 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAbs64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPCompare64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDiv64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMax64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMaxNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMin64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMinNumeric64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMul64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPMulX64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPNeg64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipExponent64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRecipStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRoundInt64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtEstimate64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPRSqrtStepFused64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSqrt64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToHalf>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPDoubleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPHalfToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedS64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPSingleToFixedU64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS16ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS32ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedU64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToDouble>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPFixedS64ToSingle>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,380 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "dynarmic/backend/ppc64/a32_core.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/emit_context.h"
|
||||
#include "dynarmic/backend/ppc64/emit_ppc64.h"
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAddWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSubWithFlag32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturation>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedDoublingMultiplyReturnHigh32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedAdd64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::UnsignedSaturatedSub64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Packed
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingAddSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedHalvingSubAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedAddS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubU16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSaturatedSubS16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedAbsDiffSumU8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::PackedSelect>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
// Crypto
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32Castagnoli64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO8>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO16>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO32>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::CRC32ISO64>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESDecryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESEncryptSingleRound>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESInverseMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::AESMixColumns>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SM4AccessSubstitutionBox>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256Hash>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule0>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::SHA256MessageSchedule1>(powah::Context&, EmitContext&, IR::Inst*) {
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "dynarmic/interface/exclusive_monitor.h"
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
|
||||
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
|
||||
|
||||
size_t ExclusiveMonitor::GetProcessorCount() const {
|
||||
return exclusive_addresses.size();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Lock() {
|
||||
lock.Lock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Unlock() {
|
||||
lock.Unlock();
|
||||
}
|
||||
|
||||
bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) {
|
||||
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
|
||||
Lock();
|
||||
if (exclusive_addresses[processor_id] != masked_address) {
|
||||
Unlock();
|
||||
return false;
|
||||
}
|
||||
for (VAddr& other_address : exclusive_addresses)
|
||||
if (other_address == masked_address)
|
||||
other_address = INVALID_EXCLUSIVE_ADDRESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Clear() {
|
||||
Lock();
|
||||
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::ClearProcessor(size_t processor_id) {
|
||||
Lock();
|
||||
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -1,26 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
enum class HostLoc : uint8_t {
|
||||
R0, R1, R2, R3, R4, R5, R6, R7, R8, R9,
|
||||
R10, R11, R12, R13, R14, R15, R16, R17, R18, R19,
|
||||
R20, R21, R22, R23, R24, R25, R26, R27, R28, R29,
|
||||
R30, R31,
|
||||
FR0, FR1, FR2, FR3, FR4, FR5, FR6, FR7, FR8, FR9,
|
||||
FR10, FR11, FR12, FR13, FR14, FR15, FR16, FR17, FR18, FR19,
|
||||
FR20, FR21, FR22, FR23, FR24, FR25, FR26, FR27, FR28, FR29,
|
||||
FR30, FR31,
|
||||
VR0, VR1, VR2, VR3, VR4, VR5, VR6, VR7, VR8, VR9,
|
||||
VR10, VR11, VR12, VR13, VR14, VR15, VR16, VR17, VR18, VR19,
|
||||
VR20, VR21, VR22, VR23, VR24, VR25, VR26, VR27, VR28, VR29,
|
||||
VR30, VR31,
|
||||
FirstSpill,
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::PPC64
|
||||
@@ -1,213 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <variant>
|
||||
|
||||
#include "dynarmic/backend/ppc64/reg_alloc.h"
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/common/always_false.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
constexpr size_t spill_offset = offsetof(StackLayout, spill);
|
||||
constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type);
|
||||
|
||||
static bool IsValuelessType(IR::Type type) {
|
||||
return type == IR::Type::Table;
|
||||
}
|
||||
|
||||
void HostLocInfo::UpdateUses() {
|
||||
accumulated_uses += uses_this_inst;
|
||||
uses_this_inst = 0;
|
||||
if (accumulated_uses == expected_uses) {
|
||||
values.clear();
|
||||
accumulated_uses = 0;
|
||||
expected_uses = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegAlloc::IsValueLive(IR::Inst* inst) const {
|
||||
return !!ValueLocation(inst);
|
||||
}
|
||||
|
||||
void RegAlloc::UpdateAllUses() {
|
||||
for (auto& gpr : gprs)
|
||||
gpr.UpdateUses();
|
||||
for (auto& fpr : fprs)
|
||||
fpr.UpdateUses();
|
||||
for (auto& spill : spills)
|
||||
spill.UpdateUses();
|
||||
}
|
||||
|
||||
void RegAlloc::DefineAsExisting(IR::Inst* inst, IR::Value arg) {
|
||||
ASSERT(!ValueLocation(inst));
|
||||
if (arg.IsImmediate()) {
|
||||
inst->ReplaceUsesWith(arg);
|
||||
} else {
|
||||
auto& info = ValueInfo(arg.GetInst());
|
||||
info.values.emplace_back(inst);
|
||||
info.expected_uses += inst->UseCount();
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::AssertNoMoreUses() const {
|
||||
const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
|
||||
ASSERT(std::all_of(gprs.begin(), gprs.end(), is_empty));
|
||||
ASSERT(std::all_of(fprs.begin(), fprs.end(), is_empty));
|
||||
ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
|
||||
}
|
||||
|
||||
std::optional<u32> RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs) const {
|
||||
auto const order = PPC64::GPR_ORDER;
|
||||
if (auto const it = std::find_if(order.begin(), order.end(), [&](u32 i) {
|
||||
return regs[i].values.empty() && !regs[i].locked;
|
||||
}); it != order.end())
|
||||
return *it;
|
||||
// TODO: Actual proper LRU
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void RegAlloc::SpillGpr(u32 index) {
|
||||
ASSERT(!gprs[index].locked && !gprs[index].realized);
|
||||
if (!gprs[index].values.empty()) {
|
||||
const u32 new_location_index = FindFreeSpill();
|
||||
code.STD(powah::GPR{index}, powah::R1, spill_offset + new_location_index * spill_slot_size);
|
||||
spills[new_location_index] = std::exchange(gprs[index], {});
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::SpillFpr(u32 index) {
|
||||
ASSERT(!fprs[index].locked && !fprs[index].realized);
|
||||
if (!fprs[index].values.empty()) {
|
||||
const u32 new_location_index = FindFreeSpill();
|
||||
//code.FSD(powah::FPR{index}, spill_offset + new_location_index * spill_slot_size, powah::sp);
|
||||
spills[new_location_index] = std::exchange(fprs[index], {});
|
||||
}
|
||||
}
|
||||
|
||||
u32 RegAlloc::FindFreeSpill() const {
|
||||
const auto iter = std::find_if(spills.begin(), spills.end(), [](const HostLocInfo& info) { return info.values.empty(); });
|
||||
ASSERT(iter != spills.end() && "All spill locations are full");
|
||||
return u32(iter - spills.begin());
|
||||
}
|
||||
|
||||
std::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const {
|
||||
const auto fn = [value](const HostLocInfo& info) {
|
||||
return info.Contains(value);
|
||||
};
|
||||
if (const auto iter = std::ranges::find_if(gprs, fn); iter != gprs.end())
|
||||
return HostLoc(u32(HostLoc::R0) + u32(iter - gprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(fprs, fn); iter != fprs.end())
|
||||
return HostLoc(u32(HostLoc::FR0) + u32(iter - fprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(vprs, fn); iter != vprs.end())
|
||||
return HostLoc(u32(HostLoc::VR0) + u32(iter - vprs.begin()));
|
||||
else if (const auto iter = std::ranges::find_if(spills, fn); iter != spills.end())
|
||||
return HostLoc(u32(HostLoc::FirstSpill) + u32(iter - spills.begin()));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline bool HostLocIsGpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::R0) && u8(h) <= u8(HostLoc::R31);
|
||||
}
|
||||
inline bool HostLocIsFpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::FR0) && u8(h) <= u8(HostLoc::FR31);
|
||||
}
|
||||
inline bool HostLocIsVpr(HostLoc h) noexcept {
|
||||
return u8(h) >= u8(HostLoc::VR0) && u8(h) <= u8(HostLoc::VR31);
|
||||
}
|
||||
inline std::variant<powah::GPR, powah::FPR> HostLocToReg(HostLoc h) noexcept {
|
||||
if (HostLocIsGpr(h))
|
||||
return powah::GPR{uint32_t(h)};
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
HostLocInfo& RegAlloc::ValueInfo(HostLoc h) {
|
||||
if (u8(h) >= u8(HostLoc::R0) && u8(h) <= u8(HostLoc::R31))
|
||||
return gprs[size_t(h)];
|
||||
else if (u8(h) >= u8(HostLoc::FR0) && u8(h) <= u8(HostLoc::FR31))
|
||||
return gprs[size_t(h) - size_t(HostLoc::FR0)];
|
||||
else if (u8(h) >= u8(HostLoc::VR0) && u8(h) <= u8(HostLoc::VR31))
|
||||
return vprs[size_t(h) - size_t(HostLoc::VR0)];
|
||||
auto const index = size_t(h) - size_t(HostLoc::FirstSpill);
|
||||
ASSERT(index <= spills.size());
|
||||
return spills[index];
|
||||
}
|
||||
|
||||
HostLocInfo& RegAlloc::ValueInfo(const IR::Inst* value) {
|
||||
const auto fn = [value](const HostLocInfo& info) {
|
||||
return info.Contains(value);
|
||||
};
|
||||
if (const auto iter = std::find_if(gprs.begin(), gprs.end(), fn); iter != gprs.end())
|
||||
return *iter;
|
||||
else if (const auto iter = std::find_if(fprs.begin(), fprs.end(), fn); iter != fprs.end())
|
||||
return *iter;
|
||||
else if (const auto iter = std::find_if(spills.begin(), spills.end(), fn); iter != spills.end())
|
||||
return *iter;
|
||||
ASSERT(false && "unimp");
|
||||
}
|
||||
|
||||
/// @brief Defines a register RegLock to use (and locks it)
|
||||
RegLock<powah::GPR> RegAlloc::ScratchGpr() {
|
||||
auto const r = AllocateRegister(gprs);
|
||||
return RegLock(*this, powah::GPR{*r});
|
||||
}
|
||||
|
||||
/// @brief Uses the given GPR of the argument
|
||||
RegLock<powah::GPR> RegAlloc::UseGpr(IR::Value arg) {
|
||||
if (arg.IsImmediate()) {
|
||||
// HOLY SHIT EVIL HAXX
|
||||
auto const reg = ScratchGpr();
|
||||
auto const imm = arg.GetImmediateAsU64();
|
||||
if (imm >= 0xffffffff) {
|
||||
auto const lo = uint32_t(imm >> 0), hi = uint32_t(imm >> 32);
|
||||
if (lo == hi) {
|
||||
code.LIS(reg, imm >> 16);
|
||||
code.ORI(reg, reg, imm & 0xffff);
|
||||
code.RLDIMI(reg, reg, 32, 0);
|
||||
} else {
|
||||
ASSERT(false && "larger >32bit imms");
|
||||
}
|
||||
} else if (imm > 0xffff && imm <= 0xffffffff) {
|
||||
code.LIS(reg, imm >> 16);
|
||||
code.ORI(reg, reg, imm & 0xffff);
|
||||
} else if (imm <= 0xffff) {
|
||||
code.LI(reg, imm);
|
||||
}
|
||||
return reg;
|
||||
} else {
|
||||
auto const loc = ValueLocation(arg.GetInst());
|
||||
ASSERT(loc && HostLocIsGpr(*loc));
|
||||
return RegLock(*this, std::get<powah::GPR>(HostLocToReg(*loc)));
|
||||
}
|
||||
}
|
||||
|
||||
void RegAlloc::DefineValue(IR::Inst* inst, powah::GPR const gpr) noexcept {
|
||||
ASSERT(!ValueLocation(inst) && "inst has already been defined");
|
||||
ValueInfo(HostLoc(gpr.index)).values.push_back(inst);
|
||||
}
|
||||
|
||||
void RegAlloc::DefineValue(IR::Inst* inst, IR::Value arg) noexcept {
|
||||
ASSERT(!ValueLocation(inst) && "inst has already been defined");
|
||||
if (arg.IsImmediate()) {
|
||||
HostLoc const loc{u8(ScratchGpr().value.index)};
|
||||
ValueInfo(loc).values.push_back(inst);
|
||||
auto const value = arg.GetImmediateAsU64();
|
||||
if (value >= 0x7fff) {
|
||||
ASSERT(false && "unimp");
|
||||
} else {
|
||||
//code.LI(HostLocToReg(loc), value);
|
||||
}
|
||||
} else {
|
||||
ASSERT(ValueLocation(arg.GetInst()) && "arg must already be defined");
|
||||
const HostLoc loc = *ValueLocation(arg.GetInst());
|
||||
ValueInfo(loc).values.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,109 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/backend/ppc64/stack_layout.h"
|
||||
#include "dynarmic/backend/ppc64/hostloc.h"
|
||||
#include "dynarmic/ir/cond.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/value.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
struct HostLocInfo final {
|
||||
std::vector<const IR::Inst*> values;
|
||||
size_t uses_this_inst = 0;
|
||||
size_t accumulated_uses = 0;
|
||||
size_t expected_uses = 0;
|
||||
/// @brief Lock usage of this register UNTIL a DefineValue() is issued
|
||||
bool locked = false;
|
||||
bool realized = false;
|
||||
bool Contains(const IR::Inst* value) const {
|
||||
return std::find(values.begin(), values.end(), value) != values.end();
|
||||
}
|
||||
void SetupScratchLocation() {
|
||||
ASSERT(IsCompletelyEmpty());
|
||||
realized = true;
|
||||
}
|
||||
bool IsCompletelyEmpty() const {
|
||||
return values.empty() && !locked && !realized && !accumulated_uses && !expected_uses && !uses_this_inst;
|
||||
}
|
||||
void UpdateUses();
|
||||
};
|
||||
|
||||
struct RegAlloc;
|
||||
|
||||
/// @brief Allows to use RAII to denote liveness/locking of a given register
|
||||
/// this basically means that we can use temporals and not need to go thru
|
||||
/// any weird deallocation stuffs :)
|
||||
template<typename T> struct RegLock {
|
||||
constexpr RegLock(RegAlloc& reg_alloc, T const value) noexcept
|
||||
: reg_alloc{reg_alloc}
|
||||
, value{value}
|
||||
{
|
||||
SetLock(true);
|
||||
}
|
||||
inline ~RegLock() noexcept { SetLock(false); }
|
||||
constexpr operator T const&() noexcept { return value; }
|
||||
constexpr operator T() const noexcept { return value; }
|
||||
inline void SetLock(bool v) noexcept;
|
||||
RegAlloc& reg_alloc;
|
||||
const T value;
|
||||
};
|
||||
|
||||
struct RegAlloc {
|
||||
explicit RegAlloc(powah::Context& code) : code{code} {}
|
||||
bool IsValueLive(IR::Inst* inst) const;
|
||||
void DefineAsExisting(IR::Inst* inst, IR::Value arg);
|
||||
|
||||
void SpillAll();
|
||||
void UpdateAllUses();
|
||||
void AssertNoMoreUses() const;
|
||||
|
||||
RegLock<powah::GPR> ScratchGpr();
|
||||
RegLock<powah::GPR> UseGpr(IR::Value arg);
|
||||
void DefineValue(IR::Inst* inst, powah::GPR const gpr) noexcept;
|
||||
void DefineValue(IR::Inst* inst, IR::Value arg) noexcept;
|
||||
private:
|
||||
template<typename T>
|
||||
friend struct RegLock;
|
||||
|
||||
std::optional<u32> AllocateRegister(const std::array<HostLocInfo, 32>& regs) const;
|
||||
void SpillGpr(u32 index);
|
||||
void SpillFpr(u32 index);
|
||||
u32 FindFreeSpill() const;
|
||||
|
||||
std::optional<HostLoc> ValueLocation(const IR::Inst* value) const;
|
||||
HostLocInfo& ValueInfo(HostLoc host_loc);
|
||||
HostLocInfo& ValueInfo(const IR::Inst* value);
|
||||
|
||||
powah::Context& code;
|
||||
std::array<HostLocInfo, 32> gprs;
|
||||
std::array<HostLocInfo, 32> fprs;
|
||||
std::array<HostLocInfo, 32> vprs;
|
||||
std::array<HostLocInfo, SpillCount> spills;
|
||||
};
|
||||
|
||||
template<> inline void RegLock<powah::GPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.gprs[value.index].locked = v;
|
||||
}
|
||||
template<> inline void RegLock<powah::FPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.fprs[value.index].locked = v;
|
||||
}
|
||||
template<> inline void RegLock<powah::VPR>::SetLock(bool v) noexcept {
|
||||
reg_alloc.vprs[value.index].locked = v;
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,20 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Dynarmic::Backend::PPC64 {
|
||||
|
||||
constexpr size_t SpillCount = 16;
|
||||
|
||||
struct alignas(16) StackLayout {
|
||||
std::array<u64, SpillCount> spill;
|
||||
};
|
||||
|
||||
static_assert(sizeof(StackLayout) % 16 == 0);
|
||||
|
||||
} // namespace Dynarmic::Backend::RV64
|
||||
@@ -1,71 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include <sys/mman.h>
|
||||
#include <powah_emit.hpp>
|
||||
#include "dynarmic/backend/ppc64/abi.h"
|
||||
#include "dynarmic/backend/ppc64/hostloc.h"
|
||||
#include "dynarmic/common/spin_lock.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
/*
|
||||
void acquire(atomic_flag* lock) {
|
||||
while(atomic_flag_test_and_set_explicit( lock, memory_order_acquire))
|
||||
;
|
||||
}
|
||||
*/
|
||||
void EmitSpinLockLock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void release(atomic_flag* lock) {
|
||||
atomic_flag_clear_explicit(lock, memory_order_release);
|
||||
}
|
||||
*/
|
||||
void EmitSpinLockUnlock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp) {
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct SpinLockImpl {
|
||||
void Initialize();
|
||||
powah::Context code;
|
||||
void* page = nullptr;
|
||||
void (*lock)(volatile int*);
|
||||
void (*unlock)(volatile int*);
|
||||
};
|
||||
|
||||
std::once_flag flag;
|
||||
SpinLockImpl impl;
|
||||
|
||||
void SpinLockImpl::Initialize() {
|
||||
page = mmap(nullptr, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
ASSERT(page != nullptr);
|
||||
code = powah::Context(page, 4096);
|
||||
lock = reinterpret_cast<void (*)(volatile int*)>(code.base);
|
||||
EmitSpinLockLock(code, Backend::PPC64::ABI_PARAM1, Backend::PPC64::ABI_PARAM2);
|
||||
code.BLR();
|
||||
unlock = reinterpret_cast<void (*)(volatile int*)>(code.base);
|
||||
EmitSpinLockUnlock(code, Backend::PPC64::ABI_PARAM1, Backend::PPC64::ABI_PARAM2);
|
||||
code.BLR();
|
||||
// TODO: free the page, rework the stupid spinlock API
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SpinLock::Lock() noexcept {
|
||||
std::call_once(flag, &SpinLockImpl::Initialize, impl);
|
||||
impl.lock(&storage);
|
||||
}
|
||||
|
||||
void SpinLock::Unlock() noexcept {
|
||||
std::call_once(flag, &SpinLockImpl::Initialize, impl);
|
||||
impl.unlock(&storage);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <powah_emit.hpp>
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
void EmitSpinLockLock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
|
||||
void EmitSpinLockUnlock(powah::Context& code, powah::GPR const ptr, powah::GPR const tmp);
|
||||
|
||||
} // namespace Dynarmic
|
||||
@@ -50,12 +50,6 @@ constexpr std::array RGBA_LUT{
|
||||
R | G | B | A, //
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
IR::Value Composite(TranslatorVisitor& v, Args... regs) {
|
||||
return v.ir.CompositeConstruct(v.F(regs)...);
|
||||
@@ -86,67 +80,53 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
|
||||
case 3: // 2D.LL
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
|
||||
info);
|
||||
case 4: // 2D.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
|
||||
{}, {}, {}, info);
|
||||
case 5: // 2D.LL.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
|
||||
v.F(reg_b + 1), v.F(reg_b), {}, info);
|
||||
case 6: // 2D.LZ.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
|
||||
zero, {}, info);
|
||||
case 7: // ARRAY_2D
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
return v.ir.ImageSampleImplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
{}, {}, {}, info);
|
||||
case 8: // ARRAY_2D.LZ
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
return v.ir.ImageSampleExplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
zero, {}, info);
|
||||
case 9: // ARRAY_2D.LZ.DC
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::ColorArray2D);
|
||||
info.is_depth.Assign(1);
|
||||
return v.ir.ImageSampleDrefExplicitLod(
|
||||
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
|
||||
v.F(reg_b + 1), zero, {}, info);
|
||||
case 10: // 3D
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color3D);
|
||||
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
|
||||
{}, info);
|
||||
case 11: // 3D.LZ
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::Color3D);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
|
||||
info);
|
||||
case 12: // CUBE
|
||||
CheckAlignment(reg_a, 2);
|
||||
info.type.Assign(TextureType::ColorCube);
|
||||
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
|
||||
{}, info);
|
||||
case 13: // CUBE.LL
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
info.type.Assign(TextureType::ColorCube);
|
||||
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
|
||||
v.F(reg_b + 1), {}, info);
|
||||
@@ -187,12 +167,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
|
||||
case 0:
|
||||
return texs.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(texs.dest_reg_a, 2);
|
||||
return texs.dest_reg_a + 1;
|
||||
case 2:
|
||||
return texs.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(texs.dest_reg_b, 2);
|
||||
return texs.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -34,12 +34,6 @@ union Encoding {
|
||||
BitField<36, 13, u64> cbuf_offset;
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
|
||||
const IR::U32 value{v.X(reg)};
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
|
||||
@@ -60,18 +54,15 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
|
||||
IR::Value coords;
|
||||
if (tld4s.aoffi != 0) {
|
||||
CheckAlignment(reg_a, 2);
|
||||
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
|
||||
IR::Value offset = MakeOffset(v, reg_b);
|
||||
if (tld4s.dc != 0) {
|
||||
CheckAlignment(reg_b, 2);
|
||||
IR::F32 dref = v.F(reg_b + 1);
|
||||
return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
|
||||
}
|
||||
return v.ir.ImageGather(handle, coords, offset, {}, info);
|
||||
}
|
||||
if (tld4s.dc != 0) {
|
||||
CheckAlignment(reg_a, 2);
|
||||
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
|
||||
IR::F32 dref = v.F(reg_b);
|
||||
return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
|
||||
@@ -86,12 +77,10 @@ IR::Reg RegStoreComponent32(u64 insn, size_t index) {
|
||||
case 0:
|
||||
return tlds4.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(tlds4.dest_reg_a, 2);
|
||||
return tlds4.dest_reg_a + 1;
|
||||
case 2:
|
||||
return tlds4.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(tlds4.dest_reg_b, 2);
|
||||
return tlds4.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -55,12 +55,6 @@ union Encoding {
|
||||
BitField<53, 4, u64> encoding;
|
||||
};
|
||||
|
||||
void CheckAlignment(IR::Reg reg, size_t alignment) {
|
||||
if (!IR::IsAligned(reg, alignment)) {
|
||||
throw NotImplementedException("Unaligned source register {}", reg);
|
||||
}
|
||||
}
|
||||
|
||||
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
|
||||
const IR::U32 value{v.X(reg)};
|
||||
return v.ir.CompositeConstruct(v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
|
||||
@@ -92,38 +86,31 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b));
|
||||
break;
|
||||
case 4:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
offsets = MakeOffset(v, reg_b);
|
||||
break;
|
||||
case 5:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
lod = v.X(reg_b);
|
||||
break;
|
||||
case 6:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
multisample = v.X(reg_b);
|
||||
break;
|
||||
case 7:
|
||||
CheckAlignment(reg_a, 2);
|
||||
texture_type = Shader::TextureType::Color3D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b));
|
||||
break;
|
||||
case 8: {
|
||||
CheckAlignment(reg_b, 2);
|
||||
const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))};
|
||||
texture_type = Shader::TextureType::ColorArray2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array);
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
CheckAlignment(reg_a, 2);
|
||||
CheckAlignment(reg_b, 2);
|
||||
texture_type = Shader::TextureType::Color2D;
|
||||
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
|
||||
lod = v.X(reg_b);
|
||||
@@ -166,12 +153,10 @@ IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
|
||||
case 0:
|
||||
return tlds.dest_reg_a;
|
||||
case 1:
|
||||
CheckAlignment(tlds.dest_reg_a, 2);
|
||||
return tlds.dest_reg_a + 1;
|
||||
case 2:
|
||||
return tlds.dest_reg_b;
|
||||
case 3:
|
||||
CheckAlignment(tlds.dest_reg_b, 2);
|
||||
return tlds.dest_reg_b + 1;
|
||||
}
|
||||
throw LogicError("Invalid store index {}", index);
|
||||
|
||||
@@ -169,11 +169,36 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
return mapping;
|
||||
}
|
||||
|
||||
struct PassthroughVertices {
|
||||
u32 count;
|
||||
u32 first;
|
||||
u32 stride;
|
||||
};
|
||||
|
||||
PassthroughVertices GetPassthroughVertices(InputTopology input_topology) {
|
||||
switch (input_topology) {
|
||||
case InputTopology::Points:
|
||||
return {1, 0, 1};
|
||||
case InputTopology::Lines:
|
||||
return {2, 0, 1};
|
||||
case InputTopology::LinesAdjacency:
|
||||
return {2, 1, 1};
|
||||
case InputTopology::Triangles:
|
||||
return {3, 0, 1};
|
||||
case InputTopology::TrianglesAdjacency:
|
||||
return {3, 0, 2};
|
||||
}
|
||||
return {3, 0, 1};
|
||||
}
|
||||
|
||||
void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
|
||||
const Shader::VaryingState& passthrough_mask,
|
||||
bool passthrough_position,
|
||||
std::optional<IR::Attribute> passthrough_layer_attr) {
|
||||
for (u32 i = 0; i < program.output_vertices; i++) {
|
||||
std::optional<IR::Attribute> passthrough_layer_attr,
|
||||
InputTopology input_topology) {
|
||||
const PassthroughVertices vertices{GetPassthroughVertices(input_topology)};
|
||||
for (u32 vertex = 0; vertex < vertices.count; vertex++) {
|
||||
const u32 i = vertices.first + vertex * vertices.stride;
|
||||
// Assign generics from input
|
||||
for (u32 j = 0; j < 32; j++) {
|
||||
if (!passthrough_mask.Generic(j)) {
|
||||
@@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program,
|
||||
ir.EndPrimitive(ir.Imm32(0));
|
||||
}
|
||||
|
||||
u32 GetOutputTopologyVertices(OutputTopology output_topology) {
|
||||
switch (output_topology) {
|
||||
case OutputTopology::PointList:
|
||||
return 1;
|
||||
case OutputTopology::LineStrip:
|
||||
return 2;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
|
||||
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (inst.GetOpcode() == IR::Opcode::Epilogue) {
|
||||
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
EmitGeometryPassthrough(
|
||||
ir, program, program.info.passthrough,
|
||||
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
|
||||
program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {},
|
||||
input_topology);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology) {
|
||||
HostTranslateInfo normalized_host_info{host_info};
|
||||
normalized_host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
@@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
}
|
||||
|
||||
if (!normalized_host_info.support_geometry_shader_passthrough) {
|
||||
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||
LowerGeometryPassthrough(program, normalized_host_info);
|
||||
program.output_vertices = GetPassthroughVertices(input_topology).count;
|
||||
LowerGeometryPassthrough(program, normalized_host_info, input_topology);
|
||||
program.is_geometry_passthrough = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -414,11 +432,12 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology) {
|
||||
Shader::OutputTopology output_topology,
|
||||
InputTopology input_topology) {
|
||||
IR::Program program;
|
||||
program.stage = Stage::Geometry;
|
||||
program.output_topology = output_topology;
|
||||
program.output_vertices = GetOutputTopologyVertices(output_topology);
|
||||
program.output_vertices = GetPassthroughVertices(input_topology).count;
|
||||
|
||||
program.is_geometry_passthrough = false;
|
||||
program.info.loads.mask = source_program.info.stores.mask;
|
||||
@@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||
|
||||
IR::IREmitter ir{*current_block};
|
||||
EmitGeometryPassthrough(ir, program, program.info.stores, true,
|
||||
source_program.info.emulated_layer);
|
||||
source_program.info.emulated_layer, input_topology);
|
||||
|
||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||
IR::IREmitter{*return_block}.Epilogue();
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace Shader::Maxwell {
|
||||
|
||||
[[nodiscard]] IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info);
|
||||
Flow::CFG& cfg, const HostTranslateInfo& host_info,
|
||||
InputTopology input_topology);
|
||||
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
@@ -32,6 +33,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf
|
||||
ObjectPool<IR::Block>& block_pool,
|
||||
const HostTranslateInfo& host_info,
|
||||
IR::Program& source_program,
|
||||
Shader::OutputTopology output_topology);
|
||||
Shader::OutputTopology output_topology,
|
||||
InputTopology input_topology);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
@@ -809,46 +810,46 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
|
||||
if (use_optimized_vertex_buffers) {
|
||||
auto& flags = maxwell3d->dirty.flags;
|
||||
u32 enabled_mask = enabled_vertex_buffers_mask;
|
||||
HostBindings<Buffer> bindings{};
|
||||
u32 last_index = (std::numeric_limits<u32>::max)();
|
||||
const auto flush_bindings = [&]() {
|
||||
if (bindings.buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
|
||||
runtime.BindVertexBuffers(bindings);
|
||||
bindings = HostBindings<Buffer>{};
|
||||
last_index = (std::numeric_limits<u32>::max)();
|
||||
};
|
||||
while (enabled_mask != 0) {
|
||||
const u32 index = std::countr_zero(enabled_mask);
|
||||
enabled_mask &= (enabled_mask - 1);
|
||||
const u32 enabled_mask = enabled_vertex_buffers_mask;
|
||||
bool any_dirty = false;
|
||||
u32 pending_mask = enabled_mask;
|
||||
while (pending_mask != 0) {
|
||||
const u32 index = std::countr_zero(pending_mask);
|
||||
pending_mask &= (pending_mask - 1);
|
||||
const Binding& binding = VertexBufferSlot(index);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
|
||||
if (!flags[Dirty::VertexBuffer0 + index]) {
|
||||
flush_bindings();
|
||||
continue;
|
||||
}
|
||||
any_dirty |= flags[Dirty::VertexBuffer0 + index];
|
||||
}
|
||||
if (enabled_mask == 0 || !any_dirty) {
|
||||
return;
|
||||
}
|
||||
const u32 min_index = static_cast<u32>(std::countr_zero(enabled_mask));
|
||||
const u32 max_index = 32u - static_cast<u32>(std::countl_zero(enabled_mask));
|
||||
HostBindings<Buffer> bindings{};
|
||||
bindings.min_index = min_index;
|
||||
bindings.max_index = max_index;
|
||||
for (u32 index = min_index; index < max_index; ++index) {
|
||||
flags[Dirty::VertexBuffer0 + index] = false;
|
||||
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
|
||||
if ((enabled_mask & (1u << index)) == 0) {
|
||||
bindings.buffers.push_back(&slot_buffers[NULL_BUFFER_ID]);
|
||||
bindings.offsets.push_back(0);
|
||||
bindings.sizes.push_back(0);
|
||||
bindings.strides.push_back(stride);
|
||||
continue;
|
||||
}
|
||||
const Binding& binding = VertexBufferSlot(index);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, binding.size);
|
||||
if (!bindings.buffers.empty() && index != last_index + 1) {
|
||||
flush_bindings();
|
||||
}
|
||||
if (bindings.buffers.empty()) {
|
||||
bindings.min_index = index;
|
||||
}
|
||||
bindings.buffers.push_back(&buffer);
|
||||
bindings.offsets.push_back(offset);
|
||||
bindings.sizes.push_back(binding.size);
|
||||
bindings.strides.push_back(stride);
|
||||
last_index = index;
|
||||
}
|
||||
flush_bindings();
|
||||
runtime.BindVertexBuffers(bindings);
|
||||
} else {
|
||||
HostBindings<typename P::Buffer> host_bindings;
|
||||
bool any_valid{false};
|
||||
|
||||
@@ -17,11 +17,13 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
||||
@@ -32,6 +34,8 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 430
|
||||
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_BUFFER 1
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
uvec3 dim;
|
||||
uint bytes_per_block_log2;
|
||||
|
||||
uvec3 origin;
|
||||
uint layer_stride;
|
||||
|
||||
uint block_size;
|
||||
uint x_shift;
|
||||
uint block_height;
|
||||
uint block_height_mask;
|
||||
} pc;
|
||||
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
||||
|
||||
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
||||
uint out_u32[];
|
||||
};
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return SwizzleTable(pos.y * 64u + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
switch (pc.bytes_per_block_log2) {
|
||||
case 2u:
|
||||
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
||||
case 3u:
|
||||
return uvec4(u64data[offset / 8u], 0u, 0u);
|
||||
case 4u:
|
||||
return u128data[offset / 16u];
|
||||
}
|
||||
return uvec4(0u);
|
||||
}
|
||||
|
||||
void main() {
|
||||
uvec3 coord = gl_GlobalInvocationID;
|
||||
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
||||
return;
|
||||
}
|
||||
|
||||
uvec3 pos = coord + pc.origin;
|
||||
pos.x <<= pc.bytes_per_block_log2;
|
||||
|
||||
uint swizzle = SwizzleOffset(pos.xy);
|
||||
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
||||
|
||||
uint offset = 0u;
|
||||
offset += pos.z * pc.layer_stride;
|
||||
offset += (block_y >> pc.block_height) * pc.block_size;
|
||||
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
||||
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
||||
offset += swizzle;
|
||||
|
||||
uvec4 texel = ReadTexel(offset);
|
||||
|
||||
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
||||
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
||||
uint out_idx = linear_index * words;
|
||||
|
||||
out_u32[out_idx] = texel.x;
|
||||
if (words > 1u) {
|
||||
out_u32[out_idx + 1u] = texel.y;
|
||||
}
|
||||
if (words > 2u) {
|
||||
out_u32[out_idx + 2u] = texel.z;
|
||||
out_u32[out_idx + 3u] = texel.w;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 430
|
||||
|
||||
#define BINDING_INPUT_BUFFER 0
|
||||
#define BINDING_OUTPUT_BUFFER 1
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
uvec3 dim;
|
||||
uint bytes_per_block_log2;
|
||||
|
||||
uvec3 origin;
|
||||
uint slice_size;
|
||||
|
||||
uint block_size;
|
||||
uint x_shift;
|
||||
uint block_height;
|
||||
uint block_height_mask;
|
||||
|
||||
uint block_depth;
|
||||
uint block_depth_mask;
|
||||
} pc;
|
||||
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
||||
|
||||
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
||||
uint out_u32[];
|
||||
};
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
|
||||
|
||||
const uint GOB_SIZE_X = 64;
|
||||
const uint GOB_SIZE_Y = 8;
|
||||
|
||||
const uint GOB_SIZE_X_SHIFT = 6;
|
||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
||||
|
||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
||||
|
||||
uint SwizzleTable(uint pos) {
|
||||
const uint t[8] = uint[](
|
||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
||||
);
|
||||
const uint i = pos >> 4;
|
||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
||||
return (h << 4) | (pos & 0xf);
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
pos = pos & SWIZZLE_MASK;
|
||||
return SwizzleTable(pos.y * 64u + pos.x);
|
||||
}
|
||||
|
||||
uvec4 ReadTexel(uint offset) {
|
||||
switch (pc.bytes_per_block_log2) {
|
||||
case 2u:
|
||||
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
||||
case 3u:
|
||||
return uvec4(u64data[offset / 8u], 0u, 0u);
|
||||
case 4u:
|
||||
return u128data[offset / 16u];
|
||||
}
|
||||
return uvec4(0u);
|
||||
}
|
||||
|
||||
void main() {
|
||||
uvec3 coord = gl_GlobalInvocationID;
|
||||
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
||||
return;
|
||||
}
|
||||
|
||||
uvec3 pos = coord + pc.origin;
|
||||
pos.x <<= pc.bytes_per_block_log2;
|
||||
|
||||
uint swizzle = SwizzleOffset(pos.xy);
|
||||
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
||||
|
||||
uint offset = 0u;
|
||||
offset += (pos.z >> pc.block_depth) * pc.slice_size;
|
||||
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
|
||||
offset += (block_y >> pc.block_height) * pc.block_size;
|
||||
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
||||
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
||||
offset += swizzle;
|
||||
|
||||
uvec4 texel = ReadTexel(offset);
|
||||
|
||||
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
||||
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
||||
uint out_idx = linear_index * words;
|
||||
|
||||
out_u32[out_idx] = texel.x;
|
||||
if (words > 1u) {
|
||||
out_u32[out_idx + 1u] = texel.y;
|
||||
}
|
||||
if (words > 2u) {
|
||||
out_u32[out_idx + 2u] = texel.z;
|
||||
out_u32[out_idx + 3u] = texel.w;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2D img_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
||||
gl_FragDepth = texelFetch(img_in, coord, 0).r;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
#extension GL_ARB_shader_stencil_export : require
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
||||
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
|
||||
vec2 scale;
|
||||
vec2 size;
|
||||
vec2 resize_factor;
|
||||
vec2 crop_offset;
|
||||
float edge_sharpness;
|
||||
};
|
||||
layout(location = 0) out highp vec2 texcoord;
|
||||
@@ -15,5 +16,5 @@ void main() {
|
||||
float x = float((gl_VertexIndex & 1) << 2);
|
||||
float y = float((gl_VertexIndex & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
|
||||
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
|
||||
texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
|
||||
vec2 scale;
|
||||
vec2 size;
|
||||
vec2 resize_factor;
|
||||
vec2 crop_offset;
|
||||
float edge_sharpness;
|
||||
};
|
||||
layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
layout( push_constant ) uniform constants {
|
||||
vec4 ViewportInfo[1];
|
||||
vec2 ResizeFactor;
|
||||
vec2 CropOffset;
|
||||
float EdgeSharpness;
|
||||
};
|
||||
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
||||
|
||||
@@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo
|
||||
}
|
||||
}
|
||||
|
||||
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return Shader::InputTopology::Points;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
return Shader::InputTopology::Lines;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
return Shader::InputTopology::LinesAdjacency;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
return Shader::InputTopology::TrianglesAdjacency;
|
||||
default:
|
||||
return Shader::InputTopology::Triangles;
|
||||
}
|
||||
}
|
||||
|
||||
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
|
||||
const Shader::IR::Program& program,
|
||||
const Shader::IR::Program* previous_program,
|
||||
@@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (key.gs_input_topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
info.input_topology = Shader::InputTopology::Points;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
info.input_topology = Shader::InputTopology::Lines;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::Triangles:
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
info.input_topology = Shader::InputTopology::Triangles;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
info.input_topology = Shader::InputTopology::LinesAdjacency;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
|
||||
break;
|
||||
}
|
||||
info.input_topology = MaxwellToInputTopology(key.gs_input_topology);
|
||||
info.glasm_use_storage_buffers = glasm_use_storage_buffers;
|
||||
return info;
|
||||
}
|
||||
@@ -483,8 +476,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
&& index == u32(Maxwell::ShaderType::Geometry);
|
||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||
auto topology = MaxwellToOutputTopology(key.gs_input_topology);
|
||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology);
|
||||
programs[index] =
|
||||
GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology,
|
||||
MaxwellToInputTopology(key.gs_input_topology));
|
||||
continue;
|
||||
}
|
||||
if (key.unique_hashes[index] == 0) {
|
||||
@@ -502,13 +497,15 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
MaxwellToInputTopology(key.gs_input_topology));
|
||||
|
||||
total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors);
|
||||
} else {
|
||||
// VertexB path when VertexA is present.
|
||||
auto& program_va{programs[0]};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
MaxwellToInputTopology(key.gs_input_topology))};
|
||||
total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
@@ -597,7 +594,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
env.Dump(hash, key.unique_hash);
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
Shader::InputTopology::Points)};
|
||||
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
||||
Shader::RuntimeInfo info;
|
||||
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
|
||||
@@ -519,7 +521,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
|
||||
VkFormat format, u32 base_level) {
|
||||
VkFormat format, u32 base_level,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
return device.CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -534,7 +537,7 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = base_level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
@@ -586,6 +589,10 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
||||
one_texture_set_layout.address(),
|
||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
||||
msaa_copy_depth_stencil_pipeline_layout(
|
||||
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
||||
two_textures_set_layout.address(),
|
||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
||||
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
|
||||
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
|
||||
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
|
||||
@@ -610,6 +617,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_depth_frag(
|
||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_depth_stencil_frag(
|
||||
device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
|
||||
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
|
||||
|
||||
@@ -920,10 +933,12 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
|
||||
ASSERT(copy.dst_subresource.num_layers == 1);
|
||||
vk::ImageView src_view =
|
||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
|
||||
static_cast<u32>(copy.src_subresource.base_level));
|
||||
static_cast<u32>(copy.src_subresource.base_level),
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vk::ImageView dst_view =
|
||||
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
||||
static_cast<u32>(copy.dst_subresource.base_level));
|
||||
static_cast<u32>(copy.dst_subresource.base_level),
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
||||
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
||||
const VkRect2D render_area{
|
||||
@@ -1191,7 +1206,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *blit_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1223,7 +1238,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *blit_depth_stencil_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1276,7 +1291,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *clear_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1332,7 +1347,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *clear_stencil_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1375,7 +1390,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *blit_msaa_color_pipelines.back();
|
||||
}
|
||||
|
||||
@@ -1413,10 +1428,202 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
bool copy_stencil) {
|
||||
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
|
||||
msaa_copy_resources.pop_front();
|
||||
}
|
||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
||||
const s32 scale_x = 1 << samples_x;
|
||||
const s32 scale_y = 1 << samples_y;
|
||||
const VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
|
||||
RenderPassKey renderpass_key{};
|
||||
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||
renderpass_key.depth_format = dst_format;
|
||||
renderpass_key.samples = samples;
|
||||
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
|
||||
const MSAACopyPipelineKey key{
|
||||
.renderpass = renderpass,
|
||||
.samples = samples,
|
||||
.msaa_to_non_msaa = false,
|
||||
};
|
||||
const VkPipeline pipeline = FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil);
|
||||
const VkPipelineLayout layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
|
||||
: *msaa_copy_pipeline_layout;
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkFormat src_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||
const VkFormat dst_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
||||
const VkImageAspectFlags attachment_aspect =
|
||||
VideoCore::Surface::GetFormatType(dst_format) ==
|
||||
VideoCore::Surface::SurfaceType::DepthStencil
|
||||
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
|
||||
: VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
for (const VideoCommon::ImageCopy& copy : copies) {
|
||||
ASSERT(copy.src_subresource.base_layer == 0);
|
||||
ASSERT(copy.src_subresource.num_layers == 1);
|
||||
ASSERT(copy.dst_subresource.base_layer == 0);
|
||||
ASSERT(copy.dst_subresource.num_layers == 1);
|
||||
vk::ImageView src_view =
|
||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
|
||||
static_cast<u32>(copy.src_subresource.base_level),
|
||||
VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
vk::ImageView src_stencil_view =
|
||||
copy_stencil ? MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
|
||||
static_cast<u32>(copy.src_subresource.base_level),
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||
: vk::ImageView{};
|
||||
vk::ImageView dst_view =
|
||||
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
||||
static_cast<u32>(copy.dst_subresource.base_level),
|
||||
attachment_aspect);
|
||||
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
||||
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
||||
const VkRect2D render_area{
|
||||
.offset = dst_offset,
|
||||
.extent = dst_extent,
|
||||
};
|
||||
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = renderpass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = dst_view.address(),
|
||||
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
|
||||
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
|
||||
.layers = 1,
|
||||
});
|
||||
const MSAACopyPushConstants push_constants{
|
||||
.dst_offset = {dst_offset.x, dst_offset.y},
|
||||
.src_offset = {copy.src_offset.x, copy.src_offset.y},
|
||||
.scale = {scale_x, scale_y},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkImageView src_stencil_handle = copy_stencil ? *src_stencil_view : VK_NULL_HANDLE;
|
||||
scheduler.Record([this, pipeline, layout, sampler, renderpass,
|
||||
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
|
||||
src_stencil_handle, src = src_image, dst = dst_image, render_area,
|
||||
attachment_aspect, push_constants](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageSubresourceRange src_range{
|
||||
.aspectMask = attachment_aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
const std::array pre_barriers{
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = src,
|
||||
.subresourceRange = src_range,
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = src_range,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
||||
0, nullptr, nullptr, pre_barriers);
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = renderpass,
|
||||
.framebuffer = framebuffer_handle,
|
||||
.renderArea = render_area,
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
const VkDescriptorSet descriptor_set =
|
||||
src_stencil_handle != VK_NULL_HANDLE
|
||||
? two_textures_descriptor_allocator.Commit()
|
||||
: one_texture_descriptor_allocator.Commit();
|
||||
if (src_stencil_handle != VK_NULL_HANDLE) {
|
||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
|
||||
src_stencil_handle);
|
||||
} else {
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
|
||||
}
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
const VkViewport viewport{
|
||||
.x = static_cast<float>(render_area.offset.x),
|
||||
.y = static_cast<float>(render_area.offset.y),
|
||||
.width = static_cast<float>(render_area.extent.width),
|
||||
.height = static_cast<float>(render_area.extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, render_area);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
const VkImageMemoryBarrier post_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = src_range,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, post_barrier);
|
||||
});
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.src_view = std::move(src_view),
|
||||
.dst_view = std::move(dst_view),
|
||||
.framebuffer = std::move(framebuffer),
|
||||
});
|
||||
if (copy_stencil) {
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.src_view = std::move(src_stencil_view),
|
||||
.dst_view = vk::ImageView{},
|
||||
.framebuffer = vk::Framebuffer{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
|
||||
const auto it = std::ranges::find(msaa_copy_keys, key);
|
||||
if (it != msaa_copy_keys.end()) {
|
||||
@@ -1458,10 +1665,83 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
}, device.StaticPipelineCache()));
|
||||
return *msaa_copy_pipelines.back();
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
||||
bool copy_stencil) {
|
||||
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
|
||||
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
|
||||
const auto it = std::ranges::find(keys, key);
|
||||
if (it != keys.end()) {
|
||||
return *pipelines[std::distance(keys.begin(), it)];
|
||||
}
|
||||
keys.push_back(key);
|
||||
const std::array stages =
|
||||
MakeStages(*clear_color_vert, copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
|
||||
: *convert_non_msaa_to_msaa_depth_frag);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = key.samples,
|
||||
.sampleShadingEnable = VK_TRUE,
|
||||
.minSampleShading = 1.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
|
||||
.failOp = VK_STENCIL_OP_REPLACE,
|
||||
.passOp = VK_STENCIL_OP_REPLACE,
|
||||
.depthFailOp = VK_STENCIL_OP_REPLACE,
|
||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.compareMask = 0xFF,
|
||||
.writeMask = 0xFF,
|
||||
.reference = 0,
|
||||
};
|
||||
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthTestEnable = VK_TRUE,
|
||||
.depthWriteEnable = VK_TRUE,
|
||||
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.depthBoundsTestEnable = VK_FALSE,
|
||||
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
|
||||
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
||||
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
|
||||
GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &depth_stencil_ci,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
|
||||
: *msaa_copy_pipeline_layout,
|
||||
.renderPass = key.renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}, device.StaticPipelineCache()));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||
ConvertPipeline(pipeline, renderpass, false);
|
||||
}
|
||||
@@ -1499,7 +1779,7 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}, device.StaticPipelineCache());
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
|
||||
@@ -1542,7 +1822,7 @@ void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass rende
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}, device.StaticPipelineCache());
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -116,6 +116,11 @@ public:
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
|
||||
|
||||
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil);
|
||||
|
||||
private:
|
||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view);
|
||||
@@ -131,6 +136,9 @@ private:
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
|
||||
const BlitDepthStencilPipelineKey& key);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
|
||||
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
||||
bool copy_stencil);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
||||
bool resolve_stencil);
|
||||
@@ -162,6 +170,7 @@ private:
|
||||
vk::PipelineLayout two_textures_pipeline_layout;
|
||||
vk::PipelineLayout clear_color_pipeline_layout;
|
||||
vk::PipelineLayout msaa_copy_pipeline_layout;
|
||||
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
|
||||
vk::ShaderModule full_screen_vert;
|
||||
vk::ShaderModule blit_color_to_color_frag;
|
||||
vk::ShaderModule blit_color_msaa_frag;
|
||||
@@ -180,6 +189,8 @@ private:
|
||||
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
|
||||
vk::Sampler linear_sampler;
|
||||
vk::Sampler nearest_sampler;
|
||||
|
||||
@@ -193,6 +204,10 @@ private:
|
||||
std::vector<vk::Pipeline> clear_stencil_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
|
||||
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
|
||||
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
|
||||
std::vector<VkRenderPass> resolve_depth_keys;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/settings.h"
|
||||
@@ -17,7 +19,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using PushConstants = std::array<u32, 4 + 2 + 1>;
|
||||
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
|
||||
|
||||
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
||||
: m_memory_allocator{memory_allocator}
|
||||
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
|
||||
|
||||
const f32 input_image_width = f32(input_image_extent.width);
|
||||
const f32 input_image_height = f32(input_image_extent.height);
|
||||
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
||||
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
||||
// expected [0, 2]
|
||||
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
|
||||
const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
|
||||
const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
|
||||
static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
|
||||
const f32 edge_sharpness =
|
||||
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
|
||||
|
||||
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
|
||||
// p * input = [0,1024], [0,768]
|
||||
// layout( push_constant ) uniform constants {
|
||||
// highp vec4 ViewportInfo[1];
|
||||
// highp vec2 ResizeFactor;
|
||||
// highp vec2 CropOffset;
|
||||
// highp float EdgeSharpness;
|
||||
// };
|
||||
PushConstants viewport_con{};
|
||||
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width));
|
||||
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height));
|
||||
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width));
|
||||
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height));
|
||||
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width);
|
||||
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height);
|
||||
viewport_con[6] = std::bit_cast<u32>(sharpening);
|
||||
viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
|
||||
viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
|
||||
viewport_con[2] = std::bit_cast<u32>(input_image_width);
|
||||
viewport_con[3] = std::bit_cast<u32>(input_image_height);
|
||||
viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
|
||||
viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
|
||||
viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
|
||||
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
|
||||
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
|
||||
|
||||
UploadImages(device, scheduler);
|
||||
UpdateDescriptorSets(device, source_image_view, image_index);
|
||||
|
||||
@@ -491,7 +491,7 @@ static vk::Pipeline CreateWrappedPipelineImpl(
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = 0,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}, device.StaticPipelineCache());
|
||||
}
|
||||
|
||||
vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
@@ -268,7 +270,7 @@ ComputePass::ComputePass(const Device& device_, Scheduler& scheduler, Descriptor
|
||||
.layout = *layout,
|
||||
.basePipelineHandle = {},
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}, device.StaticPipelineCache());
|
||||
}
|
||||
|
||||
ComputePass::~ComputePass() = default;
|
||||
@@ -872,4 +874,479 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
|
||||
|
||||
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
|
||||
std::array<u32, 3> dim;
|
||||
u32 bytes_per_block_log2;
|
||||
std::array<u32, 3> origin;
|
||||
u32 layer_stride;
|
||||
u32 block_size;
|
||||
u32 x_shift;
|
||||
u32 block_height;
|
||||
u32 block_height_mask;
|
||||
};
|
||||
static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL2D_BINDINGS{{
|
||||
{
|
||||
.binding = BL2D_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = BL2D_BINDING_OUTPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL2D_TEMPLATE{{
|
||||
{
|
||||
.dstBinding = BL2D_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr DescriptorBankInfo BL2D_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 2,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 2,
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
StagingBufferPool& staging_buffer_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
|
||||
BL2D_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
|
||||
BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
|
||||
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
||||
|
||||
bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info) {
|
||||
if (info.type != VideoCommon::ImageType::e2D) {
|
||||
return false;
|
||||
}
|
||||
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
||||
return false;
|
||||
}
|
||||
if (info.num_samples > 1) {
|
||||
return false;
|
||||
}
|
||||
if (VideoCore::Surface::IsPixelFormatASTC(info.format) ||
|
||||
VideoCore::Surface::IsPixelFormatBCn(info.format)) {
|
||||
return false;
|
||||
}
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
||||
if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
|
||||
return false;
|
||||
}
|
||||
return VideoCore::Surface::DefaultBlockWidth(info.format) == 1 &&
|
||||
VideoCore::Surface::DefaultBlockHeight(info.format) == 1;
|
||||
}
|
||||
|
||||
void BlockLinearUnswizzle2DPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& swizzled,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
if (swizzles.empty()) {
|
||||
return;
|
||||
}
|
||||
const VideoCommon::SwizzleParameters& sw = swizzles.front();
|
||||
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
|
||||
|
||||
const u32 width = sw.num_tiles.width;
|
||||
const u32 height = sw.num_tiles.height;
|
||||
const u32 depth = image.info.resources.layers;
|
||||
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
||||
const VkDeviceSize output_size =
|
||||
static_cast<VkDeviceSize>(width) * height * depth * bytes_per_block;
|
||||
|
||||
const StagingBufferRef output =
|
||||
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
|
||||
|
||||
BlockLinearUnswizzle2DPushConstants pc{};
|
||||
pc.dim = {width, height, depth};
|
||||
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
||||
pc.origin = params.origin;
|
||||
pc.layer_stride = params.layer_stride;
|
||||
pc.block_size = params.block_size;
|
||||
pc.x_shift = params.x_shift;
|
||||
pc.block_height = params.block_height;
|
||||
pc.block_height_mask = params.block_height_mask;
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
|
||||
image.guest_size_bytes - sw.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
|
||||
|
||||
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
|
||||
const u32 gx = Common::DivCeil(width, 16u);
|
||||
const u32 gy = Common::DivCeil(height, 8u);
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
|
||||
const VkBuffer out_buffer = output.buffer;
|
||||
const VkDeviceSize out_offset = output.offset;
|
||||
const VkImage dst_image = image.Handle();
|
||||
const VkImageAspectFlags aspect = image.AspectMask();
|
||||
|
||||
scheduler.Record([this, set, descriptor_data, pc, gx, gy, depth, output_size, out_buffer,
|
||||
out_offset, dst_image, aspect, width, height,
|
||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
||||
cmdbuf.Dispatch(gx, gy, depth);
|
||||
|
||||
const VkBufferMemoryBarrier buffer_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = out_buffer,
|
||||
.offset = out_offset,
|
||||
.size = output_size,
|
||||
};
|
||||
const VkImageMemoryBarrier pre_copy{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
|
||||
: VK_ACCESS_NONE),
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
|
||||
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
|
||||
|
||||
const VkBufferImageCopy copy{
|
||||
.bufferOffset = out_offset,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = depth,
|
||||
},
|
||||
.imageOffset = {0, 0, 0},
|
||||
.imageExtent = {width, height, 1},
|
||||
};
|
||||
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||
|
||||
const VkImageMemoryBarrier post_copy{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, {}, {}, post_copy);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
|
||||
|
||||
struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
|
||||
std::array<u32, 3> dim;
|
||||
u32 bytes_per_block_log2;
|
||||
std::array<u32, 3> origin;
|
||||
u32 slice_size;
|
||||
u32 block_size;
|
||||
u32 x_shift;
|
||||
u32 block_height;
|
||||
u32 block_height_mask;
|
||||
u32 block_depth;
|
||||
u32 block_depth_mask;
|
||||
};
|
||||
static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL3DB_BINDINGS{{
|
||||
{
|
||||
.binding = BL3DB_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = BL3DB_BINDING_OUTPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL3DB_TEMPLATE{{
|
||||
{
|
||||
.dstBinding = BL3DB_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr DescriptorBankInfo BL3DB_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 2,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 2,
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
StagingBufferPool& staging_buffer_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
|
||||
BL3DB_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DBufferPushConstants)>,
|
||||
BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
|
||||
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
|
||||
|
||||
bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
|
||||
const VideoCommon::ImageInfo& info) {
|
||||
if (info.type != VideoCommon::ImageType::e3D) {
|
||||
return false;
|
||||
}
|
||||
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
||||
return false;
|
||||
}
|
||||
if (info.num_samples > 1) {
|
||||
return false;
|
||||
}
|
||||
if (info.size.depth <= 1) {
|
||||
return false;
|
||||
}
|
||||
if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
|
||||
return false;
|
||||
}
|
||||
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
|
||||
return false;
|
||||
}
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
||||
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
|
||||
}
|
||||
|
||||
void BlockLinearUnswizzle3DBufferPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& swizzled,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
if (swizzles.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VideoCommon::SwizzleParameters& sw = swizzles.front();
|
||||
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
|
||||
|
||||
const u32 blocks_x = sw.num_tiles.width;
|
||||
const u32 blocks_y = sw.num_tiles.height;
|
||||
const u32 blocks_z = sw.num_tiles.depth;
|
||||
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
||||
const VkDeviceSize output_size =
|
||||
static_cast<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block;
|
||||
|
||||
const StagingBufferRef output =
|
||||
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
|
||||
|
||||
BlockLinearUnswizzle3DBufferPushConstants pc{};
|
||||
pc.dim = {blocks_x, blocks_y, blocks_z};
|
||||
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
||||
pc.origin = params.origin;
|
||||
pc.slice_size = params.slice_size;
|
||||
pc.block_size = params.block_size;
|
||||
pc.x_shift = params.x_shift;
|
||||
pc.block_height = params.block_height;
|
||||
pc.block_height_mask = params.block_height_mask;
|
||||
pc.block_depth = params.block_depth;
|
||||
pc.block_depth_mask = params.block_depth_mask;
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
|
||||
image.guest_size_bytes - sw.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
|
||||
|
||||
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
|
||||
const u32 gx = Common::DivCeil(blocks_x, 8u);
|
||||
const u32 gy = Common::DivCeil(blocks_y, 8u);
|
||||
const u32 gz = Common::DivCeil(blocks_z, 4u);
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
|
||||
const VkBuffer out_buffer = output.buffer;
|
||||
const VkDeviceSize out_offset = output.offset;
|
||||
const VkImage dst_image = image.Handle();
|
||||
const VkImageAspectFlags aspect = image.AspectMask();
|
||||
const VkExtent3D extent{
|
||||
.width = image.info.size.width,
|
||||
.height = image.info.size.height,
|
||||
.depth = image.info.size.depth,
|
||||
};
|
||||
|
||||
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
|
||||
out_offset, dst_image, aspect, extent,
|
||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
||||
cmdbuf.Dispatch(gx, gy, gz);
|
||||
|
||||
const VkBufferMemoryBarrier buffer_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = out_buffer,
|
||||
.offset = out_offset,
|
||||
.size = output_size,
|
||||
};
|
||||
const VkImageMemoryBarrier pre_copy{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_READ_BIT
|
||||
: VK_ACCESS_NONE),
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
(is_initialized ? vk::PIPELINE_STAGE_GRAPHICS_COMPUTE
|
||||
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
|
||||
|
||||
const VkBufferImageCopy copy{
|
||||
.bufferOffset = out_offset,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.imageOffset = {0, 0, 0},
|
||||
.imageExtent = extent,
|
||||
};
|
||||
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||
|
||||
const VkImageMemoryBarrier post_copy{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, {}, {}, post_copy);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -164,4 +164,42 @@ private:
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
|
||||
DescriptorPool& descriptor_pool_,
|
||||
StagingBufferPool& staging_buffer_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzle2DPass();
|
||||
|
||||
[[nodiscard]] static bool IsSupported(const VideoCommon::ImageInfo& info);
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle3DBufferPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
StagingBufferPool& staging_buffer_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzle3DBufferPass();
|
||||
|
||||
[[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -140,6 +140,8 @@ RenderPassKey MakeRenderPassKey(const FixedPipelineState& state, const Device& d
|
||||
});
|
||||
key.resolve_color =
|
||||
key.samples != VK_SAMPLE_COUNT_1_BIT && has_color && device.IsTiler();
|
||||
key.resolve_depth_stencil = key.samples != VK_SAMPLE_COUNT_1_BIT && device.IsTiler() &&
|
||||
SupportsDepthStencilResolve(device, key.depth_format);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ using VideoCommon::GenericEnvironment;
|
||||
using VideoCommon::GraphicsEnvironment;
|
||||
|
||||
constexpr u32 CACHE_VERSION = 18;
|
||||
constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128;
|
||||
constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30;
|
||||
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
|
||||
|
||||
template <typename Container>
|
||||
@@ -150,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde
|
||||
return Shader::AttributeType::Disabled;
|
||||
}
|
||||
|
||||
Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return Shader::InputTopology::Points;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
return Shader::InputTopology::Lines;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
return Shader::InputTopology::LinesAdjacency;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
return Shader::InputTopology::TrianglesAdjacency;
|
||||
default:
|
||||
return Shader::InputTopology::Triangles;
|
||||
}
|
||||
}
|
||||
|
||||
Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> programs,
|
||||
const GraphicsPipelineCacheKey& key,
|
||||
const Shader::IR::Program& program,
|
||||
@@ -268,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (key.state.topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
info.input_topology = Shader::InputTopology::Points;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
info.input_topology = Shader::InputTopology::Lines;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::Triangles:
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
info.input_topology = Shader::InputTopology::Triangles;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
info.input_topology = Shader::InputTopology::LinesAdjacency;
|
||||
break;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
|
||||
break;
|
||||
}
|
||||
info.input_topology = MaxwellToInputTopology(key.state.topology);
|
||||
info.force_early_z = key.state.early_z != 0;
|
||||
info.y_negate = key.state.y_negate != 0;
|
||||
return info;
|
||||
@@ -699,6 +694,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
if (use_vulkan_pipeline_cache) {
|
||||
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
||||
CACHE_VERSION);
|
||||
size_t size = 0;
|
||||
vulkan_pipeline_cache.Read(&size, nullptr);
|
||||
last_cache_size.store(size, std::memory_order_relaxed);
|
||||
last_flush = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
if (state.statistics) {
|
||||
@@ -706,6 +705,35 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCache::QueueVulkanPipelineCacheFlush() {
|
||||
if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) {
|
||||
return;
|
||||
}
|
||||
if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) {
|
||||
return;
|
||||
}
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto megabytes = last_cache_size.load(std::memory_order_relaxed) / (1024 * 1024);
|
||||
const std::chrono::seconds interval{
|
||||
std::max<size_t>(VULKAN_CACHE_FLUSH_MIN_SECONDS, megabytes)};
|
||||
if (last_flush.time_since_epoch().count() != 0 && now - last_flush < interval) {
|
||||
return;
|
||||
}
|
||||
if (flush_in_flight.exchange(true, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
pipelines_since_flush = 0;
|
||||
last_flush = now;
|
||||
serialization_thread.QueueWork([this] {
|
||||
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
||||
CACHE_VERSION);
|
||||
size_t size = 0;
|
||||
vulkan_pipeline_cache.Read(&size, nullptr);
|
||||
last_cache_size.store(size, std::memory_order_relaxed);
|
||||
flush_in_flight.store(false, std::memory_order_release);
|
||||
});
|
||||
}
|
||||
|
||||
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
|
||||
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
|
||||
auto& pipeline{pair->second};
|
||||
@@ -744,7 +772,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
|
||||
bool build_in_parallel) try {
|
||||
auto hash = key.Hash();
|
||||
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
||||
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
|
||||
size_t env_index{0};
|
||||
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
|
||||
const bool uses_vertex_a{key.unique_hashes[0] != 0};
|
||||
@@ -758,8 +786,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
index == static_cast<u32>(Maxwell::ShaderType::Geometry);
|
||||
if (key.unique_hashes[index] == 0 && is_emulated_stage) {
|
||||
auto topology = MaxwellToOutputTopology(key.state.topology);
|
||||
programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology);
|
||||
programs[index] =
|
||||
GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
|
||||
*layer_source_program, topology,
|
||||
MaxwellToInputTopology(key.state.topology));
|
||||
continue;
|
||||
}
|
||||
if (key.unique_hashes[index] == 0) {
|
||||
@@ -772,11 +802,13 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
MaxwellToInputTopology(key.state.topology));
|
||||
} else {
|
||||
// VertexB path when VertexA is present.
|
||||
auto& program_va{programs[0]};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
MaxwellToInputTopology(key.state.topology))};
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
|
||||
@@ -880,6 +912,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
||||
}
|
||||
SerializePipeline(key, env_ptrs, pipeline_cache_filename, CACHE_VERSION);
|
||||
});
|
||||
QueueVulkanPipelineCacheFlush();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -899,6 +932,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_},
|
||||
pipeline_cache_filename, CACHE_VERSION);
|
||||
});
|
||||
QueueVulkanPipelineCacheFlush();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -911,7 +945,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_INFO(Render_Vulkan, "{:#016x}", hash);
|
||||
LOG_DEBUG(Render_Vulkan, "{:#016x}", hash);
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
|
||||
@@ -920,7 +954,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
env.Dump(hash, key.unique_hash);
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info,
|
||||
Shader::InputTopology::Points)};
|
||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||
const bool needs_shared_mem_clamp =
|
||||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
@@ -144,6 +146,8 @@ private:
|
||||
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
|
||||
u32 expected_cache_version);
|
||||
|
||||
void QueueVulkanPipelineCacheFlush();
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
DescriptorPool& descriptor_pool;
|
||||
@@ -171,6 +175,10 @@ private:
|
||||
|
||||
std::filesystem::path vulkan_pipeline_cache_filename;
|
||||
vk::PipelineCache vulkan_pipeline_cache;
|
||||
size_t pipelines_since_flush{};
|
||||
std::chrono::steady_clock::time_point last_flush{};
|
||||
std::atomic<size_t> last_cache_size{};
|
||||
std::atomic_bool flush_in_flight{};
|
||||
|
||||
Common::ThreadWorker workers;
|
||||
Common::ThreadWorker serialization_thread;
|
||||
|
||||
@@ -725,6 +725,9 @@ public:
|
||||
if (!device.IsExtTransformFeedbackSupported()) {
|
||||
return;
|
||||
}
|
||||
if (!scheduler.IsRenderPassActive()) {
|
||||
return;
|
||||
}
|
||||
FlushBeginTFB();
|
||||
has_started = true;
|
||||
}
|
||||
@@ -741,6 +744,9 @@ public:
|
||||
if (has_flushed_end_pending) {
|
||||
if (scheduler.IsRenderPassActive()) {
|
||||
FlushEndTFB();
|
||||
} else {
|
||||
has_flushed_end_pending = false;
|
||||
has_started = false;
|
||||
}
|
||||
}
|
||||
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
|
||||
|
||||
@@ -1286,33 +1286,42 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
regs.zeta.format == Tegra::DepthFormat::X8Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::S8Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::V8Z24_UNORM;
|
||||
const bool forces_unorm_representation = device.IsExtDepthBiasControlSupported();
|
||||
|
||||
if (is_d24 && !device.SupportsD24DepthBuffer()) {
|
||||
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
|
||||
static constexpr double GUEST_TO_HOST_UNORM_BITS =
|
||||
static_cast<double>(1ULL << (32 - 24));
|
||||
|
||||
static constexpr const u64* start = NEEDS_D24;
|
||||
static constexpr const u64* end = NEEDS_D24 + length;
|
||||
if (forces_unorm_representation) {
|
||||
units = static_cast<float>(static_cast<double>(units) * GUEST_TO_HOST_UNORM_BITS);
|
||||
} else {
|
||||
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
|
||||
|
||||
const u64* it = std::find(start, end, program_id);
|
||||
static constexpr const u64* start = NEEDS_D24;
|
||||
static constexpr const u64* end = NEEDS_D24 + length;
|
||||
|
||||
if (it != end) {
|
||||
// the base formulas can be obtained from here:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
|
||||
const double rescale_factor =
|
||||
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
|
||||
const u64* it = std::find(start, end, program_id);
|
||||
|
||||
if (it != end) {
|
||||
// the base formulas can be obtained from here:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
|
||||
const double rescale_factor =
|
||||
GUEST_TO_HOST_UNORM_BITS / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
|
||||
factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
|
||||
if (device.IsExtDepthBiasControlSupported()) {
|
||||
static VkDepthBiasRepresentationInfoEXT bias_info{
|
||||
factor = regs.slope_scale_depth_bias,
|
||||
forces_unorm_representation, this](vk::CommandBuffer cmdbuf) {
|
||||
if (forces_unorm_representation) {
|
||||
const VkDepthBiasRepresentationInfoEXT bias_info{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.depthBiasRepresentation =
|
||||
VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
|
||||
.depthBiasExact = VK_FALSE,
|
||||
.depthBiasExact = static_cast<VkBool32>(device.HasExactDepthBiasControl()),
|
||||
};
|
||||
|
||||
cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
|
||||
@@ -1336,8 +1345,14 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
if (!state_tracker.TouchDepthBounds()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
|
||||
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
|
||||
float min = regs.depth_bounds[0];
|
||||
float max = regs.depth_bounds[1];
|
||||
if (!device.IsExtDepthRangeUnrestrictedSupported()) {
|
||||
min = std::clamp(min, 0.0f, 1.0f);
|
||||
max = std::clamp(max, 0.0f, 1.0f);
|
||||
}
|
||||
scheduler.Record(
|
||||
[min, max](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
|
||||
@@ -65,8 +65,61 @@ using VideoCore::Surface::SurfaceType;
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
}
|
||||
|
||||
struct ResolveAspects {
|
||||
bool depth;
|
||||
bool stencil;
|
||||
};
|
||||
|
||||
struct ResolveModes {
|
||||
VkResolveModeFlagBits depth;
|
||||
VkResolveModeFlagBits stencil;
|
||||
};
|
||||
|
||||
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
|
||||
const SurfaceType surface_type = GetSurfaceType(format);
|
||||
return ResolveAspects{
|
||||
.depth = surface_type == SurfaceType::Depth ||
|
||||
surface_type == SurfaceType::DepthStencil,
|
||||
.stencil = surface_type == SurfaceType::Stencil ||
|
||||
surface_type == SurfaceType::DepthStencil,
|
||||
};
|
||||
}
|
||||
|
||||
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
|
||||
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
|
||||
|
||||
const ResolveAspects aspects = GetResolveAspects(format);
|
||||
ResolveModes modes{
|
||||
.depth = VK_RESOLVE_MODE_NONE,
|
||||
.stencil = VK_RESOLVE_MODE_NONE,
|
||||
};
|
||||
if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) {
|
||||
modes.depth = mode;
|
||||
}
|
||||
if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) {
|
||||
modes.stencil = mode;
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
|
||||
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
|
||||
return false;
|
||||
}
|
||||
const ResolveAspects aspects = GetResolveAspects(depth_format);
|
||||
if (!aspects.depth && !aspects.stencil) {
|
||||
return false;
|
||||
}
|
||||
const ResolveModes modes = PickResolveModes(device, depth_format);
|
||||
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
|
||||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
|
||||
return false;
|
||||
}
|
||||
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
|
||||
}
|
||||
|
||||
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
||||
|
||||
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
@@ -75,7 +128,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
if (!is_new) {
|
||||
return *pair->second;
|
||||
}
|
||||
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
|
||||
static constexpr size_t MAX_ATTACHMENTS =
|
||||
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
|
||||
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
|
||||
std::array<VkAttachmentReference, 8> references{};
|
||||
u32 num_attachments{};
|
||||
u32 num_colors{};
|
||||
@@ -133,6 +188,21 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
|
||||
key.samples != VK_SAMPLE_COUNT_1_BIT &&
|
||||
SupportsDepthStencilResolve(*device, key.depth_format);
|
||||
VkAttachmentReference depth_resolve_reference{};
|
||||
if (do_resolve_depth_stencil) {
|
||||
depth_resolve_reference = VkAttachmentReference{
|
||||
.attachment = static_cast<u32>(descriptions.size()),
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
VkAttachmentDescription resolve_desc =
|
||||
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
|
||||
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
descriptions.push_back(resolve_desc);
|
||||
}
|
||||
const VkSubpassDescription subpass{
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
@@ -145,18 +215,117 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
.preserveAttachmentCount = 0,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
const VkSubpassDependency dependency{
|
||||
.srcSubpass = 0, // Current subpass
|
||||
.dstSubpass = 0, // Same subpass (self-dependency)
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
||||
const VkSubpassDependency counter_resume_dependency{
|
||||
.srcSubpass = 0,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
|
||||
.dependencyFlags = 0,
|
||||
};
|
||||
const bool can_resume_transform_feedback = device->IsExtTransformFeedbackSupported();
|
||||
|
||||
if (device->IsKhrCreateRenderPass2Supported()) {
|
||||
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
|
||||
for (const VkAttachmentDescription& description : descriptions) {
|
||||
descriptions2.push_back(VkAttachmentDescription2{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = description.flags,
|
||||
.format = description.format,
|
||||
.samples = description.samples,
|
||||
.loadOp = description.loadOp,
|
||||
.storeOp = description.storeOp,
|
||||
.stencilLoadOp = description.stencilLoadOp,
|
||||
.stencilStoreOp = description.stencilStoreOp,
|
||||
.initialLayout = description.initialLayout,
|
||||
.finalLayout = description.finalLayout,
|
||||
});
|
||||
}
|
||||
const auto promote = [](const VkAttachmentReference& reference) {
|
||||
return VkAttachmentReference2{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
|
||||
.pNext = nullptr,
|
||||
.attachment = reference.attachment,
|
||||
.layout = reference.layout,
|
||||
.aspectMask = 0,
|
||||
};
|
||||
};
|
||||
std::array<VkAttachmentReference2, 8> references2{};
|
||||
std::array<VkAttachmentReference2, 8> resolve_references2{};
|
||||
for (size_t index = 0; index < references.size(); ++index) {
|
||||
references2[index] = promote(references[index]);
|
||||
resolve_references2[index] = promote(resolve_references[index]);
|
||||
}
|
||||
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
|
||||
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
|
||||
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
|
||||
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
|
||||
.pNext = nullptr,
|
||||
.depthResolveMode = resolve_modes.depth,
|
||||
.stencilResolveMode = resolve_modes.stencil,
|
||||
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
|
||||
};
|
||||
const VkSubpassDescription2 subpass2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
|
||||
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.viewMask = 0,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = num_attachments,
|
||||
.pColorAttachments = references2.data(),
|
||||
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
|
||||
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
|
||||
.preserveAttachmentCount = 0,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
const VkMemoryBarrier2 counter_resume_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
|
||||
.pNext = nullptr,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
|
||||
.srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
|
||||
.dstAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
|
||||
};
|
||||
VkSubpassDependency2 counter_resume_dependency2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
|
||||
.pNext = nullptr,
|
||||
.srcSubpass = counter_resume_dependency.srcSubpass,
|
||||
.dstSubpass = counter_resume_dependency.dstSubpass,
|
||||
.srcStageMask = counter_resume_dependency.srcStageMask,
|
||||
.dstStageMask = counter_resume_dependency.dstStageMask,
|
||||
.srcAccessMask = counter_resume_dependency.srcAccessMask,
|
||||
.dstAccessMask = counter_resume_dependency.dstAccessMask,
|
||||
.dependencyFlags = counter_resume_dependency.dependencyFlags,
|
||||
.viewOffset = 0,
|
||||
};
|
||||
if (device->HasSynchronization2()) {
|
||||
counter_resume_dependency2.pNext = &counter_resume_barrier;
|
||||
counter_resume_dependency2.srcStageMask = 0;
|
||||
counter_resume_dependency2.dstStageMask = 0;
|
||||
counter_resume_dependency2.srcAccessMask = 0;
|
||||
counter_resume_dependency2.dstAccessMask = 0;
|
||||
}
|
||||
pair->second = device->GetLogical().CreateRenderPass2({
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.attachmentCount = static_cast<u32>(descriptions2.size()),
|
||||
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass2,
|
||||
.dependencyCount = can_resume_transform_feedback ? 1u : 0u,
|
||||
.pDependencies = can_resume_transform_feedback ? &counter_resume_dependency2 : nullptr,
|
||||
.correlatedViewMaskCount = 0,
|
||||
.pCorrelatedViewMasks = nullptr,
|
||||
});
|
||||
return *pair->second;
|
||||
}
|
||||
|
||||
pair->second = device->GetLogical().CreateRenderPass({
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -165,8 +334,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency,
|
||||
.dependencyCount = can_resume_transform_feedback ? 1u : 0u,
|
||||
.pDependencies = can_resume_transform_feedback ? &counter_resume_dependency : nullptr,
|
||||
});
|
||||
return *pair->second;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mutex>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/container_hash.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -21,6 +22,7 @@ struct RenderPassKey {
|
||||
VideoCore::Surface::PixelFormat depth_format;
|
||||
VkSampleCountFlagBits samples;
|
||||
bool resolve_color;
|
||||
bool resolve_depth_stencil;
|
||||
u32 color_clear_mask;
|
||||
bool depth_stencil_clear;
|
||||
u32 color_discard_mask;
|
||||
@@ -31,17 +33,27 @@ struct RenderPassKey {
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Vulkan::RenderPassKey> {
|
||||
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
|
||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
|
||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
|
||||
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
|
||||
|
||||
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
||||
size_t value = static_cast<size_t>(key.depth_format) << 48;
|
||||
value ^= static_cast<size_t>(key.samples) << 52;
|
||||
value ^= static_cast<size_t>(key.resolve_color) << 63;
|
||||
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
|
||||
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
|
||||
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
|
||||
for (size_t i = 0; i < key.color_formats.size(); ++i) {
|
||||
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
|
||||
u64 formats = 0;
|
||||
for (size_t index = 0; index < key.color_formats.size(); ++index) {
|
||||
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
|
||||
}
|
||||
return value;
|
||||
const u64 state = static_cast<u64>(key.depth_format) |
|
||||
(static_cast<u64>(key.samples) << 8) |
|
||||
(static_cast<u64>(key.color_clear_mask) << 16) |
|
||||
(static_cast<u64>(key.color_discard_mask) << 24) |
|
||||
(static_cast<u64>(key.resolve_color) << 32) |
|
||||
(static_cast<u64>(key.depth_stencil_clear) << 33) |
|
||||
(static_cast<u64>(key.resolve_depth_stencil) << 34);
|
||||
size_t seed = 0;
|
||||
Common::HashCombine(seed, formats);
|
||||
Common::HashCombine(seed, state);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -50,6 +62,9 @@ namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
|
||||
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
|
||||
VideoCore::Surface::PixelFormat depth_format);
|
||||
|
||||
class RenderPassCache {
|
||||
public:
|
||||
explicit RenderPassCache(const Device& device_);
|
||||
|
||||
@@ -410,8 +410,17 @@ void Scheduler::EndRenderPass()
|
||||
Record([num_images = num_renderpass_images,
|
||||
images = renderpass_images,
|
||||
ranges = renderpass_image_ranges,
|
||||
consumer_stages = device.AttachmentConsumerStages(),
|
||||
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
|
||||
vk::CommandBuffer cmdbuf) {
|
||||
static constexpr VkAccessFlags SHADER_ACCESS =
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
static constexpr VkAccessFlags COLOR_ACCESS =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
std::array<VkImageMemoryBarrier, 9> barriers;
|
||||
for (size_t i = 0; i < num_images; ++i) {
|
||||
const VkImageSubresourceRange& range = ranges[i];
|
||||
@@ -421,24 +430,25 @@ void Scheduler::EndRenderPass()
|
||||
| VK_IMAGE_ASPECT_STENCIL_BIT)) !=0;
|
||||
|
||||
VkAccessFlags src_access = 0;
|
||||
VkAccessFlags dst_access = SHADER_ACCESS;
|
||||
|
||||
if (is_color)
|
||||
if (is_color) {
|
||||
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
else if (is_depth_stencil)
|
||||
dst_access |= COLOR_ACCESS;
|
||||
} else if (is_depth_stencil) {
|
||||
src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
else
|
||||
dst_access |= DEPTH_STENCIL_ACCESS;
|
||||
} else {
|
||||
src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS;
|
||||
}
|
||||
|
||||
barriers[i] = VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = src_access,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -449,7 +459,7 @@ void Scheduler::EndRenderPass()
|
||||
}
|
||||
cmdbuf.EndRenderPass();
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages,
|
||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||
if (has_transform_feedback) {
|
||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||
|
||||
@@ -628,18 +628,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
read_barrier);
|
||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||
// TODO: Move this to another API
|
||||
cmdbuf.PipelineBarrier(
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0, nullptr, nullptr, write_barrier);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||
nullptr, nullptr, write_barrier);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||
@@ -933,6 +927,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||
}
|
||||
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::Finish() {
|
||||
@@ -995,13 +993,25 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
|
||||
}
|
||||
|
||||
VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
|
||||
VkExtent2D extent, u32 layers) {
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
ResolveShadow& shadow = resolve_shadows[msaa_image];
|
||||
if (shadow.image && shadow.format == format && shadow.extent.width == extent.width &&
|
||||
shadow.extent.height == extent.height && shadow.layers == layers) {
|
||||
shadow.extent.height == extent.height && shadow.layers == layers &&
|
||||
shadow.aspect_mask == aspect_mask) {
|
||||
shadow.up_to_date = true;
|
||||
return *shadow.view;
|
||||
}
|
||||
VkImageUsageFlags shadow_usage =
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
|
||||
shadow_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
} else {
|
||||
shadow_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
if (shadow.image) {
|
||||
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(shadow));
|
||||
}
|
||||
shadow.image = memory_allocator.CreateImage(VkImageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1013,8 +1023,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
|
||||
.arrayLayers = layers,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
.usage = shadow_usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
@@ -1029,7 +1038,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
|
||||
.format = format,
|
||||
.components{},
|
||||
.subresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
@@ -1039,6 +1048,7 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
|
||||
shadow.format = format;
|
||||
shadow.extent = extent;
|
||||
shadow.layers = layers;
|
||||
shadow.aspect_mask = aspect_mask;
|
||||
shadow.up_to_date = true;
|
||||
return *shadow.view;
|
||||
}
|
||||
@@ -1060,7 +1070,14 @@ void TextureCacheRuntime::InvalidateResolveShadow(VkImage msaa_image) {
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::EraseResolveShadow(VkImage msaa_image) {
|
||||
resolve_shadows.erase(msaa_image);
|
||||
const auto it = resolve_shadows.find(msaa_image);
|
||||
if (it == resolve_shadows.end()) {
|
||||
return;
|
||||
}
|
||||
if (it->second.image) {
|
||||
pending_resolve_shadows.emplace_back(scheduler.CurrentTick(), std::move(it->second));
|
||||
}
|
||||
resolve_shadows.erase(it);
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::BarrierFeedbackLoop() {
|
||||
@@ -1195,7 +1212,8 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
|
||||
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, READ_BARRIER, {}, middle_out_barrier);
|
||||
cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies);
|
||||
cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
vk_out_copies);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, {}, {}, post_barriers);
|
||||
});
|
||||
@@ -1357,133 +1375,83 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||
}
|
||||
|
||||
switch (dst_view.format) {
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM
|
||||
|| src_view.format == PixelFormat::B8G8R8A8_UNORM
|
||||
|| src_view.format == PixelFormat::A8B8G8R8_SRGB
|
||||
|| src_view.format == PixelFormat::B8G8R8A8_SRGB) {
|
||||
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
|
||||
case PixelFormat::R16_UNORM:
|
||||
if (src_view.format == PixelFormat::D16_UNORM) {
|
||||
return blit_image_helper.ConvertD16ToR16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
case PixelFormat::A8B8G8R8_SNORM:
|
||||
case PixelFormat::A8B8G8R8_SINT:
|
||||
case PixelFormat::A8B8G8R8_UINT:
|
||||
case PixelFormat::R5G6B5_UNORM:
|
||||
case PixelFormat::B5G6R5_UNORM:
|
||||
case PixelFormat::A1R5G5B5_UNORM:
|
||||
case PixelFormat::A2B10G10R10_UNORM:
|
||||
case PixelFormat::A2B10G10R10_UINT:
|
||||
case PixelFormat::A2R10G10B10_UNORM:
|
||||
case PixelFormat::A1B5G5R5_UNORM:
|
||||
case PixelFormat::A5B5G5R1_UNORM:
|
||||
case PixelFormat::R8_UNORM:
|
||||
case PixelFormat::R8_SNORM:
|
||||
case PixelFormat::R8_SINT:
|
||||
case PixelFormat::R8_UINT:
|
||||
case PixelFormat::R16G16B16A16_FLOAT:
|
||||
case PixelFormat::R16G16B16A16_UNORM:
|
||||
case PixelFormat::R16G16B16A16_SNORM:
|
||||
case PixelFormat::R16G16B16A16_SINT:
|
||||
case PixelFormat::R16G16B16A16_UINT:
|
||||
case PixelFormat::B10G11R11_FLOAT:
|
||||
case PixelFormat::R32G32B32A32_UINT:
|
||||
case PixelFormat::BC1_RGBA_UNORM:
|
||||
case PixelFormat::BC2_UNORM:
|
||||
case PixelFormat::BC3_UNORM:
|
||||
case PixelFormat::BC4_UNORM:
|
||||
case PixelFormat::BC4_SNORM:
|
||||
case PixelFormat::BC5_UNORM:
|
||||
case PixelFormat::BC5_SNORM:
|
||||
case PixelFormat::BC7_UNORM:
|
||||
case PixelFormat::BC6H_UFLOAT:
|
||||
case PixelFormat::BC6H_SFLOAT:
|
||||
case PixelFormat::ASTC_2D_4X4_UNORM:
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
case PixelFormat::R32G32B32A32_FLOAT:
|
||||
case PixelFormat::R32G32B32A32_SINT:
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
case PixelFormat::R32G32_SINT:
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT &&
|
||||
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
|
||||
Settings::values.fix_bloom_effects.GetValue())) {
|
||||
const Region2D region{
|
||||
.start = {0, 0},
|
||||
.end = {static_cast<s32>(dst->RenderArea().width),
|
||||
static_cast<s32>(dst->RenderArea().height)},
|
||||
};
|
||||
return blit_image_helper.BlitColor(dst, src_view, region, region,
|
||||
Tegra::Engines::Fermi2D::Filter::Point,
|
||||
Tegra::Engines::Fermi2D::Operation::SrcCopy);
|
||||
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
|
||||
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
|
||||
}
|
||||
if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
|
||||
return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
|
||||
}
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32ToR32(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::R16_FLOAT:
|
||||
case PixelFormat::R16_UNORM:
|
||||
case PixelFormat::R16_SNORM:
|
||||
case PixelFormat::R16_UINT:
|
||||
case PixelFormat::R16_SINT:
|
||||
case PixelFormat::R16G16_UNORM:
|
||||
case PixelFormat::R16G16_FLOAT:
|
||||
case PixelFormat::R16G16_UINT:
|
||||
case PixelFormat::R16G16_SINT:
|
||||
case PixelFormat::R16G16_SNORM:
|
||||
case PixelFormat::R32G32B32_FLOAT:
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
case PixelFormat::R8G8_UNORM:
|
||||
case PixelFormat::R8G8_SNORM:
|
||||
case PixelFormat::R8G8_SINT:
|
||||
case PixelFormat::R8G8_UINT:
|
||||
case PixelFormat::R32G32_UINT:
|
||||
case PixelFormat::R16G16B16X16_FLOAT:
|
||||
case PixelFormat::R32_UINT:
|
||||
case PixelFormat::R32_SINT:
|
||||
case PixelFormat::ASTC_2D_8X8_UNORM:
|
||||
case PixelFormat::ASTC_2D_8X5_UNORM:
|
||||
case PixelFormat::ASTC_2D_5X4_UNORM:
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
case PixelFormat::BC1_RGBA_SRGB:
|
||||
case PixelFormat::BC2_SRGB:
|
||||
case PixelFormat::BC3_SRGB:
|
||||
case PixelFormat::BC7_SRGB:
|
||||
case PixelFormat::A4B4G4R4_UNORM:
|
||||
case PixelFormat::G4R4_UNORM:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_5X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_5X5_UNORM:
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X8_UNORM:
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_6X6_UNORM:
|
||||
case PixelFormat::ASTC_2D_6X6_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X6_UNORM:
|
||||
case PixelFormat::ASTC_2D_10X6_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X5_UNORM:
|
||||
case PixelFormat::ASTC_2D_10X5_SRGB:
|
||||
case PixelFormat::ASTC_2D_10X10_UNORM:
|
||||
case PixelFormat::ASTC_2D_10X10_SRGB:
|
||||
case PixelFormat::ASTC_2D_12X10_UNORM:
|
||||
case PixelFormat::ASTC_2D_12X10_SRGB:
|
||||
case PixelFormat::ASTC_2D_12X12_UNORM:
|
||||
case PixelFormat::ASTC_2D_12X12_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X6_UNORM:
|
||||
case PixelFormat::ASTC_2D_8X6_SRGB:
|
||||
case PixelFormat::ASTC_2D_6X5_UNORM:
|
||||
case PixelFormat::ASTC_2D_6X5_SRGB:
|
||||
case PixelFormat::E5B9G9R9_FLOAT:
|
||||
case PixelFormat::D32_FLOAT:
|
||||
case PixelFormat::D16_UNORM:
|
||||
case PixelFormat::X8_D24_UNORM:
|
||||
case PixelFormat::S8_UINT:
|
||||
if (src_view.format == PixelFormat::R16_UNORM) {
|
||||
return blit_image_helper.ConvertR16ToD16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
case PixelFormat::D32_FLOAT_S8_UINT:
|
||||
case PixelFormat::Invalid:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM) {
|
||||
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::D32_FLOAT:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
|
||||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
|
||||
return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
|
||||
}
|
||||
if (src_view.format == PixelFormat::R32_FLOAT) {
|
||||
return blit_image_helper.ConvertR32ToD32(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
|
||||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
|
||||
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
|
||||
break;
|
||||
}
|
||||
|
||||
if (src_view.format == PixelFormat::D32_FLOAT &&
|
||||
VideoCore::Surface::GetFormatType(dst_view.format) == SurfaceType::ColorTexture &&
|
||||
(dst_view.format == PixelFormat::B5G6R5_UNORM ||
|
||||
Settings::values.fix_bloom_effects.GetValue())) {
|
||||
const Region2D region{
|
||||
.start = {0, 0},
|
||||
.end = {static_cast<s32>(dst->RenderArea().width),
|
||||
static_cast<s32>(dst->RenderArea().height)},
|
||||
};
|
||||
return blit_image_helper.BlitColor(dst, src_view, region, region,
|
||||
Tegra::Engines::Fermi2D::Filter::Point,
|
||||
Tegra::Engines::Fermi2D::Operation::SrcCopy);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
|
||||
}
|
||||
|
||||
VkFormat TextureCacheRuntime::GetSupportedFormat(VkFormat requested_format,
|
||||
@@ -1640,31 +1608,41 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
std::span<const VideoCommon::ImageCopy> copies) {
|
||||
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
|
||||
const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples;
|
||||
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
|
||||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
|
||||
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
|
||||
return;
|
||||
}
|
||||
if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 &&
|
||||
src.info.format == dst.info.format) {
|
||||
const VideoCommon::ImageCopy& copy = copies.front();
|
||||
const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle());
|
||||
if (shadow != nullptr && copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
|
||||
if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() &&
|
||||
copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
|
||||
copy.src_subresource.base_level == 0 &&
|
||||
static_cast<u32>(copy.extent.width) <= shadow->extent.width &&
|
||||
static_cast<u32>(copy.extent.height) <= shadow->extent.height) {
|
||||
static_cast<u32>(copy.extent.height) <= shadow->extent.height &&
|
||||
static_cast<u32>(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <=
|
||||
shadow->layers) {
|
||||
const VkImageAspectFlags aspect_mask = shadow->aspect_mask;
|
||||
VkPipelineStageFlags attachment_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkAccessFlags attachment_write = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
VkAccessFlags attachment_read_write =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
|
||||
attachment_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
attachment_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
attachment_read_write = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
}
|
||||
const VkImage shadow_image = *shadow->image;
|
||||
const VkImage dst_image = dst.Handle();
|
||||
const VkImageCopy region{
|
||||
.srcSubresource{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspect_mask,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
|
||||
.layerCount = static_cast<u32>(copy.src_subresource.num_layers),
|
||||
},
|
||||
.srcOffset = {0, 0, 0},
|
||||
.dstSubresource{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspect_mask,
|
||||
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
|
||||
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
|
||||
.layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
|
||||
@@ -1673,26 +1651,27 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
.extent = {copy.extent.width, copy.extent.height, 1},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([shadow_image, dst_image, region](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([shadow_image, dst_image, region, aspect_mask, attachment_stage,
|
||||
attachment_write,
|
||||
attachment_read_write](vk::CommandBuffer cmdbuf) {
|
||||
const std::array pre_barriers{
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.srcAccessMask = attachment_write,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = shadow_image,
|
||||
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
VK_REMAINING_ARRAY_LAYERS},
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | attachment_write |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
@@ -1700,7 +1679,7 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
VK_REMAINING_ARRAY_LAYERS},
|
||||
},
|
||||
};
|
||||
@@ -1715,28 +1694,25 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = shadow_image,
|
||||
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
VK_REMAINING_ARRAY_LAYERS},
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | attachment_read_write |
|
||||
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst_image,
|
||||
.subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
|
||||
VK_REMAINING_ARRAY_LAYERS},
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
cmdbuf.PipelineBarrier(attachment_stage | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, nullptr, nullptr,
|
||||
pre_barriers);
|
||||
cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
|
||||
@@ -1748,6 +1724,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dst.AspectMask() != VK_IMAGE_ASPECT_COLOR_BIT ||
|
||||
VideoCore::Surface::IsPixelFormatInteger(dst.info.format)) {
|
||||
UNIMPLEMENTED_MSG("Copying images with different samples is not supported.");
|
||||
return;
|
||||
}
|
||||
blit_image_helper.CopyMSAA(render_pass_cache, dst.Handle(), dst.info.format, src.Handle(),
|
||||
src.info.format, num_samples, copies, msaa_to_non_msaa);
|
||||
}
|
||||
@@ -1772,6 +1753,9 @@ void TextureCacheRuntime::TickFrame() {
|
||||
std::erase_if(pending_msaa_images, [this](const auto& pending) {
|
||||
return scheduler.IsFree(pending.first);
|
||||
});
|
||||
std::erase_if(pending_resolve_shadows, [this](const auto& pending) {
|
||||
return scheduler.IsFree(pending.first);
|
||||
});
|
||||
}
|
||||
|
||||
Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
|
||||
@@ -1796,6 +1780,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
||||
}
|
||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||
} else if (runtime->bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(info)) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||
} else if (runtime->bl3db_unswizzle_pass &&
|
||||
BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||
}
|
||||
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
|
||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||
@@ -1882,11 +1873,21 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
ScaleDown(true);
|
||||
}
|
||||
|
||||
const bool msaa_upload_is_depth = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
|
||||
const bool wants_msaa_upload = info.num_samples > 1
|
||||
&& (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0
|
||||
&& ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 || msaa_upload_is_depth)
|
||||
&& !VideoCore::Surface::IsPixelFormatInteger(info.format);
|
||||
|
||||
if (wants_msaa_upload) {
|
||||
const bool msaa_upload_copies_stencil =
|
||||
msaa_upload_is_depth && (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 &&
|
||||
runtime->device.IsExtShaderStencilExportSupported();
|
||||
const VkImageAspectFlags upload_aspect_mask =
|
||||
msaa_upload_is_depth
|
||||
? (msaa_upload_copies_stencil
|
||||
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
|
||||
: VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||
: aspect_mask;
|
||||
ImageInfo temp_info = info;
|
||||
temp_info.num_samples = 1;
|
||||
|
||||
@@ -1898,10 +1899,10 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
|
||||
|
||||
scheduler->RequestOutsideRenderPassOperationContext();
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, upload_aspect_mask);
|
||||
const VkBuffer src_buffer = buffer;
|
||||
const VkImage temp_vk_image = *temp_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||
const VkImageAspectFlags vk_aspect_mask = upload_aspect_mask;
|
||||
|
||||
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask,
|
||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||
@@ -1923,9 +1924,16 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
image_copies.push_back(image_copy);
|
||||
}
|
||||
|
||||
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
|
||||
temp_vk_image, info.format, info.num_samples,
|
||||
image_copies, false);
|
||||
if (msaa_upload_is_depth) {
|
||||
runtime->blit_image_helper.CopyMSAADepth(runtime->render_pass_cache, Handle(),
|
||||
info.format, temp_vk_image, info.format,
|
||||
info.num_samples, image_copies,
|
||||
msaa_upload_copies_stencil);
|
||||
} else {
|
||||
runtime->blit_image_helper.CopyMSAA(runtime->render_pass_cache, Handle(), info.format,
|
||||
temp_vk_image, info.format, info.num_samples,
|
||||
image_copies, false);
|
||||
}
|
||||
initialized = true;
|
||||
runtime->pending_msaa_images.emplace_back(scheduler->CurrentTick(), std::move(temp_image));
|
||||
|
||||
@@ -2690,7 +2698,7 @@ Framebuffer::~Framebuffer() = default;
|
||||
void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<ImageView*, NUM_RT> color_buffers,
|
||||
ImageView* depth_buffer, bool is_rescaled_) {
|
||||
boost::container::small_vector<VkImageView, NUM_RT + 1> attachments;
|
||||
boost::container::small_vector<VkImageView, NUM_RT * 2 + 2> attachments;
|
||||
RenderPassKey renderpass_key{};
|
||||
s32 num_layers = 1;
|
||||
|
||||
@@ -2719,6 +2727,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
++num_images;
|
||||
}
|
||||
const size_t num_colors = attachments.size();
|
||||
VkImage depth_image = VK_NULL_HANDLE;
|
||||
VkImageAspectFlags depth_aspect_mask = 0;
|
||||
if (depth_buffer) {
|
||||
width = (std::min)(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width)
|
||||
: depth_buffer->size.width);
|
||||
@@ -2734,6 +2744,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
++num_images;
|
||||
has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
|
||||
has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
|
||||
depth_image = depth_buffer->ImageHandle();
|
||||
depth_aspect_mask = subresource_range.aspectMask;
|
||||
} else {
|
||||
renderpass_key.depth_format = PixelFormat::Invalid;
|
||||
}
|
||||
@@ -2742,6 +2754,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler();
|
||||
renderpass_key.resolve_color = do_resolve_color;
|
||||
|
||||
const bool do_resolve_depth_stencil =
|
||||
samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE &&
|
||||
runtime.device.IsTiler() &&
|
||||
SupportsDepthStencilResolve(runtime.device, renderpass_key.depth_format);
|
||||
renderpass_key.resolve_depth_stencil = do_resolve_depth_stencil;
|
||||
|
||||
discard_msaa_color =
|
||||
ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
|
||||
|
||||
@@ -2762,8 +2780,8 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format;
|
||||
if (ENABLE_MSAA_RESOLVE_CONSUME) {
|
||||
const VkImage msaa_image = images[rt_map[index]];
|
||||
attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format,
|
||||
render_area, layers));
|
||||
attachments.push_back(runtime.GetOrCreateResolveShadow(
|
||||
msaa_image, vk_format, render_area, layers, VK_IMAGE_ASPECT_COLOR_BIT));
|
||||
continue;
|
||||
}
|
||||
VkImageCreateInfo resolve_ci{
|
||||
@@ -2808,6 +2826,16 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
}
|
||||
}
|
||||
|
||||
if (do_resolve_depth_stencil) {
|
||||
const u32 layers = static_cast<u32>((std::max)(num_layers, 1));
|
||||
const VkFormat vk_format =
|
||||
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true,
|
||||
renderpass_key.depth_format)
|
||||
.format;
|
||||
attachments.push_back(runtime.GetOrCreateResolveShadow(depth_image, vk_format, render_area,
|
||||
layers, depth_aspect_mask));
|
||||
}
|
||||
|
||||
num_color_buffers = static_cast<u32>(num_colors);
|
||||
framebuffer = runtime.device.GetLogical().CreateFramebuffer({
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
@@ -2827,11 +2855,25 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
|
||||
if (color_clear_mask == 0 && !depth_stencil_clear && color_discard_mask == 0) {
|
||||
return renderpass;
|
||||
}
|
||||
static_assert(NUM_RT <= 8);
|
||||
const u32 variant_key = color_clear_mask | (color_discard_mask << 8) |
|
||||
(static_cast<u32>(depth_stencil_clear) << 16);
|
||||
for (u32 index = 0; index < num_memoized_variants; ++index) {
|
||||
if (variant_keys[index] == variant_key) {
|
||||
return variant_render_passes[index];
|
||||
}
|
||||
}
|
||||
RenderPassKey key = render_pass_key;
|
||||
key.color_clear_mask = color_clear_mask;
|
||||
key.depth_stencil_clear = depth_stencil_clear;
|
||||
key.color_discard_mask = color_discard_mask;
|
||||
return render_pass_cache->Get(key);
|
||||
const VkRenderPass variant = render_pass_cache->Get(key);
|
||||
if (num_memoized_variants < variant_keys.size()) {
|
||||
variant_keys[num_memoized_variants] = variant_key;
|
||||
variant_render_passes[num_memoized_variants] = variant;
|
||||
++num_memoized_variants;
|
||||
}
|
||||
return variant;
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::AccelerateImageUpload(
|
||||
@@ -2843,6 +2885,15 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(image.info)) {
|
||||
return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl3db_unswizzle_pass &&
|
||||
BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
|
||||
return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||
|
||||
@@ -117,11 +117,13 @@ public:
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{};
|
||||
u32 layers = 0;
|
||||
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bool up_to_date = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
|
||||
VkExtent2D extent, u32 layers);
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkImageAspectFlags aspect_mask);
|
||||
|
||||
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
|
||||
|
||||
@@ -149,6 +151,8 @@ public:
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
|
||||
std::optional<BlockLinearUnswizzle3DBufferPass> bl3db_unswizzle_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
|
||||
@@ -156,6 +160,7 @@ public:
|
||||
std::array<vk::Buffer, indexing_slots> buffers{};
|
||||
std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
|
||||
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
|
||||
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
@@ -246,6 +251,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
|
||||
|
||||
vk::Framebuffer framebuffer;
|
||||
VkRenderPass renderpass{};
|
||||
VkExtent2D render_area{};
|
||||
@@ -263,6 +270,9 @@ private:
|
||||
RenderPassKey render_pass_key{};
|
||||
RenderPassCache* render_pass_cache{nullptr};
|
||||
bool discard_msaa_color{};
|
||||
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
|
||||
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
|
||||
mutable u32 num_memoized_variants{};
|
||||
};
|
||||
|
||||
class Image : public VideoCommon::ImageBase {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -30,12 +31,62 @@ constexpr u32 pdep(u32 value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr u32 SWIZZLE_RUN_BYTES = 16;
|
||||
constexpr u32 SWIZZLE_RUN_SHIFT = 4;
|
||||
constexpr u32 SWIZZLE_RUN_MASK = SWIZZLE_RUN_BYTES - 1;
|
||||
constexpr u32 SWIZZLE_RUN_INDEX_MASK = GOB_SIZE_X / SWIZZLE_RUN_BYTES - 1;
|
||||
|
||||
static_assert((SWIZZLE_X_BITS & SWIZZLE_RUN_MASK) == SWIZZLE_RUN_MASK);
|
||||
|
||||
constexpr std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> SWIZZLE_X_RUN_TABLE = [] {
|
||||
std::array<u32, GOB_SIZE_X / SWIZZLE_RUN_BYTES> table{};
|
||||
for (u32 index = 0; index < static_cast<u32>(table.size()); ++index) {
|
||||
table[index] = pdep<SWIZZLE_X_BITS>(index << SWIZZLE_RUN_SHIFT);
|
||||
}
|
||||
return table;
|
||||
}();
|
||||
|
||||
template <u32 mask, u32 incr_amount>
|
||||
void incrpdep(u32& value) {
|
||||
static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
|
||||
value = ((value | ~mask) + swizzled_incr) & mask;
|
||||
}
|
||||
|
||||
template <bool TO_LINEAR>
|
||||
void SwizzleRow(std::span<u8> output, std::span<const u8> input, u32 offset_zy, u32 swizzled_y,
|
||||
u32 x_shift, u32 x, u32 num_bytes, u32 linear) {
|
||||
const auto copy = [&](u32 swizzled_x, u32 count) {
|
||||
const u32 swizzled =
|
||||
offset_zy + ((x >> GOB_SIZE_X_SHIFT) << x_shift) + (swizzled_x | swizzled_y);
|
||||
u8* const dst = &output[TO_LINEAR ? swizzled : linear];
|
||||
const u8* const src = &input[TO_LINEAR ? linear : swizzled];
|
||||
std::memcpy(dst, src, count);
|
||||
x += count;
|
||||
linear += count;
|
||||
};
|
||||
|
||||
u32 swizzled_run = SWIZZLE_X_RUN_TABLE[(x >> SWIZZLE_RUN_SHIFT) & SWIZZLE_RUN_INDEX_MASK];
|
||||
u32 remaining = num_bytes;
|
||||
|
||||
const u32 head =
|
||||
(std::min)(SWIZZLE_RUN_BYTES - (x & SWIZZLE_RUN_MASK), remaining) & SWIZZLE_RUN_MASK;
|
||||
if (head != 0) {
|
||||
copy(swizzled_run | (x & SWIZZLE_RUN_MASK), head);
|
||||
remaining -= head;
|
||||
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
|
||||
}
|
||||
|
||||
while (remaining >= SWIZZLE_RUN_BYTES) {
|
||||
copy(swizzled_run, SWIZZLE_RUN_BYTES);
|
||||
remaining -= SWIZZLE_RUN_BYTES;
|
||||
incrpdep<SWIZZLE_X_BITS, SWIZZLE_RUN_BYTES>(swizzled_run);
|
||||
}
|
||||
|
||||
if (remaining != 0) {
|
||||
copy(swizzled_run, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
|
||||
void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth, u32 stride) {
|
||||
@@ -70,23 +121,9 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
|
||||
const u32 offset_y = (block_y >> block_height) * block_size +
|
||||
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
||||
|
||||
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
|
||||
for (u32 column = 0; column < width;
|
||||
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
|
||||
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
|
||||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
|
||||
|
||||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
|
||||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
|
||||
|
||||
const u32 unswizzled_offset =
|
||||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
|
||||
|
||||
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
|
||||
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
|
||||
|
||||
std::memcpy(dst, src, BYTES_PER_PIXEL);
|
||||
}
|
||||
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
|
||||
origin_x * BYTES_PER_PIXEL, width * BYTES_PER_PIXEL,
|
||||
slice * pitch * height + line * pitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,23 +166,9 @@ void SwizzleSubrectImpl(std::span<u8> output, std::span<const u8> input, u32 wid
|
||||
const u32 offset_y = (block_y >> block_height) * block_size +
|
||||
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
|
||||
|
||||
u32 swizzled_x = pdep<SWIZZLE_X_BITS>(origin_x * BYTES_PER_PIXEL);
|
||||
for (u32 column = 0; column < extent_x;
|
||||
++column, incrpdep<SWIZZLE_X_BITS, BYTES_PER_PIXEL>(swizzled_x)) {
|
||||
const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
|
||||
const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
|
||||
|
||||
const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
|
||||
const u32 swizzled_offset = base_swizzled_offset + (swizzled_x | swizzled_y);
|
||||
|
||||
const u32 unswizzled_offset =
|
||||
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
|
||||
|
||||
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
|
||||
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
|
||||
|
||||
std::memcpy(dst, src, BYTES_PER_PIXEL);
|
||||
}
|
||||
SwizzleRow<TO_LINEAR>(output, input, offset_z + offset_y, swizzled_y, x_shift,
|
||||
origin_x * BYTES_PER_PIXEL, extent_x * BYTES_PER_PIXEL,
|
||||
slice * pitch * height + line * pitch);
|
||||
}
|
||||
unprocessed_lines -= lines_in_y;
|
||||
if (unprocessed_lines == 0) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
@@ -16,6 +18,8 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/literals.h"
|
||||
#include <ranges>
|
||||
#include "common/settings.h"
|
||||
@@ -393,6 +397,17 @@ std::vector<const char*> ExtensionListForVulkan(
|
||||
return output;
|
||||
}
|
||||
|
||||
constexpr std::array<char, 8> STATIC_CACHE_MAGIC_NUMBER{'e', 'd', 'e', 'n', 's', 't', 'p', 'c'};
|
||||
constexpr u32 STATIC_CACHE_VERSION = 1;
|
||||
|
||||
std::filesystem::path StaticPipelineCacheFilename() {
|
||||
const auto shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
|
||||
if (!Common::FS::CreateDir(shader_dir)) {
|
||||
return {};
|
||||
}
|
||||
return shader_dir / "vulkan_static_pipelines.bin";
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
|
||||
@@ -780,15 +795,100 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
|
||||
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
|
||||
|
||||
owns_static_pipeline_cache = surface != VkSurfaceKHR{};
|
||||
LoadStaticPipelineCache();
|
||||
|
||||
// Initialize GPU logging if enabled
|
||||
InitializeGPULogging();
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
SaveStaticPipelineCache();
|
||||
ShutdownGPULogging();
|
||||
vmaDestroyAllocator(allocator);
|
||||
}
|
||||
|
||||
void Device::LoadStaticPipelineCache() {
|
||||
const auto create = [this](size_t size, const void* data) {
|
||||
static_pipeline_cache = logical.CreatePipelineCache({
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.initialDataSize = size,
|
||||
.pInitialData = data,
|
||||
});
|
||||
};
|
||||
if (!owns_static_pipeline_cache) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
const auto filename = StaticPipelineCacheFilename();
|
||||
if (filename.empty()) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
std::vector<char> data;
|
||||
try {
|
||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
const size_t total = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::array<char, 8> magic{};
|
||||
u32 version{};
|
||||
if (total < magic.size() + sizeof(version)) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
file.read(magic.data(), magic.size())
|
||||
.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
if (magic != STATIC_CACHE_MAGIC_NUMBER || version != STATIC_CACHE_VERSION) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
data.resize(total - magic.size() - sizeof(version));
|
||||
file.read(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
} catch (const std::ios_base::failure& e) {
|
||||
create(0, nullptr);
|
||||
return;
|
||||
}
|
||||
create(data.size(), data.empty() ? nullptr : data.data());
|
||||
}
|
||||
|
||||
void Device::SaveStaticPipelineCache() const {
|
||||
if (!owns_static_pipeline_cache || !static_pipeline_cache) {
|
||||
return;
|
||||
}
|
||||
const auto filename = StaticPipelineCacheFilename();
|
||||
if (filename.empty()) {
|
||||
return;
|
||||
}
|
||||
size_t size = 0;
|
||||
std::vector<char> data;
|
||||
static_pipeline_cache.Read(&size, nullptr);
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
data.resize(size);
|
||||
static_pipeline_cache.Read(&size, data.data());
|
||||
try {
|
||||
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
|
||||
file.exceptions(std::ofstream::failbit);
|
||||
if (!file.is_open()) {
|
||||
return;
|
||||
}
|
||||
file.write(STATIC_CACHE_MAGIC_NUMBER.data(), STATIC_CACHE_MAGIC_NUMBER.size())
|
||||
.write(reinterpret_cast<const char*>(&STATIC_CACHE_VERSION),
|
||||
sizeof(STATIC_CACHE_VERSION))
|
||||
.write(data.data(), static_cast<std::streamsize>(size));
|
||||
} catch (const std::ios_base::failure& e) {
|
||||
Common::FS::RemoveFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const {
|
||||
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
|
||||
@@ -975,6 +1075,12 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
|
||||
FOR_EACH_VK_EXTENSION(EXTENSION);
|
||||
|
||||
extensions.depth_stencil_resolve =
|
||||
extensions.depth_stencil_resolve &&
|
||||
(instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2);
|
||||
RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve,
|
||||
VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
|
||||
|
||||
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
|
||||
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
|
||||
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
|
||||
@@ -1122,6 +1228,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
|
||||
SetNext(next, properties.push_descriptor);
|
||||
}
|
||||
if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) {
|
||||
properties.depth_stencil_resolve.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
|
||||
SetNext(next, properties.depth_stencil_resolve);
|
||||
}
|
||||
if (extensions.descriptor_buffer) {
|
||||
properties.descriptor_buffer.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
|
||||
|
||||
@@ -90,6 +90,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
||||
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
||||
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
||||
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
||||
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
||||
EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
|
||||
EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
|
||||
@@ -268,6 +270,10 @@ public:
|
||||
return physical;
|
||||
}
|
||||
|
||||
VkPipelineCache StaticPipelineCache() const noexcept {
|
||||
return *static_pipeline_cache;
|
||||
}
|
||||
|
||||
/// Returns the main graphics queue.
|
||||
vk::Queue GetGraphicsQueue() const {
|
||||
return graphics_queue;
|
||||
@@ -607,6 +613,37 @@ FN_MAX_LIMIT_LIST
|
||||
return extensions.shader_stencil_export;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_create_renderpass2.
|
||||
bool IsKhrCreateRenderPass2Supported() const {
|
||||
return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_depth_stencil_resolve.
|
||||
bool IsKhrDepthStencilResolveSupported() const {
|
||||
return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) &&
|
||||
IsKhrCreateRenderPass2Supported();
|
||||
}
|
||||
|
||||
/// Returns the supported resolve modes for the depth aspect.
|
||||
VkResolveModeFlags GetDepthResolveModes() const {
|
||||
return properties.depth_stencil_resolve.supportedDepthResolveModes;
|
||||
}
|
||||
|
||||
/// Returns the supported resolve modes for the stencil aspect.
|
||||
VkResolveModeFlags GetStencilResolveModes() const {
|
||||
return properties.depth_stencil_resolve.supportedStencilResolveModes;
|
||||
}
|
||||
|
||||
/// Returns true if the depth and stencil aspects may resolve with different modes.
|
||||
bool SupportsIndependentResolve() const {
|
||||
return properties.depth_stencil_resolve.independentResolve == VK_TRUE;
|
||||
}
|
||||
|
||||
/// Returns true if only one of the depth and stencil aspects may be resolved.
|
||||
bool SupportsIndependentResolveNone() const {
|
||||
return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE;
|
||||
}
|
||||
|
||||
/// Returns true if depth/stencil operations can be performed efficiently.
|
||||
/// Either through shader export or hardware blits.
|
||||
bool CanPerformDepthStencilOperations() const {
|
||||
@@ -957,6 +994,21 @@ FN_MAX_LIMIT_LIST
|
||||
return features2.features.multiViewport;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags AttachmentConsumerStages() const {
|
||||
VkPipelineStageFlags stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
if (features2.features.geometryShader) {
|
||||
stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
|
||||
}
|
||||
return stages;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_maintenance1.
|
||||
bool IsKhrMaintenance1Supported() const {
|
||||
return extensions.maintenance1;
|
||||
@@ -1088,6 +1140,9 @@ private:
|
||||
/// Returns true if the device natively supports blitting depth stencil images.
|
||||
bool TestDepthStencilBlits(VkFormat format) const;
|
||||
|
||||
void LoadStaticPipelineCache();
|
||||
void SaveStaticPipelineCache() const;
|
||||
|
||||
private:
|
||||
VkInstance instance; ///< Vulkan instance.
|
||||
VmaAllocator allocator; ///< VMA allocator.
|
||||
@@ -1096,6 +1151,8 @@ private:
|
||||
vk::Device logical; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
vk::PipelineCache static_pipeline_cache;
|
||||
bool owns_static_pipeline_cache{};
|
||||
u32 instance_version{}; ///< Vulkan instance version.
|
||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||
u32 present_family{}; ///< Main present queue family index.
|
||||
@@ -1142,6 +1199,7 @@ private:
|
||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
};
|
||||
|
||||
@@ -184,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCreatePipelineLayout);
|
||||
X(vkCreateQueryPool);
|
||||
X(vkCreateRenderPass);
|
||||
X(vkCreateRenderPass2);
|
||||
X(vkCreateSampler);
|
||||
X(vkCreateSemaphore);
|
||||
X(vkCreateShaderModule);
|
||||
@@ -270,6 +271,11 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
if (!dld.vkQueueSubmit2) {
|
||||
Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device);
|
||||
}
|
||||
|
||||
// Render pass creation v2 is core in Vulkan 1.2, otherwise requires VK_KHR_create_renderpass2
|
||||
if (!dld.vkCreateRenderPass2) {
|
||||
Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device);
|
||||
}
|
||||
#undef X
|
||||
}
|
||||
|
||||
@@ -725,6 +731,12 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const {
|
||||
return RenderPass(object, handle, *dld);
|
||||
}
|
||||
|
||||
RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const {
|
||||
VkRenderPass object;
|
||||
Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object));
|
||||
return RenderPass(object, handle, *dld);
|
||||
}
|
||||
|
||||
DescriptorSetLayout Device::CreateDescriptorSetLayout(
|
||||
const VkDescriptorSetLayoutCreateInfo& ci) const {
|
||||
VkDescriptorSetLayout object;
|
||||
|
||||
@@ -300,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
|
||||
PFN_vkCreateQueryPool vkCreateQueryPool{};
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass{};
|
||||
PFN_vkCreateRenderPass2 vkCreateRenderPass2{};
|
||||
PFN_vkCreateSampler vkCreateSampler{};
|
||||
PFN_vkCreateSemaphore vkCreateSemaphore{};
|
||||
PFN_vkCreateShaderModule vkCreateShaderModule{};
|
||||
@@ -1045,6 +1046,8 @@ public:
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const;
|
||||
|
||||
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
|
||||
const VkDescriptorSetLayoutCreateInfo& ci) const;
|
||||
|
||||
@@ -1526,7 +1529,7 @@ public:
|
||||
}
|
||||
|
||||
void SetDepthBias(float constant_factor, float clamp, float slope_factor,
|
||||
VkDepthBiasRepresentationInfoEXT* extra) const noexcept {
|
||||
const VkDepthBiasRepresentationInfoEXT* extra) const noexcept {
|
||||
VkDepthBiasInfoEXT info{
|
||||
.sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT,
|
||||
.pNext = extra,
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/scm_rev.h"
|
||||
@@ -30,17 +26,9 @@ EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Co
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
titlebar_timer = SDL_AddTimer(2000, [](void *userdata, SDL_TimerID, Uint32) -> Uint32 {
|
||||
auto* this_ = (EmuWindow_SDL3*)userdata;
|
||||
auto const results = this_->system.GetAndResetPerfStats();
|
||||
auto const title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, results.emulation_speed * 100.0f);
|
||||
SDL_SetWindowTitle(this_->render_window, title.c_str());
|
||||
return 2000;
|
||||
}, this);
|
||||
}
|
||||
|
||||
EmuWindow_SDL3::~EmuWindow_SDL3() {
|
||||
SDL_RemoveTimer(titlebar_timer);
|
||||
system.HIDCore().UnloadInputDevices();
|
||||
input_subsystem->Shutdown();
|
||||
SDL_Quit();
|
||||
@@ -144,7 +132,8 @@ void EmuWindow_SDL3::Fullscreen() {
|
||||
switch (Settings::values.fullscreen_mode.GetValue()) {
|
||||
case Settings::FullscreenMode::Exclusive:
|
||||
// Set window size to render size before entering fullscreen in exclusive mode.
|
||||
if (const SDL_DisplayMode* display_mode_ptr = SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
|
||||
if (const SDL_DisplayMode* display_mode_ptr =
|
||||
SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
|
||||
display_mode = *display_mode_ptr;
|
||||
SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
|
||||
SDL_SetWindowFullscreenMode(render_window, &display_mode);
|
||||
@@ -176,60 +165,92 @@ void EmuWindow_SDL3::Fullscreen() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL3::OnEvent(SDL_Event& event) {
|
||||
// Notice how we skip the "update title" aspect on most events
|
||||
// this is because some WMs do NOT like changing titles while resizing
|
||||
// so let's just... not do that, thanks :)
|
||||
// Afterall we don't really expect the user to pay attention to the titlebar
|
||||
// while they're moving a lot of shit around...
|
||||
void EmuWindow_SDL3::WaitEvent() {
|
||||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
const char* error = SDL_GetError();
|
||||
if (!error || strcmp(error, "") == 0) {
|
||||
// https://github.com/libsdl-org/SDL/issues/5780
|
||||
// Sometimes SDL will return without actually having hit an error condition;
|
||||
// just ignore it in this case.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
is_shown = false;
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_EXPOSED:
|
||||
is_shown = true;
|
||||
return OnResize();
|
||||
OnResize();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
is_open = false;
|
||||
return;
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
return OnKeyEvent(int(event.key.scancode), event.key.down ? 1 : 0);
|
||||
OnKeyEvent(static_cast<int>(event.key.scancode), event.key.down ? 1 : 0);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseMotion(event.motion.x, event.motion.y);
|
||||
return;
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
// ignore if it came from touch
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||
OnMouseButton(event.button.button, event.button.down ? 1 : 0, s32(event.button.x), s32(event.button.y));
|
||||
return;
|
||||
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||
OnMouseButton(event.button.button, event.button.down ? 1 : 0,
|
||||
static_cast<s32>(event.button.x), static_cast<s32>(event.button.y));
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_FINGER_DOWN:
|
||||
return OnFingerDown(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_EVENT_FINGER_MOTION:
|
||||
return OnFingerMotion(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchID));
|
||||
break;
|
||||
case SDL_EVENT_FINGER_UP:
|
||||
return OnFingerUp();
|
||||
OnFingerUp();
|
||||
break;
|
||||
case SDL_EVENT_QUIT:
|
||||
is_open = false;
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = system.GetAndResetPerfStats();
|
||||
const auto title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)",
|
||||
Common::g_build_fullname,
|
||||
Common::g_scm_branch,
|
||||
Common::g_scm_desc,
|
||||
results.average_game_fps,
|
||||
results.emulation_speed * 100.0);
|
||||
SDL_SetWindowTitle(render_window, title.c_str());
|
||||
last_time = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
// Credits to Samantas5855 and others for this function.
|
||||
void EmuWindow_SDL3::SetWindowIcon() {
|
||||
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
// Icons do not work yet
|
||||
#else
|
||||
SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size);
|
||||
if (yuzu_icon_stream == nullptr) {
|
||||
LOG_WARNING(Frontend, "Failed to create Eden icon stream.");
|
||||
@@ -243,7 +264,6 @@ void EmuWindow_SDL3::SetWindowIcon() {
|
||||
// The icon is attached to the window pointer
|
||||
SDL_SetWindowIcon(render_window, window_icon);
|
||||
SDL_DestroySurface(window_icon);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/graphics_context.h"
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
@@ -36,7 +35,7 @@ public:
|
||||
bool IsShown() const override;
|
||||
|
||||
/// Wait for the next event on the main thread.
|
||||
void OnEvent(SDL_Event& event);
|
||||
void WaitEvent();
|
||||
|
||||
// Sets the window icon from yuzu.bmp
|
||||
void SetWindowIcon();
|
||||
@@ -81,9 +80,6 @@ protected:
|
||||
/// Called when a configuration change affects the minimal size of the window
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
/// Periodic changer of titlebar (independent of event loop)
|
||||
SDL_TimerID titlebar_timer;
|
||||
|
||||
/// Is the window still open?
|
||||
bool is_open = true;
|
||||
|
||||
@@ -93,6 +89,9 @@ protected:
|
||||
/// Internal SDL3 render window
|
||||
SDL_Window* render_window{};
|
||||
|
||||
/// Keeps track of how often to update the title bar during gameplay
|
||||
u32 last_time = 0;
|
||||
|
||||
/// Input subsystem to use with this window.
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
|
||||
+65
-75
@@ -8,12 +8,6 @@
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include "common/settings_enums.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
#define SDL_MAIN_USE_CALLBACKS 1
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
@@ -45,7 +39,9 @@
|
||||
#ifdef _WIN32
|
||||
// windows.h needs to be included before shellapi.h
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "common/windows/timer_resolution.h"
|
||||
#endif
|
||||
|
||||
@@ -178,14 +174,8 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
|
||||
std::cout << std::endl << "* " << message << std::endl << std::endl;
|
||||
}
|
||||
|
||||
struct SdlState {
|
||||
Core::System system{};
|
||||
std::unique_ptr<EmuWindow_SDL3> emu_window;
|
||||
};
|
||||
|
||||
extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
SdlState* state = new SdlState();
|
||||
|
||||
/// Application entry point
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen("CONOUT$", "wb", stdout);
|
||||
@@ -203,7 +193,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
|
||||
if (argv_w == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
std::string filepath;
|
||||
@@ -257,7 +247,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp(argv[0]);
|
||||
return SDL_APP_FAILURE;
|
||||
return 0;
|
||||
case 'g':
|
||||
filepath = std::string(optarg);
|
||||
break;
|
||||
@@ -274,7 +264,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
if (!std::regex_match(str_arg, re)) {
|
||||
std::cout << "Wrong format for option --multiplayer\n";
|
||||
PrintHelp(argv[0]);
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::smatch match;
|
||||
@@ -289,11 +279,11 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||
if (!std::regex_match(nickname, nickname_re)) {
|
||||
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
if (address.empty()) {
|
||||
LOG_ERROR(Frontend, "Address to room must not be empty");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -306,7 +296,7 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
break;
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case 'n':
|
||||
force_null_render = true;
|
||||
break;
|
||||
@@ -368,73 +358,79 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
|
||||
if (filepath.empty()) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->system.Initialize();
|
||||
Core::System system{};
|
||||
system.Initialize();
|
||||
|
||||
InputCommon::InputSubsystem input_subsystem{};
|
||||
|
||||
// Apply the command line arguments
|
||||
state->system.ApplySettings();
|
||||
system.ApplySettings();
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL3> emu_window;
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
#ifdef HAS_OPENGL
|
||||
case Settings::RendererBackend::OpenGL_GLSL:
|
||||
case Settings::RendererBackend::OpenGL_GLASM:
|
||||
case Settings::RendererBackend::OpenGL_SPIRV:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
#endif
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
case Settings::RendererBackend::Null:
|
||||
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen);
|
||||
emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, system, fullscreen);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
Common::Windows::SetCurrentTimerResolutionToMaximum();
|
||||
state->system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
|
||||
system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
|
||||
#endif
|
||||
|
||||
state->system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
state->system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
|
||||
state->system.GetFileSystemController().CreateFactories(*state->system.GetFilesystem());
|
||||
state->system.GetUserChannel().clear();
|
||||
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
|
||||
system.GetUserChannel().clear();
|
||||
|
||||
Service::AM::FrontendAppletParameters load_parameters{
|
||||
.applet_id = Service::AM::AppletId::Application,
|
||||
};
|
||||
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters);
|
||||
const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};
|
||||
|
||||
switch (load_result) {
|
||||
case Core::SystemResultStatus::Success:
|
||||
break; // Expected case
|
||||
case Core::SystemResultStatus::ErrorGetLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorNotInitialized:
|
||||
LOG_CRITICAL(Frontend, "CPUCore not initialized");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::ErrorVideoCore:
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
|
||||
return SDL_APP_FAILURE;
|
||||
return -1;
|
||||
case Core::SystemResultStatus::Success:
|
||||
break; // Expected case
|
||||
default:
|
||||
const u16 loader_id = u16(Core::SystemResultStatus::ErrorLoader);
|
||||
const u16 error_id = u16(load_result) - loader_id;
|
||||
LOG_CRITICAL(Frontend,
|
||||
"While attempting to load the ROM requested, an error occurred. Please "
|
||||
"refer to the Eden wiki for more information or the Eden discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, Loader::ResultStatus(error_id));
|
||||
return SDL_APP_FAILURE;
|
||||
if (static_cast<u32>(load_result) >
|
||||
static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
|
||||
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
|
||||
const u16 error_id = static_cast<u16>(load_result) - loader_id;
|
||||
LOG_CRITICAL(Frontend,
|
||||
"While attempting to load the ROM requested, an error occurred. Please "
|
||||
"refer to the Eden wiki for more information or the Eden discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_multiplayer) {
|
||||
@@ -443,46 +439,40 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
||||
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
||||
member->BindOnStateChanged(OnStateChanged);
|
||||
member->BindOnError(OnNetworkError);
|
||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname);
|
||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
|
||||
nickname);
|
||||
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
|
||||
} else {
|
||||
LOG_ERROR(Network, "Could not access RoomMember");
|
||||
return SDL_APP_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
|
||||
state->system.GPU().Start();
|
||||
state->system.GetCpuManager().OnGpuReady();
|
||||
system.GPU().Start();
|
||||
system.GetCpuManager().OnGpuReady();
|
||||
|
||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
state->system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
state->system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
system.GetApplicationProcessProgramID(), std::stop_token{},
|
||||
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
|
||||
}
|
||||
|
||||
// don't do anything, SDL3 already exists for us :D
|
||||
state->system.RegisterExitCallback([] {});
|
||||
void(state->system.Run());
|
||||
if (state->system.DebuggerEnabled())
|
||||
state->system.InitializeDebugger();
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" SDL_AppResult SDL_AppIterate(void *appstate) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
return state->emu_window->IsOpen() ? SDL_APP_CONTINUE : SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
state->emu_window->OnEvent(*event);
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
extern "C" void SDL_AppQuit(void *appstate, SDL_AppResult result) {
|
||||
SdlState *state = (SdlState *)appstate;
|
||||
state->system.DetachDebugger();
|
||||
void(state->system.Pause());
|
||||
state->system.ShutdownMainProcess();
|
||||
delete state;
|
||||
system.RegisterExitCallback([&] {
|
||||
// Just exit right away.
|
||||
exit(0);
|
||||
});
|
||||
void(system.Run());
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.InitializeDebugger();
|
||||
}
|
||||
while (emu_window->IsOpen()) {
|
||||
emu_window->WaitEvent();
|
||||
}
|
||||
system.DetachDebugger();
|
||||
void(system.Pause());
|
||||
system.ShutdownMainProcess();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
|
||||
@@ -19,7 +19,6 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
|
||||
- `shellcheck.sh`: Ensure POSIX compliance (and syntax sanity) for all tools in this directory and subdirectories.
|
||||
- `llvmpipe-run.sh`: Sets environment variables needed to run any command (or Eden) with llvmpipe.
|
||||
- `optimize-assets.sh`: Optimizes PNG assets with OptiPng.
|
||||
- `setup-cross-sysroot.sh`: Allows to quickly create a sysroot of a given system for cross compilation (experimental).
|
||||
- `update-cpm.sh`: Updates CPM.cmake to the latest version.
|
||||
- `update-icons.sh`: Rebuild all icons (macOS, Windows, bitmaps) based on the master SVG file (`dist/dev.eden_emu.eden.svg`)
|
||||
* Also optimizes the master SVG
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
help() {
|
||||
cat << EOF
|
||||
--arch <name> Specify the target architecture (default: ppc64)
|
||||
--os <name> Specify the OS sysroot to use (default: freebsd)
|
||||
--sysroot <path> Specify sysroot to use
|
||||
EOF
|
||||
}
|
||||
|
||||
TARGET_ARCH="ppc64"
|
||||
TARGET_OS="freebsd"
|
||||
TARGET_CMAKE="$TARGET_ARCH-pc-$TARGET_OS"
|
||||
BASE_DIR="$PWD"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
--arch) shift; TARGET_ARCH=$1; [ -z "$TARGET_ARCH" ] && die "Expected argument";;
|
||||
--os) shift; TARGET_OS=$1; [ -z "$TARGET_OS" ] && die "Expected argument";;
|
||||
--sysroot) shift; SYSROOT=$1; [ -z "$SYSROOT" ] && die "Expected argument";;
|
||||
--help) help "$@";;
|
||||
--*) die "Invalid option $1" ;;
|
||||
"$0" | "") break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -z "$SYSROOT" ] && SYSROOT="$HOME/opt/$TARGET_ARCH-$TARGET_OS/sysroot"
|
||||
mkdir -p "$SYSROOT" && cd "$SYSROOT"
|
||||
case "$TARGET_OS" in
|
||||
freebsd*)
|
||||
case "$TARGET_ARCH" in
|
||||
ppc64pe) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64pe/14.3-RELEASE/base.txz" break;;
|
||||
ppc64le) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64le/14.3-RELEASE/base.txz" break;;
|
||||
ppc64) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc64/14.3-RELEASE/base.txz" break;;
|
||||
ppc) URL="https://download.freebsd.org/ftp/releases/powerpc/powerpc/14.3-RELEASE/base.txz" break;;
|
||||
amd64 | x86_64) URL="https://download.freebsd.org/ftp/releases/amd64/14.3-RELEASE/base.txz" break;;
|
||||
arm64*) URL="https://download.freebsd.org/ftp/releases/arm64/$TARGET_ARCH/14.3-RELEASE/base.txz" break;;
|
||||
arm*) URL="https://download.freebsd.org/ftp/releases/arm/$TARGET_ARCH/14.3-RELEASE/base.txz" break;;
|
||||
*) die "Unknown arch $TARGET_ARCH" break;;
|
||||
esac
|
||||
[ -z "$TARGET_CMAKE" ] && TARGET_CMAKE="$TARGET_ARCH-pc-$TARGET_OS"
|
||||
[ -f "base.txz" ] || fetch "$URL" || die "Can't download"
|
||||
tar -xvzf base.txz
|
||||
break;;
|
||||
*) die "Unknown OS $TARGET_OS" break;;
|
||||
esac
|
||||
|
||||
[ -z "$CC" ] && CC=$(which clang)
|
||||
[ -z "$CXX" ] && CXX=$(which clang++)
|
||||
|
||||
TOOLCHAIN_FILE="$BASE_DIR/$TARGET_CMAKE-toolchain.cmake"
|
||||
cat << EOF >"$TOOLCHAIN_FILE"
|
||||
# Script to generate .cmake toolchain files :)
|
||||
# See https://man.freebsd.org/cgi/man.cgi?query=cmake-toolchains&sektion=7&manpath=FreeBSD+13.2-RELEASE+and+Ports
|
||||
|
||||
set(CMAKE_SYSROOT "$SYSROOT")
|
||||
set(CMAKE_STAGING_PREFIX "$SYSROOT")
|
||||
|
||||
set(CMAKE_C_COMPILER $CC)
|
||||
set(CMAKE_CXX_COMPILER $CXX)
|
||||
set(CMAKE_C_FLAGS "--target=$TARGET_CMAKE")
|
||||
set(CMAKE_CXX_FLAGS "--target=$TARGET_CMAKE")
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
EOF
|
||||
|
||||
# cmake \
|
||||
# -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
|
||||
# -DCMAKE_BUILD_TYPE=Release \
|
||||
# -B "build-$TARGET_CMAKE" \
|
||||
# -DDYNARMIC_TESTS=ON \
|
||||
# -DENABLE_QT=OFF \
|
||||
# -DENABLE_SDL2=OFF \
|
||||
# -DYUZU_USE_CPM=ON \
|
||||
# -DYUZU_USE_EXTERNAL_FFMPEG=ON
|
||||
#cmake --build "build-$TARGET_CMAKE" dynarmic_tests -- -j8
|
||||
Reference in New Issue
Block a user