Merge branch 'master' into title-updates

This commit is contained in:
emoose 2019-03-05 17:52:52 +00:00 committed by GitHub
commit a904f297c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
237 changed files with 13700 additions and 9349 deletions

View File

@ -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:

View File

@ -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"

View File

@ -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

View File

@ -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?

BIN
assets/icon/1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
assets/icon/128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/icon/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/icon/256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

BIN
assets/icon/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/icon/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

BIN
assets/icon/512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

BIN
assets/icon/64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

428
assets/icon/LICENSE Normal file
View File

@ -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.

View File

@ -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.

BIN
assets/icon/extra/114.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/icon/extra/120.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
assets/icon/extra/144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/icon/extra/150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
assets/icon/extra/152.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
assets/icon/extra/180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
assets/icon/extra/192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
assets/icon/extra/310.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/icon/extra/36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/icon/extra/57.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
assets/icon/extra/60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/icon/extra/70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
assets/icon/extra/72.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
assets/icon/extra/76.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/icon/extra/96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

View File

@ -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:

View File

@ -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)

View File

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -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.

View File

@ -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")

View File

@ -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_);

View File

@ -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",

View File

@ -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());

View File

@ -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 {

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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();

View File

@ -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_) {

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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...

View File

@ -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();

View File

@ -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_

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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"

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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;

View File

@ -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:
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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:

View File

@ -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);

183
src/xenia/cpu/lzx.cc Normal file
View File

@ -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;
}

Some files were not shown because too many files have changed in this diff Show More