Merge branch 'master' into title-updates
|
@ -6,11 +6,8 @@
|
|||
version: 1.0.{build}-{branch}
|
||||
|
||||
# branches to build
|
||||
# Only building master (and pull requests)
|
||||
branches:
|
||||
# whitelist
|
||||
# only:
|
||||
# - master
|
||||
|
||||
# blacklist
|
||||
except:
|
||||
- gh-pages
|
||||
|
@ -23,6 +20,10 @@ skip_commits:
|
|||
- docs/*
|
||||
- LICENSE
|
||||
- README.md
|
||||
- .travis.yml
|
||||
|
||||
# Skip branches that have an open pull request.
|
||||
skip_branch_with_pr: true
|
||||
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
@ -82,11 +83,12 @@ configuration:
|
|||
# scripts to run after build
|
||||
after_build:
|
||||
- 7z a xenia-%appveyor_repo_branch%.zip LICENSE %APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia.exe %APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia.pdb
|
||||
- 7z a xenia-vfs-dump-%appveyor_repo_branch%.zip LICENSE %APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia-vfs-dump.exe %APPVEYOR_BUILD_FOLDER%\build\bin\%PLATFORM%\%CONFIGURATION%\xenia-vfs-dump.pdb
|
||||
|
||||
# to run your custom scripts instead of automatic MSBuild
|
||||
# We also compile the tests here instead of later on.
|
||||
build_script:
|
||||
- cmd: xb.bat build --config=%CONFIGURATION% --target=src\xenia-app --target=tests\xenia-cpu-ppc-tests
|
||||
- cmd: xb.bat build --config=%CONFIGURATION% --target=src\xenia-app --target=tests\xenia-cpu-ppc-tests --target=src\xenia-vfs-dump
|
||||
|
||||
# to disable automatic builds
|
||||
#build: off
|
||||
|
@ -131,6 +133,7 @@ artifacts:
|
|||
|
||||
- path: xenia-cpu-ppc-test.log
|
||||
- path: xenia-$(appveyor_repo_branch).zip
|
||||
- path: xenia-vfs-dump-$(appveyor_repo_branch).zip
|
||||
|
||||
|
||||
#---------------------------------#
|
||||
|
@ -143,7 +146,7 @@ deploy:
|
|||
- provider: Environment
|
||||
name: xenia-master
|
||||
release: xenia-$(appveyor_repo_branch)-v$(appveyor_build_version)
|
||||
artifact: xenia-$(appveyor_repo_branch).zip
|
||||
artifact: xenia-$(appveyor_repo_branch).zip,xenia-vfs-dump-$(appveyor_repo_branch).zip
|
||||
draft: false
|
||||
prerelease: true
|
||||
on:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
# IF YOU HAVE A QUESTION THAT ISN'T A BUG REPORT, GO TO http://reddit.com/r/xenia
|
||||
# IF YOU HAVE A QUESTION THAT ISN'T A BUG REPORT, GO TO https://reddit.com/r/xenia
|
||||
#
|
||||
# DO NOT CREATE ISSUES ABOUT SPECIFIC GAMES IN THIS REPOSITORY!
|
||||
# a game specific issue would be e.g. "Game X crashes after you hit a character a certain way"
|
||||
|
|
17
.travis.yml
|
@ -9,16 +9,16 @@ os:
|
|||
# - osx
|
||||
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- llvm-toolchain-xenial-6.0
|
||||
packages:
|
||||
- clang-4.0
|
||||
- llvm-4.0-dev
|
||||
- g++-5
|
||||
- clang-6.0
|
||||
- llvm-6.0-dev
|
||||
- g++-8
|
||||
- python3
|
||||
- libc++-dev
|
||||
- libc++abi-dev
|
||||
|
@ -32,10 +32,10 @@ addons:
|
|||
|
||||
matrix:
|
||||
include:
|
||||
- env: C_COMPILER=clang-4.0 CXX_COMPILER=clang++-4.0 LINT=true
|
||||
- env: C_COMPILER=clang-6.0 CXX_COMPILER=clang++-6.0 LINT=true
|
||||
sudo: false
|
||||
- env: C_COMPILER=clang-4.0 CXX_COMPILER=clang++-4.0 BUILD=true CONFIG=Debug
|
||||
- env: C_COMPILER=clang-4.0 CXX_COMPILER=clang++-4.0 BUILD=true CONFIG=Release
|
||||
- env: C_COMPILER=clang-6.0 CXX_COMPILER=clang++-6.0 BUILD=true CONFIG=Debug
|
||||
- env: C_COMPILER=clang-6.0 CXX_COMPILER=clang++-6.0 BUILD=true CONFIG=Release
|
||||
|
||||
git:
|
||||
# We handle submodules ourselves in xenia-build setup.
|
||||
|
@ -72,3 +72,4 @@ script:
|
|||
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
|
||||
# All tests (with haswell support).
|
||||
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=true
|
||||
|
||||
|
|
21
README.md
|
@ -2,11 +2,14 @@ Xenia - Xbox 360 Emulator Research Project
|
|||
==========================================
|
||||
|
||||
Xenia is an experimental emulator for the Xbox 360. For more information see the
|
||||
[main xenia website](http://xenia.jp/).
|
||||
[main xenia website](https://xenia.jp/).
|
||||
|
||||
**Interested in supporting the core contributors?
|
||||
[Xenia Project on Patreon](https://www.patreon.com/xenia_project).**
|
||||
|
||||
Come chat with us about **emulator-related topics** on [Discord](https://discord.gg/Q9mxZf9).
|
||||
For developer chat join `#dev` but stay on topic. Lurking is fine.
|
||||
Please check the [frequently asked questions](http://xenia.jp/faq/) page before
|
||||
For developer chat join `#dev` but stay on topic. Lurking is not only fine, but encouraged!
|
||||
Please check the [frequently asked questions](https://xenia.jp/faq/) page before
|
||||
asking questions. We've got jobs/lives/etc, so don't expect instant answers.
|
||||
|
||||
Discussing illegal activities will get you banned. No warnings.
|
||||
|
@ -35,7 +38,7 @@ legally purchased devices and games and information made public on the internet
|
|||
|
||||
Windows 8.1+ with Python 3.4 and [Visual Studio 2017](https://www.visualstudio.com/downloads/) and the Windows SDKs installed:
|
||||
|
||||
> git clone https://github.com/benvanik/xenia.git
|
||||
> git clone https://github.com/xenia-project/xenia.git
|
||||
> cd xenia
|
||||
> xb setup
|
||||
|
||||
|
@ -76,16 +79,16 @@ For general rules and guidelines please see [CONTRIBUTING.md](.github/CONTRIBUTI
|
|||
Fixes and optimizations are always welcome (please!), but in addition to
|
||||
that there are some major work areas still untouched:
|
||||
|
||||
* Help work through missing functionality/bugs in game [compat](https://github.com/benvanik/xenia/issues?labels=compat)
|
||||
* Add input drivers for [PS4 controllers](https://github.com/benvanik/xenia/issues/60) (or anything else)
|
||||
* Skilled with Linux? A strong contributor is needed to [help with porting](https://github.com/benvanik/xenia/labels/cross%20platform)
|
||||
* Help work through missing functionality/bugs in game [compat](https://github.com/xenia-project/xenia/issues?labels=compat)
|
||||
* Add input drivers for [PS4 controllers](https://github.com/xenia-project/xenia/issues/60) (or anything else)
|
||||
* Skilled with Linux? A strong contributor is needed to [help with porting](https://github.com/xenia-project/xenia/labels/cross%20platform)
|
||||
|
||||
See more projects [good for contributors](https://github.com/benvanik/xenia/labels/good%20first%20issue). It's a good idea to ask on Discord/the bugs before beginning work
|
||||
See more projects [good for contributors](https://github.com/xenia-project/xenia/labels/good%20first%20issue). It's a good idea to ask on Discord/the bugs before beginning work
|
||||
on something.
|
||||
|
||||
## FAQ
|
||||
|
||||
For more see the main [frequently asked questions](http://xenia.jp/faq/) page.
|
||||
For more see the main [frequently asked questions](https://xenia.jp/faq/) page.
|
||||
|
||||
### Can I get an exe?
|
||||
|
||||
|
|
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 9.0 KiB |
|
@ -0,0 +1,428 @@
|
|||
Attribution-ShareAlike 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||
License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||
License"). To the extent this Public License may be interpreted as a
|
||||
contract, You are granted the Licensed Rights in consideration of Your
|
||||
acceptance of these terms and conditions, and the Licensor grants You
|
||||
such rights in consideration of benefits the Licensor receives from
|
||||
making the Licensed Material available under these terms and
|
||||
conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. BY-SA Compatible License means a license listed at
|
||||
creativecommons.org/compatiblelicenses, approved by Creative
|
||||
Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
e. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
g. License Elements means the license attributes listed in the name
|
||||
of a Creative Commons Public License. The License Elements of this
|
||||
Public License are Attribution and ShareAlike.
|
||||
|
||||
h. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
i. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
k. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
l. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
m. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. Additional offer from the Licensor -- Adapted Material.
|
||||
Every recipient of Adapted Material from You
|
||||
automatically receives an offer from the Licensor to
|
||||
exercise the Licensed Rights in the Adapted Material
|
||||
under the conditions of the Adapter's License You apply.
|
||||
|
||||
c. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
b. ShareAlike.
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share
|
||||
Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter's License You apply must be a Creative Commons
|
||||
license with the same License Elements, this version or
|
||||
later, or a BY-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the
|
||||
Adapter's License You apply. You may satisfy this condition
|
||||
in any reasonable manner based on the medium, means, and
|
||||
context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms
|
||||
or conditions on, or apply any Effective Technological
|
||||
Measures to, Adapted Material that restrict exercise of the
|
||||
rights granted under the Adapter's License You apply.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material,
|
||||
|
||||
including for purposes of Section 3(b); and
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
/// -------------- *** -------------- ///
|
||||
/// ///
|
||||
/// Rendering Instructions ///
|
||||
/// ///
|
||||
/// -------------- *** -------------- ///
|
||||
|
||||
For those not well-versed in Blender, this document explains
|
||||
how to render the icon in any desired resolution and color.
|
||||
|
||||
To Change Resolution:
|
||||
1. Open Icon.blend in Blender.
|
||||
2. In the right tool pane, look for "Resolution" under the
|
||||
"Dimensions" settings. It should be defaulted to 512x512.
|
||||
This is where you'll change the icon output dimensions.
|
||||
3. After you've set the value to what you want, hit F12 or
|
||||
select 'Render' > 'Render Image' from the top bar. This
|
||||
will render the icon.
|
||||
4. To save, hit F3 or select 'Image' > 'Save As Image' on
|
||||
the left toward the middle of the screen.
|
||||
|
||||
To Change Colors:
|
||||
1. Open Icon.blend in Blender.
|
||||
2. The icon's colors will appear in the bottom window as
|
||||
labeled nodes. Change them by clicking on each node.
|
||||
3. Render and save as described above.
|
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 267 KiB |
|
@ -12,12 +12,12 @@ video drivers for your card.
|
|||
* Visual Studio 2017
|
||||
* Windows Universal CRT SDK
|
||||
* [Python 3.4+](https://www.python.org/downloads/)
|
||||
* You will also need the [Windows 8.1 SDK](http://msdn.microsoft.com/en-us/windows/desktop/bg162891)
|
||||
* You will also need the [Windows 8.1 SDK](https://msdn.microsoft.com/en-us/windows/desktop/bg162891)
|
||||
* (for VS2017 just click the Windows 8.1 SDK option in the Individual Components section in the Visual Studio Installer)
|
||||
|
||||
Ensure Python is in your PATH.
|
||||
|
||||
I recommend using [Cmder](http://cmder.net/) for git and command
|
||||
I recommend using [Cmder](https://cmder.net/) for git and command
|
||||
line usage.
|
||||
|
||||
#### Debugging
|
||||
|
@ -41,7 +41,7 @@ Linux support is extremely experimental and presently incomplete.
|
|||
The build script uses LLVM/Clang 3.8. GCC should also work, but is not easily
|
||||
swappable right now.
|
||||
|
||||
[CodeLite](http://codelite.org) is the IDE of choice and `xb premake` will spit
|
||||
[CodeLite](https://codelite.org) is the IDE of choice and `xb premake` will spit
|
||||
out files for that. Make also works via `xb build`.
|
||||
|
||||
To get the latest Clang on an ubuntu system:
|
||||
|
|
14
docs/cpu.md
|
@ -123,16 +123,16 @@ The CPU is largely similar to the PPC part in the PS3, so Cell documents
|
|||
often line up for the core instructions. The 360 adds some additional AltiVec
|
||||
instructions, though, which are only documented in a few places (like the gcc source code, etc).
|
||||
|
||||
* [Free60 Info](http://www.free60.org/Xenon_\(CPU\))
|
||||
* [Power ISA docs](https://www.power.org/wp-content/uploads/2012/07/PowerISA_V2.06B_V2_PUBLIC.pdf) (aka 'PowerISA')
|
||||
* [PowerPC Programming Environments Manual](https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797/$file/pem_64bit_v3.0.2005jul15.pdf) (aka 'pem_64')
|
||||
* [PowerPC Vector PEM](https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/C40E4C6133B31EE8872570B500791108/$file/vector_simd_pem_v_2.07c_26Oct2006_cell.pdf)
|
||||
* [AltiVec PEM](http://cache.freescale.com/files/32bit/doc/ref_manual/ALTIVECPEM.pdf)
|
||||
* [Free60 Info](https://free60project.github.io/wiki/Xenon_(CPU))
|
||||
* [Power ISA docs](https://web.archive.org/web/20140603115759/https://www.power.org/wp-content/uploads/2012/07/PowerISA_V2.06B_V2_PUBLIC.pdf) (aka 'PowerISA')
|
||||
* [PowerPC Programming Environments Manual](https://web.archive.org/web/20141028181028/https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797/$file/pem_64bit_v3.0.2005jul15.pdf) (aka 'pem_64')
|
||||
* [PowerPC Vector PEM](https://web.archive.org/web/20130502201029/https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/C40E4C6133B31EE8872570B500791108/$file/vector_simd_pem_v_2.07c_26Oct2006_cell.pdf)
|
||||
* [AltiVec PEM](https://web.archive.org/web/20151110180336/https://cache.freescale.com/files/32bit/doc/ref_manual/ALTIVECPEM.pdf)
|
||||
* [VMX128 Opcodes](http://biallas.net/doc/vmx128/vmx128.txt)
|
||||
* [AltiVec Decoding](https://github.com/kakaroto/ps3ida/blob/master/plugins/PPCAltivec/src/main.cpp)
|
||||
|
||||
### x64
|
||||
|
||||
* [Intel Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html)
|
||||
* [Combined Intel Manuals](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf)
|
||||
* [Intel Manuals](https://software.intel.com/en-us/articles/intel-sdm)
|
||||
* [Combined Intel Manuals](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf)
|
||||
* [Apple AltiVec/SSE Migration Guide](https://developer.apple.com/legacy/library/documentation/Performance/Conceptual/Accelerate_sse_migration/Accelerate_sse_migration.pdf)
|
||||
|
|
|
@ -31,7 +31,7 @@ waiting for a fixed 60hz timer.
|
|||
|
||||
### Vulkan
|
||||
|
||||
See the top of [src/xenia/gpu/vulkan/vulkan_gpu_flags.cc](../src/xenia/vulkan/vulkan_gpu_flags.cc).
|
||||
See the top of [src/xenia/gpu/vulkan/vulkan_gpu_flags.cc](../src/xenia/gpu/vulkan/vulkan_gpu_flags.cc).
|
||||
|
||||
`vulkan_dump_disasm=true` "Dump shader disassembly. NVIDIA only supported."
|
||||
|
||||
|
@ -121,6 +121,6 @@ PM4 commands documented at [src/xenia/gpu/xenos.h](../src/xenia/gpu/xenos.h#L521
|
|||
|
||||
### Shaders
|
||||
|
||||
* [LLVM R600 Tables](http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AMDGPU/R600Instructions.td)
|
||||
* [LLVM R600 Tables](https://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AMDGPU/R600Instructions.td)
|
||||
** The opcode formats don't match, but the name->psuedo code is correct.
|
||||
* [xemit](https://github.com/gligli/libxemit/blob/master/xemitops.c)
|
||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 24 KiB |
|
@ -58,13 +58,13 @@ think about tabs and wrapping and such.
|
|||
|
||||
To use the `xb format` auto-formatter, you need to have a `clang-format` on your
|
||||
PATH. If you're on Windows you can do this by installing an LLVM binary package
|
||||
from [the LLVM downloads page](http://llvm.org/releases/download.html). If you
|
||||
from [the LLVM downloads page](https://llvm.org/releases/download.html). If you
|
||||
install it to the default location the `xb format` command will find it
|
||||
automatically even if you don't choose to put all of LLVM onto your PATH.
|
||||
|
||||
#### Visual Studio
|
||||
|
||||
Grab the official [experimental Visual Studio plugin](http://llvm.org/builds/).
|
||||
Grab the official [experimental Visual Studio plugin](https://llvm.org/builds/).
|
||||
To switch to the Google style go Tools -> Options -> LLVM/Clang -> ClangFormat
|
||||
and set Style to Google. Then use ctrl-r/ctrl-f to trigger the formatting.
|
||||
Unfortunately it only does the cursor by default, so you'll have to select the
|
||||
|
@ -74,7 +74,7 @@ If you have a better option, let me know!
|
|||
|
||||
#### Xcode
|
||||
|
||||
Install [Alcatraz](http://alcatraz.io/) to get the [ClangFormat](https://github.com/travisjeffery/ClangFormat-Xcode)
|
||||
Install [Alcatraz](https://github.com/alcatraz/Alcatraz) to get the [ClangFormat](https://github.com/travisjeffery/ClangFormat-Xcode)
|
||||
package. Set it to use the Google style and format on save. Never think about
|
||||
tabs or linefeeds or whatever again.
|
||||
|
||||
|
|
10
premake5.lua
|
@ -128,10 +128,6 @@ filter({"platforms:Linux", "toolset:gcc"})
|
|||
end
|
||||
|
||||
filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
||||
buildoptions({
|
||||
"-std=c++14",
|
||||
"-stdlib=libstdc++",
|
||||
})
|
||||
links({
|
||||
"c++",
|
||||
"c++abi"
|
||||
|
@ -139,6 +135,11 @@ filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
|||
disablewarnings({
|
||||
"deprecated-register"
|
||||
})
|
||||
filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
|
||||
buildoptions({
|
||||
"-std=c++14",
|
||||
"-stdlib=libstdc++",
|
||||
})
|
||||
|
||||
filter("platforms:Windows")
|
||||
system("windows")
|
||||
|
@ -228,6 +229,7 @@ solution("xenia")
|
|||
include("third_party/glslang-spirv.lua")
|
||||
include("third_party/imgui.lua")
|
||||
include("third_party/libav.lua")
|
||||
include("third_party/mspack.lua")
|
||||
include("third_party/snappy.lua")
|
||||
include("third_party/spirv-tools.lua")
|
||||
include("third_party/volk.lua")
|
||||
|
|
|
@ -260,7 +260,7 @@ bool EmulatorWindow::Initialize() {
|
|||
std::bind(&EmulatorWindow::ShowHelpWebsite, this)));
|
||||
help_menu->AddChild(MenuItem::Create(
|
||||
MenuItem::Type::kString, L"&About...",
|
||||
[this]() { LaunchBrowser("http://xenia.jp/about/"); }));
|
||||
[this]() { LaunchBrowser("https://xenia.jp/about/"); }));
|
||||
}
|
||||
main_menu->AddChild(std::move(help_menu));
|
||||
|
||||
|
@ -384,7 +384,7 @@ void EmulatorWindow::ToggleFullscreen() {
|
|||
}
|
||||
}
|
||||
|
||||
void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser("http://xenia.jp"); }
|
||||
void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser("https://xenia.jp"); }
|
||||
|
||||
void EmulatorWindow::UpdateTitle() {
|
||||
std::wstring title(base_title_);
|
||||
|
|
|
@ -15,6 +15,7 @@ project("xenia-app")
|
|||
"imgui",
|
||||
"libavcodec",
|
||||
"libavutil",
|
||||
"mspack",
|
||||
"snappy",
|
||||
"spirv-tools",
|
||||
"volk",
|
||||
|
@ -41,6 +42,8 @@ project("xenia-app")
|
|||
"WinMain", -- Use WinMain instead of main.
|
||||
})
|
||||
defines({
|
||||
"XBYAK_NO_OP_NAMES",
|
||||
"XBYAK_ENABLE_OMITTED_OPERAND",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/gflags/src",
|
||||
|
@ -50,6 +53,10 @@ project("xenia-app")
|
|||
"xenia_main.cc",
|
||||
"../base/main_"..platform_suffix..".cc",
|
||||
})
|
||||
|
||||
filter("files:xenia_main.cc or ../base/main_"..platform_suffix..".cc")
|
||||
vectorextensions("IA32") -- Disable AVX for main_win.cc so our AVX check/error can happen.
|
||||
|
||||
filter("platforms:Windows")
|
||||
files({
|
||||
"main_resources.rc",
|
||||
|
|
|
@ -44,6 +44,8 @@ DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]");
|
|||
DEFINE_string(target, "", "Specifies the target .xex or .iso to execute.");
|
||||
DEFINE_bool(fullscreen, false, "Toggles fullscreen");
|
||||
|
||||
DEFINE_string(content_root, "", "Root path for content (save/etc) storage.");
|
||||
|
||||
DEFINE_bool(mount_scratch, false, "Enable scratch mount");
|
||||
DEFINE_bool(mount_cache, false, "Enable cache mount");
|
||||
|
||||
|
@ -117,11 +119,18 @@ std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
|||
drivers.emplace_back(std::move(winkey_driver));
|
||||
}
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
if (drivers.empty()) {
|
||||
// Fallback to nop if none created.
|
||||
drivers.emplace_back(xe::hid::nop::Create(window));
|
||||
}
|
||||
for (auto it = drivers.begin(); it != drivers.end();) {
|
||||
if (XFAILED((*it)->Setup())) {
|
||||
it = drivers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (drivers.empty()) {
|
||||
// Fallback to nop if none created.
|
||||
drivers.emplace_back(xe::hid::nop::Create(window));
|
||||
}
|
||||
return drivers;
|
||||
}
|
||||
|
||||
|
@ -129,8 +138,35 @@ int xenia_main(const std::vector<std::wstring>& args) {
|
|||
Profiler::Initialize();
|
||||
Profiler::ThreadEnter("main");
|
||||
|
||||
// Figure out where content should go.
|
||||
std::wstring content_root;
|
||||
if (!FLAGS_content_root.empty()) {
|
||||
content_root = xe::to_wstring(FLAGS_content_root);
|
||||
} else {
|
||||
auto base_path = xe::filesystem::GetExecutableFolder();
|
||||
base_path = xe::to_absolute_path(base_path);
|
||||
|
||||
auto portable_path = xe::join_paths(base_path, L"portable.txt");
|
||||
if (xe::filesystem::PathExists(portable_path)) {
|
||||
content_root = xe::join_paths(base_path, L"content");
|
||||
} else {
|
||||
content_root = xe::filesystem::GetUserFolder();
|
||||
#if defined(XE_PLATFORM_WIN32)
|
||||
content_root = xe::join_paths(content_root, L"Xenia");
|
||||
#elif defined(XE_PLATFORM_LINUX)
|
||||
content_root = xe::join_paths(content_root, L".xenia");
|
||||
#else
|
||||
#warning Unhandled platform for content root.
|
||||
content_root = xe::join_paths(content_root, L"Xenia");
|
||||
#endif
|
||||
content_root = xe::join_paths(content_root, L"content");
|
||||
}
|
||||
}
|
||||
content_root = xe::to_absolute_path(content_root);
|
||||
XELOGI("Content root: %S", content_root.c_str());
|
||||
|
||||
// Create the emulator but don't initialize so we can setup the window.
|
||||
auto emulator = std::make_unique<Emulator>(L"");
|
||||
auto emulator = std::make_unique<Emulator>(L"", content_root);
|
||||
|
||||
// Main emulator display window.
|
||||
auto emulator_window = EmulatorWindow::Create(emulator.get());
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
// Helpful resources:
|
||||
// https://github.com/koolkdev/libertyv/blob/master/libav_wrapper/xma2dec.c
|
||||
// http://hcs64.com/mboard/forum.php?showthread=14818
|
||||
// https://hcs64.com/mboard/forum.php?showthread=14818
|
||||
// https://github.com/hrydgard/minidx9/blob/master/Include/xma2defs.h
|
||||
|
||||
// Forward declarations
|
||||
|
@ -41,7 +41,7 @@ namespace apu {
|
|||
// We load and swap the whole thing to splat here so that we can
|
||||
// use bitfields.
|
||||
// This could be important:
|
||||
// http://www.fmod.org/questions/question/forum-15859
|
||||
// https://www.fmod.org/questions/question/forum-15859
|
||||
// Appears to be dumped in order (for the most part)
|
||||
|
||||
struct XMA_CONTEXT_DATA {
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
namespace xe {
|
||||
|
||||
// These functions are modeled off of the Apple OSAtomic routines
|
||||
// http://developer.apple.com/library/mac/#documentation/DriversKernelHardware/Reference/libkern_ref/OSAtomic_h/
|
||||
// https://developer.apple.com/documentation/kernel/osatomic_h (?)
|
||||
// Original link (dead):
|
||||
// https://developer.apple.com/library/mac/#documentation/DriversKernelHardware/Reference/libkern_ref/OSAtomic_h/
|
||||
|
||||
#if XE_PLATFORM_MAC
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) {
|
|||
std::memcpy(thread_context.xmm_registers, &ex_info->ContextRecord->Xmm0,
|
||||
sizeof(thread_context.xmm_registers));
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ms679331(v=vs.85).aspx
|
||||
// http://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx
|
||||
// https://msdn.microsoft.com/en-us/library/ms679331(v=vs.85).aspx
|
||||
// https://msdn.microsoft.com/en-us/library/aa363082(v=vs.85).aspx
|
||||
Exception ex;
|
||||
switch (ex_info->ExceptionRecord->ExceptionCode) {
|
||||
case STATUS_ILLEGAL_INSTRUCTION:
|
||||
|
|
|
@ -20,6 +20,15 @@
|
|||
namespace xe {
|
||||
namespace filesystem {
|
||||
|
||||
// Get executable path.
|
||||
std::wstring GetExecutablePath();
|
||||
|
||||
// Get executable folder.
|
||||
std::wstring GetExecutableFolder();
|
||||
|
||||
// Get user folder.
|
||||
std::wstring GetUserFolder();
|
||||
|
||||
// Canonicalizes a path, removing ..'s.
|
||||
std::string CanonicalizePath(const std::string& original_path);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
@ -21,6 +22,21 @@
|
|||
namespace xe {
|
||||
namespace filesystem {
|
||||
|
||||
std::wstring GetExecutablePath() {
|
||||
assert_always(); // IMPLEMENT ME.
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
std::wstring GetExecutableFolder() {
|
||||
assert_always(); // IMPLEMENT ME.
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
std::wstring GetUserFolder() {
|
||||
assert_always(); // IMPLEMENT ME.
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
bool PathExists(const std::wstring& path) {
|
||||
struct stat st;
|
||||
return stat(xe::to_string(path).c_str(), &st) == 0;
|
||||
|
|
|
@ -12,11 +12,33 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
#include "xenia/base/platform_win.h"
|
||||
|
||||
namespace xe {
|
||||
namespace filesystem {
|
||||
|
||||
std::wstring GetExecutablePath() {
|
||||
wchar_t* path;
|
||||
auto error = _get_wpgmptr(&path);
|
||||
return !error ? std::wstring(path) : std::wstring();
|
||||
}
|
||||
|
||||
std::wstring GetExecutableFolder() {
|
||||
auto path = GetExecutablePath();
|
||||
return xe::find_base_path(path);
|
||||
}
|
||||
|
||||
std::wstring GetUserFolder() {
|
||||
wchar_t path[MAX_PATH];
|
||||
if (!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr,
|
||||
SHGFP_TYPE_CURRENT, path))) {
|
||||
return std::wstring();
|
||||
}
|
||||
return std::wstring(path);
|
||||
}
|
||||
|
||||
bool PathExists(const std::wstring& path) {
|
||||
DWORD attrib = GetFileAttributes(path.c_str());
|
||||
return attrib != INVALID_FILE_ATTRIBUTES;
|
||||
|
|
|
@ -156,9 +156,12 @@ class Logger {
|
|||
void WriteThread() {
|
||||
RingBuffer rb(buffer_, kBufferSize);
|
||||
uint32_t idle_loops = 0;
|
||||
while (running_) {
|
||||
while (true) {
|
||||
bool did_write = false;
|
||||
rb.set_write_offset(write_tail_);
|
||||
if (!running_ && rb.empty()) {
|
||||
break;
|
||||
}
|
||||
while (!rb.empty()) {
|
||||
did_write = true;
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
#include "third_party/xbyak/xbyak/xbyak_util.h"
|
||||
|
||||
#include <bcrypt.h>
|
||||
|
||||
DEFINE_bool(win32_high_freq, true,
|
||||
|
@ -85,11 +87,6 @@ static void RequestHighPerformance() {
|
|||
int Main() {
|
||||
auto entry_info = xe::GetEntryInfo();
|
||||
|
||||
// Request high performance timing.
|
||||
if (FLAGS_win32_high_freq) {
|
||||
RequestHighPerformance();
|
||||
}
|
||||
|
||||
// Convert command line to an argv-like format so we can share code/use
|
||||
// gflags.
|
||||
auto command_line = GetCommandLineW();
|
||||
|
@ -132,10 +129,23 @@ int Main() {
|
|||
// Initialize logging. Needs parsed FLAGS.
|
||||
xe::InitializeLogging(entry_info.name);
|
||||
|
||||
Xbyak::util::Cpu cpu;
|
||||
if (!cpu.has(Xbyak::util::Cpu::tAVX)) {
|
||||
xe::FatalError(
|
||||
"Your CPU does not support AVX, which is required by Xenia. See the "
|
||||
"FAQ for system requirements at https://xenia.jp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Print version info.
|
||||
XELOGI("Build: %s / %s on %s", XE_BUILD_BRANCH, XE_BUILD_COMMIT,
|
||||
XE_BUILD_DATE);
|
||||
|
||||
// Request high performance timing.
|
||||
if (FLAGS_win32_high_freq) {
|
||||
RequestHighPerformance();
|
||||
}
|
||||
|
||||
// Call app-provided entry point.
|
||||
int result = entry_info.entry_point(args);
|
||||
|
||||
|
|
|
@ -15,9 +15,13 @@
|
|||
namespace xe {
|
||||
|
||||
// TODO(benvanik): fancy AVX versions.
|
||||
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/cb32b70b79f430456208a2cd521d028e0ece5d5b/entry/volk/kernels/volk/volk_16u_byteswap.h
|
||||
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/f2bc76cc65ffba51a141950f98e75364e49df874/entry/volk/kernels/volk/volk_32u_byteswap.h
|
||||
// http://gnuradio.org/redmine/projects/gnuradio/repository/revisions/2c4c371885c31222362f70a1cd714415d1398021/entry/volk/kernels/volk/volk_64u_byteswap.h
|
||||
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_16u_byteswap.h
|
||||
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_32u_byteswap.h
|
||||
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_64u_byteswap.h
|
||||
// Original links:
|
||||
// https://gnuradio.org/redmine/projects/gnuradio/repository/revisions/cb32b70b79f430456208a2cd521d028e0ece5d5b/entry/volk/kernels/volk/volk_16u_byteswap.h
|
||||
// https://gnuradio.org/redmine/projects/gnuradio/repository/revisions/f2bc76cc65ffba51a141950f98e75364e49df874/entry/volk/kernels/volk/volk_32u_byteswap.h
|
||||
// https://gnuradio.org/redmine/projects/gnuradio/repository/revisions/2c4c371885c31222362f70a1cd714415d1398021/entry/volk/kernels/volk/volk_64u_byteswap.h
|
||||
|
||||
void copy_128_aligned(void* dest, const void* src, size_t count) {
|
||||
std::memcpy(dest, src, count * 16);
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
// NOTE: ordering matters here as sometimes multiple flags are defined on
|
||||
// certain platforms.
|
||||
//
|
||||
// Great resource on predefined macros: http://predef.sourceforge.net/preos.html
|
||||
// Great resource on predefined macros:
|
||||
// https://sourceforge.net/p/predef/wiki/OperatingSystems/
|
||||
// Original link: https://predef.sourceforge.net/preos.html
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
|
|
|
@ -114,7 +114,7 @@ void Profiler::ThreadEnter(const char* name) {
|
|||
void Profiler::ThreadExit() { MicroProfileOnThreadExit(); }
|
||||
|
||||
bool Profiler::OnKeyDown(int key_code) {
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
switch (key_code) {
|
||||
case VK_OEM_3: // `
|
||||
MicroProfileTogglePause();
|
||||
|
|
|
@ -27,7 +27,10 @@ StringBuffer::~StringBuffer() {
|
|||
buffer_ = nullptr;
|
||||
}
|
||||
|
||||
void StringBuffer::Reset() { buffer_offset_ = 0; }
|
||||
void StringBuffer::Reset() {
|
||||
buffer_offset_ = 0;
|
||||
buffer_[0] = 0;
|
||||
}
|
||||
|
||||
void StringBuffer::Grow(size_t additional_length) {
|
||||
if (buffer_offset_ + additional_length <= buffer_capacity_) {
|
||||
|
|
|
@ -29,7 +29,7 @@ uint32_t current_thread_system_id() {
|
|||
return static_cast<uint32_t>(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
||||
#pragma pack(push, 8)
|
||||
struct THREADNAME_INFO {
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
|
|
|
@ -42,6 +42,15 @@ class X64ThunkEmitter : public X64Emitter {
|
|||
HostToGuestThunk EmitHostToGuestThunk();
|
||||
GuestToHostThunk EmitGuestToHostThunk();
|
||||
ResolveFunctionThunk EmitResolveFunctionThunk();
|
||||
|
||||
private:
|
||||
// The following four functions provide save/load functionality for registers.
|
||||
// They assume at least StackLayout::THUNK_STACK_SIZE bytes have been
|
||||
// allocated on the stack.
|
||||
void EmitSaveVolatileRegs();
|
||||
void EmitLoadVolatileRegs();
|
||||
void EmitSaveNonvolatileRegs();
|
||||
void EmitLoadNonvolatileRegs();
|
||||
};
|
||||
|
||||
X64Backend::X64Backend() : Backend(), code_cache_(nullptr) {
|
||||
|
@ -73,8 +82,6 @@ bool X64Backend::Initialize(Processor* processor) {
|
|||
return false;
|
||||
}
|
||||
|
||||
RegisterSequences();
|
||||
|
||||
// Need movbe to do advanced LOAD/STORE tricks.
|
||||
if (FLAGS_enable_haswell_instructions) {
|
||||
machine_info_.supports_extended_load_store =
|
||||
|
@ -187,7 +194,7 @@ uint64_t ReadCapstoneReg(X64Context* context, x86_reg reg) {
|
|||
#define X86_EFLAGS_SF 0x00000080 // Sign Flag
|
||||
#define X86_EFLAGS_OF 0x00000800 // Overflow Flag
|
||||
bool TestCapstoneEflags(uint32_t eflags, uint32_t insn) {
|
||||
// http://www.felixcloutier.com/x86/Jcc.html
|
||||
// https://www.felixcloutier.com/x86/Jcc.html
|
||||
switch (insn) {
|
||||
case X86_INS_JAE:
|
||||
// CF=0 && ZF=0
|
||||
|
@ -406,6 +413,117 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
|||
mov(qword[rsp + 8 * 1], rcx);
|
||||
sub(rsp, stack_size);
|
||||
|
||||
// Save nonvolatile registers.
|
||||
EmitSaveNonvolatileRegs();
|
||||
|
||||
mov(rax, rcx);
|
||||
mov(rsi, rdx); // context
|
||||
mov(rcx, r8); // return address
|
||||
call(rax);
|
||||
|
||||
EmitLoadNonvolatileRegs();
|
||||
|
||||
add(rsp, stack_size);
|
||||
mov(rcx, qword[rsp + 8 * 1]);
|
||||
mov(rdx, qword[rsp + 8 * 2]);
|
||||
mov(r8, qword[rsp + 8 * 3]);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (HostToGuestThunk)fn;
|
||||
}
|
||||
|
||||
GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
||||
// rcx = target function
|
||||
// rdx = arg0
|
||||
// r8 = arg1
|
||||
// r9 = arg2
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
// rsp + 0 = return address
|
||||
sub(rsp, stack_size);
|
||||
|
||||
// Save off volatile registers.
|
||||
EmitSaveVolatileRegs();
|
||||
|
||||
mov(rax, rcx); // function
|
||||
mov(rcx, GetContextReg()); // context
|
||||
call(rax);
|
||||
|
||||
EmitLoadVolatileRegs();
|
||||
|
||||
add(rsp, stack_size);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (GuestToHostThunk)fn;
|
||||
}
|
||||
|
||||
// X64Emitter handles actually resolving functions.
|
||||
extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
|
||||
|
||||
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
|
||||
// ebx = target PPC address
|
||||
// rcx = context
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
|
||||
// rsp + 0 = return address
|
||||
sub(rsp, stack_size);
|
||||
|
||||
// Save volatile registers
|
||||
EmitSaveVolatileRegs();
|
||||
|
||||
mov(rcx, rsi); // context
|
||||
mov(rdx, rbx);
|
||||
mov(rax, uint64_t(&ResolveFunction));
|
||||
call(rax);
|
||||
|
||||
EmitLoadVolatileRegs();
|
||||
|
||||
add(rsp, stack_size);
|
||||
jmp(rax);
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (ResolveFunctionThunk)fn;
|
||||
}
|
||||
|
||||
void X64ThunkEmitter::EmitSaveVolatileRegs() {
|
||||
// Save off volatile registers.
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rax);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rcx);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[2])], rdx);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[3])], r8);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[4])], r9);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[5])], r10);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[6])], r11);
|
||||
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[0])], xmm0);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[1])], xmm1);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[2])], xmm2);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[3])], xmm3);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[4])], xmm4);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[5])], xmm5);
|
||||
}
|
||||
|
||||
void X64ThunkEmitter::EmitLoadVolatileRegs() {
|
||||
// Load volatile registers from our stack frame.
|
||||
// movaps(xmm0, qword[rsp + offsetof(StackLayout::Thunk, xmm[0])]);
|
||||
movaps(xmm1, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
|
||||
movaps(xmm2, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
|
||||
movaps(xmm3, qword[rsp + offsetof(StackLayout::Thunk, xmm[3])]);
|
||||
movaps(xmm4, qword[rsp + offsetof(StackLayout::Thunk, xmm[4])]);
|
||||
movaps(xmm5, qword[rsp + offsetof(StackLayout::Thunk, xmm[5])]);
|
||||
|
||||
// mov(rax, qword[rsp + offsetof(StackLayout::Thunk, r[0])]);
|
||||
mov(rcx, qword[rsp + offsetof(StackLayout::Thunk, r[1])]);
|
||||
mov(rdx, qword[rsp + offsetof(StackLayout::Thunk, r[2])]);
|
||||
mov(r8, qword[rsp + offsetof(StackLayout::Thunk, r[3])]);
|
||||
mov(r9, qword[rsp + offsetof(StackLayout::Thunk, r[4])]);
|
||||
mov(r10, qword[rsp + offsetof(StackLayout::Thunk, r[5])]);
|
||||
mov(r11, qword[rsp + offsetof(StackLayout::Thunk, r[6])]);
|
||||
}
|
||||
|
||||
void X64ThunkEmitter::EmitSaveNonvolatileRegs() {
|
||||
// Preserve nonvolatile registers.
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rbx);
|
||||
mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rcx);
|
||||
|
@ -427,12 +545,9 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
|||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[7])], xmm13);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[8])], xmm14);
|
||||
movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[9])], xmm15);
|
||||
}
|
||||
|
||||
mov(rax, rcx);
|
||||
mov(rsi, rdx); // context
|
||||
mov(rcx, r8); // return address
|
||||
call(rax);
|
||||
|
||||
void X64ThunkEmitter::EmitLoadNonvolatileRegs() {
|
||||
movaps(xmm6, qword[rsp + offsetof(StackLayout::Thunk, xmm[0])]);
|
||||
movaps(xmm7, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
|
||||
movaps(xmm8, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
|
||||
|
@ -453,100 +568,6 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
|||
mov(r13, qword[rsp + offsetof(StackLayout::Thunk, r[6])]);
|
||||
mov(r14, qword[rsp + offsetof(StackLayout::Thunk, r[7])]);
|
||||
mov(r15, qword[rsp + offsetof(StackLayout::Thunk, r[8])]);
|
||||
|
||||
add(rsp, stack_size);
|
||||
mov(rcx, qword[rsp + 8 * 1]);
|
||||
mov(rdx, qword[rsp + 8 * 2]);
|
||||
mov(r8, qword[rsp + 8 * 3]);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (HostToGuestThunk)fn;
|
||||
}
|
||||
|
||||
GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
||||
// rcx = context
|
||||
// rdx = target function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
// r10 = arg2
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
// rsp + 0 = return address
|
||||
mov(qword[rsp + 8 * 2], rdx);
|
||||
mov(qword[rsp + 8 * 1], rcx);
|
||||
sub(rsp, stack_size);
|
||||
|
||||
// Save off volatile registers.
|
||||
// TODO(DrChat): Enable this when we actually need this.
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[0])], rcx);
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[1])], rdx);
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[2])], r8);
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[3])], r9);
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[4])], r10);
|
||||
// mov(qword[rsp + offsetof(StackLayout::Thunk, r[5])], r11);
|
||||
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[1])], xmm1);
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[2])], xmm2);
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[3])], xmm3);
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[4])], xmm4);
|
||||
// movaps(qword[rsp + offsetof(StackLayout::Thunk, xmm[5])], xmm5);
|
||||
|
||||
mov(rax, rdx);
|
||||
mov(rcx, rsi); // context
|
||||
mov(rdx, r8);
|
||||
mov(r8, r9);
|
||||
mov(r9, r10);
|
||||
call(rax);
|
||||
|
||||
// movaps(xmm1, qword[rsp + offsetof(StackLayout::Thunk, xmm[1])]);
|
||||
// movaps(xmm2, qword[rsp + offsetof(StackLayout::Thunk, xmm[2])]);
|
||||
// movaps(xmm3, qword[rsp + offsetof(StackLayout::Thunk, xmm[3])]);
|
||||
// movaps(xmm4, qword[rsp + offsetof(StackLayout::Thunk, xmm[4])]);
|
||||
// movaps(xmm5, qword[rsp + offsetof(StackLayout::Thunk, xmm[5])]);
|
||||
|
||||
// mov(rcx, qword[rsp + offsetof(StackLayout::Thunk, r[0])]);
|
||||
// mov(rdx, qword[rsp + offsetof(StackLayout::Thunk, r[1])]);
|
||||
// mov(r8, qword[rsp + offsetof(StackLayout::Thunk, r[2])]);
|
||||
// mov(r9, qword[rsp + offsetof(StackLayout::Thunk, r[3])]);
|
||||
// mov(r10, qword[rsp + offsetof(StackLayout::Thunk, r[4])]);
|
||||
// mov(r11, qword[rsp + offsetof(StackLayout::Thunk, r[5])]);
|
||||
|
||||
add(rsp, stack_size);
|
||||
mov(rcx, qword[rsp + 8 * 1]);
|
||||
mov(rdx, qword[rsp + 8 * 2]);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (GuestToHostThunk)fn;
|
||||
}
|
||||
|
||||
// X64Emitter handles actually resolving functions.
|
||||
extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
|
||||
|
||||
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
|
||||
// ebx = target PPC address
|
||||
// rcx = context
|
||||
|
||||
uint32_t stack_size = 0x18;
|
||||
|
||||
// rsp + 0 = return address
|
||||
mov(qword[rsp + 8 * 2], rdx);
|
||||
mov(qword[rsp + 8 * 1], rcx);
|
||||
sub(rsp, stack_size);
|
||||
|
||||
mov(rcx, rsi); // context
|
||||
mov(rdx, rbx);
|
||||
mov(rax, uint64_t(&ResolveFunction));
|
||||
call(rax);
|
||||
|
||||
add(rsp, stack_size);
|
||||
mov(rcx, qword[rsp + 8 * 1]);
|
||||
mov(rdx, qword[rsp + 8 * 2]);
|
||||
jmp(rax);
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
return (ResolveFunctionThunk)fn;
|
||||
}
|
||||
|
||||
} // namespace x64
|
||||
|
|
|
@ -174,22 +174,25 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
|||
// If we are going above the high water mark of committed memory, commit
|
||||
// some more. It's ok if multiple threads do this, as redundant commits
|
||||
// aren't harmful.
|
||||
size_t old_commit_mark = generated_code_commit_mark_;
|
||||
if (high_mark > old_commit_mark) {
|
||||
size_t new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
size_t old_commit_mark, new_commit_mark;
|
||||
do {
|
||||
old_commit_mark = generated_code_commit_mark_;
|
||||
if (high_mark <= old_commit_mark) break;
|
||||
|
||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
generated_code_commit_mark_.compare_exchange_strong(old_commit_mark,
|
||||
new_commit_mark);
|
||||
}
|
||||
} while (generated_code_commit_mark_.compare_exchange_weak(
|
||||
old_commit_mark, new_commit_mark));
|
||||
|
||||
// Copy code.
|
||||
std::memcpy(code_address, machine_code, code_size);
|
||||
|
||||
// Fill unused slots with 0xCC
|
||||
std::memset(code_address + code_size, 0xCC,
|
||||
xe::round_up(code_size, 16) - code_size);
|
||||
std::memset(
|
||||
code_address + code_size, 0xCC,
|
||||
xe::round_up(code_size + unwind_reservation.data_size, 16) - code_size);
|
||||
|
||||
// Notify subclasses of placed code.
|
||||
PlaceCode(guest_address, machine_code, code_size, stack_size, code_address,
|
||||
|
@ -247,15 +250,17 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
|||
// If we are going above the high water mark of committed memory, commit some
|
||||
// more. It's ok if multiple threads do this, as redundant commits aren't
|
||||
// harmful.
|
||||
size_t old_commit_mark = generated_code_commit_mark_;
|
||||
if (high_mark > old_commit_mark) {
|
||||
size_t new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
size_t old_commit_mark, new_commit_mark;
|
||||
do {
|
||||
old_commit_mark = generated_code_commit_mark_;
|
||||
if (high_mark <= old_commit_mark) break;
|
||||
|
||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
||||
xe::memory::AllocationType::kCommit,
|
||||
xe::memory::PageAccess::kExecuteReadWrite);
|
||||
generated_code_commit_mark_.compare_exchange_strong(old_commit_mark,
|
||||
new_commit_mark);
|
||||
}
|
||||
} while (generated_code_commit_mark_.compare_exchange_weak(old_commit_mark,
|
||||
new_commit_mark));
|
||||
|
||||
// Copy code.
|
||||
std::memcpy(data_address, data, length);
|
||||
|
|
|
@ -20,137 +20,24 @@
|
|||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
|
||||
// When enabled, this will use Windows 8 APIs to get unwind info.
|
||||
// TODO(benvanik): figure out why the callback variant doesn't work.
|
||||
#define USE_GROWABLE_FUNCTION_TABLE
|
||||
// Function pointer definitions
|
||||
typedef DWORD(NTAPI* FnRtlAddGrowableFunctionTable)(
|
||||
_Out_ PVOID* DynamicTable,
|
||||
_In_reads_(MaximumEntryCount) PRUNTIME_FUNCTION FunctionTable,
|
||||
_In_ DWORD EntryCount, _In_ DWORD MaximumEntryCount,
|
||||
_In_ ULONG_PTR RangeBase, _In_ ULONG_PTR RangeEnd);
|
||||
|
||||
typedef VOID(NTAPI* FnRtlGrowFunctionTable)(_Inout_ PVOID DynamicTable,
|
||||
_In_ DWORD NewEntryCount);
|
||||
|
||||
typedef VOID(NTAPI* FnRtlDeleteGrowableFunctionTable)(_In_ PVOID DynamicTable);
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// Size of unwind info per function.
|
||||
// TODO(benvanik): move this to emitter.
|
||||
static const uint32_t kUnwindInfoSize = 4 + (2 * 1 + 2 + 2);
|
||||
|
||||
class Win32X64CodeCache : public X64CodeCache {
|
||||
public:
|
||||
Win32X64CodeCache();
|
||||
~Win32X64CodeCache() override;
|
||||
|
||||
bool Initialize() override;
|
||||
|
||||
void* LookupUnwindInfo(uint64_t host_pc) override;
|
||||
|
||||
private:
|
||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
||||
size_t stack_size, void* code_address,
|
||||
UnwindReservation unwind_reservation) override;
|
||||
|
||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot, void* code_address,
|
||||
size_t code_size, size_t stack_size);
|
||||
|
||||
// Growable function table system handle.
|
||||
void* unwind_table_handle_ = nullptr;
|
||||
// Actual unwind table entries.
|
||||
std::vector<RUNTIME_FUNCTION> unwind_table_;
|
||||
// Current number of entries in the table.
|
||||
std::atomic<uint32_t> unwind_table_count_ = {0};
|
||||
};
|
||||
|
||||
std::unique_ptr<X64CodeCache> X64CodeCache::Create() {
|
||||
return std::make_unique<Win32X64CodeCache>();
|
||||
}
|
||||
|
||||
Win32X64CodeCache::Win32X64CodeCache() = default;
|
||||
|
||||
Win32X64CodeCache::~Win32X64CodeCache() {
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
if (unwind_table_handle_) {
|
||||
RtlDeleteGrowableFunctionTable(unwind_table_handle_);
|
||||
}
|
||||
#else
|
||||
if (generated_code_base_) {
|
||||
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3));
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
}
|
||||
|
||||
bool Win32X64CodeCache::Initialize() {
|
||||
if (!X64CodeCache::Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute total number of unwind entries we should allocate.
|
||||
// We don't support reallocing right now, so this should be high.
|
||||
unwind_table_.resize(kMaximumFunctionCount);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Create table and register with the system. It's empty now, but we'll grow
|
||||
// it as functions are added.
|
||||
if (RtlAddGrowableFunctionTable(
|
||||
&unwind_table_handle_, unwind_table_.data(), unwind_table_count_,
|
||||
DWORD(unwind_table_.size()),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_ +
|
||||
kGeneratedCodeSize))) {
|
||||
XELOGE("Unable to create unwind function table");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Install a callback that the debugger will use to lookup unwind info on
|
||||
// demand.
|
||||
if (!RtlInstallFunctionTableCallback(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3,
|
||||
reinterpret_cast<DWORD64>(generated_code_base_), kGeneratedCodeSize,
|
||||
[](uintptr_t control_pc, void* context) {
|
||||
auto code_cache = reinterpret_cast<X64CodeCache*>(context);
|
||||
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
code_cache->LookupUnwindEntry(control_pc));
|
||||
},
|
||||
this, nullptr)) {
|
||||
XELOGE("Unable to install function table callback");
|
||||
return false;
|
||||
}
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Win32X64CodeCache::UnwindReservation
|
||||
Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||
UnwindReservation unwind_reservation;
|
||||
unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_reservation.table_slot = ++unwind_table_count_;
|
||||
unwind_reservation.entry_address = entry_address;
|
||||
assert_false(unwind_table_count_ >= kMaximumFunctionCount);
|
||||
|
||||
return unwind_reservation;
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
void* code_address,
|
||||
UnwindReservation unwind_reservation) {
|
||||
// Add unwind info.
|
||||
InitializeUnwindEntry(unwind_reservation.entry_address,
|
||||
unwind_reservation.table_slot, code_address, code_size,
|
||||
stack_size);
|
||||
|
||||
#ifdef USE_GROWABLE_FUNCTION_TABLE
|
||||
// Notify that the unwind table has grown.
|
||||
// We do this outside of the lock, but with the latest total count.
|
||||
RtlGrowFunctionTable(unwind_table_handle_, unwind_table_count_);
|
||||
#endif // USE_GROWABLE_FUNCTION_TABLE
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address, code_size);
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ssa62fwe.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/ssa62fwe.aspx
|
||||
typedef enum _UNWIND_OP_CODES {
|
||||
UWOP_PUSH_NONVOL = 0, /* info == register number */
|
||||
UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
|
||||
|
@ -209,15 +96,153 @@ typedef struct _UNWIND_INFO {
|
|||
* OPTIONAL ULONG ExceptionData[]; */
|
||||
} UNWIND_INFO, *PUNWIND_INFO;
|
||||
|
||||
// Size of unwind info per function.
|
||||
// TODO(benvanik): move this to emitter.
|
||||
static const uint32_t kUnwindInfoSize =
|
||||
sizeof(UNWIND_INFO) + (sizeof(UNWIND_CODE) * (6 - 1));
|
||||
|
||||
class Win32X64CodeCache : public X64CodeCache {
|
||||
public:
|
||||
Win32X64CodeCache();
|
||||
~Win32X64CodeCache() override;
|
||||
|
||||
bool Initialize() override;
|
||||
|
||||
void* LookupUnwindInfo(uint64_t host_pc) override;
|
||||
|
||||
private:
|
||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
||||
size_t stack_size, void* code_address,
|
||||
UnwindReservation unwind_reservation) override;
|
||||
|
||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot, void* code_address,
|
||||
size_t code_size, size_t stack_size);
|
||||
|
||||
// Growable function table system handle.
|
||||
void* unwind_table_handle_ = nullptr;
|
||||
// Actual unwind table entries.
|
||||
std::vector<RUNTIME_FUNCTION> unwind_table_;
|
||||
// Current number of entries in the table.
|
||||
std::atomic<uint32_t> unwind_table_count_ = {0};
|
||||
// Does this version of Windows support growable funciton tables?
|
||||
bool supports_growable_table_ = false;
|
||||
|
||||
FnRtlAddGrowableFunctionTable add_growable_table_ = nullptr;
|
||||
FnRtlDeleteGrowableFunctionTable delete_growable_table_ = nullptr;
|
||||
FnRtlGrowFunctionTable grow_table_ = nullptr;
|
||||
};
|
||||
|
||||
std::unique_ptr<X64CodeCache> X64CodeCache::Create() {
|
||||
return std::make_unique<Win32X64CodeCache>();
|
||||
}
|
||||
|
||||
Win32X64CodeCache::Win32X64CodeCache() = default;
|
||||
|
||||
Win32X64CodeCache::~Win32X64CodeCache() {
|
||||
if (supports_growable_table_) {
|
||||
if (unwind_table_handle_) {
|
||||
delete_growable_table_(unwind_table_handle_);
|
||||
}
|
||||
} else {
|
||||
if (generated_code_base_) {
|
||||
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Win32X64CodeCache::Initialize() {
|
||||
if (!X64CodeCache::Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute total number of unwind entries we should allocate.
|
||||
// We don't support reallocing right now, so this should be high.
|
||||
unwind_table_.resize(kMaximumFunctionCount);
|
||||
|
||||
// Check if this version of Windows supports growable function tables.
|
||||
add_growable_table_ = (FnRtlAddGrowableFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlAddGrowableFunctionTable");
|
||||
delete_growable_table_ = (FnRtlDeleteGrowableFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlDeleteGrowableFunctionTable");
|
||||
grow_table_ = (FnRtlGrowFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGrowFunctionTable");
|
||||
supports_growable_table_ =
|
||||
add_growable_table_ && delete_growable_table_ && grow_table_;
|
||||
|
||||
// Create table and register with the system. It's empty now, but we'll grow
|
||||
// it as functions are added.
|
||||
if (supports_growable_table_) {
|
||||
if (add_growable_table_(&unwind_table_handle_, unwind_table_.data(),
|
||||
unwind_table_count_, DWORD(unwind_table_.size()),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_),
|
||||
reinterpret_cast<ULONG_PTR>(generated_code_base_ +
|
||||
kGeneratedCodeSize))) {
|
||||
XELOGE("Unable to create unwind function table");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Install a callback that the debugger will use to lookup unwind info on
|
||||
// demand.
|
||||
if (!RtlInstallFunctionTableCallback(
|
||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3,
|
||||
reinterpret_cast<DWORD64>(generated_code_base_), kGeneratedCodeSize,
|
||||
[](DWORD64 control_pc, PVOID context) {
|
||||
auto code_cache = reinterpret_cast<Win32X64CodeCache*>(context);
|
||||
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||
code_cache->LookupUnwindInfo(control_pc));
|
||||
},
|
||||
this, nullptr)) {
|
||||
XELOGE("Unable to install function table callback");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Win32X64CodeCache::UnwindReservation
|
||||
Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||
UnwindReservation unwind_reservation;
|
||||
unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_reservation.table_slot = ++unwind_table_count_;
|
||||
unwind_reservation.entry_address = entry_address;
|
||||
assert_false(unwind_table_count_ >= kMaximumFunctionCount);
|
||||
|
||||
return unwind_reservation;
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
void* code_address,
|
||||
UnwindReservation unwind_reservation) {
|
||||
// Add unwind info.
|
||||
InitializeUnwindEntry(unwind_reservation.entry_address,
|
||||
unwind_reservation.table_slot, code_address, code_size,
|
||||
stack_size);
|
||||
|
||||
if (supports_growable_table_) {
|
||||
// Notify that the unwind table has grown.
|
||||
// We do this outside of the lock, but with the latest total count.
|
||||
grow_table_(unwind_table_handle_, unwind_table_count_);
|
||||
}
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address, code_size);
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot,
|
||||
void* code_address,
|
||||
size_t code_size,
|
||||
size_t stack_size) {
|
||||
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
||||
UNWIND_CODE* unwind_code = nullptr;
|
||||
|
||||
if (!stack_size) {
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = 0;
|
||||
|
@ -227,44 +252,48 @@ void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
|||
} else if (stack_size <= 128) {
|
||||
uint8_t prolog_size = 4;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 1;
|
||||
unwind_info->CountOfCodes = 0;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
// https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->CodeOffset =
|
||||
14; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_SMALL;
|
||||
unwind_code.OpInfo = stack_size / 8 - 1;
|
||||
unwind_code->UnwindOp = UWOP_ALLOC_SMALL;
|
||||
unwind_code->OpInfo = stack_size / 8 - 1;
|
||||
} else {
|
||||
// TODO(benvanik): take as parameters?
|
||||
uint8_t prolog_size = 7;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 2;
|
||||
unwind_info->CountOfCodes = 0;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
size_t co = 0;
|
||||
auto& unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.CodeOffset =
|
||||
// https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->CodeOffset =
|
||||
7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code.UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code.OpInfo = 0;
|
||||
unwind_code->UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code->OpInfo = 0; // One slot for size
|
||||
|
||||
assert_true((stack_size / 8) < 65536u);
|
||||
unwind_code = unwind_info->UnwindCode[co++];
|
||||
unwind_code.FrameOffset = (USHORT)(stack_size) / 8;
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->FrameOffset = (USHORT)(stack_size) / 8;
|
||||
}
|
||||
|
||||
if (unwind_info->CountOfCodes % 1) {
|
||||
// Count of unwind codes must always be even.
|
||||
std::memset(&unwind_info->UnwindCode[unwind_info->CountOfCodes + 1], 0,
|
||||
sizeof(UNWIND_CODE));
|
||||
}
|
||||
|
||||
// Add entry.
|
||||
|
|
|
@ -56,12 +56,13 @@ static const size_t kStashOffset = 32;
|
|||
// static const size_t kStashOffsetHigh = 32 + 32;
|
||||
|
||||
const uint32_t X64Emitter::gpr_reg_map_[X64Emitter::GPR_COUNT] = {
|
||||
Xbyak::Operand::RBX, Xbyak::Operand::R12, Xbyak::Operand::R13,
|
||||
Xbyak::Operand::R14, Xbyak::Operand::R15,
|
||||
Xbyak::Operand::RBX, Xbyak::Operand::R10, Xbyak::Operand::R11,
|
||||
Xbyak::Operand::R12, Xbyak::Operand::R13, Xbyak::Operand::R14,
|
||||
Xbyak::Operand::R15,
|
||||
};
|
||||
|
||||
const uint32_t X64Emitter::xmm_reg_map_[X64Emitter::XMM_COUNT] = {
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
|
||||
X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
|
@ -81,8 +82,8 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
|||
|
||||
if (!cpu_.has(Xbyak::util::Cpu::tAVX)) {
|
||||
xe::FatalError(
|
||||
"Your CPU is too old to support Xenia. See the FAQ for system "
|
||||
"requirements at http://xenia.jp");
|
||||
"Your CPU does not support AVX, which is required by Xenia. See the "
|
||||
"FAQ for system requirements at https://xenia.jp");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -148,11 +149,13 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
for (auto it = locals.begin(); it != locals.end(); ++it) {
|
||||
auto slot = *it;
|
||||
size_t type_size = GetTypeSize(slot->type);
|
||||
|
||||
// Align to natural size.
|
||||
stack_offset = xe::align(stack_offset, type_size);
|
||||
slot->set_constant((uint32_t)stack_offset);
|
||||
stack_offset += type_size;
|
||||
}
|
||||
|
||||
// Ensure 16b alignment.
|
||||
stack_offset -= StackLayout::GUEST_STACK_SIZE;
|
||||
stack_offset = xe::align(stack_offset, static_cast<size_t>(16));
|
||||
|
@ -160,7 +163,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
// Function prolog.
|
||||
// Must be 16b aligned.
|
||||
// Windows is very strict about the form of this and the epilog:
|
||||
// http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
|
||||
// https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=vs-2017
|
||||
// IMPORTANT: any changes to the prolog must be kept in sync with
|
||||
// X64CodeCache, which dynamically generates exception information.
|
||||
// Adding or changing anything here must be matched!
|
||||
|
@ -168,6 +171,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
assert_true((stack_size + 8) % 16 == 0);
|
||||
*out_stack_size = stack_size;
|
||||
stack_size_ = stack_size;
|
||||
|
||||
sub(rsp, (uint32_t)stack_size);
|
||||
mov(qword[rsp + StackLayout::GUEST_CTX_HOME], GetContextReg());
|
||||
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rcx);
|
||||
|
@ -221,6 +225,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
const Instr* new_tail = instr;
|
||||
if (!SelectSequence(this, instr, &new_tail)) {
|
||||
// No sequence found!
|
||||
// NOTE: If you encounter this after adding a new instruction, do a full
|
||||
// rebuild!
|
||||
assert_always();
|
||||
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
|
||||
break;
|
||||
|
@ -340,13 +346,14 @@ void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
|
|||
|
||||
// This is used by the X64ThunkEmitter's ResolveFunctionThunk.
|
||||
extern "C" uint64_t ResolveFunction(void* raw_context,
|
||||
uint32_t target_address) {
|
||||
uint64_t target_address) {
|
||||
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
|
||||
|
||||
// TODO(benvanik): required?
|
||||
assert_not_zero(target_address);
|
||||
|
||||
auto fn = thread_state->processor()->ResolveFunction(target_address);
|
||||
auto fn =
|
||||
thread_state->processor()->ResolveFunction((uint32_t)target_address);
|
||||
assert_not_null(fn);
|
||||
auto x64_fn = static_cast<X64Function*>(fn);
|
||||
uint64_t addr = reinterpret_cast<uint64_t>(x64_fn->machine_code());
|
||||
|
@ -373,10 +380,7 @@ void X64Emitter::Call(const hir::Instr* instr, GuestFunction* function) {
|
|||
// Old-style resolve.
|
||||
// Not too important because indirection table is almost always available.
|
||||
// TODO: Overwrite the call-site with a straight call.
|
||||
mov(rax, reinterpret_cast<uint64_t>(ResolveFunction));
|
||||
mov(rcx, GetContextReg());
|
||||
mov(rdx, function->address());
|
||||
call(rax);
|
||||
CallNative(&ResolveFunction, function->address());
|
||||
}
|
||||
|
||||
// Actually jump/call to rax.
|
||||
|
@ -457,16 +461,15 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
|||
auto builtin_function = static_cast<const BuiltinFunction*>(function);
|
||||
if (builtin_function->handler()) {
|
||||
undefined = false;
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
mov(rcx, GetContextReg());
|
||||
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->handler()));
|
||||
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg0()));
|
||||
mov(r9, reinterpret_cast<uint64_t>(builtin_function->arg1()));
|
||||
// rcx = target function
|
||||
// rdx = arg0
|
||||
// r8 = arg1
|
||||
// r9 = arg2
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
mov(rcx, reinterpret_cast<uint64_t>(builtin_function->handler()));
|
||||
mov(rdx, reinterpret_cast<uint64_t>(builtin_function->arg0()));
|
||||
mov(r8, reinterpret_cast<uint64_t>(builtin_function->arg1()));
|
||||
call(rax);
|
||||
// rax = host return
|
||||
}
|
||||
|
@ -474,13 +477,15 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
|||
auto extern_function = static_cast<const GuestFunction*>(function);
|
||||
if (extern_function->extern_handler()) {
|
||||
undefined = false;
|
||||
// rcx = context
|
||||
// rdx = target host function
|
||||
mov(rcx, GetContextReg());
|
||||
mov(rdx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
|
||||
mov(r8, qword[GetContextReg() + offsetof(ppc::PPCContext, kernel_state)]);
|
||||
// rcx = target function
|
||||
// rdx = arg0
|
||||
// r8 = arg1
|
||||
// r9 = arg2
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
mov(rcx, reinterpret_cast<uint64_t>(extern_function->extern_handler()));
|
||||
mov(rdx,
|
||||
qword[GetContextReg() + offsetof(ppc::PPCContext, kernel_state)]);
|
||||
call(rax);
|
||||
// rax = host return
|
||||
}
|
||||
|
@ -490,42 +495,30 @@ void X64Emitter::CallExtern(const hir::Instr* instr, const Function* function) {
|
|||
}
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(void* fn) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
mov(rcx, GetContextReg());
|
||||
call(rax);
|
||||
}
|
||||
void X64Emitter::CallNative(void* fn) { CallNativeSafe(fn); }
|
||||
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context)) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
mov(rcx, GetContextReg());
|
||||
call(rax);
|
||||
CallNativeSafe(reinterpret_cast<void*>(fn));
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0)) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
mov(rcx, GetContextReg());
|
||||
call(rax);
|
||||
CallNativeSafe(reinterpret_cast<void*>(fn));
|
||||
}
|
||||
|
||||
void X64Emitter::CallNative(uint64_t (*fn)(void* raw_context, uint64_t arg0),
|
||||
uint64_t arg0) {
|
||||
mov(rax, reinterpret_cast<uint64_t>(fn));
|
||||
mov(rcx, GetContextReg());
|
||||
mov(rdx, arg0);
|
||||
call(rax);
|
||||
mov(GetNativeParam(0), arg0);
|
||||
CallNativeSafe(reinterpret_cast<void*>(fn));
|
||||
}
|
||||
|
||||
void X64Emitter::CallNativeSafe(void* fn) {
|
||||
// rcx = context
|
||||
// rdx = target function
|
||||
// r8 = arg0
|
||||
// r9 = arg1
|
||||
// r10 = arg2
|
||||
// rcx = target function
|
||||
// rdx = arg0
|
||||
// r8 = arg1
|
||||
// r9 = arg2
|
||||
auto thunk = backend()->guest_to_host_thunk();
|
||||
mov(rax, reinterpret_cast<uint64_t>(thunk));
|
||||
mov(rcx, GetContextReg());
|
||||
mov(rdx, reinterpret_cast<uint64_t>(fn));
|
||||
mov(rcx, reinterpret_cast<uint64_t>(fn));
|
||||
call(rax);
|
||||
// rax = host return
|
||||
}
|
||||
|
@ -535,6 +528,18 @@ void X64Emitter::SetReturnAddress(uint64_t value) {
|
|||
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], rax);
|
||||
}
|
||||
|
||||
Xbyak::Reg64 X64Emitter::GetNativeParam(uint32_t param) {
|
||||
if (param == 0)
|
||||
return rdx;
|
||||
else if (param == 1)
|
||||
return r8;
|
||||
else if (param == 2)
|
||||
return r9;
|
||||
|
||||
assert_always();
|
||||
return r9;
|
||||
}
|
||||
|
||||
// Important: If you change these, you must update the thunks in x64_backend.cc!
|
||||
Xbyak::Reg64 X64Emitter::GetContextReg() { return rsi; }
|
||||
Xbyak::Reg64 X64Emitter::GetMembaseReg() { return rdi; }
|
||||
|
@ -637,7 +642,7 @@ static const vec128_t xmm_consts[] = {
|
|||
/* XMMUnpackFLOAT16_2 */
|
||||
vec128i(0x0D0C0F0Eu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMPackFLOAT16_4 */
|
||||
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x05040706u, 0x01000302u),
|
||||
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x01000302u, 0x05040706u),
|
||||
/* XMMUnpackFLOAT16_4 */
|
||||
vec128i(0x09080B0Au, 0x0D0C0F0Eu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||
/* XMMPackSHORT_Min */ vec128i(0x403F8001u),
|
||||
|
@ -738,7 +743,7 @@ Xbyak::Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
|||
}
|
||||
|
||||
void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v) {
|
||||
// http://www.agner.org/optimize/optimizing_assembly.pdf
|
||||
// https://www.agner.org/optimize/optimizing_assembly.pdf
|
||||
// 13.4 Generating constants
|
||||
if (!v.low && !v.high) {
|
||||
// 0000...
|
||||
|
|
|
@ -139,13 +139,13 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
|||
std::vector<SourceMapEntry>* out_source_map);
|
||||
|
||||
public:
|
||||
// Reserved: rsp
|
||||
// Reserved: rsp, rsi, rdi
|
||||
// Scratch: rax/rcx/rdx
|
||||
// xmm0-2
|
||||
// Available: rbx, r12-r15 (save to get r8-r11, rbp, rsi, rdi?)
|
||||
// xmm6-xmm15 (save to get xmm3-xmm5)
|
||||
static const int GPR_COUNT = 5;
|
||||
static const int XMM_COUNT = 10;
|
||||
// Available: rbx, r10-r15
|
||||
// xmm4-xmm15 (save to get xmm3)
|
||||
static const int GPR_COUNT = 7;
|
||||
static const int XMM_COUNT = 12;
|
||||
|
||||
static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) {
|
||||
auto idx = gpr_reg_map_[v->reg.index];
|
||||
|
@ -187,6 +187,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
|||
void CallNativeSafe(void* fn);
|
||||
void SetReturnAddress(uint64_t value);
|
||||
|
||||
Xbyak::Reg64 GetNativeParam(uint32_t param);
|
||||
|
||||
Xbyak::Reg64 GetContextReg();
|
||||
Xbyak::Reg64 GetMembaseReg();
|
||||
void ReloadContext();
|
||||
|
|
|
@ -0,0 +1,629 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Xenia Developers. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef XENIA_CPU_BACKEND_X64_X64_OP_H_
|
||||
#define XENIA_CPU_BACKEND_X64_X64_OP_H_
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_emitter.h"
|
||||
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
// TODO(benvanik): direct usings.
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::hir;
|
||||
using namespace Xbyak;
|
||||
|
||||
// Selects the right byte/word/etc from a vector. We need to flip logical
|
||||
// indices (0,1,2,3,4,5,6,7,...) = (3,2,1,0,7,6,5,4,...)
|
||||
#define VEC128_B(n) ((n) ^ 0x3)
|
||||
#define VEC128_W(n) ((n) ^ 0x1)
|
||||
#define VEC128_D(n) (n)
|
||||
#define VEC128_F(n) (n)
|
||||
|
||||
enum KeyType {
|
||||
KEY_TYPE_X = OPCODE_SIG_TYPE_X,
|
||||
KEY_TYPE_L = OPCODE_SIG_TYPE_L,
|
||||
KEY_TYPE_O = OPCODE_SIG_TYPE_O,
|
||||
KEY_TYPE_S = OPCODE_SIG_TYPE_S,
|
||||
KEY_TYPE_V_I8 = OPCODE_SIG_TYPE_V + INT8_TYPE,
|
||||
KEY_TYPE_V_I16 = OPCODE_SIG_TYPE_V + INT16_TYPE,
|
||||
KEY_TYPE_V_I32 = OPCODE_SIG_TYPE_V + INT32_TYPE,
|
||||
KEY_TYPE_V_I64 = OPCODE_SIG_TYPE_V + INT64_TYPE,
|
||||
KEY_TYPE_V_F32 = OPCODE_SIG_TYPE_V + FLOAT32_TYPE,
|
||||
KEY_TYPE_V_F64 = OPCODE_SIG_TYPE_V + FLOAT64_TYPE,
|
||||
KEY_TYPE_V_V128 = OPCODE_SIG_TYPE_V + VEC128_TYPE,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
union InstrKey {
|
||||
struct {
|
||||
uint32_t opcode : 8;
|
||||
uint32_t dest : 5;
|
||||
uint32_t src1 : 5;
|
||||
uint32_t src2 : 5;
|
||||
uint32_t src3 : 5;
|
||||
uint32_t reserved : 4;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
operator uint32_t() const { return value; }
|
||||
|
||||
InstrKey() : value(0) {}
|
||||
InstrKey(uint32_t v) : value(v) {}
|
||||
InstrKey(const Instr* i) : value(0) {
|
||||
opcode = i->opcode->num;
|
||||
uint32_t sig = i->opcode->signature;
|
||||
dest =
|
||||
GET_OPCODE_SIG_TYPE_DEST(sig) ? OPCODE_SIG_TYPE_V + i->dest->type : 0;
|
||||
src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
|
||||
if (src1 == OPCODE_SIG_TYPE_V) {
|
||||
src1 += i->src1.value->type;
|
||||
}
|
||||
src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
|
||||
if (src2 == OPCODE_SIG_TYPE_V) {
|
||||
src2 += i->src2.value->type;
|
||||
}
|
||||
src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
|
||||
if (src3 == OPCODE_SIG_TYPE_V) {
|
||||
src3 += i->src3.value->type;
|
||||
}
|
||||
}
|
||||
|
||||
template <Opcode OPCODE, KeyType DEST = KEY_TYPE_X, KeyType SRC1 = KEY_TYPE_X,
|
||||
KeyType SRC2 = KEY_TYPE_X, KeyType SRC3 = KEY_TYPE_X>
|
||||
struct Construct {
|
||||
static const uint32_t value =
|
||||
(OPCODE) | (DEST << 8) | (SRC1 << 13) | (SRC2 << 18) | (SRC3 << 23);
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(InstrKey) <= 4, "Key must be 4 bytes");
|
||||
|
||||
template <typename... Ts>
|
||||
struct CombinedStruct;
|
||||
template <>
|
||||
struct CombinedStruct<> {};
|
||||
template <typename T, typename... Ts>
|
||||
struct CombinedStruct<T, Ts...> : T, CombinedStruct<Ts...> {};
|
||||
|
||||
struct OpBase {};
|
||||
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
struct Op : OpBase {
|
||||
static const KeyType key_type = KEY_TYPE;
|
||||
};
|
||||
|
||||
struct VoidOp : Op<VoidOp, KEY_TYPE_X> {
|
||||
protected:
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
friend struct Op;
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
friend struct I;
|
||||
void Load(const Instr::Op& op) {}
|
||||
};
|
||||
|
||||
struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
|
||||
uint64_t value;
|
||||
|
||||
protected:
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
friend struct Op;
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
friend struct I;
|
||||
void Load(const Instr::Op& op) { this->value = op.offset; }
|
||||
};
|
||||
|
||||
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
|
||||
Function* value;
|
||||
|
||||
protected:
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
friend struct Op;
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
friend struct I;
|
||||
bool Load(const Instr::Op& op) {
|
||||
this->value = op.symbol;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct LabelOp : Op<LabelOp, KEY_TYPE_L> {
|
||||
hir::Label* value;
|
||||
|
||||
protected:
|
||||
template <typename T, KeyType KEY_TYPE>
|
||||
friend struct Op;
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
friend struct I;
|
||||
void Load(const Instr::Op& op) { this->value = op.label; }
|
||||
};
|
||||
|
||||
template <typename T, KeyType KEY_TYPE, typename REG_TYPE, typename CONST_TYPE>
|
||||
struct ValueOp : Op<ValueOp<T, KEY_TYPE, REG_TYPE, CONST_TYPE>, KEY_TYPE> {
|
||||
typedef REG_TYPE reg_type;
|
||||
const Value* value;
|
||||
bool is_constant;
|
||||
virtual bool ConstantFitsIn32Reg() const { return true; }
|
||||
const REG_TYPE& reg() const {
|
||||
assert_true(!is_constant);
|
||||
return reg_;
|
||||
}
|
||||
operator const REG_TYPE&() const { return reg(); }
|
||||
bool IsEqual(const T& b) const {
|
||||
if (is_constant && b.is_constant) {
|
||||
return reinterpret_cast<const T*>(this)->constant() == b.constant();
|
||||
} else if (!is_constant && !b.is_constant) {
|
||||
return reg_.getIdx() == b.reg_.getIdx();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool IsEqual(const Xbyak::Reg& b) const {
|
||||
if (is_constant) {
|
||||
return false;
|
||||
} else if (!is_constant) {
|
||||
return reg_.getIdx() == b.getIdx();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool operator==(const T& b) const { return IsEqual(b); }
|
||||
bool operator!=(const T& b) const { return !IsEqual(b); }
|
||||
bool operator==(const Xbyak::Reg& b) const { return IsEqual(b); }
|
||||
bool operator!=(const Xbyak::Reg& b) const { return !IsEqual(b); }
|
||||
void Load(const Instr::Op& op) {
|
||||
value = op.value;
|
||||
is_constant = value->IsConstant();
|
||||
if (!is_constant) {
|
||||
X64Emitter::SetupReg(value, reg_);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
REG_TYPE reg_;
|
||||
};
|
||||
|
||||
struct I8Op : ValueOp<I8Op, KEY_TYPE_V_I8, Reg8, int8_t> {
|
||||
typedef ValueOp<I8Op, KEY_TYPE_V_I8, Reg8, int8_t> BASE;
|
||||
const int8_t constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i8;
|
||||
}
|
||||
};
|
||||
struct I16Op : ValueOp<I16Op, KEY_TYPE_V_I16, Reg16, int16_t> {
|
||||
typedef ValueOp<I16Op, KEY_TYPE_V_I16, Reg16, int16_t> BASE;
|
||||
const int16_t constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i16;
|
||||
}
|
||||
};
|
||||
struct I32Op : ValueOp<I32Op, KEY_TYPE_V_I32, Reg32, int32_t> {
|
||||
typedef ValueOp<I32Op, KEY_TYPE_V_I32, Reg32, int32_t> BASE;
|
||||
const int32_t constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i32;
|
||||
}
|
||||
};
|
||||
struct I64Op : ValueOp<I64Op, KEY_TYPE_V_I64, Reg64, int64_t> {
|
||||
typedef ValueOp<I64Op, KEY_TYPE_V_I64, Reg64, int64_t> BASE;
|
||||
const int64_t constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.i64;
|
||||
}
|
||||
bool ConstantFitsIn32Reg() const override {
|
||||
int64_t v = BASE::value->constant.i64;
|
||||
if ((v & ~0x7FFFFFFF) == 0) {
|
||||
// Fits under 31 bits, so just load using normal mov.
|
||||
return true;
|
||||
} else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
|
||||
// Negative number that fits in 32bits.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct F32Op : ValueOp<F32Op, KEY_TYPE_V_F32, Xmm, float> {
|
||||
typedef ValueOp<F32Op, KEY_TYPE_V_F32, Xmm, float> BASE;
|
||||
const float constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.f32;
|
||||
}
|
||||
};
|
||||
struct F64Op : ValueOp<F64Op, KEY_TYPE_V_F64, Xmm, double> {
|
||||
typedef ValueOp<F64Op, KEY_TYPE_V_F64, Xmm, double> BASE;
|
||||
const double constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.f64;
|
||||
}
|
||||
};
|
||||
struct V128Op : ValueOp<V128Op, KEY_TYPE_V_V128, Xmm, vec128_t> {
|
||||
typedef ValueOp<V128Op, KEY_TYPE_V_V128, Xmm, vec128_t> BASE;
|
||||
const vec128_t& constant() const {
|
||||
assert_true(BASE::is_constant);
|
||||
return BASE::value->constant.v128;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DEST, typename... Tf>
|
||||
struct DestField;
|
||||
template <typename DEST>
|
||||
struct DestField<DEST> {
|
||||
DEST dest;
|
||||
|
||||
protected:
|
||||
bool LoadDest(const Instr* i) {
|
||||
Instr::Op op;
|
||||
op.value = i->dest;
|
||||
dest.Load(op);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct DestField<VoidOp> {
|
||||
protected:
|
||||
bool LoadDest(const Instr* i) { return true; }
|
||||
};
|
||||
|
||||
template <hir::Opcode OPCODE, typename... Ts>
|
||||
struct I;
|
||||
template <hir::Opcode OPCODE, typename DEST>
|
||||
struct I<OPCODE, DEST> : DestField<DEST> {
|
||||
typedef DestField<DEST> BASE;
|
||||
static const hir::Opcode opcode = OPCODE;
|
||||
static const uint32_t key =
|
||||
InstrKey::Construct<OPCODE, DEST::key_type>::value;
|
||||
static const KeyType dest_type = DEST::key_type;
|
||||
const Instr* instr;
|
||||
|
||||
protected:
|
||||
template <typename SEQ, typename T>
|
||||
friend struct Sequence;
|
||||
bool Load(const Instr* i) {
|
||||
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
|
||||
instr = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
|
||||
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
|
||||
typedef DestField<DEST> BASE;
|
||||
static const hir::Opcode opcode = OPCODE;
|
||||
static const uint32_t key =
|
||||
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
|
||||
static const KeyType dest_type = DEST::key_type;
|
||||
static const KeyType src1_type = SRC1::key_type;
|
||||
const Instr* instr;
|
||||
SRC1 src1;
|
||||
|
||||
protected:
|
||||
template <typename SEQ, typename T>
|
||||
friend struct Sequence;
|
||||
bool Load(const Instr* i) {
|
||||
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
|
||||
instr = i;
|
||||
src1.Load(i->src1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
|
||||
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
|
||||
typedef DestField<DEST> BASE;
|
||||
static const hir::Opcode opcode = OPCODE;
|
||||
static const uint32_t key =
|
||||
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
|
||||
SRC2::key_type>::value;
|
||||
static const KeyType dest_type = DEST::key_type;
|
||||
static const KeyType src1_type = SRC1::key_type;
|
||||
static const KeyType src2_type = SRC2::key_type;
|
||||
const Instr* instr;
|
||||
SRC1 src1;
|
||||
SRC2 src2;
|
||||
|
||||
protected:
|
||||
template <typename SEQ, typename T>
|
||||
friend struct Sequence;
|
||||
bool Load(const Instr* i) {
|
||||
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
|
||||
instr = i;
|
||||
src1.Load(i->src1);
|
||||
src2.Load(i->src2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2,
|
||||
typename SRC3>
|
||||
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
||||
typedef DestField<DEST> BASE;
|
||||
static const hir::Opcode opcode = OPCODE;
|
||||
static const uint32_t key =
|
||||
InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type,
|
||||
SRC2::key_type, SRC3::key_type>::value;
|
||||
static const KeyType dest_type = DEST::key_type;
|
||||
static const KeyType src1_type = SRC1::key_type;
|
||||
static const KeyType src2_type = SRC2::key_type;
|
||||
static const KeyType src3_type = SRC3::key_type;
|
||||
const Instr* instr;
|
||||
SRC1 src1;
|
||||
SRC2 src2;
|
||||
SRC3 src3;
|
||||
|
||||
protected:
|
||||
template <typename SEQ, typename T>
|
||||
friend struct Sequence;
|
||||
bool Load(const Instr* i) {
|
||||
if (InstrKey(i).value == key && BASE::LoadDest(i)) {
|
||||
instr = i;
|
||||
src1.Load(i->src1);
|
||||
src2.Load(i->src2);
|
||||
src3.Load(i->src3);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static const T GetTempReg(X64Emitter& e);
|
||||
template <>
|
||||
const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
||||
return e.al;
|
||||
}
|
||||
template <>
|
||||
const Reg16 GetTempReg<Reg16>(X64Emitter& e) {
|
||||
return e.ax;
|
||||
}
|
||||
template <>
|
||||
const Reg32 GetTempReg<Reg32>(X64Emitter& e) {
|
||||
return e.eax;
|
||||
}
|
||||
template <>
|
||||
const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
|
||||
return e.rax;
|
||||
}
|
||||
|
||||
template <typename SEQ, typename T>
|
||||
struct Sequence {
|
||||
typedef T EmitArgType;
|
||||
|
||||
static constexpr uint32_t head_key() { return T::key; }
|
||||
|
||||
static bool Select(X64Emitter& e, const Instr* i) {
|
||||
T args;
|
||||
if (!args.Load(i)) {
|
||||
return false;
|
||||
}
|
||||
SEQ::Emit(e, args);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename REG_FN>
|
||||
static void EmitUnaryOp(X64Emitter& e, const EmitArgType& i,
|
||||
const REG_FN& reg_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
reg_fn(e, i.dest);
|
||||
} else {
|
||||
if (i.dest != i.src1) {
|
||||
e.mov(i.dest, i.src1);
|
||||
}
|
||||
reg_fn(e, i.dest);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename REG_REG_FN, typename REG_CONST_FN>
|
||||
static void EmitCommutativeBinaryOp(X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn,
|
||||
const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
if (i.src2.is_constant) {
|
||||
// Both constants.
|
||||
if (i.src1.ConstantFitsIn32Reg()) {
|
||||
e.mov(i.dest, i.src2.constant());
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
|
||||
} else if (i.src2.ConstantFitsIn32Reg()) {
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
} else {
|
||||
// src1 constant.
|
||||
if (i.dest == i.src2) {
|
||||
if (i.src1.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
|
||||
e.mov(temp, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
} else {
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
}
|
||||
}
|
||||
} else if (i.src2.is_constant) {
|
||||
if (i.dest == i.src1) {
|
||||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
} else {
|
||||
e.mov(i.dest, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, i.src1);
|
||||
}
|
||||
} else {
|
||||
if (i.dest == i.src1) {
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
} else if (i.dest == i.src2) {
|
||||
reg_reg_fn(e, i.dest, i.src1);
|
||||
} else {
|
||||
e.mov(i.dest, i.src1);
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename REG_REG_FN, typename REG_CONST_FN>
|
||||
static void EmitAssociativeBinaryOp(X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn,
|
||||
const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
assert_true(!i.src2.is_constant);
|
||||
if (i.dest == i.src2) {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2);
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
} else {
|
||||
e.mov(i.dest, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
}
|
||||
} else if (i.src2.is_constant) {
|
||||
if (i.dest == i.src1) {
|
||||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
} else {
|
||||
e.mov(i.dest, i.src1);
|
||||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i.dest == i.src1) {
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
} else if (i.dest == i.src2) {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2);
|
||||
e.mov(i.dest, i.src1);
|
||||
reg_reg_fn(e, i.dest, temp);
|
||||
} else {
|
||||
e.mov(i.dest, i.src1);
|
||||
reg_reg_fn(e, i.dest, i.src2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
static void EmitCommutativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
|
||||
const FN& fn) {
|
||||
if (i.src1.is_constant) {
|
||||
assert_true(!i.src2.is_constant);
|
||||
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
||||
fn(e, i.dest, e.xmm0, i.src2);
|
||||
} else if (i.src2.is_constant) {
|
||||
assert_true(!i.src1.is_constant);
|
||||
e.LoadConstantXmm(e.xmm0, i.src2.constant());
|
||||
fn(e, i.dest, i.src1, e.xmm0);
|
||||
} else {
|
||||
fn(e, i.dest, i.src1, i.src2);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
static void EmitAssociativeBinaryXmmOp(X64Emitter& e, const EmitArgType& i,
|
||||
const FN& fn) {
|
||||
if (i.src1.is_constant) {
|
||||
assert_true(!i.src2.is_constant);
|
||||
e.LoadConstantXmm(e.xmm0, i.src1.constant());
|
||||
fn(e, i.dest, e.xmm0, i.src2);
|
||||
} else if (i.src2.is_constant) {
|
||||
assert_true(!i.src1.is_constant);
|
||||
e.LoadConstantXmm(e.xmm0, i.src2.constant());
|
||||
fn(e, i.dest, i.src1, e.xmm0);
|
||||
} else {
|
||||
fn(e, i.dest, i.src1, i.src2);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename REG_REG_FN, typename REG_CONST_FN>
|
||||
static void EmitCommutativeCompareOp(X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn,
|
||||
const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
assert_true(!i.src2.is_constant);
|
||||
if (i.src1.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
|
||||
e.mov(temp, i.src1.constant());
|
||||
reg_reg_fn(e, i.src2, temp);
|
||||
}
|
||||
} else if (i.src2.is_constant) {
|
||||
assert_true(!i.src1.is_constant);
|
||||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.src1, temp);
|
||||
}
|
||||
} else {
|
||||
reg_reg_fn(e, i.src1, i.src2);
|
||||
}
|
||||
}
|
||||
template <typename REG_REG_FN, typename REG_CONST_FN>
|
||||
static void EmitAssociativeCompareOp(X64Emitter& e, const EmitArgType& i,
|
||||
const REG_REG_FN& reg_reg_fn,
|
||||
const REG_CONST_FN& reg_const_fn) {
|
||||
if (i.src1.is_constant) {
|
||||
assert_true(!i.src2.is_constant);
|
||||
if (i.src1.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()),
|
||||
true);
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src1)::reg_type>(e);
|
||||
e.mov(temp, i.src1.constant());
|
||||
reg_reg_fn(e, i.dest, i.src2, temp, true);
|
||||
}
|
||||
} else if (i.src2.is_constant) {
|
||||
assert_true(!i.src1.is_constant);
|
||||
if (i.src2.ConstantFitsIn32Reg()) {
|
||||
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()),
|
||||
false);
|
||||
} else {
|
||||
auto temp = GetTempReg<typename decltype(i.src2)::reg_type>(e);
|
||||
e.mov(temp, i.src2.constant());
|
||||
reg_reg_fn(e, i.dest, i.src1, temp, false);
|
||||
}
|
||||
} else {
|
||||
reg_reg_fn(e, i.dest, i.src1, i.src2, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_CPU_BACKEND_X64_X64_OP_H_
|
|
@ -0,0 +1,553 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Xenia Developers. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_sequences.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/cpu/backend/x64/x64_op.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
volatile int anchor_control = 0;
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_DEBUG_BREAK
|
||||
// ============================================================================
|
||||
struct DEBUG_BREAK : Sequence<DEBUG_BREAK, I<OPCODE_DEBUG_BREAK, VoidOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) { e.DebugBreak(); }
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK, DEBUG_BREAK);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_DEBUG_BREAK_TRUE
|
||||
// ============================================================================
|
||||
struct DEBUG_BREAK_TRUE_I8
|
||||
: Sequence<DEBUG_BREAK_TRUE_I8, I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I8Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct DEBUG_BREAK_TRUE_I16
|
||||
: Sequence<DEBUG_BREAK_TRUE_I16,
|
||||
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I16Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct DEBUG_BREAK_TRUE_I32
|
||||
: Sequence<DEBUG_BREAK_TRUE_I32,
|
||||
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct DEBUG_BREAK_TRUE_I64
|
||||
: Sequence<DEBUG_BREAK_TRUE_I64,
|
||||
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct DEBUG_BREAK_TRUE_F32
|
||||
: Sequence<DEBUG_BREAK_TRUE_F32,
|
||||
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct DEBUG_BREAK_TRUE_F64
|
||||
: Sequence<DEBUG_BREAK_TRUE_F64,
|
||||
I<OPCODE_DEBUG_BREAK_TRUE, VoidOp, F64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.DebugBreak();
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_DEBUG_BREAK_TRUE, DEBUG_BREAK_TRUE_I8,
|
||||
DEBUG_BREAK_TRUE_I16, DEBUG_BREAK_TRUE_I32,
|
||||
DEBUG_BREAK_TRUE_I64, DEBUG_BREAK_TRUE_F32,
|
||||
DEBUG_BREAK_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_TRAP
|
||||
// ============================================================================
|
||||
struct TRAP : Sequence<TRAP, I<OPCODE_TRAP, VoidOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.Trap(i.instr->flags);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_TRAP, TRAP);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_TRAP_TRUE
|
||||
// ============================================================================
|
||||
struct TRAP_TRUE_I8
|
||||
: Sequence<TRAP_TRUE_I8, I<OPCODE_TRAP_TRUE, VoidOp, I8Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct TRAP_TRUE_I16
|
||||
: Sequence<TRAP_TRUE_I16, I<OPCODE_TRAP_TRUE, VoidOp, I16Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct TRAP_TRUE_I32
|
||||
: Sequence<TRAP_TRUE_I32, I<OPCODE_TRAP_TRUE, VoidOp, I32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct TRAP_TRUE_I64
|
||||
: Sequence<TRAP_TRUE_I64, I<OPCODE_TRAP_TRUE, VoidOp, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct TRAP_TRUE_F32
|
||||
: Sequence<TRAP_TRUE_F32, I<OPCODE_TRAP_TRUE, VoidOp, F32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct TRAP_TRUE_F64
|
||||
: Sequence<TRAP_TRUE_F64, I<OPCODE_TRAP_TRUE, VoidOp, F64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Trap(i.instr->flags);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_TRAP_TRUE, TRAP_TRUE_I8, TRAP_TRUE_I16,
|
||||
TRAP_TRUE_I32, TRAP_TRUE_I64, TRAP_TRUE_F32,
|
||||
TRAP_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_CALL
|
||||
// ============================================================================
|
||||
struct CALL : Sequence<CALL, I<OPCODE_CALL, VoidOp, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src1.value->is_guest());
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src1.value));
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL, CALL);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_CALL_TRUE
|
||||
// ============================================================================
|
||||
struct CALL_TRUE_I8
|
||||
: Sequence<CALL_TRUE_I8, I<OPCODE_CALL_TRUE, VoidOp, I8Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I16
|
||||
: Sequence<CALL_TRUE_I16, I<OPCODE_CALL_TRUE, VoidOp, I16Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I32
|
||||
: Sequence<CALL_TRUE_I32, I<OPCODE_CALL_TRUE, VoidOp, I32Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_I64
|
||||
: Sequence<CALL_TRUE_I64, I<OPCODE_CALL_TRUE, VoidOp, I64Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_F32
|
||||
: Sequence<CALL_TRUE_F32, I<OPCODE_CALL_TRUE, VoidOp, F32Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_TRUE_F64
|
||||
: Sequence<CALL_TRUE_F64, I<OPCODE_CALL_TRUE, VoidOp, F64Op, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
assert_true(i.src2.value->is_guest());
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip);
|
||||
e.Call(i.instr, static_cast<GuestFunction*>(i.src2.value));
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL_TRUE, CALL_TRUE_I8, CALL_TRUE_I16,
|
||||
CALL_TRUE_I32, CALL_TRUE_I64, CALL_TRUE_F32,
|
||||
CALL_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_CALL_INDIRECT
|
||||
// ============================================================================
|
||||
struct CALL_INDIRECT
|
||||
: Sequence<CALL_INDIRECT, I<OPCODE_CALL_INDIRECT, VoidOp, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.CallIndirect(i.instr, i.src1);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT, CALL_INDIRECT);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_CALL_INDIRECT_TRUE
|
||||
// ============================================================================
|
||||
struct CALL_INDIRECT_TRUE_I8
|
||||
: Sequence<CALL_INDIRECT_TRUE_I8,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I8Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_INDIRECT_TRUE_I16
|
||||
: Sequence<CALL_INDIRECT_TRUE_I16,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I16Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_INDIRECT_TRUE_I32
|
||||
: Sequence<CALL_INDIRECT_TRUE_I32,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I32Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_INDIRECT_TRUE_I64
|
||||
: Sequence<CALL_INDIRECT_TRUE_I64,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, I64Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_INDIRECT_TRUE_F32
|
||||
: Sequence<CALL_INDIRECT_TRUE_F32,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F32Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
struct CALL_INDIRECT_TRUE_F64
|
||||
: Sequence<CALL_INDIRECT_TRUE_F64,
|
||||
I<OPCODE_CALL_INDIRECT_TRUE, VoidOp, F64Op, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
Xbyak::Label skip;
|
||||
e.jz(skip, CodeGenerator::T_NEAR);
|
||||
e.CallIndirect(i.instr, i.src2);
|
||||
e.L(skip);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL_INDIRECT_TRUE, CALL_INDIRECT_TRUE_I8,
|
||||
CALL_INDIRECT_TRUE_I16, CALL_INDIRECT_TRUE_I32,
|
||||
CALL_INDIRECT_TRUE_I64, CALL_INDIRECT_TRUE_F32,
|
||||
CALL_INDIRECT_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_CALL_EXTERN
|
||||
// ============================================================================
|
||||
struct CALL_EXTERN
|
||||
: Sequence<CALL_EXTERN, I<OPCODE_CALL_EXTERN, VoidOp, SymbolOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.CallExtern(i.instr, i.src1.value);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_CALL_EXTERN, CALL_EXTERN);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_RETURN
|
||||
// ============================================================================
|
||||
struct RETURN : Sequence<RETURN, I<OPCODE_RETURN, VoidOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// If this is the last instruction in the last block, just let us
|
||||
// fall through.
|
||||
if (i.instr->next || i.instr->block->next) {
|
||||
e.jmp(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_RETURN, RETURN);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_RETURN_TRUE
|
||||
// ============================================================================
|
||||
struct RETURN_TRUE_I8
|
||||
: Sequence<RETURN_TRUE_I8, I<OPCODE_RETURN_TRUE, VoidOp, I8Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
struct RETURN_TRUE_I16
|
||||
: Sequence<RETURN_TRUE_I16, I<OPCODE_RETURN_TRUE, VoidOp, I16Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
struct RETURN_TRUE_I32
|
||||
: Sequence<RETURN_TRUE_I32, I<OPCODE_RETURN_TRUE, VoidOp, I32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
struct RETURN_TRUE_I64
|
||||
: Sequence<RETURN_TRUE_I64, I<OPCODE_RETURN_TRUE, VoidOp, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
struct RETURN_TRUE_F32
|
||||
: Sequence<RETURN_TRUE_F32, I<OPCODE_RETURN_TRUE, VoidOp, F32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
struct RETURN_TRUE_F64
|
||||
: Sequence<RETURN_TRUE_F64, I<OPCODE_RETURN_TRUE, VoidOp, F64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jnz(e.epilog_label(), CodeGenerator::T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_RETURN_TRUE, RETURN_TRUE_I8, RETURN_TRUE_I16,
|
||||
RETURN_TRUE_I32, RETURN_TRUE_I64, RETURN_TRUE_F32,
|
||||
RETURN_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_SET_RETURN_ADDRESS
|
||||
// ============================================================================
|
||||
struct SET_RETURN_ADDRESS
|
||||
: Sequence<SET_RETURN_ADDRESS,
|
||||
I<OPCODE_SET_RETURN_ADDRESS, VoidOp, I64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.SetReturnAddress(i.src1.constant());
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_SET_RETURN_ADDRESS, SET_RETURN_ADDRESS);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_BRANCH
|
||||
// ============================================================================
|
||||
struct BRANCH : Sequence<BRANCH, I<OPCODE_BRANCH, VoidOp, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.jmp(i.src1.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH, BRANCH);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_BRANCH_TRUE
|
||||
// ============================================================================
|
||||
struct BRANCH_TRUE_I8
|
||||
: Sequence<BRANCH_TRUE_I8, I<OPCODE_BRANCH_TRUE, VoidOp, I8Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_I16
|
||||
: Sequence<BRANCH_TRUE_I16, I<OPCODE_BRANCH_TRUE, VoidOp, I16Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_I32
|
||||
: Sequence<BRANCH_TRUE_I32, I<OPCODE_BRANCH_TRUE, VoidOp, I32Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_I64
|
||||
: Sequence<BRANCH_TRUE_I64, I<OPCODE_BRANCH_TRUE, VoidOp, I64Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_F32
|
||||
: Sequence<BRANCH_TRUE_F32, I<OPCODE_BRANCH_TRUE, VoidOp, F32Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_TRUE_F64
|
||||
: Sequence<BRANCH_TRUE_F64, I<OPCODE_BRANCH_TRUE, VoidOp, F64Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jnz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_TRUE, BRANCH_TRUE_I8, BRANCH_TRUE_I16,
|
||||
BRANCH_TRUE_I32, BRANCH_TRUE_I64, BRANCH_TRUE_F32,
|
||||
BRANCH_TRUE_F64);
|
||||
|
||||
// ============================================================================
|
||||
// OPCODE_BRANCH_FALSE
|
||||
// ============================================================================
|
||||
struct BRANCH_FALSE_I8
|
||||
: Sequence<BRANCH_FALSE_I8, I<OPCODE_BRANCH_FALSE, VoidOp, I8Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I16
|
||||
: Sequence<BRANCH_FALSE_I16,
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I16Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I32
|
||||
: Sequence<BRANCH_FALSE_I32,
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I32Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_I64
|
||||
: Sequence<BRANCH_FALSE_I64,
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, I64Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.test(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_F32
|
||||
: Sequence<BRANCH_FALSE_F32,
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, F32Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
struct BRANCH_FALSE_F64
|
||||
: Sequence<BRANCH_FALSE_F64,
|
||||
I<OPCODE_BRANCH_FALSE, VoidOp, F64Op, LabelOp>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vptest(i.src1, i.src1);
|
||||
e.jz(i.src2.value->name, e.T_NEAR);
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_BRANCH_FALSE, BRANCH_FALSE_I8, BRANCH_FALSE_I16,
|
||||
BRANCH_FALSE_I32, BRANCH_FALSE_I64, BRANCH_FALSE_F32,
|
||||
BRANCH_FALSE_F64);
|
||||
|
||||
} // namespace x64
|
||||
} // namespace backend
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "xenia/cpu/hir/instr.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace backend {
|
||||
|
@ -19,7 +21,25 @@ namespace x64 {
|
|||
|
||||
class X64Emitter;
|
||||
|
||||
void RegisterSequences();
|
||||
typedef bool (*SequenceSelectFn)(X64Emitter&, const hir::Instr*);
|
||||
extern std::unordered_map<uint32_t, SequenceSelectFn> sequence_table;
|
||||
|
||||
template <typename T>
|
||||
bool Register() {
|
||||
sequence_table.insert({T::head_key(), T::Select});
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename Tn, typename... Ts>
|
||||
static bool Register() {
|
||||
bool b = true;
|
||||
b = b && Register<T>(); // Call the above function
|
||||
b = b && Register<Tn, Ts...>(); // Call ourself again (recursively)
|
||||
return b;
|
||||
}
|
||||
#define EMITTER_OPCODE_TABLE(name, ...) \
|
||||
const auto X64_INSTR_##name = Register<__VA_ARGS__>();
|
||||
|
||||
bool SelectSequence(X64Emitter* e, const hir::Instr* i,
|
||||
const hir::Instr** new_tail);
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef XENIA_CPU_COMPILER_COMPILER_PASSES_H_
|
||||
#define XENIA_CPU_COMPILER_COMPILER_PASSES_H_
|
||||
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
|
||||
#include "xenia/cpu/compiler/passes/constant_propagation_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/context_promotion_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/control_flow_analysis_pass.h"
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_pass.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/cpu/compiler/compiler.h"
|
||||
#include "xenia/cpu/ppc/ppc_context.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
// TODO(benvanik): remove when enums redefined.
|
||||
using namespace xe::cpu::hir;
|
||||
|
||||
using xe::cpu::hir::Block;
|
||||
using xe::cpu::hir::HIRBuilder;
|
||||
using xe::cpu::hir::Instr;
|
||||
using xe::cpu::hir::Value;
|
||||
|
||||
ConditionalGroupPass::ConditionalGroupPass() : CompilerPass() {}
|
||||
|
||||
ConditionalGroupPass::~ConditionalGroupPass() {}
|
||||
|
||||
bool ConditionalGroupPass::Initialize(Compiler* compiler) {
|
||||
if (!CompilerPass::Initialize(compiler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < passes_.size(); ++i) {
|
||||
auto& pass = passes_[i];
|
||||
if (!pass->Initialize(compiler)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConditionalGroupPass::Run(HIRBuilder* builder) {
|
||||
bool dirty;
|
||||
int loops = 0;
|
||||
do {
|
||||
assert_true(loops < 20); // arbitrary number
|
||||
dirty = false;
|
||||
for (size_t i = 0; i < passes_.size(); ++i) {
|
||||
scratch_arena()->Reset();
|
||||
auto& pass = passes_[i];
|
||||
auto subpass = dynamic_cast<ConditionalGroupSubpass*>(pass.get());
|
||||
if (!subpass) {
|
||||
if (!pass->Run(builder)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
bool result = false;
|
||||
if (!subpass->Run(builder, result)) {
|
||||
return false;
|
||||
}
|
||||
dirty |= result;
|
||||
}
|
||||
}
|
||||
loops++;
|
||||
} while (dirty);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConditionalGroupPass::AddPass(std::unique_ptr<CompilerPass> pass) {
|
||||
passes_.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_
|
||||
#define XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/cpu/compiler/compiler_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
class ConditionalGroupPass : public CompilerPass {
|
||||
public:
|
||||
ConditionalGroupPass();
|
||||
virtual ~ConditionalGroupPass() override;
|
||||
|
||||
bool Initialize(Compiler* compiler) override;
|
||||
|
||||
bool Run(hir::HIRBuilder* builder) override;
|
||||
|
||||
void AddPass(std::unique_ptr<CompilerPass> pass);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<CompilerPass>> passes_;
|
||||
};
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_PASS_H_
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
|
||||
|
||||
#include "xenia/cpu/compiler/compiler.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
ConditionalGroupSubpass::ConditionalGroupSubpass() : CompilerPass() {}
|
||||
|
||||
ConditionalGroupSubpass::~ConditionalGroupSubpass() = default;
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_
|
||||
#define XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_
|
||||
|
||||
#include "xenia/base/arena.h"
|
||||
#include "xenia/cpu/compiler/compiler_pass.h"
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class Processor;
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
class Compiler;
|
||||
namespace passes {
|
||||
|
||||
class ConditionalGroupSubpass : public CompilerPass {
|
||||
public:
|
||||
ConditionalGroupSubpass();
|
||||
virtual ~ConditionalGroupSubpass();
|
||||
|
||||
bool Run(hir::HIRBuilder* builder) override {
|
||||
bool dummy;
|
||||
return Run(builder, dummy);
|
||||
}
|
||||
|
||||
virtual bool Run(hir::HIRBuilder* builder, bool& result) = 0;
|
||||
};
|
||||
|
||||
} // namespace passes
|
||||
} // namespace compiler
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_CPU_COMPILER_PASSES_CONDITIONAL_GROUP_SUBPASS_H_
|
|
@ -31,11 +31,12 @@ using xe::cpu::hir::HIRBuilder;
|
|||
using xe::cpu::hir::TypeName;
|
||||
using xe::cpu::hir::Value;
|
||||
|
||||
ConstantPropagationPass::ConstantPropagationPass() : CompilerPass() {}
|
||||
ConstantPropagationPass::ConstantPropagationPass()
|
||||
: ConditionalGroupSubpass() {}
|
||||
|
||||
ConstantPropagationPass::~ConstantPropagationPass() {}
|
||||
|
||||
bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
||||
bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
|
||||
// Once ContextPromotion has run there will likely be a whole slew of
|
||||
// constants that can be pushed through the function.
|
||||
// Example:
|
||||
|
@ -63,6 +64,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
// v1 = 19
|
||||
// v2 = 0
|
||||
|
||||
result = false;
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
auto i = block->instr_head;
|
||||
|
@ -76,6 +78,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -86,6 +89,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -98,6 +102,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT:
|
||||
|
@ -109,6 +114,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
}
|
||||
i->Replace(&OPCODE_CALL_info, i->flags);
|
||||
i->src1.symbol = function;
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_CALL_INDIRECT_TRUE:
|
||||
|
@ -120,6 +126,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -132,6 +139,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_BRANCH_FALSE:
|
||||
|
@ -143,6 +151,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
} else {
|
||||
i->Remove();
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -152,6 +161,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Cast(target_type);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_CONVERT:
|
||||
|
@ -160,6 +170,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Convert(target_type, RoundMode(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_ROUND:
|
||||
|
@ -167,6 +178,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Round(RoundMode(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_ZERO_EXTEND:
|
||||
|
@ -175,6 +187,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->ZeroExtend(target_type);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SIGN_EXTEND:
|
||||
|
@ -183,6 +196,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->SignExtend(target_type);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_TRUNCATE:
|
||||
|
@ -191,6 +205,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Truncate(target_type);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -210,6 +225,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->Replace(&OPCODE_LOAD_MMIO_info, 0);
|
||||
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
|
||||
i->src2.offset = address;
|
||||
result = true;
|
||||
} else {
|
||||
auto heap = memory->LookupHeap(address);
|
||||
uint32_t protect;
|
||||
|
@ -222,18 +238,22 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
case INT8_TYPE:
|
||||
v->set_constant(xe::load<uint8_t>(host_addr));
|
||||
i->Remove();
|
||||
result = true;
|
||||
break;
|
||||
case INT16_TYPE:
|
||||
v->set_constant(xe::load<uint16_t>(host_addr));
|
||||
i->Remove();
|
||||
result = true;
|
||||
break;
|
||||
case INT32_TYPE:
|
||||
v->set_constant(xe::load<uint32_t>(host_addr));
|
||||
i->Remove();
|
||||
result = true;
|
||||
break;
|
||||
case INT64_TYPE:
|
||||
v->set_constant(xe::load<uint64_t>(host_addr));
|
||||
i->Remove();
|
||||
result = true;
|
||||
break;
|
||||
case VEC128_TYPE:
|
||||
vec128_t val;
|
||||
|
@ -241,6 +261,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
val.high = xe::load<uint64_t>(host_addr + 8);
|
||||
v->set_constant(val);
|
||||
i->Remove();
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(v->type);
|
||||
|
@ -270,6 +291,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
|
||||
i->src2.offset = address;
|
||||
i->set_src3(value);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -278,11 +300,15 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
if (i->src1.value->IsConstant()) {
|
||||
if (i->src1.value->type != VEC128_TYPE) {
|
||||
if (i->src1.value->IsConstantTrue()) {
|
||||
v->set_from(i->src2.value);
|
||||
i->Remove();
|
||||
auto src2 = i->src2.value;
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src2);
|
||||
result = true;
|
||||
} else if (i->src1.value->IsConstantFalse()) {
|
||||
v->set_from(i->src3.value);
|
||||
i->Remove();
|
||||
auto src3 = i->src3.value;
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src3);
|
||||
result = true;
|
||||
} else if (i->src2.value->IsConstant() &&
|
||||
i->src3.value->IsConstant()) {
|
||||
// TODO: Select
|
||||
|
@ -303,6 +329,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_constant(uint8_t(0));
|
||||
}
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_IS_FALSE:
|
||||
|
@ -313,6 +340,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_constant(uint8_t(0));
|
||||
}
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_IS_NAN:
|
||||
|
@ -327,6 +355,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_constant(uint8_t(0));
|
||||
}
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -336,6 +365,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantEQ(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_NE:
|
||||
|
@ -343,6 +373,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantNE(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SLT:
|
||||
|
@ -350,6 +381,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantSLT(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SLE:
|
||||
|
@ -357,6 +389,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantSLE(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGT:
|
||||
|
@ -364,6 +397,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantSGT(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_SGE:
|
||||
|
@ -371,6 +405,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantSGE(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULT:
|
||||
|
@ -378,6 +413,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantULT(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_ULE:
|
||||
|
@ -385,6 +421,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantULE(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGT:
|
||||
|
@ -392,6 +429,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantUGT(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_COMPARE_UGE:
|
||||
|
@ -399,6 +437,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
bool value = i->src1.value->IsConstantUGE(i->src2.value);
|
||||
i->dest->set_constant(uint8_t(value));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -411,6 +450,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Add(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_ADD_CARRY:
|
||||
|
@ -431,6 +471,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->set_src1(ca);
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SUB:
|
||||
|
@ -438,6 +479,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Sub(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_MUL:
|
||||
|
@ -445,6 +487,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Mul(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else if (i->src1.value->IsConstant() ||
|
||||
i->src2.value->IsConstant()) {
|
||||
// Reorder the sources to make things simpler.
|
||||
|
@ -458,12 +501,14 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
if (s2->type != VEC128_TYPE && s2->IsConstantOne()) {
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(s1);
|
||||
result = true;
|
||||
} else if (s2->type == VEC128_TYPE) {
|
||||
auto& c = s2->constant;
|
||||
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
|
||||
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(s1);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -473,6 +518,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->MulHi(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_DIV:
|
||||
|
@ -480,6 +526,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Div(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else if (i->src2.value->IsConstant()) {
|
||||
// Division by one = no-op.
|
||||
Value* src1 = i->src1.value;
|
||||
|
@ -487,12 +534,14 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->src2.value->IsConstantOne()) {
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src1);
|
||||
result = true;
|
||||
} else if (i->src2.value->type == VEC128_TYPE) {
|
||||
auto& c = i->src2.value->constant;
|
||||
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
|
||||
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src1);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -503,6 +552,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
Value::MulAdd(v, i->src1.value, i->src2.value, i->src3.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else {
|
||||
// Multiply part is constant.
|
||||
Value* mul = builder->AllocValue();
|
||||
|
@ -513,6 +563,8 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->Replace(&OPCODE_ADD_info, 0);
|
||||
i->set_src1(mul);
|
||||
i->set_src2(add);
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -523,6 +575,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
Value::MulSub(v, i->src1.value, i->src2.value, i->src3.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else {
|
||||
// Multiply part is constant.
|
||||
Value* mul = builder->AllocValue();
|
||||
|
@ -533,6 +586,8 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
i->Replace(&OPCODE_SUB_info, 0);
|
||||
i->set_src1(mul);
|
||||
i->set_src2(add);
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -541,6 +596,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Max(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_NEG:
|
||||
|
@ -548,6 +604,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Neg();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_ABS:
|
||||
|
@ -555,6 +612,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Abs();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SQRT:
|
||||
|
@ -562,6 +620,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Sqrt();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_RSQRT:
|
||||
|
@ -569,6 +628,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->RSqrt();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_RECIP:
|
||||
|
@ -576,6 +636,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Recip();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_AND:
|
||||
|
@ -583,6 +644,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->And(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_OR:
|
||||
|
@ -590,6 +652,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Or(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_XOR:
|
||||
|
@ -597,11 +660,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Xor(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else if (!i->src1.value->IsConstant() &&
|
||||
!i->src2.value->IsConstant() &&
|
||||
i->src1.value == i->src2.value) {
|
||||
v->set_zero(v->type);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_NOT:
|
||||
|
@ -609,6 +674,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Not();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHL:
|
||||
|
@ -616,6 +682,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Shl(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else if (i->src2.value->IsConstantZero()) {
|
||||
auto src1 = i->src1.value;
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src1);
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHR:
|
||||
|
@ -623,6 +695,12 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Shr(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
} else if (i->src2.value->IsConstantZero()) {
|
||||
auto src1 = i->src1.value;
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(src1);
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SHA:
|
||||
|
@ -630,6 +708,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->Sha(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): ROTATE_LEFT
|
||||
|
@ -638,6 +717,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->ByteSwap();
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_CNTLZ:
|
||||
|
@ -645,6 +725,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_zero(v->type);
|
||||
v->CountLeadingZeros(i->src1.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
// TODO(benvanik): INSERT/EXTRACT
|
||||
|
@ -654,6 +735,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_zero(v->type);
|
||||
v->Extract(i->src1.value, i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_SPLAT:
|
||||
|
@ -661,6 +743,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_zero(v->type);
|
||||
v->Splat(i->src1.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_COMPARE_EQ:
|
||||
|
@ -668,6 +751,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorCompareEQ(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_COMPARE_SGT:
|
||||
|
@ -675,6 +759,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorCompareSGT(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_COMPARE_SGE:
|
||||
|
@ -682,6 +767,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorCompareSGE(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_COMPARE_UGT:
|
||||
|
@ -689,6 +775,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorCompareUGT(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_COMPARE_UGE:
|
||||
|
@ -696,6 +783,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorCompareUGE(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_CONVERT_F2I:
|
||||
|
@ -704,6 +792,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->VectorConvertF2I(i->src1.value,
|
||||
!!(i->flags & ARITHMETIC_UNSIGNED));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_CONVERT_I2F:
|
||||
|
@ -712,6 +801,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->VectorConvertI2F(i->src1.value,
|
||||
!!(i->flags & ARITHMETIC_UNSIGNED));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_SHL:
|
||||
|
@ -719,6 +809,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorShl(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_SHR:
|
||||
|
@ -726,6 +817,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorShr(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_ROTATE_LEFT:
|
||||
|
@ -733,6 +825,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->VectorRol(i->src2.value, hir::TypeName(i->flags));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_ADD:
|
||||
|
@ -743,6 +836,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
||||
!!(arith_flags & ARITHMETIC_SATURATE));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case OPCODE_VECTOR_SUB:
|
||||
|
@ -753,6 +847,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
||||
!!(arith_flags & ARITHMETIC_SATURATE));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -761,6 +856,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->DotProduct3(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -769,6 +865,19 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) {
|
|||
v->set_from(i->src1.value);
|
||||
v->DotProduct4(i->src2.value);
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_VECTOR_AVERAGE:
|
||||
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
||||
v->set_from(i->src1.value);
|
||||
uint32_t arith_flags = i->flags >> 8;
|
||||
v->VectorAverage(i->src2.value, hir::TypeName(i->flags & 0xFF),
|
||||
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
||||
!!(arith_flags & ARITHMETIC_SATURATE));
|
||||
i->Remove();
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -10,19 +10,19 @@
|
|||
#ifndef XENIA_CPU_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||
#define XENIA_CPU_COMPILER_PASSES_CONSTANT_PROPAGATION_PASS_H_
|
||||
|
||||
#include "xenia/cpu/compiler/compiler_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
class ConstantPropagationPass : public CompilerPass {
|
||||
class ConstantPropagationPass : public ConditionalGroupSubpass {
|
||||
public:
|
||||
ConstantPropagationPass();
|
||||
~ConstantPropagationPass() override;
|
||||
|
||||
bool Run(hir::HIRBuilder* builder) override;
|
||||
bool Run(hir::HIRBuilder* builder, bool& result) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
@ -489,7 +489,7 @@ int CompareValueUse(const Value::Use* a, const Value::Use* b) {
|
|||
} // namespace
|
||||
void RegisterAllocationPass::SortUsageList(Value* value) {
|
||||
// Modified in-place linked list sort from:
|
||||
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c
|
||||
// https://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c
|
||||
if (!value->use_head) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,17 +23,18 @@ using xe::cpu::hir::HIRBuilder;
|
|||
using xe::cpu::hir::Instr;
|
||||
using xe::cpu::hir::Value;
|
||||
|
||||
SimplificationPass::SimplificationPass() : CompilerPass() {}
|
||||
SimplificationPass::SimplificationPass() : ConditionalGroupSubpass() {}
|
||||
|
||||
SimplificationPass::~SimplificationPass() {}
|
||||
|
||||
bool SimplificationPass::Run(HIRBuilder* builder) {
|
||||
EliminateConversions(builder);
|
||||
SimplifyAssignments(builder);
|
||||
bool SimplificationPass::Run(HIRBuilder* builder, bool& result) {
|
||||
result = false;
|
||||
result |= EliminateConversions(builder);
|
||||
result |= SimplifyAssignments(builder);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
|
||||
bool SimplificationPass::EliminateConversions(HIRBuilder* builder) {
|
||||
// First, we check for truncates/extensions that can be skipped.
|
||||
// This generates some assignments which then the second step will clean up.
|
||||
// Both zero/sign extends can be skipped:
|
||||
|
@ -43,6 +44,7 @@ void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
|
|||
// v1.i64 = zero/sign_extend v0.i32 (may be dead code removed later)
|
||||
// v2.i32 = v0.i32
|
||||
|
||||
bool result = false;
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
auto i = block->instr_head;
|
||||
|
@ -51,20 +53,21 @@ void SimplificationPass::EliminateConversions(HIRBuilder* builder) {
|
|||
// back to definition).
|
||||
if (i->opcode == &OPCODE_TRUNCATE_info) {
|
||||
// Matches zero/sign_extend + truncate.
|
||||
CheckTruncate(i);
|
||||
result |= CheckTruncate(i);
|
||||
} else if (i->opcode == &OPCODE_BYTE_SWAP_info) {
|
||||
// Matches byte swap + byte swap.
|
||||
// This is pretty rare within the same basic block, but is in the
|
||||
// memcpy hot path and (probably) worth it. Maybe.
|
||||
CheckByteSwap(i);
|
||||
result |= CheckByteSwap(i);
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SimplificationPass::CheckTruncate(Instr* i) {
|
||||
bool SimplificationPass::CheckTruncate(Instr* i) {
|
||||
// Walk backward up src's chain looking for an extend. We may have
|
||||
// assigns, so skip those.
|
||||
auto src = i->src1.value;
|
||||
|
@ -80,6 +83,7 @@ void SimplificationPass::CheckTruncate(Instr* i) {
|
|||
// Types match, use original by turning this into an assign.
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(def->src1.value);
|
||||
return true;
|
||||
}
|
||||
} else if (def->opcode == &OPCODE_ZERO_EXTEND_info) {
|
||||
// Value comes from a zero extend.
|
||||
|
@ -87,12 +91,14 @@ void SimplificationPass::CheckTruncate(Instr* i) {
|
|||
// Types match, use original by turning this into an assign.
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(def->src1.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimplificationPass::CheckByteSwap(Instr* i) {
|
||||
bool SimplificationPass::CheckByteSwap(Instr* i) {
|
||||
// Walk backward up src's chain looking for a byte swap. We may have
|
||||
// assigns, so skip those.
|
||||
auto src = i->src1.value;
|
||||
|
@ -107,11 +113,13 @@ void SimplificationPass::CheckByteSwap(Instr* i) {
|
|||
// Types match, use original by turning this into an assign.
|
||||
i->Replace(&OPCODE_ASSIGN_info, 0);
|
||||
i->set_src1(def->src1.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
|
||||
bool SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
|
||||
// Run over the instructions and rename assigned variables:
|
||||
// v1 = v0
|
||||
// v2 = v1
|
||||
|
@ -129,27 +137,35 @@ void SimplificationPass::SimplifyAssignments(HIRBuilder* builder) {
|
|||
// of that instr. Because we may have chains, we do this recursively until
|
||||
// we find a non-assign def.
|
||||
|
||||
bool result = false;
|
||||
auto block = builder->first_block();
|
||||
while (block) {
|
||||
auto i = block->instr_head;
|
||||
while (i) {
|
||||
uint32_t signature = i->opcode->signature;
|
||||
if (GET_OPCODE_SIG_TYPE_SRC1(signature) == OPCODE_SIG_TYPE_V) {
|
||||
i->set_src1(CheckValue(i->src1.value));
|
||||
bool modified = false;
|
||||
i->set_src1(CheckValue(i->src1.value, modified));
|
||||
result |= modified;
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC2(signature) == OPCODE_SIG_TYPE_V) {
|
||||
i->set_src2(CheckValue(i->src2.value));
|
||||
bool modified = false;
|
||||
i->set_src2(CheckValue(i->src2.value, modified));
|
||||
result |= modified;
|
||||
}
|
||||
if (GET_OPCODE_SIG_TYPE_SRC3(signature) == OPCODE_SIG_TYPE_V) {
|
||||
i->set_src3(CheckValue(i->src3.value));
|
||||
bool modified = false;
|
||||
i->set_src3(CheckValue(i->src3.value, modified));
|
||||
result |= modified;
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
block = block->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Value* SimplificationPass::CheckValue(Value* value) {
|
||||
Value* SimplificationPass::CheckValue(Value* value, bool& result) {
|
||||
auto def = value->def;
|
||||
if (def && def->opcode == &OPCODE_ASSIGN_info) {
|
||||
// Value comes from an assignment - recursively find if it comes from
|
||||
|
@ -162,8 +178,10 @@ Value* SimplificationPass::CheckValue(Value* value) {
|
|||
}
|
||||
replacement = def->src1.value;
|
||||
}
|
||||
result = true;
|
||||
return replacement;
|
||||
}
|
||||
result = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,27 +10,27 @@
|
|||
#ifndef XENIA_CPU_COMPILER_PASSES_SIMPLIFICATION_PASS_H_
|
||||
#define XENIA_CPU_COMPILER_PASSES_SIMPLIFICATION_PASS_H_
|
||||
|
||||
#include "xenia/cpu/compiler/compiler_pass.h"
|
||||
#include "xenia/cpu/compiler/passes/conditional_group_subpass.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace compiler {
|
||||
namespace passes {
|
||||
|
||||
class SimplificationPass : public CompilerPass {
|
||||
class SimplificationPass : public ConditionalGroupSubpass {
|
||||
public:
|
||||
SimplificationPass();
|
||||
~SimplificationPass() override;
|
||||
|
||||
bool Run(hir::HIRBuilder* builder) override;
|
||||
bool Run(hir::HIRBuilder* builder, bool& result) override;
|
||||
|
||||
private:
|
||||
void EliminateConversions(hir::HIRBuilder* builder);
|
||||
void CheckTruncate(hir::Instr* i);
|
||||
void CheckByteSwap(hir::Instr* i);
|
||||
bool EliminateConversions(hir::HIRBuilder* builder);
|
||||
bool CheckTruncate(hir::Instr* i);
|
||||
bool CheckByteSwap(hir::Instr* i);
|
||||
|
||||
void SimplifyAssignments(hir::HIRBuilder* builder);
|
||||
hir::Value* CheckValue(hir::Value* value);
|
||||
bool SimplifyAssignments(hir::HIRBuilder* builder);
|
||||
hir::Value* CheckValue(hir::Value* value, bool& result);
|
||||
};
|
||||
|
||||
} // namespace passes
|
||||
|
|
|
@ -19,9 +19,32 @@
|
|||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
enum class ExportCategory : uint8_t {
|
||||
kNone = 0,
|
||||
kAudio,
|
||||
kAvatars,
|
||||
kContent,
|
||||
kDebug,
|
||||
kFileSystem,
|
||||
kInput,
|
||||
kMemory,
|
||||
kMisc,
|
||||
kModules,
|
||||
kNetworking,
|
||||
kThreading,
|
||||
kUI,
|
||||
kUserProfiles,
|
||||
kVideo,
|
||||
};
|
||||
|
||||
struct ExportTag {
|
||||
typedef uint32_t type;
|
||||
|
||||
// packed like so:
|
||||
// ll...... cccccccc ........ ..bihssi
|
||||
|
||||
static const int CategoryShift = 16;
|
||||
|
||||
// Export is implemented in some form and can be used.
|
||||
static const type kImplemented = 1u << 0;
|
||||
// Export is a stub and is probably bad.
|
||||
|
@ -35,17 +58,6 @@ struct ExportTag {
|
|||
// Export blocks the calling thread
|
||||
static const type kBlocking = 1u << 5;
|
||||
|
||||
static const type kThreading = 1u << 10;
|
||||
static const type kInput = 1u << 11;
|
||||
static const type kAudio = 1u << 12;
|
||||
static const type kVideo = 1u << 13;
|
||||
static const type kFileSystem = 1u << 14;
|
||||
static const type kModules = 1u << 15;
|
||||
static const type kUserProfiles = 1u << 16;
|
||||
static const type kNetworking = 1u << 17;
|
||||
static const type kMemory = 1u << 18;
|
||||
static const type kDebug = 1u << 19;
|
||||
|
||||
// Export will be logged on each call.
|
||||
static const type kLog = 1u << 30;
|
||||
// Export's result will be logged on each call.
|
||||
|
|
|
@ -764,7 +764,7 @@ void HIRBuilder::CommentFormat(const char* format, ...) {
|
|||
va_start(args, format);
|
||||
size_t chars_written = vsnprintf(p, kMaxCommentSize - 1, format, args);
|
||||
va_end(args);
|
||||
size_t rewind = kMaxCommentSize - chars_written;
|
||||
size_t rewind = kMaxCommentSize - chars_written - 1;
|
||||
arena_->Rewind(rewind);
|
||||
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
|
||||
i->src1.offset = (uint64_t)p;
|
||||
|
|
|
@ -1376,6 +1376,27 @@ void Value::DotProduct4(Value* other) {
|
|||
}
|
||||
}
|
||||
|
||||
void Value::VectorAverage(Value* other, TypeName type, bool is_unsigned,
|
||||
bool saturate) {
|
||||
assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE);
|
||||
switch (type) {
|
||||
case INT16_TYPE: {
|
||||
// TODO(gibbed): is this correct?
|
||||
alignas(16) int8_t result[16];
|
||||
__m128i src1 =
|
||||
_mm_load_si128(reinterpret_cast<const __m128i*>(constant.v128.i8));
|
||||
__m128i src2 = _mm_load_si128(
|
||||
reinterpret_cast<const __m128i*>(other->constant.v128.i8));
|
||||
__m128i dest = _mm_avg_epu16(src1, src2);
|
||||
_mm_store_si128(reinterpret_cast<__m128i*>(result), dest);
|
||||
std::memcpy(constant.v128.i8, result, sizeof(result));
|
||||
} break;
|
||||
default:
|
||||
assert_unhandled_case(type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Value::ByteSwap() {
|
||||
switch (type) {
|
||||
case INT8_TYPE:
|
||||
|
|
|
@ -170,6 +170,7 @@ class Value {
|
|||
constant.v128 = value;
|
||||
}
|
||||
void set_from(const Value* other) {
|
||||
assert_true(other->IsConstant());
|
||||
type = other->type;
|
||||
flags = other->flags;
|
||||
constant.v128 = other->constant.v128;
|
||||
|
@ -535,6 +536,8 @@ class Value {
|
|||
void VectorSub(Value* other, TypeName type, bool is_unsigned, bool saturate);
|
||||
void DotProduct3(Value* other);
|
||||
void DotProduct4(Value* other);
|
||||
void VectorAverage(Value* other, TypeName type, bool is_unsigned,
|
||||
bool saturate);
|
||||
void ByteSwap();
|
||||
void CountLeadingZeros(const Value* other);
|
||||
bool Compare(Opcode opcode, Value* other);
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/lzx.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
#include "xenia/base/byte_order.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
|
||||
#include "third_party/mspack/lzx.h"
|
||||
#include "third_party/mspack/mspack.h"
|
||||
|
||||
typedef struct mspack_memory_file_t {
|
||||
struct mspack_system sys;
|
||||
void* buffer;
|
||||
off_t buffer_size;
|
||||
off_t offset;
|
||||
} mspack_memory_file;
|
||||
mspack_memory_file* mspack_memory_open(struct mspack_system* sys, void* buffer,
|
||||
const size_t buffer_size) {
|
||||
assert_true(buffer_size < INT_MAX);
|
||||
if (buffer_size >= INT_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
mspack_memory_file* memfile =
|
||||
(mspack_memory_file*)calloc(1, sizeof(mspack_memory_file));
|
||||
if (!memfile) {
|
||||
return NULL;
|
||||
}
|
||||
memfile->buffer = buffer;
|
||||
memfile->buffer_size = (off_t)buffer_size;
|
||||
memfile->offset = 0;
|
||||
return memfile;
|
||||
}
|
||||
void mspack_memory_close(mspack_memory_file* file) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
free(memfile);
|
||||
}
|
||||
int mspack_memory_read(struct mspack_file* file, void* buffer, int chars) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
const off_t remaining = memfile->buffer_size - memfile->offset;
|
||||
const off_t total = std::min(static_cast<off_t>(chars), remaining);
|
||||
std::memcpy(buffer, (uint8_t*)memfile->buffer + memfile->offset, total);
|
||||
memfile->offset += total;
|
||||
return (int)total;
|
||||
}
|
||||
int mspack_memory_write(struct mspack_file* file, void* buffer, int chars) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
const off_t remaining = memfile->buffer_size - memfile->offset;
|
||||
const off_t total = std::min(static_cast<off_t>(chars), remaining);
|
||||
std::memcpy((uint8_t*)memfile->buffer + memfile->offset, buffer, total);
|
||||
memfile->offset += total;
|
||||
return (int)total;
|
||||
}
|
||||
void* mspack_memory_alloc(struct mspack_system* sys, size_t chars) {
|
||||
return std::calloc(chars, 1);
|
||||
}
|
||||
void mspack_memory_free(void* ptr) { free(ptr); }
|
||||
void mspack_memory_copy(void* src, void* dest, size_t chars) {
|
||||
std::memcpy(dest, src, chars);
|
||||
}
|
||||
struct mspack_system* mspack_memory_sys_create() {
|
||||
struct mspack_system* sys =
|
||||
(struct mspack_system*)std::calloc(1, sizeof(struct mspack_system));
|
||||
if (!sys) {
|
||||
return NULL;
|
||||
}
|
||||
sys->read = mspack_memory_read;
|
||||
sys->write = mspack_memory_write;
|
||||
sys->alloc = mspack_memory_alloc;
|
||||
sys->free = mspack_memory_free;
|
||||
sys->copy = mspack_memory_copy;
|
||||
return sys;
|
||||
}
|
||||
void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); }
|
||||
|
||||
int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest,
|
||||
size_t dest_len, uint32_t window_size, void* window_data,
|
||||
size_t window_data_len) {
|
||||
uint32_t window_bits = 0;
|
||||
uint32_t temp_sz = window_size;
|
||||
for (size_t m = 0; m < 32; m++, window_bits++) {
|
||||
temp_sz >>= 1;
|
||||
if (temp_sz == 0x00000000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int result_code = 1;
|
||||
|
||||
mspack_system* sys = mspack_memory_sys_create();
|
||||
mspack_memory_file* lzxsrc =
|
||||
mspack_memory_open(sys, (void*)lzx_data, lzx_len);
|
||||
mspack_memory_file* lzxdst = mspack_memory_open(sys, dest, dest_len);
|
||||
lzxd_stream* lzxd =
|
||||
lzxd_init(sys, (struct mspack_file*)lzxsrc, (struct mspack_file*)lzxdst,
|
||||
window_bits, 0, 0x8000, (off_t)dest_len, 0);
|
||||
|
||||
if (lzxd) {
|
||||
if (window_data) {
|
||||
// zero the window and then copy window_data to the end of it
|
||||
std::memset(lzxd->window, 0, window_data_len);
|
||||
std::memcpy(lzxd->window + (window_size - window_data_len), window_data,
|
||||
window_data_len);
|
||||
lzxd->ref_data_size = (uint32_t)window_data_len;
|
||||
}
|
||||
|
||||
result_code = lzxd_decompress(lzxd, (off_t)dest_len);
|
||||
|
||||
lzxd_free(lzxd);
|
||||
lzxd = NULL;
|
||||
}
|
||||
if (lzxsrc) {
|
||||
mspack_memory_close(lzxsrc);
|
||||
lzxsrc = NULL;
|
||||
}
|
||||
if (lzxdst) {
|
||||
mspack_memory_close(lzxdst);
|
||||
lzxdst = NULL;
|
||||
}
|
||||
if (sys) {
|
||||
mspack_memory_sys_destroy(sys);
|
||||
sys = NULL;
|
||||
}
|
||||
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len,
|
||||
uint32_t window_size, void* dest) {
|
||||
void* patch_end = (char*)patch + patch_len;
|
||||
auto* cur_patch = patch;
|
||||
|
||||
while (patch_end > cur_patch) {
|
||||
int patch_sz = -4; // 0 byte patches need us to remove 4 byte from next
|
||||
// patch addr because of patch_data field
|
||||
if (cur_patch->compressed_len == 0 && cur_patch->uncompressed_len == 0 &&
|
||||
cur_patch->new_addr == 0 && cur_patch->old_addr == 0)
|
||||
break;
|
||||
switch (cur_patch->compressed_len) {
|
||||
case 0: // fill with 0
|
||||
std::memset((char*)dest + cur_patch->new_addr, 0,
|
||||
cur_patch->uncompressed_len);
|
||||
break;
|
||||
case 1: // copy from old -> new
|
||||
std::memcpy((char*)dest + cur_patch->new_addr,
|
||||
(char*)dest + cur_patch->old_addr,
|
||||
cur_patch->uncompressed_len);
|
||||
break;
|
||||
default: // delta patch
|
||||
patch_sz =
|
||||
cur_patch->compressed_len - 4; // -4 because of patch_data field
|
||||
|
||||
int result = lzx_decompress(
|
||||
cur_patch->patch_data, cur_patch->compressed_len,
|
||||
(char*)dest + cur_patch->new_addr, cur_patch->uncompressed_len,
|
||||
window_size, (char*)dest + cur_patch->old_addr,
|
||||
cur_patch->uncompressed_len);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cur_patch++;
|
||||
cur_patch = (xe::xex2_delta_patch*)((char*)cur_patch +
|
||||
patch_sz); // TODO: make this less ugly
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|