mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 01:00:17 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a1ca654db | ||
|
|
623b29de8c | ||
|
|
9be67fb22a | ||
|
|
cb9286c11e | ||
|
|
1e40148f60 | ||
|
|
d9d5303aa0 | ||
|
|
517b575783 | ||
|
|
605994dca2 | ||
|
|
ac97c05271 | ||
|
|
c8bb02f3a6 | ||
|
|
6b12c1768f | ||
|
|
693097c5b1 | ||
|
|
47e823b773 | ||
|
|
fd6d33d197 | ||
|
|
d8b583bc51 | ||
|
|
be41f14452 | ||
|
|
cf5bd202d4 | ||
|
|
8893cc6a7d | ||
|
|
1d82e9e59e | ||
|
|
5b017f9a66 | ||
|
|
3399052eaa | ||
|
|
3df951695e | ||
|
|
8d1f9a9081 | ||
|
|
68adb1ae12 | ||
|
|
4961604807 | ||
|
|
d6b7f32d33 | ||
|
|
6dc69063f3 | ||
|
|
2916c4f34c | ||
|
|
04137d0da5 | ||
|
|
afa81294c9 | ||
|
|
86bdbe65e5 | ||
|
|
63dee0e132 | ||
|
|
6618bb809b | ||
|
|
d2b19c0b1c | ||
|
|
90a8c4f114 | ||
|
|
866baf66db | ||
|
|
9498a41f9b | ||
|
|
1340ffabe4 | ||
|
|
b5115e096f | ||
|
|
235e05e8b9 | ||
|
|
26b4bd80bf | ||
|
|
a8881e8835 | ||
|
|
d1da1b0be5 | ||
|
|
a19c0184a6 | ||
|
|
2d0dced9e7 | ||
|
|
52cb96d630 | ||
|
|
94017d2e65 | ||
|
|
13b93ef6b5 | ||
|
|
c83a5d1bc3 | ||
|
|
c20777d558 | ||
|
|
9a0fc4e7e8 | ||
|
|
417e8836a4 | ||
|
|
237865e064 | ||
|
|
45223cc7e7 | ||
|
|
8d34222556 | ||
|
|
b6344c4753 | ||
|
|
c5bda47b2a | ||
|
|
5067737052 | ||
|
|
5143100a73 | ||
|
|
8b6e767742 | ||
|
|
331549c64a | ||
|
|
be0b29bbc6 | ||
|
|
ee8f8ed176 | ||
|
|
5b8cbae1e1 | ||
|
|
b98ef9e48b | ||
|
|
fd724448ab | ||
|
|
f4083883ca | ||
|
|
9a4fc758c1 | ||
|
|
40d2cd8992 | ||
|
|
ab44f800f7 | ||
|
|
1a5bc39d09 | ||
|
|
4bd997adaa | ||
|
|
244ef94e9b | ||
|
|
a85ef061d1 | ||
|
|
4e847d03b7 | ||
|
|
ce9324f50f | ||
|
|
0897fc4a0a | ||
|
|
d256bc59fd | ||
|
|
e0d6f4bc34 | ||
|
|
09284331a7 | ||
|
|
73e61b0c8e | ||
|
|
a30866a886 | ||
|
|
3e1da51cae | ||
|
|
4efe973b65 | ||
|
|
f6cc75de87 | ||
|
|
6b082f4ef7 | ||
|
|
8a73783a07 | ||
|
|
32b4b02302 | ||
|
|
528b01ad7a | ||
|
|
cd0492a143 | ||
|
|
26bf903b93 | ||
|
|
aae4de106a | ||
|
|
c495d8120d | ||
|
|
a6d9425091 | ||
|
|
b06ebf486f | ||
|
|
b17464d81a | ||
|
|
2ab8a97055 | ||
|
|
34a5eb6d6f | ||
|
|
2a1a863c58 | ||
|
|
bab5927931 | ||
|
|
6255eba524 | ||
|
|
37108058d1 | ||
|
|
c3ad571062 | ||
|
|
04e0baadc6 | ||
|
|
5ffbd97f01 | ||
|
|
7d4380fe6d | ||
|
|
7d5157b602 | ||
|
|
e9a32da997 | ||
|
|
b3564a4a48 | ||
|
|
2294419353 | ||
|
|
df8b78b327 | ||
|
|
2ea6a2a141 | ||
|
|
db776fae4e | ||
|
|
b45b7c3441 | ||
|
|
7805552edb | ||
|
|
520dfd7edf | ||
|
|
60671ee6cb | ||
|
|
6635765ea9 | ||
|
|
3e4ff23ab6 | ||
|
|
93ed411899 | ||
|
|
0b0db08ebf | ||
|
|
3419f40c00 | ||
|
|
8260ddda13 | ||
|
|
872e49f02a | ||
|
|
f0f6bc7a73 | ||
|
|
5820c251e8 | ||
|
|
eff97509c0 | ||
|
|
3128d0c148 | ||
|
|
0216d7b552 | ||
|
|
87b473c7ee | ||
|
|
f9d647c852 | ||
|
|
2c175f1e0b | ||
|
|
96813b0d6c | ||
|
|
967ac863be | ||
|
|
fa546fb492 | ||
|
|
99ab9864aa | ||
|
|
11246355e2 | ||
|
|
0d236940e0 | ||
|
|
a0afb4be5a | ||
|
|
ca45485d48 | ||
|
|
c8bb0e2d51 | ||
|
|
e9aeebb3a1 | ||
|
|
1ad46c5af1 | ||
|
|
08bc7a8364 | ||
|
|
e7179ff84b | ||
|
|
13bfdcad06 | ||
|
|
38a707b38e | ||
|
|
ada7915b01 | ||
|
|
9775a2148e | ||
|
|
bc8696828b | ||
|
|
7d792e65f0 | ||
|
|
14b5b881c5 | ||
|
|
830e41459c | ||
|
|
0db99ea4f6 | ||
|
|
22f1cf0e2a | ||
|
|
80493487db | ||
|
|
59ce750af6 | ||
|
|
4ac81e22de | ||
|
|
a0a382cc5c | ||
|
|
6cc7d065aa | ||
|
|
93b9ca4bdb | ||
|
|
cc0c45cf47 | ||
|
|
4a504bdd00 | ||
|
|
66c8dd2013 | ||
|
|
91ac4ccdd8 | ||
|
|
849b83bd02 | ||
|
|
286f103dab | ||
|
|
2a49037789 | ||
|
|
235f6fbe39 | ||
|
|
b43db294ec | ||
|
|
5e6c6adf8b | ||
|
|
50c3ee047c | ||
|
|
c0ef6da6a8 | ||
|
|
2063411443 | ||
|
|
4a5eefec25 | ||
|
|
eaad9e99a9 | ||
|
|
62406a45be | ||
|
|
03537d351d | ||
|
|
0cb53fff89 | ||
|
|
3e250fa071 | ||
|
|
1d4b81f556 | ||
|
|
c26f19ed4d | ||
|
|
9619cea097 | ||
|
|
44dbecc2ac | ||
|
|
415632209f | ||
|
|
b7e81e867d | ||
|
|
4f276aae82 | ||
|
|
c80254f6f4 | ||
|
|
8adb1ca3a2 | ||
|
|
0dd1959d65 | ||
|
|
f4ab7cceab | ||
|
|
8cc799ab65 | ||
|
|
acb329b8e7 | ||
|
|
3020e39428 | ||
|
|
4a094c5dee | ||
|
|
f8eabb4f33 | ||
|
|
4da492d361 | ||
|
|
a200d2c91c | ||
|
|
f1663abe81 | ||
|
|
af205e9f5f | ||
|
|
032d1fbba2 | ||
|
|
313c4bb784 | ||
|
|
ceb484fd3a | ||
|
|
b98c7bd853 | ||
|
|
4c045148b6 | ||
|
|
9f29bba4aa | ||
|
|
2c592bd8a8 | ||
|
|
8319a85524 | ||
|
|
8ad45071e4 | ||
|
|
3379f3a8f9 | ||
|
|
c76205040e | ||
|
|
648ef617cf | ||
|
|
5fac51f659 | ||
|
|
5d63dc179c | ||
|
|
858566c356 | ||
|
|
fb94e62df1 | ||
|
|
51e4fe7f0a | ||
|
|
be03767c0b | ||
|
|
adeea56ea8 | ||
|
|
6923c47a70 | ||
|
|
26d9ddef9f | ||
|
|
c6c2555e1b | ||
|
|
283a5c1293 | ||
|
|
b31976d0e2 | ||
|
|
f922e6420b | ||
|
|
7ff06c828f | ||
|
|
be16cc1456 | ||
|
|
59df3d13cc | ||
|
|
1adf518e6f | ||
|
|
68171ad9b5 | ||
|
|
78a923b5b0 | ||
|
|
0c6741438b | ||
|
|
5d0c080a9d | ||
|
|
5f49b57948 | ||
|
|
38778953a1 | ||
|
|
21a364c2ad | ||
|
|
3294cc209f | ||
|
|
ae5d41e46b | ||
|
|
43f83fd6d2 | ||
|
|
8cf47f8221 | ||
|
|
43b6cc1cb9 | ||
|
|
1ab6726498 | ||
|
|
c21acd0d45 | ||
|
|
e637751112 | ||
|
|
fac6377ed3 | ||
|
|
4f3cb933cb | ||
|
|
b2825bd848 | ||
|
|
83eb7f4ad3 | ||
|
|
c3854c211a | ||
|
|
61cd3eb3ce | ||
|
|
490352f568 | ||
|
|
4004d5a3b7 | ||
|
|
761447467e | ||
|
|
3f38ed94ca | ||
|
|
ad877d4088 | ||
|
|
92fc6b1909 | ||
|
|
4b11dcb72a | ||
|
|
dd7c78dcd7 | ||
|
|
a20ef403c1 | ||
|
|
be06511d3d | ||
|
|
b7a60337e8 | ||
|
|
315b8e9c1e | ||
|
|
155a2b0194 | ||
|
|
8c874f4540 | ||
|
|
942b8549c6 | ||
|
|
fa577b4527 | ||
|
|
15537f8c8c | ||
|
|
434a74a88a | ||
|
|
1fdd6fbb14 | ||
|
|
458bebf598 | ||
|
|
2b10b9792b | ||
|
|
0dd02da57c | ||
|
|
309139e203 | ||
|
|
350c33ada4 | ||
|
|
bdd35e5743 | ||
|
|
ceb7082e37 | ||
|
|
2d6dc80039 | ||
|
|
c92df21627 | ||
|
|
539de0492e | ||
|
|
efd41de8ea | ||
|
|
99ae862875 | ||
|
|
b81ea43601 | ||
|
|
fcd4974cff | ||
|
|
8550c9f2dd | ||
|
|
15dd70487f | ||
|
|
8fad0c8fdb | ||
|
|
b6c0a9f2a0 | ||
|
|
b5a6dd031b | ||
|
|
650877adca | ||
|
|
432b6fccf2 | ||
|
|
47f3c983ee | ||
|
|
9565247ada | ||
|
|
c59b9fbede | ||
|
|
7c96893579 | ||
|
|
8ebef457fc | ||
|
|
9eee879be0 | ||
|
|
64affa8c8e | ||
|
|
dc041bdf0e | ||
|
|
51c43a0761 | ||
|
|
aa67142e6f | ||
|
|
ae569da895 | ||
|
|
d77acdf474 | ||
|
|
264ec44276 | ||
|
|
da49eb3394 | ||
|
|
5447ec3ec8 | ||
|
|
ff3974c4a1 | ||
|
|
358f12e074 | ||
|
|
c5648ac84c | ||
|
|
30e1f871ad | ||
|
|
5c624684b7 | ||
|
|
ef11b534ef | ||
|
|
cd32eeb428 | ||
|
|
43984c810d | ||
|
|
a50689aa8b | ||
|
|
311d6d4385 | ||
|
|
a19c59f5b2 | ||
|
|
6a8f68022f | ||
|
|
fdcb6206da | ||
|
|
6ea977ba34 | ||
|
|
9b35cb4408 | ||
|
|
afa869e1f2 | ||
|
|
43ddba4e82 | ||
|
|
e3380957ba | ||
|
|
21687f4dc5 | ||
|
|
41a1a9d400 | ||
|
|
82c2b49a2e | ||
|
|
fbc49c1648 | ||
|
|
d2f636893d | ||
|
|
e5a9bd4d3e | ||
|
|
ca24ea1d36 | ||
|
|
998e53aa70 | ||
|
|
de890e7387 | ||
|
|
042a71be96 | ||
|
|
0a61741596 | ||
|
|
91a5a625ac | ||
|
|
f68593ea97 | ||
|
|
e15730782b | ||
|
|
fc27893f20 | ||
|
|
5b8bf8dd24 | ||
|
|
0c48f1409e | ||
|
|
8c1f2eb0b3 | ||
|
|
07613456d2 | ||
|
|
493bb3bef3 | ||
|
|
cf8572e090 | ||
|
|
d120f12c93 | ||
|
|
695322d446 | ||
|
|
d34cde12ae | ||
|
|
a7dc9d00d2 | ||
|
|
dbde2e400e | ||
|
|
bdeefa7bf0 | ||
|
|
51e513df14 | ||
|
|
0106b362d6 | ||
|
|
c7f265158f | ||
|
|
fbf5325bdd | ||
|
|
9b2bd8df28 | ||
|
|
2a074204bc | ||
|
|
d79fb37135 | ||
|
|
d01b40c5aa | ||
|
|
e871a43387 | ||
|
|
ff45b37904 | ||
|
|
62aab57ef7 | ||
|
|
208fa0d1fc | ||
|
|
634d4fead0 | ||
|
|
0bdddedfe6 | ||
|
|
971740991b | ||
|
|
cb672e035c | ||
|
|
4f64ba582c | ||
|
|
fbe2583a04 | ||
|
|
befe9dcbae | ||
|
|
aacb0f6891 | ||
|
|
040cc746e7 | ||
|
|
e0c5840f2c | ||
|
|
48027f1d2e | ||
|
|
9dc0717cdb | ||
|
|
8de7dd8fd0 | ||
|
|
79e9477b14 | ||
|
|
0b8f29000e | ||
|
|
e530e3676f | ||
|
|
0c5e5dbdf2 | ||
|
|
d8591a8f14 | ||
|
|
941107120c | ||
|
|
ffc17c90c5 | ||
|
|
5285f4baa6 | ||
|
|
037781c4d0 | ||
|
|
4d2221a456 | ||
|
|
466dc03c19 | ||
|
|
5e1e09a43d | ||
|
|
3a0b475cfa | ||
|
|
4ece56ac4d | ||
|
|
ea7af248e8 | ||
|
|
42b186a76c | ||
|
|
bdca7109a4 | ||
|
|
074bedf25c | ||
|
|
bdb199b6d6 | ||
|
|
e9afabc4cb | ||
|
|
da1532f3b8 | ||
|
|
c73d2b2bca | ||
|
|
da43db8787 | ||
|
|
ab8aff6e0d | ||
|
|
8a7caa82b2 | ||
|
|
2706c9aaae | ||
|
|
96f2a461f8 | ||
|
|
cf966eb2e0 | ||
|
|
6b860cd68e | ||
|
|
6b4ccc46a8 | ||
|
|
8acb18b2b9 | ||
|
|
832a634f64 | ||
|
|
ec196b47e2 | ||
|
|
4dc2cf2b32 | ||
|
|
e264194453 | ||
|
|
ddce28df11 | ||
|
|
fc5bf5d261 | ||
|
|
45e5e58c7e | ||
|
|
0dc7fd75e1 | ||
|
|
10fbf60259 | ||
|
|
4e55f29589 | ||
|
|
1ddfa3e7ed | ||
|
|
ec3883181b | ||
|
|
e2ec645855 | ||
|
|
f0d3004363 | ||
|
|
e708b83dbb | ||
|
|
a9d3851684 | ||
|
|
7e4ba77d72 | ||
|
|
449ea3e80b | ||
|
|
8fd91a5b7a | ||
|
|
e3d6db2eb5 | ||
|
|
d930de30ef | ||
|
|
b36378369a | ||
|
|
cbf4c9a687 | ||
|
|
bcebe7d47a | ||
|
|
e9b057bc8b | ||
|
|
afda855caf | ||
|
|
6ed36e3304 | ||
|
|
35c01038ba | ||
|
|
43a087d554 | ||
|
|
a1fed323df | ||
|
|
0bcd1c7df5 | ||
|
|
a3ba1ae199 | ||
|
|
a2278cfb1e | ||
|
|
98cb54e3e4 | ||
|
|
cb17127ccb | ||
|
|
93b6dbdc2b | ||
|
|
bea0adc3d1 | ||
|
|
9c339c85c6 | ||
|
|
9cf7545f8d | ||
|
|
c657165e56 | ||
|
|
05ae7d3c5e | ||
|
|
f94529ebb2 | ||
|
|
852ab4e245 | ||
|
|
bb6e7360a2 | ||
|
|
50ae18cbaf | ||
|
|
1adbf66fee | ||
|
|
8222acdf20 |
No files matched your search
@@ -13,18 +13,22 @@ env:
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
FEX_FORCE32BITALLOCATOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.arch }}
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [[self-hosted, x64], [self-hosted, ARMv8.0], [self-hosted, ARMv8.2]]
|
||||
arch: [[self-hosted, x64], [self-hosted, ARMv8.0], [self-hosted, ARMv8.2], [self-hosted, ARMv8.4]]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set runner label
|
||||
run: echo "runner_label=${{ matrix.arch[1] }}" >> $GITHUB_ENV
|
||||
|
||||
- name : submodule checkout
|
||||
# Need to update submodules
|
||||
run: git submodule update --init --depth 1
|
||||
@@ -45,7 +49,7 @@ jobs:
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@@ -113,13 +117,36 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC64.log || true
|
||||
|
||||
- name: gcc target tests 32
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the gvisor tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target gcc_target_tests_32
|
||||
|
||||
- name: GCC32 Test Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_GCC32.log || true
|
||||
|
||||
- name: Struct verifier tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target struct_verifier
|
||||
|
||||
- name: Struct verifier Test Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_StructVerifier.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Cap out the log files at 20M in case something crash spins and dumps fault text
|
||||
# ASM tests get quite close to 10MB
|
||||
run: truncate --size=20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
|
||||
run: truncate --size=<20M ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log || true
|
||||
|
||||
- name: Set runner name
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -30,3 +30,6 @@
|
||||
shallow = true
|
||||
path = External/fex-gcc-target-tests-bins
|
||||
url = https://github.com/FEX-Emu/fex-gcc-target-tests-bins.git
|
||||
[submodule "External/jemalloc"]
|
||||
path = External/jemalloc
|
||||
url = https://github.com/FEX-Emu/jemalloc.git
|
||||
+140
-33
@@ -12,6 +12,8 @@ option(ENABLE_ASAN "Enables Clang ASAN" FALSE)
|
||||
option(ENABLE_TSAN "Enables Clang TSAN" FALSE)
|
||||
option(ENABLE_ASSERTIONS "Enables assertions in build" FALSE)
|
||||
option(ENABLE_VISUAL_DEBUGGER "Enables the visual debugger for compiling" FALSE)
|
||||
option(ENABLE_STRICT_WERROR "Enables stricter -Werror for CI" FALSE)
|
||||
option(ENABLE_WERROR "Enables -Werror" FALSE)
|
||||
|
||||
set (X86_C_COMPILER "x86_64-linux-gnu-gcc" CACHE STRING "c compiler for compiling x86 guest libs")
|
||||
set (X86_CXX_COMPILER "x86_64-linux-gnu-g++" CACHE STRING "c++ compiler for compiling x86 guest libs")
|
||||
@@ -27,14 +29,6 @@ if (ENABLE_ASSERTIONS)
|
||||
add_definitions(-DASSERTIONS_ENABLED=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_IWYU)
|
||||
find_program(IWYU_EXE "iwyu")
|
||||
if (IWYU_EXE)
|
||||
message(STATUS "IWYU enabled")
|
||||
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin)
|
||||
@@ -67,8 +61,8 @@ endif()
|
||||
|
||||
if (ENABLE_ASAN)
|
||||
add_definitions(-DENABLE_ASAN=1)
|
||||
add_compile_options(-fno-omit-frame-pointer -fsanitize=address)
|
||||
link_libraries(-fno-omit-frame-pointer -fsanitize=address)
|
||||
add_compile_options(-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope)
|
||||
link_libraries(-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope)
|
||||
endif()
|
||||
|
||||
if (ENABLE_TSAN)
|
||||
@@ -83,6 +77,14 @@ set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer")
|
||||
set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-pointer")
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE)
|
||||
if (NOT ENABLE_X86_HOST_DEBUG)
|
||||
message(FATAL_ERROR
|
||||
" Be warned: FEX isn't optimized for x86_64 hosts!\n"
|
||||
" Support for x86_64 hosts is only for debugging and convenience!\n"
|
||||
" Don't expect amazing performance or optimal code generation!\n"
|
||||
" Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!")
|
||||
endif()
|
||||
set(_M_X86_64 1)
|
||||
add_definitions(-D_M_X86_64=1)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
@@ -91,23 +93,26 @@ endif()
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
set(_M_ARM_64 1)
|
||||
add_definitions(-D_M_ARM_64=1)
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(External/vixl/src/)
|
||||
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
add_definitions(-DVIXL_DEBUG=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(External/vixl/src/)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# This means we were attempted to get compiled with GCC
|
||||
message(FATAL_ERROR "FEX doesn't support getting compiled with GCC!")
|
||||
endif()
|
||||
|
||||
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter Development)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Python 3.0 REQUIRED COMPONENTS Interpreter)
|
||||
pkg_check_modules(XXHASH libxxhash REQUIRED)
|
||||
|
||||
add_definitions(-Wno-trigraphs)
|
||||
add_definitions(-DGLOBAL_DATA_DIRECTORY="${DATA_DIRECTORY}/")
|
||||
|
||||
add_subdirectory(External/jemalloc/)
|
||||
include_directories(External/jemalloc/pregen/include/)
|
||||
|
||||
add_subdirectory(External/cpp-optparse/)
|
||||
include_directories(External/cpp-optparse/)
|
||||
|
||||
@@ -149,32 +154,115 @@ if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
|
||||
if(_M_ARM_64)
|
||||
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
|
||||
# Manually detect newer CPU revisions until clang and llvm fixes their bug
|
||||
# This script will either provide a supported CPU or 'native'
|
||||
# Additionally -march doesn't work under AArch64+Clang, so you have to use -mcpu or -mtune
|
||||
execute_process(COMMAND python3 "${PROJECT_SOURCE_DIR}/Scripts/aarch64_fit_native.py" "/proc/cpuinfo"
|
||||
OUTPUT_VARIABLE AARCH64_CPU)
|
||||
|
||||
string(STRIP ${AARCH64_CPU} AARCH64_CPU)
|
||||
|
||||
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
|
||||
if(COMPILER_SUPPORTS_CPU_TYPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${AARCH64_CPU}")
|
||||
if(ENABLE_WERROR OR ENABLE_STRICT_WERROR)
|
||||
add_compile_options(-Werror)
|
||||
if (NOT ENABLE_STRICT_WERROR)
|
||||
# Disable some Werror that can add frustration when developing
|
||||
add_compile_options(-Wno-error=unused-variable)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(_M_ARM_64)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 999999.0)
|
||||
# Clang 12.0 fixed the -mcpu=native bug with mixed big.little implementers
|
||||
# Clang can not currently check for native Apple M1 type in hypervisor. Currently disabled
|
||||
check_cxx_compiler_flag("-mcpu=native" COMPILER_SUPPORTS_CPU_TYPE)
|
||||
if(COMPILER_SUPPORTS_CPU_TYPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
|
||||
endif()
|
||||
else()
|
||||
# Due to an oversight in llvm, it declares any reasonably new Kryo CPU to only be ARMv8.0
|
||||
# Manually detect newer CPU revisions until clang and llvm fixes their bug
|
||||
# This script will either provide a supported CPU or 'native'
|
||||
# Additionally -march doesn't work under AArch64+Clang, so you have to use -mcpu or -mtune
|
||||
execute_process(COMMAND python3 "${PROJECT_SOURCE_DIR}/Scripts/aarch64_fit_native.py" "/proc/cpuinfo" "${CMAKE_CXX_COMPILER_VERSION}"
|
||||
OUTPUT_VARIABLE AARCH64_CPU)
|
||||
|
||||
string(STRIP ${AARCH64_CPU} AARCH64_CPU)
|
||||
|
||||
check_cxx_compiler_flag("-mcpu=${AARCH64_CPU}" COMPILER_SUPPORTS_CPU_TYPE)
|
||||
if(COMPILER_SUPPORTS_CPU_TYPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${AARCH64_CPU}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_IWYU)
|
||||
find_program(IWYU_EXE "iwyu")
|
||||
if (IWYU_EXE)
|
||||
message(STATUS "IWYU enabled")
|
||||
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_CLANG_FORMAT)
|
||||
find_program(CLANG_TIDY_EXE "clang-tidy")
|
||||
if (NOT CLANG_TIDY_EXE)
|
||||
message(FATAL_ERROR "Couldn't find clang-tidy")
|
||||
endif()
|
||||
|
||||
set(CLANG_TIDY_FLAGS
|
||||
"-checks=*"
|
||||
"-fuchsia*"
|
||||
"-bugprone-macro-parentheses"
|
||||
"-clang-analyzer-core.*"
|
||||
"-cppcoreguidelines-pro-type-*"
|
||||
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
|
||||
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
|
||||
"-cppcoreguidelines-avoid-c-arrays"
|
||||
"-cppcoreguidelines-avoid-magic-numbers"
|
||||
"-cppcoreguidelines-pro-bounds-constant-array-index"
|
||||
"-cppcoreguidelines-no-malloc"
|
||||
"-cppcoreguidelines-special-member-functions"
|
||||
"-cppcoreguidelines-owning-memory"
|
||||
"-cppcoreguidelines-macro-usage"
|
||||
"-cppcoreguidelines-avoid-goto"
|
||||
"-google-readability-function-size"
|
||||
"-google-readability-namespace-comments"
|
||||
"-google-readability-braces-around-statements"
|
||||
"-google-build-using-namespace"
|
||||
"-hicpp-*"
|
||||
"-llvm-namespace-comment"
|
||||
"-llvm-include-order" # Messes up with case sensitivity
|
||||
"-llvmlibc-*"
|
||||
"-misc-unused-parameters"
|
||||
"-modernize-loop-convert"
|
||||
"-modernize-use-auto"
|
||||
"-modernize-avoid-c-arrays"
|
||||
"-modernize-use-nodiscard"
|
||||
"readability-*"
|
||||
"-readability-function-size"
|
||||
"-readability-implicit-bool-conversion"
|
||||
"-readability-braces-around-statements"
|
||||
"-readability-else-after-return"
|
||||
"-readability-magic-numbers"
|
||||
"-readability-named-parameter"
|
||||
"-readability-uppercase-literal-suffix"
|
||||
"-cert-err34-c"
|
||||
"-cert-err58-cpp"
|
||||
"-bugprone-exception-escape"
|
||||
)
|
||||
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
|
||||
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
|
||||
endif()
|
||||
|
||||
add_compile_options(-Wall)
|
||||
|
||||
add_subdirectory(External/FEXCore)
|
||||
|
||||
add_subdirectory(Source/)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/Config.h.in
|
||||
${CMAKE_BINARY_DIR}/generated/Config.h)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
message(STATUS "Unit tests are enabled")
|
||||
endif()
|
||||
add_subdirectory(External/FEXCore)
|
||||
|
||||
add_subdirectory(Source/)
|
||||
add_subdirectory(Data/AppConfig/)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(unittests/)
|
||||
endif()
|
||||
|
||||
@@ -185,16 +273,35 @@ if (BUILD_THUNKS)
|
||||
PREFIX host-libs
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/HostLibs"
|
||||
BINARY_DIR "Host"
|
||||
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: host-libs\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkHostsInstall
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Host
|
||||
)"
|
||||
DEPENDS host-libs
|
||||
)
|
||||
|
||||
ExternalProject_Add(guest-libs
|
||||
PREFIX guest-libs
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
|
||||
BINARY_DIR "Guest"
|
||||
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}"
|
||||
CMAKE_ARGS "-DX86_C_COMPILER:STRING=${X86_C_COMPILER}" "-DX86_CXX_COMPILER:STRING=${X86_CXX_COMPILER}" "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: guest-libs\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target ThunkGuestsInstall
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
|
||||
)"
|
||||
DEPENDS guest-libs
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,25 @@
|
||||
file(GLOB CONFIG_SOURCES CONFIGURE_DEPENDS *.json)
|
||||
file(GLOB GEN_CONFIG_SOURCES CONFIGURE_DEPENDS *.json.in)
|
||||
|
||||
# Any application configuration json file gets installed
|
||||
foreach(CONFIG_SRC ${CONFIG_SOURCES})
|
||||
install(FILES ${CONFIG_SRC}
|
||||
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
|
||||
endforeach()
|
||||
|
||||
# Any configuration file json file that needs to be generated
|
||||
# First generate then install it
|
||||
foreach(GEN_CONFIG_SRC ${GEN_CONFIG_SOURCES})
|
||||
# Get the filename only component
|
||||
get_filename_component(CONFIG_NAME ${GEN_CONFIG_SRC} NAME_WE)
|
||||
|
||||
# Configure it
|
||||
configure_file(
|
||||
${GEN_CONFIG_SRC}
|
||||
${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json)
|
||||
|
||||
# Then install the configured json
|
||||
install(
|
||||
FILES ${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json
|
||||
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
|
||||
endforeach()
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"Env": "STEAM_GAME_LAUNCH_SHELL=@CMAKE_INSTALL_PREFIX@/bin/FEXBash"
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -4,7 +4,8 @@ FROM ubuntu:20.04 as builder
|
||||
RUN DEBIAN_FRONTEND="noninteractive" apt-get update
|
||||
RUN DEBIAN_FRONTEND="noninteractive" apt install -y cmake \
|
||||
clang-10 llvm-10 nasm ninja-build libnuma-dev \
|
||||
libcap-dev libglfw3-dev libepoxy-dev
|
||||
libcap-dev libglfw3-dev libepoxy-dev python3-dev \
|
||||
python3 linux-headers-generic
|
||||
|
||||
COPY . /opt/FEX
|
||||
|
||||
|
||||
Vendored
+12
-12
@@ -4,6 +4,17 @@ project(${PROJECT_NAME}
|
||||
VERSION 0.01
|
||||
LANGUAGES CXX)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(_M_X86_64 1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
set(_M_ARM_64 1)
|
||||
endif()
|
||||
|
||||
set(ENABLE_JIT_X86_64 ${_M_X86_64} CACHE BOOL "Enable the x86_64 JIT")
|
||||
set(ENABLE_JIT_ARM64 ${_M_ARM_64} CACHE BOOL "Enable the ARM64 JIT")
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
option(ENABLE_JITSYMBOLS "Enable visibility of JITSymbols in profiling tools" FALSE)
|
||||
|
||||
@@ -17,22 +28,11 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(_M_X86_64 1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
message(STATUS "Enabling x86-64 JIT")
|
||||
set(ENABLE_JIT 1)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
message(STATUS "Enabling AArch64 JIT")
|
||||
set(_M_ARM_64 1)
|
||||
set(ENABLE_JIT 1)
|
||||
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
|
||||
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
|
||||
# Useful to have for freestanding libFEXCore
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(External/vixl/src/)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
import datetime
|
||||
import json
|
||||
import sys
|
||||
|
||||
def print_header():
|
||||
header = '''#ifndef OPT_BASE
|
||||
#define OPT_BASE(type, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_BOOL
|
||||
#define OPT_BOOL(group, enum, json, default) OPT_BASE(bool, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_UINT8
|
||||
#define OPT_UINT8(group, enum, json, default) OPT_BASE(uint8_t, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_INT32
|
||||
#define OPT_INT32(group, enum, json, default) OPT_BASE(int32_t, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_UINT32
|
||||
#define OPT_UINT32(group, enum, json, default) OPT_BASE(uint32_t, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_UINT64
|
||||
#define OPT_UINT64(group, enum, json, default) OPT_BASE(uint64_t, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_STR
|
||||
#define OPT_STR(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
|
||||
#endif
|
||||
#ifndef OPT_STRARRAY
|
||||
#define OPT_STRARRAY(group, enum, json, default) OPT_BASE(std::string, group, enum, json, default)
|
||||
#endif
|
||||
|
||||
'''
|
||||
output_file.write(header)
|
||||
|
||||
def print_tail():
|
||||
tail = '''#undef OPT_BASE
|
||||
#undef OPT_BOOL
|
||||
#undef OPT_UINT8
|
||||
#undef OPT_INT32
|
||||
#undef OPT_UINT32
|
||||
#undef OPT_UINT64
|
||||
#undef OPT_STR
|
||||
#undef OPT_STRARRAY
|
||||
'''
|
||||
output_file.write(tail)
|
||||
|
||||
def print_config(type, group_name, json_name, default_value):
|
||||
output_file.write("OPT_{0} ({1}, {2}, {3}, {4})\n".format(type.upper(), group_name.upper(), json_name.upper(), json_name, default_value))
|
||||
|
||||
def print_options(options):
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
default = op_vals["Default"]
|
||||
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
|
||||
# Wrap the string argument in quotes
|
||||
default = "\"" + default + "\""
|
||||
|
||||
print_config(
|
||||
op_vals["Type"],
|
||||
op_group,
|
||||
op_key,
|
||||
default)
|
||||
|
||||
output_file.write("\n")
|
||||
|
||||
def print_unnamed_options(options):
|
||||
output_file.write("// Unnamed configuration options\n")
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
default = op_vals["Default"]
|
||||
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
|
||||
# Wrap the string argument in quotes
|
||||
default = "\"" + default + "\""
|
||||
|
||||
print_config(
|
||||
op_vals["Type"],
|
||||
op_group,
|
||||
op_key.upper(), # KEY is the enum here, there is no json configuration for these
|
||||
default)
|
||||
|
||||
output_file.write("\n")
|
||||
|
||||
def print_man_option(short, long, desc, default):
|
||||
if (short != None):
|
||||
output_man.write(".It Fl {0} , ".format(short))
|
||||
else:
|
||||
output_man.write(".It ")
|
||||
|
||||
output_man.write("Fl Fl {0}=".format(long))
|
||||
|
||||
output_man.write("\n");
|
||||
|
||||
# Print description
|
||||
for line in desc:
|
||||
output_man.write(".Pp\n")
|
||||
output_man.write("{0}\n".format(line))
|
||||
|
||||
output_man.write(".Pp\n")
|
||||
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
|
||||
output_man.write(".Pp\n\n")
|
||||
|
||||
def print_man_env_option(name, desc, default):
|
||||
output_man.write("\\fBFEX_{0}\\fR\n".format(name))
|
||||
|
||||
# Print description
|
||||
for line in desc:
|
||||
output_man.write(".Pp\n")
|
||||
output_man.write("{0}\n".format(line))
|
||||
|
||||
output_man.write(".Pp\n")
|
||||
output_man.write("\\fBdefault:\\fR {0}\n".format(default))
|
||||
output_man.write(".Pp\n\n")
|
||||
|
||||
def print_man_options(options):
|
||||
output_man.write(".Sh OPTIONS\n")
|
||||
output_man.write(".Bl -tag -width -indent\n")
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
short = None
|
||||
long = op_key.lower()
|
||||
|
||||
if ("ShortArg" in op_vals):
|
||||
short = op_vals["ShortArg"]
|
||||
|
||||
default = op_vals["Default"]
|
||||
|
||||
# Textual default rather than enum based
|
||||
if ("TextDefault" in op_vals):
|
||||
default = op_vals["TextDefault"]
|
||||
|
||||
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
|
||||
# Wrap the string argument in quotes
|
||||
default = "'" + default + "'"
|
||||
print_man_option(
|
||||
short,
|
||||
long,
|
||||
op_vals["Desc"],
|
||||
default
|
||||
)
|
||||
|
||||
output_man.write(".El\n")
|
||||
|
||||
def print_man_environment(options):
|
||||
output_man.write(".Sh ENVIRONMENT\n")
|
||||
output_man.write(".Bl -tag -width -indent\n")
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
default = op_vals["Default"]
|
||||
|
||||
# Textual default rather than enum based
|
||||
if ("TextDefault" in op_vals):
|
||||
default = op_vals["TextDefault"]
|
||||
|
||||
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
|
||||
# Wrap the string argument in quotes
|
||||
default = "'" + default + "'"
|
||||
print_man_env_option(
|
||||
op_key.upper(),
|
||||
op_vals["Desc"],
|
||||
default
|
||||
)
|
||||
|
||||
print_man_environment_tail()
|
||||
output_man.write(".El\n")
|
||||
|
||||
def print_man_environment_tail():
|
||||
|
||||
# Additional environment variables that live outside of the normal loop
|
||||
print_man_env_option(
|
||||
"FEX_APP_CONFIG_LOCATION",
|
||||
[
|
||||
"Allows the user to override where FEX looks for configuration files",
|
||||
"By default FEX will look in {$HOME, $XDG_CONFIG_HOME}/.fex-emu/",
|
||||
"This will override the full path",
|
||||
],
|
||||
"''")
|
||||
|
||||
print_man_env_option(
|
||||
"FEX_APP_CONFIG",
|
||||
[
|
||||
"Allows the user to override where FEX looks for only the application config file",
|
||||
"By default FEX will look in {$HOME, $XDG_CONFIG_HOME}/.fex-emu/Config.json",
|
||||
"This will override this file location",
|
||||
"One must be careful with this option as it will override any applications that load with execve as well"
|
||||
"If you need to support applications that execve then use FEX_APP_CONFIG_LOCATION instead"
|
||||
],
|
||||
"''")
|
||||
|
||||
print_man_env_option(
|
||||
"FEX_APP_DATA_LOCATION",
|
||||
[
|
||||
"Allows the user to override where FEX looks for data files",
|
||||
"By default FEX will look in {$HOME, $XDG_DATA_HOME}/.fex-emu/",
|
||||
"This will override the full path",
|
||||
"This is the folder where FEX stores generated files like IR cache"
|
||||
],
|
||||
"''")
|
||||
|
||||
def print_man_header():
|
||||
header ='''.Dd {0}
|
||||
.Dt FEX
|
||||
.Os Linux
|
||||
.Sh NAME
|
||||
.Nm FEXLoader
|
||||
.Nm FEXInterpreter
|
||||
.Nm FEXBash
|
||||
.Nd Fast x86-64 and x86 emulation.
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op options
|
||||
.Op Ar --
|
||||
.Ar Application
|
||||
<args> ...
|
||||
.Pp
|
||||
.Nm FEXInterpreter
|
||||
.Ar Application
|
||||
<args> ...
|
||||
.Pp
|
||||
.Nm FEXBash
|
||||
.Ar <args> ...
|
||||
.Sh DESCRIPTION
|
||||
FEX allows you to run x86 and x86-64 binaries on an AArch64 host, similar to qemu-user and box86.
|
||||
It has native support for a rootfs overlay, so you don't need to chroot, as well as some thunklibs so it can forward things like GL to the host.
|
||||
FEX presents a Linux 5.0 interface to the guest, and supports both AArch64 and x86-64 as hosts.
|
||||
FEX is very much work in progress, so expect things to change.
|
||||
'''
|
||||
output_man.write(header.format(datetime.datetime.now().strftime("%d-%m-%Y")))
|
||||
|
||||
def print_man_tail():
|
||||
tail ='''.Sh FILES
|
||||
.Bl -tag -width "$prefix/share/fex-emu/GuestThunks" -compact
|
||||
.It Pa $XDG_HOME_DIR/.fex-emu
|
||||
Default FEX user configuration directory
|
||||
.It Pa $prefix/share/fex-emu/AppConfig
|
||||
System level application configuration files
|
||||
.It Pa $prefix/share/fex-emu/GuestThunks
|
||||
guest-side thunk data libraries
|
||||
.It Pa $prefix/lib/fex-emu/HostThunks
|
||||
host-side thunks for guest communication
|
||||
.El
|
||||
'''
|
||||
output_man.write(tail)
|
||||
|
||||
def print_config_option(type, group_name, json_name, default_value, short, choices, desc):
|
||||
if (type == "bool"):
|
||||
# Bool gets some special handling to add an inverted case
|
||||
output_argloader.write("{0}Group".format(group_name))
|
||||
|
||||
options = ""
|
||||
AddedArg = False
|
||||
if (short != None):
|
||||
AddedArg = True
|
||||
options += "\"-{0}\"".format(short)
|
||||
|
||||
if (AddedArg):
|
||||
options += ", "
|
||||
options += "\"--{0}\"".format(json_name.lower())
|
||||
|
||||
output_argloader.write(".add_option({0})".format(options))
|
||||
|
||||
output_argloader.write("\n")
|
||||
|
||||
output_argloader.write("\t.action(\"store_true\")\n")
|
||||
|
||||
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
|
||||
|
||||
# help
|
||||
output_argloader.write("\t.help(\n")
|
||||
desc_line_ender = ""
|
||||
if (len(desc) > 1):
|
||||
desc_line_ender = "\\n"
|
||||
|
||||
for line in desc:
|
||||
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
|
||||
output_argloader.write("\t)\n")
|
||||
|
||||
output_argloader.write("\t.set_default({0});\n\n".format(default_value));
|
||||
|
||||
output_argloader.write("{0}Group".format(group_name))
|
||||
output_argloader.write(".add_option(\"--no-{0}\")\n".format(json_name.lower()))
|
||||
|
||||
# Inverted case sets the bool to false
|
||||
output_argloader.write("\t.action(\"store_false\")\n")
|
||||
|
||||
output_argloader.write("\t.dest(\"{0}\");\n".format(json_name));
|
||||
else:
|
||||
output_argloader.write("{0}Group".format(group_name))
|
||||
options = ""
|
||||
AddedArg = False
|
||||
if (short != None):
|
||||
AddedArg = True
|
||||
options += "\"-{0}\"".format(short)
|
||||
|
||||
if (AddedArg):
|
||||
options += ", "
|
||||
options += "\"--{0}\"".format(json_name.lower())
|
||||
|
||||
output_argloader.write(".add_option({0})".format(options))
|
||||
|
||||
output_argloader.write("\n")
|
||||
|
||||
output_argloader.write("\t.dest(\"{0}\")\n".format(json_name));
|
||||
|
||||
if (choices != None):
|
||||
output_argloader.write("\t.choices({\n")
|
||||
for choice in choices:
|
||||
output_argloader.write("\t\t\"{0}\",\n".format(choice))
|
||||
output_argloader.write("\t})\n")
|
||||
|
||||
|
||||
# help
|
||||
output_argloader.write("\t.help(\n")
|
||||
desc_line_ender = ""
|
||||
if (len(desc) > 1):
|
||||
desc_line_ender = "\\n"
|
||||
|
||||
for line in desc:
|
||||
output_argloader.write("\t\t\"{0}{1}\"\n".format(line, desc_line_ender))
|
||||
output_argloader.write("\t)\n")
|
||||
|
||||
output_argloader.write("\t.set_default({0});\n".format(default_value));
|
||||
|
||||
output_argloader.write("\n");
|
||||
|
||||
def print_argloader_options(options):
|
||||
output_argloader.write("#ifdef BEFORE_PARSE\n")
|
||||
output_argloader.write("#undef BEFORE_PARSE\n")
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
default = op_vals["Default"]
|
||||
|
||||
if (op_vals["Type"] == "str" or op_vals["Type"] == "strarray"):
|
||||
# Wrap the string argument in quotes
|
||||
default = "\"" + default + "\""
|
||||
|
||||
# Textual default rather than enum based
|
||||
if ("TextDefault" in op_vals):
|
||||
default = "\"" + op_vals["TextDefault"] + "\""
|
||||
|
||||
short = None
|
||||
choices = None
|
||||
|
||||
if ("ShortArg" in op_vals):
|
||||
short = op_vals["ShortArg"]
|
||||
if ("Choices" in op_vals):
|
||||
choices = op_vals["Choices"]
|
||||
|
||||
print_config_option(
|
||||
op_vals["Type"],
|
||||
op_group,
|
||||
op_key,
|
||||
default,
|
||||
short,
|
||||
choices,
|
||||
op_vals["Desc"])
|
||||
|
||||
output_argloader.write("\n")
|
||||
output_argloader.write("#endif\n")
|
||||
|
||||
def print_parse_argloader_options(options):
|
||||
output_argloader.write("#ifdef AFTER_PARSE\n")
|
||||
output_argloader.write("#undef AFTER_PARSE\n")
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
output_argloader.write("if (Options.is_set_by_user(\"{0}\")) {{\n".format(op_key))
|
||||
|
||||
value_type = op_vals["Type"]
|
||||
NeedsString = False
|
||||
conversion_func = "std::to_string"
|
||||
if ("ArgumentHandler" in op_vals):
|
||||
NeedsString = True
|
||||
conversion_func = "FEX::Handler::{0}".format(op_vals["ArgumentHandler"])
|
||||
if (value_type == "str"):
|
||||
NeedsString = True
|
||||
conversion_func = ""
|
||||
|
||||
if (value_type == "strarray"):
|
||||
# these need a bit more help
|
||||
output_argloader.write("\tauto Array = Options.all(\"{0}\");\n".format(op_key))
|
||||
output_argloader.write("\tfor (auto iter = Array.begin(); iter != Array.end(); ++iter) {\n")
|
||||
output_argloader.write("\t\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, *iter);\n".format(op_key.upper()))
|
||||
output_argloader.write("\t}\n")
|
||||
else:
|
||||
if (NeedsString):
|
||||
output_argloader.write("\tstd::string UserValue = Options[\"{0}\"];\n".format(op_key))
|
||||
else:
|
||||
output_argloader.write("\t{0} UserValue = Options.get(\"{1}\");\n".format(value_type, op_key))
|
||||
|
||||
output_argloader.write("\tSet(FEXCore::Config::ConfigOption::CONFIG_{0}, {1}(UserValue));\n".format(op_key.upper(), conversion_func))
|
||||
output_argloader.write("}\n")
|
||||
|
||||
output_argloader.write("#endif\n")
|
||||
|
||||
def check_for_duplicate_options(options):
|
||||
short_map = []
|
||||
long_map = []
|
||||
|
||||
# Spin through all the items and see if we have a duplicate option
|
||||
for op_group, group_vals in options.items():
|
||||
for op_key, op_vals in group_vals.items():
|
||||
short = None
|
||||
long = op_key.lower()
|
||||
long_invert = None
|
||||
if ("ShortArg" in op_vals):
|
||||
short = op_vals["ShortArg"]
|
||||
if (op_vals["Type"] == "bool"):
|
||||
long_invert = "no-" + long
|
||||
|
||||
# Check for short key duplication
|
||||
if (short != None):
|
||||
if (short in short_map):
|
||||
raise Exception("Short config '{0}' for option '{1}' has duplicate entry!".format(short, op_key))
|
||||
else:
|
||||
short_map.append(short)
|
||||
|
||||
# Check for long key duplication
|
||||
if (long in long_map):
|
||||
raise Exception("Long config '{0}' has duplicate entry!".format(long))
|
||||
else:
|
||||
long_map.append(long)
|
||||
|
||||
# Check for long key duplication
|
||||
if (long_invert != None):
|
||||
if (long_invert in long_map):
|
||||
raise Exception("Long config '{0}' has duplicate entry!".format(long_invert))
|
||||
else:
|
||||
long_map.append(long_invert)
|
||||
|
||||
if (len(sys.argv) < 5):
|
||||
sys.exit()
|
||||
|
||||
output_filename = sys.argv[2]
|
||||
output_man_page = sys.argv[3]
|
||||
output_argumentloader_filename = sys.argv[4]
|
||||
|
||||
json_file = open(sys.argv[1], "r")
|
||||
json_text = json_file.read()
|
||||
json_file.close()
|
||||
|
||||
json_object = json.loads(json_text)
|
||||
|
||||
options = json_object["Options"]
|
||||
unnamed_options = json_object["UnnamedOptions"]
|
||||
|
||||
check_for_duplicate_options(options)
|
||||
|
||||
# Generate config include file
|
||||
output_file = open(output_filename, "w")
|
||||
print_header()
|
||||
print_options(options)
|
||||
print_unnamed_options(unnamed_options)
|
||||
print_tail()
|
||||
output_file.close()
|
||||
|
||||
# Generate man file
|
||||
output_man = open(output_man_page, "w")
|
||||
print_man_header()
|
||||
print_man_options(options)
|
||||
print_man_environment(options)
|
||||
print_man_tail()
|
||||
|
||||
output_man.close()
|
||||
|
||||
# Generate argument loader code
|
||||
output_argloader = open(output_argumentloader_filename, "w")
|
||||
print_argloader_options(options);
|
||||
print_parse_argloader_options(options);
|
||||
output_argloader.close()
|
||||
+25
-19
@@ -108,10 +108,10 @@ def print_ir_sizes(ops, defines):
|
||||
|
||||
output_file.write("[[maybe_unused]] static size_t GetSize(IROps Op) { return IRSizes[Op]; }\n\n")
|
||||
|
||||
output_file.write("std::string_view const& GetName(IROps Op);\n")
|
||||
output_file.write("uint8_t GetArgs(IROps Op);\n")
|
||||
output_file.write("FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
|
||||
output_file.write("bool HasSideEffects(IROps Op);\n")
|
||||
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) std::string_view const& GetName(IROps Op);\n")
|
||||
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) uint8_t GetArgs(IROps Op);\n")
|
||||
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
|
||||
output_file.write("__attribute__((const)) __attribute__((visibility(\"default\"))) bool HasSideEffects(IROps Op);\n")
|
||||
|
||||
output_file.write("#undef IROP_SIZES\n")
|
||||
output_file.write("#endif\n\n")
|
||||
@@ -277,7 +277,7 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
output_file.write("\tusing IRPair = Wrapper<T>;\n\n")
|
||||
|
||||
output_file.write("\tIRPair<IROp_Header> AllocateRawOp(size_t HeaderSize) {\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<IROp_Header*>(Data.Allocate(HeaderSize));\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<IROp_Header*>(DualListData.DataAllocate(HeaderSize));\n")
|
||||
output_file.write("\t\tmemset(Op, 0, HeaderSize);\n")
|
||||
output_file.write("\t\tOp->Op = IROps::OP_DUMMY;\n")
|
||||
output_file.write("\t\treturn IRPair<IROp_Header>{Op, CreateNode(Op)};\n")
|
||||
@@ -286,7 +286,7 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
output_file.write("\ttemplate<class T, IROps T2>\n")
|
||||
output_file.write("\tT *AllocateOrphanOp() {\n")
|
||||
output_file.write("\t\tsize_t Size = FEXCore::IR::GetSize(T2);\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<T*>(Data.Allocate(Size));\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<T*>(DualListData.DataAllocate(Size));\n")
|
||||
output_file.write("\t\tmemset(Op, 0, Size);\n")
|
||||
output_file.write("\t\tOp->Header.Op = T2;\n")
|
||||
output_file.write("\t\treturn Op;\n")
|
||||
@@ -295,25 +295,25 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
output_file.write("\ttemplate<class T, IROps T2>\n")
|
||||
output_file.write("\tIRPair<T> AllocateOp() {\n")
|
||||
output_file.write("\t\tsize_t Size = FEXCore::IR::GetSize(T2);\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<T*>(Data.Allocate(Size));\n")
|
||||
output_file.write("\t\tauto Op = reinterpret_cast<T*>(DualListData.DataAllocate(Size));\n")
|
||||
output_file.write("\t\tmemset(Op, 0, Size);\n")
|
||||
output_file.write("\t\tOp->Header.Op = T2;\n")
|
||||
output_file.write("\t\treturn IRPair<T>{Op, CreateNode(&Op->Header)};\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tuint8_t GetOpSize(OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
|
||||
output_file.write("\t\treturn HeaderOp->Size;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tuint8_t GetOpElements(OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
|
||||
output_file.write("\t\tLogMan::Throw::A(HeaderOp->HasDest, \"Op %s has no dest\\n\", GetName(HeaderOp->Op));\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
|
||||
output_file.write("\t\tLOGMAN_THROW_A(HeaderOp->HasDest, \"Op %s has no dest\\n\", GetName(HeaderOp->Op));\n")
|
||||
output_file.write("\t\treturn HeaderOp->Size / HeaderOp->ElementSize;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tbool OpHasDest(OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(Data.Begin());\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
|
||||
output_file.write("\t\treturn HeaderOp->HasDest;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
@@ -387,11 +387,14 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
output_file.write(") {\n")
|
||||
|
||||
output_file.write("\t\tauto Op = AllocateOp<IROp_%s, IROps::OP_%s>();\n" % (op_key, op_key.upper()))
|
||||
output_file.write("\t\tOp.first->Header.NumArgs = %d;\n" % (SSAArgs))
|
||||
|
||||
if (SSAArgs != 0):
|
||||
output_file.write("\t\tauto ListDataBegin = DualListData.ListBegin();\n")
|
||||
for i in range(0, SSAArgs):
|
||||
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListDataBegin);\n" % (i, i))
|
||||
|
||||
if (SSAArgs != 0):
|
||||
for i in range(0, SSAArgs):
|
||||
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListData.Begin());\n" % (i, i))
|
||||
output_file.write("\t\tssa%d->AddUse();\n" % (i))
|
||||
|
||||
if (HasArgs):
|
||||
@@ -399,11 +402,6 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
data_name = op_vals["Args"][i]
|
||||
output_file.write("\t\tOp.first->%s = %s;\n" % (data_name, data_name))
|
||||
|
||||
if (HasFixedDestSize):
|
||||
output_file.write("\t\tOp.first->Header.Size = %d;\n" % FixedDestSize)
|
||||
if (HasDestSize):
|
||||
output_file.write("\t\tOp.first->Header.Size = %s;\n" % DestSize)
|
||||
|
||||
if (HasDest):
|
||||
# We can only infer a size if we have arguments
|
||||
if not (HasFixedDestSize or HasDestSize):
|
||||
@@ -412,10 +410,18 @@ def print_ir_allocator_helpers(ops, defines):
|
||||
if (SSAArgs != 0):
|
||||
for i in range(0, SSAArgs):
|
||||
output_file.write("\t\tuint8_t Size%d = GetOpSize(ssa%s);\n" % (i, i))
|
||||
for i in range(0, SSAArgs):
|
||||
output_file.write("\t\tInferSize = std::max(InferSize, Size%d);\n" % (i))
|
||||
|
||||
output_file.write("\t\tOp.first->Header.Size = InferSize;\n")
|
||||
|
||||
output_file.write("\t\tOp.first->Header.NumArgs = %d;\n" % (SSAArgs))
|
||||
|
||||
if (HasFixedDestSize):
|
||||
output_file.write("\t\tOp.first->Header.Size = %d;\n" % FixedDestSize)
|
||||
if (HasDestSize):
|
||||
output_file.write("\t\tOp.first->Header.Size = %s;\n" % DestSize)
|
||||
|
||||
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / (%s);\n" % NumElements)
|
||||
|
||||
if (HasDest):
|
||||
@@ -499,7 +505,7 @@ def print_ir_parser_allocator_helpers(ops, defines):
|
||||
|
||||
if (SSAArgs != 0):
|
||||
for i in range(0, SSAArgs):
|
||||
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(ListData.Begin());\n" % (i, i))
|
||||
output_file.write("\t\tOp.first->Header.Args[%d] = ssa%d->Wrapped(DualListData.ListBegin());\n" % (i, i))
|
||||
output_file.write("\t\tssa%d->AddUse();\n" % (i))
|
||||
|
||||
if (HasArgs):
|
||||
|
||||
+130
-94
@@ -1,49 +1,4 @@
|
||||
if (ENABLE_CLANG_FORMAT)
|
||||
find_program(CLANG_TIDY_EXE "clang-tidy")
|
||||
set(CLANG_TIDY_FLAGS
|
||||
"-checks=*"
|
||||
"-fuchsia*"
|
||||
"-bugprone-macro-parentheses"
|
||||
"-clang-analyzer-core.*"
|
||||
"-cppcoreguidelines-pro-type-*"
|
||||
"-cppcoreguidelines-pro-bounds-array-to-pointer-decay"
|
||||
"-cppcoreguidelines-pro-bounds-pointer-arithmetic"
|
||||
"-cppcoreguidelines-avoid-c-arrays"
|
||||
"-cppcoreguidelines-avoid-magic-numbers"
|
||||
"-cppcoreguidelines-pro-bounds-constant-array-index"
|
||||
"-cppcoreguidelines-no-malloc"
|
||||
"-cppcoreguidelines-special-member-functions"
|
||||
"-cppcoreguidelines-owning-memory"
|
||||
"-cppcoreguidelines-macro-usage"
|
||||
"-cppcoreguidelines-avoid-goto"
|
||||
"-google-readability-function-size"
|
||||
"-google-readability-namespace-comments"
|
||||
"-google-readability-braces-around-statements"
|
||||
"-google-build-using-namespace"
|
||||
"-hicpp-*"
|
||||
"-llvm-namespace-comment"
|
||||
"-llvm-include-order" # Messes up with case sensitivity
|
||||
"-misc-unused-parameters"
|
||||
"-modernize-loop-convert"
|
||||
"-modernize-use-auto"
|
||||
"-modernize-avoid-c-arrays"
|
||||
"-modernize-use-nodiscard"
|
||||
"readability-*"
|
||||
"-readability-function-size"
|
||||
"-readability-implicit-bool-conversion"
|
||||
"-readability-braces-around-statements"
|
||||
"-readability-else-after-return"
|
||||
"-readability-magic-numbers"
|
||||
"-readability-named-parameter"
|
||||
"-readability-uppercase-literal-suffix"
|
||||
"-cert-err34-c"
|
||||
"-cert-err58-cpp"
|
||||
"-bugprone-exception-escape"
|
||||
)
|
||||
string(REPLACE ";" "," CLANG_TIDY_FLAGS "${CLANG_TIDY_FLAGS}")
|
||||
|
||||
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} "${CLANG_TIDY_FLAGS}")
|
||||
endif()
|
||||
set (MAN_DIR ${CMAKE_INSTALL_PREFIX}/share/man CACHE PATH "MAN_DIR")
|
||||
|
||||
set (SRCS
|
||||
Common/Paths.cpp
|
||||
@@ -130,6 +85,11 @@ set (SRCS
|
||||
Interface/Core/X86Tables.cpp
|
||||
Interface/Core/X86DebugInfo.cpp
|
||||
Interface/Core/X86HelperGen.cpp
|
||||
Interface/Core/ArchHelpers/Arm64_stubs.cpp
|
||||
Interface/Core/ArchHelpers/Arm64Emitter.cpp
|
||||
Interface/Core/Dispatcher/Dispatcher.cpp
|
||||
Interface/Core/Dispatcher/X86Dispatcher.cpp
|
||||
Interface/Core/Dispatcher/Arm64Dispatcher.cpp
|
||||
Interface/Core/Interpreter/InterpreterCore.cpp
|
||||
Interface/Core/Interpreter/InterpreterOps.cpp
|
||||
Interface/Core/X86Tables/BaseTables.cpp
|
||||
@@ -154,6 +114,7 @@ set (SRCS
|
||||
Interface/IR/Passes/DeadContextStoreElimination.cpp
|
||||
Interface/IR/Passes/IRCompaction.cpp
|
||||
Interface/IR/Passes/IRValidation.cpp
|
||||
Interface/IR/Passes/LongDivideRemovalPass.cpp
|
||||
Interface/IR/Passes/ValueDominanceValidation.cpp
|
||||
Interface/IR/Passes/PhiValidation.cpp
|
||||
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
|
||||
@@ -161,61 +122,63 @@ set (SRCS
|
||||
Interface/IR/Passes/StaticRegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/RegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/SyscallOptimization.cpp
|
||||
Utils/ELFLoader.cpp
|
||||
Utils/Allocator.cpp
|
||||
Utils/Allocator/64BitAllocator.cpp
|
||||
Utils/ELFContainer.cpp
|
||||
Utils/ELFSymbolDatabase.cpp
|
||||
Utils/LogManager.cpp
|
||||
Utils/Threads.cpp
|
||||
)
|
||||
|
||||
if (_M_X86_64)
|
||||
list(APPEND SRCS Interface/Core/Interpreter/x86_64Dispatcher.cpp)
|
||||
endif()
|
||||
if(_M_ARM_64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/ArchHelpers/Arm64.cpp
|
||||
Interface/Core/Interpreter/Arm64Dispatcher.cpp)
|
||||
Interface/Core/ArchHelpers/Arm64.cpp)
|
||||
endif()
|
||||
|
||||
set (JIT_LIBS )
|
||||
if (ENABLE_JIT)
|
||||
if (_M_X86_64)
|
||||
add_definitions(-D_M_X86_64=1)
|
||||
if (NOT FORCE_AARCH64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/x86_64/JIT.cpp
|
||||
Interface/Core/JIT/x86_64/ALUOps.cpp
|
||||
Interface/Core/JIT/x86_64/AtomicOps.cpp
|
||||
Interface/Core/JIT/x86_64/BranchOps.cpp
|
||||
Interface/Core/JIT/x86_64/ConversionOps.cpp
|
||||
Interface/Core/JIT/x86_64/EncryptionOps.cpp
|
||||
Interface/Core/JIT/x86_64/FlagOps.cpp
|
||||
Interface/Core/JIT/x86_64/MemoryOps.cpp
|
||||
Interface/Core/JIT/x86_64/MiscOps.cpp
|
||||
Interface/Core/JIT/x86_64/MoveOps.cpp
|
||||
Interface/Core/JIT/x86_64/VectorOps.cpp)
|
||||
endif()
|
||||
endif()
|
||||
if(_M_ARM_64)
|
||||
add_definitions(-D_M_ARM_64=1)
|
||||
add_definitions(-DVIXL_INCLUDE_TARGET_AARCH64=1)
|
||||
add_definitions(-DVIXL_CODE_BUFFER_MMAP=1)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/Arm64/JIT.cpp
|
||||
Interface/Core/JIT/Arm64/ALUOps.cpp
|
||||
Interface/Core/JIT/Arm64/AtomicOps.cpp
|
||||
Interface/Core/JIT/Arm64/BranchOps.cpp
|
||||
Interface/Core/JIT/Arm64/ConversionOps.cpp
|
||||
Interface/Core/JIT/Arm64/EncryptionOps.cpp
|
||||
Interface/Core/JIT/Arm64/FlagOps.cpp
|
||||
Interface/Core/JIT/Arm64/MemoryOps.cpp
|
||||
Interface/Core/JIT/Arm64/MiscOps.cpp
|
||||
Interface/Core/JIT/Arm64/MoveOps.cpp
|
||||
Interface/Core/JIT/Arm64/VectorOps.cpp)
|
||||
list(APPEND JIT_LIBS vixl)
|
||||
endif()
|
||||
set(DEFINES )
|
||||
|
||||
if (_M_X86_64)
|
||||
list(APPEND DEFINES -D_M_X86_64=1)
|
||||
endif()
|
||||
|
||||
if (_M_ARM_64)
|
||||
list(APPEND DEFINES -D_M_ARM_64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_X86_64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/x86_64/JIT.cpp
|
||||
Interface/Core/JIT/x86_64/ALUOps.cpp
|
||||
Interface/Core/JIT/x86_64/AtomicOps.cpp
|
||||
Interface/Core/JIT/x86_64/BranchOps.cpp
|
||||
Interface/Core/JIT/x86_64/ConversionOps.cpp
|
||||
Interface/Core/JIT/x86_64/EncryptionOps.cpp
|
||||
Interface/Core/JIT/x86_64/FlagOps.cpp
|
||||
Interface/Core/JIT/x86_64/MemoryOps.cpp
|
||||
Interface/Core/JIT/x86_64/MiscOps.cpp
|
||||
Interface/Core/JIT/x86_64/MoveOps.cpp
|
||||
Interface/Core/JIT/x86_64/VectorOps.cpp)
|
||||
list(APPEND DEFINES -DJIT_X86_64)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_ARM64)
|
||||
list(APPEND DEFINES -DJIT_ARM64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/Arm64/JIT.cpp
|
||||
Interface/Core/JIT/Arm64/ALUOps.cpp
|
||||
Interface/Core/JIT/Arm64/AtomicOps.cpp
|
||||
Interface/Core/JIT/Arm64/BranchOps.cpp
|
||||
Interface/Core/JIT/Arm64/ConversionOps.cpp
|
||||
Interface/Core/JIT/Arm64/EncryptionOps.cpp
|
||||
Interface/Core/JIT/Arm64/FlagOps.cpp
|
||||
Interface/Core/JIT/Arm64/MemoryOps.cpp
|
||||
Interface/Core/JIT/Arm64/MiscOps.cpp
|
||||
Interface/Core/JIT/Arm64/MoveOps.cpp
|
||||
Interface/Core/JIT/Arm64/VectorOps.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JITSYMBOLS)
|
||||
add_definitions(-DENABLE_JITSYMBOLS=1)
|
||||
list(APPEND DEFINES -DENABLE_JITSYMBOLS=1)
|
||||
endif()
|
||||
|
||||
# Generate IR include file
|
||||
@@ -251,21 +214,64 @@ add_custom_command(
|
||||
set_source_files_properties(${OUTPUT_IR_NAME} PROPERTIES
|
||||
GENERATED TRUE)
|
||||
|
||||
# Create teh target
|
||||
# Create the target
|
||||
add_custom_target(IR_INC
|
||||
DEPENDS "${OUTPUT_NAME}"
|
||||
DEPENDS "${OUTPUT_IR_DOC}")
|
||||
|
||||
# Generate the configuration include file
|
||||
set(OUTPUT_CONFIG_FOLDER "${CMAKE_BINARY_DIR}/include/FEXCore/Config")
|
||||
set(OUTPUT_CONFIG_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigValues.inl")
|
||||
set(OUTPUT_CONFIG_OPTION_NAME "${OUTPUT_CONFIG_FOLDER}/ConfigOptions.inl")
|
||||
set(INPUT_CONFIG_NAME "${CMAKE_CURRENT_SOURCE_DIR}/Interface/Config/Config.json")
|
||||
set(OUTPUT_MAN_NAME "${CMAKE_BINARY_DIR}/generated/FEX.1")
|
||||
|
||||
add_custom_target(CREATE_CONFIG_FOLDER ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_CONFIG_FOLDER}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTPUT_CONFIG_NAME}"
|
||||
OUTPUT "${OUTPUT_CONFIG_OPTION_NAME}"
|
||||
OUTPUT "${OUTPUT_MAN_NAME}"
|
||||
DEPENDS "${INPUT_CONFIG_NAME}"
|
||||
DEPENDS CREATE_CONFIG_FOLDER
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py"
|
||||
COMMAND "python3" "${CMAKE_CURRENT_SOURCE_DIR}/../Scripts/config_generator.py" "${INPUT_CONFIG_NAME}" "${OUTPUT_CONFIG_NAME}" "${OUTPUT_MAN_NAME}"
|
||||
"${OUTPUT_CONFIG_OPTION_NAME}"
|
||||
)
|
||||
|
||||
set_source_files_properties(${OUTPUT_CONFIG_NAME} PROPERTIES
|
||||
GENERATED TRUE)
|
||||
set_source_files_properties(${OUTPUT_CONFIG_OPTION_NAME} PROPERTIES
|
||||
GENERATED TRUE)
|
||||
|
||||
set_source_files_properties(${OUTPUT_MAN_NAME} PROPERTIES
|
||||
GENERATED TRUE)
|
||||
|
||||
# Create the target
|
||||
add_custom_target(CONFIG_INC
|
||||
DEPENDS "${OUTPUT_CONFIG_NAME}"
|
||||
DEPENDS "${OUTPUT_CONFIG_OPTION_NAME}"
|
||||
DEPENDS "${OUTPUT_MAN_NAME}")
|
||||
|
||||
# Install the man page
|
||||
install(FILES ${OUTPUT_MAN_NAME} DESTINATION ${MAN_DIR}/man1)
|
||||
|
||||
# Add in diagnostic colours if the option is available.
|
||||
# Ninja code generator will kill colours if this isn't here
|
||||
check_cxx_compiler_flag(-fdiagnostics-color=always GCC_COLOR)
|
||||
check_cxx_compiler_flag(-fcolor-diagnostics CLANG_COLOR)
|
||||
|
||||
function(AddLibrary Name Type)
|
||||
function(AddObject Name Type)
|
||||
add_library(${Name} ${Type} ${SRCS})
|
||||
add_dependencies(${Name} IR_INC)
|
||||
target_link_libraries(${Name} pthread rt ${JIT_LIBS} ${LINUX_LIBS} dl)
|
||||
add_dependencies(${Name} CONFIG_INC)
|
||||
|
||||
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
|
||||
|
||||
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
@@ -275,9 +281,16 @@ function(AddLibrary Name Type)
|
||||
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
|
||||
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
|
||||
|
||||
target_compile_definitions(${Name} PRIVATE ${DEFINES})
|
||||
|
||||
target_compile_options(${Name}
|
||||
PRIVATE
|
||||
-Wno-trigraphs -Wall)
|
||||
-Wall
|
||||
-Werror=implicit-fallthrough
|
||||
|
||||
-Wno-trigraphs
|
||||
-ffunction-sections
|
||||
)
|
||||
|
||||
if (GCC_COLOR)
|
||||
target_compile_options(${Name}
|
||||
@@ -291,6 +304,29 @@ function(AddLibrary Name Type)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(AddLibrary Name Type)
|
||||
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
||||
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
|
||||
|
||||
target_include_directories(${Name} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_include_directories(${Name} PUBLIC "${PROJECT_SOURCE_DIR}/include/")
|
||||
target_include_directories(${Name} PUBLIC "${CMAKE_BINARY_DIR}/include/")
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
|
||||
target_link_options(${Name}
|
||||
PRIVATE
|
||||
"LINKER:--gc-sections"
|
||||
"LINKER:--strip-all"
|
||||
"LINKER:--as-needed"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
AddObject(${PROJECT_NAME}_object OBJECT)
|
||||
AddLibrary(${PROJECT_NAME} STATIC)
|
||||
AddLibrary(${PROJECT_NAME}_shared SHARED)
|
||||
|
||||
|
||||
+7
-6
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "Common/MathUtils.h"
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <cstring>
|
||||
@@ -16,16 +17,16 @@ struct BitSet final {
|
||||
ElementType *Memory;
|
||||
void Allocate(size_t Elements) {
|
||||
size_t AllocateSize = AlignUp(Elements, MinimumSizeBits) / MinimumSize;
|
||||
LogMan::Throw::A((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
Memory = static_cast<ElementType*>(malloc(AllocateSize));
|
||||
LOGMAN_THROW_A((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
Memory = static_cast<ElementType*>(FEXCore::Allocator::malloc(AllocateSize));
|
||||
}
|
||||
void Realloc(size_t Elements) {
|
||||
size_t AllocateSize = AlignUp(Elements, MinimumSizeBits) / MinimumSize;
|
||||
LogMan::Throw::A((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
Memory = static_cast<ElementType*>(realloc(Memory, AllocateSize));
|
||||
LOGMAN_THROW_A((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
Memory = static_cast<ElementType*>(FEXCore::Allocator::realloc(Memory, AllocateSize));
|
||||
}
|
||||
void Free() {
|
||||
free(Memory);
|
||||
FEXCore::Allocator::free(Memory);
|
||||
Memory = nullptr;
|
||||
}
|
||||
bool Get(T Element) {
|
||||
@@ -60,7 +61,7 @@ struct BitSetView final {
|
||||
ElementType *Memory;
|
||||
|
||||
void GetView(BitSet<T> &Set, uint64_t ElementOffset) {
|
||||
LogMan::Throw::A((ElementOffset % MinimumSize) == 0,
|
||||
LOGMAN_THROW_A((ElementOffset % MinimumSize) == 0,
|
||||
"Bitset view offset needs to be aligned to size of backing element");
|
||||
Memory = &Set.Memory[ElementOffset / MinimumSizeBits];
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ extFloat80_t
|
||||
switch ( roundingMode ) {
|
||||
case softfloat_round_near_even:
|
||||
if ( !(sigA & UINT64_C( 0x7FFFFFFFFFFFFFFF )) ) break;
|
||||
__attribute__((fallthrough));
|
||||
case softfloat_round_near_maxMag:
|
||||
if ( exp == 0x3FFE ) goto mag1;
|
||||
break;
|
||||
|
||||
+7
@@ -38,4 +38,11 @@ namespace FEXCore::StrConv {
|
||||
*Result = Value;
|
||||
return true;
|
||||
}
|
||||
template <typename T,
|
||||
typename = std::enable_if<std::is_enum<T>::value, T>>
|
||||
[[maybe_unused]] static bool Conv(std::string_view Value, T *Result) {
|
||||
*Result = static_cast<T>(std::stoull(std::string(Value), nullptr, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+251
-105
@@ -4,118 +4,123 @@
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <filesystem>
|
||||
#include <pwd.h>
|
||||
#include <map>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FEXCore::Config {
|
||||
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
|
||||
switch (Option) {
|
||||
case FEXCore::Config::CONFIG_MULTIBLOCK:
|
||||
CTX->Config.Multiblock = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_MAXBLOCKINST:
|
||||
CTX->Config.MaxInstPerBlock = Config;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_DEFAULTCORE:
|
||||
CTX->Config.Core = static_cast<FEXCore::Config::ConfigCore>(Config);
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
|
||||
CTX->Config.VirtualMemSize = Config;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_SINGLESTEP:
|
||||
CTX->Config.RunningMode = Config != 0 ? FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP : FEXCore::Context::CoreRunningMode::MODE_RUN;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_GDBSERVER:
|
||||
Config != 0 ? CTX->StartGdbServer() : CTX->StopGdbServer();
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_IS64BIT_MODE:
|
||||
CTX->Config.Is64BitMode = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_TSO_ENABLED:
|
||||
CTX->Config.TSOEnabled = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_SMC_CHECKS:
|
||||
CTX->Config.SMCChecks = static_cast<FEXCore::Config::ConfigSMCChecks>(Config);
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
|
||||
CTX->Config.ABILocalFlags = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_ABI_NO_PF:
|
||||
CTX->Config.ABINoPF = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_VALIDATE_IR_PARSER:
|
||||
CTX->Config.ValidateIRarser = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_AOTIR_GENERATE:
|
||||
CTX->Config.AOTIRCapture = Config != 0;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_AOTIR_LOAD:
|
||||
CTX->Config.AOTIRLoad = Config != 0;
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown configuration option");
|
||||
char const* FindUserHomeThroughUID() {
|
||||
auto passwd = getpwuid(geteuid());
|
||||
if (passwd) {
|
||||
return passwd->pw_dir;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *GetHomeDirectory() {
|
||||
char const *HomeDir = getenv("HOME");
|
||||
|
||||
// Try to get home directory from uid
|
||||
if (!HomeDir) {
|
||||
HomeDir = FindUserHomeThroughUID();
|
||||
}
|
||||
|
||||
// try the PWD
|
||||
if (!HomeDir) {
|
||||
HomeDir = getenv("PWD");
|
||||
}
|
||||
|
||||
// Still doesn't exit? You get local
|
||||
if (!HomeDir) {
|
||||
HomeDir = ".";
|
||||
}
|
||||
|
||||
return HomeDir;
|
||||
}
|
||||
|
||||
std::string GetConfigDirectory(bool Global) {
|
||||
std::string ConfigDir;
|
||||
if (Global) {
|
||||
ConfigDir = GLOBAL_DATA_DIRECTORY;
|
||||
}
|
||||
else {
|
||||
char const *HomeDir = GetHomeDirectory();
|
||||
char const *ConfigXDG = getenv("XDG_CONFIG_HOME");
|
||||
char const *ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
|
||||
if (ConfigOverride) {
|
||||
// Config override completely overrides the config directory
|
||||
ConfigDir = ConfigOverride;
|
||||
}
|
||||
else {
|
||||
ConfigDir = ConfigXDG ? ConfigXDG : HomeDir;
|
||||
ConfigDir += "/.fex-emu/";
|
||||
}
|
||||
|
||||
// Ensure the folder structure is created for our configuration
|
||||
if (!std::filesystem::exists(ConfigDir) &&
|
||||
!std::filesystem::create_directories(ConfigDir)) {
|
||||
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigDir.c_str());
|
||||
// Let's go local in this case
|
||||
return "./";
|
||||
}
|
||||
}
|
||||
|
||||
return ConfigDir;
|
||||
}
|
||||
|
||||
std::string GetConfigFileLocation() {
|
||||
std::string ConfigFile{};
|
||||
const char *AppConfig = getenv("FEX_APP_CONFIG");
|
||||
if (AppConfig) {
|
||||
// App config environment variable overwrites only the config file
|
||||
ConfigFile = AppConfig;
|
||||
}
|
||||
else {
|
||||
ConfigFile = GetConfigDirectory(false) + "Config.json";
|
||||
}
|
||||
return ConfigFile;
|
||||
}
|
||||
|
||||
std::string GetApplicationConfig(std::string &Filename, bool Global) {
|
||||
std::string ConfigFile = GetConfigDirectory(Global);
|
||||
if (!Global &&
|
||||
!std::filesystem::exists(ConfigFile) &&
|
||||
!std::filesystem::create_directories(ConfigFile)) {
|
||||
LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigFile.c_str());
|
||||
// Let's go local in this case
|
||||
return "./";
|
||||
}
|
||||
|
||||
ConfigFile += "AppConfig/" + Filename + ".json";
|
||||
return ConfigFile;
|
||||
}
|
||||
|
||||
std::string GetDataDirectory() {
|
||||
std::string DataDir{};
|
||||
|
||||
char const *HomeDir = GetHomeDirectory();
|
||||
char const *DataXDG = getenv("XDG_DATA_HOME");
|
||||
char const *DataOverride = getenv("FEX_APP_DATA_LOCATION");
|
||||
if (DataOverride) {
|
||||
// Data override will override the complete directory
|
||||
DataDir = DataOverride;
|
||||
}
|
||||
else {
|
||||
DataDir = DataXDG ?: HomeDir;
|
||||
DataDir += "/.fex-emu/";
|
||||
}
|
||||
return DataDir;
|
||||
}
|
||||
|
||||
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config) {
|
||||
}
|
||||
|
||||
void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, std::string const &Config) {
|
||||
switch (Option) {
|
||||
case CONFIG_ROOTFSPATH:
|
||||
CTX->Config.RootFSPath = Config;
|
||||
break;
|
||||
case CONFIG_THUNKLIBSPATH:
|
||||
CTX->Config.ThunkLibsPath = Config;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_DUMPIR:
|
||||
CTX->Config.DumpIR = Config;
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown configuration option");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetConfig(FEXCore::Context::Context *CTX, ConfigOption Option) {
|
||||
switch (Option) {
|
||||
case FEXCore::Config::CONFIG_MULTIBLOCK:
|
||||
return CTX->Config.Multiblock;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_MAXBLOCKINST:
|
||||
return CTX->Config.MaxInstPerBlock;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_DEFAULTCORE:
|
||||
return CTX->Config.Core;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_VIRTUALMEMSIZE:
|
||||
return CTX->Config.VirtualMemSize;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_SINGLESTEP:
|
||||
return CTX->Config.RunningMode == FEXCore::Context::CoreRunningMode::MODE_SINGLESTEP ? 1 : 0;
|
||||
case FEXCore::Config::CONFIG_GDBSERVER:
|
||||
return CTX->GetGdbServerStatus();
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_IS64BIT_MODE:
|
||||
return CTX->Config.Is64BitMode;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_TSO_ENABLED:
|
||||
return CTX->Config.TSOEnabled;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_SMC_CHECKS:
|
||||
return CTX->Config.SMCChecks;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS:
|
||||
return CTX->Config.ABILocalFlags;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_ABI_NO_PF:
|
||||
return CTX->Config.ABINoPF;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_VALIDATE_IR_PARSER:
|
||||
return CTX->Config.ValidateIRarser;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_AOTIR_GENERATE:
|
||||
return CTX->Config.AOTIRCapture;
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_AOTIR_LOAD:
|
||||
return CTX->Config.AOTIRLoad;
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown configuration option");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -149,6 +154,7 @@ namespace FEXCore::Config {
|
||||
|
||||
private:
|
||||
void MergeConfigMap(const LayerOptions &Options);
|
||||
void MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value);
|
||||
};
|
||||
|
||||
void MetaLayer::Load() {
|
||||
@@ -163,10 +169,56 @@ namespace FEXCore::Config {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MetaLayer::MergeEnvironmentVariables(ConfigOption const &Option, LayerValue const &Value) {
|
||||
// Environment variables need a bit of additional work
|
||||
// We want to merge the arrays rather than overwrite entirely
|
||||
auto MetaEnvironment = OptionMap.find(Option);
|
||||
if (MetaEnvironment == OptionMap.end()) {
|
||||
// Doesn't exist, just insert
|
||||
OptionMap.insert_or_assign(Option, Value);
|
||||
return;
|
||||
}
|
||||
|
||||
// If an environment variable exists in both current meta and in the incoming layer then the meta layer value is overwritten
|
||||
std::unordered_map<std::string, std::string> LookupMap;
|
||||
const auto AddToMap = [&LookupMap](FEXCore::Config::LayerValue const &Value) {
|
||||
for (const auto &EnvVar : Value) {
|
||||
const auto ItEq = EnvVar.find_first_of('=');
|
||||
if (ItEq == std::string::npos) {
|
||||
// Broken environment variable
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
auto Key = std::string(EnvVar.begin(), EnvVar.begin() + ItEq);
|
||||
auto Value = std::string(EnvVar.begin() + ItEq + 1, EnvVar.end());
|
||||
|
||||
// Add the key to the map, overwriting whatever previous value was there
|
||||
LookupMap.insert_or_assign(std::move(Key), std::move(Value));
|
||||
}
|
||||
};
|
||||
|
||||
AddToMap(MetaEnvironment->second);
|
||||
AddToMap(Value);
|
||||
|
||||
// Now with the two layers merged in the map
|
||||
// Add all the values to the option
|
||||
Erase(Option);
|
||||
for (auto &Val : LookupMap) {
|
||||
// Set will emplace multiple options in to its list
|
||||
Set(Option, Val.first + "=" + Val.second);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaLayer::MergeConfigMap(const LayerOptions &Options) {
|
||||
// Insert this layer's options, overlaying previous options that exist here
|
||||
for (auto &it : Options) {
|
||||
OptionMap.insert_or_assign(it.first, it.second);
|
||||
if (it.first == FEXCore::Config::ConfigOption::CONFIG_ENV) {
|
||||
MergeEnvironmentVariables(it.first, it.second);
|
||||
}
|
||||
else {
|
||||
OptionMap.insert_or_assign(it.first, it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,8 +241,91 @@ namespace FEXCore::Config {
|
||||
}
|
||||
}
|
||||
|
||||
std::string ExpandPath(std::string PathName) {
|
||||
if (PathName.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::filesystem::path Path{PathName};
|
||||
|
||||
// Expand home if it exists
|
||||
if (Path.is_relative()) {
|
||||
std::string Home = getenv("HOME") ?: "";
|
||||
// Home expansion only works if it is the first character
|
||||
// This matches bash behaviour
|
||||
if (PathName.at(0) == '~') {
|
||||
PathName.replace(0, 1, Home);
|
||||
return PathName;
|
||||
}
|
||||
|
||||
// Expand relative path to absolute
|
||||
Path = std::filesystem::absolute(Path);
|
||||
|
||||
// Only return if it exists
|
||||
if (std::filesystem::exists(Path)) {
|
||||
return Path;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ReloadMetaLayer() {
|
||||
Meta->Load();
|
||||
|
||||
// Do configuration option fix ups after everything is reloaded
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THREADS)) {
|
||||
FEX_CONFIG_OPT(Cores, THREADS);
|
||||
if (Cores == 0) {
|
||||
// When the number of emulated CPU cores is zero then auto detect
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THREADS, std::to_string(get_nprocs_conf()));
|
||||
}
|
||||
}
|
||||
|
||||
auto ExpandPathIfExists = [](FEXCore::Config::ConfigOption Config, std::string PathName) {
|
||||
auto NewPath = ExpandPath(PathName);
|
||||
if (!NewPath.empty()) {
|
||||
FEXCore::Config::EraseSet(Config, NewPath);
|
||||
}
|
||||
};
|
||||
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_ROOTFS)) {
|
||||
FEX_CONFIG_OPT(PathName, ROOTFS);
|
||||
auto ExpandedString = ExpandPath(PathName());
|
||||
if (!ExpandedString.empty()) {
|
||||
// Adjust the path if it ended up being relative
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, ExpandedString);
|
||||
}
|
||||
else if (!PathName().empty()) {
|
||||
// If the filesystem doesn't exist then let's see if it exists in the fex-emu folder
|
||||
std::string NamedRootFS = GetDataDirectory() + "RootFS/" + PathName();
|
||||
if (std::filesystem::exists(NamedRootFS)) {
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, NamedRootFS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKHOSTLIBS)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKHOSTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKHOSTLIBS, PathName());
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKGUESTLIBS)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKGUESTLIBS);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKGUESTLIBS, PathName());
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_THUNKCONFIG)) {
|
||||
FEX_CONFIG_OPT(PathName, THUNKCONFIG);
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_THUNKCONFIG, PathName());
|
||||
}
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_OUTPUTLOG)) {
|
||||
FEX_CONFIG_OPT(PathName, OUTPUTLOG);
|
||||
if (PathName() != "stdout" && PathName() != "stderr") {
|
||||
ExpandPathIfExists(FEXCore::Config::CONFIG_OUTPUTLOG, PathName());
|
||||
}
|
||||
}
|
||||
|
||||
if (FEXCore::Config::Exists(FEXCore::Config::CONFIG_SINGLESTEP)) {
|
||||
// Single stepping also enforces single instruction size blocks
|
||||
Set(FEXCore::Config::ConfigOption::CONFIG_MAXINST, std::to_string(1u));
|
||||
}
|
||||
}
|
||||
|
||||
void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer) {
|
||||
@@ -213,6 +348,10 @@ namespace FEXCore::Config {
|
||||
Meta->Set(Option, Data);
|
||||
}
|
||||
|
||||
void Erase(ConfigOption Option) {
|
||||
Meta->Erase(Option);
|
||||
}
|
||||
|
||||
void EraseSet(ConfigOption Option, std::string Data) {
|
||||
Meta->EraseSet(Option, Data);
|
||||
}
|
||||
@@ -252,8 +391,14 @@ namespace FEXCore::Config {
|
||||
}
|
||||
}
|
||||
|
||||
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
|
||||
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
|
||||
template bool Value<bool>::GetIfExists(FEXCore::Config::ConfigOption Option, bool Default);
|
||||
template int8_t Value<int8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int8_t Default);
|
||||
template uint8_t Value<uint8_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint8_t Default);
|
||||
template int16_t Value<int16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int16_t Default);
|
||||
template uint16_t Value<uint16_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint16_t Default);
|
||||
template int32_t Value<int32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int32_t Default);
|
||||
template uint32_t Value<uint32_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint32_t Default);
|
||||
template int64_t Value<int64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, int64_t Default);
|
||||
template uint64_t Value<uint64_t>::GetIfExists(FEXCore::Config::ConfigOption Option, uint64_t Default);
|
||||
|
||||
// Constructor
|
||||
@@ -270,5 +415,6 @@ namespace FEXCore::Config {
|
||||
*List = **Value;
|
||||
}
|
||||
}
|
||||
template void Value<std::string>::GetListIfExists(FEXCore::Config::ConfigOption Option, std::list<std::string> *List);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
{
|
||||
"Options": {
|
||||
"CPU": {
|
||||
"Core": {
|
||||
"Type": "uint32",
|
||||
"Default": "FEXCore::Config::ConfigCore::CONFIG_IRJIT",
|
||||
"TextDefault": "irjit",
|
||||
"ShortArg": "c",
|
||||
"Choices": [ "irint", "irjit", "host" ],
|
||||
"ArgumentHandler": "CoreHandler",
|
||||
"Desc": [
|
||||
"Which CPU core to use",
|
||||
"host only exists on x86_64",
|
||||
"[irint, irjit, host]"
|
||||
]
|
||||
},
|
||||
"Multiblock": {
|
||||
"Type": "bool",
|
||||
"Default": "true",
|
||||
"ShortArg": "m",
|
||||
"Desc": [
|
||||
"Controls multiblock code compilation"
|
||||
]
|
||||
},
|
||||
"MaxInst": {
|
||||
"Type": "int32",
|
||||
"Default": "5000",
|
||||
"ShortArg": "n",
|
||||
"Desc": [
|
||||
"Maximum number of instruction to store in a block"
|
||||
]
|
||||
},
|
||||
"Threads": {
|
||||
"Type": "uint32",
|
||||
"Default": "1",
|
||||
"ShortArg": "T",
|
||||
"Desc": [
|
||||
"Number of physical hardware threads to tell the process we have.",
|
||||
"0 will auto detect."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Emulation": {
|
||||
"RootFS": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"ShortArg": "R",
|
||||
"Desc": [
|
||||
"Which Root filesystem prefix to use",
|
||||
"This can be a filesystem path",
|
||||
"\teg: ~/RootFS/Debian_x86_64",
|
||||
"Or this can be a name of a rootfs",
|
||||
"If the named rootfs exists in the FEX data folder then it will use that one",
|
||||
"\teg: $HOME/.fex-emu/RootFS/<RootFS name>/",
|
||||
"Or if you have XDG_DATA_HOME the config will search in that directory",
|
||||
"\teg: $XDG_DATA_HOME/.fex-emu/RootFS/<RootFS name>/"
|
||||
]
|
||||
},
|
||||
"ThunkHostLibs": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"ShortArg": "t",
|
||||
"Desc": [
|
||||
"Folder to find the host-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkGuestLibs": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"ShortArg": "j",
|
||||
"Desc": [
|
||||
"Folder to find the guest-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkConfig": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"ShortArg": "k",
|
||||
"Desc": [
|
||||
"A json file specifying where to overlay the thunks."
|
||||
]
|
||||
},
|
||||
"Env": {
|
||||
"Type": "strarray",
|
||||
"Default": "",
|
||||
"ShortArg": "E",
|
||||
"Desc": [
|
||||
"Adds an environment variable to the emulated environment."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Debug": {
|
||||
"SingleStep": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"ShortArg": "S",
|
||||
"Desc": [
|
||||
"Single stepping configuration."
|
||||
]
|
||||
},
|
||||
"GdbServer": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"ShortArg": "G",
|
||||
"Desc": [
|
||||
"Enables the GDB server."
|
||||
]
|
||||
},
|
||||
"DumpIR": {
|
||||
"Type": "str",
|
||||
"Default": "no",
|
||||
"Desc": [
|
||||
"Folder to dump the IR in to.",
|
||||
"[no, stdout, stderr, <Folder>]"
|
||||
]
|
||||
},
|
||||
"DumpGPRs": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"ShortArg": "g",
|
||||
"Desc": [
|
||||
"When the test harness ends, print the GPR state."
|
||||
]
|
||||
},
|
||||
"O0": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"ShortArg": "O0",
|
||||
"Desc": [
|
||||
"Disables optimizations passes for debugging."
|
||||
]
|
||||
},
|
||||
"Force32BitAllocator": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Forces use of the 32-bit allocator on 32-bit applications",
|
||||
"Used to work around ulimit problems of CI runner",
|
||||
"Potentially useful for debugging memory problems",
|
||||
"32-bit allocator is always used if your host kernel is older than 4.17"
|
||||
]
|
||||
}
|
||||
|
||||
},
|
||||
"Logging": {
|
||||
"SilentLog": {
|
||||
"Type": "bool",
|
||||
"Default": "true",
|
||||
"ShortArg": "s",
|
||||
"Desc": [
|
||||
"Disables logging"
|
||||
]
|
||||
},
|
||||
"OutputLog": {
|
||||
"Type": "str",
|
||||
"Default": "stderr",
|
||||
"ShortArg": "o",
|
||||
"Desc": [
|
||||
"File to write FEX output to.",
|
||||
"[stdout, stderr, <Filename>]"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Hacks": {
|
||||
"SMCChecks": {
|
||||
"Type": "uint8",
|
||||
"Default": "FEXCore::Config::CONFIG_SMC_MMAN",
|
||||
"TextDefault": "mman",
|
||||
"ArgumentHandler": "SMCCheckHandler",
|
||||
"Desc": [
|
||||
"Checks code for modification before execution.",
|
||||
"\tnone: No checks",
|
||||
"\tmman: Invalidate on mmap, mprotect, munmap",
|
||||
"\tfull: Validate code before every run (slow)"
|
||||
]
|
||||
},
|
||||
"TSOEnabled": {
|
||||
"Type": "bool",
|
||||
"Default": "true",
|
||||
"Desc": [
|
||||
"Controls TSO IR ops.",
|
||||
"Highly likely to break any multithreaded application if disabled."
|
||||
]
|
||||
},
|
||||
"ABILocalFlags": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"When enabled enables an optimization around flags.",
|
||||
"Assumes flags are not used across cals.",
|
||||
"Hand-written assembly can violate this assumption."
|
||||
]
|
||||
},
|
||||
"ABINoPF": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"When enabled enables an optimization around parity flag calculation.",
|
||||
"Removes the calculation of the parity flag from GPR instructions.",
|
||||
"Assuming no uses rely on it"
|
||||
]
|
||||
},
|
||||
"ParanoidTSO": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Makes TSO operations even more strict.",
|
||||
"Forces vector loadstores to also become atomic."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Misc": {
|
||||
"AOTIRCapture": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Captures IR and generates an AOT IR cache.",
|
||||
"Captures both the loaded executable and libraries it loads."
|
||||
]
|
||||
},
|
||||
"AOTIRGenerate": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Scans file for executable code and generates an AOT IR cache.",
|
||||
"Does not run the executable."
|
||||
]
|
||||
},
|
||||
"AOTIRLoad": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Loads an AOT IR cache for the loaded executable."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"UnnamedOptions": {
|
||||
"Misc": {
|
||||
"IS_INTERPRETER": {
|
||||
"Type": "bool",
|
||||
"Default": "false"
|
||||
},
|
||||
"INTERPRETER_INSTALLED": {
|
||||
"Type": "bool",
|
||||
"Default": "false"
|
||||
},
|
||||
"APP_FILENAME": {
|
||||
"Type": "str",
|
||||
"Default": ""
|
||||
},
|
||||
"IS64BIT_MODE": {
|
||||
"Type": "bool",
|
||||
"Default": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-30
@@ -23,6 +23,9 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void DestroyContext(FEXCore::Context::Context *CTX) {
|
||||
if (CTX->ParentThread) {
|
||||
CTX->DestroyThread(CTX->ParentThread);
|
||||
}
|
||||
delete CTX;
|
||||
}
|
||||
|
||||
@@ -47,6 +50,9 @@ namespace FEXCore::Context {
|
||||
CTX->Step();
|
||||
}
|
||||
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP) {
|
||||
CTX->CompileBlock(CTX->ParentThread->CurrentFrame, GuestRIP);
|
||||
}
|
||||
|
||||
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
|
||||
return CTX->RunUntilExit();
|
||||
@@ -65,11 +71,11 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
|
||||
memcpy(State, &CTX->ParentThread->State.State, sizeof(FEXCore::Core::CPUState));
|
||||
memcpy(State, CTX->ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
|
||||
memcpy(&CTX->ParentThread->State.State, State, sizeof(FEXCore::Core::CPUState));
|
||||
memcpy(CTX->ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
void Pause(FEXCore::Context::Context *CTX) {
|
||||
@@ -84,10 +90,6 @@ namespace FEXCore::Context {
|
||||
CTX->CustomCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
void SetFallbackCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory) {
|
||||
CTX->FallbackCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
bool AddVirtualMemoryMapping([[maybe_unused]] FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
|
||||
return false;
|
||||
}
|
||||
@@ -123,20 +125,12 @@ namespace FEXCore::Context {
|
||||
CTX->StopThread(Thread);
|
||||
}
|
||||
|
||||
void DeleteForkedThreads(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
// This function is called after fork
|
||||
// We need to cleanup some of the thread data that is dead
|
||||
for (auto &DeadThread : CTX->Threads) {
|
||||
if (DeadThread == Thread) {
|
||||
continue;
|
||||
}
|
||||
void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->DestroyThread(Thread);
|
||||
}
|
||||
|
||||
// Setting running to false ensures that when they are shutdown we won't send signals to kill them
|
||||
DeadThread->State.RunningEvents.Running = false;
|
||||
}
|
||||
|
||||
// We now only have one thread
|
||||
CTX->IdleWaitRefCount = 1;
|
||||
void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->CleanupAfterFork(Thread);
|
||||
}
|
||||
|
||||
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation) {
|
||||
@@ -147,11 +141,11 @@ namespace FEXCore::Context {
|
||||
CTX->SyscallHandler = Handler;
|
||||
}
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
|
||||
return CTX->CPUID.RunFunction(Function);
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {
|
||||
return CTX->CPUID.RunFunction(Function, Leaf);
|
||||
}
|
||||
|
||||
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::istream>(const std::string&)> CacheReader) {
|
||||
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader) {
|
||||
CTX->AOTIRLoader = CacheReader;
|
||||
}
|
||||
|
||||
@@ -159,6 +153,10 @@ namespace FEXCore::Context {
|
||||
return CTX->WriteAOTIRCache(CacheWriter);
|
||||
}
|
||||
|
||||
void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
|
||||
CTX->WriteFilesWithCode(Writer);
|
||||
}
|
||||
|
||||
void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name) {
|
||||
return CTX->AddNamedRegion(Base, Length, Offset, Name);
|
||||
}
|
||||
@@ -178,10 +176,6 @@ namespace Debug {
|
||||
return CTX->GetRuntimeStatsForThread(Thread);
|
||||
}
|
||||
|
||||
FEXCore::Core::CPUState GetCPUState(FEXCore::Context::Context *CTX) {
|
||||
return CTX->GetCPUState();
|
||||
}
|
||||
|
||||
bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
|
||||
return CTX->GetDebugDataForRIP(RIP, Data);
|
||||
}
|
||||
@@ -198,10 +192,6 @@ namespace Debug {
|
||||
// void SetIRForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir) {
|
||||
// CTX->SetIRForRIP(RIP, ir);
|
||||
// }
|
||||
|
||||
FEXCore::Core::ThreadState *GetThreadState(FEXCore::Context::Context *CTX) {
|
||||
return CTX->GetThreadState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+96
-54
@@ -12,14 +12,15 @@
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <istream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace FEXCore {
|
||||
class ThunkHandler;
|
||||
@@ -28,7 +29,8 @@ class GdbServer;
|
||||
class SiganlDelegator;
|
||||
|
||||
namespace CPU {
|
||||
class JITCore;
|
||||
class Arm64JITCore;
|
||||
class X86JITCore;
|
||||
}
|
||||
namespace HLE {
|
||||
class SyscallHandler;
|
||||
@@ -50,36 +52,73 @@ namespace FEXCore::Context {
|
||||
MODE_SINGLESTEP = 1,
|
||||
};
|
||||
|
||||
struct AOTIRCaptureCacheEntry {
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint64_t crc;
|
||||
IR::IRListView *IR;
|
||||
IR::RegisterAllocationData *RAData;
|
||||
};
|
||||
|
||||
struct AOTIRInlineEntry {
|
||||
uint64_t GuestHash;
|
||||
uint64_t GuestLength;
|
||||
|
||||
/* RAData followed by IRData */
|
||||
uint8_t InlineData[0];
|
||||
|
||||
IR::RegisterAllocationData *GetRAData();
|
||||
IR::IRListView *GetIRData();
|
||||
};
|
||||
|
||||
struct AOTIRInlineIndexEntry {
|
||||
uint64_t GuestStart;
|
||||
uint64_t DataOffset;
|
||||
};
|
||||
|
||||
struct AOTIRInlineIndex {
|
||||
uint64_t Count;
|
||||
uint64_t DataBase;
|
||||
AOTIRInlineIndexEntry Entries[0];
|
||||
|
||||
AOTIRInlineEntry *Find(uint64_t GuestStart);
|
||||
AOTIRInlineEntry *GetInlineEntry(uint64_t DataOffset);
|
||||
};
|
||||
|
||||
struct Context {
|
||||
friend class FEXCore::HLE::SyscallHandler;
|
||||
friend class FEXCore::CPU::JITCore;
|
||||
#ifdef JIT_ARM64
|
||||
friend class FEXCore::CPU::Arm64JITCore;
|
||||
#endif
|
||||
#ifdef JIT_X86_64
|
||||
friend class FEXCore::CPU::X86JITCore;
|
||||
#endif
|
||||
|
||||
friend class FEXCore::IR::Validation::IRValidation;
|
||||
|
||||
struct {
|
||||
bool Multiblock {false};
|
||||
bool BreakOnFrontendFailure {true};
|
||||
int64_t MaxInstPerBlock {-1LL};
|
||||
uint64_t VirtualMemSize {1ULL << 36};
|
||||
CoreRunningMode RunningMode {CoreRunningMode::MODE_RUN};
|
||||
FEXCore::Config::ConfigCore Core {FEXCore::Config::CONFIG_INTERPRETER};
|
||||
bool GdbServer {false};
|
||||
std::string RootFSPath;
|
||||
std::string ThunkLibsPath;
|
||||
|
||||
bool Is64BitMode {true};
|
||||
bool TSOEnabled {true};
|
||||
FEXCore::Config::ConfigSMCChecks SMCChecks {FEXCore::Config::CONFIG_SMC_MMAN};
|
||||
bool ABILocalFlags {false};
|
||||
bool ABINoPF {false};
|
||||
|
||||
bool AOTIRCapture {false};
|
||||
bool AOTIRLoad {false};
|
||||
|
||||
std::string DumpIR;
|
||||
uint64_t VirtualMemSize{1ULL << 36};
|
||||
|
||||
// this is for internal use
|
||||
bool ValidateIRarser { false };
|
||||
|
||||
FEX_CONFIG_OPT(Multiblock, MULTIBLOCK);
|
||||
FEX_CONFIG_OPT(SingleStepConfig, SINGLESTEP);
|
||||
FEX_CONFIG_OPT(GdbServer, GDBSERVER);
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
|
||||
FEX_CONFIG_OPT(ABILocalFlags, ABILOCALFLAGS);
|
||||
FEX_CONFIG_OPT(ABINoPF, ABINOPF);
|
||||
FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE);
|
||||
FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE);
|
||||
FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD);
|
||||
FEX_CONFIG_OPT(SMCChecks, SMCCHECKS);
|
||||
FEX_CONFIG_OPT(Core, CORE);
|
||||
FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST);
|
||||
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
|
||||
FEX_CONFIG_OPT(DumpIR, DUMPIR);
|
||||
} Config;
|
||||
|
||||
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
@@ -105,30 +144,30 @@ namespace FEXCore::Context {
|
||||
std::unique_ptr<FEXCore::ThunkHandler> ThunkHandler;
|
||||
|
||||
CustomCPUFactoryType CustomCPUFactory;
|
||||
CustomCPUFactoryType FallbackCPUFactory;
|
||||
std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> CustomExitHandler;
|
||||
|
||||
struct AOTIRCacheEntry {
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint64_t crc;
|
||||
IR::IRListView *IR;
|
||||
IR::RegisterAllocationData *RAData;
|
||||
AOTIRInlineIndex *Array;
|
||||
void *mapping;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
std::function<std::unique_ptr<std::istream>(const std::string&)> AOTIRLoader;
|
||||
std::unordered_map<std::string, std::map<uint64_t, AOTIRCacheEntry>> AOTIRCache;
|
||||
std::unordered_map<std::string, AOTIRCacheEntry> AOTIRCache;
|
||||
std::function<int(const std::string&)> AOTIRLoader;
|
||||
std::unordered_map<std::string, std::map<uint64_t, AOTIRCaptureCacheEntry>> AOTIRCaptureCache;
|
||||
|
||||
struct AddrToFileEntry {
|
||||
uint64_t Start;
|
||||
uint64_t Len;
|
||||
uint64_t Offset;
|
||||
std::string fileid;
|
||||
std::string filename;
|
||||
void *CachedFileEntry;
|
||||
bool ContainsCode;
|
||||
};
|
||||
|
||||
std::map<uint64_t, AddrToFileEntry> AddrToFile;
|
||||
|
||||
std::map<std::string, std::string> FilesWithCode;
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
std::unique_ptr<FEXCore::BlockSamplingData> BlockData;
|
||||
@@ -162,27 +201,30 @@ namespace FEXCore::Context {
|
||||
|
||||
static void RemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
// Wrapper which takes CpuStateFrame instead of InternalThreadState
|
||||
static void RemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
RemoveCodeEntry(Frame->Thread, GuestRIP);
|
||||
}
|
||||
|
||||
// Debugger interface
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
uint64_t GetThreadCount() const;
|
||||
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(uint64_t Thread);
|
||||
FEXCore::Core::CPUState GetCPUState();
|
||||
bool GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data);
|
||||
bool FindHostCodeForRIP(uint64_t RIP, uint8_t **Code);
|
||||
|
||||
// XXX:
|
||||
// bool FindIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList **ir);
|
||||
// void SetIRForRIP(uint64_t RIP, FEXCore::IR::IntrusiveIRList *const ir);
|
||||
FEXCore::Core::ThreadState *GetThreadState();
|
||||
void LoadEntryList();
|
||||
|
||||
std::tuple<FEXCore::IR::IRListView *, FEXCore::IR::RegisterAllocationData *, uint64_t, uint64_t, uint64_t, uint64_t> GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
uintptr_t CompileBlock(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
uintptr_t CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
|
||||
|
||||
// same as CompileBlock, but aborts on failure
|
||||
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
|
||||
|
||||
bool LoadAOTIRCache(std::istream &stream);
|
||||
bool LoadAOTIRCache(int streamfd);
|
||||
bool WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer);
|
||||
|
||||
// Used for thread creation from syscalls
|
||||
void InitializeCompiler(FEXCore::Core::InternalThreadState* State, bool CompileThread);
|
||||
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
@@ -191,6 +233,9 @@ namespace FEXCore::Context {
|
||||
void CopyMemoryMapping(FEXCore::Core::InternalThreadState *ParentThread, FEXCore::Core::InternalThreadState *ChildThread);
|
||||
void RunThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
void DestroyThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void CleanupAfterFork(FEXCore::Core::InternalThreadState *ExceptForThread);
|
||||
|
||||
std::vector<FEXCore::Core::InternalThreadState*> *const GetThreads() { return &Threads; }
|
||||
|
||||
void AddNamedRegion(uintptr_t Base, uintptr_t Size, uintptr_t Offset, const std::string &filename);
|
||||
@@ -200,13 +245,15 @@ namespace FEXCore::Context {
|
||||
FEXCore::JITSymbols Symbols;
|
||||
#endif
|
||||
|
||||
// Public for threading
|
||||
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
protected:
|
||||
void ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, bool AlsoClearIRCache);
|
||||
|
||||
private:
|
||||
void WaitForIdleWithTimeout();
|
||||
|
||||
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void NotifyPause();
|
||||
|
||||
void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length);
|
||||
@@ -214,18 +261,13 @@ namespace FEXCore::Context {
|
||||
FEXCore::CodeLoader *LocalLoader{};
|
||||
|
||||
// Entry Cache
|
||||
bool GetFilenameHash(std::string const &Filename, std::string &Hash);
|
||||
void AddThreadRIPsToEntryList(FEXCore::Core::InternalThreadState *Thread);
|
||||
void SaveEntryList();
|
||||
std::set<uint64_t> EntryList;
|
||||
std::vector<uint64_t> InitLocations;
|
||||
uint64_t StartingRIP;
|
||||
std::mutex ExitMutex;
|
||||
std::unique_ptr<GdbServer> DebugServer;
|
||||
|
||||
bool StartPaused = false;
|
||||
FEXCore::Config::Value<std::string> AppFilename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
|
||||
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
|
||||
};
|
||||
|
||||
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args);
|
||||
uint64_t HandleSyscall(FEXCore::HLE::SyscallHandler *Handler, FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args);
|
||||
}
|
||||
@@ -38,8 +38,8 @@ static bool StoreCAS8(uint8_t &Expected, uint8_t Val, uint64_t Addr) {
|
||||
return Atom->compare_exchange_strong(Expected, Val);
|
||||
}
|
||||
|
||||
bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
|
||||
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
|
||||
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
|
||||
|
||||
if (info->si_code != BUS_ADRALN) {
|
||||
@@ -827,8 +827,8 @@ std::tuple<uint64_t, bool> DoCAS64(
|
||||
|
||||
}
|
||||
|
||||
bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
|
||||
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
|
||||
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
|
||||
|
||||
if (info->si_code != BUS_ADRALN) {
|
||||
@@ -921,8 +921,8 @@ bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = reinterpret_cast<mcontext_t*>(_mcontext);
|
||||
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
mcontext_t* mcontext = &reinterpret_cast<ucontext_t*>(_ucontext)->uc_mcontext;
|
||||
siginfo_t* info = reinterpret_cast<siginfo_t*>(_info);
|
||||
|
||||
if (info->si_code != BUS_ADRALN) {
|
||||
|
||||
@@ -12,6 +12,12 @@ namespace FEXCore::ArchHelpers::Arm64 {
|
||||
constexpr uint32_t ATOMIC_MEM_MASK = 0x3B200C00;
|
||||
constexpr uint32_t ATOMIC_MEM_INST = 0x38200000;
|
||||
|
||||
constexpr uint32_t LDAXP_MASK = 0xBF'FF'80'00;
|
||||
constexpr uint32_t LDAXP_INST = 0x88'7F'80'00;
|
||||
|
||||
constexpr uint32_t STLXP_MASK = 0xBF'E0'80'00;
|
||||
constexpr uint32_t STLXP_INST = 0x88'20'80'00;
|
||||
|
||||
// Load ops are 4 bits
|
||||
// Acquire and release bits are independent on the instruction
|
||||
constexpr uint32_t ATOMIC_ADD_OP = 0b0000;
|
||||
@@ -24,7 +30,7 @@ namespace FEXCore::ArchHelpers::Arm64 {
|
||||
constexpr uint32_t ATOMIC_UMIN_OP = 0b0111;
|
||||
constexpr uint32_t ATOMIC_SWAP_OP = 0b1000;
|
||||
|
||||
bool HandleCASPAL(void *_mcontext, void *_info, uint32_t Instr);
|
||||
bool HandleCASAL(void *_mcontext, void *_info, uint32_t Instr);
|
||||
bool HandleAtomicMemOp(void *_mcontext, void *_info, uint32_t Instr);
|
||||
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr);
|
||||
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr);
|
||||
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr);
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define STATE x28
|
||||
|
||||
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
|
||||
Arm64Emitter::Arm64Emitter(size_t size) : vixl::aarch64::Assembler(size, vixl::aarch64::PositionDependentCode) {
|
||||
CPU.SetUp();
|
||||
|
||||
auto Features = vixl::CPUFeatures::InferFromOS();
|
||||
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
|
||||
// RCPC is bugged on Snapdragon 865
|
||||
// Causes glibc cond16 test to immediately throw assert
|
||||
// __pthread_mutex_cond_lock: Assertion `mutex->__data.__owner == 0'
|
||||
SupportsRCPC = false; //Features.Has(vixl::CPUFeatures::Feature::kRCpc);
|
||||
|
||||
if (SupportsAtomics) {
|
||||
// Hypervisor can hide this on the c630?
|
||||
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
|
||||
}
|
||||
|
||||
SetCPUFeatures(Features);
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE("Host CPU doesn't support atomics. Expect bad performance");
|
||||
}
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
// We need to get the CPU's cache line size
|
||||
// We expect sane targets that have correct cacheline sizes across clusters
|
||||
uint64_t CTR;
|
||||
__asm volatile ("mrs %[ctr], ctr_el0"
|
||||
: [ctr] "=r"(CTR));
|
||||
|
||||
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
|
||||
ICacheLineSize = 4 << (CTR & 0xF);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Arm64Emitter::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
|
||||
bool Is64Bit = Reg.IsX();
|
||||
int Segments = Is64Bit ? 4 : 2;
|
||||
|
||||
if (Is64Bit && ((~Constant)>> 16) == 0) {
|
||||
movn(Reg, (~Constant) & 0xFFFF);
|
||||
return;
|
||||
}
|
||||
|
||||
movz(Reg, (Constant) & 0xFFFF, 0);
|
||||
for (int i = 1; i < Segments; ++i) {
|
||||
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
|
||||
if (Part) {
|
||||
movk(Reg, Part, i * 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushCalleeSavedRegisters() {
|
||||
// We need to save pairs of registers
|
||||
// We save r19-r30
|
||||
MemOperand PairOffset(sp, -16, PreIndex);
|
||||
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
|
||||
{x19, x20},
|
||||
{x21, x22},
|
||||
{x23, x24},
|
||||
{x25, x26},
|
||||
{x27, x28},
|
||||
{x29, x30},
|
||||
}};
|
||||
|
||||
for (auto &RegPair : CalleeSaved) {
|
||||
stp(RegPair.first, RegPair.second, PairOffset);
|
||||
}
|
||||
|
||||
// Additionally we need to store the lower 64bits of v8-v15
|
||||
// Here's a fun thing, we can use two ST4 instructions to store everything
|
||||
// We just need a single sub to sp before that
|
||||
const std::array<
|
||||
std::tuple<vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister>, 2> FPRs = {{
|
||||
{v8, v9, v10, v11},
|
||||
{v12, v13, v14, v15},
|
||||
}};
|
||||
|
||||
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
|
||||
sub(sp, sp, VectorSaveSize);
|
||||
// SP supporting move
|
||||
// We just saved x19 so it is safe
|
||||
add(x19, sp, 0);
|
||||
|
||||
MemOperand QuadOffset(x19, 32, PostIndex);
|
||||
for (auto &RegQuad : FPRs) {
|
||||
st4(std::get<0>(RegQuad).D(),
|
||||
std::get<1>(RegQuad).D(),
|
||||
std::get<2>(RegQuad).D(),
|
||||
std::get<3>(RegQuad).D(),
|
||||
0,
|
||||
QuadOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopCalleeSavedRegisters() {
|
||||
const std::array<
|
||||
std::tuple<vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister>, 2> FPRs = {{
|
||||
{v12, v13, v14, v15},
|
||||
{v8, v9, v10, v11},
|
||||
}};
|
||||
|
||||
MemOperand QuadOffset(sp, 32, PostIndex);
|
||||
for (auto &RegQuad : FPRs) {
|
||||
ld4(std::get<0>(RegQuad).D(),
|
||||
std::get<1>(RegQuad).D(),
|
||||
std::get<2>(RegQuad).D(),
|
||||
std::get<3>(RegQuad).D(),
|
||||
0,
|
||||
QuadOffset);
|
||||
}
|
||||
|
||||
MemOperand PairOffset(sp, 16, PostIndex);
|
||||
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
|
||||
{x29, x30},
|
||||
{x27, x28},
|
||||
{x25, x26},
|
||||
{x23, x24},
|
||||
{x21, x22},
|
||||
{x19, x20},
|
||||
}};
|
||||
|
||||
for (auto &RegPair : CalleeSaved) {
|
||||
ldp(RegPair.first, RegPair.second, PairOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Arm64Emitter::SpillStaticRegs() {
|
||||
for (size_t i = 0; i < SRA64.size(); i+=2) {
|
||||
stp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
|
||||
stp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::FillStaticRegs() {
|
||||
for (size_t i = 0; i < SRA64.size(); i+=2) {
|
||||
ldp(SRA64[i], SRA64[i+1], MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[i])));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
|
||||
ldp(SRAFPR[i].Q(), SRAFPR[i+1].Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
|
||||
sub(sp, sp, SPOffset);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
str(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
str(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
|
||||
str(lr, MemOperand(sp, i * 8));
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
ldr(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
ldr(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
|
||||
ldr(lr, MemOperand(sp, i * 8));
|
||||
|
||||
add(sp, sp, SPOffset);
|
||||
}
|
||||
|
||||
void Arm64Emitter::ResetStack() {
|
||||
if (SpillSlots == 0)
|
||||
return;
|
||||
|
||||
if (IsImmAddSub(SpillSlots * 16)) {
|
||||
add(sp, sp, SpillSlots * 16);
|
||||
} else {
|
||||
// Too big to fit in a 12bit immediate
|
||||
LoadConstant(x0, SpillSlots * 16);
|
||||
add(sp, sp, x0);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Emitter::Align16B() {
|
||||
uint64_t CurrentOffset = GetBuffer()->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
for (uint64_t i = (16 - (CurrentOffset & 0xF)); i != 0; i -= 4) {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
// All but x29 are caller saved
|
||||
const std::array<aarch64::Register, 16> SRA64 = {
|
||||
x4, x5, x6, x7, x8, x9, x10, x11,
|
||||
x12, x18, x17, x16, x15, x14, x13, x29
|
||||
};
|
||||
|
||||
// All are callee saved
|
||||
const std::array<aarch64::Register, 9> RA64 = {
|
||||
x20, x21, x22, x23, x24, x25, x26, x27,
|
||||
x19
|
||||
};
|
||||
|
||||
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
|
||||
{x20, x21},
|
||||
{x22, x23},
|
||||
{x24, x25},
|
||||
{x26, x27},
|
||||
}};
|
||||
|
||||
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
|
||||
{w20, w21},
|
||||
{w22, w23},
|
||||
{w24, w25},
|
||||
{w26, w27},
|
||||
}};
|
||||
|
||||
// All are caller saved
|
||||
const std::array<aarch64::VRegister, 16> SRAFPR = {
|
||||
v16, v17, v18, v19, v20, v21, v22, v23,
|
||||
v24, v25, v26, v27, v28, v29, v30, v31
|
||||
};
|
||||
|
||||
// v8..v15 = (lower 64bits) Callee saved
|
||||
const std::array<aarch64::VRegister, 12> RAFPR = {
|
||||
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
|
||||
v8, v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
// This class contains common emitter utility functions that can
|
||||
// be used by both Arm64 JIT and ARM64 Dispatcher
|
||||
class Arm64Emitter : public vixl::aarch64::Assembler {
|
||||
protected:
|
||||
Arm64Emitter(size_t size);
|
||||
|
||||
vixl::aarch64::CPU CPU;
|
||||
bool SupportsAtomics{};
|
||||
bool SupportsRCPC{};
|
||||
|
||||
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
|
||||
void SpillStaticRegs();
|
||||
void FillStaticRegs();
|
||||
|
||||
void PushDynamicRegsAndLR();
|
||||
void PopDynamicRegsAndLR();
|
||||
|
||||
void PushCalleeSavedRegisters();
|
||||
void PopCalleeSavedRegisters();
|
||||
|
||||
void ResetStack();
|
||||
void Align16B();
|
||||
|
||||
uint32_t SpillSlots{};
|
||||
|
||||
uint32_t DCacheLineSize{};
|
||||
uint32_t ICacheLineSize{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "Interface/Core/ArchHelpers/Arm64.h"
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
namespace FEXCore::ArchHelpers::Arm64 {
|
||||
|
||||
#ifndef _M_ARM_64
|
||||
// These are stub implementations that exist only to allow instantiating the arm64 jit
|
||||
// on non arm platforms.
|
||||
|
||||
// Obvously such a configuration can't do the actual arm64-specific stuff
|
||||
|
||||
bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
ERROR_AND_DIE("HandleCASPAL Not Implemented");
|
||||
}
|
||||
|
||||
bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
ERROR_AND_DIE("HandleCASAL Not Implemented");
|
||||
}
|
||||
|
||||
bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) {
|
||||
ERROR_AND_DIE("HandleAtomicMemOp Not Implemented");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/UContext.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace FEXCore::ArchHelpers::Context {
|
||||
|
||||
struct X86ContextBackup {
|
||||
// Host State
|
||||
// RIP and RSP is stored in GPRs here
|
||||
uint64_t GPRs[23];
|
||||
FEXCore::x86_64::_libc_fpstate FPRState;
|
||||
|
||||
// Guest state
|
||||
int Signal;
|
||||
FEXCore::Core::CPUState GuestState;
|
||||
|
||||
static constexpr int RedZoneSize = 128;
|
||||
};
|
||||
|
||||
struct ArmContextBackup {
|
||||
// Host State
|
||||
uint64_t GPRs[31];
|
||||
uint64_t PrevSP;
|
||||
uint64_t PrevPC;
|
||||
uint64_t PState;
|
||||
uint32_t FPSR;
|
||||
uint32_t FPCR;
|
||||
__uint128_t FPRs[32];
|
||||
|
||||
// Guest state
|
||||
int Signal;
|
||||
FEXCore::Core::CPUState GuestState;
|
||||
|
||||
// Arm64 doesn't have a red zone
|
||||
static constexpr int RedZoneSize = 0;
|
||||
};
|
||||
|
||||
static inline mcontext_t* GetMContext(void* ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
return &_context->uc_mcontext;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
|
||||
static inline uint64_t GetSp(void* ucontext) {
|
||||
return GetMContext(ucontext)->sp;
|
||||
}
|
||||
|
||||
static inline uint64_t GetPc(void* ucontext) {
|
||||
return GetMContext(ucontext)->pc;
|
||||
}
|
||||
|
||||
static inline void SetSp(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->sp = val;
|
||||
}
|
||||
|
||||
static inline void SetPc(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->pc = val;
|
||||
}
|
||||
|
||||
static inline uint64_t GetState(void* ucontext) {
|
||||
return GetMContext(ucontext)->regs[28];
|
||||
}
|
||||
|
||||
static inline void SetState(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->regs[28] = val;
|
||||
}
|
||||
|
||||
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
|
||||
return GetMContext(ucontext)->regs[id];
|
||||
}
|
||||
|
||||
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
|
||||
GetMContext(ucontext)->regs[id] = val;
|
||||
}
|
||||
|
||||
constexpr uint32_t FPR_MAGIC = 0x46508001U;
|
||||
|
||||
struct HostCTXHeader {
|
||||
uint32_t Magic;
|
||||
uint32_t Size;
|
||||
};
|
||||
|
||||
struct HostFPRState {
|
||||
HostCTXHeader Head;
|
||||
uint32_t FPSR;
|
||||
uint32_t FPCR;
|
||||
__uint128_t FPRs[32];
|
||||
};
|
||||
|
||||
using ContextBackup = ArmContextBackup;
|
||||
template <typename T>
|
||||
static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, ArmContextBackup>::value) {
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
memcpy(&Backup->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
|
||||
Backup->PrevSP = ArchHelpers::Context::GetSp(ucontext);
|
||||
Backup->PrevPC = ArchHelpers::Context::GetPc(ucontext);
|
||||
Backup->PState = _mcontext->pstate;
|
||||
|
||||
// Host FPR state starts at _mcontext->reserved[0];
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LOGMAN_THROW_A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
|
||||
Backup->FPSR = HostState->FPSR;
|
||||
Backup->FPCR = HostState->FPCR;
|
||||
memcpy(&Backup->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
|
||||
} else {
|
||||
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, ArmContextBackup>::value) {
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LOGMAN_THROW_A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
|
||||
memcpy(&HostState->FPRs[0], &Backup->FPRs[0], 32 * sizeof(__uint128_t));
|
||||
HostState->FPCR = Backup->FPCR;
|
||||
HostState->FPSR = Backup->FPSR;
|
||||
|
||||
// Restore GPRs and other state
|
||||
_mcontext->pstate = Backup->PState;
|
||||
ArchHelpers::Context::SetPc(ucontext, Backup->PrevPC);
|
||||
ArchHelpers::Context::SetSp(ucontext, Backup->PrevSP);
|
||||
memcpy(&_mcontext->regs[0], &Backup->GPRs[0], 31 * sizeof(uint64_t));
|
||||
} else {
|
||||
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _M_X86_64
|
||||
|
||||
static inline uint64_t GetSp(void* ucontext) {
|
||||
return GetMContext(ucontext)->gregs[REG_RSP];
|
||||
}
|
||||
|
||||
static inline uint64_t GetPc(void* ucontext) {
|
||||
return GetMContext(ucontext)->gregs[REG_RIP];
|
||||
}
|
||||
|
||||
static inline void SetSp(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->gregs[REG_RSP] = val;
|
||||
}
|
||||
|
||||
static inline void SetPc(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->gregs[REG_RIP] = val;
|
||||
}
|
||||
|
||||
static inline uint64_t GetState(void* ucontext) {
|
||||
return GetMContext(ucontext)->gregs[REG_R14];
|
||||
}
|
||||
|
||||
static inline void SetState(void* ucontext, uint64_t val) {
|
||||
GetMContext(ucontext)->gregs[REG_R14] = val;
|
||||
}
|
||||
|
||||
static inline uint64_t GetArmReg(void* ucontext, uint32_t id) {
|
||||
ERROR_AND_DIE("Not impelented for x86 host");
|
||||
}
|
||||
|
||||
static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
|
||||
ERROR_AND_DIE("Not impelented for x86 host");
|
||||
}
|
||||
|
||||
using ContextBackup = X86ContextBackup;
|
||||
template <typename T>
|
||||
static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, X86ContextBackup>::value) {
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&Backup->GPRs[0], &_mcontext->gregs[0], sizeof(X86ContextBackup::GPRs));
|
||||
// Copy the FPRState
|
||||
memcpy(&Backup->FPRState, _mcontext->fpregs, sizeof(X86ContextBackup::FPRState));
|
||||
// XXX: Save 256bit and 512bit AVX register state
|
||||
} else {
|
||||
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, X86ContextBackup>::value) {
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&_mcontext->gregs[0], &Backup->GPRs[0], sizeof(X86ContextBackup::GPRs));
|
||||
// Copy the FPRState
|
||||
memcpy(_mcontext->fpregs, &Backup->FPRState, sizeof(X86ContextBackup::FPRState));
|
||||
} else {
|
||||
ERROR_AND_DIE("Wrong context type"); // This must be a runtime error
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace FEXCore::ArchHelpers::Context
|
||||
+172
-4
@@ -1,11 +1,42 @@
|
||||
/*
|
||||
$info$
|
||||
tags: opcodes|cpuid
|
||||
desc: Handles presented capability bits for guest cpu
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "git_version.h"
|
||||
|
||||
#include <cstring>
|
||||
#ifdef _M_X86_64
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore {
|
||||
//#define CPUID_AMD
|
||||
#ifdef _M_ARM_64
|
||||
static uint32_t GetCycleCounterFrequency() {
|
||||
uint64_t Result{};
|
||||
__asm("mrs %[Res], CNTFRQ_EL0"
|
||||
: [Res] "=r" (Result));
|
||||
return Result;
|
||||
}
|
||||
#else
|
||||
static uint32_t GetCycleCounterFrequency() {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
__cpuid(0, eax, ebx, ecx, edx);
|
||||
if (eax >= 0x15) {
|
||||
__cpuid(0x15, eax, ebx, ecx, edx);
|
||||
|
||||
if (eax && ebx && ecx) {
|
||||
return ecx * ebx / eax;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_0h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
@@ -93,7 +124,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h() {
|
||||
(1 << 16) | // Page Attribute Table
|
||||
(1 << 17) | // 36bit page size extension
|
||||
(0 << 18) | // Processor serial number
|
||||
(0 << 19) | // CLFLUSH
|
||||
(1 << 19) | // CLFLUSH
|
||||
(0 << 20) | // Reserved
|
||||
(0 << 21) | // Debug store
|
||||
(0 << 22) | // Thermal monitor and software controled clock
|
||||
@@ -251,6 +282,18 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h() {
|
||||
return Res;
|
||||
}
|
||||
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_15h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
// TSC frequency = ECX * EBX / EAX
|
||||
uint32_t FrequencyHz = GetCycleCounterFrequency();
|
||||
if (FrequencyHz) {
|
||||
Res.eax = 1;
|
||||
Res.ebx = 1;
|
||||
Res.ecx = FrequencyHz;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Highest extended function implemented
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0000h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
@@ -375,10 +418,120 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0004h() {
|
||||
return Res;
|
||||
}
|
||||
|
||||
// L1 Cache and TLB identifiers
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0005h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
|
||||
// L1 TLB Information for 2MB and 4MB pages
|
||||
Res.eax =
|
||||
(64 << 0) | // Number of TLB instruction entries
|
||||
(255 << 8) | // instruction TLB associativity type (full)
|
||||
(64 << 16) | // Number of TLB data entries
|
||||
(255 << 24); // data TLB associativity type (full)
|
||||
|
||||
// L1 TLB Information for 4KB pages
|
||||
Res.ebx =
|
||||
(64 << 0) | // Number of TLB instruction entries
|
||||
(255 << 8) | // instruction TLB associativity type (full)
|
||||
(64 << 16) | // Number of TLB data entries
|
||||
(255 << 24); // data TLB associativity type (full)
|
||||
|
||||
// L1 data cache identifiers
|
||||
Res.ecx =
|
||||
(64 << 0) | // L1 data cache size line in bytes
|
||||
(1 << 8) | // L1 data cachelines per tag
|
||||
(8 << 16) | // L1 data cache associativity
|
||||
(32 << 24); // L1 data cache size in KB
|
||||
|
||||
// L1 instruction cache identifiers
|
||||
Res.edx =
|
||||
(64 << 0) | // L1 instruction cache line size in bytes
|
||||
(1 << 8) | // L1 instruction cachelines per tag
|
||||
(4 << 16) | // L1 instruction cache associativity
|
||||
(64 << 24); // L1 instruction cache size in KB
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
// L2 Cache identifiers
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0006h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
|
||||
// L2 TLB Information for 2MB and 4MB pages
|
||||
Res.eax =
|
||||
(1024 << 0) | // Number of TLB instruction entries
|
||||
(6 << 12) | // instruction TLB associativity type
|
||||
(1536 << 16) | // Number of TLB data entries
|
||||
(3 << 28); // data TLB associativity type
|
||||
|
||||
// L2 TLB Information for 4KB pages
|
||||
Res.ebx =
|
||||
(1024 << 0) | // Number of TLB instruction entries
|
||||
(6 << 12) | // instruction TLB associativity type
|
||||
(1536 << 16) | // Number of TLB data entries
|
||||
(5 << 28); // data TLB associativity type
|
||||
|
||||
// L2 cache identifiers
|
||||
Res.ecx =
|
||||
(64 << 0) | // cacheline size
|
||||
(1 << 8) | // cachelines per tag
|
||||
(6 << 12) | // cache associativity
|
||||
(512 << 16); // L2 cache size in KB
|
||||
|
||||
// L3 cache identifiers
|
||||
Res.edx =
|
||||
(64 << 0) | // cacheline size
|
||||
(1 << 8) | // cachelines per tag
|
||||
(6 << 12) | // cache associativity
|
||||
(16 << 18); // L2 cache size in KB
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Advanced power management
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0007h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
Res.eax = (1 << 2); // APIC timer not affected by p-state
|
||||
Res.edx =
|
||||
(1 << 8); // Invariant TSC
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Virtual and physical address sizes
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0008h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
Res.eax =
|
||||
(48 << 0) | // PhysAddrSize = 48-bit
|
||||
(48 << 8) | // LinAddrSize = 48-bit
|
||||
(0 << 16); // GuestPhysAddrSize == PhysAddrSize
|
||||
|
||||
Res.ebx =
|
||||
(0 << 2) | // XSaveErPtr: Saving and restoring error pointers
|
||||
(0 << 1) | // IRPerf: Instructions retired count support
|
||||
(0 << 0); // CLZERO support
|
||||
|
||||
uint32_t CoreCount = Cores() - 1;
|
||||
Res.ecx =
|
||||
(0 << 16) | // PerfTscSize: Performance timestamp count size
|
||||
(0 << 12) | // ApicIdSize: Number of bits in ApicID
|
||||
(CoreCount << 0); // Count count subtract one
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
// TLB 1GB page identifiers
|
||||
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0019h() {
|
||||
FEXCore::CPUID::FunctionResults Res{};
|
||||
Res.eax =
|
||||
(0xF << 28) | // L1 DTLB associativity for 1GB pages
|
||||
(64 << 16) | // L1 DTLB entry count for 1GB pages
|
||||
(0xF << 12) | // L1 ITLB associativity for 1GB pages
|
||||
(64 << 0); // L1 ITLB entry count for 1GB pages
|
||||
|
||||
Res.ebx =
|
||||
(0 << 28) | // L2 DTLB associativity for 1GB pages
|
||||
(0 << 16) | // L2 DTLB entry count for 1GB pages
|
||||
(0 << 12) | // L2 ITLB associativity for 1GB pages
|
||||
(0 << 0); // L2 ITLB entry count for 1GB pages
|
||||
return Res;
|
||||
}
|
||||
|
||||
@@ -408,7 +561,11 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
|
||||
// 0x12: Intel SGX capability enumeration
|
||||
// 0x13: Reserved
|
||||
// 0x14: Intel Processor trace
|
||||
// 0x15: Timestamp counter information
|
||||
#ifndef CPUID_AMD
|
||||
// Timestamp counter information
|
||||
// Doesn't exist on AMD hardware
|
||||
RegisterFunction(0x15, std::bind(&CPUIDEmu::Function_15h, this));
|
||||
#endif
|
||||
// 0x16: Processor frequency information
|
||||
// 0x17: SoC vendor attribute enumeration
|
||||
|
||||
@@ -423,12 +580,23 @@ void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
|
||||
// Processor brand string continued
|
||||
RegisterFunction(0x8000'0004, std::bind(&CPUIDEmu::Function_8000_0004h, this));
|
||||
// 0x8000'0005: L1 Cache and TLB identifiers
|
||||
#ifdef CPUID_AMD
|
||||
RegisterFunction(0x8000'0005, std::bind(&CPUIDEmu::Function_8000_0005h, this));
|
||||
#else
|
||||
// This is full reserved on Intel platforms
|
||||
RegisterFunction(0x8000'0005, std::bind(&CPUIDEmu::Function_Reserved, this));
|
||||
#endif
|
||||
// 0x8000'0006: L2 Cache identifiers
|
||||
RegisterFunction(0x8000'0006, std::bind(&CPUIDEmu::Function_8000_0006h, this));
|
||||
// Advanced power management information
|
||||
RegisterFunction(0x8000'0007, std::bind(&CPUIDEmu::Function_8000_0007h, this));
|
||||
// 0x8000'0008: Virtual and physical address sizes
|
||||
// Virtual and physical address sizes
|
||||
RegisterFunction(0x8000'0008, std::bind(&CPUIDEmu::Function_8000_0008h, this));
|
||||
|
||||
// 0x8000'000A: SVM Revision
|
||||
// 0x8000'0019: TLB 1GB page identifiers
|
||||
// TLB 1GB page identifiers
|
||||
RegisterFunction(0x8000'0019, std::bind(&CPUIDEmu::Function_8000_0019h, this));
|
||||
|
||||
// 0x8000'001A: Performance optimization identifiers
|
||||
// 0x8000'001B: Instruction based sampling identifiers
|
||||
// 0x8000'001C: Lightweight profiling capabilities
|
||||
|
||||
+9
-2
@@ -3,6 +3,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
namespace FEXCore {
|
||||
@@ -23,7 +24,7 @@ private:
|
||||
public:
|
||||
void Init(FEXCore::Context::Context *ctx);
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function) {
|
||||
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, [[maybe_unused]] uint32_t Leaf) {
|
||||
auto Handler = FunctionHandlers.find(Function);
|
||||
|
||||
if (Handler == FunctionHandlers.end()) {
|
||||
@@ -37,6 +38,7 @@ public:
|
||||
}
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEX_CONFIG_OPT(Cores, THREADS);
|
||||
|
||||
using FunctionHandler = std::function<FEXCore::CPUID::FunctionResults()>;
|
||||
void RegisterFunction(uint32_t Function, FunctionHandler Handler) {
|
||||
@@ -51,13 +53,18 @@ private:
|
||||
FEXCore::CPUID::FunctionResults Function_02h();
|
||||
FEXCore::CPUID::FunctionResults Function_06h();
|
||||
FEXCore::CPUID::FunctionResults Function_07h();
|
||||
FEXCore::CPUID::FunctionResults Function_15h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0000h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0001h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0002h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0003h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0004h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0005h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0006h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0007h();
|
||||
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0008h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0009h();
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0019h();
|
||||
FEXCore::CPUID::FunctionResults Function_Reserved();
|
||||
};
|
||||
}
|
||||
+14
-10
@@ -5,6 +5,12 @@
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
namespace FEXCore {
|
||||
static void* ThreadHandler(void *Arg) {
|
||||
FEXCore::CompileService *This = reinterpret_cast<FEXCore::CompileService*>(Arg);
|
||||
This->ExecutionThread();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CompileService::CompileService(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CTX {ctx}
|
||||
, ParentThread {Thread} {
|
||||
@@ -16,9 +22,7 @@ namespace FEXCore {
|
||||
CTX->InitializeCompiler(CompileThreadData.get(), true);
|
||||
CompileThreadData->CPUBackend->CopyNecessaryDataForCompileThread(ParentThread->CPUBackend.get());
|
||||
|
||||
WorkerThread = std::thread([this]() {
|
||||
ExecutionThread();
|
||||
});
|
||||
WorkerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
|
||||
}
|
||||
|
||||
void CompileService::Initialize() {
|
||||
@@ -30,7 +34,7 @@ namespace FEXCore {
|
||||
ShuttingDown = true;
|
||||
// Kick the working thread
|
||||
StartWork.NotifyAll();
|
||||
WorkerThread.join();
|
||||
WorkerThread->join(nullptr);
|
||||
}
|
||||
|
||||
void CompileService::ClearCache(FEXCore::Core::InternalThreadState *Thread) {
|
||||
@@ -57,7 +61,7 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
LogMan::Throw::A(CompileThreadData->LocalIRCache.size() == 0, "Compile service must never have LocalIRCache");
|
||||
LOGMAN_THROW_A(CompileThreadData->LocalIRCache.size() == 0, "Compile service must never have LocalIRCache");
|
||||
|
||||
CompileMutex.unlock();
|
||||
}
|
||||
@@ -92,7 +96,7 @@ namespace FEXCore {
|
||||
|
||||
// Set our thread name so we can see its relation
|
||||
char ThreadName[16]{};
|
||||
snprintf(ThreadName, 16, "%ld-CS", ParentThread->State.ThreadManager.TID.load());
|
||||
snprintf(ThreadName, 16, "%ld-CS", ParentThread->ThreadManager.TID.load());
|
||||
pthread_setname_np(pthread_self(), ThreadName);
|
||||
|
||||
while (true) {
|
||||
@@ -120,15 +124,15 @@ namespace FEXCore {
|
||||
// If we had a work item then work on it
|
||||
if (Item) {
|
||||
// Make sure it's not in lookup cache by accident
|
||||
LogMan::Throw::A(CompileThreadData->LookupCache->FindBlock(Item->RIP) == 0, "Compile Service must never have entries in the LookupCache");
|
||||
|
||||
LOGMAN_THROW_A(CompileThreadData->LookupCache->FindBlock(Item->RIP) == 0, "Compile Service must never have entries in the LookupCache");
|
||||
|
||||
// Code isn't in cache, compile now
|
||||
// Set our thread state's RIP
|
||||
CompileThreadData->State.State.rip = Item->RIP;
|
||||
CompileThreadData->CurrentFrame->State.rip = Item->RIP;
|
||||
|
||||
auto [CodePtr, IRList, DebugData, RAData, Generated, StartAddr, Length] = CTX->CompileCode(CompileThreadData.get(), Item->RIP);
|
||||
|
||||
LogMan::Throw::A(Generated == true, "Compile Service doesn't have IR Cache");
|
||||
LOGMAN_THROW_A(Generated == true, "Compile Service doesn't have IR Cache");
|
||||
|
||||
if (!CodePtr) {
|
||||
// XXX: We currently have the expectation that compile service code will be significantly smaller than regular thread's code
|
||||
|
||||
+5
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <FEXCore/Utils/Threads.h>
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -45,12 +46,14 @@ class CompileService final {
|
||||
WorkItem *CompileCode(uint64_t RIP);
|
||||
void ClearCache(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
// Public for threading
|
||||
void ExecutionThread();
|
||||
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *ParentThread;
|
||||
|
||||
void ExecutionThread();
|
||||
std::thread WorkerThread;
|
||||
std::unique_ptr<FEXCore::Threads::Thread> WorkerThread;
|
||||
std::unique_ptr<FEXCore::Core::InternalThreadState> CompileThreadData;
|
||||
|
||||
std::mutex QueueMutex{};
|
||||
|
||||
+370
-326
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,364 @@
|
||||
#include "Interface/Core/ArchHelpers/MContext.h"
|
||||
#include "Interface/Core/Dispatcher/Arm64Dispatcher.h"
|
||||
|
||||
#include "Interface/Core/Interpreter/InterpreterClass.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE x28
|
||||
|
||||
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
|
||||
: Dispatcher(ctx, Thread), Arm64Emitter(MAX_DISPATCHER_CODE_SIZE) {
|
||||
SRAEnabled = config.StaticRegisterAssignment;
|
||||
SetAllowAssembler(true);
|
||||
|
||||
auto Buffer = GetBuffer();
|
||||
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
|
||||
|
||||
// while (true) {
|
||||
// Ptr = FindBlock(RIP)
|
||||
// if (!Ptr)
|
||||
// Ptr = CTX->CompileBlock(RIP);
|
||||
//
|
||||
// Ptr();
|
||||
// }
|
||||
|
||||
uint64_t VirtualMemorySize = Thread->LookupCache->GetVirtualMemorySize();
|
||||
Literal l_VirtualMemory {VirtualMemorySize};
|
||||
Literal l_PagePtr {Thread->LookupCache->GetPagePointer()};
|
||||
Literal l_L1Ptr {Thread->LookupCache->GetL1Pointer()};
|
||||
Literal l_CTX {reinterpret_cast<uintptr_t>(CTX)};
|
||||
Literal l_Sleep {reinterpret_cast<uint64_t>(SleepThread)};
|
||||
Literal l_CompileBlock {GetCompileBlockPtr()};
|
||||
Literal l_ExitFunctionLink {config.ExitFunctionLink};
|
||||
Literal l_ExitFunctionLinkThis {config.ExitFunctionLinkThis};
|
||||
|
||||
// Push all the register we need to save
|
||||
PushCalleeSavedRegisters();
|
||||
|
||||
// Push our memory base to the correct register
|
||||
// Move our thread pointer to the correct register
|
||||
// This is passed in to parameter 0 (x0)
|
||||
mov(STATE, x0);
|
||||
|
||||
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
||||
// regardless of where we were in the stack
|
||||
add(x0, sp, 0);
|
||||
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
|
||||
|
||||
AbsoluteLoopTopAddressFillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
|
||||
if (SRAEnabled) {
|
||||
FillStaticRegs();
|
||||
}
|
||||
|
||||
// We want to ensure that we are 16 byte aligned at the top of this loop
|
||||
Align16B();
|
||||
aarch64::Label FullLookup{};
|
||||
aarch64::Label CallBlock{};
|
||||
aarch64::Label LoopTop{};
|
||||
aarch64::Label ExitSpillSRA{};
|
||||
aarch64::Label ThreadPauseHandler{};
|
||||
|
||||
bind(&LoopTop);
|
||||
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
|
||||
|
||||
// Load in our RIP
|
||||
// Don't modify x2 since it contains our RIP once the block doesn't exist
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
|
||||
auto RipReg = x2;
|
||||
|
||||
// L1 Cache
|
||||
ldr(x0, &l_L1Ptr);
|
||||
|
||||
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
|
||||
add(x0, x0, Operand(x3, Shift::LSL, 4));
|
||||
ldp(x3, x0, MemOperand(x0));
|
||||
cmp(x0, RipReg);
|
||||
b(&FullLookup, Condition::ne);
|
||||
|
||||
if (!config.ExecuteBlocksWithCall) {
|
||||
br(x3);
|
||||
} else {
|
||||
b(&CallBlock);
|
||||
}
|
||||
|
||||
// L1C check failed, do a full lookup
|
||||
bind(&FullLookup);
|
||||
|
||||
// This is the block cache lookup routine
|
||||
// It matches what is going on it LookupCache.h::FindBlock
|
||||
ldr(x0, &l_PagePtr);
|
||||
|
||||
// Mask the address by the virtual address size so we can check for aliases
|
||||
if (__builtin_popcountl(VirtualMemorySize) == 1) {
|
||||
and_(x3, RipReg, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
}
|
||||
else {
|
||||
ldr(x3, &l_VirtualMemory);
|
||||
and_(x3, RipReg, x3);
|
||||
}
|
||||
|
||||
aarch64::Label NoBlock;
|
||||
{
|
||||
// Offset the address and add to our page pointer
|
||||
lsr(x1, x3, 12);
|
||||
|
||||
// Load the pointer from the offset
|
||||
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
|
||||
|
||||
// If page pointer is zero then we have no block
|
||||
cbz(x0, &NoBlock);
|
||||
|
||||
// Steal the page offset
|
||||
and_(x1, x3, 0x0FFF);
|
||||
|
||||
// Shift the offset by the size of the block cache entry
|
||||
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry))));
|
||||
|
||||
// Load the guest address first to ensure it maps to the address we are currently at
|
||||
// This fixes aliasing problems
|
||||
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)));
|
||||
cmp(x1, RipReg);
|
||||
b(&NoBlock, Condition::ne);
|
||||
|
||||
// Now load the actual host block to execute if we can
|
||||
ldr(x3, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)));
|
||||
cbz(x3, &NoBlock);
|
||||
|
||||
// If we've made it here then we have a real compiled block
|
||||
{
|
||||
// update L1 cache
|
||||
ldr(x0, &l_L1Ptr);
|
||||
|
||||
and_(x1, RipReg, LookupCache::L1_ENTRIES_MASK);
|
||||
add(x0, x0, Operand(x1, Shift::LSL, 4));
|
||||
stp(x3, x2, MemOperand(x0));
|
||||
|
||||
// Jump to the block
|
||||
if (!config.ExecuteBlocksWithCall) {
|
||||
br(x3);
|
||||
} else {
|
||||
bind(&CallBlock);
|
||||
mov(x0, STATE);
|
||||
blr(x3);
|
||||
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
|
||||
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
ldr(x0, &l_CTX);
|
||||
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
|
||||
// If the value == 0 then branch to the top
|
||||
cbz(x0, &LoopTop);
|
||||
// Else we need to pause now
|
||||
b(&ThreadPauseHandler);
|
||||
} else {
|
||||
// Unconditionally loop to the top
|
||||
// We will only stop on error when compiling a block or signal
|
||||
b(&LoopTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bind(&ExitSpillSRA);
|
||||
ThreadStopHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
if (SRAEnabled)
|
||||
SpillStaticRegs();
|
||||
|
||||
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
|
||||
PopCalleeSavedRegisters();
|
||||
|
||||
// Return from the function
|
||||
// LR is set to the correct return location now
|
||||
ret();
|
||||
}
|
||||
|
||||
{
|
||||
ExitFunctionLinkerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
if (SRAEnabled)
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x0, &l_ExitFunctionLinkThis);
|
||||
mov(x1, STATE);
|
||||
mov(x2, lr);
|
||||
|
||||
ldr(x3, &l_ExitFunctionLink);
|
||||
blr(x3);
|
||||
|
||||
if (SRAEnabled)
|
||||
FillStaticRegs();
|
||||
br(x0);
|
||||
}
|
||||
|
||||
// Need to create the block
|
||||
{
|
||||
bind(&NoBlock);
|
||||
|
||||
ldr(x0, &l_CTX);
|
||||
mov(x1, STATE);
|
||||
ldr(x3, &l_CompileBlock);
|
||||
|
||||
if (SRAEnabled)
|
||||
SpillStaticRegs();
|
||||
|
||||
// X2 contains our guest RIP
|
||||
blr(x3); // { CTX, Frame, RIP}
|
||||
|
||||
if (SRAEnabled)
|
||||
FillStaticRegs();
|
||||
|
||||
b(&LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
SignalHandlerReturnAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
|
||||
// Now to get back to our old location we need to do a fault dance
|
||||
// We can't use SIGTRAP here since gdb catches it and never gives it to the application!
|
||||
hlt(0);
|
||||
}
|
||||
|
||||
{
|
||||
ThreadPauseHandlerAddressSpillSRA = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
if (SRAEnabled)
|
||||
SpillStaticRegs();
|
||||
|
||||
bind(&ThreadPauseHandler);
|
||||
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
// We are pausing, this means the frontend should be waiting for this thread to idle
|
||||
// We will have faulted and jumped to this location at this point
|
||||
|
||||
// Call our sleep handler
|
||||
ldr(x0, &l_CTX);
|
||||
mov(x1, STATE);
|
||||
ldr(x2, &l_Sleep);
|
||||
blr(x2);
|
||||
|
||||
PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
// Fault to start running again
|
||||
hlt(0);
|
||||
}
|
||||
|
||||
{
|
||||
// The expectation here is that a thunked function needs to call back in to the JIT in a reentrant safe way
|
||||
// To do this safely we need to do some state tracking and register saving
|
||||
//
|
||||
// eg:
|
||||
// JIT Call->
|
||||
// Thunk->
|
||||
// Thunk callback->
|
||||
//
|
||||
// The thunk callback needs to execute JIT code and when it returns, it needs to safely return to the thunk rather than JIT space
|
||||
// This is handled by pushing a return address trampoline to the stack so when the guest address returns it hits our custom thunk return
|
||||
// - This will safely return us to the thunk
|
||||
//
|
||||
// On return to the thunk, the thunk can get whatever its return value is from the thread context depending on ABI handling on its end
|
||||
// When the thunk itself returns, it'll do its regular return logic there
|
||||
// void ReentrantCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
|
||||
|
||||
// We expect the thunk to have previously pushed the registers it was using
|
||||
PushCalleeSavedRegisters();
|
||||
|
||||
// First thing we need to move the thread state pointer back in to our register
|
||||
mov(STATE, x0);
|
||||
|
||||
// Make sure to adjust the refcounter so we don't clear the cache now
|
||||
LoadConstant(x0, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
|
||||
ldr(w2, MemOperand(x0));
|
||||
add(w2, w2, 1);
|
||||
str(w2, MemOperand(x0));
|
||||
|
||||
// Now push the callback return trampoline to the guest stack
|
||||
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
|
||||
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
|
||||
sub(x2, x2, 16);
|
||||
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
|
||||
|
||||
// Store the trampoline to the guest stack
|
||||
// Guest stack is now correctly misaligned after a regular call instruction
|
||||
str(x0, MemOperand(x2));
|
||||
|
||||
// Store RIP to the context state
|
||||
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
|
||||
|
||||
// load static regs
|
||||
if (SRAEnabled)
|
||||
FillStaticRegs();
|
||||
|
||||
// Now go back to the regular dispatcher loop
|
||||
b(&LoopTop);
|
||||
}
|
||||
|
||||
place(&l_VirtualMemory);
|
||||
place(&l_PagePtr);
|
||||
place(&l_L1Ptr);
|
||||
place(&l_CTX);
|
||||
place(&l_Sleep);
|
||||
place(&l_CompileBlock);
|
||||
place(&l_ExitFunctionLink);
|
||||
place(&l_ExitFunctionLinkThis);
|
||||
|
||||
|
||||
FinalizeCode();
|
||||
Start = reinterpret_cast<uint64_t>(DispatchPtr);
|
||||
End = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr));
|
||||
GetBuffer()->SetExecutable();
|
||||
|
||||
#if ENABLE_JITSYMBOLS
|
||||
std::string Name = "Dispatch_" + std::to_string(::gettid());
|
||||
CTX->Symbols.Register(reinterpret_cast<void*>(DispatchPtr), End - reinterpret_cast<uint64_t>(DispatchPtr), Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::SpillSRA(void *ucontext) {
|
||||
for(int i = 0; i < SRA64.size(); i++) {
|
||||
ThreadState->CurrentFrame->State.gregs[i] = ArchHelpers::Context::GetArmReg(ucontext, SRA64[i].GetCode());
|
||||
}
|
||||
// TODO: Also recover FPRs, not sure where the neon context is
|
||||
// This is usually not needed
|
||||
/*
|
||||
for(int i = 0; i < SRAFPR.size(); i++) {
|
||||
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
|
||||
State->State.State.xmm[i][0] = _mcontext.neon[SRAFPR[i].GetCode()];
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
|
||||
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
DispatcherConfig config;
|
||||
config.ExecuteBlocksWithCall = true;
|
||||
|
||||
Dispatcher = std::make_unique<Arm64Dispatcher>(ctx, Thread, config);
|
||||
DispatchPtr = Dispatcher->DispatchPtr;
|
||||
CallbackPtr = Dispatcher->CallbackPtr;
|
||||
|
||||
// TODO: It feels wrong to initialize this way
|
||||
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
public:
|
||||
Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
|
||||
|
||||
protected:
|
||||
void SpillSRA(void *ucontext) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include "Common/MathUtils.h"
|
||||
#include "Interface/Core/ArchHelpers/MContext.h"
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
--ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
|
||||
// Go to sleep
|
||||
Thread->StartRunning.Wait();
|
||||
|
||||
Thread->RunningEvents.Running = true;
|
||||
++ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
}
|
||||
|
||||
void Dispatcher::StoreThreadState(int Signal, void *ucontext) {
|
||||
// We can end up getting a signal at any point in our host state
|
||||
// Jump to a handler that saves all state so we can safely return
|
||||
uint64_t OldSP = ArchHelpers::Context::GetSp(ucontext);
|
||||
uintptr_t NewSP = OldSP;
|
||||
|
||||
size_t StackOffset = sizeof(ArchHelpers::Context::ContextBackup);
|
||||
|
||||
// We need to back up behind the host's red zone
|
||||
// We do this on the guest side as well
|
||||
// (does nothing on arm hosts)
|
||||
NewSP -= ArchHelpers::Context::ContextBackup::RedZoneSize;
|
||||
|
||||
NewSP -= StackOffset;
|
||||
NewSP = AlignDown(NewSP, 16);
|
||||
|
||||
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
|
||||
ArchHelpers::Context::BackupContext(ucontext, Context);
|
||||
|
||||
// Retain the action pointer so we can see it when we return
|
||||
Context->Signal = Signal;
|
||||
|
||||
// Save guest state
|
||||
// We can't guarantee if registers are in context or host GPRs
|
||||
// So we need to save everything
|
||||
memcpy(&Context->GuestState, ThreadState->CurrentFrame, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Set the new SP
|
||||
ArchHelpers::Context::SetSp(ucontext, NewSP);
|
||||
|
||||
SignalFrames.push(NewSP);
|
||||
}
|
||||
|
||||
void Dispatcher::RestoreThreadState(void *ucontext) {
|
||||
uint64_t OldSP = SignalFrames.top();
|
||||
SignalFrames.pop();
|
||||
uintptr_t NewSP = OldSP;
|
||||
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
|
||||
|
||||
// First thing, reset the guest state
|
||||
memcpy(ThreadState->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Now restore host state
|
||||
ArchHelpers::Context::RestoreContext(ucontext, Context);
|
||||
|
||||
// Restore the previous signal state
|
||||
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
|
||||
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
|
||||
}
|
||||
|
||||
bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
StoreThreadState(Signal, ucontext);
|
||||
auto Frame = ThreadState->CurrentFrame;
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
++SignalHandlerRefCounter;
|
||||
|
||||
// Set the new PC
|
||||
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
|
||||
// Set our state register to point to our guest thread data
|
||||
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
||||
|
||||
|
||||
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
if (!(GuestStack->ss_flags & SS_DISABLE)) {
|
||||
// If our guest is already inside of the alternative stack
|
||||
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
|
||||
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
|
||||
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
|
||||
if (OldGuestSP >= AltStackBase &&
|
||||
OldGuestSP <= AltStackEnd) {
|
||||
// We are already in the alt stack, the rest of the code will handle adjusting this
|
||||
}
|
||||
else {
|
||||
NewGuestSP = AltStackEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Back up past the redzone, which is 128bytes
|
||||
// Don't need this offset if we aren't going to be putting siginfo in to it
|
||||
NewGuestSP -= 128;
|
||||
|
||||
if (GuestAction->sa_flags & SA_SIGINFO) {
|
||||
if (SRAEnabled) {
|
||||
if (!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
|
||||
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
|
||||
} else {
|
||||
// We are in jit, SRA must be spilled
|
||||
SpillSRA(ucontext);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup ucontext a bit
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
|
||||
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
|
||||
uint64_t UContextLocation = NewGuestSP;
|
||||
|
||||
NewGuestSP -= sizeof(siginfo_t);
|
||||
uint64_t SigInfoLocation = NewGuestSP;
|
||||
|
||||
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
|
||||
siginfo_t *guest_siginfo = reinterpret_cast<siginfo_t*>(SigInfoLocation);
|
||||
|
||||
// We have extended float information
|
||||
guest_uctx->uc_flags |= FEXCore::x86_64::UC_FP_XSTATE;
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->uc_mcontext.fpregs = &guest_uctx->__fpregs_mem;
|
||||
|
||||
#define COPY_REG(x) \
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = Frame->State.gregs[X86State::REG_##x];
|
||||
COPY_REG(R8);
|
||||
COPY_REG(R9);
|
||||
COPY_REG(R10);
|
||||
COPY_REG(R11);
|
||||
COPY_REG(R12);
|
||||
COPY_REG(R13);
|
||||
COPY_REG(R14);
|
||||
COPY_REG(R15);
|
||||
COPY_REG(RDI);
|
||||
COPY_REG(RSI);
|
||||
COPY_REG(RBP);
|
||||
COPY_REG(RBX);
|
||||
COPY_REG(RDX);
|
||||
COPY_REG(RAX);
|
||||
COPY_REG(RCX);
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
|
||||
// Copy float registers
|
||||
memcpy(guest_uctx->__fpregs_mem._st, Frame->State.mm, sizeof(Frame->State.mm));
|
||||
memcpy(guest_uctx->__fpregs_mem._xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
|
||||
|
||||
// FCW store default
|
||||
guest_uctx->__fpregs_mem.fcw = Frame->State.FCW;
|
||||
|
||||
// Reconstruct FSW
|
||||
guest_uctx->__fpregs_mem.fsw =
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) |
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) |
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
|
||||
|
||||
// Copy over signal stack information
|
||||
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
|
||||
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
|
||||
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
|
||||
|
||||
// siginfo_t
|
||||
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(info);
|
||||
guest_siginfo->si_signo = Signal;
|
||||
switch (Signal) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
guest_siginfo->si_code = HostSigInfo->si_code;
|
||||
guest_siginfo->si_errno = HostSigInfo->si_errno;
|
||||
// Macro expansion to get the si_addr
|
||||
guest_siginfo->si_addr = HostSigInfo->si_addr;
|
||||
break;
|
||||
default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break;
|
||||
}
|
||||
|
||||
Frame->State.gregs[X86State::REG_RSI] = SigInfoLocation;
|
||||
Frame->State.gregs[X86State::REG_RDX] = UContextLocation;
|
||||
}
|
||||
else {
|
||||
// XXX: 32bit Support
|
||||
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
|
||||
uint64_t UContextLocation = 0; // NewGuestSP;
|
||||
NewGuestSP -= sizeof(FEXCore::x86::siginfo_t);
|
||||
uint64_t SigInfoLocation = 0; // NewGuestSP;
|
||||
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = UContextLocation;
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = SigInfoLocation;
|
||||
}
|
||||
|
||||
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
|
||||
}
|
||||
else {
|
||||
Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
|
||||
}
|
||||
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
Frame->State.gregs[X86State::REG_RDI] = Signal;
|
||||
|
||||
// Set up the new SP for stack handling
|
||||
NewGuestSP -= 8;
|
||||
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
}
|
||||
else {
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
LOGMAN_THROW_A(CTX->X86CodeGen.SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
||||
Frame->State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dispatcher::HandleSIGILL(int Signal, void *info, void *ucontext) {
|
||||
|
||||
if (ArchHelpers::Context::GetPc(ucontext) == SignalHandlerReturnAddress) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ArchHelpers::Context::GetPc(ucontext) == PauseReturnInstruction) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
|
||||
auto Frame = ThreadState->CurrentFrame;
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
|
||||
// We are in jit, SRA must be spilled
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddressSpillSRA);
|
||||
} else {
|
||||
if (SRAEnabled) {
|
||||
// We are in non-jit, SRA is already spilled
|
||||
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
|
||||
}
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
|
||||
}
|
||||
|
||||
// Set the new PC
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddress);
|
||||
|
||||
// Set our state register to point to our guest thread data
|
||||
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
++SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
|
||||
// Our thread is stopping
|
||||
// We don't care about anything at this point
|
||||
// Set the stack to our starting location when we entered the core and get out safely
|
||||
ArchHelpers::Context::SetSp(ucontext, Frame->ReturningStackLocation);
|
||||
|
||||
// Our ref counting doesn't matter anymore
|
||||
SignalHandlerRefCounter = 0;
|
||||
|
||||
// Set the new PC
|
||||
if (SRAEnabled && IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), false)) {
|
||||
// We are in jit, SRA must be spilled
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddressSpillSRA);
|
||||
} else {
|
||||
if (SRAEnabled) {
|
||||
// We are in non-jit, SRA is already spilled
|
||||
LOGMAN_THROW_A(!IsAddressInJITCode(ArchHelpers::Context::GetPc(ucontext), true), "Signals in dispatcher have unsynchronized context");
|
||||
}
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddress);
|
||||
}
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Dispatcher::GetCompileBlockPtr() {
|
||||
using ClassPtrType = void (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
PtrCast CompileBlockPtr;
|
||||
CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlockJit;
|
||||
return CompileBlockPtr.Data;
|
||||
}
|
||||
|
||||
void Dispatcher::RemoveCodeBuffer(uint8_t* start_to_remove) {
|
||||
for (auto iter = CodeBuffers.begin(); iter != CodeBuffers.end(); ++iter) {
|
||||
auto [start, end] = *iter;
|
||||
if (start == reinterpret_cast<uint64_t>(start_to_remove)) {
|
||||
CodeBuffers.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) {
|
||||
for (auto [start, end] : CodeBuffers) {
|
||||
if (Address >= start && Address < end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IncludeDispatcher) {
|
||||
return IsAddressInDispatcher(Address);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
struct DispatcherConfig {
|
||||
bool ExecuteBlocksWithCall = false;
|
||||
uintptr_t ExitFunctionLink = 0;
|
||||
uintptr_t ExitFunctionLinkThis = 0;
|
||||
bool StaticRegisterAssignment = false;
|
||||
};
|
||||
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() = default;
|
||||
CPUBackend::AsmDispatch DispatchPtr;
|
||||
CPUBackend::JITCallback CallbackPtr;
|
||||
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
|
||||
|
||||
/**
|
||||
* @name Dispatch Helper functions
|
||||
* @{ */
|
||||
uint64_t ThreadStopHandlerAddress{};
|
||||
uint64_t ThreadStopHandlerAddressSpillSRA{};
|
||||
uint64_t AbsoluteLoopTopAddress{};
|
||||
uint64_t AbsoluteLoopTopAddressFillSRA{};
|
||||
uint64_t ThreadPauseHandlerAddress{};
|
||||
uint64_t ThreadPauseHandlerAddressSpillSRA{};
|
||||
uint64_t ExitFunctionLinkerAddress{};
|
||||
uint64_t SignalHandlerReturnAddress{};
|
||||
uint64_t PauseReturnInstruction{};
|
||||
|
||||
/** @} */
|
||||
|
||||
uint32_t SignalHandlerRefCounter{};
|
||||
|
||||
uint64_t Start{};
|
||||
uint64_t End{};
|
||||
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
bool HandleSIGILL(int Signal, void *info, void *ucontext);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
|
||||
void RegisterCodeBuffer(uint8_t* start, size_t size) {
|
||||
CodeBuffers.emplace_back(reinterpret_cast<uint64_t>(start),
|
||||
reinterpret_cast<uint64_t>(start + size));
|
||||
}
|
||||
|
||||
void RemoveCodeBuffer(uint8_t* start);
|
||||
|
||||
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
|
||||
bool IsAddressInDispatcher(uint64_t Address) {
|
||||
return Address >= Start && Address < End;
|
||||
}
|
||||
|
||||
protected:
|
||||
Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CTX {ctx}
|
||||
, ThreadState {Thread} {}
|
||||
|
||||
void StoreThreadState(int Signal, void *ucontext);
|
||||
void RestoreThreadState(void *ucontext);
|
||||
std::stack<uint64_t> SignalFrames;
|
||||
|
||||
bool SRAEnabled = false;
|
||||
virtual void SpillSRA(void *ucontext) {}
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *ThreadState;
|
||||
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
|
||||
|
||||
static uint64_t GetCompileBlockPtr();
|
||||
|
||||
private:
|
||||
std::vector<std::tuple<uint64_t, uint64_t>> CodeBuffers; // Start, End
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
|
||||
|
||||
#include "Interface/Core/Interpreter/InterpreterClass.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE r14
|
||||
|
||||
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config)
|
||||
: Dispatcher(ctx, Thread)
|
||||
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE, nullptr, this) {
|
||||
|
||||
using namespace Xbyak;
|
||||
using namespace Xbyak::util;
|
||||
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
|
||||
|
||||
// Temp registers
|
||||
// rax, rcx, rdx, rsi, r8, r9,
|
||||
// r10, r11
|
||||
//
|
||||
// Callee Saved
|
||||
// rbx, rbp, r12, r13, r14, r15
|
||||
//
|
||||
// 1St Argument: rdi <ThreadState>
|
||||
// XMM:
|
||||
// All temp
|
||||
|
||||
// while (true) {
|
||||
// Ptr = FindBlock(RIP)
|
||||
// if (!Ptr)
|
||||
// Ptr = CTX->CompileBlock(RIP);
|
||||
//
|
||||
// if (Ptr)
|
||||
// Ptr();
|
||||
// else
|
||||
// {
|
||||
// Ptr = FallbackCore->CompileBlock()
|
||||
// if (Ptr)
|
||||
// Ptr()
|
||||
// else {
|
||||
// ShouldStop = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Bunch of exit state stuff
|
||||
|
||||
// x86-64 ABI has the stack aligned when /call/ happens
|
||||
// Which means the destination has a misaligned stack at that point
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
mov(STATE, rdi);
|
||||
|
||||
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
||||
// regardless of where we were in the stack
|
||||
mov(qword [rdi + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)], rsp);
|
||||
|
||||
Label LoopTop;
|
||||
Label FullLookup;
|
||||
Label CallBlock;
|
||||
Label NoBlock;
|
||||
Label ExitBlock;
|
||||
Label ThreadPauseHandler;
|
||||
|
||||
L(LoopTop);
|
||||
AbsoluteLoopTopAddressFillSRA = AbsoluteLoopTopAddress = getCurr<uint64_t>();
|
||||
|
||||
{
|
||||
// Load our RIP
|
||||
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
|
||||
|
||||
// L1 Cache
|
||||
mov(r13, Thread->LookupCache->GetL1Pointer());
|
||||
mov(rax, rdx);
|
||||
|
||||
and_(rax, LookupCache::L1_ENTRIES_MASK);
|
||||
shl(rax, 4);
|
||||
cmp(qword[r13 + rax + 8], rdx);
|
||||
jne(FullLookup);
|
||||
|
||||
if (!config.ExecuteBlocksWithCall) {
|
||||
jmp(qword[r13 + rax + 0]);
|
||||
} else {
|
||||
mov(rax, qword[r13 + rax + 0]);
|
||||
jmp(CallBlock);
|
||||
}
|
||||
|
||||
L(FullLookup);
|
||||
mov(r13, Thread->LookupCache->GetPagePointer());
|
||||
|
||||
// Full lookup
|
||||
mov(rax, rdx);
|
||||
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
and_(rax, rbx);
|
||||
shr(rax, 12);
|
||||
|
||||
// Load page pointer
|
||||
mov(rdi, qword [r13 + rax * 8]);
|
||||
|
||||
cmp(rdi, 0);
|
||||
je(NoBlock);
|
||||
|
||||
mov (rax, rdx);
|
||||
and_(rax, 0x0FFF);
|
||||
|
||||
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
|
||||
|
||||
// check for aliasing
|
||||
mov(rcx, qword [rdi + rax + 8]);
|
||||
cmp(rcx, rdx);
|
||||
jne(NoBlock);
|
||||
|
||||
// Load the block pointer
|
||||
mov(rax, qword [rdi + rax]);
|
||||
|
||||
cmp(rax, 0);
|
||||
je(NoBlock);
|
||||
|
||||
// Update L1
|
||||
|
||||
mov(r13, Thread->LookupCache->GetL1Pointer());
|
||||
mov(rcx, rdx);
|
||||
and_(rcx, LookupCache::L1_ENTRIES_MASK);
|
||||
shl(rcx, 1);
|
||||
mov(qword[r13 + rcx*8 + 8], rdx);
|
||||
mov(qword[r13 + rcx*8 + 0], rax);
|
||||
|
||||
// Real block if we made it here
|
||||
if (!config.ExecuteBlocksWithCall) {
|
||||
jmp(rax);
|
||||
} else {
|
||||
L(CallBlock);
|
||||
mov(rdi, STATE);
|
||||
call(rax);
|
||||
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
|
||||
|
||||
// If the value == 0 then branch to the top
|
||||
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
|
||||
je(LoopTop);
|
||||
// Else we need to pause now
|
||||
jmp(ThreadPauseHandler);
|
||||
ud2();
|
||||
}
|
||||
else {
|
||||
jmp(LoopTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
L(ExitBlock);
|
||||
ThreadStopHandlerAddress = getCurr<uint64_t>();
|
||||
|
||||
add(rsp, 8);
|
||||
|
||||
pop(r15);
|
||||
pop(r14);
|
||||
pop(r13);
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
|
||||
ret();
|
||||
}
|
||||
|
||||
// Block creation
|
||||
{
|
||||
L(NoBlock);
|
||||
|
||||
// {rdi, rsi, rdx}
|
||||
mov(rdi, reinterpret_cast<uint64_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, GetCompileBlockPtr());
|
||||
|
||||
call(rax);
|
||||
|
||||
// rdx already contains RIP here
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
ExitFunctionLinkerAddress = getCurr<uint64_t>();
|
||||
// {rdi, rsi, rdx}
|
||||
mov(rdi, config.ExitFunctionLinkThis);
|
||||
mov(rsi, STATE);
|
||||
mov(rdx, rax); // rax is set at the block end
|
||||
|
||||
mov(rax, config.ExitFunctionLink);
|
||||
call(rax);
|
||||
jmp(rax);
|
||||
}
|
||||
|
||||
{
|
||||
// Pause handler
|
||||
ThreadPauseHandlerAddress = getCurr<uint64_t>();
|
||||
L(ThreadPauseHandler);
|
||||
|
||||
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
|
||||
|
||||
call(rax);
|
||||
|
||||
// XXX: Unsupported atm
|
||||
PauseReturnInstruction = getCurr<uint64_t>();
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
CallbackPtr = getCurr<CPUBackend::JITCallback>();
|
||||
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
// First thing we need to move the thread state pointer back in to our register
|
||||
mov(STATE, rdi);
|
||||
// XXX: XMM?
|
||||
|
||||
// Make sure to adjust the refcounter so we don't clear the cache now
|
||||
mov(rax, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
|
||||
add(dword [rax], 1);
|
||||
|
||||
// Now push the callback return trampoline to the guest stack
|
||||
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
|
||||
mov(rax, CTX->X86CodeGen.CallbackReturn);
|
||||
|
||||
// Store the trampoline to the guest stack
|
||||
// Guest stack is now correctly misaligned after a regular call instruction
|
||||
sub(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 16);
|
||||
mov(rbx, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])]);
|
||||
mov(qword [rbx], rax);
|
||||
|
||||
// Store RIP to the context state
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], rsi);
|
||||
|
||||
// Back to the loop top now
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
// Signal return handler
|
||||
SignalHandlerReturnAddress = getCurr<uint64_t>();
|
||||
|
||||
ud2();
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
|
||||
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
|
||||
// rdi = thread
|
||||
// rsi = rsp
|
||||
|
||||
mov(rsp, rsi);
|
||||
|
||||
// Now jump back to the thunk
|
||||
// XXX: XMM?
|
||||
add(rsp, 8);
|
||||
|
||||
pop(r15);
|
||||
pop(r14);
|
||||
pop(r13);
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
|
||||
ret();
|
||||
}
|
||||
ready();
|
||||
|
||||
Start = reinterpret_cast<uint64_t>(getCode());
|
||||
End = Start + getSize();
|
||||
|
||||
#if ENABLE_JITSYMBOLS
|
||||
std::string Name = "Dispatch_" + std::to_string(::gettid());
|
||||
CTX->Symbols.Register(Start, End-Start, Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
X86Dispatcher::~X86Dispatcher() {
|
||||
|
||||
}
|
||||
|
||||
#ifdef _M_X86_64
|
||||
|
||||
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
DispatcherConfig config;
|
||||
config.ExecuteBlocksWithCall = true;
|
||||
|
||||
Dispatcher = std::make_unique<X86Dispatcher>(ctx, Thread, config);
|
||||
DispatchPtr = Dispatcher->DispatchPtr;
|
||||
CallbackPtr = Dispatcher->CallbackPtr;
|
||||
|
||||
// TODO: It feels wrong to initialize this way
|
||||
ctx->InterpreterCallbackReturn = Dispatcher->ReturnPtr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
|
||||
#define XBYAK64
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator, public Xbyak::Allocator {
|
||||
public:
|
||||
X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, DispatcherConfig &config);
|
||||
|
||||
virtual ~X86Dispatcher() override;
|
||||
|
||||
// Xbyak::Allocator
|
||||
Xbyak::uint8 *alloc(size_t size) override { Size = size; return reinterpret_cast<uint8_t*>(FEXCore::Allocator::mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); }
|
||||
void free(Xbyak::uint8 *p) override { FEXCore::Allocator::munmap(p, Size); }
|
||||
bool useProtect() const override { return false; }
|
||||
|
||||
private:
|
||||
size_t Size{};
|
||||
};
|
||||
|
||||
}
|
||||
+74
-36
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-meta-blocks
|
||||
desc: Extracts instruction & block meta info, frontend multiblock logic
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/Frontend.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
@@ -8,6 +15,7 @@
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Debug/X86Tables.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <set>
|
||||
|
||||
namespace FEXCore::Frontend {
|
||||
using namespace FEXCore::X86Tables;
|
||||
@@ -117,7 +125,7 @@ Decoder::Decoder(FEXCore::Context::Context *ctx)
|
||||
|
||||
uint8_t Decoder::ReadByte() {
|
||||
uint8_t Byte = InstStream[InstructionSize];
|
||||
LogMan::Throw::A(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
|
||||
LOGMAN_THROW_A(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
|
||||
Instruction[InstructionSize] = Byte;
|
||||
InstructionSize++;
|
||||
return Byte;
|
||||
@@ -129,26 +137,17 @@ uint8_t Decoder::PeekByte(uint8_t Offset) {
|
||||
}
|
||||
|
||||
uint64_t Decoder::ReadData(uint8_t Size) {
|
||||
uint64_t Res{};
|
||||
#define READ_DATA(x, y) \
|
||||
case x: { \
|
||||
y const *Data = reinterpret_cast<y const*>(&InstStream[InstructionSize]); \
|
||||
Res = *Data; \
|
||||
} \
|
||||
break
|
||||
|
||||
switch (Size) {
|
||||
case 0: return 0;
|
||||
READ_DATA(1, uint8_t);
|
||||
READ_DATA(2, uint16_t);
|
||||
case 3: memcpy(&Res, &InstStream[InstructionSize], Size);
|
||||
READ_DATA(4, uint32_t);
|
||||
READ_DATA(8, uint64_t);
|
||||
default:
|
||||
LogMan::Msg::A("Unknown data size to read");
|
||||
return 0;
|
||||
if (Size == 0) {
|
||||
return 0;
|
||||
}
|
||||
#undef READ_DATA
|
||||
|
||||
if (Size > sizeof(uint64_t)) {
|
||||
LOGMAN_MSG_A("Unknown data size to read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t Res = 0;
|
||||
std::memcpy(&Res, &InstStream[InstructionSize], Size);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for(size_t i = 0; i < Size; ++i) {
|
||||
@@ -157,6 +156,7 @@ uint64_t Decoder::ReadData(uint8_t Size) {
|
||||
#else
|
||||
SkipBytes(Size);
|
||||
#endif
|
||||
|
||||
return Res;
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
Operand->TypeSIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
|
||||
|
||||
uint64_t Literal {0};
|
||||
LogMan::Throw::A(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
|
||||
LOGMAN_THROW_A(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
|
||||
|
||||
Literal = ReadData(Displacement);
|
||||
if (Displacement == 1) {
|
||||
@@ -344,7 +344,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
return false;
|
||||
}
|
||||
|
||||
LogMan::Throw::A(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
|
||||
LOGMAN_THROW_A(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
|
||||
"Group Ops should have been decoded before this!");
|
||||
|
||||
uint8_t DestSize{};
|
||||
@@ -467,7 +467,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
}
|
||||
|
||||
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
|
||||
LogMan::Throw::A(!HasMODRM, "This instruction shouldn't have ModRM!");
|
||||
LOGMAN_THROW_A(!HasMODRM, "This instruction shouldn't have ModRM!");
|
||||
|
||||
// If the REX is in the byte that means the lower nibble of the OP contains the destination GPR
|
||||
// This also means that the destination is always a GPR on these ones
|
||||
@@ -476,6 +476,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
DecodeInst->Dest.TypeGPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
|
||||
CurrentDest->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
|
||||
|
||||
if (CurrentDest->TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t Bytes = Info->MoreBytes;
|
||||
@@ -502,27 +505,36 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
GPR.TypeGPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
|
||||
GPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
|
||||
|
||||
if (GPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
|
||||
// ModRM.mod == 0b11 == Register
|
||||
// ModRM.Mod != 0b11 == Register-direct addressing
|
||||
if (ModRM.mod == 0b11) {
|
||||
NonGPR.TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
NonGPR.TypeGPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
|
||||
NonGPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
|
||||
if (NonGPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
|
||||
(this->*Disp)(&NonGPR, ModRM);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
size_t CurrentSrc = 0;
|
||||
|
||||
if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM) {
|
||||
if (Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SF_MOD_DST) {
|
||||
ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest);
|
||||
if (!ModRMOperand(DecodeInst->Src[CurrentSrc], DecodeInst->Dest, HasXMMSrc, HasXMMDst, HasMMSrc, HasMMDst, Is8BitSrc, Is8BitDest))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc);
|
||||
if (!ModRMOperand(DecodeInst->Dest, DecodeInst->Src[CurrentSrc], HasXMMDst, HasXMMSrc, HasMMDst, HasMMSrc, Is8BitDest, Is8BitSrc))
|
||||
return false;
|
||||
}
|
||||
++CurrentSrc;
|
||||
}
|
||||
@@ -541,7 +553,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
}
|
||||
|
||||
if (Bytes != 0) {
|
||||
LogMan::Throw::A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
|
||||
LOGMAN_THROW_A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
|
||||
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = Bytes;
|
||||
|
||||
@@ -567,7 +579,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Literal = Literal;
|
||||
}
|
||||
|
||||
LogMan::Throw::A(Bytes == 0, "Inst at 0x%lx: 0x%04x '%s' Had an instruction of size %d with %d remaining", DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name, InstructionSize, Bytes);
|
||||
LOGMAN_THROW_A(Bytes == 0, "Inst at 0x%lx: 0x%04x '%s' Had an instruction of size %d with %d remaining", DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name, InstructionSize, Bytes);
|
||||
DecodeInst->InstSize = InstructionSize;
|
||||
return true;
|
||||
}
|
||||
@@ -592,7 +604,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
return false;
|
||||
}
|
||||
|
||||
LogMan::Throw::A(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
|
||||
LOGMAN_THROW_A(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
|
||||
|
||||
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
|
||||
Info->Type <= FEXCore::X86Tables::TYPE_GROUP_11) {
|
||||
@@ -648,7 +660,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
3,
|
||||
};
|
||||
uint8_t Field = RegToField[ModRM.reg];
|
||||
LogMan::Throw::A(Field != 255, "Invalid field selected!");
|
||||
LOGMAN_THROW_A(Field != 255, "Invalid field selected!");
|
||||
|
||||
LocalOp = (Field << 3) | ModRM.rm;
|
||||
return NormalOp(&SecondModRMTableOps[LocalOp], LocalOp);
|
||||
@@ -682,7 +694,10 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
uint8_t Byte2 = ReadByte();
|
||||
pp = Byte2 & 0b11;
|
||||
map_select = Byte1 & 0b11111;
|
||||
LogMan::Throw::A(map_select >= 1 && map_select <= 3, "We don't understand a map_select of: %d", map_select);
|
||||
if (!(map_select >= 1 && map_select <= 3)) {
|
||||
LogMan::Msg::E("We don't understand a map_select of: %d", map_select);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t VEXOp = ReadByte();
|
||||
@@ -730,6 +745,8 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
DecodeInst->PC = PC;
|
||||
|
||||
for(;;) {
|
||||
if (InstructionSize >= MAX_INST_SIZE)
|
||||
return false;
|
||||
uint8_t Op = ReadByte();
|
||||
switch (Op) {
|
||||
case 0x0F: {// Escape Op
|
||||
@@ -880,7 +897,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
auto Info = &FEXCore::X86Tables::BaseOps[Op];
|
||||
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
|
||||
LogMan::Throw::A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
|
||||
LOGMAN_THROW_A(CTX->Config.Is64BitMode, "Got REX prefix in 32bit mode");
|
||||
DecodeInst->Flags |= DecodeFlags::FLAG_REX_PREFIX;
|
||||
|
||||
// Widening displacement
|
||||
@@ -910,6 +927,10 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
|
||||
}
|
||||
|
||||
if (DecodeInst->Dest.TypeNone.Type == FEXCore::X86Tables::DecodedOperand::TYPE_GPR) {
|
||||
assert(DecodeInst->Dest.TypeGPR.GPR != 255);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -919,6 +940,7 @@ void Decoder::BranchTargetInMultiblockRange() {
|
||||
|
||||
// If the RIP setting is conditional AND within our symbol range then it can be considered for multiblock
|
||||
uint64_t TargetRIP = 0;
|
||||
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
|
||||
bool Conditional = true;
|
||||
|
||||
switch (DecodeInst->OP) {
|
||||
@@ -928,24 +950,33 @@ void Decoder::BranchTargetInMultiblockRange() {
|
||||
// auto RIPOffset = LoadSource(Op, Op->Src[0], Op->Flags);
|
||||
// auto RIPTargetConst = _Constant(Op->PC + Op->InstSize);
|
||||
// Target offset is PC + InstSize + Literal
|
||||
LogMan::Throw::A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
|
||||
break;
|
||||
}
|
||||
case 0xE9:
|
||||
case 0xEB: // Both are unconditional JMP instructions
|
||||
LogMan::Throw::A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
|
||||
Conditional = false;
|
||||
break;
|
||||
case 0xE8: // Call - Immediate target, We don't want to inline calls
|
||||
if (ExternalBranches) {
|
||||
ExternalBranches->insert(DecodeInst->PC + DecodeInst->InstSize);
|
||||
}
|
||||
[[fallthrough]];
|
||||
case 0xC2: // RET imm
|
||||
case 0xC3: // RET
|
||||
case 0xE8: // Call - Immediate target, We don't want to inline calls
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (GPRSize == 4) {
|
||||
// If we are running a 32bit guest then wrap around addresses that go above 32bit
|
||||
TargetRIP &= 0xFFFFFFFFU;
|
||||
}
|
||||
|
||||
// If the target RIP is within the symbol ranges then we are golden
|
||||
if (TargetRIP >= SymbolMinAddress && TargetRIP < SymbolMaxAddress) {
|
||||
// Update our conditional branch ranges before we return
|
||||
@@ -965,6 +996,10 @@ void Decoder::BranchTargetInMultiblockRange() {
|
||||
BlocksToDecode.find(TargetRIP) == BlocksToDecode.end()) {
|
||||
BlocksToDecode.emplace(TargetRIP);
|
||||
}
|
||||
} else {
|
||||
if (ExternalBranches) {
|
||||
ExternalBranches->insert(TargetRIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,7 +1023,7 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
|
||||
// If we don't have symbols available then we become a bit optimistic about multiblock ranges
|
||||
if (!SymbolAvailable) {
|
||||
// If we don't have a symbol available then assume all branches are valid for multiblock
|
||||
SymbolMaxAddress = ~0ULL;
|
||||
SymbolMaxAddress = SectionMaxAddress;
|
||||
SymbolMinAddress = EntryPoint;
|
||||
}
|
||||
|
||||
@@ -1018,7 +1053,10 @@ bool Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC)
|
||||
|
||||
if (ErrorDuringDecoding) {
|
||||
LogMan::Msg::D("Couldn't Decode something at 0x%lx, Started at 0x%lx", PC + PCOffset, PC);
|
||||
LogMan::Throw::A(Blocks.size() != 1, "Decode Error in entry block");
|
||||
if (Blocks.size() == 1) {
|
||||
return false;
|
||||
}
|
||||
LOGMAN_THROW_A(Blocks.size() != 1, "Decode Error in entry block");
|
||||
|
||||
CurrentBlockDecoding.HasInvalidInstruction = true;
|
||||
if (ErrorDuringDecoding && Blocks.size() != 1) {
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
uint64_t DecodedMinAddress {};
|
||||
uint64_t DecodedMaxAddress {~0ULL};
|
||||
|
||||
void SetSectionMaxAddress(uint64_t v) { SectionMaxAddress = v; }
|
||||
void SetExternalBranches(std::set<uint64_t> *v) { ExternalBranches = v; }
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
|
||||
@@ -65,10 +67,12 @@ private:
|
||||
uint64_t MaxCondBranchBackwards {~0ULL};
|
||||
uint64_t SymbolMaxAddress {};
|
||||
uint64_t SymbolMinAddress {~0ULL};
|
||||
uint64_t SectionMaxAddress {~0ULL};
|
||||
|
||||
std::vector<DecodedBlocks> Blocks;
|
||||
std::set<uint64_t> BlocksToDecode;
|
||||
std::set<uint64_t> HasBlocks;
|
||||
std::set<uint64_t> *ExternalBranches {nullptr};
|
||||
|
||||
// ModRM rm decoding
|
||||
using DecodeModRMPtr = void (FEXCore::Frontend::Decoder::*)(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM);
|
||||
|
||||
+22
-10
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|gdbserver
|
||||
desc: Provides a gdb interface to the guest state
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
@@ -232,17 +239,17 @@ std::string GdbServer::readRegs() {
|
||||
bool Found = false;
|
||||
|
||||
for (auto &Thread : *Threads) {
|
||||
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
continue;
|
||||
}
|
||||
state = Thread->State.State;
|
||||
memcpy(&state, Thread->CurrentFrame, sizeof(state));
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Found) {
|
||||
// If set to an invalid thread then just get the parent thread ID
|
||||
state = CTX->GetCPUState();
|
||||
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
|
||||
}
|
||||
|
||||
// Encode the GDB context definition
|
||||
@@ -284,17 +291,17 @@ GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
|
||||
bool Found = false;
|
||||
|
||||
for (auto &Thread : *Threads) {
|
||||
if (Thread->State.ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
if (Thread->ThreadManager.GetTID() != CurrentDebuggingThread) {
|
||||
continue;
|
||||
}
|
||||
state = Thread->State.State;
|
||||
memcpy(&state, Thread->CurrentFrame, sizeof(state));
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Found) {
|
||||
// If set to an invalid thread then just get the parent thread ID
|
||||
state = CTX->GetCPUState();
|
||||
memcpy(&state, CTX->ParentThread->CurrentFrame, sizeof(state));
|
||||
}
|
||||
|
||||
|
||||
@@ -525,7 +532,7 @@ GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
|
||||
ss << "<threads>\n";
|
||||
for (size_t i = 0; i < Threads->size(); ++i) {
|
||||
auto Thread = Threads->at(i);
|
||||
ss << "\t<thread id=\"" << std::hex << Thread->State.ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->State.ThreadManager.GetTID()) << "\">\n";
|
||||
ss << "\t<thread id=\"" << std::hex << Thread->ThreadManager.GetTID() << "\" core=\"" << i << "\" name=\"" << getThreadName(Thread->ThreadManager.GetTID()) << "\">\n";
|
||||
ss << "\t</thread>\n";
|
||||
}
|
||||
|
||||
@@ -653,7 +660,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
|
||||
ss << "m";
|
||||
for (size_t i = 0; i < Threads->size(); ++i) {
|
||||
auto Thread = Threads->at(i);
|
||||
ss << std::hex << Thread->State.ThreadManager.TID << ",";
|
||||
ss << std::hex << Thread->ThreadManager.TID << ",";
|
||||
}
|
||||
return {ss.str(), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
@@ -672,7 +679,7 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
|
||||
if (match("qC")) {
|
||||
// Returns the current Thread ID
|
||||
std::ostringstream ss;
|
||||
ss << "m" << std::hex << CTX->ParentThread->State.ThreadManager.TID;
|
||||
ss << "m" << std::hex << CTX->ParentThread->ThreadManager.TID;
|
||||
return {ss.str(), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
if (match("QStartNoAckMode")) {
|
||||
@@ -952,9 +959,14 @@ void GdbServer::GdbServerLoop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
static void* ThreadHandler(void *Arg) {
|
||||
FEXCore::GdbServer *This = reinterpret_cast<FEXCore::GdbServer*>(Arg);
|
||||
This->GdbServerLoop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GdbServer::StartThread() {
|
||||
gdbServerThread = std::thread(&GdbServer::GdbServerLoop, this);
|
||||
gdbServerThread = FEXCore::Threads::Thread::Create(ThreadHandler, this);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
|
||||
|
||||
+15
-4
@@ -1,23 +1,34 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|gdbserver
|
||||
$end_info$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include "Common/NetStream.h"
|
||||
|
||||
#include <FEXCore/Utils/Threads.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
class GdbServer {
|
||||
public:
|
||||
GdbServer(FEXCore::Context::Context *ctx);
|
||||
|
||||
// Public for threading
|
||||
void GdbServerLoop();
|
||||
|
||||
private:
|
||||
void Break(int signal);
|
||||
|
||||
std::unique_ptr<std::iostream> OpenSocket();
|
||||
void StartThread();
|
||||
void GdbServerLoop();
|
||||
std::string ReadPacket(std::iostream &stream);
|
||||
void SendPacket(std::ostream &stream, std::string packet);
|
||||
|
||||
@@ -50,14 +61,14 @@ private:
|
||||
HandledPacketType readReg(std::string& packet);
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
std::thread gdbServerThread;
|
||||
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
|
||||
std::unique_ptr<std::iostream> CommsStream;
|
||||
std::mutex sendMutex;
|
||||
bool SettingNoAckMode{false};
|
||||
bool NoAckMode{false};
|
||||
std::string ThreadString{};
|
||||
uint32_t CurrentDebuggingThread{};
|
||||
FEXCore::Config::Value<std::string> Filename{FEXCore::Config::CONFIG_APP_FILENAME, ""};
|
||||
FEX_CONFIG_OPT(Filename, APP_FILENAME);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,563 +0,0 @@
|
||||
#include "Common/MathUtils.h"
|
||||
#include "Interface/Core/Interpreter/InterpreterClass.h"
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
--ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
|
||||
// Go to sleep
|
||||
Thread->StartRunning.Wait();
|
||||
|
||||
Thread->State.RunningEvents.Running = true;
|
||||
++ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
}
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
#define STATE x28
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
|
||||
class DispatchGenerator : public vixl::aarch64::Assembler {
|
||||
public:
|
||||
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
|
||||
CPUBackend::AsmDispatch DispatchPtr;
|
||||
CPUBackend::JITCallback CallbackPtr;
|
||||
|
||||
uint64_t ThreadStopHandlerAddress;
|
||||
uint64_t AbsoluteLoopTopAddress;
|
||||
uint64_t ThreadPauseHandlerAddress;
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *State;
|
||||
private:
|
||||
void StoreThreadState(int Signal, void *ucontext);
|
||||
void RestoreThreadState(void *ucontext);
|
||||
void PushCalleeSavedRegisters();
|
||||
void PopCalleeSavedRegisters();
|
||||
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
|
||||
std::stack<uint64_t> SignalFrames;
|
||||
};
|
||||
|
||||
void DispatchGenerator::PushCalleeSavedRegisters() {
|
||||
// We need to save pairs of registers
|
||||
// We save r19-r30
|
||||
MemOperand PairOffset(sp, -16, PreIndex);
|
||||
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
|
||||
{x19, x20},
|
||||
{x21, x22},
|
||||
{x23, x24},
|
||||
{x25, x26},
|
||||
{x27, x28},
|
||||
{x29, x30},
|
||||
}};
|
||||
|
||||
for (auto &RegPair : CalleeSaved) {
|
||||
stp(RegPair.first, RegPair.second, PairOffset);
|
||||
}
|
||||
|
||||
// Additionally we need to store the lower 64bits of v8-v15
|
||||
// Here's a fun thing, we can use two ST4 instructions to store everything
|
||||
// We just need a single sub to sp before that
|
||||
const std::array<
|
||||
std::tuple<vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister>, 2> FPRs = {{
|
||||
{v8, v9, v10, v11},
|
||||
{v12, v13, v14, v15},
|
||||
}};
|
||||
|
||||
uint32_t VectorSaveSize = sizeof(uint64_t) * 8;
|
||||
sub(sp, sp, VectorSaveSize);
|
||||
// SP supporting move
|
||||
// We just saved x19 so it is safe
|
||||
add(x19, sp, 0);
|
||||
|
||||
MemOperand QuadOffset(x19, 32, PostIndex);
|
||||
for (auto &RegQuad : FPRs) {
|
||||
st4(std::get<0>(RegQuad).D(),
|
||||
std::get<1>(RegQuad).D(),
|
||||
std::get<2>(RegQuad).D(),
|
||||
std::get<3>(RegQuad).D(),
|
||||
0,
|
||||
QuadOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchGenerator::PopCalleeSavedRegisters() {
|
||||
const std::array<
|
||||
std::tuple<vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister,
|
||||
vixl::aarch64::VRegister>, 2> FPRs = {{
|
||||
{v12, v13, v14, v15},
|
||||
{v8, v9, v10, v11},
|
||||
}};
|
||||
|
||||
MemOperand QuadOffset(sp, 32, PostIndex);
|
||||
for (auto &RegQuad : FPRs) {
|
||||
ld4(std::get<0>(RegQuad).D(),
|
||||
std::get<1>(RegQuad).D(),
|
||||
std::get<2>(RegQuad).D(),
|
||||
std::get<3>(RegQuad).D(),
|
||||
0,
|
||||
QuadOffset);
|
||||
}
|
||||
|
||||
MemOperand PairOffset(sp, 16, PostIndex);
|
||||
const std::array<std::pair<vixl::aarch64::XRegister, vixl::aarch64::XRegister>, 6> CalleeSaved = {{
|
||||
{x29, x30},
|
||||
{x27, x28},
|
||||
{x25, x26},
|
||||
{x23, x24},
|
||||
{x21, x22},
|
||||
{x19, x20},
|
||||
}};
|
||||
|
||||
for (auto &RegPair : CalleeSaved) {
|
||||
ldp(RegPair.first, RegPair.second, PairOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchGenerator::LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant) {
|
||||
bool Is64Bit = Reg.IsX();
|
||||
int Segments = Is64Bit ? 4 : 2;
|
||||
|
||||
movz(Reg, (Constant) & 0xFFFF, 0);
|
||||
for (int i = 1; i < Segments; ++i) {
|
||||
uint16_t Part = (Constant >> (i * 16)) & 0xFFFF;
|
||||
if (Part) {
|
||||
movk(Reg, Part, i * 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: vixl::aarch64::Assembler(MAX_DISPATCHER_CODE_SIZE, vixl::aarch64::PositionDependentCode)
|
||||
, CTX {ctx}
|
||||
, State {Thread} {
|
||||
|
||||
SetAllowAssembler(true);
|
||||
auto Buffer = GetBuffer();
|
||||
|
||||
DispatchPtr = Buffer->GetOffsetAddress<CPUBackend::AsmDispatch>(GetCursorOffset());
|
||||
|
||||
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
|
||||
// Ptr = FindBlock(RIP)
|
||||
// if (!Ptr)
|
||||
// Ptr = CTX->CompileBlock(RIP);
|
||||
//
|
||||
// if (Ptr)
|
||||
// Ptr();
|
||||
// else
|
||||
// {
|
||||
// Ptr = FallbackCore->CompileBlock()
|
||||
// if (Ptr)
|
||||
// Ptr()
|
||||
// else {
|
||||
// ShouldStop = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Push all the register we need to save
|
||||
PushCalleeSavedRegisters();
|
||||
|
||||
// Push our memory base to the correct register
|
||||
// Move our thread pointer to the correct register
|
||||
// This is passed in to parameter 0 (x0)
|
||||
mov(STATE, x0);
|
||||
|
||||
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
||||
// regardless of where we were in the stack
|
||||
add(x0, sp, 0);
|
||||
str(x0, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
|
||||
|
||||
Label Exit;
|
||||
Label LoopTop;
|
||||
Label NoBlock;
|
||||
Label ThreadPauseHandler;
|
||||
bind(&LoopTop);
|
||||
AbsoluteLoopTopAddress = GetLabelAddress<uint64_t>(&LoopTop);
|
||||
|
||||
// Load in our RIP
|
||||
// Don't modify x2 since it contains our RIP once the block doesn't exist
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
|
||||
auto RipReg = x2;
|
||||
|
||||
// Mask the address by the virtual address size so we can check for aliases
|
||||
LoadConstant(x3, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
and_(x3, RipReg, x3);
|
||||
|
||||
{
|
||||
// This is the block cache lookup routine
|
||||
// It matches what is going on it LookupCache.h::FindBlock
|
||||
LoadConstant(x0, Thread->LookupCache->GetPagePointer());
|
||||
|
||||
// Offset the address and add to our page pointer
|
||||
lsr(x1, x3, 12);
|
||||
|
||||
// Load the pointer from the offset
|
||||
ldr(x0, MemOperand(x0, x1, Shift::LSL, 3));
|
||||
|
||||
// If page pointer is zero then we have no block
|
||||
cbz(x0, &NoBlock);
|
||||
|
||||
// Steal the page offset
|
||||
and_(x1, x3, 0x0FFF);
|
||||
|
||||
// Shift the offset by the size of the block cache entry
|
||||
add(x0, x0, Operand(x1, Shift::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry))));
|
||||
|
||||
// Load the guest address first to ensure it maps to the address we are currently at
|
||||
// This fixes aliasing problems
|
||||
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode)));
|
||||
cmp(x1, RipReg);
|
||||
b(&NoBlock, Condition::ne);
|
||||
|
||||
// Now load the actual host block to execute if we can
|
||||
ldr(x1, MemOperand(x0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode)));
|
||||
cbz(x1, &NoBlock);
|
||||
|
||||
// If we've made it here then we have a real compiled block
|
||||
{
|
||||
mov(x0, STATE);
|
||||
blr(x1);
|
||||
}
|
||||
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
|
||||
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
|
||||
ldr(w0, MemOperand(x0, offsetof(FEXCore::Context::Context, Config.RunningMode)));
|
||||
// If the value == 0 then branch to the top
|
||||
cbz(x0, &LoopTop);
|
||||
// Else we need to pause now
|
||||
b(&ThreadPauseHandler);
|
||||
}
|
||||
else {
|
||||
// Unconditionally loop to the top
|
||||
// We will only stop on error when compiling a block or signal
|
||||
b(&LoopTop);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bind(&Exit);
|
||||
ThreadStopHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
PopCalleeSavedRegisters();
|
||||
|
||||
// Return from the function
|
||||
// LR is set to the correct return location now
|
||||
ret();
|
||||
}
|
||||
|
||||
// Need to create the block
|
||||
{
|
||||
bind(&NoBlock);
|
||||
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, CTX)));
|
||||
mov(x1, STATE);
|
||||
|
||||
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
PtrCast Ptr;
|
||||
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
|
||||
LoadConstant(x3, Ptr.Data);
|
||||
|
||||
// X2 contains our guest RIP
|
||||
blr(x3); // { CTX, ThreadState, RIP}
|
||||
|
||||
b(&LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
bind(&ThreadPauseHandler);
|
||||
ThreadPauseHandlerAddress = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
// We are pausing, this means the frontend should be waiting for this thread to idle
|
||||
// We will have faulted and jumped to this location at this point
|
||||
|
||||
// Call our sleep handler
|
||||
LoadConstant(x0, reinterpret_cast<uintptr_t>(CTX));
|
||||
mov(x1, STATE);
|
||||
LoadConstant(x2, reinterpret_cast<uint64_t>(SleepThread));
|
||||
blr(x2);
|
||||
|
||||
// XXX: Unsupported atm
|
||||
//PauseReturnInstruction = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
//// Fault to start running again
|
||||
//hlt(0);
|
||||
}
|
||||
|
||||
{
|
||||
CallbackPtr = Buffer->GetOffsetAddress<CPUBackend::JITCallback>(GetCursorOffset());
|
||||
|
||||
// We expect the thunk to have previously pushed the registers it was using
|
||||
PushCalleeSavedRegisters();
|
||||
|
||||
// First thing we need to move the thread state pointer back in to our register
|
||||
mov(STATE, x0);
|
||||
|
||||
// Now push the callback return trampoline to the guest stack
|
||||
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
|
||||
LoadConstant(x0, CTX->X86CodeGen.CallbackReturn);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
|
||||
sub(x2, x2, 16);
|
||||
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
|
||||
|
||||
// Store the trampoline to the guest stack
|
||||
// Guest stack is now correctly misaligned after a regular call instruction
|
||||
str(x0, MemOperand(x2));
|
||||
|
||||
// Store RIP to the context state
|
||||
str(x1, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.rip)));
|
||||
|
||||
// Now go back to the regular dispatcher loop
|
||||
b(&LoopTop);
|
||||
}
|
||||
|
||||
FinalizeCode();
|
||||
uint64_t CodeEnd = Buffer->GetOffsetAddress<uint64_t>(GetCursorOffset());
|
||||
vixl::aarch64::CPU::EnsureIAndDCacheCoherency(reinterpret_cast<void*>(DispatchPtr), CodeEnd - reinterpret_cast<uint64_t>(DispatchPtr));
|
||||
GetBuffer()->SetExecutable();
|
||||
}
|
||||
|
||||
|
||||
struct HostCTXHeader {
|
||||
uint32_t Magic;
|
||||
uint32_t Size;
|
||||
};
|
||||
|
||||
constexpr uint32_t FPR_MAGIC = 0x46508001U;
|
||||
|
||||
struct HostFPRState {
|
||||
HostCTXHeader Head;
|
||||
uint32_t FPSR;
|
||||
uint32_t FPCR;
|
||||
__uint128_t FPRs[32];
|
||||
};
|
||||
|
||||
struct ContextBackup {
|
||||
// Host State
|
||||
uint64_t GPRs[31];
|
||||
uint64_t PrevSP;
|
||||
uint64_t PrevPC;
|
||||
uint64_t PState;
|
||||
uint32_t FPSR;
|
||||
uint32_t FPCR;
|
||||
__uint128_t FPRs[32];
|
||||
|
||||
// Guest state
|
||||
int Signal;
|
||||
FEXCore::Core::CPUState GuestState;
|
||||
};
|
||||
|
||||
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// We can end up getting a signal at any point in our host state
|
||||
// Jump to a handler that saves all state so we can safely return
|
||||
uint64_t OldSP = _mcontext->sp;
|
||||
uintptr_t NewSP = OldSP;
|
||||
|
||||
size_t StackOffset = sizeof(ContextBackup);
|
||||
NewSP -= StackOffset;
|
||||
NewSP = AlignDown(NewSP, 16);
|
||||
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
memcpy(&Context->GPRs[0], &_mcontext->regs[0], 31 * sizeof(uint64_t));
|
||||
Context->PrevSP = _mcontext->sp;
|
||||
Context->PrevPC = _mcontext->pc;
|
||||
Context->PState = _mcontext->pstate;
|
||||
|
||||
// Host FPR state starts at _mcontext->reserved[0];
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
|
||||
Context->FPSR = HostState->FPSR;
|
||||
Context->FPCR = HostState->FPCR;
|
||||
memcpy(&Context->FPRs[0], &HostState->FPRs[0], 32 * sizeof(__uint128_t));
|
||||
|
||||
// Retain the action pointer so we can see it when we return
|
||||
Context->Signal = Signal;
|
||||
|
||||
// Save guest state
|
||||
// We can't guarantee if registers are in context or host GPRs
|
||||
// So we need to save everything
|
||||
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Set the new SP
|
||||
_mcontext->sp = NewSP;
|
||||
}
|
||||
|
||||
void DispatchGenerator::RestoreThreadState(void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
uint64_t OldSP = _mcontext->sp;
|
||||
uintptr_t NewSP = OldSP;
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
|
||||
// First thing, reset the guest state
|
||||
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Now restore host state
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LogMan::Throw::A(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x%08x", HostState->Head.Magic);
|
||||
memcpy(&HostState->FPRs[0], &Context->FPRs[0], 32 * sizeof(__uint128_t));
|
||||
Context->FPCR = HostState->FPCR;
|
||||
Context->FPSR = HostState->FPSR;
|
||||
|
||||
// Restore GPRs and other state
|
||||
_mcontext->pstate = Context->PState;
|
||||
_mcontext->pc = Context->PrevPC;
|
||||
_mcontext->sp = Context->PrevSP;
|
||||
memcpy(&_mcontext->regs[0], &Context->GPRs[0], 31 * sizeof(uint64_t));
|
||||
|
||||
// Restore the previous signal state
|
||||
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
|
||||
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
|
||||
}
|
||||
|
||||
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->pc = AbsoluteLoopTopAddress;
|
||||
// Set x28 (which is our state register) to point to our guest thread data
|
||||
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
|
||||
|
||||
State->State.State.gregs[X86State::REG_RDI] = Signal;
|
||||
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
if (!(GuestStack->ss_flags & SS_DISABLE)) {
|
||||
// If our guest is already inside of the alternative stack
|
||||
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
|
||||
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
|
||||
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
|
||||
if (OldGuestSP >= AltStackBase &&
|
||||
OldGuestSP <= AltStackEnd) {
|
||||
// We are already in the alt stack, the rest of the code will handle adjusting this
|
||||
}
|
||||
else {
|
||||
NewGuestSP = AltStackEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Back up past the redzone, which is 128bytes
|
||||
// Don't need this offset if we aren't going to be putting siginfo in to it
|
||||
NewGuestSP -= 128;
|
||||
|
||||
if (GuestAction->sa_flags & SA_SIGINFO) {
|
||||
// XXX: siginfo_t(RSI), ucontext (RDX)
|
||||
State->State.State.gregs[X86State::REG_RSI] = 0;
|
||||
State->State.State.gregs[X86State::REG_RDX] = 0;
|
||||
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
|
||||
}
|
||||
else {
|
||||
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
|
||||
}
|
||||
|
||||
// Set up the new SP for stack handling
|
||||
NewGuestSP -= 8;
|
||||
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->pc = ThreadPauseHandlerAddress;
|
||||
|
||||
// Set our state register to point to our guest thread data
|
||||
_mcontext->regs[28 /* STATE */] = reinterpret_cast<uint64_t>(State);
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Our thread is stopping
|
||||
// We don't care about anything at this point
|
||||
// Set the stack to our starting location when we entered the JIT and get out safely
|
||||
_mcontext->sp = State->State.ReturningStackLocation;
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->pc = ThreadStopHandlerAddress;
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
Generator = new DispatchGenerator(ctx, Thread);
|
||||
DispatchPtr = Generator->DispatchPtr;
|
||||
CallbackPtr = Generator->CallbackPtr;
|
||||
|
||||
// TODO: Implement this. It is missing from the dispatcher
|
||||
// TODO: It feels wrong to initialize this way
|
||||
ctx->InterpreterCallbackReturn = nullptr;
|
||||
}
|
||||
|
||||
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
DispatchGenerator *Gen = Generator;
|
||||
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
}
|
||||
|
||||
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
DispatchGenerator *Gen = Generator;
|
||||
return Gen->HandleSignalPause(Signal, info, ucontext);
|
||||
}
|
||||
|
||||
void InterpreterCore::DeleteAsmDispatch() {
|
||||
delete Generator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
class DispatchGenerator;
|
||||
class X86DispatchGenerator;
|
||||
class Arm64DispatchGenerator;
|
||||
|
||||
#define DESTMAP_AS_MAP 0
|
||||
#if DESTMAP_AS_MAP
|
||||
@@ -20,16 +22,14 @@ using DestMapType = std::vector<uint32_t>;
|
||||
class InterpreterCore final : public CPUBackend {
|
||||
public:
|
||||
explicit InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
~InterpreterCore() override;
|
||||
std::string GetName() override { return "Interpreter"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
bool NeedsOpDispatch() override { return true; }
|
||||
|
||||
void CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
|
||||
void DeleteAsmDispatch();
|
||||
|
||||
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
|
||||
|
||||
@@ -38,8 +38,6 @@ private:
|
||||
FEXCore::Core::InternalThreadState *State;
|
||||
|
||||
uint32_t AllocateTmpSpace(size_t Size);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
|
||||
template<typename Res>
|
||||
Res GetDest(void* SSAData, IR::OrderedNodeWrapper Op);
|
||||
@@ -47,7 +45,7 @@ private:
|
||||
template<typename Res>
|
||||
Res GetSrc(void* SSAData, IR::OrderedNodeWrapper Src);
|
||||
|
||||
DispatchGenerator *Generator{};
|
||||
std::unique_ptr<Dispatcher> Dispatcher{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
#include "Common/SoftFloat.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
#include "Interface/Core/ArchHelpers/Arm64.h"
|
||||
#endif
|
||||
#include "Interface/Core/ArchHelpers/MContext.h"
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/DebugData.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
@@ -22,62 +21,64 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#ifdef _M_X86_64
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include "InterpreterOps.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
static void InterpreterExecution(FEXCore::Core::InternalThreadState *Thread) {
|
||||
auto LocalEntry = Thread->LocalIRCache.find(Thread->State.State.rip);
|
||||
|
||||
InterpreterOps::InterpretIR(Thread, LocalEntry->second.IR.get(), LocalEntry->second.DebugData.get());
|
||||
}
|
||||
static void InterpreterExecution(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
auto LocalEntry = Thread->LocalIRCache.find(Thread->CurrentFrame->State.rip);
|
||||
|
||||
InterpreterOps::InterpretIR(Thread, Thread->CurrentFrame->State.rip, LocalEntry->second.IR.get(), LocalEntry->second.DebugData.get());
|
||||
}
|
||||
|
||||
bool InterpreterCore::HandleSIGBUS(int Signal, void *info, void *ucontext) {
|
||||
#ifdef _M_ARM_64
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
uint32_t *PC = (uint32_t*)_mcontext->pc;
|
||||
uint32_t Instr = PC[0];
|
||||
if ((Instr & FEXCore::ArchHelpers::Arm64::CASPAL_MASK) == FEXCore::ArchHelpers::Arm64::CASPAL_INST) { // CASPAL
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleCASPAL(_mcontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
_mcontext->pc += 4;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS CASPAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((Instr & FEXCore::ArchHelpers::Arm64::CASAL_MASK) == FEXCore::ArchHelpers::Arm64::CASAL_INST) { // CASAL
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleCASAL(_mcontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
_mcontext->pc += 4;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS CASAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(_mcontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
_mcontext->pc += 4;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
uint8_t Op = (PC[0] >> 12) & 0xF;
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
constexpr bool is_arm64 = true;
|
||||
#else
|
||||
constexpr bool is_arm64 = false;
|
||||
#endif
|
||||
|
||||
if constexpr (is_arm64) {
|
||||
uint32_t *PC = reinterpret_cast<uint32_t*>(ArchHelpers::Context::GetPc(ucontext));
|
||||
uint32_t Instr = PC[0];
|
||||
if ((Instr & FEXCore::ArchHelpers::Arm64::CASPAL_MASK) == FEXCore::ArchHelpers::Arm64::CASPAL_INST) { // CASPAL
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleCASPAL(ucontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS CASPAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((Instr & FEXCore::ArchHelpers::Arm64::CASAL_MASK) == FEXCore::ArchHelpers::Arm64::CASAL_INST) { // CASAL
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleCASAL(ucontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS CASAL: PC: %p Instruction: 0x%08x\n", PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((Instr & FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_MASK) == FEXCore::ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op
|
||||
if (FEXCore::ArchHelpers::Arm64::HandleAtomicMemOp(ucontext, info, Instr)) {
|
||||
// Skip this instruction now
|
||||
ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
uint8_t Op = (PC[0] >> 12) & 0xF;
|
||||
LogMan::Msg::E("Unhandled JIT SIGBUS Atomic mem op 0x%02x: PC: %p Instruction: 0x%08x\n", Op, PC, PC[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
|
||||
CreateAsmDispatch(ctx, Thread);
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
InterpreterCore *Core = reinterpret_cast<InterpreterCore*>(Thread->CPUBackend.get());
|
||||
return Core->HandleSignalPause(Signal, info, ucontext);
|
||||
return Core->Dispatcher->HandleSignalPause(Signal, info, ucontext);
|
||||
});
|
||||
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
@@ -101,7 +102,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
|
||||
|
||||
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
|
||||
InterpreterCore *Core = reinterpret_cast<InterpreterCore*>(Thread->CPUBackend.get());
|
||||
return Core->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
return Core->Dispatcher->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
};
|
||||
|
||||
for (uint32_t Signal = 0; Signal < SignalDelegator::MAX_SIGNALS; ++Signal) {
|
||||
@@ -110,13 +111,7 @@ InterpreterCore::InterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InterpreterCore::~InterpreterCore() {
|
||||
DeleteAsmDispatch();
|
||||
}
|
||||
|
||||
|
||||
void *InterpreterCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
return reinterpret_cast<void*>(InterpreterExecution);
|
||||
}
|
||||
|
||||
|
||||
+184
-162
File diff suppressed because it is too large.
Load diff
@@ -36,7 +36,7 @@ namespace FEXCore::CPU {
|
||||
class InterpreterOps {
|
||||
|
||||
public:
|
||||
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
|
||||
static void InterpretIR(FEXCore::Core::InternalThreadState *Thread, uint64_t Entry, FEXCore::IR::IRListView *CurrentIR, FEXCore::Core::DebugData *DebugData);
|
||||
static bool GetFallbackHandler(IR::IROp_Header *IROp, FallbackInfo *Info);
|
||||
};
|
||||
};
|
||||
@@ -1,470 +0,0 @@
|
||||
#include "Common/MathUtils.h"
|
||||
#include "Interface/Core/Interpreter/InterpreterClass.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE r14
|
||||
|
||||
class DispatchGenerator : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread);
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
|
||||
CPUBackend::AsmDispatch DispatchPtr;
|
||||
CPUBackend::JITCallback CallbackPtr;
|
||||
FEXCore::Context::Context::IntCallbackReturn ReturnPtr;
|
||||
|
||||
uint64_t ThreadStopHandlerAddress;
|
||||
uint64_t AbsoluteLoopTopAddress;
|
||||
uint64_t ThreadPauseHandlerAddress;
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *State;
|
||||
private:
|
||||
void StoreThreadState(int Signal, void *ucontext);
|
||||
void RestoreThreadState(void *ucontext);
|
||||
std::stack<uint64_t> SignalFrames;
|
||||
};
|
||||
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
--ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
|
||||
// Go to sleep
|
||||
Thread->StartRunning.Wait();
|
||||
|
||||
Thread->State.RunningEvents.Running = true;
|
||||
++ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
}
|
||||
|
||||
DispatchGenerator::DispatchGenerator(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE)
|
||||
, CTX {ctx}
|
||||
, State {Thread} {
|
||||
using namespace Xbyak;
|
||||
using namespace Xbyak::util;
|
||||
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
|
||||
|
||||
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
|
||||
// Ptr = FindBlock(RIP)
|
||||
// if (!Ptr)
|
||||
// Ptr = CTX->CompileBlock(RIP);
|
||||
//
|
||||
// if (Ptr)
|
||||
// Ptr();
|
||||
// else
|
||||
// {
|
||||
// Ptr = FallbackCore->CompileBlock()
|
||||
// if (Ptr)
|
||||
// Ptr()
|
||||
// else {
|
||||
// ShouldStop = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Bunch of exit state stuff
|
||||
|
||||
// x86-64 ABI has the stack aligned when /call/ happens
|
||||
// Which means the destination has a misaligned stack at that point
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
mov(STATE, rdi);
|
||||
|
||||
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
||||
// regardless of where we were in the stack
|
||||
mov(qword [rdi + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)], rsp);
|
||||
|
||||
Label LoopTop;
|
||||
Label NoBlock;
|
||||
Label ExitBlock;
|
||||
Label ThreadPauseHandler;
|
||||
|
||||
L(LoopTop);
|
||||
AbsoluteLoopTopAddress = getCurr<uint64_t>();
|
||||
|
||||
{
|
||||
mov(r13, Thread->LookupCache->GetPagePointer());
|
||||
|
||||
// Load our RIP
|
||||
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
|
||||
|
||||
mov(rax, rdx);
|
||||
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
and_(rax, rbx);
|
||||
shr(rax, 12);
|
||||
|
||||
// Load page pointer
|
||||
mov(rdi, qword [r13 + rax * 8]);
|
||||
|
||||
cmp(rdi, 0);
|
||||
je(NoBlock);
|
||||
|
||||
mov (rax, rdx);
|
||||
and_(rax, 0x0FFF);
|
||||
|
||||
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
|
||||
|
||||
// check for aliasing
|
||||
mov(rcx, qword [rdi + rax + 8]);
|
||||
cmp(rcx, rdx);
|
||||
jne(NoBlock);
|
||||
|
||||
// Load the block pointer
|
||||
mov(rax, qword [rdi + rax]);
|
||||
|
||||
cmp(rax, 0);
|
||||
je(NoBlock);
|
||||
|
||||
// Real block if we made it here
|
||||
mov(rdi, STATE);
|
||||
call(rax);
|
||||
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
|
||||
|
||||
// If the value == 0 then branch to the top
|
||||
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
|
||||
je(LoopTop);
|
||||
// Else we need to pause now
|
||||
jmp(ThreadPauseHandler);
|
||||
ud2();
|
||||
}
|
||||
else {
|
||||
jmp(LoopTop);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
L(ExitBlock);
|
||||
ThreadStopHandlerAddress = getCurr<uint64_t>();
|
||||
|
||||
add(rsp, 8);
|
||||
|
||||
pop(r15);
|
||||
pop(r14);
|
||||
pop(r13);
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
|
||||
ret();
|
||||
}
|
||||
|
||||
// Block creation
|
||||
{
|
||||
L(NoBlock);
|
||||
|
||||
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
PtrCast Ptr;
|
||||
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
|
||||
|
||||
// {rdi, rsi, rdx}
|
||||
mov(rdi, reinterpret_cast<uint64_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, Ptr.Data);
|
||||
|
||||
call(rax);
|
||||
|
||||
// rdx already contains RIP here
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
// Pause handler
|
||||
ThreadPauseHandlerAddress = getCurr<uint64_t>();
|
||||
L(ThreadPauseHandler);
|
||||
|
||||
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
|
||||
|
||||
call(rax);
|
||||
|
||||
// XXX: Unsupported atm
|
||||
// uint64_t PauseReturnInstruction = getCurr<uint64_t>();
|
||||
// ud2();
|
||||
}
|
||||
|
||||
{
|
||||
CallbackPtr = getCurr<CPUBackend::JITCallback>();
|
||||
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
// First thing we need to move the thread state pointer back in to our register
|
||||
mov(STATE, rdi);
|
||||
// XXX: XMM?
|
||||
|
||||
// Now push the callback return trampoline to the guest stack
|
||||
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
|
||||
mov(rax, CTX->X86CodeGen.CallbackReturn);
|
||||
|
||||
// Store the trampoline to the guest stack
|
||||
// Guest stack is now correctly misaligned after a regular call instruction
|
||||
sub(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 16);
|
||||
mov(rbx, qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])]);
|
||||
mov(qword [rbx], rax);
|
||||
|
||||
// Store RIP to the context state
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], rsi);
|
||||
|
||||
// Back to the loop top now
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
|
||||
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
|
||||
// rdi = thread
|
||||
// rsi = rsp
|
||||
|
||||
mov(rsp, rsi);
|
||||
|
||||
// Now jump back to the thunk
|
||||
// XXX: XMM?
|
||||
add(rsp, 8);
|
||||
|
||||
pop(r15);
|
||||
pop(r14);
|
||||
pop(r13);
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
|
||||
ret();
|
||||
}
|
||||
ready();
|
||||
}
|
||||
|
||||
struct ContextBackup {
|
||||
uint64_t StoredCookie;
|
||||
// Host State
|
||||
// RIP and RSP is stored in GPRs here
|
||||
uint64_t GPRs[NGREG];
|
||||
_libc_fpstate FPRState;
|
||||
|
||||
// Guest state
|
||||
int Signal;
|
||||
FEXCore::Core::CPUState GuestState;
|
||||
};
|
||||
|
||||
void DispatchGenerator::StoreThreadState(int Signal, void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// We can end up getting a signal at any point in our host state
|
||||
// Jump to a handler that saves all state so we can safely return
|
||||
uint64_t OldSP = _mcontext->gregs[REG_RSP];
|
||||
uintptr_t NewSP = OldSP;
|
||||
|
||||
size_t StackOffset = sizeof(ContextBackup);
|
||||
|
||||
// We need to back up behind the host's red zone
|
||||
// We do this on the guest side as well
|
||||
NewSP -= 128;
|
||||
NewSP -= StackOffset;
|
||||
NewSP = AlignDown(NewSP, 16);
|
||||
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
|
||||
Context->StoredCookie = 0x4142434445464748ULL;
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&Context->GPRs[0], &_mcontext->gregs[0], NGREG * sizeof(_mcontext->gregs[0]));
|
||||
// Copy the FPRState
|
||||
memcpy(&Context->FPRState, _mcontext->fpregs, sizeof(_libc_fpstate));
|
||||
|
||||
// XXX: Save 256bit and 512bit AVX register state
|
||||
|
||||
// Retain the action pointer so we can see it when we return
|
||||
Context->Signal = Signal;
|
||||
|
||||
// Save guest state
|
||||
// We can't guarantee if registers are in context or host GPRs
|
||||
// So we need to save everything
|
||||
memcpy(&Context->GuestState, &State->State, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Set the new SP
|
||||
_mcontext->gregs[REG_RSP] = NewSP;
|
||||
|
||||
SignalFrames.push(NewSP);
|
||||
}
|
||||
|
||||
void DispatchGenerator::RestoreThreadState(void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
uint64_t OldSP = SignalFrames.top();
|
||||
SignalFrames.pop();
|
||||
uintptr_t NewSP = OldSP;
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
|
||||
if (Context->StoredCookie != 0x4142434445464748ULL) {
|
||||
LogMan::Msg::D("COOKIE WAS NOT CORRECT!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// First thing, reset the guest state
|
||||
memcpy(&State->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Now restore host state
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&_mcontext->gregs[0], &Context->GPRs[0], NGREG * sizeof(_mcontext->gregs[0]));
|
||||
// Copy the FPRState
|
||||
memcpy(_mcontext->fpregs, &Context->FPRState, sizeof(_libc_fpstate));
|
||||
|
||||
// Restore the previous signal state
|
||||
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
|
||||
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
|
||||
}
|
||||
|
||||
bool DispatchGenerator::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = AbsoluteLoopTopAddress;
|
||||
// Set our state register to point to our guest thread data
|
||||
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
|
||||
|
||||
uint64_t OldGuestSP = State->State.State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
if (!(GuestStack->ss_flags & SS_DISABLE)) {
|
||||
// If our guest is already inside of the alternative stack
|
||||
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
|
||||
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
|
||||
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
|
||||
if (OldGuestSP >= AltStackBase &&
|
||||
OldGuestSP <= AltStackEnd) {
|
||||
// We are already in the alt stack, the rest of the code will handle adjusting this
|
||||
}
|
||||
else {
|
||||
NewGuestSP = AltStackEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Back up past the redzone, which is 128bytes
|
||||
// Don't need this offset if we aren't going to be putting siginfo in to it
|
||||
NewGuestSP -= 128;
|
||||
|
||||
State->State.State.gregs[X86State::REG_RDI] = Signal;
|
||||
|
||||
if (GuestAction->sa_flags & SA_SIGINFO) {
|
||||
// XXX: siginfo_t(RSI), ucontext (RDX)
|
||||
State->State.State.gregs[X86State::REG_RSI] = 0;
|
||||
State->State.State.gregs[X86State::REG_RDX] = 0;
|
||||
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
|
||||
}
|
||||
else {
|
||||
State->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
|
||||
}
|
||||
|
||||
// Set up the new SP for stack handling
|
||||
NewGuestSP -= 8;
|
||||
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
State->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DispatchGenerator::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
FEXCore::Core::SignalEvent SignalReason = State->SignalReason.load();
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = ThreadPauseHandlerAddress;
|
||||
|
||||
// Set our state register to point to our guest thread data
|
||||
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(State);
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Our thread is stopping
|
||||
// We don't care about anything at this point
|
||||
// Set the stack to our starting location when we entered the core and get out safely
|
||||
_mcontext->gregs[REG_RSP] = State->State.ReturningStackLocation;
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
State->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterpreterCore::CreateAsmDispatch(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
Generator = new DispatchGenerator(ctx, Thread);
|
||||
DispatchPtr = Generator->DispatchPtr;
|
||||
CallbackPtr = Generator->CallbackPtr;
|
||||
|
||||
// TODO: It feels wrong to initialize this way
|
||||
ctx->InterpreterCallbackReturn = Generator->ReturnPtr;
|
||||
}
|
||||
|
||||
bool InterpreterCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
DispatchGenerator *Gen = Generator;
|
||||
return Gen->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
}
|
||||
|
||||
bool InterpreterCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
DispatchGenerator *Gen = Generator;
|
||||
return Gen->HandleSignalPause(Signal, info, ucontext);
|
||||
}
|
||||
|
||||
void InterpreterCore::DeleteAsmDispatch() {
|
||||
delete Generator;
|
||||
}
|
||||
|
||||
}
|
||||
+50
-63
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
@@ -29,7 +34,7 @@ static int64_t LREM(int64_t SrcHigh, int64_t SrcLow, int64_t Divisor) {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(TruncElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_TruncElementPair>();
|
||||
|
||||
@@ -41,7 +46,7 @@ DEF_OP(TruncElementPair) {
|
||||
mov(Dst.second, Src.second);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Truncation size: %d", Op->Size); break;
|
||||
default: LOGMAN_MSG_A("Unhandled Truncation size: %d", Op->Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +59,7 @@ DEF_OP(Constant) {
|
||||
DEF_OP(EntrypointOffset) {
|
||||
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
|
||||
|
||||
auto Constant = IR->GetHeader()->Entry + Op->Offset;
|
||||
auto Constant = Entry + Op->Offset;
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
LoadConstant(Dst, Constant);
|
||||
}
|
||||
@@ -90,7 +95,7 @@ DEF_OP(Add) {
|
||||
case 8:
|
||||
add(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Add size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Add size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
@@ -100,7 +105,7 @@ DEF_OP(Add) {
|
||||
case 8:
|
||||
add(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Add size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Add size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +121,7 @@ DEF_OP(Sub) {
|
||||
case 8:
|
||||
sub(GRS(Node), GRS(Op->Header.Args[0].ID()), Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Sub size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Sub size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
@@ -126,7 +131,7 @@ DEF_OP(Sub) {
|
||||
case 8:
|
||||
sub(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Sub size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Sub size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +147,7 @@ DEF_OP(Neg) {
|
||||
case 8:
|
||||
neg(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,12 +159,11 @@ DEF_OP(Mul) {
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
mul(Dst.W(), GetReg<RA_32>(Op->Header.Args[0].ID()), GetReg<RA_32>(Op->Header.Args[1].ID()));
|
||||
sxtw(Dst, Dst);
|
||||
break;
|
||||
case 8:
|
||||
mul(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +179,7 @@ DEF_OP(UMul) {
|
||||
case 8:
|
||||
mul(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +216,7 @@ DEF_OP(Div) {
|
||||
sdiv(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown DIV Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown DIV Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +243,7 @@ DEF_OP(UDiv) {
|
||||
udiv(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UDIV Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +290,7 @@ DEF_OP(Rem) {
|
||||
msub(GetReg<RA_64>(Node), TMP1, Divisor, Dividend);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown REM Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown REM Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +332,7 @@ DEF_OP(URem) {
|
||||
msub(GetReg<RA_64>(Node), TMP1, Divisor, Dividend);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UREM Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown UREM Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,12 +344,12 @@ DEF_OP(MulH) {
|
||||
sxtw(TMP1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
sxtw(TMP2, GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
mul(TMP1, TMP1, TMP2);
|
||||
sbfx(GetReg<RA_64>(Node), TMP1, 32, 32);
|
||||
ubfx(GetReg<RA_64>(Node), TMP1, 32, 32);
|
||||
break;
|
||||
case 8:
|
||||
smulh(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +366,7 @@ DEF_OP(UMulH) {
|
||||
case 8:
|
||||
umulh(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +402,6 @@ DEF_OP(Xor) {
|
||||
|
||||
DEF_OP(Lshl) {
|
||||
auto Op = IROp->C<IR::IROp_Lshl>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint64_t Const;
|
||||
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
|
||||
lsl(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
|
||||
@@ -409,7 +412,6 @@ DEF_OP(Lshl) {
|
||||
|
||||
DEF_OP(Lshr) {
|
||||
auto Op = IROp->C<IR::IROp_Lshr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint64_t Const;
|
||||
if (IsInlineConstant(Op->Header.Args[1], &Const)) {
|
||||
lsr(GRS(Node), GRS(Op->Header.Args[0].ID()), (unsigned int)Const);
|
||||
@@ -460,7 +462,7 @@ DEF_OP(Ror) {
|
||||
break;
|
||||
}
|
||||
|
||||
default: LogMan::Msg::A("Unhandled ROR size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled ROR size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
@@ -473,7 +475,7 @@ DEF_OP(Ror) {
|
||||
break;
|
||||
}
|
||||
|
||||
default: LogMan::Msg::A("Unhandled ROR size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled ROR size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,7 +494,7 @@ DEF_OP(Extr) {
|
||||
break;
|
||||
}
|
||||
|
||||
default: LogMan::Msg::A("Unhandled EXTR size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled EXTR size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,14 +526,10 @@ DEF_OP(LDiv) {
|
||||
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
|
||||
|
||||
#if _M_X86_64
|
||||
CallRuntime(LDIV);
|
||||
#else
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(LDIV));
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
@@ -541,7 +539,7 @@ DEF_OP(LDiv) {
|
||||
mov(GetReg<RA_64>(Node), x0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LDIV Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown LDIV Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,14 +569,10 @@ DEF_OP(LUDiv) {
|
||||
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
|
||||
|
||||
#if _M_X86_64
|
||||
CallRuntime(LUDIV);
|
||||
#else
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(LUDIV));
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
@@ -588,7 +582,7 @@ DEF_OP(LUDiv) {
|
||||
mov(GetReg<RA_64>(Node), x0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LUDIV Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown LUDIV Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,14 +622,10 @@ DEF_OP(LRem) {
|
||||
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
|
||||
|
||||
#if _M_X86_64
|
||||
CallRuntime(LREM);
|
||||
#else
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(LREM));
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
@@ -645,7 +635,7 @@ DEF_OP(LRem) {
|
||||
mov(GetReg<RA_64>(Node), x0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LREM Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown LREM Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,14 +672,11 @@ DEF_OP(LURem) {
|
||||
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Header.Args[2].ID()));
|
||||
|
||||
#if _M_X86_64
|
||||
CallRuntime(LUREM);
|
||||
#else
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(LUREM));
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
// Fix the stack and any values that were stepped on
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -698,7 +685,7 @@ DEF_OP(LURem) {
|
||||
mov(GetReg<RA_64>(Node), x0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LUREM Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LUREM Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,7 +699,7 @@ DEF_OP(Not) {
|
||||
case 8:
|
||||
mvn(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,7 +730,7 @@ DEF_OP(Popcount) {
|
||||
// fmov has zero extended, unused bytes are zero
|
||||
addv(VTMP1.B(), VTMP1.V8B());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Popcount size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Popcount size: %d", OpSize);
|
||||
}
|
||||
|
||||
auto Dst = GetReg<RA_32>(Node);
|
||||
@@ -792,7 +779,7 @@ DEF_OP(FindMSB) {
|
||||
clz(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
sub(Dst, TMP1, Dst);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +800,7 @@ DEF_OP(FindTrailingZeros) {
|
||||
rbit(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
clz(GetReg<RA_64>(Node), GetReg<RA_64>(Node));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,7 +819,7 @@ DEF_OP(CountLeadingZeroes) {
|
||||
case 8:
|
||||
clz(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,7 +837,7 @@ DEF_OP(Rev) {
|
||||
case 8:
|
||||
rev(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,15 +859,15 @@ DEF_OP(Bfi) {
|
||||
bfi(TMP1, GetReg<RA_64>(Op->Header.Args[1].ID()), Op->lsb, Op->Width);
|
||||
mov(GetReg<RA_64>(Node), TMP1);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown BFI size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown BFI size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Bfe) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
LogMan::Throw::A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
|
||||
LogMan::Throw::A(Op->Width != 0, "Invalid BFE width of 0");
|
||||
LOGMAN_THROW_A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
|
||||
LOGMAN_THROW_A(Op->Width != 0, "Invalid BFE width of 0");
|
||||
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
ubfx(Dst, GetReg<RA_64>(Op->Header.Args[0].ID()), Op->lsb, Op->Width);
|
||||
@@ -920,12 +907,12 @@ Condition MapSelectCC(IR::CondClassType Cond) {
|
||||
case FEXCore::IR::COND_FGT: return Condition::hi;
|
||||
case FEXCore::IR::COND_FU: return Condition::vs;
|
||||
case FEXCore::IR::COND_FNU: return Condition::vc;
|
||||
case FEXCore::IR::COND_VS:;
|
||||
case FEXCore::IR::COND_VC:;
|
||||
case FEXCore::IR::COND_VS:
|
||||
case FEXCore::IR::COND_VC:
|
||||
case FEXCore::IR::COND_MI:
|
||||
case FEXCore::IR::COND_PL:
|
||||
default:
|
||||
LogMan::Msg::A("Unsupported compare type");
|
||||
LOGMAN_MSG_A("Unsupported compare type");
|
||||
return Condition::nv;
|
||||
}
|
||||
}
|
||||
@@ -943,9 +930,9 @@ DEF_OP(Select) {
|
||||
} else if (IsFPR(Op->Cmp1.ID())) {
|
||||
fcmp(GRFCMP(Op->Cmp1.ID()), GRFCMP(Op->Cmp2.ID()));
|
||||
} else {
|
||||
LogMan::Msg::A("Select: Expected GPR or FPR");
|
||||
LOGMAN_MSG_A("Select: Expected GPR or FPR");
|
||||
}
|
||||
|
||||
|
||||
auto cc = MapSelectCC(Op->Cond);
|
||||
|
||||
uint64_t const_true, const_false;
|
||||
@@ -954,7 +941,7 @@ DEF_OP(Select) {
|
||||
|
||||
if (is_const_true || is_const_false) {
|
||||
if (is_const_false != true || is_const_true != true || const_true != 1 || const_false != 0) {
|
||||
LogMan::Msg::A("Select: Unsupported compare inline parameters");
|
||||
LOGMAN_MSG_A("Select: Unsupported compare inline parameters");
|
||||
}
|
||||
cset(GRS(Node), cc);
|
||||
} else {
|
||||
@@ -978,7 +965,7 @@ DEF_OP(VExtractToGPR) {
|
||||
case 8:
|
||||
umov(GetReg<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Idx);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled ExtractElementSize: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled ExtractElementSize: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,11 +1009,11 @@ DEF_OP(FCmp) {
|
||||
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
}
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
|
||||
|
||||
bool set = false;
|
||||
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) {
|
||||
LogMan::Throw::A(IR::FCMP_FLAG_EQ == 0, "IR::FCMP_FLAG_EQ must equal 0");
|
||||
LOGMAN_THROW_A(IR::FCMP_FLAG_EQ == 0, "IR::FCMP_FLAG_EQ must equal 0");
|
||||
// EQ or unordered
|
||||
cset(Dst, Condition::eq); // Z = 1
|
||||
csinc(Dst, Dst, xzr, Condition::vc); // IF !V ? Z : 1
|
||||
@@ -1057,8 +1044,8 @@ DEF_OP(FCmp) {
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
void JITCore::RegisterALUHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterALUHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
|
||||
REGISTER_OP(CONSTANT, Constant);
|
||||
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
|
||||
|
||||
+35
-29
@@ -1,9 +1,15 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(CASPair) {
|
||||
auto Op = IROp->C<IR::IROp_CASPair>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -28,7 +34,7 @@ DEF_OP(CASPair) {
|
||||
mov(Dst.first, TMP3);
|
||||
mov(Dst.second, TMP4);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -83,7 +89,7 @@ DEF_OP(CASPair) {
|
||||
bind(&LoopExpected);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unsupported: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +115,7 @@ DEF_OP(CAS) {
|
||||
case 2: casalh(TMP2.W(), Desired.W(), MemOperand(MemSrc)); break;
|
||||
case 4: casal(TMP2.W(), Desired.W(), MemOperand(MemSrc)); break;
|
||||
case 8: casal(TMP2.X(), Desired.X(), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unsupported: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
|
||||
}
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
}
|
||||
@@ -200,7 +206,7 @@ DEF_OP(CAS) {
|
||||
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,7 +222,7 @@ DEF_OP(AtomicAdd) {
|
||||
case 2: staddlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 4: staddl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 8: staddl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -258,7 +264,7 @@ DEF_OP(AtomicAdd) {
|
||||
cbnz(TMP2, &LoopTop);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,7 +281,7 @@ DEF_OP(AtomicSub) {
|
||||
case 2: staddlh(TMP2.W(), MemOperand(MemSrc)); break;
|
||||
case 4: staddl(TMP2.W(), MemOperand(MemSrc)); break;
|
||||
case 8: staddl(TMP2.X(), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -317,7 +323,7 @@ DEF_OP(AtomicSub) {
|
||||
cbnz(TMP2, &LoopTop);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +340,7 @@ DEF_OP(AtomicAnd) {
|
||||
case 2: stclrlh(TMP2.W(), MemOperand(MemSrc)); break;
|
||||
case 4: stclrl(TMP2.W(), MemOperand(MemSrc)); break;
|
||||
case 8: stclrl(TMP2.X(), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -376,7 +382,7 @@ DEF_OP(AtomicAnd) {
|
||||
cbnz(TMP2, &LoopTop);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,7 +398,7 @@ DEF_OP(AtomicOr) {
|
||||
case 2: stsetlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 4: stsetl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 8: stsetl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -434,7 +440,7 @@ DEF_OP(AtomicOr) {
|
||||
cbnz(TMP2, &LoopTop);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,7 +456,7 @@ DEF_OP(AtomicXor) {
|
||||
case 2: steorlh(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 4: steorl(GetReg<RA_32>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
case 8: steorl(GetReg<RA_64>(Op->Header.Args[1].ID()), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -492,7 +498,7 @@ DEF_OP(AtomicXor) {
|
||||
cbnz(TMP2, &LoopTop);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,7 +515,7 @@ DEF_OP(AtomicSwap) {
|
||||
case 2: swplh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: swpl(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: swpl(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -552,7 +558,7 @@ DEF_OP(AtomicSwap) {
|
||||
mov(GetReg<RA_64>(Node), TMP2.X());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -567,7 +573,7 @@ DEF_OP(AtomicFetchAdd) {
|
||||
case 2: ldaddalh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: ldaddal(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: ldaddal(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -613,7 +619,7 @@ DEF_OP(AtomicFetchAdd) {
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,7 +635,7 @@ DEF_OP(AtomicFetchSub) {
|
||||
case 2: ldaddalh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: ldaddal(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: ldaddal(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -675,7 +681,7 @@ DEF_OP(AtomicFetchSub) {
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,7 +697,7 @@ DEF_OP(AtomicFetchAnd) {
|
||||
case 2: ldclralh(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: ldclral(TMP2.W(), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: ldclral(TMP2.X(), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -737,7 +743,7 @@ DEF_OP(AtomicFetchAnd) {
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -752,7 +758,7 @@ DEF_OP(AtomicFetchOr) {
|
||||
case 2: ldsetalh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: ldsetal(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: ldsetal(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -798,7 +804,7 @@ DEF_OP(AtomicFetchOr) {
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,7 +819,7 @@ DEF_OP(AtomicFetchXor) {
|
||||
case 2: ldeoralh(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 4: ldeoral(GetReg<RA_32>(Op->Header.Args[1].ID()), GetReg<RA_32>(Node), MemOperand(MemSrc)); break;
|
||||
case 8: ldeoral(GetReg<RA_64>(Op->Header.Args[1].ID()), GetReg<RA_64>(Node), MemOperand(MemSrc)); break;
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -859,14 +865,14 @@ DEF_OP(AtomicFetchXor) {
|
||||
mov(GetReg<RA_64>(Node), TMP2);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Atomic size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled Atomic size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterAtomicHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterAtomicHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(CASPAIR, CASPair);
|
||||
REGISTER_OP(CAS, CAS);
|
||||
REGISTER_OP(ATOMICADD, AtomicAdd);
|
||||
|
||||
+37
-34
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
|
||||
@@ -8,7 +14,7 @@
|
||||
namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(GuestCallDirect) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
}
|
||||
@@ -35,7 +41,7 @@ DEF_OP(CallbackReturn) {
|
||||
|
||||
// spill back to CTX
|
||||
SpillStaticRegs();
|
||||
|
||||
|
||||
// First we must reset the stack
|
||||
ResetStack();
|
||||
|
||||
@@ -46,9 +52,9 @@ DEF_OP(CallbackReturn) {
|
||||
str(w2, MemOperand(x0));
|
||||
|
||||
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
|
||||
add(x2, x2, 8);
|
||||
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])));
|
||||
str(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])));
|
||||
|
||||
PopCalleeSavedRegisters();
|
||||
|
||||
@@ -67,7 +73,7 @@ DEF_OP(ExitFunction) {
|
||||
uint64_t NewRIP;
|
||||
|
||||
if (IsInlineConstant(Op->NewRIP, &NewRIP) || IsInlineEntrypointOffset(Op->NewRIP, &NewRIP)) {
|
||||
Literal l_BranchHost{ExitFunctionLinkerAddress};
|
||||
Literal l_BranchHost{ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress};
|
||||
Literal l_BranchGuest{NewRIP};
|
||||
|
||||
ldr(x0, &l_BranchHost);
|
||||
@@ -77,9 +83,9 @@ DEF_OP(ExitFunction) {
|
||||
place(&l_BranchGuest);
|
||||
} else {
|
||||
RipReg = GetReg<RA_64>(Op->Header.Args[0].ID());
|
||||
|
||||
|
||||
// L1 Cache
|
||||
LoadConstant(x0, State->LookupCache->GetL1Pointer());
|
||||
LoadConstant(x0, ThreadState->LookupCache->GetL1Pointer());
|
||||
|
||||
and_(x3, RipReg, LookupCache::L1_ENTRIES_MASK);
|
||||
add(x0, x0, Operand(x3, Shift::LSL, 4));
|
||||
@@ -90,8 +96,8 @@ DEF_OP(ExitFunction) {
|
||||
br(x1);
|
||||
|
||||
bind(&FullLookup);
|
||||
LoadConstant(TMP1, AbsoluteLoopTopAddress);
|
||||
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, State.rip)));
|
||||
LoadConstant(TMP1, ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress);
|
||||
str(RipReg, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip)));
|
||||
br(TMP1);
|
||||
}
|
||||
}
|
||||
@@ -131,12 +137,12 @@ Condition MapBranchCC(IR::CondClassType Cond) {
|
||||
case FEXCore::IR::COND_FGT: return Condition::hi;
|
||||
case FEXCore::IR::COND_FU: return Condition::vs;
|
||||
case FEXCore::IR::COND_FNU: return Condition::vc;
|
||||
case FEXCore::IR::COND_VS:;
|
||||
case FEXCore::IR::COND_VC:;
|
||||
case FEXCore::IR::COND_VS:
|
||||
case FEXCore::IR::COND_VC:
|
||||
case FEXCore::IR::COND_MI:
|
||||
case FEXCore::IR::COND_PL:
|
||||
default:
|
||||
LogMan::Msg::A("Unsupported compare type");
|
||||
LOGMAN_MSG_A("Unsupported compare type");
|
||||
return Condition::nv;
|
||||
}
|
||||
}
|
||||
@@ -163,10 +169,10 @@ DEF_OP(CondJump) {
|
||||
bool isConst = IsInlineConstant(Op->Cmp2, &Const);
|
||||
|
||||
if (isConst && Const == 0 && Op->Cond.Val == FEXCore::IR::COND_EQ) {
|
||||
LogMan::Throw::A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
|
||||
LOGMAN_THROW_A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
|
||||
cbz(GRCMP(Op->Cmp1.ID()), TrueTargetLabel);
|
||||
} else if (isConst && Const == 0 && Op->Cond.Val == FEXCore::IR::COND_NEQ) {
|
||||
LogMan::Throw::A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
|
||||
LOGMAN_THROW_A(IsGPR(Op->Cmp1.ID()), "CondJump: Expected GPR");
|
||||
cbnz(GRCMP(Op->Cmp1.ID()), TrueTargetLabel);
|
||||
} else {
|
||||
if (IsGPR(Op->Cmp1.ID())) {
|
||||
@@ -177,12 +183,12 @@ DEF_OP(CondJump) {
|
||||
} else if (IsFPR(Op->Cmp1.ID())) {
|
||||
fcmp(GRFCMP(Op->Cmp1.ID()), GRFCMP(Op->Cmp2.ID()));
|
||||
} else {
|
||||
LogMan::Msg::A("CondJump: Expected GPR or FPR");
|
||||
LOGMAN_MSG_A("CondJump: Expected GPR or FPR");
|
||||
}
|
||||
|
||||
b(TrueTargetLabel, MapBranchCC(Op->Cond));
|
||||
}
|
||||
|
||||
|
||||
if (FalseIter == JumpTargets.end()) {
|
||||
FalseTargetLabel = &JumpTargets.try_emplace(Op->FalseBlock.ID()).first->second;
|
||||
}
|
||||
@@ -217,7 +223,7 @@ DEF_OP(Syscall) {
|
||||
blr(x3);
|
||||
|
||||
add(sp, sp, SPOffset);
|
||||
|
||||
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
FillStaticRegs();
|
||||
@@ -239,16 +245,12 @@ DEF_OP(Thunk) {
|
||||
|
||||
mov(x0, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
#if _M_X86_64
|
||||
ERROR_AND_DIE("JIT: OP_THUNK not supported with arm simulator")
|
||||
#else
|
||||
auto thunkFn = State->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
LoadConstant(x2, (uintptr_t)thunkFn);
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
|
||||
FillStaticRegs(); // load from ctx after ra64 refill
|
||||
}
|
||||
|
||||
@@ -260,9 +262,9 @@ DEF_OP(ValidateCode) {
|
||||
int idx = 0;
|
||||
|
||||
LoadConstant(GetReg<RA_64>(Node), 0);
|
||||
LoadConstant(x0, IR->GetHeader()->Entry + Op->Offset);
|
||||
LoadConstant(x0, Entry + Op->Offset);
|
||||
LoadConstant(x1, 1);
|
||||
|
||||
|
||||
while (len >= 8)
|
||||
{
|
||||
ldr(x2, MemOperand(x0, idx));
|
||||
@@ -302,17 +304,16 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
|
||||
DEF_OP(RemoveCodeEntry) {
|
||||
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
|
||||
// Arguments are passed as follows:
|
||||
// X0: Thread
|
||||
// X1: RIP
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
|
||||
|
||||
mov(x0, STATE);
|
||||
LoadConstant(x1, IR->GetHeader()->Entry);
|
||||
|
||||
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
|
||||
LoadConstant(x1, Entry);
|
||||
|
||||
LoadConstant(x2, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
|
||||
SpillStaticRegs();
|
||||
blr(x2);
|
||||
FillStaticRegs();
|
||||
@@ -323,15 +324,17 @@ DEF_OP(RemoveCodeEntry) {
|
||||
|
||||
DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
|
||||
// x0 = CPUID Handler
|
||||
// x1 = CPUID Function
|
||||
// x2 = CPUID Leaf
|
||||
LoadConstant(x0, reinterpret_cast<uint64_t>(&CTX->CPUID));
|
||||
mov(x1, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
|
||||
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t);
|
||||
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t, uint32_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
@@ -354,8 +357,8 @@ DEF_OP(CPUID) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
|
||||
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
|
||||
REGISTER_OP(GUESTRETURN, GuestReturn);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(VInsGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VInsGPR>();
|
||||
mov(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
@@ -25,7 +31,7 @@ DEF_OP(VInsGPR) {
|
||||
ins(GetDst(Node).V2D(), Op->Index, GetReg<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +52,12 @@ DEF_OP(VCastFromGPR) {
|
||||
case 8:
|
||||
fmov(GetDst(Node).D(), GetReg<RA_64>(Op->Header.Args[0].ID()).X());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_U) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
@@ -89,7 +95,7 @@ DEF_OP(Float_FToF) {
|
||||
fcvt(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown FCVT sizes: 0x%x", Conv);
|
||||
default: LOGMAN_MSG_A("Unknown FCVT sizes: 0x%x", Conv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +108,7 @@ DEF_OP(Vector_UToF) {
|
||||
case 8:
|
||||
ucvtf(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +121,7 @@ DEF_OP(Vector_SToF) {
|
||||
case 8:
|
||||
scvtf(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +134,7 @@ DEF_OP(Vector_FToZU) {
|
||||
case 8:
|
||||
fcvtzu(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +147,7 @@ DEF_OP(Vector_FToZS) {
|
||||
case 8:
|
||||
fcvtzs(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +162,7 @@ DEF_OP(Vector_FToU) {
|
||||
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
fcvtzu(GetDst(Node).V2D(), GetDst(Node).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +177,7 @@ DEF_OP(Vector_FToS) {
|
||||
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
fcvtzs(GetDst(Node).V2D(), GetDst(Node).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,13 +194,13 @@ DEF_OP(Vector_FToF) {
|
||||
fcvtn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Conversion Type : 0%04x", Conv); break;
|
||||
default: LOGMAN_MSG_A("Unknown Conversion Type : 0%04x", Conv); break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(AESImc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESImc>();
|
||||
@@ -83,8 +89,8 @@ DEF_OP(AESKeyGenAssist) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterEncryptionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterEncryptionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VAESIMC, AESImc);
|
||||
REGISTER_OP(VAESENC, AESEnc);
|
||||
REGISTER_OP(VAESENCLAST, AESEncLast);
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(GetHostFlag) {
|
||||
auto Op = IROp->C<IR::IROp_GetHostFlag>();
|
||||
ubfx(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Header.Args[0].ID()), Op->Flag, 1);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterFlagHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterFlagHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
+176
-1003
File diff suppressed because it is too large.
Load diff
+26
-92
@@ -1,9 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
|
||||
@@ -29,56 +36,18 @@ namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
// All but x29 are caller saved
|
||||
const std::array<aarch64::Register, 16> SRA64 = {
|
||||
x4, x5, x6, x7, x8, x9, x10, x11,
|
||||
x12, x18, x17, x16, x15, x14, x13, x29
|
||||
};
|
||||
|
||||
// All are callee saved
|
||||
const std::array<aarch64::Register, 9> RA64 = {
|
||||
x20, x21, x22, x23, x24, x25, x26, x27,
|
||||
x19
|
||||
};
|
||||
|
||||
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA64Pair = {{
|
||||
{x20, x21},
|
||||
{x22, x23},
|
||||
{x24, x25},
|
||||
{x26, x27},
|
||||
}};
|
||||
|
||||
const std::array<std::pair<aarch64::Register, aarch64::Register>, 4> RA32Pair = {{
|
||||
{w20, w21},
|
||||
{w22, w23},
|
||||
{w24, w25},
|
||||
{w26, w27},
|
||||
}};
|
||||
|
||||
// All are caller saved
|
||||
const std::array<aarch64::VRegister, 16> SRAFPR = {
|
||||
v16, v17, v18, v19, v20, v21, v22, v23,
|
||||
v24, v25, v26, v27, v28, v29, v30, v31
|
||||
};
|
||||
|
||||
// v8..v15 = (lower 64bits) Callee saved
|
||||
const std::array<aarch64::VRegister, 12> RAFPR = {
|
||||
/*v0, v1, v2, v3,*/v4, v5, v6, v7, // v0 ~ v3 are used as temps
|
||||
v8, v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
class JITCore final : public CPUBackend, public vixl::aarch64::Assembler {
|
||||
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
|
||||
public:
|
||||
struct CodeBuffer {
|
||||
uint8_t *Ptr;
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
|
||||
explicit Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
|
||||
~JITCore() override;
|
||||
~Arm64JITCore() override;
|
||||
std::string GetName() override { return "JIT"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
@@ -86,21 +55,22 @@ public:
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
bool HandleSIGILL(int Signal, void *info, void *ucontext);
|
||||
bool HandleSIGBUS(int Signal, void *info, void *ucontext);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
|
||||
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
|
||||
static CodeBuffer AllocateNewCodeBuffer(size_t Size);
|
||||
CodeBuffer AllocateNewCodeBuffer(size_t Size);
|
||||
|
||||
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
|
||||
std::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
|
||||
Label *PendingTargetLabel;
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *State;
|
||||
FEXCore::Core::InternalThreadState *ThreadState;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
uint64_t Entry;
|
||||
|
||||
std::map<IR::OrderedNodeWrapper::NodeOffsetType, aarch64::Label> JumpTargets;
|
||||
|
||||
@@ -146,6 +116,8 @@ private:
|
||||
|
||||
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node);
|
||||
|
||||
IR::PhysicalRegister GetPhys(uint32_t Node);
|
||||
|
||||
bool IsFPR(uint32_t Node);
|
||||
bool IsGPR(uint32_t Node);
|
||||
|
||||
@@ -162,9 +134,6 @@ private:
|
||||
#if DEBUG
|
||||
vixl::aarch64::Decoder Decoder;
|
||||
#endif
|
||||
vixl::aarch64::CPU CPU;
|
||||
bool SupportsAtomics{};
|
||||
bool SupportsRCPC{};
|
||||
|
||||
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
|
||||
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
|
||||
@@ -181,8 +150,6 @@ private:
|
||||
// For code safety we can't delete code buffers until outside of all signals
|
||||
std::vector<CodeBuffer> CodeBuffers{};
|
||||
|
||||
// This is the codebuffer that our dispatcher lives in
|
||||
CodeBuffer DispatcherCodeBuffer{};
|
||||
// This is the current code buffer that we are tracking
|
||||
CodeBuffer *CurrentCodeBuffer{};
|
||||
|
||||
@@ -190,60 +157,24 @@ private:
|
||||
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 128;
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 2;
|
||||
|
||||
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
|
||||
|
||||
#if DEBUG
|
||||
vixl::aarch64::Disassembler Disasm;
|
||||
#endif
|
||||
|
||||
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant);
|
||||
|
||||
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
|
||||
void PushCalleeSavedRegisters();
|
||||
void PopCalleeSavedRegisters();
|
||||
|
||||
static uint64_t ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
|
||||
|
||||
/**
|
||||
* @name Dispatch Helper functions
|
||||
* @{ */
|
||||
uint64_t AbsoluteLoopTopAddressFillSRA{};
|
||||
uint64_t AbsoluteLoopTopAddress{};
|
||||
uint64_t ThreadPauseHandlerAddressSpillSRA{};
|
||||
uint64_t ExitFunctionLinkerAddress{};
|
||||
uint64_t ThreadPauseHandlerAddress{};
|
||||
|
||||
uint64_t ThreadStopHandlerAddressSpillSRA{};
|
||||
uint64_t ThreadStopHandlerAddress{};
|
||||
uint64_t PauseReturnInstruction{};
|
||||
|
||||
uint32_t SignalHandlerRefCounter{};
|
||||
|
||||
void StoreThreadState(int Signal, void *ucontext);
|
||||
void RestoreThreadState(void *ucontext);
|
||||
/** @} */
|
||||
static uint64_t ExitFunctionLink(Arm64JITCore *core, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
|
||||
|
||||
struct CompilerSharedData {
|
||||
uint64_t SignalReturnInstruction{};
|
||||
|
||||
uint32_t *SignalHandlerRefCounterPtr{};
|
||||
FEXCore::CPU::Dispatcher *Dispatcher{};
|
||||
};
|
||||
|
||||
CompilerSharedData ThreadSharedData;
|
||||
IR::RegisterAllocationPass *RAPass;
|
||||
IR::RegisterAllocationData *RAData;
|
||||
|
||||
uint32_t SpillSlots{};
|
||||
|
||||
void SpillStaticRegs();
|
||||
void FillStaticRegs();
|
||||
|
||||
void PushDynamicRegsAndLR();
|
||||
void PopDynamicRegsAndLR();
|
||||
|
||||
void ResetStack();
|
||||
|
||||
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
|
||||
using OpHandler = void (Arm64JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
|
||||
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
|
||||
void RegisterALUHandlers();
|
||||
void RegisterAtomicHandlers();
|
||||
@@ -375,8 +306,11 @@ private:
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(LoadMemTSO);
|
||||
DEF_OP(StoreMemTSO);
|
||||
DEF_OP(ParanoidLoadMemTSO);
|
||||
DEF_OP(ParanoidStoreMemTSO);
|
||||
DEF_OP(VLoadMemElement);
|
||||
DEF_OP(VStoreMemElement);
|
||||
DEF_OP(CacheLineClear);
|
||||
|
||||
///< Misc ops
|
||||
DEF_OP(EndBlock);
|
||||
|
||||
+243
-73
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(LoadContext) {
|
||||
auto Op = IROp->C<IR::IROp_LoadContext>();
|
||||
@@ -23,7 +29,7 @@ DEF_OP(LoadContext) {
|
||||
case 8:
|
||||
ldr(GetReg<RA_64>(Node), MemOperand(STATE, Op->Offset));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -44,7 +50,7 @@ DEF_OP(LoadContext) {
|
||||
case 16:
|
||||
ldr(Dst, MemOperand(STATE, Op->Offset));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +72,7 @@ DEF_OP(StoreContext) {
|
||||
case 8:
|
||||
str(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(STATE, Op->Offset));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -87,7 +93,7 @@ DEF_OP(StoreContext) {
|
||||
case 16:
|
||||
str(Src, MemOperand(STATE, Op->Offset));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,61 +101,60 @@ DEF_OP(StoreContext) {
|
||||
|
||||
DEF_OP(LoadRegister) {
|
||||
auto Op = IROp->C<IR::IROp_LoadRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
if (Op->Class == IR::GPRClass) {
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.gregs[0])) / 8;
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0])) / 8;
|
||||
auto regOffs = Op->Offset & 7;
|
||||
|
||||
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
|
||||
LOGMAN_THROW_A(regId < SRA64.size(), "out of range regId");
|
||||
|
||||
auto reg = SRA64[regId];
|
||||
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
ubfx(GetReg<RA_64>(Node), reg, regOffs * 8, 8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
ubfx(GetReg<RA_64>(Node), reg, 0, 16);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
if (GetReg<RA_64>(Node).GetCode() != reg.GetCode())
|
||||
mov(GetReg<RA_32>(Node), reg.W());
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
if (GetReg<RA_64>(Node).GetCode() != reg.GetCode())
|
||||
mov(GetReg<RA_64>(Node), reg);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
|
||||
auto regOffs = Op->Offset & 15;
|
||||
|
||||
LogMan::Throw::A(regId < SRAFPR.size(), "out of range regId");
|
||||
LOGMAN_THROW_A(regId < SRAFPR.size(), "out of range regId");
|
||||
|
||||
auto guest = SRAFPR[regId];
|
||||
auto host = GetSrc(Node);
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
mov(host.B(), guest.B());
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
fmov(host.H(), guest.H());
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LogMan::Throw::A((regOffs & 3) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A((regOffs & 3) == 0, "unexpected regOffs");
|
||||
if (regOffs == 0) {
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
fmov(host.S(), guest.S());
|
||||
@@ -159,7 +164,7 @@ DEF_OP(LoadRegister) {
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LogMan::Throw::A((regOffs & 7) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A((regOffs & 7) == 0, "unexpected regOffs");
|
||||
if (regOffs == 0) {
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
mov(host.D(), guest.D());
|
||||
@@ -169,56 +174,54 @@ DEF_OP(LoadRegister) {
|
||||
break;
|
||||
|
||||
case 16:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
mov(host.Q(), guest.Q());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LogMan::Throw::A(false, "Unhandled Op->Class %d", Op->Class);
|
||||
LOGMAN_THROW_A(false, "Unhandled Op->Class %d", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(StoreRegister) {
|
||||
auto Op = IROp->C<IR::IROp_StoreRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
|
||||
if (Op->Class == IR::GPRClass) {
|
||||
auto regId = Op->Offset / 8 - 1;
|
||||
auto regOffs = Op->Offset & 7;
|
||||
|
||||
LogMan::Throw::A(regId < SRA64.size(), "out of range regId");
|
||||
LOGMAN_THROW_A(regId < SRA64.size(), "out of range regId");
|
||||
|
||||
auto reg = SRA64[regId];
|
||||
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LogMan::Throw::A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), regOffs * 8, 8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 16);
|
||||
break;
|
||||
|
||||
|
||||
case 4:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 32);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
if (GetReg<RA_64>(Op->Value.ID()).GetCode() != reg.GetCode())
|
||||
mov(reg, GetReg<RA_64>(Op->Value.ID()));
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == IR::FPRClass) {
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::ThreadState, State.xmm[0][0])) / 16;
|
||||
auto regId = (Op->Offset - offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0])) / 16;
|
||||
auto regOffs = Op->Offset & 15;
|
||||
|
||||
LogMan::Throw::A(regId < SRAFPR.size(), "regId out of range");
|
||||
LOGMAN_THROW_A(regId < SRAFPR.size(), "regId out of range");
|
||||
|
||||
auto guest = SRAFPR[regId];
|
||||
auto host = GetSrc(Op->Value.ID());
|
||||
@@ -229,28 +232,28 @@ DEF_OP(StoreRegister) {
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LogMan::Throw::A((regOffs & 1) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A((regOffs & 1) == 0, "unexpected regOffs");
|
||||
ins(guest.V8H(), regOffs/2, host.V8H(), 0);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LogMan::Throw::A((regOffs & 3) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A((regOffs & 3) == 0, "unexpected regOffs");
|
||||
ins(guest.V4S(), regOffs/4, host.V4S(), 0);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LogMan::Throw::A((regOffs & 7) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A((regOffs & 7) == 0, "unexpected regOffs");
|
||||
ins(guest.V2D(), regOffs / 8, host.V2D(), 0);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
LogMan::Throw::A(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_A(regOffs == 0, "unexpected regOffs");
|
||||
if (guest.GetCode() != host.GetCode())
|
||||
mov(guest.Q(), host.Q());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LogMan::Throw::A(false, "Unhandled Op->Class %d", Op->Class);
|
||||
LOGMAN_THROW_A(false, "Unhandled Op->Class %d", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,15 +287,15 @@ DEF_OP(LoadContextIndexed) {
|
||||
ldr(GetReg<RA_64>(Node), MemOperand(TMP1, Op->BaseOffset));
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
LogMan::Msg::A("Invalid Class load of size 16");
|
||||
LOGMAN_MSG_A("Invalid Class load of size 16");
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -329,12 +332,12 @@ DEF_OP(LoadContextIndexed) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,15 +373,15 @@ DEF_OP(StoreContextIndexed) {
|
||||
str(value, MemOperand(TMP1, Op->BaseOffset));
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
LogMan::Msg::A("Invalid Class load of size 16");
|
||||
LOGMAN_MSG_A("Invalid Class load of size 16");
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -417,12 +420,12 @@ DEF_OP(StoreContextIndexed) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,7 +453,7 @@ DEF_OP(SpillRegister) {
|
||||
str(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -466,10 +469,10 @@ DEF_OP(SpillRegister) {
|
||||
str(GetSrc(Op->Header.Args[0].ID()), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::A("Unhandled SpillRegister class: %d", Op->Class.Val);
|
||||
LOGMAN_MSG_A("Unhandled SpillRegister class: %d", Op->Class.Val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +499,7 @@ DEF_OP(FillRegister) {
|
||||
ldr(GetReg<RA_64>(Node), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -512,10 +515,10 @@ DEF_OP(FillRegister) {
|
||||
ldr(GetDst(Node), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::A("Unhandled FillRegister class: %d", Op->Class.Val);
|
||||
LOGMAN_MSG_A("Unhandled FillRegister class: %d", Op->Class.Val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,12 +533,12 @@ DEF_OP(StoreFlag) {
|
||||
strb(GetReg<RA_64>(Op->Header.Args[0].ID()), MemOperand(STATE, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag));
|
||||
}
|
||||
|
||||
MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
MemOperand Arm64JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
if (Offset.IsInvalid()) {
|
||||
return MemOperand(Base);
|
||||
} else {
|
||||
if (OffsetScale != 1 && OffsetScale != AccessSize) {
|
||||
LogMan::Msg::A("Unhandled GenerateMemOperand OffsetScale: %d", OffsetScale);
|
||||
LOGMAN_MSG_A("Unhandled GenerateMemOperand OffsetScale: %d", OffsetScale);
|
||||
}
|
||||
uint64_t Const;
|
||||
if (IsInlineConstant(Offset, &Const)) {
|
||||
@@ -547,10 +550,11 @@ MemOperand JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Register Bas
|
||||
case IR::MEM_OFFSET_UXTW.Val: return MemOperand(Base, RegOffset.W(), Extend::UXTW, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_SXTW.Val: return MemOperand(Base, RegOffset.W(), Extend::SXTW, (int)std::log2(OffsetScale) );
|
||||
|
||||
default: LogMan::Msg::A("Unhandled GenerateMemOperand OffsetType: %d", OffsetType.Val); break;
|
||||
default: LOGMAN_MSG_A("Unhandled GenerateMemOperand OffsetType: %d", OffsetType.Val); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
DEF_OP(LoadMem) {
|
||||
@@ -574,7 +578,7 @@ DEF_OP(LoadMem) {
|
||||
case 8:
|
||||
ldr(Dst, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -595,7 +599,7 @@ DEF_OP(LoadMem) {
|
||||
case 16:
|
||||
ldr(Dst, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,7 +610,7 @@ DEF_OP(LoadMemTSO) {
|
||||
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
LogMan::Msg::A("LoadMemTSO: No offset allowed");
|
||||
LOGMAN_MSG_A("LoadMemTSO: No offset allowed");
|
||||
}
|
||||
|
||||
if (SupportsRCPC && Op->Class == FEXCore::IR::GPRClass) {
|
||||
@@ -629,7 +633,7 @@ DEF_OP(LoadMemTSO) {
|
||||
case 8:
|
||||
ldapr(Dst, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
nop();
|
||||
}
|
||||
@@ -654,7 +658,7 @@ DEF_OP(LoadMemTSO) {
|
||||
case 8:
|
||||
ldar(Dst, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
nop();
|
||||
}
|
||||
@@ -675,7 +679,7 @@ DEF_OP(LoadMemTSO) {
|
||||
case 16:
|
||||
ldr(Dst, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
dmb(InnerShareable, BarrierAll);
|
||||
}
|
||||
@@ -702,7 +706,7 @@ DEF_OP(StoreMem) {
|
||||
case 8:
|
||||
str(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -723,7 +727,7 @@ DEF_OP(StoreMem) {
|
||||
case 16:
|
||||
str(Src, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -733,7 +737,7 @@ DEF_OP(StoreMemTSO) {
|
||||
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
LogMan::Msg::A("StoreMemTSO: No offset allowed");
|
||||
LOGMAN_MSG_A("StoreMemTSO: No offset allowed");
|
||||
}
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
@@ -753,7 +757,7 @@ DEF_OP(StoreMemTSO) {
|
||||
case 8:
|
||||
stlr(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
nop();
|
||||
}
|
||||
@@ -777,23 +781,182 @@ DEF_OP(StoreMemTSO) {
|
||||
case 16:
|
||||
str(Src, MemSrc);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
dmb(InnerShareable, BarrierAll);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(ParanoidLoadMemTSO) {
|
||||
auto Op = IROp->C<IR::IROp_LoadMemTSO>();
|
||||
|
||||
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
LOGMAN_MSG_A("LoadMemTSO: No offset allowed");
|
||||
}
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Size == 1) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
ldarb(Dst, MemSrc);
|
||||
}
|
||||
else {
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
nop();
|
||||
switch (Op->Size) {
|
||||
case 2:
|
||||
ldarh(Dst, MemSrc);
|
||||
break;
|
||||
case 4:
|
||||
ldar(Dst.W(), MemSrc);
|
||||
break;
|
||||
case 8:
|
||||
ldar(Dst, MemSrc);
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
nop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto Dst = GetDst(Node);
|
||||
switch (Op->Size) {
|
||||
case 2:
|
||||
nop();
|
||||
ldarh(TMP1, MemSrc);
|
||||
nop();
|
||||
fmov(Dst, TMP1);
|
||||
break;
|
||||
case 4:
|
||||
nop();
|
||||
ldar(TMP1.W(), MemSrc);
|
||||
nop();
|
||||
fmov(Dst, TMP1);
|
||||
break;
|
||||
case 8:
|
||||
nop();
|
||||
ldar(TMP1, MemSrc);
|
||||
nop();
|
||||
fmov(Dst, TMP1);
|
||||
break;
|
||||
case 16:
|
||||
nop();
|
||||
ldaxp(TMP1, TMP2, MemSrc);
|
||||
clrex();
|
||||
mov(Dst.V2D(), 0, TMP1);
|
||||
mov(Dst.V2D(), 1, TMP2);
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(ParanoidStoreMemTSO) {
|
||||
auto Op = IROp->C<IR::IROp_StoreMemTSO>();
|
||||
auto MemSrc = MemOperand(GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
LOGMAN_MSG_A("StoreMemTSO: No offset allowed");
|
||||
}
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
if (Op->Size == 1) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
stlrb(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
|
||||
}
|
||||
else {
|
||||
nop();
|
||||
switch (Op->Size) {
|
||||
case 2:
|
||||
stlrh(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
|
||||
break;
|
||||
case 4:
|
||||
stlr(GetReg<RA_32>(Op->Header.Args[1].ID()), MemSrc);
|
||||
break;
|
||||
case 8:
|
||||
stlr(GetReg<RA_64>(Op->Header.Args[1].ID()), MemSrc);
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
nop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto Src = GetSrc(Op->Header.Args[1].ID());
|
||||
if (Op->Size == 1) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
mov(TMP1, Src.V4S(), 0);
|
||||
stlrb(TMP1, MemSrc);
|
||||
}
|
||||
else {
|
||||
switch (Op->Size) {
|
||||
case 2:
|
||||
mov(TMP1, Src.V4S(), 0);
|
||||
nop();
|
||||
stlrh(TMP1, MemSrc);
|
||||
nop();
|
||||
break;
|
||||
case 4:
|
||||
mov(TMP1, Src.V4S(), 0);
|
||||
nop();
|
||||
stlr(TMP1.W(), MemSrc);
|
||||
nop();
|
||||
break;
|
||||
case 8:
|
||||
mov(TMP1, Src.V2D(), 0);
|
||||
nop();
|
||||
stlr(TMP1, MemSrc);
|
||||
nop();
|
||||
break;
|
||||
case 16: {
|
||||
// Move vector to GPRs
|
||||
mov(TMP1, Src.V2D(), 0);
|
||||
mov(TMP2, Src.V2D(), 1);
|
||||
Label B;
|
||||
bind(&B);
|
||||
|
||||
nop(); // < Overwritten with DMB
|
||||
// ldaxp must not have both the destination registers be the same
|
||||
ldaxp(xzr, TMP3, MemSrc); // <- Can hit SIGBUS
|
||||
nop(); // < Overwritten with DMB
|
||||
stlxp(TMP3, TMP1, TMP2, MemSrc); // <- Can also hit SIGBUS
|
||||
cbnz(TMP3, &B); // < Overwritten with DMB
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VLoadMemElement) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VStoreMemElement) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClear) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClear>();
|
||||
|
||||
auto MemReg = GetReg<RA_64>(Op->Header.Args[0].ID());
|
||||
|
||||
// Clear dcache only
|
||||
// icache doesn't matter here since the guest application shouldn't be calling clflush on JIT code.
|
||||
mov(TMP1, MemReg);
|
||||
for (size_t i = 0; i < std::max(1U, DCacheLineSize / 64U); ++i) {
|
||||
dc(DataCacheOp::CVAU, TMP1);
|
||||
add(TMP1, TMP1, DCacheLineSize);
|
||||
}
|
||||
dsb(InnerShareable, BarrierAll);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMemoryHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterMemoryHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(LOADCONTEXT, LoadContext);
|
||||
REGISTER_OP(STORECONTEXT, StoreContext);
|
||||
REGISTER_OP(LOADREGISTER, LoadRegister);
|
||||
@@ -806,10 +969,17 @@ void JITCore::RegisterMemoryHandlers() {
|
||||
REGISTER_OP(STOREFLAG, StoreFlag);
|
||||
REGISTER_OP(LOADMEM, LoadMem);
|
||||
REGISTER_OP(STOREMEM, StoreMem);
|
||||
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
|
||||
if (ParanoidTSO()) {
|
||||
REGISTER_OP(LOADMEMTSO, ParanoidLoadMemTSO);
|
||||
REGISTER_OP(STOREMEMTSO, ParanoidStoreMemTSO);
|
||||
}
|
||||
else {
|
||||
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
|
||||
}
|
||||
REGISTER_OP(VLOADMEMELEMENT, VLoadMemElement);
|
||||
REGISTER_OP(VSTOREMEMELEMENT, VStoreMemElement);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+53
-10
@@ -1,10 +1,23 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
static void PrintValue(uint64_t Value) {
|
||||
LogMan::Msg::D("Value: 0x%lx", Value);
|
||||
}
|
||||
|
||||
static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
|
||||
LogMan::Msg::D("Value: 0x%016lx'%016lx", ValueUpper, Value);
|
||||
}
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(Fence) {
|
||||
auto Op = IROp->C<IR::IROp_Fence>();
|
||||
@@ -18,7 +31,7 @@ DEF_OP(Fence) {
|
||||
case IR::Fence_Store.Val:
|
||||
dmb(FullSystem, BarrierWrites);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Fence: %d", Op->Fence); break;
|
||||
default: LOGMAN_MSG_A("Unknown Fence: %d", Op->Fence); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,30 +42,38 @@ DEF_OP(Break) {
|
||||
case 5: // Guest ud2
|
||||
hlt(4);
|
||||
break;
|
||||
case 1: // Int <imm8>
|
||||
hlt(4);
|
||||
break;
|
||||
case 2: // overflow
|
||||
hlt(4);
|
||||
break;
|
||||
case 3: // int 1
|
||||
hlt(4);
|
||||
break;
|
||||
case 4: { // HLT
|
||||
// Time to quit
|
||||
// Set our stack to the starting stack location
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)));
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
|
||||
add(sp, TMP1, 0);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
LoadConstant(TMP1, ThreadStopHandlerAddressSpillSRA);
|
||||
LoadConstant(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddressSpillSRA);
|
||||
br(TMP1);
|
||||
break;
|
||||
}
|
||||
case 6: { // INT3
|
||||
ResetStack();
|
||||
|
||||
LoadConstant(TMP1, ThreadPauseHandlerAddressSpillSRA);
|
||||
LoadConstant(TMP1, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddressSpillSRA);
|
||||
br(TMP1);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);
|
||||
default: LOGMAN_MSG_A("Unknown Break reason: %d", Op->Reason);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(GetRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_GetRoundingMode>();
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
mrs(Dst, FPCR);
|
||||
lsr(Dst, Dst, 22);
|
||||
@@ -110,9 +131,31 @@ DEF_OP(SetRoundingMode) {
|
||||
msr(FPCR, TMP1);
|
||||
}
|
||||
|
||||
DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
|
||||
if (IsGPR(Op->Header.Args[0].ID())) {
|
||||
mov(x0, GetReg<RA_64>(Op->Header.Args[0].ID()));
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(PrintValue));
|
||||
}
|
||||
else {
|
||||
fmov(x0, GetSrc(Op->Header.Args[0].ID()).V1D());
|
||||
// Bug in vixl that source vector needs to b V1D rather than V2D?
|
||||
fmov(x1, GetSrc(Op->Header.Args[0].ID()).V1D(), 1);
|
||||
LoadConstant(x3, reinterpret_cast<uint64_t>(PrintVectorValue));
|
||||
}
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMiscHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterMiscHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(DUMMY, NoOp);
|
||||
REGISTER_OP(IRHEADER, NoOp);
|
||||
REGISTER_OP(CODEBLOCK, NoOp);
|
||||
@@ -122,7 +165,7 @@ void JITCore::RegisterMiscHandlers() {
|
||||
REGISTER_OP(BREAK, Break);
|
||||
REGISTER_OP(PHI, NoOp);
|
||||
REGISTER_OP(PHIVALUE, NoOp);
|
||||
REGISTER_OP(PRINT, Unhandled);
|
||||
REGISTER_OP(PRINT, Print);
|
||||
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
|
||||
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
|
||||
REGISTER_OP(INVALIDATEFLAGS, NoOp);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(ExtractElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
|
||||
switch (Op->Header.Size) {
|
||||
@@ -20,7 +26,7 @@ DEF_OP(ExtractElementPair) {
|
||||
mov (GetReg<RA_64>(Node), Regs[Op->Element]);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Size"); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size"); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +52,7 @@ DEF_OP(CreateElementPair) {
|
||||
RegTmp = x0;
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Size"); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size"); break;
|
||||
}
|
||||
|
||||
if (Dst.first.GetCode() != RegSecond.GetCode()) {
|
||||
@@ -68,8 +74,8 @@ DEF_OP(Mov) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
|
||||
+162
-121
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|arm64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(VectorZero) {
|
||||
uint8_t OpSize = IROp->Size;
|
||||
switch (OpSize) {
|
||||
@@ -16,7 +22,7 @@ DEF_OP(VectorZero) {
|
||||
eor(GetDst(Node).V16B(), GetDst(Node).V16B(), GetDst(Node).V16B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +32,28 @@ DEF_OP(VectorImm) {
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
// movi with 64bit element size doesn't do what we want here
|
||||
LoadConstant(TMP1.X(), Op->Immediate);
|
||||
dup(GetDst(Node).V2D(), TMP1.X());
|
||||
}
|
||||
else {
|
||||
movi(GetDst(Node).VCast(OpSize * 8, Elements), Op->Immediate);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CreateVector2) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(CreateVector4) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(SplatVector2) {
|
||||
auto Op = IROp->C<IR::IROp_SplatVector2>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
|
||||
uint8_t ElementSize = OpSize / 2;
|
||||
|
||||
@@ -51,14 +64,14 @@ DEF_OP(SplatVector2) {
|
||||
case 8:
|
||||
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(SplatVector4) {
|
||||
auto Op = IROp->C<IR::IROp_SplatVector4>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
|
||||
uint8_t ElementSize = OpSize / 4;
|
||||
|
||||
@@ -69,7 +82,7 @@ DEF_OP(SplatVector4) {
|
||||
case 8:
|
||||
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +118,7 @@ DEF_OP(VMov) {
|
||||
mov(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +156,7 @@ DEF_OP(VAdd) {
|
||||
add(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +179,7 @@ DEF_OP(VSub) {
|
||||
sub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +202,7 @@ DEF_OP(VUQAdd) {
|
||||
uqadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +225,7 @@ DEF_OP(VUQSub) {
|
||||
uqsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +248,7 @@ DEF_OP(VSQAdd) {
|
||||
sqadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +271,7 @@ DEF_OP(VSQSub) {
|
||||
sqsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +293,7 @@ DEF_OP(VAddP) {
|
||||
addp(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -301,7 +314,7 @@ DEF_OP(VAddP) {
|
||||
addp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,7 +333,7 @@ DEF_OP(VAddV) {
|
||||
case 8:
|
||||
addp(GetDst(Node).VCast(OpSize * 8, 1), GetSrc(Op->Header.Args[0].ID()).VCast(OpSize * 8, Elements));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +348,7 @@ DEF_OP(VURAvg) {
|
||||
urhadd(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +363,7 @@ DEF_OP(VAbs) {
|
||||
abs(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -362,7 +375,7 @@ DEF_OP(VAbs) {
|
||||
case 8:
|
||||
abs(GetDst(Node).VCast(OpSize * 8, Elements), GetSrc(Op->Header.Args[0].ID()).VCast(OpSize * 8, Elements));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,7 +394,7 @@ DEF_OP(VFAdd) {
|
||||
fadd(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -395,7 +408,7 @@ DEF_OP(VFAdd) {
|
||||
fadd(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,7 +424,7 @@ DEF_OP(VFAddP) {
|
||||
faddp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +442,7 @@ DEF_OP(VFSub) {
|
||||
fsub(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -443,7 +456,7 @@ DEF_OP(VFSub) {
|
||||
fsub(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,7 +475,7 @@ DEF_OP(VFMul) {
|
||||
fmul(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -476,7 +489,7 @@ DEF_OP(VFMul) {
|
||||
fmul(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,7 +508,7 @@ DEF_OP(VFDiv) {
|
||||
fdiv(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -509,7 +522,7 @@ DEF_OP(VFDiv) {
|
||||
fdiv(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -521,28 +534,36 @@ DEF_OP(VFMin) {
|
||||
// Scalar
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4: {
|
||||
fmin(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
|
||||
fcmp(GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
|
||||
fcsel(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S(), Condition::mi);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fmin(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
fcsel(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D(), Condition::mi);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Vector
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4: {
|
||||
fmin(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
fcmgt(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
mov(VTMP2.V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V4S(), VTMP2.V4S());
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fmin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
fcmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -554,28 +575,36 @@ DEF_OP(VFMax) {
|
||||
// Scalar
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4: {
|
||||
fmax(GetDst(Node).S(), GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
|
||||
fcmp(GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S());
|
||||
fcsel(GetDst(Node).S(), GetSrc(Op->Header.Args[1].ID()).S(), GetSrc(Op->Header.Args[0].ID()).S(), Condition::mi);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fmax(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
fcsel(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D(), Condition::mi);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Vector
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4: {
|
||||
fmax(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
fcmgt(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
mov(VTMP2.V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V4S(), VTMP2.V4S());
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
fmax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
fcmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,7 +625,7 @@ DEF_OP(VFRecp) {
|
||||
fdiv(GetDst(Node).D(), VTMP1.D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -612,7 +641,7 @@ DEF_OP(VFRecp) {
|
||||
fdiv(GetDst(Node).V2D(), VTMP1.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,7 +660,7 @@ DEF_OP(VFSqrt) {
|
||||
fsqrt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -645,7 +674,7 @@ DEF_OP(VFSqrt) {
|
||||
fsqrt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -668,7 +697,7 @@ DEF_OP(VFRSqrt) {
|
||||
fdiv(GetDst(Node).D(), VTMP1.D(), VTMP2.D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -686,7 +715,7 @@ DEF_OP(VFRSqrt) {
|
||||
fdiv(GetDst(Node).V2D(), VTMP1.V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -707,7 +736,7 @@ DEF_OP(VNeg) {
|
||||
case 8:
|
||||
neg(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,7 +750,7 @@ DEF_OP(VFNeg) {
|
||||
case 8:
|
||||
fneg(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported Not size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported Not size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,10 +775,13 @@ DEF_OP(VUMin) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
umin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
cmhi(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,10 +801,13 @@ DEF_OP(VSMin) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
smin(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
cmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bif(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -792,10 +827,13 @@ DEF_OP(VUMax) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
umax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
cmhi(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,10 +853,13 @@ DEF_OP(VSMax) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
smax(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
cmgt(VTMP1.V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
mov(VTMP2.V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
bit(VTMP2.V16B(), GetSrc(Op->Header.Args[1].ID()).V16B(), VTMP1.V16B());
|
||||
mov(GetDst(Node).V2D(), VTMP2.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,7 +880,7 @@ DEF_OP(VZip) {
|
||||
zip1(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -860,7 +901,7 @@ DEF_OP(VZip) {
|
||||
zip1(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -882,7 +923,7 @@ DEF_OP(VZip2) {
|
||||
zip2(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -903,7 +944,7 @@ DEF_OP(VZip2) {
|
||||
zip2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -936,7 +977,7 @@ DEF_OP(VCMPEQ) {
|
||||
cmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -958,7 +999,7 @@ DEF_OP(VCMPEQ) {
|
||||
cmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -977,7 +1018,7 @@ DEF_OP(VCMPEQZ) {
|
||||
cmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -999,7 +1040,7 @@ DEF_OP(VCMPEQZ) {
|
||||
cmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1018,7 +1059,7 @@ DEF_OP(VCMPGT) {
|
||||
cmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1040,7 +1081,7 @@ DEF_OP(VCMPGT) {
|
||||
cmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1059,7 +1100,7 @@ DEF_OP(VCMPGTZ) {
|
||||
cmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1081,7 +1122,7 @@ DEF_OP(VCMPGTZ) {
|
||||
cmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1100,7 +1141,7 @@ DEF_OP(VCMPLTZ) {
|
||||
cmlt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1122,7 +1163,7 @@ DEF_OP(VCMPLTZ) {
|
||||
cmlt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1141,7 +1182,7 @@ DEF_OP(VFCMPEQ) {
|
||||
fcmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1159,7 +1200,7 @@ DEF_OP(VFCMPEQ) {
|
||||
fcmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1178,7 +1219,7 @@ DEF_OP(VFCMPNEQ) {
|
||||
fcmeq(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
mvn(GetDst(Node).V8B(), GetDst(Node).V8B());
|
||||
}
|
||||
@@ -1197,7 +1238,7 @@ DEF_OP(VFCMPNEQ) {
|
||||
fcmeq(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
mvn(GetDst(Node).V16B(), GetDst(Node).V16B());
|
||||
}
|
||||
@@ -1217,7 +1258,7 @@ DEF_OP(VFCMPLT) {
|
||||
fcmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1235,7 +1276,7 @@ DEF_OP(VFCMPLT) {
|
||||
fcmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1254,7 +1295,7 @@ DEF_OP(VFCMPGT) {
|
||||
fcmgt(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1272,7 +1313,7 @@ DEF_OP(VFCMPGT) {
|
||||
fcmgt(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1291,7 +1332,7 @@ DEF_OP(VFCMPLE) {
|
||||
fcmge(GetDst(Node).D(), GetSrc(Op->Header.Args[1].ID()).D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1309,7 +1350,7 @@ DEF_OP(VFCMPLE) {
|
||||
fcmge(GetDst(Node).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1332,7 +1373,7 @@ DEF_OP(VFCMPORD) {
|
||||
orr(GetDst(Node).V8B(), VTMP1.V8B(), VTMP2.V8B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1356,7 +1397,7 @@ DEF_OP(VFCMPORD) {
|
||||
orr(GetDst(Node).V16B(), VTMP1.V16B(), VTMP2.V16B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1381,7 +1422,7 @@ DEF_OP(VFCMPUNO) {
|
||||
mvn(GetDst(Node).V8B(), GetDst(Node).V8B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1408,21 +1449,21 @@ DEF_OP(VFCMPUNO) {
|
||||
mvn(GetDst(Node).V16B(), GetDst(Node).V16B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUShl) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShr) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VSShr) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShlS) {
|
||||
@@ -1449,7 +1490,7 @@ DEF_OP(VUShlS) {
|
||||
ushl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,7 +1522,7 @@ DEF_OP(VUShrS) {
|
||||
ushl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,13 +1554,13 @@ DEF_OP(VSShrS) {
|
||||
sshl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), VTMP1.V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VInsElement) {
|
||||
auto Op = IROp->C<IR::IROp_VInsElement>();
|
||||
|
||||
|
||||
auto reg = GetSrc(Op->Header.Args[0].ID());
|
||||
|
||||
if (GetDst(Node).GetCode() != reg.GetCode()) {
|
||||
@@ -1544,7 +1585,7 @@ DEF_OP(VInsElement) {
|
||||
mov(reg.V2D(), Op->DestIdx, GetSrc(Op->Header.Args[1].ID()).V2D(), Op->SrcIdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
if (GetDst(Node).GetCode() != reg.GetCode()) {
|
||||
@@ -1554,7 +1595,7 @@ DEF_OP(VInsElement) {
|
||||
|
||||
DEF_OP(VInsScalarElement) {
|
||||
auto Op = IROp->C<IR::IROp_VInsScalarElement>();
|
||||
|
||||
|
||||
auto reg = GetSrc(Op->Header.Args[0].ID());
|
||||
|
||||
if (GetDst(Node).GetCode() != reg.GetCode()) {
|
||||
@@ -1579,7 +1620,7 @@ DEF_OP(VInsScalarElement) {
|
||||
mov(reg.V2D(), Op->DestIdx, GetSrc(Op->Header.Args[1].ID()).V2D(), 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
if (GetDst(Node).GetCode() != reg.GetCode()) {
|
||||
@@ -1603,7 +1644,7 @@ DEF_OP(VExtractElement) {
|
||||
case 8:
|
||||
mov(GetDst(Node).D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Index);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled ExtractElementSize: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled ExtractElementSize: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1718,7 +1759,7 @@ DEF_OP(VUShrI) {
|
||||
ushr(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1743,7 +1784,7 @@ DEF_OP(VSShrI) {
|
||||
sshr(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), std::min((uint8_t)(Op->Header.ElementSize * 8 - 1), Op->BitShift));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1771,7 +1812,7 @@ DEF_OP(VShlI) {
|
||||
shl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1792,7 +1833,7 @@ DEF_OP(VUShrNI) {
|
||||
shrn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1812,7 +1853,7 @@ DEF_OP(VUShrNI2) {
|
||||
shrn2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D(), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
mov(GetDst(Node), VTMP1);
|
||||
@@ -1835,7 +1876,7 @@ DEF_OP(VSXTL) {
|
||||
case 8:
|
||||
sxtl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1851,7 +1892,7 @@ DEF_OP(VSXTL2) {
|
||||
case 8:
|
||||
sxtl2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1867,7 +1908,7 @@ DEF_OP(VUXTL) {
|
||||
case 8:
|
||||
uxtl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1883,7 +1924,7 @@ DEF_OP(VUXTL2) {
|
||||
case 8:
|
||||
uxtl2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1899,7 +1940,7 @@ DEF_OP(VSQXTN) {
|
||||
case 4:
|
||||
sqxtn(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1921,7 +1962,7 @@ DEF_OP(VSQXTN2) {
|
||||
sqxtn(VTMP2.V2S(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
ins(VTMP1.V4S(), 1, VTMP2.V4S(), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1935,7 +1976,7 @@ DEF_OP(VSQXTN2) {
|
||||
case 4:
|
||||
sqxtn2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
mov(GetDst(Node), VTMP1);
|
||||
@@ -1953,7 +1994,7 @@ DEF_OP(VSQXTUN) {
|
||||
case 4:
|
||||
sqxtun(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1975,7 +2016,7 @@ DEF_OP(VSQXTUN2) {
|
||||
sqxtun(VTMP2.V2S(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
ins(VTMP1.V4S(), 1, VTMP2.V4S(), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1989,7 +2030,7 @@ DEF_OP(VSQXTUN2) {
|
||||
case 4:
|
||||
sqxtun2(VTMP1.V4S(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
mov(GetDst(Node), VTMP1);
|
||||
@@ -2014,7 +2055,7 @@ DEF_OP(VMul) {
|
||||
mul(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2033,7 +2074,7 @@ DEF_OP(VUMull) {
|
||||
umull(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2052,7 +2093,7 @@ DEF_OP(VSMull) {
|
||||
smull(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2071,7 +2112,7 @@ DEF_OP(VUMull2) {
|
||||
umull2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2090,7 +2131,7 @@ DEF_OP(VSMull2) {
|
||||
smull2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2107,13 +2148,13 @@ DEF_OP(VTBL1) {
|
||||
tbl(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterVectorHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void Arm64JITCore::RegisterVectorHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(CREATEVECTOR2, CreateVector2);
|
||||
|
||||
+2
-1
@@ -11,5 +11,6 @@ struct InternalThreadState;
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateJITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
}
|
||||
+44
-38
@@ -1,8 +1,14 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(TruncElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_TruncElementPair>();
|
||||
|
||||
@@ -14,7 +20,7 @@ DEF_OP(TruncElementPair) {
|
||||
mov(Dst.second, Src.second);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled Truncation size: %d", Op->Size); break;
|
||||
default: LOGMAN_MSG_A("Unhandled Truncation size: %d", Op->Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +32,7 @@ DEF_OP(Constant) {
|
||||
DEF_OP(EntrypointOffset) {
|
||||
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
|
||||
|
||||
auto Constant = IR->GetHeader()->Entry + Op->Offset;
|
||||
auto Constant = Entry + Op->Offset;
|
||||
mov(GetDst<RA_64>(Node), Constant);
|
||||
}
|
||||
|
||||
@@ -64,7 +70,7 @@ DEF_OP(Add) {
|
||||
case 8:
|
||||
add(rax, Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled Add size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Add size: %d", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -75,7 +81,7 @@ DEF_OP(Add) {
|
||||
case 8:
|
||||
add(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled Add size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Add size: %d", OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +103,7 @@ DEF_OP(Sub) {
|
||||
case 8:
|
||||
sub(rax, Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled Sub size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Sub size: %d", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -108,7 +114,7 @@ DEF_OP(Sub) {
|
||||
case 8:
|
||||
sub(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled Sub size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Sub size: %d", OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +136,7 @@ DEF_OP(Neg) {
|
||||
Src = GetSrc<RA_64>(Op->Header.Args[0].ID());
|
||||
Dst = GetDst<RA_64>(Node);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled Neg size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled Neg size: %d", OpSize);
|
||||
break;
|
||||
}
|
||||
mov(Dst, Src);
|
||||
@@ -154,7 +160,7 @@ DEF_OP(Mul) {
|
||||
imul(rax, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(Dst, rax);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +179,7 @@ DEF_OP(UMul) {
|
||||
mul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +218,7 @@ DEF_OP(Div) {
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UDIV Size: %d", Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +261,7 @@ DEF_OP(UDiv) {
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UDIV OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown UDIV OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +298,7 @@ DEF_OP(Rem) {
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UDIV Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown UDIV Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +341,7 @@ DEF_OP(URem) {
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown UDIV OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown UDIV OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +360,7 @@ DEF_OP(MulH) {
|
||||
imul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +379,7 @@ DEF_OP(UMulH) {
|
||||
mul(GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Sext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown Sext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +441,7 @@ DEF_OP(Lshl) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
shl(GetDst<RA_64>(Node), Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown LSHL Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
|
||||
};
|
||||
} else {
|
||||
mov(rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
@@ -450,7 +456,7 @@ DEF_OP(Lshl) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
shl(GetDst<RA_64>(Node), cl);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown LSHL Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -482,7 +488,7 @@ DEF_OP(Lshr) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
shr(GetDst<RA_64>(Node), Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size: %d\n", OpSize); break;
|
||||
};
|
||||
|
||||
} else {
|
||||
@@ -506,7 +512,7 @@ DEF_OP(Lshr) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
shr(GetDst<RA_64>(Node), cl);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size: %d\n", OpSize); break;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -540,7 +546,7 @@ DEF_OP(Ashr) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
sar(GetDst<RA_64>(Node), Const);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown ASHR Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown ASHR Size: %d\n", OpSize); break;
|
||||
};
|
||||
|
||||
} else {
|
||||
@@ -565,7 +571,7 @@ DEF_OP(Ashr) {
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
sar(GetDst<RA_64>(Node), cl);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown ASHR Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown ASHR Size: %d\n", OpSize); break;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -590,7 +596,7 @@ DEF_OP(Ror) {
|
||||
ror(rax, Const);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown ROR Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown ROR Size: %d\n", OpSize); break;
|
||||
}
|
||||
} else {
|
||||
mov (rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
@@ -606,7 +612,7 @@ DEF_OP(Ror) {
|
||||
ror(rax, cl);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown ROR Size: %d\n", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown ROR Size: %d\n", OpSize); break;
|
||||
}
|
||||
}
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
@@ -662,7 +668,7 @@ DEF_OP(LDiv) {
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LDIV OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LDIV OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,7 +700,7 @@ DEF_OP(LUDiv) {
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LUDIV OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LUDIV OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +732,7 @@ DEF_OP(LRem) {
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LREM OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LREM OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,7 +764,7 @@ DEF_OP(LURem) {
|
||||
mov(GetDst<RA_64>(Node), rdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown LUDIV OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown LUDIV OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,7 +829,7 @@ DEF_OP(FindMSB) {
|
||||
case 8:
|
||||
bsr(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +853,7 @@ DEF_OP(FindTrailingZeros) {
|
||||
mov(rax, 0x40);
|
||||
cmovz(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,7 +876,7 @@ DEF_OP(CountLeadingZeroes) {
|
||||
lzcnt(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -909,7 +915,7 @@ DEF_OP(CountLeadingZeroes) {
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -931,7 +937,7 @@ DEF_OP(Rev) {
|
||||
mov (GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
bswap(GetDst<RA_64>(Node).cvt64());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown REV size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,7 +972,7 @@ DEF_OP(Bfe) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
LogMan::Throw::A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
|
||||
LOGMAN_THROW_A(OpSize <= 8, "OpSize is too large for BFE: %d", OpSize);
|
||||
|
||||
auto Dst = GetDst<RA_64>(Node);
|
||||
|
||||
@@ -1067,7 +1073,7 @@ DEF_OP(Select) {
|
||||
|
||||
if (is_const_true || is_const_false) {
|
||||
if (is_const_false != true || is_const_true != true || const_true != 1 || const_false != 0) {
|
||||
LogMan::Msg::A("Select: Unsupported compare inline parameters");
|
||||
LOGMAN_MSG_A("Select: Unsupported compare inline parameters");
|
||||
}
|
||||
(this->*SetCC)(al);
|
||||
movzx(Dst, al);
|
||||
@@ -1098,7 +1104,7 @@ DEF_OP(VExtractToGPR) {
|
||||
pextrq(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()), Op->Idx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1168,8 +1174,8 @@ DEF_OP(FCmp) {
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
void JITCore::RegisterALUHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterALUHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
|
||||
REGISTER_OP(CONSTANT, Constant);
|
||||
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
|
||||
|
||||
+38
-33
@@ -1,8 +1,14 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(CASPair) {
|
||||
auto Op = IROp->C<IR::IROp_CAS>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -49,7 +55,7 @@ DEF_OP(CASPair) {
|
||||
mov(Dst.second, rdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unsupported: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +74,6 @@ DEF_OP(CAS) {
|
||||
|
||||
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Header.Args[2].ID());
|
||||
|
||||
mov(rdx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
// RCX now contains pointer
|
||||
@@ -76,31 +81,31 @@ DEF_OP(CAS) {
|
||||
// RDX contains our desired
|
||||
|
||||
lock();
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
cmpxchg(byte [MemReg], dl);
|
||||
movzx(rax, al);
|
||||
cmpxchg(byte [MemReg], GetSrc<RA_8>(Op->Header.Args[1].ID()));
|
||||
movzx(GetDst<RA_64>(Node), al);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
cmpxchg(word [MemReg], dx);
|
||||
movzx(rax, ax);
|
||||
cmpxchg(word [MemReg], GetSrc<RA_16>(Op->Header.Args[1].ID()));
|
||||
movzx(GetDst<RA_64>(Node), ax);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
cmpxchg(dword [MemReg], edx);
|
||||
cmpxchg(dword [MemReg], GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
// RAX now contains the result
|
||||
mov (GetDst<RA_64>(Node), eax);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
cmpxchg(qword [MemReg], rdx);
|
||||
cmpxchg(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
// RAX now contains the result
|
||||
mov (GetDst<RA_64>(Node), rax);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unsupported: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unsupported: %d", OpSize);
|
||||
}
|
||||
|
||||
// RAX now contains the result
|
||||
mov (GetDst<RA_64>(Node), rax);
|
||||
}
|
||||
|
||||
DEF_OP(AtomicAdd) {
|
||||
@@ -122,7 +127,7 @@ DEF_OP(AtomicAdd) {
|
||||
case 8:
|
||||
add(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +149,7 @@ DEF_OP(AtomicSub) {
|
||||
case 8:
|
||||
sub(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +171,7 @@ DEF_OP(AtomicAnd) {
|
||||
case 8:
|
||||
and_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +193,7 @@ DEF_OP(AtomicOr) {
|
||||
case 8:
|
||||
or_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +215,7 @@ DEF_OP(AtomicXor) {
|
||||
case 8:
|
||||
xor_(qword [MemReg], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,17 +227,17 @@ DEF_OP(AtomicSwap) {
|
||||
|
||||
switch (Op->Size) {
|
||||
case 1:
|
||||
mov(GetDst<RA_8>(Node), GetSrc<RA_8>(Op->Header.Args[1].ID()));
|
||||
movzx(GetDst<RA_64>(Node), GetSrc<RA_8>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xchg(byte [MemReg], GetDst<RA_8>(Node));
|
||||
break;
|
||||
case 2:
|
||||
mov(GetDst<RA_16>(Node), GetSrc<RA_16>(Op->Header.Args[1].ID()));
|
||||
movzx(GetDst<RA_64>(Node), GetSrc<RA_16>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xchg(word [MemReg], GetDst<RA_16>(Node));
|
||||
break;
|
||||
case 4:
|
||||
mov(GetDst<RA_32>(Node), GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xchg(dword [MemReg], GetDst<RA_32>(Node));
|
||||
break;
|
||||
@@ -241,7 +246,7 @@ DEF_OP(AtomicSwap) {
|
||||
lock();
|
||||
xchg(qword [MemReg], GetDst<RA_64>(Node));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,13 +256,13 @@ DEF_OP(AtomicFetchAdd) {
|
||||
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Header.Args[0].ID());
|
||||
switch (Op->Size) {
|
||||
case 1:
|
||||
mov(cl, GetSrc<RA_8>(Op->Header.Args[1].ID()));
|
||||
movzx(rcx, GetSrc<RA_8>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xadd(byte [MemReg], cl);
|
||||
movzx(GetDst<RA_32>(Node), cl);
|
||||
break;
|
||||
case 2:
|
||||
mov(cx, GetSrc<RA_16>(Op->Header.Args[1].ID()));
|
||||
movzx(rcx, GetSrc<RA_16>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xadd(word [MemReg], cx);
|
||||
movzx(GetDst<RA_32>(Node), cx);
|
||||
@@ -266,7 +271,7 @@ DEF_OP(AtomicFetchAdd) {
|
||||
mov(ecx, GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
lock();
|
||||
xadd(dword [MemReg], ecx);
|
||||
mov(GetDst<RA_32>(Node), ecx);
|
||||
mov(GetDst<RA_64>(Node), ecx);
|
||||
break;
|
||||
case 8:
|
||||
mov(rcx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
@@ -274,7 +279,7 @@ DEF_OP(AtomicFetchAdd) {
|
||||
xadd(qword [MemReg], rcx);
|
||||
mov(GetDst<RA_64>(Node), rcx);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,7 +316,7 @@ DEF_OP(AtomicFetchSub) {
|
||||
xadd(qword [MemReg], rcx);
|
||||
mov(GetDst<RA_64>(Node), rcx);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +394,7 @@ DEF_OP(AtomicFetchAnd) {
|
||||
mov(GetDst<RA_64>(Node), TMP3.cvt64());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +471,7 @@ DEF_OP(AtomicFetchOr) {
|
||||
mov(GetDst<RA_64>(Node), TMP3.cvt64());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,13 +548,13 @@ DEF_OP(AtomicFetchXor) {
|
||||
mov(GetDst<RA_64>(Node), TMP3.cvt64());
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled AtomicFetchAdd size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterAtomicHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterAtomicHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(CASPAIR, CASPair);
|
||||
REGISTER_OP(CAS, CAS);
|
||||
REGISTER_OP(ATOMICADD, AtomicAdd);
|
||||
|
||||
+21
-16
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
@@ -6,7 +12,7 @@
|
||||
#include <Interface/HLE/Thunks/Thunks.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(GuestCallDirect) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
}
|
||||
@@ -40,7 +46,7 @@ DEF_OP(CallbackReturn) {
|
||||
sub(dword [rax], 1);
|
||||
|
||||
// We need to adjust an additional 8 bytes to get back to the original "misaligned" RSP state
|
||||
add(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 8);
|
||||
add(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSP])], 8);
|
||||
|
||||
// Now jump back to the thunk
|
||||
// XXX: XMM?
|
||||
@@ -75,12 +81,12 @@ DEF_OP(ExitFunction) {
|
||||
jmp(qword[rax]);
|
||||
|
||||
L(l_BranchHost);
|
||||
dq(ExitFunctionLinkerAddress);
|
||||
dq(ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress);
|
||||
L(l_BranchGuest);
|
||||
dq(NewRIP);
|
||||
} else {
|
||||
Xbyak::Reg RipReg = GetSrc<RA_64>(Op->NewRIP.ID());
|
||||
|
||||
|
||||
// L1 Cache
|
||||
mov(rcx, ThreadState->LookupCache->GetL1Pointer());
|
||||
mov(rax, RipReg);
|
||||
@@ -89,14 +95,14 @@ DEF_OP(ExitFunction) {
|
||||
shl(rax, 4);
|
||||
|
||||
Xbyak::RegExp LookupBase = rcx + rax;
|
||||
|
||||
|
||||
cmp(qword[LookupBase + 8], RipReg);
|
||||
jne(FullLookup);
|
||||
jmp(qword[LookupBase + 0]);
|
||||
|
||||
L(FullLookup);
|
||||
mov(rax, AbsoluteLoopTopAddress);
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], RipReg);
|
||||
mov(rax, ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress);
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, State.rip)], RipReg);
|
||||
jmp(rax);
|
||||
}
|
||||
|
||||
@@ -227,7 +233,7 @@ DEF_OP(Thunk) {
|
||||
sub(rsp, 8); // Align
|
||||
|
||||
mov(rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
|
||||
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
|
||||
@@ -247,7 +253,7 @@ DEF_OP(ValidateCode) {
|
||||
int idx = 0;
|
||||
|
||||
xor_(GetDst<RA_64>(Node), GetDst<RA_64>(Node));
|
||||
mov(rax, IR->GetHeader()->Entry + Op->Offset);
|
||||
mov(rax, Entry + Op->Offset);
|
||||
mov(rbx, 1);
|
||||
while (len >= 4) {
|
||||
cmp(dword[rax + idx], *(uint32_t*)(OldCode + idx));
|
||||
@@ -271,8 +277,6 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
|
||||
DEF_OP(RemoveCodeEntry) {
|
||||
auto Op = IROp->C<IR::IROp_RemoveCodeEntry>();
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
|
||||
for (auto &Reg : RA64)
|
||||
@@ -282,11 +286,11 @@ DEF_OP(RemoveCodeEntry) {
|
||||
sub(rsp, 8); // Align
|
||||
|
||||
mov(rdi, STATE);
|
||||
mov(rax, IR->GetHeader()->Entry); // imm64 move
|
||||
mov(rax, Entry); // imm64 move
|
||||
mov(rsi, rax);
|
||||
|
||||
|
||||
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntry));
|
||||
mov(rax, reinterpret_cast<uintptr_t>(&Context::Context::RemoveCodeEntryFromJit));
|
||||
call(rax);
|
||||
|
||||
if (NumPush & 1)
|
||||
@@ -299,7 +303,7 @@ DEF_OP(RemoveCodeEntry) {
|
||||
DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
|
||||
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function);
|
||||
using ClassPtrType = FEXCore::CPUID::FunctionResults (FEXCore::CPUIDEmu::*)(uint32_t Function, uint32_t Leaf);
|
||||
union {
|
||||
ClassPtrType ClassPtr;
|
||||
uint64_t Raw;
|
||||
@@ -316,6 +320,7 @@ DEF_OP(CPUID) {
|
||||
// Result: RAX, RDX. 4xi32
|
||||
|
||||
mov (rsi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov (rdx, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov (rdi, reinterpret_cast<uint64_t>(&CTX->CPUID));
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
@@ -341,8 +346,8 @@ DEF_OP(CPUID) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(GUESTCALLDIRECT, GuestCallDirect);
|
||||
REGISTER_OP(GUESTCALLINDIRECT, GuestCallIndirect);
|
||||
REGISTER_OP(GUESTRETURN, GuestReturn);
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(VInsGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VInsGPR>();
|
||||
movapd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
@@ -25,7 +31,7 @@ DEF_OP(VInsGPR) {
|
||||
pinsrq(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[1].ID()), Op->Index);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +52,12 @@ DEF_OP(VCastFromGPR) {
|
||||
case 8:
|
||||
vmovq(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()).cvt64());
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_U) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
@@ -89,12 +95,12 @@ DEF_OP(Float_FToF) {
|
||||
cvtsd2ss(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown FCVT sizes: 0x%x", Conv);
|
||||
default: LOGMAN_MSG_A("Unknown FCVT sizes: 0x%x", Conv);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_UToF) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_SToF) {
|
||||
@@ -115,12 +121,12 @@ DEF_OP(Vector_SToF) {
|
||||
cvtsi2sd(xmm15, rax);
|
||||
movlhps(GetDst(Node), xmm15);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZU) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZS) {
|
||||
@@ -132,12 +138,12 @@ DEF_OP(Vector_FToZS) {
|
||||
case 8:
|
||||
cvttpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToU) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToS) {
|
||||
@@ -149,7 +155,7 @@ DEF_OP(Vector_FToS) {
|
||||
case 8:
|
||||
cvtpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,13 +172,13 @@ DEF_OP(Vector_FToF) {
|
||||
cvtpd2ps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Conversion Type : 0%04x", Conv); break;
|
||||
default: LOGMAN_MSG_A("Unknown Conversion Type : 0%04x", Conv); break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(AESImc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESImc>();
|
||||
@@ -35,8 +41,8 @@ DEF_OP(AESKeyGenAssist) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterEncryptionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterEncryptionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(VAESIMC, AESImc);
|
||||
REGISTER_OP(VAESENC, AESEnc);
|
||||
REGISTER_OP(VAESENCLAST, AESEncLast);
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(GetHostFlag) {
|
||||
auto Op = IROp->C<IR::IROp_GetHostFlag>();
|
||||
|
||||
@@ -14,8 +20,8 @@ DEF_OP(GetHostFlag) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterFlagHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterFlagHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
+85
-620
@@ -1,5 +1,13 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
desc: Main glue logic of the x86-64 splatter backend
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include "Interface/Core/Dispatcher/X86Dispatcher.h"
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
|
||||
@@ -7,6 +15,7 @@
|
||||
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Core/UContext.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <signal.h>
|
||||
@@ -22,324 +31,29 @@ CodeBuffer AllocateNewCodeBuffer(size_t Size) {
|
||||
CodeBuffer Buffer;
|
||||
Buffer.Size = Size;
|
||||
Buffer.Ptr = static_cast<uint8_t*>(
|
||||
mmap(nullptr,
|
||||
FEXCore::Allocator::mmap(nullptr,
|
||||
Buffer.Size,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0));
|
||||
LogMan::Throw::A(Buffer.Ptr != reinterpret_cast<uint8_t*>(~0ULL), "Couldn't allocate code buffer");
|
||||
LOGMAN_THROW_A(Buffer.Ptr != reinterpret_cast<uint8_t*>(~0ULL), "Couldn't allocate code buffer");
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
void FreeCodeBuffer(CodeBuffer Buffer) {
|
||||
munmap(Buffer.Ptr, Buffer.Size);
|
||||
FEXCore::Allocator::munmap(Buffer.Ptr, Buffer.Size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
struct ContextBackup {
|
||||
uint64_t StoredCookie;
|
||||
// Host State
|
||||
// RIP and RSP is stored in GPRs here
|
||||
uint64_t GPRs[NGREG];
|
||||
_libc_fpstate FPRState;
|
||||
|
||||
// Guest state
|
||||
int Signal;
|
||||
FEXCore::Core::CPUState GuestState;
|
||||
};
|
||||
|
||||
void JITCore::StoreThreadState(int Signal, void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// We can end up getting a signal at any point in our host state
|
||||
// Jump to a handler that saves all state so we can safely return
|
||||
uint64_t OldSP = _mcontext->gregs[REG_RSP];
|
||||
uintptr_t NewSP = OldSP;
|
||||
|
||||
size_t StackOffset = sizeof(ContextBackup);
|
||||
|
||||
// We need to back up behind the host's red zone
|
||||
// We do this on the guest side as well
|
||||
NewSP -= 128;
|
||||
NewSP -= StackOffset;
|
||||
NewSP = AlignDown(NewSP, 16);
|
||||
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
|
||||
Context->StoredCookie = 0x4142434445464748ULL;
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&Context->GPRs[0], &_mcontext->gregs[0], NGREG * sizeof(_mcontext->gregs[0]));
|
||||
// Copy the FPRState
|
||||
memcpy(&Context->FPRState, _mcontext->fpregs, sizeof(_libc_fpstate));
|
||||
|
||||
// XXX: Save 256bit and 512bit AVX register state
|
||||
|
||||
// Retain the action pointer so we can see it when we return
|
||||
Context->Signal = Signal;
|
||||
|
||||
// Save guest state
|
||||
// We can't guarantee if registers are in context or host GPRs
|
||||
// So we need to save everything
|
||||
memcpy(&Context->GuestState, &ThreadState->State, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Set the new SP
|
||||
_mcontext->gregs[REG_RSP] = NewSP;
|
||||
|
||||
SignalFrames.push(NewSP);
|
||||
}
|
||||
|
||||
void JITCore::RestoreThreadState(void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
uint64_t OldSP = SignalFrames.top();
|
||||
SignalFrames.pop();
|
||||
uintptr_t NewSP = OldSP;
|
||||
ContextBackup *Context = reinterpret_cast<ContextBackup*>(NewSP);
|
||||
|
||||
if (Context->StoredCookie != 0x4142434445464748ULL) {
|
||||
LogMan::Msg::D("COOKIE WAS NOT CORRECT!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// First thing, reset the guest state
|
||||
memcpy(&ThreadState->State, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Now restore host state
|
||||
|
||||
// Copy the GPRs
|
||||
memcpy(&_mcontext->gregs[0], &Context->GPRs[0], NGREG * sizeof(_mcontext->gregs[0]));
|
||||
// Copy the FPRState
|
||||
memcpy(_mcontext->fpregs, &Context->FPRState, sizeof(_libc_fpstate));
|
||||
|
||||
// Restore the previous signal state
|
||||
// This allows recursive signals to properly handle signal masking as we are walking back up the list of signals
|
||||
CTX->SignalDelegation->SetCurrentSignal(Context->Signal);
|
||||
}
|
||||
|
||||
bool JITCore::HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = AbsoluteLoopTopAddress;
|
||||
// Set our state register to point to our guest thread data
|
||||
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(ThreadState);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
++SignalHandlerRefCounter;
|
||||
|
||||
uint64_t OldGuestSP = ThreadState->State.State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
if (!(GuestStack->ss_flags & SS_DISABLE)) {
|
||||
// If our guest is already inside of the alternative stack
|
||||
// Then that means we are hitting recursive signals and we need to walk back the stack correctly
|
||||
uint64_t AltStackBase = reinterpret_cast<uint64_t>(GuestStack->ss_sp);
|
||||
uint64_t AltStackEnd = AltStackBase + GuestStack->ss_size;
|
||||
if (OldGuestSP >= AltStackBase &&
|
||||
OldGuestSP <= AltStackEnd) {
|
||||
// We are already in the alt stack, the rest of the code will handle adjusting this
|
||||
}
|
||||
else {
|
||||
NewGuestSP = AltStackEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Back up past the redzone, which is 128bytes
|
||||
// Don't need this offset if we aren't going to be putting siginfo in to it
|
||||
NewGuestSP -= 128;
|
||||
|
||||
if (GuestAction->sa_flags & SA_SIGINFO) {
|
||||
// Setup ucontext a bit
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
|
||||
uint64_t UContextLocation = NewGuestSP;
|
||||
|
||||
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
|
||||
|
||||
// We have extended float information
|
||||
guest_uctx->uc_flags |= FEXCore::x86_64::UC_FP_XSTATE;
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->uc_mcontext.fpregs = &guest_uctx->__fpregs_mem;
|
||||
|
||||
#define COPY_REG(x) \
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = ThreadState->State.State.gregs[X86State::REG_##x];
|
||||
COPY_REG(R8);
|
||||
COPY_REG(R9);
|
||||
COPY_REG(R10);
|
||||
COPY_REG(R11);
|
||||
COPY_REG(R12);
|
||||
COPY_REG(R13);
|
||||
COPY_REG(R14);
|
||||
COPY_REG(R15);
|
||||
COPY_REG(RDI);
|
||||
COPY_REG(RSI);
|
||||
COPY_REG(RBP);
|
||||
COPY_REG(RBX);
|
||||
COPY_REG(RDX);
|
||||
COPY_REG(RAX);
|
||||
COPY_REG(RCX);
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
|
||||
// Copy float registers
|
||||
memcpy(guest_uctx->__fpregs_mem._st, ThreadState->State.State.mm, sizeof(ThreadState->State.State.mm));
|
||||
memcpy(guest_uctx->__fpregs_mem._xmm, ThreadState->State.State.xmm, sizeof(ThreadState->State.State.xmm));
|
||||
|
||||
// FCW store default
|
||||
guest_uctx->__fpregs_mem.fcw = ThreadState->State.State.FCW;
|
||||
|
||||
// Reconstruct FSW
|
||||
guest_uctx->__fpregs_mem.fsw =
|
||||
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) |
|
||||
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
|
||||
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) |
|
||||
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
||||
(ThreadState->State.State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
|
||||
|
||||
// Copy over signal stack information
|
||||
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
|
||||
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
|
||||
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
|
||||
|
||||
// XXX: siginfo_t(RSI)
|
||||
ThreadState->State.State.gregs[X86State::REG_RSI] = 0x4142434445460000;
|
||||
ThreadState->State.State.gregs[X86State::REG_RDX] = UContextLocation;
|
||||
}
|
||||
else {
|
||||
// XXX: 32bit Support
|
||||
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
|
||||
uint64_t UContextLocation = 0; // NewGuestSP;
|
||||
NewGuestSP -= sizeof(FEXCore::x86::siginfo_t);
|
||||
uint64_t SigInfoLocation = 0; // NewGuestSP;
|
||||
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = UContextLocation;
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = SigInfoLocation;
|
||||
}
|
||||
|
||||
ThreadState->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
|
||||
}
|
||||
else {
|
||||
ThreadState->State.State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.handler);
|
||||
}
|
||||
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
ThreadState->State.State.gregs[X86State::REG_RDI] = Signal;
|
||||
|
||||
// Set up the new SP for stack handling
|
||||
NewGuestSP -= 8;
|
||||
*(uint64_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
ThreadState->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
}
|
||||
else {
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = CTX->X86CodeGen.SignalReturn;
|
||||
LogMan::Throw::A(CTX->X86CodeGen.SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
||||
ThreadState->State.State.gregs[X86State::REG_RSP] = NewGuestSP;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void JITCore::CopyNecessaryDataForCompileThread(CPUBackend *Original) {
|
||||
JITCore *Core = reinterpret_cast<JITCore*>(Original);
|
||||
void X86JITCore::CopyNecessaryDataForCompileThread(CPUBackend *Original) {
|
||||
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Original);
|
||||
ThreadSharedData = Core->ThreadSharedData;
|
||||
}
|
||||
|
||||
bool JITCore::HandleSIGILL(int Signal, void *info, void *ucontext) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
if (_mcontext->gregs[REG_RIP] == ThreadSharedData.SignalHandlerReturnAddress) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_mcontext->gregs[REG_RIP] == PauseReturnInstruction) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool JITCore::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = ThreadPauseHandlerAddress;
|
||||
|
||||
// Set our state register to point to our guest thread data
|
||||
_mcontext->gregs[REG_R14] = reinterpret_cast<uint64_t>(ThreadState);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
++SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
|
||||
ucontext_t* _context = (ucontext_t*)ucontext;
|
||||
mcontext_t* _mcontext = &_context->uc_mcontext;
|
||||
|
||||
// Our thread is stopping
|
||||
// We don't care about anything at this point
|
||||
// Set the stack to our starting location when we entered the JIT and get out safely
|
||||
_mcontext->gregs[REG_RSP] = ThreadState->State.ReturningStackLocation;
|
||||
|
||||
// Our ref counting doesn't matter anymore
|
||||
SignalHandlerRefCounter = 0;
|
||||
|
||||
// Set the new PC
|
||||
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void JITCore::PushRegs() {
|
||||
void X86JITCore::PushRegs() {
|
||||
for (auto &Xmm : RAXMM_x) {
|
||||
sub(rsp, 16);
|
||||
movaps(ptr[rsp], Xmm);
|
||||
@@ -353,7 +67,7 @@ void JITCore::PushRegs() {
|
||||
sub(rsp, 8); // Align
|
||||
}
|
||||
|
||||
void JITCore::PopRegs() {
|
||||
void X86JITCore::PopRegs() {
|
||||
auto NumPush = RA64.size();
|
||||
|
||||
if (NumPush & 1)
|
||||
@@ -367,11 +81,11 @@ void JITCore::PopRegs() {
|
||||
}
|
||||
}
|
||||
|
||||
void JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
|
||||
void X86JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
|
||||
FallbackInfo Info;
|
||||
if (!InterpreterOps::GetFallbackHandler(IROp, &Info)) {
|
||||
auto Name = FEXCore::IR::GetName(IROp->Op);
|
||||
LogMan::Msg::A("Unhandled IR Op: %s", std::string(Name).c_str());
|
||||
LOGMAN_MSG_A("Unhandled IR Op: %s", std::string(Name).c_str());
|
||||
} else {
|
||||
switch(Info.ABI) {
|
||||
case FABI_VOID_U16: {
|
||||
@@ -569,22 +283,20 @@ void JITCore::Op_Unhandled(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
|
||||
case FABI_UNKNOWN:
|
||||
default:
|
||||
auto Name = FEXCore::IR::GetName(IROp->Op);
|
||||
LogMan::Msg::A("Unhandled IR Fallback abi: %s %d", std::string(Name).c_str(), Info.ABI);
|
||||
LOGMAN_MSG_A("Unhandled IR Fallback abi: %s %d", std::string(Name).c_str(), Info.ABI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JITCore::Op_NoOp(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
|
||||
void X86JITCore::Op_NoOp(FEXCore::IR::IROp_Header *IROp, uint32_t Node) {
|
||||
}
|
||||
|
||||
JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread)
|
||||
X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread)
|
||||
: CodeGenerator(Buffer.Size, Buffer.Ptr, nullptr)
|
||||
, CTX {ctx}
|
||||
, ThreadState {Thread}
|
||||
, InitialCodeBuffer {Buffer}
|
||||
{
|
||||
ThreadSharedData.SignalHandlerRefCounterPtr = &SignalHandlerRefCounter;
|
||||
|
||||
CurrentCodeBuffer = &InitialCodeBuffer;
|
||||
|
||||
RAPass = Thread->PassManager->GetRAPass();
|
||||
@@ -600,7 +312,7 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < FEXCore::IR::IROps::OP_LAST + 1; ++i) {
|
||||
OpHandlers[i] = &JITCore::Op_Unhandled;
|
||||
OpHandlers[i] = &X86JITCore::Op_Unhandled;
|
||||
}
|
||||
|
||||
RegisterALUHandlers();
|
||||
@@ -615,22 +327,32 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
|
||||
RegisterEncryptionHandlers();
|
||||
|
||||
if (!CompileThread) {
|
||||
CreateCustomDispatch(Thread);
|
||||
DispatcherConfig config;
|
||||
config.ExitFunctionLink = reinterpret_cast<uintptr_t>(&ExitFunctionLink);
|
||||
config.ExitFunctionLinkThis = reinterpret_cast<uintptr_t>(this);
|
||||
|
||||
Dispatcher = std::make_unique<X86Dispatcher>(CTX, ThreadState, config);
|
||||
DispatchPtr = Dispatcher->DispatchPtr;
|
||||
CallbackPtr = Dispatcher->CallbackPtr;
|
||||
|
||||
ThreadSharedData.SignalHandlerRefCounterPtr = &Dispatcher->SignalHandlerRefCounter;
|
||||
ThreadSharedData.SignalHandlerReturnAddress = Dispatcher->SignalHandlerReturnAddress;
|
||||
ThreadSharedData.Dispatcher = Dispatcher.get();
|
||||
|
||||
// This will register the host signal handler per thread, which is fine
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->HandleSIGILL(Signal, info, ucontext);
|
||||
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->Dispatcher->HandleSIGILL(Signal, info, ucontext);
|
||||
});
|
||||
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->HandleSignalPause(Signal, info, ucontext);
|
||||
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->Dispatcher->HandleSignalPause(Signal, info, ucontext);
|
||||
});
|
||||
|
||||
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
|
||||
JITCore *Core = reinterpret_cast<JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
X86JITCore *Core = reinterpret_cast<X86JITCore*>(Thread->CPUBackend.get());
|
||||
return Core->Dispatcher->HandleGuestSignal(Signal, info, ucontext, GuestAction, GuestStack);
|
||||
};
|
||||
|
||||
for (uint32_t Signal = 0; Signal < SignalDelegator::MAX_SIGNALS; ++Signal) {
|
||||
@@ -639,20 +361,17 @@ JITCore::JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadSt
|
||||
}
|
||||
}
|
||||
|
||||
JITCore::~JITCore() {
|
||||
X86JITCore::~X86JITCore() {
|
||||
for (auto CodeBuffer : CodeBuffers) {
|
||||
FreeCodeBuffer(CodeBuffer);
|
||||
}
|
||||
CodeBuffers.clear();
|
||||
|
||||
if (DispatcherCodeBuffer.Ptr) {
|
||||
// Dispatcher may not exist if this is a compile thread
|
||||
FreeCodeBuffer(DispatcherCodeBuffer);
|
||||
}
|
||||
|
||||
FreeCodeBuffer(InitialCodeBuffer);
|
||||
}
|
||||
|
||||
void JITCore::ClearCache() {
|
||||
void X86JITCore::ClearCache() {
|
||||
if (*ThreadSharedData.SignalHandlerRefCounterPtr == 0) {
|
||||
if (!CodeBuffers.empty()) {
|
||||
// If we have more than one code buffer we are tracking then walk them and delete
|
||||
@@ -686,30 +405,30 @@ void JITCore::ClearCache() {
|
||||
// We have signal handlers that have generated code
|
||||
// This means that we can not safely clear the code at this point in time
|
||||
// Allocate some new code buffers that we can switch over to instead
|
||||
auto NewCodeBuffer = AllocateNewCodeBuffer(JITCore::INITIAL_CODE_SIZE);
|
||||
auto NewCodeBuffer = AllocateNewCodeBuffer(X86JITCore::INITIAL_CODE_SIZE);
|
||||
EmplaceNewCodeBuffer(NewCodeBuffer);
|
||||
setNewBuffer(NewCodeBuffer.Ptr, NewCodeBuffer.Size);
|
||||
}
|
||||
}
|
||||
|
||||
IR::PhysicalRegister JITCore::GetPhys(uint32_t Node) {
|
||||
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
|
||||
auto PhyReg = RAData->GetNodeRegister(Node);
|
||||
|
||||
LogMan::Throw::A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
|
||||
LOGMAN_THROW_A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
|
||||
|
||||
return PhyReg;
|
||||
}
|
||||
|
||||
bool JITCore::IsFPR(uint32_t Node) {
|
||||
bool X86JITCore::IsFPR(uint32_t Node) {
|
||||
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
|
||||
}
|
||||
|
||||
bool JITCore::IsGPR(uint32_t Node) {
|
||||
bool X86JITCore::IsGPR(uint32_t Node) {
|
||||
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
|
||||
}
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg JITCore::GetSrc(uint32_t Node) {
|
||||
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) {
|
||||
// rax, rcx, rdx, rsi, r8, r9,
|
||||
// r10
|
||||
// Callee Saved
|
||||
@@ -728,24 +447,24 @@ Xbyak::Reg JITCore::GetSrc(uint32_t Node) {
|
||||
}
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetSrc<JITCore::RA_64>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetSrc<JITCore::RA_32>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetSrc<JITCore::RA_16>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetSrc<JITCore::RA_8>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node);
|
||||
|
||||
Xbyak::Xmm JITCore::GetSrc(uint32_t Node) {
|
||||
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
return RAXMM_x[PhyReg.Reg];
|
||||
}
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg JITCore::GetDst(uint32_t Node) {
|
||||
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
if (RAType == RA_64)
|
||||
return RA64[PhyReg.Reg].cvt64();
|
||||
@@ -760,19 +479,19 @@ Xbyak::Reg JITCore::GetDst(uint32_t Node) {
|
||||
}
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetDst<JITCore::RA_64>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetDst<JITCore::RA_32>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetDst<JITCore::RA_16>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node);
|
||||
|
||||
template
|
||||
Xbyak::Reg JITCore::GetDst<JITCore::RA_8>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node);
|
||||
|
||||
template<uint8_t RAType>
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair(uint32_t Node) {
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
if (RAType == RA_64)
|
||||
return RA64Pair[PhyReg.Reg];
|
||||
@@ -781,17 +500,17 @@ std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair(uint32_t Node) {
|
||||
}
|
||||
|
||||
template
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair<JITCore::RA_64>(uint32_t Node);
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node);
|
||||
|
||||
template
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> JITCore::GetSrcPair<JITCore::RA_32>(uint32_t Node);
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node);
|
||||
|
||||
Xbyak::Xmm JITCore::GetDst(uint32_t Node) {
|
||||
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
return RAXMM_x[PhyReg.Reg];
|
||||
}
|
||||
|
||||
bool JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
|
||||
@@ -805,13 +524,13 @@ bool JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Va
|
||||
}
|
||||
}
|
||||
|
||||
bool JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
|
||||
auto Op = OpHeader->C<IR::IROp_InlineEntrypointOffset>();
|
||||
if (Value) {
|
||||
*Value = IR->GetHeader()->Entry + Op->Offset;
|
||||
*Value = Entry + Op->Offset;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -819,7 +538,7 @@ bool JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::CondClassType cond) {
|
||||
std::tuple<X86JITCore::SetCC, X86JITCore::CMovCC, X86JITCore::JCC> X86JITCore::GetCC(IR::CondClassType cond) {
|
||||
switch (cond.Val) {
|
||||
case FEXCore::IR::COND_EQ: return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
|
||||
case FEXCore::IR::COND_NEQ: return { &CodeGenerator::setne, &CodeGenerator::cmovne, &CodeGenerator::jne };
|
||||
@@ -844,7 +563,7 @@ std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::Con
|
||||
case FEXCore::IR::COND_VS:
|
||||
case FEXCore::IR::COND_VC:
|
||||
default:
|
||||
LogMan::Msg::A("Unsupported compare type");
|
||||
LOGMAN_MSG_A("Unsupported compare type");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -852,12 +571,12 @@ std::tuple<JITCore::SetCC, JITCore::CMovCC, JITCore::JCC> JITCore::GetCC(IR::Con
|
||||
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
|
||||
}
|
||||
|
||||
void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
JumpTargets.clear();
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
|
||||
this->Entry = Entry;
|
||||
this->RAData = RAData;
|
||||
auto HeaderOp = IR->GetHeader();
|
||||
|
||||
// Fairly excessive buffer range to make sure we don't overflow
|
||||
uint32_t BufferRange = SSACount * 16;
|
||||
@@ -865,7 +584,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
|
||||
ThreadState->CTX->ClearCodeCache(ThreadState, false);
|
||||
}
|
||||
|
||||
void *Entry = getCurr<void*>();
|
||||
void *GuestEntry = getCurr<void*>();
|
||||
this->IR = IR;
|
||||
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
@@ -874,20 +593,20 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
static_assert(sizeof(CTX->Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
mov(rax, qword [STATE + (offsetof(FEXCore::Core::InternalThreadState, CTX))]);
|
||||
mov(rax, reinterpret_cast<uint64_t>(CTX));
|
||||
|
||||
// If the value == 0 then branch to the top
|
||||
cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
|
||||
je(RunBlock);
|
||||
// Else we need to pause now
|
||||
mov(rax, ThreadPauseHandlerAddress);
|
||||
mov(rax, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddress);
|
||||
jmp(rax);
|
||||
ud2();
|
||||
|
||||
L(RunBlock);
|
||||
}
|
||||
|
||||
LogMan::Throw::A(RAData != nullptr, "Needs RA");
|
||||
LOGMAN_THROW_A(RAData != nullptr, "Needs RA");
|
||||
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
@@ -896,7 +615,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
|
||||
}
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(HeaderOp->Entry);
|
||||
BlockSamplingData::BlockData *SamplingData = CTX->BlockData->GetBlockData(Entry);
|
||||
if (GetSamplingData) {
|
||||
mov(rcx, reinterpret_cast<uintptr_t>(SamplingData));
|
||||
rdtsc();
|
||||
@@ -944,7 +663,7 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
|
||||
using namespace FEXCore::IR;
|
||||
{
|
||||
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
|
||||
LogMan::Throw::A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_A(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
uint32_t Node = IR->GetID(BlockNode);
|
||||
auto IsTarget = JumpTargets.find(Node);
|
||||
@@ -1013,40 +732,29 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const *IR, [
|
||||
}
|
||||
PendingTargetLabel = nullptr;
|
||||
|
||||
void *Exit = getCurr<void*>();
|
||||
void *GuestExit = getCurr<void*>();
|
||||
this->IR = nullptr;
|
||||
|
||||
ready();
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(Exit) - reinterpret_cast<uintptr_t>(Entry);
|
||||
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(GuestExit) - reinterpret_cast<uintptr_t>(GuestEntry);
|
||||
}
|
||||
return Entry;
|
||||
return GuestEntry;
|
||||
}
|
||||
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
--ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
|
||||
// Go to sleep
|
||||
Thread->StartRunning.Wait();
|
||||
|
||||
Thread->State.RunningEvents.Running = true;
|
||||
++ctx->IdleWaitRefCount;
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
}
|
||||
|
||||
uint64_t JITCore::ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadState *Thread, uint64_t *record) {
|
||||
uint64_t X86JITCore::ExitFunctionLink(X86JITCore *core, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
|
||||
auto Thread = Frame->Thread;
|
||||
auto GuestRip = record[1];
|
||||
|
||||
auto HostCode = Thread->LookupCache->FindBlock(GuestRip);
|
||||
|
||||
if (!HostCode) {
|
||||
Thread->State.State.rip = GuestRip;
|
||||
return core->AbsoluteLoopTopAddress;
|
||||
Thread->CurrentFrame->State.rip = GuestRip;
|
||||
return core->ThreadSharedData.Dispatcher->AbsoluteLoopTopAddress;
|
||||
}
|
||||
|
||||
auto LinkerAddress = core->ExitFunctionLinkerAddress;
|
||||
auto LinkerAddress = core->ThreadSharedData.Dispatcher->ExitFunctionLinkerAddress;
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
// undo the link
|
||||
record[0] = LinkerAddress;
|
||||
@@ -1056,250 +764,7 @@ uint64_t JITCore::ExitFunctionLink(JITCore *core, FEXCore::Core::InternalThreadS
|
||||
return HostCode;
|
||||
}
|
||||
|
||||
void JITCore::CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread) {
|
||||
DispatcherCodeBuffer = AllocateNewCodeBuffer(MAX_DISPATCHER_CODE_SIZE);
|
||||
setNewBuffer(DispatcherCodeBuffer.Ptr, DispatcherCodeBuffer.Size);
|
||||
|
||||
// Temp registers
|
||||
// rax, rcx, rdx, rsi, r8, r9,
|
||||
// r10, r11
|
||||
//
|
||||
// Callee Saved
|
||||
// rbx, rbp, r12, r13, r14, r15
|
||||
//
|
||||
// 1St Argument: rdi <ThreadState>
|
||||
// XMM:
|
||||
// All temp
|
||||
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
|
||||
|
||||
// while (!Thread->State.RunningEvents.ShouldStop.load()) {
|
||||
// Ptr = FindBlock(RIP)
|
||||
// if (!Ptr)
|
||||
// Ptr = CTX->CompileBlock(RIP);
|
||||
//
|
||||
// if (Ptr)
|
||||
// Ptr();
|
||||
// else
|
||||
// {
|
||||
// Ptr = FallbackCore->CompileBlock()
|
||||
// if (Ptr)
|
||||
// Ptr()
|
||||
// else {
|
||||
// ShouldStop = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Bunch of exit state stuff
|
||||
|
||||
// x86-64 ABI has the stack aligned when /call/ happens
|
||||
// Which means the destination has a misaligned stack at that point
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
mov(STATE, rdi);
|
||||
|
||||
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
||||
// regardless of where we were in the stack
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)], rsp);
|
||||
|
||||
Label LoopTop;
|
||||
Label FullLookup;
|
||||
Label NoBlock;
|
||||
Label ThreadPauseHandler{};
|
||||
|
||||
L(LoopTop);
|
||||
AbsoluteLoopTopAddress = getCurr<uint64_t>();
|
||||
{
|
||||
// Load our RIP
|
||||
mov(rdx, qword [STATE + offsetof(FEXCore::Core::CPUState, rip)]);
|
||||
|
||||
// L1 Cache
|
||||
mov(r13, Thread->LookupCache->GetL1Pointer());
|
||||
mov(rax, rdx);
|
||||
|
||||
and_(rax, LookupCache::L1_ENTRIES_MASK);
|
||||
shl(rax, 4);
|
||||
cmp(qword[r13 + rax + 8], rdx);
|
||||
jne(FullLookup);
|
||||
jmp(qword[r13 + rax + 0]);
|
||||
|
||||
L(FullLookup);
|
||||
mov(r13, Thread->LookupCache->GetPagePointer());
|
||||
|
||||
// Full lookup
|
||||
mov(rax, rdx);
|
||||
mov(rbx, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
and_(rax, rbx);
|
||||
shr(rax, 12);
|
||||
|
||||
// Load page pointer
|
||||
mov(rdi, qword [r13 + rax * 8]);
|
||||
|
||||
cmp(rdi, 0);
|
||||
je(NoBlock);
|
||||
|
||||
mov (rax, rdx);
|
||||
and_(rax, 0x0FFF);
|
||||
|
||||
shl(rax, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
|
||||
|
||||
// check for aliasing
|
||||
mov(rcx, qword [rdi + rax + 8]);
|
||||
cmp(rcx, rdx);
|
||||
jne(NoBlock);
|
||||
|
||||
// Load the block pointer
|
||||
mov(rax, qword [rdi + rax]);
|
||||
|
||||
cmp(rax, 0);
|
||||
je(NoBlock);
|
||||
|
||||
// Update L1
|
||||
mov(r13, Thread->LookupCache->GetL1Pointer());
|
||||
mov(rcx, rdx);
|
||||
and_(rcx, LookupCache::L1_ENTRIES_MASK);
|
||||
shl(rcx, 1);
|
||||
mov(qword[r13 + rcx*8 + 8], rdx);
|
||||
mov(qword[r13 + rcx*8 + 0], rax);
|
||||
|
||||
// Real block if we made it here
|
||||
jmp(rax);
|
||||
}
|
||||
|
||||
{
|
||||
ThreadStopHandlerAddress = getCurr<uint64_t>();
|
||||
|
||||
add(rsp, 8);
|
||||
|
||||
pop(r15);
|
||||
pop(r14);
|
||||
pop(r13);
|
||||
pop(r12);
|
||||
pop(rbp);
|
||||
pop(rbx);
|
||||
|
||||
ret();
|
||||
}
|
||||
|
||||
{
|
||||
ExitFunctionLinkerAddress = getCurr<uint64_t>();
|
||||
// {rdi, rsi, rdx}
|
||||
mov(rdi, (uintptr_t)this);
|
||||
mov(rsi, STATE);
|
||||
mov(rdx, rax); // rax is set at the block end
|
||||
|
||||
mov(rax, (uintptr_t)&ExitFunctionLink);
|
||||
call(rax);
|
||||
jmp(rax);
|
||||
}
|
||||
|
||||
Label FallbackCore;
|
||||
// Block creation
|
||||
{
|
||||
L(NoBlock);
|
||||
|
||||
using ClassPtrType = uintptr_t (FEXCore::Context::Context::*)(FEXCore::Core::InternalThreadState *, uint64_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
PtrCast Ptr;
|
||||
Ptr.ClassPtr = &FEXCore::Context::Context::CompileBlock;
|
||||
|
||||
// {rdi, rsi, rdx}
|
||||
mov(rdi, reinterpret_cast<uint64_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, Ptr.Data);
|
||||
|
||||
call(rax);
|
||||
|
||||
// RAX contains nulptr or block ptr here
|
||||
cmp(rax, 0);
|
||||
je(FallbackCore);
|
||||
// rdx already contains RIP here
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
{
|
||||
L(FallbackCore);
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
// Signal return handler
|
||||
ThreadSharedData.SignalHandlerReturnAddress = getCurr<uint64_t>();
|
||||
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
// Pause handler
|
||||
ThreadPauseHandlerAddress = getCurr<uint64_t>();
|
||||
L(ThreadPauseHandler);
|
||||
|
||||
mov(rdi, reinterpret_cast<uintptr_t>(CTX));
|
||||
mov(rsi, STATE);
|
||||
mov(rax, reinterpret_cast<uint64_t>(SleepThread));
|
||||
|
||||
call(rax);
|
||||
|
||||
PauseReturnInstruction = getCurr<uint64_t>();
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
CallbackPtr = getCurr<CPUBackend::JITCallback>();
|
||||
|
||||
push(rbx);
|
||||
push(rbp);
|
||||
push(r12);
|
||||
push(r13);
|
||||
push(r14);
|
||||
push(r15);
|
||||
sub(rsp, 8);
|
||||
|
||||
// First thing we need to move the thread state pointer back in to our register
|
||||
mov(STATE, rdi);
|
||||
// XXX: XMM?
|
||||
|
||||
// Make sure to adjust the refcounter so we don't clear the cache now
|
||||
mov(rax, reinterpret_cast<uint64_t>(&SignalHandlerRefCounter));
|
||||
add(dword [rax], 1);
|
||||
|
||||
// Now push the callback return trampoline to the guest stack
|
||||
// Guest will be misaligned because calling a thunk won't correct the guest's stack once we call the callback from the host
|
||||
mov(rax, CTX->X86CodeGen.CallbackReturn);
|
||||
|
||||
// Store the trampoline to the guest stack
|
||||
// Guest stack is now correctly misaligned after a regular call instruction
|
||||
sub(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])], 16);
|
||||
mov(rbx, qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.gregs[X86State::REG_RSP])]);
|
||||
mov(qword [rbx], rax);
|
||||
|
||||
// Store RIP to the context state
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::InternalThreadState, State.State.rip)], rsi);
|
||||
|
||||
// Back to the loop top now
|
||||
jmp(LoopTop);
|
||||
}
|
||||
|
||||
#if ENABLE_JITSYMBOLS
|
||||
std::string Name = "Dispatch_" + std::to_string(::gettid());
|
||||
CTX->Symbols.Register(DispatcherCodeBuffer.Ptr, DispatcherCodeBuffer.Size, Name);
|
||||
#endif
|
||||
|
||||
ready();
|
||||
|
||||
setNewBuffer(InitialCodeBuffer.Ptr, InitialCodeBuffer.Size);
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateJITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return new JITCore(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? JITCore::MAX_CODE_SIZE : JITCore::INITIAL_CODE_SIZE), CompileThread);
|
||||
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return new X86JITCore(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? X86JITCore::MAX_CODE_SIZE : X86JITCore::INITIAL_CODE_SIZE), CompileThread);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
+22
-32
@@ -1,11 +1,18 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/BlockSamplingData.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JIT.h"
|
||||
#include "Common/MathUtils.h"
|
||||
|
||||
#define XBYAK64
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
@@ -54,12 +61,12 @@ const std::array<std::pair<Xbyak::Reg, Xbyak::Reg>, 4> RA64Pair = {{ {rsi, r8},
|
||||
const std::array<Xbyak::Reg, 11> RAXMM = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
|
||||
const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11};
|
||||
|
||||
class JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
|
||||
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
|
||||
public:
|
||||
explicit JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
|
||||
~JITCore() override;
|
||||
explicit X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, CodeBuffer Buffer, bool CompileThread);
|
||||
~X86JITCore() override;
|
||||
std::string GetName() override { return "JIT"; }
|
||||
void *CompileCode(FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
void *CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
||||
|
||||
void *MapRegion(void* HostPtr, uint64_t, uint64_t) override { return HostPtr; }
|
||||
|
||||
@@ -69,10 +76,6 @@ public:
|
||||
|
||||
static constexpr size_t INITIAL_CODE_SIZE = 1024 * 1024 * 16;
|
||||
static constexpr size_t MAX_CODE_SIZE = 1024 * 1024 * 256;
|
||||
|
||||
bool HandleSIGILL(int Signal, void *info, void *ucontext);
|
||||
bool HandleSignalPause(int Signal, void *info, void *ucontext);
|
||||
bool HandleGuestSignal(int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack);
|
||||
void CopyNecessaryDataForCompileThread(CPUBackend *Original) override;
|
||||
|
||||
private:
|
||||
@@ -80,6 +83,8 @@ private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Core::InternalThreadState *ThreadState;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
std::unique_ptr<FEXCore::CPU::Dispatcher> Dispatcher;
|
||||
uint64_t Entry;
|
||||
|
||||
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, Label> JumpTargets;
|
||||
Xbyak::util::Cpu Features{};
|
||||
@@ -128,7 +133,6 @@ private:
|
||||
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
|
||||
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
|
||||
|
||||
void CreateCustomDispatch(FEXCore::Core::InternalThreadState *Thread);
|
||||
IR::RegisterAllocationPass *RAPass;
|
||||
FEXCore::IR::RegisterAllocationData *RAData;
|
||||
|
||||
@@ -136,13 +140,11 @@ private:
|
||||
bool GetSamplingData {true};
|
||||
#endif
|
||||
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 1;
|
||||
|
||||
void EmplaceNewCodeBuffer(CodeBuffer Buffer) {
|
||||
CurrentCodeBuffer = &CodeBuffers.emplace_back(Buffer);
|
||||
}
|
||||
|
||||
static uint64_t ExitFunctionLink(JITCore* code, FEXCore::Core::InternalThreadState *Thread, uint64_t *record);
|
||||
static uint64_t ExitFunctionLink(X86JITCore* code, FEXCore::Core::CpuStateFrame *Frame, uint64_t *record);
|
||||
|
||||
// This is the initial code buffer that we will fall back to
|
||||
// In a program without signals and code clearing, we will typically
|
||||
@@ -153,39 +155,26 @@ private:
|
||||
// For code safety we can't delete code buffers until outside of all signals
|
||||
std::vector<CodeBuffer> CodeBuffers{};
|
||||
|
||||
// This is the codebuffer that our dispatcher lives in
|
||||
CodeBuffer DispatcherCodeBuffer{};
|
||||
// This is the current code buffer that we are tracking
|
||||
CodeBuffer *CurrentCodeBuffer{};
|
||||
|
||||
uint64_t AbsoluteLoopTopAddress{};
|
||||
uint64_t ExitFunctionLinkerAddress{};
|
||||
uint64_t ThreadStopHandlerAddress{};
|
||||
uint64_t ThreadPauseHandlerAddress{};
|
||||
|
||||
uint64_t PauseReturnInstruction{};
|
||||
|
||||
uint32_t SignalHandlerRefCounter{};
|
||||
|
||||
struct CompilerSharedData {
|
||||
uint64_t SignalHandlerReturnAddress{};
|
||||
|
||||
uint32_t *SignalHandlerRefCounterPtr{};
|
||||
FEXCore::CPU::Dispatcher *Dispatcher{};
|
||||
};
|
||||
|
||||
CompilerSharedData ThreadSharedData;
|
||||
|
||||
void StoreThreadState(int Signal, void *ucontext);
|
||||
void RestoreThreadState(void *ucontext);
|
||||
std::stack<uint64_t> SignalFrames;
|
||||
uint32_t SpillSlots{};
|
||||
using SetCC = void (JITCore::*)(const Operand& op);
|
||||
using CMovCC = void (JITCore::*)(const Reg& reg, const Operand& op);
|
||||
using JCC = void (JITCore::*)(const Label& label, LabelType type);
|
||||
using SetCC = void (X86JITCore::*)(const Operand& op);
|
||||
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
|
||||
using JCC = void (X86JITCore::*)(const Label& label, LabelType type);
|
||||
|
||||
std::tuple<SetCC, CMovCC, JCC> GetCC(IR::CondClassType cond);
|
||||
|
||||
using OpHandler = void (JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
|
||||
using OpHandler = void (X86JITCore::*)(FEXCore::IR::IROp_Header *IROp, uint32_t Node);
|
||||
std::array<OpHandler, FEXCore::IR::IROps::OP_LAST + 1> OpHandlers {};
|
||||
void RegisterALUHandlers();
|
||||
void RegisterAtomicHandlers();
|
||||
@@ -210,7 +199,7 @@ private:
|
||||
|
||||
///< ALU Ops
|
||||
DEF_OP(TruncElementPair);
|
||||
DEF_OP(Constant);
|
||||
DEF_OP(Constant);
|
||||
DEF_OP(EntrypointOffset);
|
||||
DEF_OP(InlineConstant);
|
||||
DEF_OP(InlineEntrypointOffset);
|
||||
@@ -319,6 +308,7 @@ private:
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(VLoadMemElement);
|
||||
DEF_OP(VStoreMemElement);
|
||||
DEF_OP(CacheLineClear);
|
||||
|
||||
///< Misc ops
|
||||
DEF_OP(EndBlock);
|
||||
|
||||
+49
-34
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
@@ -5,7 +11,7 @@
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(LoadContext) {
|
||||
auto Op = IROp->C<IR::IROp_LoadContext>();
|
||||
@@ -30,10 +36,10 @@ DEF_OP(LoadContext) {
|
||||
}
|
||||
break;
|
||||
case 16: {
|
||||
LogMan::Msg::A("Invalid GPR load of size 16");
|
||||
LOGMAN_MSG_A("Invalid GPR load of size 16");
|
||||
}
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -63,7 +69,7 @@ DEF_OP(LoadContext) {
|
||||
movups(GetDst(Node), xword [STATE + Op->Offset]);
|
||||
}
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,7 +100,7 @@ DEF_OP(StoreContext) {
|
||||
case 16:
|
||||
LogMan::Msg::D("Invalid store size of 16");
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -123,7 +129,7 @@ DEF_OP(StoreContext) {
|
||||
movups(xword [STATE + Op->Offset], GetSrc(Op->Header.Args[0].ID()));
|
||||
}
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreContext size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreContext size: %d", OpSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,15 +160,15 @@ DEF_OP(LoadContextIndexed) {
|
||||
mov(GetDst<RA_64>(Node), qword [rax + index * Op->Stride]);
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
LogMan::Msg::A("Invalid Class load of size 16");
|
||||
LOGMAN_MSG_A("Invalid Class load of size 16");
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -189,7 +195,7 @@ DEF_OP(LoadContextIndexed) {
|
||||
vmovq(GetDst(Node), qword [rax + index * Op->Stride]);
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -217,12 +223,12 @@ DEF_OP(LoadContextIndexed) {
|
||||
movups(GetDst(Node), xword [STATE + rax]);
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled LoadContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,13 +248,13 @@ DEF_OP(StoreContextIndexed) {
|
||||
case 4:
|
||||
case 8: {
|
||||
if (!(size == 1 || size == 2 || size == 4 || size == 8)) {
|
||||
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", Op->Size);
|
||||
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", Op->Size);
|
||||
}
|
||||
mov(AddressFrame(Op->Size * 8) [rax + index * Op->Stride], value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -273,7 +279,7 @@ DEF_OP(StoreContextIndexed) {
|
||||
vmovq(AddressFrame(Op->Size * 8) [rax + index * Op->Stride], value);
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", size);
|
||||
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -301,12 +307,12 @@ DEF_OP(StoreContextIndexed) {
|
||||
movups(xword [STATE + rax], value);
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled StoreContextIndexed size: %d", size);
|
||||
LOGMAN_MSG_A("Unhandled StoreContextIndexed size: %d", size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
|
||||
LOGMAN_MSG_A("Unhandled StoreContextIndexed stride: %d", Op->Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +340,7 @@ DEF_OP(SpillRegister) {
|
||||
mov(qword [rsp + SlotOffset], GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -350,10 +356,10 @@ DEF_OP(SpillRegister) {
|
||||
movaps(xword [rsp + SlotOffset], GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled SpillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled SpillRegister size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::A("Unhandled SpillRegister class: %d", Op->Class.Val);
|
||||
LOGMAN_MSG_A("Unhandled SpillRegister class: %d", Op->Class.Val);
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +388,7 @@ DEF_OP(FillRegister) {
|
||||
mov(GetDst<RA_64>(Node), qword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled FillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled FillRegister size: %d", OpSize);
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -398,10 +404,10 @@ DEF_OP(FillRegister) {
|
||||
movaps(GetDst(Node), xword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unhandled FillRegister size: %d", OpSize);
|
||||
default: LOGMAN_MSG_A("Unhandled FillRegister size: %d", OpSize);
|
||||
}
|
||||
} else {
|
||||
LogMan::Msg::A("Unhandled FillRegister class: %d", Op->Class.Val);
|
||||
LOGMAN_MSG_A("Unhandled FillRegister class: %d", Op->Class.Val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,16 +425,16 @@ DEF_OP(StoreFlag) {
|
||||
mov(byte [STATE + (offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag)], al);
|
||||
}
|
||||
|
||||
Xbyak::RegExp JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
Xbyak::RegExp X86JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
if (Offset.IsInvalid()) {
|
||||
return Base;
|
||||
} else {
|
||||
if (OffsetScale != 1 && OffsetScale != 2 && OffsetScale != 4 && OffsetScale != 8) {
|
||||
LogMan::Msg::A("Unhandled GenerateModRM OffsetScale: %d", OffsetScale);
|
||||
LOGMAN_MSG_A("Unhandled GenerateModRM OffsetScale: %d", OffsetScale);
|
||||
}
|
||||
|
||||
if (OffsetType != IR::MEM_OFFSET_SXTX) {
|
||||
LogMan::Msg::A("Unhandled GenerateModRM OffsetType: %d", OffsetType.Val);
|
||||
LOGMAN_MSG_A("Unhandled GenerateModRM OffsetType: %d", OffsetType.Val);
|
||||
}
|
||||
|
||||
uint64_t Const;
|
||||
@@ -469,7 +475,7 @@ DEF_OP(LoadMem) {
|
||||
mov(Dst, qword [MemPtr]);
|
||||
}
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -505,7 +511,7 @@ DEF_OP(LoadMem) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled LoadMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled LoadMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,7 +537,7 @@ DEF_OP(StoreMem) {
|
||||
case 8:
|
||||
mov(qword [MemPtr], GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -554,22 +560,30 @@ DEF_OP(StoreMem) {
|
||||
else
|
||||
movups(xword [MemPtr], GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unhandled StoreMem size: %d", Op->Size);
|
||||
default: LOGMAN_MSG_A("Unhandled StoreMem size: %d", Op->Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VLoadMemElement) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VStoreMemElement) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClear) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClear>();
|
||||
|
||||
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
|
||||
|
||||
clflush(ptr [MemReg]);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMemoryHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterMemoryHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(LOADCONTEXT, LoadContext);
|
||||
REGISTER_OP(STORECONTEXT, StoreContext);
|
||||
REGISTER_OP(LOADREGISTER, Unhandled); // SRA specific, not supported on this backend
|
||||
@@ -586,6 +600,7 @@ void JITCore::RegisterMemoryHandlers() {
|
||||
REGISTER_OP(STOREMEMTSO, StoreMem);
|
||||
REGISTER_OP(VLOADMEMELEMENT, VLoadMemElement);
|
||||
REGISTER_OP(VSTOREMEMELEMENT, VStoreMemElement);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+25
-10
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
@@ -6,7 +12,7 @@ static void PrintValue(uint64_t Value) {
|
||||
LogMan::Msg::D("Value: 0x%lx", Value);
|
||||
}
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(Fence) {
|
||||
auto Op = IROp->C<IR::IROp_Fence>();
|
||||
@@ -20,7 +26,7 @@ DEF_OP(Fence) {
|
||||
case IR::Fence_Store.Val:
|
||||
sfence();
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Fence: %d", Op->Fence); break;
|
||||
default: LOGMAN_MSG_A("Unknown Fence: %d", Op->Fence); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +37,22 @@ DEF_OP(Break) {
|
||||
case 5: // Guest ud2
|
||||
ud2();
|
||||
break;
|
||||
case 1: // Int <imm8>
|
||||
ud2();
|
||||
break;
|
||||
case 2: // overflow
|
||||
ud2();
|
||||
break;
|
||||
case 3: // int 1
|
||||
ud2();
|
||||
break;
|
||||
case 4: { // HLT
|
||||
// Time to quit
|
||||
// Set our stack to the starting stack location
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
mov(TMP1, ThreadStopHandlerAddress);
|
||||
mov(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddress);
|
||||
jmp(TMP1);
|
||||
break;
|
||||
}
|
||||
@@ -50,21 +65,21 @@ DEF_OP(Break) {
|
||||
}
|
||||
|
||||
// This jump target needs to be a constant offset here
|
||||
mov(TMP1, ThreadPauseHandlerAddress);
|
||||
mov(TMP1, ThreadSharedData.Dispatcher->ThreadPauseHandlerAddress);
|
||||
jmp(TMP1);
|
||||
}
|
||||
else {
|
||||
// If we don't have a gdb server attached then....crash?
|
||||
// Treat this case like HLT
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::ThreadState, ReturningStackLocation)]);
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
mov(TMP1, ThreadStopHandlerAddress);
|
||||
mov(TMP1, ThreadSharedData.Dispatcher->ThreadStopHandlerAddress);
|
||||
jmp(TMP1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Break reason: %d", Op->Reason);
|
||||
default: LOGMAN_MSG_A("Unknown Break reason: %d", Op->Reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,8 +140,8 @@ DEF_OP(Print) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMiscHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterMiscHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(DUMMY, NoOp);
|
||||
REGISTER_OP(IRHEADER, NoOp);
|
||||
REGISTER_OP(CODEBLOCK, NoOp);
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(ExtractElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
|
||||
switch (Op->Header.Size) {
|
||||
@@ -19,7 +25,7 @@ DEF_OP(ExtractElementPair) {
|
||||
mov (GetDst<RA_64>(Node), Regs[Op->Element]);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Size"); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size"); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +51,7 @@ DEF_OP(CreateElementPair) {
|
||||
RegTmp = rax;
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Size"); break;
|
||||
default: LOGMAN_MSG_A("Unknown Size"); break;
|
||||
}
|
||||
|
||||
if (Dst.first != RegSecond) {
|
||||
@@ -67,8 +73,8 @@ DEF_OP(Mov) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
|
||||
+128
-106
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
$info$
|
||||
tags: backend|x86-64
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/x86_64/JITClass.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
#define DEF_OP(x) void JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
DEF_OP(VectorZero) {
|
||||
auto Dst = GetDst(Node);
|
||||
vpxor(Dst, Dst, Dst);
|
||||
@@ -56,24 +62,24 @@ DEF_OP(VectorImm) {
|
||||
}
|
||||
|
||||
DEF_OP(CreateVector2) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(CreateVector4) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(SplatVector) {
|
||||
auto Op = IROp->C<IR::IROp_SplatVector2>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
LOGMAN_THROW_A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize);
|
||||
uint8_t Elements = 0;
|
||||
|
||||
switch (Op->Header.Op) {
|
||||
case IR::OP_SPLATVECTOR4: Elements = 4; break;
|
||||
case IR::OP_SPLATVECTOR2: Elements = 2; break;
|
||||
default: LogMan::Msg::A("Uknown Splat size"); break;
|
||||
default: LOGMAN_MSG_A("Uknown Splat size"); break;
|
||||
}
|
||||
|
||||
uint8_t ElementSize = OpSize / Elements;
|
||||
@@ -86,7 +92,7 @@ DEF_OP(SplatVector) {
|
||||
case 8:
|
||||
movddup(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.Size); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +130,7 @@ DEF_OP(VMov) {
|
||||
movaps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +168,7 @@ DEF_OP(VAdd) {
|
||||
vpaddq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +191,7 @@ DEF_OP(VSub) {
|
||||
vpsubq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +206,7 @@ DEF_OP(VUQAdd) {
|
||||
vpaddusw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +221,7 @@ DEF_OP(VUQSub) {
|
||||
vpsubusw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +236,7 @@ DEF_OP(VSQAdd) {
|
||||
vpaddsw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +251,7 @@ DEF_OP(VSQSub) {
|
||||
vpsubsw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +288,7 @@ DEF_OP(VAddP) {
|
||||
case 4:
|
||||
vphaddd(GetDst(Node), xmm15, xmm14);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -311,7 +317,7 @@ DEF_OP(VAddP) {
|
||||
case 4:
|
||||
vphaddd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,7 +349,7 @@ DEF_OP(VAddV) {
|
||||
pinsrd(xmm15, eax, 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
movaps(Dest, xmm15);
|
||||
@@ -360,7 +366,7 @@ DEF_OP(VURAvg) {
|
||||
vpavgw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +389,7 @@ DEF_OP(VAbs) {
|
||||
vpabsq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +408,7 @@ DEF_OP(VFAdd) {
|
||||
vaddsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -416,7 +422,7 @@ DEF_OP(VFAdd) {
|
||||
vaddpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -430,7 +436,7 @@ DEF_OP(VFAddP) {
|
||||
case 8:
|
||||
vhaddpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +455,7 @@ DEF_OP(VFSub) {
|
||||
vsubsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -463,7 +469,7 @@ DEF_OP(VFSub) {
|
||||
vsubpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,7 +489,7 @@ DEF_OP(VFMul) {
|
||||
vmulsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -497,7 +503,7 @@ DEF_OP(VFMul) {
|
||||
vmulpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,7 +523,7 @@ DEF_OP(VFDiv) {
|
||||
vdivsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -531,7 +537,7 @@ DEF_OP(VFDiv) {
|
||||
vdivpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,7 +557,7 @@ DEF_OP(VFMin) {
|
||||
vminsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -565,7 +571,7 @@ DEF_OP(VFMin) {
|
||||
vminpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,7 +591,7 @@ DEF_OP(VFMax) {
|
||||
vmaxsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -599,7 +605,7 @@ DEF_OP(VFMax) {
|
||||
vmaxpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,7 +623,7 @@ DEF_OP(VFRecp) {
|
||||
vdivss(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -630,7 +636,7 @@ DEF_OP(VFRecp) {
|
||||
vdivps(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -650,7 +656,7 @@ DEF_OP(VFSqrt) {
|
||||
vsqrtsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -664,7 +670,7 @@ DEF_OP(VFSqrt) {
|
||||
vsqrtpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,7 +696,7 @@ DEF_OP(VFRSqrt) {
|
||||
divsd(GetDst(Node), xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -704,7 +710,7 @@ DEF_OP(VFRSqrt) {
|
||||
divps(GetDst(Node), xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -729,7 +735,7 @@ DEF_OP(VNeg) {
|
||||
vpsubq(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,7 +756,7 @@ DEF_OP(VFNeg) {
|
||||
vxorpd(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,20 +768,36 @@ DEF_OP(VNot) {
|
||||
|
||||
DEF_OP(VUMin) {
|
||||
auto Op = IROp->C<IR::IROp_VUMin>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
vpminub(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
if (Op->Header.Size == Op->Header.ElementSize) {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 8: {
|
||||
// This isn't very nice on x86 until AVX-512
|
||||
pextrq(TMP1, GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
pextrq(TMP2, GetSrc(Op->Header.Args[1].ID()), 0);
|
||||
cmp(TMP1, TMP2);
|
||||
cmovb(TMP2, TMP1);
|
||||
pinsrq(GetDst(Node), TMP2, 0);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
case 2: {
|
||||
vpminuw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
vpminub(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
vpminuw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
vpminud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
case 4: {
|
||||
vpminud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,7 +816,7 @@ DEF_OP(VSMin) {
|
||||
vpminsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +835,7 @@ DEF_OP(VUMax) {
|
||||
vpmaxud(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,7 +854,7 @@ DEF_OP(VSMax) {
|
||||
vpmaxsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,7 +879,7 @@ DEF_OP(VZip) {
|
||||
punpcklqdq(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
movapd(GetDst(Node), xmm15);
|
||||
}
|
||||
@@ -884,7 +906,7 @@ DEF_OP(VZip2) {
|
||||
vpunpckhdq(GetDst(Node), xmm15, xmm14);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -905,7 +927,7 @@ DEF_OP(VZip2) {
|
||||
punpckhqdq(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
movapd(GetDst(Node), xmm15);
|
||||
}
|
||||
@@ -934,7 +956,7 @@ DEF_OP(VCMPEQ) {
|
||||
case 8:
|
||||
vpcmpeqq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,7 +977,7 @@ DEF_OP(VCMPEQZ) {
|
||||
case 8:
|
||||
vpcmpeqq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,7 +997,7 @@ DEF_OP(VCMPGT) {
|
||||
case 8:
|
||||
vpcmpgtq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,7 +1018,7 @@ DEF_OP(VCMPGTZ) {
|
||||
case 8:
|
||||
vpcmpgtq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,7 +1039,7 @@ DEF_OP(VCMPLTZ) {
|
||||
case 8:
|
||||
vpcmpgtq(GetDst(Node), xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,7 +1055,7 @@ DEF_OP(VFCMPEQ) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1044,7 +1066,7 @@ DEF_OP(VFCMPEQ) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 0);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1061,7 +1083,7 @@ DEF_OP(VFCMPNEQ) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 4);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1073,7 +1095,7 @@ DEF_OP(VFCMPNEQ) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 4);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1090,7 +1112,7 @@ DEF_OP(VFCMPLT) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 1);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1101,7 +1123,7 @@ DEF_OP(VFCMPLT) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 1);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1118,7 +1140,7 @@ DEF_OP(VFCMPGT) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[1].ID()), GetSrc(Op->Header.Args[0].ID()), 1);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1129,7 +1151,7 @@ DEF_OP(VFCMPGT) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[1].ID()), GetSrc(Op->Header.Args[0].ID()), 1);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1146,7 +1168,7 @@ DEF_OP(VFCMPLE) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 2);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1157,7 +1179,7 @@ DEF_OP(VFCMPLE) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 2);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1174,7 +1196,7 @@ DEF_OP(VFCMPORD) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 7);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1185,7 +1207,7 @@ DEF_OP(VFCMPORD) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 7);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1202,7 +1224,7 @@ DEF_OP(VFCMPUNO) {
|
||||
case 8:
|
||||
vcmpsd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 3);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1213,21 +1235,21 @@ DEF_OP(VFCMPUNO) {
|
||||
case 8:
|
||||
vcmppd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()), 3);
|
||||
break;
|
||||
default: LogMan::Msg::A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unsupported elementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUShl) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShr) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VSShr) {
|
||||
LogMan::Msg::A("Unimplemented");
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShlS) {
|
||||
@@ -1246,7 +1268,7 @@ DEF_OP(VUShlS) {
|
||||
vpsllq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,7 +1288,7 @@ DEF_OP(VUShrS) {
|
||||
vpsrlq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1283,7 +1305,7 @@ DEF_OP(VSShrS) {
|
||||
break;
|
||||
}
|
||||
case 8: // Doesn't exist on x86
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1316,7 +1338,7 @@ DEF_OP(VInsElement) {
|
||||
pinsrq(xmm15, rax, Op->DestIdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
movapd(GetDst(Node), xmm15);
|
||||
@@ -1351,7 +1373,7 @@ DEF_OP(VInsScalarElement) {
|
||||
pinsrq(xmm15, rax, Op->DestIdx);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
movapd(GetDst(Node), xmm15);
|
||||
@@ -1381,7 +1403,7 @@ DEF_OP(VExtractElement) {
|
||||
pinsrq(GetDst(Node), rax, 0);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,7 +1454,7 @@ DEF_OP(VUShrI) {
|
||||
psrlq(GetDst(Node), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1448,7 +1470,7 @@ DEF_OP(VSShrI) {
|
||||
psrad(GetDst(Node), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1468,7 +1490,7 @@ DEF_OP(VShlI) {
|
||||
psllq(GetDst(Node), Op->BitShift);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1498,7 +1520,7 @@ DEF_OP(VUShrNI) {
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
vmovq(xmm15, rax);
|
||||
@@ -1534,7 +1556,7 @@ DEF_OP(VUShrNI2) {
|
||||
mov(rcx, 0x0B'0A'09'08'03'02'01'00); // Upper
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
vmovq(xmm15, rax);
|
||||
@@ -1561,7 +1583,7 @@ DEF_OP(VSXTL) {
|
||||
case 8:
|
||||
pmovsxdq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,7 +1602,7 @@ DEF_OP(VSXTL2) {
|
||||
case 8:
|
||||
pmovsxdq(GetDst(Node), GetDst(Node));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1596,7 +1618,7 @@ DEF_OP(VUXTL) {
|
||||
case 8:
|
||||
pmovzxdq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,7 +1637,7 @@ DEF_OP(VUXTL2) {
|
||||
case 8:
|
||||
pmovzxdq(GetDst(Node), GetDst(Node));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1628,7 +1650,7 @@ DEF_OP(VSQXTN) {
|
||||
case 2:
|
||||
packssdw(xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
psrldq(xmm15, 8);
|
||||
movaps(GetDst(Node), xmm15);
|
||||
@@ -1647,7 +1669,7 @@ DEF_OP(VSQXTN2) {
|
||||
case 2:
|
||||
packssdw(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
|
||||
if (OpSize == 8) {
|
||||
@@ -1665,7 +1687,7 @@ DEF_OP(VSQXTUN) {
|
||||
case 2:
|
||||
packusdw(xmm15, GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
psrldq(xmm15, 8);
|
||||
movaps(GetDst(Node), xmm15);
|
||||
@@ -1684,7 +1706,7 @@ DEF_OP(VSQXTUN2) {
|
||||
case 2:
|
||||
packusdw(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
default: LogMan::Msg::A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
default: LOGMAN_MSG_A("Unknown element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
if (OpSize == 8) {
|
||||
psrldq(xmm15, OpSize / 2);
|
||||
@@ -1704,7 +1726,7 @@ DEF_OP(VMul) {
|
||||
vpmulld(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1735,7 +1757,7 @@ DEF_OP(VUMull) {
|
||||
vpmuludq(GetDst(Node), xmm14, xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1770,7 +1792,7 @@ DEF_OP(VSMull) {
|
||||
vpmuldq(GetDst(Node), xmm14, xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1806,7 +1828,7 @@ DEF_OP(VUMull2) {
|
||||
vpmuludq(GetDst(Node), xmm14, xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1846,7 +1868,7 @@ DEF_OP(VSMull2) {
|
||||
vpmuldq(GetDst(Node), xmm14, xmm15);
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1864,13 +1886,13 @@ DEF_OP(VTBL1) {
|
||||
vpshufb(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
break;
|
||||
}
|
||||
default: LogMan::Msg::A("Unknown OpSize: %d", OpSize); break;
|
||||
default: LOGMAN_MSG_A("Unknown OpSize: %d", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void JITCore::RegisterVectorHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
|
||||
void X86JITCore::RegisterVectorHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(CREATEVECTOR2, CreateVector2);
|
||||
|
||||
+17
-8
@@ -1,6 +1,15 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|block-database
|
||||
desc: Stores information about blocks, and provides C++ implementations to lookup the blocks
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/Core.h"
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace FEXCore {
|
||||
@@ -19,27 +28,27 @@ LookupCache::LookupCache(FEXCore::Context::Context *CTX)
|
||||
// Allocate a region of memory that we can use to back our block pointers
|
||||
// We need one pointer per page of virtual memory
|
||||
// At 64GB of virtual memory this will allocate 128MB of virtual memory space
|
||||
PagePointer = reinterpret_cast<uintptr_t>(mmap(nullptr, ctx->Config.VirtualMemSize / 4096 * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
PagePointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, ctx->Config.VirtualMemSize / 4096 * 8, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
|
||||
// Allocate our memory backing our pages
|
||||
// We need 32KB per guest page (One pointer per byte)
|
||||
// XXX: We can drop down to 16KB if we store 4byte offsets from the code base
|
||||
// We currently limit to 128MB of real memory for caching for the total cache size.
|
||||
// Can end up being inefficient if we compile a small number of blocks per page
|
||||
PageMemory = reinterpret_cast<uintptr_t>(mmap(nullptr, CODE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LogMan::Throw::A(PageMemory != -1ULL, "Failed to allocate page memory");
|
||||
PageMemory = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, CODE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_A(PageMemory != -1ULL, "Failed to allocate page memory");
|
||||
|
||||
// L1 Cache
|
||||
L1Pointer = reinterpret_cast<uintptr_t>(mmap(nullptr, L1_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LogMan::Throw::A(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
|
||||
L1Pointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, L1_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_A(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
|
||||
|
||||
VirtualMemSize = ctx->Config.VirtualMemSize;
|
||||
}
|
||||
|
||||
LookupCache::~LookupCache() {
|
||||
munmap(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8);
|
||||
munmap(reinterpret_cast<void*>(PageMemory), CODE_SIZE);
|
||||
munmap(reinterpret_cast<void*>(L1Pointer), L1_SIZE);
|
||||
FEXCore::Allocator::munmap(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8);
|
||||
FEXCore::Allocator::munmap(reinterpret_cast<void*>(PageMemory), CODE_SIZE);
|
||||
FEXCore::Allocator::munmap(reinterpret_cast<void*>(L1Pointer), L1_SIZE);
|
||||
}
|
||||
|
||||
void LookupCache::HintUsedRange(uint64_t Address, uint64_t Size) {
|
||||
|
||||
+8
-5
@@ -39,13 +39,17 @@ public:
|
||||
|
||||
void AddBlockMapping(uint64_t Address, void *HostCode, uint64_t Start, uint64_t Length) {
|
||||
auto InsertPoint = BlockList.emplace(Address, (uintptr_t)HostCode);
|
||||
LogMan::Throw::A(InsertPoint.second == true, "Dupplicate block mapping added");
|
||||
LOGMAN_THROW_A(InsertPoint.second == true, "Dupplicate block mapping added");
|
||||
|
||||
for (auto CurrentPage = Start >> 12, EndPage = (Start + Length) >> 12; CurrentPage <= EndPage; CurrentPage++) {
|
||||
CodePages[CurrentPage].push_back(Address);
|
||||
}
|
||||
|
||||
// no need to update L1 or L2, they will get updated on first lookup
|
||||
// There is no need to update L1 or L2, they will get updated on first lookup
|
||||
// However, adding to L1 here increases performance
|
||||
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = (uintptr_t)HostCode;
|
||||
}
|
||||
|
||||
void Erase(uint64_t Address) {
|
||||
@@ -105,9 +109,8 @@ private:
|
||||
void CacheBlockMapping(uint64_t Address, uintptr_t HostCode) {
|
||||
// Do L1
|
||||
auto &L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
if (L1Entry.GuestCode == Address) {
|
||||
L1Entry.GuestCode = L1Entry.HostCode = 0;
|
||||
}
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = HostCode;
|
||||
|
||||
// Do ful map
|
||||
auto FullAddress = Address;
|
||||
|
||||
+329
-127
File diff suppressed because it is too large.
Load diff
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
OrderedNode* GetNewJumpBlock(uint64_t RIP) {
|
||||
auto it = JumpTargets.find(RIP);
|
||||
LogMan::Throw::A(it != JumpTargets.end(), "Couldn't find block generated for 0x%lx", RIP);
|
||||
LOGMAN_THROW_A(it != JumpTargets.end(), "Couldn't find block generated for 0x%lx", RIP);
|
||||
return it->second.BlockEntry;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
it->second.HaveEmitted = true;
|
||||
|
||||
if (CurrentCodeBlock->Wrapped(ListData.Begin()).ID() == it->second.BlockEntry->Wrapped(ListData.Begin()).ID()) return;
|
||||
if (CurrentCodeBlock->Wrapped(DualListData.ListBegin()).ID() == it->second.BlockEntry->Wrapped(DualListData.ListBegin()).ID()) return;
|
||||
|
||||
// We have hit a RIP that is a jump target
|
||||
// Thus we need to end up in a new block
|
||||
@@ -81,14 +81,14 @@ public:
|
||||
// rdi, 0x8
|
||||
// cmp qword [rdi-8], 0
|
||||
// jne .label
|
||||
if (!BlockSetRIP) {
|
||||
if (LastOp && !BlockSetRIP) {
|
||||
auto it = JumpTargets.find(NextRIP);
|
||||
if (it == JumpTargets.end() && LastOp) {
|
||||
if (it == JumpTargets.end()) {
|
||||
|
||||
uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4;
|
||||
// If we don't have a jump target to a new block then we have to leave
|
||||
// Set the RIP to the next instruction and leave
|
||||
auto RelocatedNextRIP = _EntrypointOffset(NextRIP - Current_Header->Entry, GPRSize);
|
||||
auto RelocatedNextRIP = _EntrypointOffset(NextRIP - Entry, GPRSize);
|
||||
_ExitFunction(RelocatedNextRIP);
|
||||
}
|
||||
else if (it != JumpTargets.end()) {
|
||||
@@ -104,6 +104,7 @@ public:
|
||||
OpDispatchBuilder(FEXCore::Context::Context *ctx);
|
||||
|
||||
void ResetWorkingList();
|
||||
void ResetDecodeFailure() { DecodeFailure = false; }
|
||||
bool HadDecodeFailure() { return DecodeFailure; }
|
||||
|
||||
void BeginFunction(uint64_t RIP, std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
|
||||
@@ -458,6 +459,8 @@ public:
|
||||
template<uint8_t FenceType>
|
||||
void FenceOp(OpcodeArgs);
|
||||
|
||||
void StoreFenceOrCLFlush(OpcodeArgs);
|
||||
|
||||
void PSADBW(OpcodeArgs);
|
||||
|
||||
void AESImcOp(OpcodeArgs);
|
||||
@@ -475,8 +478,8 @@ public:
|
||||
OrderedNode *GetPackedRFLAG(bool Lower8);
|
||||
|
||||
void SetMultiblock(bool _Multiblock) { Multiblock = _Multiblock; }
|
||||
bool GetMultiblock() { return Multiblock; }
|
||||
|
||||
bool HandledLock = false;
|
||||
private:
|
||||
bool DecodeFailure{false};
|
||||
FEXCore::IR::IROp_IRHeader *Current_Header{};
|
||||
|
||||
+13
-5
@@ -1,4 +1,12 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|x86-guest-code
|
||||
desc: Guest-side assembly helpers used by the backends
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86HelperGen.h"
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
@@ -24,15 +32,15 @@ X86GeneratedCode::X86GeneratedCode() {
|
||||
}
|
||||
|
||||
X86GeneratedCode::~X86GeneratedCode() {
|
||||
munmap(CodePtr, CODE_SIZE);
|
||||
FEXCore::Allocator::munmap(CodePtr, CODE_SIZE);
|
||||
}
|
||||
|
||||
void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
|
||||
FEXCore::Config::Value<bool> Is64BitMode{FEXCore::Config::CONFIG_IS64BIT_MODE, 0};
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
|
||||
if (Is64BitMode()) {
|
||||
// 64bit mode can have its sigret handler anywhere
|
||||
return mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
return FEXCore::Allocator::mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
// First 64bit page
|
||||
@@ -42,14 +50,14 @@ void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
|
||||
// We need to have the sigret handler in the lower 32bits of memory space
|
||||
// Scan top down and try to allocate a location
|
||||
for (size_t Location = 0xFFFF'E000; Location != 0x0; Location -= 0x1000) {
|
||||
void *Ptr = mmap(reinterpret_cast<void*>(Location), Size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
void *Ptr = FEXCore::Allocator::mmap(reinterpret_cast<void*>(Location), Size, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
if (Ptr != MAP_FAILED &&
|
||||
reinterpret_cast<uintptr_t>(Ptr) >= LOCATION_MAX) {
|
||||
// Failed to map in the lower 32bits
|
||||
// Try again
|
||||
// Can happen in the case that host kernel ignores MAP_FIXED_NOREPLACE
|
||||
munmap(Ptr, Size);
|
||||
FEXCore::Allocator::munmap(Ptr, Size);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|x86-guest-code
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
meta: frontend|x86-tables ~ Metadata that drives the frontend x86/64 decoding
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
@@ -279,13 +285,13 @@ void InitializeBaseTables(Context::OperatingMode Mode) {
|
||||
{0xEA, 1, X86InstInfo{"JMPF", TYPE_INST, FLAGS_NONE, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(BaseOps, BaseOpTable, sizeof(BaseOpTable) / sizeof(BaseOpTable[0]));
|
||||
GenerateTable(BaseOps, BaseOpTable, std::size(BaseOpTable));
|
||||
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(BaseOps, BaseOpTable_64, sizeof(BaseOpTable_64) / sizeof(BaseOpTable_64[0]));
|
||||
GenerateTable(BaseOps, BaseOpTable_64, std::size(BaseOpTable_64));
|
||||
}
|
||||
else {
|
||||
GenerateTable(BaseOps, BaseOpTable_32, sizeof(BaseOpTable_32) / sizeof(BaseOpTable_32[0]));
|
||||
GenerateTable(BaseOps, BaseOpTable_32, std::size(BaseOpTable_32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -38,6 +44,6 @@ void InitializeDDDTables() {
|
||||
{0xB7, 1, X86InstInfo{"PMULHRW", TYPE_3DNOW_INST, FLAGS_MODRM, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(DDDNowOps, DDDNowOpTable, sizeof(DDDNowOpTable) / sizeof(DDDNowOpTable[0]));
|
||||
GenerateTable(DDDNowOps, DDDNowOpTable, std::size(DDDNowOpTable));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -20,6 +26,6 @@ void InitializeEVEXTables() {
|
||||
{0xE7, 1, X86InstInfo{"VMOVNTDQ", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(EVEXTableOps, EVEXTable, sizeof(EVEXTable) / sizeof(EVEXTable[0]));
|
||||
GenerateTable(EVEXTableOps, EVEXTable, std::size(EVEXTable));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -54,7 +60,7 @@ void InitializeH0F38Tables() {
|
||||
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
@@ -92,6 +98,6 @@ void InitializeH0F38Tables() {
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(H0F38TableOps, H0F38Table, sizeof(H0F38Table) / sizeof(H0F38Table[0]));
|
||||
GenerateTable(H0F38TableOps, H0F38Table, std::size(H0F38Table));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -48,10 +54,10 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
|
||||
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(H0F3ATableOps, H0F3ATable, sizeof(H0F3ATable) / sizeof(H0F3ATable[0]));
|
||||
GenerateTable(H0F3ATableOps, H0F3ATable, std::size(H0F3ATable));
|
||||
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(H0F3ATableOps, H0F3ATable_64, sizeof(H0F3ATable_64) / sizeof(H0F3ATable_64[0]));
|
||||
GenerateTable(H0F3ATableOps, H0F3ATable_64, std::size(H0F3ATable_64));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -152,12 +158,12 @@ void InitializePrimaryGroupTables(Context::OperatingMode Mode) {
|
||||
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, sizeof(PrimaryGroupOpTable) / sizeof(PrimaryGroupOpTable[0]));
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable, std::size(PrimaryGroupOpTable));
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, sizeof(PrimaryGroupOpTable_64) / sizeof(PrimaryGroupOpTable_64[0]));
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_64, std::size(PrimaryGroupOpTable_64));
|
||||
}
|
||||
else {
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, sizeof(PrimaryGroupOpTable_32) / sizeof(PrimaryGroupOpTable_32[0]));
|
||||
GenerateTable(PrimaryInstGroupOps, PrimaryGroupOpTable_32, std::size(PrimaryGroupOpTable_32));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -477,7 +483,7 @@ void InitializeSecondaryGroupTables() {
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, sizeof(SecondaryExtensionOpTable) / sizeof(SecondaryExtensionOpTable[0]));
|
||||
GenerateTable(SecondInstGroupOps, SecondaryExtensionOpTable, std::size(SecondaryExtensionOpTable));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -46,6 +52,6 @@ void InitializeSecondaryModRMTables() {
|
||||
{((3 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, sizeof(SecondaryModRMExtensionOpTable) / sizeof(SecondaryModRMExtensionOpTable[0]));
|
||||
GenerateTable(SecondModRMTableOps, SecondaryModRMExtensionOpTable, std::size(SecondaryModRMExtensionOpTable));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -571,18 +577,18 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
|
||||
{0xFF, 1, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}},
|
||||
};
|
||||
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable, sizeof(TwoByteOpTable) / sizeof(TwoByteOpTable[0]));
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable, std::size(TwoByteOpTable));
|
||||
|
||||
if (Mode == Context::MODE_64BIT) {
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable_64, sizeof(TwoByteOpTable_64) / sizeof(TwoByteOpTable_64[0]));
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable_64, std::size(TwoByteOpTable_64));
|
||||
}
|
||||
else {
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable_32, sizeof(TwoByteOpTable_32) / sizeof(TwoByteOpTable_32[0]));
|
||||
GenerateTable(SecondBaseOps, TwoByteOpTable_32, std::size(TwoByteOpTable_32));
|
||||
}
|
||||
|
||||
GenerateTableWithCopy(RepModOps, RepModOpTable, sizeof(RepModOpTable) / sizeof(RepModOpTable[0]), SecondBaseOps);
|
||||
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, sizeof(RepNEModOpTable) / sizeof(RepNEModOpTable[0]), SecondBaseOps);
|
||||
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, sizeof(OpSizeModOpTable) / sizeof(OpSizeModOpTable[0]), SecondBaseOps);
|
||||
GenerateTableWithCopy(RepModOps, RepModOpTable, std::size(RepModOpTable), SecondBaseOps);
|
||||
GenerateTableWithCopy(RepNEModOps, RepNEModOpTable, std::size(RepNEModOpTable), SecondBaseOps);
|
||||
GenerateTableWithCopy(OpSizeModOps, OpSizeModOpTable, std::size(OpSizeModOpTable), SecondBaseOps);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -500,7 +506,7 @@ void InitializeVEXTables() {
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(VEXTableOps, VEXTable, sizeof(VEXTable) / sizeof(VEXTable[0]));
|
||||
GenerateTable(VEXTableGroupOps, VEXGroupTable, sizeof(VEXGroupTable) / sizeof(VEXGroupTable[0]));
|
||||
GenerateTable(VEXTableOps, VEXTable, std::size(VEXTable));
|
||||
GenerateTable(VEXTableGroupOps, VEXGroupTable, std::size(VEXGroupTable));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <FEXCore/Debug/X86Tables.h>
|
||||
|
||||
@@ -27,7 +33,7 @@ static inline void GenerateTable(X86InstInfo *FinalTable, U8U8InfoStruct const *
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
FinalTable[OpNum + i] = Info;
|
||||
#ifndef NDEBUG
|
||||
++Total;
|
||||
@@ -44,7 +50,7 @@ static inline void GenerateTable(X86InstInfo *FinalTable, U16U8InfoStruct const
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
FinalTable[OpNum + i] = Info;
|
||||
#ifndef NDEBUG
|
||||
++Total;
|
||||
@@ -61,7 +67,7 @@ static inline void GenerateTableWithCopy(X86InstInfo *FinalTable, U8U8InfoStruct
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
if (Info.Type == TYPE_COPY_OTHER) {
|
||||
FinalTable[OpNum + i] = OtherLocal[OpNum + i];
|
||||
}
|
||||
@@ -83,7 +89,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, U16U8InfoStruct con
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LogMan::Throw::A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_A(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry %s->%s", FinalTable[OpNum + i].Name, Info.Name);
|
||||
if ((OpNum & 0b11'000'000) == 0b11'000'000) {
|
||||
// If the mod field is 0b11 then it is a regular op
|
||||
FinalTable[OpNum + i] = Info;
|
||||
@@ -91,7 +97,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, U16U8InfoStruct con
|
||||
else {
|
||||
// If the mod field is !0b11 then this instruction is duplicated through the whole mod [0b00, 0b10] range
|
||||
// and the modrm.rm space because that is used part of the instruction encoding
|
||||
LogMan::Throw::A((OpNum & 0b11'000'000) == 0, "Only support mod field of zero in this path");
|
||||
LOGMAN_THROW_A((OpNum & 0b11'000'000) == 0, "Only support mod field of zero in this path");
|
||||
for (uint16_t mod = 0b00'000'000; mod < 0b11'000'000; mod += 0b01'000'000) {
|
||||
for (uint16_t rm = 0b000; rm < 0b1'000; ++rm) {
|
||||
FinalTable[(OpNum | mod | rm) + i] = Info;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -254,6 +260,6 @@ void InitializeX87Tables() {
|
||||
#undef OPD
|
||||
#undef OPDReg
|
||||
|
||||
GenerateX87Table(X87Ops, X87OpTable, sizeof(X87OpTable) / sizeof(X87OpTable[0]));
|
||||
GenerateX87Table(X87Ops, X87OpTable, std::size(X87OpTable));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: frontend|x86-tables
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/X86Tables/X86Tables.h"
|
||||
|
||||
namespace FEXCore::X86Tables {
|
||||
@@ -119,7 +125,7 @@ void InitializeXOPTables() {
|
||||
};
|
||||
#undef OPD
|
||||
|
||||
GenerateTable(XOPTableOps, XOPTable, sizeof(XOPTable) / sizeof(XOPTable[0]));
|
||||
GenerateTable(XOPTableGroupOps, XOPGroupTable, sizeof(XOPGroupTable) / sizeof(XOPGroupTable[0]));
|
||||
GenerateTable(XOPTableOps, XOPTable, std::size(XOPTable));
|
||||
GenerateTable(XOPTableGroupOps, XOPGroupTable, std::size(XOPGroupTable));
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
meta: glue|thunks ~ FEXCore side of thunks: Registration, Lookup
|
||||
tags: glue|thunks
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include "Thunks.h"
|
||||
|
||||
@@ -23,7 +30,7 @@ static thread_local FEXCore::Core::InternalThreadState *Thread;
|
||||
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
|
||||
struct ExportEntry { uint8_t *sha256; ThunkedFunction* Fn; };
|
||||
|
||||
class ThunkHandler_impl final: public ThunkHandler {
|
||||
@@ -41,9 +48,8 @@ namespace FEXCore {
|
||||
Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback
|
||||
*/
|
||||
static void CallCallback(void *callback, void *arg0, void* arg1) {
|
||||
|
||||
Thread->State.State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
|
||||
Thread->State.State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
|
||||
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0;
|
||||
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1;
|
||||
|
||||
Thread->CTX->HandleCallback((uintptr_t)callback);
|
||||
}
|
||||
@@ -57,10 +63,10 @@ namespace FEXCore {
|
||||
auto Name = Args->Name;
|
||||
auto CallbackThunks = Args->CallbackThunks;
|
||||
|
||||
auto SOName = CTX->Config.ThunkLibsPath + "/" + (const char*)Name + "-host.so";
|
||||
auto SOName = CTX->Config.ThunkHostLibsPath() + "/" + (const char*)Name + "-host.so";
|
||||
|
||||
LogMan::Msg::D("Load lib: %s -> %s", Name, SOName.c_str());
|
||||
|
||||
|
||||
auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW);
|
||||
|
||||
if (!Handle) {
|
||||
@@ -78,7 +84,7 @@ namespace FEXCore {
|
||||
LogMan::Msg::E("Load lib: failed to find export %s", InitSym.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto Exports = InitFN((void*)&CallCallback, CallbackThunks);
|
||||
|
||||
auto That = reinterpret_cast<ThunkHandler_impl*>(CTX->ThunkHandler.get());
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
$info$
|
||||
tags: glue|thunks
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
|
||||
+16
-3
@@ -79,7 +79,6 @@
|
||||
"Blocks"
|
||||
],
|
||||
"Args": [
|
||||
"uint64_t", "Entry",
|
||||
"uint32_t", "BlockCount"
|
||||
]
|
||||
},
|
||||
@@ -793,6 +792,17 @@
|
||||
]
|
||||
},
|
||||
|
||||
"CacheLineClear": {
|
||||
"Desc": ["Does a 64 byte cacheline clear at the address specified"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Addr"
|
||||
]
|
||||
},
|
||||
|
||||
"Add": {
|
||||
"Desc": [ "Integer Add",
|
||||
"Will truncate to 64 or 32bits"
|
||||
@@ -1138,7 +1148,11 @@
|
||||
"DestClass": "GPRPair",
|
||||
"FixedDestSize": "8",
|
||||
"NumElements": "2",
|
||||
"SSAArgs": "1"
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Function",
|
||||
"Leaf"
|
||||
]
|
||||
},
|
||||
|
||||
"Bfi": {
|
||||
@@ -1559,7 +1573,6 @@
|
||||
"Depending on backend, may only support GPR printing"
|
||||
],
|
||||
"OpClass": "Misc",
|
||||
"DestSize": "GetOpSize(ssa0)",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Value"
|
||||
|
||||
+9
-2
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
meta: ir|dumper ~ IR -> Text
|
||||
tags: ir|dumper
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
@@ -24,6 +31,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
|
||||
*out << "#0x" << std::hex << Arg;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const char* Arg) {
|
||||
*out << Arg;
|
||||
}
|
||||
@@ -91,7 +99,7 @@ static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWr
|
||||
*out << "%ssa" << std::to_string(Arg.ID());
|
||||
if (RAData) {
|
||||
auto PhyReg = RAData->GetNodeRegister(Arg.ID());
|
||||
|
||||
|
||||
switch (PhyReg.Class) {
|
||||
case FEXCore::IR::GPRClass.Val: *out << "(GPR"; break;
|
||||
case FEXCore::IR::GPRFixedClass.Val: *out << "(GPRFixed"; break;
|
||||
@@ -159,7 +167,6 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationDa
|
||||
++CurrentIndent;
|
||||
AddIndent();
|
||||
*out << "(%ssa0) " << "IRHeader ";
|
||||
*out << "#0x" << std::hex << HeaderOp->Entry << ", ";
|
||||
*out << "%ssa" << HeaderOp->Blocks.ID() << ", ";
|
||||
*out << "#" << std::dec << HeaderOp->BlockCount << std::endl;
|
||||
|
||||
|
||||
+23
-17
@@ -1,19 +1,25 @@
|
||||
/*
|
||||
$info$
|
||||
meta: ir|emitter ~ C++ Functions to generate IR. See IR.json for spec.
|
||||
tags: ir|emitter
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
void IREmitter::ResetWorkingList() {
|
||||
Data.Reset();
|
||||
ListData.Reset();
|
||||
DualListData.Reset();
|
||||
CodeBlocks.clear();
|
||||
CurrentWriteCursor = nullptr;
|
||||
// This is necessary since we do "null" pointer checks
|
||||
InvalidNode = reinterpret_cast<OrderedNode*>(ListData.Allocate(sizeof(OrderedNode)));
|
||||
InvalidNode = reinterpret_cast<OrderedNode*>(DualListData.ListAllocate(sizeof(OrderedNode)));
|
||||
memset(InvalidNode, 0, sizeof(OrderedNode));
|
||||
CurrentCodeBlock = nullptr;
|
||||
}
|
||||
|
||||
void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode, AllNodesIterator After, AllNodesIterator End) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t ListBegin = DualListData.ListBegin();
|
||||
auto NodeId = Node->Wrapped(ListBegin).ID();
|
||||
|
||||
while (After != End) {
|
||||
@@ -38,8 +44,8 @@ void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode,
|
||||
}
|
||||
|
||||
void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
uintptr_t ListBegin = DualListData.ListBegin();
|
||||
uintptr_t DataBegin = DualListData.DataBegin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
OrderedNodeWrapper OldArgWrapper = IROp->Args[Arg];
|
||||
@@ -50,8 +56,8 @@ void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode
|
||||
}
|
||||
|
||||
void IREmitter::RemoveArgUses(OrderedNode *Node) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
uintptr_t ListBegin = DualListData.ListBegin();
|
||||
uintptr_t DataBegin = DualListData.DataBegin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
|
||||
@@ -65,7 +71,7 @@ void IREmitter::RemoveArgUses(OrderedNode *Node) {
|
||||
void IREmitter::Remove(OrderedNode *Node) {
|
||||
RemoveArgUses(Node);
|
||||
|
||||
Node->Unlink(ListData.Begin());
|
||||
Node->Unlink(DualListData.ListBegin());
|
||||
}
|
||||
|
||||
IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode* insertAfter) {
|
||||
@@ -76,14 +82,14 @@ IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode
|
||||
if (insertAfter) {
|
||||
LinkCodeBlocks(insertAfter, CodeNode);
|
||||
} else {
|
||||
LogMan::Throw::A(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
|
||||
|
||||
LOGMAN_THROW_A(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
|
||||
|
||||
// Find last block
|
||||
auto LastBlock = CurrentCodeBlock;
|
||||
|
||||
while (LastBlock->Header.Next.GetNode(ListData.Begin()) != InvalidNode)
|
||||
LastBlock = LastBlock->Header.Next.GetNode(ListData.Begin());
|
||||
|
||||
while (LastBlock->Header.Next.GetNode(DualListData.ListBegin()) != InvalidNode)
|
||||
LastBlock = LastBlock->Header.Next.GetNode(DualListData.ListBegin());
|
||||
|
||||
// Append it after the last block
|
||||
LinkCodeBlocks(LastBlock, CodeNode);
|
||||
}
|
||||
@@ -95,12 +101,12 @@ IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode
|
||||
|
||||
void IREmitter::SetCurrentCodeBlock(OrderedNode *Node) {
|
||||
CurrentCodeBlock = Node;
|
||||
LogMan::Throw::A(Node->Op(Data.Begin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(Data.Begin())->Op)).c_str());
|
||||
SetWriteCursor(Node->Op(Data.Begin())->CW<IROp_CodeBlock>()->Begin.GetNode(ListData.Begin()));
|
||||
LOGMAN_THROW_A(Node->Op(DualListData.DataBegin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(DualListData.DataBegin())->Op)).c_str());
|
||||
SetWriteCursor(Node->Op(DualListData.DataBegin())->CW<IROp_CodeBlock>()->Begin.GetNode(DualListData.ListBegin()));
|
||||
}
|
||||
|
||||
void IREmitter::ReplaceWithConstant(OrderedNode *Node, uint64_t Value) {
|
||||
auto Header = Node->Op(Data.Begin());
|
||||
auto Header = Node->Op(DualListData.DataBegin());
|
||||
|
||||
if (IRSizes[Header->Op] >= sizeof(IROp_Constant)) {
|
||||
// Unlink any arguments the node currently has
|
||||
|
||||
+14
-9
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
$info$
|
||||
meta: ir|parser ~ Text -> IR
|
||||
tags: ir|parser
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
@@ -490,13 +497,11 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Entry = DecodeValue<uint64_t>(Def.Args[0]);
|
||||
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[2]);
|
||||
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[1]);
|
||||
|
||||
if (!CheckPrintError(Def, Entry.first)) return false;
|
||||
if (!CheckPrintError(Def, CodeBlockCount.first)) return false;
|
||||
|
||||
IRHeader = _IRHeader(InvalidNode, Entry.second, CodeBlockCount.second);
|
||||
IRHeader = _IRHeader(InvalidNode, CodeBlockCount.second);
|
||||
}
|
||||
|
||||
SetWriteCursor(nullptr); // isolate the header from everything following
|
||||
@@ -515,7 +520,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
if (i == 1) {
|
||||
// First code block is the entry block
|
||||
// Link the header to the first block
|
||||
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(ListData.Begin());
|
||||
IRHeader.first->Blocks = CodeBlock.Node->Wrapped(DualListData.ListBegin());
|
||||
}
|
||||
CodeBlocks.emplace_back(CodeBlock.Node);
|
||||
}
|
||||
@@ -548,7 +553,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
CurrentBlock = Def.Node;
|
||||
CurrentBlockOp = CurrentBlock->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
CurrentBlockOp = CurrentBlock->Op(DualListData.DataBegin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -566,7 +571,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
if (!CheckPrintError(Def, Adjust.first)) return false;
|
||||
|
||||
Def.Node = _BeginBlock(Adjust.second);
|
||||
CurrentBlockOp->Begin = Def.Node->Wrapped(ListData.Begin());
|
||||
CurrentBlockOp->Begin = Def.Node->Wrapped(DualListData.ListBegin());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -583,7 +588,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
if (!CheckPrintError(Def, Adjust.first)) return false;
|
||||
|
||||
Def.Node = _EndBlock(Adjust.second);
|
||||
CurrentBlockOp->Last = Def.Node->Wrapped(ListData.Begin());
|
||||
CurrentBlockOp->Last = Def.Node->Wrapped(DualListData.ListBegin());
|
||||
|
||||
CurrentBlock = nullptr;
|
||||
CurrentBlockOp = nullptr;
|
||||
@@ -610,7 +615,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
if (Def.HasDefinition) {
|
||||
auto IROp = Def.Node->Op(Data.Begin());
|
||||
auto IROp = Def.Node->Op(DualListData.DataBegin());
|
||||
if (Def.Size.Elements()) {
|
||||
IROp->Size = Def.Size.Bytes() * Def.Size.Elements();
|
||||
IROp->ElementSize = Def.Size.Bytes();
|
||||
|
||||
+16
-1
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
$info$
|
||||
meta: ir|opts ~ IR to IR Optimization
|
||||
tags: ir|opts
|
||||
desc: Defines which passes are run, and runs them
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/IR/PassManager.h"
|
||||
#include "Interface/IR/Passes.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
@@ -7,10 +15,17 @@
|
||||
namespace FEXCore::IR {
|
||||
|
||||
void PassManager::AddDefaultPasses(bool InlineConstants, bool StaticRegisterAllocation) {
|
||||
FEXCore::Config::Value<bool> DisablePasses{FEXCore::Config::CONFIG_DEBUG_DISABLE_OPTIMIZATION_PASSES, false};
|
||||
FEX_CONFIG_OPT(DisablePasses, O0);
|
||||
|
||||
if (!DisablePasses()) {
|
||||
InsertPass(CreateContextLoadStoreElimination());
|
||||
|
||||
if (Is64BitMode()) {
|
||||
// This needs to run after RCLSE
|
||||
// This only matters for 64-bit code since these instructions don't exist in 32-bit
|
||||
InsertPass(CreateLongDivideEliminationPass());
|
||||
}
|
||||
|
||||
InsertPass(CreateDeadStoreElimination());
|
||||
InsertPass(CreatePassDeadCodeElimination());
|
||||
InsertPass(CreateConstProp(InlineConstants));
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
/*
|
||||
$info$
|
||||
tags: ir|opts
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
@@ -77,6 +84,8 @@ private:
|
||||
ValidationPasses.emplace_back(Pass);
|
||||
}
|
||||
#endif
|
||||
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ FEXCore::IR::Pass* CreatePassDeadCodeElimination();
|
||||
FEXCore::IR::Pass* CreateIRCompaction();
|
||||
FEXCore::IR::RegisterAllocationPass* CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA);
|
||||
FEXCore::IR::Pass* CreateStaticRegisterAllocationPass();
|
||||
FEXCore::IR::Pass* CreateLongDivideEliminationPass();
|
||||
|
||||
namespace Validation {
|
||||
FEXCore::IR::Pass* CreateIRValidation();
|
||||
|
||||
+17
-10
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
$info$
|
||||
tags: ir|opts
|
||||
desc: ConstProp, ZExt elim, addressgen coalesce, const pooling, fcmp reduction, const inlining
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
|
||||
#if defined(_M_ARM_64)
|
||||
//aarch64 heuristics
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
@@ -158,13 +166,8 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
|
||||
bool Changed = false;
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
|
||||
auto Header = CurrentIR.GetHeader();
|
||||
|
||||
auto OriginalWriteCursor = IREmit->GetWriteCursor();
|
||||
|
||||
auto HeaderOp = CurrentIR.GetHeader();
|
||||
|
||||
{
|
||||
|
||||
// constants are pooled per block
|
||||
@@ -197,7 +200,8 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
auto SelectOp = SelectOpHdr->CW<IR::IROp_Select>();
|
||||
|
||||
// the value isn't used after the select otherwise
|
||||
if (SelectOpHdr->Op == OP_SELECT && SelectOpNode->NumUses == 1
|
||||
// make sure the sizes match
|
||||
if (SelectOpHdr->Size == UnaryOpHdr->Size && SelectOpHdr->Op == OP_SELECT && SelectOpNode->NumUses == 1
|
||||
&& IREmit->IsValueConstant(SelectOp->TrueVal)
|
||||
&& IREmit->IsValueConstant(SelectOp->FalseVal)) {
|
||||
|
||||
@@ -256,7 +260,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
auto ghf = IROp->CW<IR::IROp_GetHostFlag>();
|
||||
|
||||
auto fcmp = IREmit->GetOpHeader(ghf->GPR)->CW<IR::IROp_FCmp>();
|
||||
LogMan::Throw::A(fcmp->Header.Op == OP_FCMP || fcmp->Header.Op == OP_F80CMP, "Unexpected OP_GETHOSTFLAG source");
|
||||
LOGMAN_THROW_A(fcmp->Header.Op == OP_FCMP || fcmp->Header.Op == OP_F80CMP, "Unexpected OP_GETHOSTFLAG source");
|
||||
if(fcmp->Header.Op == OP_FCMP) {
|
||||
fcmp->Flags |= 1 << ghf->Flag;
|
||||
}
|
||||
@@ -459,7 +463,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
LOGMAN_MSG_A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -475,7 +479,7 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
uint64_t Constant1;
|
||||
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
LOGMAN_MSG_A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -743,8 +747,11 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 338 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user