mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 22:00:19 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85c2889652 | ||
|
|
23dd056b60 | ||
|
|
71043e372a | ||
|
|
c412d073b9 | ||
|
|
8fb03ff1b9 | ||
|
|
c2b6aef6f4 | ||
|
|
24547318c6 | ||
|
|
b693112c80 | ||
|
|
48d1184066 | ||
|
|
8da9ebc2e0 | ||
|
|
5ba510474b | ||
|
|
51214d1be1 | ||
|
|
4d6e15d7af | ||
|
|
4721894427 | ||
|
|
d429865b6e | ||
|
|
ca5881a72c | ||
|
|
7151b9daff | ||
|
|
d9b5e28b22 | ||
|
|
aa7954a7d6 | ||
|
|
802c70d1ab | ||
|
|
8f905988e9 | ||
|
|
478c5595ad | ||
|
|
3977e1f29e | ||
|
|
2b1ef97354 | ||
|
|
eaddf7f1a5 | ||
|
|
3237de3085 | ||
|
|
df3d398d31 | ||
|
|
b44b3401b7 | ||
|
|
c28ca0fac9 | ||
|
|
235e2b6c2c | ||
|
|
d68b84bc27 | ||
|
|
edca528608 | ||
|
|
b75e8f2abf | ||
|
|
ec3158e4cd | ||
|
|
9c8c8041e0 | ||
|
|
e3adaacb51 | ||
|
|
c49e11484f | ||
|
|
7b4b9a80fa | ||
|
|
c7ad066987 | ||
|
|
f4d229f1ba | ||
|
|
25a8a00771 | ||
|
|
a67f7422b2 | ||
|
|
ed8150cfb6 | ||
|
|
462a163ba7 | ||
|
|
6374175a64 | ||
|
|
280b15ba2a | ||
|
|
2a0b488e99 | ||
|
|
3ef7c4ab51 | ||
|
|
e72d746036 | ||
|
|
0bb4091e34 | ||
|
|
6822fc595c | ||
|
|
a506a589dd | ||
|
|
684a5977dd | ||
|
|
ac3682e058 | ||
|
|
d5faf01f5a | ||
|
|
ecba1b6838 | ||
|
|
8d8b029285 | ||
|
|
bf6f855868 | ||
|
|
aa6a499329 | ||
|
|
0971650ef9 | ||
|
|
64c4fdccf7 | ||
|
|
d715ffbc8e | ||
|
|
aef801b5b5 | ||
|
|
6c9e29796b | ||
|
|
364bb3ac1e | ||
|
|
428ea68507 | ||
|
|
5cf59408a7 | ||
|
|
1596843015 | ||
|
|
af6582ff5b | ||
|
|
1799d4c675 | ||
|
|
808e1c0330 | ||
|
|
dacd96cab5 | ||
|
|
ca4d3bf64d | ||
|
|
ea38b043c1 | ||
|
|
a39746df2e | ||
|
|
2367a8e50b | ||
|
|
cb121d7f17 | ||
|
|
412793c21d | ||
|
|
ce2286c48b | ||
|
|
d5694d6de0 | ||
|
|
121218aa8a | ||
|
|
6bb53fa758 | ||
|
|
89aa0c5471 | ||
|
|
53fcbf6afa | ||
|
|
2f5643ae6b | ||
|
|
d162ac8b3d | ||
|
|
c9a704fbde | ||
|
|
9d5a822a3a | ||
|
|
50eba4066a | ||
|
|
6116ae5330 | ||
|
|
447226576f | ||
|
|
3f8b872f17 | ||
|
|
e573ddc2db | ||
|
|
fe9aa681f0 | ||
|
|
1b2f2c1559 | ||
|
|
84c75a86c3 | ||
|
|
eedbde6f15 | ||
|
|
4e441e5a08 | ||
|
|
3e287a36c2 | ||
|
|
eadc477695 | ||
|
|
59aa324678 | ||
|
|
219bce1467 | ||
|
|
46bde401bd | ||
|
|
01beac4956 | ||
|
|
fcd981e6b7 | ||
|
|
825833cfcc | ||
|
|
4a4c49bf68 | ||
|
|
763cea423a | ||
|
|
0fee355ff5 | ||
|
|
6f6f3c9dc5 | ||
|
|
25e5d88ab2 | ||
|
|
87013340bb | ||
|
|
47c075ccc9 | ||
|
|
b1a32d4ccf | ||
|
|
7af6a8dbdf | ||
|
|
383e99e4ef | ||
|
|
8c7cfc4d11 | ||
|
|
496ee730c8 | ||
|
|
71f7ff5101 | ||
|
|
1ea00f68a2 | ||
|
|
8df7c2d84f | ||
|
|
46557a7a1f | ||
|
|
d8c2a8271f | ||
|
|
cc4c705fc0 | ||
|
|
22f249fcf6 | ||
|
|
c262362a03 | ||
|
|
212df9aa7b | ||
|
|
691e39ec76 | ||
|
|
107cae2975 | ||
|
|
6742e0c376 | ||
|
|
8f70137b1a | ||
|
|
707db51b1b | ||
|
|
5b5fa1aa29 | ||
|
|
2b9cc9666a | ||
|
|
82eba22292 | ||
|
|
5c84e8f23c | ||
|
|
081b61677a | ||
|
|
9c54814b98 | ||
|
|
35d7b855ed | ||
|
|
0b8799274c | ||
|
|
ace2b737d8 | ||
|
|
ad85268524 | ||
|
|
832a320e22 | ||
|
|
83763df6fd | ||
|
|
bee868e9ba | ||
|
|
c41de81694 | ||
|
|
41aaeb1ff0 | ||
|
|
16be2792ab | ||
|
|
6610bb355c | ||
|
|
4312fd7291 | ||
|
|
89a225a96d | ||
|
|
a590977639 | ||
|
|
0d0d116bde | ||
|
|
341bdb5a54 | ||
|
|
2f3dbfb289 | ||
|
|
169cfbbeed | ||
|
|
f97a4afd8f | ||
|
|
868e4a6d81 | ||
|
|
6f48f7d3ac | ||
|
|
1ed3ecb409 | ||
|
|
2cb455b9d4 | ||
|
|
d4b5bf0f78 | ||
|
|
69013772c1 | ||
|
|
3448c83431 | ||
|
|
e4c84542ea | ||
|
|
acddc0323b | ||
|
|
c4285f0d30 | ||
|
|
977d6dd247 | ||
|
|
2bb27fffb7 | ||
|
|
0261ed353d | ||
|
|
96fecfd7c5 | ||
|
|
9fac1b8105 | ||
|
|
95fbcd7b9a | ||
|
|
6adf227611 | ||
|
|
b5cb429243 | ||
|
|
26ba8079a3 | ||
|
|
f999d30bc5 | ||
|
|
ecb1cc4ed4 | ||
|
|
5622bcae16 | ||
|
|
f4539ee289 | ||
|
|
d2138694b4 | ||
|
|
1767e21273 | ||
|
|
8f9d799342 | ||
|
|
a3b0b246f4 | ||
|
|
dee85f14fe | ||
|
|
27309114be | ||
|
|
704afed97b | ||
|
|
121f0a2c6c | ||
|
|
1c580ec92c | ||
|
|
ab8fc721a0 | ||
|
|
790447115c | ||
|
|
80abeac28a | ||
|
|
54915f87ce | ||
|
|
f34f1309a7 | ||
|
|
0ad52b7d19 | ||
|
|
809f60df06 | ||
|
|
cbdcd8253c | ||
|
|
7ac2cd7cc8 | ||
|
|
cf1bb1348c | ||
|
|
b60a26ff9e | ||
|
|
b805c07342 | ||
|
|
8d69f539ac | ||
|
|
c8c0054f67 | ||
|
|
1085385bbe | ||
|
|
56460b220c | ||
|
|
a583ebe590 | ||
|
|
ce8175a800 | ||
|
|
c987e1ef44 | ||
|
|
b36ec152d2 | ||
|
|
44c62e703e | ||
|
|
0f59c1d5e3 | ||
|
|
5739f0b459 | ||
|
|
4145fabfb6 | ||
|
|
ae34b1e521 | ||
|
|
c17da25617 | ||
|
|
cc8ef16240 | ||
|
|
30fac81c41 | ||
|
|
bbcca80b60 | ||
|
|
56ae3716bb | ||
|
|
2fba3a4a9a | ||
|
|
8ba0312d40 | ||
|
|
53623ffa72 | ||
|
|
d6199687f4 | ||
|
|
7cb413dde6 | ||
|
|
c5a7fc1e2a | ||
|
|
0869b0aa29 | ||
|
|
4e81f847f2 | ||
|
|
8fa27d2d9f | ||
|
|
e5a8a29efe | ||
|
|
f967f53176 | ||
|
|
31fefaae0d | ||
|
|
7f9edbf39e | ||
|
|
760b9c8e7f | ||
|
|
cc7fb008fc | ||
|
|
98dbfbe654 | ||
|
|
6444726614 | ||
|
|
0a562fbb10 | ||
|
|
7d8950de40 | ||
|
|
3fdde0c90b | ||
|
|
e776f4cd4e | ||
|
|
e7d7dd13d7 | ||
|
|
d5c83a2e45 | ||
|
|
37ccb13917 | ||
|
|
8439cf410f | ||
|
|
614d9448f7 | ||
|
|
70efdbba5d | ||
|
|
12fee91ebb | ||
|
|
bcb7e20619 | ||
|
|
afc5e8a140 | ||
|
|
4be6626c89 | ||
|
|
5f2b6d629b | ||
|
|
1b8f5f08f0 | ||
|
|
79674c697a | ||
|
|
416d8c1df6 | ||
|
|
d03b6a9382 | ||
|
|
df22e0c796 | ||
|
|
79a3bd75cc | ||
|
|
e6acdcc583 | ||
|
|
cd78984228 | ||
|
|
2f007c5f0a | ||
|
|
ae64a1e30c | ||
|
|
097184c3e0 | ||
|
|
84a95adae1 | ||
|
|
123b6728e4 | ||
|
|
69b4fc98ed | ||
|
|
35cf7703b1 | ||
|
|
8c1137543b | ||
|
|
b8e66e56a0 | ||
|
|
fbb008e510 | ||
|
|
04678f8404 | ||
|
|
1b5aaf1fb8 | ||
|
|
d8e4873f43 | ||
|
|
998a3d8353 | ||
|
|
336dedbed5 | ||
|
|
955595be8a | ||
|
|
576bd4f69a | ||
|
|
cd6915917b | ||
|
|
0adbe31112 | ||
|
|
d5138f509c | ||
|
|
1fe6fc3feb | ||
|
|
c03a7fd482 | ||
|
|
31c47f05f4 | ||
|
|
edad24479b | ||
|
|
bba58732e0 | ||
|
|
8d1cc6cf51 | ||
|
|
f03d0be5ef | ||
|
|
a2f4f494a9 | ||
|
|
9de25c200c | ||
|
|
fe1f00aadb | ||
|
|
a847fac4ca | ||
|
|
0496506fb7 | ||
|
|
e544591c9c | ||
|
|
e5237d1149 | ||
|
|
b9c848c5e9 | ||
|
|
b64e61a793 | ||
|
|
e80e2bdafe | ||
|
|
ac23bce0ba | ||
|
|
1051cd97cf | ||
|
|
45330fdd5d | ||
|
|
bd296d7a11 | ||
|
|
249e19bf2a | ||
|
|
baf52ed286 | ||
|
|
dd7e1baa78 | ||
|
|
80a209d6dc | ||
|
|
ab228c1fcb | ||
|
|
0dde233b1e | ||
|
|
cf0d92d968 | ||
|
|
ce4b052242 | ||
|
|
125bb3afe1 | ||
|
|
30ee2c18ef | ||
|
|
ff1a5dd4c2 | ||
|
|
b9c9c7d671 | ||
|
|
62d9961bd1 | ||
|
|
4b7ed9599d | ||
|
|
c8951de9dd | ||
|
|
63517c377d | ||
|
|
5005ebdfc9 | ||
|
|
a2615f0e52 | ||
|
|
0317d381fe | ||
|
|
31b5181bca | ||
|
|
7ae59055ff | ||
|
|
3524117c23 | ||
|
|
e8ad1ca0a0 | ||
|
|
3ac1650001 | ||
|
|
4f8ae81562 | ||
|
|
329d624a99 | ||
|
|
de92624c9a | ||
|
|
c6a034da40 | ||
|
|
737e76968a | ||
|
|
7c6d49155c | ||
|
|
4d404ea94d | ||
|
|
dc810c7a1e | ||
|
|
e0ee4e71f8 | ||
|
|
ab7dac90a2 | ||
|
|
3a423fbc41 | ||
|
|
0982ec617d | ||
|
|
b45f27c8d0 | ||
|
|
54f62b6701 | ||
|
|
9664d98ba5 | ||
|
|
0c318834b5 | ||
|
|
4c2836f4b3 | ||
|
|
a112db169c | ||
|
|
0dc33ec893 | ||
|
|
0e93ba532f | ||
|
|
9e524a30d6 | ||
|
|
ecca4e4abc | ||
|
|
51791e9efa | ||
|
|
5eab087cc2 | ||
|
|
cd05cdaa57 | ||
|
|
f5e18ccea6 | ||
|
|
0da6b07770 | ||
|
|
05805356fb | ||
|
|
a72ebfdec0 | ||
|
|
59e5da9c7f | ||
|
|
cfd59db998 | ||
|
|
1fa6bf1cc8 | ||
|
|
a2f89de71e | ||
|
|
36a27de286 | ||
|
|
9b685ba824 | ||
|
|
8e9d5fe6ac | ||
|
|
89aa590615 | ||
|
|
3960e0f2a3 | ||
|
|
c3c52d01d3 | ||
|
|
0caef59e6a | ||
|
|
99e6924ed1 | ||
|
|
431e1629b0 | ||
|
|
ff9f702e52 | ||
|
|
6903159f30 | ||
|
|
504b7a03ad | ||
|
|
6933c2aaff | ||
|
|
5fd00e31bb | ||
|
|
a984674dae | ||
|
|
8589119725 | ||
|
|
5e0205378b | ||
|
|
bff2f2e5f9 | ||
|
|
5f9052a675 | ||
|
|
8691b3964f | ||
|
|
4dfe0a0595 | ||
|
|
164299ce25 | ||
|
|
9269ca6ebe | ||
|
|
93fe22e045 | ||
|
|
3849278d5a | ||
|
|
c0f976e200 | ||
|
|
3143749f64 | ||
|
|
1704805e32 | ||
|
|
3aabe07720 | ||
|
|
c5815ab59e | ||
|
|
f6fcfbabd5 | ||
|
|
a0835c95e4 | ||
|
|
ce0c24eee9 | ||
|
|
c9f0ecb1e8 | ||
|
|
50febb3459 | ||
|
|
601b0f96b1 | ||
|
|
018661609a | ||
|
|
98d935d972 | ||
|
|
b07660c4fa | ||
|
|
8037231376 | ||
|
|
6e422a8691 | ||
|
|
3d347ed565 | ||
|
|
13446d8b6b | ||
|
|
08c34bf573 | ||
|
|
3a64ea1650 | ||
|
|
db3439b61b | ||
|
|
7814be7467 | ||
|
|
8a7e0432f1 | ||
|
|
ff1d51c7bd | ||
|
|
f2602e3136 | ||
|
|
313ce0fed8 | ||
|
|
ba0887defa | ||
|
|
063841f491 | ||
|
|
4aa5c78150 | ||
|
|
c1688fa392 | ||
|
|
0dd03e9cb6 | ||
|
|
8d60d70553 | ||
|
|
c17340547c | ||
|
|
34aa6bf6c7 | ||
|
|
82a6ce63b4 | ||
|
|
3ac6ba0fe2 | ||
|
|
8b0fb4fa19 | ||
|
|
9d437b8863 | ||
|
|
f72469c80a | ||
|
|
eec7972808 | ||
|
|
fc9343e7a4 | ||
|
|
be8e353d12 | ||
|
|
53217acdd0 | ||
|
|
c1e0ea5e0a | ||
|
|
467afc7248 | ||
|
|
62753071f4 | ||
|
|
de32de9edd | ||
|
|
aa7d2c3f7e | ||
|
|
2a1dd68583 | ||
|
|
80909eaa84 | ||
|
|
c6a557abe2 | ||
|
|
8d7b8729f1 | ||
|
|
3f9a1c3751 | ||
|
|
0dfe617d96 | ||
|
|
235ad441b4 | ||
|
|
5cd0f6e7e7 | ||
|
|
2627ed3193 | ||
|
|
e3bb8b43ab | ||
|
|
8ba6a3bda9 | ||
|
|
d288249609 | ||
|
|
33d7c6be9e | ||
|
|
c13acc5b60 | ||
|
|
fc9be28d51 | ||
|
|
944f400d19 | ||
|
|
5aee30a7b5 |
No files matched your search
@@ -40,7 +40,6 @@ jobs:
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
|
||||
|
||||
- name : submodule checkout
|
||||
@@ -65,7 +64,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 -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DENABLE_INTERPRETER=True -DBUILD_FEX_LINUX_TESTS=True -DBUILD_THUNKS=True -DCMAKE_INSTALL_PREFIX=${{runner.workspace}}/build/install
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@@ -178,6 +177,51 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_FEXLinuxTests.log || true
|
||||
|
||||
- name: Thunkgen tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunkgen_tests
|
||||
|
||||
- name: Thunkgen 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_ThunkgenTests.log || true
|
||||
|
||||
- name: Install
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target install
|
||||
|
||||
- name: Test GL No-Thunks
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
DISPLAY: ":0"
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_nothunks
|
||||
|
||||
- name: No thunks 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_NoThunkResults.log || true
|
||||
|
||||
- name: Test GL Thunks
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
env:
|
||||
DISPLAY: ":0"
|
||||
run: cmake --build . --config $BUILD_TYPE --target thunk_functional_tests_thunks
|
||||
|
||||
- name: Thunks 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_ThunkResults.log || true
|
||||
|
||||
- name: Truncate test results
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
@@ -197,4 +241,3 @@ jobs:
|
||||
name: Results-${{ env.runner_name }}
|
||||
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
|
||||
retention-days: 3
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
name: Vixl Simulator run
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.arch }}
|
||||
strategy:
|
||||
matrix:
|
||||
# Only the x86-64 runner is fast enough to run this
|
||||
arch: [[self-hosted, x64], [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: Set rootfs paths
|
||||
run: |
|
||||
echo "FEX_ROOTFS_MOUNT=/mnt/AutoNFS/rootfs/" >> $GITHUB_ENV
|
||||
echo "FEX_ROOTFS_PATH=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
echo "FEX_ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
echo "ROOTFS=$HOME/Rootfs/" >> $GITHUB_ENV
|
||||
|
||||
- name: Update RootFS cache
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
run: $GITHUB_WORKSPACE/Scripts/CI_FetchRootFS.py
|
||||
|
||||
- name : submodule checkout
|
||||
# Need to update submodules
|
||||
run: |
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --depth 1
|
||||
|
||||
- name: Clean Build Environment
|
||||
run: rm -Rf ${{runner.workspace}}/build
|
||||
|
||||
- name: Create Build Environment
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# 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_VIXL_SIMULATOR=True -DENABLE_LTO=False -DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: ASM Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
|
||||
- name: ASM 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_ASM.log || true
|
||||
|
||||
- name: IR Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target ir_tests
|
||||
|
||||
- name: IR 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_IR.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
|
||||
|
||||
- name: Set runner name
|
||||
if: ${{ always() }}
|
||||
run: echo "runner_name=$(hostname)" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload results
|
||||
if: ${{ always() }}
|
||||
uses: 'actions/upload-artifact@v2'
|
||||
with:
|
||||
name: Results-${{ env.runner_name }}
|
||||
path: ${{runner.workspace}}/build/Testing/Temporary/LastTest_*.log
|
||||
retention-days: 3
|
||||
|
||||
@@ -10,3 +10,4 @@ out/
|
||||
.vscode/
|
||||
.vs/
|
||||
*.pyc
|
||||
.cache
|
||||
@@ -49,3 +49,7 @@
|
||||
shallow = true
|
||||
path = External/robin-map
|
||||
url = https://github.com/Tessil/robin-map.git
|
||||
[submodule "External/Vulkan-Headers"]
|
||||
shallow = true
|
||||
path = External/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ThunksDB": {
|
||||
"GL": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ThunksDB": {
|
||||
"Vulkan": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
+70
-142
@@ -7,6 +7,7 @@ CHECK_INCLUDE_FILES ("gdb/jit-reader.h" HAVE_GDB_JIT_READER_H)
|
||||
option(BUILD_TESTS "Build unit tests to ensure sanity" TRUE)
|
||||
option(BUILD_FEX_LINUX_TESTS "Build FEXLinuxTests, requires x86 compiler" FALSE)
|
||||
option(BUILD_THUNKS "Build thunks" FALSE)
|
||||
option(BUILD_CLANG_THUNKS "Build thunks with clang" FALSE)
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
option(ENABLE_IWYU "Enables include what you use program" FALSE)
|
||||
option(ENABLE_LTO "Enable LTO with compilation" TRUE)
|
||||
@@ -20,7 +21,6 @@ option(ENABLE_GDB_SYMBOLS "Enables GDBSymbols integration support" ${HAVE_GDB_JI
|
||||
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)
|
||||
option(ENABLE_STATIC_PIE "Enables static-pie build" FALSE)
|
||||
option(ENABLE_JEMALLOC "Enables jemalloc allocator" TRUE)
|
||||
option(ENABLE_OFFLINE_TELEMETRY "Enables FEX offline telemetry" TRUE)
|
||||
option(ENABLE_COMPILE_TIME_TRACE "Enables time trace compile option" FALSE)
|
||||
@@ -28,10 +28,23 @@ option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE)
|
||||
option(ENABLE_INTERPRETER "Enables FEX's Interpreter" FALSE)
|
||||
option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE)
|
||||
option(ENABLE_TERMUX_BUILD "Forces building for Termux on a non-Termux build machine" FALSE)
|
||||
option(ENABLE_VIXL_SIMULATOR "Forces the FEX JIT to use the VIXL simulator" FALSE)
|
||||
|
||||
set (X86_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86.cmake" CACHE FILEPATH "Toolchain file for the x86 (cross-)compiler")
|
||||
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
|
||||
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
|
||||
set (DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/share/fex-emu" CACHE PATH "global data directory")
|
||||
|
||||
# uninstall target
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
# These options are meant for package management
|
||||
set (TUNE_CPU "native" CACHE STRING "Override the CPU the build is tuned for")
|
||||
set (TUNE_ARCH "generic" CACHE STRING "Override the Arch the build is tuned for")
|
||||
@@ -85,10 +98,9 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(_M_X86_64 1)
|
||||
add_definitions(-D_M_X86_64=1)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
set (X86_TOOLCHAIN_FILE "")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
|
||||
set(_M_ARM_64 1)
|
||||
add_definitions(-D_M_ARM_64=1)
|
||||
endif()
|
||||
@@ -140,130 +152,6 @@ if(DEFINED ENV{TERMUX_VERSION} OR ENABLE_TERMUX_BUILD)
|
||||
set(ENABLE_JEMALLOC FALSE)
|
||||
endif()
|
||||
|
||||
if (ENABLE_STATIC_PIE)
|
||||
if (_M_ARM_64 AND ENABLE_LLD)
|
||||
message (FATAL_ERROR "Static linking does not currently work with AArch64+lld. Use GNU ld for now.")
|
||||
endif()
|
||||
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/Determine_iplt.c
|
||||
"int main(int argc, char* argv[])
|
||||
{
|
||||
return 0;
|
||||
}")
|
||||
|
||||
# Compile the test application with our LD_OVERRIDE and static-pie options
|
||||
try_compile(
|
||||
COMPILE_RESULT
|
||||
${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp
|
||||
${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/Determine_iplt.c
|
||||
COMPILE_DEFINITIONS "-fPIE ${LD_OVERRIDE}"
|
||||
LINK_LIBRARIES "-static-pie ${LD_OVERRIDE}"
|
||||
COPY_FILE ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/Determine_iplt
|
||||
)
|
||||
|
||||
if (${COMPILE_RESULT})
|
||||
# Read the symbols from the elf
|
||||
execute_process(COMMAND
|
||||
readelf -s ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/Determine_iplt
|
||||
OUTPUT_FILE ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/plt_out.txt
|
||||
OUTPUT_VARIABLE PLT_SYMBOLS)
|
||||
|
||||
# Pull out the __rela_iplt_{start,end} symbols if they exist
|
||||
execute_process(COMMAND
|
||||
"grep" "__rela_iplt" ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/plt_out.txt
|
||||
OUTPUT_VARIABLE PLT_SYMBOLS)
|
||||
|
||||
set (SYMBOLS_FINE TRUE)
|
||||
set (HAS_IPLT -1)
|
||||
# Check if we have any symbols in our grep output
|
||||
# The symbols must either not exist at all OR the symbols are zero
|
||||
if (PLT_SYMBOLS)
|
||||
string(FIND ${PLT_SYMBOLS} "__rela_iplt_start" HAS_IPLT)
|
||||
endif()
|
||||
|
||||
if (NOT HAS_IPLT EQUAL -1)
|
||||
# We have some symbols from readelf. Let's parse the results to check if they are zero
|
||||
# Format: '35: 0000000000000000 0 NOTYPE LOCAL HIDDEN UND __rela_iplt_start'
|
||||
string(REPLACE "\n" ";" SYMBOL_LIST ${PLT_SYMBOLS})
|
||||
foreach (SYMBOL ${SYMBOL_LIST})
|
||||
# strip any leading and trailing whitespace
|
||||
string (STRIP ${SYMBOL} SYMBOL)
|
||||
# Convert string to a list
|
||||
string(REPLACE " " ";" SYMBOL_VALUES ${SYMBOL}})
|
||||
# Pull out the address argument
|
||||
list(GET SYMBOL_VALUES 1 OFFSET)
|
||||
|
||||
# Check against integer zero
|
||||
if (NOT ${OFFSET} EQUAL 0)
|
||||
# Symbol wasn't zero, this now fails
|
||||
set (SYMBOLS_FINE FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (SYMBOLS_FINE)
|
||||
# We can now exnable static-pie
|
||||
set (STATIC_PIE_OPTIONS "-static-pie")
|
||||
# Pthreads has an issue with exposing symbols
|
||||
# We need to make some concessions to the pthread gods
|
||||
if (ENABLE_LLD)
|
||||
set (PTHREAD_LIB
|
||||
-Wl,--undefined-glob=pthread_*
|
||||
-Wl,--undefined=__cxa_finalize
|
||||
-Wl,--undefined=_pthread_cleanup_push_defer
|
||||
-Wl,--undefined=_pthread_cleanup_pop_restore
|
||||
-Wl,--undefined=__pthread_cleanup_upto
|
||||
pthread)
|
||||
else()
|
||||
set (PTHREAD_LIB
|
||||
-Wl,--undefined=pthread_join
|
||||
-Wl,--undefined=pthread_attr_getdetachstate
|
||||
-Wl,--undefined=pthread_sigmask
|
||||
-Wl,--undefined=pthread_mutex_lock
|
||||
-Wl,--undefined=pthread_cond_init
|
||||
-Wl,--undefined=pthread_attr_init
|
||||
-Wl,--undefined=pthread_mutex_unlock
|
||||
-Wl,--undefined=pthread_mutexattr_destroy
|
||||
-Wl,--undefined=pthread_detach
|
||||
-Wl,--undefined=pthread_mutex_init
|
||||
-Wl,--undefined=pthread_getattr_np
|
||||
-Wl,--undefined=pthread_cond_timedwait
|
||||
-Wl,--undefined=pthread_attr_destroy
|
||||
-Wl,--undefined=pthread_mutexattr_settype
|
||||
-Wl,--undefined=pthread_rwlock_unlock
|
||||
-Wl,--undefined=pthread_rwlock_wrlock
|
||||
-Wl,--undefined=pthread_setspecific
|
||||
-Wl,--undefined=pthread_create
|
||||
-Wl,--undefined=pthread_cond_clockwait
|
||||
-Wl,--undefined=pthread_key_create
|
||||
-Wl,--undefined=pthread_rwlock_rdlock
|
||||
-Wl,--undefined=pthread_setname_np
|
||||
-Wl,--undefined=pthread_cond_signal
|
||||
-Wl,--undefined=pthread_mutexattr_init
|
||||
-Wl,--undefined=pthread_attr_setstack
|
||||
-Wl,--undefined=pthread_self
|
||||
-Wl,--undefined=pthread_getaffinity_np
|
||||
-Wl,--undefined=pthread_cond_wait
|
||||
-Wl,--undefined=pthread_mutex_trylock
|
||||
-Wl,--undefined=pthread_cond_broadcast
|
||||
-Wl,--undefined=pthread_cond_destroy
|
||||
-Wl,--undefined=pthread_getspecific
|
||||
-Wl,--undefined=pthread_key_delete
|
||||
-Wl,--undefined=pthread_once
|
||||
-Wl,--undefined=__cxa_finalize
|
||||
-Wl,--undefined=_pthread_cleanup_push_defer
|
||||
-Wl,--undefined=_pthread_cleanup_pop_restore
|
||||
-Wl,--undefined=__pthread_cleanup_upto
|
||||
pthread)
|
||||
endif()
|
||||
else()
|
||||
message (FATAL_ERROR "Application has __rela_iplt_{start,end} symbols. Which means static-pie can't be enabled")
|
||||
endif()
|
||||
else()
|
||||
message (FATAL_ERROR "Couldn't compile static-pie test. Static-pie can't be enabled! Is your glibc compiled without static-pie?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_ASAN)
|
||||
add_definitions(-DENABLE_ASAN=1)
|
||||
add_compile_options(-fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope)
|
||||
@@ -488,8 +376,6 @@ if (BUILD_TESTS)
|
||||
endif()
|
||||
|
||||
add_subdirectory(FEXHeaderUtils/)
|
||||
include_directories(FEXHeaderUtils/)
|
||||
|
||||
add_subdirectory(External/FEXCore)
|
||||
|
||||
# Binfmt_misc files must be installed prior to Source/ installs
|
||||
@@ -499,15 +385,20 @@ add_subdirectory(Source/)
|
||||
add_subdirectory(Data/AppConfig/)
|
||||
|
||||
# Install the ThunksDB file
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_SOURCE_DIR}/Data/ThunksDB.json
|
||||
DESTINATION ${DATA_DIRECTORY}/)
|
||||
file(GLOB CONFIG_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Data/*.json)
|
||||
|
||||
# Any application configuration json file gets installed
|
||||
foreach(CONFIG_SRC ${CONFIG_SOURCES})
|
||||
install(FILES ${CONFIG_SRC}
|
||||
DESTINATION ${DATA_DIRECTORY}/)
|
||||
endforeach()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(unittests/)
|
||||
endif()
|
||||
|
||||
if (BUILD_THUNKS)
|
||||
set (FEX_PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR})
|
||||
add_subdirectory(ThunkLibs/Generator)
|
||||
|
||||
# Thunk targets for both host libraries and IDE integration
|
||||
@@ -523,10 +414,31 @@ if (BUILD_THUNKS)
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
|
||||
BINARY_DIR "Guest"
|
||||
CMAKE_ARGS
|
||||
"-DBITNESS=64"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_TOOLCHAIN_FILE}"
|
||||
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_64_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
|
||||
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
|
||||
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
DEPENDS thunkgen
|
||||
)
|
||||
|
||||
ExternalProject_Add(guest-libs-32
|
||||
PREFIX guest-libs-32
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ThunkLibs/GuestLibs"
|
||||
BINARY_DIR "Guest_32"
|
||||
CMAKE_ARGS
|
||||
"-DBITNESS=32"
|
||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
|
||||
"-DENABLE_CLANG_THUNKS=${ENABLE_CLANG_THUNKS}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${X86_32_TOOLCHAIN_FILE}"
|
||||
"-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
"-DSTRUCT_VERIFIER=${CMAKE_SOURCE_DIR}/Scripts/StructPackVerifier.py"
|
||||
"-DFEX_PROJECT_SOURCE_DIR=${FEX_PROJECT_SOURCE_DIR}"
|
||||
"-DGENERATOR_EXE=$<TARGET_FILE:thunkgen>"
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_ALWAYS ON
|
||||
@@ -541,6 +453,28 @@ if (BUILD_THUNKS)
|
||||
)"
|
||||
DEPENDS guest-libs
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: guest-libs-32\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} --build . --target install
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
|
||||
)"
|
||||
DEPENDS guest-libs-32
|
||||
)
|
||||
|
||||
add_custom_target(uninstall_guest-libs
|
||||
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest
|
||||
)
|
||||
|
||||
add_custom_target(uninstall_guest-libs-32
|
||||
COMMAND ${CMAKE_COMMAND} "--build" "." "--target" "uninstall"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Guest_32
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_guest-libs)
|
||||
add_dependencies(uninstall uninstall_guest-libs-32)
|
||||
endif()
|
||||
|
||||
set(FEX_VERSION_MAJOR "0")
|
||||
@@ -593,15 +527,9 @@ endif()
|
||||
|
||||
# Package creation
|
||||
set (CPACK_GENERATOR "DEB")
|
||||
if (ENABLE_STATIC_PIE)
|
||||
set (CPACK_PACKAGE_NAME fex-emu-static)
|
||||
set (CPACK_DEBIAN_PACKAGE_CONFLICTS "fex-emu")
|
||||
else()
|
||||
set (CPACK_PACKAGE_NAME fex-emu)
|
||||
set (CPACK_DEBIAN_PACKAGE_CONFLICTS "fex-emu-static")
|
||||
endif()
|
||||
set (CPACK_PACKAGE_NAME fex-emu)
|
||||
set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${GIT_DESCRIBE_STRING}_${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.org>")
|
||||
set (CPACK_PACKAGE_CONTACT "FEX-Emu Maintainers <team@fex-emu.com>")
|
||||
set (CPACK_PACKAGE_VERSION_MAJOR "${FEX_VERSION_MAJOR}")
|
||||
set (CPACK_PACKAGE_VERSION_MINOR "${FEX_VERSION_MINOR}")
|
||||
set (CPACK_PACKAGE_VERSION_PATCH "${FEX_VERSION_PATCH}")
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@fex-emu.org. All
|
||||
reported by contacting the project team at team@fex-emu.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
@@ -11,15 +11,15 @@ endforeach()
|
||||
# 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)
|
||||
get_filename_component(CONFIG_NAME ${GEN_CONFIG_SRC} NAME_WLE)
|
||||
|
||||
# Configure it
|
||||
configure_file(
|
||||
${GEN_CONFIG_SRC}
|
||||
${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json)
|
||||
${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME})
|
||||
|
||||
# Then install the configured json
|
||||
install(
|
||||
FILES ${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}.json
|
||||
FILES ${CMAKE_BINARY_DIR}/Data/AppConfig/${CONFIG_NAME}
|
||||
DESTINATION ${DATA_DIRECTORY}/AppConfig/)
|
||||
endforeach()
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -165,6 +165,14 @@
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libXfixes.so.3.1.0"
|
||||
]
|
||||
},
|
||||
"OpenCL": {
|
||||
"Library" : "libOpenCL-guest.so",
|
||||
"Overlay": [
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so",
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1",
|
||||
"@PREFIX_LIB@/x86_64-linux-gnu/libOpenCL.so.1.0.0"
|
||||
]
|
||||
},
|
||||
"":{}
|
||||
}
|
||||
}
|
||||
Vendored
+12
-6
@@ -9,12 +9,19 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64|^arm64|^armv8\.*")
|
||||
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")
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# If the vixl simulator is enabled then we are using the ARM64 JIT
|
||||
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" FALSE)
|
||||
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" TRUE)
|
||||
else()
|
||||
option(ENABLE_JIT_X86_64 "Enable the x86_64 JIT" ${_M_X86_64})
|
||||
option(ENABLE_JIT_ARM64 "Enable the ARM64 JIT" ${_M_ARM_64})
|
||||
endif()
|
||||
|
||||
option(ENABLE_CLANG_FORMAT "Run clang format over the source" FALSE)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
@@ -27,7 +34,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_DIR}/External/vixl/)
|
||||
# Useful to have for freestanding libFEXCore
|
||||
add_subdirectory(External/vixl/)
|
||||
@@ -46,14 +52,14 @@ if (OVERRIDE_VERSION STREQUAL "detect")
|
||||
|
||||
if (GIT_FOUND)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_SHORT_HASH
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe
|
||||
COMMAND ${GIT_EXECUTABLE} describe --abbrev=7
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE_STRING
|
||||
ERROR_QUIET
|
||||
|
||||
-10
@@ -321,8 +321,6 @@ def print_ir_structs(defines):
|
||||
|
||||
|
||||
if op.SSAArgNum > 0:
|
||||
# Add helpers for accessing SSA arguments, given how frequently they're accessed
|
||||
|
||||
output_file.write("\t// Get index of argument by name\n")
|
||||
SSAArg = 0
|
||||
for arg in op.Arguments:
|
||||
@@ -330,14 +328,6 @@ def print_ir_structs(defines):
|
||||
output_file.write("\tstatic constexpr size_t {}_Index = {};\n".format(arg.Name, SSAArg))
|
||||
SSAArg = SSAArg + 1
|
||||
|
||||
output_file.write("\n")
|
||||
output_file.write("\t[[nodiscard]] OrderedNodeWrapper& Args(size_t Index) {\n")
|
||||
output_file.write("\t\treturn Header.Args[Index];\n")
|
||||
output_file.write("\t}\n")
|
||||
output_file.write("\t[[nodiscard]] const OrderedNodeWrapper& Args(size_t Index) const {\n")
|
||||
output_file.write("\t\treturn Header.Args[Index];\n")
|
||||
output_file.write("\t}\n")
|
||||
|
||||
|
||||
output_file.write("};\n")
|
||||
|
||||
|
||||
+9
-4
@@ -177,6 +177,11 @@ if (_M_ARM_64)
|
||||
list(APPEND DEFINES -D_M_ARM_64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VIXL_SIMULATOR)
|
||||
# We can run the simulator on both x86-64 or AArch64 hosts
|
||||
list(APPEND DEFINES -DVIXL_SIMULATOR=1 -DVIXL_INCLUDE_SIMULATOR_AARCH64=1)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_X86_64)
|
||||
list(APPEND SRCS
|
||||
Interface/Core/JIT/x86_64/JIT.cpp
|
||||
@@ -213,7 +218,7 @@ if (ENABLE_JIT_ARM64)
|
||||
)
|
||||
endif()
|
||||
|
||||
set (LIBS vixl dl xxhash tiny-json)
|
||||
set (LIBS fmt::fmt vixl dl xxhash tiny-json FEXHeaderUtils)
|
||||
if (ENABLE_JEMALLOC)
|
||||
list (APPEND LIBS FEX_jemalloc)
|
||||
endif()
|
||||
@@ -359,14 +364,14 @@ endfunction()
|
||||
|
||||
# Build FEXCore_Config static library
|
||||
add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS})
|
||||
target_link_libraries(FEXCore_Base fmt::fmt tiny-json)
|
||||
target_link_libraries(FEXCore_Base ${LIBS})
|
||||
AddDefaultOptionsToTarget(FEXCore_Base)
|
||||
|
||||
function(AddObject Name Type)
|
||||
add_library(${Name} ${Type} ${SRCS})
|
||||
add_dependencies(${Name} IR_INC)
|
||||
|
||||
target_link_libraries(${Name} FEXCore_Base ${LIBS})
|
||||
target_link_libraries(${Name} FEXCore_Base)
|
||||
AddDefaultOptionsToTarget(${Name})
|
||||
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
@@ -374,7 +379,7 @@ endfunction()
|
||||
|
||||
function(AddLibrary Name Type)
|
||||
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
||||
target_link_libraries(${Name} FEXCore_Base ${LIBS})
|
||||
target_link_libraries(${Name} FEXCore_Base)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
|
||||
AddDefaultOptionsToTarget(${Name})
|
||||
|
||||
+3
-3
@@ -20,12 +20,12 @@ struct BitSet final {
|
||||
ElementType *Memory;
|
||||
void Allocate(size_t Elements) {
|
||||
size_t AllocateSize = AlignUp(Elements, MinimumSizeBits) / MinimumSize;
|
||||
LOGMAN_THROW_A_FMT((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
LOGMAN_THROW_AA_FMT((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_FMT((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
LOGMAN_THROW_AA_FMT((AllocateSize * MinimumSize) >= Elements, "Fail");
|
||||
Memory = static_cast<ElementType*>(FEXCore::Allocator::realloc(Memory, AllocateSize));
|
||||
}
|
||||
void Free() {
|
||||
@@ -64,7 +64,7 @@ struct BitSetView final {
|
||||
ElementType *Memory;
|
||||
|
||||
void GetView(BitSet<T> &Set, uint64_t ElementOffset) {
|
||||
LOGMAN_THROW_A_FMT((ElementOffset % MinimumSize) == 0,
|
||||
LOGMAN_THROW_AA_FMT((ElementOffset % MinimumSize) == 0,
|
||||
"Bitset view offset needs to be aligned to size of backing element");
|
||||
Memory = &Set.Memory[ElementOffset / MinimumSizeBits];
|
||||
}
|
||||
|
||||
+5
-2
@@ -7,6 +7,11 @@
|
||||
|
||||
namespace FEXCore {
|
||||
JITSymbols::JITSymbols() : fp{nullptr, std::fclose} {
|
||||
}
|
||||
|
||||
JITSymbols::~JITSymbols() = default;
|
||||
|
||||
void JITSymbols::InitFile() {
|
||||
const auto PerfMap = fmt::format("/tmp/perf-{}.map", getpid());
|
||||
|
||||
fp.reset(fopen(PerfMap.c_str(), "wb"));
|
||||
@@ -16,8 +21,6 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
JITSymbols::~JITSymbols() = default;
|
||||
|
||||
void JITSymbols::Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
|
||||
if (!fp) return;
|
||||
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ public:
|
||||
JITSymbols();
|
||||
~JITSymbols();
|
||||
|
||||
void InitFile();
|
||||
void Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize);
|
||||
void Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name);
|
||||
void Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset);
|
||||
|
||||
+23
-13
@@ -82,7 +82,7 @@ namespace JSON {
|
||||
json_t const* ConfigList = json_getProperty(json, "Config");
|
||||
|
||||
if (!ConfigList) {
|
||||
LogMan::Msg::EFmt("Couldn't get config list");
|
||||
// This is a non-error if the configuration file exists but no Config section
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,15 +154,20 @@ namespace JSON {
|
||||
return ConfigDir;
|
||||
}
|
||||
|
||||
std::string GetConfigFileLocation() {
|
||||
std::string GetConfigFileLocation(bool Global) {
|
||||
std::string ConfigFile{};
|
||||
const char *AppConfig = getenv("FEX_APP_CONFIG");
|
||||
if (AppConfig) {
|
||||
// App config environment variable overwrites only the config file
|
||||
ConfigFile = AppConfig;
|
||||
if (Global) {
|
||||
ConfigFile = GetConfigDirectory(true) + "Config.json";
|
||||
}
|
||||
else {
|
||||
ConfigFile = GetConfigDirectory(false) + "Config.json";
|
||||
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;
|
||||
}
|
||||
@@ -206,7 +211,8 @@ namespace JSON {
|
||||
static std::map<FEXCore::Config::LayerType, std::unique_ptr<FEXCore::Config::Layer>> ConfigLayers;
|
||||
static FEXCore::Config::Layer *Meta{};
|
||||
|
||||
constexpr std::array<FEXCore::Config::LayerType, 6> LoadOrder = {
|
||||
constexpr std::array<FEXCore::Config::LayerType, 7> LoadOrder = {
|
||||
FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN,
|
||||
FEXCore::Config::LayerType::LAYER_MAIN,
|
||||
FEXCore::Config::LayerType::LAYER_GLOBAL_APP,
|
||||
FEXCore::Config::LayerType::LAYER_LOCAL_APP,
|
||||
@@ -613,7 +619,7 @@ namespace JSON {
|
||||
// Application loaders
|
||||
class MainLoader final : public FEXCore::Config::OptionMapper {
|
||||
public:
|
||||
explicit MainLoader();
|
||||
explicit MainLoader(FEXCore::Config::LayerType Type);
|
||||
explicit MainLoader(std::string ConfigFile);
|
||||
void Load() override;
|
||||
|
||||
@@ -659,9 +665,9 @@ namespace JSON {
|
||||
}
|
||||
}
|
||||
|
||||
MainLoader::MainLoader()
|
||||
: FEXCore::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
|
||||
, Config{FEXCore::Config::GetConfigFileLocation()} {
|
||||
MainLoader::MainLoader(FEXCore::Config::LayerType Type)
|
||||
: FEXCore::Config::OptionMapper(Type)
|
||||
, Config{FEXCore::Config::GetConfigFileLocation(Type == FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN)} {
|
||||
}
|
||||
|
||||
MainLoader::MainLoader(std::string ConfigFile)
|
||||
@@ -735,12 +741,16 @@ namespace JSON {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<FEXCore::Config::Layer> CreateGlobalMainLayer() {
|
||||
return std::make_unique<FEXCore::Config::MainLoader>(FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN);
|
||||
}
|
||||
|
||||
std::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(std::string const *File) {
|
||||
if (File) {
|
||||
return std::make_unique<FEXCore::Config::MainLoader>(*File);
|
||||
}
|
||||
else {
|
||||
return std::make_unique<FEXCore::Config::MainLoader>();
|
||||
return std::make_unique<FEXCore::Config::MainLoader>(FEXCore::Config::LayerType::LAYER_MAIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
"Cache JIT object code to drive.",
|
||||
"Allows JIT code to be shared between applications"
|
||||
]
|
||||
},
|
||||
"EnableAVX": {
|
||||
"Type": "bool",
|
||||
"Default": "true",
|
||||
"Desc": [
|
||||
"Determines whether or not we use the expanded register file for AVX or not"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Emulation": {
|
||||
@@ -83,6 +90,13 @@
|
||||
"Folder to find the guest-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkGuestLibs32": {
|
||||
"Type": "str",
|
||||
"Default": "@CMAKE_INSTALL_PREFIX@/share/fex-emu/GuestThunks_32/",
|
||||
"Desc": [
|
||||
"Folder to find the 32-bit guest-side thunking libraries."
|
||||
]
|
||||
},
|
||||
"ThunkConfig": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
@@ -309,6 +323,16 @@
|
||||
"Forces a process to stall out on initialization",
|
||||
"Useful for a process that keeps restarting and doesn't work"
|
||||
]
|
||||
},
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"An application that uses try-catch or longjump extensively needs the ability to do context aware state flushing",
|
||||
"In the case of FEX's block-linking, it won't always ensure that RIP is synchronized.",
|
||||
"If an exception occurs and RIP isn't synchronized, then FEX's exception stack restore may not long jump as expected",
|
||||
"Can be useful for Wine applications that rely on stack unwinding"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Misc": {
|
||||
@@ -334,6 +358,13 @@
|
||||
"Desc": [
|
||||
"Loads an AOT IR cache for the loaded executable."
|
||||
]
|
||||
},
|
||||
"ServerSocketPath": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"Desc": [
|
||||
"Override for a FEXServer socket path. Only useful for chroots."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -351,6 +382,17 @@
|
||||
"Type": "str",
|
||||
"Default": ""
|
||||
},
|
||||
"APP_CONFIG_NAME": {
|
||||
"Type": "str",
|
||||
"Default": "",
|
||||
"Desc": [
|
||||
"This is the application config name that has been loaded.",
|
||||
"This differs from APP_FILENAME in two ways",
|
||||
"Where APP_FILENAME always points to the executable path that FEX-Emu is executing.",
|
||||
"This matches what is used to load the AppLayer configuration name.",
|
||||
"When running through a compatibility layer like wine, this will only be the exe name, instead of wine full path."
|
||||
]
|
||||
},
|
||||
"IS64BIT_MODE": {
|
||||
"Type": "bool",
|
||||
"Default": "false"
|
||||
|
||||
@@ -110,6 +110,10 @@ namespace FEXCore::Context {
|
||||
void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t Syscall, [[maybe_unused]] FEXCore::HLE::SyscallVisitor *Visitor) {
|
||||
}
|
||||
|
||||
HostFeatures GetHostFeatures(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->HostFeatures;
|
||||
}
|
||||
|
||||
void HandleCallback(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
|
||||
CTX->HandleCallback(Thread, RIP);
|
||||
}
|
||||
@@ -198,6 +202,10 @@ namespace FEXCore::Context {
|
||||
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
CTX->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
|
||||
CTX->CompileRIP(CTX->ParentThread, RIP);
|
||||
|
||||
+29
-9
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/JitSymbols.h"
|
||||
#include "FEXHeaderUtils/ScopedSignalMask.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "Interface/Core/HostFeatures.h"
|
||||
#include "Interface/Core/X86HelperGen.h"
|
||||
#include "Interface/Core/ObjectCache/ObjectCacheService.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
@@ -10,10 +10,12 @@
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -113,6 +115,8 @@ namespace FEXCore::Context {
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION);
|
||||
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(x86dec_SynchronizeRIPOnAllBlocks, X86DEC_SYNCHRONIZERIPONALLBLOCKS);
|
||||
FEX_CONFIG_OPT(EnableAVX, ENABLEAVX);
|
||||
} Config;
|
||||
|
||||
FEXCore::HostFeatures HostFeatures;
|
||||
@@ -121,6 +125,7 @@ namespace FEXCore::Context {
|
||||
FEXCore::Core::InternalThreadState* ParentThread;
|
||||
std::vector<FEXCore::Core::InternalThreadState*> Threads;
|
||||
std::atomic_bool CoreShuttingDown{false};
|
||||
bool NeedToCheckXID{true};
|
||||
|
||||
std::mutex IdleWaitMutex;
|
||||
std::condition_variable IdleWaitCV;
|
||||
@@ -129,8 +134,8 @@ namespace FEXCore::Context {
|
||||
Event PauseWait;
|
||||
bool Running{};
|
||||
|
||||
std::shared_mutex CodeInvalidationMutex;
|
||||
|
||||
std::shared_mutex CodeInvalidationMutex;
|
||||
|
||||
FEXCore::CPUIDEmu CPUID;
|
||||
FEXCore::HLE::SyscallHandler *SyscallHandler{};
|
||||
FEXCore::HLE::SourcecodeResolver *SourcecodeResolver{};
|
||||
@@ -170,13 +175,26 @@ namespace FEXCore::Context {
|
||||
void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
|
||||
// Must be called from owning thread
|
||||
static void RemoveThreadCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
static void ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
static void ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker);
|
||||
|
||||
// Wrapper which takes CpuStateFrame instead of InternalThreadState
|
||||
template<auto Fn>
|
||||
static uint64_t ThreadExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
|
||||
FHU::ScopedSignalMaskWithSharedLock lk(Frame->Thread->CTX->CodeInvalidationMutex);
|
||||
|
||||
return Fn(Frame, record);
|
||||
}
|
||||
|
||||
// Wrapper which takes CpuStateFrame instead of InternalThreadState and unique_locks CodeInvalidationMutex
|
||||
// Must be called from owning thread
|
||||
static void RemoveThreadCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
RemoveThreadCodeEntry(Frame->Thread, GuestRIP);
|
||||
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
|
||||
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(Thread->CTX->CodeInvalidationMutex);
|
||||
|
||||
ThreadRemoveCodeEntry(Thread, GuestRIP);
|
||||
}
|
||||
|
||||
// returns false if a handler was already registered
|
||||
@@ -305,6 +323,8 @@ namespace FEXCore::Context {
|
||||
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
|
||||
|
||||
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
|
||||
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
|
||||
|
||||
@@ -350,7 +370,7 @@ namespace FEXCore::Context {
|
||||
bool StartPaused = false;
|
||||
bool IsMemoryShared = false;
|
||||
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
|
||||
|
||||
|
||||
std::shared_mutex CustomIRMutex;
|
||||
std::unordered_map<uint64_t, std::tuple<std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)>, void *, void *>> CustomIRHandlers;
|
||||
FEXCore::CPU::CPUBackendFeatures BackendFeatures;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <aarch64/cpu-aarch64.h>
|
||||
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Telemetry.h>
|
||||
|
||||
@@ -1986,7 +1987,8 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) {
|
||||
DesiredFunction = NEGDesired;
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}", AtomicOp);
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}",
|
||||
ToUnderlying(AtomicOp));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2039,7 +2041,8 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) {
|
||||
DesiredFunction = NEGDesired;
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}", AtomicOp);
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}",
|
||||
ToUnderlying(AtomicOp));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2092,7 +2095,8 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) {
|
||||
DesiredFunction = NEGDesired;
|
||||
break;
|
||||
default:
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}", AtomicOp);
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}",
|
||||
ToUnderlying(AtomicOp));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <utility>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
#define STATE x28
|
||||
|
||||
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
|
||||
Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
|
||||
@@ -25,11 +24,15 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
|
||||
, EmitterCTX {ctx} {
|
||||
CPU.SetUp();
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
auto Features = vixl::CPUFeatures::All();
|
||||
#else
|
||||
auto Features = vixl::CPUFeatures::InferFromOS();
|
||||
if (ctx->HostFeatures.SupportsAtomics) {
|
||||
// Hypervisor can hide this on the c630?
|
||||
Features.Combine(vixl::CPUFeatures::Feature::kLORegions);
|
||||
}
|
||||
#endif
|
||||
|
||||
SetCPUFeatures(Features);
|
||||
}
|
||||
@@ -209,19 +212,30 @@ void Arm64Emitter::SpillStaticRegs(bool FPRs, uint32_t GPRSpillMask, uint32_t FP
|
||||
}
|
||||
|
||||
if (FPRs) {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
|
||||
auto Reg1 = SRAFPR[i];
|
||||
auto Reg2 = SRAFPR[i+1];
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i++) {
|
||||
const auto Reg = SRAFPR[i];
|
||||
|
||||
if (((1U << Reg1.GetCode()) & FPRSpillMask) &&
|
||||
((1U << Reg2.GetCode()) & FPRSpillMask)) {
|
||||
stp(Reg1.Q(), Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
if (((1U << Reg.GetCode()) & FPRSpillMask) != 0) {
|
||||
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
st1b(Reg.Z().VnB(), PRED_TMP_32B, SVEMemOperand(STATE, TMP4));
|
||||
}
|
||||
}
|
||||
else if (((1U << Reg1.GetCode()) & FPRSpillMask)) {
|
||||
str(Reg1.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
}
|
||||
else if (((1U << Reg2.GetCode()) & FPRSpillMask)) {
|
||||
str(Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i+1][0])));
|
||||
} else {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i += 2) {
|
||||
const auto Reg1 = SRAFPR[i];
|
||||
const auto Reg2 = SRAFPR[i + 1];
|
||||
|
||||
if (((1U << Reg1.GetCode()) & FPRSpillMask) &&
|
||||
((1U << Reg2.GetCode()) & FPRSpillMask)) {
|
||||
stp(Reg1.Q(), Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0])));
|
||||
}
|
||||
else if (((1U << Reg1.GetCode()) & FPRSpillMask)) {
|
||||
str(Reg1.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0])));
|
||||
}
|
||||
else if (((1U << Reg2.GetCode()) & FPRSpillMask)) {
|
||||
str(Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i+1][0])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,19 +260,37 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
}
|
||||
|
||||
if (FPRs) {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i+=2) {
|
||||
auto Reg1 = SRAFPR[i];
|
||||
auto Reg2 = SRAFPR[i+1];
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
// Set up predicate registers.
|
||||
// We don't bother spilling these in SpillStaticRegs,
|
||||
// since all that matters is we restore them on a fill.
|
||||
// It's not a concern if they get trounced by something else.
|
||||
ptrue(PRED_TMP_16B.VnB(), SVE_VL16);
|
||||
ptrue(PRED_TMP_32B.VnB(), SVE_VL32);
|
||||
|
||||
if (((1U << Reg1.GetCode()) & FPRFillMask) &&
|
||||
((1U << Reg2.GetCode()) & FPRFillMask)) {
|
||||
ldp(Reg1.Q(), Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
for (size_t i = 0; i < SRAFPR.size(); i++) {
|
||||
const auto Reg = SRAFPR[i];
|
||||
|
||||
if (((1U << Reg.GetCode()) & FPRFillMask) != 0) {
|
||||
mov(TMP4, offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
ld1b(Reg.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(STATE, TMP4));
|
||||
}
|
||||
}
|
||||
else if (((1U << Reg1.GetCode()) & FPRFillMask)) {
|
||||
ldr(Reg1.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i][0])));
|
||||
}
|
||||
else if (((1U << Reg2.GetCode()) & FPRFillMask)) {
|
||||
ldr(Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm[i+1][0])));
|
||||
} else {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i += 2) {
|
||||
const auto Reg1 = SRAFPR[i];
|
||||
const auto Reg2 = SRAFPR[i + 1];
|
||||
|
||||
if (((1U << Reg1.GetCode()) & FPRFillMask) &&
|
||||
((1U << Reg2.GetCode()) & FPRFillMask)) {
|
||||
ldp(Reg1.Q(), Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0])));
|
||||
}
|
||||
else if (((1U << Reg1.GetCode()) & FPRFillMask)) {
|
||||
ldr(Reg1.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i][0])));
|
||||
}
|
||||
else if (((1U << Reg2.GetCode()) & FPRFillMask)) {
|
||||
ldr(Reg2.Q(), MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[i+1][0])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,20 +298,31 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
}
|
||||
|
||||
void Arm64Emitter::PushDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto FPRSize = RAFPR.size() * FPRRegSize;
|
||||
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
|
||||
|
||||
sub(sp, sp, SPOffset);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
str(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
if (CanUseSVE) {
|
||||
for (const auto& RA : RAFPR) {
|
||||
mov(TMP4, i * 8);
|
||||
st1b(RA.Z().VnB(), PRED_TMP_32B, SVEMemOperand(sp, TMP4));
|
||||
i += 4;
|
||||
}
|
||||
} else {
|
||||
for (const auto& RA : RAFPR) {
|
||||
str(RA.Q(), MemOperand(sp, i * 8));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
for (const auto& RA : RA64) {
|
||||
str(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
@@ -289,18 +332,29 @@ void Arm64Emitter::PushDynamicRegsAndLR() {
|
||||
}
|
||||
|
||||
void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8 + RAFPR.size() * 16, 16);
|
||||
const auto CanUseSVE = EmitterCTX->HostFeatures.SupportsAVX;
|
||||
const auto GPRSize = (RA64.size() + 1) * Core::CPUState::GPR_REG_SIZE;
|
||||
const auto FPRRegSize = CanUseSVE ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto FPRSize = RAFPR.size() * FPRRegSize;
|
||||
const uint64_t SPOffset = AlignUp(GPRSize + FPRSize, 16);
|
||||
int i = 0;
|
||||
|
||||
for (auto RA : RAFPR)
|
||||
{
|
||||
ldr(RA.Q(), MemOperand(sp, i * 8));
|
||||
i+=2;
|
||||
if (CanUseSVE) {
|
||||
for (const auto& RA : RAFPR) {
|
||||
mov(TMP4, i * 8);
|
||||
ld1b(RA.Z().VnB(), PRED_TMP_32B.Zeroing(), SVEMemOperand(sp, TMP4));
|
||||
i += 4;
|
||||
}
|
||||
} else {
|
||||
for (const auto& RA : RAFPR) {
|
||||
ldr(RA.Q(), MemOperand(sp, i * 8));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // All GPRs should be caller saved
|
||||
for (auto RA : RA64)
|
||||
{
|
||||
for (const auto& RA : RA64) {
|
||||
ldr(RA, MemOperand(sp, i * 8));
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <aarch64/cpu-aarch64.h>
|
||||
#include <aarch64/operands-aarch64.h>
|
||||
#include <platform-vixl.h>
|
||||
#ifdef VIXL_SIMULATOR
|
||||
#include <aarch64/simulator-aarch64.h>
|
||||
#include <aarch64/simulator-constants-aarch64.h>
|
||||
#endif
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
@@ -58,6 +62,27 @@ const std::array<aarch64::VRegister, 12> RAFPR = {
|
||||
v8, v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
// Contains the address to the currently available CPU state
|
||||
#define STATE x28
|
||||
|
||||
// GPR temporaries. Only x3 can be used across spill boundaries
|
||||
// so if these ever need to change, be very careful about that.
|
||||
#define TMP1 x0
|
||||
#define TMP2 x1
|
||||
#define TMP3 x2
|
||||
#define TMP4 x3
|
||||
|
||||
// Vector temporaries
|
||||
#define VTMP1 v1
|
||||
#define VTMP2 v2
|
||||
#define VTMP3 v3
|
||||
|
||||
// Predicate register temporaries (used when AVX support is enabled)
|
||||
// PRED_TMP_16B indicates a predicate register that indicates the first 16 bytes set to 1.
|
||||
// PRED_TMP_32B indicates a predicate register that indicates the first 32 bytes set to 1.
|
||||
#define PRED_TMP_16B p6
|
||||
#define PRED_TMP_32B p7
|
||||
|
||||
// This class contains common emitter utility functions that can
|
||||
// be used by both Arm64 JIT and ARM64 Dispatcher
|
||||
class Arm64Emitter : public vixl::aarch64::Assembler {
|
||||
@@ -67,6 +92,10 @@ protected:
|
||||
FEXCore::Context::Context *EmitterCTX;
|
||||
vixl::aarch64::CPU CPU;
|
||||
void LoadConstant(vixl::aarch64::Register Reg, uint64_t Constant, bool NOPPad = false);
|
||||
|
||||
// NOTE: These functions WILL clobber the register TMP4 if AVX support is enabled
|
||||
// and FPRs are being spilled or filled. If only GPRs are spilled/filled, then
|
||||
// TMP4 is left alone.
|
||||
void SpillStaticRegs(bool FPRs = true, uint32_t GPRSpillMask = ~0U, uint32_t FPRSpillMask = ~0U);
|
||||
void FillStaticRegs(bool FPRs = true, uint32_t GPRFillMask = ~0U, uint32_t FPRFillMask = ~0U);
|
||||
|
||||
@@ -83,6 +112,71 @@ protected:
|
||||
void PopCalleeSavedRegisters();
|
||||
|
||||
void Align16B();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Generates a vixl simulator runtime call.
|
||||
//
|
||||
// This matches behaviour of vixl's macro assembler, but we need to reimplement it since we aren't using the macro assembler.
|
||||
// This isn't too complex with how vixl emits this.
|
||||
//
|
||||
// Emit:
|
||||
// 1) hlt(kRuntimeCallOpcode)
|
||||
// 2) Simulator wrapper handler
|
||||
// 3) Function to call
|
||||
// 4) Style of the function call (Call versus tail-call)
|
||||
template<typename R, typename... P>
|
||||
void GenerateRuntimeCall(R (*Function)(P...)) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
|
||||
|
||||
uintptr_t FunctionAddress = reinterpret_cast<uintptr_t>(Function);
|
||||
|
||||
hlt(kRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Runtime function address to call
|
||||
dc(FunctionAddress);
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
template<typename R, typename... P>
|
||||
void GenerateIndirectRuntimeCall(vixl::aarch64::Register Reg) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<R, P...>::Wrapper));
|
||||
|
||||
hlt(kIndirectRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Register that contains the function to call
|
||||
dc(Reg.GetCode());
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateIndirectRuntimeCall<float, __uint128_t>(vixl::aarch64::Register Reg) {
|
||||
uintptr_t SimulatorWrapperAddress = reinterpret_cast<uintptr_t>(
|
||||
&(Simulator::RuntimeCallStructHelper<float, __uint128_t>::Wrapper));
|
||||
|
||||
hlt(kIndirectRuntimeCallOpcode);
|
||||
|
||||
// Simulator wrapper address pointer.
|
||||
dc(SimulatorWrapperAddress);
|
||||
|
||||
// Register that contains the function to call
|
||||
dc(Reg.GetCode());
|
||||
|
||||
// Call type
|
||||
dc32(kCallRuntime);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
FEX_CONFIG_OPT(StaticRegisterAllocation, SRA);
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) {
|
||||
static inline __uint128_t GetArmFPR(void* ucontext, uint32_t id) {
|
||||
auto MContext = GetMContext(ucontext);
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&MContext->__reserved[0]);
|
||||
LOGMAN_THROW_A_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
|
||||
LOGMAN_THROW_AA_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
|
||||
|
||||
return HostState->FPRs[id];
|
||||
}
|
||||
@@ -143,7 +143,7 @@ static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
|
||||
// Host FPR state starts at _mcontext->reserved[0];
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LOGMAN_THROW_A_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
|
||||
LOGMAN_THROW_AA_FMT(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));
|
||||
@@ -163,7 +163,7 @@ static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LOGMAN_THROW_A_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
|
||||
LOGMAN_THROW_AA_FMT(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;
|
||||
|
||||
+3
-3
@@ -58,8 +58,8 @@ auto CPUBackend::AllocateNewCodeBuffer(size_t Size) -> CodeBuffer {
|
||||
Buffer.Size = Size;
|
||||
Buffer.Ptr = static_cast<uint8_t *>(
|
||||
FEXCore::Allocator::mmap(nullptr, Buffer.Size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_A_FMT(!!Buffer.Ptr, "Couldn't allocate code buffer");
|
||||
|
||||
LOGMAN_THROW_AA_FMT(!!Buffer.Ptr, "Couldn't allocate code buffer");
|
||||
|
||||
if (ThreadState->CTX->Config.GlobalJITNaming()) {
|
||||
ThreadState->CTX->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
|
||||
}
|
||||
@@ -84,4 +84,4 @@ bool CPUBackend::IsAddressInCodeBuffer(uintptr_t Address) const {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -8,11 +8,11 @@ $end_info$
|
||||
#include "Common/StringConv.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "Interface/Core/HostFeatures.h"
|
||||
#include "Utils/FileLoading.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
|
||||
#include "git_version.h"
|
||||
@@ -39,6 +39,7 @@ namespace ProductNames {
|
||||
static const char ARM_A78C[] = "Cortex-A78C";
|
||||
static const char ARM_A710[] = "Cortex-A710";
|
||||
static const char ARM_X1[] = "Cortex-X1";
|
||||
static const char ARM_X1C[] = "Cortex-X1C";
|
||||
static const char ARM_X2[] = "Cortex-X2";
|
||||
static const char ARM_N1[] = "Neoverse N1";
|
||||
static const char ARM_N2[] = "Neoverse N2";
|
||||
@@ -83,7 +84,10 @@ static uint32_t CalculateNumberOfCPUs() {
|
||||
return CPUs;
|
||||
}
|
||||
|
||||
// TODO: Replace usages with CTX->HostFeatures.EnableAVX
|
||||
// when AVX implementations are further along.
|
||||
constexpr uint32_t SUPPORTS_AVX = 0;
|
||||
|
||||
// #define CPUID_AMD
|
||||
#ifdef CPUID_AMD
|
||||
constexpr uint32_t FAMILY_IDENTIFIER =
|
||||
@@ -151,7 +155,7 @@ void CPUIDEmu::SetupHostHybridFlag() {
|
||||
// CPU priority order
|
||||
// This is mostly arbitrary but will sort by some sort of CPU priority by performance
|
||||
// Relative list so things they will commonly end up in big.little configurations sort of relate
|
||||
static constexpr std::array<CPUMIDR, 35> CPUMIDRs = {{
|
||||
static constexpr std::array<CPUMIDR, 36> CPUMIDRs = {{
|
||||
// Typically big CPU cores
|
||||
{0x61, 0x023, 1, ProductNames::ARM_Firestorm}, // Apple M1 Firestorm
|
||||
|
||||
@@ -161,6 +165,7 @@ void CPUIDEmu::SetupHostHybridFlag() {
|
||||
{0x41, 0xd49, 1, ProductNames::ARM_N2}, // N2
|
||||
{0x41, 0xd48, 1, ProductNames::ARM_X2}, // X2
|
||||
{0x41, 0xd47, 1, ProductNames::ARM_A710}, // A710
|
||||
{0x41, 0xd4C, 1, ProductNames::ARM_X1C}, // X1C
|
||||
{0x41, 0xd44, 1, ProductNames::ARM_X1}, // X1
|
||||
{0x41, 0xd42, 1, ProductNames::ARM_A78AE}, // A78AE
|
||||
{0x41, 0xd41, 1, ProductNames::ARM_A78}, // A78
|
||||
|
||||
+72
-23
@@ -154,6 +154,16 @@ namespace FEXCore::Context {
|
||||
if (Config.CacheObjectCodeCompilation() != FEXCore::Config::ConfigObjectCodeHandler::CONFIG_NONE) {
|
||||
CodeObjectCacheService = std::make_unique<FEXCore::CodeSerialize::CodeObjectSerializeService>(this);
|
||||
}
|
||||
if (!Config.EnableAVX) {
|
||||
HostFeatures.SupportsAVX = false;
|
||||
}
|
||||
|
||||
if (Config.BlockJITNaming() ||
|
||||
Config.GlobalJITNaming() ||
|
||||
Config.LibraryJITNaming()) {
|
||||
// Only initialize symbols file if enabled. Ensures we don't pollute /tmp with empty files.
|
||||
Symbols.InitFile();
|
||||
}
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
@@ -184,9 +194,11 @@ namespace FEXCore::Context {
|
||||
greg = 0;
|
||||
}
|
||||
|
||||
for (auto& xmm : NewThreadState.xmm) {
|
||||
for (auto& xmm : NewThreadState.xmm.avx.data) {
|
||||
xmm[0] = 0xDEADBEEFULL;
|
||||
xmm[1] = 0xBAD0DAD1ULL;
|
||||
xmm[2] = 0xDEADCAFEULL;
|
||||
xmm[3] = 0xBAD2CAD3ULL;
|
||||
}
|
||||
memset(NewThreadState.flags, 0, Core::CPUState::NUM_EFLAG_BITS);
|
||||
NewThreadState.flags[1] = 1;
|
||||
@@ -209,7 +221,7 @@ namespace FEXCore::Context {
|
||||
#if (_M_X86_64 && JIT_X86_64)
|
||||
FEXCore::CPU::InitializeX86JITSignalHandlers(this);
|
||||
BackendFeatures = FEXCore::CPU::GetX86JITBackendFeatures();
|
||||
#elif (_M_ARM_64 && JIT_ARM64)
|
||||
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
|
||||
FEXCore::CPU::InitializeArm64JITSignalHandlers(this);
|
||||
BackendFeatures = FEXCore::CPU::GetArm64JITBackendFeatures();
|
||||
#else
|
||||
@@ -226,16 +238,16 @@ namespace FEXCore::Context {
|
||||
|
||||
DispatcherConfig.StaticRegisterAllocation = Config.StaticRegisterAllocation && BackendFeatures.SupportsStaticRegisterAllocation;
|
||||
|
||||
#if (_M_X86_64)
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
|
||||
#elif (_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateArm64(this, DispatcherConfig);
|
||||
#elif JIT_X86_64
|
||||
Dispatcher = FEXCore::CPU::Dispatcher::CreateX86(this, DispatcherConfig);
|
||||
#else
|
||||
ERROR_AND_DIE_FMT("FEXCore has been compiled with an unknown target");
|
||||
#endif
|
||||
|
||||
// Initialize common signal handlers
|
||||
|
||||
|
||||
auto PauseHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
|
||||
};
|
||||
@@ -495,10 +507,21 @@ namespace FEXCore::Context {
|
||||
ExecutionThreadHandler *Arg = reinterpret_cast<ExecutionThreadHandler*>(FEXCore::Allocator::malloc(sizeof(ExecutionThreadHandler)));
|
||||
Arg->This = this;
|
||||
Arg->Thread = Thread;
|
||||
Thread->StartPaused = NeedToCheckXID;
|
||||
Thread->ExecutionThread = FEXCore::Threads::Thread::Create(ThreadHandler, Arg);
|
||||
|
||||
// Wait for the thread to have started
|
||||
Thread->ThreadWaiting.Wait();
|
||||
|
||||
if (NeedToCheckXID) {
|
||||
// The first time an application creates a thread, GLIBC installs their SETXID signal handler.
|
||||
// FEX needs to capture all signals and defer them to the guest.
|
||||
// Once FEX creates its first guest thread, overwrite the GLIBC SETXID handler *again* to ensure
|
||||
// FEX maintains control of the signal handler on this signal.
|
||||
NeedToCheckXID = false;
|
||||
SignalDelegation->CheckXIDHandler();
|
||||
Thread->StartRunning.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void Context::InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread) {
|
||||
@@ -546,11 +569,11 @@ namespace FEXCore::Context {
|
||||
break;
|
||||
#endif
|
||||
case FEXCore::Config::CONFIG_IRJIT:
|
||||
Thread->PassManager->InsertRegisterAllocationPass(DoSRA);
|
||||
Thread->PassManager->InsertRegisterAllocationPass(DoSRA, HostFeatures.SupportsAVX);
|
||||
|
||||
#if (_M_X86_64 && JIT_X86_64)
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateX86JITCore(this, Thread);
|
||||
#elif (_M_ARM_64 && JIT_ARM64)
|
||||
#elif (_M_ARM_64 && JIT_ARM64) || defined(VIXL_SIMULATOR)
|
||||
Thread->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, Thread);
|
||||
#else
|
||||
ERROR_AND_DIE_FMT("FEXCore has been compiled without a viable JIT core");
|
||||
@@ -717,7 +740,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
Thread->OpDispatcher->ReownOrClaimBuffer();
|
||||
Thread->OpDispatcher->ResetWorkingList();
|
||||
|
||||
@@ -726,7 +749,7 @@ namespace FEXCore::Context {
|
||||
|
||||
|
||||
std::shared_lock lk(CustomIRMutex);
|
||||
|
||||
|
||||
auto Handler = CustomIRHandlers.find(GuestRIP);
|
||||
if (Handler != CustomIRHandlers.end()) {
|
||||
TotalInstructions = 1;
|
||||
@@ -762,6 +785,13 @@ namespace FEXCore::Context {
|
||||
// Reset any block-specific state
|
||||
Thread->OpDispatcher->StartNewBlock();
|
||||
|
||||
if (Config.x86dec_SynchronizeRIPOnAllBlocks) {
|
||||
// Ensure the RIP is synchronized to the context on block entry.
|
||||
// In the case of block linking, the RIP may not have synchronized.
|
||||
auto NewRIP = Thread->OpDispatcher->_EntrypointOffset(Block.Entry - GuestRIP, GPRSize);
|
||||
Thread->OpDispatcher->_StoreContext(GPRSize, IR::GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
|
||||
}
|
||||
|
||||
uint64_t InstsInBlock = Block.NumInstructions;
|
||||
|
||||
for (size_t i = 0; i < InstsInBlock; ++i) {
|
||||
@@ -775,7 +805,7 @@ namespace FEXCore::Context {
|
||||
if (ExtendedDebugInfo) {
|
||||
Thread->OpDispatcher->_GuestOpcode(Block.Entry + BlockInstructionsLength - GuestRIP);
|
||||
}
|
||||
|
||||
|
||||
if (Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) {
|
||||
auto ExistingCodePtr = reinterpret_cast<uint64_t*>(Block.Entry + BlockInstructionsLength);
|
||||
|
||||
@@ -788,7 +818,7 @@ namespace FEXCore::Context {
|
||||
Thread->OpDispatcher->SetTrueJumpTarget(InvalidateCodeCond, CodeWasChangedBlock);
|
||||
|
||||
Thread->OpDispatcher->SetCurrentCodeBlock(CodeWasChangedBlock);
|
||||
Thread->OpDispatcher->_RemoveThreadCodeEntry();
|
||||
Thread->OpDispatcher->_ThreadRemoveCodeEntry();
|
||||
Thread->OpDispatcher->_ExitFunction(Thread->OpDispatcher->_EntrypointOffset(Block.Entry + BlockInstructionsLength - GuestRIP, GPRSize));
|
||||
|
||||
auto NextOpBlock = Thread->OpDispatcher->CreateNewCodeBlockAfter(CurrentBlock);
|
||||
@@ -842,7 +872,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Thread->OpDispatcher->Finalize();
|
||||
|
||||
Thread->FrontendDecoder->DelayedDisownBuffer();
|
||||
@@ -1090,7 +1120,7 @@ namespace FEXCore::Context {
|
||||
// Now notify the thread that we are initialized
|
||||
Thread->ThreadWaiting.NotifyAll();
|
||||
|
||||
if (Thread != Thread->CTX->ParentThread || StartPaused) {
|
||||
if (Thread != Thread->CTX->ParentThread || StartPaused || Thread->StartPaused) {
|
||||
// Parent thread doesn't need to wait to run
|
||||
Thread->StartRunning.Wait();
|
||||
}
|
||||
@@ -1144,24 +1174,30 @@ namespace FEXCore::Context {
|
||||
|
||||
for (auto it = lower; it != upper; it++) {
|
||||
for (auto Address: it->second) {
|
||||
Context::RemoveThreadCodeEntry(Thread, Address);
|
||||
Context::ThreadRemoveCodeEntry(Thread, Address);
|
||||
}
|
||||
it->second.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
static void InvalidateGuestCodeRangeInternal(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
std::lock_guard lk(CTX->ThreadCreationMutex);
|
||||
|
||||
|
||||
for (auto &Thread : CTX->Threads) {
|
||||
InvalidateGuestThreadCodeRange(Thread, Start, Length);
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
|
||||
std::unique_lock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
|
||||
InvalidateGuestCodeRange(CTX, Start, Length);
|
||||
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
|
||||
}
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
|
||||
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
|
||||
CallAfter(Start, Length);
|
||||
}
|
||||
|
||||
@@ -1191,7 +1227,15 @@ namespace FEXCore::Context {
|
||||
CTX->MarkMemoryShared();
|
||||
}
|
||||
|
||||
void Context::RemoveThreadCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
void Context::ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
|
||||
std::shared_lock lk(Thread->CTX->CodeInvalidationMutex);
|
||||
|
||||
Thread->LookupCache->AddBlockLink(GuestDestination, HostLink, delinker);
|
||||
}
|
||||
|
||||
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
LogMan::Throw::AFmt(Thread->CTX->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(Thread->LookupCache->WriteLock);
|
||||
|
||||
Thread->DebugStore.erase(GuestRIP);
|
||||
@@ -1230,7 +1274,7 @@ namespace FEXCore::Context {
|
||||
Thread->CurrentFrame->State.rip = RIP;
|
||||
|
||||
// Erase the RIP from all the storage backings if it exists
|
||||
RemoveThreadCodeEntry(Thread, RIP);
|
||||
ThreadRemoveCodeEntry(Thread, RIP);
|
||||
|
||||
// We don't care if compilation passes or not
|
||||
CompileBlock(Thread->CurrentFrame, RIP);
|
||||
@@ -1288,8 +1332,13 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
ThunkHandler->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,12 +38,20 @@ namespace FEXCore::CPU {
|
||||
using namespace vixl;
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE x28
|
||||
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 8192;
|
||||
|
||||
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
|
||||
: FEXCore::CPU::Dispatcher(ctx), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
|
||||
, config(config) {
|
||||
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
|
||||
#ifdef VIXL_SIMULATOR
|
||||
, Simulator {&Decoder}
|
||||
#endif
|
||||
{
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Hardcode a 256-bit vector width if we are running in the simulator.
|
||||
Simulator.SetVectorLengthInBits(256);
|
||||
#endif
|
||||
|
||||
SetAllowAssembler(true);
|
||||
|
||||
DispatchPtr = GetCursorAddress<AsmDispatch>();
|
||||
@@ -179,7 +187,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ret();
|
||||
}
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// VIXL simulator can't run syscalls.
|
||||
constexpr bool SignalSafeCompile = false;
|
||||
#else
|
||||
constexpr bool SignalSafeCompile = true;
|
||||
#endif
|
||||
{
|
||||
ExitFunctionLinkerAddress = GetCursorAddress<uint64_t>();
|
||||
if (config.StaticRegisterAllocation)
|
||||
@@ -207,8 +220,12 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
mov(x0, STATE);
|
||||
mov(x1, lr);
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
|
||||
blr(x3);
|
||||
ldr(x2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionLink));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uintptr_t, void *, void *>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
if (SignalSafeCompile) {
|
||||
// Now restore the signal mask
|
||||
@@ -267,8 +284,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ldr(x3, &l_CompileBlock);
|
||||
|
||||
// X2 contains our guest RIP
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void *, uint64_t, void *>(x3);
|
||||
#else
|
||||
blr(x3); // { CTX, Frame, RIP}
|
||||
|
||||
#endif
|
||||
|
||||
if (SignalSafeCompile) {
|
||||
// Now restore the signal mask
|
||||
@@ -301,7 +321,7 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
{
|
||||
// Guest SIGILL handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
UnimplementedInstructionAddress = GetCursorAddress<uint64_t>();
|
||||
GuestSignal_SIGILL = GetCursorAddress<uint64_t>();
|
||||
|
||||
if (config.StaticRegisterAllocation)
|
||||
SpillStaticRegs();
|
||||
@@ -310,26 +330,29 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
}
|
||||
|
||||
{
|
||||
// Guest Overflow handler
|
||||
// Guest SIGTRAP handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
OverflowExceptionInstructionAddress = GetCursorAddress<uint64_t>();
|
||||
GuestSignal_SIGTRAP = GetCursorAddress<uint64_t>();
|
||||
|
||||
if (config.StaticRegisterAllocation)
|
||||
SpillStaticRegs();
|
||||
|
||||
LoadConstant(w1, 1);
|
||||
strb(w1, STATE_PTR(CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException));
|
||||
LoadConstant(w1, X86State::X86_TRAPNO_OF);
|
||||
str(w1, STATE_PTR(CpuStateFrame, SynchronousFaultData.TrapNo));
|
||||
LoadConstant(w1, 0x80);
|
||||
str(w1, STATE_PTR(CpuStateFrame, SynchronousFaultData.si_code));
|
||||
LoadConstant(x1, 0);
|
||||
str(w1, STATE_PTR(CpuStateFrame, SynchronousFaultData.err_code));
|
||||
brk(0);
|
||||
}
|
||||
|
||||
{
|
||||
// Guest Overflow handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
GuestSignal_SIGSEGV = GetCursorAddress<uint64_t>();
|
||||
|
||||
if (config.StaticRegisterAllocation)
|
||||
SpillStaticRegs();
|
||||
|
||||
// hlt/udf = SIGILL
|
||||
// brk = SIGTRAP
|
||||
// ??? = SIGSEGV
|
||||
// Force a SIGSEGV by loading zero
|
||||
LoadConstant(x1, 0);
|
||||
ldr(x1, MemOperand(x1));
|
||||
}
|
||||
|
||||
@@ -347,7 +370,11 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
ldr(x0, &l_CTX);
|
||||
mov(x1, STATE);
|
||||
ldr(x2, &l_Sleep);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void *, void *>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PauseReturnInstruction = GetCursorAddress<uint64_t>();
|
||||
// Fault to start running again
|
||||
@@ -410,11 +437,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LUDIVHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUDIV));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -429,11 +459,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LDIVHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LDIV));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -448,11 +481,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LUREMHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LUREM));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -467,11 +503,14 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
LREMHandlerAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
ldr(x3, STATE_PTR(CpuStateFrame, Pointers.AArch64.LREM));
|
||||
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Result is now in x0
|
||||
@@ -502,13 +541,27 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const Dispatche
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
void Arm64Dispatcher::ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
|
||||
Simulator.RunFrom(reinterpret_cast<Instruction const*>(DispatchPtr));
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
Simulator.WriteXRegister(0, reinterpret_cast<int64_t>(Frame));
|
||||
Simulator.WriteXRegister(1, RIP);
|
||||
Simulator.RunFrom(reinterpret_cast<Instruction const*>(CallbackPtr));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Used by GenerateGDBPauseCheck, GenerateInterpreterTrampoline, destination buffer is set before use
|
||||
static thread_local vixl::aarch64::Assembler emit((uint8_t*)&emit, 1);
|
||||
|
||||
size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) {
|
||||
|
||||
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxGDBPauseCheckSize);
|
||||
|
||||
|
||||
vixl::CodeBufferCheckScope scope(&emit, MaxGDBPauseCheckSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
|
||||
|
||||
aarch64::Label RunBlock;
|
||||
@@ -543,8 +596,8 @@ size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t Gues
|
||||
}
|
||||
|
||||
size_t Arm64Dispatcher::GenerateInterpreterTrampoline(uint8_t *CodeBuffer) {
|
||||
LOGMAN_THROW_A_FMT(!config.StaticRegisterAllocation, "GenerateInterpreterTrampoline dispatcher does not support SRA");
|
||||
|
||||
LOGMAN_THROW_AA_FMT(!config.StaticRegisterAllocation, "GenerateInterpreterTrampoline dispatcher does not support SRA");
|
||||
|
||||
*emit.GetBuffer() = vixl::CodeBuffer(CodeBuffer, MaxInterpreterTrampolineSize);
|
||||
|
||||
vixl::CodeBufferCheckScope scope(&emit, MaxInterpreterTrampolineSize, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
|
||||
@@ -570,7 +623,7 @@ size_t Arm64Dispatcher::GenerateInterpreterTrampoline(uint8_t *CodeBuffer) {
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {
|
||||
for(int i = 0; i < SRA64.size(); i++) {
|
||||
for (size_t i = 0; i < SRA64.size(); i++) {
|
||||
if (IgnoreMask & (1U << SRA64[i].GetCode())) {
|
||||
// Skip this one, it's already spilled
|
||||
continue;
|
||||
@@ -578,14 +631,21 @@ void Arm64Dispatcher::SpillSRA(FEXCore::Core::InternalThreadState *Thread, void
|
||||
Thread->CurrentFrame->State.gregs[i] = ArchHelpers::Context::GetArmReg(ucontext, SRA64[i].GetCode());
|
||||
}
|
||||
|
||||
for(int i = 0; i < SRAFPR.size(); i++) {
|
||||
auto FPR = ArchHelpers::Context::GetArmFPR(ucontext, SRAFPR[i].GetCode());
|
||||
memcpy(&Thread->CurrentFrame->State.xmm[i][0], &FPR, sizeof(__uint128_t));
|
||||
if (EmitterCTX->HostFeatures.SupportsAVX) {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i++) {
|
||||
auto FPR = ArchHelpers::Context::GetArmFPR(ucontext, SRAFPR[i].GetCode());
|
||||
memcpy(&Thread->CurrentFrame->State.xmm.avx.data[i][0], &FPR, sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < SRAFPR.size(); i++) {
|
||||
auto FPR = ArchHelpers::Context::GetArmFPR(ucontext, SRAFPR[i].GetCode());
|
||||
memcpy(&Thread->CurrentFrame->State.xmm.sse.data[i][0], &FPR, sizeof(__uint128_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) {
|
||||
// Setup dispatcher specific pointers that need to be accessed from JIT code
|
||||
// Setup dispatcher specific pointers that need to be accessed from JIT code
|
||||
{
|
||||
auto &Common = Thread->CurrentFrame->Pointers.Common;
|
||||
|
||||
@@ -594,8 +654,9 @@ void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thr
|
||||
Common.ExitFunctionLinker = ExitFunctionLinkerAddress;
|
||||
Common.ThreadStopHandlerSpillSRA = ThreadStopHandlerAddressSpillSRA;
|
||||
Common.ThreadPauseHandlerSpillSRA = ThreadPauseHandlerAddressSpillSRA;
|
||||
Common.UnimplementedInstructionHandler = UnimplementedInstructionAddress;
|
||||
Common.OverflowExceptionHandler = OverflowExceptionInstructionAddress;
|
||||
Common.GuestSignal_SIGILL = GuestSignal_SIGILL;
|
||||
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
|
||||
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
|
||||
Common.SignalReturnHandler = SignalHandlerReturnAddress;
|
||||
|
||||
auto &AArch64 = Thread->CurrentFrame->Pointers.AArch64;
|
||||
@@ -610,4 +671,4 @@ std::unique_ptr<Dispatcher> Dispatcher::CreateArm64(FEXCore::Context::Context *C
|
||||
return std::make_unique<Arm64Dispatcher>(CTX, Config);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
#include <aarch64/simulator-aarch64.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -20,6 +24,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
|
||||
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) override;
|
||||
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) override;
|
||||
|
||||
@@ -29,7 +38,11 @@ class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
uint64_t LDIVHandlerAddress{};
|
||||
uint64_t LUREMHandlerAddress{};
|
||||
uint64_t LREMHandlerAddress{};
|
||||
DispatcherConfig config;
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
vixl::aarch64::Decoder Decoder;
|
||||
vixl::aarch64::Simulator Simulator;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
+104
-23
@@ -99,6 +99,7 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
|
||||
SignalFrames.pop();
|
||||
}
|
||||
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
uintptr_t NewSP = OldSP;
|
||||
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
|
||||
|
||||
@@ -159,10 +160,22 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
|
||||
COPY_REG(RCX);
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
FEXCore::x86_64::_libc_fpstate *fpstate = reinterpret_cast<FEXCore::x86_64::_libc_fpstate*>(guest_uctx->uc_mcontext.fpregs);
|
||||
auto *xstate = reinterpret_cast<x86_64::xstate*>(guest_uctx->uc_mcontext.fpregs);
|
||||
auto *fpstate = &xstate->fpstate;
|
||||
|
||||
// Copy float registers
|
||||
memcpy(Frame->State.mm, fpstate->_st, sizeof(Frame->State.mm));
|
||||
memcpy(Frame->State.xmm, fpstate->_xmm, sizeof(Frame->State.xmm));
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&Frame->State.xmm.avx.data[i][0], &fpstate->_xmm[i], sizeof(__uint128_t));
|
||||
}
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&Frame->State.xmm.avx.data[i][2], &xstate->ymmh.ymmh_space[i], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
memcpy(Frame->State.xmm.sse.data, fpstate->_xmm, sizeof(Frame->State.xmm.sse.data));
|
||||
}
|
||||
|
||||
// FCW store default
|
||||
Frame->State.FCW = fpstate->fcw;
|
||||
@@ -216,7 +229,8 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
|
||||
COPY_REG(RCX);
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
FEXCore::x86::_libc_fpstate *fpstate = reinterpret_cast<FEXCore::x86::_libc_fpstate*>(guest_uctx->uc_mcontext.fpregs);
|
||||
auto *xstate = reinterpret_cast<x86::xstate*>(guest_uctx->uc_mcontext.fpregs);
|
||||
auto *fpstate = &xstate->fpstate;
|
||||
|
||||
// Copy float registers
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_MMS; ++i) {
|
||||
@@ -225,7 +239,16 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
|
||||
}
|
||||
|
||||
// Extended XMM state
|
||||
memcpy(fpstate->_xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
|
||||
if (IsAVXEnabled) {
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&fpstate->_xmm[i], &Frame->State.xmm.avx.data[i][0], sizeof(__uint128_t));
|
||||
}
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&xstate->ymmh.ymmh_space[i], &Frame->State.xmm.avx.data[i][2], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
memcpy(Frame->State.xmm.sse.data, fpstate->_xmm, sizeof(Frame->State.xmm.sse.data));
|
||||
}
|
||||
|
||||
// FCW store default
|
||||
Frame->State.FCW = fpstate->fcw;
|
||||
@@ -274,6 +297,26 @@ static uint32_t ConvertSignalToError(int Signal, siginfo_t *HostSigInfo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void SetXStateInfo(T* xstate, bool is_avx_enabled) {
|
||||
auto* fpstate = &xstate->fpstate;
|
||||
|
||||
fpstate->sw_reserved.magic1 = x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC;
|
||||
fpstate->sw_reserved.extended_size = is_avx_enabled ? sizeof(T) : 0;
|
||||
|
||||
fpstate->sw_reserved.xfeatures |= x86_64::fpx_sw_bytes::FEATURE_FP |
|
||||
x86_64::fpx_sw_bytes::FEATURE_SSE;
|
||||
if (is_avx_enabled) {
|
||||
fpstate->sw_reserved.xfeatures |= x86_64::fpx_sw_bytes::FEATURE_YMM;
|
||||
}
|
||||
|
||||
fpstate->sw_reserved.xstate_size = fpstate->sw_reserved.extended_size;
|
||||
|
||||
if (is_avx_enabled) {
|
||||
xstate->xstate_hdr.xfeatures = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) {
|
||||
auto ContextBackup = StoreThreadState(Thread, Signal, ucontext);
|
||||
|
||||
@@ -293,13 +336,14 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
// Pulling from context here
|
||||
bool Is64BitMode = CTX->Config.Is64BitMode;
|
||||
uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
|
||||
const bool Is64BitMode = CTX->Config.Is64BitMode;
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
|
||||
|
||||
// Spill the SRA regardless of signal handler type
|
||||
// We are going to be returning to the top of the dispatcher which will fill again
|
||||
// Otherwise we might load garbage
|
||||
if (SRAEnabled) {
|
||||
if (config.StaticRegisterAllocation) {
|
||||
if (Thread->CPUBackend->IsAddressInCodeBuffer(OldPC)) {
|
||||
uint32_t IgnoreMask{};
|
||||
#ifdef _M_ARM_64
|
||||
@@ -374,8 +418,13 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
if (GuestAction->sa_flags & SA_SIGINFO) {
|
||||
// Setup ucontext a bit
|
||||
if (Is64BitMode) {
|
||||
NewGuestSP -= sizeof(FEXCore::x86_64::_libc_fpstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(FEXCore::x86_64::_libc_fpstate));
|
||||
if (IsAVXEnabled) {
|
||||
NewGuestSP -= sizeof(x86_64::xstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(x86_64::xstate));
|
||||
} else {
|
||||
NewGuestSP -= sizeof(x86_64::_libc_fpstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(x86_64::_libc_fpstate));
|
||||
}
|
||||
uint64_t FPStateLocation = NewGuestSP;
|
||||
|
||||
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
|
||||
@@ -397,8 +446,9 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
guest_uctx->uc_flags = FEXCore::x86_64::UC_FP_XSTATE;
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->uc_mcontext.fpregs = reinterpret_cast<FEXCore::x86_64::_libc_fpstate*>(FPStateLocation);
|
||||
FEXCore::x86_64::_libc_fpstate *fpstate = reinterpret_cast<FEXCore::x86_64::_libc_fpstate*>(FPStateLocation);
|
||||
guest_uctx->uc_mcontext.fpregs = reinterpret_cast<x86_64::_libc_fpstate*>(FPStateLocation);
|
||||
auto *xstate = reinterpret_cast<x86_64::xstate*>(FPStateLocation);
|
||||
SetXStateInfo(xstate, IsAVXEnabled);
|
||||
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = Frame->State.rip;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL] = 0;
|
||||
@@ -415,6 +465,7 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
|
||||
// Overwrite si_code
|
||||
guest_siginfo->si_code = Thread->CurrentFrame->SynchronousFaultData.si_code;
|
||||
Signal = Frame->SynchronousFaultData.Signal;
|
||||
}
|
||||
else {
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_TRAPNO] = ConvertSignalToTrapNo(Signal, HostSigInfo);
|
||||
@@ -443,9 +494,21 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
|
||||
auto* fpstate = &xstate->fpstate;
|
||||
|
||||
// Copy float registers
|
||||
memcpy(fpstate->_st, Frame->State.mm, sizeof(Frame->State.mm));
|
||||
memcpy(fpstate->_xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&fpstate->_xmm[i], &Frame->State.xmm.avx.data[i][0], sizeof(__uint128_t));
|
||||
}
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
memcpy(&xstate->ymmh.ymmh_space[i], &Frame->State.xmm.avx.data[i][2], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
memcpy(fpstate->_xmm, Frame->State.xmm.sse.data, sizeof(Frame->State.xmm.sse.data));
|
||||
}
|
||||
|
||||
// FCW store default
|
||||
fpstate->fcw = Frame->State.FCW;
|
||||
@@ -470,8 +533,13 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
else {
|
||||
ContextBackup->Flags |= ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT;
|
||||
|
||||
NewGuestSP -= sizeof(FEXCore::x86::_libc_fpstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(FEXCore::x86::_libc_fpstate));
|
||||
if (IsAVXEnabled) {
|
||||
NewGuestSP -= sizeof(x86::xstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(x86::xstate));
|
||||
} else {
|
||||
NewGuestSP -= sizeof(x86::_libc_fpstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(x86::_libc_fpstate));
|
||||
}
|
||||
uint64_t FPStateLocation = NewGuestSP;
|
||||
|
||||
NewGuestSP -= sizeof(FEXCore::x86::ucontext_t);
|
||||
@@ -494,7 +562,8 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->uc_mcontext.fpregs = static_cast<uint32_t>(FPStateLocation);
|
||||
FEXCore::x86::_libc_fpstate *fpstate = reinterpret_cast<FEXCore::x86::_libc_fpstate*>(FPStateLocation);
|
||||
auto *xstate = reinterpret_cast<x86::xstate*>(FPStateLocation);
|
||||
SetXStateInfo(xstate, IsAVXEnabled);
|
||||
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_GS] = Frame->State.gs;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_FS] = Frame->State.fs;
|
||||
@@ -504,11 +573,12 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_TRAPNO] = Frame->SynchronousFaultData.TrapNo;
|
||||
guest_siginfo->si_code = Frame->SynchronousFaultData.si_code;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = Frame->SynchronousFaultData.err_code;
|
||||
Signal = Frame->SynchronousFaultData.Signal;
|
||||
}
|
||||
else {
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_TRAPNO] = ConvertSignalToTrapNo(Signal, HostSigInfo);
|
||||
guest_siginfo->si_code = HostSigInfo->si_code;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = ConvertSignalToError(Signal, HostSigInfo);
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = ConvertSignalToError(Signal, HostSigInfo);
|
||||
}
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = Frame->State.rip;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86::FEX_REG_CS] = Frame->State.cs;
|
||||
@@ -528,6 +598,8 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
COPY_REG(RSP);
|
||||
#undef COPY_REG
|
||||
|
||||
auto *fpstate = &xstate->fpstate;
|
||||
|
||||
// Copy float registers
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_MMS; ++i) {
|
||||
// 32-bit st register size is only 10 bytes. Not padded to 16byte like x86-64
|
||||
@@ -536,7 +608,16 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
|
||||
// Extended XMM state
|
||||
fpstate->status = FEXCore::x86::fpstate_magic::MAGIC_XFPSTATE;
|
||||
memcpy(fpstate->_xmm, Frame->State.xmm, sizeof(Frame->State.xmm));
|
||||
if (IsAVXEnabled) {
|
||||
for (size_t i = 0; i < std::size(Frame->State.xmm.avx.data); i++) {
|
||||
memcpy(&fpstate->_xmm[i], &Frame->State.xmm.avx.data[i][0], sizeof(__uint128_t));
|
||||
}
|
||||
for (size_t i = 0; i < std::size(Frame->State.xmm.avx.data); i++) {
|
||||
memcpy(&xstate->ymmh.ymmh_space[i], &Frame->State.xmm.avx.data[i][2], sizeof(__uint128_t));
|
||||
}
|
||||
} else {
|
||||
memcpy(fpstate->_xmm, Frame->State.xmm.sse.data, sizeof(Frame->State.xmm.sse.data));
|
||||
}
|
||||
|
||||
// FCW store default
|
||||
fpstate->fcw = Frame->State.FCW;
|
||||
@@ -619,14 +700,14 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
else {
|
||||
NewGuestSP -= 4;
|
||||
*(uint32_t*)NewGuestSP = SignalReturn;
|
||||
LOGMAN_THROW_A_FMT(SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
||||
LOGMAN_THROW_AA_FMT(SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
||||
Frame->State.gregs[FEXCore::X86State::REG_RSP] = NewGuestSP;
|
||||
}
|
||||
|
||||
// The guest starts its signal frame with a zero initialized FPU
|
||||
// Set that up now. Little bit costly but it's a requirement
|
||||
// This state will be restored on rt_sigreturn
|
||||
memset(Frame->State.xmm, 0, sizeof(Frame->State.xmm));
|
||||
memset(Frame->State.xmm.avx.data, 0, sizeof(Frame->State.xmm));
|
||||
memset(Frame->State.mm, 0, sizeof(Frame->State.mm));
|
||||
Frame->State.FCW = 0x37F;
|
||||
Frame->State.FTW = 0xFFFF;
|
||||
@@ -665,11 +746,11 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Thread, Signal, ucontext);
|
||||
|
||||
if (SRAEnabled && Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
if (config.StaticRegisterAllocation && Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
// We are in jit, SRA must be spilled
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadPauseHandlerAddressSpillSRA);
|
||||
} else {
|
||||
if (SRAEnabled) {
|
||||
if (config.StaticRegisterAllocation) {
|
||||
// We are in non-jit, SRA is already spilled
|
||||
LOGMAN_THROW_A_FMT(!IsAddressInDispatcher(ArchHelpers::Context::GetPc(ucontext)),
|
||||
"Signals in dispatcher have unsynchronized context");
|
||||
@@ -698,11 +779,11 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
|
||||
Thread->CurrentFrame->SignalHandlerRefCounter = 0;
|
||||
|
||||
// Set the new PC
|
||||
if (SRAEnabled && Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
if (config.StaticRegisterAllocation && Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
// We are in jit, SRA must be spilled
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddressSpillSRA);
|
||||
} else {
|
||||
if (SRAEnabled) {
|
||||
if (config.StaticRegisterAllocation) {
|
||||
// We are in non-jit, SRA is already spilled
|
||||
LOGMAN_THROW_A_FMT(!IsAddressInDispatcher(ArchHelpers::Context::GetPc(ucontext)),
|
||||
"Signals in dispatcher have unsynchronized context");
|
||||
|
||||
@@ -32,7 +32,7 @@ struct DispatcherConfig {
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() = default;
|
||||
|
||||
|
||||
/**
|
||||
* @name Dispatch Helper functions
|
||||
* @{ */
|
||||
@@ -44,8 +44,9 @@ public:
|
||||
uint64_t ThreadPauseHandlerAddressSpillSRA{};
|
||||
uint64_t ExitFunctionLinkerAddress{};
|
||||
uint64_t SignalHandlerReturnAddress{};
|
||||
uint64_t UnimplementedInstructionAddress{};
|
||||
uint64_t OverflowExceptionInstructionAddress{};
|
||||
uint64_t GuestSignal_SIGILL{};
|
||||
uint64_t GuestSignal_SIGTRAP{};
|
||||
uint64_t GuestSignal_SIGSEGV{};
|
||||
uint64_t IntCallbackReturnAddress{};
|
||||
|
||||
uint64_t PauseReturnInstruction{};
|
||||
@@ -74,28 +75,29 @@ public:
|
||||
|
||||
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
|
||||
void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
|
||||
virtual void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
DispatchPtr(Frame);
|
||||
}
|
||||
|
||||
void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
virtual void ExecuteJITCallback(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP) {
|
||||
CallbackPtr(Frame, RIP);
|
||||
}
|
||||
|
||||
protected:
|
||||
Dispatcher(FEXCore::Context::Context *ctx)
|
||||
Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &Config)
|
||||
: CTX {ctx}
|
||||
, config {Config}
|
||||
{}
|
||||
|
||||
ArchHelpers::Context::ContextBackup* StoreThreadState(FEXCore::Core::InternalThreadState *Thread, int Signal, void *ucontext);
|
||||
void RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext);
|
||||
std::stack<uint64_t, std::vector<uint64_t>> SignalFrames;
|
||||
|
||||
bool SRAEnabled = false;
|
||||
virtual void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {}
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
DispatcherConfig config;
|
||||
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE r14
|
||||
|
||||
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
|
||||
: Dispatcher(ctx)
|
||||
: Dispatcher(ctx, config)
|
||||
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE,
|
||||
FEXCore::Allocator::mmap(nullptr, MAX_DISPATCHER_CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
|
||||
nullptr) {
|
||||
|
||||
LOGMAN_THROW_A_FMT(!config.StaticRegisterAllocation, "X86 dispatcher does not support SRA");
|
||||
LOGMAN_THROW_AA_FMT(!config.StaticRegisterAllocation, "X86 dispatcher does not support SRA");
|
||||
|
||||
using namespace Xbyak;
|
||||
using namespace Xbyak::util;
|
||||
@@ -347,24 +347,29 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherCon
|
||||
{
|
||||
// Guest SIGILL handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
UnimplementedInstructionAddress = getCurr<uint64_t>();
|
||||
GuestSignal_SIGILL = getCurr<uint64_t>();
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
// Guest Overflow handler
|
||||
// Guest SIGTRAP handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
OverflowExceptionInstructionAddress = getCurr<uint64_t>();
|
||||
GuestSignal_SIGTRAP = getCurr<uint64_t>();
|
||||
|
||||
// ud2 = SIGILL
|
||||
// int3 = SIGTRAP
|
||||
// hlt = SIGSEGV
|
||||
|
||||
add(byte STATE_PTR(CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException), 1);
|
||||
mov(dword STATE_PTR(CpuStateFrame, SynchronousFaultData.TrapNo), X86State::X86_TRAPNO_OF);
|
||||
mov(dword STATE_PTR(CpuStateFrame, SynchronousFaultData.err_code), 0);
|
||||
mov(dword STATE_PTR(CpuStateFrame, SynchronousFaultData.si_code), 0x80);
|
||||
int3();
|
||||
}
|
||||
|
||||
{
|
||||
// Guest SIGSEGV handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
GuestSignal_SIGSEGV = getCurr<uint64_t>();
|
||||
|
||||
// ud2 = SIGILL
|
||||
// int3 = SIGTRAP
|
||||
// hlt = SIGSEGV
|
||||
hlt();
|
||||
}
|
||||
|
||||
@@ -477,8 +482,9 @@ void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Threa
|
||||
Common.ExitFunctionLinker = ExitFunctionLinkerAddress;
|
||||
Common.ThreadStopHandlerSpillSRA = ThreadStopHandlerAddress;
|
||||
Common.ThreadPauseHandlerSpillSRA = ThreadPauseHandlerAddress;
|
||||
Common.UnimplementedInstructionHandler = UnimplementedInstructionAddress;
|
||||
Common.OverflowExceptionHandler = OverflowExceptionInstructionAddress;
|
||||
Common.GuestSignal_SIGILL = GuestSignal_SIGILL;
|
||||
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
|
||||
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
|
||||
Common.SignalReturnHandler = SignalHandlerReturnAddress;
|
||||
|
||||
auto &Interpreter = Thread->CurrentFrame->Pointers.Interpreter;
|
||||
|
||||
+15
-20
@@ -188,7 +188,7 @@ Decoder::~Decoder() {
|
||||
|
||||
uint8_t Decoder::ReadByte() {
|
||||
uint8_t Byte = InstStream[InstructionSize];
|
||||
LOGMAN_THROW_A_FMT(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
|
||||
LOGMAN_THROW_AA_FMT(InstructionSize < MAX_INST_SIZE, "Max instruction size exceeded!");
|
||||
Instruction[InstructionSize] = Byte;
|
||||
InstructionSize++;
|
||||
return Byte;
|
||||
@@ -200,14 +200,7 @@ uint8_t Decoder::PeekByte(uint8_t Offset) const {
|
||||
}
|
||||
|
||||
uint64_t Decoder::ReadData(uint8_t Size) {
|
||||
if (Size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Size > sizeof(uint64_t)) {
|
||||
LOGMAN_MSG_A_FMT("Unknown data size to read");
|
||||
return 0;
|
||||
}
|
||||
LOGMAN_THROW_AA_FMT(Size != 0 && Size <= sizeof(uint64_t), "Unknown data size to read");
|
||||
|
||||
uint64_t Res = 0;
|
||||
std::memcpy(&Res, &InstStream[InstructionSize], Size);
|
||||
@@ -347,13 +340,15 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
Operand->Data.SIB.Index = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X ? 1 : 0, SIB.index, false, false, false, false, 0b100);
|
||||
Operand->Data.SIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
|
||||
LOGMAN_THROW_AA_FMT(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
|
||||
|
||||
uint64_t Literal = ReadData(Displacement);
|
||||
if (Displacement == 1) {
|
||||
Literal = static_cast<int8_t>(Literal);
|
||||
if (Displacement) {
|
||||
uint64_t Literal = ReadData(Displacement);
|
||||
if (Displacement == 1) {
|
||||
Literal = static_cast<int8_t>(Literal);
|
||||
}
|
||||
Operand->Data.SIB.Offset = Literal;
|
||||
}
|
||||
Operand->Data.SIB.Offset = Literal;
|
||||
}
|
||||
else if (ModRM.mod == 0) {
|
||||
// Explained in Table 1-14. "Operand Addressing Using ModRM and SIB Bytes"
|
||||
@@ -404,7 +399,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(!(Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 && Info->Type <= FEXCore::X86Tables::TYPE_GROUP_P),
|
||||
LOGMAN_THROW_AA_FMT(!(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{};
|
||||
@@ -528,7 +523,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_FMT(!HasMODRM, "This instruction shouldn't have ModRM!");
|
||||
LOGMAN_THROW_AA_FMT(!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
|
||||
@@ -637,7 +632,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
}
|
||||
|
||||
if (Bytes != 0) {
|
||||
LOGMAN_THROW_A_FMT(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
|
||||
LOGMAN_THROW_AA_FMT(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
|
||||
|
||||
DecodeInst->Src[CurrentSrc].Data.Literal.Size = Bytes;
|
||||
|
||||
@@ -662,7 +657,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
DecodeInst->Src[CurrentSrc].Data.Literal.Value = Literal;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Bytes == 0, "Inst at 0x{:x}: 0x{:04x} '{}' Had an instruction of size {} with {} remaining",
|
||||
LOGMAN_THROW_AA_FMT(Bytes == 0, "Inst at 0x{:x}: 0x{:04x} '{}' Had an instruction of size {} with {} remaining",
|
||||
DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name ?: "UND", InstructionSize, Bytes);
|
||||
DecodeInst->InstSize = InstructionSize;
|
||||
return true;
|
||||
@@ -688,7 +683,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX,
|
||||
LOGMAN_THROW_AA_FMT(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX,
|
||||
"REX PREFIX should have been decoded before this!");
|
||||
|
||||
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
|
||||
@@ -745,7 +740,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
3,
|
||||
};
|
||||
uint8_t Field = RegToField[ModRM.reg];
|
||||
LOGMAN_THROW_A_FMT(Field != 255, "Invalid field selected!");
|
||||
LOGMAN_THROW_AA_FMT(Field != 255, "Invalid field selected!");
|
||||
|
||||
LocalOp = (Field << 3) | ModRM.rm;
|
||||
return NormalOp(&SecondModRMTableOps[LocalOp], LocalOp);
|
||||
|
||||
+29
-5
@@ -259,7 +259,7 @@ struct FEX_PACKED GDBContextDefinition {
|
||||
uint32_t fctrl;
|
||||
uint32_t fstat;
|
||||
uint32_t dummies[6];
|
||||
uint64_t xmm[Core::CPUState::NUM_XMMS][2];
|
||||
uint64_t xmm[Core::CPUState::NUM_XMMS][4];
|
||||
uint32_t mxcsr;
|
||||
};
|
||||
|
||||
@@ -306,7 +306,7 @@ std::string GdbServer::readRegs() {
|
||||
GDB.fstat |= static_cast<uint32_t>(state.flags[FEXCore::X86State::X87FLAG_C2_LOC]) << 10;
|
||||
GDB.fstat |= static_cast<uint32_t>(state.flags[FEXCore::X86State::X87FLAG_C3_LOC]) << 14;
|
||||
|
||||
memcpy(&GDB.xmm[0], &state.xmm[0], sizeof(GDB.xmm));
|
||||
memcpy(&GDB.xmm[0], &state.xmm.avx.data[0], sizeof(GDB.xmm));
|
||||
|
||||
return encodeHex((unsigned char *)&GDB, sizeof(GDBContextDefinition));
|
||||
}
|
||||
@@ -382,9 +382,9 @@ GdbServer::HandledPacketType GdbServer::readReg(const std::string& packet) {
|
||||
}
|
||||
else if (addr >= offsetof(GDBContextDefinition, xmm[0][0]) &&
|
||||
addr < offsetof(GDBContextDefinition, xmm[16][0])) {
|
||||
const auto XmmIndex = (addr - offsetof(GDBContextDefinition, xmm[0][0])) / Core::CPUState::XMM_REG_SIZE;
|
||||
const auto *Data = (unsigned char *)&state.xmm[XmmIndex][0];
|
||||
return {encodeHex(Data, Core::CPUState::XMM_REG_SIZE), HandledPacketType::TYPE_ACK};
|
||||
const auto XmmIndex = (addr - offsetof(GDBContextDefinition, xmm[0][0])) / Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto *Data = (unsigned char *)&state.xmm.avx.data[XmmIndex][0];
|
||||
return {encodeHex(Data, Core::CPUState::XMM_AVX_REG_SIZE), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
else if (addr == offsetof(GDBContextDefinition, mxcsr)) {
|
||||
uint32_t Empty{};
|
||||
@@ -490,6 +490,30 @@ std::string buildTargetXML() {
|
||||
reg("mxcsr", "int", 32);
|
||||
|
||||
xml << "</feature>\n";
|
||||
|
||||
xml << "<feature name='org.gnu.gdb.i386.avx'>";
|
||||
xml <<
|
||||
R"(<vector id="v4f" type="ieee_single" count="4"/>
|
||||
<vector id="v2d" type="ieee_double" count="2"/>
|
||||
<vector id="v16i8" type="int8" count="16"/>
|
||||
<vector id="v8i16" type="int16" count="8"/>
|
||||
<vector id="v4i32" type="int32" count="4"/>
|
||||
<vector id="v2i64" type="int64" count="2"/>
|
||||
<union id="vec128">
|
||||
<field name="v4_float" type="v4f"/>
|
||||
<field name="v2_double" type="v2d"/>
|
||||
<field name="v16_int8" type="v16i8"/>
|
||||
<field name="v8_int16" type="v8i16"/>
|
||||
<field name="v4_int32" type="v4i32"/>
|
||||
<field name="v2_int64" type="v2i64"/>
|
||||
<field name="uint128" type="uint128"/>
|
||||
</union>
|
||||
)";
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_XMMS; i++) {
|
||||
reg(fmt::format("ymm{}h", i), "vec128", 128);
|
||||
}
|
||||
xml << "</feature>\n";
|
||||
|
||||
xml << "</target>";
|
||||
xml << std::flush;
|
||||
|
||||
|
||||
+59
-28
@@ -1,7 +1,7 @@
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "Interface/Core/HostFeatures.h"
|
||||
#include <FEXCore/Core/HostFeatures.h>
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
#include "aarch64/disasm-aarch64.h"
|
||||
@@ -50,8 +50,12 @@ static uint32_t GetDCZID() {
|
||||
|
||||
|
||||
HostFeatures::HostFeatures() {
|
||||
#ifdef _M_ARM_64
|
||||
#if defined(_M_ARM_64) || defined(VIXL_SIMULATOR)
|
||||
#ifdef VIXL_SIMULATOR
|
||||
auto Features = vixl::CPUFeatures::All();
|
||||
#else
|
||||
auto Features = vixl::CPUFeatures::InferFromOS();
|
||||
#endif
|
||||
SupportsAES = Features.Has(vixl::CPUFeatures::Feature::kAES);
|
||||
SupportsCRC = Features.Has(vixl::CPUFeatures::Feature::kCRC32);
|
||||
SupportsAtomics = Features.Has(vixl::CPUFeatures::Feature::kAtomics);
|
||||
@@ -62,6 +66,24 @@ HostFeatures::HostFeatures() {
|
||||
SupportsRCPC = Features.Has(vixl::CPUFeatures::Feature::kRCpc);
|
||||
SupportsTSOImm9 = Features.Has(vixl::CPUFeatures::Feature::kRCpcImm);
|
||||
|
||||
Supports3DNow = true;
|
||||
SupportsSSE4A = true;
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// Hardcode enable SVE with 256-bit wide registers.
|
||||
SupportsAVX = true;
|
||||
#else
|
||||
SupportsAVX = Features.Has(vixl::CPUFeatures::Feature::kSVE2) &&
|
||||
vixl::aarch64::CPU::ReadSVEVectorLengthInBits() >= 256;
|
||||
#endif
|
||||
SupportsSHA = true;
|
||||
SupportsBMI1 = true;
|
||||
SupportsBMI2 = true;
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE_FMT("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;
|
||||
@@ -71,31 +93,6 @@ HostFeatures::HostFeatures() {
|
||||
DCacheLineSize = 4 << ((CTR >> 16) & 0xF);
|
||||
ICacheLineSize = 4 << (CTR & 0xF);
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
|
||||
}
|
||||
#endif
|
||||
#ifdef _M_X86_64
|
||||
Xbyak::util::Cpu Features{};
|
||||
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
|
||||
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
|
||||
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
|
||||
SupportsRCPC = true;
|
||||
SupportsTSOImm9 = true;
|
||||
|
||||
// xbyak doesn't know how to check for CLZero
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
// First ensure we support a new enough extended CPUID function range
|
||||
__cpuid(0x8000'0000, eax, ebx, ecx, edx);
|
||||
if (eax >= 0x8000'0008U) {
|
||||
// CLZero defined in 8000_00008_EBX[bit 0]
|
||||
__cpuid(0x8000'0008, eax, ebx, ecx, edx);
|
||||
SupportsCLZERO = ebx & 1;
|
||||
}
|
||||
|
||||
SupportsFlushInputsToZero = true;
|
||||
SupportsFloatExceptions = true;
|
||||
#else
|
||||
// Test if this CPU supports float exception trapping by attempting to enable
|
||||
// On unsupported these bits are architecturally defined as RAZ/WI
|
||||
constexpr uint32_t ExceptionEnableTraps =
|
||||
@@ -116,6 +113,39 @@ HostFeatures::HostFeatures() {
|
||||
SetFPCR(OriginalFPCR);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#if defined(_M_X86_64) && !defined(VIXL_SIMULATOR)
|
||||
Xbyak::util::Cpu Features{};
|
||||
SupportsAES = Features.has(Xbyak::util::Cpu::tAESNI);
|
||||
SupportsCRC = Features.has(Xbyak::util::Cpu::tSSE42);
|
||||
SupportsRAND = Features.has(Xbyak::util::Cpu::tRDRAND) && Features.has(Xbyak::util::Cpu::tRDSEED);
|
||||
SupportsRCPC = true;
|
||||
SupportsTSOImm9 = true;
|
||||
Supports3DNow = Features.has(Xbyak::util::Cpu::t3DN) && Features.has(Xbyak::util::Cpu::tE3DN);
|
||||
SupportsSSE4A = Features.has(Xbyak::util::Cpu::tSSE4a);
|
||||
SupportsAVX = true;
|
||||
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
|
||||
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
|
||||
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
|
||||
|
||||
// xbyak doesn't know how to check for CLZero
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
// First ensure we support a new enough extended CPUID function range
|
||||
__cpuid(0x8000'0000, eax, ebx, ecx, edx);
|
||||
if (eax >= 0x8000'0008U) {
|
||||
// CLZero defined in 8000_00008_EBX[bit 0]
|
||||
__cpuid(0x8000'0008, eax, ebx, ecx, edx);
|
||||
SupportsCLZERO = ebx & 1;
|
||||
}
|
||||
|
||||
SupportsFlushInputsToZero = true;
|
||||
SupportsFloatExceptions = true;
|
||||
#endif
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// simulator doesn't support dc(ZVA)
|
||||
SupportsCLZERO = false;
|
||||
#else
|
||||
// Check if we can support cacheline clears
|
||||
uint32_t DCZID = GetDCZID();
|
||||
if ((DCZID & DCZID_DZP_MASK) == 0) {
|
||||
@@ -125,5 +155,6 @@ HostFeatures::HostFeatures() {
|
||||
// This means we can use the instruction
|
||||
SupportsCLZERO = DCZID_Bytes == CPUIDEmu::CACHELINE_SIZE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+213
-211
@@ -20,7 +20,7 @@ DEF_OP(TruncElementPair) {
|
||||
|
||||
switch (IROp->Size) {
|
||||
case 4: {
|
||||
uint64_t *Src = GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t *Src = GetSrc<uint64_t*>(Data->SSAData, Op->Pair);
|
||||
uint64_t Result{};
|
||||
Result = Src[0] & ~0U;
|
||||
Result |= Src[1] << 32;
|
||||
@@ -69,11 +69,11 @@ DEF_OP(CycleCounter) {
|
||||
|
||||
DEF_OP(Add) {
|
||||
auto Op = IROp->C<IR::IROp_Add>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Func = [](auto a, auto b) { return a + b; };
|
||||
auto *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
auto *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
const auto Func = [](auto a, auto b) { return a + b; };
|
||||
|
||||
switch (OpSize) {
|
||||
DO_OP(4, uint32_t, Func)
|
||||
@@ -84,11 +84,11 @@ DEF_OP(Add) {
|
||||
|
||||
DEF_OP(Sub) {
|
||||
auto Op = IROp->C<IR::IROp_Sub>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Func = [](auto a, auto b) { return a - b; };
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
const auto Func = [](auto a, auto b) { return a - b; };
|
||||
|
||||
switch (OpSize) {
|
||||
DO_OP(4, uint32_t, Func)
|
||||
@@ -99,9 +99,9 @@ DEF_OP(Sub) {
|
||||
|
||||
DEF_OP(Neg) {
|
||||
auto Op = IROp->C<IR::IROp_Neg>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src = *GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint64_t Src = *GetSrc<int64_t*>(Data->SSAData, Op->Src);
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = -static_cast<int32_t>(Src);
|
||||
@@ -115,10 +115,10 @@ DEF_OP(Neg) {
|
||||
|
||||
DEF_OP(Mul) {
|
||||
auto Op = IROp->C<IR::IROp_Mul>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
@@ -138,10 +138,10 @@ DEF_OP(Mul) {
|
||||
|
||||
DEF_OP(UMul) {
|
||||
auto Op = IROp->C<IR::IROp_UMul>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
@@ -161,9 +161,9 @@ DEF_OP(UMul) {
|
||||
|
||||
DEF_OP(Div) {
|
||||
auto Op = IROp->C<IR::IROp_Div>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
@@ -179,7 +179,7 @@ DEF_OP(Div) {
|
||||
GD = static_cast<int64_t>(Src1) / static_cast<int64_t>(Src2);
|
||||
break;
|
||||
case 16: {
|
||||
__int128_t Tmp = *GetSrc<__int128_t*>(Data->SSAData, Op->Header.Args[0]) / *GetSrc<__int128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
__int128_t Tmp = *GetSrc<__int128_t*>(Data->SSAData, Op->Src1) / *GetSrc<__int128_t*>(Data->SSAData, Op->Src2);
|
||||
memcpy(GDP, &Tmp, 16);
|
||||
break;
|
||||
}
|
||||
@@ -189,10 +189,10 @@ DEF_OP(Div) {
|
||||
|
||||
DEF_OP(UDiv) {
|
||||
auto Op = IROp->C<IR::IROp_UDiv>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
@@ -208,7 +208,7 @@ DEF_OP(UDiv) {
|
||||
GD = static_cast<uint64_t>(Src1) / static_cast<uint64_t>(Src2);
|
||||
break;
|
||||
case 16: {
|
||||
__uint128_t Tmp = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]) / *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
__uint128_t Tmp = *GetSrc<__uint128_t*>(Data->SSAData, Op->Src1) / *GetSrc<__uint128_t*>(Data->SSAData, Op->Src2);
|
||||
memcpy(GDP, &Tmp, 16);
|
||||
break;
|
||||
}
|
||||
@@ -218,10 +218,10 @@ DEF_OP(UDiv) {
|
||||
|
||||
DEF_OP(Rem) {
|
||||
auto Op = IROp->C<IR::IROp_Rem>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
@@ -237,7 +237,7 @@ DEF_OP(Rem) {
|
||||
GD = static_cast<int64_t>(Src1) % static_cast<int64_t>(Src2);
|
||||
break;
|
||||
case 16: {
|
||||
__int128_t Tmp = *GetSrc<__int128_t*>(Data->SSAData, Op->Header.Args[0]) % *GetSrc<__int128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
__int128_t Tmp = *GetSrc<__int128_t*>(Data->SSAData, Op->Src1) % *GetSrc<__int128_t*>(Data->SSAData, Op->Src2);
|
||||
memcpy(GDP, &Tmp, 16);
|
||||
break;
|
||||
}
|
||||
@@ -247,10 +247,10 @@ DEF_OP(Rem) {
|
||||
|
||||
DEF_OP(URem) {
|
||||
auto Op = IROp->C<IR::IROp_URem>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
@@ -266,7 +266,7 @@ DEF_OP(URem) {
|
||||
GD = static_cast<uint64_t>(Src1) % static_cast<uint64_t>(Src2);
|
||||
break;
|
||||
case 16: {
|
||||
__uint128_t Tmp = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]) % *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
__uint128_t Tmp = *GetSrc<__uint128_t*>(Data->SSAData, Op->Src1) % *GetSrc<__uint128_t*>(Data->SSAData, Op->Src2);
|
||||
memcpy(GDP, &Tmp, 16);
|
||||
break;
|
||||
}
|
||||
@@ -276,10 +276,10 @@ DEF_OP(URem) {
|
||||
|
||||
DEF_OP(MulH) {
|
||||
auto Op = IROp->C<IR::IROp_MulH>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
@@ -298,10 +298,10 @@ DEF_OP(MulH) {
|
||||
|
||||
DEF_OP(UMulH) {
|
||||
auto Op = IROp->C<IR::IROp_UMulH>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = static_cast<uint64_t>(Src1) * static_cast<uint64_t>(Src2);
|
||||
@@ -324,11 +324,11 @@ DEF_OP(UMulH) {
|
||||
|
||||
DEF_OP(Or) {
|
||||
auto Op = IROp->C<IR::IROp_Or>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Func = [](auto a, auto b) { return a | b; };
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
const auto Func = [](auto a, auto b) { return a | b; };
|
||||
|
||||
switch (OpSize) {
|
||||
DO_OP(1, uint8_t, Func)
|
||||
@@ -342,11 +342,11 @@ DEF_OP(Or) {
|
||||
|
||||
DEF_OP(And) {
|
||||
auto Op = IROp->C<IR::IROp_And>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Func = [](auto a, auto b) { return a & b; };
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
const auto Func = [](auto a, auto b) { return a & b; };
|
||||
|
||||
switch (OpSize) {
|
||||
DO_OP(1, uint8_t, Func)
|
||||
@@ -361,8 +361,8 @@ DEF_OP(Andn) {
|
||||
auto Op = IROp->C<IR::IROp_Andn>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
constexpr auto Func = [](auto a, auto b) {
|
||||
using Type = decltype(a);
|
||||
return static_cast<Type>(a & static_cast<Type>(~b));
|
||||
@@ -379,11 +379,11 @@ DEF_OP(Andn) {
|
||||
|
||||
DEF_OP(Xor) {
|
||||
auto Op = IROp->C<IR::IROp_Xor>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Func = [](auto a, auto b) { return a ^ b; };
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->Src1);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->Src2);
|
||||
const auto Func = [](auto a, auto b) { return a ^ b; };
|
||||
|
||||
switch (OpSize) {
|
||||
DO_OP(1, uint8_t, Func)
|
||||
@@ -396,11 +396,11 @@ DEF_OP(Xor) {
|
||||
|
||||
DEF_OP(Lshl) {
|
||||
auto Op = IROp->C<IR::IROp_Lshl>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint8_t Mask = OpSize * 8 - 1;
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
const uint8_t Mask = OpSize * 8 - 1;
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = static_cast<uint32_t>(Src1) << (Src2 & Mask);
|
||||
@@ -414,11 +414,11 @@ DEF_OP(Lshl) {
|
||||
|
||||
DEF_OP(Lshr) {
|
||||
auto Op = IROp->C<IR::IROp_Lshr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint8_t Mask = OpSize * 8 - 1;
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
const uint8_t Mask = OpSize * 8 - 1;
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = static_cast<uint32_t>(Src1) >> (Src2 & Mask);
|
||||
@@ -432,11 +432,11 @@ DEF_OP(Lshr) {
|
||||
|
||||
DEF_OP(Ashr) {
|
||||
auto Op = IROp->C<IR::IROp_Ashr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint8_t Mask = OpSize * 8 - 1;
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
const uint8_t Mask = OpSize * 8 - 1;
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = (uint32_t)(static_cast<int32_t>(Src1) >> (Src2 & Mask));
|
||||
@@ -450,12 +450,12 @@ DEF_OP(Ashr) {
|
||||
|
||||
DEF_OP(Ror) {
|
||||
auto Op = IROp->C<IR::IROp_Ror>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Ror = [] (auto In, auto R) {
|
||||
auto RotateMask = sizeof(In) * 8 - 1;
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src2);
|
||||
const auto Ror = [] (auto In, auto R) {
|
||||
const auto RotateMask = sizeof(In) * 8 - 1;
|
||||
R &= RotateMask;
|
||||
return (In >> R) | (In << (sizeof(In) * 8 - R));
|
||||
};
|
||||
@@ -474,11 +474,11 @@ DEF_OP(Ror) {
|
||||
|
||||
DEF_OP(Extr) {
|
||||
auto Op = IROp->C<IR::IROp_Extr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Extr = [] (auto Src1, auto Src2, uint8_t lsb) -> decltype(Src1) {
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Upper);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Lower);
|
||||
const auto Extr = [] (auto Src1, auto Src2, uint8_t lsb) -> decltype(Src1) {
|
||||
__uint128_t Result{};
|
||||
Result = Src1;
|
||||
Result <<= sizeof(Src1) * 8;
|
||||
@@ -500,7 +500,7 @@ DEF_OP(Extr) {
|
||||
}
|
||||
|
||||
DEF_OP(PDep) {
|
||||
const auto Op = IROp->C<IR::IROp_PExt>();
|
||||
const auto Op = IROp->C<IR::IROp_PDep>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
if (OpSize != 4 && OpSize != 8) {
|
||||
@@ -508,10 +508,10 @@ DEF_OP(PDep) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t Input = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Args(0))
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Args(0));
|
||||
uint64_t Mask = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Args(1))
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Args(1));
|
||||
const uint64_t Input = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Input)
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Input);
|
||||
uint64_t Mask = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Mask)
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Mask);
|
||||
|
||||
uint64_t Result = 0;
|
||||
for (uint64_t Index = 0; Mask > 0; Index++) {
|
||||
@@ -532,10 +532,10 @@ DEF_OP(PExt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t Input = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Args(0))
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Args(0));
|
||||
uint64_t Mask = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Args(1))
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Args(1));
|
||||
const uint64_t Input = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Input)
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Input);
|
||||
uint64_t Mask = OpSize == 4 ? *GetSrc<uint32_t*>(Data->SSAData, Op->Mask)
|
||||
: *GetSrc<uint64_t*>(Data->SSAData, Op->Mask);
|
||||
|
||||
uint64_t Result = 0;
|
||||
for (uint64_t Offset = 0; Mask > 0; Offset++) {
|
||||
@@ -549,39 +549,39 @@ DEF_OP(PExt) {
|
||||
|
||||
DEF_OP(LDiv) {
|
||||
auto Op = IROp->C<IR::IROp_LDiv>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
switch (OpSize) {
|
||||
case 2: {
|
||||
uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
int32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
int32_t Res = Source / Divisor;
|
||||
const uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Lower);
|
||||
const uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Upper);
|
||||
const int16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Divisor);
|
||||
const int32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
const int32_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<int16_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
int64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
int64_t Res = Source / Divisor;
|
||||
const uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Lower);
|
||||
const uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Upper);
|
||||
const int32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Divisor);
|
||||
const int64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
const int64_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<int32_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int64_t Divisor = *GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
__int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source / Divisor;
|
||||
const uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Lower);
|
||||
const uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Upper);
|
||||
const int64_t Divisor = *GetSrc<int64_t*>(Data->SSAData, Op->Divisor);
|
||||
const __int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
const __int128_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
memcpy(GDP, &Res, OpSize);
|
||||
@@ -593,39 +593,39 @@ DEF_OP(LDiv) {
|
||||
|
||||
DEF_OP(LUDiv) {
|
||||
auto Op = IROp->C<IR::IROp_LUDiv>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit divide from x86-64
|
||||
switch (OpSize) {
|
||||
case 2: {
|
||||
uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
uint32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
uint32_t Res = Source / Divisor;
|
||||
const uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Lower);
|
||||
const uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Upper);
|
||||
const uint16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Divisor);
|
||||
const uint32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
const uint32_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<uint16_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
uint64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
uint64_t Res = Source / Divisor;
|
||||
const uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Lower);
|
||||
const uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Upper);
|
||||
const uint32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Divisor);
|
||||
const uint64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
const uint64_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<uint32_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint64_t Divisor = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__uint128_t Res = Source / Divisor;
|
||||
const uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Lower);
|
||||
const uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Upper);
|
||||
const uint64_t Divisor = *GetSrc<uint64_t*>(Data->SSAData, Op->Divisor);
|
||||
const __uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
const __uint128_t Res = Source / Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
memcpy(GDP, &Res, OpSize);
|
||||
@@ -637,39 +637,39 @@ DEF_OP(LUDiv) {
|
||||
|
||||
DEF_OP(LRem) {
|
||||
auto Op = IROp->C<IR::IROp_LRem>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit Remainder from x86-64
|
||||
switch (OpSize) {
|
||||
case 2: {
|
||||
uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
int32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
int32_t Res = Source % Divisor;
|
||||
const uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Lower);
|
||||
const uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Upper);
|
||||
const int16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Divisor);
|
||||
const int32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
const int32_t Res = Source % Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<int16_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
int64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
int64_t Res = Source % Divisor;
|
||||
const uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Lower);
|
||||
const uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Upper);
|
||||
const int32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Divisor);
|
||||
const int64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
const int64_t Res = Source % Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<int32_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
int64_t Divisor = *GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
__int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__int128_t Res = Source % Divisor;
|
||||
const uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Lower);
|
||||
const uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Upper);
|
||||
const int64_t Divisor = *GetSrc<int64_t*>(Data->SSAData, Op->Divisor);
|
||||
const __int128_t Source = (static_cast<__int128_t>(SrcHigh) << 64) | SrcLow;
|
||||
const __int128_t Res = Source % Divisor;
|
||||
// We only store the lower bits of the result
|
||||
memcpy(GDP, &Res, OpSize);
|
||||
break;
|
||||
@@ -680,39 +680,39 @@ DEF_OP(LRem) {
|
||||
|
||||
DEF_OP(LURem) {
|
||||
auto Op = IROp->C<IR::IROp_LURem>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
// Each source is OpSize in size
|
||||
// So you can have up to a 128bit Remainder from x86-64
|
||||
switch (OpSize) {
|
||||
case 2: {
|
||||
uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
uint32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
uint32_t Res = Source % Divisor;
|
||||
const uint16_t SrcLow = *GetSrc<uint16_t*>(Data->SSAData, Op->Lower);
|
||||
const uint16_t SrcHigh = *GetSrc<uint16_t*>(Data->SSAData, Op->Upper);
|
||||
const uint16_t Divisor = *GetSrc<uint16_t*>(Data->SSAData, Op->Divisor);
|
||||
const uint32_t Source = (static_cast<uint32_t>(SrcHigh) << 16) | SrcLow;
|
||||
const uint32_t Res = Source % Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<uint16_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
uint64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
uint64_t Res = Source % Divisor;
|
||||
const uint32_t SrcLow = *GetSrc<uint32_t*>(Data->SSAData, Op->Lower);
|
||||
const uint32_t SrcHigh = *GetSrc<uint32_t*>(Data->SSAData, Op->Upper);
|
||||
const uint32_t Divisor = *GetSrc<uint32_t*>(Data->SSAData, Op->Divisor);
|
||||
const uint64_t Source = (static_cast<uint64_t>(SrcHigh) << 32) | SrcLow;
|
||||
const uint64_t Res = Source % Divisor;
|
||||
|
||||
// We only store the lower bits of the result
|
||||
GD = static_cast<uint32_t>(Res);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint64_t Divisor = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
__uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
__uint128_t Res = Source % Divisor;
|
||||
const uint64_t SrcLow = *GetSrc<uint64_t*>(Data->SSAData, Op->Lower);
|
||||
const uint64_t SrcHigh = *GetSrc<uint64_t*>(Data->SSAData, Op->Upper);
|
||||
const uint64_t Divisor = *GetSrc<uint64_t*>(Data->SSAData, Op->Divisor);
|
||||
const __uint128_t Source = (static_cast<__uint128_t>(SrcHigh) << 64) | SrcLow;
|
||||
const __uint128_t Res = Source % Divisor;
|
||||
// We only store the lower bits of the result
|
||||
memcpy(GDP, &Res, OpSize);
|
||||
break;
|
||||
@@ -723,62 +723,62 @@ DEF_OP(LURem) {
|
||||
|
||||
DEF_OP(Not) {
|
||||
auto Op = IROp->C<IR::IROp_Not>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
const uint64_t mask[9]= { 0, 0xFF, 0xFFFF, 0, 0xFFFFFFFF, 0, 0, 0, 0xFFFFFFFFFFFFFFFFULL };
|
||||
uint64_t Mask = mask[OpSize];
|
||||
const uint64_t Mask = mask[OpSize];
|
||||
GD = (~Src) & Mask;
|
||||
}
|
||||
|
||||
DEF_OP(Popcount) {
|
||||
auto Op = IROp->C<IR::IROp_Popcount>();
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::popcount(Src);
|
||||
}
|
||||
|
||||
DEF_OP(FindLSB) {
|
||||
auto Op = IROp->C<IR::IROp_FindLSB>();
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Result = FindFirstSetBit(Src);
|
||||
const uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
const uint64_t Result = FindFirstSetBit(Src);
|
||||
GD = Result - 1;
|
||||
}
|
||||
|
||||
DEF_OP(FindMSB) {
|
||||
auto Op = IROp->C<IR::IROp_FindMSB>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 2: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 4: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 8: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 1: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint8_t*>(Data->SSAData, Op->Src))) - 1; break;
|
||||
case 2: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint16_t*>(Data->SSAData, Op->Src))) - 1; break;
|
||||
case 4: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint32_t*>(Data->SSAData, Op->Src))) - 1; break;
|
||||
case 8: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint64_t*>(Data->SSAData, Op->Src))) - 1; break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown FindMSB size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(FindTrailingZeros) {
|
||||
auto Op = IROp->C<IR::IROp_FindTrailingZeros>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
auto Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
@@ -788,26 +788,26 @@ DEF_OP(FindTrailingZeros) {
|
||||
|
||||
DEF_OP(CountLeadingZeroes) {
|
||||
auto Op = IROp->C<IR::IROp_CountLeadingZeroes>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
auto Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint8_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint16_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint32_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
@@ -817,12 +817,12 @@ DEF_OP(CountLeadingZeroes) {
|
||||
|
||||
DEF_OP(Rev) {
|
||||
auto Op = IROp->C<IR::IROp_Rev>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 2: GD = BSwap16(*GetSrc<uint16_t*>(Data->SSAData, Op->Header.Args[0])); break;
|
||||
case 4: GD = BSwap32(*GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[0])); break;
|
||||
case 8: GD = BSwap64(*GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0])); break;
|
||||
case 2: GD = BSwap16(*GetSrc<uint16_t*>(Data->SSAData, Op->Src)); break;
|
||||
case 4: GD = BSwap32(*GetSrc<uint32_t*>(Data->SSAData, Op->Src)); break;
|
||||
case 8: GD = BSwap64(*GetSrc<uint64_t*>(Data->SSAData, Op->Src)); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown REV size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
@@ -830,34 +830,36 @@ DEF_OP(Rev) {
|
||||
DEF_OP(Bfi) {
|
||||
auto Op = IROp->C<IR::IROp_Bfi>();
|
||||
uint64_t SourceMask = (1ULL << Op->Width) - 1;
|
||||
if (Op->Width == 64)
|
||||
if (Op->Width == 64) {
|
||||
SourceMask = ~0ULL;
|
||||
uint64_t DestMask = ~(SourceMask << Op->lsb);
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint64_t Res = (Src1 & DestMask) | ((Src2 & SourceMask) << Op->lsb);
|
||||
}
|
||||
const uint64_t DestMask = ~(SourceMask << Op->lsb);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Dest);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
const uint64_t Res = (Src1 & DestMask) | ((Src2 & SourceMask) << Op->lsb);
|
||||
GD = Res;
|
||||
}
|
||||
|
||||
DEF_OP(Bfe) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
|
||||
LOGMAN_THROW_A_FMT(IROp->Size <= 8, "OpSize is too large for BFE: {}", IROp->Size);
|
||||
LOGMAN_THROW_AA_FMT(IROp->Size <= 8, "OpSize is too large for BFE: {}", IROp->Size);
|
||||
uint64_t SourceMask = (1ULL << Op->Width) - 1;
|
||||
if (Op->Width == 64)
|
||||
if (Op->Width == 64) {
|
||||
SourceMask = ~0ULL;
|
||||
}
|
||||
SourceMask <<= Op->lsb;
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Src);
|
||||
GD = (Src & SourceMask) >> Op->lsb;
|
||||
}
|
||||
|
||||
DEF_OP(Sbfe) {
|
||||
auto Op = IROp->C<IR::IROp_Sbfe>();
|
||||
|
||||
LOGMAN_THROW_A_FMT(IROp->Size <= 8, "OpSize is too large for SBFE: {}", IROp->Size);
|
||||
int64_t Src = *GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t ShiftLeftAmount = (64 - (Op->Width + Op->lsb));
|
||||
uint64_t ShiftRightAmount = ShiftLeftAmount + Op->lsb;
|
||||
LOGMAN_THROW_AA_FMT(IROp->Size <= 8, "OpSize is too large for SBFE: {}", IROp->Size);
|
||||
int64_t Src = *GetSrc<int64_t*>(Data->SSAData, Op->Src);
|
||||
const uint64_t ShiftLeftAmount = (64 - (Op->Width + Op->lsb));
|
||||
const uint64_t ShiftRightAmount = ShiftLeftAmount + Op->lsb;
|
||||
Src <<= ShiftLeftAmount;
|
||||
Src >>= ShiftRightAmount;
|
||||
GD = Src;
|
||||
@@ -865,20 +867,20 @@ DEF_OP(Sbfe) {
|
||||
|
||||
DEF_OP(Select) {
|
||||
auto Op = IROp->C<IR::IROp_Select>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
|
||||
|
||||
uint64_t ArgTrue;
|
||||
uint64_t ArgFalse;
|
||||
|
||||
if (OpSize == 4) {
|
||||
ArgTrue = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
ArgFalse = *GetSrc<uint32_t*>(Data->SSAData, Op->Header.Args[3]);
|
||||
ArgTrue = *GetSrc<uint32_t*>(Data->SSAData, Op->TrueVal);
|
||||
ArgFalse = *GetSrc<uint32_t*>(Data->SSAData, Op->FalseVal);
|
||||
} else {
|
||||
ArgTrue = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[2]);
|
||||
ArgFalse = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[3]);
|
||||
ArgTrue = *GetSrc<uint64_t*>(Data->SSAData, Op->TrueVal);
|
||||
ArgFalse = *GetSrc<uint64_t*>(Data->SSAData, Op->FalseVal);
|
||||
}
|
||||
|
||||
bool CompResult;
|
||||
@@ -894,9 +896,9 @@ DEF_OP(Select) {
|
||||
DEF_OP(VExtractToGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VExtractToGPR>();
|
||||
|
||||
uint32_t SourceSize = GetOpSize(Data->CurrentIR, Op->Header.Args[0]);
|
||||
const uint32_t SourceSize = GetOpSize(Data->CurrentIR, Op->Vector);
|
||||
|
||||
LOGMAN_THROW_A_FMT(IROp->Size <= 16, "OpSize is too large for VExtractToGPR: {}", IROp->Size);
|
||||
LOGMAN_THROW_AA_FMT(IROp->Size <= 16, "OpSize is too large for VExtractToGPR: {}", IROp->Size);
|
||||
|
||||
if (SourceSize == 16) {
|
||||
__uint128_t SourceMask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
|
||||
@@ -904,7 +906,7 @@ DEF_OP(VExtractToGPR) {
|
||||
if (Op->Header.ElementSize == 8)
|
||||
SourceMask = ~0ULL;
|
||||
|
||||
__uint128_t Src = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src = *GetSrc<__uint128_t*>(Data->SSAData, Op->Vector);
|
||||
Src >>= Shift;
|
||||
Src &= SourceMask;
|
||||
memcpy(GDP, &Src, Op->Header.ElementSize);
|
||||
@@ -915,7 +917,7 @@ DEF_OP(VExtractToGPR) {
|
||||
if (Op->Header.ElementSize == 8)
|
||||
SourceMask = ~0ULL;
|
||||
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Vector);
|
||||
Src >>= Shift;
|
||||
Src &= SourceMask;
|
||||
GD = Src;
|
||||
@@ -924,25 +926,25 @@ DEF_OP(VExtractToGPR) {
|
||||
|
||||
DEF_OP(Float_ToGPR_ZS) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: { // int64_t <- float
|
||||
int64_t Dst = (int64_t)std::trunc(*GetSrc<float*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int64_t Dst = (int64_t)std::trunc(*GetSrc<float*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // int64_t <- double
|
||||
int64_t Dst = (int64_t)std::trunc(*GetSrc<double*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int64_t Dst = (int64_t)std::trunc(*GetSrc<double*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0404: { // int32_t <- float
|
||||
int32_t Dst = (int32_t)std::trunc(*GetSrc<float*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int32_t Dst = (int32_t)std::trunc(*GetSrc<float*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // int32_t <- double
|
||||
int32_t Dst = (int32_t)std::trunc(*GetSrc<double*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int32_t Dst = (int32_t)std::trunc(*GetSrc<double*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
@@ -951,25 +953,25 @@ DEF_OP(Float_ToGPR_ZS) {
|
||||
|
||||
DEF_OP(Float_ToGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: { // int64_t <- float
|
||||
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<float*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int64_t Dst = (int64_t)std::nearbyint(*GetSrc<float*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // int64_t <- double
|
||||
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<double*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int64_t Dst = (int64_t)std::nearbyint(*GetSrc<double*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0404: { // int32_t <- float
|
||||
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<float*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int32_t Dst = (int32_t)std::nearbyint(*GetSrc<float*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // int32_t <- double
|
||||
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<double*>(Data->SSAData, Op->Header.Args[0]));
|
||||
const int32_t Dst = (int32_t)std::nearbyint(*GetSrc<double*>(Data->SSAData, Op->Scalar));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
@@ -980,9 +982,9 @@ DEF_OP(FCmp) {
|
||||
auto Op = IROp->C<IR::IROp_FCmp>();
|
||||
uint32_t ResultFlags{};
|
||||
if (Op->ElementSize == 4) {
|
||||
float Src1 = *GetSrc<float*>(Data->SSAData, Op->Header.Args[0]);
|
||||
float Src2 = *GetSrc<float*>(Data->SSAData, Op->Header.Args[1]);
|
||||
bool Unordered = std::isnan(Src1) || std::isnan(Src2);
|
||||
const float Src1 = *GetSrc<float*>(Data->SSAData, Op->Scalar1);
|
||||
const float Src2 = *GetSrc<float*>(Data->SSAData, Op->Scalar2);
|
||||
const bool Unordered = std::isnan(Src1) || std::isnan(Src2);
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_LT)) {
|
||||
if (Unordered || (Src1 < Src2)) {
|
||||
ResultFlags |= (1 << IR::FCMP_FLAG_LT);
|
||||
@@ -1000,9 +1002,9 @@ DEF_OP(FCmp) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
bool Unordered = std::isnan(Src1) || std::isnan(Src2);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Scalar1);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Scalar2);
|
||||
const bool Unordered = std::isnan(Src1) || std::isnan(Src2);
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_LT)) {
|
||||
if (Unordered || (Src1 < Src2)) {
|
||||
ResultFlags |= (1 << IR::FCMP_FLAG_LT);
|
||||
|
||||
@@ -42,7 +42,7 @@ DEF_OP(ExitFunction) {
|
||||
uintptr_t* ContextPtr = reinterpret_cast<uintptr_t*>(Data->State->CurrentFrame);
|
||||
|
||||
void *ContextData = reinterpret_cast<void*>(ContextPtr);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->NewRIP);
|
||||
|
||||
memcpy(ContextData, Src, OpSize);
|
||||
|
||||
@@ -51,22 +51,22 @@ DEF_OP(ExitFunction) {
|
||||
|
||||
DEF_OP(Jump) {
|
||||
auto Op = IROp->C<IR::IROp_Jump>();
|
||||
uintptr_t ListBegin = Data->CurrentIR->GetListData();
|
||||
uintptr_t DataBegin = Data->CurrentIR->GetData();
|
||||
const uintptr_t ListBegin = Data->CurrentIR->GetListData();
|
||||
const uintptr_t DataBegin = Data->CurrentIR->GetData();
|
||||
|
||||
Data->BlockIterator = IR::NodeIterator(ListBegin, DataBegin, Op->Header.Args[0]);
|
||||
Data->BlockIterator = IR::NodeIterator(ListBegin, DataBegin, Op->TargetBlock);
|
||||
Data->BlockResults.Redo = true;
|
||||
}
|
||||
|
||||
DEF_OP(CondJump) {
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
uintptr_t ListBegin = Data->CurrentIR->GetListData();
|
||||
uintptr_t DataBegin = Data->CurrentIR->GetData();
|
||||
const uintptr_t ListBegin = Data->CurrentIR->GetListData();
|
||||
const uintptr_t DataBegin = Data->CurrentIR->GetData();
|
||||
|
||||
bool CompResult;
|
||||
|
||||
uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
|
||||
uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
|
||||
const uint64_t Src1 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp1);
|
||||
const uint64_t Src2 = *GetSrc<uint64_t*>(Data->SSAData, Op->Cmp2);
|
||||
|
||||
if (Op->CompareSize == 4)
|
||||
CompResult = IsConditionTrue<uint32_t, int32_t, float>(Op->Cond.Val, Src1, Src2);
|
||||
@@ -127,7 +127,7 @@ DEF_OP(Thunk) {
|
||||
auto Op = IROp->C<IR::IROp_Thunk>();
|
||||
|
||||
auto thunkFn = Data->State->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
thunkFn(*GetSrc<void**>(Data->SSAData, Op->Header.Args[0]));
|
||||
thunkFn(*GetSrc<void**>(Data->SSAData, Op->ArgPtr));
|
||||
}
|
||||
|
||||
DEF_OP(ValidateCode) {
|
||||
@@ -141,15 +141,15 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(RemoveThreadCodeEntry) {
|
||||
Data->State->CTX->RemoveThreadCodeEntry(Data->State, Data->CurrentEntry);
|
||||
DEF_OP(ThreadRemoveCodeEntry) {
|
||||
Data->State->CTX->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
|
||||
}
|
||||
|
||||
DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
uint64_t *DstPtr = GetDest<uint64_t*>(Data->SSAData, Node);
|
||||
uint64_t Arg = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Leaf = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const uint64_t Arg = *GetSrc<uint64_t*>(Data->SSAData, Op->Function);
|
||||
const uint64_t Leaf = *GetSrc<uint64_t*>(Data->SSAData, Op->Leaf);
|
||||
|
||||
auto Results = Data->State->CTX->CPUID.RunFunction(Arg, Leaf);
|
||||
memcpy(DstPtr, &Results, sizeof(uint32_t) * 4);
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
|
||||
DEF_OP(VInsGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VInsGPR>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->DestVector);
|
||||
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Src);
|
||||
|
||||
uint64_t Offset = Op->DestIdx * Op->Header.ElementSize * 8;
|
||||
__uint128_t Mask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
|
||||
@@ -35,31 +35,31 @@ DEF_OP(VInsGPR) {
|
||||
|
||||
DEF_OP(VCastFromGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VCastFromGPR>();
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Header.Args[0]), Op->Header.ElementSize);
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Src), Op->Header.ElementSize);
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0404: { // Float <- int32_t
|
||||
float Dst = (float)*GetSrc<int32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const float Dst = (float)*GetSrc<int32_t*>(Data->SSAData, Op->Src);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- int64_t
|
||||
float Dst = (float)*GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const float Dst = (float)*GetSrc<int64_t*>(Data->SSAData, Op->Src);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0804: { // Double <- int32_t
|
||||
double Dst = (double)*GetSrc<int32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const double Dst = (double)*GetSrc<int32_t*>(Data->SSAData, Op->Src);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // Double <- int64_t
|
||||
double Dst = (double)*GetSrc<int64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const double Dst = (double)*GetSrc<int64_t*>(Data->SSAData, Op->Src);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
@@ -68,15 +68,15 @@ DEF_OP(Float_FromGPR_S) {
|
||||
|
||||
DEF_OP(Float_FToF) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FToF>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: { // Double <- Float
|
||||
double Dst = (double)*GetSrc<float*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const double Dst = (double)*GetSrc<float*>(Data->SSAData, Op->Scalar);
|
||||
memcpy(GDP, &Dst, 8);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- Double
|
||||
float Dst = (float)*GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const float Dst = (float)*GetSrc<double*>(Data->SSAData, Op->Scalar);
|
||||
memcpy(GDP, &Dst, 4);
|
||||
break;
|
||||
}
|
||||
@@ -86,14 +86,14 @@ DEF_OP(Float_FToF) {
|
||||
|
||||
DEF_OP(Vector_SToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_SToF>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
const uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
const auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, float, int32_t, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, double, int64_t, Func, 0, 0)
|
||||
@@ -104,14 +104,14 @@ DEF_OP(Vector_SToF) {
|
||||
|
||||
DEF_OP(Vector_FToZS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
const uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return std::trunc(a); };
|
||||
const auto Func = [](auto a, auto min, auto max) { return std::trunc(a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
|
||||
@@ -122,14 +122,14 @@ DEF_OP(Vector_FToZS) {
|
||||
|
||||
DEF_OP(Vector_FToS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToS>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
const uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return std::nearbyint(a); };
|
||||
const auto Func = [](auto a, auto min, auto max) { return std::nearbyint(a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
|
||||
@@ -140,14 +140,14 @@ DEF_OP(Vector_FToS) {
|
||||
|
||||
DEF_OP(Vector_FToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToF>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
const auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
switch (Conv) {
|
||||
case 0x0804: { // Double <- float
|
||||
// Only the lower elements from the source
|
||||
@@ -172,17 +172,17 @@ DEF_OP(Vector_FToF) {
|
||||
|
||||
DEF_OP(Vector_FToI) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToI>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Vector);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
auto Func_Nearest = [](auto a) { return std::rint(a); };
|
||||
auto Func_Neg = [](auto a) { return std::floor(a); };
|
||||
auto Func_Pos = [](auto a) { return std::ceil(a); };
|
||||
auto Func_Trunc = [](auto a) { return std::trunc(a); };
|
||||
auto Func_Host = [](auto a) { return std::rint(a); };
|
||||
const uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
const auto Func_Nearest = [](auto a) { return std::rint(a); };
|
||||
const auto Func_Neg = [](auto a) { return std::floor(a); };
|
||||
const auto Func_Pos = [](auto a) { return std::ceil(a); };
|
||||
const auto Func_Trunc = [](auto a) { return std::trunc(a); };
|
||||
const auto Func_Host = [](auto a) { return std::rint(a); };
|
||||
|
||||
switch (Op->Round) {
|
||||
case FEXCore::IR::Round_Nearest.Val:
|
||||
|
||||
@@ -360,7 +360,7 @@ namespace FEXCore::CPU {
|
||||
|
||||
DEF_OP(AESImc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESImc>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Vector);
|
||||
|
||||
// Pseudo-code
|
||||
// Dst = InvMixColumns(STATE)
|
||||
@@ -371,8 +371,8 @@ DEF_OP(AESImc) {
|
||||
|
||||
DEF_OP(AESEnc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
|
||||
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
|
||||
|
||||
// Pseudo-code
|
||||
// STATE = Src1
|
||||
@@ -391,8 +391,8 @@ DEF_OP(AESEnc) {
|
||||
|
||||
DEF_OP(AESEncLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
|
||||
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
|
||||
|
||||
// Pseudo-code
|
||||
// STATE = Src1
|
||||
@@ -409,8 +409,8 @@ DEF_OP(AESEncLast) {
|
||||
|
||||
DEF_OP(AESDec) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
|
||||
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
|
||||
|
||||
// Pseudo-code
|
||||
// STATE = Src1
|
||||
@@ -429,8 +429,8 @@ DEF_OP(AESDec) {
|
||||
|
||||
DEF_OP(AESDecLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
auto Src1 = *GetSrc<__uint128_t*>(Data->SSAData, Op->State);
|
||||
auto Src2 = *GetSrc<__uint128_t*>(Data->SSAData, Op->Key);
|
||||
|
||||
// Pseudo-code
|
||||
// STATE = Src1
|
||||
@@ -447,7 +447,7 @@ DEF_OP(AESDecLast) {
|
||||
|
||||
DEF_OP(AESKeyGenAssist) {
|
||||
auto Op = IROp->C<IR::IROp_VAESKeyGenAssist>();
|
||||
uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->Src);
|
||||
|
||||
// Pseudo-code
|
||||
// X3 = Src1[127:96]
|
||||
|
||||
+83
-96
@@ -20,99 +20,90 @@ DEF_OP(F80LOADFCW) {
|
||||
|
||||
DEF_OP(F80ADD) {
|
||||
auto Op = IROp->C<IR::IROp_F80Add>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FADD(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FADD(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80SUB) {
|
||||
auto Op = IROp->C<IR::IROp_F80Sub>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FSUB(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FSUB(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80MUL) {
|
||||
auto Op = IROp->C<IR::IROp_F80Mul>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FMUL(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FMUL(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80DIV) {
|
||||
auto Op = IROp->C<IR::IROp_F80Div>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FDIV(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FDIV(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80FYL2X) {
|
||||
auto Op = IROp->C<IR::IROp_F80FYL2X>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FYL2X(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FYL2X(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80ATAN) {
|
||||
auto Op = IROp->C<IR::IROp_F80ATAN>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FATAN(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FATAN(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80FPREM1) {
|
||||
auto Op = IROp->C<IR::IROp_F80FPREM1>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FREM1(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FREM1(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80FPREM) {
|
||||
auto Op = IROp->C<IR::IROp_F80FPREM>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FREM(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FREM(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80SCALE) {
|
||||
auto Op = IROp->C<IR::IROp_F80SCALE>();
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FSCALE(Src1, Src2);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
const auto Tmp = X80SoftFloat::FSCALE(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80CVT) {
|
||||
auto Op = IROp->C<IR::IROp_F80CVT>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
@@ -131,9 +122,9 @@ DEF_OP(F80CVT) {
|
||||
|
||||
DEF_OP(F80CVTINT) {
|
||||
auto Op = IROp->C<IR::IROp_F80CVTInt>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
|
||||
switch (OpSize) {
|
||||
case 2: {
|
||||
@@ -160,13 +151,13 @@ DEF_OP(F80CVTTO) {
|
||||
|
||||
switch (Op->SrcSize) {
|
||||
case 4: {
|
||||
float Src = *GetSrc<float *>(Data->SSAData, Op->Header.Args[0]);
|
||||
float Src = *GetSrc<float *>(Data->SSAData, Op->X80Src);
|
||||
X80SoftFloat Tmp = Src;
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
double Src = *GetSrc<double *>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src = *GetSrc<double *>(Data->SSAData, Op->X80Src);
|
||||
X80SoftFloat Tmp = Src;
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
break;
|
||||
@@ -180,13 +171,13 @@ DEF_OP(F80CVTTOINT) {
|
||||
|
||||
switch (Op->SrcSize) {
|
||||
case 2: {
|
||||
int16_t Src = *GetSrc<int16_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
int16_t Src = *GetSrc<int16_t*>(Data->SSAData, Op->Src);
|
||||
X80SoftFloat Tmp = Src;
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int32_t Src = *GetSrc<int32_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
int32_t Src = *GetSrc<int32_t*>(Data->SSAData, Op->Src);
|
||||
X80SoftFloat Tmp = Src;
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
break;
|
||||
@@ -197,77 +188,73 @@ DEF_OP(F80CVTTOINT) {
|
||||
|
||||
DEF_OP(F80ROUND) {
|
||||
auto Op = IROp->C<IR::IROp_F80Round>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FRNDINT(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FRNDINT(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80F2XM1) {
|
||||
auto Op = IROp->C<IR::IROp_F80F2XM1>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::F2XM1(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::F2XM1(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80TAN) {
|
||||
auto Op = IROp->C<IR::IROp_F80TAN>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FTAN(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FTAN(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80SQRT) {
|
||||
auto Op = IROp->C<IR::IROp_F80SQRT>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FSQRT(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FSQRT(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80SIN) {
|
||||
auto Op = IROp->C<IR::IROp_F80SIN>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FSIN(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FSIN(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80COS) {
|
||||
auto Op = IROp->C<IR::IROp_F80COS>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FCOS(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FCOS(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80XTRACT_EXP) {
|
||||
auto Op = IROp->C<IR::IROp_F80XTRACT_EXP>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FXTRACT_EXP(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FXTRACT_EXP(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80XTRACT_SIG) {
|
||||
auto Op = IROp->C<IR::IROp_F80XTRACT_SIG>();
|
||||
X80SoftFloat Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Tmp;
|
||||
Tmp = X80SoftFloat::FXTRACT_SIG(Src);
|
||||
const auto Src = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src);
|
||||
const auto Tmp = X80SoftFloat::FXTRACT_SIG(Src);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(X80SoftFloat));
|
||||
}
|
||||
|
||||
DEF_OP(F80CMP) {
|
||||
auto Op = IROp->C<IR::IROp_F80Cmp>();
|
||||
uint32_t ResultFlags{};
|
||||
X80SoftFloat Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]);
|
||||
X80SoftFloat Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const auto Src1 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src1);
|
||||
const auto Src2 = *GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src2);
|
||||
bool eq, lt, nan;
|
||||
X80SoftFloat::FCMP(Src1, Src2, &eq, <, &nan);
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_LT) &&
|
||||
@@ -288,7 +275,7 @@ DEF_OP(F80CMP) {
|
||||
|
||||
DEF_OP(F80BCDLOAD) {
|
||||
auto Op = IROp->C<IR::IROp_F80BCDLoad>();
|
||||
uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const uint8_t *Src1 = GetSrc<uint8_t*>(Data->SSAData, Op->X80Src);
|
||||
uint64_t BCD{};
|
||||
// We walk through each uint8_t and pull out the BCD encoding
|
||||
// Each 4bit split is a digit
|
||||
@@ -323,7 +310,7 @@ DEF_OP(F80BCDLOAD) {
|
||||
|
||||
DEF_OP(F80BCDSTORE) {
|
||||
auto Op = IROp->C<IR::IROp_F80BCDStore>();
|
||||
X80SoftFloat Src1 = X80SoftFloat::FRNDINT(*GetSrc<X80SoftFloat*>(Data->SSAData, Op->Header.Args[0]));
|
||||
X80SoftFloat Src1 = X80SoftFloat::FRNDINT(*GetSrc<X80SoftFloat*>(Data->SSAData, Op->X80Src));
|
||||
bool Negative = Src1.Sign;
|
||||
|
||||
// Clear the Sign bit
|
||||
@@ -358,74 +345,74 @@ DEF_OP(F80BCDSTORE) {
|
||||
|
||||
DEF_OP(F64SIN) {
|
||||
auto Op = IROp->C<IR::IROp_F64SIN>();
|
||||
double Src = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Tmp = sin(Src);
|
||||
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
|
||||
const double Tmp = sin(Src);
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64COS) {
|
||||
auto Op = IROp->C<IR::IROp_F64COS>();
|
||||
double Src = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Tmp = cos(Src);
|
||||
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
|
||||
const double Tmp = cos(Src);
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64TAN) {
|
||||
auto Op = IROp->C<IR::IROp_F64TAN>();
|
||||
double Src = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Tmp = tan(Src);
|
||||
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
|
||||
const double Tmp = tan(Src);
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64F2XM1) {
|
||||
auto Op = IROp->C<IR::IROp_F64F2XM1>();
|
||||
double Src = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Tmp = exp2(Src) - 1.0;
|
||||
const double Src = *GetSrc<double*>(Data->SSAData, Op->Src);
|
||||
const double Tmp = exp2(Src) - 1.0;
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64ATAN) {
|
||||
auto Op = IROp->C<IR::IROp_F64ATAN>();
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
double Tmp = atan2(Src1, Src2);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
|
||||
const double Tmp = atan2(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64FPREM) {
|
||||
auto Op = IROp->C<IR::IROp_F64FPREM>();
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
double Tmp = fmod(Src1, Src2);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
|
||||
const double Tmp = fmod(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64FPREM1) {
|
||||
auto Op = IROp->C<IR::IROp_F64FPREM1>();
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
double Tmp = remainder(Src1, Src2);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
|
||||
const double Tmp = remainder(Src1, Src2);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64FYL2X) {
|
||||
auto Op = IROp->C<IR::IROp_F64FYL2X>();
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
double Tmp = Src2 * log2(Src1);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
|
||||
const double Tmp = Src2 * log2(Src1);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
DEF_OP(F64SCALE) {
|
||||
auto Op = IROp->C<IR::IROp_F64SCALE>();
|
||||
double Src1 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[0]);
|
||||
double Src2 = *GetSrc<double*>(Data->SSAData, Op->Header.Args[1]);
|
||||
double trunc = (double)(int64_t)(Src2); //truncate
|
||||
double Tmp = Src1 * exp2(trunc);
|
||||
const double Src1 = *GetSrc<double*>(Data->SSAData, Op->Src1);
|
||||
const double Src2 = *GetSrc<double*>(Data->SSAData, Op->Src2);
|
||||
const double trunc = (double)(int64_t)(Src2); //truncate
|
||||
const double Tmp = Src1 * exp2(trunc);
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(double));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
|
||||
DEF_OP(GetHostFlag) {
|
||||
auto Op = IROp->C<IR::IROp_GetHostFlag>();
|
||||
GD = (*GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]) >> Op->Flag) & 1;
|
||||
GD = (*GetSrc<uint64_t*>(Data->SSAData, Op->Value) >> Op->Flag) & 1;
|
||||
}
|
||||
#undef DEF_OP
|
||||
|
||||
|
||||
@@ -159,21 +159,26 @@
|
||||
break; \
|
||||
}
|
||||
|
||||
struct InterpVector256 {
|
||||
__uint128_t Lower;
|
||||
__uint128_t Upper;
|
||||
};
|
||||
|
||||
template<typename Res>
|
||||
Res GetDest(void* SSAData, FEXCore::IR::OrderedNodeWrapper Op) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.ID().Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.ID().Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
|
||||
template<typename Res>
|
||||
Res GetDest(void* SSAData, FEXCore::IR::NodeID Op) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Op.Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Op.Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
|
||||
|
||||
template<typename Res>
|
||||
Res GetSrc(void* SSAData, FEXCore::IR::OrderedNodeWrapper Src) {
|
||||
auto DstPtr = &reinterpret_cast<__uint128_t*>(SSAData)[Src.ID().Value];
|
||||
auto DstPtr = &reinterpret_cast<InterpVector256*>(SSAData)[Src.ID().Value];
|
||||
return reinterpret_cast<Res>(DstPtr);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "InterpreterDefines.h"
|
||||
#include "InterpreterOps.h"
|
||||
#include "F80Ops.h"
|
||||
|
||||
@@ -121,7 +122,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(INLINESYSCALL, InlineSyscall);
|
||||
REGISTER_OP(THUNK, Thunk);
|
||||
REGISTER_OP(VALIDATECODE, ValidateCode);
|
||||
REGISTER_OP(REMOVETHREADCODEENTRY, RemoveThreadCodeEntry);
|
||||
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
|
||||
REGISTER_OP(CPUID, CPUID);
|
||||
|
||||
// Conversion ops
|
||||
@@ -180,13 +181,10 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
// Move ops
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
REGISTER_OP(MOV, Mov);
|
||||
|
||||
// Vector ops
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(SPLATVECTOR2, SplatVector);
|
||||
REGISTER_OP(SPLATVECTOR4, SplatVector);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
@@ -245,8 +243,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(VUSHRS, VUShrS);
|
||||
REGISTER_OP(VSSHRS, VSShrS);
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
|
||||
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VSLI, VSLI);
|
||||
@@ -256,7 +252,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(VSHLI, VShlI);
|
||||
REGISTER_OP(VUSHRNI, VUShrNI);
|
||||
REGISTER_OP(VUSHRNI2, VUShrNI2);
|
||||
REGISTER_OP(VBITCAST, VBitcast);
|
||||
REGISTER_OP(VSXTL, VSXTL);
|
||||
REGISTER_OP(VSXTL2, VSXTL2);
|
||||
REGISTER_OP(VUXTL, VUXTL);
|
||||
@@ -336,29 +331,34 @@ void InterpreterOps::Op_NoOp(FEXCore::IR::IROp_Header *IROp, IROpData *Data, IR:
|
||||
void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::IR::IRListView const *CurrentIR) {
|
||||
volatile void *StackEntry = alloca(0);
|
||||
|
||||
uintptr_t ListSize = CurrentIR->GetSSACount();
|
||||
const uintptr_t ListSize = CurrentIR->GetSSACount();
|
||||
|
||||
static_assert(sizeof(FEXCore::IR::IROp_Header) == 4);
|
||||
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
|
||||
|
||||
auto BlockEnd = CurrentIR->GetBlocks().end();
|
||||
|
||||
InterpreterOps::IROpData OpData{};
|
||||
OpData.State = Frame->Thread;
|
||||
OpData.SSAData = alloca(ListSize * 16);
|
||||
OpData.CurrentEntry = Frame->State.rip;
|
||||
OpData.CurrentIR = CurrentIR;
|
||||
OpData.StackEntry = StackEntry;
|
||||
OpData.BlockIterator = CurrentIR->GetBlocks().begin();
|
||||
constexpr size_t ListEntrySizeInBytes = sizeof(InterpVector256);
|
||||
const size_t SSADataSize = ListSize * ListEntrySizeInBytes;
|
||||
|
||||
// Clear them all to zero. Required for Zero-extend semantics
|
||||
memset(OpData.SSAData, 0, ListSize * 16);
|
||||
InterpreterOps::IROpData OpData{
|
||||
.State = Frame->Thread,
|
||||
.CurrentEntry = Frame->State.rip,
|
||||
.CurrentIR = CurrentIR,
|
||||
.StackEntry = StackEntry,
|
||||
.SSAData = alloca(SSADataSize),
|
||||
.BlockResults = {},
|
||||
.BlockIterator = CurrentIR->GetBlocks().begin(),
|
||||
};
|
||||
|
||||
// Clear all SSAData entries to zero. Required for Zero-extend semantics
|
||||
memset(OpData.SSAData, 0, SSADataSize);
|
||||
|
||||
while (1) {
|
||||
using namespace FEXCore::IR;
|
||||
auto [BlockNode, BlockHeader] = OpData.BlockIterator();
|
||||
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
// Reset the block results per block
|
||||
memset(&OpData.BlockResults, 0, sizeof(OpData.BlockResults));
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(InlineSyscall);
|
||||
DEF_OP(Thunk);
|
||||
DEF_OP(ValidateCode);
|
||||
DEF_OP(RemoveThreadCodeEntry);
|
||||
DEF_OP(ThreadRemoveCodeEntry);
|
||||
DEF_OP(CPUID);
|
||||
|
||||
///< Conversion ops
|
||||
@@ -207,7 +207,6 @@ namespace FEXCore::CPU {
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -264,8 +263,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -275,7 +272,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
@@ -59,7 +59,7 @@ DEF_OP(StoreContext) {
|
||||
ContextPtr += Op->Offset;
|
||||
|
||||
void *MemData = reinterpret_cast<void*>(ContextPtr);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Value);
|
||||
memcpy(MemData, Src, OpSize);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ DEF_OP(StoreRegister) {
|
||||
|
||||
DEF_OP(LoadContextIndexed) {
|
||||
auto Op = IROp->C<IR::IROp_LoadContextIndexed>();
|
||||
uint64_t Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Index);
|
||||
|
||||
uintptr_t ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
|
||||
|
||||
@@ -103,14 +103,14 @@ DEF_OP(LoadContextIndexed) {
|
||||
|
||||
DEF_OP(StoreContextIndexed) {
|
||||
auto Op = IROp->C<IR::IROp_StoreContextIndexed>();
|
||||
uint64_t Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[1]);
|
||||
uint64_t Index = *GetSrc<uint64_t*>(Data->SSAData, Op->Index);
|
||||
|
||||
uintptr_t ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
|
||||
ContextPtr += Op->BaseOffset;
|
||||
ContextPtr += Index * Op->Stride;
|
||||
|
||||
void *MemData = reinterpret_cast<void*>(ContextPtr);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src = GetSrc<void*>(Data->SSAData, Op->Value);
|
||||
memcpy(MemData, Src, IROp->Size);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ DEF_OP(LoadFlag) {
|
||||
|
||||
DEF_OP(StoreFlag) {
|
||||
auto Op = IROp->C<IR::IROp_StoreFlag>();
|
||||
uint8_t Arg = *GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint8_t Arg = *GetSrc<uint8_t*>(Data->SSAData, Op->Value);
|
||||
|
||||
uintptr_t ContextPtr = reinterpret_cast<uintptr_t>(Data->State->CurrentFrame);
|
||||
ContextPtr += offsetof(FEXCore::Core::CPUState, flags[0]);
|
||||
@@ -228,9 +228,9 @@ DEF_OP(StoreMem) {
|
||||
|
||||
DEF_OP(VLoadMemElement) {
|
||||
auto Op = IROp->C<IR::IROp_VLoadMemElement>();
|
||||
void const *MemData = *GetSrc<void const**>(Data->SSAData, Op->Header.Args[0]);
|
||||
void const *MemData = *GetSrc<void const**>(Data->SSAData, Op->Value);
|
||||
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Header.Args[1]), 16);
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Addr), 16);
|
||||
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(GDP) + (Op->Header.ElementSize * Op->Index)),
|
||||
MemData, Op->Header.ElementSize);
|
||||
}
|
||||
@@ -238,8 +238,8 @@ DEF_OP(VLoadMemElement) {
|
||||
DEF_OP(VStoreMemElement) {
|
||||
#define STORE_DATA(x, y) \
|
||||
case x: { \
|
||||
y *MemData = *GetSrc<y**>(Data->SSAData, Op->Header.Args[0]); \
|
||||
memcpy(MemData, &GetSrc<y*>(Data->SSAData, Op->Header.Args[1])[Op->Index], sizeof(y)); \
|
||||
y *MemData = *GetSrc<y**>(Data->SSAData, Op->Value); \
|
||||
memcpy(MemData, &GetSrc<y*>(Data->SSAData, Op->Addr)[Op->Index], sizeof(y)); \
|
||||
break; \
|
||||
}
|
||||
|
||||
|
||||
+24
-12
@@ -46,14 +46,26 @@ DEF_OP(Fence) {
|
||||
|
||||
DEF_OP(Break) {
|
||||
auto Op = IROp->C<IR::IROp_Break>();
|
||||
switch (Op->Reason) {
|
||||
case FEXCore::IR::Break_Halt: // HLT
|
||||
StopThread(Data->State);
|
||||
|
||||
Data->State->CurrentFrame->SynchronousFaultData.FaultToTopAndGeneratedException = 1;
|
||||
Data->State->CurrentFrame->SynchronousFaultData.Signal = Op->Reason.Signal;
|
||||
Data->State->CurrentFrame->SynchronousFaultData.TrapNo = Op->Reason.TrapNumber;
|
||||
Data->State->CurrentFrame->SynchronousFaultData.err_code = Op->Reason.ErrorRegister;
|
||||
Data->State->CurrentFrame->SynchronousFaultData.si_code = Op->Reason.si_code;
|
||||
|
||||
switch (Op->Reason.Signal) {
|
||||
case SIGILL:
|
||||
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGILL);
|
||||
break;
|
||||
case FEXCore::IR::Break_InvalidInstruction:
|
||||
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGILL);
|
||||
case SIGTRAP:
|
||||
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGTRAP);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGSEGV);
|
||||
break;
|
||||
default:
|
||||
FHU::Syscalls::tgkill(Data->State->ThreadManager.PID, Data->State->ThreadManager.TID, SIGTRAP);
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Break Reason: {}", Op->Reason); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +100,7 @@ DEF_OP(GetRoundingMode) {
|
||||
|
||||
DEF_OP(SetRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
|
||||
uint8_t GuestRounding = *GetSrc<uint8_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto GuestRounding = *GetSrc<uint8_t*>(Data->SSAData, Op->RoundMode);
|
||||
#ifdef _M_ARM_64
|
||||
uint64_t HostRounding{};
|
||||
__asm volatile(R"(
|
||||
@@ -128,16 +140,16 @@ DEF_OP(SetRoundingMode) {
|
||||
|
||||
DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
if (OpSize <= 8) {
|
||||
uint64_t Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
|
||||
LogMan::Msg::IFmt(">>>> Value in Arg: 0x{:x}, {}", Src, Src);
|
||||
}
|
||||
else if (OpSize == 16) {
|
||||
__uint128_t Src = *GetSrc<__uint128_t*>(Data->SSAData, Op->Header.Args[0]);
|
||||
uint64_t Src0 = Src;
|
||||
uint64_t Src1 = Src >> 64;
|
||||
const auto Src = *GetSrc<__uint128_t*>(Data->SSAData, Op->Value);
|
||||
const uint64_t Src0 = Src;
|
||||
const uint64_t Src1 = Src >> 64;
|
||||
LogMan::Msg::IFmt(">>>> Value[0] in Arg: 0x{:x}, {}", Src0, Src0);
|
||||
LogMan::Msg::IFmt(" Value[1] in Arg: 0x{:x}, {}", Src1, Src1);
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
|
||||
DEF_OP(ExtractElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
|
||||
uintptr_t Src = GetSrc<uintptr_t>(Data->SSAData, Op->Header.Args[0]);
|
||||
const auto Src = GetSrc<uintptr_t>(Data->SSAData, Op->Pair);
|
||||
memcpy(GDP,
|
||||
reinterpret_cast<void*>(Src + Op->Header.Size * Op->Element), Op->Header.Size);
|
||||
}
|
||||
|
||||
DEF_OP(CreateElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_CreateElementPair>();
|
||||
void *Src_Lower = GetSrc<void*>(Data->SSAData, Op->Header.Args[0]);
|
||||
void *Src_Upper = GetSrc<void*>(Data->SSAData, Op->Header.Args[1]);
|
||||
const void *Src_Lower = GetSrc<void*>(Data->SSAData, Op->Lower);
|
||||
const void *Src_Upper = GetSrc<void*>(Data->SSAData, Op->Upper);
|
||||
|
||||
uint8_t *Dst = GetDest<uint8_t*>(Data->SSAData, Node);
|
||||
|
||||
@@ -30,13 +30,6 @@ DEF_OP(CreateElementPair) {
|
||||
memcpy(Dst + IROp->ElementSize, Src_Upper, IROp->ElementSize);
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Header.Args[0]), OpSize);
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
+898
-724
File diff suppressed because it is too large.
Load diff
@@ -1084,8 +1084,8 @@ DEF_OP(Bfi) {
|
||||
|
||||
DEF_OP(Bfe) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
LOGMAN_THROW_A_FMT(IROp->Size <= 8, "OpSize is too large for BFE: {}", IROp->Size);
|
||||
LOGMAN_THROW_A_FMT(Op->Width != 0, "Invalid BFE width of 0");
|
||||
LOGMAN_THROW_AA_FMT(IROp->Size <= 8, "OpSize is too large for BFE: {}", IROp->Size);
|
||||
LOGMAN_THROW_AA_FMT(Op->Width != 0, "Invalid BFE width of 0");
|
||||
|
||||
auto Dst = GetReg<RA_64>(Node);
|
||||
ubfx(Dst, GetReg<RA_64>(Op->Src.ID()), Op->lsb, Op->Width);
|
||||
@@ -1249,7 +1249,7 @@ DEF_OP(FCmp) {
|
||||
bool set = false;
|
||||
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) {
|
||||
LOGMAN_THROW_A_FMT(IR::FCMP_FLAG_EQ == 0, "IR::FCMP_FLAG_EQ must equal 0");
|
||||
LOGMAN_THROW_AA_FMT(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
|
||||
|
||||
@@ -81,7 +81,7 @@ bool Arm64JITCore::ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uin
|
||||
size_t DataIndex{};
|
||||
for (size_t j = 0; j < NumRelocations; ++j) {
|
||||
const FEXCore::CPU::Relocation *Reloc = reinterpret_cast<const FEXCore::CPU::Relocation *>(&EntryRelocations[DataIndex]);
|
||||
LOGMAN_THROW_A_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to");
|
||||
LOGMAN_THROW_AA_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to");
|
||||
|
||||
switch (Reloc->Header.Type) {
|
||||
case FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL: {
|
||||
|
||||
@@ -40,7 +40,7 @@ DEF_OP(CallbackReturn) {
|
||||
ResetStack();
|
||||
|
||||
// We can now lower the ref counter again
|
||||
|
||||
|
||||
ldr(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
|
||||
sub(w2, w2, 1);
|
||||
str(w2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SignalHandlerRefCounter)));
|
||||
@@ -197,7 +197,11 @@ DEF_OP(Syscall) {
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SyscallHandlerFunc)));
|
||||
mov(x1, STATE);
|
||||
mov(x2, sp);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, void*, void*, void*>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
#endif
|
||||
|
||||
add(sp, sp, SPOffset);
|
||||
|
||||
@@ -381,7 +385,11 @@ DEF_OP(Thunk) {
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
LoadConstant(x2, (uintptr_t)thunkFn);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -436,7 +444,7 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(RemoveThreadCodeEntry) {
|
||||
DEF_OP(ThreadRemoveCodeEntry) {
|
||||
// Arguments are passed as follows:
|
||||
// X0: Thread
|
||||
// X1: RIP
|
||||
@@ -446,9 +454,13 @@ DEF_OP(RemoveThreadCodeEntry) {
|
||||
mov(x0, STATE);
|
||||
LoadConstant(x1, Entry);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.RemoveThreadCodeEntryFromJIT)));
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadRemoveCodeEntryFromJIT)));
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void*, void*>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
FillStaticRegs();
|
||||
|
||||
// Fix the stack and any values that were stepped on
|
||||
@@ -459,6 +471,7 @@ DEF_OP(CPUID) {
|
||||
auto Op = IROp->C<IR::IROp_CPUID>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
// x0 = CPUID Handler
|
||||
// x1 = CPUID Function
|
||||
@@ -467,10 +480,13 @@ DEF_OP(CPUID) {
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDFunction)));
|
||||
mov(x1, GetReg<RA_64>(Op->Function.ID()));
|
||||
mov(x2, GetReg<RA_64>(Op->Leaf.ID()));
|
||||
SpillStaticRegs();
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, void*, uint64_t, uint64_t>(x3);
|
||||
#else
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
#endif
|
||||
|
||||
FillStaticRegs();
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
// Results are in x0, x1
|
||||
@@ -492,7 +508,7 @@ void Arm64JITCore::RegisterBranchHandlers() {
|
||||
REGISTER_OP(INLINESYSCALL, InlineSyscall);
|
||||
REGISTER_OP(THUNK, Thunk);
|
||||
REGISTER_OP(VALIDATECODE, ValidateCode);
|
||||
REGISTER_OP(REMOVETHREADCODEENTRY, RemoveThreadCodeEntry);
|
||||
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
|
||||
REGISTER_OP(CPUID, CPUID);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
+96
-26
@@ -27,6 +27,7 @@ $end_info$
|
||||
#include <FEXCore/Core/UContext.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
|
||||
#include "Interface/Core/Interpreter/InterpreterOps.h"
|
||||
|
||||
@@ -93,7 +94,12 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
uxth(w0, GetReg<RA_32>(IROp->Args[0].ID()));
|
||||
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, uint16_t>(x1);
|
||||
#else
|
||||
blr(x1);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -108,7 +114,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
fmov(v0.S(), GetSrc(IROp->Args[0].ID()).S()) ;
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, float>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -127,7 +137,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -152,7 +166,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
mov(w0, GetReg<RA_32>(IROp->Args[0].ID()));
|
||||
}
|
||||
ldr(x1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint32_t>(x1);
|
||||
#else
|
||||
blr(x1);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -173,7 +191,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<float, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -192,7 +214,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -209,7 +235,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -228,7 +258,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
mov(v0.D(), GetSrc(IROp->Args[0].ID()).D());
|
||||
mov(v1.D(), GetSrc(IROp->Args[1].ID()).D());
|
||||
ldr(x0, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<double, double, double>(x0);
|
||||
#else
|
||||
blr(x0);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -248,7 +282,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -266,7 +304,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint32_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -284,7 +326,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -305,8 +351,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
|
||||
|
||||
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
|
||||
#else
|
||||
blr(x4);
|
||||
|
||||
#endif
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
FillStaticRegs();
|
||||
@@ -323,7 +372,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w1, GetSrc(IROp->Args[0].ID()).V8H(), 4);
|
||||
|
||||
ldr(x2, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t>(x2);
|
||||
#else
|
||||
blr(x2);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -346,7 +399,11 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
umov(w3, GetSrc(IROp->Args[1].ID()).V8H(), 4);
|
||||
|
||||
ldr(x4, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])));
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<__uint128_t, uint64_t, uint64_t, uint64_t, uint64_t>(x4);
|
||||
#else
|
||||
blr(x4);
|
||||
#endif
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
@@ -361,7 +418,8 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
case FABI_UNKNOWN:
|
||||
default:
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}", FEXCore::IR::GetName(IROp->Op), Info.ABI);
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
|
||||
FEXCore::IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -395,7 +453,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
|
||||
vixl::aarch64::CPU::EnsureIAndDCacheCoherency((void*)branch, 24);
|
||||
|
||||
// Add de-linking handler
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
|
||||
vixl::aarch64::Assembler emit((uint8_t*)(branch), 24);
|
||||
vixl::CodeBufferCheckScope scope(&emit, 24, vixl::CodeBufferCheckScope::kDontReserveBufferSpace, vixl::CodeBufferCheckScope::kNoAssert);
|
||||
Literal l_BranchHost{LinkerAddress};
|
||||
@@ -410,7 +468,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
|
||||
record[0] = HostCode;
|
||||
|
||||
// Add de-linking handler
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
record[0] = LinkerAddress;
|
||||
});
|
||||
}
|
||||
@@ -424,6 +482,7 @@ void Arm64JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
|
||||
, Arm64Emitter(ctx, 0)
|
||||
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
|
||||
, CTX {ctx} {
|
||||
|
||||
RAPass = Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA");
|
||||
@@ -470,10 +529,10 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
// Common
|
||||
auto &Common = ThreadState->CurrentFrame->Pointers.Common;
|
||||
|
||||
|
||||
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
|
||||
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
|
||||
Common.RemoveThreadCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::RemoveThreadCodeEntryFromJit);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
|
||||
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
|
||||
|
||||
{
|
||||
@@ -483,7 +542,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
|
||||
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Arm64JITCore_ExitFunctionLink);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<Arm64JITCore_ExitFunctionLink>);
|
||||
|
||||
|
||||
// Fill in the fallback handlers
|
||||
@@ -491,7 +550,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
// Platform Specific
|
||||
auto &AArch64 = ThreadState->CurrentFrame->Pointers.AArch64;
|
||||
|
||||
|
||||
AArch64.LUDIV = reinterpret_cast<uint64_t>(LUDIV);
|
||||
AArch64.LDIV = reinterpret_cast<uint64_t>(LDIV);
|
||||
AArch64.LUREM = reinterpret_cast<uint64_t>(LUREM);
|
||||
@@ -508,6 +567,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
}, true);
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
if (!Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) {
|
||||
// Wasn't a sigbus in JIT code
|
||||
@@ -516,6 +576,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
|
||||
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(Thread->CTX->Config.ParanoidTSO(), Signal, info, ucontext);
|
||||
}, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Arm64JITCore::EmitDetectionString() {
|
||||
@@ -527,7 +588,7 @@ void Arm64JITCore::EmitDetectionString() {
|
||||
|
||||
void Arm64JITCore::ClearCache() {
|
||||
// Get the backing code buffer
|
||||
|
||||
|
||||
auto CodeBuffer = GetEmptyCodeBuffer();
|
||||
*GetBuffer() = vixl::CodeBuffer(CodeBuffer->Ptr, CodeBuffer->Size);
|
||||
EmitDetectionString();
|
||||
@@ -549,12 +610,12 @@ template<>
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(IR::NodeID Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Reg.Class == IR::GPRFixedClass.Val || Reg.Class == IR::GPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass.Val) {
|
||||
return SRA64[Reg.Reg].W();
|
||||
} else if (Reg.Class == IR::GPRClass.Val) {
|
||||
return RA64[Reg.Reg].W();
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Class: {}", Reg.Class);
|
||||
}
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
@@ -564,12 +625,12 @@ template<>
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(IR::NodeID Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Reg.Class == IR::GPRFixedClass.Val || Reg.Class == IR::GPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass.Val) {
|
||||
return SRA64[Reg.Reg];
|
||||
} else if (Reg.Class == IR::GPRClass.Val) {
|
||||
return RA64[Reg.Reg];
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Class: {}", Reg.Class);
|
||||
}
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
@@ -590,12 +651,12 @@ std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JI
|
||||
aarch64::VRegister Arm64JITCore::GetSrc(IR::NodeID Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Reg.Class == IR::FPRFixedClass.Val || Reg.Class == IR::FPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (Reg.Class == IR::FPRFixedClass.Val) {
|
||||
return SRAFPR[Reg.Reg];
|
||||
} else if (Reg.Class == IR::FPRClass.Val) {
|
||||
return RAFPR[Reg.Reg];
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Class: {}", Reg.Class);
|
||||
}
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
@@ -604,12 +665,12 @@ aarch64::VRegister Arm64JITCore::GetSrc(IR::NodeID Node) const {
|
||||
aarch64::VRegister Arm64JITCore::GetDst(IR::NodeID Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Reg.Class == IR::FPRFixedClass.Val || Reg.Class == IR::FPRClass.Val, "Unexpected Class: {}", Reg.Class);
|
||||
|
||||
if (Reg.Class == IR::FPRFixedClass.Val) {
|
||||
return SRAFPR[Reg.Reg];
|
||||
} else if (Reg.Class == IR::FPRClass.Val) {
|
||||
return RAFPR[Reg.Reg];
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Class: {}", Reg.Class);
|
||||
}
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
@@ -665,7 +726,11 @@ bool Arm64JITCore::IsGPR(IR::NodeID Node) const {
|
||||
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
|
||||
}
|
||||
|
||||
void *Arm64JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
|
||||
void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData,
|
||||
bool GDBEnabled) {
|
||||
using namespace aarch64;
|
||||
JumpTargets.clear();
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
@@ -718,10 +783,12 @@ void *Arm64JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IR
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
if (SpillSlots) {
|
||||
if (IsImmAddSub(SpillSlots * 16)) {
|
||||
sub(sp, sp, SpillSlots * 16);
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
sub(sp, sp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
LoadConstant(x0, SpillSlots * 16);
|
||||
LoadConstant(x0, TotalSpillSlotsSize);
|
||||
sub(sp, sp, x0);
|
||||
}
|
||||
}
|
||||
@@ -732,7 +799,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IR
|
||||
using namespace FEXCore::IR;
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
#endif
|
||||
|
||||
auto BlockStartHostCode = GetCursorAddress<uint8_t *>();
|
||||
@@ -789,14 +856,17 @@ void *Arm64JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IR
|
||||
}
|
||||
|
||||
void Arm64JITCore::ResetStack() {
|
||||
if (SpillSlots == 0)
|
||||
if (SpillSlots == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsImmAddSub(SpillSlots * 16)) {
|
||||
add(sp, sp, SpillSlots * 16);
|
||||
const auto TotalSpillSlotsSize = SpillSlots * MaxSpillSlotSize;
|
||||
|
||||
if (IsImmAddSub(TotalSpillSlotsSize)) {
|
||||
add(sp, sp, TotalSpillSlotsSize);
|
||||
} else {
|
||||
// Too big to fit in a 12bit immediate
|
||||
LoadConstant(x0, SpillSlots * 16);
|
||||
LoadConstant(x0, TotalSpillSlotsSize);
|
||||
add(sp, sp, x0);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-17
@@ -23,16 +23,6 @@ $end_info$
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#define STATE x28
|
||||
#define TMP1 x0
|
||||
#define TMP2 x1
|
||||
#define TMP3 x2
|
||||
#define TMP4 x3
|
||||
|
||||
#define VTMP1 v1
|
||||
#define VTMP2 v2
|
||||
#define VTMP3 v3
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
@@ -66,6 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
const bool HostSupportsSVE{};
|
||||
|
||||
Label *PendingTargetLabel;
|
||||
FEXCore::Context::Context *CTX;
|
||||
@@ -309,7 +300,7 @@ private:
|
||||
DEF_OP(InlineSyscall);
|
||||
DEF_OP(Thunk);
|
||||
DEF_OP(ValidateCode);
|
||||
DEF_OP(RemoveThreadCodeEntry);
|
||||
DEF_OP(ThreadRemoveCodeEntry);
|
||||
DEF_OP(CPUID);
|
||||
|
||||
///< Conversion ops
|
||||
@@ -364,13 +355,10 @@ private:
|
||||
///< Move ops
|
||||
DEF_OP(ExtractElementPair);
|
||||
DEF_OP(CreateElementPair);
|
||||
DEF_OP(Mov);
|
||||
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector2);
|
||||
DEF_OP(SplatVector4);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -429,8 +417,6 @@ private:
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -440,7 +426,6 @@ private:
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
+68
-38
@@ -115,30 +115,32 @@ DEF_OP(LoadRegister) {
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
ubfx(GetReg<RA_64>(Node), reg, regOffs * 8, 8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
ubfx(GetReg<RA_64>(Node), reg, 0, 16);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(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_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(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(Core::CpuStateFrame, State.xmm[0][0])) / Core::CPUState::XMM_REG_SIZE;
|
||||
auto regOffs = Op->Offset & 15;
|
||||
const auto regSize = CTX->HostFeatures.SupportsAVX ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto regId = (Op->Offset - offsetof(Core::CpuStateFrame, State.xmm.avx.data[0][0])) / regSize;
|
||||
const auto regOffs = Op->Offset & 15;
|
||||
|
||||
LOGMAN_THROW_A_FMT(regId < SRAFPR.size(), "out of range regId");
|
||||
|
||||
@@ -147,17 +149,17 @@ DEF_OP(LoadRegister) {
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
mov(host.B(), guest.B());
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
fmov(host.H(), guest.H());
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LOGMAN_THROW_A_FMT((regOffs & 3) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT((regOffs & 3) == 0, "unexpected regOffs");
|
||||
if (regOffs == 0) {
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
fmov(host.S(), guest.S());
|
||||
@@ -167,7 +169,7 @@ DEF_OP(LoadRegister) {
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LOGMAN_THROW_A_FMT((regOffs & 7) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT((regOffs & 7) == 0, "unexpected regOffs");
|
||||
if (regOffs == 0) {
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
mov(host.D(), guest.D());
|
||||
@@ -177,13 +179,13 @@ DEF_OP(LoadRegister) {
|
||||
break;
|
||||
|
||||
case 16:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
if (host.GetCode() != guest.GetCode())
|
||||
mov(host.Q(), guest.Q());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class);
|
||||
LOGMAN_THROW_AA_FMT(false, "Unhandled Op->Class {}", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,29 +202,31 @@ DEF_OP(StoreRegister) {
|
||||
|
||||
switch(Op->Header.Size) {
|
||||
case 1:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0 || regOffs == 1, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), regOffs * 8, 8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 16);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
bfi(reg, GetReg<RA_64>(Op->Value.ID()), 0, 32);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(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(Core::CpuStateFrame, State.xmm[0][0])) / Core::CPUState::XMM_REG_SIZE;
|
||||
auto regOffs = Op->Offset & 15;
|
||||
const auto regSize = CTX->HostFeatures.SupportsAVX ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto regId = (Op->Offset - offsetof(Core::CpuStateFrame, State.xmm.avx.data[0][0])) / regSize;
|
||||
const auto regOffs = Op->Offset & 15;
|
||||
|
||||
LOGMAN_THROW_A_FMT(regId < SRAFPR.size(), "regId out of range");
|
||||
|
||||
@@ -235,28 +239,28 @@ DEF_OP(StoreRegister) {
|
||||
break;
|
||||
|
||||
case 2:
|
||||
LOGMAN_THROW_A_FMT((regOffs & 1) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT((regOffs & 1) == 0, "unexpected regOffs");
|
||||
ins(guest.V8H(), regOffs/2, host.V8H(), 0);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LOGMAN_THROW_A_FMT((regOffs & 3) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT((regOffs & 3) == 0, "unexpected regOffs");
|
||||
ins(guest.V4S(), regOffs/4, host.V4S(), 0);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
LOGMAN_THROW_A_FMT((regOffs & 7) == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT((regOffs & 7) == 0, "unexpected regOffs");
|
||||
ins(guest.V2D(), regOffs / 8, host.V2D(), 0);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
LOGMAN_THROW_A_FMT(regOffs == 0, "unexpected regOffs");
|
||||
LOGMAN_THROW_AA_FMT(regOffs == 0, "unexpected regOffs");
|
||||
if (guest.GetCode() != host.GetCode())
|
||||
mov(guest.Q(), host.Q());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unhandled Op->Class {}", Op->Class);
|
||||
LOGMAN_THROW_AA_FMT(false, "Unhandled Op->Class {}", Op->Class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,9 +446,9 @@ DEF_OP(StoreContextIndexed) {
|
||||
}
|
||||
|
||||
DEF_OP(SpillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * 16;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -464,23 +468,36 @@ DEF_OP(SpillRegister) {
|
||||
str(GetReg<RA_64>(Op->Value.ID()), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Src = GetSrc(Op->Value.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
str(GetSrc(Op->Value.ID()).S(), MemOperand(sp, SlotOffset));
|
||||
str(Src.S(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
str(GetSrc(Op->Value.ID()).D(), MemOperand(sp, SlotOffset));
|
||||
str(Src.D(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
str(GetSrc(Op->Value.ID()), MemOperand(sp, SlotOffset));
|
||||
str(Src, MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
// TODO: Eliminate ptrue with statically allocated predicate register.
|
||||
ptrue(p7.VnB(), SVE_VL32);
|
||||
mov(TMP3, SlotOffset);
|
||||
st1b(Src.Z().VnB(), p7, SVEMemOperand(sp, TMP3));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
|
||||
@@ -488,9 +505,9 @@ DEF_OP(SpillRegister) {
|
||||
}
|
||||
|
||||
DEF_OP(FillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
const auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
@@ -510,23 +527,36 @@ DEF_OP(FillRegister) {
|
||||
ldr(GetReg<RA_64>(Node), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Dst = GetDst(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
ldr(GetDst(Node).S(), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst.S(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ldr(GetDst(Node).D(), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst.D(), MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
ldr(GetDst(Node), MemOperand(sp, SlotOffset));
|
||||
ldr(Dst, MemOperand(sp, SlotOffset));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
// TODO: Eliminate ptrue with statically allocated predicate register.
|
||||
ptrue(p7.VnB(), SVE_VL32);
|
||||
mov(TMP3, SlotOffset);
|
||||
ld1b(Dst.Z().VnB(), p7.Zeroing(), SVEMemOperand(sp, TMP3));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
|
||||
|
||||
+36
-37
@@ -37,43 +37,38 @@ DEF_OP(Fence) {
|
||||
|
||||
DEF_OP(Break) {
|
||||
auto Op = IROp->C<IR::IROp_Break>();
|
||||
switch (Op->Reason) {
|
||||
case FEXCore::IR::Break_Unimplemented: // Hard fault
|
||||
case FEXCore::IR::Break_Interrupt: // Guest ud2
|
||||
hlt(4);
|
||||
break;
|
||||
case FEXCore::IR::Break_Overflow: // overflow
|
||||
ResetStack();
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.OverflowExceptionHandler)));
|
||||
br(TMP1);
|
||||
break;
|
||||
case FEXCore::IR::Break_Halt: { // HLT
|
||||
// Time to quit
|
||||
// Set our stack to the starting stack location
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)));
|
||||
add(sp, TMP1, 0);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadStopHandlerSpillSRA)));
|
||||
br(TMP1);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::Break_Interrupt3: { // INT3
|
||||
ResetStack();
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadPauseHandlerSpillSRA)));
|
||||
br(TMP1);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::Break_InvalidInstruction:
|
||||
{
|
||||
ResetStack();
|
||||
// First we must reset the stack
|
||||
ResetStack();
|
||||
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.UnimplementedInstructionHandler)));
|
||||
br(TMP1);
|
||||
LoadConstant(w1, 1);
|
||||
strb(w1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException)));
|
||||
LoadConstant(w1, Op->Reason.Signal);
|
||||
strb(w1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.Signal)));
|
||||
LoadConstant(w1, Op->Reason.TrapNumber);
|
||||
str(w1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.TrapNo)));
|
||||
LoadConstant(w1, Op->Reason.si_code);
|
||||
str(w1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.si_code)));
|
||||
LoadConstant(x1, Op->Reason.ErrorRegister);
|
||||
str(w1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.err_code)));
|
||||
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Break reason: {}", Op->Reason);
|
||||
switch (Op->Reason.Signal) {
|
||||
case SIGILL:
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGILL)));
|
||||
br(TMP1);
|
||||
break;
|
||||
case SIGTRAP:
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)));
|
||||
br(TMP1);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGSEGV)));
|
||||
br(TMP1);
|
||||
break;
|
||||
default:
|
||||
ldr(TMP1, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)));
|
||||
br(TMP1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +119,11 @@ DEF_OP(SetRoundingMode) {
|
||||
|
||||
mrs(TMP1, FPCR);
|
||||
|
||||
// vixl simulator doesn't support anything beyond ties-to-even rounding
|
||||
#ifndef VIXL_SIMULATOR
|
||||
// Insert the rounding flags
|
||||
bfi(TMP1, TMP2, 22, 2);
|
||||
#endif
|
||||
|
||||
// Insert the FTZ flag
|
||||
lsr(TMP2, Src, 2);
|
||||
@@ -139,6 +137,7 @@ DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
PushDynamicRegsAndLR();
|
||||
SpillStaticRegs();
|
||||
|
||||
if (IsGPR(Op->Value.ID())) {
|
||||
mov(x0, GetReg<RA_64>(Op->Value.ID()));
|
||||
@@ -150,10 +149,10 @@ DEF_OP(Print) {
|
||||
fmov(x1, GetSrc(Op->Value.ID()).V1D(), 1);
|
||||
ldr(x3, MemOperand(STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintVectorValue)));
|
||||
}
|
||||
SpillStaticRegs();
|
||||
blr(x3);
|
||||
FillStaticRegs();
|
||||
|
||||
blr(x3);
|
||||
|
||||
FillStaticRegs();
|
||||
PopDynamicRegsAndLR();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,17 +68,11 @@ DEF_OP(CreateElementPair) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
mov(GetReg<RA_64>(Node), GetReg<RA_64>(Op->Value.ID()));
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
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);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+3212
-1146
File diff suppressed because it is too large.
Load diff
+236
-238
File diff suppressed because it is too large.
Load diff
+11
-11
@@ -33,7 +33,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(SignalReturn) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16); // + 8 to consume return address
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
|
||||
}
|
||||
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
|
||||
@@ -42,7 +42,7 @@ DEF_OP(SignalReturn) {
|
||||
DEF_OP(CallbackReturn) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16); // + 8 to consume return address
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
|
||||
}
|
||||
|
||||
// Make sure to adjust the refcounter so we don't clear the cache now
|
||||
@@ -71,7 +71,7 @@ DEF_OP(ExitFunction) {
|
||||
|
||||
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16);
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
uint64_t NewRIP;
|
||||
@@ -117,9 +117,9 @@ DEF_OP(ExitFunction) {
|
||||
|
||||
DEF_OP(Jump) {
|
||||
const auto Op = IROp->C<IR::IROp_Jump>();
|
||||
const auto ArgID = Op->Args(0).ID();
|
||||
const auto Target = Op->TargetBlock.ID();
|
||||
|
||||
PendingTargetLabel = &JumpTargets.try_emplace(ArgID).first->second;
|
||||
PendingTargetLabel = &JumpTargets.try_emplace(Target).first->second;
|
||||
}
|
||||
|
||||
#define GRCMP(Node) (Op->CompareSize == 4 ? GetSrc<RA_32>(Node) : GetSrc<RA_64>(Node))
|
||||
@@ -209,7 +209,7 @@ DEF_OP(Thunk) {
|
||||
if (NumPush & 1)
|
||||
sub(rsp, 8); // Align
|
||||
|
||||
mov(rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(rdi, GetSrc<RA_64>(Op->ArgPtr.ID()));
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
|
||||
@@ -253,7 +253,7 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(RemoveThreadCodeEntry) {
|
||||
DEF_OP(ThreadRemoveCodeEntry) {
|
||||
auto NumPush = RA64.size();
|
||||
|
||||
for (auto &Reg : RA64)
|
||||
@@ -266,7 +266,7 @@ DEF_OP(RemoveThreadCodeEntry) {
|
||||
mov(rax, Entry); // imm64 move
|
||||
mov(rsi, rax);
|
||||
|
||||
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.RemoveThreadCodeEntryFromJIT)]);
|
||||
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadRemoveCodeEntryFromJIT)]);
|
||||
|
||||
if (NumPush & 1)
|
||||
add(rsp, 8); // Align
|
||||
@@ -288,8 +288,8 @@ DEF_OP(CPUID) {
|
||||
// Result: RAX, RDX. 4xi32
|
||||
|
||||
// rsi can be in the source registers, so copy argument to edx first
|
||||
mov (edx, GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
mov (esi, GetSrc<RA_32>(Op->Header.Args[0].ID()));
|
||||
mov (edx, GetSrc<RA_32>(Op->Leaf.ID()));
|
||||
mov (esi, GetSrc<RA_32>(Op->Function.ID()));
|
||||
mov (rdi, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.CPUIDObj)]);
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
@@ -322,7 +322,7 @@ void X86JITCore::RegisterBranchHandlers() {
|
||||
REGISTER_OP(SYSCALL, Syscall);
|
||||
REGISTER_OP(THUNK, Thunk);
|
||||
REGISTER_OP(VALIDATECODE, ValidateCode);
|
||||
REGISTER_OP(REMOVETHREADCODEENTRY, RemoveThreadCodeEntry);
|
||||
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
|
||||
REGISTER_OP(CPUID, CPUID);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
@@ -45,18 +45,18 @@ DEF_OP(VCastFromGPR) {
|
||||
auto Op = IROp->C<IR::IROp_VCastFromGPR>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1:
|
||||
movzx(rax, GetSrc<RA_8>(Op->Header.Args[0].ID()));
|
||||
movzx(rax, GetSrc<RA_8>(Op->Src.ID()));
|
||||
vmovq(GetDst(Node), rax);
|
||||
break;
|
||||
case 2:
|
||||
movzx(rax, GetSrc<RA_16>(Op->Header.Args[0].ID()));
|
||||
movzx(rax, GetSrc<RA_16>(Op->Src.ID()));
|
||||
vmovq(GetDst(Node), rax);
|
||||
break;
|
||||
case 4:
|
||||
vmovd(GetDst(Node), GetSrc<RA_32>(Op->Header.Args[0].ID()).cvt32());
|
||||
vmovd(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()).cvt32());
|
||||
break;
|
||||
case 8:
|
||||
vmovq(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()).cvt64());
|
||||
vmovq(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()).cvt64());
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown VCastFromGPR element size: {}", Op->Header.ElementSize);
|
||||
}
|
||||
@@ -64,22 +64,23 @@ DEF_OP(VCastFromGPR) {
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
|
||||
switch (Conv) {
|
||||
case 0x0404: { // Float <- int32_t
|
||||
cvtsi2ss(GetDst(Node), GetSrc<RA_32>(Op->Header.Args[0].ID()));
|
||||
cvtsi2ss(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()));
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- int64_t
|
||||
cvtsi2ss(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
cvtsi2ss(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()));
|
||||
break;
|
||||
}
|
||||
case 0x0804: { // Double <- int32_t
|
||||
cvtsi2sd(GetDst(Node), GetSrc<RA_32>(Op->Header.Args[0].ID()));
|
||||
cvtsi2sd(GetDst(Node), GetSrc<RA_32>(Op->Src.ID()));
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // Double <- int64_t
|
||||
cvtsi2sd(GetDst(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
cvtsi2sd(GetDst(Node), GetSrc<RA_64>(Op->Src.ID()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -87,14 +88,15 @@ DEF_OP(Float_FromGPR_S) {
|
||||
|
||||
DEF_OP(Float_FToF) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FToF>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
|
||||
switch (Conv) {
|
||||
case 0x0804: { // Double <- Float
|
||||
cvtss2sd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtss2sd(GetDst(Node), GetSrc(Op->Scalar.ID()));
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- Double
|
||||
cvtsd2ss(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtsd2ss(GetDst(Node), GetSrc(Op->Scalar.ID()));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Float_FToF sizes: 0x{:x}", Conv);
|
||||
@@ -105,7 +107,7 @@ DEF_OP(Vector_SToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_SToF>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
cvtdq2ps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtdq2ps(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
case 8:
|
||||
// This operation is a bit disgusting in x86
|
||||
@@ -113,8 +115,8 @@ DEF_OP(Vector_SToF) {
|
||||
// 1) First extract the top 64bits
|
||||
// 2) Do a scalar conversion on each
|
||||
// 3) Make sure to merge them together at the end
|
||||
pextrq(rax, GetSrc(Op->Header.Args[0].ID()), 1);
|
||||
pextrq(rcx, GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
pextrq(rax, GetSrc(Op->Vector.ID()), 1);
|
||||
pextrq(rcx, GetSrc(Op->Vector.ID()), 0);
|
||||
cvtsi2sd(GetDst(Node), rcx);
|
||||
cvtsi2sd(xmm15, rax);
|
||||
movlhps(GetDst(Node), xmm15);
|
||||
@@ -127,10 +129,10 @@ DEF_OP(Vector_FToZS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
cvttps2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvttps2dq(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
case 8:
|
||||
cvttpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvttpd2dq(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Vector_FToZS element size: {}", Op->Header.ElementSize);
|
||||
}
|
||||
@@ -140,10 +142,10 @@ DEF_OP(Vector_FToS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
cvtps2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtps2dq(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
case 8:
|
||||
cvtpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtpd2dq(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Vector_FToS element size: {}", Op->Header.ElementSize);
|
||||
}
|
||||
@@ -151,15 +153,15 @@ DEF_OP(Vector_FToS) {
|
||||
|
||||
DEF_OP(Vector_FToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToF>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
const uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
|
||||
switch (Conv) {
|
||||
case 0x0804: { // Double <- Float
|
||||
cvtps2pd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtps2pd(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- Double
|
||||
cvtpd2ps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
cvtpd2ps(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Vector_FToF conversion type : 0x{:04x}", Conv); break;
|
||||
@@ -190,10 +192,10 @@ DEF_OP(Vector_FToI) {
|
||||
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
roundps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
|
||||
roundps(GetDst(Node), GetSrc(Op->Vector.ID()), RoundMode);
|
||||
break;
|
||||
case 8:
|
||||
roundpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
|
||||
roundpd(GetDst(Node), GetSrc(Op->Vector.ID()), RoundMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,44 +17,44 @@ namespace FEXCore::CPU {
|
||||
|
||||
DEF_OP(AESImc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESImc>();
|
||||
vaesimc(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
vaesimc(GetDst(Node), GetSrc(Op->Vector.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(AESEnc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
vaesenc(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
vaesenc(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(AESEncLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
vaesenclast(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
vaesenclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(AESDec) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
vaesdec(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
vaesdec(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(AESDecLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
vaesdeclast(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
vaesdeclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
}
|
||||
|
||||
DEF_OP(AESKeyGenAssist) {
|
||||
auto Op = IROp->C<IR::IROp_VAESKeyGenAssist>();
|
||||
vaeskeygenassist(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), Op->RCON);
|
||||
vaeskeygenassist(GetDst(Node), GetSrc(Op->Src.ID()), Op->RCON);
|
||||
}
|
||||
|
||||
DEF_OP(CRC32) {
|
||||
auto Op = IROp->C<IR::IROp_CRC32>();
|
||||
switch (IROp->Size) {
|
||||
case 4:
|
||||
mov(TMP1, GetSrc<RA_32>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_32>(Node), GetSrc<RA_32>(Op->Header.Args[0].ID()));
|
||||
mov(TMP1, GetSrc<RA_32>(Op->Src2.ID()));
|
||||
mov(GetDst<RA_32>(Node), GetSrc<RA_32>(Op->Src1.ID()));
|
||||
break;
|
||||
case 8:
|
||||
mov(TMP1, GetSrc<RA_64>(Op->Header.Args[1].ID()));
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(TMP1, GetSrc<RA_64>(Op->Src2.ID()));
|
||||
mov(GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Src1.ID()));
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown CRC32 size: {}", IROp->Size);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(GetHostFlag) {
|
||||
auto Op = IROp->C<IR::IROp_GetHostFlag>();
|
||||
|
||||
mov(rax, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
mov(rax, GetSrc<RA_64>(Op->Value.ID()));
|
||||
shr(rax, Op->Flag);
|
||||
and_(rax, 1);
|
||||
mov(GetDst<RA_64>(Node), rax);
|
||||
|
||||
+35
-23
@@ -25,6 +25,7 @@ $end_info$
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/RegisterAllocationData.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -59,32 +60,42 @@ static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
void X86JITCore::PushRegs() {
|
||||
sub(rsp, 16 * RAXMM_x.size());
|
||||
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
sub(rsp, AVXRegSize * RAXMM_x.size());
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
movaps(ptr[rsp + i * 16], RAXMM_x[i]);
|
||||
vmovups(ptr[rsp + i * AVXRegSize], ToYMM(RAXMM_x[i]));
|
||||
}
|
||||
|
||||
for (auto &Reg : RA64)
|
||||
for (const auto &Reg : RA64) {
|
||||
push(Reg);
|
||||
}
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
if (NumPush & 1)
|
||||
sub(rsp, 8); // Align
|
||||
const auto NumPush = RA64.size();
|
||||
if ((NumPush & 1) != 0) {
|
||||
// Align
|
||||
sub(rsp, 8);
|
||||
}
|
||||
}
|
||||
|
||||
void X86JITCore::PopRegs() {
|
||||
auto NumPush = RA64.size();
|
||||
const auto AVXRegSize = Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto NumPush = RA64.size();
|
||||
|
||||
if (NumPush & 1)
|
||||
add(rsp, 8); // Align
|
||||
for (uint32_t i = RA64.size(); i > 0; --i)
|
||||
pop(RA64[i - 1]);
|
||||
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
movaps(RAXMM_x[i], ptr[rsp + i * 16]);
|
||||
if ((NumPush & 1) != 0) {
|
||||
// Align
|
||||
add(rsp, 8);
|
||||
}
|
||||
|
||||
add(rsp, 16 * RAXMM_x.size());
|
||||
for (uint32_t i = RA64.size(); i > 0; --i) {
|
||||
pop(RA64[i - 1]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < RAXMM_x.size(); ++i) {
|
||||
vmovups(ToYMM(RAXMM_x[i]), ptr[rsp + i * AVXRegSize]);
|
||||
}
|
||||
|
||||
add(rsp, AVXRegSize * RAXMM_x.size());
|
||||
}
|
||||
|
||||
void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
@@ -293,7 +304,8 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
case FABI_UNKNOWN:
|
||||
default:
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}", FEXCore::IR::GetName(IROp->Op), Info.ABI);
|
||||
LOGMAN_MSG_A_FMT("Unhandled IR Fallback ABI: {} {}",
|
||||
IR::GetName(IROp->Op), ToUnderlying(Info.ABI));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -312,7 +324,7 @@ static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame,
|
||||
}
|
||||
|
||||
auto LinkerAddress = Frame->Pointers.Common.ExitFunctionLinker;
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
// undo the link
|
||||
record[0] = LinkerAddress;
|
||||
});
|
||||
@@ -361,7 +373,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
|
||||
|
||||
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
|
||||
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
|
||||
Common.RemoveThreadCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::RemoveThreadCodeEntryFromJit);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
|
||||
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
|
||||
|
||||
{
|
||||
@@ -371,7 +383,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
|
||||
|
||||
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
|
||||
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&X86JITCore_ExitFunctionLink);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
|
||||
|
||||
// Fill in the fallback handlers
|
||||
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers);
|
||||
@@ -407,7 +419,7 @@ void X86JITCore::ClearCache() {
|
||||
IR::PhysicalRegister X86JITCore::GetPhys(IR::NodeID Node) const {
|
||||
auto PhyReg = RAData->GetNodeRegister(Node);
|
||||
|
||||
LOGMAN_THROW_A_FMT(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa{}. Class: {}", Node, PhyReg.Class);
|
||||
LOGMAN_THROW_AA_FMT(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa{}. Class: {}", Node, PhyReg.Class);
|
||||
|
||||
return PhyReg;
|
||||
}
|
||||
@@ -592,12 +604,12 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
setSize(getSize() + GDBSize);
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(RAData != nullptr, "Needs RA");
|
||||
LOGMAN_THROW_AA_FMT(RAData != nullptr, "Needs RA");
|
||||
|
||||
SpillSlots = RAData->SpillSlots();
|
||||
|
||||
if (SpillSlots) {
|
||||
sub(rsp, SpillSlots * 16);
|
||||
sub(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
#ifdef BLOCKSTATS
|
||||
@@ -649,7 +661,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
using namespace FEXCore::IR;
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
auto BlockIROp = BlockHeader->CW<IROp_CodeBlock>();
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
#endif
|
||||
|
||||
auto BlockStartHostCode = getCurr<uint8_t *>();
|
||||
|
||||
@@ -181,6 +181,10 @@ private:
|
||||
[[nodiscard]] Xbyak::Xmm GetSrc(IR::NodeID Node) const;
|
||||
[[nodiscard]] Xbyak::Xmm GetDst(IR::NodeID Node) const;
|
||||
|
||||
[[nodiscard]] static Xbyak::Ymm ToYMM(const Xbyak::Xmm& xmm) {
|
||||
return Xbyak::Ymm{xmm.getIdx()};
|
||||
}
|
||||
|
||||
[[nodiscard]] Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset,
|
||||
IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
|
||||
|
||||
@@ -205,7 +209,7 @@ private:
|
||||
* @brief Current guest RIP entrypoint
|
||||
*/
|
||||
uint8_t *GuestEntry{};
|
||||
|
||||
|
||||
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);
|
||||
@@ -312,7 +316,7 @@ private:
|
||||
DEF_OP(Syscall);
|
||||
DEF_OP(Thunk);
|
||||
DEF_OP(ValidateCode);
|
||||
DEF_OP(RemoveThreadCodeEntry);
|
||||
DEF_OP(ThreadRemoveCodeEntry);
|
||||
DEF_OP(CPUID);
|
||||
|
||||
///< Conversion ops
|
||||
@@ -362,12 +366,10 @@ private:
|
||||
///< Move ops
|
||||
DEF_OP(ExtractElementPair);
|
||||
DEF_OP(CreateElementPair);
|
||||
DEF_OP(Mov);
|
||||
|
||||
///< Vector ops
|
||||
DEF_OP(VectorZero);
|
||||
DEF_OP(VectorImm);
|
||||
DEF_OP(SplatVector);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
@@ -426,8 +428,6 @@ private:
|
||||
DEF_OP(VUShrS);
|
||||
DEF_OP(VSShrS);
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
@@ -437,7 +437,6 @@ private:
|
||||
DEF_OP(VShlI);
|
||||
DEF_OP(VUShrNI);
|
||||
DEF_OP(VUShrNI2);
|
||||
DEF_OP(VBitcast);
|
||||
DEF_OP(VSXTL);
|
||||
DEF_OP(VSXTL2);
|
||||
DEF_OP(VUXTL);
|
||||
|
||||
+36
-18
@@ -333,10 +333,10 @@ DEF_OP(StoreContextIndexed) {
|
||||
}
|
||||
|
||||
DEF_OP(SpillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const auto Op = IROp->C<IR::IROp_SpillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
@@ -355,36 +355,44 @@ DEF_OP(SpillRegister) {
|
||||
mov(qword [rsp + SlotOffset], GetSrc<RA_64>(Op->Value.ID()));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Src = GetSrc(Op->Value.ID());
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
movss(dword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movss(dword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
movsd(qword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movsd(qword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
movaps(xword [rsp + SlotOffset], GetSrc(Op->Value.ID()));
|
||||
movaps(xword [rsp + SlotOffset], Src);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
vmovaps(yword [rsp + SlotOffset], ToYMM(Src));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled SpillRegister class: {}", Op->Class.Val);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
DEF_OP(FillRegister) {
|
||||
auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
const auto Op = IROp->C<IR::IROp_FillRegister>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
const uint32_t SlotOffset = Op->Slot * MaxSpillSlotSize;
|
||||
|
||||
uint32_t SlotOffset = Op->Slot * 16;
|
||||
if (Op->Class == FEXCore::IR::GPRClass) {
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
@@ -403,23 +411,33 @@ DEF_OP(FillRegister) {
|
||||
mov(GetDst<RA_64>(Node), qword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else if (Op->Class == FEXCore::IR::FPRClass) {
|
||||
const auto Dst = GetDst(Node);
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
movss(GetDst(Node), dword [rsp + SlotOffset]);
|
||||
movss(Dst, dword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
movsd(GetDst(Node), qword [rsp + SlotOffset]);
|
||||
movsd(Dst, qword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
movaps(GetDst(Node), xword [rsp + SlotOffset]);
|
||||
movaps(Dst, xword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
case 32: {
|
||||
vmovaps(ToYMM(Dst), yword [rsp + SlotOffset]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister size: {}", OpSize);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unhandled FillRegister class: {}", Op->Class.Val);
|
||||
|
||||
+25
-51
@@ -45,56 +45,30 @@ DEF_OP(Fence) {
|
||||
|
||||
DEF_OP(Break) {
|
||||
auto Op = IROp->C<IR::IROp_Break>();
|
||||
switch (Op->Reason) {
|
||||
case FEXCore::IR::Break_Unimplemented: // Hard fault
|
||||
case FEXCore::IR::Break_Interrupt: // Guest ud2
|
||||
ud2();
|
||||
break;
|
||||
case FEXCore::IR::Break_Overflow: // overflow
|
||||
// Need to be outside of JIT cache space to ensure cache clearing correctness
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.OverflowExceptionHandler)]);
|
||||
break;
|
||||
case FEXCore::IR::Break_Halt: { // HLT
|
||||
// Time to quit
|
||||
// Set our stack to the starting stack location
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadStopHandlerSpillSRA)]);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::Break_Interrupt3: // INT3
|
||||
{
|
||||
if (CTX->GetGdbServerStatus()) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16);
|
||||
}
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize);
|
||||
}
|
||||
|
||||
// This jump target needs to be a constant offset here
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadPauseHandlerSpillSRA)]);
|
||||
}
|
||||
else {
|
||||
// If we don't have a gdb server attached then....crash?
|
||||
// Treat this case like HLT
|
||||
mov(rsp, qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, ReturningStackLocation)]);
|
||||
mov(byte [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.FaultToTopAndGeneratedException)], 1);
|
||||
mov(byte [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.Signal)], Op->Reason.Signal);
|
||||
mov(dword [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.TrapNo)], Op->Reason.TrapNumber);
|
||||
mov(dword [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.err_code)], Op->Reason.ErrorRegister);
|
||||
mov(dword [STATE + offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData.si_code)], Op->Reason.si_code);
|
||||
|
||||
// Now we need to jump to the thread stop handler
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.ThreadStopHandlerSpillSRA)]);
|
||||
}
|
||||
switch (Op->Reason.Signal) {
|
||||
case SIGILL:
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGILL)]);
|
||||
break;
|
||||
case SIGTRAP:
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)]);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGSEGV)]);
|
||||
break;
|
||||
default:
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP)]);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::Break_InvalidInstruction:
|
||||
{
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * 16);
|
||||
}
|
||||
|
||||
// Need to be outside of JIT cache space to ensure cache clearing correctness
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.UnimplementedInstructionHandler)]);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown Break reason: {}", Op->Reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +84,7 @@ DEF_OP(GetRoundingMode) {
|
||||
|
||||
DEF_OP(SetRoundingMode) {
|
||||
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
|
||||
auto Src = GetSrc<RA_32>(Op->Header.Args[0].ID());
|
||||
auto Src = GetSrc<RA_32>(Op->RoundMode.ID());
|
||||
|
||||
// Load old mxcsr
|
||||
// Only stores to memory
|
||||
@@ -135,13 +109,13 @@ DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
PushRegs();
|
||||
if (IsGPR(Op->Header.Args[0].ID())) {
|
||||
mov (rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
if (IsGPR(Op->Value.ID())) {
|
||||
mov (rdi, GetSrc<RA_64>(Op->Value.ID()));
|
||||
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintValue)]);
|
||||
}
|
||||
else {
|
||||
pextrq(rdi, GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
pextrq(rsi, GetSrc(Op->Header.Args[0].ID()), 1);
|
||||
pextrq(rdi, GetSrc(Op->Value.ID()), 0);
|
||||
pextrq(rsi, GetSrc(Op->Value.ID()), 1);
|
||||
|
||||
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.PrintVectorValue)]);
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ DEF_OP(ExtractElementPair) {
|
||||
auto Op = IROp->C<IR::IROp_ExtractElementPair>();
|
||||
switch (Op->Header.Size) {
|
||||
case 4: {
|
||||
auto Src = GetSrcPair<RA_32>(Op->Header.Args[0].ID());
|
||||
auto Src = GetSrcPair<RA_32>(Op->Pair.ID());
|
||||
std::array<Xbyak::Reg, 2> Regs = {Src.first, Src.second};
|
||||
mov (GetDst<RA_32>(Node), Regs[Op->Element]);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto Src = GetSrcPair<RA_64>(Op->Header.Args[0].ID());
|
||||
auto Src = GetSrcPair<RA_64>(Op->Pair.ID());
|
||||
std::array<Xbyak::Reg, 2> Regs = {Src.first, Src.second};
|
||||
mov (GetDst<RA_64>(Node), Regs[Op->Element]);
|
||||
break;
|
||||
@@ -45,15 +45,15 @@ DEF_OP(CreateElementPair) {
|
||||
switch (IROp->ElementSize) {
|
||||
case 4: {
|
||||
Dst = GetSrcPair<RA_32>(Node);
|
||||
RegFirst = GetSrc<RA_32>(Op->Header.Args[0].ID());
|
||||
RegSecond = GetSrc<RA_32>(Op->Header.Args[1].ID());
|
||||
RegFirst = GetSrc<RA_32>(Op->Lower.ID());
|
||||
RegSecond = GetSrc<RA_32>(Op->Upper.ID());
|
||||
RegTmp = eax;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
Dst = GetSrcPair<RA_64>(Node);
|
||||
RegFirst = GetSrc<RA_64>(Op->Header.Args[0].ID());
|
||||
RegSecond = GetSrc<RA_64>(Op->Header.Args[1].ID());
|
||||
RegFirst = GetSrc<RA_64>(Op->Lower.ID());
|
||||
RegSecond = GetSrc<RA_64>(Op->Upper.ID());
|
||||
RegTmp = rax;
|
||||
break;
|
||||
}
|
||||
@@ -73,17 +73,11 @@ DEF_OP(CreateElementPair) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Mov) {
|
||||
auto Op = IROp->C<IR::IROp_Mov>();
|
||||
mov (GetDst<RA_64>(Node), GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
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);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+1256
-755
File diff suppressed because it is too large.
Load diff
@@ -91,7 +91,7 @@ bool X86JITCore::ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uint6
|
||||
size_t DataIndex{};
|
||||
for (size_t j = 0; j < NumRelocations; ++j) {
|
||||
const FEXCore::CPU::Relocation *Reloc = reinterpret_cast<const FEXCore::CPU::Relocation *>(&EntryRelocations[DataIndex]);
|
||||
LOGMAN_THROW_A_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to");
|
||||
LOGMAN_THROW_AA_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to");
|
||||
|
||||
switch (Reloc->Header.Type) {
|
||||
case FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL: {
|
||||
|
||||
+2
-2
@@ -37,11 +37,11 @@ LookupCache::LookupCache(FEXCore::Context::Context *CTX)
|
||||
// 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>(FEXCore::Allocator::mmap(nullptr, CODE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_A_FMT(PageMemory != -1ULL, "Failed to allocate page memory");
|
||||
LOGMAN_THROW_AA_FMT(PageMemory != -1ULL, "Failed to allocate page memory");
|
||||
|
||||
// L1 Cache
|
||||
L1Pointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::mmap(nullptr, L1_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_A_FMT(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
|
||||
LOGMAN_THROW_AA_FMT(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
|
||||
|
||||
VirtualMemSize = ctx->Config.VirtualMemSize;
|
||||
}
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public:
|
||||
std::lock_guard<std::recursive_mutex> lk(WriteLock);
|
||||
|
||||
[[maybe_unused]] auto Inserted = BlockList.emplace(Address, (uintptr_t)HostCode).second;
|
||||
LOGMAN_THROW_A_FMT(Inserted, "Duplicate block mapping added");
|
||||
LOGMAN_THROW_AA_FMT(Inserted, "Duplicate block mapping added");
|
||||
|
||||
// There is no need to update L1 or L2, they will get updated on first lookup
|
||||
// However, adding to L1 here increases performance
|
||||
|
||||
+108
-41
@@ -17,6 +17,7 @@ $end_info$
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -125,10 +126,20 @@ void OpDispatchBuilder::ThunkOp(OpcodeArgs) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
uint8_t *sha256 = (uint8_t *)(Op->PC + 2);
|
||||
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
// x86-64 ABI puts the function argument in RDI
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RDI)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// x86 fastcall ABI puts the function argument in ECX
|
||||
_Thunk(
|
||||
_LoadContext(GPRSize, GPRClass, GPROffset(X86State::REG_RCX)),
|
||||
*reinterpret_cast<SHA256Sum*>(sha256)
|
||||
);
|
||||
}
|
||||
|
||||
auto Constant = _Constant(GPRSize);
|
||||
auto OldSP = _LoadContext(GPRSize, GPRClass, RSPOffset);
|
||||
@@ -350,7 +361,7 @@ void OpDispatchBuilder::SecondaryALUOp(OpcodeArgs) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", IROp);
|
||||
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", ToUnderlying(IROp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1589,7 +1600,12 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
case 2: // CS
|
||||
case FEXCore::X86State::REG_R9: // CS
|
||||
// CPL3 can't write to this
|
||||
_Break(FEXCore::IR::Break_InvalidInstruction, 0);
|
||||
_Break(FEXCore::IR::BreakDefinition {
|
||||
.ErrorRegister = 0,
|
||||
.Signal = SIGILL,
|
||||
.TrapNumber = 0,
|
||||
.si_code = 0,
|
||||
});
|
||||
break;
|
||||
case 3: // SS
|
||||
case FEXCore::X86State::REG_R10: // SS
|
||||
@@ -4503,7 +4519,7 @@ void OpDispatchBuilder::Finalize() {
|
||||
|
||||
[[maybe_unused]] const FEXCore::IR::IROp_Header *IROp =
|
||||
RealNode->Op(DualListData.DataBegin());
|
||||
LOGMAN_THROW_A_FMT(IROp->Op == OP_IRHEADER, "First op in function must be our header");
|
||||
LOGMAN_THROW_AA_FMT(IROp->Op == OP_IRHEADER, "First op in function must be our header");
|
||||
|
||||
// Let's walk the jump blocks and see if we have handled every block target
|
||||
for (auto &Handler : JumpTargets) {
|
||||
@@ -4529,7 +4545,7 @@ uint8_t OpDispatchBuilder::GetDstSize(X86Tables::DecodedOp Op) const {
|
||||
|
||||
const uint32_t DstSizeFlag = X86Tables::DecodeFlags::GetSizeDstFlags(Op->Flags);
|
||||
const uint8_t Size = Sizes[DstSizeFlag];
|
||||
LOGMAN_THROW_A_FMT(Size != 0, "Invalid destination size for op");
|
||||
LOGMAN_THROW_AA_FMT(Size != 0, "Invalid destination size for op");
|
||||
return Size;
|
||||
}
|
||||
|
||||
@@ -4547,7 +4563,7 @@ uint8_t OpDispatchBuilder::GetSrcSize(X86Tables::DecodedOp Op) const {
|
||||
|
||||
const uint32_t SrcSizeFlag = X86Tables::DecodeFlags::GetSizeSrcFlags(Op->Flags);
|
||||
const uint8_t Size = Sizes[SrcSizeFlag];
|
||||
LOGMAN_THROW_A_FMT(Size != 0, "Invalid destination size for op");
|
||||
LOGMAN_THROW_AA_FMT(Size != 0, "Invalid destination size for op");
|
||||
return Size;
|
||||
}
|
||||
|
||||
@@ -4641,7 +4657,14 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
Src = _LoadContext(OpSize, FPRClass, offsetof(FEXCore::Core::CPUState, mm[gpr - FEXCore::X86State::REG_MM_0]));
|
||||
}
|
||||
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
|
||||
Src = _LoadContext(OpSize, FPRClass, offsetof(FEXCore::Core::CPUState, xmm[gpr - FEXCore::X86State::REG_XMM_0][Operand.Data.GPR.HighBits ? 1 : 0]));
|
||||
const auto gprIndex = gpr - X86State::REG_XMM_0;
|
||||
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
|
||||
|
||||
if (CTX->HostFeatures.SupportsAVX) {
|
||||
Src = _LoadContext(OpSize, FPRClass, offsetof(Core::CPUState, xmm.avx.data[gprIndex][highIndex]));
|
||||
} else {
|
||||
Src = _LoadContext(OpSize, FPRClass, offsetof(Core::CPUState, xmm.sse.data[gprIndex][highIndex]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Src = _LoadContext(OpSize, GPRClass, offsetof(FEXCore::Core::CPUState, gregs[gpr]) + (Operand.Data.GPR.HighBits ? 1 : 0));
|
||||
@@ -4783,7 +4806,14 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
_StoreContext(OpSize, Class, Src, offsetof(FEXCore::Core::CPUState, mm[gpr - FEXCore::X86State::REG_MM_0]));
|
||||
}
|
||||
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
|
||||
_StoreContext(OpSize, Class, Src, offsetof(FEXCore::Core::CPUState, xmm[gpr - FEXCore::X86State::REG_XMM_0][Operand.Data.GPR.HighBits ? 1 : 0]));
|
||||
const auto gprIndex = gpr - X86State::REG_XMM_0;
|
||||
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
|
||||
|
||||
if (CTX->HostFeatures.SupportsAVX) {
|
||||
_StoreContext(OpSize, Class, Src, offsetof(Core::CPUState, xmm.avx.data[gprIndex][highIndex]));
|
||||
} else {
|
||||
_StoreContext(OpSize, Class, Src, offsetof(Core::CPUState, xmm.sse.data[gprIndex][highIndex]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (GPRSize == 8 && OpSize == 4) {
|
||||
@@ -4791,11 +4821,11 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
// For all other sizes, the upper bits are guaranteed to already be zero
|
||||
OrderedNode *Value = GetOpSize(Src) == 8 ? _Bfe(4, 32, 0, Src) : Src;
|
||||
|
||||
LOGMAN_THROW_A_FMT(!Operand.Data.GPR.HighBits, "Can't handle 32bit store to high 8bit register");
|
||||
LOGMAN_THROW_AA_FMT(!Operand.Data.GPR.HighBits, "Can't handle 32bit store to high 8bit register");
|
||||
_StoreContext(GPRSize, Class, Value, offsetof(FEXCore::Core::CPUState, gregs[gpr]));
|
||||
}
|
||||
else {
|
||||
LOGMAN_THROW_A_FMT(!(GPRSize == 4 && OpSize > 4), "Oops had a {} GPR load", OpSize);
|
||||
LOGMAN_THROW_AA_FMT(!(GPRSize == 4 && OpSize > 4), "Oops had a {} GPR load", OpSize);
|
||||
_StoreContext(std::min(GPRSize, OpSize), Class, Src, offsetof(FEXCore::Core::CPUState, gregs[gpr]) + (Operand.Data.GPR.HighBits ? 1 : 0));
|
||||
}
|
||||
}
|
||||
@@ -5029,7 +5059,7 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", IROp);
|
||||
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", ToUnderlying(IROp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5069,37 +5099,58 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
FEXCore::IR::BreakReason Reason{};
|
||||
uint8_t Literal{};
|
||||
bool setRIP = false;
|
||||
IR::BreakDefinition Reason;
|
||||
bool SetRIPToNext = false;
|
||||
|
||||
switch (Op->OP) {
|
||||
case 0xCD:
|
||||
Reason = FEXCore::IR::Break_Interrupt;
|
||||
Literal = Op->Src[0].Data.Literal.Value;
|
||||
case 0xCD: { // INT imm8
|
||||
uint8_t Literal = Op->Src[0].Data.Literal.Value;
|
||||
|
||||
if (Literal == 0x80) {
|
||||
// Syscall on linux
|
||||
SyscallOp(Op);
|
||||
return;
|
||||
}
|
||||
|
||||
Reason.ErrorRegister = Literal << 3 | (0b010);
|
||||
Reason.Signal = SIGSEGV;
|
||||
// GP is raised when task-gate isn't setup to be valid
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_GP;
|
||||
Reason.si_code = 0x80;
|
||||
break;
|
||||
}
|
||||
case 0xCE: // INTO
|
||||
Reason.ErrorRegister = 0;
|
||||
Reason.Signal = SIGSEGV;
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_OF;
|
||||
Reason.si_code = 0x80;
|
||||
break;
|
||||
case 0xF1: // INT1
|
||||
Reason.ErrorRegister = 0;
|
||||
Reason.Signal = SIGTRAP;
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_DB;
|
||||
Reason.si_code = 1;
|
||||
SetRIPToNext = true;
|
||||
break;
|
||||
case 0xCE:
|
||||
Reason = FEXCore::IR::Break_Overflow;
|
||||
break;
|
||||
case 0xF1:
|
||||
Reason = FEXCore::IR::Break_Interrupt;
|
||||
break;
|
||||
case 0xF4: {
|
||||
Reason = FEXCore::IR::Break_Halt;
|
||||
setRIP = true;
|
||||
case 0xF4: { // HLT
|
||||
Reason.ErrorRegister = 0;
|
||||
Reason.Signal = SIGSEGV;
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_GP;
|
||||
Reason.si_code = 0x80;
|
||||
break;
|
||||
}
|
||||
case 0x0B:
|
||||
Reason = FEXCore::IR::Break_Interrupt;
|
||||
case 0x0B: // UD2
|
||||
Reason.ErrorRegister = 0;
|
||||
Reason.Signal = SIGILL;
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_UD;
|
||||
Reason.si_code = 2;
|
||||
break;
|
||||
case 0xCC:
|
||||
Reason = FEXCore::IR::Break_Interrupt3;
|
||||
setRIP = true;
|
||||
case 0xCC: // INT3
|
||||
Reason.ErrorRegister = 0;
|
||||
Reason.Signal = SIGTRAP;
|
||||
Reason.TrapNumber = X86State::X86_TRAPNO_BP;
|
||||
Reason.si_code = 0x80;
|
||||
SetRIPToNext = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5108,13 +5159,17 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
|
||||
if (setRIP) {
|
||||
BlockSetRIP = setRIP;
|
||||
if (SetRIPToNext) {
|
||||
BlockSetRIP = SetRIPToNext;
|
||||
|
||||
// We want to set RIP to the next instruction after HLT/INT3
|
||||
// We want to set RIP to the next instruction after INT3/INT1
|
||||
auto NewRIP = GetRelocatedPC(Op);
|
||||
_StoreContext(GPRSize, GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
|
||||
}
|
||||
else if (Op->OP != 0xCE) {
|
||||
auto NewRIP = GetRelocatedPC(Op, -Op->InstSize);
|
||||
_StoreContext(GPRSize, GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
|
||||
}
|
||||
|
||||
if (Op->OP == 0xCE) { // Conditional to only break if Overflow == 1
|
||||
auto Flag = GetRFLAG(FEXCore::X86State::RFLAG_OF_LOC);
|
||||
@@ -5127,7 +5182,7 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
|
||||
auto NewRIP = GetRelocatedPC(Op);
|
||||
_StoreContext(GPRSize, GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
|
||||
_Break(Reason, Literal);
|
||||
_Break(Reason);
|
||||
|
||||
// Make sure to start a new block after ending this one
|
||||
auto JumpTarget = CreateNewCodeBlockAfter(FalseBlock);
|
||||
@@ -5135,7 +5190,8 @@ void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
SetCurrentCodeBlock(JumpTarget);
|
||||
}
|
||||
else {
|
||||
_Break(Reason, Literal);
|
||||
BlockSetRIP = true;
|
||||
_Break(Reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5238,7 +5294,13 @@ void OpDispatchBuilder::UnimplementedOp(OpcodeArgs) {
|
||||
// We don't actually support this instruction
|
||||
// Multiblock may hit it though
|
||||
_StoreContext(GPRSize, GPRClass, GetRelocatedPC(Op, -Op->InstSize), offsetof(FEXCore::Core::CPUState, rip));
|
||||
_Break(FEXCore::IR::Break_Unimplemented, 0);
|
||||
_Break(FEXCore::IR::BreakDefinition {
|
||||
.ErrorRegister = 0,
|
||||
.Signal = SIGILL,
|
||||
.TrapNumber = 0,
|
||||
.si_code = 0,
|
||||
});
|
||||
|
||||
BlockSetRIP = true;
|
||||
|
||||
if (Multiblock) {
|
||||
@@ -5256,7 +5318,12 @@ void OpDispatchBuilder::InvalidOp(OpcodeArgs) {
|
||||
// We don't actually support this instruction
|
||||
// Multiblock may hit it though
|
||||
_StoreContext(GPRSize, GPRClass, GetRelocatedPC(Op, -Op->InstSize), offsetof(FEXCore::Core::CPUState, rip));
|
||||
_Break(FEXCore::IR::Break_InvalidInstruction, 0);
|
||||
_Break(FEXCore::IR::BreakDefinition {
|
||||
.ErrorRegister = 0,
|
||||
.Signal = SIGILL,
|
||||
.TrapNumber = 0,
|
||||
.si_code = 0,
|
||||
});
|
||||
BlockSetRIP = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -665,7 +665,7 @@ private:
|
||||
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, OrderedNode *const Src, int8_t Align, MemoryAccessType AccessType = MemoryAccessType::ACCESS_DEFAULT);
|
||||
|
||||
[[nodiscard]] static uint32_t GPROffset(X86State::X86Reg reg) {
|
||||
LOGMAN_THROW_A_FMT(reg <= X86State::X86Reg::REG_R15, "Invalid reg used");
|
||||
LOGMAN_THROW_AA_FMT(reg <= X86State::X86Reg::REG_R15, "Invalid reg used");
|
||||
return static_cast<uint32_t>(offsetof(Core::CPUState, gregs[static_cast<size_t>(reg)]));
|
||||
}
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ void OpDispatchBuilder::SHA256RNDS2Op(OpcodeArgs) {
|
||||
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *XMM0 = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, xmm[0]));
|
||||
OrderedNode *XMM0 = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, xmm.avx.data[0]));
|
||||
|
||||
auto A0 = _VExtractToGPR(16, 4, Src, 3);
|
||||
auto B0 = _VExtractToGPR(16, 4, Src, 2);
|
||||
|
||||
@@ -74,13 +74,12 @@ void OpDispatchBuilder::MOVLPOp(OpcodeArgs) {
|
||||
// xmm, xmm is movhlps special case
|
||||
if (Op->Src[0].IsGPR()) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
|
||||
Src = _VExtractElement(16, 8, Src, 1);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 1, Dest, Src);
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 16, 16);
|
||||
}
|
||||
else {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, 8, 16);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, Result, 8, 16);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +111,7 @@ void OpDispatchBuilder::MOVSSOp(OpcodeArgs) {
|
||||
// MOVSS xmm1, xmm2
|
||||
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], 4, Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(16, 4, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 4, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else if (Op->Dest.IsGPR()) {
|
||||
@@ -133,7 +132,7 @@ void OpDispatchBuilder::MOVSDOp(OpcodeArgs) {
|
||||
// xmm1[63:0] <- xmm2[63:0]
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(16, 8, 0, Dest, Src);
|
||||
auto Result = _VInsElement(16, 8, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else if (Op->Dest.IsGPR()) {
|
||||
@@ -335,7 +334,7 @@ void OpDispatchBuilder::VectorScalarALUOp(OpcodeArgs) {
|
||||
|
||||
if (Size != ElementSize) {
|
||||
// Insert the lower bits
|
||||
Result = _VInsScalarElement(Size, ElementSize, 0, Dest, Result);
|
||||
Result = _VInsElement(Size, ElementSize, 0, 0, Dest, Result);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -381,7 +380,7 @@ void OpDispatchBuilder::VectorUnaryOp(OpcodeArgs) {
|
||||
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
auto Result = _VInsScalarElement(GetSrcSize(Op), ElementSize, 0, Dest, ALUOp);
|
||||
auto Result = _VInsElement(GetSrcSize(Op), ElementSize, 0, 0, Dest, ALUOp);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else {
|
||||
@@ -440,10 +439,16 @@ void OpDispatchBuilder::MOVQOp(OpcodeArgs) {
|
||||
// This instruction is a bit special that if the destination is a register then it'll ZEXT the 64bit source to 128bit
|
||||
if (Op->Dest.IsGPR()) {
|
||||
const auto gpr = Op->Dest.Data.GPR.GPR;
|
||||
const auto gprIndex = gpr - X86State::REG_XMM_0;
|
||||
|
||||
_StoreContext(8, FPRClass, Src, offsetof(FEXCore::Core::CPUState, xmm[gpr - FEXCore::X86State::REG_XMM_0][0]));
|
||||
const auto fprLowOffset = CTX->HostFeatures.SupportsAVX ? offsetof(Core::CPUState, xmm.avx.data[gprIndex][0])
|
||||
: offsetof(Core::CPUState, xmm.sse.data[gprIndex][0]);
|
||||
const auto fprHighOffset = CTX->HostFeatures.SupportsAVX ? offsetof(Core::CPUState, xmm.avx.data[gprIndex][1])
|
||||
: offsetof(Core::CPUState, xmm.sse.data[gprIndex][1]);
|
||||
|
||||
_StoreContext(8, FPRClass, Src, fprLowOffset);
|
||||
auto Const = _Constant(0);
|
||||
_StoreContext(8, GPRClass, Const, offsetof(FEXCore::Core::CPUState, xmm[gpr - FEXCore::X86State::REG_XMM_0][1]));
|
||||
_StoreContext(8, GPRClass, Const, fprHighOffset);
|
||||
}
|
||||
else {
|
||||
// This is simple, just store the result
|
||||
@@ -562,7 +567,7 @@ void OpDispatchBuilder::PSHUFBOp(OpcodeArgs) {
|
||||
|
||||
template<size_t ElementSize, bool HalfSize, bool Low>
|
||||
void OpDispatchBuilder::PSHUFDOp(OpcodeArgs) {
|
||||
LOGMAN_THROW_A_FMT(ElementSize != 0, "What. No element size?");
|
||||
LOGMAN_THROW_AA_FMT(ElementSize != 0, "What. No element size?");
|
||||
const auto Size = GetSrcSize(Op);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
uint8_t Shuffle = Op->Src[1].Data.Literal.Value;
|
||||
@@ -599,7 +604,7 @@ void OpDispatchBuilder::PSHUFDOp<4, false, true>(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize>
|
||||
void OpDispatchBuilder::SHUFOp(OpcodeArgs) {
|
||||
LOGMAN_THROW_A_FMT(ElementSize != 0, "What. No element size?");
|
||||
LOGMAN_THROW_AA_FMT(ElementSize != 0, "What. No element size?");
|
||||
const auto Size = GetSrcSize(Op);
|
||||
OrderedNode *Src1 = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
@@ -972,7 +977,8 @@ void OpDispatchBuilder::PAVGOp<2>(OpcodeArgs);
|
||||
|
||||
void OpDispatchBuilder::MOVDDUPOp(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Res = _SplatVector2(Src);
|
||||
OrderedNode *Res = _VDupElement(16, GetSrcSize(Op), Src, 0);
|
||||
|
||||
StoreResult(FPRClass, Op, Res, -1);
|
||||
}
|
||||
|
||||
@@ -986,7 +992,7 @@ void OpDispatchBuilder::CVTGPR_To_FPR(OpcodeArgs) {
|
||||
|
||||
OrderedNode *Dest = LoadSource_WithOpSize(FPRClass, Op, Op->Dest, 16, Op->Flags, -1);
|
||||
|
||||
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
|
||||
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
|
||||
|
||||
StoreResult(FPRClass, Op, Src, -1);
|
||||
}
|
||||
@@ -1085,7 +1091,7 @@ void OpDispatchBuilder::Scalar_CVT_Float_To_Float(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
|
||||
Src = _Float_FToF(DstElementSize, SrcElementSize, Src);
|
||||
Src = _VInsScalarElement(16, DstElementSize, 0, Dest, Src);
|
||||
Src = _VInsElement(16, DstElementSize, 0, 0, Dest, Src);
|
||||
|
||||
StoreResult(FPRClass, Op, Src, -1);
|
||||
}
|
||||
@@ -1275,7 +1281,7 @@ void OpDispatchBuilder::VFCMPOp(OpcodeArgs) {
|
||||
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Result);
|
||||
Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Result);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
@@ -1390,10 +1396,18 @@ void OpDispatchBuilder::FXSaveOp(OpcodeArgs) {
|
||||
|
||||
_StoreMem(FPRClass, 16, MemLocation, MMReg, 16);
|
||||
}
|
||||
unsigned NumRegs = CTX->Config.Is64BitMode ? 16 : 8;
|
||||
|
||||
const auto NumRegs = CTX->Config.Is64BitMode ? 16U : 8U;
|
||||
const auto GetXMMOffset = [this](size_t i) {
|
||||
if (CTX->HostFeatures.SupportsAVX) {
|
||||
return offsetof(Core::CPUState, xmm.avx.data[i]);
|
||||
} else {
|
||||
return offsetof(Core::CPUState, xmm.sse.data[i]);
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < NumRegs; ++i) {
|
||||
OrderedNode *XMMReg = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, xmm[i]));
|
||||
OrderedNode *XMMReg = _LoadContext(16, FPRClass, GetXMMOffset(i));
|
||||
OrderedNode *MemLocation = _Add(Mem, _Constant(i * 16 + 160));
|
||||
|
||||
_StoreMem(FPRClass, 16, MemLocation, XMMReg, 16);
|
||||
@@ -1439,12 +1453,20 @@ void OpDispatchBuilder::FXRStoreOp(OpcodeArgs) {
|
||||
auto MMReg = _LoadMem(FPRClass, 16, MemLocation, 16);
|
||||
_StoreContext(16, FPRClass, MMReg, offsetof(FEXCore::Core::CPUState, mm[i]));
|
||||
}
|
||||
unsigned NumRegs = CTX->Config.Is64BitMode ? 16 : 8;
|
||||
|
||||
const auto NumRegs = CTX->Config.Is64BitMode ? 16U : 8U;
|
||||
const auto GetXMMOffset = [this](size_t i) {
|
||||
if (CTX->HostFeatures.SupportsAVX) {
|
||||
return offsetof(Core::CPUState, xmm.avx.data[i]);
|
||||
} else {
|
||||
return offsetof(Core::CPUState, xmm.sse.data[i]);
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < NumRegs; ++i) {
|
||||
OrderedNode *MemLocation = _Add(Mem, _Constant(i * 16 + 160));
|
||||
auto XMMReg = _LoadMem(FPRClass, 16, MemLocation, 16);
|
||||
_StoreContext(16, FPRClass, XMMReg, offsetof(FEXCore::Core::CPUState, xmm[i]));
|
||||
_StoreContext(16, FPRClass, XMMReg, GetXMMOffset(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,8 +1612,12 @@ void OpDispatchBuilder::MOVQ2DQ(OpcodeArgs) {
|
||||
|
||||
// This instruction is a bit special in that if the source is MMX then it zexts to 128bit
|
||||
if constexpr (ToXMM) {
|
||||
const auto Index = Op->Dest.Data.GPR.GPR - FEXCore::X86State::REG_XMM_0;
|
||||
const auto Offset = CTX->HostFeatures.SupportsAVX ? offsetof(FEXCore::Core::CPUState, xmm.avx.data[Index][0])
|
||||
: offsetof(FEXCore::Core::CPUState, xmm.sse.data[Index][0]);
|
||||
|
||||
Src = _VMov(16, Src);
|
||||
_StoreContext(16, FPRClass, Src, offsetof(FEXCore::Core::CPUState, xmm[Op->Dest.Data.GPR.GPR - FEXCore::X86State::REG_XMM_0][0]));
|
||||
_StoreContext(16, FPRClass, Src, Offset);
|
||||
}
|
||||
else {
|
||||
// This is simple, just store the result
|
||||
@@ -1688,8 +1714,8 @@ void OpDispatchBuilder::PFNACCOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *ResSubSrc{};
|
||||
OrderedNode *ResSubDest{};
|
||||
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
|
||||
auto UpperSubSrc = _VExtractElement(Size, 4, Src, 1);
|
||||
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
|
||||
auto UpperSubSrc = _VDupElement(Size, 4, Src, 1);
|
||||
|
||||
ResSubDest = _VFSub(4, 4, Dest, UpperSubDest);
|
||||
ResSubSrc = _VFSub(4, 4, Src, UpperSubSrc);
|
||||
@@ -1707,7 +1733,7 @@ void OpDispatchBuilder::PFPNACCOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *ResAdd{};
|
||||
OrderedNode *ResSub{};
|
||||
auto UpperSubDest = _VExtractElement(Size, 4, Dest, 1);
|
||||
auto UpperSubDest = _VDupElement(Size, 4, Dest, 1);
|
||||
|
||||
ResSub = _VFSub(4, 4, Dest, UpperSubDest);
|
||||
ResAdd = _VFAddP(Size, 4, Src, Src);
|
||||
@@ -1840,8 +1866,6 @@ void OpDispatchBuilder::PMADDWD(OpcodeArgs) {
|
||||
|
||||
if (Size == 8) {
|
||||
Size <<= 1;
|
||||
Src1 = _VBitcast(Size, 2, Src1);
|
||||
Src2 = _VBitcast(Size, 2, Src2);
|
||||
}
|
||||
|
||||
auto Src1_L = _VSXTL(Size, 2, Src1); // [15:0 ], [31:16], [32:47 ], [63:48 ]
|
||||
@@ -1928,9 +1952,6 @@ void OpDispatchBuilder::PMULHW(OpcodeArgs) {
|
||||
|
||||
OrderedNode *Res{};
|
||||
if (Size == 8) {
|
||||
Dest = _VBitcast(Size * 2, 2, Dest);
|
||||
Src = _VBitcast(Size * 2, 2, Src);
|
||||
|
||||
// Implementation is more efficient for 8byte registers
|
||||
if (Signed)
|
||||
Res = _VSMull(Size * 2, 2, Dest, Src);
|
||||
@@ -2307,7 +2328,7 @@ void OpDispatchBuilder::VectorRound(OpcodeArgs) {
|
||||
if constexpr (Scalar) {
|
||||
// Insert the lower bits
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
auto Result = _VInsScalarElement(GetDstSize(Op), ElementSize, 0, Dest, Src);
|
||||
auto Result = _VInsElement(GetDstSize(Op), ElementSize, 0, 0, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
else {
|
||||
@@ -2356,7 +2377,7 @@ void OpDispatchBuilder::VectorVariableBlend(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
|
||||
// The mask is hardcoded to be xmm0 in this instruction
|
||||
OrderedNode *Mask = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, xmm[0]));
|
||||
OrderedNode *Mask = _LoadContext(16, FPRClass, offsetof(FEXCore::Core::CPUState, xmm.avx.data[0]));
|
||||
// Each element is selected by the high bit of that element size
|
||||
// Dest[ElementIdx] = Xmm0[ElementIndex][HighBit] ? Src : Dest;
|
||||
//
|
||||
|
||||
@@ -1185,7 +1185,7 @@ void OpDispatchBuilder::X87FNSAVE(OpcodeArgs) {
|
||||
// upper 16 bits [79:64]
|
||||
_StoreMem(FPRClass, 8, ST0Location, data, 1);
|
||||
ST0Location = _Add(ST0Location, _Constant(8));
|
||||
auto topBytes = _VExtractElement(16, 2, data, 4);
|
||||
auto topBytes = _VDupElement(16, 2, data, 4);
|
||||
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
|
||||
|
||||
// reset to default
|
||||
|
||||
@@ -996,7 +996,7 @@ void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
|
||||
// upper 16 bits [79:64]
|
||||
_StoreMem(FPRClass, 8, ST0Location, data, 1);
|
||||
ST0Location = _Add(ST0Location, _Constant(8));
|
||||
auto topBytes = _VExtractElement(16, 2, data, 4);
|
||||
auto topBytes = _VDupElement(16, 2, data, 4);
|
||||
_StoreMem(FPRClass, 2, ST0Location, topBytes, 1);
|
||||
|
||||
// reset to default
|
||||
|
||||
@@ -33,7 +33,7 @@ static inline void GenerateTable(X86InstInfo *FinalTable, X86TablesInfoStruct<Op
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LOGMAN_THROW_A_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_AA_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
FinalTable[OpNum + i] = Info;
|
||||
#ifndef NDEBUG
|
||||
++Total;
|
||||
@@ -51,7 +51,7 @@ static inline void GenerateTableWithCopy(X86InstInfo *FinalTable, X86TablesInfoS
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LOGMAN_THROW_A_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_AA_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
if (Info.Type == TYPE_COPY_OTHER) {
|
||||
FinalTable[OpNum + i] = OtherLocal[OpNum + i];
|
||||
}
|
||||
@@ -74,7 +74,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, X86TablesInfoStruct
|
||||
auto OpNum = Op.first;
|
||||
X86InstInfo const &Info = Op.Info;
|
||||
for (uint32_t i = 0; i < Op.second; ++i) {
|
||||
LOGMAN_THROW_A_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", FinalTable[OpNum + i].Name, Info.Name);
|
||||
LOGMAN_THROW_AA_FMT(FinalTable[OpNum + i].Type == TYPE_UNKNOWN, "Duplicate Entry {}->{}", 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;
|
||||
@@ -82,7 +82,7 @@ static inline void GenerateX87Table(X86InstInfo *FinalTable, X86TablesInfoStruct
|
||||
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_FMT((OpNum & 0b11'000'000) == 0, "Only support mod field of zero in this path");
|
||||
LOGMAN_THROW_AA_FMT((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;
|
||||
|
||||
+169
-103
@@ -28,6 +28,10 @@ $end_info$
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#endif
|
||||
|
||||
struct LoadlibArgs {
|
||||
const char *Name;
|
||||
};
|
||||
@@ -66,12 +70,23 @@ namespace FEXCore {
|
||||
struct ExportEntry { uint8_t *sha256; ThunkedFunction* Fn; };
|
||||
|
||||
struct TrampolineInstanceInfo {
|
||||
uintptr_t HostPacker;
|
||||
void* HostPacker;
|
||||
uintptr_t CallCallback;
|
||||
uintptr_t GuestUnpacker;
|
||||
uintptr_t GuestTarget;
|
||||
};
|
||||
|
||||
// Opaque type pointing to an instance of HostToGuestTrampolineTemplate and its
|
||||
// embedded TrampolineInstanceInfo
|
||||
struct HostToGuestTrampolinePtr;
|
||||
const auto HostToGuestTrampolineSize = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate;
|
||||
|
||||
static TrampolineInstanceInfo& GetInstanceInfo(HostToGuestTrampolinePtr* Trampoline) {
|
||||
const auto Length = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate;
|
||||
const auto InstanceInfoOffset = Length - sizeof(TrampolineInstanceInfo);
|
||||
return *reinterpret_cast<TrampolineInstanceInfo*>(reinterpret_cast<char*>(Trampoline) + InstanceInfoOffset);
|
||||
}
|
||||
|
||||
struct GuestcallInfo {
|
||||
uintptr_t GuestUnpacker;
|
||||
uintptr_t GuestTarget;
|
||||
@@ -94,7 +109,9 @@ namespace FEXCore {
|
||||
}
|
||||
};
|
||||
|
||||
class ThunkHandler_impl final: public ThunkHandler {
|
||||
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker);
|
||||
|
||||
struct ThunkHandler_impl final: public ThunkHandler {
|
||||
std::shared_mutex ThunksMutex;
|
||||
|
||||
std::unordered_map<IR::SHA256Sum, ThunkedFunction*, TruncatingSHA256Hash> Thunks = {
|
||||
@@ -108,23 +125,28 @@ namespace FEXCore {
|
||||
{ 0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff },
|
||||
&IsLibLoaded
|
||||
},
|
||||
{
|
||||
// sha256(fex:is_host_heap_allocation)
|
||||
{ 0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e },
|
||||
&IsHostHeapAllocation
|
||||
},
|
||||
{
|
||||
// sha256(fex:link_address_to_function)
|
||||
{ 0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77 },
|
||||
&LinkAddressToGuestFunction
|
||||
},
|
||||
{
|
||||
// sha256(fex:make_host_trampoline_for_guest_function)
|
||||
{ 0x1e, 0x51, 0x6b, 0x07, 0x39, 0xeb, 0x50, 0x59, 0xb3, 0xf3, 0x4f, 0xca, 0xdd, 0x58, 0x37, 0xe9, 0xf0, 0x30, 0xe5, 0x89, 0x81, 0xc7, 0x14, 0xfb, 0x24, 0xf9, 0xba, 0xe7, 0x0e, 0x00, 0x1e, 0x86 },
|
||||
&MakeHostTrampolineForGuestFunction
|
||||
}
|
||||
// sha256(fex:allocate_host_trampoline_for_guest_function)
|
||||
{ 0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a },
|
||||
&AllocateHostTrampolineForGuestFunction
|
||||
},
|
||||
};
|
||||
|
||||
// Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away.
|
||||
// Ideally we track when a library has been unloaded and remove it from this set before the memory backing goes away.
|
||||
std::set<std::string> Libs;
|
||||
|
||||
std::unordered_map<GuestcallInfo, uintptr_t, GuestcallInfoHash> GuestcallToHostTrampoline;
|
||||
std::unordered_map<GuestcallInfo, HostToGuestTrampolinePtr*, GuestcallInfoHash> GuestcallToHostTrampoline;
|
||||
|
||||
uint8_t *HostTrampolineInstanceDataPtr;
|
||||
size_t HostTrampolineInstanceDataAvailable = 0;
|
||||
@@ -157,11 +179,11 @@ namespace FEXCore {
|
||||
auto args = reinterpret_cast<args_t*>(argsv);
|
||||
auto CTX = Thread->CTX;
|
||||
|
||||
LOGMAN_THROW_A_FMT(args->original_callee, "Tried to link null pointer address to guest function");
|
||||
LOGMAN_THROW_A_FMT(args->target_addr, "Tried to link address to null pointer guest function");
|
||||
LOGMAN_THROW_AA_FMT(args->original_callee, "Tried to link null pointer address to guest function");
|
||||
LOGMAN_THROW_AA_FMT(args->target_addr, "Tried to link address to null pointer guest function");
|
||||
if (!CTX->Config.Is64BitMode) {
|
||||
LOGMAN_THROW_A_FMT((args->original_callee >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
LOGMAN_THROW_A_FMT((args->target_addr >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
LOGMAN_THROW_AA_FMT((args->original_callee >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
LOGMAN_THROW_AA_FMT((args->target_addr >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Thunks: Adding guest trampoline from address {:#x} to guest function {:#x}",
|
||||
@@ -194,98 +216,44 @@ namespace FEXCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a host-callable trampoline to call guest functions via the host ABI.
|
||||
* Guest-side helper to initiate creation of a host trampoline for
|
||||
* calling guest functions. This must be followed by a host-side call
|
||||
* to FinalizeHostTrampolineForGuestFunction to make the trampoline
|
||||
* usable.
|
||||
*
|
||||
* This trampoline uses the same calling convention as the given HostPacker. Trampolines
|
||||
* are cached, so it's safe to call this function repeatedly on the same arguments without
|
||||
* leaking memory.
|
||||
*
|
||||
* Invoking the returned trampoline has the effect of:
|
||||
* - packing the arguments (using the HostPacker identified by its SHA256)
|
||||
* - performing a host->guest transition
|
||||
* - unpacking the arguments via GuestUnpacker
|
||||
* - calling the function at GuestTarget
|
||||
*
|
||||
* The primary use case of this is ensuring that guest function pointers ("callbacks")
|
||||
* passed to thunked APIs can safely be called by the native host library.
|
||||
* This two-step initialization is equivalent to a host-side call to
|
||||
* MakeHostTrampolineForGuestFunction. The split is needed if the
|
||||
* host doesn't have all information needed to create the trampoline
|
||||
* on its own.
|
||||
*/
|
||||
static void MakeHostTrampolineForGuestFunction(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
IR::SHA256Sum *HostPackerSha256;
|
||||
uintptr_t GuestUnpacker;
|
||||
uintptr_t GuestTarget;
|
||||
uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
static void AllocateHostTrampolineForGuestFunction(void* ArgsRV) {
|
||||
struct ArgsRV_t {
|
||||
uintptr_t GuestUnpacker;
|
||||
uintptr_t GuestTarget;
|
||||
uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
LOGMAN_THROW_A_FMT(args->GuestTarget, "Tried to create host-trampoline to null pointer guest function");
|
||||
args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker);
|
||||
}
|
||||
|
||||
const auto CTX = Thread->CTX;
|
||||
const auto ThunkHandler = reinterpret_cast<ThunkHandler_impl *>(CTX->ThunkHandler.get());
|
||||
/**
|
||||
* Checks if the given pointer is allocated on the host heap.
|
||||
*
|
||||
* This is useful for thunking APIs that need to work with both guest
|
||||
* and host heap pointers.
|
||||
*/
|
||||
static void IsHostHeapAllocation(void* ArgsRV) {
|
||||
#ifdef ENABLE_JEMALLOC
|
||||
struct ArgsRV_t {
|
||||
void* ptr;
|
||||
bool rv;
|
||||
} *args = reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
const GuestcallInfo gci = { args->GuestUnpacker, args->GuestTarget };
|
||||
|
||||
// Try first with shared_lock
|
||||
{
|
||||
std::shared_lock lk(ThunkHandler->ThunksMutex);
|
||||
|
||||
auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci);
|
||||
if (found != ThunkHandler->GuestcallToHostTrampoline.end()) {
|
||||
args->rv = found->second;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard lk(ThunkHandler->ThunksMutex);
|
||||
|
||||
// Retry lookup with full lock before making a new trampoline to avoid double trampolines
|
||||
{
|
||||
auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci);
|
||||
if (found != ThunkHandler->GuestcallToHostTrampoline.end()) {
|
||||
args->rv = found->second;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No entry found => create new trampoline
|
||||
auto HostPackerEntry = ThunkHandler->Thunks.find(*args->HostPackerSha256);
|
||||
if (HostPackerEntry == ThunkHandler->Thunks.end()) {
|
||||
ERROR_AND_DIE_FMT("Unknown host packing function for callback");
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x}",
|
||||
args->GuestTarget);
|
||||
|
||||
const auto Length = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate;
|
||||
const auto InstanceInfoOffset = Length - sizeof(TrampolineInstanceInfo);
|
||||
|
||||
if (ThunkHandler->HostTrampolineInstanceDataAvailable < Length) {
|
||||
const auto allocation_step = 16 * 1024;
|
||||
ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step;
|
||||
ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap(
|
||||
0, ThunkHandler->HostTrampolineInstanceDataAvailable,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
LOGMAN_THROW_A_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr");
|
||||
}
|
||||
|
||||
const TrampolineInstanceInfo NewTrampolineInfo {
|
||||
.HostPacker = reinterpret_cast<uintptr_t>(HostPackerEntry->second),
|
||||
.CallCallback = (uintptr_t)&CallCallback,
|
||||
.GuestUnpacker = args->GuestUnpacker,
|
||||
.GuestTarget = args->GuestTarget
|
||||
};
|
||||
|
||||
uint8_t* const HostTrampoline = ThunkHandler->HostTrampolineInstanceDataPtr;
|
||||
ThunkHandler->HostTrampolineInstanceDataAvailable -= Length;
|
||||
ThunkHandler->HostTrampolineInstanceDataPtr += Length;
|
||||
|
||||
memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, Length);
|
||||
memcpy(HostTrampoline + InstanceInfoOffset, &NewTrampolineInfo, sizeof(NewTrampolineInfo));
|
||||
|
||||
args->rv = reinterpret_cast<uintptr_t>(HostTrampoline);
|
||||
|
||||
ThunkHandler->GuestcallToHostTrampoline[gci] = args->rv;
|
||||
args->rv = je_is_known_allocation(args->ptr);
|
||||
#else
|
||||
// Thunks usage without jemalloc isn't supported
|
||||
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void LoadLib(void *ArgsV) {
|
||||
@@ -352,9 +320,7 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
|
||||
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) override {
|
||||
|
||||
std::shared_lock lk(ThunksMutex);
|
||||
|
||||
@@ -367,12 +333,112 @@ namespace FEXCore {
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void RegisterTLSState(FEXCore::Core::InternalThreadState *Thread) override {
|
||||
::Thread = Thread;
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override {
|
||||
for (auto & Definition : Definitions) {
|
||||
Thunks.emplace(Definition.Sum, Definition.ThunkFunction);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ThunkHandler* ThunkHandler::Create() {
|
||||
return new ThunkHandler_impl();
|
||||
return new ThunkHandler_impl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a host-callable trampoline to call guest functions via the host ABI.
|
||||
*
|
||||
* This trampoline uses the same calling convention as the given HostPacker. Trampolines
|
||||
* are cached, so it's safe to call this function repeatedly on the same arguments without
|
||||
* leaking memory.
|
||||
*
|
||||
* Invoking the returned trampoline has the effect of:
|
||||
* - packing the arguments (using the HostPacker identified by its SHA256)
|
||||
* - performing a host->guest transition
|
||||
* - unpacking the arguments via GuestUnpacker
|
||||
* - calling the function at GuestTarget
|
||||
*
|
||||
* The primary use case of this is ensuring that guest function pointers ("callbacks")
|
||||
* passed to thunked APIs can safely be called by the native host library.
|
||||
*
|
||||
* Returns a pointer to the generated host trampoline and its TrampolineInstanceInfo.
|
||||
*
|
||||
* If HostPacker is zero, the trampoline will be partially initialized and needs to be
|
||||
* finalized with a call to FinalizeHostTrampolineForGuestFunction. A typical use case
|
||||
* is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side,
|
||||
* and provide the HostPacker host-side.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY
|
||||
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
|
||||
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
|
||||
|
||||
const auto CTX = Thread->CTX;
|
||||
const auto ThunkHandler = reinterpret_cast<ThunkHandler_impl *>(CTX->ThunkHandler.get());
|
||||
|
||||
const GuestcallInfo gci = { GuestUnpacker, GuestTarget };
|
||||
|
||||
// Try first with shared_lock
|
||||
{
|
||||
std::shared_lock lk(ThunkHandler->ThunksMutex);
|
||||
|
||||
auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci);
|
||||
if (found != ThunkHandler->GuestcallToHostTrampoline.end()) {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard lk(ThunkHandler->ThunksMutex);
|
||||
|
||||
// Retry lookup with full lock before making a new trampoline to avoid double trampolines
|
||||
{
|
||||
auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci);
|
||||
if (found != ThunkHandler->GuestcallToHostTrampoline.end()) {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x} via unpacker {:#x}",
|
||||
GuestTarget, GuestUnpacker);
|
||||
|
||||
if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) {
|
||||
const auto allocation_step = 16 * 1024;
|
||||
ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step;
|
||||
ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap(
|
||||
0, ThunkHandler->HostTrampolineInstanceDataAvailable,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr");
|
||||
}
|
||||
|
||||
auto HostTrampoline = reinterpret_cast<HostToGuestTrampolinePtr* const>(ThunkHandler->HostTrampolineInstanceDataPtr);
|
||||
ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize;
|
||||
ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize;
|
||||
memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize);
|
||||
GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo {
|
||||
.HostPacker = HostPacker,
|
||||
.CallCallback = (uintptr_t)&ThunkHandler_impl::CallCallback,
|
||||
.GuestUnpacker = GuestUnpacker,
|
||||
.GuestTarget = GuestTarget
|
||||
};
|
||||
|
||||
ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline;
|
||||
return HostTrampoline;
|
||||
}
|
||||
|
||||
FEX_DEFAULT_VISIBILITY
|
||||
void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) {
|
||||
auto& Trampoline = GetInstanceInfo(TrampolineAddress);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Trampoline.CallCallback == (uintptr_t)&ThunkHandler_impl::CallCallback,
|
||||
"Invalid trampoline at {} passed to {}", fmt::ptr(TrampolineAddress), __FUNCTION__);
|
||||
|
||||
if (!Trampoline.HostPacker) {
|
||||
LogMan::Msg::DFmt("Thunks: Finalizing trampoline at {} with host packer {}", fmt::ptr(TrampolineAddress), fmt::ptr(HostPacker));
|
||||
Trampoline.HostPacker = HostPacker;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,10 @@ $end_info$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -28,5 +32,7 @@ namespace FEXCore {
|
||||
virtual ~ThunkHandler() { }
|
||||
|
||||
static ThunkHandler* Create();
|
||||
|
||||
virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
|
||||
};
|
||||
};
|
||||
+3
-3
@@ -127,7 +127,7 @@ namespace FEXCore::IR {
|
||||
|
||||
auto Array = (AOTIRInlineIndex *)((char*)FilePtr + IndexOffset);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Entry->Array == nullptr && Entry->FilePtr == nullptr, "Entry must not be initialized here");
|
||||
LOGMAN_THROW_AA_FMT(Entry->Array == nullptr && Entry->FilePtr == nullptr, "Entry must not be initialized here");
|
||||
Entry->Array = Array;
|
||||
Entry->FilePtr = FilePtr;
|
||||
Entry->Size = Size;
|
||||
@@ -387,7 +387,7 @@ namespace FEXCore::IR {
|
||||
auto Inserted = AOTIRCache.insert({fileid, AOTIRCacheEntry { .FileId = fileid, .Filename = filename }});
|
||||
auto Entry = &(Inserted.first->second);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Entry->Array == nullptr, "Duplicate LoadAOTIRCacheEntry");
|
||||
LOGMAN_THROW_AA_FMT(Entry->Array == nullptr, "Duplicate LoadAOTIRCacheEntry");
|
||||
|
||||
if (CTX->Config.AOTIRLoad && AOTIRLoader) {
|
||||
auto streamfd = AOTIRLoader(fileid);
|
||||
@@ -403,7 +403,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
void AOTIRCaptureCache::UnloadAOTIRCacheEntry(AOTIRCacheEntry *Entry) {
|
||||
LOGMAN_THROW_A_FMT(Entry != nullptr, "Removing not existing entry");
|
||||
LOGMAN_THROW_AA_FMT(Entry != nullptr, "Removing not existing entry");
|
||||
|
||||
if (Entry->Array) {
|
||||
FEXCore::Allocator::munmap(Entry->FilePtr, Entry->Size);
|
||||
|
||||
+34
-62
@@ -123,12 +123,12 @@
|
||||
"constexpr FEXCore::IR::MemOffsetType MEM_OFFSET_UXTW {1}",
|
||||
"constexpr FEXCore::IR::MemOffsetType MEM_OFFSET_SXTW {2}",
|
||||
|
||||
"constexpr FEXCore::IR::BreakReason Break_Unimplemented {0}",
|
||||
"constexpr FEXCore::IR::BreakReason Break_Interrupt {1}",
|
||||
"constexpr FEXCore::IR::BreakReason Break_Interrupt3 {2}",
|
||||
"constexpr FEXCore::IR::BreakReason Break_Halt {3}",
|
||||
"constexpr FEXCore::IR::BreakReason Break_Overflow {4}",
|
||||
"constexpr FEXCore::IR::BreakReason Break_InvalidInstruction {5}"
|
||||
"struct BreakDefinition {",
|
||||
" uint16_t ErrorRegister;",
|
||||
" uint8_t Signal;",
|
||||
" uint8_t TrapNumber;",
|
||||
" uint8_t si_code;",
|
||||
"};"
|
||||
],
|
||||
"IRTypes" : {
|
||||
"i1": "bool",
|
||||
@@ -150,7 +150,7 @@
|
||||
"SyscallFlags": "FEXCore::IR::SyscallFlags",
|
||||
"SHA256Sum": "SHA256Sum",
|
||||
"MemOffsetType": "MemOffsetType",
|
||||
"BreakReason": "BreakReason",
|
||||
"BreakDefinition": "BreakDefinition",
|
||||
"RoundType": "RoundType"
|
||||
},
|
||||
"Ops": {
|
||||
@@ -192,7 +192,7 @@
|
||||
"DestSize": "8"
|
||||
},
|
||||
|
||||
"RemoveThreadCodeEntry": {
|
||||
"ThreadRemoveCodeEntry": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "GetOpSize(_NewRIP)"
|
||||
},
|
||||
"Break BreakReason:$Reason, u8:$Literal": {
|
||||
"Break BreakDefinition:$Reason": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"SignalReturn": {
|
||||
@@ -302,10 +302,6 @@
|
||||
}
|
||||
},
|
||||
"Moves": {
|
||||
"GPR = Mov GPR:$Value": {
|
||||
"DestSize": "GetOpSize(_Value)"
|
||||
},
|
||||
|
||||
"GPR = ExtractElementPair GPRPair:$Pair, u8:$Element": {
|
||||
"Desc": ["Extracts a register for the register pair"],
|
||||
"DestSize": "GetOpSize(_Pair) >> 1"
|
||||
@@ -364,11 +360,11 @@
|
||||
},
|
||||
|
||||
"StoreContext u8:#ByteSize, RegisterClass:$Class, SSA:$Value, u32:$Offset": {
|
||||
"Desc": ["Stores a value to the context with offset",
|
||||
"Ctx[Offset] = Value",
|
||||
"Zero Extends if value's type is too small",
|
||||
"Truncates if value's type is too large"
|
||||
],
|
||||
"Desc": ["Stores a value to the context with offset",
|
||||
"Ctx[Offset] = Value",
|
||||
"Zero Extends if value's type is too small",
|
||||
"Truncates if value's type is too large"
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "ByteSize",
|
||||
"EmitValidation": [
|
||||
@@ -845,27 +841,25 @@
|
||||
"DestSize": "std::max<uint8_t>(4, std::max<uint8_t>(GetOpSize(_TrueVal), GetOpSize(_FalseVal)))"
|
||||
},
|
||||
"GPR = Extr GPR:$Upper, GPR:$Lower, u8:$LSB": {
|
||||
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
|
||||
"It then extracts a bitfield width that size of a GPR from the LSB",
|
||||
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
|
||||
"<Size * 2> ConcatValue = $Upper:$Lower",
|
||||
"Result = ConcatValue<LSB+Size - 1: LSB>"
|
||||
]
|
||||
"Desc": ["Concats the two GPRs to create a value that is the size of the full two GPRs",
|
||||
"It then extracts a bitfield width that size of a GPR from the LSB",
|
||||
"Valid LSB range is 0-31 for 32bit and 0-63 for 64bit",
|
||||
"<Size * 2> ConcatValue = $Upper:$Lower",
|
||||
"Result = ConcatValue<LSB+Size - 1: LSB>"
|
||||
]
|
||||
},
|
||||
"GPR = PDep GPR:$Input, GPR:$Mask": {
|
||||
"Desc": [
|
||||
"Performs a parallel bit deposit.",
|
||||
"Takes the contiguous low-order bits and deposits them into",
|
||||
"the destination at the locations specified by the Mask."
|
||||
]
|
||||
"Desc": ["Performs a parallel bit deposit.",
|
||||
"Takes the contiguous low-order bits and deposits them into",
|
||||
"the destination at the locations specified by the Mask."
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = PExt GPR:$Input, GPR:$Mask": {
|
||||
"Desc": [
|
||||
"Performs a parallel bit extract.",
|
||||
"Each bit set in the mask will select the corresponding bit in the Input",
|
||||
"and transfers them to the lower contiguous bits in the destination."
|
||||
]
|
||||
"Desc": ["Performs a parallel bit extract.",
|
||||
"Each bit set in the mask will select the corresponding bit in the Input",
|
||||
"and transfers them to the lower contiguous bits in the destination."
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = LDiv GPR:$Lower, GPR:$Upper, GPR:$Divisor": {
|
||||
@@ -924,15 +918,6 @@
|
||||
}
|
||||
},
|
||||
"Vector": {
|
||||
"FPR = SplatVector2 FPR:$Scalar": {
|
||||
"NumElements": "2",
|
||||
"DestSize": "GetOpSize(_Scalar) * 2"
|
||||
},
|
||||
"FPR = SplatVector4 FPR:$Scalar": {
|
||||
"NumElements": "4",
|
||||
"DestSize": "GetOpSize(_Scalar) * 4"
|
||||
},
|
||||
|
||||
"FPR = VMov u8:#RegisterSize, FPR:$Source": {
|
||||
"Desc" : ["Copy vector register",
|
||||
"When Register size is smaller than Source register size,",
|
||||
@@ -941,12 +926,6 @@
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
|
||||
"FPR = VBitcast u8:#RegisterSize, u8:#ElementSize, FPR:$Source": {
|
||||
"Desc": ["Workaround for issue with LLVM breaking when loading scalar elements to vectors"],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = VectorZero u8:#RegisterSize": {
|
||||
"Desc": ["Generates a vector zero",
|
||||
"Useful to generate a zero vector without any previous dependencies"
|
||||
@@ -1038,9 +1017,6 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VExtractElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
|
||||
"DestSize": "ElementSize"
|
||||
},
|
||||
"FPR = VDupElement u8:#RegisterSize, u8:#ElementSize, FPR:$Vector, u8:$Index": {
|
||||
"Desc": ["Duplicates one element from the source register across the whole register"],
|
||||
"DestSize": "RegisterSize",
|
||||
@@ -1124,9 +1100,9 @@
|
||||
},
|
||||
|
||||
"FPR = VRev64 u8:#RegisterSize, u8:#ElementSize, FPR:$Vector": {
|
||||
"Desc" : ["Reverses elements in 64-bit halfwords",
|
||||
"Available element size: 1byte, 2 byte, 4 byte"
|
||||
],
|
||||
"Desc" : ["Reverses elements in 64-bit halfwords",
|
||||
"Available element size: 1byte, 2 byte, 4 byte"
|
||||
],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
@@ -1320,10 +1296,6 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VInsScalarElement u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, FPR:$SrcScalar": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VInsGPR u8:#RegisterSize, u8:#ElementSize, u8:$DestIdx, FPR:$DestVector, GPR:$Src": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
@@ -1587,9 +1559,9 @@
|
||||
"DestSize": "16"
|
||||
},
|
||||
"GPR = F80Cmp FPR:$X80Src1, FPR:$X80Src2, u32:$Flags": {
|
||||
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"Desc": ["Does a scalar unordered compare and stores the asked for flags in to a GPR",
|
||||
"Ordering flag result is true if either float input is NaN"
|
||||
],
|
||||
"DestSize": "4"
|
||||
},
|
||||
"FPR = F80BCDLoad FPR:$X80Src": {
|
||||
|
||||
+6
-1
@@ -182,7 +182,12 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, FEXCore::IR::BreakDefinition Arg) {
|
||||
*out << "{" << Arg.ErrorRegister << ".";
|
||||
*out << static_cast<uint32_t>(Arg.Signal) << ".";
|
||||
*out << static_cast<uint32_t>(Arg.TrapNumber) << ".";
|
||||
*out << static_cast<uint32_t>(Arg.si_code) << "}";
|
||||
}
|
||||
|
||||
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData) {
|
||||
auto HeaderOp = IR->GetHeader();
|
||||
|
||||
+4
-2
@@ -8,6 +8,7 @@ $end_info$
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <array>
|
||||
@@ -87,7 +88,8 @@ FEXCore::IR::RegisterClassType IREmitter::WalkFindRegClass(OrderedNode *Node) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled op type: {} {} in argument class validation", IROp->Op, GetOpName(Node));
|
||||
LOGMAN_MSG_A_FMT("Unhandled op type: {} {} in argument class validation",
|
||||
ToUnderlying(IROp->Op), GetOpName(Node));
|
||||
break;
|
||||
}
|
||||
return InvalidClass;
|
||||
@@ -167,7 +169,7 @@ IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlockAfter(OrderedNode
|
||||
if (insertAfter) {
|
||||
LinkCodeBlocks(insertAfter, CodeNode);
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
|
||||
LOGMAN_THROW_AA_FMT(CurrentCodeBlock != nullptr, "CurrentCodeBlock must not be null here");
|
||||
|
||||
// Find last block
|
||||
auto LastBlock = CurrentCodeBlock;
|
||||
|
||||
+28
-14
@@ -252,22 +252,36 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::BreakReason> DecodeValue(const std::string &Arg) {
|
||||
static constexpr std::array<std::string_view, 6> Names = {
|
||||
"Unimplemented",
|
||||
"Interrupt",
|
||||
"Interrupt3",
|
||||
"Halt",
|
||||
"Overfloat",
|
||||
"InvalidInstruction",
|
||||
};
|
||||
std::pair<DecodeFailure, FEXCore::IR::BreakDefinition> DecodeValue(const std::string &Arg) {
|
||||
uint32_t tmp{};
|
||||
std::stringstream ss{Arg};
|
||||
BreakDefinition Reason{};
|
||||
|
||||
for (size_t i = 0; i < Names.size(); ++i) {
|
||||
if (Names[i] == Arg) {
|
||||
return {DecodeFailure::DECODE_OKAY, BreakReason{static_cast<uint8_t>(i)}};
|
||||
}
|
||||
// Seek past '{'
|
||||
ss.seekg(1, std::ios::cur);
|
||||
ss >> Reason.ErrorRegister;
|
||||
|
||||
// Seek past '.'
|
||||
ss.seekg(1, std::ios::cur);
|
||||
ss >> tmp;
|
||||
Reason.Signal = tmp;
|
||||
|
||||
// Seek past '.'
|
||||
ss.seekg(1, std::ios::cur);
|
||||
ss >> tmp;
|
||||
Reason.TrapNumber = tmp;
|
||||
|
||||
// Seek past '.'
|
||||
ss.seekg(1, std::ios::cur);
|
||||
ss >> tmp;
|
||||
Reason.si_code = tmp;
|
||||
|
||||
if (ss.fail()) {
|
||||
return {DecodeFailure::DECODE_INVALIDCHAR, {}};
|
||||
}
|
||||
else {
|
||||
return {DecodeFailure::DECODE_OKAY, Reason};
|
||||
}
|
||||
return {DecodeFailure::DECODE_INVALID_BREAKTYPE, {}};
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
+6
-6
@@ -20,7 +20,7 @@ void PassManager::AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineCo
|
||||
FEX_CONFIG_OPT(DisablePasses, O0);
|
||||
|
||||
if (!DisablePasses()) {
|
||||
InsertPass(CreateContextLoadStoreElimination());
|
||||
InsertPass(CreateContextLoadStoreElimination(ctx->HostFeatures.SupportsAVX));
|
||||
|
||||
if (Is64BitMode()) {
|
||||
// This needs to run after RCLSE
|
||||
@@ -28,7 +28,7 @@ void PassManager::AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineCo
|
||||
InsertPass(CreateLongDivideEliminationPass());
|
||||
}
|
||||
|
||||
InsertPass(CreateDeadStoreElimination());
|
||||
InsertPass(CreateDeadStoreElimination(ctx->HostFeatures.SupportsAVX));
|
||||
InsertPass(CreatePassDeadCodeElimination());
|
||||
InsertPass(CreateConstProp(InlineConstants, ctx->HostFeatures.SupportsTSOImm9));
|
||||
|
||||
@@ -39,12 +39,12 @@ void PassManager::AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineCo
|
||||
|
||||
// only do SRA if enabled and JIT
|
||||
if (InlineConstants && StaticRegisterAllocation)
|
||||
InsertPass(CreateStaticRegisterAllocationPass());
|
||||
InsertPass(CreateStaticRegisterAllocationPass(ctx->HostFeatures.SupportsAVX));
|
||||
}
|
||||
else {
|
||||
// only do SRA if enabled and JIT
|
||||
if (InlineConstants && StaticRegisterAllocation)
|
||||
InsertPass(CreateStaticRegisterAllocationPass());
|
||||
InsertPass(CreateStaticRegisterAllocationPass(ctx->HostFeatures.SupportsAVX));
|
||||
}
|
||||
|
||||
// If the IR is compacted post-RA then the node indexing gets messed up and the backend isn't able to find the register assigned to a node
|
||||
@@ -61,8 +61,8 @@ void PassManager::AddDefaultValidationPasses() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void PassManager::InsertRegisterAllocationPass(bool OptimizeSRA) {
|
||||
InsertPass(IR::CreateRegisterAllocationPass(GetPass("Compaction"), OptimizeSRA), "RA");
|
||||
void PassManager::InsertRegisterAllocationPass(bool OptimizeSRA, bool SupportsAVX) {
|
||||
InsertPass(IR::CreateRegisterAllocationPass(GetPass("Compaction"), OptimizeSRA, SupportsAVX), "RA");
|
||||
}
|
||||
|
||||
bool PassManager::Run(IREmitter *IREmit) {
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public:
|
||||
return PassPtr;
|
||||
}
|
||||
|
||||
void InsertRegisterAllocationPass(bool OptimizeSRA);
|
||||
void InsertRegisterAllocationPass(bool OptimizeSRA, bool SupportsAVX);
|
||||
|
||||
bool Run(IREmitter *IREmit);
|
||||
|
||||
|
||||
+6
-4
@@ -12,14 +12,16 @@ class RegisterAllocationPass;
|
||||
class RegisterAllocationData;
|
||||
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool InlineConstants, bool SupportsTSOImm9);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateContextLoadStoreElimination();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateContextLoadStoreElimination(bool SupportsAVX);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateSyscallOptimization();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination(bool SupportsAVX);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreatePassDeadCodeElimination();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateIRCompaction(FEXCore::Utils::IntrusivePooledAllocator &Allocator);
|
||||
std::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateStaticRegisterAllocationPass();
|
||||
std::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass,
|
||||
bool OptimizeSRA,
|
||||
bool SupportsAVX);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateStaticRegisterAllocationPass(bool SupportsAVX);
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateLongDivideEliminationPass();
|
||||
|
||||
namespace Validation {
|
||||
|
||||
+10
-10
@@ -6,7 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
|
||||
#if defined(_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
//aarch64 heuristics
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
#include "aarch64/cpu-aarch64.h"
|
||||
@@ -45,20 +45,20 @@ uint64_t getMask(IROp_Header* Op) {
|
||||
return (~0ULL) >> (64 - NumBits);
|
||||
}
|
||||
|
||||
#ifdef _M_X86_64
|
||||
// very lazy heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
|
||||
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
|
||||
}
|
||||
#elif defined(_M_ARM_64)
|
||||
#if JIT_ARM64
|
||||
//aarch64 heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { if (width < 32) width = 32; return vixl::aarch64::Assembler::IsImmLogical(imm, width); }
|
||||
static bool IsImmAddSub(uint64_t imm) { return vixl::aarch64::Assembler::IsImmAddSub(imm); }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == AccessSize;
|
||||
}
|
||||
#elif JIT_X86_64
|
||||
// very lazy heuristics
|
||||
static bool IsImmLogical(uint64_t imm, unsigned width) { return imm < 0x8000'0000; }
|
||||
static bool IsImmAddSub(uint64_t imm) { return imm < 0x8000'0000; }
|
||||
static bool IsMemoryScale(uint64_t Scale, uint8_t AccessSize) {
|
||||
return Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8;
|
||||
}
|
||||
#else
|
||||
#error No inline constant heuristics for this target
|
||||
#endif
|
||||
@@ -299,7 +299,7 @@ void ConstProp::FCMPOptimization(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
auto ghf = IROp->CW<IR::IROp_GetHostFlag>();
|
||||
|
||||
auto fcmp = IREmit->GetOpHeader(ghf->Value)->CW<IR::IROp_FCmp>();
|
||||
LOGMAN_THROW_A_FMT(fcmp->Header.Op == OP_FCMP || fcmp->Header.Op == OP_F80CMP, "Unexpected OP_GETHOSTFLAG source");
|
||||
LOGMAN_THROW_AA_FMT(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;
|
||||
}
|
||||
|
||||
+76
-74
@@ -25,7 +25,7 @@ $end_info$
|
||||
namespace {
|
||||
struct ContextMemberClassification {
|
||||
size_t Offset;
|
||||
uint8_t Size;
|
||||
uint16_t Size;
|
||||
};
|
||||
|
||||
enum LastAccessType {
|
||||
@@ -76,10 +76,7 @@ namespace {
|
||||
std::vector<ContextMemberInfo> ClassificationInfo;
|
||||
};
|
||||
|
||||
constexpr static std::array<LastAccessType, 16> DefaultAccess = {
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_INVALID, // PAD
|
||||
constexpr static std::array<LastAccessType, 15> DefaultAccess = {
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
@@ -88,14 +85,16 @@ namespace {
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_INVALID, // PAD
|
||||
ACCESS_NONE,
|
||||
ACCESS_INVALID, // SSE padding in non-AVX case
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
ACCESS_NONE,
|
||||
};
|
||||
|
||||
static void ClassifyContextStruct(ContextInfo *ContextClassificationInfo) {
|
||||
static void ClassifyContextStruct(ContextInfo *ContextClassificationInfo, bool SupportsAVX) {
|
||||
auto ContextClassification = &ContextClassificationInfo->ClassificationInfo;
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
@@ -118,32 +117,12 @@ namespace {
|
||||
});
|
||||
}
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, gregs[16]),
|
||||
sizeof(uint64_t),
|
||||
},
|
||||
DefaultAccess[2], ///< NOP padding
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, xmm[0][0]) + sizeof(FEXCore::Core::CPUState::xmm[0]) * i,
|
||||
FEXCore::Core::CPUState::XMM_REG_SIZE,
|
||||
},
|
||||
DefaultAccess[3],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, es),
|
||||
sizeof(FEXCore::Core::CPUState::es),
|
||||
},
|
||||
DefaultAccess[5],
|
||||
DefaultAccess[2],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -152,7 +131,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, cs),
|
||||
sizeof(FEXCore::Core::CPUState::cs),
|
||||
},
|
||||
DefaultAccess[6],
|
||||
DefaultAccess[3],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -161,7 +140,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, ss),
|
||||
sizeof(FEXCore::Core::CPUState::ss),
|
||||
},
|
||||
DefaultAccess[7],
|
||||
DefaultAccess[4],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -170,7 +149,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, ds),
|
||||
sizeof(FEXCore::Core::CPUState::ds),
|
||||
},
|
||||
DefaultAccess[8],
|
||||
DefaultAccess[5],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -179,7 +158,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, gs),
|
||||
sizeof(FEXCore::Core::CPUState::gs),
|
||||
},
|
||||
DefaultAccess[4],
|
||||
DefaultAccess[6],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -188,10 +167,43 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, fs),
|
||||
sizeof(FEXCore::Core::CPUState::fs),
|
||||
},
|
||||
DefaultAccess[9],
|
||||
DefaultAccess[7],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
if (SupportsAVX) {
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, xmm.avx.data[0][0]) + FEXCore::Core::CPUState::XMM_AVX_REG_SIZE * i,
|
||||
FEXCore::Core::CPUState::XMM_AVX_REG_SIZE,
|
||||
},
|
||||
DefaultAccess[8],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, xmm.sse.data[0][0]) + FEXCore::Core::CPUState::XMM_SSE_REG_SIZE * i,
|
||||
FEXCore::Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
},
|
||||
DefaultAccess[8],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, xmm.sse.pad[0][0]),
|
||||
static_cast<uint16_t>(FEXCore::Core::CPUState::XMM_SSE_REG_SIZE * FEXCore::Core::CPUState::NUM_XMMS),
|
||||
},
|
||||
DefaultAccess[9],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_FLAGS; ++i) {
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
@@ -203,22 +215,13 @@ namespace {
|
||||
});
|
||||
}
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, flags[48]),
|
||||
sizeof(uint64_t),
|
||||
},
|
||||
DefaultAccess[11], ///< NOP padding
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, mm[0][0]) + sizeof(FEXCore::Core::CPUState::mm[0]) * i,
|
||||
FEXCore::Core::CPUState::MM_REG_SIZE
|
||||
},
|
||||
DefaultAccess[12],
|
||||
DefaultAccess[11],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
@@ -230,7 +233,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, gdt[0]) + sizeof(FEXCore::Core::CPUState::gdt[0]) * i,
|
||||
sizeof(FEXCore::Core::CPUState::gdt[0]),
|
||||
},
|
||||
DefaultAccess[13],
|
||||
DefaultAccess[12],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
}
|
||||
@@ -241,7 +244,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, FCW),
|
||||
sizeof(FEXCore::Core::CPUState::FCW),
|
||||
},
|
||||
DefaultAccess[14],
|
||||
DefaultAccess[13],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -251,7 +254,7 @@ namespace {
|
||||
offsetof(FEXCore::Core::CPUState, FTW),
|
||||
sizeof(FEXCore::Core::CPUState::FTW),
|
||||
},
|
||||
DefaultAccess[15],
|
||||
DefaultAccess[14],
|
||||
FEXCore::IR::InvalidClass,
|
||||
});
|
||||
|
||||
@@ -266,7 +269,7 @@ namespace {
|
||||
ClassifiedStructSize += it.Class.Size;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(ClassifiedStructSize == sizeof(FEXCore::Core::CPUState),
|
||||
LOGMAN_THROW_AA_FMT(ClassifiedStructSize == sizeof(FEXCore::Core::CPUState),
|
||||
"Classified CPUStruct size doesn't match real CPUState struct size! {} (classified) != {} (real)",
|
||||
ClassifiedStructSize, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
@@ -275,7 +278,7 @@ namespace {
|
||||
ContextClassificationInfo->Lookup.size(), sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
static void ResetClassificationAccesses(ContextInfo *ContextClassificationInfo) {
|
||||
static void ResetClassificationAccesses(ContextInfo *ContextClassificationInfo, bool SupportsAVX) {
|
||||
auto ContextClassification = &ContextClassificationInfo->ClassificationInfo;
|
||||
|
||||
auto SetAccess = [&](size_t Offset, auto Access) {
|
||||
@@ -289,36 +292,36 @@ namespace {
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_GPRS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[1]);
|
||||
}
|
||||
|
||||
SetAccess(Offset++, DefaultAccess[2]);
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[3]);
|
||||
}
|
||||
|
||||
SetAccess(Offset++, DefaultAccess[3]);
|
||||
SetAccess(Offset++, DefaultAccess[4]);
|
||||
SetAccess(Offset++, DefaultAccess[5]);
|
||||
SetAccess(Offset++, DefaultAccess[6]);
|
||||
SetAccess(Offset++, DefaultAccess[7]);
|
||||
SetAccess(Offset++, DefaultAccess[8]);
|
||||
SetAccess(Offset++, DefaultAccess[9]);
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_XMMS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[8]);
|
||||
}
|
||||
|
||||
if (!SupportsAVX) {
|
||||
SetAccess(Offset++, DefaultAccess[9]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_FLAGS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[10]);
|
||||
}
|
||||
|
||||
SetAccess(Offset++, DefaultAccess[11]);
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[12]);
|
||||
SetAccess(Offset++, DefaultAccess[11]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_GDTS; ++i) {
|
||||
SetAccess(Offset++, DefaultAccess[13]);
|
||||
SetAccess(Offset++, DefaultAccess[12]);
|
||||
}
|
||||
|
||||
SetAccess(Offset++, DefaultAccess[13]);
|
||||
SetAccess(Offset++, DefaultAccess[14]);
|
||||
SetAccess(Offset++, DefaultAccess[15]);
|
||||
}
|
||||
|
||||
struct BlockInfo {
|
||||
@@ -330,8 +333,8 @@ namespace {
|
||||
|
||||
class RCLSE final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
RCLSE() {
|
||||
ClassifyContextStruct(&ClassifiedStruct);
|
||||
explicit RCLSE(bool SupportsAVX_) : SupportsAVX{SupportsAVX_} {
|
||||
ClassifyContextStruct(&ClassifiedStruct, SupportsAVX);
|
||||
DCE = FEXCore::IR::CreatePassDeadCodeElimination();
|
||||
}
|
||||
bool Run(FEXCore::IR::IREmitter *IREmit) override;
|
||||
@@ -341,6 +344,8 @@ private:
|
||||
ContextInfo ClassifiedStruct;
|
||||
std::unordered_map<FEXCore::IR::NodeID, BlockInfo> OffsetToBlockMap;
|
||||
|
||||
bool SupportsAVX;
|
||||
|
||||
ContextMemberInfo *FindMemberInfo(ContextInfo *ClassifiedInfo, uint32_t Offset, uint8_t Size);
|
||||
ContextMemberInfo *RecordAccess(ContextMemberInfo *Info, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode *Node, FEXCore::IR::OrderedNode *StoreNode = nullptr);
|
||||
ContextMemberInfo *RecordAccess(ContextInfo *ClassifiedInfo, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode *Node, FEXCore::IR::OrderedNode *StoreNode = nullptr);
|
||||
@@ -355,8 +360,8 @@ ContextMemberInfo *RCLSE::FindMemberInfo(ContextInfo *ContextClassificationInfo,
|
||||
}
|
||||
|
||||
ContextMemberInfo *RCLSE::RecordAccess(ContextMemberInfo *Info, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode *Node, FEXCore::IR::OrderedNode *StoreNode) {
|
||||
LOGMAN_THROW_A_FMT((Offset + Size) <= (Info->Class.Offset + Info->Class.Size), "Access to context item went over member size");
|
||||
LOGMAN_THROW_A_FMT(Info->Accessed != ACCESS_INVALID, "Tried to access invalid member");
|
||||
LOGMAN_THROW_AA_FMT((Offset + Size) <= (Info->Class.Offset + Info->Class.Size), "Access to context item went over member size");
|
||||
LOGMAN_THROW_AA_FMT(Info->Accessed != ACCESS_INVALID, "Tried to access invalid member");
|
||||
|
||||
// If we aren't fully overwriting the member then it is a partial write that we need to track
|
||||
if (Size < Info->Class.Size) {
|
||||
@@ -444,7 +449,6 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
|
||||
* %ssa26 i128 = LoadMem %ssa25 i64, 0x10
|
||||
* (%%ssa27) StoreContext %ssa26 i128, 0x10, 0xb0
|
||||
* %ssa28 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa29 i128 = VBitcast %ssa26 i128
|
||||
*
|
||||
* eg.
|
||||
* %ssa6 i128 = LoadContext 0x10, 0x90
|
||||
@@ -457,13 +461,11 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
|
||||
* eg.
|
||||
* (%%ssa189) StoreContext %ssa188 i128, 0x10, 0xa0
|
||||
* %ssa190 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa191 i128 = VBitcast %ssa188 i128
|
||||
* %ssa192 i128 = VAdd %ssa191 i128, %ssa190 i128, 0x10, 0x4
|
||||
* %ssa192 i128 = VAdd %ssa188 i128, %ssa190 i128, 0x10, 0x4
|
||||
* (%%ssa193) StoreContext %ssa192 i128, 0x10, 0xa0
|
||||
* Converts to
|
||||
* %ssa173 i128 = LoadContext 0x10, 0x90
|
||||
* %ssa174 i128 = VBitcast %ssa172 i128
|
||||
* %ssa175 i128 = VAdd %ssa174 i128, %ssa173 i128, 0x10, 0x4
|
||||
* %ssa175 i128 = VAdd %ssa172 i128, %ssa173 i128, 0x10, 0x4
|
||||
* (%%ssa176) StoreContext %ssa175 i128, 0x10, 0xa0
|
||||
|
||||
*/
|
||||
@@ -483,7 +485,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) {
|
||||
auto BlockOp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto BlockEnd = IREmit->GetIterator(BlockOp->Last);
|
||||
|
||||
ResetClassificationAccesses(&LocalInfo);
|
||||
ResetClassificationAccesses(&LocalInfo, SupportsAVX);
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_STORECONTEXT) {
|
||||
@@ -675,14 +677,14 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) {
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::OPTIMIZETHROUGH) != FEXCore::IR::SyscallFlags::OPTIMIZETHROUGH) {
|
||||
// We can't track through these
|
||||
ResetClassificationAccesses(&LocalInfo);
|
||||
ResetClassificationAccesses(&LocalInfo, SupportsAVX);
|
||||
}
|
||||
}
|
||||
else if (IROp->Op == OP_STORECONTEXTINDEXED ||
|
||||
IROp->Op == OP_LOADCONTEXTINDEXED ||
|
||||
IROp->Op == OP_BREAK) {
|
||||
// We can't track through these
|
||||
ResetClassificationAccesses(&LocalInfo);
|
||||
ResetClassificationAccesses(&LocalInfo, SupportsAVX);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -710,8 +712,8 @@ bool RCLSE::Run(FEXCore::IR::IREmitter *IREmit) {
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateContextLoadStoreElimination() {
|
||||
return std::make_unique<RCLSE>();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateContextLoadStoreElimination(bool SupportsAVX) {
|
||||
return std::make_unique<RCLSE>(SupportsAVX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,69 @@ constexpr int PropagationRounds = 5;
|
||||
|
||||
class DeadStoreElimination final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
explicit DeadStoreElimination(bool SupportsAVX_) : SupportsAVX{SupportsAVX_} {}
|
||||
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
private:
|
||||
bool SupportsAVX;
|
||||
|
||||
bool IsFPR(uint32_t Offset) const {
|
||||
const auto [begin, end] = [this]() -> std::pair<ptrdiff_t, ptrdiff_t> {
|
||||
if (SupportsAVX) {
|
||||
return {
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[0][0]),
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[16][0])
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]),
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[16][0])
|
||||
};
|
||||
}
|
||||
}();
|
||||
|
||||
if (Offset < begin || Offset >= end)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTrackedWriteFPR(uint32_t Offset, uint8_t Size) const {
|
||||
if (Size != 16 && Size != 8 && Size != 4)
|
||||
return false;
|
||||
if (Offset & 15)
|
||||
return false;
|
||||
|
||||
return IsFPR(Offset);
|
||||
}
|
||||
|
||||
uint64_t FPRBit(uint32_t Offset, uint32_t Size) const {
|
||||
if (!IsFPR(Offset)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto begin = offsetof(Core::CpuStateFrame, State.xmm.avx.data[0][0]);
|
||||
|
||||
const auto regSize = SupportsAVX ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto regn = (Offset - begin) / regSize;
|
||||
const auto bitn = regn * 3;
|
||||
|
||||
if (!IsTrackedWriteFPR(Offset, Size))
|
||||
return 7UL << (bitn);
|
||||
|
||||
if (Size == 16)
|
||||
return 7UL << (bitn);
|
||||
else if (Size == 8)
|
||||
return 3UL << (bitn);
|
||||
else if (Size == 4)
|
||||
return 1UL << (bitn);
|
||||
else
|
||||
LOGMAN_MSG_A_FMT("Unexpected FPR size {}", Size);
|
||||
|
||||
return 7UL << (bitn); // Return maximum on failure case
|
||||
}
|
||||
};
|
||||
|
||||
struct FlagInfo {
|
||||
@@ -74,60 +136,12 @@ struct FPRInfo {
|
||||
uint64_t kill { 0 };
|
||||
};
|
||||
|
||||
bool IsFPR(uint32_t Offset) {
|
||||
|
||||
auto begin = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0]);
|
||||
auto end = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[16][0]);
|
||||
|
||||
if (Offset < begin || Offset >= end)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsTrackedWriteFPR(uint32_t Offset, uint8_t Size) {
|
||||
if (Size != 16 && Size != 8 && Size != 4)
|
||||
return false;
|
||||
if (Offset & 15)
|
||||
return false;
|
||||
|
||||
return IsFPR(Offset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t FPRBit(uint32_t Offset, uint32_t Size) {
|
||||
if (!IsFPR(Offset)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto begin = offsetof(Core::CpuStateFrame, State.xmm[0][0]);
|
||||
|
||||
auto regn = (Offset - begin) / Core::CPUState::XMM_REG_SIZE;
|
||||
auto bitn = regn * 3;
|
||||
|
||||
if (!IsTrackedWriteFPR(Offset, Size))
|
||||
return 7UL << (bitn);
|
||||
|
||||
if (Size == 16)
|
||||
return 7UL << (bitn);
|
||||
else if (Size == 8)
|
||||
return 3UL << (bitn);
|
||||
else if (Size == 4)
|
||||
return 1UL << (bitn);
|
||||
else
|
||||
LOGMAN_MSG_A_FMT("Unexpected FPR size {}", Size);
|
||||
|
||||
return 7UL << (bitn); // Return maximum on failure case
|
||||
}
|
||||
|
||||
struct Info {
|
||||
FlagInfo flag;
|
||||
GPRInfo gpr;
|
||||
FPRInfo fpr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief This is a temporary pass to detect simple multiblock dead flag/gpr/fpr stores
|
||||
*
|
||||
@@ -341,8 +355,8 @@ bool DeadStoreElimination::Run(IREmitter *IREmit) {
|
||||
return Changed;
|
||||
}
|
||||
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination() {
|
||||
return std::make_unique<DeadStoreElimination>();
|
||||
std::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination(bool SupportsAVX) {
|
||||
return std::make_unique<DeadStoreElimination>(SupportsAVX);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,7 +77,7 @@ bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
|
||||
auto HeaderNode = CurrentIR.GetHeaderNode();
|
||||
auto HeaderOp = CurrentIR.GetHeader();
|
||||
LOGMAN_THROW_A_FMT(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader");
|
||||
LOGMAN_THROW_AA_FMT(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader");
|
||||
|
||||
// This compaction pass is something that we need to ensure correct ordering and distances between IROps
|
||||
// Later on we assume that an IROp's SSA value live range is its Node locations
|
||||
@@ -101,7 +101,7 @@ bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
{
|
||||
// Generate our codeblocks and link them together
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
LOGMAN_THROW_A_FMT(BlockHeader->Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockHeader->Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
auto LocalBlockIRNode = LocalBuilder._CodeBlock(LocalHeaderOp, LocalHeaderOp); // Use LocalHeaderOp as a dummy arg for now
|
||||
OldToNewRemap[CurrentIR.GetID(BlockNode).Value].NodeID = LocalIR.GetID(LocalBlockIRNode.Node);
|
||||
@@ -165,7 +165,7 @@ bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
for (auto &Block : GeneratedCodeBlocks) {
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
auto BlockIROp = LocalIR.GetOp<FEXCore::IR::IROp_CodeBlock>(Block.NewNode);
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
#endif
|
||||
|
||||
for (auto [LocalNode, LocalIROp] : LocalIR.GetCode(Block.NewNode)) {
|
||||
|
||||
@@ -63,7 +63,7 @@ bool IRValidation::Run(IREmitter *IREmit) {
|
||||
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
if (!EntryBlock) {
|
||||
EntryBlock = BlockNode;
|
||||
|
||||
@@ -197,11 +197,11 @@ bool RAValidation::Run(IREmitter *IREmit) {
|
||||
|
||||
// Get the control flow graph from the validation pass
|
||||
auto ValidationPass = Manager->GetPass<IRValidation>("IRValidation");
|
||||
LOGMAN_THROW_A_FMT(ValidationPass != nullptr, "Couldn't find IRValidation pass");
|
||||
LOGMAN_THROW_AA_FMT(ValidationPass != nullptr, "Couldn't find IRValidation pass");
|
||||
|
||||
auto& OffsetToBlockMap = ValidationPass->OffsetToBlockMap;
|
||||
|
||||
LOGMAN_THROW_A_FMT(ValidationPass->EntryBlock != nullptr, "No entry point");
|
||||
LOGMAN_THROW_AA_FMT(ValidationPass->EntryBlock != nullptr, "No entry point");
|
||||
BlocksToVisit.push_front(ValidationPass->EntryBlock); // Currently only a single entry point
|
||||
|
||||
bool HadError = false;
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace {
|
||||
|
||||
class ConstrainedRAPass final : public RegisterAllocationPass {
|
||||
public:
|
||||
ConstrainedRAPass(FEXCore::IR::Pass* _CompactionPass, bool OptimizeSRA);
|
||||
ConstrainedRAPass(FEXCore::IR::Pass* _CompactionPass, bool OptimizeSRA, bool SupportsAVX);
|
||||
~ConstrainedRAPass();
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
@@ -293,6 +293,7 @@ namespace {
|
||||
RegisterGraph *Graph;
|
||||
FEXCore::IR::Pass* CompactionPass;
|
||||
bool OptimizeSRA;
|
||||
bool SupportsAVX;
|
||||
|
||||
std::vector<LiveRange> LiveRanges;
|
||||
|
||||
@@ -340,8 +341,8 @@ namespace {
|
||||
bool RunAllocateVirtualRegisters(IREmitter *IREmit);
|
||||
};
|
||||
|
||||
ConstrainedRAPass::ConstrainedRAPass(FEXCore::IR::Pass* _CompactionPass, bool _OptimizeSRA)
|
||||
: CompactionPass {_CompactionPass}, OptimizeSRA(_OptimizeSRA) {
|
||||
ConstrainedRAPass::ConstrainedRAPass(FEXCore::IR::Pass* _CompactionPass, bool _OptimizeSRA, bool _SupportsAVX)
|
||||
: CompactionPass {_CompactionPass}, OptimizeSRA(_OptimizeSRA), SupportsAVX{_SupportsAVX} {
|
||||
}
|
||||
|
||||
ConstrainedRAPass::~ConstrainedRAPass() {
|
||||
@@ -349,8 +350,8 @@ namespace {
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::AllocateRegisterSet(uint32_t RegisterCount, uint32_t ClassCount) {
|
||||
LOGMAN_THROW_A_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
|
||||
LOGMAN_THROW_A_FMT(ClassCount <= INVALID_CLASS, "Up to {} classes supported", INVALID_CLASS);
|
||||
LOGMAN_THROW_AA_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
|
||||
LOGMAN_THROW_AA_FMT(ClassCount <= INVALID_CLASS, "Up to {} classes supported", INVALID_CLASS);
|
||||
|
||||
Graph = AllocateRegisterGraph(ClassCount);
|
||||
|
||||
@@ -363,7 +364,7 @@ namespace {
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::AddRegisters(FEXCore::IR::RegisterClassType Class, uint32_t RegisterCount) {
|
||||
LOGMAN_THROW_A_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
|
||||
LOGMAN_THROW_AA_FMT(RegisterCount <= INVALID_REG, "Up to {} regs supported", INVALID_REG);
|
||||
|
||||
AllocatePhysicalRegisters(Graph, Class, RegisterCount);
|
||||
}
|
||||
@@ -396,7 +397,7 @@ namespace {
|
||||
const auto BeginID = Op->Begin.ID();
|
||||
const auto LastID = Op->Last.ID();
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Header.Op == OP_CODEBLOCK, "Block not defined by codeblock?");
|
||||
LOGMAN_THROW_AA_FMT(Op->Header.Op == OP_CODEBLOCK, "Block not defined by codeblock?");
|
||||
|
||||
LiveRange->Begin = std::min(LiveRange->Begin, BeginID);
|
||||
LiveRange->End = std::max(LiveRange->End, BeginID);
|
||||
@@ -453,7 +454,7 @@ namespace {
|
||||
|
||||
// If the destination hasn't yet been set then set it now
|
||||
if (IROp->HasDest) {
|
||||
LOGMAN_THROW_A_FMT(NodeLiveRange.Begin.Value == UINT32_MAX,
|
||||
LOGMAN_THROW_AA_FMT(NodeLiveRange.Begin.Value == UINT32_MAX,
|
||||
"Node begin already defined?");
|
||||
NodeLiveRange.Begin = Node;
|
||||
// Default to ending right where after it starts
|
||||
@@ -491,7 +492,7 @@ namespace {
|
||||
|
||||
const auto ArgNode = Arg.ID();
|
||||
auto& ArgNodeLiveRange = LiveRanges[ArgNode.Value];
|
||||
LOGMAN_THROW_A_FMT(ArgNodeLiveRange.Begin.Value != UINT32_MAX,
|
||||
LOGMAN_THROW_AA_FMT(ArgNodeLiveRange.Begin.Value != UINT32_MAX,
|
||||
"%ssa{} used by %ssa{} before defined?", ArgNode, Node);
|
||||
|
||||
const auto ArgNodeBlockID = Graph->Nodes[ArgNode.Value].Head.BlockID;
|
||||
@@ -544,48 +545,62 @@ namespace {
|
||||
|
||||
// Is an OP_STOREREGISTER eligible to write directly to the SRA reg?
|
||||
auto IsPreWritable = [](uint8_t Size, RegisterClassType StaticClass) {
|
||||
LOGMAN_THROW_A_FMT(StaticClass == GPRFixedClass || StaticClass == FPRFixedClass, "Unexpected static class {}", StaticClass);
|
||||
if (StaticClass == GPRFixedClass) {
|
||||
return Size == 8;
|
||||
} else if (StaticClass == FPRFixedClass) {
|
||||
return Size == 16;
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected static class {}", StaticClass);
|
||||
}
|
||||
return false; // Unknown
|
||||
};
|
||||
|
||||
// Is an OP_LOADREGISTER eligible to read directly from the SRA reg?
|
||||
auto IsAliasable = [](uint8_t Size, RegisterClassType StaticClass, uint32_t Offset) {
|
||||
LOGMAN_THROW_A_FMT(StaticClass == GPRFixedClass || StaticClass == FPRFixedClass, "Unexpected static class {}", StaticClass);
|
||||
if (StaticClass == GPRFixedClass) {
|
||||
// We need more meta info to support not-size-of-reg
|
||||
return (Size == 8 /*|| Size == 4*/) && ((Offset & 7) == 0);
|
||||
} else if (StaticClass == FPRFixedClass) {
|
||||
// We need more meta info to support not-size-of-reg
|
||||
return (Size == 16 /*|| Size == 8 || Size == 4*/) && ((Offset & 15) == 0);
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected static class {}", StaticClass);
|
||||
}
|
||||
return false; // Unknown
|
||||
};
|
||||
|
||||
// Get SRA Reg and Class from a Context offset
|
||||
auto GetRegAndClassFromOffset = [](uint32_t Offset) {
|
||||
auto beginGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0]);
|
||||
auto endGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[16]);
|
||||
const auto GetFPRBeginAndEnd = [this]() -> std::pair<ptrdiff_t, ptrdiff_t> {
|
||||
if (SupportsAVX) {
|
||||
return {
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[0][0]),
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.avx.data[16][0]),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]),
|
||||
offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[16][0]),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
auto beginFpr = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0]);
|
||||
auto endFpr = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[16][0]);
|
||||
// Get SRA Reg and Class from a Context offset
|
||||
const auto GetRegAndClassFromOffset = [&, this](uint32_t Offset) {
|
||||
const auto beginGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0]);
|
||||
const auto endGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[16]);
|
||||
|
||||
const auto [beginFpr, endFpr] = GetFPRBeginAndEnd();
|
||||
|
||||
LOGMAN_THROW_AA_FMT((Offset >= beginGpr && Offset < endGpr) || (Offset >= beginFpr && Offset < endFpr), "Unexpected Offset {}", Offset);
|
||||
|
||||
if (Offset >= beginGpr && Offset < endGpr) {
|
||||
auto reg = (Offset - beginGpr) / Core::CPUState::GPR_REG_SIZE;
|
||||
return PhysicalRegister(GPRFixedClass, reg);
|
||||
} else if (Offset >= beginFpr && Offset < endFpr) {
|
||||
auto reg = (Offset - beginFpr) / Core::CPUState::XMM_REG_SIZE;
|
||||
const auto size = SupportsAVX ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto reg = (Offset - beginFpr) / size;
|
||||
return PhysicalRegister(FPRFixedClass, reg);
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Offset {}", Offset);
|
||||
return PhysicalRegister::Invalid();
|
||||
}
|
||||
|
||||
return PhysicalRegister::Invalid();
|
||||
};
|
||||
|
||||
auto GprSize = Graph->Set.Classes[GPRFixedClass.Val].PhysicalCount;
|
||||
@@ -594,34 +609,37 @@ namespace {
|
||||
|
||||
// Get a StaticMap entry from context offset
|
||||
const auto GetStaticMapFromOffset = [&](uint32_t Offset) -> LiveRange** {
|
||||
auto beginGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0]);
|
||||
auto endGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[16]);
|
||||
const auto beginGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[0]);
|
||||
const auto endGpr = offsetof(FEXCore::Core::CpuStateFrame, State.gregs[16]);
|
||||
|
||||
auto beginFpr = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[0][0]);
|
||||
auto endFpr = offsetof(FEXCore::Core::CpuStateFrame, State.xmm[16][0]);
|
||||
const auto [beginFpr, endFpr] = GetFPRBeginAndEnd();
|
||||
|
||||
LOGMAN_THROW_AA_FMT((Offset >= beginGpr && Offset < endGpr) || (Offset >= beginFpr && Offset < endFpr), "Unexpected Offset {}", Offset);
|
||||
|
||||
if (Offset >= beginGpr && Offset < endGpr) {
|
||||
auto reg = (Offset - beginGpr) / Core::CPUState::GPR_REG_SIZE;
|
||||
return &StaticMaps[reg];
|
||||
} else if (Offset >= beginFpr && Offset < endFpr) {
|
||||
auto reg = (Offset - beginFpr) / Core::CPUState::XMM_REG_SIZE;
|
||||
const auto size = SupportsAVX ? Core::CPUState::XMM_AVX_REG_SIZE
|
||||
: Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
const auto reg = (Offset - beginFpr) / size;
|
||||
return &StaticMaps[GprSize + reg];
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected offset {}", Offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// Get a StaticMap entry from reg and class
|
||||
const auto GetStaticMapFromReg = [&](IR::PhysicalRegister PhyReg) -> LiveRange** {
|
||||
LOGMAN_THROW_A_FMT(PhyReg.Class == GPRFixedClass.Val || PhyReg.Class == FPRFixedClass.Val, "Unexpected Class {}", PhyReg.Class);
|
||||
|
||||
if (PhyReg.Class == GPRFixedClass.Val) {
|
||||
return &StaticMaps[PhyReg.Reg];
|
||||
} else if (PhyReg.Class == FPRFixedClass.Val) {
|
||||
return &StaticMaps[GprSize + PhyReg.Reg];
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "Unexpected Class {}", PhyReg.Class);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// First pass: Mark pre-writes
|
||||
@@ -778,7 +796,7 @@ namespace {
|
||||
|
||||
for (auto [BlockNode, BlockHeader] : IR->GetBlocks()) {
|
||||
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
LOGMAN_THROW_AA_FMT(BlockIROp->Header.Op == IR::OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
const auto BlockNodeID = IR->GetID(BlockNode);
|
||||
const auto BlockBeginID = BlockIROp->Begin.ID();
|
||||
@@ -888,7 +906,7 @@ namespace {
|
||||
};
|
||||
|
||||
// SpanStart/SpanEnd assume SSA id will fit in 24bits
|
||||
LOGMAN_THROW_A_FMT(NodeCount <= 0xff'ffff, "Block too large for Spans");
|
||||
LOGMAN_THROW_AA_FMT(NodeCount <= 0xff'ffff, "Block too large for Spans");
|
||||
|
||||
SpanStart.resize(NodeCount);
|
||||
SpanEnd.resize(NodeCount);
|
||||
@@ -924,7 +942,7 @@ namespace {
|
||||
});
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A_FMT(Active.Items[0] == 0, "Interference bug");
|
||||
LOGMAN_THROW_AA_FMT(Active.Items[0] == 0, "Interference bug");
|
||||
SpanStart.clear();
|
||||
SpanEnd.clear();
|
||||
}
|
||||
@@ -1350,7 +1368,7 @@ namespace {
|
||||
auto LastCursor = IREmit->GetWriteCursor();
|
||||
auto [CodeNode, IROp] = IR.at(SpillPointId)();
|
||||
|
||||
LOGMAN_THROW_A_FMT(IROp->HasDest, "Can't spill with no dest");
|
||||
LOGMAN_THROW_AA_FMT(IROp->HasDest, "Can't spill with no dest");
|
||||
|
||||
const auto Node = IR.GetID(CodeNode);
|
||||
RegisterNode *CurrentNode = &Graph->Nodes[Node.Value];
|
||||
@@ -1539,7 +1557,7 @@ namespace {
|
||||
return Changed;
|
||||
}
|
||||
|
||||
std::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA) {
|
||||
return std::make_unique<ConstrainedRAPass>(CompactionPass, OptimizeSRA);
|
||||
std::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass(FEXCore::IR::Pass* CompactionPass, bool OptimizeSRA, bool SupportsAVX) {
|
||||
return std::make_unique<ConstrainedRAPass>(CompactionPass, OptimizeSRA, SupportsAVX);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 405 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user